Docker : comment utiliser un service de l'hôte depuis un conteneur ?

il y a 2 ans

Attention : cet article ne couvre que Docker sous Linux. Je n'ai pas testé sous Windows. Sous MacOS l'inteface docker0 n'étant pas accessible, cette astuce ne fonctionnera pas.

Imaginons que vous vouliez utiliser un service présent sur un serveur hôte depuis un conteneur docker. Ce n'est pas forcément très propre mais croyez-moi, si vous êtes amené à utiliser Docker ça va forcément vous arriver. On peut prendre le cas où une application embarquée dans un ou plusieurs conteneurs nécessiterait l'accès au service SMTP ou à une base de données présent sur l'hôte.

Cas simple : le service écoute sur la bonne interface

Si le service en question écoute sur toutes les interfaces ou sur l'interface "docker0" (pour rappel, cette interface est créée automatiquement lors du démarrage de dockerd) c'est le cas idéal et surtout le plus simple à mettre en oeuvre.

En effet, il suffit alors dans le conteneur de faire référence à l'IP de l'hôte sur le réseau docker0 (chez moi 172.17.0.1).

Faisons un essai en tentant d'utiliser le port SSH (22) de l'hôte depuis un conteneur :

$ # démarrage du service docker
$ sudo systemctl start docker
$ # récupération de l'IP de l'hôte sur l'interface docker0
$ ifconfig | grep -A 1 docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
$ # ouverture du shell d'un conteneur
$ docker run -it alpine sh
$ telnet 172.17.0.1 22
SSH-2.0-OpenSSH_7.4
$ # le service est accessible !

Cas plus complexe : le service n'écoute pas sur la bonne interface

