SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-01

สอน 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 แต่บน image 24.04 ที่ยังไม่ได้ติดตั้งอะไรจริง ๆ จะล้มเหลวก่อนหน้านั้นหนึ่งขั้นที่ Command 'pip3' not found, but can be installed with: sudo apt install python3-pip และการติดตั้ง pip จะนำไปสู่ข้อจำกัดที่แท้จริง:

pip3 install ansible
error: 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 scripts ของตัวเอง ansible, ansible-playbook และรายการอื่น ๆ เป็น entry points ของ dependency ansible-core ดังนั้นหากไม่มี flag นี้ pipx จะปฏิเสธการติดตั้งพร้อมข้อความ No apps associated with package ansible or its dependencies และให้ติดตั้งแพ็กเกจ ansible ไม่ใช่ ansible-core แบบเปล่า เพราะแพ็กเกจเต็มจะรวม community collections และ playbook นี้ใช้โมดูลจาก 2 ชุด ได้แก่ ansible.posix และ community.general

ansible --version

ผลลัพธ์ที่ถูกต้องจะเริ่มด้วยบรรทัดลักษณะ ansible [core 2.19.x] และระบุ Python ที่ใช้ทำงาน รุ่น core ปัจจุบันใด ๆ ก็เพียงพอสำหรับเนื้อหาทั้งหมดนี้ หากเห็น 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=root

web1 คือชื่อแทนที่คุณเลือก ชื่อนี้จะแสดงในเอาต์พุต และใช้ระบุเป้าหมายด้วย --limit web1 ส่วน ansible_host คือที่อยู่จริง [vps] คือกลุ่ม และ [vps:vars] ใช้กำหนดตัวแปรให้กับโฮสต์ทุกเครื่องในกลุ่มนั้น ส่วน ansible_user คือบัญชีที่ Ansible ใช้เข้าสู่ระบบ ถัดจากนั้น ให้เพิ่ม ansible.cfg เพื่อไม่ต้องพิมพ์ -i ซ้ำอีก:

[defaults]
inventory = inventory.ini

Ansible จะอ่าน 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=0

changed=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 with 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 ไม่ถูกต้อง ไม่มีการคัดลอกคีย์ไปยังโฮสต์ดังกล่าว หรือกำลังใช้คีย์ที่ไม่ถูกต้อง ทดลองด้วย ssh root@10.0.0.10 โดยตรง จากนั้นใช้ ssh -v เพื่อดูว่ามีการเสนอคีย์ใดบ้าง หาก SSH ด้วยรหัสผ่านใช้งานได้ แต่ Ansible ใช้งานไม่ได้ แสดงว่ายังไม่ได้ระบุ ssh-copy-id

Missing sudo password.

web1 | FAILED! => {
    "msg": "Missing sudo password"
}

คุณตั้งค่า become: true และเชื่อมต่อในฐานะผู้ใช้ที่ไม่ใช่ root โดยผู้ใช้นั้นต้องใช้รหัสผ่านสำหรับ sudo ให้เพิ่ม -K (--ask-become-pass) ในบรรทัดคำสั่ง หรือเพิ่มรายการ sudoers แบบ NOPASSWD ให้ผู้ใช้ ซึ่งเป็นเหตุผลโดยตรงที่ 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

เกือบทุกครั้งเกิดจากการเยื้องบรรทัดไม่ถูกต้อง เช่น คีย์อยู่ในระดับที่ผิด หรือไม่มีช่องว่างหลังเครื่องหมายโคลอน หมายเลขบรรทัดที่รายงานจะชี้ไปยังบริเวณใกล้เคียงข้อผิดพลาด ไม่จำเป็นต้องเป็นบรรทัดที่ผิด ให้ตรวจสอบบรรทัดก่อนหน้าด้วย ข้อผิดพลาดที่เกี่ยวข้องคือ found character '\t' that cannot start any token ซึ่งหมายความว่ามีแท็บปะปนเข้ามา YAML ไม่อนุญาตให้ใช้แท็บ ให้ตรวจสอบด้วย ansible-playbook site.yml --syntax-check เป็นขั้นตอนอัตโนมัติก่อนการเรียกใช้ทุกครั้ง และตั้งค่าเอดิเตอร์ให้ใช้การเยื้อง 2 ช่องว่างสำหรับ YAML

/usr/bin/python3: not found. พบไม่บ่อยในอิมเมจ Ubuntu 24.04 มาตรฐาน แต่พบได้บ่อยในอิมเมจแบบ minimal หรือ netboot การเรียกใช้โมดูลล้มเหลวเนื่องจากเป้าหมายไม่มี Python ให้ติดตั้งเบื้องต้นด้วยโมดูล raw ซึ่งเป็นโมดูลเดียวที่ไม่ต้องมีสิ่งใดอยู่ในเครื่องปลายทาง โดยใช้ 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 ที่เลือกเองเมื่อมีเหตุผลที่จำเป็น