文章目录
  1. 1. Dockerfile基本框架
  2. 2. 编写Dockerfile常用命令
  3. 3. 通过Dockerfile 创建镜像
  4. 4. 总结

docker容器是在指定的镜像上运行的, 创建docker镜像的方法也有很多中比如直接到docker Hub上通过docker pull创建一个镜像, 也可以通过修改其它镜像然后提交修改docker commit 来创建新的镜像, 还有如导入某个现有的镜像, 但是在实际中难觅要定制化改进出符合特定场景的镜像, 虽然用上述方式也是可以创建新的镜像的, 但是缺点也很多, 主要缺点是不便于维护, 其实创建docker 镜像还有一种最好的办法就是手工打造Dockerfile, 然后通过docker build编译生成新的镜像, 下面本文就来实践怎么编写符合规范的Dockerfile, 然后又是怎么样通过Dockerfile来创建合适的镜像的。

需求
需求是完成一个Dockerfile, 通过该Dockerfile创建一个Web应用, 该web应用为apache托管的一个静态页面网站, 换句话说, 我们写一个Dockerfile, 用来创建一个实验楼公司的网站应用, 就是 http://www.simplecloud.cn 这个站点。这个站点是纯静态的页面, 我们也可以直接下载得到。
从实现这个需求中去实践和学习关于Dockerfile如下三方面的内容,

  • 了解Dockerfile 基本框架
  • 学习Dockerfile 编写常用命令
  • 通过Dockerfile 构建镜像

Dockerfile基本框架

一份Dockerfile一般包含下面几个部分,

  • 基础镜像:以哪个镜像作为基础进行制作,用法是FROM 基础镜像名称
  • 维护者信息:需要写下该Dockerfile编写人的姓名或邮箱,用法是MANITAINER 名字/邮箱
  • 镜像操作命令:对基础镜像要进行的改造命令,比如安装新的软件,进行哪些特殊配置等,常见的是RUN命令
  • 容器启动命令:当基于该镜像的容器启动时需要执行哪些命令,常见的是CMD命令或ENTRYPOINT

下面创建一份Dockerfile 输入如下内容,

1
2
3
4
5
6
7
8
9
10
11
12
13
# Version 0.1

# 基础镜像
FROM nbuntu:latest

# 维护者信息
MAINTAINER lihong/leehongitrd@163.com

# 镜像操作命令
RUN apt-get -yqq update && apt-get install -yqq apache2 && apt-get clean

# 容器启动命令
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

因为直接FROM ubuntu:latest 创建的镜像, 然后基于这个镜像运行的容器中是没有ifconfig, ping等命令的, 所以为了本次试验自己就新建了一个镜像nbuntu, 这个镜像就包含了ifconfig, ping等命令

上面的Dockerfile就做了一件事,即创建一个apache的镜像,

  • FROM指定基础镜像,如果镜像名称中没有制定TAG, 默认为latest
  • RUN命令默认使用/bin/sh Shell执行,默认为root权限。如果命令过长需要换行,需要在行末尾加\\
  • CMD 命令也是默认在/bin/sh Shell中执行,并且默认只能有一条,如果是多条CMD命令则只有最后一条执行。用户也可以在docker run命令创建容器时指定新的CMD命令来覆盖Dockerfile里的CMD。

docker build将上述Dockerfile构建名为test:0.1的新镜像,

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
lihong@dev:~/docker$ docker build -t test:0.1 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nbuntu:latest
---> f16a2621960a
Step 2 : MAINTAINER lihong/leehongitrd@163.com
---> Running in 668004fe61bd
---> cb74681dcdc9
Removing intermediate container 668004fe61bd
Step 3 : RUN apt -yqq update && apt install -yqq apache2 && apt clean
---> Running in 00968a59fa09

