第三章 镜像

4960阅读 0评论2018-03-18 woaimaidong
分类:云计算

镜像是Docker的三大核心概念之一。

    Docker运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker会尝试先从默认镜像仓库下载(默认使用Docker Hub公共注册服务器中的仓库),用户也可以通过配置,使用自定义的镜像仓库。


    本章将介绍围绕镜像这一核心概念的具体操作,包括如何使用pull命令从Docker Hub仓库中下载镜像到本地;如何查看本地已有的镜像信息;如何在远端仓库使用search命令进行搜索和过滤;如何删除镜像标签和镜像文件;如何创建用户定制的镜像并且保存为外部文件。最后,还将介绍如何向Docker Hub仓库中推送自己的镜像。


3.1 获取镜像


    镜像是Docker运行容器的前提。

    读者可以使用docker pull命令从网络上下载镜像。该命令的格式为docker pull NAME[:TAG]。对于Docker镜像来说,如果不显式地指定TAG,则默认会选择latest标签,即下载仓库中最新版本的镜像。


    下面,笔者将演示如何从Docker HubUbuntu仓库下载一个最新的Ubuntu操作系统的镜像。

    $ sudo docker pull ubuntu 

    ubuntu:latest: The image you are pulling has been verified 

    d497ad3926c8: Downloading [======>                   ] 25.41 MB/201.6 MB 51m14s 

    ccb62158e970: Download complete 

    e791be0477f2: Download complete 

    3680052c0f5c: Download complete 

    22093c35d77b: Download complete 

    5506de2b643b: Download complete 

    511136ea3c5a: Download complete

    该命令实际上下载的就是ubuntu:latest镜像,目前最新的14.04版本的镜像。


    下载过程中可以看出,镜像文件一般由若干层组成,行首的2185fd50e2ca这样的字串代表了各层的ID。下载过程中会获取并输出镜像的各层信息。层(Layer)其实是AUFSAdvanced Union File System,一种联合文件系统)中的重要概念,是实现增量保存与更新的基础。

读者还可以通过指定标签来下载特定版本的某一个镜像,例如14.04标签的镜像。

    $ sudo docker pull ubuntu:14.04


上面两条命令实际上都相当于$ sudo docker pull registry.hub.docker.com/ubuntu:latest命令,即从默认的注册服务器 registry.hub.docker.com中的ubuntu仓库来下载标记为latest的镜像。

用户也可以选择从其他注册服务器的仓库下载。此时,需要在仓库名称前指定完整的仓库注册服务器地址。例如从DockerPool社区的镜像源dl.dockerpool.com下载最新的Ubuntu镜像。

$ sudo docker pull dl.dockerpool.com:5000/ubuntu

下载镜像到本地后,即可随时使用该镜像了,例如利用该镜像创建一个容器,在其中运行 bash 应用。

    $ sudo docker run -t -i ubuntu /bin/bash 

    root@fe7fc4bd8fc9:/#

注意:由于网络原因,将无法完成从Docker Hub 的仓库下载最新的操作系统镜像,建议读者使用本章3.5节 基于本地模板导入的方法

3.2查看镜像信息

使用docker images命令可以列出本地主机上已有的镜像。

例如,下面的命令列出了本地刚从官方下载的ubuntu:14.04镜像,以及从DockerPool镜像源下载的ubuntu:latest镜像。

    $ sudo docker images 

    REPOSITORY           TAG        IMAGE ID     CREATED       VIRTUAL SIZE 

    ubuntu             14.04         5506de2b643b      1 weeks ago        197.8 MB 

    dl.dockerpool.com:5000/ubuntu  latest   5506de2b643b   1 weeks ago    197.8 MB


在列出信息中,可以看到几个字段信息:


来自于哪个仓库,比如ubuntu仓库。


镜像的标签信息,比如14.04


镜像的ID号(唯一)。


创建时间。


镜像大小。


其中镜像的ID信息十分重要,它唯一标识了镜像。


