1
2
3
4
5
作者:李晓辉

微信联系:lxh_chat

联系邮箱: 939958092@qq.com

第一章 提⾼命令⾏运⾏效率

虽然一个一个命令执行也是能完成大部分的工作,但是这效率低且非常消耗精神,借助 Bash shell 环境和脚本编写功能,您可以通过将 Linux 命令与 shell 脚本组合在⼀起,解决重复⽽困难的实际问题。

Bash shell 脚本是⼀种可执⾏的⽂件,其中包含了命令列表,⽽且可能还包含⽤于控制整体任务决策的编程逻辑,bash脚本的后缀名一般为.sh,执行bash脚本,需要给脚本加上执行权限或者用bash 命令去执行。

脚本编辑器

有些人用txt或者Windows记事本写脚本,但这种效率很低,也容易出错,选择一个高级编辑器是编写脚本的必要手段,高级编辑器提供语法检查、语法高亮等功能,比如Linux里的vim或者vscode都是不错的编辑器,注意不要选择notepad++,这个作者在官网和公网上公然反华,支持港台独立等,以下是vscode官方下载页面,打开后会自动下载便携版,不需要安装,解压即可使用

1
https://code.visualstudio.com/docs/?dv=winzip

指定命令解释器

脚本的第⼀⾏以符号 #! 开头,通常称为 she-bang 或 hash-bang,其源于这两个字符的名称,sharp 或 hash 以及 bang

1
#!/usr/bin/bash

/usr/bin/bash是一个解释器的文件位置,这告诉系统,接下来我写的内容,由谁来负责翻译和执行

PATH 环境变量

一般来说,我们不需要特别声明PATH环境变量,只有当我们在脚本中使用的命令不在默认的PATH路径中的时候,才需要声明

管理PATH路径

以下路径以冒号隔开,如果你的脚本中,命令都放在以下位置,就不需要声明

1
2
[root@lixiaohui ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

假设我们的命令在/mnt,就需要在脚本中第二行申明新的PATH变量

1
2
3
[root@lixiaohui ~]# PATH=$PATH:/mnt
[root@lixiaohui ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/mnt

查询某一个在PATH变量路径中的命令位于服务器的具体位置:

1
2
[root@lixiaohui ~]# which useradd
/usr/sbin/useradd

特殊字符

部分字符和词语对 Bash shell 具有特殊含义,不能直接使用,需要使用转义或者其他引号处理

  1. 转义字符 \

转义字符 \ 用于在字符串中转义特殊字符,使其成为普通字符。

示例:

在这个例子中,$ 转义了 $ 符号,因此 $USER 被打印成普通字符串,而不是变量。

1
echo "Hello, \$USER"  # 输出: Hello, $USER
  1. 单引号 ' '

单引号内的内容会被原样处理,不会解析其中的变量或转义字符。

示例:

在这个例子中,单引号内的 $USER 被当作普通字符串,而不是变量。

1
echo 'Hello, $USER'  # 输出: Hello, $USER
  1. 双引号 " "

双引号内的内容会解析变量和转义字符,但不会解析特殊字符(如空格和换行符)。

示例:

在这个例子中,双引号内的 $USER 变量会被解析并替换为实际的用户名。

1
echo "Hello, $USER"  # 输出: Hello, your_username
  1. 反撇号 `` 和 $()

反撇号和 $() 用于命令替换,即在执行命令后将其输出作为字符串插入到命令中。

示例

在这个例子中,反撇号和 $() 都用于执行 date 命令,并将其输出插入到 echo 命令中。

1
2
3
echo "Today is `date`"   # 使用反撇号
echo "Today is $(date)" # 使用 $()
# 输出: Today is Sun Dec 24 12:34:56 CST 2024

符号总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 定义变量
name="Alice"

# 使用单引号
echo 'Hello, $name, today is `date`' # 输出: Hello, $name, today is `date`

# 使用双引号
echo "Hello, $name, today is `date`" # 输出: Hello, Alice, today is `date`

# 使用转义字符
echo "Hello, \$name, today is \`date\`" # 输出: Hello, $name, today is `date`

# 使用反撇号
echo "Hello, $name, today is $(date)" # 输出: Hello, Alice, today is Sun Dec 24 12:34:56 CST 2024

脚本输出处理

  1. 正常输出内容重定向: > filename

  2. 错误输出内容重定向: 2> errorfile

  3. 正常和错误合并输出到文件: &> filename

  4. 正常和错误不做保留只丢弃: &> /dev/null

循环和条件结构

在编程中,循环和条件结构是两个基本但非常重要的概念。它们帮助我们控制代码的执行流程,根据特定条件执行不同的代码块,并重复执行某些代码块。

条件结构

条件结构用于根据特定条件执行不同的代码块。在 Bash 中,常见的条件结构是 if 语句。

if 语句

语法:

1
2
3
4
5
6
7
if [ condition ]; then
# 当 condition 为真时执行的代码
elif [ another_condition ]; then
# 当 another_condition 为真时执行的代码
else
# 当条件都不为真时执行的代码
fi

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat > if.sh <<-'EOF'
#!/bin/bash
echo "请输入一个数字:"
read number

if [ $number -gt 10 ]; then
echo "你输入的数字大于 10"
elif [ $number -eq 10 ]; then
echo "你输入的数字等于 10"
else
echo "你输入的数字小于 10"
fi
EOF

执行看看效果

1
2
3
4
5
6
7
8
[root@lixiaohui ~]# bash if.sh
请输入一个数字:
5
你输入的数字小于 10
[root@lixiaohui ~]# bash if.sh
请输入一个数字:
12
你输入的数字大于 10

case 语句

case 语句根据变量的值匹配不同的模式,并执行相应的代码块。当一个模式匹配时,case 语句执行相应的代码块并跳出结构。

语法:

1
2
3
4
5
6
7
8
9
10
11
case $variable in
pattern1)
# 当变量匹配 pattern1 时执行的代码
;;
pattern2)
# 当变量匹配 pattern2 时执行的代码
;;
*)
# 以上模式都不匹配时执行的代码
;;
esac

示例:

解释:

读取输入: 使用 read 命令读取用户输入的星期几。

模式匹配: 根据用户输入的值匹配不同的模式:

如果输入是 Mon,则输出 “今天是星期一”。

如果输入是 Tue,则输出 “今天是星期二”。

类似地,处理其他几天的输入。

如果输入是 Sat 或 Sun,则输出 “今天是周末”。

如果输入不匹配任何模式,则输出 “无效的输入”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

echo "请输入一个星期几(例如,Mon, Tue, Wed, ...):"
read day

case $day in
"Mon")
echo "今天是星期一"
;;
"Tue")
echo "今天是星期二"
;;
"Wed")
echo "今天是星期三"
;;
"Thu")
echo "今天是星期四"
;;
"Fri")
echo "今天是星期五"
;;
"Sat" | "Sun")
echo "今天是周末"
;;
*)
echo "无效的输入"
;;
esac

循环结构

循环结构用于重复执行一段代码。Bash 中有几种常见的循环结构,包括 for 循环、while 循环和 until 循环。

for 循环

语法:

1
2
3
for var in list; do
# 执行的代码
done

示例

1
2
3
4
5
#!/bin/bash

for i in {1..5}; do
echo "这是第 $i 次循环"
done

while 循环

while 循环会在给定的条件为真(true)时执行循环体内的代码块。当条件为假(false)时,循环终止。

语法:

1
2
3
while [ condition ]; do
# 执行的代码
done

示例:

解释:

初始化: counter=1 初始化计数器变量。

条件检查: [ $counter -le 5 ] 检查计数器是否小于或等于 5。

循环体: echo “这是第 $counter 次循环” 打印当前循环次数,然后 ((counter++)) 将计数器递增。

循环结束: 当 counter 超过 5 时,条件变为假,循环终止。

1
2
3
4
5
6
7
#!/bin/bash

counter=1
while [ $counter -le 5 ]; do
echo "这是第 $counter 次循环"
((counter++))
done

until 循环

until 循环会在给定的条件为假(false)时执行循环体内的代码块。当条件为真(true)时,循环终止。

语法:

1
2
3
until [ condition ]; do
# 执行的代码
done

示例:

解释:

初始化: counter=1 初始化计数器变量。

条件检查: [ $counter -gt 5 ] 检查计数器是否大于 5。

循环体: echo “这是第 $counter 次循环” 打印当前循环次数,然后 ((counter++)) 将计数器递增。

循环结束: 当 counter 大于 5 时,条件变为真,循环终止。

1
2
3
4
5
6
7
#!/bin/bash

counter=1
until [ $counter -gt 5 ]; do
echo "这是第 $counter 次循环"
((counter++))
done

Bash 脚本退出代码

Bash 脚本退出代码(Exit Codes)用于指示脚本或命令执行的结果。退出代码是一个整数,通常在命令或脚本执行完成后返回。标准惯例是,退出代码为 0 表示成功,任何非 0 的值表示失败或错误。

检查上一个命令的退出代码

在 Bash 中,可以使用内置变量 $? 检查上一个命令的退出代码:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

# 执行命令
ls /some/nonexistent/directory

# 检查退出代码
if [ $? -eq 0 ]; then
echo "命令成功执行"
else
echo "命令执行失败"
fi

设置自定义退出代码

在脚本中,可以使用 exit 命令设置自定义的退出代码:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

# 执行一些操作
if [ some_condition ]; then
echo "成功"
exit 0
else
echo "失败"
exit 1
fi

常见退出代码

0: 成功(Success)

1: 一般错误(General error)

2: 错误的用法(Misuse of shell builtins)

126: 命令不可执行(Command invoked cannot execute)

127: 命令未找到(Command not found)

128: 无效的退出参数(Invalid argument to exit)

130: 命令通过 Ctrl+C 被终止(Script terminated by Control-C)

255: 退出状态码超出范围(Exit status out of range)

test 命令

test 命令用于检查某些条件是否为真,并返回相应的退出状态码。它在编写脚本时非常有用,可以用于测试字符串、文件、目录以及数值比较等。

常用表达式总结

字符串比较:

-z STRING:字符串为空

-n STRING:字符串非空

STRING1 = STRING2:两个字符串相等

STRING1 != STRING2:两个字符串不相等

文件和目录测试:

-e FILE:文件存在

-r FILE:文件可读

-w FILE:文件可写

-x FILE:文件可执行

-d FILE:是目录

-f FILE:是常规文件

数值比较:

NUM1 -eq NUM2:两个数值相等

NUM1 -ne NUM2:两个数值不相等

NUM1 -gt NUM2:NUM1 大于 NUM2

NUM1 -lt NUM2:NUM1 小于 NUM2

NUM1 -ge NUM2:NUM1 大于或等于 NUM2

NUM1 -le NUM2:NUM1 小于或等于 NUM2

语法:

1
test EXPRESSION

测试字符串

test 命令可以用于测试字符串的各种属性,包括字符串是否为空、两个字符串是否相等等。

示例:

测试字符串是否为空:

1
2
3
4
5
6
str=""
if [ -z "$str" ]; then
echo "字符串为空"
else
echo "字符串不为空"
fi

测试字符串是否非空:

1
2
3
4
5
6
str="Hello"
if [ -n "$str" ]; then
echo "字符串不为空"
else
echo "字符串为空"
fi

测试两个字符串是否相等:

1
2
3
4
5
6
7
str1="Hello"
str2="Hello"
if [ "$str1" = "$str2" ]; then
echo "两个字符串相等"
else
echo "两个字符串不相等"
fi

测试目录和文件

test 命令还可以用于测试文件和目录的各种属性,例如文件是否存在、是否可读、是否为目录等。

示例:

测试文件是否存在:

1
2
3
4
5
if [ -e "/path/to/file" ]; then
echo "文件存在"
else
echo "文件不存在"
fi

测试文件是否可读:

1
2
3
4
5
if [ -r "/path/to/file" ]; then
echo "文件可读"
else
echo "文件不可读"
fi

测试路径是否为目录:

1
2
3
4
5
if [ -d "/path/to/directory" ]; then
echo "这是一个目录"
else
echo "这不是一个目录"
fi

测试文件是否为常规文件:

1
2
3
4
5
if [ -f "/path/to/file" ]; then
echo "这是一个常规文件"
else
echo "这不是一个常规文件"
fi

数值比较

test 命令可以用于比较数值,例如检查两个数值是否相等、一个数值是否大于另一个数值等。

示例:

比较两个数值是否相等:

1
2
3
4
5
6
7
num1=5
num2=5
if [ $num1 -eq $num2 ]; then
echo "两个数值相等"
else
echo "两个数值不相等"
fi

比较一个数值是否大于另一个数值:

1
2
3
4
5
6
7
num1=5
num2=3
if [ $num1 -gt $num2 ]; then
echo "$num1 大于 $num2"
else
echo "$num1 不大于 $num2"
fi

比较一个数值是否小于另一个数值:

1
2
3
4
5
6
7
num1=5
num2=8
if [ $num1 -lt $num2 ]; then
echo "$num1 小于 $num2"
else
echo "$num1 不小于 $num2"
fi

正则表达式匹配输出

grep 是 Linux 中非常强大的命令行工具,用于搜索文本文件中的内容。它支持使用正则表达式(regular expressions)来匹配复杂的搜索模式。

grep 命令的基本语法如下:

1
grep [选项] PATTERN [文件...]

常用选项和正则介绍

常用选项

-i:忽略大小写

-v:显示不匹配的行

-r:递归搜索目录

-n:显示匹配行的行号

-l:仅显示包含匹配模式的文件名

-c:显示匹配的行数

-E:使用扩展正则表达式(等同于 egrep)

-q 选项用于静默模式(quiet mode),它会抑制所有输出,只返回退出状态码

-A 选项用于显示匹配行之后的指定行数,格式为 -A NUM,其中 NUM 是显示的行数。

-B 选项用于显示匹配行之前的指定行数,格式为 -B NUM,其中 NUM 是显示的行数。

-e 选项用于指定多个搜索模式

常见的正则表达式符号:

.:匹配任意单个字符

^:匹配行的开头

$:匹配行的结尾

*:匹配前面的字符零次或多次

+:匹配前面的字符一次或多次(需要使用扩展正则表达式 -E)

?:匹配前面的字符零次或一次(需要使用扩展正则表达式 -E)

[]:匹配括号内的任意字符

|:表示“或”(需要使用扩展正则表达式 -E)

匹配案例

假设有一个文件 example.txt,内容如下:

1
2
3
4
5
apple
banana
Apple
orange
grape

简单匹配:

1
grep 'apple' example.txt 

输出

1
apple

忽略大小写:

1
grep -i 'apple' example.txt

输出

1
2
apple
Apple

显示行号:

1
grep -n 'apple' example.txt

输出

1
1:apple

匹配行的开头:

1
grep '^a' example.txt

输出

1
apple

匹配行的结尾:

1
grep 'e$' example.txt

输出

1
2
apple
orange

匹配任意字符:

1
grep 'a.p' example.txt

输出:

1
apple

匹配零次或多次:

1
grep 'a*' example.txt

输出:

1
2
3
4
5
apple
banana
Apple
orange
grape

匹配一次或多次(使用扩展正则表达式 -E):

1
grep -E 'a+' example.txt

输出:

1
2
3
4
apple
banana
orange
grape

使用“或”操作符(扩展正则表达式 -E):

1
grep -E 'apple|orange' example.txt

输出:

1
2
apple
orange

静默模式

1
2
3
[root@lixiaohui ~]# grep -q apple example.txt
[root@lixiaohui ~]# echo $?
0

匹配行之后的指定行数

1
2
3
4
[root@lixiaohui ~]# grep -A 2 apple example.txt
apple
banana
Apple

匹配行之前的指定行数

1
2
3
4
[root@lixiaohui ~]# grep -B 2 Apple example.txt
apple
banana
Apple

指定多个搜索模式

1
2
3
[root@lixiaohui ~]# grep -e ^a -e a$ example.txt
apple
banana

不匹配指定模式的行

1
2
3
4
5
[root@lixiaohui ~]# grep -v 'apple' example.txt
banana
Apple
orange
grape

第二章 调度未来任务

在这一章,我们主要是学习在Linux系统中,怎么调度任务,以及让任务在未来的某个时间执行

atd 服务

atd 服务用于在特定时间执行一次性任务,通常通过 at 命令进行管理。它是一个非常适合运行在未来某个时间点执行的任务的工具。此工具安排的任务会放在/var/spool/at/

先启动此服务

1
2
sudo systemctl start atd
sudo systemctl enable atd # 设置为开机启动

这个命令将在当前时间后一分钟执行 echo ‘Hello World’ > /tmp/hello.txt,将结果写入 /tmp/hello.txt 文件。

1
2
3
[root@lixiaohui ~]# echo "echo 'Hello World' > /tmp/hello.txt" | at now + 1 minute
warning: commands will be executed using /bin/sh
job 1 at Mon Dec 23 01:57:00 2024

查询一下看看

1
2
[root@lixiaohui ~]# atq
1 Mon Dec 23 01:57:00 2024 a root

当然,也可以用交互式完成任务安排

需要注意的是,此命令不校验你的命令输入是否正确,且退出是用的ctrl d快捷键

1
2
3
4
5
[root@lixiaohui ~]# at 15:30
warning: commands will be executed using /bin/sh
at> echo hello xiaohui > /tmp/hello.txt
at> <EOT>
job 2 at Mon Dec 23 15:30:00 2024

删除某个at任务,后面的2是任务编号

1
atrm 2

crond 服务

crond 服务用于定期执行计划任务,通常通过 cron 表达式进行管理。它适用于需要周期性运行的任务。用crontab安排的命令将放在/var/spool/cron/

先启动服务

1
2
sudo systemctl start crond
sudo systemctl enable crond # 设置为开机启动

周期性计划,需要了解时间的安排,以下配置文件中可以看到时间安排和时间取值范围:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lixiaohui ~]# cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

一般而言,超级管理员为系统安排的计划任务,有可能会放入上述配置文件,不过实际情况下,我们都是为用户安排任务,例如用root用户安排,这就是超级用户给系统安排的,所以以上配置文件中,一般不做任何修改

这段 /etc/crontab 文件显示了 cron 服务的配置文件格式,用于定义计划任务的调度。

解释 crontab 文件内容:
SHELL=/bin/bash:指定用于执行命令的 Shell。

PATH=/sbin:/bin:/usr/sbin:/usr/bin:指定执行命令时的路径。

MAILTO=root:指定任务执行的输出将发送到 root 用户的邮箱。如果为空,则不会发送邮件。

时间顺序可以当口诀背诵:分时日月周

时间格式的表达:

调度规范描述
​ 0 ​ 0 * * *每天午夜运行指定的任务
​ 0 ​ 0 * * 7每周日运行指定的任务
​ 0 ​ * * * *每小时运行指定的任务
*/4 * * * *每四分钟运行指定的任务

为自己添加一个cron周期性任务

1
2
3
4
5
6
7
8
9
10
11
crontab -e
# 然后会用自动用vim打开一个空白文件,输出以下内容

# 每天早上8点执行备份
0 8 * * * /usr/local/bin/backup.sh

# 每小时执行一次清理临时文件
0 * * * * /usr/local/bin/cleanup.sh

# 每月的第一天凌晨2点运行系统更新
0 2 1 * * /usr/local/bin/system_update.sh

查看用户的 crontab 任务:

1
crontab -l

为user1添加一个cron周期性任务

为被人安排任务,需要有特权才可以

1
crontab -e -u user1

查询user1名下的cron任务

1
crontab -l -u user1

管理临时⽂件

systemd-tmpfiles-setup 服务是 systemd 套件的一部分,主要用于创建和清理临时文件和目录。这些临时文件和目录的管理规则定义在 /etc/tmpfiles.d/ 目录和 /etc/systemd/tmpfiles.d/ 目录中。它在系统启动时运行,以确保系统中的临时文件和目录按照配置文件中的规则被正确创建、清理或管理。

systemd-tmpfiles-setup 服务的主要功能

创建临时文件和目录:根据配置文件中的规则,创建系统运行所需的临时文件和目录。

清理临时文件和目录:按照预定义的时间和条件,删除不再需要的临时文件和目录,防止磁盘空间被不必要的文件占用。

设置文件权限和属性:配置文件中可以定义文件和目录的权限、所有者、群组等属性,确保安全性和正确性。

配置文件通常位于以下几个目录中:

  1. /etc/tmpfiles.d/ # 管理员最常用

  2. /run/tmpfiles.d/

  3. /usr/lib/tmpfiles.d/

配置文件的格式通常如下:

1
2
#Type  Path         Mode UID  GID  Age Argument
d /run/tmpfiles 0755 root root - -

各字段的含义如下:

Type:操作类型,如创建目录(d),创建文件(f),清理目录(r),设置权限(a)等。

Path:文件或目录路径。

Mode:权限模式,如 0755。

UID:所有者的用户 ID。

GID:所有者的组 ID。

Age:文件或目录的最大存在时间,超时则被删除。

Argument:额外的参数,根据操作类型不同而不同。

假设我们要创建一个名为 myapp 的临时目录,并设置适当的权限,可以创建如下配置文件:

创建配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cat > /etc/tmpfiles.d/myapp.conf <<-EOF
# 创建 /run/myapp 目录,权限为 0755,所有者为 root
d /run/myapp 0755 root root -

