docker容器技術學習筆記(12、Docker Swarm)

Docker Swarm Mode

Docker v1.12 是一個非常重要的版本,Docker 重新實現了集羣的編排方式。在此之前,提供集羣功能的 Docker Swarm 是一個單獨的軟件,而且依賴外部數據庫(比如 Consul、etcd 或 Zookeeper)。

從 v1.12 開始,Docker Swarm 的功能已經完全與 Docker Engine 集成,要管理集羣,只需要啓動 Swarm Mode。安裝好 Docker,Swarm 就已經在那裏了,服務發現也在那裏了(不需要安裝 Consul 等外部數據庫)。

相比 Kubernetes,用 Docker Swarm 創建集羣非常簡單,不需要額外安裝任何軟件,也不需要做任何額外的配置。很適合作爲學習容器編排引擎的起點。

重要概念

  • swarm

swarm 運行 Docker Engine 的多個主機組成的集羣。從 v1.12 開始,集羣管理和編排功能已經集成進 Docker Engine。當 Docker Engine 初始化了一個 swarm 或者加入到一個存在的 swarm 時,它就啓動了 swarm mode。沒啓動 swarm mode 時,Docker 執行的是容器命令;運行 swarm mode 後,Docker 增加了編排 service 的能力。Docker 允許在同一個 Docker 主機上既運行 swarm service,又運行單獨的容器。

  • node

swarm 中的每個 Docker Engine 都是一個 node,有兩種類型的 node:manager 和 worker。爲了向 swarm 中部署應用,我們需要在 manager node 上執行部署命令,manager node 會將部署任務拆解並分配給一個或多個 worker node 完成部署。manager node 負責執行編排和集羣管理工作,保持並維護 swarm 處於期望的狀態。swarm 中如果有多個 manager node,它們會自動協商並選舉出一個 leader 執行編排任務。woker node 接受並執行由 manager node 派發的任務。默認配置下 manager node 同時也是一個 worker node,不過可以將其配置成 manager-only node,讓其專職負責編排和集羣管理工作。work node 會定期向 manager node 報告自己的狀態和它正在執行的任務的狀態,這樣 manager 就可以維護整個集羣的狀態。

  • service

service 定義了 worker node 上要執行的任務。swarm 的主要編排任務就是保證 service 處於期望的狀態下。舉一個 service 的例子:在 swarm 中啓動一個 http 服務,使用的鏡像是 httpd:latest,副本數爲 3。manager node 負責創建這個 service,經過分析知道需要啓動 3 個 httpd 容器,根據當前各 worker node 的狀態將運行容器的任務分配下去,比如 worker1 上運行兩個容器,worker2 上運行一個容器。運行了一段時間,worker2 突然宕機了,manager 監控到這個故障,於是立即在 worker3 上啓動了一個新的 httpd 容器。這樣就保證了 service 處於期望的三個副本狀態。

創建swarm 集羣

swarm-manager 是 manager node,swarm-worker1 和 swarm-worker2 是 worker node。創建三節點的 swarm 集羣。

在 swarm-manager 上執行如下命令創建 swarm。

docker swarm init --advertise-addr 10.10.8.125
--advertise-addr 指定與其他 node 通信的地址。

執行 docker node ls 查看當前 swarm 的 node,目前只有一個 manager。複製前面的 docker swarm join 命令,在 swarm-worker1 和 swarm-worker2 上執行,將它們添加到 swarm 中。命令輸出如下:

docker swarm join --token SWMTKN-1-3rx3jnumdxja3rm4k5h8pijqbttoctjvy4p3fo7t0kdxwffcoz-5uwscs6wh2s9lpqg3lclv00y7 10.10.8.125:2377

docker node ls 可以看到兩個 worker node 已經添加進來了。如果當時沒有記錄下 docker swarm init 提示的添加 worker 的完整命令,可以通過 docker swarm join-token worker 查看。

運行service

現在部署一個運行 httpd 鏡像的 service,執行如下命令:
docker service create --name web_server httpd
部署 service 的命令形式與運行容器的 docker run 很相似,--name 爲 service 命名,httpd 爲鏡像的名字。