TAG信息用于标记来自同一个仓库的不同镜像。例如ubuntu仓库中有多个镜像,通过TAG信息来区分发行版本,包括10.0412.0412.1013.0414.04等标签。


为了方便在后续工作中使用这个镜像,还可以使用docker tag命令为本地镜像添加新的标签。例如添加一个新的ubuntu:latest镜像标签如下:



    $ sudo docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest


再次使用docker images列出本地主机上镜像信息,可以看到多了一个ubuntu:latest标签的镜像。



    $ sudo docker images 

    REPOSITORY           TAG         IMAGE ID       CREATED          VIRTUAL SIZE 

    ubuntu              14.04         5506de2b643b     1 weeks ago       197.8 MB 

    dl.dockerpool.com:5000/ubuntu latest   5506de2b643b    1 weeks ago    192.8 MB 

    ubuntu              latest         5506de2b643b      1 weeks ago       192.8 MB


细心的读者可能会注意到,这些不同标签的镜像的ID是完全一致的,说明它们实际上指向了同一个镜像文件,只是别名不同而已。标签在这里起到了引用或快捷方式的作用。


使用docker inspect命令可以获取该镜像的详细信息。

    $ sudo docker inspect 5506de2b643b 

    [{ 

        "Architecture": "amd64", 

        "Author": "", 

        "Comment": "", 

        "Config": { 

            "AttachStderr": false, 

            "AttachStdin": false, 

            "AttachStdout": false, 

            "Cmd": [ 

                "/bin/bash" 

            ], 

            "CpuShares": 0, 

            "Cpuset": "", 

            "Domainname": "", 

            "Entrypoint": null, 

            "Env": [ 

                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 

            ], 

            "ExposedPorts": null, 

            "Hostname": "065262ce3c91", 

            "Image":"964692831e07f7362f5c3fedf0c4b81a622f2c6e3ec5f19d0eddff21afd64c12", 

            "Memory": 0, 

            "MemorySwap": 0, 

            "NetworkDisabled": false, 

            "OnBuild": [], 

            "OpenStdin": false, 

            "PortSpecs": null, 

            "StdinOnce": false, 

            "Tty": false, 

            "User": "", 

            "Volumes": null, 

            "WorkingDir": "" 

        }, 

        "Container":"f26bc14cc07412402bdab911b8a935fead0322649cf042cee8515c02ebdfa53a", 

        "ContainerConfig": { 

            "AttachStderr": false, 

            "AttachStdin": false, 

            "AttachStdout": false, 

            "Cmd": [ 

                "/bin/sh", 

                "-c", 

                "#(nop) CMD [/bin/bash]" 

            ], 

            "CpuShares": 0, 

            "Cpuset": "", 

            "Domainname": "", 

            "Entrypoint": null, 

            "Env": [ 

                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 

            ], 

            "ExposedPorts": null, 

            "Hostname": "065262ce3c91", 

            "Image":"964692831e07f7362f5c3fedf0c4b81a622f2c6e3ec5f19d0eddff21afd64c12", 

            "Memory": 0, 

            "MemorySwap": 0, 

            "NetworkDisabled": false, 

            "OnBuild": [], 

            "OpenStdin": false, 

            "PortSpecs": null, 

            "StdinOnce": false, 

            "Tty": false, 

            "User": "", 

            "Volumes": null, 

            "WorkingDir": "" 

        }, 

        "Created": "2014-09-23T22:37:05.812213629Z", 

        "DockerVersion": "1.2.0", 

        "Id": "53bf7a53e8903fce40d24663901aac6211373a8d8b4effe08bc884e63e181805", 

        "Os": "linux", 

        "Parent":"964692831e07f7362f5c3fedf0c4b81a622f2c6e3ec5f19d0eddff21afd64c12", 

        "Size": 0 

    } 

    ]

docker inspect命令返回的是一个JSON格式的消息,如果我们只要其中一项内容时,可以使用-f参数来指定,例如,获取镜像的Architecture信息:

    $ sudo docker inspect -f {{".Architecture"}} 550 

    amd64


