คำตอบสั้น ๆ: เริ่มใช้ Ansible จากศูนย์ ตั้ง inventory เขียน Playbook แรก และสั่งงานหลายเครื่องพร้อมกันโดยไม่ต้องติดตั้งอะไรบนเครื่องปลายทาง
คู่มือนี้อธิบาย Ansible ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
เมื่อมีเซิร์ฟเวอร์เกินสามเครื่อง การ SSH เข้าไปแก้ทีละเครื่องเริ่มเป็นปัญหา ไม่ใช่เพราะช้าอย่างเดียว แต่เพราะเครื่องจะค่อย ๆ ต่างกันโดยไม่มีใครรู้ เครื่องหนึ่งมีการตั้งค่าที่อีกเครื่องไม่มี และเมื่อเกิดปัญหาจึงไล่ไม่ถูกว่าต่างกันตรงไหน
Ansible แก้เรื่องนี้ด้วยการเขียนสิ่งที่ต้องการให้เป็นไฟล์ แล้วสั่งให้ทุกเครื่องเป็นไปตามนั้น จุดเด่นคือไม่ต้องติดตั้งอะไรบนเครื่องปลายทางเลย ใช้ SSH ที่มีอยู่แล้ว
สิ่งที่ต้องเตรียม
- เครื่องควบคุมหนึ่งเครื่อง จะเป็นเครื่อง Mac หรือ Linux ของคุณเองก็ได้
- SSH Key ที่เข้าเซิร์ฟเวอร์ปลายทางได้โดยไม่ต้องใส่รหัสผ่าน
- Python บนเครื่องปลายทาง ซึ่งมีมาให้แล้วในทุกดิสทริบิวชัน
# macOS
brew install ansible
# Ubuntu / Debian
sudo apt install -y ansible
ansible --version
ขั้นตอนที่ 1: สร้างโครงงาน
mkdir -p ~/infra/{playbooks,group_vars,files}
cd ~/infra
nano inventory.ini
[web]
web1 ansible_host=10.0.0.11
web2 ansible_host=10.0.0.12
[db]
db1 ansible_host=10.0.0.21
[production:children]
web
db
[all:vars]
ansible_user=deploy
ansible_ssh_common_args='-o [email protected]'
ansible_python_interpreter=/usr/bin/python3
บรรทัด ansible_ssh_common_args ทำให้ Ansible วิ่งผ่าน Bastion Host ได้เหมือนที่คุณ SSH ด้วยมือ
สร้างไฟล์ตั้งค่า
nano ansible.cfg
[defaults]
inventory = inventory.ini
host_key_checking = True
retry_files_enabled = False
stdout_callback = yaml
interpreter_python = auto_silent
[ssh_connection]
pipelining = True
pipelining = True ทำให้เร็วขึ้นอย่างเห็นได้ชัดเพราะลดจำนวนรอบการเชื่อมต่อ SSH
ขั้นตอนที่ 2: ทดสอบการเชื่อมต่อ
ansible all -m ping
ansible web -m ping
ansible all -a "uptime"
ansible all -a "df -h /"
คำสั่งเหล่านี้เรียกว่า ad-hoc command เหมาะกับการดูข้อมูลเร็ว ๆ โดยไม่ต้องเขียน Playbook
ดูข้อมูลของเครื่องที่ Ansible เก็บได้
ansible web1 -m setup | head -50
ansible all -m setup -a 'filter=ansible_distribution*'
ขั้นตอนที่ 3: เขียน Playbook แรก
nano playbooks/base.yml
---
- name: ตั้งค่าพื้นฐานของเซิร์ฟเวอร์ทุกเครื่อง
hosts: all
become: true
vars:
common_packages:
- htop
- curl
- git
- unattended-upgrades
tasks:
- name: ตั้ง Timezone เป็นเวลาไทย
community.general.timezone:
name: Asia/Bangkok
- name: อัปเดตรายการแพ็กเกจ
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: ติดตั้งแพ็กเกจพื้นฐาน
ansible.builtin.package:
name: "{{ common_packages }}"
state: present
- name: ตั้งค่า sshd ให้ปลอดภัย
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/99-hardening.conf
mode: '0644'
content: |
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3
notify: reload sshd
- name: เปิดใช้งาน fail2ban
ansible.builtin.service:
name: fail2ban
state: started
enabled: true
handlers:
- name: reload sshd
ansible.builtin.service:
name: ssh
state: reloaded
ส่วน handlers คือแนวคิดสำคัญ มันจะทำงานเฉพาะเมื่องานที่ notify มีการเปลี่ยนแปลงจริงเท่านั้น ทำให้ไม่ต้องรีโหลดบริการทุกครั้งที่รัน
ขั้นตอนที่ 4: ตรวจก่อนรันจริงเสมอ
# ตรวจไวยากรณ์
ansible-playbook playbooks/base.yml --syntax-check
# ดูว่าจะทำอะไรบ้างโดยไม่แตะเครื่องจริง
ansible-playbook playbooks/base.yml --check --diff
# จำกัดให้ทำเครื่องเดียวก่อน
ansible-playbook playbooks/base.yml --limit web1
ลำดับนี้คือนิสัยที่ควรทำทุกครั้ง โดยเฉพาะ --check --diff ซึ่งแสดงความต่างของไฟล์ที่จะถูกแก้ ทำให้เห็นผลกระทบก่อนเกิดขึ้นจริง
ขั้นตอนที่ 5: รันจริง
ansible-playbook playbooks/base.yml
อ่านผลสรุปท้ายสุด ตัวเลข changed คือจำนวนงานที่แก้อะไรจริง หากรันซ้ำครั้งที่สองแล้วได้ changed=0 ทั้งหมด แปลว่า Playbook ของคุณเขียนถูกหลัก คือสั่งเดิมกี่ครั้งผลก็เหมือนเดิม
สำหรับการเปลี่ยนแปลงที่กระทบผู้ใช้ ให้ทยอยทำทีละส่วน
ansible-playbook playbooks/base.yml --serial 1
ansible-playbook playbooks/deploy.yml --limit 'web*' --serial '30%'
ขั้นตอนที่ 6: เก็บความลับด้วย Ansible Vault
อย่าเก็บรหัสผ่านเป็นข้อความธรรมดาในไฟล์ที่เข้า Git
ansible-vault create group_vars/production/secrets.yml
db_root_password: "รหัสผ่านจริง"
api_token: "token"
ansible-vault edit group_vars/production/secrets.yml
ansible-vault view group_vars/production/secrets.yml
ansible-playbook playbooks/base.yml --ask-vault-pass