在生产环境中一般我们会对基本的环境进行自构建,从而利用images的分层特性去层层构建上层的业务镜像。

1.默认情况下我们会首先构建一个基本的base镜像,这个镜像可能包含了linux具体的发行版本,以及基本的软件包,比如wget,vi等。在该层面上,镜像的改动会很少,频次也会很低。

2.其次我们可以在base镜像之上构建新的平台镜像,比如说ssh,java,tomcat等。在基础环境层,相比较上一层来说修改频次稍微会有点大,因为可能涉及到基本软件的版本调整或者参数调整。

3.然后在可以在基本的平台镜像之上构建业务镜像,业务镜像是可以直接启动应用程序的,也就是需要启动服务进程的。该层镜像就是直接和业务代码融合的镜像,随着业务的更新,镜像也会频繁的改动上线。

问题:如果我们构建业务镜像中默认需要启动多个服务,比如需要启动sshd和tomcat或者是一个nginx,那么就不能通过构建镜像的时候去使用CMD命令,因为CMD命令会继承上层images的CMD命令,从而导致上层的CMD命令失效。那么想要既继承上层的sshd,又需要启动业务进程,普通的方式可以采用脚本定义,并在业务镜像层进行RUN脚本。

所以比较好的方法:使用supervisord来管理images中的多个服务进程。可以在基本镜像层进行构建supervisord镜像,然后在上层业务层通过配置supervisord.conf来管理对个进程,实现一个容器中启动多个服务进程。

构建无需启动服务的pass层镜像

该环节是提供给用户基本的软件运行环境,用户可以通过bash登录去启动业务程序。而根据业务的变化特征,以及基础服务的抽象程度,我们可以将该层的image镜像分成以下三个image进行叠加。

构建符合实际业务场景的base镜像

注意:给image中打入sshd服务的主要原因还是想尽可能的满足用户当前的使用习惯,同时又能够充分利用docker image的特性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM centos6.8-base
MAINTAINER xuxuebiao
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key ;\
    ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key ;\
    sed -ri 's/session    required     pam_loginuid.so/#session    required     pam_loginuid.so/g' /etc/pam.d/sshd ;\
    mkdir -p /root/.ssh && chown root.root /root && chmod 700 /root/.ssh ;\
    sed -ri 's/#Port 22/Port 52568/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 52568
ENV LANG=zh_CN.UTF-8;\
    LC_ALL=zh_CN.UTF-8;\
    TZ "Asia/Shanghai";\
    TERM xterm
CMD /usr/sbin/sshd -D
1
#docker build -t centos6.8-sshd .

构建上层基本软件环境

注意:上层SSHD镜像默认使用CMD启动了一个sshd服务,因此这层PAAS层无法直接启动nginx和tomcat,本层只是构建了一个基本环境,容器启动后需要登录bash中执行脚本进行启动。这样交付的环境其实就相当于PAAS层的环境

构建一个基于jdk7tomcat6的基本镜像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
FROM centos6.8-sshd
MAINTAINER "xuxuebiao"
ENV TZ "Asia/Shanghai";\
    PATH $PATH:/export/data/

RUN useradd admin;\
    mkdir -p /export/servers/{jdk1.7.0_71,tomcat6.0.33,nginx};\
    mkdir -p /export/{App,auto_deploy,Config,data,Data,Domains,home,Logs,servers,Shell};
WORKDIR /export/data/
EXPOSE 80
EXPOSE 8080
ADD jdk1.7.0_71  /export/servers/jdk1.7.0_71
ADD tomcat6.0.33 /export/servers/tomcat6.0.33
ADD profile /etc/profile
ADD nginx  /export/servers/nginx
ADD auto-add-tomcat /home/admin/
COPY start_nginx /etc/init.d/nginx
COPY init_nginx.sh /export/Shell/
RUN chmod a+x /etc/init.d/nginx;\
    chmod a+x /export/Shell/init_nginx.sh;\
    chown admin.admin -R /export/ /home/admin/

构建一个基于python27的基本环境,环境中本身已经安装各种第三方程序库:

1
2
3
4
5
6
7
8
9
FROM centos6.8-sshd
MAINTAINER biaoge
ADD python27 /usr/local/python27
RUN ln -s /usr/local/python27/bin/python2.7 /usr/local/bin/python27 ;\
    ln -s  /usr/local/python27/bin/pip /usr/local/bin/pip;\
    ln -s /usr/local/python27/bin/ipython /usr/local/bin/ipython;

