文章目录
前言
本文均翻译自官方文档。
Docker引擎
Docker Engine是一种开源容器化技术,用于构建和容器化应用程序。Docker引擎作为一个CS架构应用程序:
- 守护进程(dockerd):Docker守护进程监听Docker API请求,管理Docker对象,如镜像、容器、网络和卷。守护进程还可以与其他守护进程通信以管理Docker服务。
- 客户端(docker):Docker客户端是许多Docker用户与Docker交互的主要方式。当您使用诸如
docker run
之类的命令时,客户端将这些命令发送给dockerd,后者执行这些命令。docker命令使用docker API。Docker客户端可以与多个守护进程通信。 - Docker Registers:Docker注册中心存储Docker镜像。Docker Hub是一个任何人都可以使用的公共注册中心,默认情况下Docker会在Docker Hub上查找镜像。
下面的命令运行一个ubuntu容器,以交互方式连接到本地命令行会话,并运行/bin/bash
bash
docker run -i -t ubuntu /bin/bash
当您运行此命令时,会发生以下情况:
- 如果你在本地没有ubuntu镜像,Docker会从你配置的注册表中提取它,就像你手动运行
docker pull ubuntu
一样。 - Docker创建一个新容器,就像您手动运行
docker container create
命令一样。 - Docker为容器分配一个读写文件系统,作为它的最后一层。这允许运行中的容器在其本地文件系统中创建或修改文件和目录。
- Docker创建了一个网络接口,将容器连接到默认网络,因为您没有指定任何网络选项。这包括为容器分配一个IP地址。默认情况下,容器可以使用主机的网络连接连接到外部网络。
- Docker启动容器并执行
/bin/bash
由于容器以交互方式运行并连接到您的终端,因此您可以使用键盘提供输入,而Docker将输出记录到您的终端。 - 当您运行
exit
命令终止/bin/bash
命令时,容器会停止,但不会被移除。您可以重新启动或删除它。
当你使用Docker时,那么你正在创建和使用镜像、容器、网络、卷、插件和其它Docker对象。学习Doker的使用,就是学习Docker对象的使用。
镜像管理
镜像是一个只读模板,带有创建Docker容器的说明。通常,一个镜像基于另一个映像,并进行一些额外的定制。例如,您可以构建一个基于ubuntu镜像的镜像,但是安装Apache web服务器和您的应用程序,以及使应用程序运行所需的配置细节。您可以创建自己的镜像,也可以只使用其他人创建并发布在注册中心的镜像。要构建自己的镜像,需要创建一个Dockerfile,该文件使用简单的语法来定义创建和运行镜像所需的步骤。Dockerfile中的每条指令都会在镜像中创建一个层。当你改变Dockerfile并重建镜像时,只有那些已经改变的层才会被重建。与其他虚拟化技术相比,这是镜像如此轻量级、小巧和快速的部分原因。
容器管理
容器是镜像的可运行实例。您可以使用Docker API或CLI创建、启动、停止、移动或删除容器。您可以将容器连接到一个或多个网络,将存储附加到其上,甚至可以根据其当前状态创建新镜像。默认情况下,容器相对较好地与其他容器及其主机隔离。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。容器是由它的镜像以及在创建或启动它时提供给它的任何配置选项定义的。当容器被删除时,任何未存储在持久存储中的对其状态的更改都会消失。
容器运行
Docker在隔离的容器中运行进程。容器是在主机上运行的进程。主机可以是本地主机,也可以是远程主机。当操作员执行docker run
时,运行的容器进程是隔离的,因为它有自己的文件系统,自己的网络和自己独立于主机的隔离进程树。基本的docker run
命令如下:
bash
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
前台运行和后台运行
当启动Docker容器时,你必须首先决定你是想在后台以分离模式运行容器,还是在默认的前台模式下运行容器。
- 要以分离模式启动容器,可以使用
-d=true
或仅使用-d
选项,要使用分离的容器进行输入/输出,请使用网络连接或共享卷。这些是必需的,因为容器不再侦听运行docker的命令行。
bash
docker run -d -p 80:80 my_image nginx -g 'daemon off;'
- 在前台模式下(默认模式),
docker run
可以在容器中启动进程,并将控制台连接到该进程的标准输入、输出和标准错误。它甚至可以假装是一个TTY并传递信号。以下是在该模式下可以配置的选项:
bash
-a=[] #附加到' STDIN ', ' STDOUT ' 或 ' STDERR '
-t #分配一个伪tty
--sig-proxy=true #将接收到的所有信号代理到进程(仅限非tty模式)
-i #保持STDIN打开,即使没有连接
如果没有-a
选项,Docker将默认附加到STDOUT
和STDERR
,对于交互式进程,必须同时使用-i -t
(-it
),以便为容器进程分配tty。
容器识别
可以通过以下三种方式识别一个容器:
- UUID长识别符
- UUID短识别符
- 名称
UUID标识符来自Docker守护进程。如果您没有使用--name
选项分配容器名称,那么守护进程将为您生成一个随机字符串名称。定义名称是为容器添加含义的方便方法。如果你指定了一个名称,你可以在Docker网络中引用容器时使用它。这适用于后台和前台Docker容器。
重启策略
使用--restart
标志,你可以指定一个重启策略,来决定一个容器在退出时应该或不应该如何重启。
no
:当容器退出时,不要自动重启容器。这是默认值。on-failure[:max-retries]
:仅当容器以非零退出状态退出时才重新启动。可以选择限制Docker守护进程尝试重新启动的次数always
:无论退出状态如何,始终重新启动容器。容器也总是在守护进程启动时启动。unless-stopped
:无论退出状态如何,总是重新启动容器,包括在守护进程启动时,除非容器在Docker守护进程停止之前被置于停止状态。
清除
默认情况下,即使在容器退出后,容器的文件系统仍然存在。这使得调试更容易(因为您可以检查最终状态),并且默认情况下保留所有数据。但是如果您正在运行短期前台进程,这些容器文件系统就会堆积起来。如果你想让Docker在容器退出时自动清理容器并删除文件系统,你可以添加--rm
标志。
容器日志
docker logs
命令用来显示正在运行的容器的日志信息。docker service logs
命令用来显示参与服务的所有容器的日志信息。记录的信息和日志格式几乎完全取决于容器的ENDPOINT
命令。
数据管理
默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着:
- 当容器不再存在时,数据不会持久存在,并且如果另一个进程需要数据,则很难从容器中取出数据。
- 容器的可写层与容器运行的主机紧密耦合。您不能轻易地将数据移到其它地方
- 写入容器的可写层需要一个存储驱动程序来管理文件系统。存储驱动程序使用Linux内核提供了一个联合文件系统。与使用直接写入主机文件系统的数据卷相比,这种额外的抽象降低了性能。
Docker提供卷和绑定挂载两种方式将容器内的文件存储到容器主机上,这样即使在容器停止后文件也能被持久化。Docker还支持在主机内存中存储文件的容器,但这样的文件不会被持久化。
无论选择使用哪种类型的挂载,从容器中看到的数据都是一样的。它以目录或容器文件系统中的单个文件的形式公开。
卷挂载
卷是保存由Docker容器生成和使用的数据的首选机制。绑定挂载依赖于主机的目录结构和操作系统,卷完全由Docker管理。
- 当您创建卷时,它存储在Docker主机上的一个目录中。当您将卷挂载到容器中时,这个目录就是挂载到容器中的目录。这与绑定挂载的工作方式类似,不同之处在于卷由Docker管理,并且与主机的核心功能隔离。
- 给定的卷可以同时安装到多个容器中。当没有正在运行的容器正在使用卷时,卷仍然对Docker可用,并且不会自动删除。您可以使用
docker volume prune
来删除未使用的卷。 - 当您挂载一个卷时,它可以是命名的或匿名的。匿名卷被赋予一个随机的名称,保证在给定的Docker主机中是唯一的。与命名卷一样,即使删除了使用匿名卷的容器,匿名卷也会持续存在,除非在创建容器时使用
--rm
标志。Docker会自动移除使用--rm
标志创建的容器的匿名卷挂载。
创建和管理卷
与绑定挂载不同,您可以在任何容器的作用域之外创建和管理卷。
- 创建卷
bash
docker volume create my-vol
- 列举卷
bash
docker volume ls
local my-vol
- 检查卷
bash
docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
- 删除卷
bash
docker volume rm my-vol
启动带有卷的容器
如果你用一个还不存在的卷启动一个容器,Docker会为你创建这个卷。下面的示例将卷myvol2挂载到容器的/app/中。
bash
docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
通过Docker Compose使用卷
下面的例子展示了一个带有卷的Docker Compose服务:
yml
services:
frontend:
image: node:lts
volumes:
- myapp:/home/node/app
volumes:
myapp:
第一次运行docker compose up
创建一个卷。当您随后运行该命令时,Docker会重用相同的卷。您可以使用docker volume create
直接在Compose外部创建卷,然后在Compose内部引用它。Yaml如下:
yml
services:
frontend:
image: node:lts
volumes:
- myapp:/home/node/app
volumes:
myapp:
external: true
使用只读卷
对于一些开发应用程序,容器需要写入到绑定挂载中,以便更改传播回Docker主机。在其他时候,容器只需要读取数据。多个容器可以挂载相同的卷。您可以同时将单个卷挂载为对某些容器的读写,而对其他容器则为只读。
bash
docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
nginx:latest
备份、恢复和迁移卷
例如,创建一个名为dbstore
的新容器:
bash
docker run -v /dbdata --name dbstore ubuntu /bin/bash
在接下来的命令中:
- 启动一个新容器并从
dbstore
容器挂载卷 - 挂载本地主机目录为
/backup
- 传递一个命令,将
dbdata
卷的内容压缩到/backup
目录下的backup.tar
文件。
bash
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
使用刚刚创建的备份,您可以将其恢复到相同的容器,或者在其他地方创建的另一个容器。
例如,创建一个名为dbstore2
的新容器:
bash
docker run -v /dbdata --name dbstore2 ubuntu /bin/bash
然后,在新容器的数据卷中解压缩备份文件:
bash
docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"
删除容器后,Docker数据卷仍然存在。有两种类型的卷需要考虑:
- 命名卷具有来自容器外部的特定源。
- 匿名卷没有特定的来源。因此,在删除容器时,您可以指示Docker引擎守护进程删除它们。
要自动删除匿名卷,使用--rm
选项:
bash
docker run --rm -v /foo -v awesome:/bar busybox top
删除所有未使用的卷并释放空间:
bash
docker volume prune
绑定挂载
与卷相比,绑定挂载的功能有限。使用绑定挂载时,主机上的文件或目录将挂载到容器中。文件或目录通过其在主机上的完整路径进行引用。该文件或目录不需要已经存在于Docker主机上。如果它还不存在,则按需创建它。绑定挂载速度很快,但它们依赖于主机的文件系统具有可用的特定目录结构。如果您正在开发新的Docker应用程序,请考虑使用命名卷。并且你不能使用Docker CLI命令直接管理绑定挂载。
用绑定挂载启动一个容器
考虑这样一种情况:您有一个目录源代码,当您构建源代码时,工件被保存到另一个目录source/target/
中。您希望工件对位于/app/
的容器可用,并且您希望每次在开发主机上构建源代码时容器都能访问新的构建。使用以下命令将目标目录绑定到/app/
容器中。
bash
docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
nginx:latest
Docker Compose使用绑定挂载
一个带有绑定挂载的Docker Compose服务是这样的:
yml
services:
frontend:
image: node:lts
volumes:
- type: bind
source: ./static
target: /opt/app/static
volumes:
myapp:
使用只读绑定挂载
对于某些开发应用程序,容器需要写入绑定挂载,以便将更改传播回Docker主机。在其他时候,容器只需要读访问。
bash
docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app,readonly \
nginx:latest
tmpfs挂载
一个tmpfs挂载不会被持久化到磁盘上,无论是在Docker主机上还是在容器中。在容器的生命周期内,容器可以使用它来存储非持久状态或敏感信息。下面的示例在Nginx容器的/app
上创建一个tmpfs挂载:
bash
docker run -d \
-it \
--name tmptest \
--mount type=tmpfs,destination=/app \
nginx:latest
TMPFS挂载允许以下两个配置选项:
选项 | 说明 |
---|---|
tmpfs-size | mpfs挂载大小(以字节为单位)。默认为无限制。 |
tmpfs-mode | 用八进制表示的tmpfs文件格式 |
挂载方式的选取
卷是在Docker容器和服务中持久化数据的首选方式。卷的一些用例包括:
- 在多个正在运行的容器之间共享数据。如果您没有显式地创建它,那么卷将在第一次挂载到容器中时被创建。当容器停止或被移除时,卷仍然存在。多个容器可以同时挂载同一个卷,可以是读写的,也可以是只读的。卷只有在显式删除时才会被删除。
- 当Docker主机不能保证具有给定的目录或文件结构时。卷帮助您将Docker主机的配置与容器运行时解耦。
- 当您希望将容器的数据存储在远程主机或云提供商上,而不是本地时。
- 当您需要将数据从一个Docker主机备份、恢复或迁移到另一个Docker主机时,卷是更好的选择。您可以停止使用卷的容器,然后备份卷的目录。
- 当你的应用程序需要在Docker Desktop上的高性能I/O时。卷存储在Linux VM而不是主机中,这意味着读写延迟更低,吞吐量更高。
- 当您的应用程序需要具有完全本机文件系统行为时。
通常,在可能的情况下应该使用卷。绑定挂载适用于以下类型的用例:
- 从主机到容器共享配置文件。这就是Docker在默认情况下为容器提供DNS解析的方式,通过将
/etc/resolv.conf
从主机挂载到每个容器中。 - 在Docker主机和容器上的开发环境之间共享源代码或构建工件。例如,您可以将Maven目标/目录挂载到容器中,并且每次在Docker主机上构建Maven项目时,容器都可以访问重新构建的工件。
- 当Docker主机的文件或目录结构保证与容器所需的绑定挂载一致时。
tmpfs挂载的最佳选取原则:
- tmpfs挂载最适合于不希望数据在主机上或容器中持久化的情况。这可能是出于安全原因,也可能是为了在应用程序需要写入大量非持久性状态数据时保护容器的性能。
挂载过程中文件和目录的默认处理行为
如果将空卷挂载到容器中存在文件或目录的目录中,则这些文件或目录将被复制到卷中。类似地,如果启动一个容器并指定一个不存在的卷,则会为您创建一个空卷。这是预填充另一个容器需要的数据的好方法。如果将绑定挂载或非空卷挂载到容器中存在某些文件或目录的目录中,则这些文件或目录将被挂载掩盖,掩盖的文件不会被删除或更改,但在挂载绑定挂载或卷时无法访问。
网络管理
容器网络是指容器相互连接和通信的能力,或者与非docker工作负载进行通信的能力。容器在默认情况下启用了网络连接。容器没有关于它所连接的网络类型的信息,或者它们的对等节点是否也是Docker工作负载。容器只能看到一个带有IP地址、网关、路由表、DNS服务和其他网络详细信息的网络接口。
用户自定义网络
您可以创建自定义的网络,并将多个容器连接到同一个网络。一旦连接到用户定义的网络,容器就可以使用容器IP地址或容器名称相互通信。下面的示例使用bridge网络驱动程序创建一个网络,并在创建的网络中运行一个容器:
bash
docker network create -d bridge my-net
docker run --network=my-net -itd --name=container3 busybox
除了用户定义的网络之外,您还可以使用--network container:<name|id>
标志格式,将一个容器直接附加到另一个容器的网络中。
网络驱动程序
Docker网络子系统提供以下驱动程序:
- bridge:默认的网络驱动程序。如果您没有指定驱动程序,这就是您正在创建的网络类型。当应用程序在需要与同一主机上的其他容器通信的容器中运行时,通常使用该驱动程序创建网络。
- host:取消容器和Docker主机之间的网络隔离,直接使用主机的网络。
- overlay:将多个Docker守护进程连接在一起,并使Swarm服务和容器能够跨节点通信。这种策略不需要做操作系统级别的路由。
- ipvlan:提供对IPv4和IPv6寻址的完全控制
- macvlan:允许您为容器分配一个MAC地址,使其看起来像网络中的物理设备。Docker守护进程通过容器的MAC地址将流量路由到容器。在处理希望直接连接到物理网络的遗留应用程序时,使用macvlan驱动程序有时是最佳选择,而不是通过Docker主机的网络堆栈路由。
- none:将容器与主机和其他容器完全隔离。
网络驱动的选取策略如下:
- 默认桥接网络非常适合运行不需要特殊网络功能的容器。
- 用户定义的桥接网络使同一Docker主机上的容器能够相互通信。用户定义的网络通常为属于公共项目或组件的多个容器定义一个隔离的网络。
- host驱动与容器共享主机的网络。当您使用这个驱动程序时,容器的网络不会与主机隔离。
- 当你需要在不同的Docker主机上运行的容器进行通信,或者当多个应用程序使用Swarm服务一起工作时,overlay驱动网络是最好的。
- 当您从虚拟机设置迁移或需要容器看起来像网络上的物理主机,每个容器都有唯一的MAC地址时,macvlan网络是最好的。
- IPvlan类似于Macvlan,但不为容器分配唯一的MAC地址。当可以分配给网络接口或端口的MAC地址数量受到限制时,请考虑使用IPvlan。
Bridge网络驱动
桥接网络使用软件桥接,允许连接到同一桥接网络的容器进行通信,同时提供与未连接到该桥接网络的容器的隔离。Docker网桥驱动程序会自动在主机上安装规则,这样不同网桥网络上的容器就不能直接相互通信。桥接网络适用于运行在同一Docker守护进程主机上的容器。当你启动Docker时,一个默认的桥接网络(也称为bridge)会自动创建,新启动的容器会连接到它,除非另有指定。您还可以创建用户定义的自定义桥接网络。自定义网桥网络优于默认网桥网络。用户自定义桥接网络和默认桥接网络的区别如下:
- 用户自定义网桥在容器之间提供自动DNS解析:默认网桥网络上的容器只能通过IP地址相互访问,在用户定义的桥接网络中,容器可以通过名称或别名相互解析。
- 用户定义桥提供更好的隔离:所有没有指定
--network
的容器都连接到默认的桥接网络。用户定义的网络提供了一个限定范围的网络,其中只有附加到该网络的容器能够进行通信。 - 可以动态地从用户定义的网络中附加和分离容器:在容器的生存期内,您可以动态地将其与用户定义的网络连接起来或断开连接。要从默认桥接网络中删除容器,需要停止容器并使用不同的网络选项重新创建它。
- 每个用户自定义的网络可单独配置:默认网桥也可以配置,但所有使用默认网桥的容器都会受到影响,而用户自定义的网桥可以单独配置。
Host网络驱动
如果您对容器使用Host网络模式,则该容器的网络堆栈不会与Docker主机隔离(容器共享主机的网络命名空间),并且容器不会获得自己的IP地址分配。例如,如果您运行一个绑定到端口80的容器,并且您使用Host网络,则容器的应用程序在主机IP地址的端口80上可用。Host模式网络可以用于以下用例:
- 优化性能
- 在容器需要处理大量端口额情况下
管理用户自定义网络
- 创建自定义网络。
bash
docker network create my-net
- 删除自定义网络。
bash
docker network rm my-net
- 连接到自定义网络
bash
#运行容器时可以加入多个网络
docker run--name my-nginx \
--network my-net \
--publish 8080:80 \
nginx:latest
#也可以将正在运行的容器加入网络
docker network connect my-net my-nginx
- 断开连接
bash
docker network disconnect my-net my-nginx
发布端口
默认情况下,当您使用docker create
或docker run
创建或运行容器时,容器不会向外部世界公开其任何端口。使用--publish
标志使端口对Docker以外的服务可用。这会在主机中创建一个防火墙规则,将容器端口映射到Docker主机上指向外部世界的端口。如果您希望使一个容器可以被其他容器访问,则不需要发布容器的端口。您可以通过将容器连接到相同的网络来启用容器间通信。
bash
docker run -p 8080:80 nginx
IP地址和主机名
默认情况下,容器会为它连接的每个Docker网络获取一个IP地址。容器从网络的IP子网中接收IP地址。Docker守护进程为容器执行动态子网划分和IP地址分配。每个网络也有一个默认的子网掩码和网关。当容器启动时,它只能使用--network
标志连接到单个网络,也可以使用docker network connect
命令将正在运行的容器连接到其他网络。在这两种情况下,都可以使用--ip
标志来指定容器在特定网络上的IP地址。同样,在Docker中,容器的主机名默认是容器的ID。可以使用--hostname
覆盖主机名。当使用docker network connect
连接到现有网络时,可以使用--alias
标志为该网络上的容器指定一个额外的网络别名。