生产上的 Docker 镜像构建

生产上的 Docker 镜像构建

在生产环境中实际进行镜像构建时,我们需要仔细结合业务考虑,下面从一个通用的角度来看看生产环境上的镜像可以怎样构建。

规划

根据架构来进行镜像分层设计:

  • 应用服务层(Application Level)
  • 运行环境层(Runtime Level)
  • 操作系统层(OS Level)

创建如下目录结构


[root@linux-node1 docker]# tree
.
├── app
│   ├── xxx-admin
│   └── xxx-api
├── runtime
│   ├── java
│   ├── php
│   └── python
└── system
    ├── centos
    ├── centos-ssh
    └── ubuntu

实施构建

构建 centos 基础系统镜像


# 下载阿里的 EPEL repo 文件
[root@linux-node1 centos]# wget http://mirrors.aliyun.com/repo/epel-7.repo

# 编写 centos 基础系统镜像构建 Dockerfile
[root@linux-node1 centos]# vim Dockerfile 
# Dockerfile for CentOS

# Base image
FROM centos

# Maintainer
MAINTAINER Jamin Zhang zhangjamin@163.com

# Config EPEL
ADD epel.repo /etc/yum.repos.d/

# Base pkgs
RUN yum install -y wget mysql-devel supervisor git redis tree net-tools sudo psmisc && yum clean all

# 执行构建
[root@linux-node1 centos]# docker build -t jaminzhang/centos:base .
Sending build context to Docker daemon 4.096 kB
Step 1 : FROM centos
 ---> 980e0e4c79ec
Step 2 : MAINTAINER Jamin Zhang zhangjamin@163.com
 ---> Using cache
 ---> 6a7aa317f098
Step 3 : ADD epel.repo /etc/yum.repos.d/
 ---> 31739d51fe47
Removing intermediate container 499f0617a99d
Step 4 : RUN yum install -y wget mysql-devel supervisor git redis tree net-tools sudo psmisc && yum clean all
 ---> Running in 2d126394fcc2
Loaded plugins: fastestmirror, ovl
...此处内容省略...
Complete!
Loaded plugins: fastestmirror, ovl
Cleaning repos: base epel extras updates
Cleaning up everything
Cleaning up list of fastest mirrors
 ---> b0cfe8ee533b
Removing intermediate container 87d1affab0ed
Successfully built b0cfe8ee533b

# 查看镜像
[root@linux-node1 centos]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
jaminzhang/centos           base                b0cfe8ee533b        47 seconds ago      283.3 MB
...此处内容省略...

构建带 SSH 服务的 centos 基础系统镜像

基于上面的 Dockerfile,只需要 yum 安装上 openssh 相关软件包,还有加上 SSH 服务的配置


[root@linux-node1 centos-ssh]# cat Dockerfile 
# Dockerfile for CentOS

# Base image
FROM centos

# Maintainer
MAINTAINER Jamin Zhang zhangjamin@163.com

# Config EPEL
ADD epel.repo /etc/yum.repos.d/

# Base pkgs
RUN yum install -y openssh-clients openssl-devel openssh-server wget mysql-devel supervisor git redis tree net-tools sudo psmisc && yum clean all

# Config SSH
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN echo "root:jaminzhang" | chpasswd

构建 Python 运行环境镜像


[root@linux-node1 docker]# cd runtime/python/

# 编写 Dockerfile
[root@linux-node1 python]# vim Dockerfile
# Dockerfile for Python Runtime

# Base image
FROM jaminzhang/centos:base

# Maintainer
MAINTAINER Jamin Zhang zhangjamin@163.com

# Python Env
RUN yum install -y python-devel python-pip supervisor

# Upgrade pip
RUN pip install --upgrade pip

# 执行构建
root@linux-node1 python]# docker build -t jaminzhang/centos-python .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM jaminzhang/centos:base
 ---> b0cfe8ee533b
Step 2 : MAINTAINER Jamin Zhang zhangjamin@163.com
 ---> Running in c475c9ea8590
 ---> 959ca1b56c2b
Removing intermediate container c475c9ea8590
Step 3 : RUN yum install -y python-devel python-pip supervisor
 ---> Running in 5489d50d3f75
