Dockerfile最佳实践

573 阅读8分钟

Dockerfile最佳实践 本次分享⼤纲如下: Dcokerfile涉及到指令介绍 在Dockerfile中最常⽤的指令有以下⼏个: FROM指令,FROM就是指定基础镜像,因此⼀个 Dockerfile 中 FROM 是必备的指令,并且 必须是第⼀条指令。⽐如说,我要基于ubuntu 14.04的镜像的基础上进⾏构建应⽤,因此第 ⼀⾏我会写 FROM ubuntu:14.04 ADD指令,将本地⽂件添加到镜像中,⽐如我需要添加⼀个python的requirements.txt⽤来 初始化环境,我会写 ADD requirements.txt /home/root COPY指令,和ADD类似,就是拷⻉本地路径的⽂件到镜像中,ADD 指令和 COPY 的格式 和性质基本⼀致。但是在 COPY 基础上增加了⼀些功能。⽐如如果ADD的是⼀个URL路径 ADD会⾃动进⾏下载等,⽬前ADD的这些⾼级功能⽐较隐晦不建议⼤家使⽤。我这⾥再举例 COPY的⽤法,⽐如我想把app.py的⽂件拷⻉到/usr/local/bin下,我会这么写 COPY app.py /usr/local/bin/ RUN指令,执⾏⼀个命令⾏操作,类似Linux的shell,举例,我要在镜像中安装⼀些依赖环 境,想通过yum安装python3,我会这么写,注意不要执⾏交互式命令,注意yum的-y参数 RUN yum install -y python3 CMD指令,这个指令是运⾏容器时启动服务的⼊⼝配置,⽐如,我想基于supervisord启动 容器,我会如下的写法 CMD ["/usr/local/bin/supervisord", "-c", "/path/to/app/supervisord.conf" ] ENTRYPOINT指令,启动容器的进程,经常和CMD配合使⽤,⽐如经常在Linux下执⾏命令 的时候其实默认的是⽤shell的⽗进程在执⾏当前进程,因此这个⼊⼝程序我们可以拿shell在 举例如下 ENTRYPOINT ["/bin/bash"] WORKDIR指令,指定代码的默认⼯作⽬录,也就是你进⼊容器的时候默认的路径,就好⽐ 我们Linux下分配完⽤户后,默认进⼊系统的路径是/home/⽤户名的路径。⽐如,我想我的 ⼯作路径为/data/www,那我可以设置如下 WORKDIR /data/www ENV指令,设定容器运⾏的时候的环境变量,⽐如我想设置⼀个环境变量 ENV_APP_PORT,我启动我的进程的时候读取环境变量,如果存在就以这个环境变量设置 的端⼝启动,我可以这样写 ENV ENV_APP_PORT 8899 LABEL指令,给启动的容器打上⼀些标签,主要⽤来表示这个容器的⼀些特征,⽐如我启动 的这个容器是掌阅⽤户组的,那么我可以打⼀个如下的标签,举例: LABEL group="zhangyue.user" LABEL version="1.0" 以上就是Dockerfile中最常⽤的⼀些指令,在开发过程中基本涉及到的⼤多数都可以⽤以上指令 配合完成⼯作。 善⽤.dockerignore 和 .gitignore ⼀样,如果不想某些⽂件被打包到镜像可以在根⽬录设置 .dockerignore 以下是⼀个内部的 .dockerignore 举例,⼤家看看语法结构

git

.git .gitignore

python

.pyc pycache/ /pycache/ //pycache/ ///pycache/ .py[cod] /.py[cod] //.py[cod] ///*.py[cod]

project

script test tpl readme.txt requirements *.log

Docker

docker-compose.yml .docker Dockerfile

Vim swap files

.swp /.swp //.swp ///.swp

Python mode for VIM

.ropeproject /.ropeproject //.ropeproject ///.ropeproject

PyCharm

.idea

Virtual environment

.env

Installer logs

pip-log.txt pip-delete-this-directory.txt

CI

