Dockerfile best practices

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
使用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的重用.
参考

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脚本 : 

#!/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.


[参考]
相关文章
|
缓存 算法 前端开发
掌握了Docker Layer Caching才敢自称精通Dockerfile
长话短说:本次原创将向您展示在Docker中使用Layer Cache以加快镜像构建
掌握了Docker Layer Caching才敢自称精通Dockerfile
how to deploy application from WebIDE to ABAP
Created by Wang, Jerry, last modified on Aug 14, 2015
how to deploy application from WebIDE to ABAP
How applications are staged in SAP Cloud Platform CloudFoundry environment
How applications are staged in SAP Cloud Platform CloudFoundry environment
88 0
How applications are staged in SAP Cloud Platform CloudFoundry environment
|
Ruby 开发工具 关系型数据库
|
Oracle 关系型数据库 MySQL
Website Cloud Architecture Best Practices
Most corporate users can customize their technical architecture according to individual business needs, achieving a Web-scale IT system design.
2862 0
Website Cloud Architecture Best Practices