比起docker commit 更推荐使用Dockerfile文件和docker build创建镜像,Dockerfile 使用基于DSL(Domain Specific Language)语法指令来构建镜像,通过该方法创建镜像更具备可重复性(只要文件存在就可以一直使用该文件创建镜像),透明性(只需知道怎么使用指令能够完成什么操作,不需要知道指令是如何工作的)以及幂等性(无论执行多少次结果都是一样的)。
(一)、如何使用Dockerfile创建镜像
下面我们来使用Docker创建一个镜像,该镜像包含一个简单的web服务。
在你希望的路径创建一个目录 mkdir static_web
vim Dockerfile
我先利用自己本地已存在的镜像写了一个简单的Dockerfile 文件,如下面的代码所示
#Version: 0.0.1
FROM ubuntu:12.04
MAINTAINER wendianfei "wendianfei@163.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html
EXPOSE 80
FROM ubuntu:12.04 ###从基础镜像运行一个容器
MAINTAINER wendianfei "wendianfei@163.com" ##写入作者的信息
RUN apt-get update && apt-get install -y nginx ###安装nginx服务
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html ###修改nginx默认web页面
EXPOSE 80 ###说明容器内部程序使用的端口
紧接着我们执行docker build 来读这个文件创建镜像。Dockerfile 中的指令会按顺序从上到下执行,所以应该根据需要合理安排指令的顺序。如果失败了则会停止继续运行。
[root@VM-0-16-centos static_web]# docker build -t wendianfei/static_web .
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu:12.04
---> 5b117edd0b76 #####基础镜像的ID
Step 2/5 : MAINTAINER wendianfei "wendianfei@163.com"
---> Running in f7c7c7e2db83 ###为了添加作者信息二运行的容器
Removing intermediate container f7c7c7e2db83 ###移除临时容器
---> 6284e1683e3d ###将更改后的容器保存为新的镜像
Step 3/5 : RUN apt-get update && apt-get install -y nginx
---> Running in 440c5e20dec2 ###利用上一个镜像6284e1683e3d 创建一个新的临时容器来安装nginx操作
Get:1 http://archive.ubuntu.com precise Release.gpg [198 B]
Get:2 http://archive.ubuntu.com precise-updates Release.gpg [198 B]
Get:3 http://archive.ubuntu.com precise-security Release.gpg [181 B]
Get:4 http://archive.ubuntu.com precise Release [49.6 kB]
Get:5 http://archive.ubuntu.com precise-updates Release [55.4 kB]
Get:6 http://archive.ubuntu.com precise-security Release [55.5 kB]
Get:7 http://archive.ubuntu.com precise/main Sources [1175 kB]
Get:8 http://archive.ubuntu.com precise/restricted Sources [5306 B]
Get:9 http://archive.ubuntu.com precise/universe Sources [6239 kB]
....太长了不写了
凑巧的是我们执行的过程中出错了,但是仍然会生成一个可用的镜像,这对我们调试非常有用。
上面可以看到错误提示是目录不存在。但是不知道具体错在哪,那么这个时候我们就可以使用最后一个可用的镜像来创建一个可交互的容器来排查问题。
[root@VM-0-16-centos static_web]# docker run -it --name set4test 434efdcf563c /bin/bash
root@244d0bee0563:/# cd /usr/share/nginx/www/
50x.html index.html
我们进入容器后发现,我们安装的nginx版本,web目录不是 /usr/share/nginx/html/ 而是 /usr/share/nginx/www/ ,找到问题就好办了我们只要把Dockerfile 里面的目录修改一下再继续执行就好。
#Version: 0.0.1
FROM ubuntu:12.04
MAINTAINER wendianfei "wendianfei@163.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/www/index.html
EXPOSE 80
然后再次执行docker build
[root@VM-0-16-centos static_web]# docker build -t wendianfei/static_web .
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu:12.04
---> 5b117edd0b76
Step 2/5 : MAINTAINER wendianfei "wendianfei@163.com"
---> Using cache
---> 6284e1683e3d
Step 3/5 : RUN apt-get update && apt-get install -y nginx
---> Using cache
---> 434efdcf563c
Step 4/5 : RUN echo 'Hi, I am in your container' >/usr/share/nginx/www/index.html
---> Running in 9d782fd5adb1
Removing intermediate container 9d782fd5adb1
---> 26d3a01cd350
Step 5/5 : EXPOSE 80
---> Running in dd31bad90f10
Removing intermediate container dd31bad90f10
---> 2944f46ad47a
Successfully built 2944f46ad47a
Successfully tagged wendianfei/static_web:latest
[root@VM-0-16-centos static_web]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wendianfei/static_web latest 2944f46ad47a 7 minutes ago 146MB
ubuntu 12.04 5b117edd0b76 4 years ago 104MB
我们发现这次前三步里面都有"Using cache"这个字段,这也是Dockerfile特性之一,再次支持只会从做了改变的那一步开始执行,之前的全部使用缓存的内容。
(二)使用新建的镜像来运行一个容器。
docker run --name dockerfile_test -d -p ::80 -it wendianfei/static_web /bin/bash
然后在浏览器输入http://81.68.237.197:32779/ 发现居然没法访问。
我们使用伪终端进入容器查看,发现nginx没有运行。想了一下,因为我们创建镜像的时候只是安装nginx,并没有启用nginx。我们启用nginx,重新在浏览器输入地址发现可以访问了。
root@da798308b4e8:/# nginx
root@da798308b4e8:/# ps axu |grep nginx
root 41 0.0 0.0 62924 1332 ? Ss 08:55 0:00 nginx: master process nginx
www-data 42 0.0 0.0 63232 1644 ? S 08:55 0:00 nginx: worker process
www-data 43 0.0 0.0 63232 1644 ? S 08:55 0:00 nginx: worker process
www-data 44 0.0 0.0 63232 1644 ? S 08:55 0:00 nginx: worker process
www-data 45 0.0 0.0 63232 1644 ? S 08:55 0:00 nginx: worker process
这样登陆进容器再起服务太麻烦了。我们重新尝试再创建容器的时候启动服务。
docker run --name dockerfile_test2 -d -p ::80 -it wendianfei/static_web nginx -g "daemon off;"
然后在浏览器输入http://81.68.237.197:32780/ 发现可以访问
(三)Dockerfile 与 构建缓存
由于每一步构建过程都会将结果提交为镜像,Dockerfile构建过程会将之前的镜像作为缓存,比如我们上面第4步失败,重试的时候会直接从第四步开始,前三步都使用缓存,这样做的好处是节省了前三步的时间。然后有些时候我们又需要他某些步骤重新执行,比如APT包有了更新,但是我们的构建过程使用缓存就不会重新构建。有两个方法解决。
(1)使用--no--cache 参数来构建镜像
$docker build --no--cache -t wendianfei/static_web:v1 .
(2)在写Dockerfile 使用ENV REFRESHED_AT 时间
#Version: 0.0.1
FROM ubuntu:12.04
MAINTAINER wendianfei "wendianfei@163.com"
ENV REFRESHED_AT 2021-04-18
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html
EXPOSE 80
如果想刷新一个构建只需要修改ENV 指令中的日期。这会使得构建过程从ENV这一行重新开始执行。