.codeclimate.yml .travis.yml .dockerignore 的作⽤就是避免⼀些与应⽤⽆关的⽂件加载到镜像中,毕竟构建镜像的时候任 何多余的⽂件都会使得镜像的体积变⼤。因此我们要善于利⽤ .dockerignore ⽂件把不必要的 临时⽂件进⾏屏蔽掉。 善⽤构建缓存 构建docker镜像的时候,⼤家需要去理解镜像有⼀个分层的概念,什么是分层,如何判断我的镜 像有多少层?我告诉⼤家⼀个简单的办法,你在Dockerfile⾥⾯使⽤了多少指令⼤概就是增加了 多少个分层,为什么说是增加了多少个分层呢,是因为FROM指令导⼊的镜像也有⾃⼰的分层, 因此总共的层数就是FROM导⼊镜像的层数加上新增的层数就是这个镜像总的层数。 减少镜像的分层有利于降低镜像构建的⼤⼩,降低镜像的⼤⼩有利于镜像分发和加快启动速度。 因此我们要尽可能减少镜像分层。接下来我就给⼤家分享⼀些⼩技巧⽤来降低镜像的分层。 在docker1.10及更⾼版本RUN,COPY,ADD会创建新的分层,其他指令只创建中间临时镜像 并不会直接影响最终的镜像⼤⼩。 合并多⾏参数⽐如安装软件包不要每个软件包都写⼀个RUN,⽽是合并起来 RUN apt-get update && apt-get install -y
bzr
cvs
git
mercurial
subversion 合并多⾏LABEL LABEL vendor=ACME\ Incorporated
com.example.is-beta=
com.example.is-production=""
com.example.version="0.0.1-beta"
com.example.release-date="2015-02-12" 及时清理垃圾⽂件 RUN apt-get update && apt-get install -y
aufs-tools
automake
build-essential
curl
dpkg-sig
libcap-dev
libsqlite3-dev
mercurial
reprepro
ruby1.9.1
ruby1.9.1-dev
s3cmd=1.1.*
&& rm -rf /var/lib/apt/lists/* 编写Dockerfile的注意事项 善于使⽤管道PIPES避免环境构建⽆效,⽐如执⾏⼀个命令的时候,Docker判断这个命令是 否成功执⾏是基于这个命令的返回值来进⾏,如果返回值是0就认为成功,否则就认为失 败,⽽管道符号很容易把指令的正确返回值给屏蔽掉,导致后续的命令在前⾯命令执⾏失败 的情况下继续执⾏⽽没有发⽣中断。 RUN wget -O - some.site | wc -l > /number 如何来避免这个情况呢?我来给⼤家⼀个推荐的写法,就是增加set -o pipefail,举例如下: RUN set -o pipefail && wget -O - some.site | wc -l > /number 默认的shell执⾏环境是/bin/sh,⽽/bin/bash和/bin/sh是有⼀些区别的,如果想指定shell执 ⾏命令可以按照如下⽅法: RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - some.site | wc -l > /number"] 合理的选择ADD还是COPY ADD和COPY指令很相似,不过COPY指令仅仅把⽂件拷⻉到镜像层⽽ADD指令还有⼀些额 外的功能,⽐如把⽂件解压缩 ADD rootfs.tar.xz / ,执⾏下载 ADD example.com/big.tar.xz /usr/src/things/ ,不过官⽅不建议使⽤ADD的⽅式 来下载⽂件,因为如果⽤curl或者wget的话可以在⼀个层内下载完⽂件并解压后删除不必要 的tar包,⽽ADD指令下载需要有两个分层执⾏这些逻辑。 如果镜像有多个层次,执⾏COPY时可以把COPY分成多次,避免⼀次COPY完成,因为⽂ 件发⽣变更的话会导致cache失效,如果COPY⼀个没有修改的⽂件的话是会有构建缓存 的,如果COPY的是⼀⼤堆⽂件的话,任何⼀个⽂件变更都会导致cache失效 明确指定容器内的⽤户和组权限 为了安全考虑如果不指定⽤户和组权限默认⽤的root账户,因此可能有⼀定⻛险,如果⼀个 容器可以在⾮root⽤户下启动的话建议都指定下⽤户,⽐如: RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgre s 其中--no-log-init参数是为了解决⼀个历史遗留的bug,这个bug发⽣在go 打包tar时会产⽣ ⼀个⽇志到/var/log/faillog,如果没有设置这个参数⽇志⽂件会越来越多,不过 Debian/Ubuntu系统⽬前不⽀持这个指令。 避免使⽤sudo指令,可以考虑⽤gosu指令替代 适配公司的规范 每个公司内部都会有⼀些公司内的约定,⽐如: 如何给Dockerfile打造出来的镜像命名,命名的⽅式,版本号如何约定 是否允许打latest标签的镜像推送到仓库 是否需要删除镜像构建的中间层 镜像中哪些环境变量和标签不能随意使⽤,⽐如,内部服务发现或者docker-compose会基 于容器打⼀些标签是否会冲突等都需要命名上进⾏规范 以上规范每个公司可能都会有差异,我提出⼀些点了⽅便⼤家扩展思路,就不在⼀⼀列举出来所 有场景。 和镜像有关的命令 和docker仓库相关的命令有docker push,docker pull 和外部tar⽂件相关的命令有docker save,docker load 基于Dockerfile构建镜像相关的命令有docker build 构建完镜像后给镜像打tag的命令docker tag 基于运⾏时的容器⽣成镜像的命令docker commit 基于镜像启动容器的命令docker run 每个命令的⽤法都可以通过docker <对应命令> -h 查看其帮助信息,由于命令较多,我不再挨个 介绍其功能,加我微信我以⼀个命令的帮助信息给⼤家展示下,其余的都是类似的, V18250933113

5cc31431345ae38208c6f833691cc88.jpg