Dockerfile最佳实践

作者: 9c46ece5b7bd | 来源:发表于2017-05-19 11:05 被阅读80次

    在生产环境中一般我们会对基本的环境进行自构建,从而利用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就会失效


    二、构建开箱即用的SaaS层镜像(容器启动之后即可提供相应的服务。比如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]\$'

    ENTRYPOINT 指令详解

    $ 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
    

    相关文章

      网友评论

        本文标题:Dockerfile最佳实践

        本文链接:https://www.haomeiwen.com/subject/coqyxxtx.html