RHCE8

shell语句

case判断语句

#!/bin.bash
cat <<EOF
############################################
#                                          #
# welcome everybody            #
#                                          #
############################################
EOF

read -p "dvd is connceted ?(y/n)" ans

case $ans in 
    y|Y)
        ;;
    n|N)
        echo "please connect!"
        exit 1 ;;
    *)
        echo "only y or n"
        exit 1 ;;
esac

统计文件行数

#!/bin/bash
# 必须输入一个文件
if [ $# -eq 0 ];then
    echo "must insert a file."
    exit 1
fi

# 判断输入的文件是否存在或者是不是一个文件类型
if [ ! -f $! ]; then
    echo "$1 is not a file or not exist."
    exit 1
fi

# 统计输入的文件行数
declare -i n=0
while read aa ;
do
    let n=$n+1
done < $1

# 输出统计信息
echo "$n $1"

计划任务

at计划任务

是一次性的,不会重复性的执行

# 设置好计划任务后,按 Ctrl+D 进行提交
[zhuchun@server1 ~]$ at 10:00 2022-12-03
warning: commands will be executed using /bin/sh
at> /home/zhuchun/c.sh 
at> <EOT>
job 1 at Sat Dec  3 10:00:00 2022
[zhuchun@server1 ~]$ date
2022年 12月 02日 星期五 03:10:30 PST
# 不加具体时间的话,默认取未来日期的当前的时
[zhuchun@server1 ~]$ at 2022-12-3
warning: commands will be executed using /bin/sh
at> /home/zhuchun/c.sh
at> <EOT>
job 4 at Sat Dec  3 03:10:00 2022

# 三天后的上午10点执行
[zhuchun@server1 ~]$ at 10am+3days
warning: commands will be executed using /bin/sh
at> /home/zhuchun/c.sh
at> <EOT>
job 7 at Mon Dec  5 10:00:00 2022

[zhuchun@server1 ~]$ atq
1   Sat Dec  3 10:00:00 2022 a zhuchun
4   Sat Dec  3 03:10:00 2022 a zhuchun
7   Mon Dec  5 10:00:00 2022 a zhuchun
[zhuchun@server1 ~]$ at -l
1   Sat Dec  3 10:00:00 2022 a zhuchun
4   Sat Dec  3 03:10:00 2022 a zhuchun
7   Mon Dec  5 10:00:00 2022 a zhuchun
# 查看某个任务的具体内容
[zhuchun@server1 ~]$ at -c 7
[root@server1 ~]# ll /var/spool/at/
总用量 24
-rwx------. 1 zhuchun zhuchun 4505 12月  2 02:15 a0000101a8b9d8
-rwx------. 1 zhuchun zhuchun 4505 12月  2 03:10 a0000401a8b83e
-rwx------. 1 zhuchun zhuchun 4505 12月  2 03:14 a0000701a8c518
drwx------. 2 root    root       6 8月  12 2018 spool
# 查看某个任务的具体内容
[root@server1 ~]# cat /var/spool/at/a0000101a8b9d8
# 删掉7号任务,jobid是7
[root@server1 ~]# atrm 7
[root@server1 ~]# at -d 7
# 此文件配置哪些用户没有配置计划任务的权限
[root@server1 ~]# cat /etc/at.deny 
zhuchun
# 此文件配置哪些用户可以有配置计划任务的权限
[root@server1 ~]# cat /etc/at.allow 
zhuchun
# 如果同一个用户 deny 和 allow 都配置了,则拥有 allow 权限

crontab 计划任务

是周期性的,循环执行的

# 查看当前用户的计划任务
[root@server1 ~]# crontab -l
no crontab for root

# 查询 zhuchun 用户的计划任务
[root@server1 ~]# crontab -l -u zhuchun
no crontab for zhuchun

# 编辑当前用户的计划任务
[root@server1 ~]# crontab -e
# 在crontab -e 编辑器中,凡是以井号开头的都是注释信息
# 分    时      天      月      周      命令
# 中间用空格或者 tab 都可以
# 分:几点积分
# 时:几点
# 天:几号
# 月:几月份
# 周:星期几
# *:代表 每

# 每天早晨七点整
0       7       *       *       *       /home/zhuchun/c.sh
# 每周一至周五 早晨7点整执行
0       7       *       *       1-5     /home/zhuchun/c.sh
0       7       *       *       1-5/1   /home/zhuchun/c.sh
# 每周一、三、五执行
0       7       *       *       1,3,5   /home/zhuchun/c.sh
0       7       *       *       1-5/2   /home/zhuchun/c.sh
# 每周一到五,七点0分5分10分执行
0,5,10  7       *       *       1-5     /home/zhuchun/c.sh
0-10/5  7       *       *       1-5     /home/zhuchun/c.sh
# 一季度的每周一到五,七点0/5/10分执行
0-10/5  7       *       1-3     1-5     /home/zhuchun/c.sh
# 一季度上旬7点整执行
0       7       1-10    1-3     *       /home/zhuchun/c.sh
# 一季度上旬的周一至周五7点整执行
0       7       1-10    1-3     1-5     /home/zhuchun/c.sh

[root@server1 ~]# cat /var/spool/cron/root
# 删除计划任务
[root@server1 ~]# crontab -r
# 不给zhuchun用户配置 crontab 的权限
[root@server1 ~]# cat /etc/cron.deny
zhuchun
# 给zhuchun用户配置 crontab 的权限,allow优先
[root@server1 ~]# cat /etc/cron.allow 
zhuchun
# 脚本放/etc/cron.daily里,每天都会执行一次
# /etc/cron.hourly,每小时都会执行一次

[root@server1 ~]# ll /etc/cron*
-rw-r--r--. 1 root root   8 12月  2 03:46 /etc/cron.allow
-rw-r--r--. 1 root root   8 12月  2 03:45 /etc/cron.deny
-rw-r--r--. 1 root root 451 1月   8 2021 /etc/crontab

/etc/cron.d:
总用量 8
-rw-r--r--. 1 root root 128 6月  12 2019 0hourly
-rw-r--r--. 1 root root 108 1月  11 2021 raid-check

/etc/cron.daily:
总用量 4
-rwxr-xr-x. 1 root root 189 1月   4 2018 logrotate

/etc/cron.hourly:
总用量 4
-rwxr-xr-x. 1 root root 575 6月  12 2019 0anacron

/etc/cron.monthly:
总用量 0

/etc/cron.weekly:
总用量 0
[root@server1 ~]# 
# 配置一个定期清理临时文件的定时任务
[root@server1 ~]# systemd-tmpfiles --clean
# systemd-tmpfiles 会读取规则文件,规则文件路径在 /usr/lib/tmpfiles.d/、/run/tmpfiles.d/
# 管理员想自己定义的话,在 /etc/tmpfiles.d/xxx.conf 文件中定义

[root@server1 tmpfiles.d]# cat aa.conf 
# /tmp/aa 这个目录如不存在,则给创建出来,所属用户所属组均为 root。权限维 0755 ,/tmp/aa 目录下文件 10s 清理一次。
d   /tmp/aa     0755    root    root    10s

# 读取临时文件规则,执行创建,在 /tmp/aa 目录会生成,或者制定规则配置
[root@server1 aa]# systemctl start systemd-tmpfiles-clean
[root@server1 tmpfiles.d]# systemd-tmpfiles --create
[root@server1 tmpfiles.d]# systemd-tmpfiles --create aa.conf

[root@server1 aa]# cat /usr/lib/systemd/system/systemd-tmpfiles-clean.timer

[Unit]
Description=Daily Cleanup of Temporary Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)

[Timer]
OnBootSec=15min
OnUnitActiveSec=1d

使用 tuned 优化系统

优化的思想

三层优化
1、硬件升级
2、OS优化

  • 内核参数
  • 硬件参数
    3、app的优化

tuned

