Dockerfile最佳实践

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享