Sunday 19 June 2016

Docker Basics & Container Customization - Linux

Objective:
Learn how to customize a Docker container image and use it to instantiate application instances across different Linux servers

Introduction: 
Docker captures full application environment into a virtual container that can be deployed across different Linux servers. System administrators and software developers are learning that Docker can help them deploy application images on Linux quickly, reliably, and consistently without dependency and portability problems. Docker containers can define application and its dependencies using small text file(Dockerfile) that can be moved to different Linux releases and quickly rebuilt.  Also Dockerized application are very easy to migrate to another different linux servers either executed as a bare metal in a virtual machine or Linux instances in the cloud.

I would demonstrate how to create Docker container on RHEL 7, modify and use to deploy multiple application instance.  Docker containers are a lightweight virtualization technology for Linux. They provide isolation from other applications and processes running on the same system but make system calls to the same shared Linux kernel, similar to Linux LXC application containers. Docker containers have their own namespace, so they are fully isolated from one another—processes running in one container can't see or impact processes running in another. By default, each container gets its own networking stack, private network interfaces, and IP address, and Docker creates a virtual bridge so containers can communicate.



Getting Started 
I am usig Docker installation on Redhat 7.2 and installtion document can be found at https://docs.docker.com/v1.8/installation/rhel/

You could have your own Docker hub repository to store images that can be used to build running containers. I would pull few of the images from the Docker hub repository for test environment.

[sunlnx@sandbox ~]$docker pull ubuntu:latest
[sunlnx@sandbox ~]$docker pull oraclelinux:6
[sunlnx@sandbox ~]$docker pull oraclelinux:7
[sunlnx@sandbox ~]$docker pull rhel:latest
[sunlnx@sandbox ~]$docker pull mysql/mysql-server
[sunlnx@sandbox ~]$docker pull nginx:latest

To list all the docker images that were pulled above 
[sunlnx@sandbox ~]$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
nginx                latest              0d409d33b27e        2 weeks ago         182.7 MB
ubuntu               latest              2fa927b5cdd3        3 weeks ago         122 MB
oraclelinux          6                   768a3d7b605a        4 weeks ago         222.8 MB
oraclelinux          7                   df602a268e64        5 weeks ago         276.1 MB
rhel                 latest              bf2034427837        6 weeks ago         203.4 MB
mysql/mysql-server   latest              18a962a188ee        11 days ago         366.9 MB
[sunlnx@sandbox ~]$

Container Customization 
I would like to provide multiple, identical web servers across multiple Linux servers, Docker makes it easy to create a preconfigured in a container image. I would then use this pre built image and deploy it across one or many other Linux hosts. I would install "myweb" container and would configure that to deliver web content to the clients. In order to customize I would get an interactive bash shell to run an rhel "myweb" container. 

[sunlnx@sandbox ~]$ docker run -it --name myweb oraclelinux:6 /bin/bash
[root@5b62adeb3abb /]#

In a shell on my Linux host, the docker ps command shows information about the running guest container, 

