[步骤] KVM 虚拟机模板的创建 (CentOS Linux 9 版)

注意:

在创建 KVM 虚拟机之前要先安装 KVM 并创建 KVM 虚拟网络

镜像准备:

在 Rocky Linux 官网上下载安装系统所需要的镜像:

https://rockylinux.org/download

正文:

步骤一:理解创建 KVM 虚拟机模板的目的

主要用于批量克隆出新的 KVM 机器,节约创建新虚拟机的时间

步骤二:为这个虚拟机创建硬盘文件

2.1 创建硬盘文件

(只在真机上执行以下步骤)

# qemu-img create -f qcow2 /var/lib/libvirt/images/rockylinux9.qcow2 10G

(补充:这里以创建 10G 大小的 rockylinux9.qcow2 硬盘文件为例)

2.2 确认硬盘文件已创建

(只在真机上执行以下步骤)

# ls /var/lib/libvirt/images/ | grep rockylinux9.qcow2

(补充:这里以显示 rockylinux9.qcow2 硬盘文件为例)

步骤三:使用 KVM 和刚刚创建的硬盘文件新安装一台虚拟机

3.1 启动 KVM 的 virt-manager

(只在真机上执行以下步骤)

# virt-manager

3.2 在 virt-manager 上的左上角点击文件之后点击 “新建虚拟机”

(只在真机上执行以下步骤)

(步骤略)

3.2.1 选择以本地安装介质的方式安装系统

(只在真机上执行以下步骤)

(图:1)

3.2.2 选择安装系统的系统镜像

(只在真机上执行以下步骤)

(图:2)

(补充:这里以使用 Rocky-9.6-x86_6-dvd1.iso 系统镜像为例)

3.2.3 设置内存大小和 CPU 核心数

(只在真机上执行以下步骤)

(图:3)

(补充:这里以设置 2048 MiB 内容和 2 核 CPU 为例)

3.2.4 选择用刚刚创建的硬盘文件来安装系统

(只在真机上执行以下步骤)

(图:4)

(补充:这里以使用 rockylinux9.qcow2 硬盘文件为例)

3.2.5 给虚拟机命名并选择虚拟网络

(只在真机上执行以下步骤)

(注意:虚拟网络必须提前创建好)

(图:5)

(补充:这里以将虚拟机命名为 rockylinux9 并使用 network 网络为例)

3.2.6 开始安装系统

(只在真机上执行以下步骤)

(图:6)

3.2.7 选择系统语言

(只在真机上执行以下步骤)

(图:7)

3.2.8 之后进行系统配置界面

(只在真机上执行以下步骤)

需要手动配置的地方有四个:
1) “INSTALLATION DESTINATION”
2) “KDUMP”
3) “SOFTWARE SELECTION”
4) “Root Password”
分别点击以后就可以配置了

3.2.8.1 通过 “INSTALLATION DESTINATION” 对硬盘进行分区

(只在真机上执行以下步骤)

(补充:完成后点击左上角的 “DONE”)

(注意:只分一个分区,只设置一个挂载点挂载到根 “/”,使用标准硬盘类型,硬盘格式设置为 XFS)

(图:8)

(图:9)

3.2.8.2 取消 “KDUMP”

(只在真机上执行以下步骤)

(补充:完成后点击左上角的 “DONE”)

(图:10)

3.2.8.3 选择最小化安装系统

(只在真机上执行以下步骤)

(补充:完成后点击左上角的 “DONE”)

(图:11)

3.2.8.4 设置 ROOT 密码

(只在真机上执行以下步骤)

(图:12)

3.2.9 之后点击右下角的 “BEGIN INSTALLATION”

(只在真机上执行以下步骤)

(图:13)

3.2.10 安装完成后重启

(只在真机上执行以下步骤)

3.2.11 在安装系统的过程中需要注意的内容总结

(只在真机上执行以下步骤)

1) 一定要使用刚刚创建的 /var/lib/libvirt/images/rockylinux8.qcow2 作为安装虚拟机的硬件文件
2) 虚拟机网络 “0” 要提前创建好
3) 只分一个分区,只设置一个挂载点挂载到根 “/”,使用标准硬盘,硬盘格式是 XFS
4) 取消 “KDUMP”
5) 选择最小化安装系统
6) 设置 root 密码