[root@server1 tmpfiles.d]# systemctl status tuned
● tuned.service - Dynamic System Tuning Daemon
   Loaded: loaded (/usr/lib/systemd/system/tuned.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2022-12-01 16:50:47 PST; 11h ago
     Docs: man:tuned(8)
           man:tuned.conf(5)
           man:tuned-adm(8)
 Main PID: 1041 (tuned)
    Tasks: 4 (limit: 11094)
   Memory: 14.6M
   CGroup: /system.slice/tuned.service
           └─1041 /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P

12月 01 16:50:46 server1.rhce.cc systemd[1]: Starting Dynamic System Tuning Daemon...
12月 01 16:50:47 server1.rhce.cc systemd[1]: Started Dynamic System Tuning Daemon.
# tuned 提供了以下解决方案,当前使用的是 virtual-guest 方案
[root@server1 ~]# tuned-adm list 
Available profiles:
- accelerator-performance     - Throughput performance based tuning with disabled higher latency STOP states
- balanced                    - General non-specialized tuned profile
- desktop                     - Optimize for the desktop use-case
- hpc-compute                 - Optimize for HPC compute workloads
- intel-sst                   - Configure for Intel Speed Select Base Frequency
- latency-performance         - Optimize for deterministic performance at the cost of increased power consumption
- network-latency             - Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance
- network-throughput          - Optimize for streaming network throughput, generally only necessary on older CPUs or 40G+ networks
- optimize-serial-console     - Optimize for serial console use.
- powersave                   - Optimize for low power consumption
- throughput-performance      - Broadly applicable tuning that provides excellent performance across a variety of common server workloads
- virtual-guest               - Optimize for running inside a virtual guest
- virtual-host                - Optimize for running KVM guests
Current active profile: virtual-guest

# 还可以安装以下的一些方案
[root@server1 ~]# yum list tuned*
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.

上次元数据过期检查:0:29:34 前,执行于 2022年12月02日 星期五 04时05分55秒。
已安装的软件包
tuned.noarch                                                                                                                                 2.15.0-2.el8                                                                                                            @anaconda
可安装的软件包
tuned.noarch                                                                                                                                 2.15.0-2.el8_4.1                                                                                                        base     
tuned-gtk.noarch                                                                                                                             2.15.0-2.el8_4.1                                                                                                        appstream
tuned-profiles-atomic.noarch                                                                                                                 2.15.0-2.el8_4.1                                                                                                        base     
tuned-profiles-compat.noarch                                                                                                                 2.15.0-2.el8_4.1                                                                                                        base     
tuned-profiles-cpu-partitioning.noarch                                                                                                       2.15.0-2.el8_4.1                                                                                                        base     
tuned-profiles-mssql.noarch                                                                                                                  2.15.0-2.el8_4.1                                                                                                        base     
tuned-profiles-oracle.noarch                                                                                                                 2.15.0-2.el8_4.1                                                                                                        base     
tuned-utils.noarch                                                                                                                           2.15.0-2.el8_4.1                                                                                                        appstream
tuned-utils-systemtap.noarch                                                                                                                 2.15.0-2.el8_4.1                                                                                                        appstream

配置文件在 /usr/lib/tuned 路径下

[root@server1 ~]# cd /usr/lib/tuned/
[root@server1 tuned]# pwd
/usr/lib/tuned
[root@server1 tuned]# ll
总用量 16
drwxr-xr-x. 2 root root    24 11月  9 01:19 accelerator-performance
drwxr-xr-x. 2 root root    24 11月  9 01:19 balanced
drwxr-xr-x. 2 root root    24 11月  9 01:19 desktop
-rw-r--r--. 1 root root 14833 12月 17 2020 functions
drwxr-xr-x. 2 root root    24 11月  9 01:19 hpc-compute
drwxr-xr-x. 2 root root    24 11月  9 01:19 intel-sst
drwxr-xr-x. 2 root root    24 11月  9 01:19 latency-performance
drwxr-xr-x. 2 root root    24 11月  9 01:19 network-latency
drwxr-xr-x. 2 root root    24 11月  9 01:19 network-throughput
drwxr-xr-x. 2 root root    24 11月  9 01:19 optimize-serial-console
drwxr-xr-x. 2 root root    41 11月  9 01:19 powersave
drwxr-xr-x. 2 root root    27 11月  9 01:19 recommend.d
drwxr-xr-x. 2 root root    24 11月  9 01:19 throughput-performance
drwxr-xr-x. 2 root root    24 11月  9 01:19 virtual-guest
drwxr-xr-x. 2 root root    24 11月  9 01:19 virtual-host
[root@server1 tuned]# cat virtual-guest/tuned.conf 
[main]
summary=Optimize for running inside a virtual guest
include=throughput-performance

[sysctl]
# If a workload mostly uses anonymous memory and it hits this limit, the entire
# working set is buffered for I/O, and any more write buffering would require
# swapping, so it's time to throttle writes until I/O can catch up.  Workloads
# that mostly use file mappings may be able to use even higher values.
#
# The generator of dirty data starts writeback at this percentage (system default
# is 20%)
vm.dirty_ratio = 30

# Filesystem I/O is usually much more efficient than swapping, so try to keep
# swapping low.  It's usually safe to go even lower than this on systems with
# server-grade storage.
vm.swappiness = 30
[root@server1 tuned]# 

切换模式

[root@server1 ~]# tuned-adm list
Available profiles:
- accelerator-performance     - Throughput performance based tuning with disabled higher latency STOP states
- balanced                    - General non-specialized tuned profile
- desktop                     - Optimize for the desktop use-case
- hpc-compute                 - Optimize for HPC compute workloads
- intel-sst                   - Configure for Intel Speed Select Base Frequency
- latency-performance         - Optimize for deterministic performance at the cost of increased power consumption
- network-latency             - Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance
- network-throughput          - Optimize for streaming network throughput, generally only necessary on older CPUs or 40G+ networks
- optimize-serial-console     - Optimize for serial console use.
- powersave                   - Optimize for low power consumption
- rhel134                     - Optimize for running inside a virtual guest
- throughput-performance      - Broadly applicable tuning that provides excellent performance across a variety of common server workloads
- virtual-guest               - Optimize for running inside a virtual guest
- virtual-host                - Optimize for running KVM guests
Current active profile: virtual-guest
# 执行下面命令进行切换
[root@server1 ~]# tuned-adm profile rhel134

查看建议模式

[root@server1 tuned]# tuned-adm recommend 
virtual-guest
[root@server1 tuned]#

ACL访问控制列表

ACL基本用法

获取文件的访问控制列表

[root@server1 opt]# ll 1.txt 
-rw-r--r--. 1 root root 5 12月  2 04:56 1.txt
[root@server1 opt]# getfacl -c 1.txt 
user::rw-
group::r--
other::r--

[root@server1 opt]#

设置 ACL

# 给用户设权限
# zhuchun 用户就可以向 root 用户的 /opt/1.txt 文件写内容了
[root@server1 opt]# setfacl -m u:zhuchun:rw- 1.txt 
[root@server1 opt]# getfacl -c 1.txt 
user::rw-
user:zhuchun:rw-
group::r--
mask::rw-
other::r--

# 给组设权限后,再将一个已登录用户添加到此权限组,此用户必须重新登录才能拥有组的acl权限
[root@server1 opt]# setfacl -m g:zhuchun:rw- 1.txt 
[root@server1 opt]# getfacl -c 1.txt 
user::rw-
group::r--
group:zhuchun:rw-
mask::rw-
other::r--

取消ACL

[root@server1 opt]# setfacl -x u:zhuchun 1.txt 
[root@server1 opt]# getfacl -c 1.txt 
user::rw-
group::r--
mask::r--
other::r--

[root@server1 opt]#

mask权限

[root@server1 opt]# getfacl -c 1.txt

user::rw- ACL_USER_OBJ 文件的所有者
user:zhuchun:rw- ACL_USER 通过acl授权的用户
group::r-- ACL_GROUP_OBJ 文件所属组
group:zhuchun:rw- ACL_GROUP 通过acl授权的组
mask::r-- ACL_MASK ACL_USER,ACL_GROUP_OBJ,ACL_GROUP的最大权限
other::r--

如果没有手动配置 ACL_MASK 的权限,则 ACL_MASK 的权限会随着 ACL_USER,ACL_GROUP_OBJ,ACL_GROUP 的变化而变化,始终是这几个权限的最大值。
如果 mask 设置的是 ---,则用户使用的权限是 other 的权限;如果不是 ---,则用户使用的是 acl 的权限

总结:如果没有手动设置 acl 的 mask 权限,则对用户/组设置的 acl 权限生效,不用考虑 o 的权限。如果手动设置了 mask 的权限,则用户最终的权限是 acl 最终权限 +other 权限的集合。

给目录设置 ACL 权限

[root@server1 opt]# ll -d aa
drwxr-xr-x. 2 root root 6 12月  2 05:33 aa
[root@server1 opt]# getfacl -c aa
user::rwx
group::r-x
other::r-x

# 此命令针对的是 aa 目录中新建的文件
[root@server1 opt]# setfacl -m d:u:zhuchun:rwx aa
# 取消 acl 权限
[root@server1 opt]# setfacl -x d:u:zhuchun aa

SELinux

什么是 selinux

selinux全称是 Security-Enhanced Linux,目的是提高系统的安全性。当我们执行某个操作的时候,如果 selinux 认为此操作有危险,则会拒绝进一步的访问。在 Ubuntu 中叫 apparmor。

了解 selinux 的上下文

在开启了 selinux 的情况下,selinux 会为每个文件,每个进程都分配一个标签,这个标签称之为上下文。

假设一个进程 xx 的标签是 aa-p,它能访问标签为 aa-f 的文件,所以 xx 进程访问文件 aa 是没问题的,因为标签匹配。但是 xx 进程访问文件 bb 的时候却是访问不了的,因为文件 bb 的标签是 bb-f。

查看上下文

# 加上 -Z 查看,usr_t 是此文件的上下文
[root@server1 opt]# ll -dZ aa
drwxr-xr-x+ 2 root root unconfined_u:object_r:usr_t:s0 20 12月  2 05:36 aa
# httpd_t 是对应进程的上下文,特定上下文的进程只能访问特定上下文的文件
[root@server1 opt]# ps -efZ |grep httpd |grep -v grep
system_u:system_r:httpd_t:s0    root       12973       1  0 05:54 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0    apache     12976   12973  0 05:54 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0    apache     12977   12973  0 05:54 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0    apache     12978   12973  0 05:54 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0    apache     12979   12973  0 05:54 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

改变上下文

[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 6 12月  4 04:14 /www
# 改上下文,-R是递归目录
[root@server1 ~]# chcon -R -t httpd_sys_content_t /www
[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:httpd_sys_content_t:s0 6 12月  4 04:14 /www

还原上下文为默认值

[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:httpd_sys_content_t:s0 6 12月  4 04:14 /www
[root@server1 ~]# restorecon -R /www
[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 6 12月  4 04:14 /www

修改默认上下文

[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 6 12月  4 04:14 /www
# fcontext是设置文件的上下文,-a 是添加
[root@server1 ~]# semanage fcontext -a -t httpd_sys_content_t "/www(/.*)?"
[root@server1 ~]# restorecon -R /www
[root@server1 ~]# ls -ldZ /www
drwxr-xr-x. 2 root root unconfined_u:object_r:httpd_sys_content_t:s0 6 12月  4 04:14 /www
# 如果已定义过的,你再定义会报错。
[root@server1 ~]# semanage fcontext -a -t httpd_sys_content_t "/www(/.*)?"
ValueError: /www(/.*)? 的文件上下文已定义
# 删除已定义过的默认上下文
[root@server1 ~]# semanage fcontext -d -t httpd_sys_content_t "/www(/.*)?"
# 再添加默认上下文
[root@server1 ~]# semanage fcontext -a -t httpd_sys_content_t "/www(/.*)?"
# 直接更改
[root@server1 ~]# semanage fcontext -m -t httpd_sys_content_t "/www(/.*)?"

了解 selinux 的端口上下文

查看端口上下文

[root@server1 ~]# semanage port -l |grep '\b80\b'
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

给端口添加上下文

[root@server1 ~]# semanage port -a -t http_port_t -p tcp 808
[root@server1 ~]# semanage port -l |grep '\b808\b'
http_port_t                    tcp      808, 80, 81, 443, 488, 8008, 8009, 8443, 9000

取消端口上下文

[root@server1 ~]# semanage port -d -t http_port_t -p tcp 808

selinux的两种模式

查看模式

[root@server1 ~]# getenforce
Enforcing

Enforcing模式

如果不满足selinux条件的话,则selinux会阻止访问服务并提供警报

临时设置Enforcing模式

[root@server1 ~]# setenforce Enforcing
[root@server1 ~]# getenforce
Enforcing
[root@server1 ~]# setenforce 1
[root@server1 ~]# getenforce
Enforcing

Permissive

如果不满足selinux条件,则selinux不阻止访问,但是会警报

临时设置Permissive模式

[root@server1 ~]# setenforce 0
[root@server1 ~]# getenforce
Permissive
[root@server1 ~]# setenforce Permissive
[root@server1 ~]# getenforce
Permissive

永久设置模式

随便修改下面两个文件中的某一个,实际是同一个文件做的软连接

[root@server1 ~]# ll /etc/sysconfig/selinux 
lrwxrwxrwx. 1 root root 17 11月  9 01:17 /etc/sysconfig/selinux -> ../selinux/config
[root@server1 ~]# ll /etc/selinux/config 
-rw-r--r--. 1 root root 548 11月  9 01:17 /etc/selinux/config
# config 文件内容
[root@server1 ~]# cat /etc/selinux/config 

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
# 修改下面的值就可设置 selinux 的模式,enforcing、permissive这两个模式可以在线切换,但是这两个和 disabled 不能在线切换
SELINUX=enforcing
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

[root@server1 ~]# 

selinux的布尔值

理解为一个功能开关

获取布尔值

# 获取系统中所有的布尔值
[root@server1 ~]# getsebool -a

# 过滤出 ftp 相关的布尔值
[root@server1 ~]# getsebool -a |grep ftp
ftpd_anon_write --> off
ftpd_connect_all_unreserved --> off
ftpd_connect_db --> off
ftpd_full_access --> off
ftpd_use_cifs --> off
ftpd_use_fusefs --> off
ftpd_use_nfs --> off
ftpd_use_passive_mode --> off
httpd_can_connect_ftp --> off
httpd_enable_ftp_server --> off
tftp_anon_write --> off
tftp_home_dir --> off
[root@server1 ~]# getsebool -a |grep ftpd_full_access
ftpd_full_access --> off

设置布尔值(临时生效,重启后失效)

# 设置某个服务的selinux 的布尔值
[root@server1 ~]# setsebool ftpd_full_access on
[root@server1 ~]# setsebool ftpd_full_access 1
[root@server1 ~]# setsebool ftpd_full_access off
[root@server1 ~]# setsebool ftpd_full_access 0

永久设置布尔值,加上(-P)选项

[root@server1 ~]# setsebool -P ftpd_full_access on
[root@server1 ~]# setsebool -P ftpd_full_access 1
[root@server1 ~]# setsebool -P ftpd_full_access off
[root@server1 ~]# setsebool -P ftpd_full_access 0

分区管理

了解分区概念

一块硬盘在进行格式化的时候,会让你选择是用 MBR 格式还是 GPT 格式
MBR:主引导记录,主引导扇区
GPT:Guid partition table
MBR兼容性较好,不能用于大于2T的硬盘,GPT兼容性差一些,但是可以用于大于2T的硬盘,支持到128个主分区。

主分区:直接从硬盘上划分并可以直接格式化使用的分区
扩展分区-直接从硬盘上划分,但是不直接使用的分区,需要在其上面划分更多个小分区
逻辑分区-在扩展分区上划分的分区

划分分区

查看所有硬盘的分区信息

[root@server1 ~]# fdisk -l
Disk /dev/sda:100 GiB,107374182400 字节,209715200 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x16dc60ad

设备       启动      起点      末尾      扇区  大小 Id 类型
/dev/sda1  *         2048 125831167 125829120   60G 83 Linux
/dev/sda2       125831168 146802687  20971520   10G 83 Linux
/dev/sda3       146802688 150996991   4194304    2G 82 Linux swap / Solaris
/dev/sda4       150996992 154904575   3907584  1.9G 83 Linux

Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

查看某个硬盘的分区信息

[root@server1 ~]# fdisk -l /dev/sda
Disk /dev/sda:100 GiB,107374182400 字节,209715200 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x16dc60ad

设备       启动      起点      末尾      扇区  大小 Id 类型
/dev/sda1  *         2048 125831167 125829120   60G 83 Linux
/dev/sda2       125831168 146802687  20971520   10G 83 Linux
/dev/sda3       146802688 150996991   4194304    2G 82 Linux swap / Solaris
/dev/sda4       150996992 154904575   3907584  1.9G 83 Linux

创建分区

[root@server1 ~]# fdisk /dev/sdb

欢迎使用 fdisk (util-linux 2.32.1)。
更改将停留在内存中,直到您决定将更改写入磁盘。
使用写入命令前请三思。

命令(输入 m 获取帮助):m     # 输入m查看帮助文档

帮助:

  DOS (MBR)
   a   开关 可启动 标志
   b   编辑嵌套的 BSD 磁盘标签
   c   开关 dos 兼容性标志

  常规
   d   删除分区
   F   列出未分区的空闲区
   l   列出已知分区类型
   n   添加新分区
   p   打印分区表
   t   更改分区类型
   v   检查分区表
   i   打印某个分区的相关信息

  杂项
   m   打印此菜单
   u   更改 显示/记录 单位
   x   更多功能(仅限专业人员)

  脚本
   I   从 sfdisk 脚本文件加载磁盘布局
   O   将磁盘布局转储为 sfdisk 脚本文件

  保存并退出
   w   将分区表写入磁盘并退出
   q   退出而不保存更改

  新建空磁盘标签
   g   新建一份 GPT 分区表
   G   新建一份空 GPT (IRIX) 分区表
   o   新建一份的空 DOS 分区表
   s   新建一份空 Sun 分区表

命令(输入 m 获取帮助):p     # 输入 p 打印分区表
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

命令(输入 m 获取帮助):n     # 输入n创建新分区
分区类型
   p   主分区 (0个主分区,0个扩展分区,4空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p):p     # 类型为主分区
分区号 (1-4, 默认  1):       # 默认1
第一个扇区 (2048-41943039, 默认 2048):         # 默认从 2048个扇区开始
上个扇区,+sectors 或 +size{K,M,G,T,P} (2048-41943039, 默认 41943039): 4196351      # 创建2GB = 2*2048*2048*2 -1

创建了一个新分区 1,类型为“Linux”,大小为 2 GiB。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动  起点    末尾    扇区 大小 Id 类型
/dev/sdb1        2048 4196351 4194304   2G 83 Linux

命令(输入 m 获取帮助):d     # 输入d删除分区,因为只有一个分区,所以默认就删了此分区1
已选择分区 1
分区 1 已删除。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

命令(输入 m 获取帮助):n
分区类型
   p   主分区 (0个主分区,0个扩展分区,4空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p):

将使用默认回应 p。
分区号 (1-4, 默认  1): 
第一个扇区 (2048-41943039, 默认 2048): 
上个扇区,+sectors 或 +size{K,M,G,T,P} (2048-41943039, 默认 41943039): +2G      # 输入 +2G 表示分区大小为2GB

创建了一个新分区 1,类型为“Linux”,大小为 2 GiB。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动  起点    末尾    扇区 大小 Id 类型
/dev/sdb1        2048 4196351 4194304   2G 83 Linux

命令(输入 m 获取帮助):n
分区类型
   p   主分区 (1个主分区,0个扩展分区,3空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p):p
分区号 (2-4, 默认  2): 
第一个扇区 (4196352-41943039, 默认 4196352): 
上个扇区,+sectors 或 +size{K,M,G,T,P} (4196352-41943039, 默认 41943039): +2G

创建了一个新分区 2,类型为“Linux”,大小为 2 GiB。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动    起点    末尾    扇区 大小 Id 类型
/dev/sdb1          2048 4196351 4194304   2G 83 Linux
/dev/sdb2       4196352 8390655 4194304   2G 83 Linux

命令(输入 m 获取帮助):n
分区类型
   p   主分区 (2个主分区,0个扩展分区,2空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p):e     # 新建的类型是扩展分区,只有建了扩展分区才能建逻辑分区
分区号 (3,4, 默认  3): 
第一个扇区 (8390656-41943039, 默认 8390656): 
上个扇区,+sectors 或 +size{K,M,G,T,P} (8390656-41943039, 默认 41943039): 

创建了一个新分区 3,类型为“Extended”,大小为 16 GiB。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动    起点     末尾     扇区 大小 Id 类型
/dev/sdb1          2048  4196351  4194304   2G 83 Linux
/dev/sdb2       4196352  8390655  4194304   2G 83 Linux
/dev/sdb3       8390656 41943039 33552384  16G  5 扩展

命令(输入 m 获取帮助):n
所有主分区的空间都在使用中。
添加逻辑分区 5
第一个扇区 (8392704-41943039, 默认 8392704): 
上个扇区,+sectors 或 +size{K,M,G,T,P} (8392704-41943039, 默认 41943039): +2G

创建了一个新分区 5,类型为“Linux”,大小为 2 GiB。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动    起点     末尾     扇区 大小 Id 类型
/dev/sdb1          2048  4196351  4194304   2G 83 Linux
/dev/sdb2       4196352  8390655  4194304   2G 83 Linux
/dev/sdb3       8390656 41943039 33552384  16G  5 扩展
/dev/sdb5       8392704 12587007  4194304   2G 83 Linux

命令(输入 m 获取帮助):l     # 输入 l 可以查看分区的类型,及其对应的 id

 0  空              24  NEC DOS         81  Minix / 旧 Linu bf  Solaris        
 1  FAT12           27  隐藏的 NTFS Win 82  Linux swap / So c1  DRDOS/sec (FAT-
 2  XENIX root      39  Plan 9          83  Linux           c4  DRDOS/sec (FAT-
 3  XENIX usr       3c  PartitionMagic  84  OS/2 隐藏 或 In c6  DRDOS/sec (FAT-
 4  FAT16 <32M      40  Venix 80286     85  Linux 扩展      c7  Syrinx         
 5  扩展            41  PPC PReP Boot   86  NTFS 卷集       da  非文件系统数据 
 6  FAT16           42  SFS             87  NTFS 卷集       db  CP/M / CTOS / .
 7  HPFS/NTFS/exFAT 4d  QNX4.x          88  Linux 纯文本    de  Dell 工具      
 8  AIX             4e  QNX4.x 第2部分  8e  Linux LVM       df  BootIt         
 9  AIX 可启动      4f  QNX4.x 第3部分  93  Amoeba          e1  DOS 访问       
 a  OS/2 启动管理器 50  OnTrack DM      94  Amoeba BBT      e3  DOS R/O        
 b  W95 FAT32       51  OnTrack DM6 Aux 9f  BSD/OS          e4  SpeedStor      
 c  W95 FAT32 (LBA) 52  CP/M            a0  IBM Thinkpad 休 ea  Rufus 对齐     
 e  W95 FAT16 (LBA) 53  OnTrack DM6 Aux a5  FreeBSD         eb  BeOS fs        
 f  W95 扩展 (LBA)  54  OnTrackDM6      a6  OpenBSD         ee  GPT            
10  OPUS            55  EZ-Drive        a7  NeXTSTEP        ef  EFI (FAT-12/16/
11  隐藏的 FAT12    56  Golden Bow      a8  Darwin UFS      f0  Linux/PA-RISC  
12  Compaq 诊断     5c  Priam Edisk     a9  NetBSD          f1  SpeedStor      
14  隐藏的 FAT16 <3 61  SpeedStor       ab  Darwin 启动     f4  SpeedStor      
16  隐藏的 FAT16    63  GNU HURD 或 Sys af  HFS / HFS+      f2  DOS 次要       
17  隐藏的 HPFS/NTF 64  Novell Netware  b7  BSDI fs         fb  VMware VMFS    
18  AST 智能睡眠    65  Novell Netware  b8  BSDI swap       fc  VMware VMKCORE 
1b  隐藏的 W95 FAT3 70  DiskSecure 多启 bb  Boot Wizard 隐  fd  Linux raid 自动
1c  隐藏的 W95 FAT3 75  PC/IX           bc  Acronis FAT32 L fe  LANstep        
1e  隐藏的 W95 FAT1 80  旧 Minix        be  Solaris 启动    ff  BBT 

命令(输入 m 获取帮助):t     # 输入 t 来变更分区类型
分区号 (1-3,5-9, 默认  9): 2     # 选择分区2 来进行变更
Hex 代码(输入 L 列出所有代码):82      # 变更为类型Id 为 82 的类型,对应上面是 Linux swap / Solaris

已将分区“Linux”的类型更改为“Linux swap / Solaris”。

命令(输入 m 获取帮助):p
Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动     起点     末尾     扇区 大小 Id 类型
/dev/sdb1           2048  4196351  4194304   2G 83 Linux
/dev/sdb2        4196352  8390655  4194304   2G 82 Linux swap / Solaris

命令(输入 m 获取帮助):t
分区号 (1-3,5-9, 默认  9): 5
Hex 代码(输入 L 列出所有代码):8e

已将分区“Linux”的类型更改为“Linux LVM”。

命令(输入 m 获取帮助):w     # 输入 w 来进行保存我们分区的操作
分区表已调整。
将调用 ioctl() 来重新读分区表。
正在同步磁盘。

# 执行下面步骤,强制刷新下分区表,主动同步
[root@server1 ~]# partprobe /dev/sdb

# 下面两个命令都可以看到最新的分区信息
[root@server1 ~]# fdisk -l
Disk /dev/sda:100 GiB,107374182400 字节,209715200 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x16dc60ad

设备       启动      起点      末尾      扇区  大小 Id 类型
/dev/sda1  *         2048 125831167 125829120   60G 83 Linux
/dev/sda2       125831168 146802687  20971520   10G 83 Linux
/dev/sda3       146802688 150996991   4194304    2G 82 Linux swap / Solaris
/dev/sda4       150996992 154904575   3907584  1.9G 83 Linux

Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动     起点     末尾     扇区 大小 Id 类型
/dev/sdb1           2048  4196351  4194304   2G 83 Linux
/dev/sdb2        4196352  8390655  4194304   2G 82 Linux swap / Solaris
/dev/sdb3        8390656 41943039 33552384  16G  5 扩展
/dev/sdb5        8392704 12587007  4194304   2G 8e Linux LVM
/dev/sdb6       12589056 16783359  4194304   2G 8e Linux LVM
/dev/sdb7       16785408 20979711  4194304   2G 8e Linux LVM
/dev/sdb8       20981760 25176063  4194304   2G 8e Linux LVM
/dev/sdb9       25178112 29372415  4194304   2G 8e Linux LVM
[root@server1 ~]# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0  100G  0 disk 
├─sda1   8:1    0   60G  0 part /
├─sda2   8:2    0   10G  0 part /home
├─sda3   8:3    0    2G  0 part [SWAP]
└─sda4   8:4    0  1.9G  0 part 
sdb      8:16   0   20G  0 disk 
├─sdb1   8:17   0    2G  0 part 
├─sdb2   8:18   0    2G  0 part 
├─sdb3   8:19   0  512B  0 part 
├─sdb5   8:21   0    2G  0 part 
├─sdb6   8:22   0    2G  0 part 
├─sdb7   8:23   0    2G  0 part 
├─sdb8   8:24   0    2G  0 part 
└─sdb9   8:25   0    2G  0 part 

也可以使用 parted 命令来进行分区

格式化分区

# 格式化为 MBR
[root@server1 ~]# parted -s /dev/sdb mklabel msdos
# 格式化为 GUID 分区
[root@server1 ~]# parted -s /dev/sdb mklabel gpt
# 既不选择 MBR 也不是 GUID
[root@server1 ~]# parted -s /dev/sdb mklabel loop
[root@server1 ~]# mkfs.xfs /dev/sdb1 
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=131072 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=524288, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@server1 ~]# mkfs.xfs -b size=1024 /dev/sdb1 
mkfs.xfs: /dev/sdb1 appears to contain an existing filesystem (xfs).
mkfs.xfs: Use the -f option to force overwrite.
[root@server1 ~]# mkfs.xfs -b size=1024 /dev/sdb1 -f    # 通过 -f 强制进行格式化 ;-b 设置区块的大小为1k,默认是4k即4096
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=524288 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=1024   blocks=2097152, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=1024   blocks=10240, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

[root@server1 ~]# xfs_info /dev/sdb1    # 查看分区的属性
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=524288 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=1024   blocks=2097152, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=1024   blocks=10240, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

每个文件系统都有唯一的一个ID,叫作UUID

[root@server1 ~]# blkid
/dev/sda1: UUID="c5c5b198-a07c-4cf9-8c0f-4c71f5d938a2" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="16dc60ad-01"
/dev/sda2: UUID="9dc1bb8e-3294-49d9-ae86-60db9e86c9d0" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="16dc60ad-02"
/dev/sda3: UUID="4250a782-471e-4965-b0f2-a2ecd975b860" TYPE="swap" PARTUUID="16dc60ad-03"
/dev/sda4: LABEL="xx" UUID="c0bb0ee1-032c-450f-81ee-4bdd60c74a99" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="16dc60ad-04"
/dev/sdb1: UUID="3dcbe85d-27b9-4d47-b476-1a26ff3d5bed" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="1c04a3b0-01"
/dev/sdb2: PARTUUID="1c04a3b0-02"
/dev/sdb5: PARTUUID="1c04a3b0-05"
/dev/sdb6: PARTUUID="1c04a3b0-06"
/dev/sdb7: PARTUUID="1c04a3b0-07"
/dev/sdb8: PARTUUID="1c04a3b0-08"
/dev/sdb9: PARTUUID="1c04a3b0-09"
/dev/sr0: BLOCK_SIZE="2048" UUID="2021-05-03-15-21-56-00" LABEL="RHEL-8-4-0-BaseOS-x86_64" TYPE="iso9660" PTUUID="426eea3b" PTTYPE="dos"

xfs文件系统可以通过 xfs-admin 进行查看

[root@server1 ~]# xfs_admin -u /dev/sdb1
UUID = 3dcbe85d-27b9-4d47-b476-1a26ff3d5bed
# 修改UUID
[root@server1 ~]# uuidgen
5ed29923-8248-4473-88bb-a3e6501e4c9d
[root@server1 ~]# xfs_admin -U 5ed29923-8248-4473-88bb-a3e6501e4c9d /dev/sdb1 
Clearing log and setting UUID
writing all SBs
new UUID = 5ed29923-8248-4473-88bb-a3e6501e4c9d

了解swap分区

虚拟内存,为了解决物理内存不足情况下,将会使用一部分硬盘空间来模拟内存。当物理内存使用率达到70%时,就会开始使用虚拟内存,通过 /proc/sys/vm/swapiness 可以查看阈值的配置,即当内存使用率达到(100-30)% 的时候就会使用虚拟内存。

[root@server1 ~]# cat /proc/sys/vm/swappiness 
30
# 把值设置为0,可以优化使用,即尽可能使用物理内存
[root@server1 ~]# echo 0 > /proc/sys/vm/swappiness

最终能够分配出去的内存总量=物理内存*overcommit_ratio+swap分区

[root@server1 ~]# cat /proc/sys/vm/overcommit_ratio
50

假设 mem 大小是 128G,最终分配出去的内存=128*0.5+swap,如果swap=0,则最终分配出去内存为64G

查看交换分区的大小

[root@server1 ~]# cat /proc/swaps
Filename                Type        Size    Used    Priority
/dev/sda3                               partition   2097148 30932   -2
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30932   -2

swap分区不足时,可以调整swap

# 创建交换分区
[root@server1 ~]# mkswap /dev/sdb2 
正在设置交换空间版本 1,大小 = 2 GiB (2147479552  个字节)
无标签,UUID=c563a140-88d2-4b84-acc2-089b559a6997
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30932   -2
# 需要开启一下
[root@server1 ~]# swapon /dev/sdb2
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30932   -2
/dev/sdb2                               partition   2097148 0   -3

# 关闭swap分区
[root@server1 ~]# swapoff /dev/sdb2
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30556   -2

多个 swap分区的时候,优先根据“权限”来决定用哪个,数值越大,越优先。

调整使用优先级,调整权限

[root@server1 ~]# swapon -p 2 /dev/sdb2
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30812   -2
/dev/sdb2                               partition   2097148 0   2

在 fstab 中配置 swap

[root@server1 ~]# vim /etc/fstab
/dev/sdb2                                 swap                    swap    defaults,pri=2        0       0
[root@server1 ~]# swapon -a
[root@server1 ~]# swapon -s
文件名             类型      大小  已用  权限
/dev/sda3                               partition   2097148 30628   -2
/dev/sdb2                               partition   2097148 0   2

逻辑卷 lv

逻辑卷介绍

把多个分区 PV 组合成一个虚拟的硬盘 VG,在虚拟硬盘上可以再次划分逻辑卷 LV,所有逻辑卷空间大小不能超过虚拟硬盘,逻辑卷不会影响性能。

逻辑卷空间不够使用时,可以进行动态扩容,逻辑卷最大特点:空间是可变的

逻辑卷管理

创建删除逻辑卷

[root@server1 ~]# pvscan
  No matching physical volumes found
[root@server1 ~]# pvs

[root@server1 ~]# fdisk -l
Disk /dev/sda:100 GiB,107374182400 字节,209715200 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x16dc60ad

设备       启动      起点      末尾      扇区  大小 Id 类型
/dev/sda1  *         2048 125831167 125829120   60G 83 Linux
/dev/sda2       125831168 146802687  20971520   10G 83 Linux
/dev/sda3       146802688 150996991   4194304    2G 82 Linux swap / Solaris
/dev/sda4       150996992 154904575   3907584  1.9G 83 Linux

Disk /dev/sdb:20 GiB,21474836480 字节,41943040 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x1c04a3b0

设备       启动     起点     末尾     扇区 大小 Id 类型
/dev/sdb1           2048  4196351  4194304   2G 83 Linux
/dev/sdb2        4196352  8390655  4194304   2G 82 Linux swap / Solaris
/dev/sdb3        8390656 41943039 33552384  16G  5 扩展
/dev/sdb5        8392704 12587007  4194304   2G 8e Linux LVM
/dev/sdb6       12589056 16783359  4194304   2G 8e Linux LVM
/dev/sdb7       16785408 20979711  4194304   2G 8e Linux LVM
/dev/sdb8       20981760 25176063  4194304   2G 8e Linux LVM
/dev/sdb9       25178112 29372415  4194304   2G 8e Linux LVM

# 基于sdb5-9 分区创建 pv
[root@server1 ~]# pvcreate /dev/sdb{5..9}
  Physical volume "/dev/sdb5" successfully created.
  Physical volume "/dev/sdb6" successfully created.
  Physical volume "/dev/sdb7" successfully created.
  Physical volume "/dev/sdb8" successfully created.
  Physical volume "/dev/sdb9" successfully created.

# VG列为空,表示还没加入任何卷组中
[root@server1 ~]# pvs
  PV         VG Fmt  Attr PSize PFree
  /dev/sdb5     lvm2 ---  2.00g 2.00g
  /dev/sdb6     lvm2 ---  2.00g 2.00g
  /dev/sdb7     lvm2 ---  2.00g 2.00g
  /dev/sdb8     lvm2 ---  2.00g 2.00g
  /dev/sdb9     lvm2 ---  2.00g 2.00g
# 删除 pv
[root@server1 ~]# pvremove /dev/sdb{5..9}
  Labels on physical volume "/dev/sdb5" successfully wiped.
  Labels on physical volume "/dev/sdb6" successfully wiped.
  Labels on physical volume "/dev/sdb7" successfully wiped.
  Labels on physical volume "/dev/sdb8" successfully wiped.
  Labels on physical volume "/dev/sdb9" successfully wiped.

# 系统中目前没有任何卷组
[root@server1 ~]# vgs
[root@server1 ~]# vgscan
# 创建卷组,名为 vg0
[root@server1 ~]# vgcreate vg0 /dev/sdb{5..9}
  Volume group "vg0" successfully created
# 再次查看 vg 信息
[root@server1 ~]# vgs
  VG  #PV #LV #SN Attr   VSize VFree
  vg0   5   0   0 wz--n- 9.98g 9.98g
[root@server1 ~]# vgscan
  Found volume group "vg0" using metadata type lvm2
[root@server1 ~]# pvs
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/sdb5  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb6  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb7  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb8  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb9  vg0 lvm2 a--  <2.00g <2.00g

# 查看vg详细信息
[root@server1 ~]# vgdisplay
  --- Volume group ---
  VG Name               vg0
  System ID             
  Format                lvm2
  Metadata Areas        5
  Metadata Sequence No  3
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                5
  Act PV                5
  VG Size               9.98 GiB
  PE Size               4.00 MiB        # 扩展逻辑卷的时候最小大小,并且为 PE Size 的整数倍,,在创建 vg 时,通过 -s n 来设定此值,此处n默认为4
  Total PE              2555
  Alloc PE / Size       0 / 0   
  Free  PE / Size       2555 / 9.98 GiB
  VG UUID               piXDoV-oKfe-RE1a-TiPL-UvGu-i540-Bwn0L8
# 重命名 vg 的名称
[root@server1 ~]# vgrename vg0 myvg
  Volume group "vg0" successfully renamed to "myvg"

# 查看逻辑卷 lv
[root@server1 ~]# lvs
[root@server1 ~]# lvscan
# 创建逻辑卷,因为 PE=4,98不是4的整数倍,系统会自动扩展到整数倍的值即100MB,-L指定lv的大小,-l指定PE的倍数,
[root@server1 ~]# lvcreate -L 98M -n lv0 vg0
  Rounding up size to full physical extent 100.00 MiB
  Logical volume "lv0" created.
[root@server1 ~]# lvcreate -L 100M -n lv1 vg0
  Logical volume "lv1" created.
[root@server1 ~]# lvs
  LV   VG  Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  lv0  vg0 -wi-a----- 100.00m                                                    
  lv1  vg0 -wi-a----- 100.00m 
[root@server1 ~]# lvcreate -l 25 -n lv2 vg0
  Logical volume "lv2" created.          
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv2' [100.00 MiB] inherit
# 创建 lv3,使用剩余空间的 10%
[root@server1 ~]# lvcreate -l 10%free -n lv3 vg0
  Logical volume "lv3" created.
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv2' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv3' [992.00 MiB] inherit

# 删除逻辑卷
[root@server1 ~]# lvremove /dev/vg0/lv3 -f
  Logical volume "lv3" successfully removed
[root@server1 ~]# lvremove /dev/vg0/lv2 -f
  Logical volume "lv2" successfully removed
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [100.00 MiB] inherit

# 对逻辑卷进行格式化
[root@server1 ~]# mkfs.xfs /dev/vg0/lv0 
meta-data=/dev/vg0/lv0           isize=512    agcount=4, agsize=6400 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=25600, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@server1 ~]# mkfs.ext
mkfs.ext2  mkfs.ext3  mkfs.ext4  
[root@server1 ~]# mkfs.ext
mkfs.ext2  mkfs.ext3  mkfs.ext4  
[root@server1 ~]# mkfs.ext4 /dev/vg0/lv1
mke2fs 1.45.6 (20-Mar-2020)
创建含有 102400 个块(每块 1k)和 25688 个inode的文件系统
文件系统UUID:af01ebbe-7d24-488a-a4e0-716981c14a6f
超级块的备份存储于下列块: 
    8193, 24577, 40961, 57345, 73729

正在分配组表: 完成                            
正在写入inode表: 完成                            
创建日志(4096 个块)完成
写入超级块和文件系统账户统计信息: 已完成

# 挂载逻辑卷
[root@server1 ~]# mkdir /lv_xfs /lv_ext4
[root@server1 ~]# mount /dev/vg0/lv0 /lv_xfs/
[root@server1 ~]# mount /dev/vg0/lv1 /lv_ext4/
[root@server1 ~]# df -hT |grep lv
/dev/mapper/vg0-lv0 xfs        95M  6.0M   89M    7% /lv_xfs
/dev/mapper/vg0-lv1 ext4       93M  1.6M   85M    2% /lv_ext4
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [100.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [100.00 MiB] inherit

扩大及缩小逻辑卷

# 缩小vg
[root@server1 ~]# vgreduce vg0 /dev/sdb9
  Removed "/dev/sdb9" from volume group "vg0"
[root@server1 ~]# vgs
  VG  #PV #LV #SN Attr   VSize VFree
  vg0   4   0   0 wz--n- 7.98g 7.98g
[root@server1 ~]# pvs
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/sdb5  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb6  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb7  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb8  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb9      lvm2 ---   2.00g  2.00g

# 扩展vg
[root@server1 ~]# vgextend vg0 /dev/sdb9
  Volume group "vg0" successfully extended
[root@server1 ~]# pvs
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/sdb5  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb6  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb7  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb8  vg0 lvm2 a--  <2.00g <2.00g
  /dev/sdb9  vg0 lvm2 a--  <2.00g <2.00g

扩展逻辑卷的两种方式

# 1、在原有基础上增加200M
[root@server1 ~]# lvextend -L +200M /dev/vg0/lv0
  Size of logical volume vg0/lv0 changed from 100.00 MiB (25 extents) to 300.00 MiB (75 extents).
  Logical volume vg0/lv0 successfully resized.

# 2、调整总大小为300M
[root@server1 ~]# lvextend -L 300M /dev/vg0/lv1
  Size of logical volume vg0/lv1 changed from 100.00 MiB (25 extents) to 300.00 MiB (75 extents).
  Logical volume vg0/lv1 successfully resized.
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [300.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [300.00 MiB] inherit

逻辑卷扩展完,还要将文件系统扩展,不同的文件系统的扩展方式也不同

# xfs文件系统
[root@server1 ~]# df -hT|grep lv
/dev/mapper/vg0-lv0 xfs        95M  6.0M   89M    7% /lv_xfs
/dev/mapper/vg0-lv1 ext4       93M  1.6M   85M    2% /lv_ext4
[root@server1 ~]# xfs_growfs /lv_xfs/
meta-data=/dev/mapper/vg0-lv0    isize=512    agcount=4, agsize=6400 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=25600, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 25600 to 76800

# ext4文件系统
[root@server1 ~]# resize2fs /dev/vg0/lv1
resize2fs 1.45.6 (20-Mar-2020)
/dev/vg0/lv1 上的文件系统已被挂载于 /lv_ext4;需要进行在线调整大小

old_desc_blocks = 1, new_desc_blocks = 3
/dev/vg0/lv1 上的文件系统现在为 307200 个块(每块 1k)。

[root@server1 ~]# df -hT|grep lv
/dev/mapper/vg0-lv0 xfs       295M  7.9M  287M    3% /lv_xfs
/dev/mapper/vg0-lv1 ext4      287M  2.0M  270M    1% /lv_ext4

扩展逻辑卷的时候,可以直接把fs一起扩展了

#lvextend -r 选项,会自动识别逻辑卷的文件系统并自动扩展
[root@server1 ~]# lvextend -L +100M -r /dev/vg0/lv0 
  Size of logical volume vg0/lv0 changed from 300.00 MiB (75 extents) to 400.00 MiB (100 extents).
  Logical volume vg0/lv0 successfully resized.
meta-data=/dev/mapper/vg0-lv0    isize=512    agcount=12, agsize=6400 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=76800, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [400.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [300.00 MiB] inherit

缩小逻辑卷,先缩小文件系统fs,再缩小逻辑卷lv,尽量避免缩小文件系统

# xfs文件系统只能扩展,不能缩小,ext4是可以的,以下演示 ext4
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [400.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [400.00 MiB] inherit
# 先卸载文件系统
[root@server1 ~]# umount /lv_ext4 
# 执行检查逻辑卷
[root@server1 ~]# fsck -f /dev/vg0/lv1
fsck,来自 util-linux 2.32.1
e2fsck 1.45.6 (20-Mar-2020)
第 1 步:检查inode、块和大小
第 2 步:检查目录结构
第 3 步:检查目录连接性
第 4 步:检查引用计数
第 5 步:检查组概要信息
/dev/mapper/vg0-lv1:11/98800 文件(0.0% 为非连续的), 18883/409600 块
# 将文件系统缩小到 100M
[root@server1 ~]# resize2fs /dev/vg0/lv1 100M
resize2fs 1.45.6 (20-Mar-2020)
将 /dev/vg0/lv1 上的文件系统调整为 102400 个块(每块 1k)。
/dev/vg0/lv1 上的文件系统现在为 102400 个块(每块 1k)。
# 将lv缩小300M到100M
[root@server1 ~]# lvreduce -L -300M /dev/vg0/lv1 
  WARNING: Reducing active logical volume to 100.00 MiB.
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce vg0/lv1? [y/n]: y
  Size of logical volume vg0/lv1 changed from 400.00 MiB (100 extents) to 100.00 MiB (25 extents).
  Logical volume vg0/lv1 successfully resized.
# 重新挂载上去
[root@server1 ~]# mount /dev/vg0/lv1 /lv_ext4/
# 查看下大小
[root@server1 ~]# lvscan
  ACTIVE            '/dev/vg0/lv0' [400.00 MiB] inherit
  ACTIVE            '/dev/vg0/lv1' [100.00 MiB] inherit
[root@server1 ~]# df -hT | grep lv
/dev/mapper/vg0-lv0 xfs       395M  8.8M  386M    3% /lv_xfs
/dev/mapper/vg0-lv1 ext4       93M  1.6M   86M    2% /lv_ext4

逻辑卷快照

可以帮助我们备份数据,快照映射到原始lv里的那些文件,是不需要再次格式化的,需要挂载到目录才能正常使用。

创建快照

[root@server1 ~]# lvcreate -L 20M -n lv0_snap -s /dev/vg0/lv0
  Logical volume "lv0_snap" created.
[root@server1 ~]# lvcreate -L 20M -n lv1_snap -s /dev/vg0/lv1
  Logical volume "lv1_snap" created.
[root@server1 ~]# lvscan
  ACTIVE   Original '/dev/vg0/lv0' [400.00 MiB] inherit
  ACTIVE   Original '/dev/vg0/lv1' [100.00 MiB] inherit
  ACTIVE   Snapshot '/dev/vg0/lv0_snap' [20.00 MiB] inherit
  ACTIVE   Snapshot '/dev/vg0/lv1_snap' [20.00 MiB] inherit
# 通过lvs 命令查看快照和逻辑卷的关系
[root@server1 ~]# lvs
  LV       VG  Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  lv0      vg0 owi-aos--- 400.00m                                                    
  lv0_snap vg0 swi-a-s---  20.00m      lv0    0.08                                   
  lv1      vg0 owi-aos--- 100.00m                                                    
  lv1_snap vg0 swi-a-s---  20.00m      lv1    0.06

挂载 xfs 文件系统的时候,mount 要加 -o nouuid 选项。

恢复

# 先卸载
[root@server1 ~]# umount /lv_xfs 
# 再执行恢复
[root@server1 ~]# lvconvert --merge /dev/vg0/lv0_snap 
# 再进行mount
[root@server1 ~]# mount /dev/vg0/lv0 /lv_xfs/
# ext4的文件系统同样的操作
[root@server1 ~]# umount /lv_ext4 
[root@server1 ~]# lvconvert --merge /dev/vg0/lv1_snap 
  Merging of volume vg0/lv1_snap started.
  vg0/lv1: Merged: 100.00%
[root@server1 ~]# mount /dev/vg0/lv1 /lv_ext4/

stratis 和 VDO

stratis

安装

[root@server1 ~]# yum install -y stratis-cli stratisd
[root@server1 ~]# systemctl enable --now stratisd

创建存储池

[root@server1 ~]# stratis pool list
Name   Total Physical   Properties
[root@server1 ~]# parted -s /dev/sdc mklabel loop
[root@server1 ~]# stratis pool create pool1 /dev/sdc 

添加一个新设备到存储池

[root@server1 ~]# stratis pool add-data pool1 /dev/sdd
[root@server1 ~]# stratis pool list
Name                    Total Physical   Properties
pool1   20 GiB / 37.64 MiB / 19.96 GiB      ~Ca,~Cr
[root@server1 ~]# stratis blockdev list pool1 
Pool Name   Device Node   Physical Size   Tier
pool1       /dev/sdc             20 GiB   Data

stratis 的文件系统默认的大小就是1TB

列出文件系统

[root@server1 ~]# stratis filesystem list
Pool Name   Name   Used   Created   Device   UUID
[root@server1 ~]# stratis fs list
Pool Name   Name   Used   Created   Device   UUID

创建文件系统

[root@server1 ~]# stratis fs create pool1 mufs
[root@server1 ~]# stratis fs create pool1 mufs2
[root@server1 ~]# stratis fs create pool1 mufs3
[root@server1 ~]# stratis fs list
Pool Name   Name    Used      Created             Device                     UUID                            
pool1       mufs    546 MiB   Dec 06 2022 04:51   /dev/stratis/pool1/mufs    3517a1b944cf4faeb14ff8f2aa104e5a
pool1       mufs2   546 MiB   Dec 06 2022 04:51   /dev/stratis/pool1/mufs2   5f3993d36f444747bd6cba17df0d8724
pool1       mufs3   546 MiB   Dec 06 2022 04:51   /dev/stratis/pool1/mufs3   4dc1763cd32f40d2b57e1a798f801a42

挂载文件

[root@server1 /]# mkdir /1 /2 /3
[root@server1 /]# mount /dev/stratis/pool1/mufs /1
[root@server1 /]# mount /dev/stratis/pool1/mufs2 /2
[root@server1 /]# mount /dev/stratis/pool1/mufs3 /3

df 报告文件系统的大小为 1TB,是因为 stratis文件系统没有固定的大小,而且它们使用精简配置,因此 stratis 将自动调整这些文件系统的大小,以根据需要增加空间

[root@server1 /]# df -hT |grep stratis
tmpfs                                                                                           tmpfs     1.0M     0  1.0M    0% /run/stratisd/keyfiles
/dev/mapper/stratis-1-2c299b3277164ce3bd7988c920674d12-thin-fs-3517a1b944cf4faeb14ff8f2aa104e5a xfs       1.0T  7.2G 1017G    1% /1
/dev/mapper/stratis-1-2c299b3277164ce3bd7988c920674d12-thin-fs-5f3993d36f444747bd6cba17df0d8724 xfs       1.0T  7.2G 1017G    1% /2
/dev/mapper/stratis-1-2c299b3277164ce3bd7988c920674d12-thin-fs-4dc1763cd32f40d2b57e1a798f801a42 xfs       1.0T  7.2G 1017G    1% /3

VDO

VDO(Virtual Data Optimize)虚拟数据优化器是RHEL8/Centos8上新推出的一个存储相关技术(最早在7.5测试版中开始测试),是Redhat收购的Permabit公司的技术。

VDO的主要作用是节省磁盘空间,比如让1T的磁盘能装下1.5T的数据,从而降低数据中心的成本。

那vdo是如何实现的呢,关键原理主要是重删和压缩,重删就是硬盘里拷贝来相同的数据,以前要占多份空间,现在只需要1份空间就可以了。类似我们在百度网盘中上传一个大型软件安装包,能实现秒传,其实是之前就有,所以无需再传一遍,也无需再占百度一份空间。另一方面是数据压缩,类似于压缩软件的算法,也可以更加节省磁盘空间。

安装

[root@server1 /]# yum install vdo kmod-kvdo

列出vdo设备

[root@server1 ~]# vdo list

创建vdo虚拟硬盘,/dev/mapper/vdo1

[root@server1 ~]# vdo create --name vdo1 --device /dev/sdb --vdoLogicalSize 50G
Creating VDO vdo1
      The VDO volume can address 16 GB in 8 data slabs, each 2 GB.
      It can grow to address at most 16 TB of physical storage in 8192 slabs.
      If a larger maximum size might be needed, use bigger slabs.
Starting VDO vdo1
Starting compression on VDO vdo1
VDO instance 0 volume is ready at /dev/mapper/vdo1

对 /dev/mapper/vdo1 进行格式化,-K 为快速格式化,可以提升格式化速度

[root@server1 ~]# mkfs.xfs -K /dev/mapper/vdo1 
meta-data=/dev/mapper/vdo1       isize=512    agcount=4, agsize=3276800 blks
         =                       sectsz=4096  attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=13107200, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=6400, version=2
         =                       sectsz=4096  sunit=1 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@server1 ~]# df -hT | grep vdo1
文件系统                                                                                         容量  已用  可用 已用% 挂载点
/dev/mapper/vdo1                                                                                xfs        50G  390M   50G    1% /vdo1

查看 vdo设备使用情况

[root@server1 ~]# vdostats --hu
Device                    Size      Used Available Use% Space saving%
/dev/mapper/vdo1         20.0G      4.0G     16.0G  20%           99%

在 /etc/fstab 中配置挂载 vdo 设备, _netdev 不能忘记写,否则可能会无法启动系统

[root@server1 ~]# cat /etc/fstab 
/dev/mapper/vdo1              /vdo1           xfs     defaults,_netdev  0   0

删除vdo设备

[root@server1 ~]# vdo remove -n vdo1

nfs/smb客户端

nfs客户端的使用

网络文件系统,Net-File-System,类Unix系统间的文件共享

安装nfs,并配置开机启动 nfs-server 服务

[root@server1 ~]# yum install nfs-utils -y
[root@server1 ~]# systemctl enable nfs-server --now

服务端配置nfs共享目录

[root@server1 ~]# vim /etc/exports
#本地目录       客户端及权限参数
/aa             *(rw,no_root_squash)
#no_root_squash表示不压缩root权限,即客户端用root写入的时候,在服务端也保存的是root用户

# 使配置生效
[root@server1 ~]# exportfs -avr
exporting *:/aa

客户端挂载nfs-server配置的目录

# 先查看服务器共享的目录是什么
[root@server1 ~]# showmount -e 102.168.88.136
Export list for 192.168.88.136:
/aa *
# 执行挂载
[root@server1 ~]# df -hT |grep nfs
192.168.88.136:/aa                                                                              nfs4       60G  5.6G   55G   10% /aaclient

配置永久生效,重启不会失效

[root@server1 ~]# vim /etc/fstab
192.168.88.136:/aa                        /aaclient               nfs     defaults              0       0

samba客户端的使用

windows和类unix之间的文件共享

查看 windows 上现有哪些共享

PS C:\Windows\system32> net share

共享名       资源                            注解

-------------------------------------------------------------------------------
C$           C:\                             默认共享
IPC$                                         远程 IPC
D$           D:\                             默认共享
F$           F:\                             默认共享
ADMIN$       C:\Windows                      远程管理
D            D:\
命令成功完成。

查看 windows上有哪些网络用户,可以新增一个网络用户

PS C:\Windows\system32> net user

\\DESKTOP-ZC-HOME 的用户帐户

-------------------------------------------------------------------------------
__SUNLOGIN_USER__        Administrator            DefaultAccount
defaultuser0             Guest                    WDAGUtilityAccount
朱纯
命令成功完成。

# 新增一个网络用户zhuchun,密码是 123456
PS C:\Windows\system32> net user zhuchun 123456 /add
命令成功完成。

安装 smbclient

# 先查询下哪个包提供了这个命令
[root@server1 ~]# yum whatprovides */smbclient
# 然后安装提供命令的这个包
[root@server1 ~]# yum install -y samba-client-4.13.3-5.el8_4.x86_64

用Linux访问windows的共享文件夹

方式一:直接登录访问

# -L 表示列出,以 $ 符结尾的,表示隐藏共享
[root@server1 ~]# smbclient -L //192.168.3.75 -U zhuchun%123456

    Sharename       Type      Comment
    ---------       ----      -------
    ADMIN$          Disk      远程管理
    C$              Disk      默认共享
    D               Disk      
    D$              Disk      默认共享
    F$              Disk      默认共享
    IPC$            IPC       远程 IPC
SMB1 disabled -- no workgroup available

# 加上 /D 表示访问 D 盘
[root@server1 ~]# smbclient //192.168.3.75/D -U zhuchun%123456
Try "help" to get a list of possible commands.
smb: \> dir
  $RECYCLE.BIN                      DHS        0  Wed Feb  3 05:10:40 2021
  360Downloads                        D        0  Sat May  7 06:41:04 2022
  360驱动大师目录               D        0  Fri May 21 07:04:08 2021
  awrrpt_1_3717_3722.html             A   647034  Tue Jul 17 02:21:34 2018
  BaiduNetdiskDownload                D        0  Tue Nov 29 04:15:41 2022
  install                             D        0  Fri Oct 21 08:21:24 2022
  LOCAL_MAVEN_REPO                    D        0  Mon Aug 29 05:39:30 2022
  Program Files                       D        0  Sat Jul 30 08:16:34 2022
  System Volume Information         DHS        0  Wed Jun  8 04:46:37 2022
  VM                                  D        0  Tue Nov  8 23:36:51 2022
  WorkSpace                           D        0  Tue Jul  5 07:06:03 2022
  Youdao                              D        0  Tue Nov 29 04:19:44 2022
  下载                              D        0  Sun Nov  6 16:49:33 2022
  性能测试理论模型.xmind      A 26221254  Wed Apr 24 07:51:03 2019
  我的项目                        D        0  Mon Aug 16 18:26:10 2021

        30719999 blocks of size 4096. 10018470 blocks available
smb: \> quit

方式二:挂载访问

# 需要提前安装此包
[root@server1 ~]# yum install -y cifs-utils.x86_64
# 执行挂载
[root@server1 ~]# mount -o username=zhuchun,password=123456 //192.168.3.75/D /smb
[root@server1 ~]# df -hT |grep smb
//192.168.3.75/D                                                                                cifs      118G   79G   39G   68% /smb
[root@server1 ~]# ll /smb/
总用量 26240
drwxr-xr-x. 2 root root        0 2月   3 2021 '$RECYCLE.BIN'
drwxr-xr-x. 2 root root        0 5月  21 2021  360驱动大师目录
drwxr-xr-x. 2 root root        0 5月   7 2022  360Downloads
drwxr-xr-x. 2 root root        0 8月  16 2021  我的项目
drwxr-xr-x. 2 root root        0 11月  6 16:49  下载
-rwxr-xr-x. 1 root root 26221254 4月  24 2019  性能测试理论模型.xmind
-rwxr-xr-x. 1 root root   647034 7月  17 2018  awrrpt_1_3717_3722.html
drwxr-xr-x. 2 root root        0 11月 29 04:15  BaiduNetdiskDownload
drwxr-xr-x. 2 root root        0 10月 21 08:21  install
drwxr-xr-x. 2 root root        0 8月  29 05:39  LOCAL_MAVEN_REPO
drwxr-xr-x. 2 root root        0 7月  30 08:16 'Program Files'
drwxr-xr-x. 2 root root        0 6月   8 04:46 'System Volume Information'
drwxr-xr-x. 2 root root        0 11月  8 23:36  VM
drwxr-xr-x. 2 root root        0 7月   5 07:06  WorkSpace
drwxr-xr-x. 2 root root        0 11月 29 04:19  Youdao

在 /etc/fstab 中配置永久生效

[root@server1 ~]# cat /etc/fstab
//192.168.3.75/D              /smb            cifs    defaults,username=zhuchun,password=123456 0   0
[root@server1 ~]# mount -a

最后,windows里的网络用户,如果后面不用了,可以删除掉

PS C:\Windows\system32> net user zhuchun /del
命令成功完成。

了解和配置自动挂载

定义一种规则:可以把某个设备和某个目录进行关联,平时有没有把该设备挂载到目录下不用关注,但是当我去访问此目录的时候,能够将设备进行自动挂载。

需要先安装 autofs 包

[root@server1 ~]# yum install autofs -y
[root@server1 ~]# systemctl enable autofs --now
Created symlink /etc/systemd/system/multi-user.target.wants/autofs.service → /usr/lib/systemd/system/autofs.service.

配置规则,进行挂载

方式一:

[root@server1 ~]# cd /etc/auto.master.d/
[root@server1 auto.master.d]# vim aa.autofs
# 中间是用 tab 隔开,/etc/auto.aa 这个文件内容定义关联规则
/xx     /etc/auto.aa

[root@server1 auto.master.d]# cat /etc/auto.aa
# 把本地的 /dev/cdrom 挂载到本地的 /xxx/cdrom 这个目录,挂载的文件系统是 iso9660,只读模式
cdrom   -fstype=iso9660,ro  :/dev/cdrom

# 执行重启生效
[root@server1 auto.master.d]# systemctl restart autofs
[root@server1 etc]# df -hT |grep cdrom
[root@server1 etc]# ll /xx/
总用量 0

# 访问关联的目录
[root@server1 etc]# ll /xx/cdrom
总用量 48
dr-xr-xr-x. 4 zhuchun zhuchun  2048 5月   3 2021 AppStream
dr-xr-xr-x. 4 zhuchun zhuchun  2048 5月   3 2021 BaseOS
dr-xr-xr-x. 3 zhuchun zhuchun  2048 5月   3 2021 EFI
-r--r--r--. 1 zhuchun zhuchun  8154 5月   3 2021 EULA
-r--r--r--. 1 zhuchun zhuchun  1455 5月   3 2021 extra_files.json
-r--r--r--. 1 zhuchun zhuchun 18092 5月   3 2021 GPL
dr-xr-xr-x. 3 zhuchun zhuchun  2048 5月   3 2021 images
dr-xr-xr-x. 2 zhuchun zhuchun  2048 5月   3 2021 isolinux
-r--r--r--. 1 zhuchun zhuchun   103 5月   3 2021 media.repo
-r--r--r--. 1 zhuchun zhuchun  1669 5月   3 2021 RPM-GPG-KEY-redhat-beta
-r--r--r--. 1 zhuchun zhuchun  5134 5月   3 2021 RPM-GPG-KEY-redhat-release
-r--r--r--. 1 zhuchun zhuchun  1796 5月   3 2021 TRANS.TBL

# 发现居然自动挂载上了
[root@server1 etc]# df -hT |grep cdrom
/dev/sr0                                                                                        iso9660   9.5G  9.5G     0  100% /xx/cdrom

# 重启 autofs 后,自动挂载是会断开的

方式二:

# 不指定父目录
[root@server1 auto.master.d]# cat bb.autofs 
/-  /etc/auto.bb
[root@server1 auto.master.d]# cat /etc/auto.bb
#*  -fstype=nfs,rw  192.168.88.136:/rhome/&     # 这个 * 和 & 表示,访问的 * 是啥,那 & 就是啥,这个配置在下面的模拟 windows 漫游用户会用到
/yy/cdrom   -fstype=iso9660,ro  :/dev/cdrom
[root@server1 auto.master.d]# systemctl restart autofs
# 访问一下,触发自动挂载
[root@server1 auto.master.d]# ll /yy/cdrom/
总用量 48
dr-xr-xr-x. 4 zhuchun zhuchun  2048 5月   3 2021 AppStream
dr-xr-xr-x. 4 zhuchun zhuchun  2048 5月   3 2021 BaseOS
dr-xr-xr-x. 3 zhuchun zhuchun  2048 5月   3 2021 EFI
-r--r--r--. 1 zhuchun zhuchun  8154 5月   3 2021 EULA
-r--r--r--. 1 zhuchun zhuchun  1455 5月   3 2021 extra_files.json
-r--r--r--. 1 zhuchun zhuchun 18092 5月   3 2021 GPL
dr-xr-xr-x. 3 zhuchun zhuchun  2048 5月   3 2021 images
dr-xr-xr-x. 2 zhuchun zhuchun  2048 5月   3 2021 isolinux
-r--r--r--. 1 zhuchun zhuchun   103 5月   3 2021 media.repo
-r--r--r--. 1 zhuchun zhuchun  1669 5月   3 2021 RPM-GPG-KEY-redhat-beta
-r--r--r--. 1 zhuchun zhuchun  5134 5月   3 2021 RPM-GPG-KEY-redhat-release
-r--r--r--. 1 zhuchun zhuchun  1796 5月   3 2021 TRANS.TBL
[root@server1 auto.master.d]# df -hT |grep yy
/dev/sr0                                                                                        iso9660   9.5G  9.5G     0  100% /yy/cdrom
# 这种方式是不会隐藏 /yy 下面的原有文件的
[root@server1 auto.master.d]# ll /yy/
总用量 2
-rw-r--r--. 1 root    root       0 12月  6 07:09 1.txt
-rw-r--r--. 1 root    root       0 12月  6 07:09 2.txt
-rw-r--r--. 1 root    root       0 12月  6 07:09 3.txt
-rw-r--r--. 1 root    root       0 12月  6 07:09 4.txt
-rw-r--r--. 1 root    root       0 12月  6 07:09 5.txt
dr-xr-xr-x. 7 zhuchun zhuchun 2048 5月   3 2021 cdrom

此自动挂载,可以实现类似 windows 的漫游用户功能
本地登录 user1 的时候,自动创建 user1 的家目录,并挂载到服务器的 user1 的家目录

系统启动

系统启动过程

系统引导过程
1.开机 BIOS 激活 MBR
2.MBR加载第二个扇区,里面的内容是 grub 里 stage1.5 反汇编出来
3.第二个扇区加载第3~n个扇区

修改系统参数

4.grub的配置文件 /boot/grub2/grub.cfg 此文件是生效的,但是系统不给修改此文件
6.如需修改参数,应该修改 /etc/default/grub,然后重新生成 /boot/grub2/grub.cfg
7.修改完 /etc/default/grub 文件后,通过 grub2-mkconfig -o /boot/grub2/grub.cfg 命令重新生成此文件,如修改启动页面默认选择的内核、或者自动选择内核的默认等待时间参数。

[root@server1 ~]# vim /etc/default/grub 
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=4250a782-471e-4965-b0f2-a2ecd975b860 rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

grub的不同模式
1.救援模式 rescue
假设系统正常启动需要20个必须服务,如果某个必须服务出问题,就会导致系统没法启动,假设救援模式只有10个必须服务,我们进入到救援模式的时候,出问题的服务不在救援模式的必须服务内,此时系统可以启动起来。需要root密码,此类似于 windows 下的安全模式
2.emergency紧急模式
假设系统正常启动需要20个必须服务,如果某个必须服务出问题会到执行系统无法启动,假设 emergency 模式只有6个必须服务,我们进入到 emergency 模式的时候,出问题的服务不在 emergency 模式的必须服务内,此时系统可以启动起来。需要root密码,类似于windows下的安全模式

重置 root 密码

1.在内核选择页面,按 e
2.在 linux 行的末尾,输入 rd.break 按 Ctrl + x 进行提交
4.等待一会儿就进入到了系统中
5.真的系统是在 /sysroot 目录里的,是以只读的形式挂载的
6.需要重新挂载下,已可读可写方式进行挂载 mount -o remount,rw /sysroot/
7.chroot /sysroot/ 切换根路径
8.echo redhat | LANG=C passwd --stdin root ,变更 root 用户的密码为 redhat
9.touch /.autorelabel
10.exit,会执行一会儿时间,然后自动进入重启系统正常进入系统

系统修复

当 boot loader 启动程序出问题的时候,可以通过光盘引导的方式对系统进行修复,类似 win 下面的 winPE 模式

1.光盘引导的时候,不选择安装系统,选择 Troubleshooting
2.在 Troubleshooting 页面选择 “Rescue a Red Hat Enterprise Linux system",回车
3.选择 1)continue ,回车
4.chroot /nmt/sysimage
5.grub2-install /dev/sda 执行 boot loader 的重新安装
6.exit

当 /boot 下的文件有问题时,即需要重装内核,同样是上面的 1-4 步
5.mount /dev/cdrom /nmt
6.rpm -ivh /nmt/BaseOS/Packages/kernel-4.18.0-193.el8.x86_64.rpm /nmt/BaseOS/Packages/kernel-core-4.18.0-193.el8.x86_64.rpm --force
7.mkdir /boot/grub2
8.grub2-mkconfig -o /boot/grub2/grub.cfg
9.grub2-install /dev/sda 执行 boot loader 的重新安装
10.exit

防火墙

firewalld里的zone

firewalld 里的 zone 理解为一个安检入口,不同的 zone 里制定不同的规则。某网卡要和某个 zone 关联,则从此网卡进来的流量都要使用关联 zone 的对应过滤规则。

一张网卡最多只能和一个 zone 关联,不能同时关联到多个 zone ,如果没有关联到任何 zone ,则走默认的 zone。

1.有多少个 zone?
[root@server1 ~]# firewall-cmd --get-zones
block dmz drop external home internal libvirt nm-shared public trusted work

2.默认的zone是什么?如何修改默认zone?
[root@server1 ~]# firewall-cmd --get-default-zone
public
[root@server1 ~]# firewall-cmd --set-default-zone=trusted
success
[root@server1 ~]# firewall-cmd --get-zone-of-interface=ens160
trusted
[root@server1 ~]# firewall-cmd --get-default-zone
trusted

如果修改了默认的 zone,则网卡默认是和默认的zone关联的,所以网卡关联的zone也会改变,

3.网卡是和哪个zone关联?如何设置网卡和某个zone关联?
[root@server1 ~]# firewall-cmd --get-zone-of-interface=ens160
trusted

设置关联:方式一:
要修改关联,需要先解除目前的关联,解除关联后,ens160使用默认的zone
[root@server1 ~]# firewall-cmd --remove-interface=ens160 --zone=trusted
success
然后再添加关联
[root@server1 ~]# firewall-cmd --add-interface=ens160 --zone=home

设置关联:方式二:
直接一次切换,如果不指定 --zone 则切换到默认zone
[root@server1 ~]# firewall-cmd --change-interface=ens160 --zone=public
success

4.每个zone里的规则如何查看?

# 不指定 --zone 则查看默认 zone 里的规则
[root@server1 ~]# firewall-cmd --list-all
You're performing an operation over default zone ('trusted'),
but your connections/interfaces are in zone 'libvirt,public' (see --get-active-zones)
You most likely need to use --zone=libvirt option.

trusted
  target: ACCEPT
  icmp-block-inversion: no
  interfaces: 
  sources: 
  services: 
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

# 指定 --zone 查看对应 zone 里的规则
[root@server1 ~]# firewall-cmd --list-all --zone=home
home
  target: default
  icmp-block-inversion: no
  interfaces: 
  sources: 
  services: cockpit dhcpv6-client mdns samba-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

5.修改规则

放行一个服务

[root@server1 ~]# firewall-cmd --add-service=http   # 临时生效
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client http ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
[root@server1 ~]# firewall-cmd --add-service=http --permanent   # 永久生效,但是没立即生效
[root@server1 ~]# firewall-cmd --reload     #重新加载,使上面永久生效的立即生效

禁用一个服务

[root@server1 ~]# firewall-cmd --remove-service=http    # 临时生效
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:
[root@server1 ~]# firewall-cmd --remove-service=http --permanent    # 永久生效,但是没立即生效
[root@server1 ~]# firewall-cmd --reload     #重新加载,使上面永久生效的立即生效

基于端口进行限制

# 开启端口
[root@server1 ~]# firewall-cmd --add-port=8080/tcp --permanent
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
[root@server1 ~]# firewall-cmd --reload
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 8080/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

# 禁用端口
[root@server1 ~]# firewall-cmd --remove-port=8080/tcp --permanent 
success
[root@server1 ~]# firewall-cmd --reload 
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

# 开启端口区间
[root@server1 ~]# firewall-cmd --add-port=1000-2000/tcp
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 1000-2000/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

使用富规则 rich rules

放行服务的语法:
firewall-cmd --add-rich-rule='rule family=ipv4 source address=源网段 service name=服务名 accept'

[root@server1 ~]# firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.88.100 service name=http accept'
success
[root@server1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens160
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
    rule family="ipv4" source address="192.168.88.100" service name="http" accept

放行端口的语法:
firewall-cmd -add-rich-rule 'rule family=ipv4 source address=源网段 port port=N protocol=协议 accept'

记忆技巧
man -k firewall
man firewalld.richlanguage
搜索 EXAMPLE

如果已经添加了某个服务或者端口,已经是允许所有的客户端了
则在富规则里,再写 accept 是没有意义的,应该写 reject,相当于是黑名单,即允许所有但是拒绝个别。

如果没有添加某个服务或者端口,已经是拒绝所有的客户端了
则在富规则里写 accept 相当于白名单,即拒绝所有允许个别。

kickstart

kickstart 无人值守安装系统工作过程

演示

podman

# RHEL8 默认已经安装了 podman
[root@server1 ~]# yum list podman
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.

上次元数据过期检查:1:08:33 前,执行于 2022年12月09日 星期五 02时46分03秒。
已安装的软件包
podman.x86_64                                                         3.0.1-6.module+el8.4.0+10607+f4da7515                                                           @AppStream
可安装的软件包
podman.x86_64                                                         3.2.3-0.11.module_el8.4.0+942+d25aada8                                                          appstream 

镜像

镜像加速配置

[zhuchun@server1 ~]$ cat /etc/containers/registries.conf
unqualified-search-registries = ["docker.io"]
[[registry]]
prefix="docker.io"
location="2e1t0zd0.mirror.aliyuncs.com"
[zhuchun@server1 ~]$ poman pull docker.io/nginx
[zhuchun@server1 ~]$ podman images
REPOSITORY               TAG     IMAGE ID      CREATED        SIZE
docker.io/library/nginx  latest  605c77e624dd  11 months ago  146 MB
[zhuchun@server1 ~]$ podman rmi nginx
Untagged: docker.io/library/nginx:latest
Deleted: 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85

# 导出镜像
[zhuchun@server1 ~]$ podman save nginx > nginx.tar

# 导入镜像
[zhuchun@server1 ~]$ podman load -i nginx.tar 

# 有时需要登录才能拉取镜像
[zhuchun@server1 ~]$ podman login -u xxx -p yyy c.163.com

# 对镜像重新做标签
[zhuchun@server1 ~]$  podman tag docker.io/library/nginx 192.168.88.172/rhcsa/nginx:v1
[zhuchun@server1 ~]$ podman images
REPOSITORY                  TAG     IMAGE ID      CREATED        SIZE
docker.io/library/nginx     latest  605c77e624dd  11 months ago  146 MB
192.168.88.172/rhcsa/nginx  v1      605c77e624dd  11 months ago  146 MB

# 查看指定镜像的历史
[zhuchun@server1 ~]$ podman history docker.io/library/nginx:latest 

容器

# 创建容器:
# mynginx为设定的容器名称
# -d 表示后台运行
# --restart=always在podman中即使配置重启机器后也不会启动
# -p 表示端口映射,-p M ,M表示容器端口,宿主机会自动映射一个端口;-p N:M N表示容器端口,M表示映射的指定宿主机端口
# -e 表示注入环境变量到容器中
[zhuchun@server1 ~]$ podman run --name=mynginx -d --restart=always -p N:M -e myenv1=myvalue1 -e myenv2=myvalue2 docker.io/library/nginx:latest

# 查看主机上的容器,-a 表示所有容器,不带 -a 表示查找正在运行中的容器
[zhuchun@server1 ~]$ podman ps -a

# 停止容器,也可以通过容器ID来停止
[zhuchun@server1 ~]$ podman stop mynginx

# 启动容器
[zhuchun@server1 ~]$ podman start mynginx

# 删除容器,删除正在运行的容器带 -f 选项强制删除
[zhuchun@server1 ~]$ podman rm -f mynginx

# 进入容器,-t 表示产生模拟终端,-i表示交互模式
[zhuchun@server1 ~]$ podman exec -it mynginx bash
root@445209fd858b:/# exit
exit
[zhuchun@server1 ~]$ 

# 查看容器日志
[zhuchun@server1 ~]$ podman logs mynginx

# 查看日志的持续输出
[zhuchun@server1 ~]$ podman logs -f mynginx

数据卷

# 创建容器时,-v 可以将 宿主机本地目录与容器内指定路径做映射,对容器内文件做持久化
[zhuchun@server1 ~]$ podman run --name=mynginx2 -d --restart=always -v /home/zhuchun:/opt nginx:latest 
# -Z 可以指定上下文,防止 SELinux 因上下文不匹配没有权限操作
[zhuchun@server1 ~]$ podman run --name=mynginx2 -d --restart=always -v /home/zhuchun:/opt:Z nginx:latest 

# 查看容器的详细信息,能看到挂载信息
[zhuchun@server1 ~]$ podman inspect mynginx2
。。。
"Mounts": [
            {
                "Type": "bind",
                "Source": "/home/zhuchun",
                "Destination": "/opt",
                "Driver": "",
                "Mode": "",
                "Options": [
                    "rbind"
                ],
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
。。。

通过 linger 服务实现容器跟随系统自动启动

开启 linger

# 开启 Linger 表示,当这个用户通过ssh 或者控制台登录系统的时候,不是 su 过来,会自动触发,吧他创建的容器给启动
[zhuchun@server1 ~]$ loginctl enable-linger zhuchun
[zhuchun@server1 ~]$ loginctl show-user zhuchun
Linger=yes

把容器设置成开机启动

[zhuchun@server1 ~]$ mkdir -p ~/.config/systemd/user ; cd ~/.config/systemd/user
# 生成 service 文件
[zhuchun@server1 user]$ podman generate systemd --name mynginx --files --new
/home/zhuchun/.config/systemd/user/container-mynginx.service
[zhuchun@server1 user]$ systemctl --user daemon-reload 
# 设置成开启启动
[zhuchun@server1 user]$ systemctl --user enable container-mynginx.service
[zhuchun@server1 user]$ ls
container-mynginx.service
[zhuchun@server1 user]$ cat container-mynginx.service 
# container-mynginx.service
# autogenerated by Podman 3.0.2-dev
# Fri Dec  9 06:30:28 PST 2022

[Unit]
Description=Podman container-mynginx.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/container-mynginx.pid %t/container-mynginx.ctr-id
ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-mynginx.pid --cidfile %t/container-mynginx.ctr-id --cgroups=no-conmon --replace --name=mynginx -d --restart=always -p 8080:80 docker.io/library/nginx:latest
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-mynginx.ctr-id -t 10
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-mynginx.ctr-id
PIDFile=%t/container-mynginx.pid
Type=forking

[Install]
WantedBy=multi-user.target default.target
[zhuchun@server1 user]$ 

Ansible

安装 ansible

配置 yum 源

[root@server1 ~]# cat /etc/yum.repos.d/RHEL-tuna.repo
[ansible]
name=RHEL8 - Ansible
baseurl=http://mirrors.ustc.edu.cn/centos-vault/8.4.2105/configmanagement/x86_64/ansible-29/
enabled=1
gpgcheck=0

[root@server1 ~]# yum clean all
[root@server1 ~]# yum makecache
[root@server1 ~]# yum install ansible -y

使用 ansible

ansible.cfg 及清单文件

实验环境:

[zhuchun@server1 ~]$ cat /etc/hosts 
# server1 为主控机器
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.88.136  server1.rhce.cc server1
192.168.88.137  server2.rhce.cc server2
192.168.88.138  server3.rhce.cc server3

通过ansible主机控制其他机器,需要先配置 ssh 免密登录
在主控机器上执行:

[zhuchun@server1 ~]$ ssh-keygen
[zhuchun@server1 ~]$ ssh-copy-id server1
[zhuchun@server1 ~]$ ssh-copy-id server2
[zhuchun@server1 ~]$ ssh-copy-id server3

在执行 ansible 或者 ansible-playbook 命令的时候,优先使用当前目录中的 ansible.cfg 如果当前目录没有此文件,则使用默认的配置文件 /etc/ansible/ansible.cfg。

[zhuchun@server1 ~]$ mkdir chap2 && cd chap2/
[zhuchun@server1 chap2]$ vim ansible.cfg
[defaults]
inventory      = ./hosts
# 下面这段配置可以提权为root,前提是执行用户在sudoer中且su免密
#[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root

[zhuchun@server1 chap2]$ vim hosts
# []表示分组组名
[web]
# 也可以是 server[1:3] 表示 server1-server3 这个范围的主机
server1
server2
server3 ansible_ssh_port=22 # 默认是22,可以通过ansible_ssh_port配置
#[zz:children]  这个表示 zz 组包含 db 组合 web 组
#db
#web
[zhuchun@server1 chap2]$ 
# 列出 web 组的主机
[zhuchun@server1 chap2]$ ansible web --list-hosts
  hosts (3):
    server1
    server2
    server3

# 测试一下在 server2 主机执行 ping 命令
[zhuchun@server1 chap2]$ ansible server2 -m ping
server2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

# 测试一下在 web 组主机执行 shell 模块,执行 hostname 命令
[zhuchun@server1 chap2]$ ansible web -m shell -a "hostname"
server2 | CHANGED | rc=0 >>
server2
server1 | CHANGED | rc=0 >>
server1.rhce.cc
server3 | CHANGED | rc=0 >>
server3

常见模块的使用

查看ansible有哪些模块

[root@server1 ~]# ansible-doc -l

查看某个模块的帮助

[root@server1 ~]# ansible-doc 模块名

ping模块

# all是指清单文件里所有的主机,localhost 指本机
[zhuchun@server1 chap2]$ ansible all -m ping
[zhuchun@server1 chap2]$ ansible localhost -m ping

shell 模块

用来执行操作系统的命令

[zhuchun@server1 chap2]$ ansible server2 -m shell -a "hostname -s"
server2 | CHANGED | rc=0 >>
server2
[zhuchun@server1 chap2]$ ansible server2 -m shell -a "ifconfig ens160"
server2 | CHANGED | rc=0 >>
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.137  netmask 255.255.255.0  broadcast 192.168.88.255
        inet6 fe80::20c:29ff:fe65:f4a8  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:65:f4:a8  txqueuelen 1000  (Ethernet)
        RX packets 25106  bytes 31200903 (29.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7795  bytes 534341 (521.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

command 默认的模块

执行命令用

# 没指定模块则默认是 command 模块,和shell类似,但是有些区别,一些操作符,如:"<",">","|",";","&" 符号在 command 中不会起作用;还有一些变量也不会起作用
[zhuchun@server1 chap2]$ ansible server2 -a "hostname -s"
server2 | CHANGED | rc=0 >>
server2

script 模块

用来运行脚本的,参数就是脚本名,就不需要对所需要执行脚本的机器进行脚本分发

[zhuchun@server1 chap2]$ ll a.sh 
-rwxrwxr-x. 1 zhuchun zhuchun 123 12月 10 06:58 a.sh
[zhuchun@server1 chap2]$ cat a.sh 
#!/bin/bash
echo "我的主机名是 $(hostname -s)"
echo "我的IP地址是 $(ifconfig ens160 | awk '/inet /{print $2}')"
[zhuchun@server1 chap2]$ ansible server2 -m script -a "./a.sh"
server2 | CHANGED => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to server2 closed.\r\n",
    "stderr_lines": [
        "Shared connection to server2 closed."
    ],
    "stdout": "我的主机名是 server2\r\n我的IP地址是 192.168.88.137\r\n",
    "stdout_lines": [
        "我的主机名是 server2",
        "我的IP地址是 192.168.88.137"
    ]
}

get_url 下载模块

常见参数: url=下载地址 dst=下载到哪里

[zhuchun@server1 chap2]$ ansible server2 -m get_url -a "url=http://xxx.xx.xx/xx dest=/opt"

group 组管理模块

参数包括: name=组名 state=present/absent
present为创建,absent为删除

[zhuchun@server1 chap2]$ ansible server2 -m group -a "name=group1 state=present"

user 用户管理模块

常用参数包括: name=用户名 comment=”注释信息“ group=主组 groups=附属组 password={{‘字符串’ | password_hash('sha512')}} state=present/absent remove=true(remove选项在删除用户时使用,相当于 userdel -r删除家目录)

[zhuchun@server1 chap2]$ ansible server2 -m user -a "name=tom comment='its tom' group=zhuchun groups=root password={{'redhat' | password_hash('sha512')}}"

debug模块

打印调试
参数:msg=‘’
类似于 echo 命令,及其他语言的print/println/printf 命令

setup 模块

获取信息,setup所获取的这些变量--facts变量
参数 :filter=xxx(信息中的key值)

[zhuchun@server1 chap2]$ ansible server2 -m setup
[zhuchun@server1 chap2]$ ansible server2 -m setup -a "filter=ansible_bios_version"
server2 | SUCCESS => {
    "ansible_facts": {
        "ansible_bios_version": "6.00",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
# ansible 过滤两级的话,是看不到返回结果的,下面这个就没返回具体的日期
[zhuchun@server1 chap2]$ ansible server2 -m setup -a "filter=ansible_date_time.date"
server2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

file 文件模块

用来创建、删除文件或目录
常见的参数:src(软链接、硬链接专用) dest/name/path=在哪里创建文件 owner=文件拥有者 group=所属组 mode=权限 setype=上下文 state=touch/file/directory/link/hard/absent(touch-文件不存在则创建;file-文件已存在修改属性;link-软链接;hard-硬链接;absent-删除)

# 创建文件
[zhuchun@server1 chap2]$ ansible server2 -m file -a "dest=/home/zhuchun/aa.txt owner=zhuchun group=zhuchun mode=000 setype=user_home_t state=touch "
server2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "dest": "/home/zhuchun/aa.txt",
    "gid": 1000,
    "group": "zhuchun",
    "mode": "0000",
    "owner": "zhuchun",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 0,
    "state": "file",
    "uid": 1000
}
# 创建文件链接
[zhuchun@server1 chap2]$ ansible server2 -m file -a "src=/home/zhuchun/aa.txt dest=/home/zhuchun/aal.txt state=link "
server2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "dest": "/home/zhuchun/aal.txt",
    "gid": 1000,
    "group": "zhuchun",
    "mode": "0777",
    "owner": "zhuchun",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 20,
    "src": "/home/zhuchun/aa.txt",
    "state": "link",
    "uid": 1000
}

copy/fetch 拷贝文件模块

参数:src=本地源文件 dest=拷贝到远端目的文件 owner、group、setype等和file模块一样

[zhuchun@server1 chap2]$ touch bb.txt
[zhuchun@server1 chap2]$ ansible server2 -m copy -a "src=bb.txt dest=/home/zhuchun/bb-cp.txt owner=zhuchun group=zhuchun"
server2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "dest": "/home/zhuchun/bb-cp.txt",
    "gid": 1000,
    "group": "zhuchun",
    "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
    "mode": "0664",
    "owner": "zhuchun",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 0,
    "src": "/home/zhuchun/.ansible/tmp/ansible-tmp-1670687926.8385549-14583-262383626209378/source",
    "state": "file",
    "uid": 1000
}
[zhuchun@server1 chap2]$ ansible server2 -m copy -a "dest=/home/zhuchun/cc.txt content='这是远程写文件内容' owner=zhuchun group=zhuchun"
# 下面命令从远端 server2 拉取 /home/zhuchun/cc.txt 文件到本地,会创建server2 命名的逐级目录文件
[zhuchun@server1 chap2]$ ansible server2 -m fetch -a "src=/home/zhuchun/cc.txt dest=."
[zhuchun@server1 chap2]$ tree server2/
server2/
└── home
    └── zhuchun
        └── cc.txt

2 directories, 1 file

yum_repository 配置yum源

参数:name=源名称 description=”描述信息“ baseurl=源地址 enabled=yes gpgcheck=no

# 会在server2 的/etc/yum.repos.d/ 创建 ansible.repo 文件
[zhuchun@server1 chap2]$ ansible server2 -m yum_repository -a "name=ansible description='ansible源' baseurl=http://mirrors.ustc.edu.cn/centos-vault/8.4.2105/configmanagement/x86_64/ansible-29/ enabled=yes gpgcheck=no"

yum 模块

对软件包进行管理
参数:name=包名 name=’@组名‘ name=’@模块名‘ state=installed/present/removed/absent/latest

[zhuchun@server1 chap2]$ ansible server2 -m yum -a "name=vsftpd state=installed"
[zhuchun@server1 chap2]$ ansible server2 -m yum -a "name=vsftpd state=removed"
[zhuchun@server1 chap2]$ ansible server2 -m yum -a "name='@RPM 开发工具' state=installed"

package 对软件包进行管理

与 yum 模块一致

service 服务管理模块

参数:name=服务名 state=started/stopped/restarted enabled=yes/no

[zhuchun@server1 chap2]$ ansible server2 -m service -a "name=vsftpd state=started enabled=yes"

systemd 模块

参数和 service 一样,只是多了一个 daemon_reload=true
修改了服务的启动脚本的时候,需要 systemctl deamon-reload

[zhuchun@server1 chap2]$ ansible server2 -m systemd -a "name=vsftpd state=stopped enabled=no daemon_reload=true"

fierewalld 防火墙管理模块

参数:service=http/ftp/https... port=80/tcp immediate=yes/no(没有默认值) permanent=yes/no(没有默认值) state=enabled/disabled

[zhuchun@server1 chap2]$ ansible server2 -m firewalld -a "service=http immediate=yes permanent=yes state=enabled"

replace 替换操作模块

参数:path=文件名 regexp=正则表达式(修改的是哪行里的内容) replace=修改之后的内容

# 将server2 中 /home/zhuchun/cc.txt 文件内容中的 cc开头的行整行替换成 cc=456
[zhuchun@server1 chap2]$ ansible server2 -m replace -a "path=/home/zhuchun/cc.txt regexp=^cc.+ replace=cc=456"
server2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "msg": "1 replacements made"
}

lineinfile 模块

也可以用于替换
参数:path=文件名 regexp=正则 line=修改后的内容

[zhuchun@server1 chap2]$ ansible server2 -m lineinfile -a "path=/home/zhuchun/cc.txt regexp=^cc line=cc=12345667890"
server2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "backup": "",
    "changed": true,
    "msg": "line replaced"
}

磁盘管理模块

parted对硬盘进行分区

参数:device=/dev/sdb number=磁盘编号 part_type=分区类型 part_start=xxxMiB/GiB(起始点) part_end=yyyMiB/GiB state=present/absent

[zhuchun@server1 chap2]$ ansible server2 -m parted -a "device=/dev/sdb number=1 part_end=2GiB state=present"
[zhuchun@server1 chap2]$ ansible server2 -m parted -a "device=/dev/sdb number=2 part_start=2GiB part_end=5GiB state=present"
filesystem对分区进行格式化

参数:dev=对哪个设备进行格式化 fstype=指定文件系统 force=yes/no

[zhuchun@server1 chap2]$ ansible server2 -m filesystem -a "dev=/dev/sdb3 fstype=xfs"
[zhuchun@server1 chap2]$ ansible server2 -m filesystem -a "dev=/dev/sdb3 fstype=ext4 force=yes"
mount挂载分区

参数:src=设备 path=挂载点 fstype=指定文件系统 opts=挂载选项 state=mounted(除了挂载外还写入到/etc/fstab)/pesent(只写入到/etc/fstab当前并不挂载)/absent(卸载并从/etc/fstab里删除)/umounted(当前卸载并不从/etc/fstab中删除)

# 挂载并写入 /etc/fstab
[zhuchun@server1 chap2]$ ansible server2 -m mount -a "src=/dev/sdb3 path=/aa fstype=xfs state=mounted"
[zhuchun@server1 chap2]$ ansible server2 -m mount -a "src=/dev/sdb3 path=/aa fstype=xfs state=absent"
lvg管理卷组

参数:
pvs=物理卷(直接指定分区,不需要提前把pv创建出来)
pesize=PE的大小
vg=卷组名
state=present/absent

[zhuchun@server1 chap2]$ ansible server2 -m lvg -a "vg=vg0 pvs=/dev/sdb1,/dev/sdb2 state=present"
[zhuchun@server1 chap2]$ ansible server2 -m lvg -a "vg=vg0 pvs=/dev/sdb1,/dev/sdb2 state=absent"
lvol对逻辑卷进行管理

参数:
vg=卷组
lv=逻辑卷名
size=大小 M/G
force=yes/no 对删除lv生效
state=present/absent 默认是present

[zhuchun@server1 chap2]$ ansible server2 -m lvol -a "vg=vg0 lv=lv0 size=2G "
[zhuchun@server1 chap2]$ ansible server2 -m lvol -a "vg=vg0 lv=lv0 size=2G state=absent force=yes"

playbook的使用

上面都是在命令行中进行的操作,一次执行一个命令

playbook叫做剧本,剧本中包含一个个故事play

整个playbook的内容以 yaml 格式来编写,playbook 文件要以 yaml 或者 yml 为后缀

格式:

- name: 第一个play的名称
  hosts: 主机组1,主机组2,...
  tasks:
  - name: task1-name
    模块1:
        参数1: 值1
      参数2: 值2
        ...
  - name: task2-name
    模块2:
        参数1: 值1
      参数2: 值2
        ...
- name: 第二个play的名称
  hosts: 主机组1,主机组2,...
  tasks:
  - name: task1-name
    模块1:
        参数1: 值1
      参数2: 值2
        ...
  - name: task2-name
    模块2:
        参数1: 值1
      参数2: 值2
        ...
[zhuchun@server1 chap3]$ cat server-ip.yaml 
---
- hosts: server2
  tasks:
  - name: 打印IP地址
    debug: msg={{ansible_default_ipv4.address}}
[zhuchun@server1 chap3]$ ansible-playbook server-ip.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印IP地址] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "192.168.88.137"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

两个练习:
1、显示server2的IP
2、显示server2和server3的主机名

[zhuchun@server1 chap3]$ cat server-ip.yaml 
---
- name: 显示IP
  hosts: server2
  tasks:
  - name: 打印IP地址
    debug: msg={{ansible_default_ipv4.address}}
- name: 显示主机名
  hosts: server2,server3
  tasks:
  - name: 显示主机名
    debug: msg={{ansible_fqdn}}
[zhuchun@server1 chap3]$ ansible-playbook server-ip.yaml 

PLAY [显示IP] ******************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印IP地址] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "192.168.88.137"
}

PLAY [显示主机名] *****************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]
ok: [server3]

TASK [显示主机名] *****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "server2.rhce.cc"
}
ok: [server3] => {
    "msg": "server3.rhce.cc"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
server3                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[zhuchun@server1 chap3]$ 

练习:写一个 playbook xxx.yaml,实现如下操作
1、在server2上安装httpd,并启动httpd,且开机启动,防火墙里开放http服务,并设置永久生效
2、在server3上安装vsftpd,启动 vsftpd并设置开启自动启动,防火墙里开放 ftp服务并设置永久生效
3、在server2和server3上安装 autofs,启动autofs并设置开机自启

[zhuchun@server1 chap3]$ cat xxx.yaml 
---
- name: 安装httpd
  hosts: server2
  tasks:
  - name: 安装httpd
    yum: name=httpd state=installed
  - name: 启动httpd并设置开机启动
    service: name=httpd state=started enabled=yes
  - name: 防火墙里开放httpd服务
    firewalld: service=http immediate=yes permanent=yes state=enabled
- name: 安装vsftpd
  hosts: server3
  tasks:
  - name: 安装vsftpd服务
    yum: name=vsftpd state=installed
  - name: 启动vsftpd服务并设置开机启动
    service: name=vsftpd state=started enabled=yes
  - name: 防火墙里开放ftp服务
    firewalld: service=ftp immediate=yes permanent=yes state=enabled
- name: 安装autofs
  hosts: server2,server3
  tasks:
  - name: 安装autofs服务
    yum: name=autofs state=installed
  - name: 启动autofs服务并设置开机启动
    service: name=autofs state=started enabled=yes
[zhuchun@server1 chap3]$ ansible-playbook xxx.yaml

变量的使用

手动定义变量

定义格式:
vars:
变量1: 值1
变量2: 值2
...

引用变量时,用 {{变量名}},大括号内侧两边是不是有空格无所谓,不影响。

[root@server1 chap]# cat 1.yaml 
---
- hosts: server2
  vars:
    aa: 11
  tasks:
  - name: 输出一个变量
    debug: msg={{aa}}
[root@server1 chap]# ansible-playbook 1.yaml

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [输出一个变量] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": 11
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@server1 chap]# cat 1.yaml 
---
- hosts: server2
  vars:
    aa: 11
    bb: "adsfa dsfadsf"
  tasks:
  - name: 输出一个变量
    debug: msg="{{aa}}MB,这是bb:{{bb}}"
[root@server1 chap]# ansible-playbook 1.yaml

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [输出一个变量] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "11MB,这是bb:adsfa dsfadsf"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

字典变量

所谓字典,简写为 doct,就是存储多个变量的容器,可以把字典理解为是一个木桶,里面存放了很多个变量,要是使用变量的话,必须要指定是哪个木桶里的变量。
dict-name 即上面的木桶,里面的 var1、var2、var3 等就是桶里存放的变量叫做 var-name。

在字典里定义变量时,变量前面是不加 - 的,并且定义变量没有先后顺序,引用的话,通过 dict-name.var-name 的格式来引用。

[root@server1 chap]# cat 2.yaml 
---
- hosts: server2
  vars:
    xx:
      aa: 11
      bb: 22
    yy:
      aa: 1111
      bb: 2222
  tasks:
  - name: 输出字典组xx
    debug:
      msg: "{{xx}}"
  - name: 输出xx中的aa
    debug:
      msg: "{{xx.aa}}"
[root@server1 chap]# ansible-playbook 2.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [输出字典组xx] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": {
        "aa": 11,
        "bb": 22
    }
}

TASK [输出xx中的aa] **************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "11"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

列表变量

由一个个的匿名字典组成,每个匿名字典都表示一个对象。

直接在 playbook 中定义及使用变量

[root@server1 chap]# cat 3.yaml 
---
- hosts: server2
  vars:
    users:
    - uname: tom
      age: 21
      sex: M
    - uname: bob
      age: 22
      sex: F
    - uname: mary
      age: 23
      sex: F
  tasks:
  - name: 打印所有对象
    debug:
      msg: "{{users}}"
  - name: 打印第一个对象
    debug:
      msg: "{{users[0]}}"
  - name: 打印第一个对象中的uname
    debug:
      msg: "{{users[0].uname}}"
[root@server1 chap]# ansible-playbook 3.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印所有对象] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        {
            "age": 21,
            "sex": "M",
            "uname": "tom"
        },
        {
            "age": 22,
            "sex": "F",
            "uname": "bob"
        },
        {
            "age": 23,
            "sex": "F",
            "uname": "mary"
        }
    ]
}

TASK [打印第一个对象] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": {
        "age": 21,
        "sex": "M",
        "uname": "tom"
    }
}