[sunlnx@sandbox ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
5b62adeb3abb        oraclelinux:6       "/bin/bash"         6 minutes ago       Up 20 seconds                           myweb
[sunlnx@sandbox ~]$

on myweb, I will install httpd using yum and would configure the web server to display. I will create an index.html in /var/www/html on it. 

[root@5b62adeb3abb /]# yum install -y httpd
[root@5b62adeb3abb /]# echo "Web servers main page" > /var/www/html/index.html
[root@5b62adeb3abb /]# exit

Now, I want to create a new Docker image that reflects the contents of the guest container that I just configured. The following docker commit command captures the modified container into a new image named mywebser/httpd:r1

[sunlnx@sandbox ~]$ docker commit -m "ol6-httpd" `docker ps -l -q` mywebser/httpd:r1
sha256:79cf91b1a67f4ac6419b038e76c4e2de492f0eda978d2b07203e217290454108
[sunlnx@sandbox ~]$

The commit command takes as input the image ID number of the myweb container and assigns and returns an ID number for the new image. Running the docker images command now lists the new image mywebser/httpd 

[sunlnx@sandbox ~]$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED              SIZE
mywebser/httpd       r1                  79cf91b1a67f        About a minute ago   766 MB

Incase if I don't require this container I can remove with docker rm command

[sunlnx@sandbox ~]$docker rm myweb 

Because Docker containers persist even though they're no longer running, removing unneeded containers is simply a housekeeping step to reduce clutter on my host, and it allows me to reuse the name myweb1 for a new container.

Deploy Docker Image:
I 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 mywebser/httpd:r1, creating the containers myweb1, myweb2, myweb3, myweb4 and myweb5 executing httpd in each one:

[sunlnx@sandbox ~]$ docker run -d --name myweb1 -p 8080:80 mywebser/httpd:r1 /usr/sbin/httpd -D FOREGROUND
924018f9f7374b3a0ac24d71b6e7b41407dc1492344ef522a4796162fc0e6822
[sunlnx@sandbox ~]$ docker run -d --name myweb2 -p 8081:80 mywebser/httpd:r1 /usr/sbin/httpd -D FOREGROUND
2fc28962e5ab690edfc4e08c529a4206c3285c823ce924514da07ba0c196593a
[sunlnx@sandbox ~]$ docker run -d --name myweb3 -p 8082:80 mywebser/httpd:r1 /usr/sbin/httpd -D FOREGROUND
48964b1e06b29029781630b9734d734bb163603e13a00c2dd0a59f1e4d94ee23
[sunlnx@sandbox ~]$ docker run -d --name myweb4 -p 8083:80 mywebser/httpd:r1 /usr/sbin/httpd -D FOREGROUND
a5e970efd3f3586f8aa6d5e79b03484625ffcd4f22bac869878949eb6b5aaa48
[sunlnx@sandbox ~]$ docker run -d --name myweb5 -p 8084:80 mywebser/httpd:r1 /usr/sbin/httpd -D FOREGROUND
92bbf522aa41c07838626b03630bae63770c0678d06b7d698f05f203e8ed8b69
[sunlnx@sandbox ~]$

[sunlnx@sandbox ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                  NAMES
92bbf522aa41        mywebser/httpd:r1   "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8084->80/tcp   myweb5
a5e970efd3f3        mywebser/httpd:r1   "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8083->80/tcp   myweb4
48964b1e06b2        mywebser/httpd:r1   "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8082->80/tcp   myweb3
2fc28962e5ab        mywebser/httpd:r1   "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8081->80/tcp   myweb2
924018f9f737        mywebser/httpd:r1   "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp   myweb1
[sunlnx@sandbox ~]$

Using a web browser or curl, I can test the web server running in each guest:

[sunlnx@sandbox ~]$ curl http://sandbox:8080
Web servers main page
[sunlnx@sandbox ~]$ curl http://sandbox:8081
Web servers main page
[sunlnx@sandbox ~]$ curl http://sandbox:8082
Web servers main page
[sunlnx@sandbox ~]$ curl http://sandbox:8083
Web servers main page
[sunlnx@sandbox ~]$ curl http://sandbox:8084
Web servers main page

The Docker Engine also assigns each running container a virtual network interface, which you can see with the docker inspect command:
[sunlnx@sandbox ~]$ docker inspect myweb1
[sunlnx@sandbox ~]$ docker inspect -f '{{ .NetworkSettings.IPAddress }}' myweb1
172.17.0.2
[sunlnx@sandbox ~]$

Saving Docker image:
You could backup the image to a tar using docker command

[sunlnx@sandbox ~]$ docker save -o webserver1.tar mywebser/httpd:r1
[sunlnx@sandbox ~]$

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. You can find the reference from https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/

[sunlnx@sandbox dockercfg]$ cat /home/sunlnx/dockercfg/Dockerfile
FROM centos
MAINTAINER sunlnx <sunlnx@doc.com>
RUN  yum install -y httpd
RUN echo "Web servers main page" > /var/www/html/index.html
EXPOSE 80
CMD /usr/sbin/httpd -D FOREGROUND
[sunlnx@sandbox dockercfg]$

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:

[sunlnx@sandbox dockercfg]$ docker build -t centos/httpd:r1 .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM centos
latest: Pulling from library/centos
a3ed95caeb02: Pull complete
da71393503ec: Pull complete
Digest: sha256:1a62cd7c773dd5c6cf08e2e28596f6fcc99bd97e38c9b324163e0da90ed27562
Status: Downloaded newer image for centos:latest
 ---> 904d6c400333
Step 2 : MAINTAINER sunlnx <sunlnx@doc.com>
 ---> Running in f9303082b870
 ---> fd756b44b2d3
Removing intermediate container f9303082b870
Step 3 : RUN yum install -y httpd
 ---> Running in f0affc8dc005
Loaded plugins: fastestmirror, ovl
.
.
<snip>

Complete!
 ---> d8f46afa67e1
Removing intermediate container f0affc8dc005
Step 4 : RUN echo "Web servers main page" > /var/www/html/index.html
 ---> Running in a732be9c4d06
 ---> f1825360762f
Removing intermediate container a732be9c4d06
Step 5 : EXPOSE 80
 ---> Running in 318e22854e4e
 ---> eeb133e3722a
Removing intermediate container 318e22854e4e
Step 6 : CMD /usr/sbin/httpd -D FOREGROUND
 ---> Running in 1da7959c9c03
 ---> 47416f98d5ad
Removing intermediate container 1da7959c9c03
Successfully built 47416f98d5ad
[sunlnx@sandbox dockercfg]$

[sunlnx@sandbox dockercfg]$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
centos/httpd         r1                  47416f98d5ad        28 minutes ago      311 MB

[sunlnx@sandbox ~]$ docker run -d --name centosweb -p 8085:80 centos/httpd:r1 /usr/sbin/httpd -D FOREGROUND

[sunlnx@sandbox ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                  NAMES
7779813db3df        centos/httpd:r1     "/usr/sbin/httpd -D F"   About a minute ago   Up About a minute   0.0.0.0:8085->80/tcp   centosweb

[sunlnx@sandbox ~]$ curl http://sandbox:8085
Web servers main page
[sunlnx@sandbox ~]$

More information can be found in https://docs.docker.com/ please do visit and enjoy Dockering !!

Thanks for re-sharing !