在docker中,我们可以基于镜像创建容器,也可以基于容器创建镜像,镜像和容器之间是可以互相转化的,现在小编就来说说关于docker 将容器保存成镜像?下面内容希望能帮助到你,我们来一起看看吧!

docker 将容器保存成镜像(docker4:)

docker 将容器保存成镜像

在docker中,我们可以基于镜像创建容器,也可以基于容器创建镜像,镜像和容器之间是可以互相转化的。

在之前的实验中,我们使用的镜像是官方的nginx镜像,如果我们想要自己制作一个nginx镜像,该怎么办呢?我通常会使用如下两种方法:

方法一:下载官方的nginx镜像,基于官方nginx镜像启动一个容器,把需要的文件拷贝到容器中,把配置文件改成自己想要的样子,把收拾好的容器变成镜像。

方法二:下载一个基础系统的镜像,比如centos、debian或者alpine镜像,然后基于这些系统镜像启动一个容器,在这个容器中安装nginx,然后把需要的文件拷贝到容器中,修改配置文件,最后把收拾好的容器变成镜像。

其实这两种方法没有本质区别,整体思路都是基于某个镜像,启动成容器,把容器改成自己想要的样子,然后把改好的容器变成镜像。由于方法一中基于的镜像是别人已经安装好软件的镜像,所以省去了安装软件的步骤,如果是想把自己公司开发的软件容器化,则只能使用方法二了,因为只有咱们自己有公司开发的软件,安装配置过程也只有自己最清楚。

使用方法二时,需要用到一些基础的系统镜像,咱们先下载一些基础镜像,熟悉熟悉它们

docker pull debian:11.2docker pull ubuntu:18.04docker pull centos:7.9.2009docker pull alpine:latest

拉取上述基础镜像后,可以发现,centos的体积较大,alpine的体积最小

