2026-04-15
复习和预习
昨天课堂内容
- 正则表达式
- grep
- sed
课前思考
-
解释:什么是正则表达式?正则表达式有什么作用?
-
如何写匹配有效IPv4地址的正则表达式?
-
如果查看
/var/log/messages中2026-04-14上午九点到下午1点之间段OpenSSH相关日志?
[root@shell ~ 09:58:35]# sed -n '/Apr 14 09:/,/Apr 14 13:/p' /var/log/messages | grep OpenSSH
Apr 14 09:40:03 centos7 systemd: Starting OpenSSH server daemon...
Apr 14 09:40:03 centos7 systemd: Started OpenSSH server daemon.
- 如何过滤出
/etc/profile中有效配置内容?
[root@shell ~ 10:01:48]# egrep -v '^\s*(#|$)' /etc/profile
- 使用
echo {1..10}生成序列后,如何使用sed改写成新序列1+2+..+9+10?
[root@shell ~ 10:03:36]# echo {1..10} | sed 's/ /+/g'
1+2+3+4+5+6+7+8+9+10
- 如何使用sed修改/etc/hosts:在
127.0.0.1开头的行下方添加新行,内容是10.1.8.10 www.wanho.net www?
[root@shell ~ 10:05:26]# sed -i '/^127\.0\.0\.1/a10.1.8.10 www.wanho.net www' /etc/hosts
[root@shell ~ 10:05:36]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
10.1.8.10 www.wanho.net www
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
今天课堂内容
- awk
- ansible 环境准备
- ansible 配置
- ansible adhoc 命令
- playbook 编写和执行
awk
awk 介绍
-
awk 是其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。
-
awk 是一个强大的文本分析工具。
-
awk 更像一门编程语言,他可以自定义变量,有条件语句,有循环,有数组,有正则,有函数等。
-
awk 按行读取数据,根据给出的条件进行查找,并在找出来的行中进行操作。
-
awk 有三种形势,awk,gawk,nawk,平时所说的awk其实就是gawk。
awk 命令
awk 命令格式
awk [options] 'script' file(s)
awk [options] -f scriptfile file(s)
script,定义如何处理数据。file,是 awk 处理的数据来源,awk 也可以来自其它命令的输出。-f scriptfile从脚本文件中读取awk命令,每行都是一个独立的script。
script 格式如下:
BEGIN { action }
pattern { action }
END { action }
脚本通常是被单引号或双引号包住,一个awk脚本通常由四部分组成:
-
BEGIN { action }语句块,awk 执行pattern { action }前要执行的脚本。 -
pattern { action }语句块,决定动作语句何时触发,可以是以下任意一种: - 正则表达式:使用通配符的扩展集。 - 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。 - 模式匹配表达式:使用运算符~(匹配)和~!(不匹配)。 -
END { action }语句块,awk 执行pattern { action }后要执行的脚本。 - action部分,决定对数据如何处理,由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大刮号内。常见的action包括:变量或数组赋值、输出命令、内置函数、控制流语句。
BEGIN { action }、pattern { action }、END { action }都是可选项目。
awk 工作流

