docker-- 启动一个nginx 容器并修改内容后利用commit生成新的镜像

拉个镜像:

docker pull nginx

启动容器:

docker run --name webserver1 -d -p 80:80  nginx

看下进程:

# ps -ef|grep docker
root 153804 1 0 09:57 ? 00:00:25 /usr/bin/dockerd
root 153817 153804 0 09:57 ? 00:00:02 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
root 155488 153804 0 10:09 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
root 155494 153817 0 10:09 ? 00:00:00 docker-containerd-shim e033ce3a2d90f340bac864c32dd2f613ce6179cba366ad10d5c8deaf2147097d /var/run/docker/libcontainerd/e033ce3a2d90f340bac864c32dd2f613ce6179cba366ad10d5c8deaf2147097d docker-runc

用docker exec 进入容器的bash,并修改页面

# docker exec -it webserver1 bash
root@e033ce3a2d90:/# cat /etc/*ease*
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@e033ce3a2d90:/# exit

从浏览器访问页面会发生变化。

我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动。

# docker diff webserver1
C /run
A /run/nginx.pid
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
C /root
A /root/.bash_history

现在我们定制好了变化,我们希望能将其保存下来形成镜像。

要知道,当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

# docker commit --author "admin@moneyslow" --message "modify index.html" webserver1 nginx:v2
sha256:3e3413f5d5263eb85d316ac794d8babd6cd36b38c79dac624531fed5254420c9

其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空。

我们可以在 docker images 中看到这个新定制的镜像:

# docker images nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 3e3413f5d526 About a minute ago 109MB
nginx latest 3448f27c273f 8 days ago 109MB

我们还可以用 `docker history` 具体查看镜像内的历史记录,如果比较 `nginx:latest` 的历史记录,我们会发现新增了我们刚刚提交的这一层。

docker-- 启动一个nginx 容器并修改内容后利用commit生成新的镜像

新的镜像定制好后,我们可以来运行这个镜像。

# docker run --name webserver2 -d -p 81:80 nginx:v2
50f00085b48fc36b22dc3b150f0ed728e160b39135714902fbaea8727c63a478

浏览器可访问 http://ip:81

顺便提一下,容器被访问的日志查看:

docker ps 查看容器id,利用docker logs 查看id或容器名称 的日志:

# docker logs 50f00085b48f
172.30.30.5 - - [19/May/2017:03:40:21 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "-"
172.30.30.5 - - [19/May/2017:03:40:22 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "-"

这里我们命名为新的服务为 webserver2,并且映射到 81 端口。如果是 Docker for Mac/Windows 或 Linux 桌面的话,我们就可以直接访问 http://localhost:81 看到结果,其内容应该和之前修改后的 webserver 一样。至此,我们第一次完成了定制镜像,使用的是 docker commit 命令,手动操作给旧的镜像添加了新的一层,形成新的镜像,对镜像多层存储应该有了更直观的感觉。

慎用 docker commit
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。
首先,如果仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。
而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到™。这会让镜像更加臃肿。
docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,定制行为应该使用 Dockerfile 来完成。