美文网首页软件测试测试员的那点事Docker容器
Testops之路3.Dockerfile的最佳实践

Testops之路3.Dockerfile的最佳实践

作者: KalvinDai | 来源:发表于2019-04-05 16:39 被阅读3次

    常用命令

    类型 命令
    基础镜像 FROM
    元数据 LABEL
    镜像操作指令 RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
    容器启动时执行指令 CMD、ENTRYPOINT

    FROM

    image

    用来指定基础镜像,然后通过在基础镜像上构建新的镜像,基础镜像一般有远程或本地仓库。并且Dockerfile文件第一行必须的FROM指令,如果一个Dockerfile需要创建多个镜像,可以使用多个FROM指令。

    尽量使用官方的baseimage

    LABEL

    image

    可以理解为代码中的注释

    RUN

    运行所有基础镜像能支持的命令,同样也可以使用多条RUN指令,可以使用\来换行

    image

    反斜线换行
    安装完成后清理缓存

    WORKDIR

    指定RUN、CMD、ENTRYPIONT指定的命令的运行目录。可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前的命令指定的路径。

    WORKDIR /path/to/workdir
    
    image

    ADD&COPY

    相同

    COPY 命令是为最基本的用法设计的,概念清晰,操作简单。而 ADD 命令基本上是 COPY 命令的超集,可以实现一些方便、酷炫的拷贝操作。

    • 对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。
    ADD <源路径>... <目标路径>
    COPY <源路径>... <目标路径>
    

    <源路径>可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

    ADD hom* /mydir/
    COPY hom?.txt /mydir/
    
    • 复制指定的源文件、目录、URL到容器的指定目录中。所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0。


      image

    差异

    • 如果源文件是可识别的压缩格式,ADD 命令会帮忙解压缩;
    • COPY 命令区别于 ADD 命令的一个用法是在 multistage 场景下。在 multistage 的用法中,可以使用 COPY 命令把前一阶段构建的产物拷贝到另一个镜像中。
    • 从 url 拷贝文件到镜像中,但官方不建议这么做。建议使用wget或curl。
    image

    ENV

    这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。

    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...
    
    image

    USER

    用于指定运行镜像所使用的用户
    使用USER指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。

    USER user
    USER user:group
    USER uid
    USER uid:gid
    USER user:gid
    USER uid:group
    

    RUN、CMD、ENTRYPOINT区别

    RUN 在构建的时候执行,并生成一个新的镜像,CMD和ENTRYPOINT 是在容器运行的时候执行,在构建时不进行任何操作。

    一个Dockerfile可以有多个RUN,但只允许有一个 CMD或ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的 CMD或ENTRYPOINT 指令。

    ENTRYPOINT 与 CMD 非常类似,不同的是通过 docker run执行的命令不会覆盖 ENTRYPOINT,而 docker run命令中指定的任何参数,都会被当做参数再次传递给 ENTRYPOINT。

    image image image image image

    EXPOSE

    为构建的镜像设置监听端口,使容器在运行时监听。

    EXPOSE <port> [<port>...]
    

    假设有一个python的小程序,app.py文件

    from flask import Flask
    app = Flask(__name__)
    @app.route('/')
    def hello():
        return "hello docker"
    if __name__ == '__main__':
        app.run()
    
    python app.py
     * Serving Flask app "app" (lazy loading)
     * Environment: production
       WARNING: Do not use the development server in a production environment.
       Use a production WSGI server instead.
     * Debug mode: off
     * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    

    访问http://127.0.0.1:5000/,可以看到服务成功启动,hello docker。

    FROM python
    RUN pip install flask
    WORKDIR /app
    COPY app.py .
    
    EXPOSE 5000
    CMD ["python", "app.py"]
    
    两种方式的区别是:
        -P : 是容器内部端口随机映射到主机的高端口。
        -p : 是容器内部端口绑定到指定的主机端口。
    
    docker build -t flask-demo .
    docker run -p 5000:5000 -d b0720c4f347c
    

    ONBUILD

    ONBUILD用于配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令。

    ONBUILD [INSTRUCTION]
    
    FROM node:0.12.6
    RUN mkdir -p /usr/src/app
    WORKDIR /usr/src/app
    ONBUILD COPY package.json /usr/src/app/
    ONBUILD RUN npm install
    

    基于上面构建一个名为node-onbuild父镜像

    FROM node-onbuild
    COPY . /usr/src/app
    CMD [ "npm", "start" ]
    

    会发现父镜像dockerfile的内容中ONBUILD部分

    ONBUILD COPY package.json /usr/src/app/
    ONBUILD RUN npm install
    

    是在构建子镜像的时候才执行的

    最佳实践

    减少层数

    一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。

    FROM centos:7
    MAINTAINER www.ctnrs.com
    RUN yum install epel-release -y 
    RUN yum install -y gcc gcc-c++ make -y
    RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz
    RUN tar zxf php-5.6.36.tar.gz
    RUN cd php-5.6.36
    RUN ./configure --prefix=/usr/local/php 
    RUN make -j 4 
    RUN make install
    EXPOSE 9000
    CMD ["php-fpm"]
    

    改成

    FROM centos:7
    MAINTAINER www.ctnrs.com
    RUN yum install epel-release -y && \
        yum install -y gcc gcc-c++ make
    
    RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
        tar zxf php-5.6.36.tar.gz && \
        cd php-5.6.36 && \
        ./configure --prefix=/usr/local/php && \
        make -j 4 && make install
    EXPOSE 9000
    CMD ["php-fpm"]
    

    清理无用数据

    一次RUN形成新的一层,如果没有在同一层删除,无论文件是否最后删除,都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。

    FROM centos:7
    MAINTAINER www.ctnrs.com
    RUN yum install epel-release -y && \
        yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
        libcurl-devel libjpeg-devel libpng-devel openssl-devel \
        libmcrypt-devel libxslt-devel libtidy-devel autoconf \
        iproute net-tools telnet wget curl && \
        yum clean all && \
        rm -rf /var/cache/yum/*
    
    RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
        tar zxf php-5.6.36.tar.gz && \
        cd php-5.6.36 && \
        ./configure --prefix=/usr/local/php \
        make -j 4 && make install && \
        cd / && rm -rf php*
    

    减少网络传输时间

    最好在内部有一个存放软件包的地方,最好的方法就是建立php-composer、maven、npm等私有仓库,减少网络传输时间,提高镜像构建速度。

    使用多阶段构建

    使用多阶段构建,您可以在Dockerfile中使用多个FROM语句。每条FROM指令都可以使用不同的基础,并且每条指令都开始构建的新阶段。您可以选择性地将工件从一个阶段复制到另一个阶段,在最终图像中留下不需要的所有内容。

    Java例子

    FROM maven AS build
    ADD ./pom.xml pom.xml
    ADD ./src src/
    RUN mvn clean package
    
    FROM lizhenliang/tomcat
    RUN rm -rf /usr/local/tomcat/webapps/ROOT
    COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war
    

    Golang例子

    FROM golang:1.7.3
    WORKDIR /go/src/github.com/alexellis/href-counter/
    RUN go get -d -v golang.org/x/net/html  
    COPY app.go .
    RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
    
    FROM alpine:latest  
    RUN apk --no-cache add ca-certificates
    WORKDIR /root/
    COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
    CMD ["./app"] 
    

    相关文章

      网友评论

        本文标题:Testops之路3.Dockerfile的最佳实践

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