In the last post we quickly looked at how you can install everything you need to run docker containers on a CentOS 7 host and did bring up the official PostgreSQL image. However there are good reasons not to rely on an existing image: You want to deliver a pre-configured base image which includes everything your application requires and you want to have control over that image. When this image needs to get updated you can update the base image and then re-deploy it to wherever you need to update the base image.
For CentOS there is a script which you can use to build your base image on GitHub. For other distributions you can check the docker documentation. This script basically makes use of the “–installroot” switch of yum which allows to install packages to another location than the default one.
Using this script we can build a CentOS 7 base image. There are three parameters you can use:
1 2 3 4 5 6 | -p "" The list of packages to install in the container. The default is blank. -g "" The groups of packages to install in the container. The default is "Core" . -y The path to the yum config to install packages from. The default is /etc/yum .conf for Centos /RHEL and /etc/dnf/dnf .conf for Fedora |
We’ll use the “-p” switch to install all the packages which are required to build PostgreSQL from source (this depends on the compile options, of course) and some additional packages which are useful for daily work (such as screen):
1 | [docker@centos7 ~]$ sudo . /mkimage-yum .sh -p "gcc openldap-devel python-devel readline-devel redhat-lsb bison flex perl-ExtUtils-Embed zlib-devel crypto-utils openssl-devel pam-devel libxml2-devel libxslt-devel tcl tcl-devel openssh-clients bzip2 net-tools wget screen ksh unzip" centospg |
Once done the new image is available locally:
1 2 3 4 5 | [docker@centos7 ~]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE centospg 7.2.1511 184509483d52 40 seconds ago 510.6 MB postgres latest 78e3985acac0 2 days ago 264.7 MB hello-world latest c54a2cc56cbb 5 months ago 1.848 kB |
If you wonder how that image made it into docker take a look a the last lines of the script:
1 | tar --numeric-owner -c -C "$target" . | docker import - $name:$version |
What happened is that a tar file of the temporary chroot directory was created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [docker@centos7 ~]$ ls -latr /tmp/mkimage-yum .sh.dKHtsq/ total 24 drwxrwxrwt. 2 root root 6 Aug 12 2015 tmp dr-xr-xr-x. 2 root root 6 Aug 12 2015 sys drwxr-xr-x. 2 root root 6 Aug 12 2015 srv drwxr-xr-x. 2 root root 6 Aug 12 2015 run dr-xr-x---. 2 root root 6 Aug 12 2015 root dr-xr-xr-x. 2 root root 6 Aug 12 2015 proc drwxr-xr-x. 2 root root 6 Aug 12 2015 opt drwxr-xr-x. 2 root root 6 Aug 12 2015 mnt drwxr-xr-x. 2 root root 6 Aug 12 2015 media drwxr-xr-x. 2 root root 6 Aug 12 2015 home drwxr-xr-x. 2 root root 4096 Aug 12 2015 dev dr-xr-xr-x. 2 root root 6 Aug 12 2015 boot lrwxrwxrwx. 1 root root 7 Dec 10 15:02 bin -> usr /bin lrwxrwxrwx. 1 root root 8 Dec 10 15:02 sbin -> usr /sbin lrwxrwxrwx. 1 root root 9 Dec 10 15:02 lib64 -> usr /lib64 lrwxrwxrwx. 1 root root 7 Dec 10 15:02 lib -> usr /lib dr-xr-xr-x. 17 root root 4096 Dec 10 15:02 . drwxr-xr-x. 13 root root 4096 Dec 10 15:02 usr drwxr-xr-x. 17 root root 4096 Dec 10 15:02 var drwxr-xr-x. 19 root root 4096 Dec 10 15:02 etc drwxrwxrwt. 11 root root 4096 Dec 12 11:43 .. |
… which then was imported into docker with the “docker import” command.
To test if it really works we can start it and execute bash:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [docker@centos7 ~]$ docker run -it -- rm centospg:7.2.1511 bash [root@cf690e9d9476 /]$ cat /etc/os-release NAME= "CentOS Linux" VERSION= "7 (Core)" ID= "centos" ID_LIKE= "rhel fedora" VERSION_ID= "7" PRETTY_NAME= "CentOS Linux 7 (Core)" ANSI_COLOR= "0;31" CPE_NAME= "cpe:/o:centos:centos:7" HOME_URL= "https://www.centos.org/" BUG_REPORT_URL= "https://bugs.centos.org/" CENTOS_MANTISBT_PROJECT= "CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION= "7" REDHAT_SUPPORT_PRODUCT= "centos" REDHAT_SUPPORT_PRODUCT_VERSION= "7" |
Fine, so far so good. Now we can really begin: We have our own CentOS base image where we want our PostgreSQL image to be based on. How then can we create a PostgreSQL image on top of our base image?
We’ll execute the commands necessary first and provide explanations afterwards. So, the first step:
1 2 3 4 5 6 7 | [docker@centos7 ~]$ sudo yum install -y git [docker@centos7 ~]$ mkdir centospg [docker@centos7 ~]$ cd centospg [docker@centos7 centospg]$ git init Initialized empty Git repository in /home/docker/centospglatest/ .git/ [docker@centos7 centospg]$ git config --global user.email "daniel@abc.def" [docker@centos7 centospg]$ git config --global user.name "Daniel" |
Why do we need git? It is not required to use git at all but you probably would like to have your files which are used to build your containers managed by git so that you can use all advantages of GIT combined with the advantages of docker. It will will make more and more sense as we step through all the commands.
What we need to create now is a so called Dockerfile. This file lists the instructions that Docker will execute to build you image. Lets go:
1 2 3 4 5 6 | [docker@centos7 centospg]$ touch Dockerfile [docker@centos7 centospg]$ git add Dockerfile [docker@centos7 centospg]$ git commit -m "initial" Dockerfile [master (root-commit) ce3727a] initial 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Dockerfile |
Our very basic Dockerfile will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # use our CentOS base images as source FROM centospg:7.2.1511 # set the PostgreSQL we will download ENV PG_VERSION 9.6.1 # include the PostgreSQL binaries in the PATH ENV PATH /u01/app/postgres/product/96/db_01/bin :$PATH # add a postgres group and postgres user RUN groupadd postgres RUN useradd -g postgres -m postgres # prepare the directories RUN mkdir -p /u01/app/postgres RUN chown postgres:postgres /u01/app/postgres # allow sudo for the postgres user RUN echo 'postgres ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers # download, configure, compile and install PostgreSQL from source USER postgres RUN wget https: //ftp .postgresql.org /pub/source/v ${PG_VERSION} /postgresql- ${PG_VERSION}. tar .bz2 -O /var/tmp/postgresql- ${PG_VERSION}. tar .bz2 RUN cd /var/tmp ; tar -axf /var/tmp/postgresql- ${PG_VERSION}. tar .bz2 RUN rm -f /var/tmp/postgresql- ${PG_VERSION}. tar .bz2 RUN cd /var/tmp/postgres *; . /configure --prefix= /u01/app/postgres/product/96/db_01 RUN cd /var/tmp/postgres *; make RUN cd /var/tmp/postgres *; make install # cleanup RUN rm -rf /var/tmp/postgres * |
Using this Dockerfile we can build our PostgreSQL image:
1 | [docker@centos7 centospg]$ docker build -t centospg:PG9.6.1 . |
The output of this is quite long, here a snippet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | Sending build context to Docker daemon 45.06 kB Step 1 : FROM centospg:7.2.1511 ---> 184509483d52 Step 2 : ENV PG_VERSION 9.6.1 ---> Running in 054900c7ebe1 ---> 866815b9f092 Removing intermediate container 054900c7ebe1 Step 3 : ENV PATH /u01/app/postgres/product/96/db_01/bin :$PATH ---> Running in 46bcf7667a06 ---> 94c9adb0402b Removing intermediate container 46bcf7667a06 Step 4 : RUN groupadd postgres ---> Running in 24a7d9b7a1ea ---> eb4ff8268e2e Removing intermediate container 24a7d9b7a1ea Step 5 : RUN useradd -g postgres -m postgres ---> Running in 3e09b556fed8 ---> acff1dcf2d4c Removing intermediate container 3e09b556fed8 Step 6 : RUN mkdir -p /u01/app/postgres ---> Running in 999a62d075c0 ---> fa4bdfa74d31 Removing intermediate container 999a62d075c0 Step 7 : RUN chown postgres:postgres /u01/app/postgres ---> Running in 37773e484260 ---> 668c491b534b Removing intermediate container 37773e484260 Step 8 : RUN echo 'postgres ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ---> Running in bb9cbfd20623 ---> 589959efbda5 Removing intermediate container bb9cbfd20623 Step 9 : USER postgres ---> Running in f70b8c70c3fc ---> 32d3d3d603d2 Removing intermediate container f70b8c70c3fc Step 10 : RUN wget https: //ftp .postgresql.org /pub/source/v ${PG_VERSION} /postgresql- ${PG_VERSION}. tar .bz2 -O /var/tmp/postgresql- ${PG_VERSION}. tar .bz2 ---> Running in c5cc11840a15 --2016-12-12 12:43:05-- https: //ftp .postgresql.org /pub/source/v9 .6.1 /postgresql-9 .6.1. tar .bz2 Resolving ftp .postgresql.org ( ftp .postgresql.org)... 174.143.35.246, 217.196.149.55, 87.238.57.227, ... Connecting to ftp .postgresql.org ( ftp .postgresql.org)|174.143.35.246|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 19260568 (18M) [application /x-bzip ] Saving to: '/var/tmp/postgresql-9.6.1.tar.bz2' 0K .......... .......... .......... .......... .......... 0% 180K 1m44s 50K .......... .......... .......... .......... .......... 0% 368K 77s ... ---> 645cf59717f4 Removing intermediate container c5cc11840a15 Step 11 : RUN cd /var/tmp ; tar -axf /var/tmp/postgresql- ${PG_VERSION}. tar .bz2 ---> Running in 6a47968ddeb5 ... # PostgreSQL configure, make, make install ... PostgreSQL installation complete. ---> 7f6b11b357d7 Removing intermediate container 041441816c4d Step 16 : RUN rm -rf /var/tmp/postgres * ---> Running in 480cc2157b9a ---> a7b0bf1d1c35 Removing intermediate container 480cc2157b9a Successfully built a7b0bf1d1c35 |
Once all of that completed we have a new Docker image:
1 2 3 4 5 6 | [docker@centos7 centospg]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE centospg PG9.6.1 a7b0bf1d1c35 45 seconds ago 706.7 MB centospg 7.2.1511 184509483d52 47 hours ago 510.6 MB postgres latest 78e3985acac0 4 days ago 264.7 MB hello-world latest c54a2cc56cbb 5 months ago 1.848 kB |
Using this image we can now bring up a container which complies to all our standards we build into the base images:
1 2 3 4 5 | [docker@centos7 centospg]$ docker run -it centospg:PG9.6.1 bash [postgres@7ac7780b0b1b /]$ which initdb /u01/app/postgres/product/96/db_01/bin/initdb [postgres@7ac7780b0b1b /]$ initdb --version initdb (PostgreSQL) 9.6.1 |
Finally we commit our Dockerfile:
1 2 3 | [docker@centos7 centospg]$ git commit -m "First working version" Dockerfile [master f0ba897] First working version 1 file changed, 25 insertions(+) |
Something to start with, isn’t it?
Note: The Docker best practices tell you to add only few instructions to a Dockerfile because every instruction creates a new layer. In general you should run only one service per image. This makes it easier to scale you containers.