×
Community Blog Application Containerization

Application Containerization

This article introduces containerization and shows how to build container images using Docker and Docker Compose.

By Alex, Alibaba Cloud Community Blog author

The concept of application containerization requires isolating application instances on the same operating system. It is different from virtual machines as applications share the operating system (OS) kernel. Containers run across different operating systems and diverse infrastructure such as bare-metal, virtual private server (VPS), and cloud platforms.

A container has files, an environment, plugins, and libraries that enable an isolated software instance to run. Collectively, these components constitute an image that is independently executable on the kernel. Images are deployed on hosts and since several containers run on the same resource, they are more effective than virtual machines. Docker is one of the most popular containerization tools available today and RKT container engine is equally famous. These two technologies are based on universal runtime (runC) and app container (appc) respectively. Docker Engine is an open-source containerization technology available on most operating systems. Interaction with containerization may subsequently lead you towards Docker. While there are vendor-specific tools available, most organizations choose portable open-source tools with a large user base.

The image below shows the architecture:

1
Source: Nexinto

As a developer, you may want to introduce new code into Docker images and use a reliable, consistent method of creating images. Dockerfile provides a declarative and consistent method of creating Docker images. Further, you may want to containerize multiple heterogeneous containers. This tutorial gives you a walkthrough of the basics of containerization, building Docker images from Dockerfiles and container networking.

Requirements

For this tutorial, we shall require the following:

  • Alibaba Cloud ECS running Ubuntu 16.04
  • Docker Community Edition

Dockerfiles and Docker Images

Dockerfiles are simple text-based files with a list of commands and executing such commands using the Docker command line creates a specific image.

The Docker build command evaluates the specified software versions required for image creation. Kindly note to place a Dockerfile in its own directory instead of the root directory.

Dockerfile Commands

Let's take a quick look at the list of various Dockerfile commands:

  • ADD: copies files from one source to a container's filesystem
  • CMD: executes commands in a specified container
  • WORKDIR: specifies the path for the CMD command
  • FROM: defines the base image for the build
  • ENV: specifies the environment variables
  • USER: represents the user on which containers run
  • ENTRYPOINT: configures a container that runs as an executable
  • RUN: executes a command
  • EXPOSE: creates a port to enable networking of containers via an externally accessible address
  • MAINTAINER: manages the details of the image creator
  • VOLUME: provides containers access to the host storage

Build An Image From A Dockerfile

Now, let's create a Dockerfile and then build a Docker image.

mkdir dockerfiles
cd dockerfiles

For this article, we will use a sample Dockerfile

First, create a file as shown below.

sudo nano mysql

Next, paste the below text in the file and save.

