Dockerfile best practices

使用Dockerfile创建image前, 建议遵循的一些规则.

1. container是阅后即焚式的, 即运行一次后, 如果停掉了, 那么重新运行一个(全新的). 

这点和虚拟机有点不一样, 虚拟机有自己的东西, 可以存在虚拟机内部, 如果虚拟机停掉, 再起来, 在虚拟机运行过程中的变更是会被保留的.

但是container不一样, container产生的变更都会"消失", 除非挂载外部的volume并将变更写入外部volume. 

所以设计时应该考虑到container的特性, image只是一个只读的环境而已, container每次启动就是一个全新的环境. 

为了打破这个问题, 例如要将container的数据持久化, 那么必须挂载外部volume来保存, 在container停止或rm后, 还可以启动其他的container来挂载这个volume.

例如数据库的数据文件, 建议外挂volume.

image我们可以只作为一个运行环境来设计, 例如数据库的image就只有数据库软件; WEB服务的image就只有web服务对应的软件; 而数据文件以及WEB站点文件夹都应该外挂的方式来访问. 这样的话, 当container rm后, 数据还在. 再启动container即可.

2. 使用,dockerignore文件排除建立image不需要的文件和文件夹.

或者使用空文件夹来放Dockerfile.

3. 在image中避免安装不必要的包, 例如database image不需要text edit包. 尽量使image小一点.

4. 每个container只允许一个进程, 如果一个服务有多个进程并且需要相互访问的话, 建议起多个container, 并使用container link来将各个进程连接起来. 方便container的重用.

参考

http://blog.163.com/digoal@126/blog/static/163877040201492710339762/

5. 简化Dockerfile层次, 提高可读性.

6. 当指令比较长时, 建议分行

例如

RUN apt-get update && apt-get install -y \

  bzr \

  cvs \

  git \

  mercurial \

  subversion

7. 使用镜像缓存注意, 在指令执行前, 判断是否有镜像缓存可以直接使用

例如 : 

ADD a /

将检查a文件的checksum , 对比以前是否调用过同样的指令, 并且checksum一致.

以上指令将产生一个镜像, 但是在执行前, 可以检查docker server中是否已经存在该image, 那么就直接使用. 而不需要重新生成一个新的中间过程image.

--no-cache=true 表示不使用cache,

8. 指令使用建议 

FROM

建议使用最小化的image.

RUN

有依赖的指令, 最好使用&&写在一条指令中, 例如
RUN apt-get update && apt-get install -y package-bar package-foo package-baz

比较长的指令, 最好分行, 并排序
RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    btrfs-tools \
    build-essential \
    curl \
    dpkg-sig \
    git \
    iptables \
    libapparmor-dev \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    parallel \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.0*

ENV

可用于设置路径, 软件需要的特殊环境变量, 或者便于管理的环境变量等.
使用docker inspect可查.
For example, ENV PATH /usr/local/nginx/bin:$PATH will ensure that CMD [“nginx”] just works.
The ENV instruction is also useful for providing required environment variables specific to services you wish to containerize, such as Postgres’s PGDATA.
Lastly, ENV can also be used to set commonly used version numbers so that version bumps are easier to maintain, as seen in the following example:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

ADD & COPY

一般建议使用COPY, 如果要下载URL到image, 建议使用wget或curl ,然后解压.
For example, you should avoid doing things like:
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

And instead, do something like:
RUN mdkir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.gz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

不需要帮忙自动解压的地方, 建议使用COPY.

ENTRYPOINT

一般建议在末尾加上exec "$@" 那么将传递给ENTRYPOINT脚本.

例如 postgresql的Dockerfile对应的ENTRYPOINT脚本 : 

https://github.com/docker-library/postgres

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

另一个例子 : 

(for example, docker run -it mysql mysqld --some --flags will transparently run mysqld --some --flags after ENTRYPOINT runs initdb).

mysql的ENTRYPOINT脚本 : 

#!/bin/bash
set -e

if [ -z "$(ls -A /var/lib/mysql)" -a "${1%_safe}" = 'mysqld' ]; then
	if [ -z "$MYSQL_ROOT_PASSWORD" ]; then
		echo >&2 'error: database is uninitialized and MYSQL_ROOT_PASSWORD not set'
		echo >&2 '  Did you forget to add -e MYSQL_ROOT_PASSWORD=... ?'
		exit 1
	fi

	mysql_install_db --user=mysql --datadir=/var/lib/mysql

	# These statements _must_ be on individual lines, and _must_ end with
	# semicolons (no line breaks or comments are permitted).
	# TODO proper SQL escaping on ALL the things D:
	TEMP_FILE='/tmp/mysql-first-time.sql'
	cat > "$TEMP_FILE" <<-EOSQL
		DELETE FROM mysql.user ;
		CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
		GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ;
		DROP DATABASE IF EXISTS test ;
	EOSQL

	if [ "$MYSQL_DATABASE" ]; then
		echo "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE ;" >> "$TEMP_FILE"
	fi

	if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
		echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" >> "$TEMP_FILE"

		if [ "$MYSQL_DATABASE" ]; then
			echo "GRANT ALL ON $MYSQL_DATABASE.* TO '$MYSQL_USER'@'%' ;" >> "$TEMP_FILE"
		fi
	fi

	echo 'FLUSH PRIVILEGES ;' >> "$TEMP_FILE"

	set -- "$@" --init-file="$TEMP_FILE"
fi

chown -R mysql:mysql /var/lib/mysql
exec "$@"

使用例子 : 

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]

VOLUME

The VOLUME instruction should be used to expose any database storage area, configuration storage, or files/folders created by your docker container. You are strongly encouraged to use VOLUME for any mutable and/or user-serviceable parts of your image.

