Ansible copy Module: Copy Files ไปยัง Remote Servers

Ansible copy module ใช้คัดลอกไฟล์จาก control node (เครื่องที่รัน Ansible) ไปยัง remote server หรือสร้างไฟล์จาก content ที่กำหนดโดยตรงใน Playbook เป็น module พื้นฐานที่ใช้บ่อยมากในงาน configuration management ตั้งแต่ deploy config files, certificates, ไปจนถึง scripts

บทความนี้อธิบายการใช้ copy module ครอบคลุมตั้งแต่การ copy ไฟล์พื้นฐาน, กำหนด permission, สร้างไฟล์จาก content, backup ไฟล์เดิม, ไปจนถึงความแตกต่างระหว่าง copy, template, และ synchronize module

Copy ไฟล์พื้นฐาน

Parameter หลักของ copy module คือ src (ไฟล์ต้นทางบน control node) และ dest (path ปลายทางบน remote server)

---
- name: Copy files to remote servers
  hosts: webservers
  become: true
  tasks:
    - name: Copy nginx config
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf

    - name: Copy SSL certificate
      copy:
        src: files/server.crt
        dest: /etc/ssl/certs/server.crt

    - name: Copy script
      copy:
        src: scripts/deploy.sh
        dest: /opt/scripts/deploy.sh

ค่า src เป็น relative path จาก Playbook directory หรือจาก files/ subdirectory ของ role ถ้า src เป็น directory (ลงท้ายด้วย /) จะ copy เฉพาะ contents ข้างใน ถ้าไม่มี / จะ copy ทั้ง directory

กำหนด Owner, Group และ Permission

ใช้ owner, group, และ mode กำหนด file permission หลัง copy — ทำงานเหมือน chown และ chmod

---
- name: Copy with permissions
  hosts: all
  become: true
  tasks:
    - name: Copy config with correct owner
      copy:
        src: files/app.conf
        dest: /etc/app/app.conf
        owner: www-data
        group: www-data
        mode: '0644'

    - name: Copy executable script
      copy:
        src: scripts/backup.sh
        dest: /usr/local/bin/backup.sh
        owner: root
        group: root
        mode: '0755'    # executable

    - name: Copy private key
      copy:
        src: files/private.key
        dest: /etc/ssl/private/server.key
        owner: root
        group: ssl-cert
        mode: '0640'    # owner read/write, group read only

mode รองรับทั้ง octal format ('0644') และ symbolic format ('u=rw,g=r,o=r') ควรใส่ quotes รอบ octal เสมอเพื่อป้องกัน YAML ตีความเป็น integer

สร้างไฟล์จาก Content

แทนที่จะ copy จากไฟล์ สามารถใช้ content parameter เขียน text โดยตรงลงใน remote file เหมาะสำหรับไฟล์ขนาดเล็กหรือ config ที่ต้องการ inline

---
- name: Create files from content
  hosts: all
  become: true
  tasks:
    - name: Create motd file
      copy:
        content: |
          Welcome to Production Server
          Unauthorized access is prohibited.
          All activities are monitored and logged.
        dest: /etc/motd
        owner: root
        mode: '0644'

    - name: Create simple config
      copy:
        content: "SERVER_ENV=production\nDEBUG=false\nLOG_LEVEL=info\n"
        dest: /etc/app/env
        mode: '0644'

    - name: Create hosts entry
      copy:
        content: "192.168.1.10 app-server\n192.168.1.11 db-server\n"
        dest: /etc/hosts.custom
        mode: '0644'

Backup ไฟล์เดิมก่อน Overwrite

backup: yes สร้างไฟล์ backup ของเดิมพร้อม timestamp ก่อนที่จะ overwrite ป้องกันการสูญหายของ config เดิมเมื่อ deploy ใหม่

---
- name: Deploy with backup
  hosts: webservers
  become: true
  tasks:
    - name: Update nginx config with backup
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        backup: yes    # สร้าง backup เช่น nginx.conf.2026-04-16@10:30~
        owner: root
        mode: '0644'
      notify: reload nginx

    - name: Update app config with backup
      copy:
        src: files/app.conf
        dest: /etc/app/app.conf
        backup: yes

  handlers:
    - name: reload nginx
      service:
        name: nginx
        state: reloaded

ไฟล์ backup จะมีชื่อในรูปแบบ filename.YYYY-MM-DD@HH:MM~ เก็บไว้ใน directory เดิม เหมาะสำหรับ rollback manual ถ้า config ใหม่มีปัญหา

ตรวจสอบ Checksum และ Force Copy

โดย default copy module ตรวจ checksum ของไฟล์ปลายทางก่อน ถ้าเหมือนกับต้นทางจะ skip (idempotent) ใช้ force: no เพื่อ copy เฉพาะเมื่อไฟล์ยังไม่มีอยู่