通過 docker service ls 可以查看當前 swarm 中的 service。REPLICAS 顯示當前副本信息,0/1 的意思是 web_server 這個 service 期望的容器副本數量爲 1,目前已經啓動的副本數量爲 0。也就是當前 service 還沒有部署完成。命令 docker service ps 可以查看 service 每個副本的狀態。

可以看到 service 唯一的副本被分派到 swarm-worker1,當前的狀態是 Preparing,還沒達到期望的狀態 Running,我們不僅要問,這個副本在 Preparing 什麼呢?其實答案很簡單,swarm-worker1 是在 pull 鏡像,下載完成後,副本就會處於 Running 狀態了。

service伸縮

對於 web 服務,我們通常會運行多個實例。這樣可以負載均衡,同時也能提供高可用。swarm 要實現這個目標非常簡單,增加 service 的副本數就可以了。在 swarm-manager 上執行如下命令:docker service scale web_server=5。副本數增加到 5,通過 docker service ls 和 docker service ps 查看副本的詳細信息。

默認配置下 manager node 也是 worker node,所以 swarm-manager 上也運行了副本。如果不希望在 manager 上運行 service,可以執行如下命令:docker node update --availability drain swarm-manager

我們還可以 scale down,減少副本數,運行下面的命令:
docker service scale web_server=3

實現failover

創建 service 的時候,我們沒有告訴 swarm 發生故障時該如何處理,只是說明了我們期望的狀態(比如運行3個副本),swarm 會盡最大的努力達成這個期望狀態,無論發生什麼狀況。

現在swarm-worker1上有兩個副本,swarm-worker2上有一個副本,當我們關閉 swarm-worker1。Swarm 會檢測到 swarm-worker1 的故障,並標記爲 Down。Swarm 會將 swarm-worker1 上的副本調度到其他可用節點。我們可以通過 docker service ps 觀察這個 failover 過程。可以看到,web_server.1 和 web_server.2 已經從 swarm-worker1 遷移到了 swarm-worker2,之前運行在故障節點 swarm-worker1 上的副本狀態被標記爲 Shutdown。

訪問service

服務並沒有暴露給外部網絡,只能在 Docker 主機上訪問。換句話說,當前配置下,我們無法訪問 service web_server。

要將 service 暴露到外部,方法其實很簡單,執行下面的命令:
docker service update --publish-add 8080:80 web_server

如果是新建 service,可以直接用使用 --publish 參數,比如:
docker service create --name web_server --publish 8080:80 --replicas=2 httpd

器在 80 端口上監聽 http 請求,--publish-add 8080:80 將容器的 80 映射到主機的 8080 端口,這樣外部網絡就能訪問到 service 了。curl 集羣中任何一個節點的 8080 端口,都能夠訪問到 web_server。

routing mesh

當我們訪問任何節點的 8080 端口時,swarm 內部的 load balancer 會將請求轉發給 web_server 其中的一個副本。這就是 routing mesh 的作用。

所以,無論訪問哪個節點,即使該節點上沒有運行 service 的副本,最終都能訪問到 service。另外,我們還可以配置一個外部 load balancer,將請求路由到 swarm service。比如配置 HAProxy,將請求分發到各個節點的 8080 端口。

ingress 網絡

當我們應用 --publish-add 8080:80 時,swarm 會重新配置 service,之前的所有副本都被 Shutdown,然後啓動了新的副本。

查看新副本的容器網絡配置:

# docker exec web_server.2.r2d9wt0ik82dmczxy5p2qwnv8 ip r
default via 172.18.0.1 dev eth1 
10.255.0.0/16 dev eth0  proto kernel  scope link  src 10.255.0.7 
172.18.0.0/16 dev eth1  proto kernel  scope link  src 172.18.0.3 

容器的網絡與 --publish-add 之前已經大不一樣了,現在有兩塊網卡,每塊網卡連接不同的 Docker 網絡。
實際上:
1、eth0 連接的是一個 overlay 類型的網絡,名字爲 ingress,其作用是讓運行在不同主機上的容器可以相互通信。
2、eth1 連接的是一個 bridge 類型的網絡,名字爲 docker_gwbridge,其作用是讓容器能夠訪問到外網。

