美文网首页
第7章 Docker容器

第7章 Docker容器

作者: super_pcm | 来源:发表于2019-12-24 17:17 被阅读0次

    7.1 Docker容器——简介

    容器是镜像的运行时实例。正如从虚拟机模板启动VM一样,用户也同样从单个镜像上启动一个或多个容器。虚拟机和容器最大的区别是容器更快并且更加轻量级——与虚拟机运行在完整的操作系统之上相比,容器会共享其所在主机的操作系统/内核。

    启动容器的简便方式是使用 docker container run 命令。该命令可以携带很多参数,在其基础的格式 docker container run <image> <app>中,指定启动所需的镜像以及要运行的应用。docker container run -it ubuntu /bin/bash 则会启动某个ubuntu Linux容器,并允许Bash Shell作为其应用。

    -it参数可以将当前终端连接到容器的shell终端之上。容器随着其中运行的应用的退出而终止。在上面的示例中,Linux容器会在bash shell退出后终止。

    使用docker container stop 命令可以手动停止容器运行,并且使用docker container start再次启动该容器。如果不需要使用该容器了,则使用docker container rm 命令来删除容器。

    7.2 Dcker容器——详解

    7.2.1 容器 VS 虚拟机

    容器和虚拟机都依赖于宿主机才能运行,下面示例假设宿主机是一台需要运行4个业务应用的物理服务器。

    在虚拟机的模型中我们通常在宿主机上运行4个VM并且在上面安装4个操作系统;但是在容器模型中,容器引擎直接获取系统资源,包括进程数、文件系统以及网络栈等等。对比下,容器的额外开销要小得多。

    从更高层面上来讲,Hypervisor是硬件虚拟化(Hardware Virtualization)——Hypervisor将硬件物理资源划分为虚拟资源;另外,容器时操作系统虚拟机(OS Virtualization)——容器将系统资源划分为虚拟资源。

    虚拟机模型将底层硬件资源划分到虚拟机当中,每个虚拟机都包含了虚拟的CPU、内存以及磁盘资源。因此,每个虚拟机都需要自己的操作系统来声明、初始化并管理这些虚拟机资源。而操作系统本身时有额外的开销的:例如每个操作系统需要CPU、内存的资源,都需要一个独立的许可证,并且都需要打补丁升级,每个操作系统也都面临被攻击的风险。

    容器模型具有在宿主机操作系统中运行的单个内核。在一台主机上运行数十个甚至上百个容器都是可能的——容器共享一个操作系统/内核。这意味着只有一个操作系统消耗CPU、内存等资源,只有一个系统需要授权,只有一个系统需要升级和打补丁。同时,只有一个操作系统面临被攻击的风险。简言之,就是只有一份OS损耗。

    量变引起质变,上面示例只有4个业务应用的场景,如果是成百上千个应用....

    此外,容器的启动时间远远小于虚拟机。因为容器不是完整的操作系统,容器内部并不需要内核,也就没有定位、解压以及初始化的过程——更不用提在内核启动过程中对硬件的遍历和初始化了。这些在容器的启动过程中统统不需要!唯一需要的就是位于下层操作系统的共享内核启动了。最终的结果就是,容器可以在1S内启动,唯一对容器启动时间有影响的就是容器内应用启动所花费的时间。

    7.2.2 启动容器

    通常登录Docker主机后第一件事情就是检查Docker是否正在运行。在这里我们可以使用docker version命令来查看

    [pangcm@docker01 ~]$ docker version
    Client: Docker Engine - Community
     Version:           19.03.2
     API version:       1.40
     Go version:        go1.12.8
     Git commit:        6a30dfc
     Built:             Thu Aug 29 05:28:55 2019
     OS/Arch:           linux/amd64
     Experimental:      false
    
    Server: Docker Engine - Community
     Engine:
      Version:          19.03.2
      API version:      1.40 (minimum version 1.12)
      Go version:       go1.12.8
      Git commit:       6a30dfc
      Built:            Thu Aug 29 05:27:34 2019
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          1.2.6
      GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
     runc:
      Version:          1.0.0-rc8
      GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
     docker-init:
      Version:          0.18.0
      GitCommit:        fec3683
    

    这个命令会显示Docker的版本信息,以及client和server的内容。

    使用docker container run命令来启动容器,例如我们上面多次举例的启动一个ubuntu容器:

    docker container run -it ubuntu /bin/bash
    

    当敲击回车键之后,Docker客户端选择合适的API来调用Docker daemon。Docker daemon接受到命令并搜索Docker本地缓存,观察是否有命令请求的镜像。上面的示例中,我们的本地有缓存的镜像,如果没有缓存,那就会到Docker Hub上查找对应的镜像,找到后拉取到本地,然后创建容器。

    7.2.3 容器进程

    上面创建的容器,一旦我们退出了bash shell,该容器也会退出(终止)。原因是容器如果不允许任何进程则无法存在,退出了bash shell也就是等于杀死了容器。我们使用 Crtl-PQ组合键来退出容器但不会终止容器运行,然后我们使用docker container ls命令来观察当前系统中正在运行的容器列表。

    [pangcm@docker01 ~]$ docker container ls 
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    cdea83d65909        ubuntu              "/bin/bash"         6 minutes ago       Up 6 minutes                            adoring_banach
    

    可以看到该容器还在运行中,使用docker container exec 命令可以重新连接到Docker终端。

    [pangcm@docker01 ~]$ docker container exec -it cde bash
    root@cdea83d65909:/#
    

    这时候容器运行了两个bash 进程,我们这时候退出bash容器并不会终止,原因是还有一个bash shell在运行着。退出后,通过命令 docker container ps 可以确认容器是否在运行中。

    我们使用docker container stop 和 docker container rm 命令来停止和删除容器。

    docker stop cde
    docker rm cde
    

    7.2.4 容器的生命周期

    坊间流传容器不能持久化数据,其实容器是可以做到的。人们认为容器不擅长持久化工作或者持久化数据,很大程度是因为容器在非持久化方面表现得太出色。我们来做一个演示,首先我们先启动一个ubuntu的容器。

    #1.启动一个ubuntu容器,并且命名为test1
    docker container run --name test1 -it ubuntu /bin/bash
    
    #2.在容器中写入部分数据到tmp目录下
    echo "I will stay here" >/tmp/test.log
    
    #3.使用Crtl-PQ命令退出容器,然后停止容器
    docker container stop test1
    
    #4. 确认容器是不是exited的状态了
    docker container ls -a 
    
    #5.重新启动test1容器
    docker container start test1
    
    #6.连接到容器中,然后查看tmp目录下的那个文件是否还存在
    docker container exec -it test1 bash
    cat /tmp/test.log
    

    可以看到在一开始创建的人家还是保留在容器里面的,这就证明了容器时可以持久化数据的。但是说到持久化,卷(volume)才是容器中存储持久化数据的首选方式。

    我们在演示这个例子的同时也看到了容器的生命周期是怎样的。可以根据需要多次启动、停止容器,这些执行都很快。容器及其数据是安全的,直到删除容器前,容器都不会丢失其中的数据。

    7.2.5 优雅地停止容器

    前面我们使用docker container stop 来停止容器,该命令会给容器内进程发送将要停止的告警信息,给进程机会来有序处理停止前要做的事情。然后就可以使用docker container rm 命令来删除容器了。但是如果我们在删除容器的命令上加上 -f 参数,那就要暴力多了。这会直接销毁运行中的容器,不会发出任何警告。

    这背后的原理可以通过Linux的信号来解析,docker container stop 命令向容器内的PID 1进程发送了SIGTERM这样的信号。就像前文提到的一样,会为进程预留一个清理并优雅停止的机会。如果10s内进程没有终止,那么就会收到SIGKILL信号。这是致命一击,但是进程起码有10s的时间来“解决” 自己。而 docker container rm -f 就是直接发出SIGKILL的信号,直接杀掉容器。

    7.2.6 利用重启策略进行容器的自我修复

    通常建议在运行容器时配置好重启策略。这是容器的一种自我修复能力,可以在指定事件或者错误后重启来完成自我修复。重启策略应用于每个容器,可以作为参数被强制传入 docker container run 命令中,或者在Compose文件中声明。

    重启策略包括 always、unless-stoped 和 on-failed 三种。

    always策略时最简单的方式,除非容器被明确停止,比如通过docker container stop 命令,否则该策略会一直尝试重启处于停止状态的容器。一种简单的证明方式是启动一个新的交互式容器,并在后面指定 --restart always 策略,同时在命令中指定运行的Shell进程。当容器退出交互式容器的时候,容器会被杀死。但是因为指定了 --restart always 策略,所以容器会自动重启。如果运行docker container ls 命令,就会看到容器的启动时间小于创建时间。

    [pangcm@docker01 ~]$ docker container run -it  --restart always ubuntu /bin/bash
    root@881a478cae9a:/# exit 
    exit
    [pangcm@docker01 ~]$ docker container ls 
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    881a478cae9a        ubuntu              "/bin/bash"         22 seconds ago      Up 12 seconds                           cool_mestorf
    

    可以看到容器是在22秒之前创建的,但是却在12秒前才启动。这是因为在容器中输入退出命令的时候,容器被杀死,然后Docker又重新启动了该容器。

    --restart always 策略有个比较有趣的特征,就是当daemon重启的时候,停止的容器也会被重启。

    always和unless-stopped 的最大区别,就是那些指定了 --restart unless-stopped并处于Stopped(Exited)状态的容器,不会在Docker daemon重启的时候被重启。

    on-failure 策略会在退出容器并且返回值不是0的时候,重启容器。就算容器处于stopped状态,Docker daemon重启的时候,容器也会被重启。

    7.2.7 Web服务器示例

    上面已经介绍了如何启动一个简单的容器,并与其交互。同时也知道了如何停止、重启以及删除一个容器。现在来看一个Linux Web服务器示例。该示例会在80端口上启动一个简单的web服务。

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

    该命令会在你的Dokcer 主机上启动一个nginx容器,因为使用了 -d 参数,所以并不会进入容器的shell里面去。 -d 表示后台模式,告知容器在后台运行。此外,这里还指定了 -p 参数,p 参数将Dokcer主机的短裤映射到容器内。可以想下你访问web服务的顺序是先到Docker然后再转到Docker容器中的。所以前面的那个端口是Dokcer主机的端口。

    现在容器已经在运行中了,你可以通过命令查看,或者用浏览器打开这个Docker主机的80端口查看。

    [pangcm@docker01 ~]$ docker container ls 
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
    6134f2808424        nginx               "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        0.0.0.0:80->80/tcp   webserver
    

    7.2.8 查看容器详情

    前面我们一直使用 docker container ls 命令去查看容器的运行情况,这样显示的信息非常少。下面我们使用docker image inspect 来查看容器运行的详情。

    docker image inspect nginx
    ...
            "Config": {
                "Hostname": "",
                "Domainname": "",
                "User": "",
                "AttachStdin": false,
                "AttachStdout": false,
                "AttachStderr": false,
                "ExposedPorts": {
                    "80/tcp": {}
                },
                "Tty": false,
                "OpenStdin": false,
                "StdinOnce": false,
                "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                    "NGINX_VERSION=1.17.5",
                    "NJS_VERSION=0.3.6",
                    "PKG_RELEASE=1~buster"
                ],
                "Cmd": [
                    "nginx",
                    "-g",
                    "daemon off;"
                ]
    ...
    

    这里截取了其中的一部分,其中Cmd那部分展示了容器将会执行的命令或应用。在构建镜像的时候指定默认命令是一种很普遍的做法,因为这样可以简化容器的启动。这也为镜像指定了默认的行为,并且从侧面阐述了镜像的用途——可以通过inspect镜像的方式来了解所要运行的镜像。

    此外,还有 docker container inspect的命令,会展示非常多的信息,有兴趣可以查看下。

    7.3 容器——命令

    • docker container run 是启动新容器的命令
    • docker container ls 用于列出所有在运行状态的容器,如果加上 -a 还可以看到处于停止状态的容器
    • docker container exec 运行用户正在运行状态的容器中,启动一个新的进程。
    • docker container stop 命令会停止运行中的容器,并将其状态置为 Exited(0)。
    • docker container start 会重启处于停止状态的容器,可以使用容器的名称或者ID
    • docker container rm 会删除停止运行的容器。
    • docker container inspect 命令会显示容器的配置细节和运行时信息。

    7.4 本章小结

    在本章中,对容器和虚拟机两种模型进行了比较。其中重点关注了虚拟机模型的缺点,并且分析了虚拟机模型相对于物理机模型的巨大优势,以及容器模型如果能带来更加显著的提升。

    本章还介绍了如何使用 docker container run 命令启动一组简单的容器,并且对比了前台和后台运行容器在交互方面的差异性。

    此外还了解了杀死容器中PID为1的进程会杀死容器。同时还了解了如何启动、停止以及删除容器。

    在本章最后介绍了docker container inspect 命令,该命令可以查看容器元数据的细节信息。

    相关文章

      网友评论

          本文标题:第7章 Docker容器

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