FROM debian:stretch-slim
    

    # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
    RUN groupadd -r mysql && useradd -r -g mysql mysql
    

    RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*
    

    # add gosu for easy step-down from root
    ENV GOSU_VERSION 1.7
    RUN set -x \
        && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
        && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \
        && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \
        && export GNUPGHOME="$(mktemp -d)" \
        && gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
        && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
        && gpgconf --kill all \
        && rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc \
        && chmod +x /usr/local/bin/gosu \
        && gosu nobody true \
        && apt-get purge -y --auto-remove ca-certificates wget
    

    RUN mkdir /docker-entrypoint-initdb.d
    

    RUN apt-get update && apt-get install -y --no-install-recommends \
    # for MYSQL_RANDOM_ROOT_PASSWORD
            pwgen \
    # FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db:
    # File::Basename
    # File::Copy
    # Sys::Hostname
    # Data::Dumper
            perl \
    # mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory
            libaio1 \
    # mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
            libncurses5 \
        && rm -rf /var/lib/apt/lists/*
    

    ENV MYSQL_MAJOR 5.5
    ENV MYSQL_VERSION 5.5.62
    

    RUN apt-get update && apt-get install -y ca-certificates wget --no-install-recommends && rm -rf /var/lib/apt/lists/* \
        && wget "https://cdn.mysql.com/Downloads/MySQL-$MYSQL_MAJOR/mysql-$MYSQL_VERSION-linux-glibc2.12-x86_64.tar.gz" -O mysql.tar.gz \
        && wget "https://cdn.mysql.com/Downloads/MySQL-$MYSQL_MAJOR/mysql-$MYSQL_VERSION-linux-glibc2.12-x86_64.tar.gz.asc" -O mysql.tar.gz.asc \
        && apt-get purge -y --auto-remove ca-certificates wget \
        && export GNUPGHOME="$(mktemp -d)" \
    # gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported
        && gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys A4A9406876FCBD3C456770C88C718D3B5072E1F5 \
        && gpg --batch --verify mysql.tar.gz.asc mysql.tar.gz \
        && gpgconf --kill all \
        && rm -rf "$GNUPGHOME" mysql.tar.gz.asc \
        && mkdir /usr/local/mysql \
        && tar -xzf mysql.tar.gz -C /usr/local/mysql --strip-components=1 \
        && rm mysql.tar.gz \
        && rm -rf /usr/local/mysql/mysql-test /usr/local/mysql/sql-bench \
        && rm -rf /usr/local/mysql/bin/*-debug /usr/local/mysql/bin/*_embedded \
        && find /usr/local/mysql -type f -name "*.a" -delete \
        && apt-get update && apt-get install -y binutils && rm -rf /var/lib/apt/lists/* \
        && { find /usr/local/mysql -type f -executable -exec strip --strip-all '{}' + || true; } \
        && apt-get purge -y --auto-remove binutils
    ENV PATH $PATH:/usr/local/mysql/bin:/usr/local/mysql/scripts
    

    # replicate some of the way the APT package configuration works
    # this is only for 5.5 since it doesn't have an APT repo, and will go away when 5.5 does
    RUN mkdir -p /etc/mysql/conf.d \
        && { \
            echo '[mysqld]'; \
            echo 'skip-host-cache'; \
            echo 'skip-name-resolve'; \
            echo 'datadir = /var/lib/mysql'; \
            echo '!includedir /etc/mysql/conf.d/'; \
        } > /etc/mysql/my.cnf
    

    RUN mkdir -p /var/lib/mysql /var/run/mysqld \
        && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
    # ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime
        && chmod 777 /var/run/mysqld
    

    VOLUME /var/lib/mysql
    

    COPY docker-entrypoint.sh /usr/local/bin/
    RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
    ENTRYPOINT ["docker-entrypoint.sh"]
    

    EXPOSE 3306
    CMD ["mysqld"]

Run the command below to establish the path to the Dockerfile.

pwd

Run the following command inside the directory containing Dockerfile.

docker build -t IMAGE_NAME .

Make sure to rename the image appropriately. Till now, we know how to build a single container. However, it's a very basic concept and doesn't suffice advance use cases. The next section introduces Docker Compose which adds to the advancement of the concept.

Docker Compose

Docker Compose helps to create and run a multiple container application. Follow the steps below to use Docker Compose:

  • Create a Dockerfile defining the environment variables for your app
  • Create a docker-compose.yml file defining your app services variables
  • Execute docker-compose up to run the application

Moving ahead, let's explore the use of Docker Compose files. Here's a sample docker-compose.yml file:

version: "2"
 services:
redis-master: 
image: redis:latest 
ports:
- "6379"
redis-slave: 
image: gcr.io/google_samples/gb-redisslave:v1 
ports: 
- "6379" 
environment: - GET_HOSTS_FROM=dns
frontend: 
image: gcr.io/google-samples/gb-frontend:v3 
ports: 
- "80:80" 
environment: 
- GET_HOSTS_FROM=dns

A Docker Compose file maintains configurations for creating containers to reuse whenever possible. Furthermore, to ensure data integrity while isolating environments, unique build numbers are used, if necessary, to maintain and reassign volumes to the new containers.

Docker Networking

Docker networking configurations enable container isolation for security. By default, Docker installs the bridge, none, and host networks upon installation. A container may use any of the installed networks if it is specified using the --net flag.

You can create more networks if you wish and assign containers to them. Networks also facilitate container communication both within and across them, as long as the container is connected to all the required networks.

Run the command below to create a new network:

sudo docker network create sample_net

Execute the following command to get a list of available networks.

docker network ls

The output shows the following list of networks.

NETWORK ID          NAME                DRIVER              SCOPE
7bb95b5aba4f        bridge              bridge              local
d3e1851dfab4        host                host                local
71ef04fce6c4        none                null                local
cef39ad3d250        sample_net          bridge              local

Run the following command for assigning a container to a network.

docker run container --net=sample_net

Inspect the containers connected to a network using the below command.

docker inspect <network_name>

Now, let's explore three types of networking.

Host Mode Networking

Host mode networking in Docker allows exposure of containers on the public network since they inherit the host IP.

Refer to the file below:

docker run -d --net=host ubuntu:18.04 tail -f /dev/null
ip addr | grep -A 2 eth0:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
    inet **192.168.7.10**/22 brd 192.168.100.10 scope global dynamic eth0