TASK [打印第一个对象中的uname] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "tom"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 

通过文件定义变量,在脚本中引用

[root@server1 chap]# cat users.txt 
xx: 111
yy: 222
users:
- uname: tom
  age: 21
  sex: M
- uname: bob
  age: 22
  sex: F
- uname: mary
  age: 23
  sex: F
[root@server1 chap]# cat 4.yaml 
---
- hosts: server2
  vars:
    aa: "{{xx}}"
  vars_files:
  - users.txt
  tasks:
  - name: 打印所有对象
    debug:
      msg: "{{users}}"
  - name: 打印第一个对象
    debug:
      msg: "{{users[0]}}"
  - name: 打印第一个对象中的uname
    debug:
      msg: "{{users[0].uname}}"
[root@server1 chap]# ansible-playbook 4.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印所有对象] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        {
            "age": 21,
            "sex": "M",
            "uname": "tom"
        },
        {
            "age": 22,
            "sex": "F",
            "uname": "bob"
        },
        {
            "age": 23,
            "sex": "F",
            "uname": "mary"
        }
    ]
}

TASK [打印第一个对象] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": {
        "age": 21,
        "sex": "M",
        "uname": "tom"
    }
}

TASK [打印第一个对象中的uname] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "tom"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]#

注册变量

[root@server1 chap]# cat 5.yaml 
---
- hosts: server2
  tasks:
  - name: 执行命令
    shell: "whoami"
    register: aa
  - name: 显示命令内容
    debug: msg={{aa}}
  - name: 显示命令输出
    debug: msg={{aa.stdout}}
  - name: 显示命令执行的返回值
    debug: msg={{aa.rc}}