# 创建 /tmp/myapp.log 文件,权限为 0644,所有者为 root
f /tmp/myapp.log 0644 root root -

# 删除 /tmp 目录下超过7天未访问的文件
d /tmp 1777 root root 7d

# 删除 /var/tmp 目录下超过30天未访问的文件
d /var/tmp 1777 root root 30d

# 清理 /run/myapp 目录中超过1小时未访问的文件
d /run/myapp 0755 root root 1h
EOF

启用服务

1
[root@lixiaohui ~]# systemctl enable systemd-tmpfiles-setup systemd-tmpfiles-clean --now

默认情况下,文件的创建和清理,会根据你的配置文件自动执行,如需手工触发,可以这么做

1
2
systemd-tmpfiles --create /etc/tmpfiles.d/myapp.conf
systemd-tmpfiles --remove /etc/tmpfiles.d/myapp.conf

第三章 分析和存储⽇志

操作系统内核和其他进程为系统运⾏时发⽣的事件记录⽇志。这些⽇志⽤于系统审核和问题的故障排除,红帽企业 Linux 使⽤基于 syslog 协议的标准⽇志记录系统来记录系统消息。许多程序使⽤⽇志系统来记录事件,并将它们整理到⽇志⽂件中。systemd-journald 和 rsyslog 服务处理红帽企业 Linux 9 中的 syslog 消息。

systemd-journald 服务是操作系统事件⽇志架构的核⼼。systemd-journald 服务从许多来
源收集事件消息:

  • 系统内核
  • 启动过程早期阶段的输出
  • 守护进程的标准输出和标准错误
  • 系统⽇志事件

systemd-journald 服务将⽇志重构为⼀种标准格式,并写进带有索引的结构化系统⽇志中。默认情况下,该⽇志存储在系统重启后不保留的⽂件系统上。

rsyslog 服务对 syslog 消息进⾏排序,并将它们写⼊到 /var/log ⽬录下的⽇志⽂件中。

日志文件所存储消息的类型
/var/log/messages大多数系统日志消息记录在此处。例外包括与身份验证、电子邮件处理和调度作业执行相关的消息,以及纯粹与调试相关的消息。
/var/log/secure与安全性和身份验证事件相关的 syslog 消息。
/var/log/maillog与邮件服务器相关的 syslog 消息。
/var/log/cron与调度作业执行相关的 syslog 消息。
/var/log/boot.log与系统启动相关的非 syslog 控制台消息。

查看 Syslog ⽂件

许多程序使⽤ syslog 协议将事件记录到系统。以下为消息来源表格

代码设备设备描述
0kern内核消息
1user用户级消息
2mail邮件系统消息
3daemon系统守护进程消息
4auth身份验证和安全消息
5syslog内部 syslog 消息
6lpr打印机消息
7新闻网络新闻消息
8uucpUUCP 协议消息
9cron时钟守护进程消息
10authpriv非系统授权消息
11ftpFTP 协议消息
16-23local0 到 local7自定义本地消息

下表列出了标准的 syslog 优先级,以降序排序:

代码优先级优先级描述
0emerg系统不可用
1alert必须立即采取措施
2crit临界情况
3err非严重错误状况
4warning警告情况
5notice正常但重要的事件
6info信息性事件
7debug调试级别消息

rsyslog 服务使用日志消息的来源和优先级来确定如何进行处理。规则在 /etc/rsyslog.conf 文件和 /etc/rsyslog.d 目录中具有 .conf 扩展名的任何文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@lixiaohui ~]# cat /etc/rsyslog.conf
...
#### RULES ####

# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.* /dev/console

# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none /var/log/messages

# The authpriv file has restricted access.
authpriv.* /var/log/secure

# Log all the mail messages in one place.
mail.* -/var/log/maillog


# Log cron stuff
cron.* /var/log/cron

⽇志⽂件轮转

logrotate 命令会轮转⽇志⽂件,以防⽌它们在 /var/log ⽬录中占⽤太多空间

假设我们有一个应用程序日志文件位于/var/log/messages,配置一个 logrotate 规则。通过这个规则,我们将管理 messages 日志文件,以防止它占用过多的磁盘空间。

创建 logrotate 配置文件

我们在 /etc/logrotate.d/ 目录下创建一个名为 messages 的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cat > /etc/logrotate.d/messages <<-EOF
/var/log/messages {
# 每天轮转日志
daily

# 保留最近 7 个日志文件
rotate 7

# 压缩轮转后的日志文件
compress

# 如果日志文件丢失,不报错
missingok

# 如果日志文件为空,不进行轮转
notifempty

# 在执行 postrotate 和 prerotate 指令时共享脚本
sharedscripts

# 在轮转的日志文件名后添加日期后缀
dateext

# 创建新的空白日志文件,权限为0640,所有者为root
create 0640 root root

# 在日志轮转后执行的命令
postrotate
/usr/bin/systemctl reload rsyslog > /dev/null 2>&1 || true
endscript
}
EOF

这种服务不需要我们手工启动,不过我们可以手工测试一下配置文件是否正确

1
logrotate /etc/logrotate.d/messages

看看效果

1
2
3
[root@lixiaohui ~]# ls -lh /var/log/messages*
-rw-r-----. 1 root root 310 Dec 23 03:16 /var/log/messages
-rw-------. 1 root root 20K Dec 23 03:16 /var/log/messages-20241223.gz

分析 Syslog 条⽬

tail 命令是一个非常有用的工具,用于查看文件的末尾部分。它通常用于分析日志文件,因为日志文件会不断增长,而我们通常只需要查看最新的日志条目。

查看文件的最后10行

1
tail /var/log/messages

显示文件的最后n行

1
tail -n 20 /var/log/messages

持续监控文件(实时输出)

1
tail -f /var/log/messages

跟踪多个文件

1
tail -f /var/log/messages /var/log/syslog

⼿动发送 Syslog 消息

logger 命令可将消息发送到 rsyslog 服务

1
2
3
logger -p user.info "This is an informational message"
logger -p user.warn "This is a warning message"
logger -p user.err "This is an error message"

看看日志

1
2
3
4
5
6
7
[root@lixiaohui ~]# tail /var/log/messages
Dec 23 03:16:33 lixiaohui systemd[1]: Reloading System Logging Service...
Dec 23 03:16:33 lixiaohui systemd[1]: Reloaded System Logging Service.
Dec 23 03:16:33 lixiaohui rsyslogd[1047]: [origin software="rsyslogd" swVersion="8.2102.0-117.el9" x-pid="1047" x-info="https://www.rsyslog.com"] rsyslogd was HUPed
Dec 23 03:22:09 lixiaohui root[1647]: This is an informational message
Dec 23 03:22:09 lixiaohui root[1648]: This is a warning message
Dec 23 03:22:09 lixiaohui root[1649]: This is an error message

systemd-journald 服务

systemd-journald 服务将⽇志数据存储在带有索引的结构化⼆进制⽂件中,在红帽企业 Linux 中,默认情况下使⽤基于内存的 /run/log ⽬录来存储系统⽇志。系统关机时,/run/log ⽬录的内容将丢失。

journalctl 命令是一个强大的工具,用于从 systemd 日志中检索和查看日志消息。它可以过滤、格式化和查看由 systemd 管理的各种日志条目。

查看系统启动以来的日志

1
journalctl -b

按时间过滤日志

1
2
3
journalctl --since "today"

journalctl --since "2023-12-23 00:00:00" --until "2023-12-23 23:59:59"

查看特定服务的日志

1
journalctl -u sshd.service

实时输出日志

1
journalctl -f

按优先级过滤日志

1
2
3
journalctl -p err

journalctl -p warning

以 JSON 格式输出日志

1
journalctl -u sshd.service -p info --since "today" -o json-pretty

持久化日志

systemd-journald 服务默认情况下使⽤基于内存的 /run/log ⽬录来存储系统⽇志。系统关机时,/run/log ⽬录的内容将丢失,这对于日志而言,默认没有持久化,我们来看看怎么持久化。

按照以下步骤操作:

  1. 打开日志配置文件:编辑 /etc/systemd/journald.conf 文件。
1
vim /etc/systemd/journald.conf
  1. 修改持久化设置:找到 Storage= 选项,将其设置为 persistent。
1
2
[Journal]
Storage=persistent
  1. 创建持久化存储路径:/var/log/journal/。
1
2
3
mkdir /var/log/journal
chown -R root:systemd-journal /var/log/journal
chmod -R 2755 /var/log/journal
  1. 重启 systemd-journald:运行 sudo systemctl restart systemd-journald 以使更改生效。
1
systemctl restart systemd-journald

重启服务后,你就会在目录里看到它自动创建了它自己的索引目录

要实在看不到,重启一下机器

1
2
[root@lixiaohui ~]# ls /var/log/journal/
a82f9cf33b514eaf8bb2e41518ad77ae

维护准确的时间

在日志中,时间是一个很重要的东西,尤其是管理了多个机器时,维护时间的统一是必要的,不然无法分析故障,我们先来看看时间的设置

显示时间

1
2
3
4
5
6
7
8
[root@lixiaohui ~]# timedatectl
Local time: Mon 2024-12-23 03:47:55 EST
Universal time: Mon 2024-12-23 08:47:55 UTC
RTC time: Mon 2024-12-23 08:47:52
Time zone: America/New_York (EST, -0500)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no

关闭NTP同步

1
[root@lixiaohui ~]# timedatectl set-ntp 0

设置时间

1
2
3
4
5
6
7
8
9
[root@lixiaohui ~]# timedatectl set-time '2025-01-01 02:00:00'
[root@lixiaohui ~]# timedatectl
Local time: Wed 2025-01-01 02:00:03 EST
Universal time: Wed 2025-01-01 07:00:03 UTC
RTC time: Wed 2025-01-01 07:00:04
Time zone: America/New_York (EST, -0500)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no

列出并设置时区

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# timedatectl list-timezones | grep -i shanghai
Asia/Shanghai
[root@lixiaohui ~]# timedatectl set-timezone Asia/Shanghai
[root@lixiaohui ~]# timedatectl
Local time: Wed 2025-01-01 15:02:09 CST
Universal time: Wed 2025-01-01 07:02:09 UTC
RTC time: Wed 2025-01-01 07:02:11
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no

chronyd 是 Chrony 项目中的一个守护进程,用于同步系统时钟。它是一个替代 ntpd 的 NTP 客户端和服务器,具有更好的精度和灵活性,尤其适用于频繁断开网络连接或存在高抖动的环境。

chronyd 的主要配置文件是 /etc/chrony.conf。你可以编辑这个文件来配置时间同步源和选项。

启动 chronyd 服务:

1
systemctl enable chronyd --now

管理/etc/chrony.conf 配置文件

以下为从阿里云同步时间的案例

1
2
[root@lixiaohui ~]# vim /etc/chrony.conf
server ntp.aliyun.com iburst

以下为向特定网段授时的案例

1
2
3
[root@lixiaohui ~]# vim /etc/chrony.conf
...
allow 192.168/16

启用并启动 chronyd 服务:

每次修改了配置文件,都重启一下这个服务

1
systemctl restart chronyd

启用 NTP 并设置时区:

1
2
timedatectl set-ntp true
timedatectl set-timezone Asia/Shanghai

查看时间同步状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@lixiaohui ~]# chronyc tracking
Reference ID : AC19FEFE (ntp.aliyun.com)
Stratum : 9
Ref time (UTC) : Mon Dec 23 09:00:06 2024
System time : 0.000031878 seconds fast of NTP time
Last offset : +0.000053212 seconds
RMS offset : 0.000709722 seconds
Frequency : 15.382 ppm slow
Residual freq : +0.021 ppm
Skew : 0.723 ppm
Root delay : 0.000938652 seconds
Root dispersion : 0.000058568 seconds
Update interval : 64.6 seconds
Leap status : Normal

[root@lixiaohui ~]# chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp.aliyun.com 8 6 377 18 +37us[ +90us] +/- 469us

第四章 归档和传输⽂件

管理压缩的 tar 存档

tar 命令是 Linux 系统中常用的归档工具,用于创建和操作归档文件(通常称为 tarball)。tar 命令非常强大,可以用于备份数据、传输文件、解压缩文件等多种场景。

常见使用场景

创建归档文件:将多个文件或目录打包成一个归档文件。

解压归档文件:从归档文件中提取文件和目录。

查看归档内容:列出归档文件的内容。

添加文件到归档:向现有的归档文件中添加文件。

压缩和解压:与压缩工具(如 gzip 或 bzip2)结合使用,创建压缩归档文件或解压它们。

常见选项

-c:创建一个新的归档文件。

-x:从归档文件中提取文件。

-t:列出归档文件的内容。

-f:指定归档文件的文件名。

-v:显示详细信息(verbose)。

-z:通过 gzip 压缩归档文件。

-j:通过 bzip2 压缩归档文件。

-J: 通过 xz 压缩归档文件。

-C:切换到指定目录。这是大写的C

创建归档文件

将 /etc/ 目录打包成 documents.tar 文件:

1
tar -cvf documents.tar /etc

创建压缩归档文件

使用 gzip 压缩 /etc 目录,生成 documents.tar.gz 文件:

1
tar -czvf documents.tar.gz /etc

解压归档文件

解压 documents.tar 到当前目录:

1
tar -xvf documents.tar

将 documents.tar 解压到 /tmp 目录:

1
tar -xvf documents.tar -C /tmp

在系统之间安全地传输⽂件

在系统之间安全地传输文件是日常运维和开发工作中非常常见的需求。以下是三种常用的安全文件传输工具:scp、sftp 和 rsync,以及每种工具的使用案例。

scp(Secure Copy)

scp 是用于在本地和远程主机之间安全地复制文件的命令。它使用 SSH 协议来加密传输,确保数据的安全性。

从本地主机复制文件到远程主机:

1
scp /etc/fstab root@serverb:/tmp/

从远程主机复制文件到本地主机:

1
scp root@serverb:/etc/hostname /tmp/

复制目录: 使用 -r 选项递归地复制整个目录:

1
scp -r root@serverb:/etc/ /tmp/

sftp(Secure File Transfer Protocol)

sftp 是一种与 FTP 类似,但使用 SSH 协议进行加密的文件传输协议。它提供了一个交互式的命令行界面用于管理文件传输。

连接到远程服务器:

1
sftp root@serverb

ls 列出目录内容

put localfile /remotepath/remotefile

get /remotepath/remotefile /localpath/localfile

exit

rsync

rsync 是一个快速且通用的文件复制工具,适用于本地和远程文件同步。它可以增量传输文件,只传输更改过的部分,从而提高效率。rsync 也支持 SSH 协议进行加密传输。

常见选项

-a:归档模式,递归复制目录,并保留符号链接、文件权限、用户和组所有权、时间戳等属性。等同于 -rlptgoD。

-r:递归复制目录及其内容(已包含在 -a 中)。

-l:保留符号链接(已包含在 -a 中)。

-p:保留文件权限(已包含在 -a 中)。

-t:保留时间戳(已包含在 -a 中)。

-g:保留文件所属组(已包含在 -a 中)。

-o:保留文件所有者(需 root 权限,已包含在 -a 中)。

-D:保留设备文件和特殊文件(已包含在 -a 中)。

同步本地和远程目录:

1
rsync -avz /path/to/localdir user@remotehost:/path/to/remotedir

从远程服务器同步到本地目录:

1
rsync -avz user@remotehost:/path/to/remotedir /path/to/localdir

第五章 调优系统性能

命令行调整调优配置集

tuned 是一个用于动态调整系统性能的工具,它可以根据不同的工作负载自动调整系统设置。

  • 静态调优会对配置集中由tuned 守护进程在启动时应⽤的预定义 kernel 参数进⾏配置。对于静态调优⽽⾔,tuned 守护进程针对整体性能预期来设置内核参数,不随活跃度变化⽽调整这些参数。

  • 对于动态调优⽽⾔,tuned 守护进程会监视系统活动,并根据运⾏时⾏为变化来调整设置。动态调优会从选定调优配置集的初始设置开始,不断调整调优设置以适应当前⼯作负载。

我们的课程中涉及到的是静态调优方法,静态调优的方法是调整预期的配置文件并使其生效

安装tuned

1
2
dnf install tuned -y
systemctl enable --now tuned

查看配置文件

tuned 应⽤将调优配置集存储在 /usr/lib/tuned 和 /etc/tuned ⽬录下。每个配置集都有⼀个
单独的⽬录,⽬录中包含 tuned.conf 主配置⽂件以及其他可选⽂件。

1
2
3
4
[root@lixiaohui ~]# ls /usr/lib/tuned/
accelerator-performance desktop intel-sst network-throughput recommend.d virtual-host
aws functions latency-performance optimize-serial-console throughput-performance
balanced hpc-compute network-latency powersave virtual-guest

看看配置文件参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@lixiaohui ~]# grep -v -e ^$ -e ^# /usr/lib/tuned/balanced/tuned.conf
[main]
summary=General non-specialized tuned profile
[modules]
cpufreq_conservative=+r
[cpu]
priority=10
governor=conservative|powersave
energy_perf_bias=normal
[audio]
timeout=10
[video]
radeon_powersave=dpm-balanced, auto
[disk]
[scsi_host]
alpm=medium_power

看看都有哪些配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@lixiaohui ~]# tuned-adm list
Available profiles:
- accelerator-performance - Throughput performance based tuning with disabled higher latency STOP states
- aws - Optimize for aws ec2 instances
- 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: balanced

看看当前激活的配置文件

1
2
[root@lixiaohui ~]# tuned-adm active
Current active profile: virtual-guest

切换至balanced

1
2
3
[root@lixiaohui ~]# tuned-adm profile balanced
[root@lixiaohui ~]# tuned-adm active
Current active profile: balanced

tuned-adm recommend 命令可以为系统推荐调优配置集

1
2
[root@lixiaohui ~]# tuned-adm recommend
virtual-guest

使⽤tuned-adm off 命令来关闭 tuned 应⽤调优活动

1
[root@lixiaohui ~]# tuned-adm off

如果还想开,就profile一个新的就行

通过 Web 控制台管理配置集

先启动cockpit,并用root登录

1
2
3
4
5
6
[root@lixiaohui ~]# echo "" > /etc/cockpit/disallowed-users
[root@lixiaohui ~]# systemctl enable cockpit --now
[root@lixiaohui ~]# firewall-cmd --add-port=9090/tcp
success
[root@lixiaohui ~]# firewall-cmd --add-port=9090/tcp --permanent
success

http://你的服务器ip

登录后,点击性能集这里

tuned

选择好执行,点击change即可

tuned

影响进程调度

在 Linux 系统中,进程优先级决定了调度程序为某个进程分配 CPU 时间的优先级高低。优先级越高,进程被调度执行的机会就越大,反之亦然。

进程优先级

Linux 使用一个称为 niceness 的值来表示进程的优先级。niceness 值范围从 -20(最高优先级)到 19(最低优先级),默认值为 0。值越低,优先级越高。

负值(如 -10):提高优先级,使进程获得更多的 CPU 时间。

零值(0):默认优先级。

正值(如 10):降低优先级,使进程获得较少的 CPU 时间。

nice 命令

nice 命令用于启动一个进程并指定其初始 niceness 值。

示例

启动一个sleep 1000的进程,并将其 niceness 值设为 10,这将启动sleep 1000,并指定其初始 niceness 值为 10(较低优先级)。

1
nice -n 10 sleep 1000

上面的命令是不是卡住啦,正常的,因为要sleep 1000秒,直接ctrl c取消,放后台去执行看看

可以看到,我们的经常以10的优先级在运行中

1
2
3
4
5
[root@lixiaohui ~]# nice -n 10 sleep 1000 &
[1] 11487
[root@lixiaohui ~]#
[root@lixiaohui ~]# ps axo pid,comm,ni | grep 11487
11487 sleep 10

renice 命令

renice 命令用于修改一个已经在运行的进程的 niceness 值。

示例

刚才有一个进程 ID 为 11487 的正在运行的进程。你可以使用 renice 命令将其 niceness 值改为 -5(较高优先级):

1
2
3
4
[root@lixiaohui ~]# renice -n -5 -p 11487
11487 (process ID) old priority 10, new priority -5
[root@lixiaohui ~]# ps axo pid,comm,ni | grep 11487
11487 sleep -5

不过需要注意的是,只有root用户可以提高优先级,对于普通用户而言,只能降低优先级,而不能提高,普通用户最高优先级就是0

第六章 管理 SELinux 安全性

SELinux 基本概念

SELinux(Security-Enhanced Linux)是一个 Linux 内核模块和一套用户空间工具,旨在提供强制访问控制(MAC)安全机制。它最初由美国国家安全局(NSA)开发,后被集成到 Linux 内核中,用于增强系统的安全性。

如果一个应用想要加载某个文件或端口号,必须要经过SELinux的同意,不让应用将无法加载文件或端口号,而这个同意则是指:应用加载的文件或端口在应用兼容的标签列表中,举例来说

Web 服务器apache进程标有 httpd_t 类型的上下⽂。/var/www/html/ ⽬录和其他位置上的Web 服务器⽂件和⽬录标有 httpd_sys_content_t 类型上下⽂。/tmp 和 /var/tmp ⽬录中的临时⽂件具有 tmp_t 类型上下⽂作为标签。Web 服务器的端⼝具有 http_port_t 类型上下⽂作为标签。

