Docker : le sixième pas.

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):

Capture d’écran 2016-04-20 à 14.47.21Voici 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 :

Capture d’écran 2016-04-20 à 15.37.53

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.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Time limit is exhausted. Please reload CAPTCHA.