# docker network  ls 
NETWORK ID          NAME                DRIVER              SCOPE
cffcec359069        bridge              bridge              local
15a7c1fbba33        docker_gwbridge     bridge              local
bae92bff8199        host                host                local
l0usihpnydn3        ingress             overlay             swarm
b7ec19400b21        mac_net1            macvlan             local
cdc188b05e3c        mac_net10           macvlan             local
870eae5eb0c3        mac_net20           macvlan             local
deb4cbe62ba0        none                null                local

ingress 網絡是 swarm 創建時 Docker 爲自動我們創建的,swarm 中的每個 node 都能使用 ingress。通過 overlay 網絡,主機與容器、容器與容器之間可以相互訪問;同時,routing mesh 將外部請求路由到不同主機的容器,從而實現了外部網絡對 service 的訪問。

service之間通信

微服務架構的應用由若干 service 組成。比如有運行 httpd 的 web 前端,有提供緩存的 memcached,有存放數據的 mysql,每一層都是 swarm 的一個 service,每個 service 運行了若干容器。在這樣的架構中,service 之間是必然要通信的。

  • 服務發現

一種實現方法是將所有 service 都 publish 出去,然後通過 routing mesh 訪問。但明顯的缺點是把 memcached 和 mysql 也暴露到外網,增加了安全隱患。
如果不 publish,那麼 swarm 就要提供一種機制,能夠:
1、讓 service 通過簡單的方法訪問到其他 service。
2、當 service 副本的 IP 發生變化時,不會影響訪問該 service 的其他 service。
3、當 service 的副本數發生變化時,不會影響訪問該 service 的其他 service。

這其實就是服務發現(service discovery)。Docker Swarm 原生就提供了這項功能,通過服務發現,service 的使用者不需要知道 service 運行在哪裏,IP 是多少,有多少個副本,就能與 service 通信。

  • 創建 overlay 網絡

要使用服務發現,需要相互通信的 service 必須屬於同一個 overlay 網絡,所以我們先得創建一個新的 overlay 網絡。

docker network create --driver overlay myapp_net

直接使用 ingress 行不行?很遺憾,目前 ingress 沒有提供服務發現,必須創建自己的 overlay 網絡。

  • 部署 service 到 overlay

部署一個 web 服務,並將其掛載到新創建的 overlay 網絡。
docker service create --name my_web --replicas=3 --network myapp_net httpd

部署一個 util 服務用於測試,掛載到同一個 overlay 網絡。
docker service create --name util --network myapp_net busybox sleep 10000000
sleep 10000000 的作用是保持 busybox 容器處於運行的狀態,我們才能夠進入到容器中訪問 service my_web。

  • 驗證

通過 docker service ps util 確認 util 所在的節點爲node1。登錄到 swarm-worker1,在容器 util.1 中 ping 服務 my_web。