[root@server1 chap]# ansible-playbook 5.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [执行命令] ******************************************************************************************************************************************************************************************************************************************************************
changed: [server2]

TASK [显示命令内容] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": {
        "changed": true,
        "cmd": "whoami",
        "delta": "0:00:00.004190",
        "end": "2022-12-11 04:38:27.134560",
        "failed": false,
        "rc": 0,
        "start": "2022-12-11 04:38:27.130370",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "root",
        "stdout_lines": [
            "root"
        ]
    }
}

TASK [显示命令输出] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "root"
}

TASK [显示命令执行的返回值] ************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "0"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 

facts 变量

[root@server1 chap]# cat 6.yaml 
---
- hosts: server2
  tasks:
  - name: 打印主机名
    debug: msg="{{ansible_fqdn}}"
  - name: 打印IP
    debug: msg="{{ansible_default_ipv4.address}}"
  - name: 打印/dev/sda这个硬盘大小
    debug: msg="{{ansible_devices.sda.size}}"
  - name: 打印/dev/sda1这个分区的大小
    debug: msg="{{ansible_devices.sda.partitions.sda1.size}}"
  - name: 打印内存的总大小
    debug: msg="{{ansible_memtotal_mb}}MB"
  - name: 打印所使用的系统版本
    debug: msg="{{ansible_distribution_version}}"
