📑 目录

2026-04-15

复习和预习

昨天课堂内容

  1. 正则表达式
  2. grep
  3. sed

课前思考

  1. 解释:什么是正则表达式?正则表达式有什么作用?

  2. 如何写匹配有效IPv4地址的正则表达式?

  3. 如果查看 /var/log/messages2026-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.
  1. 如何过滤出 /etc/profile 中有效配置内容?
[root@shell ~ 10:01:48]# egrep -v '^\s*(#|$)' /etc/profile
  1. 使用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
  1. 如何使用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

今天课堂内容

  1. awk
  2. ansible 环境准备
  3. ansible 配置
  4. ansible adhoc 命令
  5. playbook 编写和执行

awk

awk 介绍

awk 命令

awk 命令格式

awk [options] 'script' file(s) 
awk [options] -f scriptfile file(s) 
  1. script,定义如何处理数据。
  2. file,是 awk 处理的数据来源,awk 也可以来自其它命令的输出。
  3. -f scriptfile 从脚本文件中读取awk命令,每行都是一个独立的script

script 格式如下:

BEGIN { action }
pattern { action }
END { action }

脚本通常是被单引号或双引号包住,一个awk脚本通常由四部分组成:

  1. BEGIN { action } 语句块,awk 执行 pattern { action } 前要执行的脚本。

  2. pattern { action } 语句块,决定动作语句何时触发,可以是以下任意一种: - 正则表达式:使用通配符的扩展集。 - 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。 - 模式匹配表达式:使用运算符~(匹配)和 ~!(不匹配)。

  3. END { action } 语句块,awk 执行 pattern { action } 后要执行的脚本。

  4. action部分,决定对数据如何处理,由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大刮号内。常见的action包括:变量或数组赋值、输出命令、内置函数、控制流语句。

BEGIN { action }pattern { action }END { action }都是可选项目。

awk 工作流

image-20230402163532397

在awk从输入输出流中读取行之前执行,通常在BEGIN语句块中执行如变量初始化,打印输出表头等操作。

pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行。

{} 类似一个循环体,会对文件中的每一行进行迭代,通常将变量初始化语句放在BEGIN语句块中,将打印结果等语句放在END语句块中。

在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:提前系统运行数据。

包括:

[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:

[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,用来集中管理其他机器。

[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最大的特点:

  1. 简单:只要能看懂English就行。
  2. 幂等性:多次执行,结果一致。假设第一次执行软件包未安装,则执行安装。第二次执行,则不需要做任何事情。

playbook 编写和执行

通过编写playbook,完成重复、复杂的任务。

[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