对大量服务器的管理,我们可能会想到用ansible,这个基于ssh进行管理的软件,功能全面丰富,是运维人员的不错选择。但他需要有一定的学习成本,一些简单的需求我们没必要特地使用它,使用Python中的paramiko库同样可以实现类似功能。
处理思路
我们要使用这个功能来更换容器类的节点,我认为可以分为以下几步
一是节点判断是否空闲,空闲断开,不空闲则加入另一个列表,等待下次可以执行的时机。
二是根据容器命令,通过筛选找到指定的对应原节点,这里可以用ip和端口号的标识。停止并删除原来的容器。接着再根据预设命令和新的image重新生成一个容器。
三是jenkins重新连接原来的节点。
四是对第一步未能操作的节点等待一定时间后重新加入上面的步骤循环。
先决条件
1是执行操作的主机具有ssh登录对应机器的密钥或其他条件。比较通用的是用私钥,将对应的公钥配置到对应机器用户的authorized_keys 文件中(前面在jenkins建立容器节点的文章用的就是这种方法)。如果机器不多的话使用用户密码也可以,要注意sshd_config文件中有没有允许对应用户,特别是root用户的密码登录。
2是执行容器命令的机器最好已经拉取新的镜像,避免不可拉取预知的等待时间,至少要配置对应仓库的密钥。 3是节点的名称要有规律,不然不利于批量处置
先写个通用的上下文管理器
下面实现了一个简易的基于paramiko的SSH管理器,预设了密钥文件是本地文件id_rsa ,root用户且无密码,实际业务对多机器有不同密钥的需求的,可以拓展此项功能,目前以够用为主。
import paramiko
import traceback
class MySSH:
def __init__(self, host):
self.host = host
self.ssh = None
def __enter__(self):
try:
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
pkey = paramiko.RSAKey.from_private_key_file('id_rsa')
self.ssh.connect(self.host, username='root', pkey=pkey)
return self.ssh
except Exception as e:
traceback.print_exc()
raise e
def __exit__(self, exc_type, exc_val, exc_tb):
try:
self.ssh.close()
except Exception as e:
traceback.print_exc()
raise e
再操作节点
12-13行处先判断Jenkins节点是否空闲,空闲则临时下线 14-16行容器下线后进行原容器的删除 17-19行根据容器命令和新镜像重新创建一个容器 21-23行将容器重新上线
from jenkinsapi.jenkins import Jenkins
host = '127.0.0.1'
port = '10003'
jks = Jenkins('http://127.0.0.1:8080/', username='bob', password='110c70ea8f41063e377aca53007a1ca2e4',
use_crumb=True)
node_name = f'{host}_{port}'
with MySSH(host) as ssh:
node = jks.get_node(node_name)
if node.is_idle():
node.set_offline('准备换系统')
docker_delete_command = """docker stop $ (docker ps -a | grep "%s" | awk ' {print $1}') && docker rm $ (docker ps -a | grep "%s" | awk ' {print $1}')""" % (
port, port)
run_command(ssh, docker_delete_command)
new_image = 'ubuntu_node:1.5'
docker_create_command = f'docker run -itd -p {port}:22 {new_image} /bin/bash'
run_command(ssh, docker_create_command)
node = jks.get_node(node_name)
node.poll()
node.set_online()
在大批量业务场景下,我们可以利用列表去存储非空闲节点,将其加入下一次循环处置,直到全部处理。