根据 freecodecamp 中ZhichengChen写的文章的学习整理(https://chinese.freecodecamp.org/news/the-docker-handbook/)

github地址:https://github.com/fhsinchy/docker-handbook-projects/

Docker简介

容器化可以将软件及其所有依赖项打包在一个自包含的软件包中,这样就可以省略麻烦的配置,直接运行。

Docker核心概念:镜像是一个只读的模板,类似于安装系统时用到的ISO文件,通过镜像来完成各种应用的部署。容器可以被启动、开始、停止、删除等,每个容器间都是相互隔离的。仓库是一个存放镜像的场所,仓库分为公共仓库和私有仓库。最大的公共仓库是Docker hub(hub.docker.com)。注意镜像类似于操作系统,容器类似于虚拟机本身。

安装Docker:Docker支持在众多平台上安装和运行,如在Mac、Linux、Windows和一些云服务器上(亚马逊云、微软云、IBM 云、阿里云、腾讯云、Google 云等)。如图10-2所示的界面是Docker安装文档页面,文档网址是https://docs.docker.com/engine/install/binaries/#install-daemon-and-client-binaries-on-linux

CentOS 7系统安装Docker

使用存储库安装:在新主机上首次安装 Docker Engine 之前,您需要设置 Docker 存储库。之后,您可以从存储库安装和更新 Docker。

设置存储库:安装yum-utils包(提供yum-config-manager 实用程序)并设置稳定存储库。

 sudo yum install -y yum-utilssudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo

安装 Docker 引擎:安装最新版本的 Docker Engine 和 containerd,或者进入下一步安装特定版本:

 sudo yum install docker-ce docker-ce-cli containerd.io

如果提示接受 GPG 密钥,请验证指纹是否匹配060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35,如果匹配 ,请接受。

要安装特定版本的 Docker Engine,请在 repo 中列出可用版本,然后选择并安装:
一种。列出并排序您的存储库中可用的版本。此示例按版本号对结果进行排序,从高到低,并被截断:

 yum list docker-ce --showduplicates | sort -r

返回的列表取决于启用了哪些存储库,并且特定于您的 CentOS 版本(.el7在本示例中由后缀表示)。

通过完全限定的包名称安装特定版本,即包名称 ( docker-ce) 加上从第一个冒号 ( :)开始的版本字符串(第 2 列),直到第一个连字符,由连字符 ( -)分隔。例如,docker-ce-18.09.1。

 sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

Docker 已安装但未启动。该docker组被创建,但没有用户添加到组。

配置 Docker 开机启动

 sudo systemctl enable docker.servicesudo systemctl enable containerd.service

Docker 基本知识

什么是容器
可以认为容器是下一代虚拟机。容器和虚拟机实际上是虚拟化物理硬件的不同方法。两者之间的主要区别是虚拟化方式。消除了整个主机操作系统层,因此与传统的虚拟机相比,容器的更轻量,资源占用更少。

什么是 Docker 镜像
镜像是分层的自包含文件,充当创建容器的模板。它们就像容器的冻结只读副本。 镜像可以通过仓库进行共享。容器只是处于运行状态的镜像。当从互联网上获取镜像并使用该镜像运行容器时,实际上是在先前的只读层之上创建了另一个临时可写层,镜像是分层只读文件,其中保留着应用程序所需的状态。

什么是仓库
已经了解了两个非常重要的部分,即 Containers 和 Images 。 最后一个是 Registry。

镜像仓库是一个集中式的位置,可以在其中上传镜像,也可以下载其他人创建的镜像。 Docker Hub 是 Docker 的默认公共仓库。

Docker 架构概述

该引擎包括三个主要组件:

Docker 守护程序: 守护程序(dockerd)是一个始终在后台运行并等待来自客户端的命令的进程。守护程序能够管理各种 Docker 对象。
Docker 客户端: 客户端(docker)是一个命令行界面程序,主要负责传输用户发出的命令。
REST API: REST API 充当守护程序和客户端之间的桥梁。使用客户端发出的任何命令都将通过 API 传递,最终到达守护程序。


运行 docker run hello-world 命令时实际发生的情况:

1、执行 docker run hello-world 命令,其中 hello-world 是镜像的名称。
2、Docker 客户端访问守护程序,告诉它获取 hello-world 镜像并从中运行一个容器。
3、Docker 守护程序在本地仓库中查找镜像,并发现它不存在,所以在终端上打印 Unable to find image 'hello-world:latest' locally。
3、然后,守护程序访问默认的公共仓库 Docker Hub,拉取 hello-world 镜像的最新副本,并在命令行中展示 4、Unable to find image 'hello-world:latest' locally。
5、Docker 守护程序根据新拉取的镜像创建一个新容器。
6、最后,Docker 守护程序运行使用 hello-world 镜像创建的容器,该镜像在终端上输出文本。

Docker 容器操作基础知识

怎样运行容器

docker run <image name>

在版本 1.13 之前,Docker 仅具有前面提到的命令语法。后来,命令行经过了重构具有了以下语法:

docker <object> <command> <options>
使用以下语法:object 表示将要操作的 Docker 对象的类型。这可以是 container、image、network 或者 volume 对象。
command 表示守护程序要执行的任务,即 run 命令。
options 可以是任何可以覆盖命令默认行为的有效参数,例如端口映射的 --publish 选项。

现在,遵循此语法,可以将 run 命令编写如下:

docker container run <image name>

image name 可以是在线仓库或本地系统中的任何镜像。这里尝试使用参考文件的作者的fhsinchy / hello-dock 镜像运行容器。 该镜像包含一个简单的 Vue.js应用程序,该应用程序在容器内部的端口 80 上运行
运行命令:

docker container run --publish 8080:80 fhsinchy/hello-dock

怎样公开端口(即开放端口)
要允许从容器外部进行访问,必须将容器内的相应端口发布到本地网络上的端口。–publish 或 -p 选项的通用语法如下:

--publish <host port>:<container port>

之前的编写了 --publish 8080:80 时,这意味着发送到主机系统端口 8080 的任何请求都将转发到容器内的端口 80。使用curl命令访问 http://127.0.0.1:8080。可以看到返回值。

如何使用容器后台执行模式
run 命令的选项是 —detach 或 -d 选项。 在上面的示例中,为了使容器继续运行,必须将终端窗口保持打开状态。关闭终端窗口会停止正在运行的容器。

命令如下:

docker container run --detach --publish 8080:80 fhsinchy/hello-dock

注意:使用 run 命令时必须记住的一件事是镜像名称必须最后出现。如果在镜像名称后放置任何内容,则将其作为参数传递给容器入口点(在在容器内执行命令小节做了解释),可能会导致意外情况。

怎样查看正在运行的容器
container ls 命令可用于列出当前正在运行的容器。执行以下命令:

[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED        STATUS        PORTS                                       NAMES
f5be61f3f4f9   centos_with_nettools   "bash"                   27 hours ago   Up 27 hours                                               eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago   Up 3 hours                                                stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago   Up 29 hours   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat

输入内容含义

NAMES:容器的名字
STATUS :已经运行的时间
CONTAINER ID:这是完整容器 ID 的前 12 个字符。完整的容器 ID 该字符长 64 个字符。在上一节中 docker container run 命令行的输出的就是完整的容器 ID 。
PORTS :宿主机端口于容器端口的映射关系
IMAGE:启动容器使用的镜像
CREATED:容器创建了多长时间

container ls 命令仅列出系统上当前正在运行的容器。为了列出过去运行的所有容器,可以使用 --all 或 -a 选项。

怎样命名或者重命名一个容器
默认情况下,每个容器都有两个标识符。 如下:

CONTAINER ID - 64 个字符的随机字符串。
NAME - 两个随机词的组合,下划线连接。

可以使用 --name 选项来命名容器。要使用名为 hello-dock-container 的 fhsinchy/hello-dock 镜像运行另一个容器,可以执行以下命令:

[root@docker ~]# docker container run --detach --publish 8888:80 --name hello-dock-container fhsinchy/hello-dock
256d0746622ada5495e70c143172c165fa1aa9d376b48df1a797b72bc2c16a72
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS              PORTS                                       NAMES
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago         Up 28 hours                                                     eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago         Up 4 hours                                                      stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago         Up 30 hours         0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# 

可以看到新命名的容器hello-dock-container已经启动

还可以使用 container rename 命令来重命名旧容器。该命令的语法如下:

docker container rename <container identifier> <new name>

将容器重命名为 hello-dock-container-2,可以执行以下命令:

[root@docker ~]# docker container rename keen_ptolemy  hello-dock-container-2
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS          PORTS                                       NAMES
63649ee7d262   fhsinchy/hello-dock    "/docker-entrypoint.…"   About a minute ago   Up 59 seconds   0.0.0.0:8800->80/tcp, :::8800->80/tcp       hello-dock-container-2
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   4 minutes ago        Up 4 minutes    0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago         Up 28 hours                                                 eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago         Up 4 hours                                                  stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago         Up 30 hours     0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]#

可以看到名字的变动,rename 命令不仅适用于处于运行状态的容器和还适用于处于停止状态的容器。

怎样停止或者杀死运行中的容器
有两个命令可以使用。 第一个是 container stop 命令。该命令的通用语法如下:

docker container stop <container identifier>

其中 container identifier 可以是容器的 ID 或名称。

[root@docker ~]# docker container stop hello-dock-container
hello-dock-container
[root@docker ~]# 

如果使用 name 作为标识符,则 name 将作为输出返回。stop 命令通过发送信号SIGTERM 来正常关闭容器。如果容器在一定时间内没有停止运行,则会发出 SIGKILL 信号,该信号会立即关闭容器。

如果要发送 SIGKILL 信号而不是 SIGTERM 信号,则可以改用 container kill 命令。container kill 命令遵循与 stop 命令相同的语法。

[root@docker ~]# docker container kill hello-dock-container-2
hello-dock-container-2
[root@docker ~]# 

怎样重新启动容器
重启时,指的如下是两种情况:
1、重新启动先前已停止或终止的容器。
2、重新启动正在运行的容器。

停止的容器保留在系统中。如果需要,可以重新启动它们。container start 命令可用于启动任何已停止或终止的容器。该命令的语法如下:

docker container start <container identifier>

可以通过执行 container ls --all 命令来获取所有容器的列表,然后寻找状态为 Exited 的容器。

[root@docker ~]# docker container ls -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS                       PORTS                                       NAMES
63649ee7d262   fhsinchy/hello-dock    "/docker-entrypoint.…"   7 minutes ago    Exited (137) 2 minutes ago                                               hello-dock-container-2
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   11 minutes ago   Exited (0) 3 minutes ago                                                 hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago     Up 28 hours                                                              eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago     Up 4 hours                                                               stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago     Up 30 hours                  0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat

现在要重新启动 hello-dock-container 容器,可以执行以下命令:

[root@docker ~]# docker container start hello-dock-container
hello-dock-container
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS         PORTS                                       NAMES
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   12 minutes ago   Up 3 seconds   0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago     Up 28 hours                                                eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago     Up 4 hours                                                 stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago     Up 30 hours    0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# 

默认情况下,container start 命令以后台模式启动容器,并保留之前进行的端口配置。因此,如果现在访问 http://127.0.0.1:8080,应该能够像以前一样访问 hello-dock 应用程序。

在想重新启动正在运行的容器,可以使用 container restart 命令。container restart 命令遵循与 container start 命令完全相同的语法。

docker container restart hello-dock-container-2

注意:这两个命令之间的主要区别在于,container restart 命令尝试停止目标容器,然后再次启动它,而 start 命令只是启动一个已经停止的容器。
在容器停止的情况下,两个命令完全相同。但是如果容器正在运行,则必须使用container restart 命令。

怎样创建而不运行容器
到目前为止,已经使用 container run 命令启动了容器,该命令实际上是两个单独命令的组合。这两个命令如下:

container create 命令从给定的镜像创建一个容器。
container start 命令将启动一个已经创建的容器。

如果分别使用这两个命令执行运行容器,可以执行以下操作 :

docker container create --publish 8080:80 fhsinchy/hello-dock

通过 container ls --all 命令的输出可以明显看出,已经使用 fhsinchy/hello-dock 镜像创建了一个名称为 hello-dock 的容器。 容器的 STATUS 目前处于 Created 状态,并且鉴于其未运行,因此不使用 --all 选项就不会列出该容器。

docker container start hello-dock

使用启动命令后,container ls --all 命令的输出,会发生变化,大多数情况下使用 container run 命令,但还会有一些情况要求使用 container create 命令。

怎样移除挂起的容器
之前查询命令的返回结果可知,已被停止或终止的容器仍保留在系统中。这些挂起的容器可能会占用空间或与较新的容器发生冲突。

可以使用 container rm 命令删除停止的容器。 通用语法如下:

docker container rm <container identifier>

要找出哪些容器没有运行,使用 container ls --all 命令并查找状态为 Exited 的容器。从输出中可以看到,ID为63649ee7d262 的容器未运行。然后执行删除命令,可以使用 container ls 命令检查容器是否被删除,命令如下:

[root@docker ~]# docker container ls -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS                        PORTS                                       NAMES
63649ee7d262   fhsinchy/hello-dock    "/docker-entrypoint.…"   17 minutes ago   Exited (137) 12 minutes ago                                               hello-dock-container-2
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   21 minutes ago   Up 9 minutes                  0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago     Up 28 hours                                                               eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago     Up 4 hours                                                                stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago     Up 30 hours                   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# docker container rm 63649ee7d262
63649ee7d262
[root@docker ~]# docker container ls -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                       NAMES
256d0746622a   fhsinchy/hello-dock    "/docker-entrypoint.…"   22 minutes ago   Up 10 minutes   0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-container
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago     Up 28 hours                                                 eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago     Up 4 hours                                                  stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago     Up 30 hours     0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# 

也可以使用 container prune 命令来一次性删除所有挂起的容器。对于正运行中的容器进行删除需要加上 -f 参数:

docker container rm -f  63649ee7d262

container run 和 container start 命令还有 --rm 选项,它们表示希望容器在停止后立即被移除。 执行以下命令,使用 --rm 选项启动另一个 hello-dock 容器:
操作过程如下:

[root@docker ~]# docker container run --rm --detach --publish 8888:80 --name hello-dock-volatile fhsinchy/hello-dock
16f21a9586d120c8552575a27c67692a8a76a5a32d076d6e77365328f7d5f662
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                                       NAMES
16f21a9586d1   fhsinchy/hello-dock    "/docker-entrypoint.…"   3 seconds ago   Up 2 seconds   0.0.0.0:8888->80/tcp, :::8888->80/tcp       hello-dock-volatile
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago    Up 28 hours                                                eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago    Up 4 hours                                                 stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago    Up 30 hours    0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# docker container stop hello-dock-volatile
hello-dock-volatile
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                  COMMAND                  CREATED        STATUS        PORTS                                       NAMES
f5be61f3f4f9   centos_with_nettools   "bash"                   28 hours ago   Up 28 hours                                               eager_poitras
6755ee250573   centos_with_nettools   "bash"                   28 hours ago   Up 4 hours                                                stupefied_lamarr
c78527a092c8   registry               "/entrypoint.sh /etc…"   30 hours ago   Up 30 hours   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# 

容器停止后已被自动删除

怎样以交互式模式运行容器:镜像可以将整个 Linux 发行版封装在其中

流行的发行版,例如 Ubuntu,Fedora 和 Debian 都在 hub 有官方的 Docker 镜像。编程语言,例如 python、php、[go](https:// hub.docker.com/_/golang) 或类似 node 和 deno 都有其官方镜像。

这些镜像不但仅运行某些预配置的程序。还将它们配置为默认情况下运行的 shell 程序。在镜像是操作系统的情况下,它可以是诸如 sh 或 bash 之类的东西,在竟像是编程语言或运行时的情况下,通常是它们的默认语言的 shell, shell 是交互式程序。被配置为运行这样的程序的镜像是交互式镜像。这些镜像需要在 container run 命令中传递特殊的 -it 选项。

例如:通过执行 docker container run centos使用 centos镜像运行一个容器,将不会发生任何事情。但是,如果使用 -it 选项执行相同的命令,会直接进入到 Ubuntu 容器内的 bash 上。

[root@docker ~]# docker container run --rm -it centos_with_nettools
[root@b83de339f3c4 /]# cat /etc/re
redhat-release  resolv.conf
[root@b83de339f3c4 /]# cat /etc/re
redhat-release  resolv.conf
[root@b83de339f3c4 /]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
[root@b83de339f3c4 /]# 

-it 选项提供了与容器内的程序进行交互的场景。此选项实际上是将两个单独的选项混在一起。

选项 -i 或 --interactive 连接到容器的输入流,以便可以将输入发送到 bash。-t 或 --tty 选项可通过分配伪 tty 来格式化展示并提供类似本机终端的体验。

怎样在容器里执行命令
将命令传递到未运行的容器的通用语法如下:

docker container run <image name> <command>

要使用 centos镜像执行 ls 命令,可以执行以下命令:
可以看到root家目录的文件(注意:这里的命令使用的了- -rm参数,所以执行完命令后,容器即被关闭

[root@docker ~]# docker container run --rm centos_with_nettools ls /root
anaconda-ks.cfg
anaconda-post.log
original-ks.cfg

在 container run 命令中,镜像名称后传递的任何内容都将传递到镜像的默认入口里。

入口点就像是通往镜像的网关。除可执行镜像外的大多数镜像(在使用可执行镜像另行说明)使用 shell 或 sh 作为默认入口点。因此,任何有效的 shell 命令都可以作为参数传递给它们。

如何处理可执行镜像
原文使用的以的 rmbyext 项目为例子讲解。这是一个简单的 Python 脚本,能够递归删除给定扩展名的文件。(这里本人并没有进行测试)
系统上已经正确设置了 Python,则该脚本应该可以在终端的任何位置使用。使用此脚本的通用语法如下:

rmbyext <file extension>

原文给出的 fhsinchy/rmbyext 镜像的行为类似。该镜像包含 rmbyext 脚本的副本,并配置为在容器内的目录 /zone上运行该脚本。(实际上就是把脚本放置到容器中执行)

现在的问题是容器与本地系统隔离,因此在容器内运行的 rmbyext 程序无法访问本地文件系统。因此,授予容器直接访问本地文件系统的一种方法是使用绑定挂载。(类似 NFS)

绑定挂载可以在本地文件系统目录(源)与容器内另一个目录(目标)之间形成双向数据绑定。这样,在目标目录中进行的任何更改都将在源目录上生效,反之亦然。

测试绑定挂载的实际应用。要使用此镜像而不是程序本身删除文件,可以执行以下命令:

docker container run --rm -v $(pwd):/zone fhsinchy/rmbyext pdf

删除结果为:
注意:这里还是使用了 --rm 参数,即执行完命令后,关闭并删除容器。使用docker container ls -a是查询不到fhsinchy/rmbyext容器的

[root@docker ~]# docker container run --rm -v $(pwd):/zone fhsinchy/rmbyext pdf
Removing: PDF
b.pdf
d.pdf
a.pdf

-v 或 --volume 选项用于为容器创建绑定挂载。该选项可以使用三个以冒号(:)分隔的字段。该选项的通用语法如下:

--volume <local file system directory absolute path>:<container file system directory absolute path>:<read write access>

第三个字段是可选的,但必须传递本地目录的绝对路径和容器内目录的绝对路径。–volume 或 -v 选项对 container run 以及 container create 命令均有效。

常规镜像和可执行镜像之间的区别在于,可执行镜像的入口点设置为自定义程序而不是 sh,在本例中为 rmbyext 程序。正如在上一小节中所学到的那样,在 container run 命令中在镜像名称之后编写的所有内容都将传递到镜像的入口点。可执行镜像并不常见,但在某些情况下可能非常有用。

Docker 镜像操作基础知识

怎样创建 Docker 镜像:
镜像是分层的自包含文件,它们充当用于创建 Docker 容器的模板。它们就像是容器的冻结的只读副本。

为了使用程序创建镜像,必须对要从镜像中获得什么有清晰的认识。以官方 nginx 镜像为例。只需执行以下命令即可使用该镜像启动容器:

现在,如果在浏览器中访问 http://127.0.0.1:8080,则会看到一个默认的响应页面。

我们也可以制作自己的镜像,为了制作自定义的 NGINX 镜像,必须清楚了解镜像的最终状态。我认为镜像应如下所示:

  1. 该镜像应预安装 NGINX,可以使用程序包管理器完成该镜像,也可以从源代码构建该镜像。
  2. 该镜像在运行时应自动启动 NGINX。

克隆原文中链接的项目仓库,命令如下:

[root@docker custom-nginx]# git clone https://github.com/fhsinchy/docker-handbook-projects.git

请进入项目根目录并在其中查找名为 custom-nginx 的目录。在该目录中创建一个名为 Dockerfile 的新文件。

Dockerfile 是指令的集合,该指令会被守护程序生成镜像。Dockerfile 的内容如下:

FROM ubuntu:latestEXPOSE 80RUN apt-get update && \apt-get install nginx -y && \apt-get clean && rm -rf /var/lib/apt/lists/*CMD ["nginx", "-g", "daemon off;"]

镜像是多层文件,在此文件中,编写的每一行(称为说明)都会为镜像创建一个层。

每个有效的 Dockerfile 均以 FROM 指令开头。该指令为生成的镜像设置基本镜像。通过在此处将 ubuntu:latest 设置为基本镜像,可以在自定义镜像中使用 Ubuntu 的所有功能,因此可以使用 apt-get 命令之类的东西来轻松安装软件包。

EXPOSE 指令表示需要发布的端口。使用此指令并不意味着不需要 --publish 端口。仍然需要显式使用 --publish 选项。该 EXPOSE 指令的工作原理类似于文档,适用于试图使用你的镜像运行容器的人员。它还有一些其他用途。

Dockerfile 中的 RUN 指令在容器 shell 内部执行命令。apt-get update && apt-get install nginx -y 命令检查更新的软件包版本并安装 NGINX。apt-get clean && rm -rf /var/lib/apt/lists/* 命令用于清除程序包缓存,因为不希望镜像中出现任何不必要的文件。这两个命令是简单的 Ubuntu 东西,没什么特别的。此处的 RUN 指令以 shell 形式编写。这些也可以以 exec 形式编写。 可以查阅官方参考了解更多信息。

最后,CMD 指令为镜像设置了默认命令。该指令以 exec 形式编写,此处包含三个独立的部分。这里,nginx 是指 NGINX 可执行文件。 -g 和 daemon off 是 NGINX 的选项。 在容器内将 NGINX 作为单个进程运行是一种最佳实践,因此请使用此选项。CMD 指令也可以以 shell 形式编写。 可以查阅官方参考了解更多信息。

既然具有有效的 Dockerfile,可以从中构建镜像。就像与容器相关的命令一样,可以使用以下语法来执行与镜像相关的命令:

docker image <command> <options>

要使用刚刚编写的 Dockerfile 构建镜像,请在 custom-nginx 目录中打开终端并执行以下命令:

为了执行镜像构建,守护程序需要两条非常具体的信息。Dockerfile 的名称和构建上下文。在上面执行的命令中:

docker image build .

看到Successfully built 131a43a3deb0 表示镜像构建成功。

  1. docker image build 是用于构建镜像的命令。守护程序在上下文中找到任何名为 Dockerfile 的文件。
  2. 最后的 " . " 设置了此构建的上下文。上下文是指在构建过程中守护程序可以访问的目录。

现在要使用此镜像运行容器,可以将 container run 命令与在构建过程中收到的镜像 ID 结合使用。这里,通过执行docker image build . 命令中的 Successfully built 131a43a3deb0行可以看到 id 为 131a43a3deb0。结果如下:

[root@docker custom-nginx]# docker container run --rm --detach --name custom-nginx-packaged --publish 9880:80 131a43a3deb0
cf3f07560d2b9f021bf96bd459a38ce6feb8110de2083c6bedf2ee9cdbbe27db
[root@docker custom-nginx]# docker container ls -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                       NAMES
cf3f07560d2b   131a43a3deb0   "nginx -g 'daemon of…"   11 seconds ago   Up 11 seconds   0.0.0.0:9880->80/tcp, :::9880->80/tcp       custom-nginx-packaged
58b2af5e6173   nginx          "/docker-entrypoint.…"   3 hours ago      Up 3 hours      0.0.0.0:8080->80/tcp, :::8080->80/tcp       default-nginx
c78527a092c8   registry       "/entrypoint.sh /etc…"   44 hours ago     Up 44 hours     0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat

如何标记 Docker 镜像:
就像容器一样,可以为镜像分配自定义标识符,而不必依赖于随机生成的 ID。如果是镜像,则称为标记而不是命名。在这种情况下,使用 --tag 或 -t 选项。

--tag <image repository>:<image tag>

repository 通常指镜像名称,而 tag 指特定的构建或版本。

以官方 mysql 镜像为例。如果想使用特定版本的MySQL(例如5.7)运行容器,则可以执行 docker container run mysql:5.7,其中 mysql 是镜像 repository,5.7 是 tag。

为了用 nginx:packaged 标签标记自定义 NGINX 镜像,可以执行以下命令:

[root@docker custom-nginx]# docker image build --tag nginx:packaged .
Sending build context to Docker daemon  1.052MB
Step 1/4 : FROM ubuntu:latest---> 9873176a8ff5
Step 2/4 : EXPOSE 80---> Using cache---> 90193623a370
Step 3/4 : RUN apt-get update &&     apt-get install nginx -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*---> Using cache---> b5949e42b10d
Step 4/4 : CMD ["nginx", "-g", "daemon off;"]---> Using cache---> 131a43a3deb0
Successfully built 131a43a3deb0
Successfully tagged nginx:packaged
[root@docker custom-nginx]# docker image ls
REPOSITORY             TAG        IMAGE ID       CREATED          SIZE
nginx                  packaged   131a43a3deb0   20 minutes ago   132MB

如果在构建期间忘记为镜像添加标记,或者你想更改标记,可以使用 image tag 命令执行此操作:

docker image tag <image id> <image repository>:<image tag>## 或者 ##docker image tag <image repository>:<image tag> <new image repository>:<new image tag>

如何删除、列表展示镜像
就像 container ls 命令一样,可以使用 image ls 命令列出本地系统中的所有镜像:

[root@docker custom-nginx]# docker image ls
REPOSITORY             TAG        IMAGE ID       CREATED          SIZE
nginx                  packaged   131a43a3deb0   20 minutes ago   132MB
centos_with_nettools   latest     6e29554cc513   47 hours ago     247MB
nginx                  latest     4f380adfc10f   9 days ago       133MB
ubuntu                 latest     9873176a8ff5   2 weeks ago      72.7MB
registry               latest     1fd8e1b0bb7e   2 months ago     26.2MB
hello-world            latest     d1165f221234   3 months ago     13.3kB
fhsinchy/hello-dock    latest     f540930e8157   5 months ago     21.9MB
fhsinchy/rmbyext       latest     90eafb66c390   5 months ago     50.9MB

以使用 image rm 命令删除此处列出的镜像。通用语法如下:

docker image rm <image identifier>

标识符可以是镜像 ID 或镜像仓库。 如果使用仓库,则还必须指定标记。要删除 custom-nginx:packaged 镜像,可以执行以下命令:
注意:如果要删除的镜像已经使用于容器,要先关闭容器再删除,否者会报错。或者使用 - f 参数

[root@docker custom-nginx]# docker image rm 131a43a3deb0
Deleted: sha256:131a43a3deb02cfe4fbea1fd46b0d4acd94e58b35cb18394a249782adfb54228
Deleted: sha256:b5949e42b10df59280a0d39b4ae785e8febf4d7e337807b66a39176864ce32fd
Deleted: sha256:38ea53ae954062a89f184b410e867ef56dc79794312fd235860ecf63dda10ec7
Deleted: sha256:90193623a3708e75df1992cee47a800bcf9a1b6ec295a60b20f78fea4fa3819a
[root@docker custom-nginx]# docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED        SIZE
centos_with_nettools   latest    6e29554cc513   47 hours ago   247MB
nginx                  latest    4f380adfc10f   9 days ago     133MB
ubuntu                 latest    9873176a8ff5   2 weeks ago    72.7MB
registry               latest    1fd8e1b0bb7e   2 months ago   26.2MB
hello-world            latest    d1165f221234   3 months ago   13.3kB
fhsinchy/hello-dock    latest    f540930e8157   5 months ago   21.9MB
fhsinchy/rmbyext       latest    90eafb66c390   5 months ago   50.9MB

还可以使用 image prune 命令来清除所有未标记的挂起的镜像,如下所示:

docker image prune --force

- -force 或 -f 选项会跳过所有确认问题。也可以使用 --all 或 -a 选项删除本地仓库中的所有缓存镜像。

理解 Docker 镜像的分层:这里为原文件的直接引用
从本书的开始,我就一直在说镜像是多层文件。在本小节中,我将演示镜像的各个层,以及它们如何在该镜像的构建过程中发挥重要作用。
在本演示中,我将使用上一小节的 custom-nginx:packaged 镜像。
要可视化镜像的多个层,可以使用 image history 命令。custom-nginx:packaged 图像的各个层可以如下所示:

docker image history custom-nginx:packaged# IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
# 7f16387f7307        5 minutes ago       /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
# 587c805fe8df        5 minutes ago       /bin/sh -c apt-get update &&     apt-get ins…   60MB
# 6fe4e51e35c1        6 minutes ago       /bin/sh -c #(nop)  EXPOSE 80                    0B
# d70eaf7277ea        17 hours ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
# <missing>           17 hours ago        /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
# <missing>           17 hours ago        /bin/sh -c [ -z "$(apt-get indextargets)" ]     0B
# <missing>           17 hours ago        /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   811B
# <missing>           17 hours ago        /bin/sh -c #(nop) ADD file:435d9776fdd3a1834…   72.9MB

此镜像有八层。最上面的一层是最新的一层,当向下移动时,这些层会变老。最顶层是通常用于运行容器的那一层。
现在,让我们仔细看看从镜像 d70eaf7277ea 到镜像 7f16387f7307 的所有镜像。我将忽略 IMAGE 是 的最下面的四层,因为它们与我们无关。
d70eaf7277ea 是由 /bin/sh -c #(nop) CMD [“/bin/bash”] 创建的,它指示Ubuntu 内的默认 shell 已成功加载。
6fe4e51e35c1 是由 /bin/sh -c #(nop) EXPOSE 80 创建的,这是代码中的第二条指令。
587c805fe8df 是由 /bin/sh -c apt-get update && apt-get install nginx -y && apt-get clean && rm -rf /var/lib/apt/lists/
创建的,这是代码中的第三条指令。如果在执行此指令期间安装了所有必需的软件包,可以看到该镜像的大小为 60MB。
最后,最上层的 7f16387f7307 是由 /bin/sh -c #(nop) CMD [“nginx”, “-g”, “daemon off;”] 创建的,它为该镜像设置了默认命令。
如你所见,该镜像由许多只读层组成,每个层都记录了由某些指令触发的一组新的状态更改。当使用镜像启动容器时,会在其他层之上获得一个新的可写层。
每次使用 Docker 时都会发生这种分层现象,这是通过一个称为 union file system 的技术概念而得以实现的。 在这里,联合意味着集合论中的联合。
根据 Wikipedia
它允许透明地覆盖独立文件系统(称为分支)的文件和目录,从而形成单个一致的文件系统。合并分支内具有相同路径的目录的内容将在新的虚拟文件系统内的单个合并目录中一起看到。
通过利用这一概念,Docker 可以避免数据重复,并且可以将先前创建的层用作以后构建的缓存。这样便产生了可在任何地方使用的紧凑,有效的镜像。

怎样从源码构建 NGINX
创建一个自定义的 NGINX 镜像。但是,不同之处在于,将从源代码构建 NGINX,而不是像上一个示例那样使用诸如 apt-get 之类的软件包管理器进行安装。

从源代码构建 NGINX,首先需要 NGINX 的源代码。 如果克隆了原文的项目仓库,则会在 custom-nginx 目录中看到一个名为 nginx-1.19.2.tar.gz 的文件。将使用此归档文件作为构建 NGINX 的源。(自行下载也是一样)

编写代码之前,先规划一下流程。 这次的镜像创建过程可以分七个步骤完成。如下:

1、获得用于构建应用程序的基础镜像,例如 ubuntu。
2、在基础镜像上安装必要的构建依赖项。
3、复制 nginx-1.19.2.tar.gz 文件到镜像里。
4、解压缩压缩包的内容并删除压缩包。
5、使用 make 工具配置构建,编译和安装程序。
6、删除解压缩的源代码。
7、运行nginx可执行文件。

现在有了一个规划,重新编辑之前的 Dockerfile 并按如下所示更新其内容:

FROM ubuntu:latestRUN apt-get update && \apt-get install build-essential\ libpcre3 \libpcre3-dev \zlib1g \zlib1g-dev \libssl-dev \-y && \apt-get clean && rm -rf /var/lib/apt/lists/*COPY nginx-1.19.2.tar.gz .RUN tar -xvf nginx-1.19.2.tar.gz && rm nginx-1.19.2.tar.gzRUN cd nginx-1.19.2 && \./configure \--sbin-path=/usr/bin/nginx \--conf-path=/etc/nginx/nginx.conf \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--with-pcre \--pid-path=/var/run/nginx.pid \--with-http_ssl_module && \make && make installRUN rm -rf /nginx-1.19.2CMD ["nginx", "-g", "daemon off;"]

以上,Dockerfile 中的代码反映了上面提到的七个步骤。

FROM 指令将 Ubuntu 设置为基本映像,从而为构建任何应用程序提供了理想的环境。

RUN 指令安装了从源代码构建 NGINX 所需的标准软件包。

这里的 COPY 指令是新的东西。该指令负责在映像内复制 nginx-1.19.2.tar.gz 文件。 COPY 指令的通用语法是 COPY <source> <destination>,其中 source 在本地文件系统中,而 destination 在镜像内部。作为目标的 . 表示镜像内的工作目录,除非另有设置,否则默认为 /。

这里的第二条 RUN 指令使用 tar 从压缩包中提取内容,然后将其删除。存档文件包含一个名为 nginx-1.19.2 的目录,其中包含源代码。

因此,下一步,将 cd 进入该目录并执行构建过程。

构建和安装完成后,使用 rm 命令删除 nginx-1.19.2 目录。

在最后一步,像以前一样以单进程模式启动 NGINX。

在 DockerFile 中常用语句含义:FROM :用于指定基于哪个镜像,格式是FROM<image>或FROM<image>:<tag>
MAINTAINER: 用于指定作者信息,格式是MAINTAIN<name>
RUN: 是镜像操作指令,格式是 RUN <command> 或 RUN ["executable","param1","param2"]
CMD:用于执行一些命令,它有3种语法格式,与RUN的语法格式类似,CMD指定容器启动时用到的命令只能有一条
EXPOSE:是用来暴露端口的命令,比如把22端口、80端口、8443端口暴露出来并赋值
ENV:用来定义环境变量,格式是<key><value>(ENV 主要为后续的 RUN 指令提供一个环境变量,也可以自定义一些环境变量,如ENV MYSQL_version5.7。)
ADD: 命令是将本地的文件或目录复制到容器的某个目录下。其中,源为DockerFile所在目录的相对路径,也可以是URL
COPY :命令和 ADD 命令类似,语法格式也一致,不同的是 COPY 命令不支持URL远程下载。
ENTRYPOINT:命令的格式类似于CMD命令的格式,容器启动时要执行的命令也类似于 CMD 命令,只有一条生效,如果写多条语句,则只有最后一条语句会生效。
VOLUME: 是用来指定挂载点的,可以创建一个从本地或其他容器挂载的挂载点,格式是VOLUME ["/data"]。
USER:指定运行容器的用户,格式是USER daemon。
WORKDIR: 用于指定一个目录,指定目录后,在目录下进行一些操作,如运行一些命令时,先进入到路径下,或者在启动容器时,使用 CMD、ENTRYPOINT 指定工作目录。

现在,要使用此代码构建镜像,请执行以下命令:

docker image build --tag custom-nginx:built .

出现Successfully built f5fe6cf605a5 Successfully tagged custom-nginx:built 即表示成功

这段代码还不错,但是还有优化地方,修改如下:

  1. 可以使用 ARG 指令创建自变量,而不是像 nginx-1.19.2.tar.gz 这样的文件名进行硬编码。这样,只需更改参数即可更改版本或文件名。
  2. 可以让守护程序在构建过程中下载文件,而不是手动下载存档。还有另一种类似于COPY 的指令,称为 ADD指令,该指令能够从互联网添加文件。

打开 Dockerfile 文件,并按如下所示更新其内容:

FROM ubuntu:latestRUN apt-get update && \apt-get install build-essential\ libpcre3 \libpcre3-dev \zlib1g \zlib1g-dev \libssl-dev \-y && \apt-get clean && rm -rf /var/lib/apt/lists/*ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}RUN cd ${FILENAME} && \./configure \--sbin-path=/usr/bin/nginx \--conf-path=/etc/nginx/nginx.conf \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--with-pcre \--pid-path=/var/run/nginx.pid \--with-http_ssl_module && \make && make installRUN rm -rf /${FILENAME}}CMD ["nginx", "-g", "daemon off;"]

该代码几乎与先前的代码块相同,除了在第 13、14 行有一条名为 ARG 的新指令,以及在第 16 行用法了 ADD 指令。有关更新代码的说明如下:

  1. ARG 指令可以像其他语言一样声明变量。以后可以使用 ${argument name} 语法访问这些变量或参数。在这里,我将文件名nginx-1.19.2 和文件扩展名 tar.gz 放在了两个单独的参数中。这样,我只需在一个地方进行更改就可以在 NGINX的较新版本或存档格式之间进行切换。在上面的代码中,我向变量添加了默认值。变量值也可以作为 image build 命令的选项传递。你可以查阅官方参考了解更多详细信息。
  2. 在 ADD 指令中,我使用上面声明的参数动态形成了下载 URL
    https://nginx.org/download/FILENAME.{FILENAME}.FILENAME.{EXTENSION} 行将在构建过程生成类似于 https://nginx.org/download/nginx-1.19.2.tar.gz 的内容。可以通过一次更改文件版本或扩展名的方式来更改文件版本或扩展名,这里要使用 ARG 指令。
  3. 默认情况下,ADD 指令不会提取从互联网获取的文件,因此在第18行使用了 tar。

最后,让我们尝试从此更新的代码构建镜像:

docker image build --tag custom-nginx:built .

现在使用 custom-nginx:built 镜像来运行容器:

[root@docker custom-nginx]# docker container run --rm --detach --name custom-nginx-built --publish 8880:80 custom-nginx:built
e46e4c3b449876e8d721dc9d872520f4ca54eac1a031d60d506136e84cb05a5d[root@docker custom-nginx]# docker container lsCONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS         PORTS                                       NAMES
e46e4c3b4498   custom-nginx:built   "nginx -g 'daemon of…"   10 seconds ago   Up 9 seconds   0.0.0.0:8880->80/tcp, :::8880->80/tcp       custom-nginx-built
58b2af5e6173   nginx                "/docker-entrypoint.…"   11 hours ago     Up 11 hours    0.0.0.0:8080->80/tcp, :::8080->80/tcp       default-nginx
c78527a092c8   registry             "/entrypoint.sh /etc…"   2 days ago       Up 2 days      0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat

测试一下,可以正常访问:

怎样优化 Docker 镜像
之前的实验中构建的镜像具有功能,但是没有经过优化。让我们使用 image ls 命令来查看镜像的大小:

[root@docker custom-nginx]# docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED         SIZE
custom-nginx           built     9a565accd876   4 minutes ago   359MB
centos_with_nettools   latest    6e29554cc513   2 days ago      247MB
nginx                  latest    4f380adfc10f   10 days ago     133MB
ubuntu                 latest    9873176a8ff5   2 weeks ago     72.7MB
registry               latest    1fd8e1b0bb7e   2 months ago    26.2MB
hello-world            latest    d1165f221234   3 months ago    13.3kB
fhsinchy/hello-dock    latest    f540930e8157   5 months ago    21.9MB
fhsinchy/rmbyext       latest    90eafb66c390   5 months ago    50.9MB

对于仅包含 NGINX 的镜像,这太大了。和官方镜像 nginx 对比并检查其大小,会看官方镜像很小。

为了寻找根本原因,让我们首先看一下 Dockerfile:

FROM ubuntu:latestRUN apt-get update && \apt-get install build-essential\ libpcre3 \libpcre3-dev \zlib1g \zlib1g-dev \libssl-dev \-y && \apt-get clean && rm -rf /var/lib/apt/lists/*ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}RUN cd ${FILENAME} && \./configure \--sbin-path=/usr/bin/nginx \--conf-path=/etc/nginx/nginx.conf \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--with-pcre \--pid-path=/var/run/nginx.pid \--with-http_ssl_module && \make && make installRUN rm -rf /${FILENAME}}CMD ["nginx", "-g", "daemon off;"]

在第 3 行看到的那样,RUN 指令安装了很多东西。尽管这些软件包对于从源代码构建 NGINX 是必需的,但对于运行它而言则不是必需的。

在安装的 6 个软件包中,只有两个是运行 NGINX 所必需的,即 libpcre3 和 zlib1g。 因此,一个更好的主意是在构建过程完成后,卸载其他软件包。

为此,请按如下所示更新的 Dockerfile :

FROM ubuntu:latestEXPOSE 80ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .RUN apt-get update && \apt-get install build-essential \ libpcre3 \libpcre3-dev \zlib1g \zlib1g-dev \libssl-dev \-y && \tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} && \cd ${FILENAME} && \./configure \--sbin-path=/usr/bin/nginx \--conf-path=/etc/nginx/nginx.conf \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--with-pcre \--pid-path=/var/run/nginx.pid \--with-http_ssl_module && \make && make install && \cd / && rm -rfv /${FILENAME} && \apt-get remove build-essential \ libpcre3-dev \zlib1g-dev \libssl-dev \-y && \apt-get autoremove -y && \apt-get clean && rm -rf /var/lib/apt/lists/*CMD ["nginx", "-g", "daemon off;"]

在第 10 行上,一条 RUN 指令正在执行所有必要的核心操作。确切的事件链如下:

  1. 从第 10 行到第 17 行,安装所有必需的软件包。
  2. 在第 18 行,将提取源代码,并删除下载的存档。
  3. 从第 19 行到第 28 行,NGINX 在系统上配置,构建和安装。
  4. 在第 29行,从下载的档案中提取的文件将被删除。
  5. 从第 30 行到第 36 行,所有不必要的软件包都将被卸载并清除缓存。运行 NGINX 需要 libpcre3 和 zlib1g 包,因此我们保留了它们。

在一条 RUN 指令中完成操作,而不是像之前那样将它们很好地拆分成多个指令。 这样镜像的层数减少,镜像的大小也会变小。(如果安装了软件包,然后按照单独的 RUN 说明将其删除,则它们将位于镜像的不同层中。尽管最终镜像不会包含已删除的包,但是由于它们存在于组成该图像的一层之一中,因此它们的大小仍将添加到最终镜像中。因此,请确保在单层上进行了此类更改。)

使用此 Dockerfile 来构建映像,并查看它们之间的区别:可以看到镜像变小了

[root@docker custom-nginx]# docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED         SIZE
custom-nginx           built     7756172aadf9   2 minutes ago   84.1MB
centos_with_nettools   latest    6e29554cc513   2 days ago      247MB
nginx                  latest    4f380adfc10f   10 days ago     133MB
ubuntu                 latest    9873176a8ff5   2 weeks ago     72.7MB
registry               latest    1fd8e1b0bb7e   2 months ago    26.2MB
hello-world            latest    d1165f221234   3 months ago    13.3kB
fhsinchy/hello-dock    latest    f540930e8157   5 months ago    21.9MB
fhsinchy/rmbyext       latest    90eafb66c390   5 months ago    50.9MB

怎样创建可执行 Docker 镜像
首先,打开之前克隆的随附仓库的目录。rmbyext 应用程序的代码位于同名子目录中(我的使用中该目录是空的)。

在开始使用 Dockerfile 之前,应该来规划最终的输出:

  1. 该镜像应预安装 Python。
  2. 它应该包含 rmbyext 脚本的副本。
  3. 应该在将要执行脚本的地方设置一个工作目录。
  4. 应该将 rmbyext 脚本设置为入口点,以便镜像可以将扩展名用作参数。

要构建上面提到的镜像,请执行以下步骤:

  1. 获得可以运行 Python 脚本基础镜像,例如 python。
  2. 将工作目录设置为易于访问的目录。
  3. 安装 Git,以便可以从原文的 GitHub 仓库中安装脚本。
  4. 使用 Git 和 pip 安装脚本。
  5. 删除不必要的构建软件包。 将 rmbyext 设置为该图像的入口点。

在 rmbyext 目录中创建一个新的 Dockerfile,并将以下代码放入其中:

FROM python:3-alpineWORKDIR /zoneRUN apk add --no-cache git && \pip install git+https://github.com/fhsinchy/rmbyext.git && \apk del gitENTRYPOINT [ "rmbyext" ]

该文件中的指令说明如下:

1、FROM 指令将 python 设置为基本镜像,从而为运行 Python 脚本提供了理想的环境。3-alpine 标记表示需要 Python 3 的 Alpine 版本。

2、这里的 WORKDIR 指令将默认工作目录设置为 /zone。这里的工作目录名称完全是随机的。 zone 是一个合适的名称,你也可以换成任何你想要的名称。

3、假设从 GitHub 安装了 rmbyext 脚本,则 git 是安装时的依赖项。第 5 行的 RUN 指令先安装 git,然后使用 Git 和 pip 安装 rmbyext 脚本。之后也删除了git。

4、最后,在第 9 行,ENTRYPOINT 指令将 rmbyext 脚本设置为该镜像的入口点。

在整个文件中,第 9 行是将镜像转换为可执行镜像的关键。现在要构建镜像,可以执行以下命令

docker image build --tag rmbyext .

输出结果如下:可以看到名为 rmbyext 的镜像

[root@docker rmbyext]# docker image ls
REPOSITORY             TAG        IMAGE ID       CREATED              SIZE
rmbyext                latest     b6e3837da8a8   About a minute ago   52.3MB
custom-nginx           built      7756172aadf9   57 minutes ago       84.1MB
centos_with_nettools   latest     6e29554cc513   2 days ago           247MB
python                 3-alpine   56302acacaa7   3 days ago           45.1MB
nginx                  latest     4f380adfc10f   10 days ago          133MB
ubuntu                 latest     9873176a8ff5   2 weeks ago          72.7MB
registry               latest     1fd8e1b0bb7e   2 months ago         26.2MB
hello-world            latest     d1165f221234   3 months ago         13.3kB
fhsinchy/hello-dock    latest     f540930e8157   5 months ago         21.9MB
fhsinchy/rmbyext       latest     90eafb66c390   5 months ago         50.9MB

测试一下:可以看到返回结果正常

[root@docker rmbyext]# docker container run --rm -v $(pwd):/zone rmbyext pdf
Removing: PDF
a.pdf
b.pdf
c.pdf[root@docker rmbyext]# ll
总用量 8
-rw-r--r-- 1 root root  35 7月   3 23:25 delete_log.log
-rw-r--r-- 1 root root 175 7月   3 23:14 Dockerfile
[root@docker rmbyext]# 

如何忽略不必要的文件:在构建镜像是可能有一些不需要的文件,可以使用这个方法排除
.dockerignore 文件包含要从镜像构建中排除的文件和目录的列表。可以在 hello-dock 目录中有一个预先创建的 .dockerignore 文件。

.git
*Dockerfile*
*docker-compose*
node_modules

该 .dockerignore 文件必须位于构建上下文中。这里提到的文件和目录将被 COPY 指令忽略。但是,如果执行绑定挂载,则 .dockerignore 文件将对此无效。

如何在 Docker 中执行多阶段构建(就是利用多个镜像生成一个需要使用生产环境镜像)

在开发模式下,npm run serve 命令启动一个开发服务器,该服务器将应用程序提供给用户。该服务器不仅提供文件,还提供热重载功能。

在生产模式下,npm run build 命令将所有 JavaScript 代码编译为一些静态 HTML、CSS 和 JavaScript 文件。要运行这些文件,不需要 node 或任何其他运行时依赖项。只需要一个像 nginx 这样的服务器。

要在应用程序以生产模式运行时创建镜像,可以执行以下步骤:

使用 node 作为基础镜像并构建应用程序。
在 node 镜像中安装 nginx 并使用它来提供静态文件。

这种方法是完全有效的。但是问题在于,node 镜像很大,并且它所承载的大多数内容对于静态文件服务而言都是不必要的。解决此问题的更好方法如下:

使用node图像作为基础并构建应用程序。
将使用 node 镜像创建的文件复制到 nginx 映像。
根据 nginx 创建最终镜像,并丢弃所有与 node 相关的东西。
这样,镜像仅包含所需的文件,变得非常方便。

这种方法是一个多阶段构建。要执行这样的构建,在 hello-dock 项目目录中创建一个新的 Dockerfile,并将以下内容放入其中:

FROM node:lts-alpine as builderWORKDIR /appCOPY ./package.json ./
RUN npm installCOPY . .
RUN npm run buildFROM nginx:stable-alpineEXPOSE 80COPY --from=builder /app/dist /usr/share/nginx/html

如你所见,Dockerfile 看起来很像以前的 Dockerfile,但有一些不同之处。该文件的解释如下:

第 1 行使用 node:lts-alpine 作为基础镜像开始构建的第一阶段。as builder 语法为此阶段分配一个名称,以便以后可以引用。
从第 3 行到第 13 行,以前已经看过很多次了。实际上,RUN npm run build 命令会编译整个应用程序,并将其存放在 /app/dist 目录中,其中 /app 是工作目录,/dist 是 vite 应用程序的默认输出目录。
第 15 行使用 nginx:stable-alpine 作为基础镜像开始构建的第二阶段。
NGINX 服务器默认在端口 80 上运行,因此添加了 EXPOSE 80 行。
最后一行是 COPY 指令。--from=builder 部分表示要从 builder 阶段复制一些文件。之后,这是一条标准的复制指令,其中 /app/dist 是 source,而 /usr/share/nginx/html 是 destination。 这里使用的 destination 是 NGINX 的默认站点路径,因此放置在其中的任何静态文件都将自动提供。

如你所见,生成的镜像是基于 nginx 的镜像,仅包含运行应用程序所需的文件。要构建此镜像,请执行以下命令:

docker image build --tag hello-dock:prod .

生成镜像后,可以通过执行以下命令来运行新容器:

docker container run \--rm \--detach \--name hello-dock-prod \--publish 8080:80 \hello-dock:prod

正在运行的应用程序应位于 http://127.0.0.1:8080 上

如果要构建具有大量依赖关系的大型应用程序,那么多阶段构建可能会非常有用。如果配置正确,则可以很好地优化和压缩分多个阶段构建的镜像。

Docker 中的网络操作基础知识

原文把 Docker 的基本网络和一个小型的多容器项目相结合,用来讲解 docker的网络操作(主要是brige网络模式)

在之前学习内容中知道容器是隔离的环境。现在考虑一个场景,其中有一个基于 Express.js notes-api 应用程序和一个 PostgreSQL 数据库服务,他们在两个单独的容器中运行。

这两个容器彼此完全隔离,并且彼此无关。那么如何连接两者? 将是一个挑战。

你可能会想到针对此问题的两种可能的解决方案。 它们如下:

使用暴露的端口访问数据库服务。
使用其 IP 地址和默认端口访问数据库服务。

第一个涉及从 postgres 容器暴露一个端口,notes-api 将通过该端口进行连接。假设来自 postgres 容器的暴露端口为 5432。现在,如果尝试从 notes-api 容器内部连接到 127.0.0.1:5432,会发现 notes-api 根本找不到数据库服务。

原因是,在 notes-api 容器内的 127.0.0.1 时,只是代表当前容器的 localhost 。postgres 服务根本不存在。结果是,notes-api 应用程序无法连接。

你可能想到的第二个解决方案找到 postgres 容器的确切 IP 地址,使用 container inspect 命令并将其与端口一起使用。 假设 postgres 容器的名称为 notes-api-db-server ,则可以通过执行以下命令轻松获得 IP 地址:

docker container inspect --format='{{range .NetworkSettings.Networks}} {{.IPAddress}} {{end}}' notes-api-db-server172.17.0.2

现在假设 postgres 的默认端口是 5432,可以通过从 notes-api 容器连接到 172.17.0.2:5432 来非常容易地访问数据库服务。

这种方法也存在问题。 不建议使用 IP 地址来引用容器。另外,如果容器被破坏并重新创建,则 IP 地址可能会更改。跟踪这些不断变化的 IP 地址可能非常麻烦。

现在,我已经排除了对原始问题的可能错误答案,正确的答案是,将它们放置在用户定义的桥接网络下即可将它们连接起来。

Docker 网络基础
Docker 中的网络是另一个逻辑对象,和容器和镜像一样。就像其他两个一样,在 docker network 组下有很多用于操纵网络的命令。

要列出系统中的网络,请执行以下命令:

[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
37b74d1784e0   bridge    bridge    local
6d9ce5cd5d00   host      host      local
5ad2ca7040f1   none      null      local

默认情况下,Docker 具有五类网络驱动。它们如下:

bridge - Docker 中的默认网络驱动程序。当多个容器以标准模式运行并且需要相互通信时,可以使用此方法。
host - 完全消除网络隔离。在 host 网络下运行的任何容器基本上都连接到主机系统的网络。
none - 此驱动程序完全禁用容器的联网。 我还没有找到其应用场景。
overlay - 这用于跨计算机连接多个 Docker 守护程序,这超出了本书的范围。
macvlan - 允许将 MAC 地址分配给容器,使它们的功能类似于网络中的物理设备。

上述五种方法中,这里仅使用 bridge 网络驱动程序进行实验。

如何在 Docker 中创建用户定义的桥接网络
在开始创建自己的桥接网络之前,应该想花一些时间来理解 Docker 随附的默认桥接网络。让我们首先列出系统上的所有网络:

[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
37b74d1784e0   bridge    bridge    local
6d9ce5cd5d00   host      host      local
5ad2ca7040f1   none      null      local

Docker 随附了一个名为 bridge 的默认桥接网络。 运行的任何容器都将自动连接到此网桥网络:

[root@docker ~]# docker container run --rm --detach --name hello-dock --publish 8088:80 fhsinchy/hello-dock
2e8c2bbc9cf6c28ea30dede0ad8328e113c6bfc5aa0310f5da5f8e54a7f78567
[root@docker ~]# docker container ls
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS        PORTS                                       NAMES
2e8c2bbc9cf6   fhsinchy/hello-dock   "/docker-entrypoint.…"   2 seconds ago   Up 1 second   0.0.0.0:8088->80/tcp, :::8088->80/tcp       hello-dock
58b2af5e6173   nginx                 "/docker-entrypoint.…"   2 days ago      Up 2 days     0.0.0.0:8080->80/tcp, :::8080->80/tcp       default-nginx
c78527a092c8   registry              "/entrypoint.sh /etc…"   3 days ago      Up 3 days     0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   brave_fermat
[root@docker ~]# docker network inspect --format='{{range .Containers}}{{.Name}}{{end}}' bridge
hello-dockdefault-nginxbrave_fermat
[root@docker ~]# 

用户定义的桥接网络比默认桥接网络多一些额外的功能。根据有关此主题的官方文档,一些值得注意的额外功能如下:

  • 用户定义的网桥可在容器之间提供自动 DNS 解析: 这意味着连接到同一网络的容器可以使用容器名称相互通信。 因此,如果你有两个名为
    notes-api 和 notes-db 的容器,则 API 容器将能够使用 notes-db 名称连接到数据库容器。
  • 用户定义的网桥提供更好的隔离性:
    默认情况下,所有容器都连接到默认桥接网络,这可能会导致它们之间的冲突。将容器连接到用户定义的桥可以确保更好的隔离。
  • 容器可以即时与用户定义的网络连接和分离:
    在容器的生命周期内,可以即时将其与用户定义的网络连接或断开连接。要从默认网桥网络中删除容器,需要停止容器并使用其他网络选项重新创建它。

既然已经了解了很多有关用户定义的网络的知识,那么现在该为自己创建一个了。可以使用 network create 命令创建网络。该命令的通用语法如下:

docker network create <network name>

要创建名称为 skynet 的网络,请执行以下命令:

docker network create skynet# 7bd5f351aa892ac6ec15fed8619fc3bbb95a7dcdd58980c28304627c8f7eb070docker network ls# NETWORK ID     NAME     DRIVER    SCOPE
# be0cab667c4b   bridge   bridge    local
# 124dccee067f   host     host      local
# 506e3822bf1f   none     null      local
# 7bd5f351aa89   skynet   bridge    local

已经使用给定名称创建了一个新网络。当前没有容器连接到该网络。在下一小节中,将学习有关将容器连接到网络的信息。

如何在 Docker 中将容器连接到网络
将容器连接到网络的方式主要有两种。首先,可以使用 network connect 命令将容器连接到网络。该命令的通用语法如下:

docker network connect <network identifier> <container identifier>

要将 hello-dock 容器连接到 skynet 网络,可以执行以下命令:

docker network connect skynet hello-dockdocker network inspect --format='{{range .Containers}} {{.Name}} {{end}}' skynet#  hello-dockdocker network inspect --format='{{range .Containers}} {{.Name}} {{end}}' bridge#  hello-dock

从两个 network inspect 命令的输出中可以看到,hello-dock 容器现在已连接到 skynet 和默认的 bridge 网络。

将容器连接到网络的第二种方法是对 container run 或 container create 命令使用 --network 选项。 该选项的通用语法如下:

--network <network identifier>

要运行连接到同一网络的另一个 hello-dock 容器,可以执行以下命令

docker container run --network skynet --rm --name alpine-box -it alpine sh# lands you into alpine linux shell/ # ping hello-dock# PING hello-dock (172.18.0.2): 56 data bytes
# 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.191 ms
# 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.103 ms
# 64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.139 ms
# 64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.142 ms
# 64 bytes from 172.18.0.2: seq=4 ttl=64 time=0.146 ms
# 64 bytes from 172.18.0.2: seq=5 ttl=64 time=0.095 ms
# 64 bytes from 172.18.0.2: seq=6 ttl=64 time=0.181 ms
# 64 bytes from 172.18.0.2: seq=7 ttl=64 time=0.138 ms
# 64 bytes from 172.18.0.2: seq=8 ttl=64 time=0.158 ms
# 64 bytes from 172.18.0.2: seq=9 ttl=64 time=0.137 ms
# 64 bytes from 172.18.0.2: seq=10 ttl=64 time=0.145 ms
# 64 bytes from 172.18.0.2: seq=11 ttl=64 time=0.138 ms
# 64 bytes from 172.18.0.2: seq=12 ttl=64 time=0.085 ms--- hello-dock ping statistics ---
13 packets transmitted, 13 packets received, 0% packet loss
round-trip min/avg/max = 0.085/0.138/0.191 ms

从 alpine-box 容器内部运行 ping hello-dock 是可行的,因为这两个容器都在同一用户定义的网桥网络下,并且自动 DNS 解析有效。

但是请记住,为了使自动 DNS 解析正常工作,必须为容器分配自定义名称。使用随机生成的名称将不起作用。

如何在 Docker 中从网络分离容器

之前了解了有关将容器连接到网络的信息。在本小节中,将学习如何分离它们。

可以使用 network disconnect 命令来执行此任务。该命令的通用语法如下:

docker network disconnect <network identifier> <container identifier>

要从 skynet 网络分离 hello-dock 容器,可以执行以下命令:

docker network disconnect skynet hello-dock

就像 network connect 命令一样,network disconnect 命令也不给出任何输出。

如何删除 Docker 中的网络

就像 Docker 中的其他逻辑对象一样,可以使用 network rm 命令删除网络。该命令的通用语法如下:

docker network rm <network identifier>

要从系统中删除 skynet 网络,可以执行以下命令:

docker network rm skynet

也可以使用 network prune 命令从系统中删除所有未使用的网络。该命令还具有 -f 或 --force 和 -a 或 --all 选项。

如何容器化多容器 JavaScript 应用程序

既然已经对 Docker 中的网络有了足够的了解,那么在本节中,将学习如何将成熟的多容器项目容器化。涉及的项目是一个基于 Express.js 和 PostgreSQL 的简单 notes-api。

在此项目中,需要使用网络连接两个容器。除此之外,还将学习诸如环境变量和命名卷之类的概念。因此,事不宜迟,让我们直接开始。

如何运行数据库服务

该项目中的数据库服务器是一个简单的 PostgreSQL 服务,使用官方的 postgres 镜像。

根据官方文档,为了使用此镜像运行容器,必须提供 POSTGRES_PASSWORD 环境变量。除此之外,还将使用 POSTGRES_DB 环境变量为默认数据库提供一个名称。默认情况下,PostgreSQL 监听 5432 端口,因此也需要公开它。

要运行数据库服务,可以执行以下命令:

docker container run \--detach \--name=notes-db \--env POSTGRES_DB=notesdb \--env POSTGRES_PASSWORD=secret \--network=notes-api-network \postgres:12# a7b287d34d96c8e81a63949c57b83d7c1d71b5660c87f5172f074bd1606196dcdocker container ls# CONTAINER ID   IMAGE         COMMAND                  CREATED              STATUS              PORTS      NAMES
# a7b287d34d96   postgres:12   "docker-entrypoint.s…"   About a minute ago   Up About a minute   5432/tcp   notes-db

container run 和 container create 命令的 --env 选项可用于向容器提供环境变量。如你所见,数据库容器已成功创建并且正在运行。

尽管容器正在运行,但是存在一个小问题。PostgreSQL、MongoDB 和 MySQL 等数据库将其数据保留在目录中。PostgreSQL使用容器内的 /var/lib/postgresql/data 目录来持久化数据。

现在,如果容器由于某种原因被破坏怎么办?将丢失所有数据。为了解决此问题,可以使用命名卷。

如何在 Docker 中使用命名卷

之前,已经使用了绑定挂载和匿名卷。命名卷与匿名卷非常相似,不同之处在于可以使用其名称引用命名卷。

卷也是 Docker 中的逻辑对象,可以使用命令行进行操作。volume create 命令可用于创建命名卷。

该命令的通用语法如下:

docker volume create <volume name>

要创建一个名为 notes-db-data 的卷,可以执行以下命令:


docker volume create notes-db-data# notes-db-datadocker volume ls# DRIVER    VOLUME NAME
# local     notes-db-data

这个卷现在可以被安装到 notes-db 容器中的 /var/lib/postgresql/data 中。为此,请停止并删除 notes-db 容器:

docker container stop notes-db# notes-dbdocker container rm notes-db# notes-db

现在运行一个新容器,并使用 --volume 或 -v 选项分配卷。

docker container run \--detach \--volume notes-db-data:/var/lib/postgresql/data \--name=notes-db \--env POSTGRES_DB=notesdb \--env POSTGRES_PASSWORD=secret \--network=notes-api-network \postgres:12# 37755e86d62794ed3e67c19d0cd1eba431e26ab56099b92a3456908c1d346791

现在检查 notes-db 容器以确保安装成功:

docker container inspect --format='{{range .Mounts}} {{ .Name }} {{end}}' notes-db#  notes-db-data

现在,这些数据将安全地存储在 notes-db-data 卷中,并且将来可以重复使用。在这里也可以使用绑定挂载代替命名卷,但是在这种情况下,我更喜欢使用命名卷。

如何从 Docker 中的容器访问日志

为了查看来自容器的日志,可以使用 container logs 命令。该命令的通用语法如下:

docker container logs <container identifier>

要从 notes-db 容器访问日志,可以执行以下命令:

docker container logs notes-db

从第 57 行的文本可以看出,数据库已启动,并准备接受来自外部的连接。该命令还有 --follow 或 -f 选项,使可以将控制台连接到日志输出并获得连续的文本流。

如何在 Docker 中创建网络并连接数据库服务

如在上一节中所学,容器必须连接到用户定义的桥接网络,才能使用容器名称相互通信。为此,请在系统中创建一个名为 notes-api-network 的网络:

docker network create notes-api-network

现在,通过执行以下命令,将 notes-db 容器连接到该网络:

docker network connect notes-api-network notes-db

如何编写 Dockerfile

转到克隆项目代码的目录。在其中,进入 notes-api/api 目录,并创建一个新的 Dockerfile。将以下代码放入文件中:

# stage one
FROM node:lts-alpine as builder# install dependencies for node-gyp
RUN apk add --no-cache python make g++WORKDIR /appCOPY ./package.json .
RUN npm install --only=prod# stage two
FROM node:lts-alpineEXPOSE 3000
ENV NODE_ENV=productionUSER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/appCOPY . .
COPY --from=builder /app/node_modules  /home/node/app/node_modulesCMD [ "node", "bin/www" ]

这是一个多阶段构建。第一阶段用于使用 node-gyp 构建和安装依赖项,第二阶段用于运行应用程序。我将简要介绍以下步骤:

阶段1使用 node:lts-alpine 作为基础,并使用 builder 作为阶段名称。
在第 5 行,安装了 python、make 和 g++。node-gyp 工具需要这三个软件包才能运行。
在第 7 行,我们将 /app 目录设置为 WORKDIR。
在第 9 和 10 行,将 package.json 文件复制到 WORKDIR 并安装所有依赖项。
第 2 阶段还使用 node-lts:alpine 作为基础镜像。
在第 16 行,将环境变量 NODE_ENV 设置为 production。这对于 API 正常运行很重要。
从第 18 行到第 20 行,将默认用户设置为 node,创建 /home/node/app 目录,并将其设置为 WORKDIR。
在第 22 行,复制了所有项目文件,在第 23 行,从 builder 阶段复制了 node_modules 目录。此目录包含运行应用程序所需的所有已构建依赖关系。
在第 25 行,设置了默认命令。
要从此 Dockerfile 构建镜像,可以执行以下命令:

docker image build --tag notes-api .

在使用该镜像运行容器之前,请确保数据库容器正在运行,并且已附加到 notes-api-network 上。

系统上,notes-db 容器正在运行,使用 notes-db-data 卷,并连接到 notes-api-network 桥接网络。

一旦确定一切就绪,就可以通过执行以下命令来运行新容器:

docker container run \--detach \--name=notes-api \--env DB_HOST=notes-db \--env DB_DATABASE=notesdb \--env DB_PASSWORD=secret \--publish=3000:3000 \--network=notes-api-network \notes-api# f9ece420872de99a060b954e3c236cbb1e23d468feffa7fed1e06985d99fb919

应该理解这个长命令,因此只简要介绍下环境变量。

notes-api 应用程序需要设置三个环境变量。它们如下:

DB_HOST - 这是数据库服务的主机。假定数据库服务和 API 都连接到同一用户定义的桥接网络,则可以使用其容器名称(在这种情况下为 notes-db)引用数据库服务。
DB_DATABASE - 此API将使用的数据库。在运行数据库服务小节,使用环境变量 POSTGRES_DB 将默认数据库名称设置为 notesdb。将在这里使用它。
DB_PASSWORD - 连接数据库的密码。这也在运行数据库服务小节涉及,使用环境变量POSTGRES_PASSWORD。
要检查容器是否正常运行,可以使用 container ls 命令:

docker container ls# CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS                    NAMES
# f9ece420872d   notes-api     "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   0.0.0.0:3000->3000/tcp   notes-api
# 37755e86d627   postgres:12   "docker-entrypoint.s…"   17 hours ago     Up 14 minutes   5432/tcp                 notes-db

容器正在运行。可以访问 http://127.0.0.1:3000/ 来查看正在使用的API。

该 API 总共有五个路由,可以在 /notes/api/api/api/routes/notes.js 文件中看到。它是用我的一个开源项目引导的。

尽管容器正在运行,但是在开始使用它之前,还有最后一件事要做。必须运行设置数据库表所必需的数据库迁移,并且可以通过在容器内执行 npm run db:migrate 命令来执行此操作。

如何在正在运行的容器中执行命令

已经了解了在停止的容器中执行命令的知识。另一种情况是在正在运行的容器内执行命令。

为此,必须使用 exec 命令在正在运行的容器内执行自定义命令。

exec 命令的通用语法如下:

docker container exec <container identifier> <command>

要执行 notes-api 容器内的 npm run db:migrate,可以执行以下命令:

docker container exec notes-api npm run db:migrate# > notes-api@ db:migrate /home/node/app
# > knex migrate:latest
#
# Using environment: production
# Batch 1 run: 1 migrations

如果要在正在运行的容器中运行交互式命令,则必须使用 -it 标志。例如,如果要访问在 notes-api 容器中运行的 shell,可以执行以下命令:

docker container exec -it notes-api sh# / # uname -a
# Linux b5b1367d6b31 5.10.9-201.fc33.x86_64 #1 SMP Wed Jan 20 16:56:23 UTC 2021 x86_64 Linux

如何在 Docker 中编写管理脚本(注意这个是作者编写的脚本,按需使用)

管理多容器项目以及网络,卷和内容意味着编写大量命令。为了简化过程,我通常会从简单的 shell脚本和 Makefile 来提高效率。

可以在 notes-api 目录中找到四个 shell 脚本。它们如下:

boot.sh - 用于启动容器(如果已存在)。
build.sh - 创建并运行容器。如果需要,它还会创建镜像,卷和网络。
destroy.sh - 删除与此项目关联的所有容器,卷和网络。
stop.sh - 停止所有正在运行的容器。
还有一个 Makefile,其中包含名为start、stop、build和 destroy 的四个目标,每个目标都调用前面提到的 shell 脚本。

如果容器在系统中处于运行状态,执行 make stop 将停止所有容器。执行 make destroy 应该停止容器并删除所有东西。确保正在 notes-api 目录中运行脚本:

make destroy# ./shutdown.sh
# stopping api container --->
# notes-api
# api container stopped ---># stopping db container --->
# notes-db
# db container stopped ---># shutdown script finished# ./destroy.sh
# removing api container --->
# notes-api
# api container removed ---># removing db container --->
# notes-db
# db container removed ---># removing db data volume --->
# notes-db-data
# db data volume removed ---># removing network --->
# notes-api-network
# network removed ---># destroy script finished

如果遇到权限拒绝错误,请在脚本上执行 chmod + x:

chmod +x boot.sh build.sh destroy.sh shutdown.sh

这里不解释这些脚本,因为它们是简单的 if-else 语句以及一些已经看过很多次的 Docker 命令。如果对 Linux Shell 有所了解,那么也应该能够理解这些脚本。

docker基本操作小结相关推荐

  1. Docker 基本操作

    基本命令 Docker 基本操作 容器操作 镜像操作 Docker 基本操作 容器操作 docker [run|start|stop|restart|kill|rm|pause|unpause] ru ...

  2. Docker 基本操作 数据卷 -- docker 数据卷基本操作、挂载数据卷

    文章目录 1. docker 数据卷基本操作 1.1 容器与数据耦合的问题 1.2 数据卷介绍 1.3 数据卷的基本语法 1.4 创建一个数据卷,并查看数据卷在宿主机的目录位置 1.5 小结 2. 挂 ...

  3. Docker 镜像小结 - 每天5分钟玩转 Docker 容器技术(21)

    本节我们对 Docker 镜像做个小结. 这一部分我们首先讨论了镜像的分层结构,然后学习了如何构建镜像,最后实践使用 Docker Hub 和本地 registry. 下面是镜像的常用操作子命令: p ...

  4. Mac 配置 docker 基本操作

    写在前面 自己在 Mac 上配置 docker 和熟悉基本操作的过程中,参考教程资料会遇到和我实际情况略有不一的地方,所以做个记录方便理解和查看.Docker 在 ubuntu win mac 上 U ...

  5. docker 基本操作Ⅲ

    1 数据卷备份恢复 - 我们一般用的最多的是把容器和本地宿主机做目录映射直接存在本地,但是还有一种就是数据卷的备份与恢复,如下就来介绍: 先来一副图来了解一下数据卷的恢复与备份 宿主机也就是我们的服务 ...

  6. Docker 镜像小结---操作指令介绍(七)

    一.搜索镜像 很多情况下我们可能需要下载某一类别的镜像,这时候我们就会用到搜索功能,也可以去 docker hub 官网页面搜索. 使用语法如下. Usage: docker search [OPTI ...

  7. docker基本操作

    以二进制源码包为例: 1.上传,解压缩 # tar xf docker-latest.tgz 2.设置环境变量 # export PATH=$PATH:/tmp/docker/docker 3.初始化 ...

  8. Docker基础学习笔记02:Docker基本操作

    文章目录 一.Docker镜像操作 (一)拉取镜像到本地 1.命令格式 2.操作演示 (二)查看本地镜像 (三)删除本地镜像 任务1:删除本地指定镜像 任务2:删除全部本地镜像 任务3.拉取镜像到本地 ...

  9. Linux安装docker及docker基本操作

    一.安装docker Docker要求运行在Centos 7上,要求系统为64位,系统内核版本3.10以上 1.uname -an 查看当前系统版本 2.yum -y install docker 下 ...

最新文章

  1. linux Fedora安装桌面,CentOS6.x\Red Hat\Fedora\Linux 安装Wine 1.7.48 桌面运行环境教程
  2. python安装3.7.2_linux环境安装python3.7.2
  3. Meet new Sentinel Go committers!
  4. php 获取 uri,获取URI地址
  5. Fortinet:行走在网络和安全融合领域的最前列
  6. 有关UITableView--cell复用问题
  7. mac php7.0 yaf 安装,MAC MAMP PRO PHP YAF 安装
  8. 头脑风暴算法代码_混乱的头脑导致混乱的代码
  9. SMARTFORMS 输出动态文本(字符长度超出255)
  10. es6新特性之Set
  11. dill:解决python的“AttributeError: Can‘t pickle local object”及无法pickle lambda函数的问题
  12. TCP/IP模型的简单解释
  13. Windows2008 R2配置FTP教程
  14. 中职学校计算机课程标准,中等职业学校课程标准发布
  15. 计算机网络物理层测试
  16. hdu 5514 2015 icpc 沈阳现场 F Frogs
  17. SwiftUI Swift 内功之如何在 Swift 中进行自动三角函数计算
  18. PC端浏览器定位(纯前端)
  19. 原创---爱普生LQ-690K打印机win7共享报错解决方案“Windows无法连接到打印机。无法找...
  20. openwrt passwall设置_和广告说再见!ADGUARD全客户端设置教程2.0

热门文章

  1. 国内计算机类三大中文学报投稿体会(转载)
  2. Operator SDK
  3. 数学之美:维特比和维特比算法
  4. 隐马尔科夫模型(HMM)模型训练:Baum-Welch算法
  5. 键盘拆开重新安装步骤_如何拆解与并重新组装你的笔记本电脑
  6. [C++]深复制与浅复制
  7. Git上传代码文件至Github
  8. 别当工具人了,手摸手教会你 Jenkins !
  9. 使用clusterProfiler进行GO富集分析
  10. 刚刚才发现,原来这四款软件可以厉害到这种程度