使用 Docker 密钥管理敏感数据

关于密钥

在 Docker Swarm 服务中,密钥是一堆数据,例如 密码、SSH 私钥、SSL 证书或其他应该 不会通过网络传输或以未加密方式存储在 Dockerfile 或 应用程序的源代码。您可以使用 Docker 密钥集中管理 此数据并将其安全地传输给需要访问 它。密钥在传输过程中加密,在 Docker 群中静态加密。给定的 secret 只能由已授予 explicit 的那些服务访问 访问权限,并且仅在这些服务任务正在运行时访问它。

您可以使用 Secret 来管理容器需要的任何敏感数据 runtime 中,但您不想存储在镜像或源代码管理中,例如:

  • 用户名和密码
  • TLS 证书和密钥
  • SSH 密钥
  • 其他重要数据,例如数据库或内部服务器的名称
  • 通用字符串或二进制内容(最大 500 kb)

注意

Docker 密钥仅适用于 swarm 服务,而不适用于 独立容器。要使用此功能,请考虑调整您的容器 作为服务运行。有状态容器通常可以以 1 的规模运行 而无需更改容器代码。

使用 Secret 的另一个用例是在 容器和一组凭证。考虑一个场景,您有 为您的应用程序提供单独的开发、测试和生产环境。 这些环境中的每一个都可以具有不同的凭证,存储在 具有相同密钥名称的 development、test 和 production 群。你 容器只需要知道 secret 的名称即可在所有 三个环境。

您还可以使用 secret 来管理非敏感数据,例如配置 文件。但是,Docker 支持使用配置来存储非敏感数据。配置被挂载到容器的 文件系统,而无需使用 RAM 磁盘。

Windows 支持

Docker 包括对 Windows 容器上的密钥的支持。哪里有 的 Diffs 中,它们在 示例如下。请记住以下显著差异:

  • Microsoft Windows 没有用于管理 RAM 磁盘的内置驱动程序,因此在 运行 Windows 容器时,密钥会以明文形式保存到 容器的根磁盘。但是,当 容器停止。此外,Windows 不支持持久化正在运行的 容器作为镜像使用或类似命令。docker commit

  • 在 Windows 上,我们建议在主机上包含 Docker 根目录的卷上启用 BitLocker,以便 确保运行容器的密钥是静态加密的。

  • 具有自定义目标的 Secret 文件不会直接绑定到 Windows 中 containers 的 S 实例,因为 Windows 不支持非目录文件绑定挂载。 相反,容器的 secret 都挂载在 (实现细节 应用程序不应依赖)。符号 链接用于从那里指向 secret 的所需目标 容器。默认目标是 。C:\ProgramData\Docker\internal\secretsC:\ProgramData\Docker\secrets

  • 创建使用 Windows 容器的服务时,要指定 密钥不支持 UID、GID 和 mode。密钥目前仅 管理员和在 容器。system

Docker 如何管理 secret

当您将密钥添加到群时,Docker 会将密钥发送给群管理器 通过双向 TLS 连接。该 secret 存储在 Raft log 中,即 加密。整个 Raft 日志在其他 manager 之间复制,确保 密钥的高可用性保证与 Swarm 的其余部分相同 管理数据。

当您向新创建或正在运行的服务授予对密钥的访问权限时, 解密的密钥将挂载到容器中的内存中文件系统中。这 挂载点在容器中的位置默认为 Linux 容器或 Windows 容器中。您还可以指定 自定义位置。/run/secrets/<secret_name>C:\ProgramData\Docker\secrets

您可以更新服务以授予其访问其他密钥的权限或撤销其 随时访问给定密钥。

如果节点是群管理器,则节点只能访问(加密的)密钥,或者 如果它正在运行已授予密钥访问权限的服务任务。 当容器任务停止运行时,共享给它的解密密钥是 从该容器的内存文件系统中卸载,并从 node 的内存。

如果节点在运行任务容器时失去与 swarm 的连接 通过访问密钥,任务容器仍然可以访问其密钥,但 在节点重新连接到 Swarm 之前,无法接收更新。

您可以随时添加或检查单个密钥,也可以列出所有密钥 秘密。您无法删除正在运行的服务所在的密钥 用。请参阅 轮换密钥 以获取 在不中断正在运行的服务的情况下删除密钥。

要更轻松地更新或回滚密钥,请考虑添加版本 number 或 date 设置为 secret 名称。通过控制能力,这变得更加容易 密钥在给定容器中的挂载点。

阅读有关 docker secret 命令的更多信息

使用这些链接阅读有关特定命令的信息,或继续阅读有关将密钥用于服务的示例

例子

本节包括三个分级示例,说明如何使用 Docker 密钥。这些示例中使用的镜像已更新,以使其 更易于使用 Docker 密钥。了解如何在 以类似的方式,请参阅在镜像中构建对 Docker 密钥的支持

注意

这些示例使用单引擎 swarm 和未扩展的服务 单纯。这些示例使用 Linux 容器,但也使用 Windows 容器 支持密钥。请参阅 Windows 支持

在 compose 文件中定义和使用密钥

和 命令都支持定义密钥 在 compose 文件中。有关详细信息,请参阅 Compose file 参考docker-composedocker stack

简单示例:密钥入门

这个简单的示例演示了 secret 如何通过几个命令工作。对于 真实示例,请继续阅读 中级示例:将 secret 与 Nginx 服务结合使用

  1. 向 Docker 添加密钥。该命令读取 standard input,因为最后一个参数(表示要读取 secret from 设置为 。docker secret create-

    $ printf "This is a secret" | docker secret create my_secret_data -
    
  2. 创建服务并授予其访问密钥的权限。默认情况下, 容器可以在 ,但 您可以使用该选项自定义容器上的文件名。redis/run/secrets/<secret_name>target

    $ docker service  create --name redis --secret my_secret_data redis:alpine
    
  3. 使用 验证任务是否正在运行且没有问题。如果 一切正常,输出看起来类似于: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)"
    
  4. 使用 获取服务任务容器的 ID,以便 您可以使用 连接到容器并读取内容 的密钥数据文件中,默认情况下,所有人都可以读取,并且具有 与 secret 的名称相同。下面的第一个命令说明了 如何找到容器 ID,第二个和第三个命令使用 shell completion 自动执行此操作。redisdocker psdocker container exec

    $ 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
    
  5. 验证 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
    
  6. 尝试删除密钥。删除失败,因为该服务是 正在运行并有权访问 secret。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
    
  7. 通过更新 服务。redis

    $ docker service update --secret-rm my_secret_data redis
    
  8. 再次重复步骤 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
    
  9. 停止并删除服务,然后从 Docker 中删除密钥。

    $ docker service rm redis
    
    $ docker secret rm my_secret_data
    

简单示例:在 Windows 服务中使用密钥

这是一个非常简单的示例,它展示了如何将机密与 Microsoft 结合使用 在 Docker for Windows 上运行的 IIS 服务在 上运行 Windows 容器 Microsoft Windows 10 的。这是一个将网页存储在 secret 中的天真示例。

此示例假定您已安装 PowerShell。

  1. 将以下内容保存到 new file 中。index.html

    <html lang="en">
      <head><title>Hello Docker</title></head>
      <body>
        <p>Hello Docker! You have deployed a HTML page.</p>
      </body>
    </html>
  2. 如果尚未初始化或加入 swarm,请初始化或加入 swarm。

    > docker swarm init
    
  3. 将文件另存为名为 的 Swarm 密钥。index.htmlhomepage

    > docker secret create homepage index.html
    
  4. 创建 IIS 服务并授予其访问密钥的权限。homepage

    > docker service create `
        --name my-iis `
        --publish published=8000,target=8000 `
        --secret src=homepage,target="\inetpub\wwwroot\index.html" `
        microsoft/iis:nanoserver
    

    注意

    从技术上讲,没有理由为此使用 secret 例;配置更合适。这个例子是 仅用于说明。

  5. 访问 IIS 服务。它应该服务 第一步中的 HTML 内容。http://localhost:8000/

  6. 删除服务和密钥。

    > docker service rm my-iis
    > docker secret rm homepage
    > docker image remove secret-test
    

中级示例:在 Nginx 服务中使用 secret

此示例分为两部分。第一部分是关于生成 站点证书,并且根本不直接涉及 Docker 密钥,但 它设置了第二个部分,用于存储 并使用站点证书和 Nginx 配置作为 Secret。

生成站点证书

为站点生成根 CA 和 TLS 证书和密钥。用于生产 sites 中,您可能希望使用服务,例如生成 TLS 证书和密钥,但此示例使用命令行工具。此步骤 有点复杂,但只是一个设置步骤,以便您拥有 作为 Docker 密钥存储的内容。如果要跳过这些子步骤, 您可以使用 Let's Encrypt 来 生成站点密钥和证书,将文件命名为 和 ,然后跳至配置 Nginx 容器Let’s Encryptsite.keysite.crt

  1. 生成根密钥。

    $ openssl genrsa -out "root-ca.key" 4096
    
  2. 使用根密钥生成 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'
    
  3. 配置根 CA。编辑一个名为 and paste 的新文件 以下内容放入其中。这会将根 CA 限制为对叶进行签名 证书,而不是中间 CA。root-ca.cnf

    [root_ca]
    basicConstraints = critical,CA:TRUE,pathlen:1
    keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
    subjectKeyIdentifier=hash
  4. 签署证书。

    $ 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
    
  5. 生成站点密钥。

    $ openssl genrsa -out "site.key" 4096
    
  6. 生成站点证书并使用站点密钥对其进行签名。

    $ openssl req -new -key "site.key" -out "site.csr" -sha256 \
              -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'
    
  7. 配置站点证书。编辑一个名为 和 的新文件 将以下内容粘贴到其中。这会限制网站 certificate 的 ID 证书,使其只能用于对服务器进行身份验证,并且 不能用于对证书进行签名。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
  8. 对站点证书进行签名。

    $ 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
    
  9. Nginx 服务不需要 and 文件,但 如果要生成新的站点证书,则需要它们。保护 文件。site.csrsite.cnfroot-ca.key

配置 Nginx 容器

  1. 生成一个非常基本的 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;
        }
    }
  2. 创建三个密钥,分别表示密钥、证书和 .您可以将任何文件存储为 secret,只要它更小即可 超过 500 KB。这允许您分离密钥、证书和 配置。在每一个 命令,最后一个参数表示要读取 secret 的 Secret 文件。在这些示例中,secret name 和 file name 相同。site.conf

    $ 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
    
  3. 创建运行 Nginx 并有权访问这三个密钥的服务。这 命令的最后一部分创建一个符号链接 从密钥位置到 ,其中 Nginx 会查找额外的配置文件。此步骤发生在 Nginx 之前 实际启动,因此如果您更改 Nginx 配置。docker service createsite.conf/etc/nginx.conf.d/

    注意

    通常,您会创建一个 Dockerfile,用于将 Dockerfile 复制到适当的位置,构建镜像,并使用自定义镜像运行容器。 此示例不需要自定义镜像。它将 the 安装到位并运行容器,所有这些都在一个步骤中完成。site.confsite.conf

    Secret 位于容器的目录中 默认情况下,这可能需要在容器中执行额外的步骤才能使 secret 中可用的 intent 的 intent 中可用。下面的示例创建了一个 symbolic 链接到文件的真实位置,以便 Nginx 可以读取它:/run/secrets/site.conf

    $ 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;'"
    

    secret 允许您指定自定义位置,而不是创建符号链接 使用选项。下面的示例说明了如何在容器内提供密钥 不使用符号链接:targetsite.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;'"
    

    和 secrets 使用简写语法,没有 自定义位置集。简短语法将密钥挂载到 '/run/secrets/ 替换为与 secret 相同的名称。在正在运行的容器中,以下内容 现在存在三个文件:site.keysite.crttarget

    • /run/secrets/site.key
    • /run/secrets/site.crt
    • /etc/nginx/conf.d/site.conf
  4. 验证 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
    
  5. 验证服务是否正常运行:您可以访问 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)
    
  6. 要在运行此示例后进行清理,请删除服务和 stored 的 secrets。nginx

    $ docker service rm nginx
    
    $ docker secret rm site.crt site.key site.conf
    

高级示例:在 WordPress 服务中使用密钥

在此示例中,您将创建具有自定义根的单节点 MySQL 服务 password 中,将凭证添加为 secrets,并创建单节点 WordPress 服务,该服务使用这些凭证连接到 MySQL。下一个示例基于此示例,向您展示如何 轮换 MySQL 密码并更新服务,以便 WordPress 服务 仍然可以连接到 MySQL。

此示例说明了使用 Docker 密钥来避免保存 敏感凭证或直接在命令中传递它们 线。

注意

为简单起见,此示例使用单引擎 swarm,并使用 单节点 MySQL 服务,因为单个 MySQL 服务器实例不能 只需使用复制的服务即可进行扩展,并设置 MySQL 集群 超出了此示例的范围。

此外,更改 MySQL 根密码并不像更改 磁盘上的文件。您必须使用查询或命令来更改 password 的 Java 中。mysqladmin

  1. 为 MySQL 生成随机字母数字密码并将其存储为 Docker secret 替换为名称。要使密码更短或更长,请调整 命令。这只是创建相对随机的 密码。在以下情况下,您可以使用其他命令生成密码 选择。mysql_passworddocker secret createopenssl

    注意

    密钥创建后,您将无法更新它。您只能 remove 并重新创建它,并且您无法删除服务所在的 secret 用。但是,您可以授予或撤销正在运行的服务对 使用 .如果您需要能够更新 secret 中,请考虑向 secret 名称添加 version 组件,以便您 可以稍后添加新版本,更新服务以使用它,然后删除 旧版本。docker service update

    最后一个参数设置为 ,这表示输入是从 标准输入。-

    $ openssl rand -base64 20 | docker secret create mysql_password -
    
    l1vinzevzhj4goakjap5ya409
    

    返回的值不是密码,而是密钥的 ID。在 在本教程的其余部分,将省略 ID 输出。

    为 MySQL 用户生成第二个密钥。这个秘密不是 与稍后创建的 WordPress 服务共享。只需要 引导服务。rootmysql

    $ openssl rand -base64 20 | docker secret create mysql_root_password -
    

    使用以下方法列出 Docker 管理的密钥:docker secret ls

    $ 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
    

    密钥存储在 swarm 的加密 Raft 日志中。

  2. 创建用于通信的用户自定义覆盖网络 在 MySQL 和 WordPress 服务之间。无需公开 MySQL 服务部署到任何外部主机或容器。

    $ docker network create -d overlay mysql_private
    
  3. 创建 MySQL 服务。MySQL 服务具有以下 特性:

    • 由于规模设置为 ,因此仅运行一个 MySQL 任务。 负载平衡 MySQL 留给读者作为练习,涉及 不仅仅是扩展服务。1

    • 只能由网络上的其他容器访问。mysql_private

    • 使用卷存储 MySQL 数据,以便持久保存 across 将重新启动到服务。mydatamysql

    • 每个密钥都挂载在文件系统的 和 中。 它们永远不会作为环境变量公开,也不能提交 拖动到镜像中。该密钥是非特权 WordPress 容器用来 连接到 MySQL。tmpfs/run/secrets/mysql_password/run/secrets/mysql_root_passworddocker commitmysql_password

    • 设置环境变量并指向 files 和 . 当 首次初始化 system 数据库。之后, 密码存储在 MySQL 系统数据库本身中。MYSQL_PASSWORD_FILEMYSQL_ROOT_PASSWORD_FILE/run/secrets/mysql_password/run/secrets/mysql_root_passwordmysql

    • 设置环境变量和 .新的 调用的数据库是在容器启动时创建的,并且用户仅具有此数据库的完全权限。这 用户无法创建或删除数据库或更改 MySQL 配置。MYSQL_USERMYSQL_DATABASEwordpresswordpress

      $ 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
      
  4. 使用命令验证容器是否正在运行。mysqldocker service ls

    $ docker service ls
    
    ID            NAME   MODE        REPLICAS  IMAGE
    wvnh0siktqr3  mysql  replicated  1/1       mysql:latest
    
  5. 现在 MySQL 已设置完毕,请创建一个连接到 MySQL 服务。WordPress 服务具有以下特点:

    • 由于规模设置为 ,因此仅运行一个 WordPress 任务。 负载平衡 WordPress 留给读者作为一项练习,因为 在容器上存储 WordPress 会话数据的限制 文件系统。1
    • 在主机的 30000 端口上公开 WordPress,以便您可以访问 it 来自外部主机。如果您没有 在主机的端口 80 上运行的 Web 服务器。
    • 连接到网络,以便它可以与容器通信,并在所有 swarm 上将端口 80 发布到端口 30000 节点。mysql_privatemysql
    • 有权访问密钥,但指定了不同的 目标文件名。WordPress 容器使用 挂载点 .mysql_password/run/secrets/wp_db_password
    • 将环境变量设置为文件 挂载 secret 的路径。WordPress 服务读取 MySQL password 字符串,并将其添加到配置文件中。WORDPRESS_DB_PASSWORD_FILEwp-config.php
    • 使用用户名和 password 输入并创建数据库(如果尚不存在)。wordpress/run/secrets/wp_db_passwordwordpress
    • 将其数据(例如主题和插件)存储在一个名为的卷中,以便在服务重新启动时这些文件仍然存在。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
    
  6. 使用 和 命令验证服务是否正在运行。docker service lsdocker 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 服务对 秘密,因为 WordPress 已将秘密复制到其 配置文件 .暂时不要这样做,因为我们 稍后使用它来方便轮换 MySQL 密码。mysql_passwordwp-config.php

  7. 从任何 swarm 节点访问并设置 WordPress 使用基于 Web 的向导。所有这些设置都存储在 MySQL 数据库中。WordPress 会自动为您的 WordPress 用户,这与密码 WordPress 完全不同 用于访问 MySQL。安全地存储此密码,例如在密码中 经理。轮换密钥后,您需要它来登录 WordPress。http://localhost:30000/wordpress

    继续写一两篇博客文章并安装 WordPress 插件或 主题来验证 WordPress 是否完全可操作且其状态已保存 跨服务重启。

  8. 如果您打算继续执行下一个 示例,演示如何轮换 MySQL root 密码。

示例:轮换密钥

此示例基于上一个示例。在这种情况下,您将创建一个新的 secret 替换为新的 MySQL 密码,将 and services 更新为 使用它,然后删除旧密钥。mysqlwordpress

注意

更改 MySQL 数据库的密码涉及运行额外的 查询或命令,而不是仅更改单个环境变量 或文件,因为镜像仅在数据库不设置 MySQL 密码时设置 已经存在,并且 MySQL 默认将密码存储在 MySQL 数据库中。 轮换密码或其他机密可能涉及 Docker。

  1. 创建新密码并将其存储为名为 的密钥。mysql_password_v2

    $ openssl rand -base64 20 | docker secret create mysql_password_v2 -
    
  2. 更新 MySQL 服务以使其能够访问旧密钥和新密钥。 请记住,您无法更新或重命名密钥,但可以撤销 secret 并使用新的目标文件名授予对它的访问权限。

    $ 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 服务重新启动时 第二次,它可以访问 Under 下的旧密钥和 Under 下的新密钥。/run/secrets/old_mysql_password/run/secrets/mysql_password

    即使 MySQL 服务可以访问旧密钥和新密钥 现在,WordPress 用户的 MySQL 密码尚未更改。

    注意

    此示例不会轮换 MySQL 密码。root

  3. 现在,使用 CLI 更改用户的 MySQL 密码。此命令从文件中读取旧密码和新密码 in 中,但不会在命令行上公开或保存它们 在 shell 历史记录中。wordpressmysqladmin/run/secrets

    快速执行此操作并继续下一步,因为 WordPress 会失败 连接到 MySQL 的能力。

    首先,找到容器任务的 ID。mysql

    $ 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)"'
    

    或:

    $ 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)"'
    
  4. 更新服务以使用新密码,同时保留目标 路径 。这会触发 使用 WordPress 服务和新密钥。wordpress/run/secrets/wp_db_password

    $ docker service update \
         --secret-rm mysql_password \
         --secret-add source=mysql_password_v2,target=wp_db_password \
         wordpress    
    
  5. 通过浏览以 http://localhost:30000/ 任何 swarm 节点。使用 WordPress 用户名和密码 从您在上一个任务中运行 WordPress 向导时开始。

    验证您撰写的博客文章是否仍然存在,以及您是否更改了任何 configuration 值,请验证它们是否仍处于更改状态。

  6. 从 MySQL 服务撤销对旧密钥的访问权限,以及 从 Docker 中删除旧密钥。

    $ docker service update \
         --secret-rm mysql_password \
         mysql
    
    $ docker secret rm mysql_password
    
  7. 运行以下命令以删除 WordPress 服务、MySQL 容器、 和 volumes,以及 Docker 密钥:mydatawpdata

    $ docker service rm wordpress mysql
    
    $ docker volume rm mydata wpdata
    
    $ docker secret rm mysql_password_v2 mysql_root_password
    

在镜像中构建对 Docker Secrets 的支持

如果您开发一个可以部署为服务的容器,并且需要 敏感数据(例如凭证)作为环境变量,请考虑 调整您的镜像以利用 Docker 密钥。一种方法是 确保在创建容器时传递给镜像的每个参数 也可以从文件中读取。

Docker 库中的许多 Docker 官方镜像,例如上面示例中使用的 wordpress 镜像,都是以这种方式更新的。

当您启动 WordPress 容器时,您需要为其提供参数 it 需要将它们设置为环境变量。WordPress 镜像已被 更新,以便包含 WordPress(例如 )也有可以读取 它们的值来自文件 () 。此策略可确保 该向后兼容性得到保留,同时允许您的容器读取 来自 Docker 托管密钥的信息,而不是直接传递。WORDPRESS_DB_PASSWORDWORDPRESS_DB_PASSWORD_FILE

注意

Docker 密钥不直接设置环境变量。这是一个 有意识的决策,因为环境变量可能会无意中泄露 容器之间(例如,如果您使用)。--link

在 Compose 中使用密钥


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 文件。

顶级元素定义两个 secrets 和 .secretsdb_passworddb_root_password

部署时,Docker 会创建这两个 secret 并使用 content 的 Compose 文件。

该服务使用两个密钥,并且正在使用一个密钥。dbwordpress

部署时,Docker 会在 服务业。这些文件永远不会保留在磁盘上,而是在内存中进行管理。/run/secrets/<secret_name>

每个服务都使用环境变量来指定服务的位置 对于该 secret 数据。

有关 secret 的 short 和 long 语法的更多信息,请参阅 Compose 规范