在現代 IT 環境中,定時任務是系統維運的重要組成部分。透過 Ansible,我們可以輕鬆地在多台伺服器上統一管理 Cron 任務,確保關鍵操作如備份、日誌清理和系統更新能夠按時執行。
基本 Cron 任務設定一個基本的每日備份任務相當簡單:
- name: 設定基本備份 Cron 任務
hosts: webservers
become: true
tasks:
- name: 新增每日備份任務
ansible.builtin.cron:
name: "每日備份"
hour: "2"
minute: "0"
job: "/usr/local/bin/backup.sh > /var/log/backup.log 2>&1"
這個 Playbook 會在所有 webservers 群組的主機上建立一個名為「每日備份」的 Cron 任務,設定在每天凌晨 2:00 執行 backup.sh 指令碼,並將輸出導向至日誌檔案。become: true
表示以 root 許可權執行,確保有足夠許可權修改 crontab。
使用特殊時間規格
Ansible 支援 Cron 的特殊時間關鍵字,讓排程更加直覺:
- name: 設定特殊時間規格的 Cron 任務
hosts: all
become: true
tasks:
- name: 每週執行系統更新
ansible.builtin.cron:
name: "每週系統更新"
special_time: weekly
job: "apt-get update && apt-get upgrade -y > /var/log/weekly_upgrade.log 2>&1"
- name: 每日清理暫存檔案
ansible.builtin.cron:
name: "每日暫存清理"
special_time: daily
job: "find /tmp -type f -mtime +7 -delete"
這個 Playbook 使用 special_time
引數設定兩個任務:一個每週執行系統更新,另一個每日清理暫存檔案。Ansible 支援的特殊時間值包括 reboot
、yearly
、annually
、monthly
、weekly
、daily
和 hourly
,讓排程更加人性化。
為特定使用者設定 Cron 任務
有時我們需要以特定使用者身分執行 Cron 任務,特別是對於資料函式庫備份這類別操作:
- name: 設定使用者特定的 Cron 任務
hosts: all
become: true
tasks:
- name: 為 dbadmin 使用者新增資料函式庫備份任務
ansible.builtin.cron:
name: "資料函式庫備份"
user: dbadmin
hour: "*/4"
minute: "0"
job: "/home/dbadmin/scripts/db_backup.sh"
這個任務會在 dbadmin 使用者的 crontab 中新增一個每 4 小時執行一次的備份任務。透過指定 user
引數,Ansible 會修改該使用者的 crontab,而非 root 使用者的。這對於需要特定許可權或環境的任務非常有用。
設定環境變數
Cron 任務通常需要特定的環境變數才能正確執行:
- name: 設定帶環境變數的 Cron 任務
hosts: all
become: true
tasks:
- name: 新增帶 PATH 的日誌輪轉任務
ansible.builtin.cron:
name: "日誌輪轉"
hour: "0"
minute: "0"
job: "rotate_logs.sh"
env:
PATH: "/usr/local/bin:/usr/bin:/bin"
MAILTO: "admin@example.com"
這個任務設定了兩個環境變數:PATH
確保系統能找到 rotate_logs.sh 指令碼,而 MAILTO
則指定任務執行結果的郵件接收者。在 Cron 環境中設定正確的 PATH 變數尤為重要,因為 Cron 預設的 PATH 通常很有限。
移除 Cron 任務
當某些任務不再需要時,我們可以輕鬆移除它們:
- name: 移除過時的 Cron 任務
hosts: all
become: true
tasks:
- name: 移除舊的備份任務
ansible.builtin.cron:
name: "舊備份任務"
state: absent
這個任務使用 state: absent
引數移除名為「舊備份任務」的 Cron 專案。Ansible 會根據任務名稱找到對應的 Cron 專案並將其刪除,這比手動編輯 crontab 檔案安全得多。
批次管理 Cron 任務
對於需要管理多個 Cron 任務的情況,使用變數檔案是個好方法:
# cron_jobs.yml
cron_jobs:
- name: "系統更新"
special_time: weekly
job: "apt-get update && apt-get upgrade -y"
- name: "資料函式庫備份"
hour: "3"
minute: "30"
job: "/usr/local/bin/db_backup.sh"
- name: "日誌清理"
hour: "1"
minute: "0"
day: "*/3"
job: "find /var/log -name \"*.gz\" -mtime +30 -delete"
然後在 Playbook 中使用這個變數檔案:
- name: 設定多個 Cron 任務
hosts: all
become: true
vars_files:
- cron_jobs.yml
tasks:
- name: 設定 Cron 任務
ansible.builtin.cron:
name: "{{ item.name }}"
hour: "{{ item.hour | default(omit) }}"
minute: "{{ item.minute | default(omit) }}"
day: "{{ item.day | default(omit) }}"
month: "{{ item.month | default(omit) }}"
weekday: "{{ item.weekday | default(omit) }}"
special_time: "{{ item.special_time | default(omit) }}"
job: "{{ item.job }}"
loop: "{{ cron_jobs }}"
這個方法將 Cron 任務定義與執行邏輯分離,使管理更加靈活。使用 default(omit)
過濾器確保只傳遞已定義的引數,避免因未定義引數而導致的錯誤。loop
指令讓我們能夠迭代處理每個任務定義。
條件式 Cron 任務設定
根據主機角色設定不同的 Cron 任務是自動化管理的常見需求:
- name: 設定角色特定的 Cron 任務
hosts: all
become: true
tasks:
- name: 為資料函式庫伺服器新增備份任務
ansible.builtin.cron:
name: "資料函式庫備份"
hour: "2"
minute: "0"
job: "/usr/local/bin/db_backup.sh"
when: "'database_servers' in group_names"
- name: 為網頁伺服器新增日誌輪轉
ansible.builtin.cron:
name: "網頁日誌輪轉"
hour: "1"
minute: "0"
job: "/usr/local/bin/rotate_web_logs.sh"
when: "'web_servers' in group_names"
這個 Playbook 使用 when
條件根據主機所屬群組決定是否執行特定任務。group_names
是 Ansible 的內建變數,包含目標主機所屬的所有群組。這種方法讓我們能夠在單一 Playbook 中處理不同型別的伺服器,大幅提高管理效率。
Ansible 與 Docker 整合:容器化佈署自動化
Docker 已成為現代應用佈署的標準工具,而 Ansible 提供了豐富的模組來管理 Docker 容器、映像和網路。這種組合讓我們能夠實作容器化應用的完整自動化生命週期管理。
安裝 Docker
首先,讓我們使用 Ansible 來安裝 Docker:
- name: 安裝 Docker
hosts: docker_hosts
become: true
tasks:
- name: 安裝必要套件
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: 新增 Docker GPG 金鑰
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: 新增 Docker 儲存函式庫
ansible.builtin.apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: 安裝 Docker 引擎
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
update_cache: yes
- name: 啟動並啟用 Docker 服務
ansible.builtin.service:
name: docker
state: started
enabled: yes
這個 Playbook 執行了安裝 Docker 的完整流程:先安裝必要的依賴套件,然後新增 Docker 的 GPG 金鑰和儲存函式庫,接著安裝 Docker 引擎,最後啟動並設定 Docker 服務為開機自動啟動。ansible_distribution_release
是 Ansible 的內建變數,會自動偵測目標系統的發行版本,確保安裝正確版本的 Docker。
管理 Docker 映像
使用 docker_image
模組可以輕鬆管理 Docker 映像:
- name: 管理 Docker 映像
hosts: docker_hosts
become: true
tasks:
- name: 提取 Nginx 映像
community.docker.docker_image:
name: nginx:latest
source: pull
- name: 提取 PostgreSQL 映像
community.docker.docker_image:
name: postgres:13
source: pull
- name: 移除舊映像
community.docker.docker_image:
name: "{{ item }}"
state: absent
loop:
- "nginx:1.18"
- "postgres:12"
這個 Playbook 展示了三種常見的 Docker 映像管理操作:提取最新的 Nginx 映像、提取特定版本的 PostgreSQL 映像,以及移除不再需要的舊版映像。使用 loop
指令可以一次處理多個映像,提高效率。
執行 Docker 容器
使用 docker_container
模組可以管理 Docker 容器的生命週期:
- name: 執行 Docker 容器
hosts: docker_hosts
become: true
tasks:
- name: 執行 Nginx 容器
community.docker.docker_container:
name: web
image: nginx:latest
state: started
restart_policy: always
ports:
- "80:80"
volumes:
- "/data/nginx/html:/usr/share/nginx/html:ro"
- name: 執行 PostgreSQL 容器
community.docker.docker_container:
name: db
image: postgres:13
state: started
restart_policy: always
env:
POSTGRES_PASSWORD: "{{ db_password }}"
POSTGRES_USER: "{{ db_user }}"
POSTGRES_DB: "{{ db_name }}"
ports:
- "5432:5432"
volumes:
- "/data/postgres:/var/lib/postgresql/data"
這個 Playbook 啟動了兩個容器:一個 Nginx 網頁伺服器和一個 PostgreSQL 資料函式庫。對於每個容器,我們設定了名稱、使用的映像、啟動狀態、重啟策略、連線埠對映和卷掛載。對於 PostgreSQL 容器,我們還設定了環境變數來指定資料函式庫憑證。restart_policy: always
確保容器在發生錯誤或系統重啟後能自動重新啟動。
使用 Docker Compose
對於多容器應用,Docker Compose 是更好的選擇。Ansible 可以輕鬆管理 Docker Compose 專案:
- name: 使用 Docker Compose 佈署
hosts: docker_hosts
become: true
tasks:
- name: 建立專案目錄
ansible.builtin.file:
path: /opt/myapp
state: directory
- name: 複製 docker-compose.yml
ansible.builtin.template:
src: templates/docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
- name: 使用 Docker Compose 佈署
community.docker.docker_compose:
project_src: /opt/myapp
state: present
docker-compose.yml.j2 範本:
version: '3'
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- app
app:
image: {{ app_image }}
environment:
DB_HOST: db
DB_USER: {{ db_user }}
DB_PASSWORD: {{ db_password }}
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_USER: {{ db_user }}
POSTGRES_PASSWORD: {{ db_password }}
POSTGRES_DB: {{ db_name }}
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
這個 Playbook 首先建立專案目錄,然後使用 Jinja2 範本生成 docker-compose.yml 檔案,最後使用 docker_compose
模組啟動服務。範本中的變數(如 {{ app_image }}
、{{ db_user }}
等)會在執行時被實際值替換。這種方法讓我們能夠在不同環境中重複使用相同的 Compose 檔案,只需調整變數即可。
建立自訂 Docker 映像
Ansible 也可以用來建立自訂 Docker 映像:
- name: 建立自訂 Docker 映像
hosts: docker_hosts
become: true
tasks:
- name: 建立建置目錄
ansible.builtin.file:
path: /tmp/docker_build
state: directory
- name: 複製 Dockerfile
ansible.builtin.template:
src: templates/Dockerfile.j2
dest: /tmp/docker_build/Dockerfile
- name: 複製應用程式檔案
ansible.builtin.copy:
src: files/app/
dest: /tmp/docker_build/app/
- name: 建置自訂映像
community.docker.docker_image:
name: myapp:{{ app_version }}
build:
path: /tmp/docker_build
source: build
Dockerfile.j2 範本:
FROM python:3.9-slim
WORKDIR /app
COPY app/ .
RUN pip install -r requirements.txt
ENV PORT={{ app_port }}
EXPOSE {{ app_port }}
CMD ["python", "app.py"]
這個 Playbook 展示瞭如何使用 Ansible 建立自訂 Docker 映像。它首先建立一個臨時建置目錄,然後複製 Dockerfile 範本和應用程式檔案,最後使用 docker_image
模組的 build
引數建置映像。Dockerfile 範本中的變數(如 {{ app_port }}
)會在執行時被替換,讓我們能夠動態調整映像設定。
Docker 網路管理
管理 Docker 網路對於多容器應用至關重要:
- name: 管理 Docker 網路
hosts: docker_hosts
become: true
tasks:
- name: 建立自訂橋接網路
community.docker.docker_network:
name: app_network
driver: bridge
- name: 在自訂網路上執行容器
community.docker.docker_container:
name: "{{ item.name }}"
image: "{{ item.image }}"
networks:
- name: app_network
state: started
loop:
- { name: "web", image: "nginx:latest" }
- { name: "app", image: "myapp:latest" }
- { name: "db", image: "postgres:13" }
這個 Playbook 首先建立一個名為 app_network
的自訂橋接網路,然後啟動三個容器並將它們連線到這個網路。使用自訂網路可以讓容器之間透過容器名稱互相通訊,簡化了設定。loop
指令讓我們能夠一次處理多個容器,提高效率。
Docker Swarm 管理
對於需要高用性和負載平衡的應用,Docker Swarm 是個不錯的選擇:
- name: 初始化 Docker Swarm
hosts: swarm_manager
become: true
tasks:
- name: 初始化 Swarm
community.docker.docker_swarm:
state: present
register: swarm_info
- name: 儲存加入令牌
ansible.builtin.set_fact:
worker_token: "{{ swarm_info.swarm_facts.JoinTokens.Worker }}"
- name: 加入 Swarm 作為工作節點
hosts: swarm_workers
become: true
tasks:
- name: 加入 Swarm 叢集
community.docker.docker_swarm:
state: join
join_token: "{{ hostvars['swarm_manager']['worker_token'] }}"
remote_addrs: [ "{{ hostvars['swarm_manager']['ansible_default_ipv4']['address'] }}:2377" ]
這個 Playbook 包含兩個 play:第一個在管理節點上初始化 Swarm 並儲存工作節點的加入令牌;第二個讓工作節點使用這個令牌加入 Swarm。register
指令將初始化結果儲存在 swarm_info
變數中,然後我們使用 set_fact
提取工作節點令牌。在第二個 play 中,我們使用 hostvars
變數存取管理節點上的令牌和 IP 地址。
佈署 Docker Swarm 服務
在 Swarm 叢集上佈署服務:
- name: 在 Swarm 上佈署服務
hosts: swarm_manager
become: true
tasks:
- name: 佈署網頁服務
community.docker.docker_swarm_service:
name: web
image: nginx:latest
mode: replicated
replicas: 3
publish:
- published_port: 80
target_port: 80
networks:
- app_network
- name: 佈署資料函式庫服務
community.docker.docker_swarm_service:
name: db
image: postgres:13
env:
POSTGRES_PASSWORD: "{{ db_password }}"
POSTGRES_USER: "{{ db_user }}"
mounts:
- source: db_data
target: /var/lib/postgresql/data
type: volume
networks:
- app_network
這個 Playbook 在 Swarm 叢集上佈署了兩個服務:一個具有三個副本的 Nginx 服務和一個 PostgreSQL 資料函式庫服務。對於網頁服務,我們設定了複製模式並指定了三個副本,這意味著 Swarm 會在叢集中維護三個容器例項,提供高用性和負載平衡。對於資料函式庫服務,我們使用了卷掛載來確保資料永續性。兩個服務都連線到同一個網路,使它們能夠互相通訊。
密碼安全性:Ansible 的最佳實踐
在系統管理中,密碼安全一直是個關鍵議題。在 Ansible 環境中,我們絕對不該在 Playbook 中直接使用明文密碼,而應該採用加密技術來保護敏感資訊。
使用 password_hash 過濾器加強安全性
Ansible 提供了 password_hash
過濾器,能將明文密碼轉換為加密雜湊值:
- name: 產生雜湊密碼
hosts: localhost
tasks:
- name: 計算密碼雜湊值
ansible.builtin.debug:
msg: "{{ '安全密碼範例' | password_hash('sha512') }}"
執行此 Playbook 後,你會得到一個 SHA-512 加密的密碼雜湊值:
ok: [localhost] => {
"msg": "$6$rounds=50000$UJWtdpxDHQbwG8DB$hG0SRgRpArdp9QOGJ3c4o7fVfW0Eq4N5QFh7tlShV3VVG48sTDwT.vOgJAEoPBINMuDzoE2rg00JMj4jDlb.l/"
}
這個雜湊值可以直接用於使用者建立或密碼更新任務中:
- name: 更新使用者密碼
hosts: all
become: true
tasks:
- name: 變更使用者密碼
ansible.builtin.user:
name: johndoe
password: "$6$rounds=50000$UJWtdpxDHQbwG8DB$hG0SRgRpArdp9QOGJ3c4o7fVfW0Eq4N5QFh7tlShV3VVG48sTDwT.vOgJAEoPBINMuDzoE2rg00JMj4jDlb.l/"
這種方法不僅提高了安全性,還讓密碼管理變得更加系統化。即使有人取得了你的 Playbook,也無法輕易還原出原始密碼。password_hash
過濾器支援多種雜湊演算法,包括 SHA-512、SHA-256 和 MD5,但出於安全考量,建議使用 SHA-512。
開發完整的自動化使用者管理系統
在管理大型基礎設施時,手動建立和維護使用者帳號既耗時又容易出錯。以下是一個完整的解決方案,整合了前面討論的技術。
使用者資料集中管理
首先,我們需要一個集中存放使用者資訊的 YAML 檔案:
---
users:
- name: johndoe
password: "{{ 'Password123' | password_hash('sha512') }}"
home: /home/johndoe
shell: /bin/bash
groups: developers
state: present
ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq5X0..."
- name: janedoe
password: "{{ 'Password456' | password_hash('sha512') }}"
home: /home/janedoe
shell: /bin/bash
groups: developers
state: present
ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvG1l..."
- name: alice
password: "{{ 'Password789' | password_hash('sha512') }}"
home: /home/alice
shell: /bin/zsh
groups: admins
state: present
ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAp7lR..."
deleted_users:
- name: bob
- name: charlie
全功能使用者管理 Playbook
接下來,我們建立一個功能完整的 Playbook,處理使用者的建立、修改和刪除:
---
- name: 全方位 Linux 使用者管理
hosts: all
become: true
vars_files:
- users.yml
tasks:
- name: 確保群組存在
ansible.builtin.group:
name: "{{ item.groups }}"
state: present
loop: "{{ users | map(attribute='groups') | unique | list }}"
ignore_errors: yes
- name: 建立或修改使用者
ansible.builtin.user:
name: "{{ item.name }}"
password: "{{ item.password }}"
home: "{{ item.home }}"
shell: "{{ item.shell }}"
groups: "{{ item.groups }}"
state: "{{ item.state }}"
loop: "{{ users }}"
- name: 確保 .ssh 目錄存在
ansible.builtin.file:
path: "{{ item.home }}/.ssh"
state: directory
mode: '0700'
owner: "{{ item.name }}"
group: "{{ item.name }}"
loop: "{{ users }}"
- name: 佈署 SSH 公鑰
ansible.builtin.copy:
content: "{{ item.ssh_key }}"
dest: "{{ item.home }}/.ssh/authorized_keys"
mode: '0600'
owner: "{{ item.name }}"
group: "{{ item.name }}"
loop: "{{ users }}"
- name: 移除指定使用者
ansible.builtin.user:
name: "{{ item.name }}"
state: absent
remove: yes
loop: "{{ deleted_users }}"
這個 Playbook 實作了幾個關鍵功能:
- 群組預處理:在建立使用者前,確保所需的群組已經存在,避免因群組不存在而導致的錯誤。
- 使用者建立與更新:根據定義的使用者資訊,統一建立或更新使用者帳號。
- SSH 金鑰佈署:自動設定 SSH 公鑰,實作無密碼安全登入。
- 使用者移除:自動刪除不再需要的使用者帳號及其主目錄。
map(attribute='groups')
過濾器從使用者列表中提取所有群組名稱,unique
過濾器則移除重複項,確保每個群組只建立一次。
執行與測試
在實際執行前,建議先使用 --check
模式進行測試:
ansible-playbook manage_users.yml -i hosts --check
確認無誤後再實際執行:
ansible-playbook manage_users.yml -i hosts
進階技巧與最佳實踐
使用者資料分層管理
對於大型組織,可以考慮將使用者資料分層管理:
inventory/
├── group_vars/
│ ├── all/
│ │ └── users.yml # 全域使用者
│ ├── developers/
│ │ └── users.yml # 開發團隊使用者
│ └── production/
│ └── users.yml # 生產環境使用者
└── host_vars/
├── server1/
│ └── users.yml # 伺服器專屬使用者
└── server2/
└── users.yml
這種結構讓使用者管理更加靈活,可以根據不同環境和伺服器角色分配使用者許可權。
使用 Ansible Vault 加密敏感資料
雖然我們已經使用 password_hash
加密碼,但使用者資料檔案中仍可能包含敏感資訊。Ansible Vault 提供了額外的保護層:
# 加密整個使用者資料檔案
ansible-vault encrypt users.yml
# 編輯加密檔案
ansible-vault edit users.yml
# 執行時提供密碼
ansible-playbook manage_users.yml -i hosts --ask-vault-pass
整合 CI/CD 流程
將使用者管理 Playbook 整合到 CI/CD 流程中,可以實作使用者管理的自動化:
# .gitlab-ci.yml
stages:
- validate
- deploy
validate_playbook:
stage: validate
script:
- ansible-playbook manage_users.yml -i hosts --syntax-check
- ansible-playbook manage_users.yml -i hosts --check
deploy_users:
stage: deploy
script:
- ansible-playbook manage_users.yml -i hosts
only:
- master
使用者活動稽核
為了滿足合規要求,我們可以加入使用者活動稽核功能:
- name: 記錄使用者變更
ansible.builtin.shell: |
echo "$(date) - User {{ item.name }} {{ item.state }} by Ansible" >> /var/log/user_management.log
loop: "{{ users }}"
changed_when: false
自動化管理
隨著雲端技術和容器化的普及,使用者管理自動化也在不斷演進。未來,我們可能會看到更多 AI 輔助的自動化管理工具,能夠預測使用者行為、自動調整許可權,甚至主動識別潛在的安全風險。但無論技術如何發展,自動化的核心價值始終不變:提高效率、減少錯誤、增強安全性。
透過 Ansible 的使用者管理技術,我們可以建立現代化、安全與高效的使用者管理系統。這不僅能大幅減輕系統管理的負擔,還能為組織建立更加安全可靠的 IT 環境。在自動化的道路上,每一步最佳化都是對未來的投資。
Ansible 提供了強大而靈活的工具,讓我們能夠自動化管理從 Cron 任務到 Docker 容器再到使用者安全的各個方面。透過這些技術,我們可以建立更加高效、安全與可靠的 IT 基礎設施,為組織的數位轉型提供堅實基礎。