468 lines
13 KiB
Markdown
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
|