在指定镜像ID的时候,通常使用该ID的前若干个字符组成的可区分字串来替代完整的 ID

3.3 搜寻镜像

    使用docker search命令可以搜索远端仓库中共享的镜像,默认搜索Docker Hub官方仓库中的镜像。用法为docker search TERM,支持的参数包括:


--automated=false仅显示自动创建的镜像。


--no-trunc=false输出信息不截断显示。


-s, --stars=0指定仅显示评价为指定星级以上的镜像。


例如,搜索带mysql关键字的镜像如下所示:

    $ sudo docker search mysql 

    NAME         DESCRIPTION          STARS     OFFICIAL   AUTOMATED 

    mysql         MySQL is a widely used, open-source relati...   213       [OK] 

    tutum/mysql      MySQL Server image - listens in port 3306....   74        [OK] 

    orchardup/mysql                                                  36        [OK] 

    tutum/lamp       LAMP image - Apache listens in port 80, an...   32        [OK] 

    tutum/wordpress  Wordpress Docker image - listens in port 8...   26        [OK] 

    paintedfox/mariadb A docker image for running MariaDB 5.5, a ... 21        [OK] 

    dockerfile/mysql  Trusted automated MySQL (http://dev.mysql....   14        [OK] 

    google/mysql     MySQL server for Google Compute Engine          13        [OK] 

    anapsix/gitlab-ci  GitLab-CI Continuous Integration in Docker... 12        [OK] 

    centurylink/drupal Drupal docker image without a DB included ... 11        [OK] 

    stenote/docker-lemp  MySQL 5.6、PHP 5.5NginxMemcache          10        [OK] 

    ...

    可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、星级(表示该镜像的受欢迎程度)、是否官方创建、是否自动创建等。

    默认的输出结果将按照星级评价进行排序。 官方的镜像说明是官方项目组创建和维护的,automated资源则允许用户验证镜像的来源和内容。

3.4 删除镜像

使用镜像的标签删除镜像

使用docker rmi命令可以删除镜像,命令格式为docker rmi IMAGE [IMAGE...],其中IMAGE可以为标签或ID


例如,要删除掉dl.dockerpool.com:5000/ubuntu:latest镜像,可以使用如下命令:

    $ sudo docker rmi dl.dockerpool.com:5000/ubuntu 

    Untagged: dl.dockerpool.com:5000/ubuntu:latest


读者可能会担心,本地的ubuntu:latest镜像是否会受到此命令的影响。无需担心,当同一个镜像拥有多个标签的时候,docker rmi命令只是删除了该镜像多个标签中的指定标签而已,并不影响镜像文件。因此上述操作相当于只是删除了镜像5506de2b643b的一个标签而已。


为保险起见,再次查看本地的镜像,发现ubuntu:latest镜像(准确地说,是5506de2b643b镜像)仍然存在:

    $ sudo docker images 

    REPOSITORY         TAG          IMAGE ID          CREATED          VIRTUAL SIZE 

    ubuntu             14.04        5506de2b643b      1 weeks ago      197.8 MB 

    ubuntu             latest       5506de2b643b      1 weeks ago      192.8 MB

但当镜像只剩下一个标签的时候就要小心了,此时再使用docker rmi命令会彻底删除该镜像。

假设本地存在一个标签为mysql:latest的镜像,且没有额外的标签指向它,执行docker rmi命令,可以看出它会删除这个镜像文件的所有AUFS层:

    $ sudo docker rmi mysql:latest 

    Untagged: mysql:latest 

    Deleted: 9a09222edf600a03ea48bd23cfa363841e45a8715237e3a58cb0167f0e8bad54 

    Deleted: 4daeda4ad839a152a3b649672bd5135977d7f81866d3bc0e16d0af3f65cc8af6 

    Deleted: cf07a411bf0883bd632940e8108dac49c64456a47f7390507de5685bbd6daf85 

    Deleted: 4f513746df18b222a07bb8d76d4b6d29752ce5dcb69bfad0ce92e6c1449a3821 

    Deleted: 228ecd435c8a29d25b77999036701a27f2d67874c915bb8eb9fb175b1f98aa60 

    Deleted: 37e4b3932afa186924a09eab332bc8ebec3aac8bac074314ed9a2d1e94547f50 

    Deleted: 898883ccfcee705e440547e30e240cb025c12410d7c9e4d2bcb11973ba075975 

    Deleted: 0a09ddcf99b7fd8fcb3525c41b54696038ecf13677f4459f1c98c742ffa60ab2 

    Deleted: 35bc8591e39be5089265a093e234d13a4b155a01d2ab9e8904eafa81664fb597 

    Deleted: 857e856e4481d59ee88a4cdedd9aaf855666bd494fa38506e6788361c0af4cda

使用镜像ID删除镜像

当使用docker rmi命令后面跟上镜像的ID(也可以是ID能进行区分的部分前缀串)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。


注意,当有该镜像创建的容器存在时,镜像文件默认是无法被删除的,例如:

先利用ubuntu镜像创建一个简单的容器,输出一句话“hello! I am here!”:

    $ sudo docker run ubuntu echo 'hello! I am here!' 

    hello! I am here!

使用docker ps -a命令可以看到本机上存在的所有容器:

    $ sudo docker ps -a 

    CONTAINER ID     IMAGE     COMMAND     CREATED     STATUS      PORTS      NAMES 

    e812617b41f6     ubuntu:latest     "echo 'hello! I am h     13 seconds ago      Exited (0) 12 seconds ago     silly_leakey

可以看到,后台存在一个退出状态的容器,是刚基于ubuntu:latest镜像创建的。

试图删除该镜像,Docker会提示有容器正在运行,无法删除:

    $ sudo docker rmi ubuntu 

    Error response from daemon: Conflict, cannot delete 5506de2b643b because the container e812617b41f6 is using it, use -f to force 

    2014/10/16 18:10:31 Error: failed to remove one or more images

如果要想强行删除镜像,可以使用-f参数:

    $ sudo docker rmi -f ubuntu

笔者不推荐使用-f参数来强制删除一个存在容器依赖的镜像,因为这样往往会造成一些遗留问题。


再次使用docker images查看本地的镜像列表,读者会发现一个标签为的临时镜像,原来被强制删除的镜像换了新的ID继续存在系统中。

    $ sudo docker images 

    REPOSITORY       TAG          IMAGE ID          CREATED           VIRTUAL SIZE 

                      2318d26665ef      3 months ago      198.7 MB

因此,正确的做法是,先删除依赖该镜像的所有容器,再来删除镜像。首先删除容器e812617b41f6

    $ sudo docker rm e81 

    e81

此时再使用临时的ID来删除镜像,此时会正常打印出删除的各层信息:

    core@localhost ~ $   docker rmi -f 2318d26665ef 

    Deleted: 2318d26665eff33e9f91c4c99036751afb40eb58f944a585372bec1407828ad3 

    Deleted: ebc34468f71dca9cb9937bf4c33062540bcacae148df8a70053bfd1acbecaa20 

    Deleted: 25f11f5fb0cb9e41531d1da8dc56351286427e070c536f7015fe76e4dae0a4bc 

    Deleted: 9bad880da3d219b10423804147d6982da1a7bb1e285777a4d746afca6215bebb 

    Deleted: 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158

此时查看本地镜像,读者会发现临时镜像已经被删除:

    $ sudo docker images 

    REPOSITORY       TAG       IMAGE ID       CREATED       VIRTUAL SIZE


3.5 创建镜像

    创建镜像的方法有三种:基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile创建。

本节将重点介绍前两种方法。最后一种基于Dockerfile创建的方法将在后续章节专门予以详细介绍。

基于已有镜像的容器创建

该方法主要是使用docker commit命令,其命令格式为docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]],主要选项包括:

-a, --author=""作者信息。

-m, --message=""提交消息。

-p, --pause=true提交时暂停容器运行。

下面将演示如何使用该命令创建一个新镜像。首先,启动一个镜像,并在其中进行修改操作,例如创建一个test文件,之后退出:

$ sudo docker run -ti ubuntu:14.04 /bin/bash  

root@a925cb40b3f0:/# touch test  

root@a925cb40b3f0:/# exit 

记住容器的IDa925cb40b3f0

此时该容器跟原ubuntu:14.04镜像相比,已经发生了改变,可以使用docker commit命令来提交为一个新的镜像。提交时可以使用ID或名称来指定容器:
 $ sudo docker commit -m "Added a new file" -a "Docker Newbee" a925cb40b3f0 test  

9e9c814023bcffc3e67e892a235afe61b02f66a947d2747f724bd317dda02f27 

顺利的话,命令会返回新创建的镜像的ID信息,例如:

1.    9e9c814023bcffc3e67e892a235afe61b02f66a947d2747f724bd317dda02f27 

此时查看本地镜像列表,即可看到新创建的镜像:
 

1.    $ sudo docker images  

2.    REPOSITORY     TAG        IMAGE ID         CREATED           VIRTUAL SIZE  

3.    test           latest     9e9c814023bc     4 seconds ago     225.4 MB 

