Learn how to customize a Docker container image and use it to instantiate application instances across different Linux servers. This article describes how to create a Dockerfile, how to allocate runtime resources to containers, and how to establish a communication channel between two containers (for example, between web server and database containers).
Introduction
This hands-on lab is designed to help you get a taste for how you can use Docker to simplify application provisioning on Linux. It demonstrates how to create a Docker container on Oracle Linux, modify it, and use it to instantiate multiple application instances. It also describes how to allocate system CPU and memory resources to a Docker container, how to set up a database container, and how other containers can connect to that database container.
Getting Started
You can download a pre-built VM template for Oracle VM VirtualBox for this lab: Oracle Linux VM Images for Hands-On Lab
Once you have downloaded and imported the VM template into VirtualBox, log to the VM as holuser with the password oracle.
To use Docker, the first step is to download and install the Docker Engine RPM packages. Oracle publishes Docker Engine RPMs for Oracle Linux 6 and Oracle Linux 7 on the public yum site and on the Unbreakable Linux Network (ULN).
Up-to-date Instructions for installing the Docker Engine are available in the Oracle Linux 6 Docker User's Guide or the Oracle Linux 7 Docker User's Guide.
The initial steps are pretty simple: first enable the ol7_addons
channel in /etc/yum.repos.d/public-yum-ol*.repo
by using the yum-config-manager
tool and then run yum install docker
.
In addition, the hands-on lab image has been pre-configured with a btrfs-based filesystem for /var/lib/docker
. The instructions above detail how to create and format a btrfs
filesystem. The following commands assume you are logged in as the holuser
user and have opened a Terminal window:
[holuser@hol10328 ~]$ sudo yum-config-manager --enable ol7_addons
[holuser@hol10328 ~]$ sudo yum install docker-engine
[holuser@hol10328 ~]$ sudo usermod -a -G docker holuser
You'll notice that the last command adds the holuser
to the docker
group. This allows the holuser
to run the Docker tool and manipulate containers. In order for the group membership to take effect, please log out and then log back into the graphical user desktop.
Once Docker has been installed, we need to enable and start it using the systemd
tool:
[holuser@hol10328 ~]$ sudo systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled)
Drop-In: /etc/systemd/system/docker.service.d
└─docker-sysconfig.conf
Active: inactive (dead)
Docs: https://docs.docker.com
Here we can see that the Docker Engine has not been started automatically after installation. First, we need to enable the Docker Engine so that it is started automatically whenever the system boots:
[holuser@hol10328 ~]$ sudo systemctl enable docker
ln -s '/usr/lib/systemd/system/docker.service' '/etc/systemd/system/multi-user.target.wants/docker.service'
Next, we need to start the Docker Engine and verify that it has started correctly:
[holuser@hol10328 ~]$ sudo systemctl start docker
[holuser@hol10328 ~]$ sudo systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled)
Drop-In: /etc/systemd/system/docker.service.d
└─docker-sysconfig.conf
Active: active (running) since Sun 2015-10-04 14:15:47 PDT; 8s ago
Docs: https://docs.docker.com
Main PID: 2427 (docker)
CGroup: /system.slice/docker.service
└─2427 /usr/bin/docker -d --selinux-enabled
Oct 04 14:15:22 hol10328.oracleworld.com systemd[1]: Starting Docker Application Container Engine...
...
Oct 04 14:15:47 hol10328.oracleworld.com systemd[1]: Started Docker Application Container Engine.
Hint: Some lines were ellipsized, use -l to show in full.
Now that the Docker Engine has started, we can check the version of both the engine and the Docker client:
[root@hol10328 ~]# docker version
Client:
Version: 1.8.2
API version: 1.20
Go version: go1.4.2
Git commit: 390110e
Built: Fri Sep 25 10:49:31 UTC 2015
OS/Arch: linux/amd64
Server:
Version: 1.8.2
API version: 1.20
Go version: go1.4.2
Git commit: 390110e
Built: Fri Sep 25 10:49:31 UTC 2015
OS/Arch: linux/amd64
We can also view information about the currently running engine, including which storage and execution drivers are active:
[root@hol10328 ~]# docker info
Containers: 0
Images: 0
Storage Driver: btrfs
Build Version: Btrfs v3.16.2
Library Version: 101
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.8.13-98.2.2.el7uek.x86_64
Operating System: Oracle Linux Server 7.1
CPUs: 1
Total Memory: 3.861 GiB
Name: hol10328.oracleworld.com
ID: D4FU:RJUB:65CH:NBLJ:UFN6:NWWY:U2CP:6PS6:4KB6:F6GX:YJSJ:6ZZL
The Docker Hub Registry is a public cloud store of images hosted by Docker, Inc. that can be used to build running containers. Repositories provide a mechanism for Docker image distribution and sharing. Docker, Inc. also hosts private repositories for a monthly fee. Alternatively, you can create a private Docker Registry behind your own corporate firewall, implementing protection mechanisms, such as SSL encryption and HTTP authentication, to restrict and protect access.
The following commands pull the Oracle Linux 6 and Oracle Linux 7 images from the public Docker Hub Registry, as well as the official MySQL image from Oracle, downloading them to the local machine:
[holuser@hol10328 ~]$ docker pull oraclelinux
[holuser@hol10328 ~]$ docker pull oraclelinux:6
[holuser@hol10328 ~]$ docker pull mysql/mysql-server
Images can include a specific tag, which indicates a particular build of that image. In the commands above, you'll notice we pulled oraclelinux
first, which always points to the latest version of Oracle Linux on the Docker Hub (currently 7.1). The second command specified the specific tag "6", which indicates that we want the latest Oracle Linux 6 build. To view all the available tags, visit the image page on the Docker Hub Registry.
During the download process, you'll notice that Docker tells us when a particular filesystem layer already exists. This is because Docker creates the final filesystem for each container by layering changes. This allows Docker to optimize both downloads and cloning operations to only operate on the layers that have changes.
Next, we can view all the available images on our local machine:
[holuser@hol10328 ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
mysql/mysql-server latest cf18a41d54bb 9 days ago 286 MB
oraclelinux 6 5ef381c513c6 3 weeks ago 156.2 MB
oraclelinux latest a8fd27de55f5 3 weeks ago 189.6 MB
Customizing a Container for Application Provisioning
Suppose that I want to provision multiple, identical web servers across multiple Linux servers in my data center. Docker makes it easy to create a preconfigured, cookie-cutter environment in a container image. We can then use this prebuilt image and deploy it across one or many Linux hosts.
To build a customized container image, we must first build a guest container, install the web server, and configure it to deliver web server content. We can use the docker run command to run an Oracle Linux 7 base container and execute a bash shell in the guest container:
[holuser@hol10328 ~]$ docker run -t -i --name guest oraclelinux /bin/bash
The Docker Engine assigns an image ID to every running container instance. Because we used the arguments -i
and -t
, the bash shell runs interactively, and the prompt reflects the first 12 characters (f85d55a6893f
) of my running container's image ID. The --name
argument specifies a name for the running container instance. If you choose not to enter a name, the Docker Engine generates a random string that incorporates the name of a notable scientist, inventor, or developer, such as evil_jones
; sad_ritchie
; sleepy_curie
and prickly_mestorf
.
Open another shell and use the docker ps
command to show information about the running guest container, including the shortened form of the image ID, the base image used to create this container, and the container name:
[holuser@hol10328 ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b761e9371d83 oraclelinux "/bin/bash" 7 seconds ago Up 6 seconds guest
Back in the original shell and in the newly-created Docker guest, we install the httpd and perl RPM packages using yum, just as you would on any other physical or virtual server. We also use yum clean all
to remove cache files that yum creates during package installation, because that will ultimately save some space in the exported container image:
[root@a1f338b303f7 /]# yum install -y httpd perl && yum clean all
ol7_UEKR3 | 1.2 kB 00:00:00
ol7_latest | 1.4 kB 00:00:00
(1/5): ol7_latest/x86_64/updateinfo | 425 kB 00:00:00
(2/5): ol7_UEKR3/x86_64/updateinfo | 48 kB 00:00:00
(3/5): ol7_latest/x86_64/group | 652 kB 00:00:01
(4/5): ol7_UEKR3/x86_64/primary | 12 MB 00:00:02
(5/5): ol7_latest/x86_64/primary | 11 MB 00:00:02
ol7_UEKR3 300/300
ol7_latest 10321/10321
Resolving Dependencies
...
Installed:
httpd.x86_64 0:2.4.6-31.0.1.el7_1.1 perl.x86_64 4:5.16.3-285.el7
...
Complete!
Cleaning repos: ol7_UEKR3 ol7_latest
Cleaning up everything
At this point, We need to configure some content for the web server to display. For simplicity, let's create a basic opening page in the /var/www/html directory on the guest:
[root@a1f338b303f7 /]# echo "Example Web Server Content" > /var/www/html/index.html
The guest container is now configured with the software environment that we want. Typing exit
stops the running guest, returning to the prompt for the Linux host:
[root@b761e9371d83 /]# exit
exit
[holuser@hol10328 ~]$
We now need to save this modified guest as an image that can be used to create new containers. To get the ID of the modified container, we use the docker ps
command with the --latest
and --quiet
parameters. This will output just the ID of the latest guest to be created or stopped:
[holuser@hol10328 ~]$ docker ps --latest --quiet
a1f338b303f7
The docker ps
command lists currently running containers by default. If we run it now, there are no results (because we have no running containers at the moment):
[holuser@hol10328 ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
We can add the --all
parameter to list both running and stopped containers:
[holuser@hol10328 ~]$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b761e9371d83 oraclelinux "/bin/bash" 10 minutes ago Exited (0) 3 minutes ago guest
We can combine the output from the docker ps --latest --quiet
command with the docker commit
command to save the last-running guest as a new image:
[holuser@hol10328 ~]$ docker commit -m "OL7-httpd" `docker ps -l -q` hol10328/httpd:r1
910864a7dca537ca9e44a350bfd6b2cae2fa157d63ae51728d10b37c2d852fb6
Let's break down this command. To save an image, we use docker commit
with a message (-m
) parameter. We're then embedding the output of the docker ps -l -q
command (using the short versions of the --latest
and --quiet
parameters) and finally providing a namespace, image name and tag for the image. We're saving our image into the hol10328
namespace with the image name of httpd
and the tag, r1
. If you want to save an image to the Docker Hub, you will need to use the namespace you created when you signed up.
If we take a look at the images now, we'll see that our image is now available:
[holuser@hol10328 ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
hol10328/httpd r1 910864a7dca5 26 seconds ago 248.6 MB
mysql/mysql-server latest cf18a41d54bb 2 days ago 286 MB
oraclelinux 6 5ef381c513c6 2 weeks ago 156.2 MB
oraclelinux latest a8fd27de55f5 2 weeks ago 189.6 MB
Deploying the Configured Docker Image
We can deploy any number of web servers now using the new Docker image as a template. The following docker run commands run the container image hol10328/httpd
, creating the containers guest1
, guest2
, and guest3
and executing httpd
in each one:
[holuser@hol10328 ~]$ docker run -d --name guest1 -p 8081:80 hol10328/httpd:r1 /usr/sbin/httpd -D FOREGROUND
f3f762543aa3a26f0cfe3aeada980fd2ebe1975e00ceedfa174821633a5891e6
[holuser@hol10328 ~]$ docker run -d --name guest2 -p 8082:80 hol10328/httpd:r1 /usr/sbin/httpd -D FOREGROUND
ca8bebb99f09b8e6c0718f06f7c34c0acfc8ee6cadc17ac16c3423c434c3ecdc
[holuser@hol10328 ~]$ docker run -d --name guest3 -p 8083:80 hol10328/httpd:r1 /usr/sbin/httpd -D FOREGROUND
4d3fe9c35884764b0fa0a0e1a791dfbfb9e1c00bd4d8963d8829dfa620c71f69
The -p
argument maps port 80 in each guest to ports 8080, 8081, or 8082 on the host. Because we didn't use the -i
or -t
parameters, all three containers have started in the background. We can use the docker ps
command to show the running guests:
[holuser@hol10328 ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
87adc4cf1510 hol10328/httpd:r1 "/usr/sbin/httpd -D F" 2 seconds ago Up 1 seconds 0.0.0.0:8083->80/tcp guest3
61fe9a593431 hol10328/httpd:r1 "/usr/sbin/httpd -D F" 6 seconds ago Up 6 seconds 0.0.0.0:8082->80/tcp guest2
8a8bdc289370 hol10328/httpd:r1 "/usr/sbin/httpd -D F" 11 seconds ago Up 11 seconds 0.0.0.0:8081->80/tcp guest1
The default IP address value of 0.0.0.0 means that the port mapping applies to all network interfaces on the host. Using a web browser or curl, we can test the web server running in each guest:
[holuser@hol10328 ~]$ curl http://hol10328:8081
Example Web Server Content
[holuser@hol10328 ~]$ curl http://hol10328:8082
Example Web Server Content
[holuser@hol10328 ~]$ curl http://hol10328:8083
Example Web Server Content
Using a Dockerfile
Now that you've seen how to create and manipulate Docker containers using the command line, the preferred way to build and customize containers is actually using Dockerfiles. A Dockerfile is a small text file that contains the instructions required to construct a container. When a Dockerflle is built, each instruction adds a layer to the container in a step-by-step process. The build creates a container, runs the next instruction in that container, and then commits the container. Docker then runs the committed image as the basis for adding the next layer. The benefit of this layered approach is that Dockerfiles with the same initial instructions reuse layers.
Dockerfiles also create an easily readable and modifiable record of the steps used to create a Docker image. Publishing a Dockerfile to the public Docker Hub or to an internal repository is generally the preferred method of creating and sharing Docker images.
To create a Dockerfile, first create a directory for it:
[holuser@hol10328 ~]$ mkdir ~/dockerfile-httpd
[holuser@hol10328 ~]$ cd ~/dockerfile-httpd/
In that directory, use a text editor to create a file called Dockerfile that contains the following contents:
# Dockerfile for creating a Docker image for OL 7 and httpd and perl
FROM oraclelinux
MAINTAINER E. Jones <ejones@email-address.com>
RUN yum install -y httpd perl && yum clean all
RUN echo "Example Web Server Content" > /var/www/html/index.html
EXPOSE 80
CMD /usr/sbin/httpd -D FOREGROUND
This Dockerfile reflects the same steps as the previous exercise in which we manually built the Docker image for my web server: it configures a new container from a base Oracle Linux 7 image, installs the httpd and perl RPMs, and adds placeholder content for the opening web page.
The docker build
command constructs a new Docker image from this Dockerfile, creating and removing temporary containers as needed during its step-by-step build process:
[holuser@hol10328 ~]$ docker build -t hol10328/httpd:r2 ~/dockerfile-httpd/.
Sending build context to Docker daemon 2.048 kB
Step 0 : FROM oraclelinux
---> a8fd27de55f5
Step 1 : MAINTAINER E. Jones <ejones@email-address.com>
---> Running in 5e47026bd9ac
---> 6c954581d8d7
Removing intermediate container 5e47026bd9ac
Step 2 : RUN yum install -y httpd perl && yum clean all
---> Running in 9e7de7675e9f
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-31.0.1.el7_1.1 will be installed
...
---> Package perl.x86_64 4:5.16.3-285.el7 will be installed
...
--> Running transaction check
...
Installed:
httpd.x86_64 0:2.4.6-31.0.1.el7_1.1 perl.x86_64 4:5.16.3-285.el7
...
Complete!
Cleaning repos: ol7_UEKR3 ol7_latest
Cleaning up everything
---> 216c72f21d9b
Removing intermediate container 9e7de7675e9f
Step 3 : RUN echo "Example Web Server Content" > /var/www/html/index.html
---> Running in 099d2d0420c9
---> a4b3da2df721
Removing intermediate container 099d2d0420c9
Step 4 : EXPOSE 80
---> Running in 4381004b4686
---> c81647c23fb5
Removing intermediate container 4381004b4686
Step 5 : CMD /usr/sbin/httpd -D FOREGROUND
---> Running in bd6df764edef
---> 2e6f94af4d8f
Removing intermediate container bd6df764edef
Successfully built 2e6f94af4d8f
The docker images
command lists the new hol10328/httpd:r2 image created from the Dockerfile:
[holuser@hol10328 ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
hol10328/httpd r2 2e6f94af4d8f 52 seconds ago 248.6 MB
hol10328/httpd r1 910864a7dca5 12 minutes ago 248.6 MB
mysql/mysql-server latest cf18a41d54bb 2 days ago 286 MB
oraclelinux 6 5ef381c513c6 2 weeks ago 156.2 MB
oraclelinux latest a8fd27de55f5 2 weeks ago 189.6 MB
Limiting Runtime Resources
A Docker container uses underlying control group (cgroup) technologies, creating a cgroup for each Docker container. Cgroups provide useful capabilities, such as collecting metrics for CPU, memory, and block I/O usage and providing resource management capabilities. For each Docker container, you can find metrics in the host's corresponding cgroup hierarchy; for example, on an Oracle Linux 7 host running the Unbreakable Enterprise Kernel Release 3 (UEKR3), a memory.stat file in the directory /sys/fs/cgroup/memory/system.slice/docker-<container ID>
lists memory-related metrics for that container. (See the Docker blog article "Gathering LXC and Docker containers metrics.")
The docker run
command provides options that enable runtime limits for memory and relative CPU allocations. These options provide a degree of resource control when executing container images. The -m
or --memory
option limits the amount of physical and swap memory available to processes within a container. For example, the following command places a limit of 256 MB for physical memory (and up to 512 MB for memory plus swap):
[holuser@hol10328 ~]$ docker run -d --memory=256m --name guest10 -p 8090:80 hol10328/httpd:r2
bcbd59911607b8f22e5731b5a174b24513415a9b9e47a2429343524003cb9762
The -c or --cpushares option provides a method of assigning a relative number of CPU shares:
[holuser@hol10328 ~]$ docker run -d --cpu-shares=512 --name guest11 -p 8091:80 hol10328/httpd:r2
b5eaa43c5e8fdf3caafe69b63cdbeaab94cd90ea5c153ec2b794b4484ff8b051
By default, a container gets 1024 CPU shares, so in this example, the guest2 container gets a relative 50 percent share of the available computing resources. Note that the host kernel applies limits only when there is resource contention. For example, if guest2 is assigned 512 shares (50 percent, as shown above) and guest1 is assigned 1024 shares (100 percent), processes in guest2 will still get 100 percent of the available CPU resources if all guest1 processes are idle.
For more information about options to the docker run command, enter docker run --help
or see the Docker run reference guide.
Connecting a Web Server Container to a MySQL Container
Suppose we want to connect my web server container to a container running an instance of MySQL. Docker makes it possible to link containers together, creating a secure channel for one container to access certain information from another.
Previously we pulled the Oracle MySQL image from the Docker Hub using the command docker pull mysql/mysql-server
. To run the MySQL image as a container named db
, we need to create a location to store the MySQL data and start a new container:
[holuser@hol10328 ~]$ cd ~
[holuser@hol10328 ~]$ mkdir mysql-datadir
[holuser@hol10328 ~]$ docker run --name db -d -e MYSQL_ROOT_PASSWORD=Oracle123 -e MYSQL_DATABASE=webdb -e MYSQL_USER=webuser -e MYSQL_PASSWORD=secret -v /home/holuser/mysql-datadir/:/var/lib/mysql mysql/mysql-server
Let's break down the run command: we start a container named db
in the background using the -d
(detached) parameter. We then set a series of environment variables using multiple -e
parameters. The MySQL image allows you to customize the database that is created on startup by using environment variables to configure the container. In this example, we are setting the MySQL root password to Oracle123
and creating a new database called webdb
. We then create a new MySQL user called webuser
with the password of secret
. The container automatically assigns the correct permission to the user for the database it creates.
Using the -v
or --volume
flag is how you can associate database storage (or, in general, any files or folders) that a Docker container needs to change while it's running. Remember that Docker containers are "ephemeral"; that is, they are transient in nature and data does not persist beyond the lifespan of a container's execution. Associating a data volume with a container using the -v
flag allows the container to access, modify, and persist data on the host.
Reminiscent of an NFS mount, the -v
flag causes the directory /home/holuser/mysql-datadir
on the host to be mounted as /var/lib/mysql
(the default database location for MySQL) on the container. From an operational standpoint, the ability to mount a database directory as a container volume in this way also provides an easy way to enable database backups.
The Docker Hub mysql/mysql-server
image includes an entrypoint script (entrypoint.sh
) that sets up the database server automatically, initializing mysqld
. We can view log messages generated from a container using the docker logs
command:
[holuser@hol10328 ~]$ docker logs db
Running mysql_install_db
2015-10-11 22:21:42 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2015-10-11 22:21:42 0 [Note] /usr/sbin/mysqld (mysqld 5.6.27) starting as process 12 ...
...
Finished mysql_install_db
MySQL init process in progress...
...
2015-10-11 22:21:50 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2015-10-11 22:21:50 0 [Note] mysqld (mysqld 5.6.27) starting as process 1 ...
We now have a MySQL container running. Next, we need to create a web server container than contains code to connect to the database and retrieve information.
Let's create a new Dockerfile that configures a web server that includes some Perl scripts:
[holuser@hol10328 ~]$ cd ~
[holuser@hol10328 ~]$ mkdir dockerfile-linked-httpd && cd dockerfile-linked-httpd
Use your favourite text editor to create a Dockerfile with the following content:
FROM oraclelinux:7
RUN yum install -y httpd perl perl-DBI.x86_64 libdbi-dbd-mysql.x86_64 perl-DBD-MySQL.x86_64 && yum clean all
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
EXPOSE 80
ENTRYPOINT /usr/sbin/httpd -D FOREGROUND
This Dockerfile introduces the new command ADD
which allows us to add a file from the host into the container during build. In order to successfully build this container, we need to create the three Perl scripts on the host, in the same directory as the Dockerfile itself.
version.pl:
#!/usr/bin/perl
use DBI;
print "Content-type: text/html\n\n";
my $dbh = DBI->connect(
"dbi:mysql:dbname=webdb:host=db",
"webuser",
"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
#!/usr/bin/perl
use strict;
use DBI;
print "Content-type: text/html\n\n";
my $dbh = DBI->connect(
"dbi:mysql:dbname=webdb:host=db",
"webuser",
"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
#!/usr/bin/perl
use strict;
use DBI;
print "Content-type: text/html\n\n";
my $dbh = DBI->connect(
"dbi:mysql:dbname=webdb;host=db",
"webuser",
"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();
These three scripts will allow us to create a basic MySQL database using initdb.pl
, then query the MySQL server using version.pl
and finally query the MySQL database we created using doquery.pl
.
Before we can run the web server, we need to build a new image using the docker build
command. Using what you learned in the previous exercise, build a new Docker image for hol10328/httpd
with the tag R3
.
Once your new Docker image is created, you can run it:
[holuser@hol10328 dockerfile-linked-httpd]$ docker run -d --name webdb -p 8080:80 --link db:db hol10328/httpd:r3
By linking containers using the --link flag, other application containers can access the MySQL database running in the db container. This simplifies the separation of database, application, and web services, making it easier to isolate services in different containers. Docker automatically configures networking and the /etc/hosts
file in each linked container.
Finally, we can run the Perl scripts in the web server container to create and query the database running in the database container
[holuser@hol10328 dockerfile-linked-httpd]$ curl http://hol10328:8080/cgi-bin/version.pl
Version = 5.6.27
[holuser@hol10328 dockerfile-linked-httpd]$ curl http://hol10328:8080/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
[holuser@hol10328 dockerfile-linked-httpd]$ curl http://hol10328:8080/cgi-bin/doquery.pl?30
Selected 2 row(s) with 3 field(s)
1 Alice 42
5 Eddie 35
[holuser@hol10328 dockerfile-linked-httpd]$ curl http://hol10328:8080/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
The Oracle Linux documentation includes a detailed example of how to link a web container with a MySQL database, and it provides sample scripts that configure httpd.conf, create a database, and perform queries. Additional background on linking containers and using data volumes is available there and also in the Docker user guide.