使用 Docker 機密管理敏感資料

關於機密

在 Docker Swarm 服務中,機密(secret)是一塊資料,例如密碼、SSH 私鑰、SSL 證書或其他不應透過網路傳輸或未加密儲存在 Dockerfile 或應用程式原始碼中的資料。您可以使用 Docker 機密 來集中管理這些資料,並將其安全地傳輸到僅需要訪問它的容器。機密在傳輸過程中和在 Docker swarm 中靜止時都是加密的。給定的機密僅對那些已明確授予訪問許可權的服務可用,且僅在這些服務任務執行時可用。

您可以使用機密來管理容器在執行時需要的任何敏感資料,但您不希望將其儲存在映象或原始碼控制中,例如:

  • 使用者名稱和密碼
  • TLS 證書和金鑰
  • SSH 金鑰
  • 其他重要資料,例如資料庫或內部伺服器的名稱
  • 通用字串或二進位制內容(最大 500 kb)
注意

Docker 機密僅對 swarm 服務可用,對獨立容器不可用。要使用此功能,請考慮將您的容器調整為作為服務執行。有狀態的容器通常可以在不更改容器程式碼的情況下以 1 的規模執行。

使用機密的另一個用例是在容器和一組憑據之間提供一個抽象層。考慮一個場景,您的應用程式有獨立的開發、測試和生產環境。這些環境中的每一個都可以有不同的憑據,儲存在開發、測試和生產 swarm 中,使用相同的機密名稱。您的容器只需要知道機密的名稱即可在這三個環境中執行。

您也可以使用機密來管理非敏感資料,例如配置檔案。但是,Docker 支援使用 配置(configs) 來儲存非敏感資料。配置會直接掛載到容器的檔案系統中,而不使用 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 日誌會在其他管理器之間複製,確保機密與 swarm 管理資料的其餘部分享有相同的高可用性保證。

當您授予一個新建立或正在執行的服務訪問機密的許可權時,解密的機密會被掛載到一個記憶體檔案系統中。在 Linux 容器中,掛載點的預設位置是 `/run/secrets/`,在 Windows 容器中是 `C:\ProgramData\Docker\secrets`。您也可以指定一個自定義位置。

您可以隨時更新服務,以授予其訪問其他機密的許可權,或撤銷其對給定機密的訪問許可權。

一個節點只有在它是 swarm 管理器或者正在執行已授予訪問機密許可權的服務任務時,才能訪問(加密的)機密。當容器任務停止執行時,共享給它的解密機密會從該容器的記憶體檔案系統中解除安裝,並從節點的記憶體中清除。

如果一個節點在執行一個有權訪問機密的任務容器時與 swarm 斷開連線,該任務容器仍然可以訪問其機密,但在節點重新連線到 swarm 之前無法接收更新。

您可以隨時新增或檢查單個機密,或列出所有機密。您不能刪除正在執行的服務正在使用的機密。請參閱 輪換機密,瞭解一種在不中斷正在執行的服務的情況下刪除機密的方法。

為了更輕鬆地更新或回滾機密,請考慮在機密名稱中新增版本號或日期。透過控制機密在給定容器內的掛載點的能力,這變得更加容易。

閱讀更多關於 docker secret 命令的資訊

使用這些連結閱讀有關特定命令的資訊,或者繼續閱讀 關於將機密與服務結合使用的示例

示例

本節包括三個漸進式示例,說明如何使用 Docker 機密。這些示例中使用的映象已經更新,以便更容易地使用 Docker 機密。要了解如何以類似方式修改您自己的映象,請參閱 在您的映象中構建對 Docker 機密的支援

注意

為簡單起見,這些示例使用單引擎 swarm 和未擴充套件的服務。這些示例使用 Linux 容器,但 Windows 容器也支援機密。請參閱 Windows 支援

在 Compose 檔案中定義和使用機密

`docker-compose` 和 `docker stack` 命令都支援在 compose 檔案中定義機密。詳情請參閱 Compose 檔案參考

簡單示例:開始使用機密

這個簡單的例子展示了機密是如何通過幾個命令工作的。對於一個真實的例子,請繼續閱讀 中級示例:將機密與 Nginx 服務結合使用

  1. 向 Docker 新增一個機密。`docker secret create` 命令會讀取標準輸入,因為最後一個引數(代表要讀取機密的檔案)被設定為 `-`。

    $ printf "This is a secret" | docker secret create my_secret_data -
    
  2. 建立一個 `redis` 服務並授予它訪問該機密的許可權。預設情況下,容器可以在 `/run/secrets/` 訪問該機密,但您可以使用 `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. 使用 `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
    
  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 服務中使用機密

這是一個非常簡單的示例,展示瞭如何在 Docker for Windows 上執行的 Microsoft IIS 服務中使用機密,該服務執行在 Microsoft Windows 10 上的 Windows 容器中。這是一個簡單的示例,將網頁儲存在一個機密中。