USER

如果服务不需要使用root权限, 那么建议使用非root用户执行,

避免使用sudo(可能引起问题).

不要频繁的变更USER, 会带来更多的层级. 

WORKDIR

改变工作目录可以使用WORKDIR, 或者在同一条指令中执行, 例如 : 

RUN cd … && do-something

但是为了提高可读性, 建议使用WORKDIR指令.

ONBUILD

ONBUILD一般用于后续会有其他image要基于该image来创建, 并需要依赖一些环境的情况(那么这些依赖的环境可以写在ONBUILD中安装执行)

ONBUILD is only useful for images that are going to be built FROM a given image. For example, you would use ONBUILD for a language stack image that builds arbitrary user software written in that language within the Dockerfile, as you can see in Ruby’s ONBUILD variants.

Images built from ONBUILD should get a separate tag, for example: ruby:1.9-onbuild or ruby:2.0-onbuild.

Be careful when putting ADD or COPY in ONBUILD. The “onbuild” image will fail catastrophically if the new build's context is missing the resource being added. Adding a separate tag, as recommended above, will help mitigate this by allowing the Dockerfile author to make a choice.

不建议在ONBUILD中使用ADD COPY.

[参考]

1. https://docs.docker.com/articles/dockerfile_best-practices/

2. https://github.com/docker-library/postgres

时间: 2024-09-11 06:50:16

Dockerfile best practices的相关文章

Dockerfile RUN, CMD &amp; ENTRYPOINT

在使用Dockerfile创建image时, 有几条指令比较容易混淆, RUN, CMD, ENTRYPOINT. RUN是在building image时会运行的指令, 在Dockerfile中可以写多条RUN指令. CMD和ENTRYPOINT则是在运行container 时会运行的指令, 都只能写一条, 如果写了多条, 则最后一条生效. CMD和ENTRYPOINT的区别是:  CMD在运行时会被command覆盖, ENTRYPOINT不会被运行时的command覆盖, 但是也可以指定.

Dockerfile最佳实践(一)

本文讲的是Dockerfile最佳实践(一),[编者的话]本文是Docker入门教程第三章-DockerFile的进阶篇,作者主要介绍了缓存.标签.端口以及CMD与ENTRYPOINT的最佳用法,并通过案例分析了注意事项,比如我们应该使用常用且不变的Dockerfile开头.通过-t标记来构建镜像.勿在Dockerfile映射公有端口等等. Dockerfile使用简单的语法来构建镜像.下面是一些建议和技巧以帮助你使用Dockerfile. 1.使用缓存 Dockerfile的每条指令都会将结果

Dockerfile最佳实践(二)

本文讲的是Dockerfile最佳实践(二),[编者的话]本文是 Docker 入门教程第三章-DockerFile 进阶篇的第二部分.作者主要介绍了 Docker 的变化.常用指令以及基础镜像的最佳用法. 自从我上一篇 Dockerfile 最佳实践后,Docker 发生了很大变化.上一篇会继续留着,这篇文章将介绍 Docker 有什么变化以及你现在应当做些什么. 1.不要开机初始化 容器模型是进程而不是机器.如果你认为你需要开机初始化,那么你就错了. 2.可信任构建 即使你不喜欢这个题目但它

通过 Docker 化一个博客网站来开启我们的 Docker 之旅

这篇文章包含 Docker 的基本概念,以及如何通过创建一个定制的 Dockerfile 来 Docker 化Dockerize一个应用. Docker 是一个过去两年来从某个 idea 中孕育而生的有趣技术,公司组织们用它在世界上每个角落来部署应用.在今天的文章中,我将讲述如何通过"Docker 化Dockerize"一个现有的应用,来开始我们的 Docker 之旅.这里提到的应用指的就是这个博客! 什么是 Docker? 当我们开始学习 Docker 基本概念时,让我们先去搞清楚什

Microsoft模式和实践:模式篇(Microsoft Patterns &amp; Practices:

    Microsoft模式和实践:模式篇(Microsoft Patterns & Practices:Patterns)[强烈推荐]     这个多达365页的文档,详细而全面的讲解了.NET中的有关模式和软件架构设计的方方面面的知识,可以说在我拿到这份文档的时候只有一个感觉:"欣喜若狂"!     在微软各个架构大师的仔细讲解中,相信你一定可以很快的了解.NET有关的模式设计和架构体系设计方面的深入知识,而这些才是最宝贵的,远比各类编码技巧要重要的多!     最后,你

.NET DateTime coding best practices

.NET DateTime coding best practicesAuthor:  Dan Rogers (danro), Microsoft Corporation January 9, 2004 SynopsisWriting programs that store, perform calculations and serialize time values using the .NET DateTime type requires an awareness of the differ

[转] Leaving patterns &amp; practices

[J.D. Meier's Blog]"Life is like skiing.  Just like skiing, the goal is not to get to the bottom of the hill. It's to have a bunch of good runs before the sun sets." – Seth Godin It's been a good run.  After more than 10 years in patterns &

Dockerfile实践优化建议

本文讲的是Dockerfile实践优化建议[编者的话]Dockerfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfile指令翻译真正的Linux命令.类似于Makefile,Dockerfile有自己书写格式和支持的命令,Docker程序解决这些命令间的依赖关系.下面是resin.io关于Dockerfile编写经验和建议的总结. 上个月,Docker发起了Docker Global

Website Cloud Architecture Best Practices

This article introduces the technical challenges brought by cloud technology to traditional enterprises, and explains in depth the best practices of cloud architecture. It was first published in Lingyun, a magazine jointly sponsored by Alibaba Cloud