[root@server1 chap]# ansible-playbook  6.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印主机名] *****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "server2.rhce.cc"
}

TASK [打印IP] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "192.168.88.137"
}

TASK [打印/dev/sda这个硬盘大小] ******************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "100.00 GB"
}

TASK [打印/dev/sda1这个分区的大小] ****************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "60.00 GB"
}

TASK [打印内存的总大小] **************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "1790MB"
}

TASK [打印所使用的系统版本] *****************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "8.4"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 

内置变量

groups
[root@server1 chap]# cat 7.yaml 
---
- hosts: server2
  tasks:
  - name: 打印groups这个内置变量
    debug: msg="{{groups}}"
  - name: 打印有哪些主机组,不打印主机
    debug: msg="{{group_names}}"
  - name: 打印web主机组这个内置变量
    debug: msg="{{groups.web}}"
  - name: 打印web主机组这个内置变量
    debug: msg="{{groups['web']}}"
[root@server1 chap]# ansible-playbook  7.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印groups这个内置变量] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": {
        "all": [
            "server1",
            "server2",
            "server3"
        ],
        "ungrouped": [],
        "web": [
            "server1",
            "server2",
            "server3"
        ]
    }
}

TASK [打印有哪些主机组,不打印主机] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        "web"
    ]
}

TASK [打印web主机组这个内置变量] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        "server1",
        "server2",
        "server3"
    ]
}

