#การติดตั้ง Ansible บน CentOS Stream 10 แบบทีละขั้นตอน

Ansible เป็นเครื่องมือ Infrastructure Automation ที่ใช้จัดการเซิร์ฟเวอร์ ติดตั้งซอฟต์แวร์ ปรับแต่งค่าระบบ และ Deploy แอปพลิเคชัน โดยใช้ไฟล์ YAML ที่เรียกว่า Playbook

จุดเด่นสำคัญคือ Ansible ทำงานแบบ Agentless กล่าวคือ เราติดตั้ง Ansible เฉพาะเครื่องควบคุมหรือ Control Node ส่วนเครื่องปลายทางหรือ Managed Node ไม่ต้องติดตั้ง Agent ของ Ansible เพียงแค่เปิดใช้งาน SSH มีบัญชีผู้ใช้สำหรับเชื่อมต่อ และมี Python ที่รองรับ

หมายเหตุ: ชื่อระบบปฏิบัติการรุ่นปัจจุบันคือ CentOS Stream 10 บทความนี้จึงใช้ชื่อนี้แทนคำว่า “CentOS 10” และไม่ใช้คำสั่งจาก CentOS Linux 7 หรือ CentOS Linux 8 ซึ่งสิ้นสุดอายุการสนับสนุนแล้ว

#ภาพรวมระบบที่ใช้ในบทความ

ตัวอย่างนี้ประกอบด้วยเซิร์ฟเวอร์ 3 เครื่อง

บทบาท Hostname IP Address หน้าที่
Control Node ansible-control 192.168.1.10 ติดตั้งและสั่งงาน Ansible
Managed Node 1 node1 192.168.1.20 เครื่องปลายทางกลุ่ม Web Server
Managed Node 2 node2 192.168.1.21 เครื่องปลายทางกลุ่ม Web Server

ให้เปลี่ยน IP Address และชื่อผู้ใช้ในตัวอย่างให้ตรงกับระบบจริง

#สิ่งที่ต้องเตรียม

  • CentOS Stream 10 สำหรับใช้เป็น Control Node
  • เครื่องปลายทาง Linux อย่างน้อย 1 เครื่อง
  • บัญชีผู้ใช้ที่ใช้คำสั่ง sudo ได้
  • Control Node ติดต่อ Managed Node ผ่านเครือข่ายได้
  • Managed Node เปิดบริการ SSH ที่พอร์ต 22 หรือพอร์ตที่กำหนดเอง
  • เวลาในแต่ละเครื่องควรตรงกัน เช่น เปิดใช้ chronyd

#เลือกแพ็กเกจ Ansible ให้เหมาะสม

Ansible มีแพ็กเกจหลักสองแบบ

แพ็กเกจ รายละเอียด เหมาะสำหรับ
ansible-core ตัวประมวลผล Ansible พร้อม Built-in Modules และ Plugins พื้นฐาน เริ่มต้นเรียนรู้ เซิร์ฟเวอร์ทั่วไป และระบบที่ต้องการควบคุม Dependencies
ansible รวม ansible-core และ Community Collections จำนวนมาก ผู้ที่ต้องใช้โมดูลจากหลายระบบและต้องการชุดเครื่องมือแบบรวม

บทความนี้ใช้ ansible-core จาก Repository ของระบบเป็นแนวทางหลัก แล้วติดตั้ง Collection ที่จำเป็นเพิ่มด้วย ansible-galaxy วิธีนี้ดูแลและอัปเดตผ่าน dnf ได้ง่าย


#ขั้นตอนที่ 1: ตรวจสอบเวอร์ชัน CentOS

เข้าสู่ระบบ Control Node แล้วตรวจสอบข้อมูลระบบปฏิบัติการ

cat /etc/os-release

ควรพบข้อมูลลักษณะนี้

NAME="CentOS Stream"
VERSION="10 (...)"
PLATFORM_ID="platform:el10"

ตรวจสอบสถาปัตยกรรมของเครื่อง

uname -m

ค่าที่พบบ่อยคือ x86_64 หรือ aarch64