当apache去加载/var/www/html/目录中的文件时,SELinux会检查,此目录所拥有的标签,是否和SELinux数据库中记录的apache允许加载的标签列表一致,一致则允许加载,不一致则拒绝加载

SELinux的安全上下文

安全上下文有时候也被称为标签,每个文件、进程和资源都有一个安全上下文(security context),该上下文包括用户、角色、类型和级别。SELinux 策略根据这些上下文来决定是否允许访问。

这里的unconfined_u:object_r:admin_home_t:s0就是我们说的安全上下文,不过在我们的课程中,我们只关注倒数第二列,也就是admin_home_t这部分,我们把这部分称为类型

1
2
3
[root@lixiaohui ~]# touch filetest
[root@lixiaohui ~]# ls -lZ filetest
-rw-r--r--. 1 root root unconfined_u:object_r:admin_home_t:s0 0 Dec 23 07:20 filetest

SELinux的模式

SELinux 有三种运行模式:

强制(Enforcing):这是最严格的模式,在这种模式下,SELinux 会强制执行其安全策略,阻止所有不符合策略的访问,并记录审计日志。

许可(Permissive):在这种模式下,SELinux 不会实际阻止访问,而是会记录所有不符合策略的访问尝试。此模式常用于调试和策略开发。

禁用(Disabled):完全禁用 SELinux,不进行任何访问控制检查或记录。

查看现在的模式

1
2
[root@lixiaohui ~]# getenforce
Enforcing

切换 SELinux 模式

需要注意的是,这种setenforce不管是0还是1,只是临时生效,重启后,就没了,想要永久生效,还得看下面的配置文件

1
2
3
[root@lixiaohui ~]# setenforce 0
[root@lixiaohui ~]# getenforce
Permissive

SELinux的配置

修改了配置文件后,将不会立刻生效,需要重启服务器后,才会生效

SELinux 配置文件位于 /etc/selinux/config,你可以通过编辑该文件来设置 SELinux 的模式和策略:

1
vim /etc/selinux/config

在这个文件中,一共就两行生效,我们关注的是第一行SELINUX=,我们只需要将第一行修改即可

1
2
SELINUX=enforcing
SELINUXTYPE=targeted

请格外注意

⾃红帽企业 Linux 9 起,只能通过在启动时使⽤ selinux=0 内核参数来完全禁⽤ SELinux。RHEL 不再⽀持在 /etc/selinux/config ⽂件中设置SELINUX=disabled 选项。

⾃ RHEL 9 起,在 /etc/selinux/config ⽂件中禁⽤ SELinux 会导致 SELinux 启动并执⾏主动实施,但不会加载任何策略。由于策略规则定义了允许的操作,因此如果未加载任何策略,则所有操作都会被拒绝。此⾏为是有意为之,旨在拦截试图绕过SELinux 保护的恶意⾏为。

控制 SELinux ⽂件上下⽂

新创建文件的上下文

SELinux 使用目录的默认上下文来决定新创建文件的上下文。这意味着在某个目录中创建的新文件会继承该目录的安全上下文类型。例如,在 /var/www/html 目录中创建的文件会继承 httpd_sys_content_t 类型,因为这个目录的默认上下文通常是为 Web 服务器内容设置的。

cp 和 mv 对文件上下文的影响

  1. cp 命令

使用 cp 命令复制文件时,新文件会根据目标目录的默认上下文类型分配新的安全上下文。

例如,假设你将一个文件从一个目录复制到另一个目录:

发现复制过来的上下文,最终是目标地的默认上下文

1
2
3
4
[root@lixiaohui ~]# cp /etc/fstab /root
[root@lixiaohui ~]# ll -Z /etc/fstab /root/fstab
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Oct 31 2023 /etc/fstab
-rw-r--r--. 1 root root unconfined_u:object_r:admin_home_t:s0 207 Dec 23 08:09 /root/fstab

当然,你可以使用额外的选项来保留其原来的标签

1
2
3
4
[root@lixiaohui ~]# cp --preserve=context /etc/fstab /tmp/fstab
[root@lixiaohui ~]# ll -Z /etc/fstab /tmp/fstab
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Oct 31 2023 /etc/fstab
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Dec 23 08:11 /tmp/fstab
  1. mv命令

使用 mv 命令移动文件时,文件的安全上下文通常保持不变,因为 mv 实际上只是改变了文件的路径而不是重新创建文件。例如:

1
2
3
4
5
[root@lixiaohui ~]# ll -Z /tmp/fstab
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Dec 23 08:11 /tmp/fstab
[root@lixiaohui ~]# mv /tmp/fstab /root/filea
[root@lixiaohui ~]# ll -Z /root/filea
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Dec 23 08:11 /root/filea

恢复文件默认上下文

这个默认上下文是指已经记录到selinux数据库里的上下文,比如上面的/root和/tmp,数据库就有记录的

我们发现,filea的标签有点不对

1
2
3
4
5
[root@lixiaohui ~]# ll -Z
total 8
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 207 Dec 23 08:11 filea
-rw-r--r--. 1 root root unconfined_u:object_r:admin_home_t:s0 0 Dec 23 07:20 filetest
-rw-r--r--. 1 root root unconfined_u:object_r:admin_home_t:s0 207 Dec 23 08:09 fstab

来,将它恢复成数据库中此路径记录的样子

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# restorecon -RvF /root/
Relabeled /root/.bash_history from unconfined_u:object_r:admin_home_t:s0 to system_u:object_r:admin_home_t:s0
Relabeled /root/filetest from unconfined_u:object_r:admin_home_t:s0 to system_u:object_r:admin_home_t:s0
Relabeled /root/.viminfo from unconfined_u:object_r:admin_home_t:s0 to system_u:object_r:admin_home_t:s0
Relabeled /root/fstab from unconfined_u:object_r:admin_home_t:s0 to system_u:object_r:admin_home_t:s0
Relabeled /root/filea from system_u:object_r:etc_t:s0 to system_u:object_r:admin_home_t:s0
[root@lixiaohui ~]# ll -Z
total 8
-rw-r--r--. 1 root root system_u:object_r:admin_home_t:s0 207 Dec 23 08:11 filea
-rw-r--r--. 1 root root system_u:object_r:admin_home_t:s0 0 Dec 23 07:20 filetest
-rw-r--r--. 1 root root system_u:object_r:admin_home_t:s0 207 Dec 23 08:09 fstab

更改 SELinux 上下⽂

在 Linux 中,修改 SELinux 上下文是确保安全策略适用于文件和目录的重要步骤。这里介绍两个常用命令 chcon 和 semanage,并展示它们的用法和区别。

chcon 命令

chcon 命令用于临时更改文件或目录的 SELinux 上下文。这个更改不会永久保留,系统重启或运行 restorecon 时会恢复为默认上下文。

基本用法

1
chcon [选项] <新的上下文> <文件或目录>

更改文件上下文类型:

假设你有一个文件夹 /mnt,你想将其上下文类型更改为 tmp_t:

1
2
3
4
5
6
[root@lixiaohui ~]# ll -Zd /mnt/
drwxr-xr-x. 2 root root system_u:object_r:mnt_t:s0 6 Aug 9 2021 /mnt/
[root@lixiaohui ~]# chcon -t tmp_t /mnt
[root@lixiaohui ~]# ll -Zd /mnt/
drwxr-xr-x. 2 root root system_u:object_r:tmp_t:s0 6 Aug 9 2021 /mnt/

更改目录及其内容的上下文类型:

递归更改目录 /mnt 及其内容的上下文类型:

1
chcon -R -t tmp_t /mnt

semanage 命令

semanage 命令用于管理 SELinux 策略,包括定义文件的默认上下文。这些更改是永久性的,在系统重启或运行 restorecon 后仍然有效。

基本用法

1
semanage fcontext -a -t <新的上下文> <文件或目录的路径正则表达式>

为目录添加默认上下文:

假设你希望 /custom/web 目录及其内容的默认上下文类型为 httpd_sys_content_t:

1
2
3
4
[root@lixiaohui ~]# mkdir -p /custom/web
[root@lixiaohui ~]# semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
[root@lixiaohui ~]# ls -lZd /custom/web/
drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 6 Dec 23 21:46 /custom/web/

我们发现,命令设置后,好像没有生效,那是因为semanage是向数据库注册条目,并不是给文件真的赋予,真的赋予需用restorecon命令从数据库读出条目并应用

1
2
[root@lixiaohui ~]# restorecon -RvF /custom/web
Relabeled /custom/web from unconfined_u:object_r:default_t:s0 to system_u:object_r:httpd_sys_content_t:s0

SELinux 常用的选项

以下是 semanage fcontext 命令的常用选项及其简要解释:

-a (add):添加新的文件上下文规则。

-d (delete):删除现有的文件上下文规则。

-m (modify):修改现有的文件上下文规则。

-l (list):列出当前的文件上下文规则。

chcon 和 semanage 的区别

chcon:用于临时更改文件或目录的上下文。适用于需要立即生效但不需要持久化的场景。系统重启或运行 restorecon 后更改会丢失。

semanage:用于永久更改文件或目录的默认上下文。适用于需要持久化的更改,系统重启或运行 restorecon 后仍然有效。

查询selinux数据库里记录的条目

最常⻅的扩展正则表达式是 (/.*)?

此语法描述为“⼀个以斜杠开头并后跟任意数量的字符的字符集,该集合可以存在或不存在”。更简单地说,此语法匹配⽬录本⾝,即使为空,也会匹配该⽬录中创建的⼏乎任何⽂件名。

1
2
3
4
5
6
7
8
9
10
11
12
[root@lixiaohui ~]# semanage fcontext -l
SELinux fcontext type Context

/ directory system_u:object_r:root_t:s0
/.* all files system_u:object_r:default_t:s0
/[^/]+ regular file system_u:object_r:etc_runtime_t:s0
/\.autofsck regular file system_u:object_r:etc_runtime_t:s0
/\.autorelabel regular file system_u:object_r:etc_runtime_t:s0
/\.ismount-test-file regular file system_u:object_r:sosreport_tmp_t:s0
/\.journal all files <<None>>
/\.snapshots(/.*)? all files system_u:object_r:snapperd_data_t:s0
...

使⽤布尔值调整 SELinux 策略

在管理 SELinux 策略时,布尔值和上下文的应用场景不同,它们各自有特定的用途和优势。以下是它们的区别和应用场景:

什么时候用布尔值

布尔值用于动态调整 SELinux 策略的特性,主要在以下场景中使用:

  1. 启用或禁用特定服务功能:布尔值可以控制某些服务的特定功能。例如,允许或禁止 HTTP 服务器发起网络连接 (httpd_can_network_connect)。

  2. 动态调整安全策略:布尔值可以在运行时进行调整,而无需重新编译或重新加载 SELinux 策略。例如,允许或禁止 FTP 服务器写入匿名用户文件 (ftpd_anon_write)。

  3. 便于测试和调试:在开发和测试环境中,可以临时启用或禁用某些策略特性,观察其影响。例如,启用系统管理员通过 SSH 登录 (ssh_sysadm_login)。

什么时候用上下文

上下文用于定义文件、目录和进程的安全属性,主要在以下场景中使用:

  1. 设置文件和目录的访问控制:通过上下文定义文件和目录的安全属性,确保只有特定进程或用户可以访问。例如,设置 Web 服务器内容的上下文为 httpd_sys_content_t

  2. 持久性策略:上下文设置是持久性的,适用于需要长期生效的安全策略。例如,配置某个目录及其子目录的默认上下文类型。

  3. 精细化控制:上下文提供了更精细的访问控制,可以针对不同类型的文件和进程进行细粒度的策略配置。例如,设置数据库文件的上下文为 mysqld_db_t

获取所有可用的布尔值

1
2
3
4
5
6
7
[root@lixiaohui ~]# getsebool -a | more
abrt_anon_write --> off
abrt_handle_event --> off
abrt_upload_watch_anon_write --> on
antivirus_can_scan_system --> off
antivirus_use_jit --> off
...

设置布尔值

使用 setsebool 命令可以设置一个布尔值的状态。setsebool 支持临时和永久两种设置方法。

临时设置:仅在当前系统运行期间生效,系统重启后将恢复默认值。

1
2
3
4
5
[root@lixiaohui ~]# getsebool httpd_enable_cgi
httpd_enable_cgi --> on
[root@lixiaohui ~]# setsebool httpd_enable_cgi off
[root@lixiaohui ~]# getsebool httpd_enable_cgi
httpd_enable_cgi --> off

临时设置后,就和数据库中记录的不一样了,我们可以用以下方式来查看现有的状态和默认的状态

可以看到,我们的状态是off,默认是on

1
2
3
[root@lixiaohui ~]# semanage boolean -l
SELinux boolean State Default Description
httpd_enable_cgi (off , on) Allow httpd to enable cgi

永久设置:使用 -P 选项可以将设置的布尔值永久生效,系统重启后仍然有效。

1
2
3
4
5
[root@lixiaohui ~]# getsebool httpd_enable_cgi
httpd_enable_cgi --> off
[root@lixiaohui ~]# setsebool -P httpd_enable_cgi off
[root@lixiaohui ~]# getsebool httpd_enable_cgi
httpd_enable_cgi --> off

调查和解决 SELinux 问题

监控 SELinux 违规

setroubleshoot-server 软件包中的 SELinux 故障排除服务提供了诊断 SELinux 问题的⼯具。当 SELinux 拒绝某⼀操作时,会在 /var/log/audit/audit.log 安全⽇志⽂件中记录⼀条
Access Vector Cache (AVC) 消息。SELinux 故障排除服务会监控 AVC 事件,并将事件摘要发送到 /var/log/messages ⽂件。

排查SELinux违规通常来说,会用到sealert,排查后,用semanage命令更正上下文即可,至于ausearch命令,用的并不多

调查SELinux问题的案例

我们先来模拟一个故障现场

搭建一个网站,然后放一个SELinux不允许加载的文件到网站根目录,用这个例子来调查问题并解决问题

1
2
3
4
5
6
7
8
9
10
11
12
[root@lixiaohui ~]# cd /root
[root@lixiaohui ~]# mkdir /lixiaohui
[root@lixiaohui ~]# echo hello lxh index > index.html
[root@lixiaohui ~]# mv index.html /lixiaohui/
[root@lixiaohui ~]# cat /lixiaohui/index.html
hello lxh index

[root@lixiaohui ~]# dnf install httpd -y
[root@lixiaohui ~]# ll -Z /lixiaohui/index.html
-rw-r--r--. 1 root root unconfined_u:object_r:admin_home_t:s0 16 Dec 23 22:49 /lixiaohui/index.html
[root@lixiaohui ~]# systemctl enable httpd --now
[root@lixiaohui ~]# curl http://127.0.0.1

由于httpd服务没有正常加载index.html,所以无法出席预期的字符串: hello lxh index,这种情况下httpd用了测试页面给我们做了替代

现在这个网站无法正常工作,你可以重启服务看看,也许是服务本身异常?如果重启后还是不好使,那就得看看日志了

1
[root@lixiaohui ~]# systemctl restart httpd

重启后还是访问不了,那就看看日志吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
[root@lixiaohui ~]# sealert -a /var/log/audit/audit.log
100% done
found 1 alerts in /var/log/audit/audit.log
--------------------------------------------------------------------------------

SELinux is preventing /usr/sbin/httpd from getattr access on the file /lixiaohui/index.html.

***** Plugin restorecon (92.2 confidence) suggests ************************

If you want to allow httpd to execute cgi scripts and to unify HTTPD handling of all content files.
Then you must tell SELinux about this by enabling the 'httpd_unified' and 'http_enable_cgi' booleans
Do
# setsebool -P httpd_unified=1 httpd_enable_cgi=1

***** Plugin catchall_labels (36.2 confidence) suggests *******************

If you want to allow httpd to have getattr access on the index.html file
Then you need to change the label on /lixiaohui/index.html
Do
# semanage fcontext -a -t FILE_TYPE '/lixiaohui/index.html'
where FILE_TYPE is one of the following: httpd_sys_content_t,xxxxxxxxxxxxxxxxxxxxxx

Then execute:
restorecon -v '/lixiaohui/index.html'

***** Plugin httpd_unified (7.83 confidence) suggests *********************

If you want to allow httpd to execute cgi scripts and to unify HTTPD handling of all content files.
Then you must tell SELinux about this by enabling the 'httpd_unified' and 'http_enable_cgi' booleans
Do
# setsebool -P httpd_unified=1 httpd_enable_cgi=1

***** Plugin catchall (1.41 confidence) suggests **************************

If you believe that httpd should be allowed getattr access on the index.html file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c 'httpd' --raw | audit2allow -M my-httpd
# semodule -X 300 -i my-httpd.pp


Additional Information:
Source Context system_u:system_r:httpd_t:s0
Target Context unconfined_u:object_r:admin_home_t:s0
Target Objects /lixiaohui/index.html [ file ]
Source httpd
Source Path /usr/sbin/httpd
Port <Unknown>
Host <Unknown>
Source RPM Packages httpd-core-2.4.57-5.el9.x86_64
Target RPM Packages
SELinux Policy RPM selinux-policy-targeted-38.1.23-1.el9.noarch
Local Policy RPM selinux-policy-targeted-38.1.23-1.el9.noarch
Selinux Enabled True
Policy Type targeted
Enforcing Mode Enforcing
Host Name lixiaohui
Platform Linux lixiaohui 5.14.0-362.8.1.el9_3.x86_64 #1 SMP
PREEMPT_DYNAMIC Tue Oct 3 11:12:36 EDT 2023 x86_64
x86_64
Alert Count 4
First Seen 2024-12-23 22:30:24 EST
Last Seen 2024-12-23 22:50:58 EST
Local ID 8a4893b0-a64f-4be5-b03e-b0b619f3e8b4

Raw Audit Messages
type=AVC msg=audit(1735012258.805:212): avc: denied { getattr } for pid=27749 comm="httpd" path="/lixiaohui/index.html" dev="vda4" ino=25179480 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0


type=SYSCALL msg=audit(1735012258.805:212): arch=x86_64 syscall=newfstatat success=no exit=EACCES a0=ffffff9c a1=7f5e1800a5b8 a2=7f5e217f17c0 a3=100 items=0 ppid=27746 pid=27749 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm=httpd exe=/usr/sbin/httpd subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=newfstatat AUID=unset UID=apache GID=apache EUID=apache SUID=apache FSUID=apache EGID=apache SGID=apache FSGID=apache

Hash: httpd,httpd_t,admin_home_t,file,getattr

好家伙,真找到了证据

SELinux is preventing /usr/sbin/httpd from getattr access on the file /lixiaohui/index.html.

而且在下面也给我们提供了可能的解决方案:

具体来说,我们需要让httpd加载的文件,具有标签

1
2
3
4
5
6
7
8
If you want to allow httpd to have getattr access on the index.html file
Then you need to change the label on /lixiaohui/index.html
Do
# semanage fcontext -a -t FILE_TYPE '/lixiaohui/index.html'
where FILE_TYPE is one of the following: httpd_sys_content_t,xxxxxxxxxxxxxxxxxxxxxx

Then execute:
restorecon -v '/lixiaohui/index.html'

根据他的提示,我们来试试

1
2
3
4
5
6
7
[root@lixiaohui ~]# semanage fcontext -a -t httpd_sys_content_t '/lixiaohui/index.html'
[root@lixiaohui ~]# restorecon -v '/lixiaohui/index.html'
Relabeled /lixiaohui/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
[root@lixiaohui ~]# systemctl restart httpd

[root@lixiaohui ~]# curl http://127.0.0.1
hello lxh index

问题解决,成功访问到我们的网站

第七章 管理基本存储

添加分区、文件系统和持久挂载

磁盘分区将一个硬盘驱动器划分为多个逻辑存储分区。可以根据不同的要求使用分区来划分存储,而硬盘分区的时候,又需要先给硬盘一个分区表,也就是说要决定分区的方案是什么,目前有MBR和GPT两种,而GPT更为主流

MBR 分区方案

mbr

主引导记录(MBR,Master Boot Record)是传统的硬盘分区方式之一,但它存在一些限制,特别是在面对现代存储需求时。以下是 MBR 分区的主要限制:

分区数量限制

  • 最多支持 4 个主分区:MBR 只允许创建最多 4 个主分区。如果需要更多的分区,可以将最后一个主分区转换为扩展分区,再在扩展分区内创建逻辑分区。

  • 逻辑分区限制:在扩展分区内最多可以创建 23 个逻辑分区,加上 3 个主分区,总共最多可以有 26 个分区。

硬盘容量限制

  • 最大支持 2TB 硬盘:MBR 使用 32 位来表示分区大小和位置,因此单个分区的最大支持容量为 2TB。对于大于 2TB 的硬盘,需要使用 GUID 分区表(GPT)来管理。

分区启动限制

  • 只能标记一个活动分区:MBR 只能标记一个活动分区用于引导操作系统。因此,只能从一个特定的分区引导系统。

安全性和冗余

  • 缺乏冗余:MBR 分区表和引导代码位于硬盘的第一个扇区,如果这个扇区损坏,整个分区表和引导信息都可能丢失。

  • 缺乏校验机制:MBR 没有内置的校验机制来验证分区表的完整性和正确性。

