Linux之Ansible自动化运维(一) - 教程
一、Ansible 工具的概念和实战
1.Ansible软件概念
Ansible是一款开源、免费的自动化工具软件,主要用于批量管理Unix、Linux服务器,具体对服务器操作:执行命令、修改配置文件、性能调优、系统上线和下线、任务计划的管理等
2.Ansible软件特点
- 基于Python语言去开发的,简洁,高效率
- 无需在客户端机器部署Agent机器
- 通过SSH协议远程登录客户端对其管理
- 批量执行的指令可以写成Playbook剧本(类似SHELL脚本)
- 可以支持二次开发,能够跟自动化运维平台整合
- 可以使用普通用户去管理客户端,支持sudo提权
3.Ansible工作原理
- 通过Ansible自动化运维工具管理客户端,需提前将客户端主机名或IP地址写入本地/etc/ansible/hosts主机清单文件中,没有被写入的客户端是不能被管理的
- 批量执行的指令或任务可以写成Playbook剧本(类似shell脚本),剧本文件无需拷贝至客户端上,在ansible管理端主动推送剧本中的命令让客户端机器去运行
- Ansible是基于SSH协议去远程连接客户端(客户端一定开启SShd服务)并且管理端会调用自带的模块或者第三方插件管理,不同模块实现的功能需求各不相同
二、Ansible自动化工具实战
配置文件修改
基于OpenEuler22.x操作系统部署一套Ansible软件gongju7,采用Yum、RPM方式安装,部署方法 和指令如下
提前将客户端的IP地址或主机名写入到ansible管理端/etc/ansible/hosts主机清单文件中(可以创建分组)
for i in $(seq 128 130);do echo 192.168.101.$i ;done >> /etc/ansible/hosts
基于ansible工具去管理客户端需要调用各自的模块去操作,常见模块:command命令、shell(linux指令,shell命令)、copy(拷贝文件或目录)、file(创建文件)、user(管理用户和组)、cron(任务计划)、yum(安装、卸载软件)、script(脚本管理)、setup(获取系统信息)
使用客户端192.168.101.6作为客户端,让客户端执行"df -h"命令去查看磁盘的使用情况,具体命令如下:
公私钥免密登录
默认通过ansible管理客户端需要输入用户名、密码,下面是对免秘钥登录操作
## 在ansible机器使用ssh-keygen
ssh-keygen
##查看秘钥对文件
ls -l
## 拷贝公钥文件
ssh-copy-id -i /root/.ssh/id_rsa.pub 192.168.101.6
## 在客户端查看公钥文件内容
cat /root/.ssh/authorized_keys
公钥情况都一致
设置免密钥之后再次执行客户端192.168.101.6命令“”df -h”(不使用-k选项密码登录)
ansible基础使用
-i选项和-m选项(默认都有)可省略不写
并发执行查看三台机器磁盘情况命令
ansible all -a "df -h" -f 100(每隔100台机器并行一次)
在ansible配置文件设置如下配置即可忽略警告
ansible_python_interpreter = /usr/bin/python3
interpreter_python = /usr/bin/python3
# 串行执行
for i in $(seq 5 7);do echo -e "\033[31m192.168.$i | CHANGED | rc=0 ##\033[0m" ;ssh -l root 192.168.101.$i "df -h" ;done
ansible模块化使用
shell模块
shell模块使用正则查看进程情况
ansible all -m shell -a "ps -ef | grep -aiE sshd"
- 通过shell模块使用yum
ansible all -m shell -a "yum -y install ntpdate"
yum 模块
## 安装yum模块
ansible 192.168.101.5 -m yum -a "name=screen,lrzsz,ntpdate state=install"
### 安装httpd工具
ansible 192.168.101.6 -m yum -a "name=httpd state=latest"
ping模块
192.168.101.5 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.101.6 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.101.7 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.101.7 port 22: No route to host",
"unreachable": true
}
user模块
## 创建user用户
[root@localhost ~]# ansible deepseek -m user -a "name=jfedu state=present home=/home/jfedu shell=/sbin/nologin"
192.168.101.6 | CHANGED => {
"changed": true,
"comment": "",
"create_home": true,
"group": 1000,
"home": "/home/jfedu",
"name": "jfedu",
"shell": "/sbin/nologin",
"state": "present",
"system": false,
"uid": 1000
}
192.168.101.7 | CHANGED => {
"changed": true,
"comment": "",
"create_home": true,
"group": 1000,
"home": "/home/jfedu",
"name": "jfedu",
"shell": "/sbin/nologin",
"state": "present",
"system": false,
"uid": 1000
}
n/nologin"
copy模块
不检测ssh远程连接秘钥
参数目录
使用copy模块,拷贝文件内容
[root@localhost ~]# ansible deepseek -m copy -a "content='good evening' dest=/usr/share/nginx/html/index.html"
192.168.101.7 | CHANGED => {
"changed": true,
"checksum": "d09bfa96b41c338eed41ea3ee2e5218340dfb313",
"dest": "/usr/share/nginx/html/index.html",
"gid": 0,
"group": "root",
"md5sum": "0fa14c6f02392c5bcaaaa979552de664",
"mode": "0644",
"owner": "root",
"size": 12,
"src": "/root/.ansible/tmp/ansible-tmp-1755692096.9817061-4999-90484632827178/source",
"state": "file",
"uid": 0
}
192.168.101.6 | CHANGED => {
"changed": true,
"checksum": "d09bfa96b41c338eed41ea3ee2e5218340dfb313",
"dest": "/usr/share/nginx/html/index.html",
"gid": 0,
"group": "root",
"md5sum": "0fa14c6f02392c5bcaaaa979552de664",
"mode": "0644",
"owner": "root",
"size": 12,
"src": "/root/.ansible/tmp/ansible-tmp-1755692096.95608-4997-183015220025852/source",
"state": "file",
"uid": 0
}
[root@localhost ~]# ansible deepseek -m shell -a "cat /usr/share/nginx/html/index.html"
192.168.101.7 | CHANGED | rc=0 >>
good evening
192.168.101.6 | CHANGED | rc=0 >>
good evening
## 使用backup选项对原文件进行备份
[root@localhost ~]# ansible deepseek -m copy -a "content='hallo' dest=/usr/share/nginx/html/index.html backup=yes "
cron模块
常见参数:
ame 任务计划名称;
cron_file 替换客户端该用户的任务计划的文件;
minute 分( 0-59 ,* ,*/2 );
hour 时( 0-23 ,* ,*/2 );
day 日( 1-31 ,* ,*/2 );
month 月( 1-12 ,* ,*/2 );
weekday 周( 0-6 或 1-7 ,* );
job 任何计划执行的命令,state要等于present;
backup 是否备份之前的任务计划;
user 新建任务计划的用户;
state 指定任务计划present、absent。
自定义实现ntp实现时间同步
## 添加定时任务
[root@localhost ~]# ansible all -m cron -a "minute=0 hour=0 day=* month=* weekday=* name='Ntpdate server for sync time' job='pool.ntp.org'"
192.168.101.5 | CHANGED => {
"changed": true,
"envs": [],
"jobs": [
"Ntpdate server for sync time"
]
}
192.168.101.7 | CHANGED => {
"changed": true,
"envs": [],
"jobs": [
"Ntpdate server for sync time"
]
}
192.168.101.6 | CHANGED => {
"changed": true,
"envs": [],
"jobs": [
"Ntpdate server for sync time"
]
}
[root@localhost ~]# cat /var/spool/cron/root
#Ansible: Ntpdate server for sync time
0 0 * * * pool.ntp.org
## 删除定时任务
[root@localhost ~]# ansible all -m cron -a "minute=0 hour=0 day=* month=* weekday=* name='Ntpdate server for sync time' state=absent job='pool.ntp.org'"
192.168.101.5 | CHANGED => {
"changed": true,
"envs": [],
"jobs": []
}
192.168.101.7 | CHANGED => {
"changed": true,
"envs": [],
"jobs": []
}
192.168.101.6 | CHANGED => {
"changed": true,
"envs": [],
"jobs": []
}
[root@localhost ~]# cat /var/spool/cron/root
[root@localhost ~]#
synchronize模块
常见参数模块内容
compress 开启压缩,默认为开启;
archive 是否采用归档模式同步,保证源和目标文件属性一致;
checksum 是否效验;
dirs 以非递归的方式传输目录;
links 同步链接文件;
recursive 是否递归yes/no;
rsync_opts 使用rsync 的参数;
copy_links 同步的时候是否复制连接;
delete 删除源中没有而目标存在的文件;
src 源目录及文件;
dest 目标目录及文件;
dest_port 目标接受的端口;
rsync_path 服务的路径,指定 rsync 命令来在远程服务器上运行;
rsync_timeout 指定rsync操作的IP超时时间;
set_remote_user 设置远程用户名;
--exclude=.log 忽略同步.log结尾的文件;
mode
- 模块练习
## 远程拷贝目录(增量拷贝)
[root@localhost ~]# ansible 192.168.101.7 -m synchronize -a "src=/root/ dest=/root/"
192.168.101.7 | CHANGED => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=>%i %n%L /root/ 192.168.101.7:/root/",
}
## 删除源目标目录重复文件
[root@localhost ~]# ansible 192.168.101.7 -m synchronize -a "src=/root/ dest=/root delete=yes"
192.168.101.7 | CHANGED => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --delete-after --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=>%i %n%L /root/ 192.168.101.7:/root rsync_opts=--no-motd,--exclude=*.log"
}
## 排除.log以外其他文件
[root@localhost ~]# ansible 192.168.101.7 -m synchronize -a "src=/root/ dest=/root delete=yes rsync_opts=--no-motd,--exclude=*.log"
192.168.101.7 | CHANGED => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --delete-after --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --no-motd --exclude=*.log --out-format=>%i %n%L /root/ 192.168.101.7:/root",
"msg": ".d..t...... .ansible/tmp/\ncd+++++++++ .ansible/tmp/ansible-local-86444xxrtdv1/\ncd+++++++++ .ansible/tmp/ansible-local-86444xxrtdv1/ansiballz_cache/\n
service模块
[root@localhost ~]# ansible 192.168.101.6 -m service -a "name=httpd state=restarted"
192.168.101.6 | CHANGED => {
"changed": true,
"name": "httpd",
"state": "started",
"status": {
"ActiveEnterTimestamp": "n/a",
"ActiveEnterTimestampMonotonic": "0",
"ActiveExitTimestamp": "n/a",
"ActiveExitTimestampMonotonic": "0",
"ActiveState": "inactive",
"After": "nss-lookup.target network.target system.slice -.mount tmp.mount basic.target httpd-init.service remote-fs.target systemd-journald.socket sysinit.target systemd-tmpfiles-setup.service",
"AllowIsolate": "no",
"AssertResult": "no",
"AssertTimestamp": "n/a",
}
[root@localhost ~]# ansible 192.168.101.6 -m shell -a "ps -ef | grep httpd"
192.168.101.6 | CHANGED | rc=0 >>
root 27375 1 0 23:19 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 27376 27375 0 23:19 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 27377 27375 0 23:19 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 27378 27375 0 23:19 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 27379 27375 0 23:19 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
root 28560 28559 0 23:21 pts/1 00:00:00 /bin/sh -c ps -ef | grep httpd
root 28562 28560 0 23:21 pts/1 00:00:00 grep httpd
三、实战ollama部署大模型框架
设置模型分组
[nginx]
192.168.101.5
[deepseek]
192.168.101.6
192.168.101.7
准备ollama-linux-amd64.tgz的linux安装包(拷贝ollama包至其他文件)
[root@localhost ~]# ls
anaconda-ks.cfg
install_panel.sh
ollama-linux-amd64.tgz
[root@localhost ~]# ansible deepseek -m copy -a "src=/root/ollama-linux-amd64.tgz dest=/usr/src owner=root group=root mode=644"
创建文件目录存放压缩目录
## 创建文件夹
[root@localhost ~]# ansible deepseek -m file -a "path=/usr/local/ollama/ state=directory mode=755"
192.168.101.7 | CHANGED => {
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/usr/local/ollama/",
"size": 4096,
"state": "directory",
"uid": 0
}
192.168.101.6 | CHANGED => {
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/usr/local/ollama/",
"size": 4096,
"state": "directory",
"uid": 0
}
## 查看文件夹是否存在
[root@localhost ~]# ansible deepseek -m shell -a "ls -ld /usr/local/ollama/"
192.168.101.6 | CHANGED | rc=0 >>
drwxr-xr-x 2 root root 4096 Aug 20 19:44 /usr/local/ollama/
192.168.101.7 | CHANGED | rc=0 >>
drwxr-xr-x 2 root root 4096 Aug 18 03:53 /usr/local/ollama/
解压压缩包到指定目录
[root@localhost ~]# ansible deepseek -m shell -a "tar nux-amd64.tgz -C /usr/local/ollama"
192.168.101.6 | CHANGED | rc=0 >>
192.168.101.7 | CHANGED | rc=0 >>
写入环境变量,启动大模型工具
## 加入环境变量
[root@localhost ~]# ansible deepseek -m shell -a "echo 'export PATH=\$PATH:/usr/local/ollama/bin/' >>/etc/profile"
192.168.101.6 | CHANGED | rc=0 >>
192.168.101.7 | CHANGED | rc=0 >>
## 生效ollama组件
[root@localhost ~]# ansible deepseek -m shell -a "source /etc/profile; ollama serve &"
192.168.101.6 | CHANGED | rc=0 >>
## 查询端口时未生效
[root@localhost ollama]# ss -tnlp|grep 11434
[root@localhost ~]# ansible deepseek -m shell -a "ps -ef | grep ollama"
192.168.101.6 | CHANGED | rc=0 >>
root 6476 6475 0 20:04 pts/1 00:00:00 /bin/sh -c ps -ef | grep ollama
root 6478 6476 0 20:04 pts/1 00:00:00 grep ollama
192.168.101.7 | CHANGED | rc=0 >>
root 15152 15151 0 04:13 pts/0 00:00:00 /bin/sh -c ps -ef | grep ollama
root 15154 15152 0 04:13 pts/0 00:00:00 grep ollama
## 启动ollama程序
ansible deepseek -m shell -a "/usr/local/ollama/bin/ollama serve"
[root@localhost ollama]# source /etc/profile;ollama pull deepseek-r1:1.5b
查看deepseek安装情况
[root@localhost ollama]# ollama ls | grep -aiE deepseek
deepseek-r1:1.5b e0979632db5a 1.1 GB 6 minutes ago
## 运行大模型
ollama run deepseek-r1:1.5b
deepseek嵌套deepseekUI模型(放在.7机器上)
## 安装ftp(文件大于4G)
yum -y install vsftpd
## 安装ftp服务
service vsftpd restart
## 创建用户
useradd adm2
passwd adm2
## 上传UI界面文件(如图)
cd /home/adm2/
## 安装docker
yum -y install docker
## 运行UI
docker run -itd -p 3000:8080 -e HF_ENDPOINT=https://hf-mirror.com -e OLLAMA_BASE_URL=http://172.17.0.1:11434 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:latest
## 重启docker
service docker restart
## 加载UI工具
docker load -i open-webui.tar
windows登录ftp服务
![]()
自定义ollama服务
cat>/etc/systemd/system/ollama.service<