Loaded plugins: fastestmirror, ovl
...此处内容省略...
Complete!
 ---> 81221403ea32
Removing intermediate container 5489d50d3f75
Step 4 : RUN pip install --upgrade pip
 ---> Running in 4e5fdf04b2c8
You are using pip version 7.1.0, however version 8.1.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
...此处内容省略...
Successfully installed pip-8.1.2
 ---> f2c8f1bade0f
Removing intermediate container 4e5fdf04b2c8
Successfully built f2c8f1bade0f

# 查看镜像
[root@linux-node1 python]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
jaminzhang/centos-python    latest              f2c8f1bade0f        32 seconds ago      453.8 MB
jaminzhang/centos-ssh       base                40af0c56e966        11 minutes ago      284.3 MB
jaminzhang/centos           base                b0cfe8ee533b        20 minutes ago      283.3 MB
...此处内容省略...

构建带 SSH 服务 的 Python 运行环境镜像


# 基于 centos-ssh 镜像构建
[root@linux-node1 runtime]# cd python-ssh
[root@linux-node1 python-ssh]# vim Dockerfile
# Dockerfile for Python Runtime include SSHD

# Base image
FROM jaminzhang/centos-ssh:base

# Maintainer
MAINTAINER Jamin Zhang zhangjamin@163.com

# Python Env
RUN yum install -y python-devel python-pip supervisor

# Upgrade pip
RUN pip install --upgrade pip
# 构建
[root@linux-node1 python-ssh]# docker build -t jaminzhang/python-ssh .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM jaminzhang/centos-ssh:base
 ---> 40af0c56e966
Step 2 : MAINTAINER Jamin Zhang zhangjamin@163.com
 ---> Running in 2abc1d904a53
 ---> 6dfc443c173f
Removing intermediate container 2abc1d904a53
Step 3 : RUN yum install -y python-devel python-pip supervisor
 ---> Running in b7b70a18db21
Loaded plugins: fastestmirror, ovl
...此处内容省略...
Complete!
 ---> f2a9f09ee6c3
Removing intermediate container b7b70a18db21
Step 4 : RUN pip install --upgrade pip
 ---> Running in 262206e28bd5
...此处内容省略...
    Uninstalling pip-7.1.0:
      Successfully uninstalled pip-7.1.0
Successfully installed pip-8.1.2
 ---> 6982a0433dea
Removing intermediate container 262206e28bd5
Successfully built 6982a0433dea

# 查看镜像
[root@linux-node1 ~]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
jaminzhang/python-ssh       latest              6982a0433dea        52 seconds ago      454.8 MB
jaminzhang/centos-python    latest              f2c8f1bade0f        About an hour ago   453.8 MB
jaminzhang/centos-ssh       base                40af0c56e966        About an hour ago   284.3 MB
jaminzhang/centos           base                b0cfe8ee533b        2 hours ago         283.3 MB
...此处内容省略...

构建基于 python-ssh 镜像的业务镜像


# 创建一个 shop-api 演示项目
[root@linux-node1 app]# mkdir shop-api
[root@linux-node1 app]# cd shop-api/

# 编写一个 Python Flask 演示项目
[root@linux-node1 shop-api]# vim app.py
[root@linux-node1 shop-api]# cat app.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello World!' 

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

# 准备 Python requirements 文件
[root@linux-node1 shop-api]# vim requirements.txt
flask

# 准备 supervisor 配置文件,我们使用 supervisor 来管理容器中的服务进程,可以做到自动重启
[root@linux-node1 shop-api]# cp /etc/supervisord.conf .
nodaemon=true	# 容器中开启前台运行
[root@linux-node1 shop-api]# vim app-supervisor.ini 
[program:shop-api]
command=/usr/bin/python2.7 /opt/app.py
process_name=%(program_name)s
autostart=true
user=www
stdout_logfile=/tmp/app.log
stderr_logfile=/tmp/app.error

[program:sshd]
command=/usr/sbin/sshd -D
process_name=%(program_name)s
autostart=true

# 编写 Dockerfile
[root@linux-node1 shop-api]# cat Dockerfile
# Dockerfile for shop-api demo project

# Base image
FROM jaminzhang/python-ssh

# Maintainer
MAINTAINER Jamin Zhang zhangjamin@163.com