---
- name: Checksum and force examples
  hosts: all
  become: true
  tasks:
    - name: Copy only if destination does not exist
      copy:
        src: files/default.conf
        dest: /etc/app/app.conf
        force: no    # ไม่ overwrite ถ้าไฟล์มีอยู่แล้ว

    - name: Always force overwrite
      copy:
        src: files/app.conf
        dest: /etc/app/app.conf
        force: yes    # default behavior

    - name: Validate file after copy
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        validate: nginx -t -c %s    # ทดสอบ config ก่อน commit

validate parameter รัน command เพื่อตรวจสอบไฟล์ก่อนที่จะ copy ไปที่ปลายทางจริง %s จะถูกแทนด้วย path ของไฟล์ชั่วคราว ถ้า command return non-zero จะไม่ copy และ task จะ fail

Copy ทั้ง Directory

การ copy directory ต้องระวัง trailing slash ของ src เพราะมีผลต่อ structure ของ destination

---
- name: Copy directories
  hosts: all
  become: true
  tasks:
    # src มี trailing slash = copy เฉพาะ contents ข้างใน
    - name: Copy config files into /etc/app/
      copy:
        src: files/app_config/    # trailing slash
        dest: /etc/app/
        owner: www-data
        mode: '0644'

    # src ไม่มี trailing slash = copy ทั้ง directory รวม folder name
    - name: Copy entire app_config directory
      copy:
        src: files/app_config     # ไม่มี trailing slash
        dest: /etc/
        # ผลลัพธ์: /etc/app_config/ พร้อม contents ข้างใน

copy vs template vs synchronize

มี 3 module สำหรับส่งไฟล์ไปยัง remote server แต่ละตัวมีจุดประสงค์ต่างกัน การเลือกใช้ให้ถูกต้องทำให้ Playbook มีประสิทธิภาพมากขึ้น

---
- name: Module comparison examples
  hosts: webservers
  become: true
  vars:
    server_name: "myapp.example.com"
    app_port: 8080

  tasks:
    # copy: ไฟล์ static ไม่มี variable
    - name: Copy static file (use copy)
      copy:
        src: files/robots.txt
        dest: /var/www/html/robots.txt

    # template: ไฟล์ที่มี Jinja2 variables
    - name: Deploy nginx config with variables (use template)
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/myapp
      # nginx.conf.j2 มี {{ server_name }} และ {{ app_port }}

    # synchronize: sync directory ขนาดใหญ่ ใช้ rsync
    - name: Sync web files (use synchronize)
      synchronize:
        src: /local/webapp/
        dest: /var/www/html/
        delete: yes    # ลบไฟล์ปลายทางที่ไม่มีในต้นทาง

สรุปการเลือก module: ใช้ copy สำหรับไฟล์ static ขนาดเล็ก, ใช้ template เมื่อไฟล์มี variables ที่ต้อง render, และใช้ synchronize เมื่อต้อง sync directory ขนาดใหญ่เพราะใช้ rsync ที่เร็วกว่า

Pattern: Deploy Application Config

ตัวอย่าง Playbook สำหรับ deploy config files ไปยัง web server พร้อม permission ที่ถูกต้อง, backup ไฟล์เดิม, และ reload service เมื่อมีการเปลี่ยนแปลง

---
- name: Deploy Web Application Config
  hosts: webservers
  become: true
  vars:
    app_user: www-data
    config_dir: /etc/myapp

  tasks:
    - name: Ensure config directory exists
      file:
        path: "{{ config_dir }}"
        state: directory
        owner: "{{ app_user }}"
        mode: '0755'

    - name: Copy main config
      copy:
        src: files/myapp.conf
        dest: "{{ config_dir }}/myapp.conf"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0640'
        backup: yes
      notify: restart myapp

    - name: Copy SSL certificate
      copy:
        src: files/server.crt
        dest: /etc/ssl/certs/myapp.crt
        owner: root
        group: ssl-cert
        mode: '0644'
      notify: reload nginx

    - name: Copy SSL private key
      copy:
        src: files/server.key
        dest: /etc/ssl/private/myapp.key
        owner: root
        group: ssl-cert
        mode: '0640'
      notify: reload nginx

    - name: Copy maintenance page
      copy:
        content: "Maintenance in progress. Please try again later.\n"
        dest: /var/www/html/maintenance.html
        owner: "{{ app_user }}"
        mode: '0644'

  handlers:
    - name: restart myapp
      service:
        name: myapp
        state: restarted

    - name: reload nginx
      service:
        name: nginx
        state: reloaded

สรุป

copy module เป็นวิธีที่ถูกต้องสำหรับ deploy ไฟล์ static ไปยัง remote server ใน Ansible Pattern ที่ควรจำ: ใช้ owner, group, mode ทุกครั้งที่ deploy config ที่ sensitive, ใส่ backup: yes สำหรับไฟล์ที่อาจต้อง rollback, และใช้ validate parameter สำหรับ config ที่มี syntax check tool เช่น nginx หรือ apache

เลือก module ให้ถูกจุดประสงค์: copy สำหรับไฟล์ static, template เมื่อต้องการ Jinja2 variables, และ synchronize เมื่อต้อง sync directory ขนาดใหญ่ด้วย rsync