TASK [打印web主机组这个内置变量] ********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        "server1",
        "server2",
        "server3"
    ]
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 
hostvars

在playbook 中,如果定义了
gather_facts: no
则不会去获取远端 facts变量,则不能在脚本中使用 fects 变量,均为未定义变量

[root@server1 chap]# cat 8.yaml 
---
- hosts: server3
- hosts: server2
  gather_facts: no
  tasks:
  - name: 打印IP
    #debug: msg="{{ansible_default_ipv4.address}}"
    debug: msg="{{hostvars['server3'].ansible_default_ipv4.address}}"
[root@server1 chap]# ansible-playbook 8.yaml 

PLAY [server3] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server3]

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [打印IP] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "192.168.88.138"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
server3                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 
inventory_hostname
 [root@server1 chap]# cat hosts 
[web]
server1
#server2
192.168.88.137
server3
[root@server1 chap]# cat 9.yaml 
---
- hosts: all
  tasks:
  - name: 打印主机名--方法一
    debug: msg="{{ansible_fqdn}}"
  - name: 打印主机名--方法二
    debug: msg="{{ansible_nodename}}"
  - name: 打印短主机名
    debug: msg="{{ansible_hostname}}"
  - name: 打印在清单文件 ./hosts 中定义的主机名
    debug: msg="{{inventory_hostname}}"
