使用 Docker Secrets 管理敏感数据
关于密钥
就 Docker Swarm 服务而言,密钥(secret) 是一组数据块,例如密码、SSH 私钥、SSL 证书或其他不应通过网络传输或以未加密形式存储在 Dockerfile 或应用程序源代码中的数据。您可以使用 Docker 密钥 集中管理这些数据,并安全地将其仅传输给需要访问的容器。在 Docker Swarm 中,密钥在传输过程中和静态存储时均经过加密。只有被明确授予访问权限的服务才能访问特定密钥,且仅限于这些服务的任务运行期间。
您可以使用密钥(secrets)来管理容器在运行时所需的任何敏感数据,同时避免将这些数据存储到镜像中或提交到源代码控制中,例如:
- 用户名和密码
- TLS 证书和密钥
- SSH 密钥
- 其他重要数据,例如数据库名称或内部服务器
- 通用字符串或二进制内容(最大 500 KB)
注意
Docker 密钥仅适用于 Swarm 服务,不适用于独立容器。要使用此功能,请考虑调整您的容器以作为服务运行。有状态容器通常可以在不更改容器代码的情况下以 1 个副本数运行。
使用密钥的另一个用例是在容器和一组凭证之间提供一层抽象。考虑以下场景:您的应用程序拥有独立的开发、测试和生产环境。 每个环境都可以拥有不同的凭证,这些凭证以相同的密钥名称存储于各自的开发、测试和生产集群中。您的容器只需知道密钥的名称即可在这三种环境中正常运行。
您还可以使用密钥来管理非敏感数据,例如配置文件。但是,Docker 支持使用 配置项(configs) 来存储非敏感数据。配置项将直接挂载到容器的文件系统中,而无需使用 RAM 磁盘。
Windows 支持
Docker 在 Windows 容器中支持 Secrets。对于实现上存在差异的地方,将在下文的示例中特别说明。请留意以下主要区别:
Microsoft Windows 没有用于管理 RAM 磁盘的内置驱动程序,因此在 运行的 Windows 容器内,密钥以明文形式持久化存储到 容器的根磁盘上。但是,当容器停止时,这些密钥会被显式删除。此外,Windows 不支持使用
docker commit或类似命令将正在运行的容器作为镜像进行持久化。在 Windows 上,我们建议启用 BitLocker 对主机上包含 Docker 根目录的卷进行加密, 以确保运行中的容器的密钥在静态时得到加密。
带有自定义目标的秘密文件不会直接绑定挂载到 Windows 容器中,因为 Windows 不支持非目录文件的绑定挂载。 相反,容器的所有秘密都会挂载在
/var/run/secrets(一个应用程序不应依赖的实现细节)内。符号 链接用于从该处指向容器内秘密的期望目标。默认目标是/var/run/secrets/1。在创建使用 Windows 容器的服务时,不支持为密钥(secrets)指定 UID、GID 和权限模式。目前,只有管理员以及容器内具有
system访问权限的用户可以访问这些密钥。
Docker 如何管理密钥
当您将密钥添加到 Swarm 时,Docker 会通过双向 TLS 连接将密钥发送到 Swarm 管理器。 该密钥存储在加密的 Raft 日志中。整个 Raft 日志会复制到其他所有管理器上, 从而为密钥提供与 Swarm 其他管理数据相同的高可用性保证。
当您授予新创建或正在运行的服务对密钥的访问权限时,
解密后的密钥将被挂载到容器的内存文件系统中。容器内挂载点的默认位置在
Linux 容器中为 /run/secrets,在
Windows 容器中为 C:\ProgramData\kubernetes\secrets。您也可以指定
自定义位置。
您可以随时更新服务,以授予其访问其他密钥的权限,或撤销其对特定密钥的访问权限。
只有当节点是 Swarm 管理器,或者正在运行被授予访问权限的服务任务时,该节点才能访问(加密的)密钥。 当容器任务停止运行时,共享给它的已解密密钥将从该容器的内存文件系统中卸载,并从节点的内存中清除。
如果节点在运行访问了密钥的任务容器时与集群失去连接,该任务容器仍然可以访问其密钥,但在节点重新连接到集群之前无法接收更新。
您可以随时添加或检查单个密钥,也可以列出所有密钥。您无法删除正在运行的服务所使用的密钥。有关在不中断运行中的服务的情况下删除密钥的方法,请参阅 轮换密钥。
为了更方便地更新或回滚密钥,建议在密钥名称中添加版本号或日期。通过控制给定容器内密钥的挂载点,这一操作变得更加容易。
阅读更多关于 docker secret 命令的内容
使用这些链接来阅读有关特定命令的说明,或继续查看 关于与服务一起使用密钥的示例。
docker secret createdocker secret inspectdocker secret lsdocker secret rm--secretdocker service create的标记--secret-add和--secret-rm标志用于docker service update
示例
本节包含三个循序渐进的示例,说明如何使用 Docker Secrets。这些示例中使用的镜像已经过更新,以便更轻松地使用 Docker Secrets。有关如何以类似方式修改您自己的镜像,请参见 将 Docker Secrets 支持构建到您的镜像中。
注意
这些示例使用单引擎 Swarm 和未缩放的服务以简化说明。示例使用 Linux 容器,但 Windows 容器也支持机密。请参阅 Windows 支持。
在 Compose 文件中定义和使用密钥
docker-compose和docker stack命令都支持在 compose 文件中定义密钥。有关详细信息,请参阅
Compose 文件参考。
简单示例:开始使用密钥
这个简单的示例通过几条命令展示了密钥的工作原理。对于实际应用的示例,请继续查看 中级示例:在 Nginx 服务中使用密钥。
向 Docker 添加一个密钥。
docker secret create命令读取标准输入,因为最后一个参数(表示从中读取密钥的文件)被设置为-。$ printf "This is a secret" | docker secret create my_secret_data -创建一个
redis服务并授予其对密钥的访问权限。默认情况下,容器可以在/run/secrets/<secret_name>处访问该密钥,但您可以使用target选项自定义容器中的文件名。$ docker service create --name redis --secret my_secret_data 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 service ps redis NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS redis.1.siftice35gla redis:alpine moby Running Running 4 seconds ago \_ redis.1.whum5b7gu13e redis:alpine moby Shutdown Failed 20 seconds ago "task: non-zero exit (1)" \_ redis.1.2s6yorvd9zow redis:alpine moby Shutdown Failed 56 seconds ago "task: non-zero exit (1)" \_ redis.1.ulfzrcyaf6pg redis:alpine moby Shutdown Failed about a minute ago "task: non-zero exit (1)" \_ redis.1.wrny5v4xyps6 redis:alpine moby Shutdown Failed 2 minutes ago "task: non-zero exit (1)"使用
docker ps获取redis服务任务容器的 ID,这样你就可以使用docker container exec连接到该容器并读取机密数据文件的内容。该文件默认对所有用户可读,且文件名与机密名称相同。下面的第一条命令演示了如何查找容器 ID,而第二和第三条命令则利用 Shell 自动补全功能自动完成此操作。$ docker ps --filter name=redis -q 5cb1c2348a59 $ docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets total 4 -r--r--r-- 1 root root 17 Dec 13 22:48 my_secret_data $ docker container exec $(docker ps --filter name=redis -q) cat /run/secrets/my_secret_data This is a secret验证提交容器后密钥是否不可用。
$ docker commit $(docker ps --filter name=redis -q) committed_redis $ docker run --rm -it committed_redis cat /run/secrets/my_secret_data cat: can't open '/run/secrets/my_secret_data': No such file or directory尝试删除该密钥。删除失败,因为
redis服务正在运行并拥有对该密钥的访问权限。$ docker secret ls ID NAME CREATED UPDATED wwwrxza8sxy025bas86593fqs my_secret_data 4 hours ago 4 hours ago $ docker secret rm my_secret_data Error response from daemon: rpc error: code = 3 desc = secret 'my_secret_data' is in use by the following service: redis通过更新服务,从正在运行的
redis服务中移除对密钥的访问权限。$ docker service update --secret-rm my_secret_data redis再次执行步骤3和4,验证该服务是否已无法再访问该密钥。容器ID会发生变化,因为
service update命令会重新部署该服务。$ docker container exec -it $(docker ps --filter name=redis -q) cat /run/secrets/my_secret_data cat: can't open '/run/secrets/my_secret_data': No such file or directory停止并删除服务,并从 Docker 中移除密钥。
$ docker service rm redis $ docker secret rm my_secret_data
简单示例:在 Windows 服务中使用机密
这是一个非常简单的示例,展示了如何在运行于 Windows 10 上的 Docker for Windows(使用 Windows 容器)中与 Microsoft IIS 服务配合使用密钥。这是一个将网页内容存储在密钥中的简单示例。
本示例假设您已安装 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>如果尚未操作,请初始化或加入集群。
> docker swarm init将
index.html文件保存为名为homepage的 Swarm 密钥。> docker secret create homepage index.html创建一个 IIS 服务并授予其对
homepage密钥的访问权限。> docker service create ` --name my-iis ` --publish published=8000,target=8000 ` --secret src=homepage,target="\inetpub\wwwroot\index.html" ` microsoft/iis:nanoserver注意
从技术上讲,在此示例中使用密钥并无必要; 配置(configs)更为合适。本示例仅用于说明。
在
http://localhost:8000/访问 IIS 服务。它应该提供第一步中的 HTML 内容。删除该服务和密钥。
> docker service rm my-iis > docker secret rm homepage > docker image remove secret-test
中级示例:在 Nginx 服务中使用机密
本示例分为两个部分。 第一部分 主要涉及生成站点证书,完全不直接涉及 Docker 密钥,但它为 第二部分 做好了准备,在第二部分中,您将存储并使用站点证书和 Nginx 配置作为密钥。
生成站点证书
生成根证书颁发机构(CA)以及您站点的TLS证书和密钥。对于生产站点,您可能希望使用类似Let’s Encrypt的服务来生成TLS证书和密钥,但本示例使用命令行工具。此步骤稍显复杂,但仅是设置步骤,以便您将其存储为Docker密钥。如果您想跳过这些子步骤,可以
使用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 serversite.csr和site.cnf文件对 Nginx 服务不是必需的,但如果你想生成新的站点证书,则需要它们。请保护root-ca.key文件。
配置 Nginx 容器
生成一个非常基础的 Nginx 配置,通过 HTTPS 提供静态文件服务。 TLS 证书和密钥以 Docker 秘密的形式存储,以便可以轻松轮换。
在当前目录中,创建一个名为
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; } }创建三个密钥,分别代表密钥、证书和
site.conf。只要文件小于500 KB,您可以将任何文件存储为密钥。这允许您将密钥、证书和配置与使用它们的服务分离。在这些命令中的每一个中,最后一个参数表示在主机机器的文件系统上读取密钥的路径。在这些示例中,密钥名称和文件名称相同。$ docker secret create site.key site.key $ docker secret create site.crt site.crt $ docker secret create site.conf site.conf$ docker secret ls ID NAME CREATED UPDATED 2hvoi9mnnaof7olr3z5g3g7fp site.key 58 seconds ago 58 seconds ago aya1dh363719pkiuoldpter4b site.crt 24 seconds ago 24 seconds ago zoa5df26f7vpcoz42qf2csth8 site.conf 11 seconds ago 11 seconds ago创建一个运行 Nginx 并可以访问三个密钥的服务。命令的最后部分会在
docker service create的位置为site.conf密钥创建一个符号链接,指向/etc/nginx.conf.d/,这是 Nginx 查找额外配置文件的位置。此步骤在 Nginx 实际启动之前执行,因此如果更改 Nginx 配置,无需重建镜像。注意
通常你会创建一个 Dockerfile,将
site.conf放入其中,构建镜像,并使用自定义镜像运行容器。 此示例不需要自定义镜像。它在一个步骤中将site.conf放入并运行容器。密钥默认位于容器内的
/run/secrets/目录中,这可能需要在容器中采取额外步骤,将密钥可用路径更改为其他路径。下面的示例为site.conf文件的真实位置创建了一个符号链接,以便 Nginx 可以读取它:$ docker service create \ --name nginx \ --secret site.key \ --secret site.crt \ --secret site.conf \ --publish published=3000,target=443 \ nginx:latest \ sh -c "ln -s /run/secrets/site.conf /etc/nginx/conf.d/site.conf && exec nginx -g 'daemon off;'"与创建符号链接不同,secrets 允许您使用
target选项指定自定义位置。下面的示例说明了如何在不使用符号链接的情况下,将site.conf秘密在容器内提供给/etc/nginx/conf.d/site.conf。$ docker service create \ --name nginx \ --secret site.key \ --secret site.crt \ --secret source=site.conf,target=/etc/nginx/conf.d/site.conf \ --publish published=3000,target=443 \ nginx:latest \ sh -c "exec nginx -g 'daemon off;'"site.key和site.crt秘密使用简写语法,没有设置自定义的target位置。简写语法将秘密挂载在/run/secrets/下,使用与秘密相同的名字。在运行的容器中,现在存在以下三个文件:/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://localhost: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.org">nginx.org</a>.<br/> Commercial support is available at <a href="https://www.nginx.com">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>$ openssl s_client -connect localhost: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服务和存储的密钥以清理。$ docker service rm nginx $ docker secret rm site.crt site.key site.conf
高级示例:在 WordPress 服务中使用密钥
在本示例中,您将创建一个具有自定义 root 密码的单节点 MySQL 服务,将凭据添加为密钥,并创建一个单节点 WordPress 服务,该服务使用这些凭据连接到 MySQL。 下一个示例 在此基础上构建,向您展示如何轮换 MySQL 密码并更新服务,以便 WordPress 服务仍能连接到 MySQL。
此示例说明了一些使用 Docker 秘密的技术,以避免在镜像中保存敏感凭据或将它们直接在命令行上传递。
注意
此示例为简化起见使用单引擎 swarm,并使用单节点 MySQL 服务,因为单个 MySQL 服务器实例无法通过简单地使用复制服务来扩展,而设置 MySQL 集群超出了本示例的范围。
此外,更改 MySQL root 密码并不像更改磁盘上的文件那么简单。您必须使用查询或
mysqladmin命令在 MySQL 中更改密码。
为 MySQL 生成一个随机的字母数字密码,并使用
docker secret create命令将其作为 Docker 秘密存储,名称为mysql_password。要使密码更短或更长,请调整openssl命令的最后一个参数。这只是一种创建相对随机密码的方法。如果您选择,可以使用其他命令生成密码。注意
创建密钥后,您无法更新它。您只能删除并重新创建它,并且无法删除正在被服务使用的密钥。但是,您可以使用
docker service update授予或撤销正在运行的服务对密钥的访问权限。如果您需要更新密钥的能力,请考虑在密钥名称中添加版本组件,以便您可以稍后添加新版本,更新服务以使用它,然后删除旧版本。最后一个参数设置为
-,表示输入从标准输入读取。$ openssl rand -base64 20 | docker secret create mysql_password - l1vinzevzhj4goakjap5ya409返回的值不是密码,而是密钥的ID。在本教程的其余部分中,将省略输出的ID。
为 MySQL
root用户生成第二个密钥。此密钥不会与稍后创建的 WordPress 服务共享。它仅用于引导mysql服务。$ openssl rand -base64 20 | docker secret create mysql_root_password -使用
docker secret ls列出由 Docker 管理的密钥:$ docker secret ls ID NAME CREATED UPDATED l1vinzevzhj4goakjap5ya409 mysql_password 41 seconds ago 41 seconds ago yvsczlx9votfw3l0nz5rlidig mysql_root_password 12 seconds ago 12 seconds ago密钥存储在群集的加密Raft日志中。
创建一个用户定义的覆盖网络,用于 MySQL 和 WordPress 服务之间的通信。无需将 MySQL 服务暴露给任何外部主机或容器。
$ docker network create -d overlay mysql_private创建 MySQL 服务。MySQL 服务具有以下特性:
由于 scale 被设置为
1,因此仅运行一个 MySQL 任务。 负载均衡 MySQL 留给读者自行练习,这不仅仅涉及扩展服务。仅可通过连接到
mysql_private网络的其他容器访问。使用卷
mydata来存储 MySQL 数据,以便在mysql服务的重启之间保持数据持久化。每个密钥都挂载在
tmpfs文件系统中,位于/run/secrets/mysql_password和/run/secrets/mysql_root_password。 它们从不作为环境变量公开,如果运行docker commit命令,也无法提交到镜像中。mysql_password密钥是无特权的WordPress容器用来连接到MySQL的那个。设置环境变量
MYSQL_PASSWORD_FILE和MYSQL_ROOT_PASSWORD_FILE分别指向 文件/run/secrets/mysql_password和/run/secrets/mysql_root_password。mysql镜像在首次初始化系统数据库时从这些文件读取密码字符串。之后, 密码将存储在 MySQL 系统数据库中。设置环境变量
MYSQL_USER和MYSQL_DATABASE。容器启动时会创建一个名为wordpress的新数据库,wordpress用户仅对这个数据库拥有完全权限。该用户无法创建或删除数据库,也不能更改 MySQL 配置。$ docker service create \ --name mysql \ --replicas 1 \ --network mysql_private \ --mount type=volume,source=mydata,destination=/var/lib/mysql \ --secret source=mysql_root_password,target=mysql_root_password \ --secret source=mysql_password,target=mysql_password \ -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \ -e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \ -e MYSQL_USER="wordpress" \ -e MYSQL_DATABASE="wordpress" \ mysql:latest
使用
docker service ls命令验证mysql容器正在运行。$ docker service ls ID NAME MODE REPLICAS IMAGE wvnh0siktqr3 mysql replicated 1/1 mysql:latest既然 MySQL 已经设置好了,创建一个连接到 MySQL 服务的 WordPress 服务。WordPress 服务具有以下特点:
- 由于 scale 被设置为
1,因此仅运行一个 WordPress 任务。 负载均衡 WordPress 被留作读者的练习,因为容器文件系统中存储 WordPress 会话数据存在限制。 - 在主机的30000端口上暴露 WordPress,以便您可以从外部主机访问 它。如果主机的80端口上没有运行 Web 服务器,您可以改用80端口。
- 连接到
mysql_private网络以便与mysql容器通信,并将端口 80 发布到所有 swarm 节点上的端口 30000。 - 访问到
mysql_password秘钥,但在容器中指定了不同的目标文件名。WordPress 容器使用挂载点/run/secrets/wp_db_password。 - 将环境变量
WORDPRESS_DB_PASSWORD_FILE设置为挂载密钥的文件 路径。WordPress 服务从该文件读取 MySQL 密码字符串,并将其添加到wp-config.php配置文件中。 - 连接到MySQL容器,使用用户名
wordpress和/run/secrets/wp_db_password中的密码,并在不存在时创建wordpress数据库。 - 将主题和插件等数据存储在一个名为
wpdata的卷中,以便在服务重启时这些文件仍然存在。
$ docker service create \ --name wordpress \ --replicas 1 \ --network mysql_private \ --publish published=30000,target=80 \ --mount type=volume,source=wpdata,destination=/var/www/html \ --secret source=mysql_password,target=wp_db_password \ -e WORDPRESS_DB_USER="wordpress" \ -e WORDPRESS_DB_PASSWORD_FILE="/run/secrets/wp_db_password" \ -e WORDPRESS_DB_HOST="mysql:3306" \ -e WORDPRESS_DB_NAME="wordpress" \ wordpress:latest- 由于 scale 被设置为
使用
docker service ls和docker service ps命令验证服务是否正在运行。$ docker service ls ID NAME MODE REPLICAS IMAGE wvnh0siktqr3 mysql replicated 1/1 mysql:latest nzt5xzae4n62 wordpress replicated 1/1 wordpress:latest$ docker service ps wordpress ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS aukx6hgs9gwc wordpress.1 wordpress:latest moby Running Running 52 seconds ago在这一点上,你实际上可以撤销 WordPress 服务对
mysql_password密钥的访问权限,因为 WordPress 已将密钥复制到其配置文件wp-config.php中。现在先不要这样做,因为我们稍后会用它来方便地轮换 MySQL 密码。从任何 swarm 节点访问
http://localhost:30000/,并使用基于 Web 的向导设置 WordPress。 所有这些设置都存储在 MySQL 的wordpress数据库中。WordPress 会自动为您的 WordPress 用户生成一个密码,该密码与 WordPress 用于访问 MySQL 的密码完全不同。请将此密码安全地保存,例如使用密码管理器。在 轮换密钥 后,您需要使用它登录 WordPress。继续写一两篇博客文章,并安装一个 WordPress 插件或主题,以验证 WordPress 是否完全正常运行,并且其状态在服务重启后仍被保存。
如果不打算继续执行下一个示例(演示如何轮换 MySQL root 密码),请不要清理任何服务或密钥。
示例:轮换密钥
此示例基于前一个示例。在此场景中,您将创建一个包含新 MySQL 密码的新密钥,更新 mysql 和 wordpress 服务以使用它,然后删除旧密钥。
注意
更改 MySQL 数据库的密码需要运行额外的查询或命令,而不仅仅是更改单个环境变量或文件,因为只有在数据库不存在时,镜像才会设置 MySQL 密码,而 MySQL 默认将密码存储在 MySQL 数据库中。轮换密码或其他密钥可能需要在 Docker 之外进行额外的步骤。
创建新密码并将其存储为名为
mysql_password_v2的密钥。$ openssl rand -base64 20 | docker secret create mysql_password_v2 -更新 MySQL 服务,使其能够访问旧的和新的密钥。 请记住,您不能更新或重命名密钥,但可以撤销密钥并使用新的目标文件名授予对其的访问权限。
$ docker service update \ --secret-rm mysql_password mysql $ docker service update \ --secret-add source=mysql_password,target=old_mysql_password \ --secret-add source=mysql_password_v2,target=mysql_password \ mysql更新服务会导致其重新启动,当 MySQL 服务第二次重新启动时,它可以在
/run/secrets/old_mysql_password下访问旧的密钥,并在/run/secrets/mysql_password下访问新的密钥。尽管MySQL服务现在可以访问旧秘密和新秘密, 但WordPress用户的MySQL密码尚未更改。
注意
此示例不会轮换 MySQL
root密码。现在,使用
mysqladminCLI 为wordpress用户更改 MySQL 密码。此命令从/run/secrets中的文件读取旧密码和新密码,但不会在命令行中显示它们或将它们保存在 shell 历史记录中。快速完成此操作并进入下一步,因为 WordPress 将失去连接到 MySQL 的能力。
首先,找到
mysql容器任务的ID。$ docker ps --filter name=mysql -q c7705cf6176f在下面的命令中替换ID,或者使用第二种变体,它使用shell扩展在单个步骤中完成所有操作。
$ docker container exec <CONTAINER_ID> \ bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'Or:
$ docker container exec $(docker ps --filter name=mysql -q) \ bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'更新
wordpress服务以使用新密码,目标路径保持在/run/secrets/wp_db_password。这将触发 WordPress 服务的滚动重启,并使用新的密钥。$ docker service update \ --secret-rm mysql_password \ --secret-add source=mysql_password_v2,target=wp_db_password \ wordpress通过再次在任何 swarm 节点上浏览到 http://localhost:30000/ 来验证 WordPress 是否正常工作。使用在上一个任务中运行 WordPress 向导时设置的 WordPress 用户名和密码。
验证你写的文章是否仍然存在,如果你更改了任何配置值,验证它们是否仍然被更改。
撤销MySQL服务对旧密钥的访问权限,并从Docker中删除旧密钥。
$ docker service update \ --secret-rm mysql_password \ mysql $ docker secret rm mysql_password运行以下命令以删除WordPress服务、MySQL容器、
mydata和wpdata卷以及Docker密钥:$ docker service rm wordpress mysql $ docker volume rm mydata wpdata $ docker secret rm mysql_password_v2 mysql_root_password
将 Docker Secrets 支持构建到您的镜像中
如果你开发了一个可以作为服务部署的容器,并且需要将敏感数据(如凭据)作为环境变量,考虑让你的镜像利用 Docker 秘钥。实现这一目标的一种方法是确保在创建容器时传递给镜像的每个参数也可以从文件中读取。
Docker library 中的许多官方镜像 Docker library,例如上面示例中使用的 wordpress 镜像,都已按这种方式进行了更新。
当你启动一个 WordPress 容器时,通过将其设置为环境变量来为其提供所需的参数。WordPress 镜像已更新,因此包含 WordPress 重要数据的环境变量(如 WORDPRESS_DB_PASSWORD)也具有可以从文件读取其值的变体(WORDPRESS_DB_PASSWORD_FILE)。这种策略确保了向后兼容性得以保留,同时允许容器从 Docker 管理的密钥中读取信息,而不是直接传递。
注意
Docker 秘钥不会直接设置环境变量。这是一个有意识的决定,因为环境变量可能在容器之间无意中泄露(例如,如果你使用
--link)。
在 Compose 中使用 Secrets
services:
db:
image: mysql:latest
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_root_password
- db_password
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: db_password.txt
db_root_password:
file: db_root_password.txt
volumes:
db_data:此示例使用 Compose 文件中的两个密钥创建一个简单的 WordPress 网站。
顶级元素 secrets 定义了两个密钥 db_password 和
db_root_password。
部署时,Docker 会创建这两个密钥,并根据 Compose 文件中指定的文件内容填充它们。
服务db使用了两个密钥,而wordpress使用了一个。
当你部署时,Docker会在/run/secrets/<secret_name>下挂载一个文件到服务中。这些文件永远不会持久化到磁盘上,而是在内存中进行管理。
每个服务使用环境变量来指定服务应在何处查找这些机密数据。
更多关于 secrets 的短语法和长语法的信息可以在 Compose 规范中找到。