通常情况下,我们使用dockerfile来创建镜像,主要是因为dockerfile可以实现脚本化,反复使用。
dockerfile通常是由一系列指令组成,一个常见的dockerfile的例子主要有以下一些指令。
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
。
为后续的 RUN
、CMD
、ENTRYPOINT
指令配置工作目录。
可以使用多个 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 ./CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo ADD ./mysql-community.repo /etc/yum.repos.d/mysql-community.repo 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 RUN yum update RUN yum install -y openssh-server php RUN groupadd -r mysql && useradd -r -g mysql mysql 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 libpwquality \ && rm -rf /var/cache/yum/* 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 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 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.gz
、apache-tomcat-8.0.28.tar.gz
、Mycat-server-1.5-alpha-20151221110028-linux.tar.gz
分别是jdk8、tomcat8、mycat1.5的安装包文件,解压后分别是jdk1.8.0_66
、apache-tomcat-8.0.28
、mycat
。CentOS-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
| [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 [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 [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 [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 [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 [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
的更新源配置,我只取其中的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镜像中的构建方法,文件内如如下,
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 [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi if [ "$1" = 'mysqld' ]; then 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 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中的配置文件 ,我注释掉了其中一行,
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> <writeHost host="hostM1" url="localhost:3306" user="root" password="111111"> </writeHost> 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
,从另一台机器可以访问该容器的服务,截图如下,
综上,容器已经可以提供对外服务。
改进
可能的改进点有以下一些,
- mysql已经提供环境变量作为传入参数来设置数据库、密码等,但是mycat并没有,需要支持;
- 可以使用supervisord来做启动命令,但这需要先安装
python-pip
,有待进一步研究;
- 镜像应该还有可以精简的地方,有待发现;
以上是本文的全部。