深入浅出Docker 读书笔记(三)

   第7章:Docker容器

容器是镜像运行的实例。单个镜像可以启动一个或多个容器,容器共享其所在主机的操作系统/内核。启动容器的简便方式是使用docker container run命令。该命令可以携带很多参数,在其基础的格式docker container run <image> <app>中,指定了启动所需的镜像以及要运行的应用。docker container run -it ubuntu /bin/bash则会启动某个 Ubuntu Linux容器,并运行 Bash Shell作为其应用。-it 参数可以将当前终端连接到容器的 Shell 终端之上。容器随着其中运行应用的退出而终止。可以使用 docker container stop 命令手动停止容器运行,并且使用 docker container start 再次启动该容器。如果再也不需要该容器,则使用 docker container rm 命令来删除容器。

虚拟机与容器的对比:两者都需要依赖于宿主机才能运行,以虚拟机与容器运行四个服务做对比来比较差异,在虚拟机模型中,首先要开启物理机并启动 Hypervisor 引导程序。一旦 Hypervisor 启动,就会占有机器上的全部物理资源,如 CPU、RAM、存储和 NICHypervisor 接下来就会将这些物理资源划分为虚拟资源,并且看起来与真实物理资源完全一致。然后 Hypervisor 会将这些资源打包进一个叫作虚拟机(VM)的软件结构当中。这样用户就可以使用这些虚拟机,并在其中安装操作系统和应用。
运行 4 个应用,要在 Hypervisor 之上需要创建 4 个虚拟机并安装 4 个操作系统,然后安装 4 个应用。当操作完成后,结构如下图所示。

运行4个业务应用的物理服务器

而容器模型则略有不同。服务器启动之后,所选择的操作系统会启动。在 Docker 世界中可以选择 Linux,或者内核支持内核中的容器原语的新版本 Windows。与虚拟机模型相同,OS 也占用了全部硬件资源。在 OS 层之上,需要安装容器引擎(如 Docker)。容器引擎可以获取系统资源,比如进程树、文件系统以及网络栈,接着将资源分割为安全的互相隔离的资源结构,称之为容器。每个容器看起来就像一个真实的操作系统,在其内部可以运行应用。按照前面的假设,需要在物理机上运行 4 个应用。因此,需要划分出 4 个容器并在每个容器中运行一个应用,如下图所示。

划分4个容器

从更高层面上来讲,Hypervisor 是硬件虚拟化(Hardware Virtualization)——Hypervisor 将硬件物理资源划分为虚拟资源。容器是操作系统虚拟化(OS Virtualization)——容器将系统资源划分为虚拟资源。从中就可以看出容器比虚拟机所带来的优势,1)消耗低安全性高:因为虚拟机模型将底层硬件资源划分到虚拟机当中。每个虚拟机都是包含了虚拟 CPU、虚拟 RAM、虚拟磁盘等资源的一种软件结构。操作系统本身是有其额外开销的。操作作系统都需要独立的许可证,并且都需要打补丁升级,每个操作系统也都面临被攻击的风险。通常将这种现象称作 OS Tax 或者 VM Tax,每个操作系统都占用一定的资源。容器模型具有在宿主机操作系统中运行的单个内核。这意味着只有一个操作系统消耗 CPU、RAM 和存储资源,授权、升级和打补丁,只有一个操作系统面临被攻击的风险。简言之,就是只有一份 OS 损耗。2)启动快:因为容器并不是完整的操作系统,所以其启动要远比虚拟机快。在容器内部并不需要内核,也就没有定位、解压以及初始化的过程——更不用提在内核启动过程中对硬件的遍历和初始化了。这些在容器启动的过程中统统都不需要!唯一需要的是位于下层操作系统的共享内核是启动了的,唯一对容器启动时间有影响的就是容器内应用启动所花费的时间。

