Communicating Between Docker Containers

https://docs.oracle.com/cd/E52668_01/E54669/html/section_rsr_p2z_fp.html


26.9 Communicating Between Docker Containers

You can use the --link option with docker run to make network connection information about a server container available to a client container. The client container uses a private networking interface to access the exposed port in the server container. Docker sets environment variables about the server container in the client container that describe the interface and the ports that are available.
The following example demonstrates how to link an oraclelinux:6.6-based client container with an HTTP server container based on the mymod/httpd:v2 image that you created in Section 26.8, “Creating a Docker Image from a Dockerfile”.



To create an HTTP server and client containers that are linked:
  1. Create an HTTP server container named http_server:
    [root@host ~]# docker run -d --name http_server mymod/httpd:v2 
    a47169154222329eed66762128755cd9fdd24d0f27ff8e0f678ef136bbc66d03
  2. Create a client container named client1 that runs the bash shell and is linked to the http_server container:
    [root@host httpd]# docker run --rm -t -i --name client1 --link http_server:server \
      oraclelinux:6.6 /bin/bash
    [root@client1 ~]#
    The argument http_server:server to the --link option aliases the name http_server as server. Docker converts the alias to upper case (SERVER) and uses this string when setting up the names of the environment variables on the client.
You can now view the environment variables in the client1 container. You can also use ping to detect the server container by name or IP address, and use curl to access the web server running on the server:
[root@client1 ~]# env
HOSTNAME=10815c22e5b4
TERM=xterm
SERVER_PORT=tcp://172.17.0.16:80
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
SERVER_PORT_80_TCP_PORT=80
SERVER_PORT_80_TCP_ADDR=172.17.0.16
SERVER_PORT_80_TCP=tcp://172.17.0.16:80
SERVER_PORT_80_TCP_PROTO=tcp
SHLVL=1
SERVER_NAME=/client1/server
HOME=/
_=/usr/bin/env
[root@client1 ~]# ping -c 1 server
PING server (172.17.0.16) 56(84) bytes of data.
64 bytes from server (172.17.0.16): icmp_seq=1 ttl=64 time=0.105 ms

--- server ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.105/0.105/0.105/0.000 ms
[root@client1 ~]# ping -c 1 172.17.0.16
PING 172.17.0.16 (172.17.0.16) 56(84) bytes of data.
64 bytes from 172.17.0.16: icmp_seq=1 ttl=64 time=0.171 ms

--- 172.17.0.16 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.171/0.171/0.171/0.000 ms
[root@client1 ~]# curl http://server
HTTP server running on guest
[root@client1 ~]# curl http://172.17.0.16
HTTP server running on guest
You can start multiple client container instances with different names, each of which can access port 80 on the server container. Docker assigns a different IP address to each client. As shown in the following example output, Docker creates an entry for the server in the /etc/hosts files on each client but it does not create entries for the names of the client containers themselves:
[root@client1 ~]# cat /etc/hosts
172.17.0.17  10815c22e5b4
127.0.0.1    localhost
::1          localhost ip6-localhost ip6-loopback
fe00::0      ip6-localnet
ff00::0      ip6-mcastprefix
ff02::1      ip6-allnodes
ff02::2      ip6-allrouters
172.17.0.16  server
[root@client1 ~]# ping -c 1 client2
ping: unknown host client2
[root@client1 ~]# ping -c 1 172.17.0.18
PING 172.17.0.18 (172.17.0.18) 56(84) bytes of data.
64 bytes from 172.17.0.18: icmp_seq=1 ttl=64 time=0.268 ms

--- 172.17.0.18 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.268/0.268/0.268/0.000 ms
By default, the clients are visible to each other on the private network only by their IP addresses.
The docker ps command shows the containers that are running:
[root@host ~]# docker ps
CONTAINER ID  IMAGE            COMMAND          CREATED  STATUS        PORTS  NAMES
449abeac3041  oraclelinux:6.6  /bin/bash        ...   Up 1 minutes         client2
10815c22e5b4  oraclelinux:6.6  /bin/bash        ...   Up 2 minutes         client1
a47169154222  mymod/httpd:v2   /usr/sbin/httpd  ...   Up 3 minutes     80/tcp  
                                         client1/server,client2/server,http_server
The NAMES column shows that http_server is linked to client1 and client2 as server. The PORTS column shows that Docker has not remapped TCP port 80 on http_server to another port on the host.