步骤四:进入新创建虚拟机修改配置

4.1 允许 root 用户远程登陆

4.1.1 修改 SSH 配置文件
# vi /etc/ssh/sshd_config

在此行下面:

......
#PermitRootLogin prohibit-password
......

添加:

......
PermitRootLogin yes
......
4.1.2 让刚刚修改的配置文件生效
# systemctl restart ssd

4.2 修改网卡个性化设置

4.2.1 修改网卡配置文件

(只在虚拟机上执行以下步骤)

# vi /etc/NetworkManager/system-connections/enp1s0.nmconnection

将全部内容修改如下:

[connection]
id=enp1s0
type=ethernet
autoconnect-priority=-999
interface-name=enp1s0
timestamp=1755516857

[ethernet]

[ipv4]
method=auto
4.2.2 使修改的网卡配置生效

(只在虚拟机上执行以下步骤)

# reboot

4.3 禁用 SELinux

(只在虚拟机上执行以下步骤)

# vi /etc/selinux/config

将全部内容修改如下:

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
# See also:
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux/changing-selinux-states-and-modes_using-selinux#changing-selinux-modes-at-boot-time_changing-selinux-states-and-modes
#
# NOTE: Up to RHEL 8 release included, SELINUX=disabled would also
# fully disable SELinux during boot. If you need a system with SELinux
# fully disabled instead of SELinux running with no policy loaded, you
# need to pass selinux=0 to the kernel command line. You can use grubby
# to persistently set the bootloader to boot with selinux=0:
#
#    grubby --update-kernel ALL --args selinux=0
#
# To revert back to SELinux enabled:
#
#    grubby --update-kernel ALL --remove-args selinux
#
SELINUX=disabled
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

4.4 禁用空路由

(只在虚拟机上执行以下步骤)

# vi /etc/sysconfig/network

将全部内容修改如下:

# Created by anaconda
NOZEROCONF="yes"

4.5 添加 Console 配置

4.5.1 修改 GRUB 内核配置文件

(只在虚拟机上执行以下步骤)

# vi /etc/default/grub

将全部内容修改如下:

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_SERIAL_COMMAND="serial --unit=1 --speed=115200"
GRUB_CMDLINE_LINUX="biosdevname=0 net.ifnames=0 console=tty0 console=ttyS0,115200n8"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
4.5.2 使修改的 GRUB 内核配置生效

(只在虚拟机上执行以下步骤)

# grub2-mkconfig -o grub

4.6 将系统自动挂载的硬盘从使用 UUID 换成硬件路径

4.6.1 显示根 “/” 分区的 UUID

(只在虚拟机上执行以下步骤)

# blkid 
/dev/vda1: UUID="682d9854-9ca6-4272-9adb-dc9bee3098f6" TYPE="xfs" PARTUUID="bf6af362-01"

(补充:这里的 UUID 是: 682d9854-9ca6-4272-9adb-dc9bee3098f6)

4.6.2 在自动挂载文件里将根 “/” 分区的 UUID 换成硬件路径

(只在虚拟机上执行以下步骤)

# vi /etc/fstab

将以下内容:

......
UUID=682d9854-9ca6-4272-9adb-dc9bee3098f6 /                   xfs     defaults        0 0

(补充:这里的 UUID 是: 682d9854-9ca6-4272-9adb-dc9bee3098f6)

修改为:

/dev/sda1 /                   xfs     defaults        0 0

4.7 删除不用的程序

(只在虚拟机上执行以下步骤)

# yum -y remove firewalld-* python-firewall

4.8 对虚拟系统进行升级

(只在虚拟机上执行以下步骤)

# yum -y update

4.9 进行分区扩展

4.9.1 安装分区扩展软件

(只在虚拟机上执行以下步骤)

# yum install -y cloud-utils-growpart
4.9.2 给开机自启配置文件相应的权限
# chmod 755 /etc/rc.local
4.9.3 设置开机自动扩容根 “/” 目录

(只在虚拟机上执行以下步骤)

# vi /etc/rc.local

添加以下内容:

......
/usr/bin/growpart /dev/sda1
/usr/sbin/xfs_growfs /

4.10 修改虚拟机系统的名称

(只在虚拟机上执行以下步骤)

# vi /etc/hostname

将全部内容修改如下:

rockylinux9

4.11 启用 serial 服务实现通过 virsh console 命令控制虚拟机

(只在虚拟机上执行以下步骤)

# systemctl start serial-getty@ttyS0
# systemctl enable serial-getty@ttyS0

4.12 清除虚拟系统的历史命令

(只在虚拟机上执行以下步骤)

# history -c

4.13 关闭虚拟机

(只在虚拟机上执行以下步骤)

# poweroff

步骤五:在真机上对虚拟机进行清理优化

(只在真机上执行以下步骤)

# sudo virt-sysprep -d rockylinux9

(补充:这里以清理 rockylinux9 虚拟机为例)


注意:如果此命令不存在
1) Rocky Linux 系统的话需要安装 libguestfs-tools
2) openSUSE 系统的话需要安装 guestfs-tools

步骤六:此时就可以将此虚拟机的硬件文件作为模板进行批量克隆虚拟机了

(只在真机上执行以下步骤)

(步骤略)

[排错] 解决 Ubuntu dpkg 数据库损坏 (apt 更新到一半时意外中断)

解决方法:

步骤一:重新配置 dpkg 数据库

# sudo dpkg --configure -a

步骤二:修复 apt 的中断

# sudo apt install --fix-broken

步骤三:删除报错的软件包

# sudo apt remove --purge postfix

(补充:这里以删除报错的 postfix 软件包为例)

步骤四:删除不用的软件包

4.1 清理 apt 缓存

# sudo apt clean

4.2 删除不用的软件包

# sudo apt autoremove

步骤五:更新所有软件包

5.1 下载所有需要更新的软件包

# sudo apt update

5.2 升级所有软件包

# sudo apt upgrade

步骤六:重新安装前面报错并删除的软件包

# sudo apt-get install postfix

(补充:这里以重新安装前面报错并删除的 postfix 软件包为例)

[内容] 软件源的设置 (Ubuntu 版)

内容一:添加 Ubuntu 软件源

# cat /etc/apt/sources.list
# Ubuntu sources have moved to /etc/apt/sources.list.d/ubuntu.sources
# vim /etc/apt/sources.list.d/ubuntu.sources

创建以下内容:

Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

内容二:添加第 3 方软件源

# cat /etc/apt/sources.list
# Ubuntu sources have moved to /etc/apt/sources.list.d/ubuntu.sources
# vim /etc/apt/sources.list.d/ubuntu.sources

创建以下内容:

Types: deb
URIs: http://mirrors.aliyun.com/ubuntu/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

(补充:这里以添加阿里云的软件源为例)

[步骤] Linux 重启后网卡上没有 IP 地址的处理方法

步骤一:在硬件管理页面检查物理网卡是否联通

记录需要使用的网络端口的网卡机器码 (MAC) 地址

(步骤略)


注意:
1) 只有物理机才需要完成这步
2) 当物理机 1 个网络模块上有 2 个网络端口时,要特别注意 2 个网络端口各自的网卡机器码 (MAC) 地址,例如:戴尔服务器网络端口的网卡机器码 (MAC) 地址在页面的最下方

步骤二:在系统中查看对应的网卡是否联通

2.1 使用 IP 命令查看对应的网卡是否联通

# ip a s

或者:

# ip address show

(补充:当出现 ”UP“ 字样时代表网卡已经启动)

(注意:nmcli device show 命令不能确认此时网卡是否联通)

2.2 使用 ethtool 命令查看对应的网卡是否联通

# ethtool eth0

(注意:nmcli device show 命令不能确认此时网卡是否联通)

步骤三:尝试配置临时 IP 地址和临时网关 (只有当确认网卡是联通的时候才进行以下操作)

3.1 尝试配置临时 IP 地址

# ifconfig eth0 192.168.0.2/24

或者:

# ip a add 192.168.0.2/24 dev eth0

(补充:这里以给 eth0 网卡添加临时 IP 地址 192.168.0.2/24 为例)