La méthode précédente ne fonctionnera pas si par exemple vous désirez acceder à un service qui n'écoute que sur l'interface loopback (IP 127.0.0.1 sur l'hôte).

Pas de panique ! On va utiliser une petite ruse rendue possible par une commande bien sympatique : redir (d'autres outils peuvent être utilisés comme iptables mais celui-ci est mon préféré car il est particulièrement simple à mettre en oeuvre). Redir va nous permettre de rediriger des connexions TCP/IP vers une autre IP.

Tout d'abord, commençons par l'installer.

RHEL :

Après avoir ajouté le dépôt "nux-misc" (instructions), installer le paquet :

$ sudo yum install --enablerepo=nux-misc -y redir

Debian :

$ sudo apt-get update && sudo apt-get install redir

Maintenant, utilisons la commande redir de manière à rediriger les connexions TCP/IP à destination de l'IP de l'hôte sur le réseau docker0 vers 127.0.0.1 (loopback de l'hôte) :

$ sudo redir --laddr <ip hôte sur docker0> --caddr <ip où le service est accessible> --lport <port du service> --cport <port du service>

Si par exemple on veut utiliser un service MySQL uniquement accessible sur l'IP 127.0.0.1 de l'hôte depuis un conteneur on écrira :

$ sudo redir --laddr 172.17.0.1 --caddr 127.0.0.1 --lport 3306 --cport 3306

Faisons maintenant le test depuis un conteneur :

$ # ouverture du shell d'un conteneur
$ docker run -it alpine sh
$ telnet 172.17.0.1 3306
J
5.7.205'.V
          j^.R.	<L]JYmysql_native_password
$ # le service est accessible !

En conclusion on peut dire que redir est une commande simple d'utilisation pour le commun des mortels (contrairement à iptables !) qui peut rendre bien des services pour un environnement de développement ou lors de la configuration rapide d'un conteneur.

Git : ignorer les modifications d'un fichier

il y a 2 ans

Prenons le cas d'un fichier de configuration suivi (le terme anglais "tracked" est plus courrant) dans un dépôt git. Imaginons que celui-ci soit différent pour chaque développeur du porjet car il contient par exemple l'utilisateur / mot de passe pour accéder à une base de données. C'est le cas typique dans lequel tout le monde retrouvera ce fichier dans la liste des fichiers modifiés continuellement ce qui est très agaçant sad.

Pour résoudre ce problème, git propose d'ignorer les modifications effectuées sur un fichier déjà suivi via la commande suivante :

$ git update-index --assume-unchanged 

Faisons le test :

$ git init
Initialized empty Git repository in /Users/romain/Dev/test_git/.git/
$ touch foo
$ git add foo
$ git commit -m "Foo added"
[master (root-commit) 13a00de] Foo added
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
$ echo "bar" > foo
$ git status
On branch master
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

	modified:   foo

no changes added to commit (use "git add" and/or "git commit -a")
$ git update-index --assume-unchanged foo
$ git status
On branch master
nothing to commit, working directory clean

Comme prévu, le fichier n'est plus présent dans la liste des fichiers ayant changé smiley.

Pour annuler l'effet de cette commande, il suffit d'utiliser l'inverse :

$ git update-index --no-assume-unchanged 

 

PHP Tour 2017

il y a 2 ans

Le PHP Tour 2017 s'est tenu cette année à Nantes les 18 et 19 mai. C'est un événement majeur de la communauté PHP Française avec le PHP Forum qui lui a lieu chaque année à Paris.

Cette année, j'ai eu la chance d'y participer et j'en suis ravi ! Je ne peux que vous conseiller de participer à ces événements qui sont - outre le fait d'apprendre de nouvelles choses - l'occasion de se rapprocher de la crème des développeur PHP en France et de voir ce qui se passe ailleurs. L'herbe n'est pas toujours plus verte chez le voisin mais il faut bien reconnaître que si on parle aux conférenciers, on se rend souvent compte assez rapidement qu'ils sont épanouis dans leur entreprise et qu'ils aiment ce qu'ils font.

Personnellement, cela m'a permis de conforter ma vision de ce qu'est un "bon" développeur web back-end aujourd'hui (et qui utilise PHP laugh) mais aussi ce qu'est une "bonne" entreprise (pour un développeur). Dans ce lot des sociétés qui font participer leurs développeurs à ce type d'événement, joliCode s'est particulièrement démarqué de par le nombre de conférences qu'ils ont menés mais surtout leur qualité. Selon moi, c'est clairement un deal "gagnant gagnant" entre l'entreprise et le développeur. D'un côté, on cite plusieurs fois par jour le nom de la société en question, elle est valorisée par la qualité des sujets traités et l'expertise de ses collaborateurs et d'un autre, on voit des développeurs passionnés qui ont l'occasion d'aller en profondeur sur des sujets qui les intéressent avec un objectif de partage de connaissance qui mène à une satisfaction personnelle et professionnelle. Tout ça pourrait bien me donner envie dans les années à venir d'animer moi aussi quelques conférences...

C'était aussi l'occasion pour quelques sociétés comme Deezer de se créer des contacts parmi la crème des développeurs PHP en France mais si vous n'habitez pas à Paris, laissez tomber frown. Quoi qu'il en soit, c'est un excellente stratégie de recrutement qui va très certainement rapporter ses fruits pour un coût minime comparé à un chasseur de têtes associé à un processus de recrutement plus ou moins lourd.

Avec le recul, je pense que l'intérêt numéro 1 des conférences organisées par l'AFUP est de permettre l'interaction entre des individus qui font souvent exactement les mêmes choses (technos, outils, projets, ...) mais qui ne sont pas liés les uns aux autres dans la vie de tous les jours. Le contenu des conférences est bien entendu l'objectif lorsqu'on se déplace à un événement pareil mais c'est le contact avec les autres qui donne le plus de pep's en sortant de là !

Pour conclure ce petit post, je vous invite vraiment à vous rendre aux événements de l'AFUP, ce que je ferais le plus possible à partir de maintenant pour ma part.

ps : les enregistrements des conférences seront prochainement disponibles sur la chaîne Youtube de l'AFUP

Epic bug #1 : 0 compte double

il y a 2 ans

Ça faisait déjà un petit moment que je pensais à aborder les bugs mémorables que j'ai eu l'occasion de rencontrer depuis que je travaille en entreprise. Je ne sais pas trop quel nom pourrait leur correspondre le mieux, je tente "epic bug" mais sans grande conviction !

Quoi qu'il en soit, c'est l'occasion pour moi de revenir sur un problème marquant comme on en rencontre souvent quand on est développeur ou ops et qui sert de leçon une fois qu'on a fini par comprendre le pourquoi du comment wink. J'espère que le concept vous plaira, je pense continuer ce genre d'articles par la suite (ce ne sont pas les sujets qui vont manquer !).

Contexte :

Dans le cadre d'une application web permettant à des utilisateurs d'envoyer des SMS à leurs prospects, notre mission était le support de l'ensemble des caractères définis par l'encodage GSM 03.38. Sans rentrer dans les détails, c'est cet encodage qui est utilisé par nos téléphones la plupart du temps, notamment lorsque 1 SMS = 160 caractères. Ce qu'il faut savoir, c'est qu'un caractère est généralement encodé sur 7 bits mais peut aussi occuper 14 bits pour certain d'entre eux comme le symbole euro. Notre mission était donc de supporter le plus de caractères possibles dont ceux comptant double (14 bits).

Symptôme :

Plusieurs jours après la mise en production de ce dev, le commercial nous remonte un problème majeur : certains envois de SMS sont facturés double aux clients ! Branle-bas de combat dans l'équipe : on a un problème de facturation et c'est la pire chose qui peut arriver ! Après avoir vérifié l'ampleur du problème on finit heureusement par se rendre compte que le soucis ne touchait que quelques SMS. Ouf !

Recherche :

Viens maintenant le moment magique de la recherche de l'origine du problème et là... rien d'évident ! Le bout de code incriminé est forcément celui qui compte le nombre de caractères de 7 bits dans le contenu des SMS, voici à quoi cela ressemblait à peu près :

function count7bitCharacters(string $msg) : int
{
    $doubleCharacters = ['€', '^', '|', '~', '[', ']', '{', '}', '\\'];

    $numberOf7bitCharacters = 0;
    foreach (countChars($msg) as $char => $occurence) {
        if (in_array($char, $doubleCharacters)) {
            $numberOf7bitCharacters += $occurence * 2;
        } else {
            $numberOf7bitCharacters += $occurence;
        }
    }

    return $numberOf7bitCharacters;
}

/**
 * Retourne un tableau associatif avec en clé chaque
 * caractère présent dans $msg et en valeur le nombre
 * d'occurence de celui-ci.
 */
function countChars(string $msg) : array
{
    // ...
}

Après quelques tests, les résultats correspondent bien à nos attentes, le symbole euro est bien compté double contrairement à la lettre "e", etc... Cependant, après un bon moment à chercher le ou les caractère(s) comptant double (à tort), nous sommes arrivé à isoler 2 messages proches mais qui diffèrent au niveau du résultat lorsqu'on les passait à notre fonction count7bitCharacters() :

echo count7bitCharacters("Cher client, bénéficiez d'une super promotion dans tous vos magasins truc muche, -15€ sur tous nos articles !");
// Résultat attendu : 110
// Résultat obtenu : 110

echo count7bitCharacters("Cher client, bénéficiez d'une super promotion dans tous vos magasins truc muche, -10% sur tous nos articles !");
// Résultat attendu : 109
// Résultat obtenu : 110

(évidemment, tout est bien simplifié pour cet article, c'était bien plus fouillis en réalité laugh)

Résolution :

Ça y est on a trouvé le coupable, c'est le '%' ! Eh non ! C'est le zéro... Mais pourquoi ?

Regardons ce qui se passe quand c'est au tour du '0' d'être sélectionné par le foreach :

$doubleCharacters = ['€', '^', '|', '~', '[', ']', '{', '}', '\\'];

// ...

if (in_array(0, $doubleCharacters)) {
    $numberOf7bitCharacters += $occurence * 2;
} else {
    $numberOf7bitCharacters += $occurence;
}

Rien ne vous choque ? Moi non plus, cependant :

  • Une subtilité de la fonction in_array() qu'on connaît pourtant quasiment tous quand on a fait un peu de PHP joue les troubles fête. 
    Rappel de la signature de in_array() :
    bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
    Ah oui, il y a cette histoire de $strict...
  • Sachant maintenant que nos comparaisons d'égalité implicites via in_array() sont non strictes, détaillons encore le code :
    if (0 == '€' || 0 == '^' || 0 == '|' /* etc... */)) {
        $numberOf7bitCharacters += $occurence * 2;
    } else {
        $numberOf7bitCharacters += $occurence;
    }
    

    Vous l'avez senti venir ? Si ce n'est pas le cas, voici l'explication :

    var_dump(0 == "N'importe quelle chaine de caractères non numérique");
    
    // bool(true)

    Une chaîne de caractères comparée de manière non stricte avec un entier entraîne le transtypage de la string en int ! En l'absence d'une valeur numérique interprétable, la chaîne vaudra 0 (int).

  • Précision : c'est bien 0 (int) qui est passé à in_array() et pas '0' (string), mais pourquoi ? Parce que ce caractère est extrait d'une clé du tableau retourné par countChars(). 
    Rappel :
    var_dump(['0' => 'foo']);
    
    // array(1) {
    //  [0] =>
    //  string(4) "toto"
    // }

    On perd le typage d'origine de la clé au profit d'un entier.

Conclusion :

Par chance, il n'y a pas eu de conséquence à ce problème puisqu'il était spécifique au caractère '0' qui est relativement peu utilisé, on s'en est bien sorti laugh ! Quoi qu'il en soit, c'est un bel exemple qui montre à quel point des tests unitaires sur un jeu de données complet peuvent éviter bien des soucis...

Conférences de l'AFUP : deux retours d'expérience

il y a 2 ans

A quelques mois du PHP Tour 2017 qui aura lieu à Nantes les 18 et 19 mai 2017, voici un retour sur le contenu des différentes conférences qui se sont tenues en 2016 grâce à l'AFUP (dont je suis membre depuis peu) qui fait un travail excellent au côté d'intervenants de qualité.

N'ayant pas eu la possibilité d'assister à ces événements en 2016, je me suis rattrapé sur la chaîne Youtube de l'AFUP : https://www.youtube.com/user/afupPHP. Si vous n'avez pas encore regardé ces conférences, cette série d'article est faite pour vous ! Je vais passer rapidement sur les conférences qui m'ont le plus marqué.

Ce premier post sur les conférences de l'AFUP est l'occasion de mettre en avant les retours d'expériences qui, au delà des considérations techniques, sont des mines d'or pour éviter de mauvais choix lorsque certaines situations se présentent. En particulier, je m'intéresse aux retours d'expériences consacrés à la mise en place de devops, de l'intégration continue et du déploiement continue car ce sont des sujets sur la table actuellement dans la société dans laquelle je travaille.

Deux conférences se sont particulièrement démarquées selon moi :

Industrialisation et automatisation chez M6Web Lille - Pierre Marichez, Renaud Bougre

C'est pour moi la meilleure conférence de l'AFUP en 2016. Les 2 intervenants expliquent comment en l'espace d'une année ils ont réussi à passer d'un environnement hétérogène à une solution quasi unique pour leur environnement de travail ainsi que leur procédure de déploiement. Au programme : jenkins, docker, gitlab, ...

Bref, à voir absolument !

La place de PHP dans l'architecture technique de Radio France

Cela aurait pu s'appeler "on vous dit tout sur notre SI !". Radio France a des besoins hétérogènes tout en nécessitant d'absorber des pics de trafic. Cette conférence présente la réponse apportée à ces problématiques en terme d'architecture technique (architecture orientée microservices, technologies, ...).

Très intéressant, particulièrement si on s'intéresse aux microservices sans forcément déjà en avoir conçu.