Attention ceci est mon brouillon avant de faire une belle documentation sur Docker (il y a à boire et à manger).
On passe donc à l’étape de l’installation de HAProxy, l’installation cible est la suivante (il va falloir que j’améliore mon server.c (que l’on va appeler server2.c) afin d’avoir une connexion avec la base de donnée):
Voici un nouveau server2.c , mais avant cela il faut installer le RPM qui permet de faire de dev :
[root@localhost ~]# yum install postgresql-devel
Pour compiler il va falloir appeler la librairie pq (pour postgresql), cela donne donc :
[root@localhost ~]# gcc -o server2 server2.c -lpq
J’ai donc ajouter dans le programme :
-un bout de code afin de voir quel est l’interface locale
-un bout de code afin de voir quel est l’ip locale.
-un bout de code pour couper la communication via QUIT, EXIT, CLOSE.
-un bout de code pour se connecter à la base de donnée.
-un bout de code pour avoir des informations via le telnet.
Le source :
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/un.h> #include "libpq-fe.h" #define MYPORT 80 #define BACKLOG 5 #define MAXCLIENTS 5 #define MAXDATASIZE 100 int main (void) { int sockfd = -1, new_fd, numbytes, highest = 0, i; int clients[MAXCLIENTS]; char buffer[MAXDATASIZE]; char localip[MAXDATASIZE]; struct sockaddr_in my_addr, their_addr; socklen_t sin_size; struct timeval tv; fd_set readfds; const char *conninfo; PGconn *conn; PGresult *res; FILE *f; char line[100], *p, *c; const char *google_dns_server = "8.8.8.8"; int dns_port = 53; struct sockaddr_in serv; int sock = socket (AF_INET, SOCK_DGRAM, 0); f = fopen ("/proc/net/route", "r"); while (fgets (line, 100, f)) { p = strtok (line, " \t"); c = strtok (NULL, " \t"); if (p != NULL && c != NULL) { if (strcmp (c, "00000000") == 0) { printf ("Default interface is : %s \n", p); break; } } } if (sock < 0) { perror ("Socket error"); } memset (&serv, 0, sizeof (serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr (google_dns_server); serv.sin_port = htons (dns_port); int err = connect (sock, (const struct sockaddr *) &serv, sizeof (serv)); struct sockaddr_in name; socklen_t namelen = sizeof (name); err = getsockname (sock, (struct sockaddr *) &name, &namelen); const char *p2 = inet_ntop (AF_INET, &name.sin_addr, localip, 100); if (p2 != NULL) { printf ("Local ip is : %s \n", localip); } else { //Some error printf ("Error number : %d . Error message : %s \n", errno, strerror (errno)); strcpy (localip, "Error"); } close (sock); conninfo = "hostaddr=127.0.0.1 port=5432 dbname=postgres user=postgres password=password"; if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) { perror ("socket"); exit (-1); } my_addr.sin_family = AF_INET; my_addr.sin_port = htons (MYPORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero (&(my_addr.sin_zero), 8); if (bind (sockfd, (struct sockaddr *) &my_addr, sizeof (struct sockaddr)) == -1) { perror ("bind"); exit (-1); } if (listen (sockfd, BACKLOG) == -1) { perror ("listen"); exit (-1); } bzero (clients, sizeof (clients)); highest = sockfd; while (1) { sin_size = sizeof (struct sockaddr_in); tv.tv_sec = 0; tv.tv_usec = 250000; FD_ZERO (&readfds); for (i = 0; i < MAXCLIENTS; i++) { if (clients[i] != 0) { FD_SET (clients[i], &readfds); } } FD_SET (sockfd, &readfds); if (select (highest + 1, &readfds, NULL, NULL, &tv) >= 0) { if (FD_ISSET (sockfd, &readfds)) { if ((new_fd = accept (sockfd, (struct sockaddr *) &their_addr, &sin_size)) == -1) { perror ("ACCEPT"); continue; } for (i = 0; i < MAXCLIENTS; i++) { if (clients[i] == 0) { clients[i] = new_fd; break; } } if (i != MAXCLIENTS) { if (new_fd > highest) { highest = clients[i]; } printf ("Connexion received from %s (slot %i) ", inet_ntoa (their_addr.sin_addr), i); send (new_fd, "\nHELLO\n", 7, MSG_NOSIGNAL); } else { send (new_fd, "\nTOO MANY CLIENT\n", 17, MSG_NOSIGNAL); close (new_fd); } } for (i = 0; i < MAXCLIENTS; i++) { if (FD_ISSET (clients[i], &readfds)) { if ((numbytes = recv (clients[i], buffer, MAXDATASIZE, 0)) <= 0) { printf ("Connexion lost from slot %i", i); close (clients[i]); clients[i] = 0; } else { buffer[numbytes] = '\0'; printf ("Received from slot %i : %s", i, buffer); if (strncmp (buffer, "POSTGRES", 6) == 0) { conn = PQconnectdb (conninfo); if (PQstatus (conn) != CONNECTION_OK) { fprintf (stderr, "Connection to database failed: %s", PQerrorMessage (conn)); send (new_fd, "\nDB KO\n", 7, MSG_NOSIGNAL); } else { send (new_fd, "\nDB OK\n", 7, MSG_NOSIGNAL); /* INSERT CLIENT IP and timestamp */ } PQfinish (conn); } if ((strncmp (buffer, "QUIT", 4) == 0)) { printf ("Connexion QUIT from slot %i", i); close (clients[i]); clients[i] = 0; } if ((strncmp (buffer, "EXIT", 4) == 0)) { printf ("Connexion EXIT from slot %i", i); close (clients[i]); clients[i] = 0; } if ((strncmp (buffer, "CLOSE", 5) == 0)) { printf ("Connexion CLOSE from slot %i", i); close (clients[i]); clients[i] = 0; } if ((strncmp (buffer, "INTERFACE", 9) == 0)) { send (new_fd, "\n", 1, MSG_NOSIGNAL); send (new_fd, localip, strlen (localip), MSG_NOSIGNAL); send (new_fd, "\n", 1, MSG_NOSIGNAL); } if ((strncmp (buffer, "IP", 2) == 0)) { send (new_fd, "\n", 1, MSG_NOSIGNAL); send (new_fd, p, strlen (p), MSG_NOSIGNAL); send (new_fd, "\n", 1, MSG_NOSIGNAL); } if ((strncmp (buffer, "DBCNX", 2) == 0)) { send (new_fd, "\n", 1, MSG_NOSIGNAL); send (new_fd, conninfo, strlen (conninfo), MSG_NOSIGNAL); send (new_fd, "\n", 1, MSG_NOSIGNAL); } } } } } else { perror ("SELECT"); continue; } } return 0; }
Un petit test, sur un terminal je lance mon ./server2 :
[root@localhost ~]# ./server2 Default interface is : enp0s3 Local ip is : 192.168.10.159 Connexion received from 127.0.0.1 (slot 0) Received from slot 0 : IP Received from slot 0 : INTERFACE Received from slot 0 : DB Received from slot 0 : POSTGRES Received from slot 0 : QUIT
Et sur l’autre terminal je fais mon telnet :
Maintenant il faut faire le Dockerfile de notre nouvelle application de test.
[root@localhost ~]# cat Dockerfile FROM fedora MAINTAINER toto toto@cyber-neurones.org COPY ./server2 /sbin/server2 RUN dnf install postgresql -y # Le port en ecoute EXPOSE 80 # Pour lancer postgres CMD ["/sbin/server2"] [root@localhost ~]# docker build -t my-server2 . Sending build context to Docker daemon 81.41 kB Step 1 : FROM fedora ---> ddd5c9c1d0f2 Step 2 : MAINTAINER toto toto@cyber-neurones.org ---> Using cache ---> bb6bc55cbbfc Step 3 : COPY ./server2 /sbin/server2 ---> Using cache ---> 9dc98bb8714f Step 4 : RUN dnf install postgresql -y ---> Running in 6ecdbee5cb9d Last metadata expiration check performed 0:00:40 ago on Fri Apr 15 02:43:33 2016. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: postgresql x86_64 9.4.7-1.fc23 updates 1.1 M postgresql-libs x86_64 9.4.7-1.fc23 updates 240 k Transaction Summary ================================================================================ Install 2 Packages Total download size: 1.3 M Installed size: 4.4 M Downloading Packages: -------------------------------------------------------------------------------- Total 5.7 kB/s | 1.3 MB 04:02 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Installing : postgresql-libs-9.4.7-1.fc23.x86_64 1/2 Installing : postgresql-9.4.7-1.fc23.x86_64 2/2 Verifying : postgresql-9.4.7-1.fc23.x86_64 1/2 Verifying : postgresql-libs-9.4.7-1.fc23.x86_64 2/2 Installed: postgresql.x86_64 9.4.7-1.fc23 postgresql-libs.x86_64 9.4.7-1.fc23 Complete! ---> db3219dbae87 Removing intermediate container 6ecdbee5cb9d Step 5 : EXPOSE 80 ---> Running in 46227ce25198 ---> 0ed57fe27084 Removing intermediate container 46227ce25198 Step 6 : CMD /sbin/server2 ---> Running in 1da4a2133df3 ---> 2b02e0bc8c6e Removing intermediate container 1da4a2133df3 Successfully built 2b02e0bc8c6e
Et maintenant le moment de vérité, on fait le test de notre server2 :
[root@localhost ~]# docker run -p 80:80 --name my-server2.1 -d my-server2
d7d4cb51000828388a09e5648e5b92094e5a17298d799b63b41d9511129b6211
[root@localhost ~]# telnet 127.0.0.1 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
HELLO
IP
eth0
INTERFACE
172.17.0.3
POSTGRES
DB KO
DBCNX
hostaddr=127.0.0.1 port=5432 dbname=postgres user=postgres password=password
QUIT
Connection closed by foreign host.
C’est l’échec, et c’est normal 🙁 … on essaye de se connecter en local, alors que le local c’est le conteneur !. Il faut donc se connecter à distance c’est à dire d’un conteneur à l’autre.
Je vais donc essayer d’utiliser la commande link :
[root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2fc533c55725 postgres "/docker-entrypoint.s" 7 hours ago Up 7 hours 0.0.0.0:5432->5432/tcp postgres2 [root@localhost ~]# docker run -p 80:80 --link postgres2:postgres2 --name my-server2.2 -d my-server2 1c35469315b9de3d720ee963cfffe010a8efc069ee027c4e6c7bff119ea8865e [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1c35469315b9 my-server2 "/sbin/server2" 18 seconds ago Up 17 seconds 0.0.0.0:80->80/tcp my-server2.2 2fc533c55725 postgres "/docker-entrypoint.s" 7 hours ago Up 7 hours 0.0.0.0:5432->5432/tcp postgres2
Je regarde toutes les variables des deux containers afin de faire les modifications sur mon programme.
[root@localhost ~]# docker exec 2fc533c55725 env PATH=/usr/lib/postgresql/9.5/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=2fc533c55725 POSTGRES_PASSWORD=password GOSU_VERSION=1.7 LANG=en_US.utf8 PG_MAJOR=9.5 PG_VERSION=9.5.2-1.pgdg80+1 PGDATA=/var/lib/postgresql/data HOME=/root [root@localhost ~]# docker exec 1c35469315b9 env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=1c35469315b9 POSTGRES2_PORT=tcp://172.17.0.2:5432 POSTGRES2_PORT_5432_TCP=tcp://172.17.0.2:5432 POSTGRES2_PORT_5432_TCP_ADDR=172.17.0.2 POSTGRES2_PORT_5432_TCP_PORT=5432 POSTGRES2_PORT_5432_TCP_PROTO=tcp POSTGRES2_NAME=/my-server2.2/postgres2 POSTGRES2_ENV_POSTGRES_PASSWORD=password POSTGRES2_ENV_GOSU_VERSION=1.7 POSTGRES2_ENV_LANG=en_US.utf8 POSTGRES2_ENV_PG_MAJOR=9.5 POSTGRES2_ENV_PG_VERSION=9.5.2-1.pgdg80+1 POSTGRES2_ENV_PGDATA=/var/lib/postgresql/data HOME=/root
On va faire notre server3.c, on a une seule ligne à modifier :
/* Avant : conninfo = "hostaddr=127.0.0.1 port=5432 dbname=postgres user=postgres password=password"; Après */ conninfo = "hostaddr=$POSTGRES2_PORT_5432_TCP_ADDR port=$POSTGRES2_PORT_5432_TCP_PORT user=postgres password=$POSTGRES2_ENV_POSTGRES_PASSWORD";
Ensuite modification du Dockerfile, puis build, … le résultat :
[root@localhost ~]# telnet 127.0.0.1 80 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. HELLO DB hostaddr=$POSTGRES2_PORT_5432_TCP_ADDR port=$POSTGRES2_PORT_5432_TCP_PORT user=postgres password=$POSTGRES2_ENV_POSTGRES_PASSWORD POSTGRES DB KO QUIT Connection closed by foreign host.
Cela aurait été trop simple 😉 Donc on va utiliser un fonction pour faire cela, on va utiliser la fonction getenv. Les modifications dans server4.c
char conninfo[MAXDATASIZE]; ... sprintf(conninfo,"hostaddr=%s port=%s user=postgres password=%s",getenv("POSTGRES2_PORT_5432_TCP_ADDR"), getenv("POSTGRES2_PORT_5432_TCP_PORT"), getenv("POSTGRES2_ENV_POSTGRES_PASSWORD"));
Je pourrais même faire mieux en faisant en sorte que cela marche en local et dans le container.
printf("POSTGRES2_PORT_5432_TCP_ADDR : %s \n", getenv("POSTGRES2_PORT_5432_TCP_ADDR")); if(getenv("POSTGRES2_PORT_5432_TCP_ADDR") == NULL) { sprintf(conninfo,"hostaddr=%s port=%s user=postgres password=%s","127.0.0.1", "5432", "postgres"); } else { sprintf(conninfo,"hostaddr=%s port=%s user=postgres password=%s",getenv("POSTGRES2_PORT_5432_TCP_ADDR"), getenv("POSTGRES2_PORT_5432_TCP_PORT"), getenv("POSTGRES2_ENV_POSTGRES_PASSWORD")); }
Le test … le stress en cas de nouvel échec 🙂 .
[root@localhost ~]# docker build -t my-server4 . Sending build context to Docker daemon 129 kB Step 1 : FROM fedora ---> ddd5c9c1d0f2 Step 2 : MAINTAINER toto toto@cyber-neurones.org ---> Using cache ---> bb6bc55cbbfc Step 3 : COPY ./server4 /sbin/server4 ---> 85cb6ab4dcea Removing intermediate container 0f75d8dd2e32 Step 4 : RUN dnf install postgresql -y ---> Running in 3025055c6cfb Last metadata expiration check performed 0:02:35 ago on Fri Apr 15 04:38:45 2016. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: postgresql x86_64 9.4.7-1.fc23 updates 1.1 M postgresql-libs x86_64 9.4.7-1.fc23 updates 240 k Transaction Summary ================================================================================ Install 2 Packages Total download size: 1.3 M Installed size: 4.4 M Downloading Packages: -------------------------------------------------------------------------------- Total 501 kB/s | 1.3 MB 00:02 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Installing : postgresql-libs-9.4.7-1.fc23.x86_64 1/2 Installing : postgresql-9.4.7-1.fc23.x86_64 2/2 Verifying : postgresql-9.4.7-1.fc23.x86_64 1/2 Verifying : postgresql-libs-9.4.7-1.fc23.x86_64 2/2 Installed: postgresql.x86_64 9.4.7-1.fc23 postgresql-libs.x86_64 9.4.7-1.fc23 Complete! ---> 9541c83ce007 Removing intermediate container 3025055c6cfb Step 5 : EXPOSE 80 ---> Running in 1ae2a911c314 ---> 944016c4027e Removing intermediate container 1ae2a911c314 Step 6 : CMD /sbin/server4 ---> Running in 7ca20a5578e2 ---> 4f8c8c3fc2e5 Removing intermediate container 7ca20a5578e2 Successfully built 4f8c8c3fc2e5 [root@localhost ~]# docker run -p 80:80 --link postgres2:postgres2 --name my-server4.1 -d my-server4 08c524d398afb37c4568399d9d1f7325feb1954a4ac1593dc68684aa36b65e45 [root@localhost ~]# telnet 127.0.0.1 80 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. HELLO DBCNX hostaddr=172.17.0.2 port=5432 user=postgres password=password IP eth0 INTERFACE 172.17.0.3 POSTGRES DB OK QUIT Connection closed by foreign host.
J’ai donc mon programme qui fait bien ce que je veux, maintenant il va falloir passer à HAProxy. A force de repousser je vais finir par ne pas mettre les pieds dedans. La notion de lien (option -link) était importante à connaitre afin de pouvoir faire un dialogue entre containers.