[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEalpine latest 9c842ac49a39 18 hours ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB

alpine是一个基于musl libc和busybox的Linux操作系统,体积非常小,所以在容器化时有先天优势,musl libc与常用的glibc基本兼容,musl libc更加轻量,但是可能缺少一些扩展,或者存在一些其他问题,在使用alpine作为基础系统时,需要进行充分的测试。

在选择基于哪个镜像容器化自己的软件时,在保证正常运行的前提下,如果最终的运行效果没有区别,优先选择体积较小的镜像。

此处使用debian和ubuntu的镜像创建两个容器,容器名为debian-test和ubuntu-test。

docker run -td --name debian-test debian:11.2docker run -td --name ubuntu-test ubuntu:18.04

上述命令的-td选项是两个短选项的合并写法,-t表示为容器分配伪终端(和前文中的docker exec命令中的-t选项作用一样),-d表示让容器在后台运行。上例中,如果只使用-d选项,不使用-t选项,会无法正常运行容器,原因是pid为1的主进程结束时,容器也会自动停止,具体情况是这样的,这些操作系统类的基础镜像默认运行的程序通常都是shell,比如bash,也就是说,bash是容器中的主进程,pid是1,如果不使用-t选项为bash分配伪终端,单独执行bash命令后,bash进程就直接结束了(没有绑定任何终端,相当于执行bash命令后一闪而过,没有任何输入输出,直接结束进程),而前文中做过实验,当pid为1的主进程结束时,容器就会停止,所以,我们需要让bash持续运行,从而实现持续运行容器的目的,使用-t选项就是为了给bash分配伪终端,让其可以通过终端占用前台,从而达到持续运行pid1进程的目的。上例中,如果只是用-t选项,不使用-d选项,为容器分配的伪终端会直接占用host的命令行,所以,-t选项配合-d选项,让容器在后台运行,可以避免容器一直占用host的命令行窗口,并持续运行容器。

前文中,启动nginx-demo容器的命令如下

docker run --name nginx-demo -d -p 80:80 nginx:latest

上述命令并没有使用-t选项,这是因为nginx镜像默认运行的程序是nginx(其实默认运行的是脚本,在脚本中通过exec命令调用nginx,让nginx的pid为1,具体过程以后再聊),nginx程序的运行不需要使用终端,所以无需使用-t选项。

通过上述对比可以说明,在使用docker run命令启动容器时,具体使用哪些选项取决于容器中运行程序的方式以及具体运行的是哪个程序,大部分容器在启动时使用-d、-t、-i选项中的一个或几个都可以满足正常持续运行容器的需求。

使用如下命令可以查看镜像或容器的默认运行程序。

docker inspect -f '{{.Config.Cmd}}' 容器名或镜像名docker inspect -f '{{.Config.Entrypoint}}' 容器名或镜像名

我在host主机上又打开了两个shell,通过两个shell分别进入刚才创建的两个容器

[root@kvm32docker2 ~]# docker exec -it ubuntu-test bashroot@92c0833b1889:/# [root@kvm32docker2 ~]# docker exec -it debian-test bashroot@b5b41ca43b68:/#

然后分别在host、ubuntu-test、debian-test中执行uname -r命令,查看内核版本,输出信息如下

[root@kvm32docker2 ~]# uname -r3.10.0-1160.el7.x86_64root@92c0833b1889:/# uname -r3.10.0-1160.el7.x86_64root@b5b41ca43b68:/# uname -r3.10.0-1160.el7.x86_64

很明显,它们输出的内核信息是完全一样的,因为在前文中说过,容器都是共享host的内核的,无论是debian镜像,还是ubuntu镜像,它们都只是打包了对应系统发行版的类库环境和命令工具,镜像中不包含内核,它们共享使用了宿主机的内核,只要宿主机的内核能够兼容对应的系统发行版,那么系统镜像就能够在host上正常运行。

为了方便后面实验,这里选择alpine镜像创建容器

[root@kvm32docker2 ~]# docker run -td --name alpine-test alpine06e550f1f88d0d9ac1e988cf50fd19b7c11d5f544027200f8296e19f7f9c86b6

启动容器后,我们可以按照方法二中的思路,进入容器,把公司开发的软件服务部署到容器中,此处为了方便演示,假设nginx就是我们要部署的服务,在docker hub中,很多官方仓库直接提供了基于alpine系统的软件镜像,比如redis、nginx等,直接pull下来就可以使用,不过话说回来,此处还是为了模拟实验,所以,咱们自己安装nginx,在alpine中,可以使用apk命令安装软件,apk是一个包管理工具,就像centos中的yum一样,只需在alpine容器中执行如下命令,即可安装nginx

apk add nginx

但是默认的软件源比较慢,我们可以把默认的软件源换成阿里或科大的镜像源,提升安装速度

换成阿里镜像源sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories换成科大镜像源sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

通过apk安装nginx后,默认的nginx配置文件路径是/etc/nginx/nginx.conf,默认的server配置文件是/etc/nginx/http.d/default.conf,这里修改一下默认server的配置,修改后配置如下

~ # cat /etc/nginx/http.d/default.conf # This is a default site configuration which will simply return 404, preventing# chance access to any other virtualhost.server { listen 80 default_server; listen [::]:80 default_server; # Everything is a 404 location / { #return 404; return 200 https://www.zsythink.net; } # You may need this to prevent return 404 recursion. location = /404.html { internal; }}

默认配置是访问根时返回404,这里改成返回200,并返回博客我的博客地址。启动nginx,用自带的wget命令访问一下默认页面

~ # nginx~ # ~ # wget -q --header="Accept: text/html" http://127.0.0.1 -O-https://www.zsythink.net~ #

返回的信息正是我设置的网址,可见nginx已经正常工作了。假设,我对现在的状态很满意,我想要基于当前容器,创建一个镜像,方便以后使用,则可以执行如下命令

docker commit -p -c 'CMD ["nginx","-g","daemon off;"]' alpine-test zsythink/nginx:test1

如上所示,docker commit命令可以将容器提交为镜像,上述命令表示基于alpine-test容器创建tag为zsythink/nginx:test1的镜像,前文已经说过tag(标签)的命名规则,上例标签中的zsythink是我在docker hub上注册的账号ID,zsythink/nginx表示zsythink账号下名为nginx的仓库,完整的写法应该是docker.io/zsythink/nginx:test1,表示docker hub上zsythink账户下nginx仓库中的tag为test1的镜像,但是由于是docker hub上的仓库,所以可以省略registry地址,于是就写成了zsythink/nginx:test1,聪明如你一定已经看出来了,我之所以把创建的镜像的标签写成上面的样子,是因为在后面的实验中,我要把这个创建的镜像推送到docker hub上,所以标签要和真实的仓库地址对应上,如果只是在本地做实验,不推送镜像到任何仓库,那么标签可以随便写。上例中,-p选项表示在基于容器创建镜像的过程中,暂停容器的运行。-c选项用于设定镜像的各种参数,上例-c选项的值为'CMD ["nginx","-g","daemon off;"]',表示将镜像的默认运行的程序设置为nginx,执行如下命令,可见alpine镜像默认运行的命令是sh

[root@kvm32docker2 ~]# docker inspect -f '{{.Config.Cmd}}' alpine[/bin/sh]

所以,基于alpine镜像启动的容器alpine-test默认运行的程序也是sh,当我们基于alpine-test容器创建自己的镜像时,如果不修改默认运行的程序,创建出的镜像将仍然默认运行sh,但是我创建这个镜像的目的是使用里面的nginx,让基于这个镜像启动的容器可以默认提供我定制好的nginx服务,所以需要通过上述方式将nginx设置为默认运行的程序,上例'CMD ["nginx","-g","daemon off;"]'中的CMD是专门用来设置默认运行程序的一个指令,在后面的文章中会总结各种设置指令的用法,此处不用纠结,上例将默认运行的程序指令设置为nginx -g 'daemon off;',表示设置nginx以非守护进程的方式运行,这么做是为了防止容器自动停止,原因仍然与pid为1的进程有关,当我们设置nginx为默认运行的程序时,nginx进程就是容器中pid为1的主进程,如果nginx以daemon的方式运行,当守护进程被fork以后,发起守护进程的源nginx进程就会结束,而源nginx进程的pid为1,这就会导致容器也随之停止,所以,我们需要阻止nginx以daemon的方式在后台运行,通过-g 'daemon off;'可以让nginx在前台保持运行,最终实现持续运行容器的目的。

当执行了docker commit命令以后,即可在镜像列表中查看到我们创建的镜像

[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzsythink/nginx test1 9fbed7b04dc0 17 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB

现在,我们就可以基于自己创建的这个镜像,启动新的容器了。比如,创建一个名为nginx-alpine的容器

[root@kvm32docker2 ~]# docker run --name nginx-alpine -d zsythink/nginx:test1cf759b1bb597694ba55c567949f56e439a6be07775f4b05794dc64f9f1eb6434[root@kvm32docker2 ~]# [root@kvm32docker2 ~]# [root@kvm32docker2 ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMEScf759b1bb597 zsythink/nginx:test1 "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds nginx-alpine06e550f1f88d alpine "/bin/sh" 3 days ago Up 3 days alpine-test

容器启动后,执行如下命令,可以查看到容器在docker默认网络下分配到的内网IP,在host主机上通过这个IP地址即可访问到容器内部的nginx,容器网络的相关话题咱们后面再聊,此处先略过。

[root@kvm32docker2 ~]# docker inspect nginx-alpine -f '{{.NetworkSettings.IPAddress}}'172.17.0.3

在host中访问容器对应的IP地址,可以看到我们预设的内容,证明容器已经正常运行了。

[root@kvm32docker2 ~]# curl 172.17.0.3https://www.zsythink.net[root@kvm32docker2 ~]#

到此,我们已经完成了基于容器制作镜像的全部过程,还是很简单的,起到关键作用的其实只是一条docker commit命令罢了,只是我说的比较啰嗦而已,除了通过docker commit可以创建镜像,还有另一种方法也可以创建镜像,这种方法需要我们编写一个叫Dockerfile的配置文件,通过Dockerfile去创建镜像,之后的文章会总结Dockerfile的用法,这里我们先使用docker commit

我们可以把自己制作好的镜像推送到远程的仓库中,分享给别人使用,在推送镜像之前,需要先确保已经登录了对应的registry

#使用docker login命令可以登录对应的registry,语法如下docker login registryURL#假设,我们在内网中建立了一个私有镜像仓库,私有仓库地址为192.168.1.100,那么使用如下命令即可登录私有仓库docker login http://192.168.1.100#当没有指定registryURL时,默认登录的registry为docker hub,如下docker login

执行docker login命令后,根据提示输入用户名和密码,即可登录docker hub,登录成功后,执行docker push命令,可以将本地镜像推送到对应的仓库中

#先查看一下镜像,此处我们要推送的镜像是zsythink/nginx:test1[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzsythink/nginx test1 9fbed7b04dc0 19 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB#在已经登录docker hub的前提下,直接执行push命令,docker会按照完整的标签地址将镜像推送到对应的仓库中。#所以,如果想要正常推送镜像,需要确保镜像的tag地址对应的仓库是真实存在的。#zsythink/nginx是我提前创建好的仓库,注册docker hub账号后,通过https://hub.docker.com/repositories地址即可创建自己的仓库[root@kvm32docker2 ~]# docker push zsythink/nginx:test1The push refers to repository [docker.io/zsythink/nginx]e68e457d8858: Pushed ff768a1413ba: Mounted from library/alpine test1: digest: sha256:f49ad91ac5c9a80c2330d9644935b72cba9bcc43754419de6873c62e4c664974 size: 739

推送完成后,登录docker hub的网页,在repositories中即可看到自己推送的镜像。

除了能将镜像推送到远程仓库,我们还可以直接将镜像从本地导出,使用docker save命令即可将镜像导出成文件。

#如下命令表示将本地的nginx:latest镜像和busybox:latest镜像导出到一个名为test.images的文件中,-o参数用于指定导出镜像时输出的文件名docker save -o test.images nginx:latest busybox:latest

如你所见,我们可以一次性导出一个或多个镜像到一个文件,上例中导出的文件名为test.images,我们可以把test.images文件拷贝到其他docker主机上,然后通过docker load命令即可把这个文件中包含的所有镜像导入到其他docker主机上,导入命令如下

docker load -i test.images

在使用docker commit命令时,如果没有指定标签,创建的镜像则不会有任何标签,只会有一个镜像ID,我们可以使用docker tag命令,为没有标签的镜像添加标签,具体过程如下

#如下命令表示基于alpine-test容器创建镜像,但是没有为创建的镜像指定标签,创建出的镜像的id为2e16a148800c......[root@kvm32docker2 ~]# docker commit -p alpine-testsha256:2e16a148800cb90394e53746b50e8944868782998e75bd13facce5b53cc146a1#通过命令查看镜像列表,可见刚才创建的id为2e16a148800c...的镜像没有任何标签信息[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE<none> <none> 2e16a148800c 4 seconds ago 9.45MBzsythink/nginx test1 9fbed7b04dc0 20 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB#使用docker tag命令,可为id为2e16a148800c的镜像添加标签#此处执行了两次docker tag命令,给id为2e16a148800c的镜像添加了两个标签#一个标签为zsythink/nginx:test2,另一个标签为test-nginx:1[root@kvm32docker2 ~]# docker tag 2e16a148800c zsythink/nginx:test2[root@kvm32docker2 ~]# [root@kvm32docker2 ~]# docker tag 2e16a148800c test-nginx:1#再次查看镜像列表,可以发现,id为2e16a148800c的镜像已经有两个标签了#这两个标签在镜像列表中就好像两个镜像一样,其实它们是一个镜像,只是使用了不同的标签而已。[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEtest-nginx 1 2e16a148800c 6 minutes ago 9.45MBzsythink/nginx test2 2e16a148800c 6 minutes ago 9.45MBzsythink/nginx test1 9fbed7b04dc0 20 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB

我们可以根据自己的需求,为同一个镜像,添加上很多个不同的标签,以便区分一个镜像在不同场景下所扮演的角色。

在同一个镜像ID有多个标签的情况下,如果想要删除某一个标签,只需要执行docker rmi命令即可,示例如下

#查看镜像列表,可见ID为2e16a148800c的镜像有两个条目[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEtest-nginx 1 2e16a148800c 6 minutes ago 9.45MBzsythink/nginx test2 2e16a148800c 6 minutes ago 9.45MBzsythink/nginx test1 9fbed7b04dc0 20 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB#首先,删除标签为test-nginx:1的镜像,执行如下命令,注意命令的返回结果[root@kvm32docker2 ~]# docker rmi test-nginx:1Untagged: test-nginx:1#此时查看镜像列表,发现对应标签为test-nginx:1的条目已经不在了[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzsythink/nginx test2 2e16a148800c 24 minutes ago 9.45MBzsythink/nginx test1 9fbed7b04dc0 21 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB#再次执行docker rmi命令,删除标签为zsythink/nginx:test2的镜像,注意命令的返回结果[root@kvm32docker2 ~]# docker rmi zsythink/nginx:test2Untagged: zsythink/nginx:test2Deleted: sha256:2e16a148800cb90394e53746b50e8944868782998e75bd13facce5b53cc146a1#再次查看镜像列表,发现ID为2e16a148800c的镜像已经不存在了[root@kvm32docker2 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzsythink/nginx test1 9fbed7b04dc0 21 hours ago 9.45MBalpine latest 9c842ac49a39 5 days ago 5.57MBdebian 11.2 6f4986d78878 3 months ago 124MBubuntu 18.04 5a214d77f5d7 5 months ago 63.1MBcentos 7.9.2009 eeb6ee3f44bd 6 months ago 204MB

通过上述实验可知,当一个镜像有多个标签时,在执行docker rmi 标签名删除镜像时,其实只是删除了对应镜像的某一个标签而已,当一个镜像只有唯一一个标签时,docker rmi 标签名才会删除对应的镜像。

这篇文章总结了如何基于容器创建镜像以及一些常用的命令,没有什么难点,只是需要孰能生巧啊,今天先总结到这里,下次见啦~

,