如何优雅的编写Dockerfile

在生产环境中一般我们会对基本的环境进行自构建,从而利用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登录去启动业务程序) ###1.首先使用base镜像构建一层sshd的基本镜像**

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 51899/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 51899
ENV LANG=zh_CN.UTF-8;\
    LC_ALL=zh_CN.UTF-8;\
    TZ "Asia/Shanghai";\
    TERM xterm
CMD /usr/sbin/sshd -D
#docker build -t centos6.8-sshd .

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

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的基本环境,环境中本身已经安装各种第三方程序库:

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就会失效


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

###1.首先使用base镜像构建一层的supervisord基本镜像** 由于supervisord是有python写的,所以可以直接在python模块包中使用

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文件配置:

[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

###2.使用上面构建的supervisord镜像进行构建启动服务镜像,包含sshd和rabbitmq**

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 51899/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 51899 5672 4369 

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

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

[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镜像:

FROM supervisord
MAINTAINER xuxuebiao@jd.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 51899/g' /etc/ssh/sshd_config ;\
    echo 'root:redhat' | chpasswd
EXPOSE 51899

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

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

基本redis镜像:

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镜像:

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 

其他案例示范:

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]\$'

$ 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":{"172.25.46.9:5001":{"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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏圣杰的专栏

Hello Docker

Docker: Build, Ship, and Run Any App, Anywhere 在任何地方构建、交付和运行任何应用 1. 引言 最近简单的学...

26190
来自专栏Brian

Docker 学习系列二之基本管理

---- 概述 之前对docker的基本的工作原理进行了简单的介绍和学习Docker工作原理 今天开始不断的学习Docker的容器、镜像和仓库管理。 Docke...

38740
来自专栏康怀帅的专栏

Docker 网络

本文介绍 Docker 网络。 官方文档:https://docs.docker.com/engine/userguide/networking/ 网络类型 b...

40440
来自专栏北京马哥教育

Docker 入门教程

几个月以前,红帽(Red Hat)宣布了在 Docker 技术上和 dotCloud 建立合作关系。在那时候,我并没有时间去学习关于 Docker 的知识,所以...

38670
来自专栏晓晨的专栏

Windows 10 安装 Docker for Windows

52920
来自专栏散尽浮华

Docker容器学习梳理--基础知识(2)

之前已经总结了Docker容器学习梳理--基础知识(1),但是不够详细,下面再完整补充下Docker学习的一些基础。 Docker是个什么东西 Docker是一...

319100
来自专栏云计算教程系列

如何在CentOS 7上安装和使用Docker

Docker是一个应用程序,它使得在容器中运行应用程序进程变得简单易行,就像虚拟机一样,只是更便携,更加资源友好,更依赖于主机操作系统。

82000
来自专栏A周立SpringCloud

Docker系列教程12-使用Maven插件构建Docker镜像

我们知道,Maven是一个强大的项目管理与构建工具。如果可以使用Maven构建Docker镜像,那么我们的工作就能得到进一步的简化。 经过调研,以下几款Mave...

50490
来自专栏微服务

Docker的核心概念,镜像操作

简介 在实际使用Docker的过程中,遇到一些问题,但是总没有系统的博文可以详细的介绍Docker,所以个人写一个由浅入深的系统学习Docker过程。 这里首...

42380
来自专栏Coding01

自建 Laravel 的 Docker 开发环境

好久没写东西,今天说一说怎么自建一个 Laravel 运行的 Docker 环境。

20620

扫码关注云+社区

领取腾讯云代金券