ansible技巧——使用ansible修改网络
ansible是现在使用最广泛的的自动化运维工具之一,它有着强大的能力以及丰富的特性。不过并非在所有方面它都能面面俱到,在修改网络方面,ansible所能获取的资料甚少,而在问答社区中更是有人回答ansible适合在平稳的网络条件下运行,不适合用来修改网络。那么,如果我真的需要为某些节点修改网络ip,怎么才能办到呢?接下来本篇文章介绍一些可行的办法。
问题还原
如下是一个简单的playbook,负责将新的ip写入到配置文件中,随后清空ip并重启network服务。
# playbooks/test.yml
- hosts: nodes[0]
gather_facts: false
tasks:
- name: Apply new network
shell: |
cat > /etc/sysconfig/network-scripts/ifcfg-{{ device }} << EOF
DEVICE={{ device }}
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
IPADDR={{ ipaddr }}
GATEWAY={{ gateway }}
NETMASK={{ netmask }}
EOF
ip address flush scope global
systemctl restart network
这个playbook乍一看没有什么问题, 但执行的时候却会发现进程一直卡在修改网络的步骤,直到ssh超时(约半小时)。
# ansible-playbook -i inventory/hosts.ini playbooks/test.yml -e device=eth0 -e ipaddr=192.168.0.10 -e gateway=192.168.0.254 -e netmask=255.255.255.0
PLAY [nodes[0]] ****************************************************************************************
TASK [Apply new network] *******************************************************************************
# hang on...
这是由于ssh远程执行命令的中途网络发生变化所导致的,ansible使用ssh连接各个托管节点,倘若ssh连接中途网络环境发生变化,那么ssh命令的执行就会一直卡住,等待托管节点返回响应,虽然这个响应可能永远也等不到了。
解决方法
一个显而易见的方法是,既然ansible内置的ssh方法是同步连接,那么不用ansible连接托管节点,改为自己手动异步ssh不就好了么?如下所示,临时将task运行的节点通过delegate_to修改为控制节点自身,随后使用ssh执行命令。异步的方法有两种,一个是通过nohup的方式,另一个是给ssh加上-f参数,这两种方式均能试ssh转入后台运行。
- name: Apply new network
shell: |
# nohup ssh -p{{ ansible_ssh_port|default(55555) }} {{ item }} 'ip address flush scope global && systemctl restart network' &
ssh -f -p{{ ansible_ssh_port|default(55555) }} {{ item }} 'ip address flush scope global && systemctl restart network'
loop: "{{ groups.nodes|reject('eq', groups.nodes.0) }}"
delegate_to: localhost
也可以使用ansible自身的异步特性做到这一点,ansible提供了async和poll两个关键词,当poll的值为0时,可以将一个task转入后台执行(poll值非0时用以设置一个任务的执行超时时间,和异步没有关系)。如下所示,该task也能正常的修改托管节点的网络,且实现较为优雅。
- name: Apply new network
shell: |
ip address flush scope global
systemctl restart network
async: 60
poll: 0
综合案例
接下来介绍一个ansible网络修改的综合案例。假设现在要部署一个三节点k8s集群,其中每个节点都包含管理外网和管理内网两个ip地址,在通过管理外网pxe安装操作系统之后,ansible会为每个节点配置管理内网的ip,但仅为master1节点配置管理外网,其余节点均不再需要管理外网的ip,为之后部署k8s服务做准备。
现在需要写一份playbook,通过管理外网ip连接到三个节点上,应用新的网络配置文件并重启网络,playbook目录结构如下:
# tree
.
├── inventory
│ ├── group_vars
│ │ └── all.yml
│ ├── hosts.ini
│ └── host_vars
│ ├── 1.1.1.1.yml
│ ├── 2.2.2.2.yml
│ └── 3.3.3.3.yml
└── playbooks
├── roles
│ └── test
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ ├── ifcfg_mgm_ex.j2
│ └── ifcfg_mgm_in.j2
└── setup.yml
8 directories, 9 files
inventory/hosts.ini文件记录被控节点的清单:
localhost
# BEGIN NODES BLOCK
[nodes]
1.1.1.1 ansible_ssh_port=22
2.2.2.2 ansible_ssh_port=22
3.3.3.3 ansible_ssh_port=22
# END NODES BLOCK
playbook/setup.yml文件描述在nodes主机组中运行test role:
---
- hosts: nodes
gather_facts: false
roles:
- { role: test }
playbooks/roles/test/tasks/main.yml文件:
- name: prepare_network | delete all /etc/sysconfig/network-scripts/ifcfg-* file
shell: rm -rf /etc/sysconfig/network-scripts/ifcfg-*
- name: prepare_network | template management internal interface to /etc/sysconfig/network-scripts
vars:
device: "{{ ifaceRoles.mgm }}"
ipaddr: "{{ intMgmIpaddr }}"
template:
src: ifcfg_mgm_in.j2
dest: /etc/sysconfig/network-scripts/ifcfg-{{ device }}
- name: prepare_network | template management external interface to /etc/sysconfig/network-scripts
vars:
device: "{{ ifaceRoles.ext }}"
ipaddr: "{{ extMgmIpaddr }}"
template:
src: ifcfg_mgm_ex.j2
dest: /etc/sysconfig/network-scripts/ifcfg-{{ device }}
when: inventory_hostname == groups.nodes.0
- name: prepare_network | restart network service on nodes, synchronous on master1 and asynchronous on the other nodes
shell: |
ip address flush scope global
systemctl restart network
async: 60
poll: "{{ inventory_hostname == groups.nodes.0 and 3 or 0 }}"
- name: prepare_network | later tasks
debug:
msg: "Later tasks can't run on nodes except master1 on account of no network connectivity."
管理外网和管理内网网卡配置文件的模板:
# playbooks/roles/test/templates/ifcfg_mgm_ex.j2
DEVICE={{ device }}
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
IPADDR={{ ipaddr }}
GATEWAY={{ gateway }}
NETMASK={{ netMask }}
# playbooks/roles/test/templates/ifcfg_mgm_in.j2
DEVICE={{ device }}
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
IPADDR={{ ipaddr }}
GATEWAY=
NETMASK={{ netMask }}
小结
综上,使用ansible修改网络并不是一件推荐的事,不过依然有办法能做到这一点。除此之外,网络ip的变化意味着ansible控制节点无法继续访问被控节点,如果在修改ip后依然需要访问被控节点,ansible提供了add_host模块用以动态添加被控节点,这样便能够继续访问。