็†ฑ้–€ๅˆ†้กž
 ่ผ‰ๅ…ฅไธญ…
็›ฎ้Œ„

๐Ÿ›  Ansible ๅฏฆๆˆฐ:ๆ‰นๆฌก็ฎก็†ๅคšๅฐ Linux ็š„ Netplan ่ˆ‡้œๆ…‹่ทฏ็”ฑ่จญๅฎš

    ๅ‰่จ€

    ็•ถไฝ ้œ€่ฆๅŒๆ™‚็ถญ้‹ๅคšๅฐ Linux ไธปๆฉŸๆ™‚,็ถฒ่ทฏ่จญๅฎš(IP、DNS、Gateway、้œๆ…‹่ทฏ็”ฑ)่‹ฅไป้ ไบบๅทฅ้€ๅฐ่ชฟๆ•ด, ไธๅช่€—ๆ™‚,ไนŸๅฎนๆ˜“ๅœจ่ฎŠๆ›ดๆ™‚้€ ๆˆ้€ฃ็ทšไธญๆ–ท。ๆœฌๆ–‡ไปฅ Ansible ๅฏฆๆˆฐ่ง’ๅบฆ,็คบ็ฏ„ๅฆ‚ไฝ•็”จ「่ง’่‰ฒๅŒ– + ๆจกๆฟๅŒ– + ๅˆ†ๆ‰นๅฅ—็”จ」 ้›†ไธญ็ฎก็† Netplan ่ˆ‡้œๆ…‹่ทฏ็”ฑ,ไธฆๅŠ ๅ…ฅ้ฉ—่ญ‰่ˆ‡ๅ›žๆปพ็ญ–็•ฅ,้™ไฝŽๅคง่ฆๆจก็ถฒ่ทฏ่ฎŠๆ›ด้ขจ้šช。

    ้ฉ็”จ็ฏ„ๅœ่ˆ‡ๅ‰็ฝฎๆขไปถ

    • ้ฉ็”จ:ไฝฟ็”จ Netplan ็š„ Linux(ๅธธ่ฆ‹ๆ–ผ Ubuntu Server / Ubuntu Desktop)。
    • ๆŽงๅˆถ็ซฏ:ๅทฒๅฎ‰่ฃ ansible(ๅปบ่ญฐ 2.14+ ไปฅไธŠ)。
    • ๅ—ๆŽง็ซฏ:ๅฏ SSH ็™ปๅ…ฅ、ๅ…ท sudo ๆฌŠ้™,ไธ” /etc/netplan/ ๅญ˜ๅœจ。
    • ่ชžๅฝ™ๅๅฅฝ:ๆŠ€่ก“ๅ…งๅฎนไธญไฝฟ็”จ Gateway

    ๐Ÿงญ ่กŒๅ‹•ๆธ…ๅ–ฎ

    ✅ ๅปบ็ซ‹ Ansible ๅฐˆๆกˆ็ตๆง‹(roles + templates + group_vars/host_vars)
    ✅ ็”จ Jinja2 ๆจกๆฟ็ตฑไธ€่ผธๅ‡บ /etc/netplan/*.yaml(ๅซ IP / DNS / Gateway / ้œๆ…‹่ทฏ็”ฑ)
    ✅ ๅฅ—็”จๅ‰ๅ…ˆ netplan generate ้ฉ—่ญ‰่ชžๆณ•
    ✅ ไปฅ serial ๅˆ†ๆ‰นๅฅ—็”จ,ๅฅ—็”จๅพŒๅš่ทฏ็”ฑ่ˆ‡้€ฃ้€šๆ€ง้ฉ—่ญ‰
    ✅ ๅคฑๆ•—ๅฏๅ›žๆปพ่‡ณๅ‚™ไปฝ่จญๅฎš,้™ไฝŽๅคง่ฆๆจกๆ–ท็ทš้ขจ้šช

    ๅฐˆๆกˆ็ตๆง‹(ๅปบ่ญฐ)

    ansible-netplan/
    ├─ inventory/
    │  ├─ hosts.ini
    ├─ group_vars/
    │  ├─ netplan_hosts.yml
    ├─ host_vars/
    │  ├─ host01.yml
    │  ├─ host02.yml
    ├─ playbooks/
    │  ├─ netplan.yml
    ├─ roles/
    │  ├─ netplan/
    │     ├─ tasks/
    │     │  └─ main.yml
    │     ├─ handlers/
    │     │  └─ main.yml
    │     ├─ templates/
    │     │  └─ 01-netcfg.yaml.j2
    │     └─ defaults/
    │        └─ main.yml

    Inventory(็คบไพ‹)

    [netplan_hosts]
    host01 ansible_host=10.10.10.11
    host02 ansible_host=10.10.10.12
    
    [netplan_hosts:vars]
    ansible_user=ubuntu
    ansible_become=true
    ansible_become_method=sudo

    ่ฎŠๆ•ธ่จญ่จˆ:ๆŠŠ「่จญๅฎš」่ฎŠๆˆ่ณ‡ๆ–™

    ๆœ€ไฝณๅฏฆๅ‹™ๆ˜ฏๆŠŠ Netplan ๅ…งๅฎนๆ‹†ๆˆ「่ณ‡ๆ–™(vars)」่ˆ‡「่ผธๅ‡บ(template)」,่ฎ“ไฝ ๅช่ชฟๆ•ด่ฎŠๆ•ธๅฐฑ่ƒฝๅ…จ็ซ™ไธ€่‡ดไธ‹็™ผ。 ไปฅไธ‹็คบ็ฏ„ๅœจ group_vars/netplan_hosts.yml ๆ”พๅ…ฑ็”จ่จญๅฎš,ๅ†็”จ host_vars ่ฆ†ๅฏซๅทฎ็•ฐ。

    group_vars/netplan_hosts.yml(ๅ…ฑ็”จ)

    netplan_filename: "01-netcfg.yaml"
    netplan_renderer: "networkd"   # ๅฏ้ธ networkd / NetworkManager
    netplan_apply: true
    
    netplan_ethernets:
      - name: "ens160"
        dhcp4: false
        addresses: []              # ๆฏๅฐไธปๆฉŸ็”จ host_vars ่ฆ†ๅฏซ
        gateway4: ""               # ๆฏๅฐไธปๆฉŸ็”จ host_vars ่ฆ†ๅฏซ
        nameservers:
          addresses: ["1.1.1.1", "8.8.8.8"]
        routes:
          - to: "10.20.0.0/16"
            via: "10.10.10.1"
            metric: 100
          - to: "172.16.0.0/12"
            via: "10.10.10.254"
            metric: 110

    host_vars/host01.yml(ๅ€‹ๅˆฅ่ฆ†ๅฏซ)

    netplan_ethernets:
      - name: "ens160"
        dhcp4: false
        addresses: ["10.10.10.11/24"]
        gateway4: "10.10.10.1"
        nameservers:
          addresses: ["1.1.1.1", "8.8.8.8"]
        routes:
          - to: "10.20.0.0/16"
            via: "10.10.10.1"
            metric: 100
          - to: "172.16.0.0/12"
            via: "10.10.10.254"
            metric: 110

    host_vars/host02.yml(ๅ€‹ๅˆฅ่ฆ†ๅฏซ)

    netplan_ethernets:
      - name: "ens160"
        dhcp4: false
        addresses: ["10.10.10.12/24"]
        gateway4: "10.10.10.1"
        nameservers:
          addresses: ["1.1.1.1", "8.8.8.8"]
        routes:
          - to: "10.30.0.0/16"
            via: "10.10.10.1"
            metric: 100

    Netplan ๆจกๆฟ:roles/netplan/templates/01-netcfg.yaml.j2

    # This file is managed by Ansible. DO NOT EDIT MANUALLY.
    network:
      version: 2
      renderer: {{ netplan_renderer }}
      ethernets:
    {% for nic in netplan_ethernets %}
        {{ nic.name }}:
          dhcp4: {{ nic.dhcp4 | default(false) | bool | lower }}
    {% if nic.addresses is defined and (nic.addresses | length) > 0 %}
          addresses:
    {% for addr in nic.addresses %}
            - {{ addr }}
    {% endfor %}
    {% endif %}
    {% if nic.gateway4 is defined and nic.gateway4 %}
          gateway4: {{ nic.gateway4 }}
    {% endif %}
    {% if nic.nameservers is defined and nic.nameservers.addresses is defined and (nic.nameservers.addresses | length) > 0 %}
          nameservers:
            addresses:
    {% for dns in nic.nameservers.addresses %}
              - {{ dns }}
    {% endfor %}
    {% endif %}
    {% if nic.routes is defined and (nic.routes | length) > 0 %}
          routes:
    {% for r in nic.routes %}
            - to: {{ r.to }}
              via: {{ r.via }}
    {% if r.metric is defined %}
              metric: {{ r.metric }}
    {% endif %}
    {% endfor %}
    {% endif %}
    {% endfor %}

    Role:defaults / handlers / tasks(ๅฅ—็”จ、้ฉ—่ญ‰、ๅ›žๆปพ)

    roles/netplan/defaults/main.yml

    netplan_filename: "01-netcfg.yaml"
    netplan_renderer: "networkd"
    netplan_apply: true
    netplan_ethernets: []

    roles/netplan/handlers/main.yml

    - name: netplan_generate
      become: true
      ansible.builtin.command: "netplan generate"
    
    - name: netplan_apply
      become: true
      ansible.builtin.command: "netplan apply"

    roles/netplan/tasks/main.yml(ๅซ้ฉ—่ญ‰่ˆ‡ๅ›žๆปพ้ชจๆžถ)

    - name: Ensure netplan directory exists
      become: true
      ansible.builtin.file:
        path: /etc/netplan
        state: directory
        mode: "0755"
    
    - name: Set paths
      ansible.builtin.set_fact:
        netplan_path: "/etc/netplan/{{ netplan_filename }}"
        netplan_backup_path: "/etc/netplan/{{ netplan_filename }}.bak.ansible"
    
    - name: Backup current netplan file if exists
      become: true
      ansible.builtin.copy:
        src: "{{ netplan_path }}"
        dest: "{{ netplan_backup_path }}"
        remote_src: true
        mode: "0644"
      ignore_errors: true
    
    - name: Deploy netplan config from template
      become: true
      ansible.builtin.template:
        src: "{{ netplan_filename }}.j2"
        dest: "{{ netplan_path }}"
        mode: "0644"
    
    - name: Validate netplan syntax (generate)
      become: true
      ansible.builtin.command: "netplan generate"
      register: netplan_generate_result
      changed_when: false
    
    - name: Apply netplan
      become: true
      ansible.builtin.command: "netplan apply"
      when: netplan_apply | bool
      register: netplan_apply_result
      changed_when: false
    
    - name: Show routes (post)
      become: true
      ansible.builtin.command: "ip route show"
      register: ip_route_post
      changed_when: false

    Playbook:playbooks/netplan.yml(ๅˆ†ๆ‰นๅฅ—็”จ)

    - name: Batch manage netplan and static routes
      hosts: netplan_hosts
      become: true
      serial: "10%"
      any_errors_fatal: true
    
      roles:
        - role: netplan

    ๅŸท่กŒๆ–นๅผ่ˆ‡้ฉ—่ญ‰ๅปบ่ญฐ

    1) Check Mode ็œ‹ๅทฎ็•ฐ

    ansible-playbook -i inventory/hosts.ini playbooks/netplan.yml --check --diff

    2) ๆญฃๅผๅฅ—็”จ(ๅˆ†ๆ‰น)

    ansible-playbook -i inventory/hosts.ini playbooks/netplan.yml

    3) ๅฅ—็”จๅพŒ้ฉ—่ญ‰(ไธปๆฉŸ็ซฏ)

    ip addr
    ip route show
    systemctl status systemd-networkd --no-pager || true
    systemctl status NetworkManager --no-pager || true

    ๐Ÿ“˜ ็ต่ชž

    ๆŠŠ Netplan ่ˆ‡้œๆ…‹่ทฏ็”ฑ「่ณ‡ๆ–™ๅŒ–、ๆจกๆฟๅŒ–、่ง’่‰ฒๅŒ–」,ๅ†้…ๅˆๅˆ†ๆ‰นๅฅ—็”จ่ˆ‡ๅŸบๆœฌ้ฉ—่ญ‰, ๅฐฑ่ƒฝไปฅ Infrastructure as Code ็š„ๆ–นๅผ็ฎก็†ๅคง้‡ไธปๆฉŸ็ถฒ่ทฏ่จญๅฎš,่ฎ“่ฎŠๆ›ดๆ›ดๅฏๆŽง、ๅฏ่ฟฝๆบฏไธ”ๆ›ดๅฎ‰ๅ…จ。

    ๐Ÿ’ฌ ็•™่จ€ไบ’ๅ‹•:ไฝ ็š„็’ฐๅขƒ่ˆ‡้œ€ๆฑ‚ๆ˜ฏไป€้บผ?

    ๅฆ‚ๆžœไฝ ๅœจๅฅ—็”จ Netplan / ้œๆ…‹่ทฏ็”ฑๆ™‚้‡ๅˆฐๆ–ท็ทš、ไธๅŒ renderer ่กŒ็‚บๅทฎ็•ฐ、ๆˆ–ๆƒณๆ”ฏๆด VLAN / ๅคš NIC / policy routing, ๆญก่ฟŽๅœจ็•™่จ€ๆไพ›ไปฅไธ‹่ณ‡่จŠ,ๆˆ‘ๅฏไปฅๅ”ๅŠฉไฝ ๆŠŠ Playbook ่ชฟๆ•ดๆˆๆ›ด่ฒผ่ฟ‘ๅฏฆ้š›ๅ ดๆ™ฏ็š„็‰ˆๆœฌ。

    • ไฝœๆฅญ็ณป็ตฑ็‰ˆๆœฌ(ไพ‹ๅฆ‚ Ubuntu 22.04/24.04)่ˆ‡ renderer(networkd / NetworkManager)
    • ไป‹้ขๅ็จฑ(ไพ‹ๅฆ‚ ens160、bond0、vlan10)่ˆ‡ IP ่ฆๅŠƒ
    • Gateway ่ˆ‡้œ€่ฆ็š„้œๆ…‹่ทฏ็”ฑ(to/via/metric)
    • ๆ˜ฏๅฆ้œ€ๅˆ†ๆต(ๅคšๅ‡บๅฃ)、ๆˆ–้œ€ policy routing / routing table

    ๅปถไผธ้–ฑ่ฎ€

    ๐Ÿ”— ๅˆ†ไบซ้€™็ฏ‡ LINE Facebook X

    ๆฒ’ๆœ‰็•™่จ€:

    ๅผต่ฒผ็•™่จ€

    ๅญ—็ดš