#ขั้นตอนที่ 2: อัปเดตระบบ

อัปเดต Package Metadata และแพ็กเกจทั้งหมดก่อนติดตั้ง

sudo dnf upgrade --refresh -y

หากมีการอัปเดต Kernel หรือไลบรารีหลัก ควรรีสตาร์ตเครื่อง

sudo reboot

หลังจากเครื่องกลับมาทำงานแล้ว ให้เข้าสู่ระบบอีกครั้ง

#ขั้นตอนที่ 3: ติดตั้ง Ansible บน Control Node

ติดตั้ง ansible-core และ SSH Client

sudo dnf install -y ansible-core openssh-clients

ตรวจสอบแพ็กเกจที่ติดตั้ง

rpm -q ansible-core

ตรวจสอบเวอร์ชัน Ansible

ansible --version
ansible-playbook --version

ผลลัพธ์จะแสดงเวอร์ชันของ ansible-core ตำแหน่งไฟล์ Configuration เวอร์ชัน Python และตำแหน่งติดตั้ง Module ตัวอย่างเช่น

ansible [core 2.x.x]
  python version = 3.x.x

หมายเลขเวอร์ชันอาจเปลี่ยนไปตามรอบการอัปเดตของ CentOS Stream 10 จึงไม่ควรยึดติดกับหมายเลขในตัวอย่าง ให้ตรวจสอบว่าโปรแกรมทำงานและเป็นเวอร์ชันที่ Repository ปัจจุบันจัดเตรียมไว้

#ขั้นตอนที่ 4: เตรียม Managed Node

ทำขั้นตอนนี้บนเครื่อง node1 และ node2 ทุกเครื่อง

#4.1 ติดตั้ง Python, SSH Server และ sudo

sudo dnf install -y python3 openssh-server sudo

#4.2 เปิดใช้งาน SSH Server

sudo systemctl enable --now sshd
sudo systemctl status sshd --no-pager

#4.3 อนุญาต SSH ผ่าน Firewall

ตรวจสอบว่า firewalld ทำงานอยู่หรือไม่

sudo systemctl is-active firewalld

หากแสดงผลเป็น active ให้อนุญาตบริการ SSH

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

#4.4 สร้างบัญชีสำหรับ Ansible

ตัวอย่างนี้สร้างบัญชีชื่อ ansible

sudo useradd --create-home --shell /bin/bash ansible
sudo passwd ansible

อนุญาตให้บัญชี ansible ใช้ sudo โดยไม่ถามรหัสผ่าน เพื่อให้ Playbook ที่กำหนด become: true ทำงานอัตโนมัติได้

echo 'ansible ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/ansible > /dev/null
sudo chmod 440 /etc/sudoers.d/ansible
sudo visudo -cf /etc/sudoers.d/ansible

คำสั่งสุดท้ายควรแสดงผลว่าไฟล์ผ่านการตรวจสอบ

/etc/sudoers.d/ansible: parsed OK

การให้สิทธิ์ NOPASSWD: ALL เหมาะกับ Lab และช่วยให้เริ่มต้นได้ง่าย สำหรับ Production ควรจำกัดคำสั่งที่อนุญาตให้เท่าที่จำเป็น หรือใช้ระบบจัดการสิทธิ์และ Secrets ขององค์กร

#ขั้นตอนที่ 5: สร้างและส่ง SSH Key

กลับมาทำงานบน Control Node

#5.1 สร้าง SSH Key

ssh-keygen -t ed25519 -C "ansible-control"

กด Enter เพื่อใช้ตำแหน่งเริ่มต้นคือ ~/.ssh/id_ed25519 และตั้ง Passphrase ตามนโยบายความปลอดภัยของระบบ

ถ้าตั้ง Passphrase และต้องการรันงานแบบอัตโนมัติ ควรใช้ ssh-agent หรือระบบจัดการกุญแจที่ปลอดภัย ไม่ควรเขียน Passphrase ไว้ใน Playbook

#5.2 ส่ง Public Key ไปยัง Managed Node