通常登录 Docker 主机后的第一件事情是检查 Docker 是否正在运行。$ docker version 当命令输出中包含 Client 和 Server 的内容时,可以继续下面的操作。如果在 Server 部分中包含了错误码,这表示 Docker daemon 很可能没有运行,或者当前用户没有权限访问。如果在 Linux 中遇到无权限访问的问题,需要确认当前用户是否属于本地 Docker UNIX 组。如果不是,可以通过usermod -aG docker <user>来添加,然后退出并重新登录 Shell,改动即可生效。可以通过一下命令检查 Docker daemon 的状态。//使用 Systemd 在 Linux 系统中执行该命令 $ service docker status    /或 $ systemctl is-active docker     //在Windows Server 2016的PowerShell窗口中运行该命令 > Get-Service docker

启动容器的一个简单的方式是通过 docker container run 命令。如果通过输入 exit 退出 Bash Shell,那么容器也会退出(终止)。原因是容器如果不运行任何进程则无法存在,杀死 Bash Shell 即杀死了容器唯一运行的进程,导致这个容器也被杀死。这对于 Windows 容器来说也是一样的,杀死容器中的主进程,则容器也会被杀死。按下 Ctrl-PQ 组合键则会退出容器但并不终止容器运行。这样做会切回到 Docker 主机的 Shell,并保持容器在后台运行。可以使用 docker container ls 命令来观察当前系统正在运行的容器列表。当前容器仍然在运行,并且可以通过 docker container exec 命令将终端重新连接到 Docker,理解这一点很重要。$ docker container exec -it 3027eb644874 bash 。   用于重连 Windows Nano Server PowerShell 容器的命令是 docker container exec -it <container-name-or-ID> pwsh.exe
       容器的生命周期:创建、运行、休眠,销毁,在这四个过程中会介绍容器数据的持久化,书中是先启动一个容器并将一部分数据写入其中。按 Ctrl-PQ 组合键退出当前容器。现在使用 docker container stop 命令来停止容器运行,切换到暂停(vacation)状态。$ docker container stop percy  可以在 docker container stop 命令中指定容器的名称或者 ID。具体格式为: docker container stop <container-id or container-name>   停止容器就像停止虚拟机一样。尽管已经停止运行,容器的全部配置和内容仍然保存在 Docker 主机的文件系统之中,并且随时可以重新启动。使用 docker container start 命令可以将容器重新启动。使用 docker container exec 命令连接到重启后的容器。确认之前创建的文件依然存在,并且文件中仍包含之前写入的数据。尽管上面的示例阐明了容器的持久化特性,还是需要指出卷(volume)才是在容器中存储持久化数据的首选方式。现在停止该容器并从系统中删除它。通过在 docker container rm 命令后面添加 -f 参数来一次性删除运行中的容器是可行的。但是,删除容器的最佳方式还是分两步,先停止容器然后删除。这样可以给容器中运行的应用/进程一个停止运行并清理残留数据的机会。因为docker container stop 命令给容器内进程发送将要停止的警告信息,给进程机会来有序处理停止前要做的事情。一旦 docker stop 命令返回后,就可以使用 docker container rm 命令删除容器了。这背后的原理可以通过 Linux/POSIX 信号来解释。docker container stop 命令向容器内的 PID 1 进程发送了 SIGTERM 这样的信号。如果 10s 内进程没有终止,那么就会收到 SIGKILL 信号。这是致命一击。但是,进程起码有 10s 的时间来“解决”自己。docker container rm <container> -f 命令不会先友好地发送 SIGTERM,这条命令会直接发出 SIGKILL。就像刚刚所打的比方一样,该命令悄悄接近并对容器发起致命一击。所以通常建议在运行容器时配置好重启策略。这是容器的一种自我修复能力,可以在指定事件或者错误后重启来完成自我修复。重启策略应用于每个容器,可以作为参数被强制传入 docker-container run 命令中,或者在 Compose 文件中声明(在使用 Docker Compose 以及 Docker Stacks 的情况下)。容器支持的重启策略包括 always、unless-stopped 和 on-failed。

快速清理:简单快速并且强制删除所用容器的清理 Docker 主机上全部运行容器的方法。这种操作一定不能在生产环境系统或者运行着重要容器的系统上执行。在 Docker 主机的 Shell 中运行下面的命令,可以删除全部容器。$docker container rm $(docker container ls -aq) -f     果将 $(docker container ls -aq) 作为参数传递给 docker container rm 命令,等价于将系统中每个容器的 ID 传给该命令。-f 标识表示强制执行,所以即使是处于运行状态的容器也会被删除。接下来,无论是运行中还是停止的容器,都会被删除并从系统中移除。

 第8章:应用容器化