# docker exec util.1.irfxosry2tzefrmvchqyanari ping -c 3 my_web
PING my_web (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.142 ms
64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.104 ms
64 bytes from 10.0.0.5: seq=2 ttl=64 time=0.092 ms

可以看到 my_web 的 IP 爲 10.0.0.2,這是哪個副本的 IP 呢?
其實哪個副本的 IP 都不是。10.0.0.2 是 my_web service 的 VIP(Virtual IP),swarm 會將對 VIP 的訪問負載均衡到每一個副本。
我們可以執行下面的命令查看每個副本的 IP。

# docker exec util.1.irfxosry2tzefrmvchqyanari nslookup tasks.my_web
Server:         127.0.0.11
Address:        127.0.0.11:53

Non-authoritative answer:
Name:   tasks.my_web
Address: 10.0.0.8
Name:   tasks.my_web
Address: 10.0.0.7
Name:   tasks.my_web
Address: 10.0.0.6

10.0.0.6、10.0.0.7、10.0.0.8 纔是各個副本自己的 IP。不過對於服務的使用者(這裏是 util.1),根本不需要知道 my_web副本的 IP,也不需要知道 my_web 的 VIP,只需直接用 service 的名字 my_web 就能訪問服務。

滾動更新service

滾動更新降低了應用更新的風險,如果某個副本更新失敗,整個更新將暫停,其他副本則可以繼續提供服務。同時,在更新的過程中,總是有副本在運行的,因此也保證了業務的連續性。

下面我們將部署三副本的服務,鏡像使用 httpd:2.2.31,然後將其更新到 httpd:2.2.32。

創建服務:
docker service create --name my_web --replicas=3 httpd:2.2.31

將 service 更新到 httpd:2.2.32:
docker service update --image httpd:2.2.32 my_web
--image 指定新的鏡像。

Swarm 將按照如下步驟執行滾動更新:
1、停止第一個副本。
2、調度任務,選擇 worker node。
3、在 worker 上用新的鏡像啓動副本。
4、如果副本(容器)運行成功,繼續更新下一個副本;如果失敗,暫停整個更新過程。

docker service ps 查看更新結果。默認配置下,Swarm 一次只更新一個副本,並且兩個副本之間沒有等待時間。我們可以通過 --update-parallelism 設置並行更新的副本數目,通過 --update-delay 指定滾動更新的間隔時間。

比如執行如下命令:
docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s my_web
service 增加到六個副本,每次更新兩個副本,間隔時間一分半鐘。docker service inspect 查看 service 的當前配置。

Swarm 還有個方便的功能是回滾,如果更新後效果不理想,可以通過 --rollback 快速恢復到更新之前的狀態。請注意,--rollback 只能回滾到上一次執行 docker service update 之前的狀態,並不能無限制地回滾。
docker service update --rollback my_web

管理數據

service 的容器副本會 scale up/down,會 failover,會在不同的主機上創建和銷燬,這就引出一個問題,如果 service 有要管理的數據,那麼這些數據應該如何存放呢?
選項一:打包在容器裏。
顯然不行。除非數據不會發生變化,否則,如何在多個副本直接保持同步呢?
選項二:數據放在 Docker 主機的本地目錄中,通過 volume 映射到容器裏。
位於同一個主機的副本倒是能夠共享這個 volume,但不同主機中的副本如何同步呢?
選項三:利用 Docker 的 volume driver,由外部 storage provider 管理和提供 volume,所有 Docker 主機 volume 將掛載到各個副本。
這是目前最佳的方案。volume 不依賴 Docker 主機和容器,生命週期由 storage provider 管理,volume 的高可用和數據有效性也全權由 provider 負責,Docker 只管使用。

replicated mode VS global mode

Swarm 可以在 service 創建或運行過程中靈活地通過 --replicas 調整容器副本的數量,內部調度器則會根據當前集羣的資源使用狀況在不同 node 上啓停容器,這就是 service 默認的 replicated mode。在此模式下,node 上運行的副本數有多有少,一般情況下,資源更豐富的 node 運行的副本數更多,反之亦然。

除了 replicated mode,service 還提供了一個 globalmode,其作用是強制在每個 node 上都運行一個且最多一個副本。

此模式特別適合需要運行 daemon 的集羣環境。比如要收集所有容器的日誌,就可以 global mode 創建 service,在所有 node 上都運行 gliderlabs/logspout 容器,即使之後有新的 node 加入,swarm 也會自動在新 node 上啓動一個 gliderlabs/logspout 副本。

 docker service create \
> --mode global \
> --name logspout \
> --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
> gliderlabs/logspout

可以通過 docker service inspect 查看 service 的 mode。

# docker service inspect logspout --pretty

ID:             bj4kj5c2egao5zu7c42tjc7uz
Name:           logspout
Service Mode:   Global
Placement:
UpdateConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Update order:      stop-first
RollbackConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Rollback order:    stop-first
ContainerSpec:
 Image:         gliderlabs/logspout:latest@sha256:90847ec896aa9f96cd558afc80e690a48d4861f4a54f92f1d56efd3ade1e5b3a
 Init:          false
Mounts:
 Target:        /var/run/docker.sock
  Source:       /var/run/docker.sock
  ReadOnly:     false
  Type:         bind
Resources:
Endpoint Mode:  vip

這裏是 Global,如果創建 service 時不指定,默認是 Replicated。

使用Label控制service的位置

使用 label可以精細控制 Service 的運行位置呢。
邏輯分兩步:
1、爲每個 node 定義 label。
2、設置 service 運行在指定 label 的 node 上。

label 可以靈活描述 node 的屬性,其形式是 key=value,用戶可以任意指定,例如將 swarm-worker1 作爲測試環境,爲其添加 label env=test:
docker node update --label-add env=test swarm-worker1

對應的,將 swarm-worker2 作爲生產環境,添加 label env=prod:
docker node update --label-add env=prod swarm-worker2

現在部署 service 到測試環境:

docker service create \
      --constraint node.labels.env==test \
      --replicas 3 \
      --name my_web \
      --publish 8080:80 \
      httpd
--constraint node.labels.env==test 限制將 service 部署到 label=test 的 node,即 swarm-worker1。

更新 service,將其遷移到生產環境:
docker service update --constraint-rm node.labels.env==test my_web
docker service update --constraint-add node.labels.env==prod my_web
刪除並添加新的 constraint,設置 node.labels.env==prod,最終所有副本都遷移到了 swarm-worker2。

label 還可以跟 global 模式配合起來使用,比如只收集生產環境中容器的日誌。

docker service create \
       --mode global \
       --constraint node.labels.env==prod \
       --name logspout \
       --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
       gliderlabs/logspout
只有 swarm-worker2 節點上纔會運行 logspout。

配置Health Check

Docker 只能從容器啓動進程的返回代碼判斷其狀態,而對於容器內部應用的運行情況基本沒有了解。
執行 docker run 命令時,通常會根據 Dockerfile 中的 CMD 或 ENTRYPOINT 啓動一個進程,這個進程的狀態就是 docker ps STATUS 列顯示容器的狀態。

命令顯示:
1、有的容器正在運行,狀態爲 UP。
2、有的容器已經正常停止了,狀態是 Exited (0)。
3、有的則因發生故障停止了,退出代碼爲非 0,例如 Exited (137)、Exited (1) 等。

Docker 支持的 Health Check 可以是任何一個單獨的命令,Docker 會在容器中執行該命令,如果返回 0,容器被認爲是 healthy,如果返回 1,則爲 unhealthy。
對於提供 HTTP 服務接口的應用,常用的 Health Check 是通過 curl 檢查 HTTP 狀態碼,比如:
curl --fail http://localhost:8080/ || exit 1
如果 curl 命令檢測到任何一個錯誤的 HTTP 狀態碼,則返回 1,Health Check 失敗。

下面我們通過例子來演示 Health Check 在 swarm 中的應用。

docker service create --name my_db \
       --health-cmd "curl --fail http://localhost:8091/pools || exit 1" \
       couchbase
--health-cmd Health Check 的命令,還有幾個相關的參數:
--timeout 命令超時的時間,默認 30s。
--interval 命令執行的間隔時間,默認 30s。
--retries 命令失敗重試的次數,默認爲 3,如果 3 次都失敗了則會將容器標記爲 unhealthy。swarm 會銷燬並重建 unhealthy 的副本。

使用Secret

我們經常要向容器傳遞敏感信息,最常見的莫過於密碼了。比如:
docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
在啓動 MySQL 容器時我們通過環境變量 MYSQL_ROOT_PASSWORD 設置了 MySQL 的管理員密碼。不過密碼是以明文的形式寫在 docker run 命令中,有潛在的安全隱患。

爲了解決這個問題,docker swarm 提供了 secret 機制,允許將敏感信息加密後保存到 secret 中,用戶可以指定哪些容器可以使用此 secret。
如果使用 secret 啓動 MySQL 容器,方法是:

1、在 swarm manager 中創建 secret my_secret_data,將密碼保存其中。
echo "my-secret-pw" | docker secret create my_secret_data -
2、啓動 MySQL service,並指定使用 secret my_secret_data。
docker service create \
--name mysql \
--secret source=my_secret_data,target=mysql_root_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
mysql:latest
① source 指定容器使用 secret 後,secret 會被解密並存放到容器的文件系統中,默認位置爲 /run/secrets/<secret_name>。--secret source=my_secret_data,target=mysql_root_password 的作用就是指定使用 secret my_secret_data,然後把器解密後的內容保存到容器 /run/secrets/mysql_root_password 文件中,文件名稱 mysql_root_password 由 target 指定。
② 環境變量 MYSQL_ROOT_PASSWORD_FILE 指定從 /run/secrets/mysql_root_password 中讀取並設置 MySQL 的管理員密碼。

Secret使用場景

我們可以用 secret 管理任何敏感數據。這些敏感數據是容器在運行時需要的,同時我們不又想將這些數據保存到鏡像中。
secret 可用於管理:
1、用戶名和密碼。
2、TLS 證書。
3、SSH 祕鑰。
4、其他小於 500 KB 的數據。
secret 只能在 swarm service 中使用。普通容器想使用 secret,可以將其包裝成副本數爲 1 的 service。

這裏我們再舉一個使用 secret 的典型場景:
數據中心有三套 swarm 環境,分別用於開發、測試和生產。對於同一個應用,在不同的環境中使用不同的用戶名密碼。我們可以在三個環境中分別創建 secret,不過使用相同的名字,比如 username 和 password。應用部署時只需要指定 secret 名字,這樣我們就可以用同一套腳本在不同的環境中部署應用了。

除了敏感數據,secret 當然也可以用於非敏感數據,比如配置文件。不過目前新版本的 Docker 提供了 config 子命令來管理不需要加密的數據。config 與 secret 命令的使用方法完全一致。

secret的安全性

當在 swarm 中創建 secret 時,Docker 通過 TLS 連接將加密後的 secret 發送給所以的 manager 節點。

secret 創建後,即使是 swarm manager 也無法查看 secret 的明文數據,只能通過 docker secret inspect 查看 secret 的一般信息。

只有當 secret 被指定的 service 使用是,Docker 纔會將解密後的 secret 以文件的形式 mount 到容器中,默認的路徑爲/run/secrets/<secret_name>。例如在前面 MySQL 的例子中,我們可以在容器中查看 secret。

當容器停止運行,Docker 會 unmount secret,並從節點上清除。

stack

stack 包含一系列 service,這些 service 組成了應用。stack 通過一個 YAML 文件定義每個 service,並描述 service 使用的資源和各種依賴。

WordPress 的 stack 版本

如果將前面 WordPress 用 stack 來定義,YAML 文件可以是這樣:


YAML 是一種閱讀性很強的文本格式,上面這個 stack 中定義了三種資源:service、secret 和 volume。
services 定義了兩個 service:dbwordpress
secrets 定義了兩個 secret:db_passworddb_root_password,在 service dbwordpress 的定義中引用了這兩個 secret。
volumes 定義了一個 volume:db_data,service db 使用了此 volume。
wordpress 通過 depends_on 指定自己依賴 db 這個 service。Docker 會保證當 db 正常運行後再啓動 wordpress
可以在 YAML 中定義的元素遠遠不止這裏看到的這幾個,完整列表和使用方法可參考文檔 https://docs.docker.com/compose/compose-file/

stack的優點

stack 將應用所包含的 service,依賴的 secret、voluem 等資源,以及它們之間的關係定義在一個 YAML 文件中。相比較手工執行命令或是腳本,stack 有明顯的優勢。

1、YAML 描述的是 What,是 stack 最終要達到的狀態。
比如 service 有幾個副本?使用哪個 image?映射的端口是什麼?而腳本則是描述如何執行命令來達到這個狀態,也就是 How。顯而易見,What 更直觀,也更容易理解。至於如何將 What 翻譯成 How,這就是 Docker swarm 的任務了,用戶只需要告訴 Docker 想達到什麼效果。

2、重複部署應用變得非常容易。
部署應用所需要的一切信息都已經寫在 YAML 中,要部署應用只需一條命令 docker stack deploy。stack 的這種自包含特性使得在不同的 Docker 環境中部署應用變得極其簡單。在開發、測試和生成環境中部署可以完全採用同一份 YAML,而且每次部署的結果都是一致的。

3、可以像管理代碼一樣管理部署。
YAML 本質上將應用的部署代碼化了,任何對應用部署環境的修改都可以通過修改 YAML 來實現。可以將 YAML 納入到版本控制系統中進行管理,任何對 YAML 的修改都會被記錄和跟蹤,甚至可以像評審代碼一樣對 YAML 執行 code review。應用部署不再是一個黑盒子,也不再是經驗豐富的工程師專有的技能,所以的細節都在 YAML 中,清晰可見。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章