EXPOSE 8000
EXPOSE 8081

构建本层的镜像Dockerfile中不能指定新的应用进程,否则基本镜像中的sshd就会失效


构建开箱即用的SaaS层镜像(容器启动之后即可提供相应的服务。比如nginx,sshd等)

首先使用base镜像构建一层的supervisord基本镜像

由于supervisord是有python写的,所以可以直接在python模块包中使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
FROM centos6.8-base
MAINTAINER biaoge
ENV LANG=zh_CN.UTF-8;\
    LC_ALL=zh_CN.UTF-8;\
    TZ="Asia/Shanghai";\
    TERM=xterm
ADD python27 /usr/local/python27
RUN ln -s /usr/local/python27/bin/python2.7 /usr/local/bin/python27 ;\
    ln -s  /usr/local/python27/bin/pip /usr/local/bin/pip;\
    ln -s /usr/local/python27/bin/ipython /usr/local/bin/ipython;\
    ln -s /usr/local/python27/bin/supervisord /usr/local/bin/supervisord;

ADD supervisord.conf /etc/supervisord.conf
EXPOSE 8000 8001 

CMD ["/usr/local/bin/supervisord","-c","/etc/supervisord.conf"]
#这里其实比较建议使用ENTRYPOINT
#ENTRYPOINT通常情况下和CMD会一起使用,区别是CMD定义的执行命令会被container创建的时候的command取代。一般情况下ENTRYPOINT会定义命令执行的主体,CMD中增加默认的参数,而实际的参数可以通过创建container的时候用command进行优化选择
#ENTRYPOINT  ["/usr/local/bin/supervisord","-c","/etc/supervisord.conf"]
#文末会有演示一个ENTRYPOINT的例子

启动之后就会默认启动supervisord.conf中配的服务。

默认的supervisord.conf文件配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[supervisord]
http_port=/var/tmp/supervisor.sock ; (default is to run a UNIX domain socket server)
logfile=/var/log/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=info               ; (logging level;default info; others: debug,warn)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)


[supervisorctl]
serverurl=unix:///var/tmp/supervisor.sock ; use a unix:// URL  for a unix socket

#额外管理的服务
#[program:httpd]
#command = /usr/sbin/httpd
#[program:sshd]
#command = /usr/sbin/sshd -D

使用上面构建的supervisord镜像进行构建服务镜像

镜像可自启动包含sshd和rabbitmq的服务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
FROM supversord
MAINTAINER biaoge