The following additional packages will be installed:
apache2-bin apache2-data apache2-utils file ifupdown iproute2
isc-dhcp-client isc-dhcp-common libapr1 libaprutil1 libaprutil1-dbd-sqlite3
libaprutil1-ldap libasn1-8-heimdal libatm1 libdns-export162 libexpat1
libgdbm3 libgssapi3-heimdal libhcrypto4-heimdal libheimbase1-heimdal
libheimntlm0-heimdal libhx509-5-heimdal libicu55 libisc-export160
libkrb5-26-heimdal libldap-2.4-2 liblua5.1-0 libmagic1 libmnl0 libperl5.22
libroken18-heimdal libsasl2-2 libsasl2-modules libsasl2-modules-db
libsqlite3-0 libssl1.0.0 libwind0-heimdal libxml2 libxtables11 mime-support
netbase openssl perl perl-modules-5.22 rename sgml-base ssl-cert xml-core
Suggested packages:
www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom ufw
ppp rdnssd iproute2-doc resolvconf avahi-autoipd isc-dhcp-client-ddns
apparmor libsasl2-modules-otp libsasl2-modules-ldap libsasl2-modules-sql
libsasl2-modules-gssapi-mit | libsasl2-modules-gssapi-heimdal
ca-certificates perl-doc libterm-readline-gnu-perl
| libterm-readline-perl-perl make sgml-base-doc openssl-blacklist debhelper
The following NEW packages will be installed:
apache2 apache2-bin apache2-data apache2-utils file ifupdown iproute2
isc-dhcp-client isc-dhcp-common libapr1 libaprutil1 libaprutil1-dbd-sqlite3
libaprutil1-ldap libasn1-8-heimdal libatm1 libdns-export162 libexpat1
libgdbm3 libgssapi3-heimdal libhcrypto4-heimdal libheimbase1-heimdal
libheimntlm0-heimdal libhx509-5-heimdal libicu55 libisc-export160
libkrb5-26-heimdal libldap-2.4-2 liblua5.1-0 libmagic1 libmnl0 libperl5.22
libroken18-heimdal libsasl2-2 libsasl2-modules libsasl2-modules-db
libsqlite3-0 libssl1.0.0 libwind0-heimdal libxml2 libxtables11 mime-support
netbase openssl perl perl-modules-5.22 rename sgml-base ssl-cert xml-core
debconf: delaying package configuration, since apt-utils is not installed
0 upgraded, 49 newly installed, 0 to remove and 0 not upgraded.
Need to get 21.4 MB of archives.
After this operation, 98.3 MB of additional disk space will be used.
Step 4 : CMD /usr/sbin/apache2ctl -D FOREGROUND
---> Running in f954154994b2
---> 4abe06cc549a
Removing intermediate container f954154994b2
Successfully built 4abe06cc549a

查看新创建的镜像test:0.1,
1
2
3
4
5
6
lihong@dev:~/docker$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.1 4abe06cc549a 24 seconds ago 274.1 MB
nbuntu latest f16a2621960a 4 hours ago 174.2 MB
redis latest e4a35914679d 7 days ago 182.9 MB
ubuntu latest 0ef2e08ed3fa 8 days ago 130 MB

使用该镜像创建容器web1, 将容器中的端口80映射到本地80端口,
1
2
lihong@dev:~/docker$ docker run -d -p 80:80 --name web1 test:0.1
27479a43f119d213509c9af6cdfcd43330f55ed0c4fbeb08fbc3dd434744f36e

通过firefox浏览器打开localhost进行测试,apache已运行,

apache

编写Dockerfile常用命令

如果构建镜像需求变更, 则只需将其增加到Dockerfile中即可。

1 指定容器运行的用户

在一些需要指定用户来运行的应用部署时非常关键,比如提供hadoop服务的容器通常会使用hadoop用户来启动服务。该用户将作为后续的RUN命令执行的用户; 例如使用mike用户来执行后续命令, 将下面的命令添加Dockerfile中, 放置到要执行的命令之前即可,

1
USER mike

2 指定后续命令的执行目录

假如后继需要运行的是一个静态网站,将启动后的工作目录切换到/var/www/html目录, 则,

1
WORKDIR /var/www/html

3 对外连接端口号
由于内部服务会启动Web服务,我们需要把对应的80端口暴露出来,可以提供给容器间互联使用,可以使用EXPOSE命令。
在镜像操作部分增加下面一句,

1
EXPOSE 80

4 设置容器主机名

ENV命令能够对容器内的环境变量进行设置, 使用该命令设置由该镜像创建的容器的主机名为dev, 向Dockerfile中增加下面一句,

1
ENV HOSTNAME dev

5 向镜像中增加文件

向镜像中添加文件有两种命令:COPYADD

COPY命令可以复制本地文件夹到镜像中,

1
COPY website /var/www/html

ADD命令支持添加本地的tar压缩包到容器中指定目录, 压缩包会被自动解压为目录, 也可以自动下载URL并拷贝到镜像, 例如,

1
2
ADD html.tar /var/www
ADD http://www.shiyanlou.com/html.tar /var/www

根据需求, 需要的一个网站放到镜像里, 需要把一个tar包添加到apache的/var/www目录下, 因此选择使用ADD命令,
1
ADD html.tar /var/www

6 CMD与ENTRYPOINT