[root@server1 chap]# ansible-playbook 9.yaml 

PLAY [all] *******************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [192.168.88.137]
ok: [server1]
ok: [server3]

TASK [打印主机名--方法一] ************************************************************************************************************************************************************************************************************************************************************
ok: [server1] => {
    "msg": "server1.rhce.cc"
}
ok: [192.168.88.137] => {
    "msg": "server2.rhce.cc"
}
ok: [server3] => {
    "msg": "server3.rhce.cc"
}

TASK [打印主机名--方法二] ************************************************************************************************************************************************************************************************************************************************************
ok: [server1] => {
    "msg": "server1.rhce.cc"
}
ok: [192.168.88.137] => {
    "msg": "server2"
}
ok: [server3] => {
    "msg": "server3"
}

TASK [打印短主机名] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server1] => {
    "msg": "server1"
}
ok: [192.168.88.137] => {
    "msg": "server2"
}
ok: [server3] => {
    "msg": "server3"
}

TASK [打印在清单文件 ./hosts 中定义的主机名] ***********************************************************************************************************************************************************************************************************************************************
ok: [server3] => {
    "msg": "server3"
}
ok: [server1] => {
    "msg": "server1"
}
ok: [192.168.88.137] => {
    "msg": "192.168.88.137"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
192.168.88.137             : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
server1                    : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
server3                    : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 

变量的过滤器

数字变量的运算

在yaml文件中如果定义的变量,它的值是数字的话,可以对其数值进行加数学运算的。

如下,定义变量 xxx值为3,一次对其进行加减乘除运算

[root@server1 chap]# cat 10.yaml 
---
- hosts: server2
  vars:
    xxx: 3
  tasks:
  - name: print+
    debug: msg="{{xxx+10}}"
  - name: print-
    debug: msg="{{xxx-10}}"
  - name: printx
    debug: msg="{{xxx*10}}"
  - name: print/
    debug: msg="{{xxx/10}}"
  - name: print^
    debug: msg="{{xxx**2}}"
[root@server1 chap]# ansible-playbook 10.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [print+] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "13"
}

TASK [print-] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "-7"
}

TASK [printx] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "30"
}

TASK [print/] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "0.3"
}

TASK [print^] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "9"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]#
数字类型

数字类型的函数,int整型、float浮点类型、abs绝对值、round四舍五入、random随机数
如整型 int ,可以把字符串转变为整型类型

# 把字符串 '10' 转成 int 类型与 xxx的3 进行相加
[root@server1 chap]# cat 11.yaml 
---
- hosts: server2
  vars:
    xxx: 3
  tasks:
  - name: print+
    debug: msg="{{xxx+('10'|int)}}"
  - name: default
    debug: msg="{{yyy | default('NONE')}}"
[root@server1 chap]# ansible-playbook 11.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [print+] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "13"
}

TASK [default] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "NONE"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]#
列表

max、length、sort、sum、shuffle打乱顺序显示

[root@server1 chap]# cat 12.yaml 
---
- hosts: server2
  vars:
    list1: [1,19,8,0,20,5]
  tasks:
  - name: 打印列表最大值
    debug: msg="{{list1|max}}"
  - name: 打印列表长度,有多少元素
    debug: msg="{{list1|length}}"
  - name: 对列表排序
    debug: msg="{{list1|sort}}"
[root@server1 chap]# ansible-playbook  12.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印列表最大值] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "20"
}

TASK [打印列表长度,有多少元素] **********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "6"
}

TASK [对列表排序] *****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": [
        0,
        1,
        5,
        8,
        19,
        20
    ]
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
字符串相关
[root@server1 chap]# cat 13.yaml 
---
- hosts: server2
  vars:
    aa: "qweqw fdasf"
  tasks:
  - name: 打印变量
    debug: msg="{{aa}}"
  - name: 打印转为大写后的字符串
    debug: msg="{{aa|upper}}"
  - name: 大写变为小写的
    debug: msg="{{aa|lower}}"
[root@server1 chap]# ansible-playbook  13.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [打印变量] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "qweqw fdasf"
}

TASK [打印转为大写后的字符串] ***********************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "QWEQW FDASF"
}

TASK [大写变为小写的] ***************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "qweqw fdasf"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# 
加密相关
[root@server1 chap]# cat 14.yaml 
---
- hosts: server2
  vars:
    aa: "qweqw fdasf"
  tasks:
  - name: md5加密
    debug: msg="{{aa|hash('md5')}}"
  - name: sha1加密
    debug: msg="{{aa|hash('sha1')}}"
  - name: sha512加密
    debug: msg="{{aa|hash('sha512')}}"
  - name: passwd加密
    debug: msg="{{aa|password_hash('sha512')}}"
[root@server1 chap]# ansible-playbook 14.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [md5加密] *****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "969073c3aa5064bb6d780b02e68de3cd"
}

TASK [sha1加密] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "f2046b1529507de02c7a00a2aa9ecae10043239c"
}

TASK [sha512加密] **************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "7fe6f876088d2dac8a7945e958154eae007b9666d43b62867b852913c05437b8e4eb3f712d381a8ea90e0b56fb3372e5add293eb5968e7ba14727f6ffce0b19f"
}

TASK [passwd加密] **************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "$6$Ug991ZyACqMR02it$sXxBJepaq7GwzqLuaMJdj04RNZXuz9h4e/ar7rheup5iubiYS.39SG6QzZ/TfiPSuk1N8lbf4auKoiGg9Op1E/"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

对 playbook 进行加密,使用 ansible-vault
加密: ansible-vault encrypt xxx.yaml
解密: ansible-vault decrypt xxx.yaml
查看加密内容: ansible-vault view xxx.yaml

密码也可通过密码文件进行传递
ansible-vault encrypt --vault-id xxx.enc xxx.yaml
ansible-vault view --vault-id xxx.enc xxx.yaml
ansible-vault decrypt --vault-id xxx.enc xxx.yaml