此示例假定您已安裝 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. 如果您還沒有這樣做,請初始化或加入 swarm。

    > docker swarm init
    
  3. 將 `index.html` 檔案儲存為一個名為 `homepage` 的 swarm 機密。

    > 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. 在 `https://:8000/` 訪問 IIS 服務。它應該提供第一步的 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.key` 和 `site.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://: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 的 root 密碼並不像更改磁碟上的檔案那麼簡單。您必須使用查詢或 `mysqladmin` 命令來更改 MySQL 中的密碼。

  1. 為 MySQL 生成一個隨機的字母數字密碼,並使用 `docker secret create` 命令將其作為名為 `mysql_password` 的 Docker 機密儲存。要使密碼變短或變長,請調整 `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
    

    這些機密儲存在 swarm 的加密 Raft 日誌中。

  2. 建立一個使用者自定義的覆蓋網路,用於 MySQL 和 WordPress 服務之間的通訊。無需將 MySQL 服務暴露給任何外部主機或容器。

    $ docker network create -d overlay mysql_private
    
  3. 建立 MySQL 服務。MySQL 服務具有以下特點:

    • 由於規模設定為 `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
      
  4. 使用 `docker service ls` 命令驗證 `mysql` 容器是否正在執行。

    $ 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` 和 `/run/secrets/wp_db_password` 中的密碼連線到 MySQL 容器,如果 `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 節點訪問 `https://:30000/`,並使用基於 Web 的嚮導設定 WordPress。所有這些設定都儲存在 MySQL 的 `wordpress` 資料庫中。WordPress 會自動為您的 WordPress 使用者生成一個密碼,這與 WordPress 用於訪問 MySQL 的密碼完全不同。請安全地儲存此密碼,例如在密碼管理器中。在輪換機密後,您需要它來登入 WordPress。

    繼續寫一兩篇部落格文章,並安裝一個 WordPress 外掛或主題,以驗證 WordPress 是否完全正常執行,並且其狀態在服務重啟後得以儲存。

  8. 如果您打算繼續下一個示例,該示例演示如何輪換 MySQL root 密碼,請不要清理任何服務或機密。

示例:輪換機密

這個例子建立在前一個例子的基礎上。在這個場景中,您將建立一個帶有新 MySQL 密碼的新機密,更新 `mysql` 和 `wordpress` 服務以使用它,然後移除舊的機密。

注意

更改 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 密碼尚未更改。

    注意

    此示例不輪換 MySQL `root` 密碼。

  3. 現在,使用 `mysqladmin` CLI 更改 `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)"'
    

    或者

    $ 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 節點上瀏覽 https://:30000/ 來驗證 WordPress 是否正常工作。使用您在先前任務中執行 WordPress 嚮導時獲得的 WordPress 使用者名稱和密碼。

    驗證您寫的部落格文章仍然存在,如果您更改了任何配置值,請驗證它們仍然被更改。

  6. 從 MySQL 服務中撤銷對舊機密的訪問許可權,並從 Docker 中刪除舊機密。

    $ docker service update \
         --secret-rm mysql_password \
         mysql
    
    $ docker secret rm mysql_password
    
  7. 執行以下命令以移除 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 機密的支援

如果您開發了一個可以作為服務部署並需要敏感資料(例如憑據)作為環境變數的容器,請考慮調整您的映象以利用 Docker 機密。一種方法是確保您在建立容器時傳遞給映象的每個引數也可以從檔案中讀取。

Docker 庫中的許多 Docker 官方映象,例如上述示例中使用的 wordpress 映象,都已透過這種方式進行了更新。

當您啟動一個 WordPress 容器時,您透過將引數設定為環境變數來為其提供所需的引數。WordPress 映象已經更新,以便包含 WordPress 重要資料的環境變數(例如 `WORDPRESS_DB_PASSWORD`)也有可以從檔案中讀取其值的變體(`WORDPRESS_DB_PASSWORD_FILE`)。這種策略確保了向後相容性,同時允許您的容器從 Docker 管理的機密中讀取資訊,而不是直接傳遞。

注意

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 檔案中使用兩個機密建立了一個簡單的 WordPress 站點。

頂級元素 `secrets` 定義了兩個機密 `db_password` 和 `db_root_password`。

部署時,Docker 會建立這兩個機密,並用 Compose 檔案中指定的檔案內容填充它們。

db 服務使用這兩個機密,而 wordpress 使用其中一個。

當您部署時,Docker 會在服務的 `/run/secrets/` 下掛載一個檔案。這些檔案永遠不會持久化到磁碟上,而是在記憶體中管理。

每個服務都使用環境變數來指定服務應該在哪裡查詢該機密資料。

有關機密的短語法和長語法的更多資訊,可以在 Compose 規範 中找到。