美文网首页
Docker 镜像和容器的基本操作

Docker 镜像和容器的基本操作

作者: Alexander_Zz | 来源:发表于2020-03-31 09:41 被阅读0次

    获取镜像

    Docker 官方提供了一个公共镜像仓库 Docker Hub,我们就可以从这上面获取镜像,获取镜像的命令 docker pull,格式如下

    $ docker pull [选项] [Docker Registry 地址[:端口]/]仓库名[:标签]
    
    • Docker 镜像仓库地址
      地址的格式一般是 <域名/IP>[:端口号],默认地址 Docker Hub
    • 仓库名
      这里的仓库名是两段式名称,即<用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像
    $ docker pull ubuntu:16.04
    16.04: Pulling from library/ubuntu
    fe703b657a32: Pull complete 
    f9df1fafd224: Pull complete 
    a645a4b887f9: Pull complete 
    57db7fe0b522: Pull complete 
    Digest: sha256:e9938f45e51d9ff46e2b05a62e0546d0f07489b7f22fbc5288defe760599e38a
    Status: Downloaded newer image for ubuntu:16.04
    docker.io/library/ubuntu:16.04
    

    上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:16.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 16.04 的镜像。从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的sha256的摘要,以确保下载一致性。

    运行

    有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 ubuntu:16.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行如下命令

    $ docker run -it --rm ubuntu:16.04 /bin/bash
    
    root@e7009c6ce357:/# cat /etc/os-release
     
    root@2a8d9e813164:/# cat /etc/os-release 
    NAME="Ubuntu"
    VERSION="16.04.6 LTS (Xenial Xerus)"
    ID=ubuntu
    ID_LIKE=debian
    PRETTY_NAME="Ubuntu 16.04.6 LTS"
    VERSION_ID="16.04"
    HOME_URL="http://www.ubuntu.com/"
    SUPPORT_URL="http://help.ubuntu.com/"
    BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
    VERSION_CODENAME=xenial
    UBUNTU_CODENAME=xenial
    
    • 参数说明
      1. -i 参数,交互式操作
      2. -t 参数,终端
      3. --rm 参数,容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间
      4. ubuntu:16.04 参数,使用 ubuntu:16.04 镜像为基础来启动容器
      5. /bin/bash 参数,交互式 Shell,此例中使用的是 bash

    进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器是 Ubuntu 16.04 LTS 系统,利用 exit 退出容器

    列出镜像

    $ docker images ls
    

    列表包含了仓库名、标签、镜像 ID、创建时间以及所占用的空间。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签

    镜像大小

    如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,ubuntu:16.04 镜像大小,在这里是 127MB,但是在 Docker Hub 显示的却是 43MB。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 docker image ls 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小
    另外一个需要注意的问题是,docker image ls 列表的镜像体积总和并非是所有镜像实际磁盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像磁盘占用空间很可能要比这个列表镜像大小的总和要小的多。可利用如下命令查看镜像、容器、数据卷所占用空间

    $ docker system df
    

    新建并启动

    所需要的命令主要为 docker run

    $ docker run ubuntu:16.04 /bin/echo 'Hello World'
    Hello World
    

    这跟在本地直接执行 /bin/echo 'Hello World' 几乎感觉不出任何区别,下面的命令则是启动一个 bash 终端,允许用户进行交互

    $ docker run -t -i ubuntu:16.04 /bin/bash
    root@105f3c437dd1:/#
    

    其中 -t 选项让 Docker 分配一个为终端 (pseudo-tty) 并绑定到容器的标准输入上,-i 则让容器的标准输入保持打开。在交互模式下,用户可以通过所创建的终端来输入命令

    root@105f3c437dd1:/# pwd
    /
    root@105f3c437dd1:/# ls
    bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
    

    当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括

    • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
    • 利用镜像创建并启动一个容器
    • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
    • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
    • 从地址池配置一个 ip 地址给容器
    • 执行用户指定的应用程序
    • 执行完毕后容器被终止

    启动已终止容器

    可利用 docker container start 命令,直接将一个已终止的容器启动运行。
    容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 pstop 来查看进程信息

    root@105f3c437dd1:/# ps
       PID TTY          TIME CMD
         1 pts/0    00:00:00 bash
        13 pts/0    00:00:00 ps
    

    可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化

    后台运行

    更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现。下面举两个例子说明一下

    • 不使用 -d 参数运行容器
    $ docker run ubuntu:16.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
    hello world
    hello world
    hello world
    hello world
    hello world
    

    容器会把输出的结果 (STDOUT) 打印到宿主机上面,如果使用了 -d 参数运行容器

    $ docker run -d ubuntu:16.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
    ffb20a32735d37d39976602daaf176b131f58b82de75088c41fe545e20928245
    

    此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)
    注:容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关
    使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息

    $ docker container ls 
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    ffb20a32735d        ubuntu:16.04        "/bin/sh -c 'while t…"   5 minutes ago       Up 5 minutes                            confident_wright
    
    获取容器输出信息,通过 docker container logs 命令
     docker container logs ffb20a32735d
    hello world
    hello world
    hello world
    hello world
    ...
    

    终止容器

    可以使用 docker container stop 来终止一个运行中的容器。当 Docker 容器中指定的应用终结时,容器也自动终止
    例如此前启动的容器,通过 exit 命令或 Ctrl + d 来退出终端时,所创建的容器立刻终止。终止状态的容器可以用 docker container ls -a 命令看到

    $ docker container ls -a
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
    ffb20a32735d        ubuntu:16.04        "/bin/sh -c 'while t…"   12 minutes ago      Up 12 minutes                                   confident_wright
    16b3faa5eb71        ubuntu:16.04        "/bin/sh -c 'while t…"   14 minutes ago      Exited (0) 14 minutes ago                       awesome_lalande
    

    处于终止状态的容器,可以通过 docker container start 命令来重新启动
    此外,docker container restart 命令会将一个运行状态的容器终止,然后再重新启动它

    进入容器

    在使用 -d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作 exec 命令 -i -t 参数
    只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。当 -i -t 参数一起使用时,则可看到我们熟悉的 Linux 命令提示符

    $ docker run -dit ubuntu:16.04
    34c4f9ab075e0ab0a3cc6d3542ce7271fd02d3aecfe0436b4fa613644aad86bc
    
    $ docker container ls 
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    34c4f9ab075e        ubuntu:16.04        "/bin/bash"              31 seconds ago      Up 30 seconds                           festive_gould
    ffb20a32735d        ubuntu:16.04        "/bin/sh -c 'while t…"   18 minutes ago      Up 18 minutes                           confident_wright
    
    $  docker exec -i ffb2 bash
    ls
    bin
    boot
    dev
    etc
    ...
    
    $ docker exec -it ffb2 bash
    root@ffb20a32735d:/#
    

    如果从这个 stdinexit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因
    更多参数说明可使用 docker exec --help 查看

    删除容器

    可使用 docker container rm 来删除一个处于终止状态的容器

    $ docker container rm confident_wright
    confident_wright
    

    也可以使用 docker rm 容器名来删除,如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器
    docker container ls -adocker ps -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器

    $ docker container prune
    

    或者

    $ docker ps -aq
    

    删除本地镜像

    如果要删除本地镜像,可以使用 docker image rm 命令,其格式如下

    $ docker image rm [选项] <镜像1> [<镜像2>...]
    

    或者

    $ docker rmi 镜像名
    

    或者用 ID、镜像名、摘要删除镜像,<镜像> 可以是镜像短 ID、镜像长 ID、镜像名或者镜像摘要,比如我们有如下镜像

    $ docker image ls
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    redis               latest              7eed8df88d3b        4 days ago          98.2MB
    nginx               latest              a1523e859360        4 days ago          127MB
    ubuntu              16.04               77be327e4b63        9 days ago          124MB
    hello-world         latest              fce289e99eb9        14 months ago       1.84kB
    

    我们可以用镜像的完整 ID,也称为长 ID,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用短 ID 来删除镜像。docker image ls 默认列出的就已经是短 ID 了,一般取前 3 个字符以上,只要足够区分于别的镜像就可以了
    比如这里,我们删除 hello-world:latest 镜像,可执行

    $ docker image rm fce
    Untagged: hello-world:latest
    Untagged: hello-world@sha256:fc6a51919cfeb2e6763f62b6d9e8815acbf7cd2e476ea353743570610737b752
    Deleted: sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
    Deleted: sha256:af0b15c8625bb1938f1d7b17081031f649fd14e6b233688eea3c5483994a66a3
    

    我们也可以用镜像名,也就是 <仓库名>:<标签>,来删除镜像

    $ docker image rm redis
    Untagged: redis:latest
    Untagged: redis@sha256:6b9920bdc913ebeeed5cd5327decabe9fa829de425b52b3f28a7215ee7c7c457
    Deleted: sha256:7eed8df88d3b3574ed937381f06e715d1b73be03ffed6acca58a6fa9f6ff3d64
    Deleted: sha256:aa9525c4752dd5fedee1f41f3db7a519b1c91038b17fb946eda78c43de3b11ae
    Deleted: sha256:86e1a89f327d93c9b28048daa5898214560c276ce953007dc14aad3c3538053e
    Deleted: sha256:20c83759b6b115fc11cf14b95fbcb4354d9f5de2eb171ea1aaaf13b52da8adb5
    Deleted: sha256:1643fe1af32a4bc89c6e4906bcc83198a74e344f207d2077603b307c199957f3
    Deleted: sha256:561c0c133828808acb409eb5798ccdeaa41b1ef50f720919f22a1c5ff2158260
    

    docker commir 定制镜像

    镜像是容器的基础,每次执行 docker run 的时候都会制定哪个镜像作为容器运行的基础。在之前的例子中,我们所使用的的都是来自于 Docker Hub 的镜像。直接使用这些镜像是可以满足一定的需求,而当这些镜像无法直接满足需求时,我们就需要定制这些镜像。
    镜像是多层存储,没一层都是在前一层基础上进行修改,而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层
    以定制一个 Web 服务器为例

    $ docker run --name webserver -d -p 80:80 nginx
    

    这条命令会用 nginx 镜像启动一个容器,命名为 webserver,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器
    如果是在 Linux 本机运行的 Docker,或者如果使用的是 Docker for Mac、Docker for Windows,那么可以直接访问http://localhost,如果使用的是 Docker Toolbox,或者是在虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际服务器地址
    直接用浏览器访问的话,我们会看到到默认的 Nginx 欢迎页面
    现在,假设我们非常不喜欢这个欢迎页面,我们希望改成欢迎 Docker 的文字,我们可以使用 docker exec 命令进入容器,修改其内容

    $ docker exec -it webserver bash
    root@2aa075fe58f8:/# echo '<h1>Hello,Docker</h1>' > /usr/share/nginx/html/index.html 
    root@2aa075fe58f8:/# exit
    

    我们以交互式终端方式进入 webserver 容器,并执行了 bash 命令,也就是获得一个可操作的 Shell。然后,我们用 <h1>Hello,Docker</h1> 覆盖了 /usr/share/nginx/html/index.html 的内容。现在我们再刷新浏览器的话,会发现内容被改变了
    我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动

    $ docker diff webserver
    C /root
    A /root/.bash_history
    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/scgi_temp
    A /var/cache/nginx/uwsgi_temp
    A /var/cache/nginx/client_temp
    A /var/cache/nginx/fastcgi_temp
    A /var/cache/nginx/proxy_temp
    C /run
    A /run/nginx.pid
    

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

    $ docker commit --author 'Rookie' --message 'change homepage' webserver nginx:v2
    sha256:ba1b48cf9a809c9cd0a1f4cd54902f9865b762cc5a7622e5f29ddb6c405b57ae
    

    其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空
    我们可以在 docker image ls 中看到这个新定制的镜像

    $ docker image ls nginx
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    nginx               v2                  ba1b48cf9a80        3 minutes ago       127MB
    nginx               latest              a1523e859360        4 days ago          127MB 
    

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

    $ docker history nginx:v2
    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    ba1b48cf9a80        4 minutes ago       nginx -g daemon off;                            94B                 change homepage
    a1523e859360        4 days ago          /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B                  
    <missing>           4 days ago          /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B                  
    <missing>           4 days ago          /bin/sh -c #(nop)  EXPOSE 80                    0B                  
    <missing>           4 days ago          /bin/sh -c ln -sf /dev/stdout /var/log/nginx…   22B                 
    <missing>           4 days ago          /bin/sh -c set -x     && addgroup --system -…   57.5MB              
    <missing>           4 days ago          /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B                  
    <missing>           4 days ago          /bin/sh -c #(nop)  ENV NJS_VERSION=0.3.8        0B                  
    <missing>           4 days ago          /bin/sh -c #(nop)  ENV NGINX_VERSION=1.17.8     0B                  
    <missing>           4 days ago          /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B                  
    <missing>           5 days ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B                  
    <missing>           5 days ago          /bin/sh -c #(nop) ADD file:e5a364615e0f69616…   69.2MB
    

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

    $ docker run --name webserv2 -d -p 81:80 nginx:v2
    

    这里我们命名为新的服务器为 webserv2,并且映射到 81 端口。如果是 Docker for Mac/Windows 或 Linux 桌面的话,我们就可以直接访问 http://localhost:81 看到结果,其内容应该和之前修改后的 webserver 一样
    至此,完成镜像的基本定制,使用的 docker commit 命令,手动操作给旧的镜像添加了新的一层,形成新的镜像
    注:docker commit 命令一般用来保存被入侵后的现场,若要定制镜像,还是应该使用 Dockerfile 完成

    相关文章

      网友评论

          本文标题:Docker 镜像和容器的基本操作

          本文链接:https://www.haomeiwen.com/subject/wjpmkhtx.html