docker commit采坑记录
最近使用docker commit碰到了一个小坑,记录分享一下。
问题现象记录:
使用官方docker镜像 nginx 1.19.2,想要修改镜像中nginx.conf中配置值,方法如下:(注:可以直接通过文件修改,此处为了给docker commit举例用)
- 使用某个镜像运行了一个容器(docker run image:tag /bin/bash)
- 在容器内修改了程序的启动参数
- 使用这个容器生成它需要的image(docker commit containerID)
理论上这个操作是没有任何问题的,实际上新的image一运行立刻就退出了 ,容器的状态为Exit(0)
查看日志,发现没有打印任何日志,推测很大的可能性是执行命令不是阻塞程序,执行完就直接退出了。
验证一下:通过docker ps对比新旧image容器的CMD指令,老image的CMD='nginx -g daemon off;’,新image的CMD=‘/bin/bash’,确认问题了,修改后的CMD覆盖掉了修改前的。
问题根源分析
1.为什么CMD会被修改
回顾全程看看cmd命令是什么时候被修改的:
修改前image的cmd=‘nginx -g daemon off;’,然后docker run -it image:tag /bin/bash启动了容器并且进入到容器内部进行修改,最后执行docker commit 。
很明显docker commit将容器当前使用的cmd(/bin/bash)设置为新创建的image的CMD。也就是原来image的cmd被刚才commit时指定的容器cmd覆盖了。
为什么会覆盖呐?docker官方描述如下:
If you list more than one
CMD
then only the last CMD
will take effect。如果有多条CMD命令,仅最后一条CMD命令生效。老image的dockerfile中CMD是这样的:
CMD ["nginx" "-g" "daemon off;"]
执行docker run命令时,最后一条cmd由原来的 nginx -g daemon off; 变为/bin/bash
docker run -it image:tag /bin/bash #容器主进程变为/bin/bash
执行docker commit时,当前容器的cmd保存最后一条/bin/bash
2.为什么新CMD‘/bin/bash’导致容器退出
因为Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。
CMD
指令就是用于指定默认的容器主进程的启动命令的。对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
为了保证容器不退出,容器的主进程(即image的dockfile中CMD执行的命令)必须是常驻运行的(一直挂起的例如tail top等前台命令,后台命令一般执行后就会退出)。
docker官方描述如下:
The main purpose of a
CMD
is to provide defaults for an executing container。CMD主要目的是为可执行容器提供默认命令。问题解决方案
1. docker run启动容器时,不指定CMD (docker run -dit image:tag,无 /bin/bash)。
启动容器时,不指定CMD,则使用默认cmd运行容器。commit时自动保留原镜像的默认CMD。
若需要进入容器bash环境编辑容器,再使用docker exec -it containerID /bin/bash即可。
2. docker commit时使用--change参数替换image中的cmd.
比如:--change=‘CMD ["nginx", "-g", "daemon off;"]'