• Dockerfile最佳实践


    Dockerfile最佳实践:

    通用优化:

    ########################################### 体积与性能 #############################################

    • 1、使用alpine系统,保证最终镜像的体积不会太大
    • 2、使用多阶段构建,使最终的镜像避免包含不必要的文件
    • 3、尽量一个RUN 来跑完所有事情,当然使用多阶段构建可以解决多层影响性能的问题
    • 4、不会发生改变的层放在上边,来使用cache,比如ENV,CMD,EXPOSE
    • 5、使用各种中国镜像加速站来安装依赖

    ########################################### 安全启动 #############################################

    • 1、使用 tini作为PID为1的进程来启动程序,避免停止需要10秒的强制停止和僵尸进程
    • 2、较复杂的应用使用 entrypoint.sh 来初始化配置,同时用 exec来替换进程
    • 3、使用 非root用户启动,保证安全性,不用USER,使用gosu来切换启动用户
    • 4、针对gosu 和 exec的使用,alpine有一个 su-exec

    ########################################### 其它优化 #############################################

    • 1、使用LABEL 来表示作者等信息
    • 2、安装依赖程序的时候使用固定版本
    • 3、时间/时区等设置正确
    • 4、多使用ARG 可以在构建时传入参数:比如镜像版本,某个依赖的版本,时区; ARG 在FROM前后是分开的
    • 5、优雅停止, STOPSIGNAL
    • 6、健康检查,HEALTHCHECK,在k8s里有健康检查可以不写这个

    一、体积与性能

    1.1、使用alpine系统,保证最终镜像的体积不会太大

    需要用当前的最新版本,要使用版本号,不要用latest,任何时候都避免用latest!

    1.2、使用多阶段构建,使最终的镜像避免包含不必要的文件

    FROM xxx AS builder
    FROM builder AS tester
    FROM builder

    1.3、尽量一个RUN 来跑完所有事情,当然使用多阶段构建可以解决多层影响性能的问题

    RUN xxxx &&
        xxxx &&
        xxxx
    

    1.4、不会发生改变的层放在上边,来使用cache,比如ENV,CMD,EXPOSE

    1.5、使用各种中国镜像加速站来安装依赖

    • ubuntu/debain/apt-get
    RUN echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib 
    
    deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib    
    
    deb http://mirrors.aliyun.com/debian-security buster/updates main         
    
    deb-src http://mirrors.aliyun.com/debian-security buster/updates main      
    
    deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib 
    
    deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib 
    
    deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib    
    
    deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib  
    '
    > /etc/apt/sources.list
    
    • alpine/apk
    RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g" /etc/apk/repositories &&
        apk update &&
        apk add --no-cache tini
    

    golang

    ENV GOPROXY https://goproxy.cn
    

    python/pip

    RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
    

    nodejs

    RUN echo 'registry=https://registry.npm.taobao.org/  
    
    sass_binary_site="https://npm.taobao.org/mirrors/node-sass/"  
    
    phantomjs_cdnurl="http://cnpmjs.org/downloads"  
    
    electron_mirror="https://npm.taobao.org/mirrors/electron/"  
    
    sqlite3_binary_host_mirror="https://foxgis.oss-cn-shanghai.aliyuncs.com/"  
    
    profiler_binary_host_mirror="https://npm.taobao.org/mirrors/node-inspector/"  
    
    chromedriver_cdnurl="https://cdn.npm.taobao.org/dist/chromedriver"  
    
    sentrycli_cdnurl=https://npm.taobao.org/mirrors/sentry-cli  
    '
    > ./npmrc
    
    

    二、安全启动

    2.1、使用 tini作为PID为1的进程来启动程序

    2.2、较复杂的应用使用 entrypoint.sh 来初始化配置,同时用 exec来替换进程

    2.3、使用 非root用户启动,保证安全性,不用USER,使用gosu来切换启动用户

    2.4、针对gosu 和 exec的使用,alpine有一个 su-exec

    因为容器中没有init/systemd,所以会导致很多问题,shell启动进程,shell不会将停止信号发给子进程,docker会等待10s,15秒后强制删除。
    参考:tini优化docker停止缓慢10s,进程收不到停止信号
    gosu

    使用 tini来启动进程,作为PID为1的进程。
    tini是专为容器设计的轻量init进程。

    
    RUN groupadd -r someone && useradd -r -g someone someone
    WORKSPACE /app
    COPY . .
    RUN chmod +x entrypoint.sh
    ENTRYPOINT ["/app/entrypoint.sh"]
    CMD ["tini", "--", "top"]
    
    ## entrypoint.sh
    #!/bin/bash
    set -ex
    
    ## 正常启动用 tini启动
    if [ "$1" = 'tini' -a "$(id -u)" = '0' ]; then
        exec gosu someone "$0" "$@";
    fi
    
    exec "$@"
    
    
    #!/bin/bash
    set -ex
    
    ## 正常启动用 tini启动
    if [ "$1" = 'tini' -a "$(id -u)" = '0' ]; then
        /usr/local/bin/python3.8 manage.py compilemessages;
        /usr/local/bin/python3.8 manage.py migrate;
        # 无法修改configmap挂载的配置文件
        # chown -R someone:someone /var/someone;
        exec gosu someone "$0" "$@";
    fi
    
    exec "$@"
    
    
    ENTRYPOINT ["/var/arkid/docker-entrypoint.sh"]
    CMD ["tini", "--", "/usr/local/bin/python3.8", "manage.py", "runserver", "0.0.0.0:80"]
    
    someone        1     0  tini -- /usr/local/bin/python3.8 manage.py runserver 0.0.0.0:80
    someone      199     1  /usr/local/bin/python3.8 manage.py runserver 0.0.0.0:80
    someone      292   199  /usr/local/bin/python3.8 manage.py runserver 0.0.0.0:80
    
    
    
    # exec形式,首选形式, exec 执行命令.
    ENTRYPOINT ["executable", "param1", "param2"]
    # shell形式,sh -c .
    ENTRYPOINT command param1 param2
    
    # exec形式,这是首选形式.
    CMD ["executable","param1","param2"]
    # 提供给ENTRYPOINT的默认参数.
    CMD ["param1","param2"]
    # shell形式, sh -c .
    CMD command param1 param2
    
    
    ## gosu无法绑定0-1023
    0-1023 是系统保留端口,只能root或者sudo启动才能绑定:
    Error: You don't have permission to access that port.
    
    可以配置一下,内核版本也有要求>=2.6.24
    setcap 'cap_net_bind_service=+ep' /usr/local/bin/python3.8
    
    

    三、其它优化

    3.1、使用LABEL

    LABEL maintainer="dev@someproject.org"
    LABEL build_date="2017-09-05"
    

    3.2、安装依赖程序的时候使用固定版本

    3.3、时间/时区等设置正确

    ARG tz="Asia/Shanghai"
    ARG apk="mirrors.ustc.edu.cn"
    
    RUN sed -i "s/dl-cdn.alpinelinux.org/${apk}/g" /etc/apk/repositories &&
        apk update &&
        apk add tini tzdata su-exec &&
        cp /usr/share/zoneinfo/${tz} /etc/localtime &&
        echo ${tz} > /etc/timezone
    

    3.4、多使用ARG 可以在构建时传入参数:比如镜像版本,某个依赖的版本,时区; ARG 在FROM前后是分开的

    3.5、优雅停止, STOPSIGNAL

    3.6、健康检查,HEALTHCHECK,在k8s里有健康检查这个就不重要了

    HEALTHCHECK --interval=2m --timeout=10s --start-period=5s --retries=3 CMD curl -f http://127.0.0.1:8080/ || exit 1
    

    四、其它问题解决

    openjdk(jenkins等)时区问题

    # 在构建Dockerfile时解决
    ENV TZ="Asia/Shanghai"
    RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime &&
           echo ${TZ} > /etc/timezone
    
    # 在启动时挂载
    -v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
    # timezone文件里写 Asia/Shanghai
    -v /xxx/timezone:/etc/timezone  
    
    # pod里使用configmap挂载
    apiVersion: v1
    Kind: ConfigMap
    data:
      timezone: Asia/Shanghai
    metadata:
      name: timezone
    
      volumeMounts:
        - mountPath: /etc/timezone
          name: config-volume
          subPath: timezone
    volumes:
      - name: config-volume
        configMap:
          name: timezone
    

    COPY指令

    不能使用 ./* , 加*无法复制文件夹
    COPY 文件不能用相对路径,只能用绝对路径,源和目的都是。

  • 相关阅读:
    跨域解决方法
    css之line-height
    untiy项目中使用MD5加密
    unity给子物体添加Shader
    unity中UI坐标转3d世界坐标
    unity项目字符串转为Vector3和Quaternion
    unity中使用Highlighting System v4.0插件给物体添加高亮
    加载AssetBundle方法
    Lua面向对象----类、继承、多继承、单例的实现
    Lua学习笔记(一)-----C#和lua的交互
  • 原文地址:https://www.cnblogs.com/leaderjs/p/14557759.html
Copyright © 2020-2023  润新知