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