ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

ดูแลเซิร์ฟเวอร์หลายเครื่องพร้อมกันด้วย Ansible
Home ดูแลเซิร์ฟเวอร์หลายเครื่องพร้อมกันด้วย Ansible

ดูแลเซิร์ฟเวอร์หลายเครื่องพร้อมกันด้วย 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

ขั้นตอนที่ 7: ใช้ Role เมื่อ Playbook เริ่มยาว

ansible-galaxy init roles/nginx
tree roles/nginx -L 1

โครงสร้าง Role แยกงาน ไฟล์ ตัวแปร และเทมเพลตออกจากกัน ทำให้นำไปใช้ซ้ำกับโครงงานอื่นได้ เรียกใช้ใน Playbook ด้วย

---
- hosts: web
  become: true
  roles:
    - nginx
    - php-fpm

ปัญหาที่พบบ่อย

ping ไม่ผ่าน ขึ้น Permission denied

ตรวจว่า SSH ด้วยมือเข้าได้ก่อน ssh [email protected] ถ้าเข้าได้แต่ Ansible ไม่ได้ ให้ตรวจ ansible_user ใน inventory และลองเพิ่ม -vvv เพื่อดูรายละเอียดการเชื่อมต่อ

ขึ้น sudo: a password is required

ผู้ใช้ยังต้องใส่รหัสผ่านตอน sudo ให้เพิ่ม --ask-become-pass หรือตั้ง NOPASSWD ให้ผู้ใช้นั้นบนเครื่องปลายทาง

รันครั้งที่สองยังขึ้น changed ทุกงาน

แปลว่ามีงานที่ไม่ได้เขียนให้ตรวจสถานะก่อนทำ มักเกิดกับ command และ shell ให้ใช้โมดูลเฉพาะทางแทน หรือเพิ่ม creates และ changed_when เพื่อบอกเงื่อนไขว่าเมื่อไรถือว่ามีการเปลี่ยนแปลง

ช้ามากเมื่อมีหลายเครื่อง

เปิด pipelining = True เพิ่มจำนวนงานขนานด้วย -f 20 และหลีกเลี่ยงการใช้ with_items กับโมดูลติดตั้งแพ็กเกจ ให้ส่งเป็นรายการเดียวแทนเพื่อให้ติดตั้งรอบเดียว

ต้องการวางมาตรฐานการดูแลเซิร์ฟเวอร์ทั้งระบบ ติดต่อ THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security