ssh-copy-id ansible@192.168.1.20
ssh-copy-id ansible@192.168.1.21

ระบบจะถามรหัสผ่านของบัญชี ansible ที่ตั้งไว้ในขั้นตอนก่อนหน้า

#5.3 ทดสอบการเชื่อมต่อ

ssh ansible@192.168.1.20

เมื่อเชื่อมต่อสำเร็จ ให้ทดสอบสิทธิ์ sudo

sudo -n true
echo $?

ถ้าแสดงค่า 0 แสดงว่าสามารถใช้ sudo แบบไม่ถามรหัสผ่านได้ จากนั้นออกจาก Managed Node

exit

ทำซ้ำกับ 192.168.1.21 การเชื่อมต่อด้วย ssh ครั้งแรกยังช่วยบันทึก Host Key ลงในไฟล์ ~/.ssh/known_hosts โดยผู้ดูแลควรตรวจสอบ Fingerprint ก่อนยอมรับ

#ขั้นตอนที่ 6: สร้างโปรเจกต์ Ansible

บน Control Node ให้สร้างโครงสร้างโปรเจกต์

mkdir -p ~/ansible-lab/{inventory,playbooks}
cd ~/ansible-lab

โครงสร้างที่ได้จะเป็นดังนี้

ansible-lab/
├── ansible.cfg
├── inventory/
│   └── hosts.yml
├── playbooks/
│   └── install-nginx.yml
└── requirements.yml

#ขั้นตอนที่ 7: สร้างไฟล์ Inventory

สร้างไฟล์ inventory/hosts.yml

all:
  children:
    webservers:
      hosts:
        node1:
          ansible_host: 192.168.1.20
        node2:
          ansible_host: 192.168.1.21
  vars:
    ansible_user: ansible
    ansible_python_interpreter: /usr/bin/python3

ความหมายของค่าที่สำคัญ

  • webservers คือชื่อกลุ่มของเครื่องปลายทาง
  • node1 และ node2 คือชื่อ Alias ที่ใช้ใน Ansible
  • ansible_host คือ IP Address หรือ DNS Name ที่ใช้เชื่อมต่อจริง
  • ansible_user คือบัญชี SSH บน Managed Node
  • ansible_python_interpreter ระบุ Python ที่ Ansible ใช้บนเครื่องปลายทาง

หาก SSH ใช้พอร์ตอื่น เช่น 2222 ให้เพิ่มตัวแปรต่อไปนี้ในระดับ Host หรือ Group

ansible_port: 2222

หากต้องใช้ Private Key คนละไฟล์ สามารถกำหนดได้ดังนี้

ansible_ssh_private_key_file: ~/.ssh/ansible_ed25519

#ขั้นตอนที่ 8: สร้างไฟล์ ansible.cfg

สร้างไฟล์ ansible.cfg ที่ Root ของโปรเจกต์

[defaults]
inventory = ./inventory/hosts.yml
host_key_checking = True
interpreter_python = auto_silent
retry_files_enabled = False

[ssh_connection]
pipelining = True

การเปิด host_key_checking ช่วยลดความเสี่ยงจากการเชื่อมต่อผิดเครื่องหรือการโจมตีแบบ Man-in-the-Middle ไม่ควรปิดเพื่อแก้ปัญหา Host Key ในระบบ Production

ตรวจสอบว่า Ansible อ่าน Configuration จากไฟล์ใด

ansible --version

บรรทัด config file ควรชี้มายังไฟล์ ~/ansible-lab/ansible.cfg

#ขั้นตอนที่ 9: ตรวจสอบ Inventory

ตรวจสอบ Syntax และแสดงโครงสร้าง Host

ansible-inventory --graph

ตัวอย่างผลลัพธ์

@all:
  |--@ungrouped:
  |--@webservers:
  |  |--node1
  |  |--node2

หากต้องการดูข้อมูลทั้งหมดในรูปแบบ JSON ให้ใช้

ansible-inventory --list

#ขั้นตอนที่ 10: ทดสอบ Managed Node ด้วยโมดูล ping

ansible all -m ansible.builtin.ping

