【Docker】进阶之路:(八)应用的容器化

【Docker】进阶之路:(八)应用的容器化


Docker的核心思想就是将应用整合到容器中,并且能在容器中实际运行。将应用整合到容器中并且运行起来的这个过程称为“容器化”(Containerizing),有时也叫作“Docker化”(Dockerizing)。容器为应用提供了运行环境,容器能够简化应用的构建、部署和运行过程。

应用容器化简介

应用的容器化是指将应用整合到容器中并且运行起来的过程。通过应用容器化,可以简化应用的构建、部署和运行。使用容器化技术也可以让应用程序向云环境的部署变得更为高效。就像容器本身一样,运行容器的操作系统也能够被瘦身。因为容器已经持有应用程序运行所需的大部分依赖,所以这些用于容器的新型宿主机操作系统就不再需要包含所有依赖了。

Docker提供了一种创建和运行已经在容器中完成配置的应用程序的方法。需要了解容器化应用的几个相关知识点如下。

  • 容器化应用不是直接在宿主机上运行的应用
    • 运行应用程序的传统方法会将应用程序直接安装到宿主机的文件系统上,并在宿主机上运行。从应用程序的视角来看,它的环境包含宿主机的进程表、文件系统、IPC设施、网络接口、端口及设备。要让应用程序运行起来,通常需要安装与应用程序搭配的辅助软件包。一般来说,这不是问题,但在有些情况下,例如想在同一个系统上运行相同软件包的不同版本,就可能会引起相关冲突。
    • 应用程序与应用程序之间也会以某种方式发生冲突。如果应用程序是服务,它可能会默认绑定特定的网络端口。在服务启动时,它可能还会读取公共配置文件。这会导致无法在同一宿主机上运行该服务的多个实例,或者至少非常棘手;同时,还会让那些想要绑定到同一端口的其他服务难以运行。
    • 直接在宿主机上运行应用程序还有一个缺点,那就是难以迁移应用程序。如果宿主机需要关机或者应用程序需要更多的计算能力——超出宿主机所能提供的,那么从宿主机上获取所有依赖并将它迁移到另一台宿主机上绝非易事。
  • 容器化应用不是直接在虚拟机上运行的应用
    • 创建虚拟机来运行应用程序能够克服直接在宿主机操作系统上运行应用程序所具有的缺点。虽然虚拟机位于宿主机上,但它作为独立的操作系统运行,包括自己的内核、文件系统、网络接口等。这样可以很容易地将几乎所有的东西都保存在独立于宿主机的操作系统中。
    • 因为虚拟机是独立的实体,所以不会出现直接在硬件上运行应用程序所产生的缺乏灵活性的弊端。可以在宿主机上启动10个不同的虚拟机来运行应用程序10次。虽然每个虚拟机上的服务监听了同一个端口号,但是因为每个虚拟机拥有不同的IP地址,所以并不会引起冲突。
    • 同样地,如果需要关闭宿主机,可以将虚拟机迁移到其他宿主机上(如果虚拟化环境支持迁移),或者直接关闭虚拟机并在新宿主机上再次启动它。
    • 一个虚拟机运行一个应用程序实例的缺点是耗费资源。应用程序可能只需要几兆字节磁盘空间来运行,但是整个虚拟机却要耗费许多吉字节的空间。再者,虚拟机的启动时间和CPU占用肯定会比应用程序自身所消耗的高得多。
      容器提供了另一种在宿主机上或虚拟机内直接运行应用程序的方式,这种方式能使应用程序更快、可移植性更好,并且更具有可扩展性。
    • 完整的应用容器化过程主要分为以下几个步骤:
      • 编写应用代码
      • 创建Dockerfile文件,其中包括对当前应用的描述、依赖以及该如何运行当前应用。
      • 对该Dockerfile执行docker image build命令。
      • 等待Docker将应用程序构建到Docker镜像中。

一旦应用容器化完成,即应用被打包为一个Docker镜像,就能以镜像的形式交付并以容器的方
式运行了。

单体应用容器化

通过一个简单的示例来讲解单体应用容器化。具体步骤如下:

  1. 获取应用代码
  2. 分析Dockerfile
  3. 构建应用镜像
  4. 运行应用
  5. 测试应用
  6. 容器应用化细节

首先,创建一个名为psweb的文件夹:

[root@docker psweb]# ll
总用量 8
-rw-r--r--. 1 root root 260 129 21:48 app.js
-rw-r--r--. 1 root root 241 129 21:54 Dockerfile
drwxr-xr-x. 2 root root  23 129 21:54 views
[root@docker psweb]# 

该目录包含应用的代码,以及界面和单元测试的子目录。
接下来,对Dockerfile文件进行分析。在Docker中,包含应用文件的目录被称为构建上下文。
查看Dockerfile文件内容:

[root@docker psweb]# cat Dockerfile 
#Linux x64
FROM ubunte # FROM指令指定镜像,作为当前镜像的一个基础镜像层。
LABEL maintainer="[email protected]" # 通过标签LABEL指定当前镜像的维护者。
#安装Node和NPM
RUN apt-get update
RUN apt install -y iproute2
## 复制到/src文件夹
COPY . /usr/share/nginx/html
WORKDIR /usr/share/nginx/html # Dockerile通过WORKDIR指令为Dockerfile中尚未执行的指令设置工作目录。
#安装依赖
EXPOSE 80 # 指令用来完成相应端口的设置,这个配置信息会作为镜像的元数据被保存下来。
ENTRYPOINT ["tail","/var/log/messages"] # ENTRYPOINT指令来指定当前镜像的入口程序。
[root@docker psweb]# 

使用如下命令,生产一个名为web:latest的镜像,命令中最后的“.”表示Docker在进行构建的
时候,使用当前目录作为构建的上下文:

[root@docker psweb]# docker image build -t web:latest .
[+] Building 51.4s (10/10) FINISHED                                                                                                                                                                                               docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                                                                        0.0s
 => => transferring dockerfile: 349B                                                                                                                                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                                                             0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                                                                                                                                           16.2s
 => [1/5] FROM docker.io/library/ubuntu@sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322                                                                                                                             0.0s
 => => resolve docker.io/library/ubuntu@sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322                                                                                                                             0.0s
 => => sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322 1.42kB / 1.42kB                                                                                                                                              0.0s
 => => sha256:7cc0576c7c0ec2384de5cbf245f41567e922aab1b075f3e8ad565f508032df17 529B / 529B                                                                                                                                                  0.0s
 => => sha256:ba6acccedd2923aee4c2acc6a23780b14ed4b8a5fa4e14e252a23b846df9b6c1 1.46kB / 1.46kB                                                                                                                                              0.0s
 => [internal] load build context                                                                                                                                                                                                           0.0s
 => => transferring context: 1.05kB                                                                                                                                                                                                         0.0s
 => [2/5] RUN apt-get update                                                                                                                                                                                                               28.5s
 => [3/5] RUN apt install -y iproute2                                                                                                                                                                                                       6.3s
 => [4/5] COPY . /usr/share/nginx/html                                                                                                                                                                                                      0.0s 
 => [5/5] WORKDIR /usr/share/nginx/html                                                                                                                                                                                                     0.0s 
 => exporting to image                                                                                                                                                                                                                      0.2s 
 => => exporting layers                                                                                                                                                                                                                     0.2s 
 => => writing image sha256:13caff8d279d06f4e50c6b62715f2310b94c1907a15a38e906e9754c2473b1af                                                                                                                                                0.0s 
 => => naming to docker.io/library/web:latest                                                                                                                                                                                               0.0s 
[root@docker psweb]# 

查看构建的Web镜像:

[root@docker psweb]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
web          latest    13caff8d279d   32 seconds ago   125MB
nginx        latest    605c77e624dd   23 months ago    141MB
[root@docker psweb]# 

创建一个镜像之后,可以将该镜像保存到镜像仓库中。登录Docker Hub:

[root@docker psweb]# docker login
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@docker psweb]# 

Docker在镜像推送中需要三个信息:

  • Registry:镜像仓库服务。
  • Repository:镜像仓库。
  • Tag:镜像标签。
    Docker默认Registry=docker.os、Tag=latest,而Repositiry没有默认值。使用如下命令为镜像重新打标签:
Error response from daemon: No such image: web:lstest
[root@docker psweb]# docker image tag web:latest circledba/kingbase-es:latest
[root@docker psweb]# docker image ls
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
circledba/kingbase-es   latest    13caff8d279d   3 minutes ago   125MB
web                     latest    13caff8d279d   3 minutes ago   125MB
nginx                   latest    605c77e624dd   23 months ago   141MB
[root@docker psweb]# 

将镜像推送到Docker Hub上:

[root@docker psweb]# docker image push circledba/kingbase-es:latest
The push refers to repository [docker.io/circledba/kingbase-es]
5f70bf18a086: Mounted from lorel/docker-stress-ng 
5a90477add8e: Pushed 
a26176de28a1: Pushed 
6acebeb09837: Pushed 
9f54eef41275: Layer already exists 
latest: digest: sha256:c10bf378a09908323cf5e4c033555fc79dd208a6febae06ad57efc0fe34fc367 size: 1365
[root@docker psweb]# 

生成环境中的多阶段构建

Dockerfile是一个用来构建镜像的文本文件,它包含了一条条指令,大部分指令都对应构建镜像。在镜像构建时需要基于一个基础镜像,这个基础镜像通过FROM指令来引入。
当镜像构建过程需要依赖多个基础镜像时,我们没办法在FROM指令中加入多个基础镜像来实现,事实上也没必要在构建的镜像中包含多个基础镜像,因为除了运行时环境基础镜像,其他都只是为编译、打包之类的中间过程服务,在运行时不再需要,因此没必要添加在镜像中增大镜像的大小。此时我们可以使用Dockerfile的多阶段构建来实现。

