Files
gh-basher83-lunar-claude-pl…/skills/ansible-best-practices/patterns/network-automation.md
2025-11-29 18:00:24 +08:00

468 lines
13 KiB
Markdown

# Network Automation Patterns
Best practices for declarative network configuration in Proxmox VE environments with Ansible.
## Pattern: Declarative Network Interface Configuration
**Problem**: Network configuration is complex, error-prone when done manually, and difficult to maintain across
multiple nodes.
**Solution**: Use declarative configuration with data structures that describe desired state.
### Configuration Model
```yaml
# group_vars/matrix_cluster.yml
network_interfaces:
management:
bridge: vmbr0
physical_port: enp4s0
address: "192.168.3.{{ node_id }}/24"
gateway: "192.168.3.1"
vlan_aware: true
vlan_ids: "9"
mtu: 1500
comment: "Management network"
ceph_public:
bridge: vmbr1
physical_port: enp5s0f0np0
address: "192.168.5.{{ node_id }}/24"
mtu: 9000
comment: "CEPH Public network"
ceph_private:
bridge: vmbr2
physical_port: enp5s0f1np1
address: "192.168.7.{{ node_id }}/24"
mtu: 9000
comment: "CEPH Private network"
# VLAN configuration
vlans:
- id: 9
raw_device: vmbr0
address: "192.168.8.{{ node_id }}/24"
comment: "Corosync network"
# Node-specific IDs
node_ids:
foxtrot: 5
golf: 6
hotel: 7
# Set node_id based on hostname
node_id: "{{ node_ids[inventory_hostname_short] }}"
```
### Implementation
```yaml
# roles/proxmox_networking/tasks/bridges.yml
---
- name: Create Proxmox bridge interfaces in /etc/network/interfaces
ansible.builtin.blockinfile:
path: /etc/network/interfaces
marker: "# {mark} ANSIBLE MANAGED BLOCK - {{ item.key }}"
block: |
# {{ item.value.comment }}
auto {{ item.value.bridge }}
iface {{ item.value.bridge }} inet static
address {{ item.value.address }}
{% if item.value.gateway is defined %}
gateway {{ item.value.gateway }}
{% endif %}
bridge-ports {{ item.value.physical_port }}
bridge-stp off
bridge-fd 0
{% if item.value.vlan_aware | default(false) %}
bridge-vlan-aware yes
{% endif %}
{% if item.value.vlan_ids is defined %}
bridge-vids {{ item.value.vlan_ids }}
{% endif %}
{% if item.value.mtu is defined and item.value.mtu != 1500 %}
mtu {{ item.value.mtu }}
{% endif %}
create: false
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
notify:
- reload networking
```
## Pattern: VLAN Interface Creation
**Problem**: VLAN interfaces must be created at runtime and persist across reboots.
**Solution**: Manage both persistent configuration and runtime state.
### Implementation
```yaml
# roles/proxmox_networking/tasks/vlans.yml
---
- name: Configure VLAN interfaces in /etc/network/interfaces
ansible.builtin.blockinfile:
path: /etc/network/interfaces
marker: "# {mark} ANSIBLE MANAGED BLOCK - vlan{{ item.id }}"
block: |
# {{ item.comment }}
auto vlan{{ item.id }}
iface vlan{{ item.id }} inet static
address {{ item.address }}
vlan-raw-device {{ item.raw_device }}
create: false
loop: "{{ vlans }}"
loop_control:
label: "vlan{{ item.id }}"
notify:
- reload networking
- name: Check if VLAN interface exists
ansible.builtin.command:
cmd: "ip link show vlan{{ item.id }}"
register: vlan_check
failed_when: false
changed_when: false
loop: "{{ vlans }}"
loop_control:
label: "vlan{{ item.id }}"
- name: Create VLAN interface at runtime
ansible.builtin.command:
cmd: "ip link add link {{ item.item.raw_device }} name vlan{{ item.item.id }} type vlan id {{ item.item.id }}"
when: item.rc != 0
loop: "{{ vlan_check.results }}"
loop_control:
label: "vlan{{ item.item.id }}"
notify:
- reload networking
- name: Bring up VLAN interface
ansible.builtin.command:
cmd: "ip link set vlan{{ item.item.id }} up"
when: item.rc != 0
loop: "{{ vlan_check.results }}"
loop_control:
label: "vlan{{ item.item.id }}"
```
## Pattern: MTU Configuration for Jumbo Frames
**Problem**: CEPH storage networks require jumbo frames (MTU 9000) for optimal performance.
**Solution**: Configure MTU at both interface and bridge level with verification.
### Implementation
```yaml
# roles/proxmox_networking/tasks/mtu.yml
---
- name: Set MTU on physical interfaces
ansible.builtin.command:
cmd: "ip link set {{ item.value.physical_port }} mtu {{ item.value.mtu }}"
when: item.value.mtu is defined and item.value.mtu > 1500
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.physical_port }}"
register: mtu_set
changed_when: mtu_set.rc == 0
- name: Set MTU on bridge interfaces
ansible.builtin.command:
cmd: "ip link set {{ item.value.bridge }} mtu {{ item.value.mtu }}"
when: item.value.mtu is defined and item.value.mtu > 1500
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
register: bridge_mtu_set
changed_when: bridge_mtu_set.rc == 0
- name: Verify MTU configuration
ansible.builtin.command:
cmd: "ip link show {{ item.value.bridge }}"
register: mtu_check
changed_when: false
failed_when: "'mtu ' + (item.value.mtu | string) not in mtu_check.stdout"
when: item.value.mtu is defined and item.value.mtu > 1500
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Test jumbo frame connectivity (CEPH networks only)
ansible.builtin.command:
cmd: "ping -c 3 -M do -s 8972 {{ hostvars[item].ansible_host }}"
register: jumbo_test
changed_when: false
failed_when: false
when:
- "'ceph' in network_interfaces"
- item != inventory_hostname
loop: "{{ groups['proxmox'] }}"
loop_control:
label: "{{ item }}"
- name: Report jumbo frame test results
ansible.builtin.debug:
msg: "Jumbo frame test to {{ item.item }}: {{ 'PASSED' if item.rc == 0 else 'FAILED' }}"
when: item is not skipped
loop: "{{ jumbo_test.results }}"
loop_control:
label: "{{ item.item }}"
```
## Pattern: Bridge VLAN-Aware Configuration
**Problem**: VMs need access to multiple VLANs through a single bridge interface.
**Solution**: Enable VLAN-aware bridges and specify allowed VLAN IDs.
### Implementation
```yaml
# roles/proxmox_networking/tasks/vlan_aware.yml
---
- name: Check current bridge VLAN awareness
ansible.builtin.command:
cmd: "bridge vlan show dev {{ item.value.bridge }}"
register: vlan_aware_check
changed_when: false
failed_when: false
when: item.value.vlan_aware | default(false)
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Enable VLAN filtering on bridge
ansible.builtin.command:
cmd: "ip link set {{ item.value.bridge }} type bridge vlan_filtering 1"
when:
- item.value.vlan_aware | default(false)
- "'vlan_filtering 0' in vlan_aware_check.results[ansible_loop.index0].stdout | default('')"
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
extended: true
register: vlan_filtering
changed_when: vlan_filtering.rc == 0
- name: Configure allowed VLANs on bridge
ansible.builtin.command:
cmd: "bridge vlan add vid {{ item.value.vlan_ids }} dev {{ item.value.bridge }} self"
when:
- item.value.vlan_aware | default(false)
- item.value.vlan_ids is defined
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
register: vlan_add
changed_when: vlan_add.rc == 0
failed_when:
- vlan_add.rc != 0
- "'already exists' not in vlan_add.stderr"
```
## Pattern: Network Configuration Validation
**Problem**: Network misconfigurations can cause node isolation and cluster failures.
**Solution**: Validate configuration before and after applying changes.
### Implementation
```yaml
# roles/proxmox_networking/tasks/validate.yml
---
- name: Verify interface configuration file syntax
ansible.builtin.command:
cmd: ifup --no-act {{ item.value.bridge }}
register: config_syntax
changed_when: false
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Check interface operational status
ansible.builtin.command:
cmd: "ip link show {{ item.value.bridge }}"
register: interface_status
changed_when: false
failed_when: "'state UP' not in interface_status.stdout"
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Verify IP address assignment
ansible.builtin.command:
cmd: "ip addr show {{ item.value.bridge }}"
register: ip_status
changed_when: false
failed_when: item.value.address.split('/')[0] not in ip_status.stdout
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Test connectivity to gateway
ansible.builtin.command:
cmd: "ping -c 3 -W 2 {{ item.value.gateway }}"
register: gateway_ping
changed_when: false
when: item.value.gateway is defined
loop: "{{ network_interfaces | dict2items }}"
loop_control:
label: "{{ item.value.bridge }}"
- name: Test connectivity to cluster peers
ansible.builtin.command:
cmd: "ping -c 3 -W 2 {{ hostvars[item].ansible_host }}"
register: peer_ping
changed_when: false
when: item != inventory_hostname
loop: "{{ groups['proxmox'] }}"
loop_control:
label: "{{ item }}"
```
## Anti-Pattern: Excessive Shell Commands
**❌ Don't Do This**:
```yaml
- name: Create VLAN interface if needed
ansible.builtin.shell: |
if ! ip link show vmbr0.{{ item.vlan }} >/dev/null 2>&1; then
ip link add link vmbr0 name vmbr0.{{ item.vlan }} type vlan id {{ item.vlan }}
ip link set vmbr0.{{ item.vlan }} up
fi
```
**Problems**:
- Shell-specific syntax
- Limited idempotency
- No check-mode support
- Harder to test
- Error handling is fragile
**✅ Do This Instead**:
```yaml
- name: Check if VLAN interface exists
ansible.builtin.command:
cmd: "ip link show vmbr0.{{ item.vlan }}"
register: vlan_check
failed_when: false
changed_when: false
- name: Create VLAN interface
ansible.builtin.command:
cmd: "ip link add link vmbr0 name vmbr0.{{ item.vlan }} type vlan id {{ item.vlan }}"
when: vlan_check.rc != 0
register: vlan_create
changed_when: vlan_create.rc == 0
- name: Bring up VLAN interface
ansible.builtin.command:
cmd: "ip link set vmbr0.{{ item.vlan }} up"
when: vlan_check.rc != 0
```
## Handler Configuration
```yaml
# roles/proxmox_networking/handlers/main.yml
---
- name: reload networking
ansible.builtin.systemd:
name: networking
state: reloaded
listen: reload networking
throttle: 1 # One node at a time to prevent cluster disruption
- name: restart networking
ansible.builtin.systemd:
name: networking
state: restarted
listen: restart networking
throttle: 1
when: not ansible_check_mode # Don't restart in check mode
```
## Complete Role Example
```yaml
# roles/proxmox_networking/tasks/main.yml
---
- name: Validate prerequisites
ansible.builtin.include_tasks: prerequisites.yml
- name: Configure bridge interfaces
ansible.builtin.include_tasks: bridges.yml
- name: Configure VLAN interfaces
ansible.builtin.include_tasks: vlans.yml
when: vlans is defined and vlans | length > 0
- name: Configure VLAN-aware bridges
ansible.builtin.include_tasks: vlan_aware.yml
- name: Configure MTU for jumbo frames
ansible.builtin.include_tasks: mtu.yml
when: network_jumbo_frames_enabled | default(false)
- name: Validate network configuration
ansible.builtin.include_tasks: validate.yml
```
## Testing
```bash
# Syntax check
ansible-playbook --syntax-check playbooks/network-config.yml
# Check mode (dry run) - won't restart networking
ansible-playbook playbooks/network-config.yml --check --diff
# Apply to single node first
ansible-playbook playbooks/network-config.yml --limit foxtrot
# Verify MTU configuration
ansible -i inventory/proxmox.yml matrix_cluster -m shell \
-a "ip link show | grep -E 'vmbr[12]' | grep mtu"
# Test jumbo frames
ansible -i inventory/proxmox.yml matrix_cluster -m shell \
-a "ping -c 3 -M do -s 8972 192.168.5.6"
```
## Matrix Cluster Example
```yaml
# Example playbook for Matrix cluster networking
---
- name: Configure Matrix Cluster Networking
hosts: matrix_cluster
become: true
serial: 1 # Configure one node at a time
roles:
- role: proxmox_networking
vars:
network_jumbo_frames_enabled: true
```
## Related Patterns
- [Cluster Automation](cluster-automation.md) - Cluster formation with corosync networking
- [CEPH Storage](ceph-automation.md) - CEPH network requirements
- [Error Handling](error-handling.md) - Network validation error handling
## References
- ProxSpray analysis: `docs/proxspray-analysis.md` (lines 209-331)
- Proxmox VE Network Configuration documentation
- Linux bridge configuration guide
- VLAN configuration best practices