เมื่อทำงานสำเร็จ แต่ละเครื่องควรตอบกลับคล้ายตัวอย่างนี้

node1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

ansible.builtin.ping ไม่ใช่ ICMP Ping แต่เป็นการทดสอบว่า Ansible สามารถเชื่อมต่อผ่าน SSH เรียกใช้ Python และส่งผลลัพธ์กลับมาได้

ทดสอบคำสั่งทั่วไปบนทุกเครื่อง

ansible all -m ansible.builtin.command -a "hostnamectl --static"
ansible all -m ansible.builtin.command -a "cat /etc/os-release"

ทดสอบการยกระดับสิทธิ์ด้วย become

ansible all --become -m ansible.builtin.command -a "id"

ผลลัพธ์ควรแสดง uid=0(root)

#ขั้นตอนที่ 11: ติดตั้ง Collection ที่ใช้ในตัวอย่าง

โมดูลพื้นฐานอยู่ใน ansible-core ส่วนโมดูลเฉพาะทางจำนวนมากถูกแยกเป็น Collections ตัวอย่าง Playbook ในบทความจะใช้ ansible.posix.firewalld

สร้างไฟล์ requirements.yml

---
collections:
  - name: ansible.posix

ติดตั้ง Collection

ansible-galaxy collection install -r requirements.yml

ตรวจสอบรายการ Collection ที่ติดตั้ง

ansible-galaxy collection list

ควรเก็บ requirements.yml ไว้ใน Git เพื่อให้สมาชิกในทีมและระบบ CI/CD ติดตั้ง Dependencies ชุดเดียวกันได้

#ขั้นตอนที่ 12: สร้าง Playbook แรก

สร้างไฟล์ playbooks/install-nginx.yml

---
- name: Install and configure Nginx
  hosts: webservers
  become: true

  tasks:
    - name: Install required packages
      ansible.builtin.dnf:
        name:
          - nginx
          - firewalld
          - python3-firewall
        state: present

    - name: Start and enable firewalld
      ansible.builtin.service:
        name: firewalld
        state: started
        enabled: true

    - name: Start and enable Nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

    - name: Create a test home page
      ansible.builtin.copy:
        dest: /usr/share/nginx/html/index.html
        content: |
          <!doctype html>
          <html lang="th">
          <head>
            <meta charset="utf-8">
            <title>Ansible on CentOS Stream 10</title>
          </head>
          <body>
            <h1>ติดตั้ง Nginx ด้วย Ansible สำเร็จ</h1>
            <p>Host: {{ inventory_hostname }}</p>
          </body>
          </html>
        owner: root
        group: root
        mode: "0644"

    - name: Allow HTTP through firewalld
      ansible.posix.firewalld:
        service: http
        permanent: true
        immediate: true
        state: enabled

Playbook นี้จะทำงานแบบ Idempotent คือเมื่อรันซ้ำ Ansible จะเปลี่ยนแปลงเฉพาะส่วนที่ยังไม่ตรงกับสถานะที่กำหนด

#ขั้นตอนที่ 13: ตรวจสอบและรัน Playbook

#13.1 ตรวจสอบ Syntax

ansible-playbook --syntax-check playbooks/install-nginx.yml

#13.2 ทดลองประเมินการเปลี่ยนแปลง

ansible-playbook --check --diff playbooks/install-nginx.yml

Check Mode เป็นการจำลองที่มีประโยชน์ แต่บางโมดูลไม่สามารถจำลองผลลัพธ์ได้สมบูรณ์ จึงควรทดสอบใน Environment สำหรับทดสอบก่อนใช้งานจริงเสมอ

#13.3 รัน Playbook

ansible-playbook playbooks/install-nginx.yml

เมื่อทำงานเสร็จจะมีสรุป PLAY RECAP

node1 : ok=5 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=5 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

จำนวน ok และ changed อาจต่างจากตัวอย่างตามสถานะเดิมของแต่ละเครื่อง จุดสำคัญคือ unreachable=0 และ failed=0

#ขั้นตอนที่ 14: ตรวจสอบผลลัพธ์