(注意:只有当确认网卡是联通的时候才进行此操作)

3.2 尝试配置临时网关

# route add default gw 192.168.0.1

或者:

# ip route add default via 192.168.0.1

(注意:只有当确认网卡是联通的时候才进行此操作)

3.3 查看配置的临时 IP 地址和临时网关

3.3.1 查看配置的临时 IP 地址
# ip a s
3.3.2 查看配置的临时网关
# route -n

[内容] Linux 进程的理解

正文:

内容一:Linux 进程的生命周期

1) 诞生或从其它进程中分叉出来
2) 准备好运行或出于可运行的状态
3) 在用户空间或内核空间运行
4) 处于可打断的睡眠状态 (Interruptable sleep) 状态或者不可打断的睡眠状态 (Uninterruptable sleep),此时进程处于睡眠状态 (可能存储在内存中也可能存储在硬盘里的 SWAP 中)
5) 结束或处于僵尸状态

内容二:进程的分类

2.1 按进程的附属关系进行分类

分为父进程和子进程 2 种进程,其中子进程附属于父进程

子进程的特点如下:
子进程有自己单独的进程 ID
子进程的父进程 ID 就是自己父进程的 ID
子进程的资源利用率和处理器计时器会被清空
子进程的待定信号会被清空
子进程不继承父进程的内存锁
子进程不继承父进程的信号
子进程不继承父进程被锁的记录
子进程不继承父进程的计时器
子进程不继承父进程未完成的异步 I/O 操作
子进程不继承父进程任何异步 I/O 文本

2.2 按进程的使用者进行分类

分为用户进程、守护进程、系统进程

用户进程守护进程系统进程
前台运行还是后台运行通常是前台运行通常是后台运行通常是后台运行
是否开机自启通常不开机启动通常是开机自启必须是开机自启
是否可以手动管理可以手动打开或者关闭可以手动打开或者关闭不能手动打开或者关闭
是否等待或者监控服务请求 (例如 Web 服务)通常不等待新的服务请求通常在等待新的服务请求或者定期执行某些新的任务通常在等待新的服务请求
进程权限权力有限,不能超出启动此进程的用户的权力范围权力有限,不能超出启动此进程的用户的权力范围权力较大,能完全访问内核的数据结构
进程运行空间通常在用户空间里运行,可以通过 user-level processes 申请进入内核空间访问内核的数据和结构通常在用户空间里运行,可以通过 user-level processes 申请进入内核空间访问内核的数据和结构只在内核空间里运行

2.3 按进程的状态进行分类

2.3.1 按进程的状态进行分类的种类列表

分为运行状态 (Running State)、可以运行状态 (Runnable State)、可中断睡眠状态 (Interruptable Sleep State)、不可中断睡眠状态 (Uninterruptable Sleep State)、僵尸状态 (Zombie State)、关闭状态 (Defunct State)

进程名进程大类状态旗帜ps 命令中的标志是否在运行
运行状态 (Running State)Running Statep->state = TASK_RUNNINGR正在处理器上运行的进程 (a running process)
可以运行状态 (Runnable State)Runnable Statep->state = TASK_RUNNINGR不在处理器上运行的进程 (a not-running process)
可中断睡眠状态 (Interruptable Sleep State)Sleeping Statep->state = TASK_INTERRUPTABLES不在处理器上运行的进程 (a not-running process)
不可中断睡眠状态 (Uninterruptable Sleep State)Sleeping Statep->state = TASK_UNINTERRUPTABLED不在处理器上运行的进程 (a not-running process)
僵尸状态 (Zombie State)Zombie StateP->state = TASK_ZOMBIEZ不在处理器上运行的进程 (a not-running process)
关闭状态 (Defunct State)Defunct StateN/AN/A不在处理器上运行的进程 (a not-running process)

(补充:这里不可中断睡眠状态 (Uninterruptable Sleep State) 的 “D” 状态,有点像是历史遗留,因为最早这种状态被视为 “Disk Wait” 所以这种状态以 D 开头)

2.3.2 按进程的状态进行分类的种类说明
2.3.2.1 正在运行状态 (Running State)

正在处理器上运行的进程。

2.3.2.2 可以运行状态 (Runnable State)