其他限制

  • 不支持现代特性:MBR 不支持许多现代硬盘管理特性,如分区标签、分区 UUID 等。

  • 兼容性问题:虽然 MBR 兼容性好,但在某些新型固态硬盘和 NVMe 驱动器上可能不再推荐使用。

综合来看,由于这些限制,MBR 已经逐渐被更先进的 GUID 分区表(GPT)所取代。GPT 没有上述限制,支持更大容量的硬盘和更多的分区,提供冗余和校验机制,是现代存储设备的首选分区方式。

GPT 分区方案

gpt

GPT(GUID Partition Table,GUID 分区表)是一种用于磁盘分区的标准,是传统 MBR(Master Boot Record,主引导记录)的现代替代方案。GPT 是 UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)标准的一部分,提供了一种更加灵活和可靠的分区方法。以下是 GPT 的一些关键特性和优点:

GPT的关键特性

  1. 支持更大容量的磁盘
    GPT 使用 64 位地址来表示分区大小和位置,因此可以支持非常大的磁盘,最大可达 18 EB(Exabytes),远超 MBR 的 2 TB 限制。

  2. 更多的分区
    GPT 不像 MBR 那样受到 4 个主分区的限制,默认情况下 GPT 支持多达 128 个分区(具体数量取决于操作系统的实现)。

  3. 冗余和恢复能力
    GPT 在磁盘的开头和结尾都存储了分区表的副本,这样可以在主分区表损坏时进行恢复。

  4. 校验和保护
    GPT 使用 CRC32 校验和来验证分区表的完整性,能够检测到数据损坏。

  5. 唯一标识符
    每个分区都有一个全球唯一标识符(GUID),确保每个分区的唯一性和可识别性。

GPT分区表结构

GPT 分区表主要由以下几部分组成:

  1. 保护性 MBR(Protective MBR)
    虽然 GPT 替代了 MBR,但仍保留一个保护性 MBR,以确保使用旧工具的系统能够识别并防止误认为磁盘未分区。

  2. 主 GPT 头(Primary GPT Header)
    位于磁盘的开头,包含分区表的位置和其他关键信息。

  3. 分区条目阵列(Partition Entry Array)
    紧随主 GPT 头,包含每个分区的详细信息,包括其 GUID、起始和结束位置等。

  4. 备份 GPT 头(Backup GPT Header)和分区条目阵列
    位于磁盘的结尾,作为主 GPT 头和分区条目阵列的备份。

GPT 优点

  • 高可靠性:通过冗余存储和校验机制,GPT 提供了更高的数据可靠性和安全性。

  • 灵活性:支持更多和更大的分区,适用于现代大容量存储需求。

  • 兼容性:保护性 MBR 确保旧系统能够识别 GPT 磁盘,避免误操作。

应用场景

  • 大容量硬盘:适用于超过 2 TB 的大容量硬盘分区。

  • 多分区需求:适用于需要多个分区的系统,如服务器和多操作系统环境。

  • 现代固态硬盘(SSD)和 NVMe 驱动器:通常使用 GPT 分区表以充分发挥其性能和容量。

创建MBR分区

传统创建分区的命令是fdisk,在很久以前,fdisk只能划分MBR分区,而现在经过升级后,fdisk同时能划分包括MBR、GPT在内的多种分区,在我们的课程中,重点介绍了parted命令,需要注意的是parted默认是10进制,也就是说1G=1000M

fdisk 案例

先看看系统上有那些硬盘

发现有/dev/vdb和/dev/vdc、/dev/vdd是空闲的

1
2
3
4
5
6
7
8
9
10
[root@lixiaohui ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 252:0 0 10G 0 disk
├─vda1 252:1 0 1M 0 part
├─vda2 252:2 0 200M 0 part /boot/efi
├─vda3 252:3 0 600M 0 part /boot
└─vda4 252:4 0 9.2G 0 part /
vdb 252:16 0 5G 0 disk
vdc 252:32 0 5G 0 disk
vdd 252:48 0 5G 0 disk

划分一个主分区,进来后,输入m先看看帮助,看看怎么用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[root@lixiaohui ~]# fdisk /dev/vdb

Welcome to fdisk (util-linux 2.37.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table. # 这里告诉我们没有找到分区表,自动创建了DOS,这就是MBR的意思
Created a new DOS disklabel with disk identifier 0xdbf3265a.

Command (m for help): m # 输入m看看帮助列表

Help:

DOS (MBR)
a toggle a bootable flag
b edit nested BSD disklabel
c toggle the dos compatibility flag

Generic
d delete a partition
F list free unpartitioned space
l list known partition types
n add a new partition
p print the partition table
t change a partition type
v verify the partition table
i print information about a partition

Misc
m print this menu
u change display/entry units
x extra functionality (experts only)

Script
I load disk layout from sfdisk script file
O dump disk layout to sfdisk script file

Save & Exit
w write table to disk and exit
q quit without saving changes

Create a new label
g create a new empty GPT partition table
G create a new empty SGI (IRIX) partition table
o create a new empty DOS partition table
s create a new empty Sun partition table


Command (m for help):

根据提示,输入n完成分区创建

1
2
3
4
5
6
7
8
9
10
Command (m for help): n  # 输入n
Partition type
p primary (0 primary, 0 extended, 4 free) # 这里告诉我们,一共有4个空闲的主分区
e extended (container for logical partitions)
Select (default p): p # 这里括号里有一个default p,是说如果你直接回车,就等于你输入了p
Partition number (1-4, default 1): 1 # 这里括号里有一个default 1,是说如果你直接回车,就等于你输入了1
First sector (2048-10485759, default 2048): 2048 # 这里括号里有一个default 2048,是说如果你直接回车,就等于你输入了2048,一个硬盘最开始的地方就是2048
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485759, default 10485759): +1G # 这里括号里有一个default 10485759,是说如果你直接回车,就等于你输入了10485759,但是这里的last是指从first到哪里是你需要的分区大小,根据他的格式和我们的需求,我输入+1G,这意思是给我来一个1G的分区

Created a new partition 1 of type 'Linux' and of size 1 GiB. # 它自动给我们选了一个Linux的分区类型

这个分区类型有很多,要和我们后期格式化的格式相呼应

分区类型标识符有助于操作系统和工具正确地识别和处理分区。具体来说:

  1. 标识文件系统类型
    分区类型标识符指示分区中使用的文件系统类型。例如,类型代码 83 表示 Linux 本地分区,通常用于 ext4、xfs 等文件系统。这样操作系统在挂载分区时,就能知道使用什么文件系统驱动程序来访问数据。

  2. 分区管理
    帮助分区管理工具识别和管理不同类型的分区。例如,当你使用 fdiskparted 等工具时,它们可以根据类型代码显示分区信息,并执行相应的操作。

  3. 引导加载器支持
    某些分区类型与引导加载器的工作方式有关。例如,类型代码 7 表示 HPFS/NTFS/exFAT 分区,用于 Windows 操作系统的引导分区。

常见分区类型代码

  • 83:Linux 本地分区,用于 ext2/ext3/ext4/xfs 等文件系统。

  • 82:Linux 交换分区(swap),用于虚拟内存交换分区。

  • 7:HPFS/NTFS/exFAT 分区,主要用于 Windows 操作系统。

  • b:W95 FAT32 分区,适用于较早的 FAT32 文件系统。

  • c:W95 FAT32(LBA)分区,支持较大硬盘的 FAT32 文件系统。

  • 8e:Linux LVM 分区,用于逻辑卷管理(LVM)。

我们看看如何更改类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Command (m for help): t # 这是更改类型的代码
Selected partition 1 # 只有一个分区,所以自动选了,如果有多个,根据需求,输入分区号
Hex code or alias (type L to list all): L # 列出目前所有的分区类型

00 Empty 24 NEC DOS 81 Minix / old Lin bf Solaris
01 FAT12 27 Hidden NTFS Win 82 Linux swap / So c1 DRDOS/sec (FAT-
02 XENIX root 39 Plan 9 83 Linux c4 DRDOS/sec (FAT-
03 XENIX usr 3c PartitionMagic 84 OS/2 hidden or c6 DRDOS/sec (FAT-
04 FAT16 <32M 40 Venix 80286 85 Linux extended c7 Syrinx
05 Extended 41 PPC PReP Boot 86 NTFS volume set da Non-FS data
06 FAT16 42 SFS 87 NTFS volume set db CP/M / CTOS / .
07 HPFS/NTFS/exFAT 4d QNX4.x 88 Linux plaintext de Dell Utility
08 AIX 4e QNX4.x 2nd part 8e Linux LVM df BootIt
09 AIX bootable 4f QNX4.x 3rd part 93 Amoeba e1 DOS access
0a OS/2 Boot Manag 50 OnTrack DM 94 Amoeba BBT e3 DOS R/O
0b W95 FAT32 51 OnTrack DM6 Aux 9f BSD/OS e4 SpeedStor
0c W95 FAT32 (LBA) 52 CP/M a0 IBM Thinkpad hi ea Linux extended
0e W95 FAT16 (LBA) 53 OnTrack DM6 Aux a5 FreeBSD eb BeOS fs
0f W95 Ext'd (LBA) 54 OnTrackDM6 a6 OpenBSD ee GPT
10 OPUS 55 EZ-Drive a7 NeXTSTEP ef EFI (FAT-12/16/
11 Hidden FAT12 56 Golden Bow a8 Darwin UFS f0 Linux/PA-RISC b
12 Compaq diagnost 5c Priam Edisk a9 NetBSD f1 SpeedStor
14 Hidden FAT16 <3 61 SpeedStor ab Darwin boot f4 SpeedStor
16 Hidden FAT16 63 GNU HURD or Sys af HFS / HFS+ f2 DOS secondary
17 Hidden HPFS/NTF 64 Novell Netware b7 BSDI fs fb VMware VMFS
18 AST SmartSleep 65 Novell Netware b8 BSDI swap fc VMware VMKCORE
1b Hidden W95 FAT3 70 DiskSecure Mult bb Boot Wizard hid fd Linux raid auto
1c Hidden W95 FAT3 75 PC/IX bc Acronis FAT32 L fe LANstep
1e Hidden W95 FAT1 80 Old Minix be Solaris boot ff BBT

Aliases:
linux - 83
swap - 82
extended - 05
uefi - EF
raid - FD
lvm - 8E
linuxex - 85
Hex code or alias (type L to list all): 83 # 根据你的需求,输入一个即可,我的需求和它自动的相匹配,所以我输入83
Changed type of partition 'Linux' to 'Linux'.

Command (m for help):

列出所有的分区,看看我们刚才做的分区是否符合预期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Command (m for help): p # 输入p是print的意思
Disk /dev/vdb: 5 GiB, 5368709120 bytes, 10485760 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xdbf3265a

Device Boot Start End Sectors Size Id Type
Command (m for help): p
Disk /dev/vdb: 5 GiB, 5368709120 bytes, 10485760 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xdbf3265a

Device Boot Start End Sectors Size Id Type
/dev/vdb1 2048 2099199 2097152 1G 83 Linux

Command (m for help):
2048 2099199 2097152 1G 83 Linux

Command (m for help):

看到我们有一个1G大小的/dev/vdb1分区,且代码为83

保存并退出

1
2
3
4
5
6
Command (m for help): w # 保存write
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

[root@lixiaohui ~]# partprobe # 用来刷新内核分区表,以防止内核没有及时识别到我们的新分区

以下是 fdisk 命令的常用选项及其简要解释:

  • d:删除一个分区。

  • m:显示帮助菜单。

  • n:添加一个新的分区。

  • p:显示分区表。

  • q:退出 fdisk 而不保存更改。

  • t:更改分区的系统 ID。

  • w:将分区表写入磁盘并退出。

parted 案例

parted分为交互式和非交互式两种,非交互式可用于脚本自动分区

交互式

启动 parted: 打开 parted 交互式会话:

看看帮助先

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@lixiaohui ~]# parted /dev/vdc
...
(parted) ? # 输入?
align-check TYPE N check partition N for TYPE(min|opt) alignment
help [COMMAND] print general help, or help on COMMAND
mklabel,mktable LABEL-TYPE create a new disklabel (partition table)
mkpart PART-TYPE [FS-TYPE] START END make a partition
name NUMBER NAME name partition NUMBER as NAME
print [devices|free|list,all] display the partition table, or available devices, or free space, or all
found partitions
quit exit program
rescue START END rescue a lost partition near START and END
resizepart NUMBER END resize partition NUMBER
rm NUMBER delete partition NUMBER
select DEVICE choose the device to edit
disk_set FLAG STATE change the FLAG on selected device
disk_toggle [FLAG] toggle the state of FLAG on selected device
set NUMBER FLAG STATE change the FLAG on partition NUMBER
toggle [NUMBER [FLAG]] toggle the state of FLAG on partition NUMBER
type NUMBER TYPE-ID or TYPE-UUID type set TYPE-ID or TYPE-UUID of partition NUMBER
unit UNIT set the default unit to UNIT
version display the version number and copyright information of GNU Parted
(parted)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@lixiaohui ~]# parted /dev/vdc
GNU Parted 3.5
Using /dev/vdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mklabel msdos # msdos就是MBR
(parted) mkpart # 新建分区
Partition type? primary/extended? primary # 分类类型
File system type? [ext2]? ext4 # 文件系统类型
Start? 2048s # 从硬盘的什么位置开始,如果已经有分区,就从上一个分区结束的地方开始,结束的地方可以看下面的print的end
End? 1G # 到哪儿结束,这就是本次分区的大小,从开始到结束的大小,输入MB的话,就是1000,输入MiB就是1024
(parted) print # 查询现有的分区
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number Start End Size Type File system Flags
1 1049kB 1000MB 999MB primary ext4
(parted) quit # 保存并退出
Information: You may need to update /etc/fstab.

一般来说,下面这个命令和partprobe起到的实际作用差不多

1
udevadm settle

非交互式分区

先摧毁现有/dev/vdc上的所有数据,因为我们要做一个一样的分区

1
2
3
[root@lixiaohui ~]# wipefs -a /dev/vdc
/dev/vdc: 2 bytes were erased at offset 0x000001fe (dos): 55 aa
/dev/vdc: calling ioctl to re-read partition table: Success

自动分区

先设置一个分区表

1
2
[root@lixiaohui ~]# parted /dev/vdc mklabel msdos
Information: You may need to update /etc/fstab.

分区

1
2
[root@lixiaohui ~]# parted /dev/vdc mkpart primary ext4 2048s 1G
Information: You may need to update /etc/fstab.

查看分区结果

删除可以用下面演示的rm加分区号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@lixiaohui ~]# parted /dev/vdc print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number Start End Size Type File system Flags
1 1049kB 1000MB 999MB primary

[root@lixiaohui ~]# parted /dev/vdc rm 1
Information: You may need to update /etc/fstab.

[root@lixiaohui ~]# parted /dev/vdc print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number Start End Size Type File system Flags

创建GPT分区

GPT才是工作中的主流分区方案

fdisk 分区

先摧毁现有/dev/vdc上的所有数据,因为我们要复用这个盘

1
2
3
[root@lixiaohui ~]# wipefs -a /dev/vdc
/dev/vdc: 2 bytes were erased at offset 0x000001fe (dos): 55 aa
/dev/vdc: calling ioctl to re-read partition table: Success
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[root@lixiaohui ~]# fdisk /dev/vdc

Welcome to fdisk (util-linux 2.37.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xa3b67ae7.

Command (m for help): g # g就是创建GPT分区表的意思
Created a new GPT disklabel (GUID: DE74EB12-0199-2849-A0F3-CB646F14AB69).

Command (m for help): n # 新建分区
Partition number (1-128, default 1): 1 # 第一个分区
First sector (2048-10485726, default 2048):2048 # 最开始的地方
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485726, default 10485726): +1G # 硬盘大小

Created a new partition 1 of type 'Linux filesystem' and of size 1 GiB.

Command (m for help): t # 更改类型
Selected partition 1
Partition type or alias (type L to list all): L # 列出所有类型,需要注意,GPT的code和MBR是不一样的
...
20 Linux filesystem 0FC63DAF-8483-4772-8E79-3D69D8477DE4
...
Aliases:
linux - 0FC63DAF-8483-4772-8E79-3D69D8477DE4
swap - 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
home - 933AC7E1-2EB4-4F13-B844-0E14E2AEF915
uefi - C12A7328-F81F-11D2-BA4B-00A0C93EC93B
raid - A19D880F-05FC-4D3B-A006-743F0F84911E
lvm - E6D6D379-F507-44C2-A23C-238F2A3DF928
Partition type or alias (type L to list all): 20 # 我选择20的Linux
Changed type of partition 'Linux filesystem' to 'Linux filesystem'.

Command (m for help): p # print看看
Disk /dev/vdc: 5 GiB, 5368709120 bytes, 10485760 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt # 已经是gpt
Disk identifier: DE74EB12-0199-2849-A0F3-CB646F14AB69

Device Start End Sectors Size Type
/dev/vdc1 2048 2099199 2097152 1G Linux filesystem

Command (m for help): w # 保存退出
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
1
[root@lixiaohui ~]# partprobe # 刷新分区表

parted 分区

交互式分区

先摧毁现有/dev/vdc上的所有数据,因为我们要复用这个盘

1
2
3
4
5
[root@lixiaohui ~]# wipefs -a /dev/vdc
/dev/vdc: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/vdc: 8 bytes were erased at offset 0x13ffffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/vdc: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
/dev/vdc: calling ioctl to re-read partition table: Success
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@lixiaohui ~]# parted /dev/vdc
GNU Parted 3.5
Using /dev/vdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mklabel gpt # 创建GPT分区表
(parted) mkpart # 新建分区
Partition name? []? lixiaohui # 分区的名称,这是有别于mbr的地方
File system type? [ext2]? ext4 # 文件系统类型
Start? 2048s # 硬盘开始的地方
End? 1G # 分区大小,G就是1000,GiB就是1024
(parted) print # 查询现有分区
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1049kB 1000MB 999MB ext4 lixiaohui

(parted) quit # 保存并退出
Information: You may need to update /etc/fstab.
1
[root@lixiaohui ~]# udevadm settle # 刷新分区表

非交互式分区

先摧毁现有/dev/vdc上的所有数据,因为我们要复用这个盘

1
2
3
4
5
[root@lixiaohui ~]# wipefs -a /dev/vdc
/dev/vdc: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/vdc: 8 bytes were erased at offset 0x13ffffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/vdc: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
/dev/vdc: calling ioctl to re-read partition table: Success

分配GPT分区表

1
2
[root@lixiaohui ~]# parted /dev/vdc mklabel gpt
Information: You may need to update /etc/fstab.

分区并查看

1
2
3
4
5
6
7
8
9
10
11
12
[root@lixiaohui ~]# parted /dev/vdc mkpart lxhpart ext4 2048s 1G
Information: You may need to update /etc/fstab.

[root@lixiaohui ~]# parted /dev/vdc print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1049kB 1000MB 999MB lxhpart
1
[root@lixiaohui ~]# udevadm settle # 刷新分区表

创建⽂件系统

分好区之后,需要格式化才能被系统正常识别和使用,格式化的过程就是赋予分区文件系统的过程

看看系统默认都支持那些格式,也就是说看看系统都支持哪些文件系统

输入mkfs.之后,快速按两次TAB键

1
2
3
[root@lixiaohui ~]# mkfs.
mkfs.cramfs mkfs.ext3 mkfs.fat mkfs.msdos mkfs.xfs
mkfs.ext2 mkfs.ext4 mkfs.minix mkfs.vfat

以 root ⽤⼾⾝份,使⽤ mkfs.xfs 命令为/dev/vdc1应⽤ XFS ⽂件系统

1
2
3
4
5
6
7
8
9
10
11
12
[root@lixiaohui ~]# mkfs.xfs /dev/vdc1
meta-data=/dev/vdc1 isize=512 agcount=4, agsize=60992 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=1 bigtime=1 inobtcount=1 nrext64=0
data = bsize=4096 blocks=243968, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=16384, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
Discarding blocks...Done.

验证vdc1是否有了文件系统

下面的筛选中,有TYPE=”XFS”,就说明已经经过了格式化

1
2
[root@lixiaohui ~]# blkid | grep vdc1
/dev/vdc1: UUID="c00d8aa1-4f3c-4185-b2a3-21fc33e928df" TYPE="xfs" PARTLABEL="lxhpart" PARTUUID="64518685-c09c-41dc-835c-b6fafa3c6e84"

临时挂载⽂件系统

添加⽂件系统后,最后⼀步是将⽂件系统挂载到⽬录上。这个目录就是挂载点,向这个挂载点写入数据,就是写到了我们的分区中

需要注意的是,mount命令挂载的效果,在umount或者重启服务器后,会解除挂载,需要手工重新挂载

将我们的/dev/vdc1挂载到/lxh

1
2
[root@lixiaohui ~]# mkdir /lxh
[root@lixiaohui ~]# mount /dev/vdc1 /lxh

验证一下看看

1
2
3
4
[root@lixiaohui ~]# df -h
Filesystem Size Used Avail Use% Mounted on
...
/dev/vdc1 889M 39M 851M 5% /lxh
1
2
[root@lixiaohui ~]# mount | grep vdc1
/dev/vdc1 on /lxh type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)

持久挂载⽂件系统