ENTRYPOINT容器启动后执行的命令, 让容器执行表现的像一个可执行程序一样, 与CMD的区别是不可以被docker run覆盖,会把docker run后面的参数当作传递给ENTRYPOINT指令的参数。Dockerfile中只能指定一个ENTRYPOINT, 如果指定了很多, 只有最后一个有效。docker run命令-entrypoint参数可以把指定的参数继续传递给ENTRYPOINT

7 挂载数据卷

apache访问的日志数据存储到宿主机可以访问的数据卷中,

1
VOLUME ["/var/log/apache2"]

8 设置容器内的环境变量

如在本需求中使用ENV设置一些apache启动的环境变量,

1
2
3
4
5
6
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apche2
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOCK_DIR /var/lock/apche2

9 使用 Supervisord

CMD只有一个命令, 当需要运行多个服务怎么办呢?最好的办法是分别在不同的容器中运行,通过--link进行连接,比如先前实验中用到的web, app, db容器。如果一定要在一个容器中运行多个服务可以考虑用Supervisord来进行进程管理,方式就是将多个启动命令放入到一个启动脚本中。

首先安装Supervisord, 添加下面内容到Dockerfile,

1
2
RUN apt-get install -yqq supervisor
RUN mkdir -p /var/log/supervisor

拷贝配置文件到指定的目录,

1
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

其中supervisord.conf文件需要放在/home/lihong/docker文件夹下,文件内容如下,
1
2
3
4
5
[supervisord]
nodaemon=true

[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2ctl -D FOREGROUND"

如果有多个服务需要启动可以在文件后继续添加[program:xxx], 比如如果有ssh服务,可以增加[program:ssh]

修改CMD命令,启动Supervisord,

1
CMD ["/usr/bin/supervisord"]

通过Dockerfile 创建镜像

将上述内容完成后放入到/home/lihong/docker/Dockerfile文件中,最终得到的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
# Version 0.2

# 基础镜像
FROM nbuntu:latest

# 维护者信息
MAINTAINER lihong/leehongitrd@163.com

# 镜像操作命令
RUN apt -yqq update && apt install -yqq apache2 && apt clean
RUN apt install -yqq supervisor
RUN mkdir -p /var/log/supervisor

VOLUME ["/var/log/apche2"]

ADD html.tar /var/www
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

WORKDIR /var/www/html

ENV HOSTNAME dev
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apche2
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOCK_DIR /var/lock/apche2

EXPOSE 80

# 容器启动命令
CMD ["/usr/bin/supervisord"]

同时在/home/lihong/docker目录下,添加supervisord.conf文件,

1
2
3
4
5
[supervisord]
nodaemon=true

[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2ctl -D FOREGROUND"

并下载静态页面文件压缩包,
1
2
cd /home/lihong/docker
wget http://labfile.oss.aliyuncs.com/courses/498/html.tar

http://simplecloud.cn 网站的页面tar包下载下来, 放到和Dockerfile文件同一个文件夹中。 然后通过docker build创建镜像, -t参数指定镜像名称,
1
2
3
4
5
6
7
8
9
10
11
12
13
lihong@dev:~/docker$ docker build -t test:0.2 .
Sending build context to Docker daemon 7.708 MB
Step 1 : FROM nbuntu:latest
---> f16a2621960a
Step 2 : MAINTAINER lihong/leehongitrd@163.com
---> Using cache
---> cb74681dcdc9
Step 3 : RUN apt -yqq update && apt install -yqq apache2 && apt clean
---> Using cache
---> a7b7dce4c67c
Step 4 : RUN apt install -yqq supervisor
---> Running in 3b86e3c6eec0
...

通过docker images查看到新镜像test:02已经创建好了,

1
2
3
4
hong@dev:~/docker$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.2 0886fdb745c8 About a minute ago 308.9 MB
test 0.1 4abe06cc549a 50 minutes ago 274.1 MB

下面用新镜像test:02创建新的容器web, 并映射本地的80端口到容器的80端口,

1
2
lihong@dev:~/docker$ docker run -d -p 80:80 --name web test:0.2
0221f5e699201a0f2d6757289970752ec696fa8fcb25b91bff7134d78af20a7e

在firefox输入本地地址访问127.0.0.1,看到我们克隆的琛石科技的网站,

website

总结

  • 通过实际案例了解Dockerfile编写的基本框架
  • 简要回顾Dockerfile 编写常用命令
  • 通过Dockerfile 构建镜像, 并测试成功

作者署名:朴实的一线攻城狮
本文标题:docker实践--05 编写Dockerfile
本文出处:http://researchlab.github.io/2017/03/07/docker-dockerfile/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。

@一线攻城狮

关注微信公众号 @一线攻城狮

总访问:
总访客: