使用 Docker 机密管理敏感数据

关于秘密

就 Docker Swarm 服务而言,秘密是一团数据,例如密码、SSH 私钥、SSL 证书或不应通过网络传输或未加密存储在 Dockerfile 或应用程序中的其他数据。源代码。您可以使用 Docker机密来集中管理这些数据,并将其安全地传输到仅需要访问它的容器。秘密在传输过程中以及在 Docker 群中静止时被加密。给定的机密只能由那些已被授予显式访问权限的服务访问,并且只能在这些服务任务运行时访问。

您可以使用机密来管理容器在运行时需要的任何敏感数据,但您不想存储在映像或源代码管理中,例如:

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

笔记

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

使用秘密的另一个用例是在容器和一组凭证之间提供一个抽象层。考虑这样一个场景,您的应用程序有单独的开发、测试和生产环境。这些环境中的每一个都可以具有不同的凭据,并以相同的秘密名称存储在开发、测试和生产群中。您的容器只需要知道密钥的名称即可在所有三个环境中运行。

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

Windows 支持

Docker 支持 Windows 容器上的机密。如果实现中存在差异,则会在下面的示例中指出。请记住以下显着差异:

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

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

  • 具有自定义目标的秘密文件不会直接绑定安装到 Windows 容器中,因为 Windows 不支持非目录文件绑定安装。相反,容器的秘密全部安装在 C:\ProgramData\Docker\internal\secrets容器内(应用程序不应依赖的实现细节)。符号链接用于从那里指向容器内秘密的所需目标。默认目标是C:\ProgramData\Docker\secrets.

  • 创建使用 Windows 容器的服务时,秘密不支持指定 UID、GID 和模式的选项。当前只有管理员和具有system容器内访问权限的用户才能访问机密。

Docker 如何管理机密

当您向 swarm 添加机密时,Docker 会通过双向 TLS 连接将该机密发送到 swarm 管理器。该秘密存储在 Raft 日志中,并且是加密的。整个 Raft 日志在其他管理器之间复制,确保机密与集群管理数据的其余部分具有相同的高可用性保证。

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

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

仅当节点是群管理器或者正在运行已被授予访问秘密的服务任务时,该节点才有权访问(加密的)秘密。当容器任务停止运行时,共享给它的解密秘密将从该容器的内存文件系统中卸载,并从节点的内存中刷新。

如果节点在运行有权访问机密的任务容器时失go与 swarm 的连接,则任务容器仍然可以访问其机密,但在节点重新连接到 swarm 之前无法接收更新。

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

要更轻松地更新或回滚机密,请考虑向机密名称添加版本号或日期。通过控制给定容器内秘密的安装点的能力,这变得更容易。

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

使用这些链接来阅读特定命令,或继续阅读 有关在服务中使用机密的示例

例子

本节包括三个分级示例,说明如何使用 Docker 秘密。这些示例中使用的映像已更新,以便更轻松地使用 Docker 机密。要了解如何以类似的方式修改您自己的映像,请参阅 在您的映像中构建对 Docker Secrets 的支持

笔记

为了简单起见,这些示例使用单引擎群和未扩展的服务。这些示例使用 Linux 容器,但 Windows 容器也支持机密。请参阅 Windows 支持

在撰写文件中定义和使用机密

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

简单的例子:开始使用秘密

这个简单的示例展示了秘密如何在几个命令中发挥作用。有关实际示例,请继续 中级示例:将机密与 Nginx 服务结合使用

  1. 向 Docker 添加一个秘密。该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. redis使用 获取服务任务容器的 ID docker ps,以便您可以docker container exec连接容器并读取 Secret 数据文件的内容,该文件默认所有人可读,且与 Secret 的名称相同。下面的第一个命令说明了如何查找容器 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
    
  5. 如果提交容器,请验证该密钥不可用。

    $ 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. 尝试消除秘密。删除失败,因为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 Windows 10 上运行 Windows 容器的 Docker for Windows 上运行的 Microsoft IIS 服务中使用机密。这是一个简单的示例,将网页存储在机密中。

此示例假设您已安装 PowerShell。

  1. 将以下内容保存到新文件中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. 如果您尚未这样做,请初始化或加入集群。

    > docker swarm init
    
  3. 将文件保存index.html为名为 的 swarm Secret homepage

    > 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
    

    笔记

    从技术上讲,没有理由在此示例中使用机密; 配置更适合。此示例仅供说明之用。

  5. 访问 IIS 服务http://localhost:8000/。它应该从第一步开始提供 HTML 内容。

  6. 删除服务和秘密。

    > 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.keysite.crt,然后跳到 配置 Nginx 容器

  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。编辑一个名为 的新文件root-ca.cnf并将以下内容粘贴到其中。这限制根 CA 只能签署叶证书而不是中间 CA。

    [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. 配置站点证书。编辑一个名为 的新文件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 服务不需要site.csr和文件,但如果您想生成新的站点证书,则需要它们。site.cnf保护root-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. 创建三个秘密,分别代表密钥、证书和 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
    
  3. 创建一个运行 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;'"
    

    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
  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. 要在运行此示例后进行清理,请删除nginx服务和存储的机密。

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

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

在此示例中,您使用自定义 root 密码创建单节点 MySQL 服务,将凭据添加为机密,并创建使用这些凭据连接到 MySQL 的单节点 WordPress 服务。下一个示例在 此示例的基础上构建,并向您展示如何轮换 MySQL 密码并更新服务,以便 WordPress 服务仍然可以连接到 MySQL。

此示例说明了一些使用 Docker 机密来避免在映像中保存敏感凭据或直接在命令行上传递它们的技术。

笔记

为了简单起见,本示例使用单引擎 swarm,并使用单节点 MySQL 服务,因为单个 MySQL 服务器实例无法仅通过复制服务进行扩展,并且设置 MySQL 集群超出了本示例的范围。

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

  1. mysql_password使用命令生成 MySQL 的随机字母数字密码,并将其存储为 Docker 机密及其名称docker secret create 。要使密码更短或更长,请调整openssl命令的最后一个参数。这只是创建相对随机密码的一种方法。如果您选择,可以使用另一个命令来生成密码。

    笔记

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

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

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

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

    为 MySQLroot用户生成第二个密钥。此机密不会与稍后创建的 WordPress 服务共享。只需要引导服务即可mysql

    $ 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
    

    这些秘密存储在集群的加密 Raft 日志中。

  2. 创建用户定义的覆盖网络,用于 MySQL 和 WordPress 服务之间的通信。无需将 MySQL 服务暴露给任何外部主机或容器。

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

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

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

    • 使用卷mydata来存储 MySQL 数据,以便在mysql服务重新启动时保持不变。

    • 每个秘密都安装在位于和 的tmpfs文件系统 中。它们永远不会作为环境变量公开,如果运行命令,也不能将它们提交到映像。这个 秘密是非特权 WordPress 容器用来连接 MySQL 的秘密。/run/secrets/mysql_password/run/secrets/mysql_root_passworddocker commitmysql_password

    • 设置环境变量MYSQL_PASSWORD_FILEMYSQL_ROOT_PASSWORD_FILE指向文件/run/secrets/mysql_password/run/secrets/mysql_root_password.mysql首次初始化系统数据库时,映像会从这些文件中读取密码字符串。之后,密码将存储在 MySQL 系统数据库本身中。

    • 设置环境变量MYSQL_USERMYSQL_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
      
  4. 使用该命令验证mysql容器是否正在运行docker service ls

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

    • 由于比例设置为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 配置文件中。
    • wordpress使用用户名和密码连接到 MySQL 容器,/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
    
  6. 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 密码。

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

    继续写一两篇博客文章并安装 WordPress 插件或主题,以验证 WordPress 是否可以完全运行,并且其状态是否在服务重新启动时保存。

  8. 如果您打算继续执行下一个示例(演示如何轮换 MySQL root 密码),请不要清除任何服务或机密。

示例:旋转秘密

此示例建立在前一个示例的基础上。在这种情况下,您使用新的 MySQL 密码创建一个新密钥,更新mysqlwordpress服务以使用它,然后删除旧密钥。

笔记

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

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

    $ openssl rand -base64 20 | docker secret create mysql_password_v2 -
    
  2. 更新 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 密码尚未更改。

    笔记

    此示例不轮换 MySQLroot密码。

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

    快速执行此操作并继续下一步,因为 WordPress 失go了连接到 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服务以使用新密码,并将目标路径保留为/run/secrets/wp_db_password。这会触发 WordPress 服务的滚动重启并使用新的密钥。

    $ docker service update \
         --secret-rm mysql_password \
         --secret-add source=mysql_password_v2,target=wp_db_password \
         wordpress    
    
  5. 再次在任意 Swarm 节点上浏览 http://localhost:30000/ 来验证 WordPress 是否正常工作。使用您在上一个任务中运行 WordPress 向导时的 WordPress 用户名和密码。

    验证您编写的博客文章是否仍然存在,如果您更改了任何配置值,请验证它们是否仍然更改。

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

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

    $ 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 容器时,您可以通过将其设置为环境变量来为其提供所需的参数。 WordPress 映像已更新,因此包含 WordPress 重要数据的环境变量(例如WORDPRESS_DB_PASSWORD)也具有可以从文件 ( WORDPRESS_DB_PASSWORD_FILE) 中读取其值的变体。此策略可确保保留向后兼容性,同时允许容器从 Docker 管理的机密中读取信息,而不是直接传递信息。

笔记

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

在 Compose 中使用 Secret


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_passworddb_root_password

部署时,Docker 会创建这两个密钥,并使用 Compose 文件中指定的文件中的内容填充它们。

db服务使用这两个秘密,并且wordpress正在使用其中一个。

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

每个服务都使用环境变量来指定该服务应在何处查找该秘密数据。

有关秘密的短语法和长语法的更多信息可以在 Compose 规范中找到。