将应用整合到容器中并且运行起来的这个过程,称为“容器化”(Containerizing),有时也叫作“Docker化”(Dockerizing)。完整的应用容器化过程主要分为以下几个步骤。

  • 编写应用代码。
  • 创建一个 Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用。
  • 对该 Dockerfile 执行 docker image build 命令。
  • 等待 Docker 将应用程序构建到 Docker 镜像中。

下图展示了上述步骤。
 

容器化的基本过程

Dockerfile 的文件。这个文件包含了对当前应用的描述,并且能指导 Docker 完成镜像的构建。在 Docker 当中,包含应用文件的目录通常被称为构建上下文(Build Context)。通常将 Dockerfile 放到构建上下文的根目录下。另外很重要的一点是,文件开头字母是大写 D,这里是一个单词。像“dockerfile”或者“Docker file”这种写法都是不允许的。


下面是这个文件中的一些关键步骤概述:以 alpine 镜像作为当前镜像基础,指定维护者(maintainer)为“[email protected]”,安装 Node.js 和 NPM,将应用的代码复制到镜像当中,设置新的工作目录,安装依赖包,记录应用的网络端口,最后将 app.js 设置为默认运行的应用。

$ cat Dockerfile
FROM alpine
LABEL maintainer="[email protected]"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
FROM 指令指定的镜像,会作为当前镜像的一个基础镜像层,当前应用的剩余内容会作为新增镜像层添加到基础镜像层之上

接下来,Dockerfile 中通过标签(LABLE)方式指定了当前镜像的维护者为“nigelpoulton@hotmail. com”。每个标签其实是一个键值对(Key-Value),在一个镜像当中可以通过增加标签的方式来为镜像添加自定义元数据。
RUN apk add --update nodejs nodejs-npm 指令使用 alpine 的 apk 包管理器将 nodejs 和 nodejs-npm 安装到当前镜像之中。RUN 指令会在 FROM 指定的 alpine 基础镜像之上,新建一个镜像层来存储这些安装内容。COPY. / src 指令将应用相关文件从构建上下文复制到了当前镜像中,并且新建一个镜像层来存储。COPY 执行结束之后,当前镜像共包含 3 层,下一步,Dockerfile 通过 WORKDIR 指令,为 Dockerfile 中尚未执行的指令设置工作目录。该目录与镜像相关,并且会作为元数据记录到镜像配置中,但不会创建新的镜像层。然后,RUN npm install 指令会根据 package.json 中的配置信息,使用 npm 来安装当前应用的相关依赖包。npm 命令会在前文设置的工作目录中执行,并且在镜像中新建镜像层来保存相应的依赖文件。目前镜像一共包含 4 层,如下图所示。

当前的4层镜像

因为当前应用需要通过 TCP 端口 8080 对外提供一个 Web 服务,所以在 Dockerfile 中通过 EXPOSE 8080 指令来完成相应端口的设置。这个配置信息会作为镜像的元数据被保存下来,并不会产生新的镜像层。最终,通过 ENTRYPOINT 指令来指定当前镜像的入口程序。ENTRYPOINT 指定的配置信息也是通过镜像元数据的形式保存下来,而不是新增镜像层。打出来的镜像最好推送到仓库,推送 Docker 镜像之前,还需要为镜像打标签。这是因为 Docker 在镜像推送的过程中需要如下信息。1)Registry(镜像仓库服务)。2)Repository(镜像仓库)。3)Tag(镜像标签)。无须为 Registry 和 Tag 指定值。当没有为上述信息指定具体值的时候,Docker 会默认Registry=docker.io、Tag=latest。但是 Docker 并没有给 Repository 提供默认值,而是从被推送镜像中的 REPOSITORY 属性值获取。

发布了105 篇原创文章 · 获赞 86 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/dingyahui123/article/details/104269209