#需要如下相关的包
#esl-erlang_18.1-1~centos~6_amd64.rpm  esl-erlang-compat-18.1-1.noarch.rpm  rabbitmq-server-3.1.5-1.noarch.rpm
COPY *.rpm /usr/local/
RUN yum localinstall /usr/local/*.rpm -y 
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key ;\
    ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key ;\
    sed -ri 's/session    required     pam_loginuid.so/#session    required     pam_loginuid.so/g' /etc/pam.d/sshd ;\
    mkdir -p /root/.ssh && chown root.root /root && chmod 700 /root/.ssh ;\
    sed -ri 's/#Port 22/Port 52568/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 52568 5672 4369 

#这里只需要将更新的配置文件拷贝进去,最终会继承父进程中的CMD去执行supervisord中定义的服务
ADD supervisord.conf /etc/supervisord.conf

用来启动sshd和rabbitmq服务的supervisord.conf配置文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[supervisord]
http_port=/var/tmp/supervisor.sock ; (default is to run a UNIX domain socket server)
logfile=/var/log/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=info               ; (logging level;default info; others: debug,warn)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)


[supervisorctl]
serverurl=unix:///var/tmp/supervisor.sock ; use a unix:// URL  for a unix socket

#额外管理的服务
[program:rabbitmq]
command = rabbitmq-server
[program:sshd]
command = /usr/sbin/sshd -D

测试访问: 使用上面Dockerfile创建images进行构建容器:

$docker run -itd –name test-ssh sshd-rabbitmq

发现创建的容器,已经通过上面的supervisord.conf中定义好的,启动了rabbitmq和sshd服务。

构建基于PaaS层的其他基本镜像

基本sshd镜像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
FROM supervisord
MAINTAINER 371990778@qq.com
#配置相关的ssh需要的文件,以及相关的用户密码
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key ;\
    ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key ;\
    sed -ri 's/session    required     pam_loginuid.so/#session    required     pam_loginuid.so/g' /etc/pam.d/sshd ;\
    mkdir -p /root/.ssh && chown root.root /root && chmod 700 /root/.ssh ;\
    sed -ri 's/#Port 22/Port 52568/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 52568

#这里只需要将更新的配置文件拷贝进去,最终会继承父进程中的CMD去执行supervisord中定义的服务
ADD supervisord.conf /etc/supervisord.conf 

相应的supervisord.conf文件中只需要增加相应的额服务启动命令

基本redis镜像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FROM sshd-base
MAINTAINER biaoge
ADD redis-2.8.9 /usr/local/redis-2.8.9 
#ADD redis.conf /etc/redis.conf
RUN ln -s /usr/local/redis-2.8.9/src/redis-cli /usr/local/bin/redis-cli ;\
    ln -s /usr/local/redis-2.8.9/src/redis-server /usr/local/bin/redis-server;\
    ln -s /usr/local/redis-2.8.9/redisd.sh /usr/local/bin/redisd.sh

EXPOSE 3679 
ADD supervisord.conf /etc/supervisord.conf
#CMD /usr/local/bin/redis-server 

基本rabbit镜像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FROM sshd-base
MAINTAINER biaoge

#需要如下相关的包
#esl-erlang_18.1-1~centos~6_amd64.rpm  esl-erlang-compat-18.1-1.noarch.rpm  rabbitmq-server-3.1.5-1.noarch.rpm
COPY *.rpm /usr/local/
RUN yum localinstall /usr/local/*.rpm -y 
EXPOSE 5672 4369 

#这里只需要将更新的配置文件拷贝进去,最终会继承父进程中的CMD去执行supervisord中定义的服务
ADD supervisord.conf /etc/supervisord.conf 

其他案例示范:

1
2
3
4
5
6
7
8
9
FROM centos:6.8
MAINTAINER "Andy_xu"

ENV TZ "Asia/Shanghai"
#ENV PATH $PATH:/export/data/
ENV TERM xterm

RUN mkdir -p /export/package
COPY * /export/package 

注意:上面dockerfile文件构建的环境没有默认的ps,需要加载export PS1='[\u@\h \W]\$'

ENTRYPOINT 指令详解

 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
$ cat Dockerfile
FROM centos6.8
ENTRYPOINT ["curl","-s","10.0.0.1:2379/info"]
#cmd 可以只定义参数
#CMD ["-v"]
$ docker build -t curl .
$ docker  run curl  (执行ENTRYPOINT定义的内容)
{"ID":"RDTS:INFK:VAZI:5X75:EYAP:DBTC:AQ42:A5WH:IVJX:K4L2:RJ2R:CZS2","Containers":21,"Images":150,"Driver":"overlay","DriverStatus":[["Backing Filesystem","extfs"]],"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":false,"NFd":42,"OomKillDisable":true,"NGoroutines":84,"SystemTime":"2017-05-19T10:51:57.545371359+08:00","ExecutionDriver":"native-0.2","LoggingDriver":"json-file","NEventsListener":0,"KernelVersion":"2.6.32-431.wy39.el6.x86_64","OperatingSystem":"\u003cunknown\u003e","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"xxbandy123":{"Name":"172.25.46.9:5001","Mirrors":[],"Secure":false,"Official":false},"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"InitSha1":"","InitPath":"/usr/bin/docker","NCPU":40,"MemTotal":135282294784,"DockerRootDir":"/export/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"HC-25-28-12.h.chinabank.com.cn","Labels":null,"ExperimentalBuild":false,"ServerVersion":"1.9.1","ClusterStore":"","ClusterAdvertise":""}

$ docker  run curl -v -I (ENTRYPOINT后面可以增加自定义参数,会覆盖掉Dockerfile中掉CMD指令)
* About to connect() to 10.0.0.1 port 5256 (#0)
*   Trying 10.0.0.1... connected
* Connected to 10.0.0.1 (10.0.0.1) port 5256 (#0)
> HEAD /info HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: 10.0.0.1:5256
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< Date: Fri, 19 May 2017 02:53:36 GMT
< Content-Length: 19
<
* Connection #0 to host 10.0.0.1 left intact
* Closing connection #0
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Date: Fri, 19 May 2017 02:53:36 GMT
Content-Length: 19