前面的文章介绍过镜像的三种获取方式:
- 下载并使用别人创建好的镜像;
- 在现有镜像上创建新的镜像;
- 从无到有创建镜像。
本文主要介绍前两种。
一、下载镜像
在Docker Hub上有大量优质镜像可以使用,我们通过docker pull命令就能获取任何我们想要的镜像:
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull命令使用可通过 docker pull --help 获取,我们简要说明下镜像名称和格式:
- Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub(docker.io)。
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
我们以nginx为例,使用docker pull下载该镜像:
$ sudo docker pull nginx:1.21.1 1.21.1: Pulling from library/nginx 33847f680f63: Pull complete dbb907d5159d: Pull complete 8a268f30c42a: Pull complete b10cf527a02d: Pull complete c90b090c213b: Pull complete 1f41b2f2bf94: Pull complete Digest: sha256:8f335768880da6baf72b70c701002b45f4932acae8d574dedfddaf967fc3ac90 Status: Downloaded newer image for nginx:1.21.1 docker.io/library/nginx:1.21.1
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io)获取镜像。而镜像名称是 nginx:1.21.1,因此将会获取官方镜像 library/nginx 仓库中标签为 1.21.1 的镜像。docker pull 命令的输出结果最后一行给出了镜像的完整名称,即:docker.io/library/nginx:1.21.1。
从下载过程中我们可以看到镜像分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。
二、在现有镜像上创建新的镜像
我们首先启动之前下载到nginx镜像:
$ sudo docker run --name webserver -d -p 80:80 nginx:1.21.1 f3152d0042ac83a967c1c16be5b5abe5cab93a9625ecb616763c13ec4f4775c7
这条命令会用 nginx 镜像启动一个容器,命名为 webserver,并且映射了 80 端口,这样我们可以访问这个 nginx 服务器。(关于启动命令后面会专门去讲,大家暂时不必细究)
我们进入此容器:
$ sudo docker exec -it webserver bash root@f3152d0042ac:/#
我们尝试访问nginx:
root@f3152d0042ac:/# curl http://localhost <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
可以看到目前页面返回的是“Welcome to nginx!”,那我们想要改变首页为“Hello Docker”并保存为新的镜像该怎么做呢?非常简单,我们直接在此容器内修改Nginx的index.html并退出即可:
root@f3152d0042ac:/# echo '<h1>Hello Docker</h1>' > /usr/share/nginx/html/index.html root@f3152d0042ac:/# exit exit
我们修改了容器中的文件其实是我们修改了容器的存储层,我们可以通过docker diff看看镜像的改动情况:
$ sudo docker diff webserver C /root A /root/.bash_history C /etc C /etc/nginx C /etc/nginx/conf.d C /etc/nginx/conf.d/default.conf C /var C /var/cache C /var/cache/nginx A /var/cache/nginx/uwsgi_temp A /var/cache/nginx/client_temp A /var/cache/nginx/fastcgi_temp A /var/cache/nginx/proxy_temp A /var/cache/nginx/scgi_temp 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
现在我们定制好了变化,我们希望能将其保存下来形成镜像。要知道,当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
docker commit命令语法格式:
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]] $ sudo docker commit \ > --author "ala" \ > --message "modify index.html" \ > webserver \ > nginx:v1 sha256:0c9cdfafb439f3815c500699b7eeaed1a69f097bcdcef9968efa023fc7a2b82e
其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空。
我们可以通过 docker images 中看到这个新定制的镜像:
$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 0c9cdfafb439 About a minute ago 133MB ubuntu 18.04 39a8cfeef173 2 weeks ago 63.1MB nginx 1.21.1 08b152afcfae 3 weeks ago 133MB nginx latest 08b152afcfae 3 weeks ago 133MB hello-world latest d1165f221234 5 months ago 13.3kB
最后我们来使用该镜像:
$ sudo docker run --name webserver1 -d -p 81:80 nginx:v1 fc6b2706dcec9080cb4b70ee1a26d1c2ce790b4ce64e2d1ff47aa5973617e3af $ sudo docker exec -it webserver1 bash
进入容器后我们访问nginx首页:
root@fc6b2706dcec:/# curl http://localhost <h1>Hello Docker</h1>
我们可以看到我们新作出的镜像是OK的。
PS:慎用docker commit
我们在实际工作中不要使用此方式制作镜像,主要有两点原因:
1、仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,将会导致镜像极为臃肿。
2、使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 黑箱镜像,也就是说除了制作镜像的人知道怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。这种黑箱镜像是难以维护的。
三、查看镜像
如果想列出已有的镜像,可以使用docker image ls 或 docker images命令:
$ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 0c9cdfafb439 46 hours ago 133MB ubuntu 18.04 39a8cfeef173 3 weeks ago 63.1MB nginx 1.21.1 08b152afcfae 3 weeks ago 133MB nginx latest 08b152afcfae 3 weeks ago 133MB hello-world latest d1165f221234 5 months ago 13.3kB $ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 0c9cdfafb439 46 hours ago 133MB ubuntu 18.04 39a8cfeef173 3 weeks ago 63.1MB nginx 1.21.1 08b152afcfae 3 weeks ago 133MB nginx latest 08b152afcfae 3 weeks ago 133MB hello-world latest d1165f221234 5 months ago 13.3kB
列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个 标签。因此,在上面的例子中,我们可以看到 nginx:latest 和 nginx:1.21.1 拥有相同的 ID,因为它们对应的是同一个镜像。
四、删除本地镜像
如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:
$ docker image rm [选项] <镜像1> [<镜像2> ...]
具体使用说明可以通过--help参数查看:
$ sudo docker image rm --help Usage: docker image rm [OPTIONS] IMAGE [IMAGE...] Remove one or more images Aliases: rm, rmi, remove Options: -f, --force Force removal of the image --no-prune Do not delete untagged parents
例如我们要删除前面制作nginx:v1镜像:
$ sudo docker image rm nginx:v1 Untagged: nginx:v1 Deleted: sha256:0c9cdfafb439f3815c500699b7eeaed1a69f097bcdcef9968efa023fc7a2b82e Deleted: sha256:0375e82c5314808631749f5072a184b6cde63f41e599fd58b91c82f56aae1667
这里要注意如果镜像对应的容器正在运行,则会删除失败,这时需要先使用docker stop [container id]停止容器,然后在使用docker rm [container id]删除容器,最后在删除镜像。我们还可以通过镜像ID来删除镜像:
$ sudo docker image rm d11 Untagged: hello-world:latest Untagged: hello-world@sha256:0fe98d7debd9049c50b597ef1f85b7c1e8cc81f59c8d623fcb2250e8bec85b38 Deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 Deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
如果使用镜像ID的话,其实并不需要指定完整ID即长ID,使用短ID即可,短ID一般取前3个字符以上,只要足够区分于别的镜像就可以了。
分类: 容器技术