[root@server1 chap]# ansible-vault encrypt 1.yaml 
New Vault password: 
Confirm New Vault password: 
Encryption successful
[root@server1 chap]# cat 1.yaml 
$ANSIBLE_VAULT;1.1;AES256
61633863316634323031316331366535643761373238613732376432323661623836303163663932
3263393639643436326466383661663063303466626334360a663264303136643430376130383632
62396237616264623264663166663465636636343633613839393464643666346563616462653162
3436366137633566350a363736623164613334386564383763636532353734393536383764326561
62646330393739393562393332636266393538333638316630656634346331306332353532316265
37306532326635353639333234323436373431636130333265393137623864396262353637306230
34616536356334383732363532313833626236663761636631393732383632636366653938316263
65326439636261656333656236656466326536656537303135323466663434303136356631633865
36633862323235653661393234643335633032333230613335363834636261663666373262666136
33333939356463336633636134623838626663663664356330666131326339623636613931303166
35666235613333643561343039353739396230663232633738356637333361633131356230393338
30353830636433323865
[root@server1 chap]# #被加密后的 1.yaml 不能运行
[root@server1 chap]# ansible-playbook --ask-vault-pass 1.yaml 
Vault password: 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [输出一个变量] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "11MB,这是bb:adsfa dsfadsf"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap]# ansible-vault view 1.yaml 
Vault password: 
---
- hosts: server2
  vars:
    aa: 11
    bb: "adsfa dsfadsf"
  tasks:
  - name: 输出一个变量
    debug: msg="{{aa}}MB,这是bb:{{bb}}"
[root@server1 chap]# ansible-vault decrypt 1.yaml 
Vault password: 
Decryption successful
[root@server1 chap]# cat 1.yaml 
---
- hosts: server2
  vars:
    aa: 11
    bb: "adsfa dsfadsf"
  tasks:
  - name: 输出一个变量
    debug: msg="{{aa}}MB,这是bb:{{bb}}"
[root@server1 chap]# 

想变更密码,可以先解密,再使用新密码加密;
也可以使用 : ansible-vault rekey xxx.yaml,根据提示输入新旧密码就ok。

判断语句

when: 当满足设定条件的时候执行某个task,常用判断包括:==等于、!=不等于、>大于、>=大于等于、<小于、<=小于等于、in、is

[root@server1 chap]# cat 15.yaml 
---
- hosts: web
  vars:
    aa: 1
    list1: [1,20,30,22,11]
    bb: 
  tasks:
  - name: aaaa
    debug: msg="111"
    when: aa >= 2 and (3 >= 2 or 3 == 2)
  - name: bbbb
    debug: msg="2222"
    when: ansible_default_ipv4.address == "192.168.88.137" 
  - name: wheninnotin
    debug: msg="3333"
    when: 1 not in list1
  - name: whenis
    debug: msg="4444"
    when : aa is defined
  - name: whenis2
    debug: msg="5555"
    when : aa is undefined
[root@server1 chap]# ansible-playbook 15.yaml 

PLAY [web] *******************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]
ok: [server1]
ok: [server3]

TASK [aaaa] ******************************************************************************************************************************************************************************************************************************************************************
skipping: [server1]
skipping: [server2]
skipping: [server3]

TASK [bbbb] ******************************************************************************************************************************************************************************************************************************************************************
skipping: [server1]
ok: [server2] => {
    "msg": "2222"
}
skipping: [server3]

TASK [wheninnotin] ***********************************************************************************************************************************************************************************************************************************************************
skipping: [server2]
skipping: [server3]
skipping: [server1]

TASK [whenis] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server1] => {
    "msg": "4444"
}
ok: [server2] => {
    "msg": "4444"
}
ok: [server3] => {
    "msg": "4444"
}

TASK [whenis2] ***************************************************************************************************************************************************************************************************************************************************************
skipping: [server3]
skipping: [server1]
skipping: [server2]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0   
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0   
server3                    : ok=2    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0   

[root@server1 chap]# 

错误处理

忽略报错:如果某个task出现问题,则后续的所有 task,包括其他 play 里的 task 都不会再执行,通过 ignore_errors: true 忽略执行错误,继续往下执行。

[root@server1 chap]# cat 16.yaml 
---
- hosts: server2
  vars:
  tasks:
  - name: aaaa
    shell: "cat /etc/host11"
    register: aa
    ignore_errors: true
  - name: 报错-没成功
    debug: msg="执行报错"
    when: aa.rc != 0
  - name: bbbb
    debug: msg="{{aa.stdout}}"
    when: aa.rc == 0
[root@server1 chap]# ansible-playbook 16.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [aaaa] ******************************************************************************************************************************************************************************************************************************************************************
fatal: [server2]: FAILED! => {"changed": true, "cmd": "cat /etc/host11", "delta": "0:00:00.003205", "end": "2022-12-12 06:04:05.600187", "msg": "non-zero return code", "rc": 1, "start": "2022-12-12 06:04:05.596982", "stderr": "cat: /etc/host11: 没有那个文件或目录", "stderr_lines": ["cat: /etc/host11: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [报错-没成功] ****************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "执行报错"
}

TASK [bbbb] ******************************************************************************************************************************************************************************************************************************************************************
skipping: [server2]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=3    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=1   

[root@server1 chap]#

有些步骤一旦执行报错,但是添加了 ignore_errors ,后续其他步骤再执行无意义,通过 fail 关键字,在执行完 fail 模块后,会退出 playbook。

[root@server1 chap]# cat 17.yaml 
---
- hosts: server2
  vars:
  tasks:
  - name: aaaa
    shell: "cat /etc/host11"
    register: aa
    ignore_errors: true
  - name: 报错-没成功
    fail: msg="执行报错"
    when: aa.rc != 0
  - name: bbbb
    debug: msg="{{aa.stdout}}"
    when: aa.rc == 0
[root@server1 chap]# ansible-playbook 17.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [aaaa] ******************************************************************************************************************************************************************************************************************************************************************
fatal: [server2]: FAILED! => {"changed": true, "cmd": "cat /etc/host11", "delta": "0:00:00.004167", "end": "2022-12-12 06:09:58.755833", "msg": "non-zero return code", "rc": 1, "start": "2022-12-12 06:09:58.751666", "stderr": "cat: /etc/host11: 没有那个文件或目录", "stderr_lines": ["cat: /etc/host11: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [报错-没成功] ****************************************************************************************************************************************************************************************************************************************************************
fatal: [server2]: FAILED! => {"changed": false, "msg": "执行报错"}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=1   

[root@server1 chap]#

block-rescue

只要有一个 task 没有成功,则block里后续的task就不再执行(除了加上 ignore_errors),整个block就被认为没成功,就会执行 rescue,如果rescue也失败,则会认为整体都失败,会退出 playbook。
block中如果都执行成功,则不再执行 rescue。

block:
  task1
  task2
rescue:
  taska
  taskb
[root@server1 chap]# cat 18.yaml 
---
- hosts: server2
  vars:
  tasks:
  - name: aaaa
    debug: msg="aaaaaaaaaaaaaaa"
  - name: taskbr
    block:
    - name: 1111
      shell: "ls /etc/hostx"
    - name: 2222
      debug: msg="2222"
    - name: 3333
      shell: "ls /etc/hosts"
    rescue:
    - name: xxxx
      shell: "ls /etc/hosts"
    - name: yyyy
      debug: msg="yyyy"
  - name: bbbb
    debug: msg="bbbbbbbbbbbbbb"
[root@server1 chap]# ansible-playbook 18.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [aaaa] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "aaaaaaaaaaaaaaa"
}

TASK [shell] *****************************************************************************************************************************************************************************************************************************************************************
fatal: [server2]: FAILED! => {"changed": true, "cmd": "ls /etc/hostx", "delta": "0:00:00.004147", "end": "2022-12-12 06:27:14.340792", "msg": "non-zero return code", "rc": 2, "start": "2022-12-12 06:27:14.336645", "stderr": "ls: 无法访问'/etc/hostx': 没有那个文件或目录", "stderr_lines": ["ls: 无法访问'/etc/hostx': 没有那个文件或目录"], "stdout": "", "stdout_lines": []}

TASK [xxxx] ******************************************************************************************************************************************************************************************************************************************************************
changed: [server2]

TASK [yyyy] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "yyyy"
}

TASK [bbbb] ******************************************************************************************************************************************************************************************************************************************************************
ok: [server2] => {
    "msg": "bbbbbbbbbbbbbb"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0   

[root@server1 chap]#

循环语句

loop关键字

[root@server1 chap]# cat users.txt 
xx: 111
yy: 222
users:
- uname: tom
  age: 21
  sex: M
- uname: bob
  age: 22
  sex: F
- uname: mary
  age: 23
  sex: F
[root@server1 chap]# cat 19.yaml 
---
- hosts: server2
  vars_files:
  - users.txt
  tasks:
  - name: 遍历用户
    debug: msg="{{item.uname}}"
    when: item.age >= 22
    loop: "{{users}}"
  - name: print xxx
    debug: msg="{{item.uname}}"
    when: item.age >=22
    loop:
    - uname: Abe
      age: 20
    - uname: Amy
      age: 23

[root@server1 chap]# ansible-playbook 19.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [遍历用户] ******************************************************************************************************************************************************************************************************************************************************************
skipping: [server2] => (item={'uname': 'tom', 'age': 21, 'sex': 'M'}) 
ok: [server2] => (item={'uname': 'bob', 'age': 22, 'sex': 'F'}) => {
    "msg": "bob"
}
ok: [server2] => (item={'uname': 'mary', 'age': 23, 'sex': 'F'}) => {
    "msg": "mary"
}

TASK [print xxx] *************************************************************************************************************************************************************************************************************************************************************
skipping: [server2] => (item={'uname': 'Abe', 'age': 20}) 
ok: [server2] => (item={'uname': 'Amy', 'age': 23}) => {
    "msg": "Amy"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

jinjia2模板

以下示例中,1.txt 这个文件内含有变量,这种文件被称为 jinjia2 模板,一般以 j2 为文件名后缀。

[root@server1 chap2]# cat 1.txt 
hello,你好,我是 {{myname}}
我的主机名是:{{ansible_fqdn}}
我的IP地址是:{{ansible_default_ipv4.address}}
[root@server1 chap2]# cat 1.yaml 
---
- hosts: server2
  vars:
    myname: zhuchun
  tasks:
  - name: 拷贝一个文件到主机里
    template: src=1.txt dest=/opt/1.txt
[root@server1 chap2]# ansible-playbook  1.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [拷贝一个文件到主机里] ************************************************************************************************************************************************************************************************************************************************************
ok: [server2]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap2]# 

[root@server2 ~]# cat /opt/1.txt 
hello,你好,我是 zhuchun
我的主机名是:server2.rhce.cc
我的IP地址是:192.168.88.137

在 jinjia2 模板文件里,也可以写循环和判断语句,不是在playbook里。

[root@server1 chap2]# cat aa.j2 
11111111111

{% if ansible_fqdn == "server3.rhce.cc" %}
aaaaaaaaaaaaa
{% endif %}

222222222222
[root@server1 chap2]# cat aa.yaml 
---
- hosts: server2
  vars:
    myname: zhuchun
  tasks:
  - name: 拷贝一个文件到主机里
    template: src=aa.j2 dest=/opt/aa.txt
[root@server1 chap2]# ansible-playbook aa.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [拷贝一个文件到主机里] ************************************************************************************************************************************************************************************************************************************************************
ok: [server2]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap2]# 

[root@server2 ~]# cat /opt/aa.txt 
11111111111

222222222222
[root@server2 ~]#

循环:

[root@server1 chap2]# cat users.txt 
xx: 111
yy: 222
users:
- uname: tom
  age: 21
  sex: M
- uname: bob
  age: 22
  sex: F
- uname: mary
  age: 23
  sex: F
[root@server1 chap2]# cat bb.j2 
1111111111

{% for i in users %}
  {{i.uname}}
{% endfor %}

2222222222
[root@server1 chap2]# cat bb.yaml 
---
- hosts: server2
  vars_files:
  - users.txt
  tasks:
  - name: 拷贝一个文件到主机里
    template: src=bb.j2 dest=/opt/bb.txt
[root@server1 chap2]# ansible-playbook bb.yaml 

PLAY [server2] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************
ok: [server2]

TASK [拷贝一个文件到主机里] ************************************************************************************************************************************************************************************************************************************************************
changed: [server2]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************
server2                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@server1 chap2]#

[root@server2 ~]# cat /opt/bb.txt 
1111111111

  tom
  bob
  mary

2222222222
[root@server2 ~]# 

handlers

用于做触发,当某个 task 执行了,则可以触发另外的一种行为。

可以有多个handlers,在tasks中关联触发的那个 name 就调用哪个 handlers

handlers:
- name: restart httpd1
  service: name=httpd state=restarted
- name: restart httpd2
  service: name=httpd state=restarted

然后在tasks中,通过 notify关键字触发 handlers,当 j2 配置文件发生变更时,则会触发 handler执行。

tasks:
- name: 拷贝配置文件
  template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  notify: restart httpd1

管理大型项目

并行运行

ansible在多台机器执行task的时候,默认是5台5台的运行,100台机器则分成20组来执行(/etc/ansible/ansible.cfg 文件中的 forks),实际使用中,根据实际情况,调整 forks的值。

在多台机器上同时执行某个task

滚动更新

在某台机器上执行完所有的task,逐台执行,滚动升级

在 playbook 中声明 serial 并指定机器数量既可

- hosts: server2
  serial: 1
  tasks:
  ...

serial: 1 表示逐台执行所有task
serial: 3 同理表示3台执行完所有task后再在下面三台执行所有 task

包含

通过 include 关键字引入其他 yaml
通过 import_tasks 关键字引入其他 yaml 中的 tasks

角色

ansible-galaxy init apache 创建一个 role

定义变量写入 defaults/main.yaml 或者 vars/main.yaml ,defaults 里的优先级比较低

tasks 写入 tasks/main.yaml 文件中

handlers 写入 handlers/main.yaml 文件中

templates 中放 j2 模板

files 中放需要拷贝的文件

meta 里面是注释信息

- hosts: server2
  vars:
    myport: 80
  roles:
  - role: apache
- hosts: server2
  vars:
    myport: 80
  roles:
  - { role: apache,tags: ["aa", "xx"] }
  - { role: http,tags: ["bb", "xx"] }

ansible-playbook -t aa xx.yaml # 只会执行含有aa标签的角色

系统自带角色

[root@server1 chap2]# yum install rhel-system-roles.noarch -y
[root@server1 chap2]# ls /usr/share/ansible/roles/
linux-system-roles.certificate      linux-system-roles.logging      linux-system-roles.postfix  linux-system-roles.timesync        rhel-system-roles.kdump            rhel-system-roles.nbde_server  rhel-system-roles.sshd
linux-system-roles.crypto_policies  linux-system-roles.metrics      linux-system-roles.selinux  linux-system-roles.tlog            rhel-system-roles.kernel_settings  rhel-system-roles.network      rhel-system-roles.storage
linux-system-roles.ha_cluster       linux-system-roles.nbde_client  linux-system-roles.ssh      rhel-system-roles.certificate      rhel-system-roles.logging          rhel-system-roles.postfix      rhel-system-roles.timesync
linux-system-roles.kdump            linux-system-roles.nbde_server  linux-system-roles.sshd     rhel-system-roles.crypto_policies  rhel-system-roles.metrics          rhel-system-roles.selinux      rhel-system-roles.tlog
linux-system-roles.kernel_settings  linux-system-roles.network      linux-system-roles.storage  rhel-system-roles.ha_cluster       rhel-system-roles.nbde_client      rhel-system-roles.ssh

使用时,可以在ansible.cfg 中通过冒号引入多个 role_path 的路径
或者把系统角色直接拷贝过来使用

ansible-galaxy

世界各地的 ansible 用户,会有些自定义的role 会传到 galaxy.ansible.com 中,类似 role 的仓库

# 安装方式一:
ansible-galaxy install ftp://ftp.rhce.cc/auto/web.tar.gz
# 安装方式二:
ansible-galaxy install -r aa.yaml -p roles
aa.yaml 内容格式为:
- src: ftp://ftp.rhce.cc/auto/web.tar.gz
  name: webxxx

故障排除

# 检查语法错误
ansible-playbook 1.yaml --syntax-check

# 模拟执行,不做任何改变
ansible-playbook 1.yaml -C
ansible-playbook 1.yaml --check
或者配置文件 check_mode: yes

# 一步步执行进行排错
ansible-playbook 1.yaml --step

# 从特定的 play 执行
ansible-playbook 1.yaml --start-at-task='taskaaa'

# 查看详细报错
ansible-playbook 1.yaml -v
ansible-playbook 1.yaml -vv
ansible-playbook 1.yaml -vvv
ansible-playbook 1.yaml -vvvv

# 记录日志
在 ansible.cfg 文件中配置:
log_path=./xxx.log

自动执行Linux任务

# cron模块
ansible server2 -m cron -a "name='aa' user=root weekday=1-3 day=1-10 hour=* minute=0 job='rm -rf /tmp'"

# reboot 模块
ansible server2 -m reboot -a "reboot_timeout=10"