⼿动挂载⽂件系统是⼀种验证已格式化的设备是否可访问以及是否按预期⽅式⼯作的好⽅法。但是,当服务器重新启动时,系统不会再次⾃动挂载⽂件系统。要确保系统在启动时⾃动挂载⽂件系统,请在 /etc/fstab ⽂件中添加⼀个条⽬,系统启动期间,会自动执行这些条目来完成挂载

/etc/fstab 是⼀个以空格分隔的⽂件,每⾏具有六个字段。如果想知道每个字段的含义,可以执行man fstab命令获取,不过需要注意的是,/etc/fstab 中存在错误的条⽬可能会导致计算机⽆法启动,你可以用mount -a命令或者findmnt –verify来验证有没有语法问题

由于我们已经用临时的方式挂载好了,所以我们先临时卸载一下

1
umount /dev/vdc1

将我们的vdc1持久性挂载到/lxh

现在在fstab中,推荐用uuid,而非类似于/dev/vdc1这种写法,所以我们来查一下vdc1的uuid是多少

1
2
[root@lixiaohui ~]# blkid | grep vdc1
/dev/vdc1: UUID="c00d8aa1-4f3c-4185-b2a3-21fc33e928df" TYPE="xfs" PARTLABEL="lxhpart" PARTUUID="64518685-c09c-41dc-835c-b6fafa3c6e84"

查到后写到fstab

注意,你要追加一行,而不要动里面原有的内容

1
2
vim /etc/fstab
UUID="c00d8aa1-4f3c-4185-b2a3-21fc33e928df" /lxh xfs defaults 0 0

各列解释

  1. UUID

    • 作用:唯一标识设备。

    • 内容"c00d8aa1-4f3c-4185-b2a3-21fc33e928df" 是一个唯一标识符,用于识别要挂载的设备或分区。这确保即使设备名称(如 /dev/vdc1)发生变化,系统也能正确找到并挂载设备。

  2. 挂载点

    • 作用:指定文件系统将被挂载到系统中的哪个位置。

    • 内容/lxh 是文件系统的挂载点,表示这个设备将挂载到 /lxh 目录。

  3. 文件系统类型

    • 作用:指定文件系统的类型。

    • 内容xfs 指定了文件系统类型为 XFS,这是一个高性能的文件系统,常用于大容量存储。

  4. 挂载选项

    • 作用:指定文件系统挂载时使用的选项。

    • 内容defaults 是一组默认挂载选项,通常包括 rw(读写)、suid(允许设置用户 ID)、dev(解释字符或块设备)、exec(允许执行二进制文件)、auto(自动挂载)、nouser(非用户挂载)和 async(异步挂载)。

  5. dump 选项

    • 作用:指定文件系统是否需要被 dump 备份。

    • 内容0 表示不需要备份。dump 是一个备份工具,该字段用于决定 dump 是否应该备份这个文件系统。

  6. fsck 选项

    • 作用:指定文件系统在启动时是否进行检查。

    • 内容0 表示不需要在启动时进行文件系统检查。fsck 是一个文件系统检查和修复工具,该字段用于决定是否在系统启动时检查这个文件系统。

在 /etc/fstab ⽂件中添加或删除条⽬时,请运⾏ systemctl daemon-reload 命令或重启服务器,以确保 systemd 守护进程加载并使⽤新配置。

1
2
[root@lixiaohui ~]# systemctl daemon-reload
[root@lixiaohui ~]# mount -a

验证一下看看

1
2
3
4
[root@lixiaohui ~]# df -h
Filesystem Size Used Avail Use% Mounted on
...
/dev/vdc1 889M 39M 851M 5% /lxh

管理交换空间

在 Linux 系统中,交换空间(Swap Space)是用于虚拟内存的一部分,当物理内存不足时,系统可以使用交换空间临时存储不常用的数据。这样可以保证系统在内存耗尽时仍能继续运行,但由于交换空间在硬盘上,速度比物理内存要慢得多,因此经常访问交换空间可能会影响系统性能。

以下是如何使用 parted 在 /dev/vdc 上划分一个新的交换分区(第二个分区),并将其格式化为交换空间的具体步骤:

先分区出来

1
2
3
4
5
6
7
8
9
[root@lixiaohui ~]# parted /dev/vdc print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1049kB 1000MB 999MB xfs lxhpart

上一个分区结束的地方是1000M,那我们的新分区就从1000开始

成功划分一个swap空间,大小也是1000M

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@lixiaohui ~]# parted /dev/vdc mkpart swaptest linux-swap 1000M 2000M
Information: You may need to update /etc/fstab.

[root@lixiaohui ~]# parted /dev/vdc print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1049kB 1000MB 999MB xfs lxhpart
2 1000MB 2000MB 999MB swaptest swap

格式化swap分区,需要注意命令和普通分区不一样

1
2
3
[root@lixiaohui ~]# mkswap /dev/vdc2
Setting up swapspace version 1, size = 953 MiB (999288832 bytes)
no label, UUID=0f12c9e7-83b9-41d8-980f-01c99381abcc

查看现有swap分区激活情况,并激活我们的swap分区

可以看到本来swap是0,激活了我们的之后,就有了数值,如果你想关闭所有的swap分区,可以用swapoff命令

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@lixiaohui ~]# free -m
total used free shared buff/cache available
Mem: 1763 425 1127 13 378 1337
Swap: 0 0 0
[root@lixiaohui ~]# swapon -s
[root@lixiaohui ~]# swapon /dev/vdc2
[root@lixiaohui ~]# swapon -s
Filename Type Size Used Priority
/dev/vdc2 partition 975868 0 -2
[root@lixiaohui ~]# free -m
total used free shared buff/cache available
Mem: 1763 426 1126 13 379 1337
Swap: 952 0 952

持久激活交换空间是必要的,因为swapon是临时的,重启就没了,和刚才一样,写入fstab就行

先看看uuid

1
2
[root@lixiaohui ~]# blkid | grep vdc2
/dev/vdc2: UUID="0f12c9e7-83b9-41d8-980f-01c99381abcc" TYPE="swap" PARTLABEL="swaptest" PARTUUID="163ebe5d-2d35-45e0-9108-002fafde68c3"

持久挂载

不要动里面原有的内容

1
2
3
vim /etc/fstab
[root@lixiaohui ~]# blkid | grep vdc2
UUID="0f12c9e7-83b9-41d8-980f-01c99381abcc" swap swap defaults 0 0

不过需要注意的是,如果你有多个交换空间的话,要注意优先级,默认情况下,系统按照优先级顺序使⽤交换空间。内核使⽤最⾼优先级的交换空间,直到其填满,然后开始使⽤低优先级的交换空间。当交换空间具有相同的优先级时,内核会以轮循⽅式向其中写⼊。

要设置优先级,请在 /etc/fstab ⽂件中使⽤ pri 选项。内核会⾸先使⽤优先级最⾼的交换空间。默认优先级为 -2,数字越大,优先级越高。

以下仅为举例

1
2
3
4
vim /etc/fstab
[root@lixiaohui ~]# blkid | grep vdc2
UUID="0f12c9e7-83b9-41d8-980f-01c99381abcc" swap swap defaults,pri=4 0 0
UUID="01d0671d-aba5-4e7c-b880-7ae15f31a2b9" swap swap defaults,pri=5 0 0 ## 数字更大,优先使用

第八章 管理存储堆栈

创建和扩展逻辑卷

逻辑卷概念

逻辑卷管理(LVM,Logical Volume Management)是 Linux 操作系统中的一种存储管理技术,它提供了比传统分区更灵活和强大的磁盘管理功能。LVM 允许你在多个物理存储设备上创建逻辑卷,并动态调整其大小。LVM 的三个基本概念是物理卷(PV,Physical Volume)、卷组(VG,Volume Group)和逻辑卷(LV,Logical Volume)。

物理卷(PV,Physical Volume)

物理卷是 LVM 的基础单元,通常是一个物理硬盘分区或者整个硬盘。每个物理卷都有一个唯一的标识符,可以加入到卷组中。物理卷是创建卷组和逻辑卷的基础。

卷组(VG,Volume Group)

卷组是由一个或多个物理卷组成的逻辑容器。卷组将多个物理存储设备组合成一个单一的逻辑存储池,提供统一的存储空间。可以在卷组上创建一个或多个逻辑卷,并动态分配和调整存储空间。

物理扩展(PE)是卷组中分配空间的基本单位。每个物理卷(PV)被分割成多个大小相等的物理扩展,这些物理扩展在卷组中进行管理和分配。当你创建或扩展逻辑卷(LV)时,实际上是分配和使用物理扩展。

逻辑卷(LV,Logical Volume)

逻辑卷是在卷组上创建的可管理单元,类似于传统的磁盘分区。逻辑卷可以像普通分区一样格式化和挂载,但它们可以动态调整大小,而不需要卸载和重新分区。逻辑卷提供了更大的灵活性,可以根据需要分配和调整存储空间。

LVM 的优点

  • 灵活性:可以动态调整逻辑卷的大小,添加或移除物理卷,不需要重启系统。

  • 快照:可以创建逻辑卷的快照,用于备份和恢复。

  • 聚合存储:可以将多个物理存储设备组合成一个单一的逻辑存储池,提高存储利用率。

  • 性能优化:支持条带化(striping)和镜像(mirroring),提高存储性能和可靠性。

逻辑卷使用的基本步骤

  1. 创建物理卷(PV)

    • 使用 pvcreate 初始化物理磁盘或分区。
  2. 创建卷组(VG)

    • 使用 vgcreate 将物理卷合并到卷组中。
  3. 查看卷组(VG)信息

    • 使用 vgdisplay 查看卷组信息。
  4. 创建逻辑卷(LV)

    • 使用 lvcreate 在卷组中创建逻辑卷。
  5. 查看逻辑卷(LV)信息

    • 使用 lvdisplay 查看逻辑卷信息。
  6. 格式化逻辑卷

    • 使用 mkfs 将逻辑卷格式化为所需的文件系统类型。
  7. 挂载逻辑卷

    • 使用 mount 将逻辑卷挂载到文件系统。
  8. 更新 /etc/fstab 文件

    • 将逻辑卷添加到 /etc/fstab 文件中,实现自动挂载。
  9. 扩展逻辑卷(可选)

    • 使用 lvextend 扩展逻辑卷,然后扩展文件系统。
  10. 扩展卷组(VG)

    • 使用 vgextend 将新的物理卷加入到已有的卷组中。

准备物理设备

如果你用的是红帽的培训环境,可以在foundation上,执行一下命令,重置servera,确保servera上的硬盘都是空闲,等servera起来后,用ssh的方式登录即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@foundation0 ~]# rht-vmctl fullreset servera -q
Powering off servera..
Full resetting servera.
Downloading virtual machine definition file for servera.
######################################################################################################### 100.0%
Downloading virtual machine disk image rh134-servera-vda.qcow2
######################################################################################################### 100.0%
Creating virtual machine disk overlay for rh134-servera-vda.qcow2
Downloading virtual machine disk image rh134-servera-vdb.qcow2
######################################################################################################### 100.0%
Creating virtual machine disk overlay for rh134-servera-vdb.qcow2
Downloading virtual machine disk image rh134-servera-vdc.qcow2
######################################################################################################### 100.0%
Creating virtual machine disk overlay for rh134-servera-vdc.qcow2
Downloading virtual machine disk image rh134-servera-vdd.qcow2
######################################################################################################### 100.0%
Creating virtual machine disk overlay for rh134-servera-vdd.qcow2
Starting servera.

确认硬盘的空闲情况

没有任何分区的了,都是空闲的

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 252:0 0 10G 0 disk
├─vda1 252:1 0 1M 0 part
├─vda2 252:2 0 200M 0 part /efi
│ /boot/efi
├─vda3 252:3 0 600M 0 part /boot
└─vda4 252:4 0 9.2G 0 part /
vdb 252:16 0 5G 0 disk
vdc 252:32 0 5G 0 disk
vdd 252:48 0 5G 0 disk

创建物理卷(PV)

实际上,绝大部分的时候,我们的每一个硬盘都是一个PV,很少情况下才需要分区,然后把分区转化成PV

这里我们用vdb来创建一个pv

1
2
3
[root@lixiaohui ~]# pvcreate /dev/vdb
Physical volume "/dev/vdb" successfully created.
Creating devices file /etc/lvm/devices/system.devices

实际上,如果是整个硬盘用于pv,pv不需要手工创建,在创建vg的时候,会自动创建出pv

创建后也可以用pvs显示缩略信息

创建卷组(VG)

用vdb创建了一个名为lxhvg的VG,并使其默认一次性分配的空间为10M,也就是说物理块(PE)为10M,它默认是4M,如果你没有特别的要求,就忽略-s 10M这个参数,我这里只是用于告诉你有这个参数可以指定一次对外分配的空间数值

1
2
[root@lixiaohui ~]# vgcreate lxhvg /dev/vdb -s 10M
Volume group "lxhvg" successfully created

看卷组(VG)信息

也可以用vgs显示缩略信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@lixiaohui ~]# vgdisplay lxhvg
--- Volume group ---
VG Name lxhvg
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 1
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size 4.99 GiB
PE Size 10.00 MiB
Total PE 511
Alloc PE / Size 0 / 0
Free PE / Size 511 / 4.99 GiB
VG UUID gej7fh-BtpP-vzfM-ULI9-FwmW-uWX1-fdt7qS

卷组(VG)信息解析

  • VG Name:卷组名称,为 lxhvg

  • System ID:系统 ID,未指定。

  • Format:卷组的格式,为 lvm2

  • Metadata Areas:元数据区域数量,为 1。

  • Metadata Sequence No:元数据序列号,为 1。

  • VG Access:卷组的访问权限,为读写(read/write)。

  • VG Status:卷组状态,为可调整(resizable)。

  • MAX LV:最大逻辑卷数量,为 0(没有硬限制)。

  • Cur LV:当前逻辑卷数量,为 0。

  • Open LV:当前打开的逻辑卷数量,为 0。

  • Max PV:最大物理卷数量,为 0(没有硬限制)。

  • Cur PV:当前物理卷数量,为 1。

  • Act PV:当前活动的物理卷数量,为 1。

  • VG Size:卷组大小,为 4.99 GiB。

  • PE Size:物理扩展(PE)的大小,为 10.00 MiB。

  • Total PE:总物理扩展数量,为 511。

  • Alloc PE / Size:已分配的物理扩展数量/大小,为 0 / 0。

  • Free PE / Size:未分配的物理扩展数量/大小,为 511 / 4.99 GiB。

  • VG UUID:卷组的唯一标识符,为 gej7fh-BtpP-vzfM-ULI9-FwmW-uWX1-fdt7qS

创建逻辑卷(LV)

-L 100M:指定逻辑卷的大小为 100MB。

-l 100:指定逻辑卷使用 100 个物理扩展(PE)。

1
2
3
4
[root@lixiaohui ~]# lvcreate -n lxhlv -L 100M lxhvg
Logical volume "lxhlv" created.
[root@lixiaohui ~]# lvcreate -n lxhlv-1 -l 100 lxhvg
Logical volume "lxhlv-1" created.

查看逻辑卷(LV)信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[root@lixiaohui ~]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
lxhlv lxhvg -wi-a----- 100.00m
lxhlv-1 lxhvg -wi-a----- 1000.00m
[root@lixiaohui ~]# lvdisplay /dev/lxhvg/lxhlv
--- Logical volume ---
LV Path /dev/lxhvg/lxhlv
LV Name lxhlv
VG Name lxhvg
LV UUID vhq2Gb-3ObQ-KfXv-Efe2-gJKN-C11c-VRNio1
LV Write Access read/write
LV Creation host, time lixiaohui, 2024-12-24 02:14:29 -0500
LV Status available
# open 0
LV Size 100.00 MiB
Current LE 10
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:0

[root@lixiaohui ~]# lvdisplay /dev/lxhvg/lxhlv-1
--- Logical volume ---
LV Path /dev/lxhvg/lxhlv-1
LV Name lxhlv-1
VG Name lxhvg
LV UUID ERrb2W-LOvq-mgrl-l37l-wF7w-8X03-bn7aCT
LV Write Access read/write
LV Creation host, time lixiaohui, 2024-12-24 02:14:42 -0500
LV Status available
# open 0
LV Size 1000.00 MiB
Current LE 100
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:1

逻辑卷(LV)信息解析

  • LV Path:逻辑卷路径,为 /dev/lxhvg/lxhlv-1

  • LV Name:逻辑卷名称,为 lxhlv-1

  • VG Name:卷组名称,为 lxhvg

  • LV UUID:逻辑卷的唯一标识符,为 ERrb2W-LOvq-mgrl-l37l-wF7w-8X03-bn7aCT

  • LV Write Access:逻辑卷的写入权限,为读写(read/write)。

  • LV Creation host, time:逻辑卷创建主机和时间,为 lixiaohui,创建时间为 2024-12-24 02:14:42 -0500

  • LV Status:逻辑卷状态,为可用(available)。

  • # open:当前打开的次数,为 0。

  • LV Size:逻辑卷大小,为 1000.00 MiB

  • Current LE:当前逻辑扩展(LE)数量,为 100。

  • Segments:段数量,为 1。

  • Allocation:分配策略,为继承(inherit)。

  • Read ahead sectors:预读扇区数,为自动(auto)。

  • Block device:块设备编号,为 253:1

格式化逻辑卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lixiaohui ~]# mkfs.xfs /dev/lxhvg/lxhlv
Filesystem should be larger than 300MB.
Log size should be at least 64MB.
Support for filesystems like this one is deprecated and they will not be supported in future releases.
meta-data=/dev/lxhvg/lxhlv isize=512 agcount=4, agsize=6400 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=1 bigtime=1 inobtcount=1 nrext64=0
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
Discarding blocks...Done.

挂载逻辑卷

1
2
[root@lixiaohui ~]# blkid | grep lxhlv
/dev/mapper/lxhvg-lxhlv: UUID="218db05c-6c21-47d7-874c-728f7a190bb1" TYPE="xfs"

挂到/mnt去

临时挂载

1
[root@lixiaohui ~]# mount /dev/lxhvg/lxhlv /mnt

永久挂载

1
2
[root@lixiaohui ~]# vim /etc/fstab
UUID="218db05c-6c21-47d7-874c-728f7a190bb1" /mnt xfs defaults 0 0

看看挂载成功没

1
2
3
[root@lixiaohui ~]# df -h
...
/dev/mapper/lxhvg-lxhlv 95M 6.0M 89M 7% /mnt

扩展卷组

新买了硬盘后,可以把硬盘加入到卷组中,以便于lv可以申请更大的空间

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# vgextend lxhvg /dev/vdc
Physical volume "/dev/vdc" successfully created.
Volume group "lxhvg" successfully extended
[root@lixiaohui ~]# vgs
VG #PV #LV #SN Attr VSize VFree
lxhvg 2 2 0 wz--n- 9.98g <8.91g

[root@lixiaohui ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/vdb lxhvg lvm2 a-- 4.99g <3.92g
/dev/vdc lxhvg lvm2 a-- 4.99g 4.99g

扩展逻辑卷

给lxhlv这个逻辑卷扩容1G过来

1
2
3
4
[root@lixiaohui ~]# lvextend /dev/lxhvg/lxhlv -L +1G
Rounding size to boundary between physical extents: <1.01 GiB.
Size of logical volume lxhvg/lxhlv changed from 100.00 MiB (10 extents) to 1.10 GiB (113 extents).
Logical volume lxhvg/lxhlv successfully resized.

接着我们就发现有点不对劲,怎么lv扩大了之后,文件系统没有扩大,还是只有100M可用?

1
2
3
4
[root@lixiaohui ~]# df -h
Filesystem Size Used Avail Use% Mounted on
...
/dev/mapper/lxhvg-lxhlv 95M 6.0M 89M 7% /mnt

这是因为扩容要分两步走,第一步是扩容lv,第二步是扩容文件系统,不同的文件系统扩容命令不同

  1. extX格式的
1
resize2fs /dev/lxhvg/lxhlv
  1. xfs格式的
1
xfs_growfs /mnt

这个太麻烦了,要想不麻烦,在扩容lv的时候加一个-r参数,我们再加1G

一步就搞定了lv大小和文件系统大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@lixiaohui ~]# lvextend /dev/lxhvg/lxhlv -L +1G -r
Rounding size to boundary between physical extents: <1.01 GiB.
Size of logical volume lxhvg/lxhlv changed from 1.10 GiB (113 extents) to <2.11 GiB (216 extents).
File system xfs found on lxhvg/lxhlv mounted at /mnt.
Extending file system xfs to <2.11 GiB (2264924160 bytes) on lxhvg/lxhlv...
xfs_growfs /dev/lxhvg/lxhlv
meta-data=/dev/mapper/lxhvg-lxhlv isize=512 agcount=4, agsize=6400 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=1 bigtime=1 inobtcount=1 nrext64=0
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 552960
xfs_growfs done
Extended file system xfs on lxhvg/lxhlv.
Logical volume lxhvg/lxhlv successfully resized.