除了处理器资源无法使用 (例如处理器资源不够或者在使用处理器资源必须要要让 I/O 进行处理的情况),其它方面都已经准备好运行的进程。

2.3.2.3 睡眠状态 (Sleeping State)

除了处理器资源,其它资源也不够被此进程使用时,进程则会进入睡眠状态。进程可能是自愿进入睡眠状态 (Sleeping State) 也可能是被内核放到睡眠状态 (Sleeping State) 里的。

当进程的资源可用时,一个信号会发送给处理器。当下次调度时,有机会安排这个处于睡眠状态 (Sleeping State) 的进程进入运行状态 (Running State) 或者是可运行状态 (Runnable State)。

有些进程永远不会结束,其会在睡眠状态 (Sleeping State) 和运行状态 (Running State) 之间来回切换。

当一个进程需要资源时,如果可用内存的容量满足这个进程,但是处理器暂时忙不过来,这个进程就会处于 Ready-to-Run state (using main memory),而当处理器又有空余资源时,这个进程就会结束 Ready-to-Run state (using main memory) 从而进入运行状态 (Running State)。而当一个进程需要资源,但内存不够满足此进程用时,则这个进程将会有进入两种状态:可中断睡眠状态 (Interruptable Sleep State) 或者不可中断睡眠状态 (Uninterruptable Sleep State)。

2.3.2.3.1 可中断睡眠状态 (Interruptable Sleep State)

进程在等待某个特殊事件的发生或者等待某个时间窗口,当某个特殊事件或者某个时间窗口发生后则会离开可中断睡眠状态 (Interruptable Sleep State)。

2.3.2.3.2 不可中断睡眠状态 (Uninterruptable Sleep State)
2.3.2.3.2.1 不可中断睡眠状态 (Uninterruptable Sleep State) 的介绍

进程在等待资源可用或者等待超时。通常发生在硬件驱动等待 disk or network I/O 时。
处于这种状态时不会处理信号,如果资源变为可用或者等待时间结束则会直接结束此状态。当处于此状态的进程回归时,会看到处于这段时间时积累的信号。

不可中断睡眠状态 (Uninterruptable Sleep State) 也是属于正常的状态,通常发生的很快因此很难被观察到。但是,如果处于不可中断睡眠状态 (Uninterruptable Sleep State) 的状态超过了合理的等待时间,那此进程会被卡住,排在此进程后面的进程也会跟着被卡住 (例如需要进入同样设备的进程或者依赖此进程的进程),这种情况下通常需要重启系统解决。

2.3.2.3.2.2 查看处于不可中断睡眠状态 (Uninterruptable Sleep State) 的进程的方法
2.3.2.3.2.2.1 方法一:单条查看
2.3.2.3.2.2.1.1 通过 ps 命令查看处于 D 状态进程的 PID 号
# ps auxH | awk '$8 ~ /^D/{print}
2.3.2.3.2.2.1.2 在系统中查看此 PID 号的堆栈信息
# sudo cat /proc/<PID>/stack
2.3.2.3.2.2.2 方法二:批量查看
# for D_PID in $(ps auxH | awk '$8 ~ /^D/{print $2}');do ps -Llp $D_PID;sudo cat /proc/$D_PID/stack;echo;done
2.3.2.4 僵尸状态 (Zombie State)

当子进程被系统叫停时,它会释放数据结构,但是它在进程表中的位置依旧会被保留。之后它会释放信号给它的父进程,由它的父进程决定此子进程是否成功退出并是否取消它在进程表中的位置。

当子进程停止,但是父进程还没有释放此进程时,此子进程将进入僵尸状态 (Zombie State)。如果父进程在释放子进程之前就死掉了,那此子进程将永远处于僵尸状态 (Zombie State)。

补充:

1) top 命令中 us 代表 user mode 用户模式,sy 代表 system mode 内核空间
2) Linux 的平均负载值为平均的 runnable or running processes (R state), and the number of processes in uninterruptable sleep (D state) over the specified interval 之和
3) 1 个处理器上同 1 时间只能运行 1 个进程

参考文献:

https://access.redhat.com/sites/default/files/attachments/processstates_20120831.pdf