- 第一步: 执行
BEGIN { commands }语句块中的语句。
在awk从输入输出流中读取行之前执行,通常在BEGIN语句块中执行如变量初始化,打印输出表头等操作。
- 第二步:从文件或标准输入中读取一行,然后执行
pattern { commands }语句块。它逐行读取数据,从第一行到最后一行重复这个过程,直到读取完所有行。
pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行。
{} 类似一个循环体,会对文件中的每一行进行迭代,通常将变量初始化语句放在BEGIN语句块中,将打印结果等语句放在END语句块中。
- 第三步:当读至输入流末尾时,执行
END { command }语句块。
在awk从输入流中读取完所有的行之后执行,比如打印所有行的分析结果,它也是一个可选语句块。
awk 示例
示例1:获取IP地址是10.1.8.10的网卡名
[root@shell ~ 10:22:06]# ip addr | grep '10.1.8.10'
inet 10.1.8.10/24 brd 10.1.8.255 scope global noprefixroute ens33
[root@shell ~ 10:22:18]# ip addr | grep '10.1.8.10' | awk '{print $NF}'
ens33
# 或者
[root@shell ~ 10:22:24]# ip addr | awk '/10.1.8.10/ {print $NF}'
ens33
示例2:查看使用率超过10的文件系统
[root@shell ~ 10:24:04]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 2.0G 0 2.0G 0% /dev
tmpfs 2.0G 0 2.0G 0% /dev/shm
tmpfs 2.0G 12M 2.0G 1% /run
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/mapper/centos-root 50G 1.6G 49G 4% /
/dev/sda1 1014M 139M 876M 14% /boot
/dev/mapper/centos-home 147G 33M 147G 1% /home
tmpfs 394M 0 394M 0% /run/user/0
tmpfs 394M 0 394M 0% /run/user/1000
[root@shell ~ 10:24:06]# df -h | sed 's/%//' | awk '$5>10 {print $0}'
Filesystem Size Used Avail Use Mounted on
/dev/sda1 1014M 139M 876M 14 /boot
# 或者
[root@shell ~ 10:24:10]# df -h | awk '$5+1-1>10 {print $0}'
/dev/sda1 1014M 139M 876M 14% /boot
示例3:提前系统运行数据。
包括:
- CPU 使用率
- 内存 使用率
- 当前用户登录数
- 当前系统负载
- 系统运行进程数量
[root@shell bin 10:43:30]# pwd
/root/bin
[root@shell bin 10:43:33]# vim monitor_os.sh
#!/bin/bash
os_info_file=/tmp/os_info.txt
top -b -n 1 | head -n 5 > ${os_info_file}
cpu_usage=$(awk '/^%Cpu/ {print $2+$4}' ${os_info_file})
echo "CPU 使用率:${cpu_usage}"
memory_usage=$(awk '/^KiB Mem/ {print ($4-$6)/$4}' ${os_info_file})
echo "内存使用率:${memory_usage}"
user_count=$(awk '/^top/ {print $6}' ${os_info_file})
echo "在线用户数量:${user_count}"
load_usage=$(awk '/^top/ {print $(NF-2),$(NF-1),$NF}' ${os_info_file})
echo "当前系统负载:${load_usage}"
running_process_count=$(awk '/^Tasks/ {print $4}' ${os_info_file})
echo "系统运行进程数量:${running_process_count}"
rm -f ${os_info_file}
思考题
统计一篇文章中出现频率最高的10个单词,并按照数量降序排序。
ansible 环境准备
ansible 架构
- 控制节点:下发指令或文件到受控制节点。
- 受控制节点:接受控制节点发过来的指令,并执行。
ansible 工作原理
ansible 控制节点通过ssh协议将python 模块推送到受控制节点,受控制节点使用python执行python模块进行相应的配置。
ansible 环境准备
实验环境
10.1.8.10 controller.laoma.cloud controller
10.1.8.11 server1.laoma.cloud server1
10.1.8.12 server2.laoma.cloud server2
10.1.8.13 server3.laoma.cloud server3
10.1.8.14 server4.laoma.cloud server4
准备虚拟机模版
准备1台干净的centos 7 虚拟机。
注意:模版虚拟机的CPU和内存的配置,建议设置为1CPU和2G内存。
开发脚本sethost:
- 不加参数执行sethost,则提示命令使用方法。
- 加参数执行sethost,则第一个参数范围是10-14。超出范围也提示命令使用方法。
- 正常执行示例:sethost 10,这设置正确的主机名和IP地址。
[root@shell ~ 13:34:31]# vim /usr/local/bin/sethost
#!/bin/bash
# test root user
(( UID!=0 )) && echo 'Please run as root.' && exit 1
usage="Usage: $0 10-14"
# test args number
(($# !=1 )) && echo $usage && exit 2
# 根据实际情况修改网卡名和域名
con_name=ens33
domain_name=laoma.cloud
host_id=$1
if ((host_id==10));then
HOSTNAME=controller.${domain_name}
elif ((11<=host_id && host_id<=14)) ;then
HOSTNAME=server$[host_id-10].${domain_name}
else
echo $usage
exit 3
fi
hostnamectl set-hostname $HOSTNAME
nmcli connection modify ${con_name} ipv4.addresses 10.1.8.${host_id}/24
nmcli connection up ${con_name}
hostname
ip -br address
[root@shell ~ 13:34:31]# chmod +x /usr/local/bin/sethost
关机打快照,快照名为ansible。
克隆虚拟机
基于模版虚拟机快照ansible,克隆出其他虚拟机,并使用sethost脚本设置主机名和IP地址。
以server1为例:
[root@shell ~ 13:34:31]# sethost 11
配置 ansible 基础环境
在模版虚拟机上配置/etc/hosts,添加ansible主机清单
[root@deploy ~ 14:51:11]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
################# ansible ####################
10.1.8.10 controller.laoma.cloud controller
10.1.8.11 server1.laoma.cloud server1
10.1.8.12 server2.laoma.cloud server2
10.1.8.13 server3.laoma.cloud server3
10.1.8.14 server4.laoma.cloud server4
################# ansible ####################
配置免密登录ansible节点:
[root@deploy ~ 14:52:58]# ssh-keygen -t rsa -N "" -f .ssh/id_rsa
[root@deploy ~ 14:54:39]# \
for host in controller server1 server2 server3 server4
do
sshpass -p123 ssh-copy-id root@$host
done
[root@deploy ~ 14:56:02]#
for host in controller server1 server2 server3 server4
do
ssh root@$host 'hostname;ip -br a show ens33;echo'
done
# 输出内容如下:
controller.laoma.cloud
ens33 UP 10.1.8.10/24 fe80::20c:29ff:fe14:2f8b/64
server1.laoma.cloud
ens33 UP 10.1.8.11/24 fe80::20c:29ff:fe6a:559b/64
server2.laoma.cloud
ens33 UP 10.1.8.12/24 fe80::20c:29ff:feec:ac41/64
server3.laoma.cloud
ens33 UP 10.1.8.13/24 fe80::20c:29ff:fe19:59c1/64
server4.laoma.cloud
ens33 UP 10.1.8.14/24 fe80::20c:29ff:feaa:4763/64
在模版虚拟机上开发脚本weihu,用来集中管理其他机器。
- weihu cmd command,将会在ansible 5台设备上执行command。
- weihu copy src dest,将模版虚拟机上的src文件复制到ansible 5台设备dest位置。
[root@deploy ~ 14:49:42]# vim /usr/local/bin/weihu
#!/bin/bash
function usage () {
echo "Usage: weihu cmd COMMAND, 在集群中所有的机器上执行对应COMMAND命令"
echo "Usage: weihu copy source target,将本地source文件,推送到集群中所有的机器上"
exit
}
action=$1
HOSTLIST='controller server1 server2 server3 server4'
(( $#<=1 )) && usage
case "$action" in
"cmd")
# 删除参数1
shift
COMMAND="$*"
for host in $HOSTLIST
do
ssh root@$host "$COMMAND"
done
;;
"copy")
# 删除参数1
shift
for host in $HOSTLIST
do
num=$#
case $num in
2)
scp -r $1 root@$host:$2
;;
#[3-9]|[1-9][0-9])
[3-9])
last=$(echo $* | awk '{print $NF}')
args_exclude_last=$(echo $* | sed "s#$last##")
scp -r ${args_exclude_last} root@$host:${last}
;;
*)
usage
esac
done
;;
*)
usage
;;
esac
[root@deploy ~ 14:50:49]# chmod +x /usr/local/bin/weihu
测试维护脚本
root@deploy ~ 14:56:09]# weihu cmd hostname
controller.laoma.cloud
server1.laoma.cloud
server2.laoma.cloud
server3.laoma.cloud
server4.laoma.cloud
[root@deploy ~ 14:56:47]# weihu copy /etc/hosts /etc/hosts
hosts 100% 449 222.1KB/s 00:00
hosts 100% 449 245.5KB/s 00:00
hosts 100% 449 275.8KB/s 00:00
hosts 100% 449 351.0KB/s 00:00
hosts 100% 449 317.4KB/s 00:00
[root@deploy ~ 14:57:33]# weihu cmd cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
################# ansible ####################
10.1.8.10 controller.laoma.cloud controller
10.1.8.11 server1.laoma.cloud server1
10.1.8.12 server2.laoma.cloud server2
10.1.8.13 server3.laoma.cloud server3
10.1.8.14 server4.laoma.cloud server4
......
准备一个专用的账户devops,用于控制节点远程登录其他节点。
[root@deploy ~ 15:21:59]# weihu cmd useradd devops
[root@deploy ~ 15:36:17]# weihu cmd id devops
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
[root@deploy ~ 15:36:22]# weihu cmd 'echo 123 | passwd --stdin devops'
[root@deploy ~ 15:37:41]# sshpass -p 123 ssh devops@server1 id
uid=1001(devops) gid=1001(devops) groups=1001(devops)
# 配置devops账户,免密提权为root
[root@deploy ~ 15:39:17]# weihu cmd "echo 'devops ALL=(ALL)NOPASSWD: ALL' > /etc/sudoers.d/devops"
配置控制节点使用devops账户免密登录所有ansible节点。
[root@deploy ~ 15:47:55]# sshpass -p123 ssh devops@controller 'ssh-keygen -t rsa -N "" -f .ssh/id_rsa'
[root@deploy ~ 15:48:30]# sshpass -p123 ssh devops@controller 'for host in controller server1 server2 server3 server4;do sshpass -p123 ssh-copy-id devops@$host;done'
# 验证控制节点免密登录其他节点
[devops@controller ~ 15:50:01]$ for host in controller server1 server2 server3 server4;do ssh devops@$host id;done
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
uid=1001(devops) gid=1001(devops) groups=1001(devops)
[devops@controller ~ 15:50:12]$ for host in controller server1 server2 server3 server4;do ssh devops@$host sudo id;done
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
ansible 配置
部署 ansible软件
控制节点
[devops@controller ~ 16:31:42]$ sudo yum install -y ansible
受管理节点
[root@deploy ~ 16:36:44]# weihu cmd yum install -y python
配置主机清单
ansible管理的主机信息要保存到一个文件中,这个文件称之为主机清单(inventory)。
[devops@controller ~ 16:33:11]$ mkdir ansible
[devops@controller ~ 16:38:45]$ cd ansible/
[devops@controller ansible 16:38:47]$ vim inventory
controller
server1
server2
server3
server4
[devops@controller ansible 16:43:15]$ ansible -i inventory -m command -a 'id' server1
server1 | CHANGED | rc=0 >>
uid=1001(devops) gid=1001(devops) groups=1001(devops)
# 参数说明:
# -i inventory,主机清单位置
# -m command,使用命令模块
# -a 'hostname',模块对应的具体参数
# server1,针对哪个机器操作
[devops@controller ansible 16:43:41]$ ansible -i inventory -m command -a 'id' -b server1
server1 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root)
# -b 提权为root操作
# 使用user模块管理用户
[devops@controller ansible 16:43:00]$ ansible -i inventory -m user -a 'name=zhangsan state=present' -b server1
server1 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1002,
"home": "/home/zhangsan",
"name": "zhangsan",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1002
}
[devops@controller ansible 16:43:46]$ ansible -i inventory -m command -a 'id zhangsan' server1
server1 | CHANGED | rc=0 >>
uid=1002(zhangsan) gid=1002(zhangsan) groups=1002(zhangsan)
# 删除用户
[devops@controller ansible 16:44:33]$ ansible -i inventory -m user -a 'name=zhangsan state=absent remove=yes' -b server1
server1 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"force": false,
"name": "zhangsan",
"remove": true,
"state": "absent"
}
[devops@controller ansible 16:45:49]$ ansible -i inventory -m command -a 'id zhangsan' server1
server1 | FAILED | rc=1 >>
id: zhangsan: no such usernon-zero return code
分组主机清单
[devops@controller ansible 16:45:53]$ vim inventory
[controllers]
controller
[webs]
server1
server2
[dbs]
server3
server4
测试
# 针对webs主机组操作
[devops@controller ansible 16:48:55]$ ansible -i inventory -m command -a 'hostname' -o webs
server2 | CHANGED | rc=0 | (stdout) server2.laoma.cloud
server1 | CHANGED | rc=0 | (stdout) server1.laoma.cloud
# -o 输出内容合并为一行
# all代表所有机器
[devops@controller ansible 16:49:03]$ ansible -i inventory -m command -a 'hostname' -o all
server2 | CHANGED | rc=0 | (stdout) server2.laoma.cloud
server1 | CHANGED | rc=0 | (stdout) server1.laoma.cloud
controller | CHANGED | rc=0 | (stdout) controller.laoma.cloud
server3 | CHANGED | rc=0 | (stdout) server3.laoma.cloud
server4 | CHANGED | rc=0 | (stdout) server4.laoma.cloud
# web主机组安装nginx
[devops@controller ansible 16:51:41]$ ansible -i inventory -m yum -a 'name=nginx state=present' -b webs
# web主机组卸载nginx
[devops@controller ansible 16:51:41]$ ansible -i inventory -m yum -a 'name=nginx state=absent' -b webs
查看模块帮助
# 查看模块清单,并过滤出yum相关信息
[devops@controller ansible 16:53:41]$ ansible-doc -l | grep yum
yum Manages packages with the `yum' package manager
yum_repository Add or remove YUM repositories
# 查看yum模块帮助信息
[devops@controller ansible 16:53:47]$ ansible-doc yum
# 直接搜索EXAMPLE
# 部分内容如下:
EXAMPLES:
- name: install the latest version of Apache
yum:
name: httpd
state: latest
- name: ensure a list of packages installed
yum:
name: "{{ packages }}"
vars:
packages:
- httpd
- httpd-tools
- name: remove the Apache package
yum:
name: httpd
state: absent
- name: install the latest version of Apache from the testing repo
yum:
name: httpd
enablerepo: testing
state: present
...
ansible最大的特点:
- 简单:只要能看懂English就行。
- 幂等性:多次执行,结果一致。假设第一次执行软件包未安装,则执行安装。第二次执行,则不需要做任何事情。
playbook 编写和执行
通过编写playbook,完成重复、复杂的任务。
-
ansible 命令类似于 shell 中单个命令。
-
playbook 类似于 shell 脚本。
[devops@controller ansible 16:55:36]$ vim deploy_web.yaml
# yaml格式起始行,一般不省略
---
# Playbook中第一个play
# play具有属性:name,hosts,become,tasks,缩进一致
# name属性,用于简要描述play
- name: debploy WebSite
# hosts属性,用于定义要在哪个受管理节点执行
hosts: webs
# tasks属性,用于描述play中任务,属性是列表格式
tasks:
# 第一个任务
# 任务具有属性:涵name和模块名等。
# name属性,用于简要描述任务
- name: latest version of httpd and firewalld installed
# 指明模块名,也就是要执行的任务
yum:
# 指定要操作的rpm包名称
name:
# rpm包名称是-开头的列表格式,或者逗号分隔的列表格式
- httpd
- firewalld
# 定义软件包的状态,lastet代表升级为最新版本
state: latest
# 第二个任务
- name: prepare index.html
# copy 模块,用于将content属性值写入到目标文件
copy:
content: "Welcome to {{ ansible_fqdn }} WebSite!\n"
dest: /var/www/html/index.html
# 第三个任务
- name: enable and start httpd
# service模块,用于启用并启动httpd服务
service:
name: httpd
enabled: true
state: started
# 第四个任务
- name: enable and start firewalld
# service模块,用于启用并启动firewalld服务
service:
name: firewalld
enabled: true
state: started
# 第五个任务
- name: firewalld permits access to httpd service
# firewalld,用于放行http服务
firewalld:
service: http
permanent: true
state: enabled
immediate: yes
# Playbook中第二个play,-开头表示列表
- name: Test WebSite
hosts: localhost
become: no
tasks:
- name: connect to intranet web server
# uri模块,用于测试网站是否可以访问
uri:
url: http://{{item}}
loop:
- server1
- server2
# yaml格式结束行,一般省略
...
# 执行剧本
[devops@controller ansible 17:01:40]$ ansible-playbook -i inventory -b deploy_web.yaml