[root@lixiaohui ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 882M 0 882M 0% /dev/shm
tmpfs 353M 9.5M 344M 3% /run
/dev/vda4 9.2G 1.9G 7.4G 20% /
/dev/vda3 536M 197M 340M 37% /boot
/dev/vda2 200M 7.0M 193M 4% /boot/efi
tmpfs 177M 0 177M 0% /run/user/0
/dev/mapper/lxhvg-lxhlv 2.2G 26M 2.1G 2% /mnt

缩容卷组VG

把pv从卷组中移出去之前,需要用下面的命令将卷组的数据,移动到别的pv去

1
2
3
4
[root@lixiaohui ~]# pvmove /dev/vdb
/dev/vdb: Moved: 2.85%
/dev/vdb: Moved: 68.35%
/dev/vdb: Moved: 100.00%

将pv从卷组中移走

1
2
3
4
5
6
7
8
9
[root@lixiaohui ~]# vgreduce lxhvg /dev/vdb
Removed "/dev/vdb" from volume group "lxhvg"
[root@lixiaohui ~]# vgs
VG #PV #LV #SN Attr VSize VFree
lxhvg 1 2 0 wz--n- 4.99g 1.90g
[root@lixiaohui ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/vdb lvm2 --- 5.00g 5.00g
/dev/vdc lxhvg lvm2 a-- 4.99g 1.90g

删除逻辑卷LV

别忘了从fstab去掉

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# umount /mnt
[root@lixiaohui ~]# lvremove /dev/lxhvg/lxhlv
Do you really want to remove active logical volume lxhvg/lxhlv? [y/n]: y
Logical volume "lxhlv" successfully removed.
[root@lixiaohui ~]# lvremove /dev/lxhvg/lxhlv-1
Do you really want to remove active logical volume lxhvg/lxhlv-1? [y/n]: y
Logical volume "lxhlv-1" successfully removed.
[root@lixiaohui ~]# lvs
[root@lixiaohui ~]# vgs
VG #PV #LV #SN Attr VSize VFree
lxhvg 1 0 0 wz--n- 4.99g 4.99g

删除卷组

1
2
3
4
5
6
7
[root@lixiaohui ~]# vgremove lxhvg
Volume group "lxhvg" successfully removed
[root@lixiaohui ~]# vgs
[root@lixiaohui ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/vdb lvm2 --- 5.00g 5.00g
/dev/vdc lvm2 --- 5.00g 5.00g

删除物理卷PV

1
2
3
4
5
[root@lixiaohui ~]# pvremove /dev/vdb
Labels on physical volume "/dev/vdb" successfully wiped.
[root@lixiaohui ~]# pvremove /dev/vdc
Labels on physical volume "/dev/vdc" successfully wiped.
[root@lixiaohui ~]# pvs

Stratis 存储管理

Stratis ⽬前作为技术预览提供

Stratis 利⽤精简配置的概念,从磁盘设备共享池构建⽂件系统。Stratis 不会在您创建⽂件系统时⽴即为其分配物理存储空间,⽽是在⽂件系统存储更多数据时动态地从池中分配该空间。因此,⽂件系统的⼤⼩可能会显⽰为 1 TiB,但从池中分配给它的实际存储空间可能仅有 100 GiB。

stratis

安装和启⽤ Stratis

1
2
[root@lixiaohui ~]# dnf install stratis-cli stratisd -y
[root@lixiaohui ~]# systemctl enable stratisd --now

创建 Stratis 池

1
2
3
4
[root@lixiaohui ~]# stratis pool create pool1 /dev/vdb
[root@lixiaohui ~]# stratis pool list
Name Total / Used / Free Properties UUID Alerts
pool1 5 GiB / 524.87 MiB / 4.49 GiB ~Ca,~Cr, Op 8b5a5f3b-8bd4-486c-bdd8-7eae2a3f5a67 WS001

扩容 Stratis 池

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# stratis pool list
Name Total / Used / Free Properties UUID Alerts
pool1 5 GiB / 524.87 MiB / 4.49 GiB ~Ca,~Cr, Op 8b5a5f3b-8bd4-486c-bdd8-7eae2a3f5a67 WS001
[root@lixiaohui ~]# stratis pool add-data pool1 /dev/vdc
[root@lixiaohui ~]# stratis pool list
Name Total / Used / Free Properties UUID Alerts
pool1 10 GiB / 528.87 MiB / 9.48 GiB ~Ca,~Cr, Op 8b5a5f3b-8bd4-486c-bdd8-7eae2a3f5a67
[root@lixiaohui ~]# stratis blockdev list pool1
Pool Name Device Node Physical Size Tier UUID
pool1 /dev/vdb 5 GiB DATA 754a9755-ffab-4b17-bec7-97add05886dc
pool1 /dev/vdc 5 GiB DATA b4961978-dbf6-4eae-9e68-a1ced375ad7f

创建 Stratis ⽂件系统

使⽤ stratis filesystem create 命令从池中创建⽂件系统。Stratis ⽂件系统的链接位于 /dev/stratis/pool1 ⽬录中

1
2
3
4
[root@lixiaohui ~]# stratis filesystem create pool1 fs1
[root@lixiaohui ~]# stratis filesystem list
Pool Filesystem Total / Used / Free Created Device UUID
pool1 fs1 1 TiB / 546 MiB / 1023.47 GiB Dec 24 2024 02:53 /dev/stratis/pool1/fs1 6d9254f4-3d46-4338-8e90-b62d698f9fef

临时挂载使用 Stratis ⽂件系统

发现默认1TB,我发现在新版本中,这个1TB的默认值在创建文件系统的时候也可以改,比如改成100TB

1
2
[root@lixiaohui ~]# df -h | grep mnt
/dev/mapper/stratis-1-8b5a5f3b8bd4486cbdd87eae2a3f5a67-thin-fs-6d9254f43d4643388e90b62d698f9fef 1.0T 7.2G 1017G 1% /mnt

写点东西进去

1
2
3
[root@lixiaohui ~]# touch /mnt/file{1..10}
[root@lixiaohui ~]# ls /mnt
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9

永久挂载 Stratis ⽂件系统

这个fstab要注意defaults后面的参数,x-systemd.requires=stratisd.service,如果未在 /etc/fstab ⽂件中为每个 Stratis ⽂件系统包含 x-systemd.requires=stratisd.service 挂载选项,则计算机将⽆法正常启动

看着长,其实不要紧,用这个方法可以查到man systemd.mount

1
2
vim /etc/fstab
UUID=6d9254f4-3d46-4338-8e90-b62d698f9fef /tmp xfs defaults,x-systemd.requires=stratisd.service 0 0

创建 Stratis ⽂件系统快照

这个快照是独立于源文件系统的,你可以看成独立的文件系统,所以可以看成备份

1
2
3
4
5
[root@lixiaohui ~]# stratis filesystem snapshot pool1 fs1 snapshot1
[root@lixiaohui ~]# stratis filesystem list
Pool Filesystem Total / Used / Free Created Device UUID
pool1 fs1 1 TiB / 546 MiB / 1023.47 GiB Dec 24 2024 02:53 /dev/stratis/pool1/fs1 6d9254f4-3d46-4338-8e90-b62d698f9fef
pool1 snapshot1 1 TiB / 546 MiB / 1023.47 GiB Dec 24 2024 02:58 /dev/stratis/pool1/snapshot1 d8ef8f26-fad0-4716-b4ff-5e5988a966aa

恢复 Stratis ⽂件系统

假设数据丢了,可以把快照挂过来复制数据

1
2
3
[root@lixiaohui ~]# mount /dev/stratis/pool1/snapshot1 /opt
[root@lixiaohui ~]# ls /opt
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9

第九章 访问⽹络附加存储

通过 NFS 管理⽹络附加存储

网络文件系统(NFS)是一种允许不同计算机系统在网络上共享文件和目录的协议。NFS 最初由 Sun Microsystems 开发,现已成为 Unix 和类 Unix 系统(如 Linux)的标准文件系统共享方法。通过 NFS,共享的文件和目录可以像本地磁盘一样在远程计算机上进行访问。

NFS 的主要特点

  1. 跨网络共享文件
    NFS 允许在网络中的不同计算机之间共享文件和目录,实现数据的集中存储和管理。

  2. 透明访问
    用户可以像访问本地文件一样访问远程文件,无需了解底层网络细节。

  3. 权限管理
    通过文件权限和访问控制列表(ACL),可以对 NFS 共享进行细粒度的访问控制。

  4. 兼容性强
    支持多种操作系统,包括 Unix、Linux 和 Windows,使得异构系统之间的数据共享变得容易。

NFS 共享的典型应用场景

  1. 家庭网络
    在家庭网络中,NFS 可用于共享多媒体文件、文档和备份。

  2. 企业环境
    在企业环境中,NFS 用于集中存储和管理用户的文件,提高数据的可用性和安全性。

  3. 开发和测试环境
    开发团队可以使用 NFS 共享项目文件,方便协同工作和版本控制。

安装NFS服务器

服务器端这部分不属于课程内容

不管是服务器还是客户端,都必须安装nfs-utils

1
[root@lixiaohui ~]# dnf install nfs-utils -y

准备资源

1
2
3
4
5
[root@lixiaohui ~]# mkdir /nfs
[root@lixiaohui ~]# chmod 777 /nfs/ -R
[root@lixiaohui ~]# semanage fcontext -a -t public_content_t "/nfs(/.*)?"
[root@lixiaohui ~]# restorecon -RvF /nfs/
Relabeled /nfs from unconfined_u:object_r:default_t:s0 to system_u:object_r:public_content_t:s0

将资源以读写方法共享给所有人

1
2
3
[root@lixiaohui ~]# vim /etc/exports.d/mynfs.exports
[root@lixiaohui ~]# cat /etc/exports.d/mynfs.exports
/nfs *(rw)

启动NFS服务

1
[root@lixiaohui ~]# systemctl enable nfs-server.service --now

开通相应的防火墙规则

1
2
3
4
[root@lixiaohui ~]# firewall-cmd --add-service=nfs --add-service=rpc-bind --add-service=mountd --permanent
success
[root@lixiaohui ~]# firewall-cmd --reload
success

管理NFS客户端

不管是服务器还是客户端,都必须安装nfs-utils

1
[root@lixiaohui ~]# dnf install nfs-utils -y

检查服务器给我们共享了什么

在nfsv3协议中,需要用showmount -e SERVER的格式看

在nfsv4协议中,用showmount -e SERVER的格式查看的时候,不会有任何返回,你直接mount它的根到本地ls看就行

此处假设我们的服务器名为servera

1
2
3
[root@serverb ~]# showmount -e servera
Export list for servera:
/nfs *

将服务器的内容,挂载到我们的/mnt上

1
2
3
[root@serverb ~]# mount servera:/nfs /mnt
[root@serverb ~]# df -h | grep mnt
servera:/nfs 9.2G 1.9G 7.4G 21% /mnt

永久挂载

1
2
[root@serverb ~]# vim /etc/fstab
servera:/nfs /mnt nfs defaults 0 0

⾃动挂载⽹络附加存储

autofs 基本概念

autofs 是一种动态管理挂载文件系统的工具,通过配置文件自动挂载和卸载文件系统。autofs 根据访问需求挂载文件系统,并在不再使用时自动卸载,从而提高系统性能和资源利用率。

autofs 的主要特点

  1. 按需挂载:只有在需要访问时才挂载文件系统,不占用资源。

  2. 自动卸载:在文件系统不使用时自动卸载,释放资源。

  3. 配置灵活:支持通过多个配置文件和映射文件来管理不同的挂载点。

  4. 兼容性强:支持 NFS、CIFS(SMB)、本地文件系统等多种文件系统类型。

autofs 的工作流程

  1. 安装和配置:通过安装 autofs 软件包并配置主自动挂载文件和映射文件来启用自动挂载功能。

  2. 触发挂载:当访问配置的挂载点时,autofs 自动挂载相应的文件系统。

  3. 自动卸载:在挂载点一段时间不被访问时,autofs 自动卸载文件系统。

使用 autofs 与配置 /etc/fstab 的比较

特性autofs/etc/fstab
挂载方式按需挂载,访问时自动挂载开机时自动挂载
卸载方式不使用时自动卸载,释放资源需要手动卸载,始终占用资源
配置文件/etc/auto.master 和映射文件/etc/fstab
性能提高性能和资源利用率可能占用不必要的系统资源
复杂度配置较复杂,但更灵活配置较简单,适用于固定挂载需求
适用场景动态挂载需求,如频繁访问的网络存储固定挂载需求,如系统启动时的必要挂载

选择使用场景

autofs:适用于动态挂载需求,特别是访问频率不高或需要提高系统性能和资源利用率的场景。

/etc/fstab:适用于固定挂载需求,如系统启动时必须挂载的文件系统。

安装autofs

autofs作为客户端这一边的自动挂载器,需要安装才能使用,我们把serverb当作客户端,因为我们刚才的servera安装了nfs服务器

1
2
[root@serverb ~]# dnf install autofs -y
[root@serverb ~]# systemctl enable autofs --now

主配置文件是:/etc/auto.master

辅助配置文件可以是:/etc/auto.master.d/xxx.autofs

直接挂载和间接挂载

在自动挂载文件系统时,autofs 提供了直接挂载和间接挂载两种方式。以下是这两种挂载方式的详细描述:

直接挂载与间接挂载的对比

特性直接挂载(Direct Mounts)间接挂载(Indirect Mounts)
配置路径/etc/auto.master 中使用 /- 前缀/etc/auto.master 中使用目录前缀
挂载点固定路径,访问时挂载到指定路径位于目录下的子目录,管理多个挂载点
适用场景需要精确控制挂载位置时管理多个挂载点,提供灵活性和简化配置
配置示例/mnt/nfs_share /etc/auto.direct/mnt /etc/auto.indirect

直接挂载(Direct Mounts)

直接挂载是指在自动挂载配置中,挂载点路径是固定的,访问时自动挂载到指定的路径。直接挂载通常用于需要精确控制挂载位置的场景。

直接挂载的配置示例

/etc/auto.master 中配置直接挂载:

1
2
3
[root@serverb ~]# vim /etc/auto.master
...
/- /etc/auto.direct

/etc/auto.direct 文件中定义具体的挂载点和挂载参数,规则文件内容如下:

这种规则如果不会写,可以参考/etc/auto.misc文件格式

1
2
[root@serverb ~]# vim /etc/auto.direct
/mnt/nfs_share -fstype=nfs,rw servera:/nfs

重启服务

1
[root@serverb ~]# systemctl restart autofs

切入到目录时,自动完成了挂载

1
2
3
4
5
[root@serverb ~]# cd /mnt/nfs_share/
[root@serverb nfs_share]# df -h
Filesystem Size Used Avail Use% Mounted on
...
servera:/nfs 9.2G 1.9G 7.4G 21% /mnt/nfs_share

间接挂载(Indirect Mounts)

间接挂载是指在自动挂载配置中,挂载点是一个目录,而实际的挂载点位于这个目录下的子目录中。间接挂载通常用于管理多个挂载点,提供更灵活的管理方式。

间接挂载的配置示例

我们希望,当我们进入到/opt/abc的时候,自动将servera的共享挂过来

/etc/auto.master 中配置间接挂载:

1
2
3
[root@serverb ~]# vim /etc/auto.master
/opt /etc/auto.indirect
···

规则文件内容如下:

这种规则如果不会写,可以参考/etc/auto.misc文件格式

1
2
[root@serverb ~]# vim /etc/auto.indirect
abc -fstype=nfs,rw servera:/nfs

重启服务

1
[root@serverb ~]# systemctl restart autofs

切入到/opt/abc时,自动挂载

1
2
3
4
5
[root@serverb nfs_share]# cd /opt/abc
[root@serverb abc]# df -h
Filesystem Size Used Avail Use% Mounted on
...
servera:/nfs 9.2G 1.9G 7.4G 21% /opt/abc

通配符挂载

在间接挂载中,可以用通配符来表示将来用户要切换的具体目录,并同时挂载服务器上和用户需求同名的目录挂载到用户需求的目录上

我们先在servera上准备一下,创建几个目录

1
2
3
4
5
6
[root@lixiaohui ~]# mkdir /nfs/lixiaohui
[root@lixiaohui ~]# mkdir /nfs/zhangsan
[root@lixiaohui ~]# restorecon -RvF /nfs/
Relabeled /nfs/lixiaohui from unconfined_u:object_r:public_content_t:s0 to system_u:object_r:public_content_t:s0
Relabeled /nfs/zhangsan from unconfined_u:object_r:public_content_t:s0 to system_u:object_r:public_content_t:s0
[root@lixiaohui ~]# chmod -R 777 /nfs

我们希望在客户端serverb上,当用户切换到/opt/lixiaohui的时候,自动将servera的/nfs/lixiaohui挂载到/opt/lixiaohui,当用户切换到/opt/zhangsan的时候,自动将servera的/nfs/zhangsan挂载到/opt/zhangsan

当然,你可能认为,这就是创建两条条目的问题,但是不能这么想,假设这是20000条呢,维护麻烦,所以规则文件要写成以下模样:

1
2
[root@serverb abc]# vim /etc/auto.indirect
* -fstype=nfs,rw servera:/nfs/&

重启服务

1
[root@serverb ~]# systemctl restart autofs

切入到/opt/zhangsan或lixiaohui时,自动挂载

非常的智能化,不再需要维护太多的条目

1
2
3
4
5
6
[root@serverb abc]# cd /opt/lixiaohui
[root@serverb lixiaohui]# df -h | grep lixiaohui
servera:/nfs/lixiaohui 9.2G 1.9G 7.4G 21% /opt/lixiaohui
[root@serverb lixiaohui]# cd /opt/zhangsan
[root@serverb zhangsan]# df -h
servera:/nfs/zhangsan 9.2G 1.9G 7.4G 21% /opt/zhangsan

systemd自动挂载

在 Linux 系统中,systemd 可以通过在 /etc/fstab 文件中添加 x-systemd.automount 选项来自动创建和管理挂载点的单元文件。单元文件的名称规则基于挂载点路径。这种方式可以简化挂载配置,并且只在需要访问时挂载,提高系统性能和资源利用率。

systemd.automount 单元仅可⽀持绝对路径挂载点,类似于 autofs 直接映射

编辑 /etc/fstab 文件: 在 /etc/fstab 文件中添加或修改挂载条目,加入 x-systemd.automount 选项。

1
2
[root@serverb ~]# vim /etc/fstab
servera:/nfs /tmp nfs defaults,x-systemd.automount 0 0

加载配置

1
[root@serverb ~]# systemctl daemon-reload

测试效果

看看是否创建了基于挂载点名称的服务,并启动

1
2
3
4
5
6
7
8
9
10
11
[root@serverb ~]# systemctl cat tmp.automount
# /run/systemd/generator/tmp.automount
# Automatically generated by systemd-fstab-generator

[Unit]
SourcePath=/etc/fstab
Documentation=man:fstab(5) man:systemd-fstab-generator(8)

[Automount]
Where=/tmp

1
2
3
4
5
6
7
8
9
10
11
12
[root@serverb ~]# systemctl start tmp.automount
[root@serverb ~]# systemctl status tmp.automount
● tmp.automount
Loaded: loaded (/etc/fstab; generated)
Active: active (waiting) since Tue 2024-12-24 04:25:04 EST; 2s ago
Until: Tue 2024-12-24 04:25:04 EST; 2s ago
Triggers: ● tmp.mount
Where: /tmp
Docs: man:fstab(5)
man:systemd-fstab-generator(8)

Dec 24 04:25:04 serverb.lab.example.com systemd[1]: Set up automount tmp.automount.

文件都在了

1
2
[root@serverb ~]# ls /tmp/
lixiaohui zhangsan
1
2
[root@serverb ~]# df | grep /tmp
servera:/nfs 9598976 1938944 7660032 21% /tmp

第十章 控制启动过程

选择启动⽬标

红帽企业 Linux 9 的启动过程

  1. 接通电源

    • 系统固件(现代 UEFI 或 BIOS)运行开机自检 (POST),并初始化硬件。
  2. 配置 BIOS 或 UEFI

    • 启动过程早期可以按特定组合键(例如 F2)配置系统 BIOS 或 UEFI。
  3. UEFI 启动固件

    • 通过搜索或配置磁盘上的主启动记录 (MBR) 来找到可启动设备。
  4. 读取启动加载器

    • 系统固件从磁盘读取启动加载器,并将控制权交给启动加载器。在 RHEL 9 系统中,启动加载器为 GRUB2。
  5. 安装 GRUB2

    • grub2-install 命令用于安装 GRUB2 作为 BIOS 系统的启动加载器。不要直接使用 grub2-install 安装 UEFI 启动加载器,RHEL 9 提供了包含必要签名的预构建文件 /boot/efi/EFI/redhat/grubx64.efi
  6. 加载 GRUB2 配置

    • GRUB2 从 /boot/grub2/grub.cfg 文件 (BIOS) 或 /boot/efi/EFI/redhat/grub.cfg 文件 (UEFI) 加载配置,并显示菜单以选择要启动的内核。
  7. GRUB2 配置

    • 使用 /etc/grub.d/ 目录和 /etc/default/grub 文件进行配置,通过 grub2-mkconfig 命令生成配置文件。
  8. 加载内核和 initramfs

    • 选择内核或超时后,启动加载器从磁盘加载内核和 initramfs 并放入内存。initramfs 镜像包含启动所需的内核模块和初始化脚本。
  9. 配置 initramfs

    • 使用 /etc/dracut.conf.d/ 目录、dracut 命令和 lsinitrd 命令配置和检查 initramfs 文件。
  10. 控制权交给内核

    • 启动加载器将控制权交给内核,并传递命令行选项和 initramfs 镜像位置。
  11. 执行 initramfs 中的 init 脚本

    • 内核初始化硬件后,从 initramfs 镜像执行 /sbin/init 脚本。在 RHEL 9 中,该脚本是指向 systemd 的链接。
  12. 执行 systemd 单元

    • initramfs 中的 systemd 单元执行 initrd.target 目标的所有单元,包括将根文件系统挂载到 /sysroot
  13. 配置 root 文件系统

    • 使用 /etc/fstab 文件进行配置。
  14. 切换根文件系统

    • 内核将根文件系统切换到 /sysroot 目录中的根文件系统,systemd 单元重新执行自身。
  15. 启动目标单元

    • systemd 查找默认目标并启动或停止单元以符合目标配置,自动解决依赖关系。
  16. 配置 systemd

    • 使用 /etc/systemd/system/default.target 文件和 /etc/systemd/system/ 目录进行配置。

boot

关机或重启

  1. systemctl poweroff 命令会停止所有运行的服务,卸载所有文件系统(或者在文件系统无法卸载时以只读形式将其重新挂载),然后关闭系统。

  2. systemctl reboot 命令会停止所有运行的服务,卸载所有文件系统,然后重新启动系统。

systemd 目标是一组系统必须启动以达到预期状态的 systemd 单元。下表列出了最重要的目标:

目标用途
graphical.target该目标支持多用户,并提供基于图形和基于文本的登录。
multi-user.target该目标支持多用户,并仅提供基于文本的登录。
rescue.target该目标提供了一个单用户系统,以支持修复您的系统。
emergency.target当 rescue.target 单元无法启动时,该目标将启动最小的系统以修复您的系统。

除了这些还有一些,可以用下面的方法查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@lixiaohui ~]# systemctl list-units --type target
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
cryptsetup.target loaded active active Local Encrypted Volumes
getty.target loaded active active Login Prompts
integritysetup.target loaded active active Local Integrity Protected Volumes
local-fs-pre.target loaded active active Preparation for Local File Systems
local-fs.target loaded active active Local File Systems
multi-user.target loaded active active Multi-User System
network-online.target loaded active active Network is Online
network-pre.target loaded active active Preparation for Network
network.target loaded active active Network
nfs-client.target loaded active active NFS client services
nss-user-lookup.target loaded active active User and Group Name Lookups
paths.target loaded active active Path Units
remote-fs-pre.target loaded active active Preparation for Remote File Systems
remote-fs.target loaded active active Remote File Systems
rpc_pipefs.target loaded active active rpc_pipefs.target
rpcbind.target loaded active active RPC Port Mapper
slices.target loaded active active Slice Units
sockets.target loaded active active Socket Units
sshd-keygen.target loaded active active sshd-keygen.target
swap.target loaded active active Swaps
sysinit.target loaded active active System Initialization
timers.target loaded active active Timer Units
veritysetup.target loaded active active Local Verity Protected Volumes

设置默认目标

系统启动时,systemd 单元会激活 default.target 目标。通常,/etc/systemd/system/ 目录中的默认目标是指向 graphical.target 或 multi-user.target 目标的符号链接。

查询系统下次启动时的target是哪个

1
2
[root@lixiaohui ~]# systemctl get-default
multi-user.target

将默认目标改一下

1
2
3
4
5
6
[root@lixiaohui ~]# systemctl set-default graphical.target
Removed "/etc/systemd/system/default.target".
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/graphical.target.
[root@lixiaohui ~]#
[root@lixiaohui ~]# systemctl get-default
graphical.target

不过以上的更改来说,需要提供已经能启动的情况下才能有效,如果已经起不来了,或者在启动时需要进入特定的target,就需要按照下面的步骤来

  1. 启动或重新启动系统。

  2. 按任意键(Enter 除外,它用于执行正常启动)中断启动加载器菜单倒计时。

  3. 将光标移至要启动的内核条目。

  4. 按 e 编辑当前条目。

  5. 将光标移到以 linux 开头的行,这是内核命令行。

  6. 附加 systemd.unit=xx.target,例如 systemd.unit=emergency.target

  7. 按 Ctrl+x 使用这些更改进行启动。

重置 root 密码

忘了密码这种事,想都可以想到,尤其是有些时候,也没人给我们交接密码,我们就得按照以下步骤尝试重置root口令

从红帽企业 Linux 9 开始,如果从 DVD 安装系统,则默认的内核在尝试进入维护模式时会要求输入 root 密码。因此,如需重置丢失的 root 密码,必须使用救援内核

按照以下步骤操作重置密码:

  1. 重新启动系统。

  2. 按任意键(Enter 除外)中断启动加载器倒计时。

  3. 将光标移到要启动的救援内核条目(名称中带有 rescue 一词的条目)。

  4. 按 e 编辑选定的条目。

  5. 将光标移到内核命令行(以 linux 开头的行)。

  6. 附加 rd.break console=tty0。利用该选项,就在系统从 initramfs 镜像向实际系统移交控制权前,系统将会中断。

  7. 按 Ctrl+x 使用这些更改来启动。如果无法正常看到shell提示符,请在上一步去掉console=tty0再重试

  8. 提示时,按 Enter 执行维护。

  9. 以读写方式重新挂载/sysroot,因为硬盘被只读挂在这里

1
mount -o remount,rw /sysroot
  1. chroot切换根目录到硬盘中
1
chroot /sysroot
  1. 设置新 root 密码
1
passwd root
  1. 重新标记SELinux
1
touch /.autorelabel
  1. 多次exit使得服务器正常启动,在启动中,你将看到SELinux重新标记,并自动重启

第一个exit将退出 chroot 存放位置,第二个exit将退出 initramfs 调试 shell。

1
2
exit
exit

修复在启动时出现的⽂件系统问题

常见文件系统挂载问题

  1. 文件系统损坏

    • 描述:文件系统损坏可能导致无法挂载。

    • 处理systemd 服务会尝试修复文件系统。如果问题无法自动修复,系统会打开紧急 shell。

  2. 设备/UUID 不存在

    • 描述:指定的设备或 UUID 在启动时未检测到或不存在。

    • 处理systemd 服务在等待设备变为可用时超时。如果设备不响应,系统会打开紧急 shell。

故障处理

  • 紧急 shell
    systemd 无法自动解决挂载问题时,系统会进入紧急 shell 模式,用户需要输入 root 用户密码进行手动故障排除和修复。

  • 检查 /etc/fstab 配置
    确保 /etc/fstab 文件中的条目和语法正确,设备或 UUID 配置准确。

  • 修复文件系统
    在紧急 shell 中使用工具(如 fsck)手动检查和修复文件系统。

具体来说,你需要掌握主动进入紧急shell的能力,这用的是上一个小节的内容,在Linux后追加emgergency.target

第十一章 管理⽹络安全

管理服务器防⽕墙

在红帽企业 Linux 9 中,nftables 框架是系统防⽕墙核⼼,取代了已弃⽤的 iptables 框架。

firewalld 服务

firewalld 服务是⼀种动态防⽕墙管理器,也是 nftables 框架的推荐前端。firewalld 服务将所有⽹络流量分为多个区域,从⽽简化防⽕墙管理。通常⽽⾔,如果流量不与允许的端⼝和协议或服务匹配,则流量会被拒绝。trusted 区域默认情况下允许所有流量,它是⼀个例外。

列出所有的区域

以下我删除了规则,只保留了名称,可以看成,我们的public处于活动状态,意思是public中的规则正在影响我们的流量

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# firewall-cmd --list-all-zones
block
dmz
drop
external
home
internal
nm-shared
public (active)
trusted
work

列出特定区域中的规则

那我们就看看public里面有什么

可以看到public区域中,关联了eth0这个网卡,并允许了一些常规服务,比如ssh什么的,而且public这个默认区域不需要指定–zone参数,而且后期服务区新增的网卡,默认也会加到public区域中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lixiaohui ~]# firewall-cmd --list-all --zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

配置 firewalld

配置防火墙有以下几种办法

  1. 安装firewall-config图形化,并用图形化配置

  2. 启用cockpit,用cockpit网页版配置

  3. 用命令行配置,这是最推荐的方法

不管你用哪种方法,一定要记得把规则放入到permanent模式中,runtime模式中的规则,重新加载规则或重启服务器后,会失效

运行时模式(runtime)

定义:运行时模式中的规则只在当前会话有效,重新加载规则或重启系统后这些规则会失效。

应用场景:适用于临时测试或短期需要的规则,方便在不影响永久配置的情况下进行临时调整。

案例:

允许http流量和2000/tcp端口通过

1
2
3
4
[root@lixiaohui ~]# firewall-cmd --add-service=http
success
[root@lixiaohui ~]# firewall-cmd --add-port=2000/tcp
success

你去测试会发现马上就生效了,我们来列出防火墙规则看看

1
2
3
4
5
6
7
8
9
[root@lixiaohui ~]# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client http ssh
ports: 2000/tcp
protocols:

重新加载一下规则看看

刚才配置的规则已丢失

1
2
3
4
5
6
7
8
9
10
[root@lixiaohui ~]# firewall-cmd --reload
success
[root@lixiaohui ~]# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:

永久模式(permanent)

定义:永久模式中的规则会被保存在配置文件中,即使系统重新启动或 firewalld 服务重新加载后,这些规则仍然存在。

应用场景:适用于需要长期生效的规则,确保在重启或重新加载服务后仍然有效。

案例:

允许http流量和2000/tcp端口通过

我们注意到,每条命令后面都有一个--permanent

1
2
3
4
[root@lixiaohui ~]# firewall-cmd --add-service=http --permanent
success
[root@lixiaohui ~]# firewall-cmd --add-port=2000/tcp --permanent
success

重新加载规则后,我们配置的规则还在,说明是持久性的

1
2
3
4
5
6
7
8
9
10
[root@lixiaohui ~]# firewall-cmd --reload
success
[root@lixiaohui ~]# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client http ssh
ports: 2000/tcp

以上两条持久性规则,想要删除,只需要把add换成remove即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@lixiaohui ~]# firewall-cmd --remove-service=http --permanent
success
[root@lixiaohui ~]# firewall-cmd --remove-port=2000/tcp --permanent
success
[root@lixiaohui ~]# firewall-cmd --reload
success
[root@lixiaohui ~]# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:

控制 SELinux 端⼝标记

除了⽂件上下⽂和进程类型标记外,SELinux 还使⽤ SELinux 上下⽂来标记⽹络端⼝。SELinux 通过标记⽹络端⼝并将规则包含在服务的⽬标策略中来控制⽹络访问。当⽬标进程尝试打开端⼝进⾏侦听时,SELinux 会验证策略是否包含⽀持该进程与上下⽂绑定的条⽬。然后,SElinux 可以阻⽌恶意服务控制本应由其他合法⽹络服务使⽤的端⼝。

列出特定的端口标记

在-l后面用grep筛选即可,不筛选,默认输出所有selinux运行的标签和端口绑定列表

1
2
3
4
5
6
[root@lixiaohui ~]# semanage port -l | grep http
http_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t tcp 5988
pegasus_https_port_t tcp 5989

如果需要修改标签和端口的绑定关系,以下是语法

1
semanage port -a -t port_label -p tcp|udp PORTNUMBER

删除语法:

1
semanage port -d -t port_label -p tcp|udp PORTNUMBER

修改语法:

1
semanage port -m -t port_label -p tcp|udp PORTNUMBER

我们来做一个案例

安装一个httpd网站服务,然后修改网站服务配置文件端口,从80改成20000,然后解决SELinux允许httpd服务加载20000的问题

1
[root@lixiaohui ~]# dnf install httpd -y

将监听端口改为20000

1
2
3
[root@lixiaohui ~]# sed -i 's/Listen 80/Listen 20000/' /etc/httpd/conf/httpd.conf
[root@lixiaohui ~]# grep ^Listen /etc/httpd/conf/httpd.conf
Listen 20000

启动服务看看

发现不管怎么重启,都起不来了

1
2
3
4
5
6
7
[root@lixiaohui ~]# systemctl enable httpd --now
Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
Job for httpd.service failed because the control process exited with error code.
See "systemctl status httpd.service" and "journalctl -xeu httpd.service" for details.
[root@lixiaohui ~]# systemctl restart httpd
Job for httpd.service failed because the control process exited with error code.
See "systemctl status httpd.service" and "journalctl -xeu httpd.service" for details.

根据提示,看看错在哪里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@lixiaohui ~]# systemctl status httpd.service -l
× httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
Active: failed (Result: exit-code) since Tue 2024-12-24 07:35:22 EST; 46s ago
Docs: man:httpd.service(8)
Process: 58331 ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND (code=exited, status=1/FAILURE)
Main PID: 58331 (code=exited, status=1/FAILURE)
Status: "Reading configuration..."
CPU: 24ms

Dec 24 07:35:07 lixiaohui systemd[1]: Starting The Apache HTTP Server...
Dec 24 07:35:22 lixiaohui httpd[58331]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::5054:ff:fe00:fa0a%eth0. S>
Dec 24 07:35:22 lixiaohui httpd[58331]: (13)Permission denied: AH00072: make_sock: could not bind to address [::]:20000
Dec 24 07:35:22 lixiaohui httpd[58331]: (13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:20000
Dec 24 07:35:22 lixiaohui httpd[58331]: no listening sockets available, shutting down
Dec 24 07:35:22 lixiaohui httpd[58331]: AH00015: Unable to open logs
Dec 24 07:35:22 lixiaohui systemd[1]: httpd.service: Main process exited, code=exited, status=1/FAILURE
Dec 24 07:35:22 lixiaohui systemd[1]: httpd.service: Failed with result 'exit-code'.
Dec 24 07:35:22 lixiaohui systemd[1]: Failed to start The Apache HTTP Server.

清晰的看到无法绑定到20000端口才失败的

那我们看看都运行绑定哪些端口

明显没有看到20000

1
2
3
4
5
6
[root@lixiaohui ~]# semanage port -l | grep http
http_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t tcp 5988
pegasus_https_port_t tcp 5989

看看selinux告警怎么说

真是个好心人,连命令都告诉我们了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@lixiaohui ~]# sealert -a /var/log/audit/audit.log
100% done
found 1 alerts in /var/log/audit/audit.log
--------------------------------------------------------------------------------

SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 20000.

***** Plugin bind_ports (92.2 confidence) suggests ************************

If you want to allow /usr/sbin/httpd to bind to network port 20000
Then you need to modify the port type.
Do
# semanage port -a -t PORT_TYPE -p tcp 20000
where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, jboss_management_port_t, jboss_messaging_port_t, ntop_port_t, puppet_port_t.

添加一下20000端口的运行策略

1
2
3
4
5
6
7
[root@lixiaohui ~]# semanage port -a -t http_port_t -p tcp 20000
[root@lixiaohui ~]# semanage port -l | grep http
http_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 20000, 80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t tcp 5988
pegasus_https_port_t tcp 5989

这回能看到20000了,重启服务看看

1
2
3
4
5
6
[root@lixiaohui ~]# systemctl restart httpd
[root@lixiaohui ~]# echo $?
0

[root@lixiaohui ~]# ss -tunlp | grep :20000
tcp LISTEN 0 511 *:20000 *:* users:(("httpd",pid=58387,fd=4),("httpd",pid=58386,fd=4),("httpd",pid=58385,fd=4),("httpd",pid=58383,fd=4))

一切都好起来了,服务成功启动

第十二章 安装红帽企业 Linux

本章另见PXE部署指南

不再赘述

第十三章 运行容器

容器简介

容器技术是一种轻量级虚拟化方法,通过在共享操作系统内核的基础上,将应用及其依赖项封装在独立的环境中运行。它提供了高度的可移植性和一致性,使应用能够在不同的计算环境之间无缝转移。常见的容器技术包括 Docker 和 podman等。

containervsvirt

容器会使⽤ Linux 内核功能,如 namespaces、控制组(cgroups)、SELinux 和安全计算模式(seccomp)

容器与虚拟机对⽐

容器的作⽤通常与 虚拟机(VM)类似,在虚拟机中,应⽤驻留在独⽴的环境中,并通过虚拟化⽹络进⾏通信。虽然该⽤例乍⼀看是⼀样的,但容器的占⽤空间更⼩,启动和停⽌⽐虚拟机更快。在内存和磁盘使⽤量⽅⾯,虚拟机通常以 GB 为单位,⽽容器以 MB 为单位。

虚拟机 vs. 容器

方面虚拟机容器
计算机功能Hypervisor(虚拟机监控器)容器引擎
管理虚拟机管理界面容器引擎或编排软件
虚拟化级别完全虚拟化环境仅相关部分
体积以GB为单位以MB字节为单位
可移植性通常只在同一虚拟机监控器上任何符合 OCI 标准的引擎

比较说明

  • 虚拟机提供一个完整的虚拟化环境,包括完整的操作系统,由虚拟机监控器管理。这使得它们更重,通常以GB为单位,并且它们的可移植性常局限于同一个虚拟机监控器环境。

  • 另一方面,容器只虚拟化运行应用所需的必要组件,使其更加轻量化和高效,通常以MB为单位。容器由容器引擎或编排软件管理,提供更高的移植性,因为它们可以在任何符合 OCI 标准的引擎上运行。

Podman 简介

Podman 是⼀个开源⼯具,可⽤于在本地管理容器。借助 Podman,可以查找、运⾏、构建或部署 OCI(开放容器计划)容器和容器镜像。Podman 默认⽆守护进程。其他⼀些容器⼯具使⽤守护进程来代理请求,由此带来了单点故障。此外,守护进程可能需要提升特权,这就存在安全问题。Podman 直接与容器、镜像和镜像仓库交互,⽆需守护进程。

安装容器管理工具

这个安装过程可能较长,请等待

1
[root@lixiaohui ~]# dnf install container-tools -y

看看安装的版本,如果能出现版本,就是安装好了

1
2
[root@lixiaohui ~]# podman -v
podman version 4.6.1

容器镜像与仓库

容器镜像(Container Image)是一个轻量级、独立的可执行软件包,其中包含了运行应用程序所需的一切,包括代码、运行时、库和配置文件。它是创建和部署容器的基础。容器镜像包含应⽤的打包版本,以及运⾏应⽤所需的所有依赖项。镜像可以脱离容器存在,但容器必须依赖镜像存在,因为容器需使⽤容器镜像来构建执⾏应⽤的运⾏时环境。

容器镜像的特点

  1. 自包含性

    • 容器镜像包含了应用程序及其所有依赖项,确保在任何环境中都可以一致地运行。
  2. 层次结构

    • 容器镜像由多个只读层组成,每一层都基于前一层。修改镜像时,会创建一个新的层,并将更改保存到该层。这种设计提高了存储和传输效率。
  3. 可移植性

    • 容器镜像可以在不同的操作系统和云平台之间无缝迁移,因为它们包含了运行应用所需的一切。
  4. 版本控制

    • 通过镜像标签(tags)和唯一标识符(SHA256 哈希值),可以对镜像进行版本管理,确保可以回滚到之前的版本。
  5. 轻量级

    • 容器镜像相比虚拟机镜像更加轻量,因为它们共享操作系统内核,并且只包含运行应用所需的最小依赖项。

容器镜像的命名规范

容器镜像的名称通常由三个部分组成:仓库名、镜像名 和 标签,案例如下:

1
registry.lab.example.com/ubi9/ubi

容器镜像仓库

容器镜像仓库(Container Registry)是用于存储和分发容器镜像的中央存储库。它允许开发者将容器镜像上传、管理和分享,使得在不同环境之间部署应用变得更加方便和一致。

常见的容器镜像仓库

  1. Docker Hub

    • 官方的公共容器镜像仓库,提供免费的公共镜像存储和付费的私有镜像存储。
  2. Harbor

    • 一个开源的企业级容器镜像仓库,提供高级的安全性和管理功能。
  3. Quay

    • 一个开源的企业级容器镜像仓库,提供高级的安全性和管理功能,课程中已内置,这个仓库是RedHat的一部分。
  4. redhat

管理podman软件的容器镜像仓库

容器注册表的默认配置文件是 /etc/containers/registries.conf 文件,如果 $HOME/.config/containers 目录中也有一个registries.conf,参数就以家目录里的为准

1
2
3
4
5
[root@lixiaohui ~]# grep -v ^# /etc/containers/registries.conf

unqualified-search-registries = ["registry.access.redhat.com", "registry.redhat.io", "docker.io"]

short-name-mode = "enforcing"

unqualified-search-registries 这里定义了从哪里下载镜像,你可以把你的私有仓库按照格式写进去

课程中的仓库地址为:registry.lab.example.com,所以我写一个案例

1
2
3
4
5
6
[root@lixiaohui ~]# grep -v ^# /etc/containers/registries.conf

unqualified-search-registries = ["registry.lab.example.com", "registry.access.redhat.com", "registry.redhat.io", "docker.io"]

insecure = true
short-name-mode = "enforcing"

我不止添加了域名,还添加了insecure = true来解决https不受信任的问题

以下是写在自己家目录中的一个样例:

1
2
3
4
5
6
7
8
mkdir -p /home/contsvc/.config/containers
cat > /home/lixiaohui/.config/containers/registries.conf <<-'EOF'
unqualified-search-registries = ['registry.lab.example.com']
[[registry]]
location = "registry.lab.example.com"
insecure = true
blocked = false
EOF

