文章目录
  1. 1. dockerfile指令
    1. 1.1. FROM
    2. 1.2. MAINTAINER
    3. 1.3. RUN
    4. 1.4. CMD
    5. 1.5. EXPOSE
    6. 1.6. ENV
    7. 1.7. ADD
    8. 1.8. COPY
    9. 1.9. ENTRYPOINT
    10. 1.10. VOLUME
    11. 1.11. USER
    12. 1.12. WORKDIR
    13. 1.13. ONBUILD
  2. 2. 构建镜像实例
    1. 2.1. 代码和文件准备
    2. 2.2. 构建和测试
    3. 2.3. 改进

通常情况下,我们使用dockerfile来创建镜像,主要是因为dockerfile可以实现脚本化,反复使用。

dockerfile通常是由一系列指令组成,一个常见的dockerfile的例子主要有以下一些指令1

dockerfile指令

FROM

格式为 FROM <image>FROM <image>:<tag>

第一条指令必须为 FROM 指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

MAINTAINER

格式为 MAINTAINER <name>,指定维护者信息。

RUN

格式为 RUN <command>RUN ["executable", "param1", "param2"]

前者将在 shell 终端中运行命令,即 /bin/sh -c;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。

CMD

支持三种格式 * CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式; * CMD command param1 param2/bin/sh 中执行,提供给需要交互的应用; * CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

EXPOSE

格式为 EXPOSE <port> [<port>...]

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。

ENV

格式为 ENV <key> <value>。 指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

例如

1
2
3
4
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

格式为 ADD <src> <dest>

该命令将复制指定的 <src> 到容器中的 <dest>。 其中 <src> 可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。

COPY

格式为 COPY <src> <dest>

复制本地主机的 <src>(为 Dockerfile 所在目录的相对路径)到容器中的 <dest>

当使用本地目录为源目录时,推荐使用 COPY

ENTRYPOINT

两种格式: * ENTRYPOINT ["executable", "param1", "param2"] * ENTRYPOINT command param1 param2(shell中执行)。

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。

VOLUME

格式为 VOLUME ["/data"]

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

USER

格式为 USER daemon

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。

当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用 gosu,而不推荐 sudo

WORKDIR

格式为 WORKDIR /path/to/workdir

为后续的 RUNCMDENTRYPOINT 指令配置工作目录。

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

1
2
3
4
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c

ONBUILD

格式为 ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

例如,Dockerfile 使用如下的内容创建了镜像 image-A

1
2
3
4
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

1
2
3
4
5
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild

构建镜像实例

本文使用centos 7为基础镜像,构建一个包括jdk8、tomcat8、php、mysql、mycat的镜像。

代码和文件准备