# Python Env
RUN useradd -s /sbin/nologin -M www

# Add file
ADD app.py /opt/app.py
ADD requirements.txt /opt/
ADD supervisord.conf /etc/supervisord.conf
ADD app-supervisor.ini /etc/supervisord.d/

# Upgrade pip
RUN /usr/bin/pip2.7 install -r /opt/requirements.txt

# Port
EXPOSE 22 5000

# CMD
CMD ["/usr/bin/supervisord","-c","/etc/supervisord.conf"]

# 项目目录结构
[root@linux-node1 app]# tree shop-api/
shop-api/
├── app.py
├── app-supervisor.ini
├── Dockerfile
├── requirements.txt
└── supervisord.conf

# 构建
[root@linux-node1 shop-api]# docker build -t jaminzhang/shop-api:v1 .
Sending build context to Docker daemon 13.82 kB
Step 1 : FROM jaminzhang/python-ssh
 ---> 6982a0433dea
Step 2 : MAINTAINER Jamin Zhang zhangjamin@163.com
 ---> Running in 11df9ec88355
 ---> e70557634737
Removing intermediate container 11df9ec88355
Step 3 : RUN useradd -s /sbin/nologin -M www
 ---> Running in eddbe1a098ad
 ---> 98a91fcd3fa5
Removing intermediate container eddbe1a098ad
Step 4 : ADD app.py /opt/app.py
 ---> 549f2b6817af
Removing intermediate container bfda4b8598f0
Step 5 : ADD requirements.txt /opt/
 ---> 53737be5a098
Removing intermediate container 819b4bb80d44
Step 6 : ADD supervisord.conf /etc/supervisord.conf
 ---> 6a0b33ab4a16
Removing intermediate container 48b2b1b72a0c
Step 7 : ADD app-supervisor.ini /etc/supervisord.d/
 ---> bc1049994316
Removing intermediate container b416a84e06f5
Step 8 : RUN /usr/bin/pip2.7 install -r /opt/requirements.txt
 ---> Running in 343d55f2e03c
Collecting flask (from -r /opt/requirements.txt (line 1))
...此处内容省略...
Successfully installed Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.11 click-6.6 flask-0.11.1 itsdangerous-0.24
 ---> 2f5ffb73ad7b
Removing intermediate container 343d55f2e03c
Step 9 : EXPOSE 22 5000
 ---> Running in e7bea8407b95
 ---> 3dff08d8b635
Removing intermediate container e7bea8407b95
Step 10 : CMD /usr/bin/supervisord -c /etc/supervisord.conf
 ---> Running in b453d2525db1
 ---> 6bd380c9ba47
Removing intermediate container b453d2525db1
Successfully built 6bd380c9ba47

# 查看镜像
[root@linux-node1 shop-api]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED              SIZE
jaminzhang/shop-api         v1                  6bd380c9ba47        About a minute ago   460 MB
jaminzhang/python-ssh       latest              6982a0433dea        53 minutes ago       454.8 MB
jaminzhang/centos-python    latest              f2c8f1bade0f        2 hours ago          453.8 MB
jaminzhang/centos-ssh       base                40af0c56e966        2 hours ago          284.3 MB
...此处内容省略...

# 运行容器
[root@linux-node1 shop-api]# docker run --name shop-api-v1 -d -p 88:5000 -p 8022:22 jaminzhang/shop-api:v1
110276d8417759781e7a26f99bb15411bdfddbd59db83d5136be594422e16b2c
[root@linux-node1 shop-api]# docker port shop-api-v1
22/tcp -> 0.0.0.0:8022
5000/tcp -> 0.0.0.0:88

# 访问测试
[root@linux-node1 shop-api]# curl "http://192.168.56.11:88/"
Hello World!

Connecting to 192.168.56.11:8022...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.

[root@110276d84177 ~]# w
 12:59:23 up 21:33,  1 user,  load average: 0.06, 0.14, 0.12
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.56.1     12:59    1.00s  0.00s  0.00s w

推荐生产环境上使用 supervisor 来管理容器中的多个服务进程。
PHP/JAVA 环境的项目镜像构建和上面 Python 项目是相同的思路,先在真实主机上安装测试好后再写 Dockerfile。

Ref

Docker Compose