登陆一下仓库看看

账号:admin

密码: redhat321

1
2
3
4
[root@lixiaohui ~]# podman login registry.lab.example.com --tls-verify=false
Username: admin
Password:
Login Succeeded!

搜索一下仓库看看都有哪些镜像

1
2
3
4
5
6
7
8
9
10
11
[root@lixiaohui ~]# podman search registry.lab.example.com/ --tls-verify=false
NAME DESCRIPTION
registry.lab.example.com/rhel8/mariadb-103
registry.lab.example.com/rhel9/mariadb-105
registry.lab.example.com/rhel9/httpd-24
registry.lab.example.com/library/nginx
registry.lab.example.com/ubi7/ubi
registry.lab.example.com/ubi9/ubi
registry.lab.example.com/ubi8/ubi
registry.lab.example.com/ubi9/python-312
registry.lab.example.com/rhel9/php-82

从仓库下载一个看看

1
2
3
4
5
6
7
8
9
[root@lixiaohui ~]# podman pull registry.lab.example.com/rhel9/httpd-24 --tls-verify=false
Trying to pull registry.lab.example.com/rhel9/httpd-24:latest...
Getting image source signatures
Copying blob 1153e061da4e done
Copying blob 358ca3c2eaff done
Copying blob e584cd196457 done
Copying config a42923e055 done
Writing manifest to image destination
a42923e05589d2d10c25e87736bc3b9537c38c2a6a87f37e9a71e288b01738a6

看看本地已经下载好的镜像

1
2
3
[root@lixiaohui ~]# podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.lab.example.com/rhel9/httpd-24 latest a42923e05589 9 months ago 380 MB

看看本地的容器镜像都有哪些元数据信息可以给我们看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@lixiaohui ~]# podman inspect registry.lab.example.com/rhel9/httpd-24:latest | more
[
{
"Id": "a42923e05589d2d10c25e87736bc3b9537c38c2a6a87f37e9a71e288b01738a6",
"Digest": "sha256:7874b82335a80269dcf99e5983c2330876f5fe8bdc33dc6aa4374958a2ffaaee",
"RepoTags": [
"registry.lab.example.com/rhel9/httpd-24:latest"
],
"RepoDigests": [
"registry.lab.example.com/rhel9/httpd-24@sha256:7874b82335a80269dcf99e5983c2330876f5fe8bdc33dc6aa4374958a2ffaaee"
],
"Parent": "",
"Comment": "",
"Created": "2024-02-29T16:16:26.003408662Z",
"Config": {
"User": "1001",
···

使⽤ Skopeo 管理镜像仓库

Skopeo 可以在不使⽤本地存储的情况下检查远程镜像或在镜像仓库之间传输镜像。

比如不下载的情况下,看看远程镜像的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lixiaohui ~]# skopeo inspect docker://registry.lab.example.com/rhel9/php-82 --tls-verify=false
{
"Name": "registry.lab.example.com/rhel9/php-82",
"Tag": "latest",
"Digest": "sha256:945fcdaf2cd32113bb37ef6fbc6190347f0f67f06eb9807f7e1759d4241eeb99",
"RepoTags": [
"1-15",
"latest"
],
"Created": "2024-06-06T03:47:48.433083262Z",
"DockerVersion": "",
"Labels": {
"architecture": "x86_64",
"build-date": "2024-06-06T03:46:06",
"com.redhat.component": "php-82-container",

使⽤ skopeo copy 命令在镜像仓库之间复制镜像

1
2
3
4
5
6
7
8
9
10
[root@lixiaohui ~]# skopeo copy docker://registry.lab.example.com/rhel9/php-82 docker://registry.lab.example.com/rhel9/lixiaohui --tls-verify=false
WARN[0000] '--tls-verify' is deprecated, instead use: --src-tls-verify, --dest-tls-verify
Getting image source signatures
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists
Copying blob a3ed95caeb02 skipped: already exists

创建一个容器

我们运行一个mariadb的数据库容器看看

先下载一下镜像

1
[root@lixiaohui ~]# podman pull registry.lab.example.com/rhel9/mariadb-105 --tls-verify=false

然后来创建一个名为lxhdb的容器看看

1
2
3
4
5
6
podman run -d \
--name lxhdb \
-p 3306:3306 \
-v /mnt:/var/lib/mysql:Z \
-e MYSQL_ROOT_PASSWORD=ABCabc123 \
registry.lab.example.com/rhel9/mariadb-105

参数解释

  • -d

    • 解释:以守护进程模式运行容器。

    • 作用:使容器在后台运行,而不占用当前终端会话。

  • -p 3306:3306

    • 解释:将主机的3306端口映射到容器的3306端口。

    • 作用:允许外部访问容器内的MySQL服务,3306端口是MySQL的默认端口。

  • -v /mnt:/var/lib/mysql:Z

    • 解释:将主机的 /mnt 目录挂载到容器的 /var/lib/mysql 目录,并设置SELinux安全上下文选项 :Z

    • 作用:持久化MySQL的数据存储到主机的 /mnt 目录中,即使容器被删除,数据也不会丢失。Z 选项确保SELinux为挂载的卷设置正确的安全上下文,以允许容器访问该卷。

  • -e MYSQL_ROOT_PASSWORD=ABCabc123

    • 解释:设置环境变量 MYSQL_ROOT_PASSWORD,指定MySQL root用户的密码。

    • 作用:为MySQL的root用户配置初始密码,确保数据库的安全性。

  • registry.lab.example.com/rhel9/mariadb-105

    • 解释:指定要运行的容器镜像。

    • 作用:从注册表 registry.lab.example.com 中拉取 rhel9/mariadb-105 镜像并运行。

运行后,用podman ps看看后台是否运行

1
2
[root@lixiaohui ~]# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

???发生了什么,为什么没有运行中的容器,那就看看有没有已经停止的容器?

1
2
3
[root@lixiaohui ~]# podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c9e0d44646e registry.lab.example.com/rhel9/mariadb-105:latest run-mysqld 3 seconds ago Exited (1) 4 seconds ago 0.0.0.0:3306->3306/tcp lxhdb

它为什么停止了?没有运行?看看日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@lixiaohui ~]# podman logs lxhdb
=> sourcing 20-validate-variables.sh ...
=> sourcing 25-validate-replication-variables.sh ...
=> sourcing 30-base-config.sh ...
---> 14:03:56 Processing basic MySQL configuration files ...
=> sourcing 60-replication-config.sh ...
=> sourcing 70-s2i-config.sh ...
---> 14:03:56 Processing additional arbitrary MySQL configuration provided by s2i ...
=> sourcing 40-paas.cnf ...
=> sourcing 50-my-tuning.cnf ...
---> 14:03:56 Initializing database ...
---> 14:03:56 Running mysql_install_db ...
mkdir: cannot create directory '/var/lib/mysql/data': Permission denied
Fatal error Can't create database directory '/var/lib/mysql/data'

The latest information about mysql_install_db is available at
https://mariadb.com/kb/en/installing-system-tables-mysql_install_db

日志上说它没有/mnt的权限,好的,那我们给/mnt权限就行

1
2
3
4
5
[root@lixiaohui ~]# ls -dZ /mnt/
system_u:object_r:container_file_t:s0:c443,c789 /mnt/
[root@lixiaohui ~]# chmod 777 /mnt/ -R
[root@lixiaohui ~]# ls -dZ /mnt/
system_u:object_r:container_file_t:s0:c443,c789 /mnt/

再启动一下容器看看,成功启动

1
2
3
4
5
[root@lixiaohui ~]# podman start lxhdb
lxhdb
[root@lixiaohui ~]# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c9e0d44646e registry.lab.example.com/rhel9/mariadb-105:latest run-mysqld 3 minutes ago Up 2 seconds 0.0.0.0:3306->3306/tcp lxhdb

试试mysql好用不,我们测试一下密码,也就是我们的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@lixiaohui ~]# dnf install mysql -y
[root@lixiaohui ~]# mysql -u root -pABCabc123 -h 127.0.0.1
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.5.5-10.5.22-MariaDB MariaDB Server

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.01 sec)

mysql> exit
Bye

进入容器的方法:

进入容器看,我们的变量也是ok的

1
2
3
4
5
[root@lixiaohui ~]# podman exec -it lxhdb /bin/bash
bash-5.1$ echo $MYSQL_ROOT_PASSWORD
ABCabc123
bash-5.1$ exit
exit

Podman Desktop

Podman Desktop 是⼀个图形⽤⼾界⾯,可⽤于管理本地环境中的容器并与之交互。 它默认使⽤Podman 引擎,也⽀持其他容器引擎,如 Docker。

以下是多平台的下载链接,在课程中,已经内置在workstation机器上

1
https://podman-desktop.io/downloads

容器和镜像的⽣命周期

常⽤于更改容器和镜像状态的 Podman ⼦命令:

lifecycle-actions

管理容器的 Podman 命令

  1. podman run

    • 启动一个新的容器。
  2. podman start

    • 启动一个已经停止的容器。
  3. podman stop

    • 停止一个正在运行的容器。
  4. podman restart

    • 重启一个正在运行或已经停止的容器。
  5. podman kill

    • 立即终止一个正在运行的容器。
  6. podman rm

    • 删除一个已经停止的容器。
  7. podman ps

    • 列出所有正在运行的容器。
  8. podman inspect

    • 显示容器的详细信息。
  9. podman logs

    • 查看容器的日志输出。
  10. podman exec

    • 在一个正在运行的容器内执行命令。
  11. podman attach

    • 连接到一个正在运行的容器。
  12. podman stats

    • 显示一个或多个容器的实时统计信息。
  13. podman top

    • 显示一个正在运行的容器中的进程信息。
  14. podman pause

    • 暂停一个或多个正在运行的容器。
  15. podman unpause

    • 恢复一个或多个暂停的容器。

常⽤于查询容器和镜像信息的⼦命令:

lifecycle-queries

管理镜像生命周期的 Podman 命令

  1. podman pull

    • 从镜像仓库拉取容器镜像。
  2. podman push

    • 将本地容器镜像推送到镜像仓库。
  3. podman build

    • 根据 Dockerfile 构建容器镜像。
  4. podman commit

    • 从一个容器的变化中创建一个新的镜像。
  5. podman images

    • 列出所有本地存储的容器镜像。
  6. podman rmi

    • 删除一个或多个本地存储的容器镜像。
  7. podman tag

    • 给一个镜像打标签或重新标记。
  8. podman history

    • 显示镜像的历史信息。

RootLess与systemd服务

使用 RootLess与systemd 服务管理容器不仅提高了系统的稳定性和安全性,还简化了容器的管理和维护过程,而且可以让systemd来控制容器在开机时容器自动运行,不再需要管理员手工start,而且可以给普通用户安排容器来提高安全性。

普通用户运行 Podman 容器

  1. 权限限制

    • 普通用户只能访问和修改他们自己创建的容器和镜像,而无法影响系统或其他用户的资源。

    • 增强了系统的安全性,因为普通用户的操作权限受到限制,不会对系统级资源造成影响。

  2. 隔离性

    • 容器运行在用户命名空间内,进一步隔离了容器和主机系统的资源。

    • 这种隔离确保即使容器中的应用被攻破,攻击者也无法提升权限并控制整个系统。

  3. 配置灵活性

    • 普通用户可以在自己的主目录中配置容器和镜像的存储位置,提供更大的灵活性和定制化。

    • 这使得多用户环境下,每个用户都可以独立管理自己的容器和镜像。

Root 用户运行 Podman 容器

  1. 完全权限

    • Root 用户具有系统级权限,可以访问和修改所有容器和镜像。

    • 可以对系统进行全面控制,包括配置网络、挂载系统文件、管理系统服务等。

  2. 更高的风险

    • 由于 root 用户具有完全权限,容器中的应用如果被攻破,攻击者可能会获取系统级权限,控制整个主机。

    • 因此,root 用户运行容器时需要更加谨慎,避免不必要的风险。

  3. 系统资源管理

    • Root 用户可以直接配置和管理系统资源,例如全局的网络配置和存储挂载。

    • 可以设置更加细粒度的资源限制,确保系统资源的合理分配和使用。

RootLess容器管理

RootLess容器管理中,用户必须用ssh、控制台登录,不能用su命令切换到普通用户的方式去工作,因为su命令无法为podman提供完整的登录会话。

我们来创建一个普通用户,让这个普通用户创建一个rootless容器,并使用systemd来守护此容器,实现开机自启动

先创建出此用户

1
2
3
4
[root@lixiaohui ~]# useradd lxhuser1
[root@lixiaohui ~]# echo lxhpassword | passwd --stdin lxhuser1
Changing password for user lxhuser1.
passwd: all authentication tokens updated successfully.

用ssh的方式切换到lxhuser1用户,只有ssh才能提供完整登录会话,所以不能su

1
2
3
[root@lixiaohui ~]# ssh lxhuser1@localhost
lxhuser1@localhost's password: `lxhpassword`
[lxhuser1@lixiaohui ~]$

下载一下镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[lxhuser1@lixiaohui ~]$ podman login -u admin -p redhat321 registry.lab.example.com --tls-verify=false
Login Succeeded!

[lxhuser1@lixiaohui ~]$ podman search registry.lab.example.com/ --tls-verify=false
NAME DESCRIPTION
registry.lab.example.com/rhel8/mariadb-103
registry.lab.example.com/rhel9/mariadb-105
registry.lab.example.com/rhel9/httpd-24
registry.lab.example.com/library/nginx
registry.lab.example.com/ubi7/ubi
registry.lab.example.com/ubi9/ubi
registry.lab.example.com/ubi8/ubi
registry.lab.example.com/ubi9/python-312
registry.lab.example.com/rhel9/php-82

[lxhuser1@lixiaohui ~]$ podman pull registry.lab.example.com/rhel8/mariadb-103 --tls-verify=false
Trying to pull registry.lab.example.com/rhel8/mariadb-103:latest...
Getting image source signatures
Copying blob 47db82df7f3f done
Copying blob 67b9f0b530d9 done
Copying blob 71391dc11a78 done
Copying blob 77c58f19bd6e done
Copying config 11a47e0fbe done
Writing manifest to image destination
11a47e0fbed05a1f423929778645063f6a9dd6b24251e7e14da9c4b0788fe1e1

为容器准备持久性存储

这里有一个知识点是podman unshare 命令

podman unshare 命令用于创建一个新的用户命名空间(User Namespace)并在其中运行一个指定的命令或启动一个新的 shell。这在需要以普通用户的身份执行一些通常需要 root 权限的操作时非常有用。podman unshare 命令为用户提供了在用户命名空间内运行命令或脚本的能力,这样可以在不提升到 root 权限的情况下执行高权限操作,从而增强系统的安全性和灵活性

创建出数据库存储的位置

1
[lxhuser1@lixiaohui ~]$ mkdir db_files

运行一个容器看看

1
2
3
4
5
6
[lxhuser1@lixiaohui ~]$ podman run -d \
--name lxhdb \
-p 3306:3306 \
-v /home/lxhuser1/db_files:/var/lib/mysql:Z \
-e MYSQL_ROOT_PASSWORD=ABCabc123 \
registry.lab.example.com/rhel8/mariadb-103

后台发现容器没起来,看看日志是权限不足

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[lxhuser1@lixiaohui ~]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

[lxhuser1@lixiaohui ~]$ podman logs lxhdb
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
=> sourcing 20-validate-variables.sh ...
=> sourcing 25-validate-replication-variables.sh ...
=> sourcing 30-base-config.sh ...
---> 02:49:11 Processing basic MySQL configuration files ...
=> sourcing 60-replication-config.sh ...
=> sourcing 70-s2i-config.sh ...
---> 02:49:11 Processing additional arbitrary MySQL configuration provided by s2i ...
=> sourcing 40-paas.cnf ...
=> sourcing 50-my-tuning.cnf ...
---> 02:49:11 Initializing database ...
---> 02:49:11 Running mysql_install_db ...
mkdir: cannot create directory '/var/lib/mysql/data': Permission denied
Fatal error Can't create database directory '/var/lib/mysql/data'

经过测试,发现容器内的用户 ID 0(通常是 root)映射到主机上的用户 ID 1003,映射范围为 1 个 UID。

容器内的用户 ID 1 到 65536 映射到主机上的用户 ID 296608 开始的 65536 个 UID

1 后的每个 UID 和 GID以 1 增量递增

1
2
3
4
5
6
[lxhuser1@lixiaohui ~]$ podman unshare cat /proc/self/uid_map
0 1003 1
1 296608 65536
[lxhuser1@lixiaohui ~]$ ls /mnt
[lxhuser1@lixiaohui ~]$ touch /mnt/lxhwrite
touch: cannot touch '/mnt/lxhwrite': Permission denied

我们试试授权给mariadb程序的用户看看,先去看看这个容器中的uid是多少,然后将本地的文件,授予给这个uid

运行一个临时容器,看看/etc/passwd里的用户

1
2
3
[lxhuser1@lixiaohui ~]$ podman run -it registry.lab.example.com/rhel8/mariadb-103 /bin/bash
bash-4.4$ grep -i mysql /etc/passwd
mysql:x:27:27:MySQL Server:/var/lib/mysql:/sbin/nologin

明白了,容器内的mysql用户uid为27,我们将本地文件授予给27就行

1
[lxhuser1@lixiaohui ~]$ podman unshare chown -R 27:27 db_files

再启动容器看看

成功启动

1
2
3
4
5
6
7
[lxhuser1@lixiaohui ~]$ podman start lxhdb
lxhdb
[lxhuser1@lixiaohui ~]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d2dd0defb216 registry.lab.example.com/rhel8/mariadb-103:latest run-mysqld 3 minutes ago Up 2 seconds 0.0.0.0:3306->3306/tcp lxhdb
[lxhuser1@lixiaohui ~]$ ls db_files/
data mysql.sock

根据映射关系,容器内的 UID 27 对应主机上的 UID 是:296608 + 27 - 1 = 296634,减1是因为原始值是1,要考虑偏移量

UID也是映射之后的,原来的1号用户uid为296608,加了26之后,正好就是296634

1
2
3
4
[lxhuser1@lixiaohui ~]$ ls -l db_files/
total 4
drwx------. 5 296634 296634 4096 Dec 24 21:53 data
srwxrwxrwx. 1 296634 296634 0 Dec 24 21:53 mysql.sock

容器已经成功运行了,不过这种手工运行的容器,机器重启后,它不会自动启动,接下来,我们试试生成systemd服务,把控制权由命令行交给systemd服务

1
[lxhuser1@lixiaohui ~]$ podman stop lxhdb

对于普通用户而言,服务文件需要放在~/.config/systemd/user/这个位置,如果忘记了,可以看看man systemd.unit

1
2
3
4
5
6
[lxhuser1@lixiaohui ~]$ mkdir -p ~/.config/systemd/user/
[lxhuser1@lixiaohui ~]$ cd .config/systemd/user/
[lxhuser1@lixiaohui user]$ podman generate systemd --name lxhdb --files
/home/lxhuser1/.config/systemd/user/container-lxhdb.service
[lxhuser1@lixiaohui user]$ ls
container-lxhdb.service

对于rootless的容器而言,所有的systemctl命令要加–user

我们来启用并启动我们的容器服务

如果在daemon-reload的时候报告找不到服务文件,而你确定又生成了的时候,那就是你没有用ssh方式登录,选择了su就会这样

1
2
3
4
5
6
7
8
9
10
11
[lxhuser1@lixiaohui user]$ systemctl --user daemon-reload
[lxhuser1@lixiaohui user]$ systemctl --user enable container-lxhdb.service
Created symlink /home/lxhuser1/.config/systemd/user/default.target.wants/container-lxhdb.service → /home/lxhuser1/.config/systemd/user/container-lxhdb.service.
[lxhuser1@lixiaohui user]$ systemctl --user start container-lxhdb.service
[lxhuser1@lixiaohui user]$ systemctl --user status container-lxhdb.service
● container-lxhdb.service - Podman container-lxhdb.service
Loaded: loaded (/home/lxhuser1/.config/systemd/user/container-lxhdb.service; enabled; preset: disabled)
Active: active (running) since Tue 2024-12-24 22:07:21 EST; 4s ago
Docs: man:podman-generate-systemd(1)
Process: 31462 ExecStart=/usr/bin/podman start lxhdb (code=exited, status=0/SUCCESS)
Main PID: 31489 (conmon)

做到现在,我们的容器服务已经可以在用户登录时自动启动了,但是并不能在服务器重启后自动启动,是的,只有你登录的时候它才能启动,为了自动无需人工干预启动,还得这么做

需要确保最后一行的Linger=yes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[lxhuser1@lixiaohui user]$ loginctl enable-linger lxhuser1
[lxhuser1@lixiaohui user]$ loginctl show-user lxhuser1
UID=1003
GID=1003
Name=lxhuser1
Timestamp=Tue 2024-12-24 21:46:59 EST
TimestampMonotonic=6855967614
RuntimePath=/run/user/1003
Service=user@1003.service
Slice=user-1003.slice
Display=10
State=active
Sessions=10
IdleHint=no
IdleSinceHint=1735096168066887
IdleSinceHintMonotonic=8204217404
Linger=yes

此时,你重启服务器,它就会自动启动了