docker ps
CONTAINER ID  IMAGE         COMMAND  CREATED
STATUS        PORTS         NAMES
dj33dd5d9023  ubuntu:18.04  tail -f  2 seconds ago
Up 2 seconds                jovial_blackwell
docker exec -it dj33dd5d9023 ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
    inet **192.168.7.10**/22 brd 192.168.100.10 scope global dynamic eth0

Container Mode Networking

Container networking is a default mode in Docker but it also allows customization of network stacks for containers which is highly suitable for Kubernetes. As mentioned earlier, it is possible to link containers to the same network. Refer to the following code:

docker run -d -P --net=bridge nginx:1.9.1

docker run -it --net=container:admiring_engelbart ubuntu:14.04 ip addr
...
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet **172.17.0.3**/16 scope global eth0

No Networking

Docker no-networking mode allows turning off networking. In this scenario, containers are placed in their own networks but without any configurations, thus disabling them as shown below.

docker run -d -P --net=none nginx:1.9.1
docker ps
CONTAINER ID  IMAGE          COMMAND   CREATED
STATUS        PORTS          NAMES
h7c55d90563f  nginx:1.9.1    nginx -g  2 minutes ago
Up 2 minutes                 grave_perlman
docker inspect h7c55d90563f | grep IPAddress
    "IPAddress": "",
    "SecondaryIPAddresses": null,

Scale Service Instances

In a preceding section, we discussed the Docker Compose configuration. Now, let's take a look at how to scale services defined in the docker-compose.ymlfile.

docker-compose scale <service name> = <no of instances>

For the above compose file, execute any of the following commands.

docker-compose scale redis-master=4

or

docker-compose up --scale redis-master=4 -d

Execute the following commands to assign a range of ports for the services.

services: 
redis-master: 
image: redis:latest 
ports: - "6379-6385:6379"

With this, you will successfully scale the services defined in the docker-compose.yml file.

Conclusion

This tutorial has explored the concepts of containerization with emphasis on the two methods of building container images- Dockerfiles and Docker Compose. Kubernetes is closely aligned with containerization, primarily for microservices and distributed applications. In a microservice architecture, APIs enable the communication of different services while the container layers handle load distribution. Kubernetes has pods that communicate with each other and are used for hosting microservices. Both Dockerfiles and Docker Compose are declarative mechanisms of building Docker applications. The article also throws some light on how to scale services using Docker Compose.

Alibaba Cloud provides a reliable platform to test different containerization technologies.

Don't have an Alibaba Cloud account? Sign up for an account and try over 40 products for free worth up to $1200. Get Started with Alibaba Cloud to learn more.

0 0 0
Share on

Alex

53 posts | 5 followers

You may also like

Comments

Alex

53 posts | 5 followers

Related Products