เมื่อมี VPS หลายเครื่อง การ SSH เข้าไปตั้งค่าทีละเครื่องไม่ใช่วิธีที่ยั่งยืน Ansible คือเครื่องมือ Configuration Management ที่ให้คุณเขียน "สูตร" การตั้งค่าครั้งเดียว แล้วรันพร้อมกันบนทุกเครื่องผ่าน SSH — ไม่ต้องติดตั้ง Agent บน VPS เป้าหมายแม้แต่ตัวเดียว
Ansible คืออะไร ทำไมต้องใช้
Ansible เป็น Open-source IT automation tool ของ Red Hat ใช้ YAML ในการเขียน Playbook ซึ่งเป็นชุดคำสั่งที่บอกว่า "เครื่องนี้ต้องมีสถานะแบบไหน" เช่น ต้องมี Nginx ติดตั้ง, ต้องมีไฟล์ config ที่กำหนด, ต้องมี user นั้น Ansible จะทำให้ระบบตรงตามที่ระบุ (idempotent — รันกี่ครั้งผลเหมือนกัน)
- Agentless — ใช้ SSH เท่านั้น ไม่ต้องติดตั้งอะไรบน VPS เป้าหมาย
- YAML-based — อ่านง่าย เขียนง่าย ไม่ต้องรู้ภาษา programming
- Idempotent — รันซ้ำก็ได้ผลเหมือนกัน ไม่มีผลข้างเคียง
- Large ecosystem — มี Module สำเร็จรูปกว่า 3,000 ตัวสำหรับทุกงาน
สิ่งที่ต้องเตรียม
- Control Node — เครื่องที่รัน Ansible (macOS, Linux หรือ VPS ที่คุณใช้งาน) ต้องมี Python 3.8+
- Managed Nodes — VPS เป้าหมาย (Ubuntu 20.04/22.04/24.04 หรือ Debian) ต้องมี Python 3 ติดตั้ง
- SSH Key — ต้องวาง public key ไว้บน managed nodes ทุกเครื่องก่อน
ติดตั้ง Ansible บน Control Node
Ubuntu / Debian
sudo apt update
sudo apt install -y ansible
macOS (Homebrew)
brew install ansible
Python pip (ทุก OS)
pip3 install ansible
ตรวจสอบเวอร์ชัน:
ansible --version
# ansible [core 2.17.x]
สร้าง Inventory File
Inventory คือไฟล์ที่ระบุว่า VPS ไหนอยู่กลุ่มไหน Ansible จะรู้ว่าต้องไปทำงานที่เครื่องใด
ไฟล์ inventory.ini (รูปแบบพื้นฐาน)
[webservers]
web1 ansible_host=203.0.113.10 ansible_user=root
web2 ansible_host=203.0.113.11 ansible_user=root
[databases]
db1 ansible_host=203.0.113.20 ansible_user=root
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519
ทดสอบ Connectivity
ansible -i inventory.ini all -m ping
ถ้าได้ pong กลับมาทุกเครื่อง แปลว่า Ansible เชื่อมต่อได้สำเร็จ
เขียน Playbook แรก — ติดตั้ง Nginx
สร้างไฟล์ setup-nginx.yml:
---
- name: ติดตั้งและเปิด Nginx
hosts: webservers
become: yes
tasks:
- name: อัปเดต apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: ติดตั้ง Nginx
apt:
name: nginx
state: present
- name: เปิด Nginx ทันทีและตั้ง autostart
service:
name: nginx
state: started
enabled: yes
- name: เปิด port 80 ใน UFW
ufw:
rule: allow
port: '80'
proto: tcp
รัน Playbook:
ansible-playbook -i inventory.ini setup-nginx.yml
idempotent ในทางปฏิบัติ: รัน Playbook นี้ 10 ครั้ง Nginx จะไม่ถูกติดตั้งซ้ำ — Ansible ตรวจสอบสถานะจริงก่อน ถ้า Package มีอยู่แล้ว task นั้นจะ skip อัตโนมัติ (สถานะ "ok" ไม่ใช่ "changed")
Variables — ทำให้ Playbook ยืดหยุ่น
---
- name: ติดตั้ง Web Stack
hosts: webservers
become: yes
vars:
php_version: "8.3"
app_user: "www-data"
tasks:
- name: ติดตั้ง PHP {{ php_version }}
apt:
name: "php{{ php_version }}-fpm"
state: present
แยก Variables ออกเป็นไฟล์
# vars/main.yml
php_version: "8.3"
mysql_root_password: "SecurePass123"
app_domain: "example.com"
# ใช้ใน playbook
vars_files:
- vars/main.yml
Handlers — รัน Task เฉพาะเมื่อมีการเปลี่ยนแปลง
Handler คือ Task พิเศษที่รันก็ต่อเมื่อ task อื่น "notify" มา — เหมาะสำหรับ restart service หลังแก้ config
tasks:
- name: คัดลอก Nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
ถ้า config ไม่เปลี่ยน Nginx จะไม่ถูก restart ช่วยลด downtime
Roles — จัดระเบียบ Playbook ขนาดใหญ่
เมื่อ Playbook ซับซ้อนขึ้น ใช้ Role แบ่งงานเป็นหมวด:
ansible-galaxy init roles/nginx
# สร้างโครงสร้าง:
# roles/nginx/
# tasks/main.yml
# handlers/main.yml
# templates/
# vars/main.yml
# defaults/main.yml
Playbook สำหรับ VPS ใหม่ (Hardening)
ตัวอย่าง Playbook สำหรับ setup VPS ใหม่ทุกเครื่องให้พร้อมใช้งานและปลอดภัย:
---
- name: Setup VPS ใหม่
hosts: new_servers
become: yes
tasks:
- name: อัปเดตระบบทั้งหมด
apt:
upgrade: dist
update_cache: yes
- name: ติดตั้ง packages พื้นฐาน
apt:
name:
- ufw
- fail2ban
- unattended-upgrades
- curl
- git
state: present
- name: สร้าง deploy user
user:
name: deploy
shell: /bin/bash
groups: sudo
append: yes
- name: วาง SSH public key
authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
- name: ปิด Root SSH Login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: Restart SSH
- name: เปิด UFW
ufw:
state: enabled
policy: deny
- name: อนุญาต SSH port
ufw:
rule: allow
port: '22'
proto: tcp
handlers:
- name: Restart SSH
service:
name: ssh
state: restarted
Ad-hoc Commands — รัน Task เดี่ยวโดยไม่ต้องเขียน Playbook
# ดู disk usage ทุกเครื่อง
ansible -i inventory.ini all -m shell -a "df -h /"
# Restart Nginx บน webservers
ansible -i inventory.ini webservers -m service -a "name=nginx state=restarted" --become
# คัดลอกไฟล์ไปทุกเครื่อง
ansible -i inventory.ini all -m copy -a "src=app.conf dest=/etc/app.conf"
Ansible Galaxy: มี Role สำเร็จรูปจาก community ที่ galaxy.ansible.com เช่น geerlingguy.mysql, geerlingguy.nginx — ติดตั้งด้วย ansible-galaxy install geerlingguy.mysql แล้วเรียกใช้ใน Playbook ได้ทันที
Tips การใช้งานจริง
- ใช้
--check(dry run) เพื่อดูว่า Playbook จะทำอะไรก่อนรันจริง:ansible-playbook --check setup.yml - ใช้
--diffควบคู่เพื่อดูความแตกต่างของไฟล์ที่จะเปลี่ยน - เก็บ inventory และ playbooks ไว้ใน Git เพื่อ version control และ team collaboration
- ใช้
ansible-vault encrypt vars/secrets.ymlเพื่อเข้ารหัส password ก่อน commit - ตั้งชื่อ Task ให้อ่านออกทุกคน — อ่าน log แล้วรู้ทันทีว่าทำอะไร
Dynamic Inventory — จัดการ VPS จาก Cloud Provider อัตโนมัติ
เมื่อโครงสร้างพื้นฐานมี VPS จำนวนมากหรือมีการเพิ่ม-ลดเครื่องบ่อย การเขียน inventory.ini แบบ static ไม่ใช่วิธีที่ดีนัก Ansible รองรับ Dynamic Inventory ที่ดึงรายชื่อเครื่องจาก API ของ Cloud Provider หรือฐานข้อมูลของคุณเองแบบ real-time
ตัวอย่าง Dynamic Inventory Script (Python)
#!/usr/bin/env python3
# dynamic_inventory.py — ดึงรายชื่อ VPS จาก API
import json, urllib.request
def get_servers():
# ดึงข้อมูล VPS จาก API (ปรับ URL ให้ตรงกับ provider ของคุณ)
req = urllib.request.urlopen('https://api.yourprovider.com/v1/servers')
servers = json.loads(req.read())
inventory = {
"webservers": {"hosts": []},
"_meta": {"hostvars": {}}
}
for s in servers:
if s.get("role") == "web":
inventory["webservers"]["hosts"].append(s["ip"])
inventory["_meta"]["hostvars"][s["ip"]] = {
"ansible_user": "root",
"app_domain": s["domain"]
}
return inventory
print(json.dumps(get_servers(), indent=2))
# ใช้ dynamic inventory แทน static file
ansible-playbook -i dynamic_inventory.py setup-nginx.yml
ข้อดีของ Dynamic Inventory คือเมื่อเพิ่ม VPS ใหม่ใน Cloud Provider ระบบจะรู้จักเครื่องนั้นโดยอัตโนมัติในครั้งถัดไปที่รัน Playbook ไม่ต้องแก้ไฟล์ inventory ด้วยมือ ซึ่งสำคัญมากเมื่อมีการ scale หรือ auto-scaling
Ansible ยังรองรับ Plugin สำเร็จรูปสำหรับ Cloud Provider หลายเจ้า เช่น amazon.aws.aws_ec2, google.cloud.gcp_compute, vultr.cloud.vultr ติดตั้งผ่าน Ansible Galaxy แล้วกำหนดค่าใน YAML file สั้นๆ แทน static inventory ได้ทันที ช่วยให้ทีม DevOps สามารถจัดการ infrastructure ขนาดใหญ่ได้อย่างมีระเบียบและลดความผิดพลาดจากการพิมพ์ IP address ผิด
Jinja2 Templates — สร้าง Config File แบบ Dynamic
หนึ่งในฟีเจอร์ที่ทรงพลังของ Ansible คือการใช้ Jinja2 Template สร้างไฟล์ config ที่แตกต่างกันในแต่ละเครื่อง โดยใช้ตัวแปรที่กำหนดไว้ใน inventory หรือ vars
ตัวอย่าง Nginx config template
# templates/nginx.conf.j2
server {
listen 80;
server_name {{ app_domain }};
root /var/www/{{ app_name }}/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
ใช้ template module ใน Playbook
tasks:
- name: สร้าง Nginx config จาก template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
owner: root
group: root
mode: '0644'
notify: Reload Nginx
แต่ละ VPS ในกลุ่ม webservers จะได้ config ที่แตกต่างกันตามค่าตัวแปรของตัวเอง เช่น domain, app name, php version ทำให้ manage หลายเว็บไซต์บน VPS ต่างเครื่องได้สะดวกโดยใช้ Playbook เดียว Jinja2 ยังรองรับ Condition, Loop และ Filter ที่ทรงพลัง ทำให้สร้าง config file ที่ซับซ้อนได้โดยไม่ต้องเขียนไฟล์แยกสำหรับแต่ละ environment เหมาะอย่างยิ่งสำหรับ VPS ที่ต้องการ config ที่คล้ายกันแต่มีความแตกต่างกันเล็กน้อยในแต่ละโดเมนหรือแต่ละลูกค้า
Ansible Vault — เข้ารหัส Secret อย่างปลอดภัย
เมื่อ Playbook มี password หรือ API key ที่ต้องเก็บเป็นความลับ ใช้ Ansible Vault เข้ารหัสไฟล์ก่อน commit ขึ้น Git เพื่อความปลอดภัย
สร้างและเข้ารหัสไฟล์ Variables
# สร้างไฟล์ secrets ที่เข้ารหัส
ansible-vault create vars/secrets.yml
# แก้ไขไฟล์ที่เข้ารหัสแล้ว
ansible-vault edit vars/secrets.yml
# เข้ารหัสไฟล์ที่มีอยู่แล้ว
ansible-vault encrypt vars/secrets.yml
# ถอดรหัสชั่วคราว (เพื่อดู)
ansible-vault decrypt vars/secrets.yml
ใช้ Vault ใน Playbook
vars_files:
- vars/main.yml
- vars/secrets.yml # ไฟล์ที่เข้ารหัส
# รัน Playbook พร้อม vault password
ansible-playbook -i inventory.ini deploy.yml --ask-vault-pass
# หรือใช้ไฟล์ password (สำหรับ CI/CD)
ansible-playbook -i inventory.ini deploy.yml --vault-password-file ~/.vault_pass
แนะนำ: ใส่ vars/secrets.yml ใน .gitignore ไม่ได้ผล เพราะอาจลืม encrypt ก่อน commit ให้ใช้ ansible-vault encrypt ทุกครั้งแทน แล้ว commit ไฟล์ที่เข้ารหัสแล้วได้อย่างปลอดภัย
Ansible ร่วมกับ CI/CD Pipeline
นำ Ansible ไปใช้ร่วมกับ CI/CD เช่น GitHub Actions ให้ deploy อัตโนมัติเมื่อมีการ push โค้ดขึ้น repository
ตัวอย่าง GitHub Actions Workflow
# .github/workflows/deploy.yml
name: Deploy with Ansible
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip3 install ansible
- name: Write SSH Key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: Run Ansible Playbook
run: |
ansible-playbook -i inventory.ini deploy.yml \
--vault-password-file <(echo "${{ secrets.VAULT_PASS }}")
ด้วย workflow นี้ทุกครั้งที่ merge โค้ดเข้า main GitHub Actions จะรัน Ansible Playbook โดยอัตโนมัติ ทำให้ deploy ไปยัง VPS ทุกเครื่องพร้อมกันโดยไม่ต้องทำเอง
| เครื่องมือ CI/CD | ใช้งานร่วมกับ Ansible | ความซับซ้อน |
|---|---|---|
| GitHub Actions | pip install ansible ใน runner | ต่ำ |
| GitLab CI | Docker image ที่มี Ansible ติดตั้งแล้ว | ต่ำ |
| Jenkins | Ansible plugin หรือ shell step | กลาง |
| AWX / Ansible Tower | Built-in — ออกแบบมาคู่กัน | สูง (enterprise) |
Troubleshooting ปัญหาที่พบบ่อย
ปัญหาที่นักพัฒนาเจอบ่อยเมื่อเริ่มใช้ Ansible และวิธีแก้ไข
SSH Connection Refused
# ตรวจสอบว่า SSH ใช้ key ถูกต้อง
ansible -i inventory.ini all -m ping -vvv
# ตรวจสอบ fingerprint ก่อน (ครั้งแรก)
ssh-keyscan -H 203.0.113.10 >> ~/.ssh/known_hosts
Permission Denied เมื่อรัน Task
# ถ้า task ต้องการ sudo ต้องใส่ become: yes
- name: ติดตั้ง Package
apt:
name: nginx
state: present
become: yes # ← บังคับต้องมี
# หรือใส่ระดับ play
- hosts: webservers
become: yes # ← ใช้กับทุก task ใน play นี้
Python ไม่ถูก Detect บน Managed Node
# ระบุ Python interpreter โดยตรงใน inventory
web1 ansible_host=203.0.113.10 ansible_python_interpreter=/usr/bin/python3
# หรือกำหนดระดับ group
[all:vars]
ansible_python_interpreter=/usr/bin/python3
เคล็ดลับ Debug: ใช้ -v, -vv, หรือ -vvv เพื่อเพิ่ม verbosity ของ output จะเห็น SSH command จริงที่ Ansible รัน ช่วยหาปัญหาได้รวดเร็ว
เปรียบเทียบ Ansible กับ Configuration Management Tools อื่น
มี tool หลายตัวที่ทำงานคล้าย Ansible แต่แต่ละตัวมีจุดแข็งและจุดอ่อนต่างกัน ช่วยให้เลือกได้เหมาะกับโปรเจกต์
| Tool | Agent | ภาษา Config | เหมาะกับ |
|---|---|---|---|
| Ansible | ไม่มี (Agentless) | YAML | ทั่วไป ทุกขนาด |
| Puppet | มี | DSL | Enterprise ขนาดใหญ่ |
| Chef | มี | Ruby DSL | Dev ที่รู้ Ruby |
| Terraform | ไม่มี | HCL | Infrastructure provisioning |
Ansible เหมาะที่สุดสำหรับการจัดการ VPS เมื่อต้องการความง่าย ไม่มี agent และทำงานผ่าน SSH ที่มีอยู่แล้ว โดยเฉพาะเมื่อทีมส่วนใหญ่คุ้นเคยกับ YAML มากกว่า Ruby หรือ DSL เฉพาะของ Puppet สำหรับโปรเจกต์ที่เพิ่งเริ่มต้นและมี VPS ไม่เกิน 50 เครื่อง Ansible แบบ standalone ก็เพียงพอมากโดยไม่ต้องลงทุนกับ Ansible Tower หรือ AWX ซึ่งเหมาะกับ enterprise ขนาดใหญ่ที่ต้องการ GUI และ RBAC สำหรับทีมหลายคน
ต้องการ VPS สำหรับรัน Ansible?
AsiaGB มี VPS Linux Ubuntu/Debian พร้อม Full Root Access เริ่มต้น 500 บาท/เดือน
ดู VPS Plans