26.9.1 Example of Linking Database and HTTP Server Containers

Note
This simple example demonstrates how to link containers. You should not use it as the basis of a production application.
The following example demonstrates how to link a container that is running a MySQL server with a container running an HTTP server.
First of all, we define a Dockerfile for the MySQL server, which we place in the/var/docker_projects/mymod/mysql directory:
FROM oraclelinux:6.6
ENV http_proxy http://proxy.mydom.com:80
RUN yum install -y mysql-server
ADD my.cnf /etc/my.cnf
ADD run.sh /opt/run.sh
RUN chmod 744 /opt/run.sh
ENTRYPOINT /opt/run.sh
The instruction keywords define how to create the image:
ADD
Copy the files my.cnf and run.sh from the /var/docker_projects/mymod/mysql directory to /etc/my.cnfand /opt/run.sh in the container.
ENTRYPOINT
Specify that the container always runs /opt/run.sh.
ENV
Define the web proxy in the build environment (as an alternative to modifying /etc/yum.conf).
FROM
Define oraclelinux:6.6 as a basis for the new image.
RUN
Install the mysql-server package and make the /opt/run.sh script executable.
The my.cnf file in /var/docker_projects/mymod/mysql contains the database configuration:
[mysqld]
bind-address=0.0.0.0

console=1
general_log=1
general_log_file=/dev/stdout
log_error=/dev/stderr

collation-server=utf8_unicode_ci
character-set-server=utf8

datadir=/var/lib/mysql
The run.sh file in /var/docker_projects/mymod/mysql contains the shell script for starting the database:
#!/bin/bash

chown -R mysql:mysql /var/lib/mysql

mysql_install_db --user=mysql > /dev/null

/usr/libexec/mysqld --user mysql --bootstrap << SQL
    FLUSH PRIVILEGES;
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
    CREATE USER dbuser IDENTIFIED BY 'secret';
    CREATE DATABASE MYDB;
    USE MYDB;
    GRANT ALL ON MYDB.* to 'dbuser'@'%';

SQL

/usr/bin/mysqld_safe --user mysql
Having set up the Dockerfile and the files my.cnf and run.sh, we can now build the image mymod/mysql:v1 and create an instance of this container named db that uses the standard MySQL connection port (3306):
# docker build -t="mymod/mysql:v1" /var/docker_projects/mymod/mysql
Uploading context  5.12 kB
Uploading context
Step 0 : FROM oraclelinux:6.6
 ---> d56e767abb61
Step 1 : ENV http_proxy  http://proxy.mydom.com:80
 ---> Running in 2d6ad386263d
 ---> f92df8c449eb
...
Step 6 : ENTRYPOINT /opt/run.sh
 ---> Running in 61e7e5bab9a1
 ---> 54b6e9473375
Removing intermediate container 61e7e5bab9a1
Successfully built 54b6e9473375
# docker run -d --name db -p 3306:3306 mymod/mysql:v1
ba8816c540513a892aa89828b5cf33464a7bb4616b56177266033d3311d0e00d
We next define a Dockerfile for the HTTP server, which we place in the /var/docker_projects/mymod/httpd2directory:
FROM oraclelinux:6.6
ENV http_proxy http://proxy.mydom.com:80
RUN yum install -y httpd perl perl-DBI.x86_64 libdbi-dbd-mysql.x86_64 perl-DBD-MySQL.x86_64
ADD version.pl /var/www/cgi-bin/version.pl
RUN chmod 755 /var/www/cgi-bin/version.pl
ADD initdb.pl /var/www/cgi-bin/initdb.pl
RUN chmod 755 /var/www/cgi-bin/initdb.pl
ADD doquery.pl /var/www/cgi-bin/doquery.pl
RUN chmod 755 /var/www/cgi-bin/doquery.pl
RUN sed -i -e '/<Directory "\/var\/www\/cgi-bin">/,/<\/Directory>/c\\\
<Directory "/var/www/cgi-bin">\n\
    Options +ExecCGI\n\
    AddHandler cgi-script .pl .cgi\n\
</Directory>' /etc/httpd/conf/httpd.conf
EXPOSE 80
ENTRYPOINT /usr/sbin/httpd -D FOREGROUND
This Dockerfile modifies the container's HTTP server configuration file (/etc/httpd/conf/httpd.conf) to allow the use of CGI scripts and installs the following Perl scripts from the /var/docker_projects/mymod/httpd2directory:
version.pl
Connecst to the database and returns its version.
#!/usr/bin/perl
use DBI;

