软件测试测试员的那点事Docker容器

Testops之路3.Dockerfile的最佳实践

2019-04-05  本文已影响3人  KalvinDai

常用命令

类型 命令
基础镜像 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 命令的超集,可以实现一些方便、酷炫的拷贝操作。

ADD <源路径>... <目标路径>
COPY <源路径>... <目标路径>

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

ADD hom* /mydir/
COPY hom?.txt /mydir/

差异

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"] 
上一篇下一篇

猜你喜欢

热点阅读