使用 Docker Configs 存储配置数据
关于 configs
Docker swarm 服务 config 允许你在服务的镜像或运行中的容器之外存储非敏感信息,例如配置文件。这使得你可以尽可能地保持镜像通用,无需将配置文件绑定挂载到容器中或使用环境变量。
Configs 的工作方式与 secrets 类似,不同之处在于它们在静态时不加密,并且直接挂载到容器的文件系统中,不使用 RAM 磁盘。Config 可以随时添加到服务或从服务中移除,服务可以共享同一个 config。你甚至可以将 configs 与环境变量或标签结合使用,以实现最大灵活性。Config 值可以是通用字符串或二进制内容(大小不超过 500 KB)。
注意
Docker configs 仅适用于 swarm 服务,不适用于独立容器。要使用此功能,请考虑将你的容器调整为以服务形式运行,并将其扩展设置为 1。
Configs 支持 Linux 和 Windows 服务。
Windows 支持
Docker 支持 Windows 容器上的 configs,但在实现方式上存在差异,如下面的示例所示。请记住以下值得注意的差异:
带有自定义目标的 config 文件不会直接绑定挂载到 Windows 容器中,因为 Windows 不支持非目录文件绑定挂载。相反,容器的 configs 都挂载到容器内的
C:\ProgramData\Docker\internal\configs
(这是一个不应被应用程序依赖的实现细节)。符号链接用于指向容器内 config 的期望目标位置。默认目标位置是C:\ProgramData\Docker\configs
。创建使用 Windows 容器的服务时,对于 configs,不支持指定 UID、GID 和 mode 的选项。目前,configs 在容器内仅可由管理员和具有
system
访问权限的用户访问。在 Windows 上,使用
--credential-spec
并采用config://<config-name>
格式创建或更新服务。这会在容器启动前直接将 gMSA 凭据文件传递给节点。工作节点上不会将 gMSA 凭据写入磁盘。更多信息,请参考 将服务部署到 swarm。
Docker 如何管理 configs
当你将 config 添加到 swarm 时,Docker 通过相互 TLS 连接将 config 发送给 swarm manager。Config 存储在 Raft 日志中,该日志是加密的。整个 Raft 日志会在其他 manager 之间复制,从而确保 configs 具有与 swarm 管理数据的其余部分相同的高可用性保障。
当你授予新创建或正在运行的服务访问 config 的权限时,config 会作为文件挂载到容器中。在 Linux 容器中,容器内的挂载点位置默认为 /<config-name>
。在 Windows 容器中,configs 都挂载到 C:\ProgramData\Docker\configs
中,并创建符号链接指向期望的位置,该位置默认为 C:\<config-name>
。
你可以使用数字 ID 或用户/组名来设置 config 的所有权(uid
和 gid
)。你还可以指定文件权限(mode
)。这些设置对于 Windows 容器将被忽略。
- 如果未设置,config 将归运行容器命令的用户(通常是
root
)及其默认组(也通常是root
)所有。 - 如果未设置,config 具有全局可读权限(mode
0444
),除非容器内设置了umask
,在这种情况下,mode 会受到该umask
值的影响。
你可以随时更新服务,授予它访问其他 configs 的权限,或撤销其对给定 config 的访问权限。
节点仅在是 swarm manager 或正在运行已获授权访问 config 的服务任务时,才能访问 configs。当容器任务停止运行时,与其共享的 configs 会从该容器的内存文件系统中卸载,并从节点的内存中清除。
如果节点在运行具有 config 访问权限的任务容器时丢失与 swarm 的连接,该任务容器仍能访问其 configs,但在节点重新连接到 swarm 之前无法接收更新。
你可以随时添加或检查单个 config,或列出所有 configs。你无法移除正在运行的服务正在使用的 config。请参阅轮换 config,了解如何在不中断运行中服务的情况下移除 config。
为了更轻松地更新或回滚 configs,考虑在 config 名称中添加版本号或日期。通过控制 config 在给定容器内的挂载点位置,这变得更容易。
要更新堆栈,请更改你的 Compose 文件,然后重新运行 docker stack deploy -c <new-compose-file> <stack-name>
。如果在该文件中使用了新的 config,你的服务将开始使用它们。请记住,config 是不可变的,因此你无法更改现有服务的 config 文件。相反,你需要创建一个新的 config 来使用不同的文件。
你可以运行 docker stack rm
来停止应用并移除堆栈。这将移除由 docker stack deploy
使用相同堆栈名称创建的任何 config。这将移除所有 configs,包括未被服务引用的 config 以及在 docker service update --config-rm
后剩余的 config。
了解更多关于 docker config
命令的信息
使用这些链接阅读特定命令,或继续阅读关于将 configs 与服务一起使用的示例。
示例
本节包含逐步示例,说明如何使用 Docker configs。
注意
这些示例为了简单起见,使用了单引擎 swarm 和未扩展的服务。示例使用了 Linux 容器,但 Windows 容器也支持 configs。
在 compose 文件中定义和使用 configs
docker stack
命令支持在 Compose 文件中定义 configs。然而,docker compose
不支持 configs
键。有关详细信息,请参阅Compose 文件参考。
简单示例:configs 入门
这个简单示例展示了 configs 如何仅通过几个命令工作。对于实际示例,请继续阅读高级示例:将 configs 与 Nginx 服务一起使用。
将 config 添加到 Docker。
docker config create
命令读取标准输入,因为最后一个参数(表示读取 config 的文件)被设置为-
。$ echo "This is a config" | docker config create my-config -
创建一个
redis
服务,并授予它访问该 config 的权限。默认情况下,容器可以在/my-config
访问 config,但你可以使用target
选项自定义容器上的文件名。$ docker service create --name redis --config my-config redis:alpine
使用
docker service ps
验证任务是否正常运行。如果一切正常,输出将类似于此:$ docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS bkna6bpn8r1a redis.1 redis:alpine ip-172-31-46-109 Running Running 8 seconds ago
使用
docker ps
获取redis
服务任务容器的 ID,以便使用docker container exec
连接到容器并读取 config 数据文件的内容,该文件默认对所有人可读,并且名称与 config 的名称相同。下面的第一个命令说明如何找到容器 ID,第二个和第三个命令使用 shell 补全自动执行此操作。$ docker ps --filter name=redis -q 5cb1c2348a59 $ docker container exec $(docker ps --filter name=redis -q) ls -l /my-config -r--r--r-- 1 root root 12 Jun 5 20:49 my-config $ docker container exec $(docker ps --filter name=redis -q) cat /my-config This is a config
尝试移除 config。移除失败,因为
redis
服务正在运行并有权访问该 config。$ docker config ls ID NAME CREATED UPDATED fzwcfuqjkvo5foqu7ts7ls578 hello 31 minutes ago 31 minutes ago $ docker config rm my-config Error response from daemon: rpc error: code = 3 desc = config 'my-config' is in use by the following service: redis
通过更新服务,从正在运行的
redis
服务中移除对该 config 的访问权限。$ docker service update --config-rm my-config redis
再次重复步骤 3 和 4,验证服务不再有权访问该 config。容器 ID 是不同的,因为
service update
命令会重新部署服务。$ docker container exec -it $(docker ps --filter name=redis -q) cat /my-config cat: can't open '/my-config': No such file or directory
停止并移除服务,并从 Docker 中移除 config。
$ docker service rm redis $ docker config rm my-config
简单示例:在 Windows 服务中使用 configs
这是一个非常简单的示例,展示了如何将 configs 与运行在安装了 Docker for Windows 的 Microsoft Windows 10 上的 Microsoft IIS 服务一起使用(运行 Windows 容器)。这是一个简单的示例,将网页存储在 config 中。
本示例假设你已安装 PowerShell。
将以下内容保存到一个新文件
index.html
中。<html lang="en"> <head><title>Hello Docker</title></head> <body> <p>Hello Docker! You have deployed a HTML page.</p> </body> </html>
如果你尚未这样做,请初始化或加入 swarm。
docker swarm init
将
index.html
文件保存为名为homepage
的 swarm config。docker config create homepage index.html
创建一个 IIS 服务,并授予它访问
homepage
config 的权限。docker service create --name my-iis --publish published=8000,target=8000 --config src=homepage,target="\inetpub\wwwroot\index.html" microsoft/iis:nanoserver
访问
http://localhost:8000/
上的 IIS 服务。它应该会提供第一步中的 HTML 内容。移除服务和 config。
docker service rm my-iis docker config rm homepage
示例:使用模板化 config
要创建内容将使用模板引擎生成的配置,请使用 --template-driver
参数并将其参数指定为引擎名称。模板将在创建容器时渲染。
将以下内容保存到一个新文件
index.html.tmpl
中。<html lang="en"> <head><title>Hello Docker</title></head> <body> <p>Hello {{ env "HELLO" }}! I'm service {{ .Service.Name }}.</p> </body> </html>
将
index.html.tmpl
文件保存为名为homepage
的 swarm config。提供参数--template-driver
并将golang
指定为模板引擎。$ docker config create --template-driver golang homepage index.html.tmpl
创建一个运行 Nginx 并有权访问环境变量 HELLO 和该 config 的服务。
$ docker service create \ --name hello-template \ --env HELLO="Docker" \ --config source=homepage,target=/usr/share/nginx/html/index.html \ --publish published=3000,target=80 \ nginx:alpine
验证服务是否正常运行:你可以访问 Nginx 服务器,并且提供了正确的输出。
$ curl http://0.0.0.0:3000 <html lang="en"> <head><title>Hello Docker</title></head> <body> <p>Hello Docker! I'm service hello-template.</p> </body> </html>
高级示例:将 configs 与 Nginx 服务一起使用
本示例分为两部分。第一部分完全关于生成站点证书,不直接涉及 Docker configs,但它为第二部分做了准备,在第二部分中,你将站点证书作为一系列 secrets 存储和使用,并将 Nginx 配置作为 config。示例展示了如何为 config 设置选项,例如容器内的目标位置和文件权限(mode
)。
生成站点证书
为您的站点生成根 CA 和 TLS 证书及密钥。对于生产站点,您可能希望使用诸如 Let’s Encrypt
这样的服务来生成 TLS 证书和密钥,但此示例使用命令行工具。此步骤稍微有些复杂,但这只是一个设置步骤,以便您获得可以存储为 Docker Secret 的内容。如果您想跳过这些子步骤,可以使用 Let's Encrypt来生成站点密钥和证书,将文件命名为 site.key
和 site.crt
,然后跳到配置 Nginx 容器。
生成根密钥。
$ openssl genrsa -out "root-ca.key" 4096
使用根密钥生成 CSR。
$ openssl req \ -new -key "root-ca.key" \ -out "root-ca.csr" -sha256 \ -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA'
配置根 CA。编辑一个名为
root-ca.cnf
的新文件,并将以下内容粘贴到其中。这将根 CA 约束为只能签署叶证书,不能签署中间 CA。[root_ca] basicConstraints = critical,CA:TRUE,pathlen:1 keyUsage = critical, nonRepudiation, cRLSign, keyCertSign subjectKeyIdentifier=hash
签署证书。
$ openssl x509 -req -days 3650 -in "root-ca.csr" \ -signkey "root-ca.key" -sha256 -out "root-ca.crt" \ -extfile "root-ca.cnf" -extensions \ root_ca
生成站点密钥。
$ openssl genrsa -out "site.key" 4096
生成站点证书并用站点密钥签署它。
$ openssl req -new -key "site.key" -out "site.csr" -sha256 \ -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'
配置站点证书。编辑一个名为
site.cnf
的新文件,并将以下内容粘贴到其中。这将站点证书约束为只能用于验证服务器,不能用于签署证书。[server] authorityKeyIdentifier=keyid,issuer basicConstraints = critical,CA:FALSE extendedKeyUsage=serverAuth keyUsage = critical, digitalSignature, keyEncipherment subjectAltName = DNS:localhost, IP:127.0.0.1 subjectKeyIdentifier=hash
签署站点证书。
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \ -CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \ -out "site.crt" -extfile "site.cnf" -extensions server
Nginx 服务不需要
site.csr
和site.cnf
文件,但如果您想生成新的站点证书,就需要它们。保护好root-ca.key
文件。
配置 Nginx 容器
生成一个非常基本的 Nginx 配置,用于通过 HTTPS 提供静态文件。TLS 证书和密钥存储为 Docker Secret,以便轻松轮换它们。
在当前目录中,创建一个名为
site.conf
的新文件,其内容如下server { listen 443 ssl; server_name localhost; ssl_certificate /run/secrets/site.crt; ssl_certificate_key /run/secrets/site.key; location / { root /usr/share/nginx/html; index index.html index.htm; } }
创建两个 Secret,分别代表密钥和证书。您可以将任何文件存储为 Secret,只要它小于 500 KB。这允许您将密钥和证书与使用它们的服务解耦。在这些示例中,Secret 名称和文件名是相同的。
$ docker secret create site.key site.key $ docker secret create site.crt site.crt
将
site.conf
文件保存到 Docker 配置中。第一个参数是配置的名称,第二个参数是要读取的文件。$ docker config create site.conf site.conf
列出配置
$ docker config ls ID NAME CREATED UPDATED 4ory233120ccg7biwvy11gl5z site.conf 4 seconds ago 4 seconds ago
创建一个运行 Nginx 并具有访问两个 Secret 和配置权限的服务。将模式设置为
0440
,以便该文件只能由其所有者及其所有者组读取,而不是所有人。$ docker service create \ --name nginx \ --secret site.key \ --secret site.crt \ --config source=site.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \ --publish published=3000,target=443 \ nginx:latest \ sh -c "exec nginx -g 'daemon off;'"
在运行中的容器内,现在存在以下三个文件
/run/secrets/site.key
/run/secrets/site.crt
/etc/nginx/conf.d/site.conf
验证 Nginx 服务是否正在运行。
$ docker service ls ID NAME MODE REPLICAS IMAGE zeskcec62q24 nginx replicated 1/1 nginx:latest $ docker service ps nginx NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS nginx.1.9ls3yo9ugcls nginx:latest moby Running Running 3 minutes ago
验证服务是否正常运行:您可以访问 Nginx 服务器,并且正在使用正确的 TLS 证书。
$ curl --cacert root-ca.crt https://0.0.0.0:3000 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support, refer to <a href="https://nginx.ac.cn">nginx.org</a>.<br/> Commercial support is available at <a href="https://www.nginx.com">www.nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
$ openssl s_client -connect 0.0.0.0:3000 -CAfile root-ca.crt CONNECTED(00000003) depth=1 /C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA verify return:1 depth=0 /C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost verify return:1 --- Certificate chain 0 s:/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost i:/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA --- Server certificate -----BEGIN CERTIFICATE----- … -----END CERTIFICATE----- subject=/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost issuer=/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA --- No client certificate CA names sent --- SSL handshake has read 1663 bytes and written 712 bytes --- New, TLSv1/SSLv3, Cipher is AES256-SHA Server public key is 4096 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : AES256-SHA Session-ID: A1A8BF35549C5715648A12FD7B7E3D861539316B03440187D9DA6C2E48822853 Session-ID-ctx: Master-Key: F39D1B12274BA16D3A906F390A61438221E381952E9E1E05D3DD784F0135FB81353DA38C6D5C021CB926E844DFC49FC4 Key-Arg : None Start Time: 1481685096 Timeout : 300 (sec) Verify return code: 0 (ok)
除非您要继续下一个示例,否则在运行此示例后进行清理,通过移除
nginx
服务以及存储的 Secret 和配置。$ docker service rm nginx $ docker secret rm site.crt site.key $ docker config rm site.conf
您现在已经配置了一个 Nginx 服务,其配置与镜像解耦。您可以使用完全相同的镜像运行多个站点,但使用不同的配置,完全无需构建自定义镜像。
示例:轮换 config
要轮换配置,您首先保存一个新配置,其名称与当前使用的配置不同。然后重新部署服务,移除旧配置并在容器内的同一挂载点添加新配置。此示例通过轮换 site.conf
配置文件,在前一个示例的基础上构建。
在本地编辑
site.conf
文件。将index.php
添加到index
行,并保存文件。server { listen 443 ssl; server_name localhost; ssl_certificate /run/secrets/site.crt; ssl_certificate_key /run/secrets/site.key; location / { root /usr/share/nginx/html; index index.html index.htm index.php; } }
使用新的
site.conf
创建一个新的 Docker 配置,名为site-v2.conf
。$ docker config create site-v2.conf site.conf
更新
nginx
服务以使用新配置代替旧配置。$ docker service update \ --config-rm site.conf \ --config-add source=site-v2.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \ nginx
使用
docker service ps nginx
验证nginx
服务是否已完全重新部署。完成后,您可以移除旧的site.conf
配置。$ docker config rm site.conf
要进行清理,您可以移除
nginx
服务以及 Secret 和配置。$ docker service rm nginx $ docker secret rm site.crt site.key $ docker config rm site-v2.conf
您现在已经更新了 nginx
服务的配置,而无需重新构建其镜像。