ตรวจสอบสถานะ Nginx ผ่าน Ansible

ansible webservers --become \
  -m ansible.builtin.command \
  -a "systemctl is-active nginx"

ทดสอบหน้าเว็บจาก Control Node

curl http://192.168.1.20
curl http://192.168.1.21

ทดสอบรัน Playbook ซ้ำ

ansible-playbook playbooks/install-nginx.yml

หากไม่มีการแก้ไขข้อมูลภายนอก Play Recap ควรแสดง changed=0 ซึ่งเป็นหลักฐานเบื้องต้นว่า Playbook ทำงานแบบ Idempotent


#ทางเลือก: ติดตั้งแพ็กเกจ Ansible แบบรวมด้วย pipx

หากต้องการแพ็กเกจ ansible รุ่นใหม่จาก Python Package Index ซึ่งรวม Community Collections ที่คัดเลือกไว้ สามารถติดตั้งผ่าน pipx เพื่อแยก Environment ออกจาก System Python

#1. เปิดใช้งาน CRB และ EPEL

ติดตั้ง Plugin สำหรับ dnf config-manager

sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --set-enabled crb

ติดตั้ง EPEL Release Package

sudo dnf install -y epel-release

หากระบบไม่พบ epel-release สามารถติดตั้งจาก URL ทางการได้

sudo dnf install -y \
  https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm

ตรวจสอบ Repository

dnf repolist --enabled

#2. ติดตั้ง pipx

sudo dnf install -y pipx
pipx ensurepath

ออกจากระบบแล้วเข้าสู่ระบบใหม่ หรือโหลด Shell Configuration อีกครั้ง

source ~/.bashrc

#3. ติดตั้ง Ansible

pipx install --include-deps ansible

ตรวจสอบเวอร์ชัน

ansible --version
ansible-community --version

อัปเกรดในภายหลังได้ด้วย

pipx upgrade --include-injected ansible

ถอนการติดตั้งด้วย

pipx uninstall ansible

ควรเลือกใช้วิธีใดวิธีหนึ่งเป็นหลักในแต่ละ Control Node เพื่อหลีกเลี่ยงความสับสนว่า Shell เรียก ansible จาก dnf หรือ pipx ตรวจสอบตำแหน่งได้ด้วย command -v ansible

#การอัปเดตและถอนการติดตั้งแบบ dnf

อัปเดต ansible-core

sudo dnf upgrade -y ansible-core

ถอนการติดตั้ง

sudo dnf remove -y ansible-core

ไฟล์โปรเจกต์ Inventory และ Playbook ใน Home Directory จะไม่ถูกลบโดยคำสั่งนี้


#การแก้ปัญหาที่พบบ่อย

#ปัญหา: No match for argument: ansible-core

ตรวจสอบ Repository ที่เปิดใช้งาน

dnf repolist --enabled
dnf repolist all | grep -i appstream

ล้าง Cache และสร้าง Metadata ใหม่

sudo dnf clean all
sudo dnf makecache
sudo dnf install -y ansible-core

หาก Image ของผู้ให้บริการ Cloud ปิด Repository บางส่วน ให้ตรวจสอบไฟล์ใน /etc/yum.repos.d/ และเอกสารของผู้ให้บริการ หรือใช้วิธี pipx ที่อธิบายไว้ข้างต้น

#ปัญหา: UNREACHABLE หรือ Permission denied (publickey)

ทดสอบ SSH โดยตรงก่อน

ssh -vvv ansible@192.168.1.20

ตรวจสอบรายการต่อไปนี้

  • IP Address, DNS Name และ ansible_user ใน Inventory ถูกต้อง
  • Public Key อยู่ใน ~/.ssh/authorized_keys ของบัญชีปลายทาง
  • สิทธิ์ Directory และไฟล์ SSH ถูกต้อง
  • Firewall อนุญาตพอร์ต SSH
  • บริการ sshd กำลังทำงาน

บน Managed Node สามารถตั้ง Permission มาตรฐานได้ดังนี้

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

