使用 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/
您可以隨時更新服務,以授予其訪問其他機密的許可權,或撤銷其對給定機密的訪問許可權。
一個節點只有在它是 swarm 管理器或者正在執行已授予訪問機密許可權的服務任務時,才能訪問(加密的)機密。當容器任務停止執行時,共享給它的解密機密會從該容器的記憶體檔案系統中解除安裝,並從節點的記憶體中清除。
如果一個節點在執行一個有權訪問機密的任務容器時與 swarm 斷開連線,該任務容器仍然可以訪問其機密,但在節點重新連線到 swarm 之前無法接收更新。
您可以隨時新增或檢查單個機密,或列出所有機密。您不能刪除正在執行的服務正在使用的機密。請參閱 輪換機密,瞭解一種在不中斷正在執行的服務的情況下刪除機密的方法。
為了更輕鬆地更新或回滾機密,請考慮在機密名稱中新增版本號或日期。透過控制機密在給定容器內的掛載點的能力,這變得更加容易。
閱讀更多關於 docker secret
命令的資訊
使用這些連結閱讀有關特定命令的資訊,或者繼續閱讀 關於將機密與服務結合使用的示例。
docker secret create
docker secret inspect
docker secret ls
docker secret rm
docker service create
的 `--secret` 標誌docker service update
的 `--secret-add` 和 `--secret-rm` 標誌
示例
本節包括三個漸進式示例,說明如何使用 Docker 機密。這些示例中使用的映象已經更新,以便更容易地使用 Docker 機密。要了解如何以類似方式修改您自己的映象,請參閱 在您的映象中構建對 Docker 機密的支援。
注意為簡單起見,這些示例使用單引擎 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/
` 訪問該機密,但您可以使用 `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 服務中使用機密
這是一個非常簡單的示例,展示瞭如何在 Docker for Windows 上執行的 Microsoft IIS 服務中使用機密,該服務執行在 Microsoft Windows 10 上的 Windows 容器中。這是一個簡單的示例,將網頁儲存在一個機密中。
此示例假定您已安裝 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 機密。
> 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
注意從技術上講,這個例子沒有理由使用機密;配置 更適合。這個例子僅用於說明。
在 `https://: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 server
Nginx 服務不需要 `site.csr` 和 `site.cnf` 檔案,但如果您想生成新的站點證書,則需要它們。請保護好 `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;'"
除了建立符號連結,機密還允許您使用 `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://: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 服務,將憑據作為機密新增,並建立一個使用這些憑據連線到 MySQL 的單節點 WordPress 服務。下一個例子 將在此基礎上構建,並向您展示如何輪換 MySQL 密碼並更新服務,以便 WordPress 服務仍然可以連線到 MySQL。
這個例子說明了一些使用 Docker 機密來避免在映象中儲存敏感憑證或在命令列中直接傳遞它們的技術。
注意為簡單起見,本例使用單引擎 swarm,並使用單節點 MySQL 服務,因為單個 MySQL 伺服器例項無法透過簡單地使用複製服務來擴充套件,而設定 MySQL 叢集超出了本例的範圍。
此外,更改 MySQL 的 root 密碼並不像更改磁碟上的檔案那麼簡單。您必須使用查詢或 `mysqladmin` 命令來更改 MySQL 中的密碼。
為 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 日誌中。
建立一個使用者自定義的覆蓋網路,用於 MySQL 和 WordPress 服務之間的通訊。無需將 MySQL 服務暴露給任何外部主機或容器。
$ docker network create -d overlay mysql_private
建立 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
使用 `docker service ls` 命令驗證 `mysql` 容器是否正在執行。
$ docker service ls ID NAME MODE REPLICAS IMAGE wvnh0siktqr3 mysql replicated 1/1 mysql:latest
現在 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
使用 `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 節點訪問 `https://: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` 密碼。
現在,使用 `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)"'
更新 `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 節點上瀏覽 https://: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 機密的支援
如果您開發了一個可以作為服務部署並需要敏感資料(例如憑據)作為環境變數的容器,請考慮調整您的映象以利用 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 規範 中找到。