print "Content-type: text/html\n\n";
my $dbh = DBI->connect(          
    "dbi:mysql:dbname=MYDB:host=db",
    "dbuser",                          
    "secret",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare("SELECT VERSION()");
$sth->execute();

my $ver = $sth->fetch();
print "Version = ", @$ver, "\n";

$sth->finish();
$dbh->disconnect();
initdb.pl
Sets up the database and populates a table with several entries.
#!/usr/bin/perl
use strict;
use DBI;

print "Content-type: text/html\n\n";
my $dbh = DBI->connect(
    "dbi:mysql:dbname=MYDB:host=db",
    "dbuser",
    "secret",
    { RaiseError => 1}
) or die $DBI::errstr;

$dbh->do("DROP TABLE IF EXISTS PEOPLE");
$dbh->do("CREATE TABLE People(Id INT PRIMARY KEY, Name TEXT, Age INT) ENGINE=InnoDB");
$dbh->do("INSERT INTO People VALUES(1,'Alice',42)");
$dbh->do("INSERT INTO People VALUES(2,'Bobby',27)");
$dbh->do("INSERT INTO People VALUES(3,'Carol',29)");
$dbh->do("INSERT INTO People VALUES(4,'Daisy',20)");
$dbh->do("INSERT INTO People VALUES(5,'Eddie',35)");
$dbh->do("INSERT INTO People VALUES(6,'Frank',21)");

my @noerr = ('Rows inserted in People table');
print @noerr;
print "\n";

my $sth = $dbh->prepare( "SELECT * FROM People" );
$sth->execute();
 
for ( 1 .. $sth->rows() ) {
    my ($id, $name, $age) = $sth->fetchrow();
    print "$id $name $age\n";
}

$sth->finish();
$dbh->disconnect();
doquery.pl
Performs a simple query on the database, using the command argument as data for the query.
#!/usr/bin/perl
use strict;
use DBI;

print "Content-type: text/html\n\n";
my $dbh = DBI->connect(          
    "dbi:mysql:dbname=MYDB;host=db",
    "dbuser",                         
    "secret",                          
    { RaiseError => 1 },
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM People WHERE Age > $ARGV[0]" );
$sth->execute();

my $fields = $sth->{NUM_OF_FIELDS};
my $rows = $sth->rows();
print "Selected $rows row(s) with $fields field(s)\n";

for ( 1 .. $rows ) {
  my ($id, $name, $age) = $sth->fetchrow();
  print "$id $name $age\n";
}

$sth->finish();
$dbh->disconnect();
Having set up the Dockerfile and the Perl scripts, we can now build the image mymod/httpd:v3 and create an instance of this container named web, which is linked to the db container and which uses the standard HTTP server port (80) on the host:
# docker build -t="mymod/httpd:v3" /var/docker_projects/mymod/httpd2
Uploading context 142.8 kB
Uploading context 
Step 0 : FROM oraclelinux:6.6
 ---> d56e767abb61
Step 1 : ENV http_proxy  http://proxy.mydom.com:80
 ---> Using cache
 ---> f92df8c449eb
...
Step 11 : ENTRYPOINT /usr/sbin/httpd -D FOREGROUND
 ---> Running in 3203c57a7204
 ---> 10dc2d7624d3
Removing intermediate container 3203c57a7204
Successfully built 10dc2d7624d3
# docker run -d --name web -p 80:80 --link db:db mymod/httpd:v3
ba8816c540513a892aa89828b5cf33464a7bb4616b56177266033d3311d0e00d
Finally, we can use curl to test the operation of the CGI scripts:
$ curl http://10.0.0.2/cgi-bin/version.pl
Version = 5.1.73-log
$ curl http://10.0.0.2/cgi-bin/initdb.pl
Rows inserted in People table
1 Alice 42
2 Bobby 27
3 Carol 29
4 Daisy 20
5 Eddie 35
6 Frank 21
$ curl http://10.0.0.2/cgi-bin/doquery.pl?30
Selected 2 row(s) with 3 field(s)
1 Alice 42
5 Eddie 35
$ curl http://10.0.0.2/cgi-bin/doquery.pl?21
Selected 4 row(s) with 3 field(s)
1 Alice 42
2 Bobby 27
3 Carol 29
5 Eddie 35

Comentarios