第一份 Playbook:把 shell 腳本翻譯成宣告式
· tech
📑 目錄
上一篇說膠帶貼到第二次就該轉正——這篇就是轉正手續。Playbook 說穿了只是「把 ad-hoc 指令寫進 YAML 檔案」,但書的第四章真正想教的,是一個世界觀的切換:腳本描述步驟,playbook 描述狀態。看懂這個差別,後面所有語法都只是細節。
同一件事,兩種世界觀
書用一個最小例子開場。裝一台 Apache,shell 腳本這樣寫:
#!/bin/bash
apt-get update
apt-get install -y apache2
systemctl start apache2
Playbook 這樣寫:
---
- hosts: web
become: true
tasks:
- name: 安裝 Apache
apt: name=apache2 state=present update_cache=yes
- name: 確保服務啟動且開機自啟
service: name=apache2 state=started enabled=yes
行數差不多,但語意完全不同——腳本的每一行是動作,playbook 的每個 task 是斷言:
這個差別的實際體感,就在 PLAY RECAP 的輸出裡:
PLAY RECAP *********************************************************
192.168.60.4 : ok=4 changed=3 ← 第一次:動手改了三件事
192.168.60.4 : ok=4 changed=0 ← 第二次:已在期望狀態,全部跳過
changed=0 就是冪等的證明——你可以把 playbook 排進 cron 定期重跑,把被手動亂改的機器拉回正軌,而不用擔心它「多做了什麼」。
骨架:play、task、handler
一份 playbook 由上到下就三層:play(對哪些機器、用什麼身分)、task(一連串狀態斷言,依序執行)、handler(被 notify 才執行的特殊 task)。Handler 是第一個值得停下來看的設計:
---
- hosts: web
become: true
handlers:
- name: restart apache
service: name=apache2 state=restarted
tasks:
- name: 部署主設定檔
template: src=apache2.conf.j2 dest=/etc/apache2/apache2.conf
notify: restart apache
- name: 部署站台設定檔
template: src=vhost.conf.j2 dest=/etc/apache2/sites-enabled/vhost.conf
notify: restart apache
這兩條規則把「什麼時候該重啟服務」這個維運判斷,直接寫死進工具的語意:設定沒變就不動它,改了五個檔案也只重啟一次。用 shell 腳本要自己維護 flag 變數才能做到;在 Ansible 裡它是預設行為。
執行與日常開關
ansible-playbook playbook.yml # 就這樣
ansible-playbook playbook.yml --limit web1 # 只打一台(先拿一台試!)
ansible-playbook playbook.yml --check # 彩排:只回報會改什麼
ansible-playbook playbook.yml -v # 看每個 task 的細節輸出
--limit 加 --check 是上正式環境前的標準組合:先在一台機器上彩排,確認 changed 的項目跟你預期一致,再全面執行。
真實世界的 playbook 都是同一首歌
第四章後半用三個完整範例收尾——Rocky Linux 的 Node.js app server、Ubuntu 的 LAMP + Drupal、Ubuntu 的 Solr。細節各異,但骨架完全是同一個 pattern:
- 加套件來源(EPEL / Remi / apt repo)
- 裝套件(
package/apt/yum) - 鋪設定(
template/copy/lineinfile,改了就notify) - 確保服務跑著(
service: started enabled=yes)
外加兩個新面孔:vars_files 把變數抽出去(同一份 playbook,換個變數檔就是另一個環境),pre_tasks 在正式 tasks 前先跑(典型用途:先 apt update 快取)。看懂這個套路之後,讀任何人的 playbook 都像讀同一首歌的變奏——伺服器設定的花樣其實很少,少到可以標準化,這正是它適合被自動化的原因。
反思
宣告式真正的紅利,是「可以安心失敗」
我最有感的不是 playbook 比腳本優雅,而是它改變了失敗的代價。部署腳本跑到一半斷線,是我做 backend 以來最討厭的時刻之一——機器卡在半套狀態,你得逐行對照腳本猜「做到哪了」,手動把它救回可重跑的起點。冪等把這件事變成:再跑一次就好。這跟後端設計的直覺完全相通——訊息會 at-least-once 重複投遞、API 要冪等鍵、排程要能重跑,分散式世界的預設就是「同一件事會再來一次」,所有不能安心重來的設計,遲早變成半夜的事故。組態管理只是把這條鐵律應用到伺服器上而已。
name: 是寫給三個月後的自己
Playbook 能當文件讀,是它比腳本高明的第二件事——但這件事不是自動發生的。我看過 task 全用 shell 模組、name 隨便寫的 playbook,那跟腳本一樣難讀。差別在紀律:name: 安裝 Apache 這種寫得像句子的斷言,串起來就是一份會自己執行的 runbook;新人問「這台機器上有什麼」,答案不是過期的 wiki,是 repo 裡這份跑得動的文件。我對團隊的要求也一樣:程式碼的註解可以少,但「意圖」必須留在某個跑得動、review 得到的地方——對 API 是測試案例,對機器就是 playbook 的 name 欄位。
腳本記錄你做過什麼,playbook 宣告機器該是什麼——前者會過期,後者每跑一次就驗證一次。