#ปัญหา: Host key verification failed

เชื่อมต่อด้วย SSH โดยตรงและตรวจสอบ Fingerprint ของเครื่องปลายทางก่อนยอมรับ

ssh ansible@192.168.1.20

หากเครื่องปลายทางถูกติดตั้งใหม่และ Host Key เปลี่ยน ให้ตรวจสอบก่อนว่าเป็นเครื่องที่ถูกต้อง แล้วจึงลบ Key เดิม

ssh-keygen -R 192.168.1.20

จากนั้นเชื่อมต่อใหม่เพื่อบันทึก Key ที่ตรวจสอบแล้ว ไม่แนะนำให้ตั้ง host_key_checking = False เพื่อข้ามปัญหาโดยไม่ตรวจสอบสาเหตุ

#ปัญหา: sudo: a password is required

ถ้าบัญชีถูกกำหนดให้ถามรหัสผ่าน sudo ให้รัน Playbook พร้อมตัวเลือกนี้

ansible-playbook --ask-become-pass playbooks/install-nginx.yml

หรือทบทวนไฟล์ /etc/sudoers.d/ansible และตรวจสอบด้วย visudo

#ปัญหา: Python Interpreter ไม่ถูกต้อง

ตรวจสอบ Python บน Managed Node

ssh ansible@192.168.1.20 'command -v python3 && python3 --version'

จากนั้นกำหนด Path ให้ตรงกับเครื่องจริง เช่น

ansible_python_interpreter: /usr/bin/python3

#ปัญหา: Collection หรือ Module ไม่พบ

ตรวจสอบ Collection ที่ติดตั้ง

ansible-galaxy collection list

ติดตั้ง Dependencies จากไฟล์ของโปรเจกต์อีกครั้ง

ansible-galaxy collection install -r requirements.yml --upgrade

#แนวทางความปลอดภัยสำหรับ Production

  • ใช้ SSH Key แทน Password และปิด Password Login หลังตรวจสอบว่า Key ใช้งานได้
  • เก็บ Passphrase และข้อมูลลับด้วย Secret Manager หรือ Ansible Vault
  • ห้ามเขียน Password, Token หรือ Private Key ลงใน Inventory และ Git Repository
  • จำกัดสิทธิ์ sudo ของบัญชี Automation ให้เหมาะกับงาน
  • เปิด host_key_checking และตรวจสอบ Fingerprint ของ Managed Node
  • คง SELinux ไว้ที่โหมด Enforcing และแก้ Policy ให้ถูกต้องเมื่อพบปัญหา
  • แยก Inventory สำหรับ Development, Staging และ Production
  • ระบุเวอร์ชัน Collections ใน requirements.yml เมื่อต้องการ Build ที่ทำซ้ำได้
  • ใช้ --check --diff และทดสอบกับเครื่องกลุ่มเล็กก่อน Deploy ทั้งระบบ
  • เก็บ Playbook, Roles และ Inventory ที่ไม่เป็นความลับไว้ใน Version Control

#สรุป

ขั้นตอนหลักของการติดตั้งและเริ่มใช้งาน Ansible บน CentOS Stream 10 คือ

  1. อัปเดต CentOS Stream 10
  2. ติดตั้ง ansible-core บน Control Node ด้วย dnf
  3. เตรียม Python, SSH และบัญชีผู้ใช้บน Managed Node
  4. สร้าง SSH Key และทดสอบการเชื่อมต่อ
  5. สร้าง Inventory และ ansible.cfg
  6. ทดสอบด้วย ansible.builtin.ping
  7. สร้าง ตรวจสอบ และรัน Playbook
  8. ทดสอบการรันซ้ำเพื่อยืนยัน Idempotency

เมื่อทำตามขั้นตอนทั้งหมดแล้ว Control Node จะพร้อมใช้บริหาร CentOS Stream 10 หลายเครื่องจากส่วนกลาง และสามารถต่อยอดไปสู่ Roles, Templates, Variables, Ansible Vault, Dynamic Inventory และ CI/CD ได้

#เอกสารอ้างอิง