所谓多阶段构建,就是允许一个Dockerfile中出现多条FROM指令,但只有最后一条FROM指令中的基础镜像作为本次构建镜像的基础镜像,其他均只为中间过程服务。每一条FROM指令都表示一个构建阶段,多条FROM指令就表示多阶段构建,后面的构建阶段可以复制前面构建阶段产生的文件——这也是多阶段构建的意义所在。多阶段构建适用于编译、打包与运行环境分离的场景。通常来说,构建一个XXApi镜像包括以下三步:

  1. 下载XXApi源码。
  2. 安装依赖,编译。
  3. 将编译好的文件夹复制到镜像目录。

在步骤1、2中,需要wget、Python、make等工具来完成,这些工具对XXApi程序的运行是非必需的。所以这时可以有两种选择,一种是先在宿主机中完成步骤1和2,然后只在Dockerfile中执行步骤3;另一种是将步骤1、2、3都放在Dockerfile中执行,在编译完成后,再执行安装工具的清理工作。
两种选择对应的两个问题分别是:

  • 宿主机中可能不存在需要的工具或依赖,安装起来很麻烦,且需要手动(或配置自动化程序)执行。
  • 程序运行的环境可能跟编译打包的环境要求完全不一样,如Go语言程序,编译需要Golang环境,而运行则不需要。

采用多阶段构建则可以比较完美地解决以上问题。构建XXApi镜像的Dockerfile文件内容如下:

FROM node:12-alpine as builder
WORKDIR /xxapi
RUN apk add --no-cache wget python make
ENV VERSION=1.9.2
RUN wget https://github.com/YMFE/xxapi/archive/refs/tags/vS(VERSION).zip
RUN unzip vS(VERSION).zip &b mv xxapi-S(VERSION)vendors
RUN cd /xxapi/vendors sb npm install --production --registry
https://registry.npm.taobao.org
FROM node:12-alpine
MAINTAINER ronwxY
ENV TZ="Asia/Shanghai"
WORKDIR /xxapi/vendors
COPY --from=builder /xxapi/vendors /xxapi/vendors
RUN mv /xxapi/vendors/config_example.json /xxapi/config.json
EXPOSE 3000
ENTRYPOINT["node"]

在该Dockerfile文件中,两条FROM指令代表两个阶段的构建,第一阶段完成步骤1和2,在第二阶段中可以直接使用第一阶段生成的内容。指令如下:

COPY --from=builder /xxapi/vendors /xxapi/vendors

表示从命名为builder的阶段中复制/xxapilvendors到当前阶段中。也可以使用以下指令:

--from=n

表示从第几个阶段中复制内容,n从0开始计算。
COPY–from除了能从前面的构建阶段中复制内容外,还能直接从已经存在的镜像中复制,这在我们需要依赖其他已存在镜像中的某些内容时非常方便和实用。

常用的命令

在Dockerfle中,指令可以分为以下两种:

  • 新增镜像的指令:FROM、RUN、COPY。
  • 新增元数据的指令:EXPOSE、WORKDIR、ENV、ENTERPOINT。

Dockerfile中的这些指令的作用如下:
● FROM指令:指定构建镜像的一个基础层。
● RUN指令:在镜像中执行命令,创建新的镜像层。
● COPY指令:将文件作为新的层添加到镜像中。
● EXPOSR指令:记录应用所使用的的网络端口。
● ENTRYPOINT指令:指定镜像以容器的方式启动后默认的运行程序。

应用容器化常用的命令为:

docker image build

此命令用于构建镜像,读取Dockerfile文件,将应用程序容器化。其常用指令选项说明如下:

  • –no-cache:默认为false。设置该指令,将不使用Build Cache构建镜像。
  • -pull:默认为false。设置该指令,总是尝试拉取镜像的最新版本。
  • -compress:默认为false。设置该指令,将使用gzip压缩构建的上下文。
  • –disable-content-trust:默认为true。设置该指令,将对镜像进行验证。
  • –file,-f:Dockerfile的完整路径,默认值为PATH/Dockerfile’。
  • -isolation:默认-isolation=“default”,即Linux命名空间。其他还有process或hyperv。
  • –label:为生成的镜像设置元数据。
  • squash:默认为false。设置该指令,将新构建出的多个层压缩为一个新层,但是将无法在多个镜像之间共享新层。设置该指令,实际上是创建了新镜像,同时保留原有镜像。
  • –tag,-t:镜像的名字及tag,通常为name:tag或者name格式;可以在一次构建中为一个镜像设置多个 tag
  • -network:默认为 default。设置该指令,在构建期间为运行指令设置网络模式。
  • –quiet,-q:默认为 false 。设置该指令,仅输出镜像 ID。
  • –force-m: 默认为 false 。设置该指令,总是删除中间环节的容器。
  • –rm: 默认–rm=true, 即整个构建过程成功后删除中间环节的容器。

猜你喜欢

转载自blog.csdn.net/sinat_36528886/article/details/134901023