基于本地模板导入

也可以直接从一个操作系统模板文件导入一个镜像。在这里,推荐使用OpenVZ提供的模板来创建。OPENVZ模板的下载地址为

比如,笔者下载了一个ubuntu-14.04的模板压缩包后,可以使用以下命令导入:$ sudo cat ubuntu-14.04-x86_64-minimal.tar.gz  |docker import - ubuntu:14.04 

然后查看新导入的镜像,已经在本地存在了:

$ sudo docker images  

REPOSITORY      TAG      IMAGE ID          CREATED             VIRTUAL SIZE  

ubuntu          14.04    05ac7c0b9383      17 seconds ago      215.5 MB 

3.6 存出和载入镜像

    可以使用 docker savedocker load命令来存出和载入镜像。

存储镜像

    如果要存储镜像到本地文件,可以使用docker save命令。例如,存出本地的ubuntu:14.04镜像为文件ubuntu_14.04.tar

$ sudo docker images  

REPOSITORY      TAG        IMAGE ID          CREATED          VIRTUAL SIZE  ubuntu          14.04      c4ff7513909d      5 weeks ago      225.4 MB  

$ sudo docker save -o ubuntu_14.04.tar ubuntu:14.04 

载入镜像

可以使用docker load从存出的本地文件中再导入到本地镜像库,例如从文件ubuntu_14.04.tar导入镜像到本地镜像列表,如下所示:
 $ sudo docker load --input ubuntu_14.04.tar 

$ sudo docker load < ubuntu_14.04.tar 

这将导入镜像以及其相关的元数据信息(包括标签等),可以使用docker images命令进行查看。

3.7 上传镜像

    可以使用docker push命令上传镜像到仓库,默认上传到DockerHub官方仓库(需要登录),命令格式为docker push NAME[:TAG]

    用户在DockerHub网站注册后,即可上传自制的镜像。例如用户user上传本地的test:latest镜像,可以先添加新的标签user/test:latest,然后用docker push命令上传镜像:

$ sudo docker tag test:latest user/test:latest  
$ sudo docker push user/test:latest  
The push refers to a repository [base/163] (len: 1)  
Sending image list  
Please login prior to push:  
Username:  
Password:  
Email: xxx@xxx.com 

第一次使用时,会提示输入登录信息或进行注册。

补充说明:

    之所以选择原创标签,标识该文章是老薛自己学习验证后,逐行敲录的。博友在学习的过程中有不解之处,欢迎来“老薛Linux技术社区”进一步交流,老薛微信号:laoxue100

上一篇:第二章 Docker的核心概念和安装
下一篇:第四章 容器