镜像的dockerfile如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
FROM centos:7
MAINTAINER arnes <arsen_mmx@163.com>
ENV REFRESHED_AT 2015-12-26
# add fast yum repos
ADD ./CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
ADD ./mysql-community.repo /etc/yum.repos.d/mysql-community.repo
# install jdk 1.8
COPY ./jdk1.8.0_66 /usr/java/jdk1.8.0_66
ENV JAVA_HOME /usr/java/jdk1.8.0_66
ENV JRE_HOME $JAVA_HOME/jre
ENV PATH $JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
ENV CLASSPATH=.:$JAVA_HOME/lib:/usr/java/jdk1.6.0_25/jre/lib:$CLASSPATH
#install tomcat
COPY ./apache-tomcat-8.0.28 /usr/tomcat/apache-tomcat-8.0.28
ENV TOMCAT_HOME /usr/tomcat/apache-tomcat-8.0.28
ENV CATALINA_HOME $TOMCAT_HOME
ENV CATALINA_BASE $TOMCAT_HOME
# install php
RUN yum update
RUN yum install -y openssh-server php
# install mysql
RUN groupadd -r mysql && useradd -r -g mysql mysql
#ADD ./mysql-community-release-el7-7.noarch.rpm /usr/mysql-community-release-el7-7.noarch.rpm
#ADD ./mysql-community-server-minimal-5.6.28-2.el7.x86_64.rpm /usr/mysql-community-server-minimal-5.6.28-2.el7.x86_64.rpm
#RUN rpm -ivh /usr/mysql-community-release-el7-7.noarch.rpm
RUN rpmkeys --import http://repo.mysql.com/RPM-GPG-KEY-mysql
RUN yum install -y https://repo.mysql.com/yum/mysql-5.6-community/docker/x86_64/mysql-community-server-minimal-5.6.28-2.el7.x86_64.rpm
#RUN yum install -y mysql-community-server
RUN yum install -y libpwquality \
&& rm -rf /var/cache/yum/*
# config mysql
ADD ./my.cnf /etc/my.cnf
RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/my.cnf \
&& echo -e "skip-host-cache\nskip-name-resolve\nlower_case_table_names=1" | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/my.cnf > /tmp/my.cnf \
&& mv /tmp/my.cnf /etc/my.cnf
VOLUME /var/lib/mysql
ENV MYSQL_ROOT_PASSWORD "111111"
ENV MYSQL_USER "mysql"
ENV MYSQL_USER_DATABASE "mysql"
ENV MYSQL_USER_PASSWORD "111111"
COPY ./mysql-entrypoint.sh /usr/bin/mysql-entrypoint.sh
# install mycat
RUN groupadd -r mycat && useradd -r -g mycat mycat
COPY ./mycat /opt/mycat
ADD ./schema.xml /opt/mycat/conf/schema.xml
COPY ./mycat-run.sh /usr/bin/mycat-run.sh
ENV MYCAT_HOME /opt/mycat
RUN chown -R mycat:mycat /opt/mycat
# export port for tomcat, mysql, mycat
EXPOSE 8080 8066 9066 3306
CMD $TOMCAT_HOME/bin/startup.sh \
&& bash -c "/usr/bin/mysql-entrypoint.sh mysqld &" \
&& echo "Waiting mysql startup." && sleep 30 \
&& bash -c "/usr/bin/mycat-run.sh" \
&& /usr/sbin/sshd -D
#CMD /usr/bin/mysql-entrypoint.sh && /usr/sbin/sshd -D

以上安装过程,主要就是按照jdk8->tomcat8->php->mysql->mycat的步骤来逐步安装的。需要用到的文件如下,

其中,Dockerfile文件上文已经列出,jdk-8u66-linux-x64.tar.gzapache-tomcat-8.0.28.tar.gzMycat-server-1.5-alpha-20151221110028-linux.tar.gz分别是jdk8、tomcat8、mycat1.5的安装包文件,解压后分别是jdk1.8.0_66apache-tomcat-8.0.28mycatCentOS-Base.repo是Centos7的更新源配置文件,用来替代默认的更新源,以加快速度,我用的是上海交大的源,文件内容如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client. You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#
[base]
name=CentOS-$releasever - Base
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#released updates
[updates]
name=CentOS-$releasever - Updates
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

mysql-community.repo是mysql的centos更新源,因为centos的默认mysql实现已经是mysql的mariadb分支,如果要用Oracle的mysql,现在在centos中改名为mysql-community,需要手动配置更新源,文件内容如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[mysql-connectors-community]
name=MySQL Connectors Community
baseurl=http://repo.mysql.com/yum/mysql-connectors-community/el/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
[mysql-tools-community]
name=MySQL Tools Community
baseurl=http://repo.mysql.com/yum/mysql-tools-community/el/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
# Enable to use MySQL 5.5
[mysql55-community]
name=MySQL 5.5 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.5-community/el/7/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
# Enable to use MySQL 5.6
[mysql56-community]
name=MySQL 5.6 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.6-community/el/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
[mysql57-community]
name=MySQL 5.7 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

mysql-community.repo的获取主要参考Oracle官方的Yum源配置方法,运行如下命令,

1
2
wget http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm
rpm -Uvh mysql57-community-release-el7-7.noarch.rpm

运行完后,发现在/etc/yum.repos.d/目录下多了msql-community的更新源配置2,我只取其中的mysql-community.repo以精简Yum更新源。

my.cnf是mysql的配置文件,这个文件主要配置了mysql的字符集UTF8,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[client]
socket=/var/run/mysqld/mysql.sock
default-character-set=utf8
[mysqld]
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
skip-character-set-client-handshake=1
character-set-server=utf8
collation-server=utf8_unicode_ci
#log-queries-not-using-indexes=1
#slow-query-log=1
#slow-query-log-file=/var/lib/mysql/mysql-slow.log
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

mysql-entrypoint.sh是mysql的docker启动脚本,用法是mysql-entrypoint.sh mysqld,该文件以及dockerfile中的mysql安装步骤都参考了mysql5.6官方Docker镜像3中的构建方法,文件内如如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/bin/bash
set -e
# if command starts with an option, prepend mysqld
if [ "${1:0:1}" = '-' ]; then
set -- mysqld "$@"
fi
if [ "$1" = 'mysqld' ]; then
# Get config
DATADIR="$("$@" --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')"
echo "DATADIR=$DATADIR"
if [ ! -d "$ /mysql" ]; then
if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
echo >&2 'error: database is uninitialized and password option is not specified '
echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
exit 1
fi
mkdir -p "$DATADIR"
chown -R mysql:mysql "$DATADIR"
echo 'Initializing database'
mysql_install_db --user=mysql --datadir="$DATADIR" --rpm --keep-my-cnf
echo 'Database initialized'
"$@" --skip-networking &
pid="$!"
mysql=( mysql --protocol=socket -uroot )
for i in {30..0}; do
if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
break
fi
echo 'MySQL init process in progress...'
sleep 1
done
if [ "$i" = 0 ]; then
echo >&2 'MySQL init process failed.'
exit 1
fi
if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
# sed is for https://bugs.mysql.com/bug.php?id=20545
mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
fi
if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
fi
"${mysql[@]}" <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
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 ;
FLUSH PRIVILEGES ;
EOSQL
if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
fi
if [ "$MYSQL_DATABASE" ]; then
echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
mysql+=( "$MYSQL_DATABASE" )
fi
if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
if [ "$MYSQL_DATABASE" ]; then
echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
fi
echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
fi
echo
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f" && echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then
"${mysql[@]}" <<-EOSQL
ALTER USER 'root'@'%' PASSWORD EXPIRE;
EOSQL
fi
if ! kill -s TERM "$pid" || ! wait "$pid"; then
echo >&2 'MySQL init process failed.'
exit 1
fi
echo
echo 'MySQL init process done. Ready for start up.'
echo
fi
chown -R mysql:mysql "$DATADIR"
fi
echo "$@"
exec "$@"

schema.xml是mycat中的配置文件4 5,我注释掉了其中一行,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...... // 省略部分内容
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="localhost:3306" user="root"
password="111111">
<!-- can have multi read hosts -->
</writeHost>
<!-- <writeHost host="hostS1" url="localhost:3316" user="root"
password="123456" /> -->
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
...... // 省略部分内容

注释掉的是有关host=“hostS1”的那一段,因为我只有一个mysql实例。mycat-run.sh是mycat的启动文件,主要添加了与mycat测试数据库一致的db1、db2、db3这3个数据库。文件内容如下,

1
2
3
4
5
6
7
#!/bin/bash
echo "CREATE database db1;" | mysql -uroot -p$MYSQL_ROOT_PASSWORD
echo "CREATE database db2;" | mysql -uroot -p$MYSQL_ROOT_PASSWORD
echo "CREATE database db3;" | mysql -uroot -p$MYSQL_ROOT_PASSWORD
$MYCAT_HOME/bin/mycat start

构建和测试

有了以上文件,就可以构建docker镜像了,docker build -t arnes/my-centos7 .,构建过程截图如下,

构建完成之后,就可以运行镜像了,运行的命令是docker run -it -p 3306:3306 -p 8066:8066 -p 8080:8080 -p 9066:9066 -d arnes/my-centos7,截图如下,

其中,大部分是mysql启动的log,这里主要是关注是否已经成功,正常使用的时候,建议使用-d后台启动。

如上所示,宿主机的ip在192.168.2.101,从另一台机器可以访问该容器的服务,截图如下,

综上,容器已经可以提供对外服务。

改进

可能的改进点有以下一些,

  1. mysql已经提供环境变量作为传入参数来设置数据库、密码等,但是mycat并没有,需要支持;
  2. 可以使用supervisord来做启动命令,但这需要先安装python-pip,有待进一步研究;
  3. 镜像应该还有可以精简的地方,有待发现;

以上是本文的全部。

文章目录
  1. 1. dockerfile指令
    1. 1.1. FROM
    2. 1.2. MAINTAINER
    3. 1.3. RUN
    4. 1.4. CMD
    5. 1.5. EXPOSE
    6. 1.6. ENV
    7. 1.7. ADD
    8. 1.8. COPY
    9. 1.9. ENTRYPOINT
    10. 1.10. VOLUME
    11. 1.11. USER
    12. 1.12. WORKDIR
    13. 1.13. ONBUILD
  2. 2. 构建镜像实例
    1. 2.1. 代码和文件准备
    2. 2.2. 构建和测试
    3. 2.3. 改进

欢迎来到Valleylord的博客!

本博的文章尽量原创。