สอน Ansible: สร้าง playbook แรกบน VPS Ubuntu 24.04
ติดตั้ง Ansible ด้วย pipx บน Ubuntu 24.04 เขียน inventory และ playbook เสริมความปลอดภัย VPS ใหม่ พร้อมแก้ Permission denied และ sudo errors
สิ่งที่คุณกำลังสร้าง
เครื่องควบคุม 1 เครื่องที่ติดตั้ง Ansible และ Ubuntu 24.04 VPS ใหม่ 1 เครื่องขึ้นไป ซึ่งมีเพียงอิมเมจมาตรฐานติดตั้งอยู่ เมื่อเสร็จสิ้น คุณจะมีไฟล์ inventory ที่ระบุชื่อเซิร์ฟเวอร์ คำสั่ง ping แบบ ad-hoc ที่ยืนยันว่าการยืนยันตัวตนทำงานได้ตั้งแต่ต้นจนจบ และ playbook ที่เรียกใช้รายการตรวจสอบ VPS ใหม่ทั้งหมดในรูปแบบโค้ด ได้แก่ ผู้ใช้ deploy พร้อม SSH key ของคุณ การเสริมความปลอดภัยให้ sshd, fail2ban, unattended upgrades และไฟร์วอลล์ที่อนุญาต OpenSSH ก่อนปฏิเสธการเชื่อมต่ออื่นทั้งหมด คุณสามารถชี้ไปที่เซิร์ฟเวอร์ 1 เครื่องหรือ 20 เครื่องได้ เรียกใช้ 2 ครั้ง และการเรียกใช้ครั้งที่ 2 จะไม่เปลี่ยนแปลงอะไร นี่คือจุดประสงค์ทั้งหมด
หลังจากจัดเตรียม VPS มา 15 ปี ผมบอกได้จากรูปแบบที่พบจริงว่า ทุกคนมักตั้งค่าเซิร์ฟเวอร์ 5 เครื่องแรกด้วยตนเอง จากนั้นต้องเสียเวลาตลอดสุดสัปดาห์กับเครื่องที่ 6 เพราะไม่มีใครจำได้ว่าตั้งค่า 5 เครื่องแรกไว้อย่างไร คู่มือนี้อธิบายเพิ่มเติมจากบทสำรวจเรื่อง การจัดการเซิร์ฟเวอร์ Linux หลายเครื่อง โปรดอ่านเมื่อคุณเริ่มรู้ตัวว่ากำลังพิมพ์ apt install เดิมลงในเทอร์มินัล 3 หน้าต่าง
Ansible คืออะไรในย่อหน้าเดียว
Ansible ทำงานโดยไม่ต้องติดตั้งเอเจนต์ ไม่มี daemon ที่ต้องติดตั้งบนเซิร์ฟเวอร์ที่ Ansible จัดการ เครื่องควบคุมจะเชื่อมต่อผ่าน SSH ตามปกติ คัดลอกโมดูล Python ขนาดเล็กไปยังเป้าหมาย เรียกใช้งานโมดูลนั้น อ่าน JSON ที่โมดูลแสดงผล แล้วลบโมดูลออก สิ่งเดียวที่เป้าหมายต้องมีคือ python3 ซึ่งมีอยู่แล้วใน Ubuntu image มาตรฐานทุกชุด คำสำคัญคือ idempotent ซึ่งมีความหมายตรงไปตรงมา: task จะอธิบาย สถานะ ไม่ใช่การกระทำ state: present สำหรับ package หมายถึง “ตรวจสอบให้แน่ใจว่าติดตั้ง package นี้แล้ว” ไม่ใช่ “เรียกใช้ตัวติดตั้ง” หากสถานะนั้นเป็นจริงอยู่แล้ว Ansible จะไม่ดำเนินการใด ๆ และรายงานเป็น ok แทน changed คุณสมบัตินี้คือหัวใจของผลิตภัณฑ์ ทำให้การเรียกใช้ playbook ซ้ำมีความปลอดภัย และการเรียกใช้ซ้ำอย่างปลอดภัยนี่เองที่เปลี่ยน shell script ให้เป็นโครงสร้างพื้นฐาน
ข้อกำหนดเบื้องต้นและข้อควรระวังตั้งแต่ต้น
- เครื่องควบคุม: แล็ปท็อปหรือ VPS ขนาดเล็กของคุณ สมมติว่าใช้ Ubuntu 24.04; macOS ทำงานเหมือนกันเมื่อ ติดตั้ง pipx จาก Homebrew แล้ว
- VPS เป้าหมายอย่างน้อย 1 เครื่องที่ใช้ Ubuntu 24.04 บน KVM และเข้าถึงได้ด้วยบัญชี root จะไม่มีการติดตั้งสิ่งใดบนเครื่องเหล่านี้
- การตรวจสอบสิทธิ์ด้วย SSH key สำหรับเป้าหมายทุกเครื่อง Ansible จะใช้การตรวจสอบสิทธิ์แบบเดียวกับคำสั่ง
sshของคุณ หากssh root@hostขอรหัสผ่าน Ansible จะทำงานไม่สำเร็จ - บน Ubuntu 24.04
pip install ansibleจะล้มเหลวพร้อมerror: externally-managed-environmentนี่เป็นนโยบายของ distro โดยเจตนา ไม่ใช่ความเสียหาย ให้ใช้ pipx - ช่องว่างใน YAML เป็นส่วนหนึ่งของไวยากรณ์ การเยื้องที่ไม่ถูกต้องจะทำให้เกิด
mapping values are not allowed in this contextและอักขระแท็บแม้เพียงหนึ่งตัวก็ทำให้ใช้งานไม่ได้ - เปิดเซสชัน SSH ที่ใช้งานได้ค้างไว้บนเป้าหมายแต่ละเครื่อง ขณะที่ playbook ทำให้ sshd มีความปลอดภัยมากขึ้น ทุกกรณีที่ช่วยกู้คืนการถูกล็อกไม่ให้เข้าถึงเครื่อง เกิดจากการปิดเซสชันสุดท้ายเพื่อ "ทดสอบจากสภาพแวดล้อมใหม่"
ขั้นตอนที่ 1: ติดตั้ง Ansible บนเครื่องควบคุมด้วย pipx แทนการใช้ pip
สัญชาตญาณทั่วไปคือการใช้ pip3 install ansible แต่บนอิมเมจ 24.04 ที่เพิ่งติดตั้งใหม่ การทำเช่นนั้นจะล้มเหลวตั้งแต่ขั้นตอนแรกด้วย Command 'pip3' not found, but can be installed with: sudo apt install python3-pip และการติดตั้ง pip เพียงอย่างเดียวจะนำคุณไปสู่ปัญหาที่แท้จริง:
pip3 install ansibleerror: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.Ubuntu 24.04 กำหนดให้ Python ของระบบเป็นแบบจัดการจากภายนอก (PEP 668) ดังนั้น pip จึงไม่สามารถติดตั้งทับไฟล์ที่จัดการโดย apt ได้ อย่าพยายามใช้ --break-system-packages เพราะชื่อ flag นั้นสื่อความหมายตรงตัวอยู่แล้ว วิธีที่สะอาดที่สุดคือการใช้ pipx ซึ่งจะสร้าง virtualenv แยกต่างหากสำหรับ Ansible และเพิ่ม binary เข้าไปใน PATH ของคุณ:
sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansibleให้เปิด shell ใหม่หลังจากรัน pipx ensurepath เพื่อให้การเปลี่ยนแปลง PATH มีผล --include-deps ไม่ใช่แค่ตัวเลือกเสริม เนื่องจากแพ็กเกจ ansible ไม่มี console script ของตัวเอง ส่วน ansible, ansible-playbook และตัวอื่นๆ เป็น entry point ของ dependency ที่ชื่อ ansible-core ดังนั้นหากไม่มี flag นี้ pipx จะปฏิเสธการติดตั้งด้วยข้อความ No apps associated with package ansible. และแจ้งให้คุณลองใหม่ด้วย --include-deps นอกจากนี้ให้ติดตั้งแพ็กเกจ ansible แทนการติดตั้ง ansible-core เปล่าๆ เพราะแพ็กเกจเต็มจะรวม community collections มาให้ด้วย และ playbook นี้จำเป็นต้องใช้โมดูลจากสองคอลเลกชันดังกล่าว (ansible.posix และ community.general)
ansible --versionณ วันที่ 2026-10-11 การติดตั้งใหม่จะดึงแพ็กเกจ ansible เวอร์ชัน 14.5.0 ซึ่งกำหนดเวอร์ชัน ansible-core ไว้ที่ 2.21.5 ดังนั้นผลลัพธ์ที่ถูกต้องเมื่อตรวจสอบจะขึ้นต้นด้วย ansible [core 2.21.5] และระบุเวอร์ชัน Python ที่ใช้งานอยู่ ซึ่ง core release ปัจจุบันสามารถใช้งานกับทุกอย่างในที่นี้ได้ Core 2.21 ต้องการ Python 3.12 หรือใหม่กว่าบนเครื่องควบคุม ซึ่งเป็นเวอร์ชันที่มาพร้อมกับ Ubuntu 24.04 หากการติดตั้งเวอร์ชันเก่ารายงานว่าเป็น core 2.19 ให้ทราบว่าซีรีส์ 2.19 จะสิ้นสุดการสนับสนุนในเดือนพฤศจิกายน 2026 หากขึ้น ansible: command not found หมายความว่า ~/.local/bin ยังไม่อยู่ใน PATH ของคุณ, คุณยังไม่ได้เปิด shell ใหม่ หรือเกิดจาก source ~/.bashrc
นั่นคือขั้นตอนการติดตั้งทั้งหมด โดยที่เครื่องเป้าหมายไม่จำเป็นต้องติดตั้งอะไรเพิ่มเติม
ขั้นตอนที่ 2: การเข้าถึงเป้าหมายทุกเครื่องด้วย SSH key
ssh-keygen -t ed25519 -C "ansible control"
ssh-copy-id root@10.0.0.10
ssh-copy-id root@10.0.0.20จากนั้นตรวจสอบให้ชัดเจน โดยทำครั้งละ 1 host:
ssh root@10.0.0.10 true && echo okบรรทัดเดียวนี้ทำหน้าที่ 2 อย่าง ได้แก่ ยืนยันว่าการยืนยันตัวตนด้วย key ทำงานได้โดยไม่ต้องใช้รหัสผ่าน และบันทึก host key ลงใน known_hosts ดำเนินการตอนนี้ เพราะ Ansible จะแสดง host key ที่ยังไม่ได้บันทึกเป็นพรอมต์แบบโต้ตอบซึ่งอยู่กลางการทำงาน และอาจดูเหมือนว่าการทำงานหยุดค้าง
ขั้นตอนที่ 3: inventory โดยเริ่มจาก INI และใช้ YAML เมื่อมีข้อมูลเพิ่มขึ้น
inventory คือไฟล์ข้อความที่แสดงรายการเครื่องที่ Ansible อาจเข้าถึงได้ สร้าง inventory.ini ในไดเรกทอรีโครงการใหม่:
[vps]
web1 ansible_host=10.0.0.10
web2 ansible_host=10.0.0.20
[vps:vars]
ansible_user=rootweb1 คือชื่อแทนที่คุณเลือก ชื่อนี้จะแสดงในเอาต์พุต และใช้ระบุเป้าหมายด้วย --limit web1 ส่วน ansible_host คือที่อยู่จริง [vps] คือกลุ่ม และ [vps:vars] ใช้กำหนดตัวแปรให้กับโฮสต์ทุกเครื่องในกลุ่มนั้น ส่วน ansible_user คือบัญชีที่ Ansible ใช้เข้าสู่ระบบ ถัดจากนั้น ให้เพิ่ม ansible.cfg เพื่อไม่ต้องพิมพ์ -i ซ้ำอีก:
[defaults]
inventory = inventory.iniAnsible จะอ่าน ansible.cfg จากไดเรกทอรีปัจจุบัน หากใช้ inventory เดิมในรูปแบบ YAML ให้บันทึกเป็น inventory.yml และกำหนดให้ ansible.cfg ชี้ไปยังชื่อนั้นแทน วิธีนี้เหมาะกว่าเมื่อโฮสต์แต่ละเครื่องมีตัวแปรหลายรายการ:
vps:
hosts:
web1:
ansible_host: 10.0.0.10
web2:
ansible_host: 10.0.0.20
vars:
ansible_user: rootทั้งสองรูปแบบให้ผลเทียบเท่ากัน INI ตรวจสอบด้วยตาได้ง่ายกว่าเมื่อมีเซิร์ฟเวอร์ 2 เครื่อง ส่วน YAML รองรับการขยายได้ดีกว่าเมื่อมี 20 เครื่อง เลือกรูปแบบใดรูปแบบหนึ่งแล้วใช้งานต่อไป
ขั้นตอนที่ 4: คำสั่ง ad-hoc และ pong สีเขียวที่ยืนยันว่าทุกอย่างทำงาน
ansible all -m pingนี่ไม่ใช่ ICMP โมดูล ping เป็นการทดสอบการทำงานครบทุกขั้นตอน ได้แก่ การเข้าสู่ระบบ SSH การคัดลอกโมดูล การเรียกใช้ Python บนเป้าหมาย และการล้างข้อมูล ผลลัพธ์ที่ถูกต้องเป็นสีเขียว โดยมีหนึ่งบล็อกต่อโฮสต์:
web1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}สีเขียว SUCCESS หมายความว่าการตรวจสอบสิทธิ์ ตัวแปลภาษา Python และการส่งข้อมูลทำงานทั้งหมด ดังนั้น playbook จะทำงานได้เช่นกัน สีแดง UNREACHABLE! หมายความว่าการส่งข้อมูลล้มเหลวก่อนที่จะเรียกใช้โมดูลใดๆ สตริงที่แสดงและวิธีแก้ไขอยู่ในส่วนโหมดความล้มเหลวด้านล่าง คำสั่ง ad-hoc ที่ควรรู้อีก 2 คำสั่งมีดังนี้:
ansible all -a "uptime"
ansible all -m apt -a "update_cache=true upgrade=dist" --becomeใช้ ad-hoc สำหรับงานที่ทำครั้งเดียวและการตรวจสอบ งานใดก็ตามที่คุณจะเรียกใช้ซ้ำ 2 ครั้งควรอยู่ใน playbook
ขั้นตอนที่ 5: playbook แรก รายการตรวจสอบ new-VPS ในรูปแบบโค้ด
นี่คือสิ่งที่คุณจะดำเนินการด้วยตนเองภายใน 10 นาทีแรกบนเซิร์ฟเวอร์ใหม่ บันทึกเป็น site.yml:
---
- name: Baseline a fresh Ubuntu VPS
hosts: vps
become: true
vars:
deploy_user: deploy
deploy_pubkey: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
baseline_packages:
- fail2ban
- unattended-upgrades
- ufw
baseline_services:
- fail2ban
- unattended-upgrades
tasks:
- name: Create the deploy user
ansible.builtin.user:
name: "{{ deploy_user }}"
groups: sudo
append: true
shell: /bin/bash
- name: Install the deploy user's SSH key
ansible.posix.authorized_key:
user: "{{ deploy_user }}"
key: "{{ deploy_pubkey }}"
- name: Passwordless sudo for the deploy user
ansible.builtin.copy:
dest: /etc/sudoers.d/deploy
content: "{{ deploy_user }} ALL=(ALL) NOPASSWD:ALL\n"
mode: "0440"
validate: /usr/sbin/visudo -cf %s
- name: Install baseline packages
ansible.builtin.apt:
name: "{{ baseline_packages }}"
state: present
update_cache: true
- name: Enable and start baseline services
ansible.builtin.service:
name: "{{ item }}"
state: started
enabled: true
loop: "{{ baseline_services }}"
- name: Harden sshd with a drop-in
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/00-hardening.conf
content: |
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
X11Forwarding no
mode: "0644"
validate: /usr/sbin/sshd -t -f %s
notify: Restart ssh
- name: Allow OpenSSH through ufw
community.general.ufw:
rule: allow
name: OpenSSH
- name: Enable ufw with default deny
community.general.ufw:
state: enabled
policy: deny
handlers:
- name: Restart ssh
ansible.builtin.service:
name: ssh
state: restartedบรรทัดที่ควรทำความเข้าใจแทนการคัดลอกมีดังนี้:
ตัวแปร อยู่ภายใต้ vars: และอ้างอิงด้วย "{{ deploy_user }}" ให้ใส่เครื่องหมายคำพูดครอบนิพจน์ทั้งหมดเมื่อค่าเริ่มต้นด้วยวงเล็บปีกกา มิฉะนั้นตัวแยกวิเคราะห์ YAML อาจตีความผิด lookup('file', ...) อ่าน public key ของคุณจากเครื่อง control ขณะรันไทม์ ดังนั้น playbook จึงไม่มีข้อมูลคีย์รวมอยู่
ลูป loop: "{{ baseline_services }}" เรียกใช้งาน service task หนึ่งครั้งต่อแต่ละรายการ และผลลัพธ์จะแสดงแต่ละรายการแยกคนละบรรทัด โปรดสังเกตว่า apt task รับรายการแพ็กเกจทั้งหมดในครั้งเดียว การทำธุรกรรม apt เพียงครั้งเดียวเร็วกว่า และเป็นรูปแบบที่แนะนำสำหรับแพ็กเกจ ส่วนลูปเหมาะกับโมดูลที่ทำงานกับทีละรายการอย่างแท้จริง
handler คือแนวคิดที่ควรทำความเข้าใจ notify: Restart ssh ไม่ได้หมายความว่า “รีสตาร์ต ssh ทันที” แต่จะจัดคิว handler ซึ่งทำงานครั้งเดียวเมื่อ play จบ และทำงาน เฉพาะ เมื่อ task ที่เรียกใช้งานรายงาน changed จริงเท่านั้น หากรัน playbook อีกครั้งในวันถัดไป ไฟล์ drop-in จะถูกต้องอยู่แล้ว copy task จะรายงาน ok และจะไม่มีการรีสตาร์ต sshd บรรทัด validate: เป็นกลไกความปลอดภัยของ trigger โดย sshd จะตรวจสอบไฟล์ก่อนแทนที่ไฟล์เดิม ดังนั้นหากมีการพิมพ์ผิด task จะล้มเหลวแทนที่จะทำให้ daemon หยุดทำงาน
PermitRootLogin prohibit-password ไม่ใช่ no โดยตั้งใจ playbook นี้เข้าสู่ระบบในฐานะ root ด้วยคีย์ prohibit-password ปิดการเข้าสู่ระบบ root ด้วยรหัสผ่าน แต่ยังคงการเชื่อมต่อของคุณไว้ เมื่อยืนยันแล้วว่า deploy user ใช้งานได้ (ssh deploy@10.0.0.10 sudo true คือ address โดยตรง เนื่องจาก web1 เป็นเพียง alias ที่ Ansible รู้จัก) ให้เปลี่ยนเป็น ansible_user=deploy ใน inventory และปรับให้เข้มงวดเป็น no ในการรันครั้งถัดไป เสริมความปลอดภัยตามลำดับที่ไม่ทำให้คุณสูญเสียการเข้าถึงเซิร์ฟเวอร์
คำนำหน้า 00- มีความสำคัญ สำหรับคีย์เวิร์ดส่วนใหญ่ sshd จะใช้รายการแรกที่พบขณะวิเคราะห์ไฟล์ และ sshd_config ของ Ubuntu จะ include sshd_config.d/*.conf ตามลำดับเชิงพจนานุกรมก่อนส่วนเนื้อหาของไฟล์เอง อิมเมจ cloud ของ Ubuntu 24.04 มี 60-cloudimg-settings.conf อยู่ในไดเรกทอรีดังกล่าวแล้ว และผู้ให้บริการที่เปิดใช้การเข้าสู่ระบบด้วยรหัสผ่านผ่าน cloud-init จะเพิ่ม 50-cloud-init.conf ที่มี PasswordAuthentication yes การตั้งชื่อของเราเป็น 00-hardening.conf ทำให้ไฟล์เรียงอยู่ก่อนและมีผลเหนือทั้งสองไฟล์
ลำดับของ task เป็นกลไกความปลอดภัยของไฟร์วอลล์ Allow OpenSSH ทำงานก่อน Enable ufw ซึ่งใช้นโยบาย deny โดย Ansible จะทำงานตามลำดับที่ระบุไว้อย่างเคร่งครัด ดังนั้นช่องทางการเข้าถึงจะมีอยู่ก่อนที่กำแพงจะถูกเปิดใช้ fail2ban ไม่ต้องมีการกำหนดค่าเพิ่มเติมก็มีประโยชน์ในกรณีนี้ ค่าเริ่มต้นของ Ubuntu จะ monitor sshd ให้โดยอัตโนมัติ และรายละเอียดเกี่ยวกับสิ่งที่ jail ทำ รวมถึงค่าที่ควรปรับ มีอยู่ใน คู่มือ fail2ban บน Ubuntu 24.04
ขั้นตอนที่ 6: ทดลองเรียกใช้ด้วย --check แล้วจึงเรียกใช้จริง
ansible-playbook site.yml --checkโหมดตรวจสอบจะเชื่อมต่อและคำนวณว่าระบบ จะ ทำอะไร แต่จะไม่เปลี่ยนแปลงใดๆ ให้อ่านจำนวน changed= ใน PLAY RECAP ที่ด้านล่าง ซึ่งเป็นจำนวนงานที่จะเปลี่ยนแปลงแต่ละโฮสต์ ข้อควรทราบที่สำคัญคือ โหมดตรวจสอบมีข้อจำกัดเชิงโครงสร้างเมื่อมีงานภายหลังที่ต้องอาศัยการเปลี่ยนแปลงจากงานก่อนหน้า อิมเมจเซิร์ฟเวอร์มาตรฐานของ Ubuntu มี ufw ติดตั้งมาแล้ว ดังนั้น playbook นี้จึงทำงานแบบ dry run ได้โดยไม่มีข้อผิดพลาด แต่หากใช้อิมเมจขั้นต่ำที่ไม่มี ufw งานของ ufw จะล้มเหลวในโหมดตรวจสอบ เพราะโหมดตรวจสอบไม่ได้ติดตั้งแพ็กเกจจริง และ module จึงไม่มีสิ่งที่จะเรียกใช้ นี่เป็นข้อจำกัดของการทำ dry run ไม่ใช่ข้อผิดพลาดใน playbook ของคุณ เมื่อแผนถูกต้องแล้ว:
ansible-playbook site.ymlแต่ละงานจะแสดงหนึ่งบรรทัดต่อโฮสต์ โดย changed เป็นสีเหลือง และ ok เป็นสีเขียว ส่วนสรุปควรแสดงดังนี้:
PLAY RECAP *********************************************************************
web1 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0จำนวน ok สิบรายการประกอบด้วยการรวบรวมข้อเท็จจริง งานแปดรายการ และ handler ค่า changed ของคุณอาจแตกต่างจากของฉันหนึ่งหรือสองรายการได้ เนื่องจากอิมเมจมาตรฐานของ Ubuntu มี ufw และ unattended-upgrades ติดตั้งมาแล้ว และ fail2ban จะเริ่มทำงานทันทีที่ apt ติดตั้งแพ็กเกจ ดังนั้นงานหนึ่งรายการอาจรายงาน ok ได้อย่างถูกต้องในการเรียกใช้ครั้งแรก เพราะสถานะที่งานประกาศนั้นมีอยู่แล้ว ตัวเลขที่ต้องเป็นศูนย์คือ unreachable และ failed ข้อควรทราบเกี่ยวกับ become: true คือ ขณะเชื่อมต่อในฐานะ root ค่านี้เป็นเพียงขั้นตอนตามรูปแบบ แต่ทันทีที่คุณเปลี่ยน ansible_user เป็น deploy จะมีการใช้ sudo จริง และไฟล์ sudoers แบบ NOPASSWD ที่ playbook นี้ติดตั้งจะช่วยไม่ให้ต้องป้อน -K ในบรรทัดคำสั่ง หากไม่มีไฟล์นี้ คุณจะได้รับ Missing sudo password ซึ่งจะกล่าวถึงด้านล่าง
ขั้นตอนที่ 7: เรียกใช้สองครั้ง ลักษณะของ idempotence
เรียกใช้คำสั่งเดิมอีกครั้งทันที:
web1 : ok=9 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0 และ ok ลดลงหนึ่ง เนื่องจาก handler ที่ไม่ได้รับการแจ้งเตือนไม่ได้ทำงาน ไม่มีการติดตั้งซ้ำ ไม่มีการ restart sshd และไม่มีการแก้ไข ufw นี่คือเหตุผลที่ playbook ทำหน้าที่เป็น audit ควบคู่กับการจัดเตรียมระบบ: เพิ่ม web3 ลงใน inventory ในเดือนหน้าแล้วเรียกใช้อีกครั้ง ระบบใหม่จะถูกสร้าง ส่วนระบบเดิมจะถูกตรวจสอบ ค่า changed ที่ไม่ใช่ศูนย์บนระบบที่คุณไม่ได้แก้ไขคือ drift และแสดงว่ามีผู้แก้ไขด้วยตนเองในส่วนที่ควรแก้ไขผ่าน playbook
จากจุดนี้ รูปแบบดังกล่าวจะขยายต่อไป playbook ถัดไปที่ควรเขียนคือ playbook ที่ติดตั้ง WireGuard VPN บน VPS เดียวกัน และปรับกฎ ufw ให้เข้มงวดยิ่งขึ้น เพื่อให้ SSH ตอบสนองเฉพาะบน tunnel เท่านั้น หลังจากนั้นจึงเขียน playbook ที่ติดตั้ง Docker และ Compose บน app server ทุกเครื่อง เมื่อ site.yml ยาวเกินสามหน้าจอ ให้แยกเป็น roles แต่ไม่ควรแยกก่อนหน้านั้น
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
UNREACHABLE พร้อมข้อความ Permission denied
web1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: root@10.0.0.10: Permission denied (publickey).",
"unreachable": true
}การเชื่อมต่อ SSH ล้มเหลวก่อนที่โมดูลใดๆ จะทำงาน: ansible_user ไม่ถูกต้อง, กุญแจ (key) ยังไม่ได้ถูกคัดลอกไปยังโฮสต์นั้น หรือมีการใช้กุญแจที่ไม่ถูกต้อง ให้ทดสอบซ้ำด้วย ssh root@10.0.0.10 ตามด้วย ssh -v เพื่อดูว่ามีการเสนอกุญแจใดบ้าง หาก SSH ด้วยรหัสผ่านใช้งานได้แต่ Ansible ใช้งานไม่ได้ แสดงว่าคุณข้ามขั้นตอน ssh-copy-id ไป
ไม่มีรหัสผ่าน sudo
web1 | FAILED! => {
"msg": "Missing sudo password"
}คุณตั้งค่า become: true ไว้และเชื่อมต่อด้วยผู้ใช้ที่ไม่ใช่ root ซึ่งผู้ใช้นั้นจำเป็นต้องใช้รหัสผ่านสำหรับ sudo ให้เพิ่ม -K (--ask-become-pass) เข้าไปในบรรทัดคำสั่ง หรือกำหนดค่า NOPASSWD ใน sudoers ให้กับผู้ใช้นั้น ซึ่งนี่คือเหตุผลที่ playbook ต้องติดตั้งค่านี้สำหรับ deploy ก่อนที่คุณจะสลับไปใช้ผู้ใช้นั้น
error: externally-managed-environment คุณรัน pip กับ Python ของระบบบน Ubuntu 24.04 ซึ่งครอบคลุมอยู่ในขั้นตอนที่ 1 แล้ว: ให้ใช้ pipx ไม่ใช่ pip และไม่ใช่ --break-system-packages
mapping values are not allowed in this context
ERROR! Syntax Error while loading YAML.
mapping values are not allowed in this contextเกือบทั้งหมดเกิดจากการเยื้องบรรทัด (indentation): คีย์อยู่ในระดับความลึกที่ไม่ถูกต้อง หรือลืมเว้นวรรคหลังเครื่องหมายทวิภาค (colon) หมายเลขบรรทัดที่แจ้งเตือนจะชี้ไป ใกล้กับ จุดที่ผิด ไม่ใช่จุดที่ผิดโดยตรง ให้ตรวจสอบบรรทัดก่อนหน้าด้วย ส่วนข้อผิดพลาดที่คล้ายกันคือ found character '\t' that cannot start any token ซึ่งหมายความว่ามีอักขระ tab หลุดเข้าไปในไฟล์ เนื่องจาก YAML ไม่อนุญาตให้ใช้ tab ให้ฝึกใช้ ansible-playbook site.yml --syntax-check จนเป็นนิสัยก่อนรันงานทุกครั้ง และตั้งค่าโปรแกรมแก้ไขข้อความของคุณให้ใช้การเยื้องสองช่องว่างสำหรับ YAML
/usr/bin/python3: not found พบได้น้อยในอิมเมจมาตรฐานของ Ubuntu 24.04 แต่พบได้บ่อยในอิมเมจแบบ minimal หรือ netboot: การทำงานของโมดูลล้มเหลวเพราะปลายทางไม่มี Python ให้ติดตั้ง Python เริ่มต้นด้วยโมดูล raw ซึ่งเป็นหนึ่งในไม่กี่โมดูลที่ไม่ต้องการ Python ที่ฝั่งปลายทาง (อีกโมดูลคือ script) และเป็นโมดูลที่สร้างมาเพื่อส่งคำสั่งผ่านไปโดยตรง: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become จากนั้นให้รัน playbook อีกครั้ง
FAQ
จำเป็นต้องติดตั้ง Ansible บนเซิร์ฟเวอร์ที่ Ansible จัดการหรือไม่
ไม่จำเป็น Ansible ทำงานแบบไม่มีเอเจนต์ โดยเครื่องควบคุมจะส่งโมดูล Python ขนาดเล็กผ่าน SSH เรียกใช้โมดูลเหล่านั้น แล้วลบออก เซิร์ฟเวอร์เป้าหมายต้องมีเพียง python3 และการเข้าถึงผ่าน SSH ซึ่งมีอยู่แล้วในอิมเมจ Ubuntu มาตรฐาน การติดตั้งเพียงรายการเดียวในคู่มือนี้เกิดขึ้นบนเครื่องควบคุมของคุณ
เหตุใด Ansible จึงแสดงข้อความ "Permission denied (publickey)"
บล็อก UNREACHABLE! ที่มี Permission denied (publickey) หมายความว่าการตรวจสอบสิทธิ์ของ SSH ล้มเหลวก่อนที่ Ansible จะเรียกใช้งานใดๆ ตรวจสอบว่า ansible_user ใน inventory ตรงกับบัญชีที่คุณตั้งค่าไว้จริง ตรวจสอบว่าคุณเรียกใช้ ssh-copy-id กับโฮสต์นั้น และตรวจสอบว่า ssh user@host แบบปกติสามารถเข้าสู่ระบบได้โดยไม่ต้องใช้รหัสผ่าน การแก้ไขคำสั่ง ssh แบบปกติจะช่วยแก้ปัญหา Ansible ด้วย เพราะทั้งสองใช้ช่องทางการเชื่อมต่อเดียวกัน
idempotent ใน Ansible หมายถึงอะไร
งานจะประกาศสถานะที่ต้องการ เช่น "แพ็กเกจนี้ติดตั้งอยู่" หรือ "บรรทัดนี้อยู่ในไฟล์นี้" แทนการประกาศการดำเนินการที่จะทำ หากสถานะดังกล่าวเป็นจริงอยู่แล้ว Ansible จะไม่ดำเนินการใดๆ และรายงาน ok แทน changed นี่คือเหตุผลที่การเรียกใช้ playbook ครั้งที่ 2 แสดง changed=0 และเหตุผลที่การเรียกใช้ซ้ำเป็นการตรวจสอบที่ปลอดภัย ไม่ใช่การติดตั้งใหม่ที่มีความเสี่ยง
ควรใช้ pip หรือ pipx เพื่อติดตั้ง Ansible บน Ubuntu 24.04
ใช้ pipx Ubuntu 24.04 กำหนดให้ Python ของระบบอยู่ภายใต้การจัดการจากภายนอก ดังนั้น pip install ansible จึงล้มเหลวด้วย error: externally-managed-environment ตามที่ออกแบบไว้ pipx install --include-deps ansible จะติดตั้ง Ansible ใน virtualenv ที่แยกออกจากกัน และทำให้ ansible, ansible-playbook และเครื่องมืออื่นๆ พร้อมใช้งานใน PATH อย่างเป็นระเบียบ
แพ็กเกจ ansible และ ansible-core แตกต่างกันอย่างไร
ansible-core คือกลไกหลักพร้อมโมดูลเพียง ansible.builtin เท่านั้น แพ็กเกจ ansible รวม core เข้ากับ collections จากชุมชนที่ผ่านการคัดสรรแล้ว ซึ่งรวมถึง ansible.posix (โมดูล authorized_key) และ community.general (โมดูล ufw) โดยทั้งสองรายการใช้ในคู่มือนี้ เริ่มต้นด้วยแพ็กเกจแบบเต็ม แล้วลดขนาดเป็น core พร้อม collections ที่เลือกเองเมื่อมีเหตุผลที่จำเป็น