前言

操作系统为什么是信创的底座

干了二十多年数据库,从Oracle到MySQL再到国产数据库,我越来越认一个理儿:操作系统是所有上层应用的底座,底座不稳,上面盖什么楼都白搭。

这几年信创推进得猛,国产操作系统从"能用"走到"好用",麒麟就是这条路上的标杆产品。但说实话,操作系统这玩意儿不是装系统能跑就行——企业环境里,PXE批量装机、DHCP地址分配、DNS域名解析、NTP时钟同步、Nginx网关、MySQL数据库、NFS文件共享、iSCSI块存储、Shell自动化、Zabbix监控、Ansible编排、Docker容器化——这十二项技能,缺一个都可能在生产环境里栽跟头。

麒麟原厂推出的KYCP(麒麟操作系统运维高级工程师)认证,就是围绕这十二项核心能力设计的。2026年8月起,KYCP考试迎来关键改革:从纯理论转向"理论+实操",综合实验题正式成为考核维度。培训时长也从3天拉长到5天24课时,多出来的时间全砸在实操上。

这篇文章,我把十二模块的实战要点串一遍,聊聊它们在企业信创项目里到底怎么用、学完这套东西你能拿到什么价值。

图片

一、PXE:信创批量装机的起点

把PXE放在第一章是有讲究的——十二个模块里只有它跟后面所有章节都有关系。一台机器装好系统,才能接着配DHCP、DNS、Nginx、MySQL。所以PXE是整个KYCP课程的起点,也是综合实验题最喜欢串联考察的模块。

PXE协议原理与工作流程

PXE的核心理念很简单:让机器在没有光驱、没有U盘的情况下,通过网卡从网络启动并自动安装操作系统。整个流程分四步:

  1. 客户端网卡发起PXE请求:BIOS/UEFI设置为网络启动后,网卡通过DHCP协议广播请求,获取IP地址、TFTP服务器地址(next-server)和引导文件名(filename

  2. TFTP传输引导程序:客户端从TFTP服务器下载pxelinux.0(BIOS)或shim.efi/grubaa64.efi(UEFI),这是整个引导链的入口

  3. 加载内核与initrd:引导程序根据配置菜单,从TFTP下载Linux内核(vmlinuz)和初始化内存盘(initrd.img),把最小运行环境加载到内存

  4. Kickstart无人值守安装:内核启动后,读取Kickstart应答文件(ks.cfg),自动完成磁盘分区、软件包选择、网络配置、用户设置等全部安装步骤

这里有个容易被忽略的细节:第三步里的安装源(操作系统ISO镜像)一般不走TFTP——TFTP协议太慢了,传几MB的引导文件还行,传几个GB的ISO不现实。所以实际部署中,安装源通过HTTP或FTP提供,Kickstart文件里用url --url="http://192.168.1.1/iso"指定。

三大核心组件拆解

课程把PXE拆成三个组件来教,每个都有对应的实验。

第一,DHCP服务——PXE的"引路人"。除了常规的IP分配,还必须配两个关键参数:next-server指向TFTP服务器IP,filename指定引导文件名。BIOS和UEFI的filename不一样,搞混了客户端就卡在PXE ROM界面不动。典型的DHCP配置片段如下:

subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    next-server 192.168.1.10;       # TFTP服务器地址
    filename "pxelinux.0";           # BIOS引导文件
}

如果是UEFI启动,filename要改成shim.efi或对应架构的efi文件。麒麟V11支持AArch64、x86_64、LoongArch64三种架构,每种架构的引导文件名都不同——这是实验里第一个容易踩的坑。

第二,TFTP服务——引导文件的"快递员"。安装tftp-server,创建/var/lib/tftpboot/目录,把pxelinux.0、内核文件、initrd、启动菜单放进去。启动菜单文件/var/lib/tftpboot/pxelinux.cfg/default定义了客户端看到的安装选项界面。这里权限问题最常见——文件属主、SELinux上下文配不对,客户端下载引导文件失败。

第三,Kickstart应答文件——安装的"自动驾驶仪"。这是个纯文本文件,定义了安装过程中所有需要人工交互的选项:语言、时区、键盘布局、磁盘分区方案、软件包列表、网络配置、root密码、安装后脚本等。

课程实验会带你逐段理解ks.cfg的结构。几个关键段落:

  • %packages段落定义要安装的软件包组,麒麟V11的软件源组织和RHEL有差异,包名和组名不完全一致

  • part命令定义磁盘分区,涉及LVM的话要写part pv.xxxvolgroup/logvol

  • %post段落是安装完成后执行的脚本,可以用来做系统初始化——比如关闭SELinux、配置yum源、安装额外软件包

我自己用Kickstart最多的是%post段——系统装完之后自动配好yum源、装好基础依赖包、关掉不必要的服务,一台机器从插电到交付不超过二十分钟。但在KYCP教学里,重点不是炫技,而是让学员理解每个段落的含义和语法,这样拿到一个现成的ks.cfg能看得懂、改得动。

麒麟V11的PXE适配要点

麒麟V11基于openEuler,PXE部署有几个跟传统CentOS/RHEL不一样的地方:

  • 网络配置格式:麒麟V11全面使用NetworkManager,Kickstart里网络配置语法跟旧版有差异

  • 引导文件路径:不同架构(x86_64/AArch64/LoongArch64)的引导文件存放路径和文件名不同,搭建TFTP根目录时要按架构分目录存放

  • 不再支持MIPS架构:如果你的环境里有MIPS机器,PXE方案要另做打算

  • 软件包组命名:麒麟V11的包组名和RHEL不完全对应,写%packages段落前最好用dnf group list确认一下

课程实验里会带着学员分别搭建x86_64和AArch64两套PXE环境,让学员亲身体验架构差异。这在实际信创项目里很实用——你不太可能只维护一种架构的服务器。

实战排障参考:ARM架构PXE安装花屏

麒麟交付知识库里有个PXE装机案例值得说一下:ARM架构服务器通过PXE安装镜像时花屏,无法继续安装。解决方法是在grub启动界面按e进编辑模式,手动添加video=VGA-1:640*480-32@60me参数强制指定分辨率。这种架构兼容性问题在x86上碰不到,但在信创项目里满地都是——龙芯、飞腾、鲲鹏各有各的显示驱动适配问题。

PXE装机链路里任何一个环节卡住,后面全部停摆。这也是综合实验题喜欢从PXE开始串联的原因——你连系统都装不上,后面的DHCP、DNS、Nginx服务部署就无从谈起。

二、DHCP:IP分配的"总调度"

PXE、DNS、NTP这些服务跑起来之前,首先得有一个东西把IP地址分下去——这就是DHCP。别看它概念简单,在企业网络里DHCP配错一块,后面的所有服务部署都跟着乱套。

课程从协议原理到生产级部署,覆盖了DHCP在企业里最常用的四个场景:基础地址分配、MAC绑定、多网段配置、中继代理。

DHCP协议原理:DORA四步交互

理解DHCP的工作流程,是排障的基础。客户端和服务端之间通过四步交互完成地址分配,业界叫它DORA流程:

Discover(发现):客户端启动后广播发送DHCP Discover报文,源IP是0.0.0.0,目的IP是255.255.255.255,意思就是"这网段里有没有DHCP服务器?我需要一个IP"。

Offer(提供):DHCP服务器收到Discover后,从地址池里挑一个可用IP,用DHCP Offer报文单播(或广播)回复给客户端,携带IP地址、子网掩码、网关、DNS、租约时长等信息。

Request(请求):客户端可能收到多个Offer(如果网络里有多个DHCP服务器),它选择第一个收到的,然后广播DHCP Request报文,确认要使用这个IP。注意这步用广播而不是单播——目的是让其他DHCP服务器知道"我被选中了",从而释放它们预留的IP。

Acknowledge(确认):服务器回复DHCP ACK,确认租约生效。客户端收到ACK后配置网卡IP、写入租约文件。

此外还有三个辅助报文类型:Decline(客户端发现IP已被占用时拒绝)、Release(客户端主动释放IP)、NAK(服务器拒绝客户端的Request)。课程实验会通过网络抓包(tcpdump/Wireshark)让学员亲眼看到这四步交互过程——笔试可能不考这么细,但综合实验题里排错时需要理解协议交互。

安装部署与配置文件结构

麒麟V11上装DHCP服务很直接:dnf install -y dhcp-server。安装后会生成模板配置文件/usr/share/doc/dhcp-server/dhcpd.conf.example,正式环境里建议从这个模板复制到/etc/dhcp/dhcpd.conf再改,别自己从零写——语法容易漏。

配置文件的核心结构是全局参数 + 子网声明(subnet),一个典型的单网段配置长这样:

# 全局参数
option domain-name "example.com";
option domain-name-servers 192.168.1.1;
default-lease-time 600;
max-lease-time 7200;
authoritative;

# 子网声明
subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    option subnet-mask 255.255.255.0;
}

课程会逐行解释每个参数的含义和配置优先级:子网声明内的参数优先级高于全局参数,客户端匹配到哪个子网就用哪个子网的配置。有几个参数特别容易被新手忽略:

  • authoritative:标识本服务器是该网段的"权威"DHCP服务器。如果客户端请求的IP不在本服务器的地址池内,加了这行服务器会直接回复DHCP NAK,不加则忽略。在多网段混合环境里这个参数会影响租约续期的行为。

  • default-lease-timemax-lease-time:默认租约和最大租约,单位是秒。客户端可以在Request里请求自己想要的租约时间,但服务器以max-lease-time为上限。生产环境里这两个值的设置直接影响IP回收速度。

  • option routersoption domain-name-servers:网关和DNS,这两个配错客户端能拿到IP但无法上网。

固定地址分配(MAC绑定)

这是DHCP在企业里最实用的功能——给特定MAC地址的机器分配固定IP。数据库服务器、核心交换机、防火墙这些关键设备,IP变了就是事故。

配置方式有两种:

  1. 在subnet声明内用host

host db-server {
    hardware ethernet 00:50:56:AB:CD:EF;
    fixed-address 192.168.1.50;
}

hardware ethernet指定MAC地址,fixed-address指定固定IP。注意这个IP必须在subnet地址范围内,但不能在range的范围里(否则可能被动态分配出去造成冲突)。

  1. 在subnet声明外用host块+group(多设备批量绑定):

group {
    host db-node1 { hardware ethernet 00:50:56:11:11:11; fixed-address 192.168.1.51; }
    host db-node2 { hardware ethernet 00:50:56:22:22:22; fixed-address 192.168.1.52; }
}

我自己的亲身教训:有一次数据库主库重启后连不上了,查了半天发现IP从.50漂移成了.103——因为DHCP租约到期重新分配了。从此以后所有数据库节点IP一律做MAC绑定,记在CMDB里,改配置先查CMDB。

DHCP中继服务——跨网段的关键组件

公司网络分了VLAN,DHCP的广播包默认不出网关,客户端在VLAN 10,DHCP服务器在VLAN 1,Discover报文到不了服务器——这时候就要中继(relay)。

DHCP中继的工作机制:中继代理(通常是三层交换机或路由器)收到客户端的广播Discover后,把目的IP改为DHCP服务器的单播地址,加上giaddr字段标记自己的IP,转发给DHCP服务器。服务器根据giaddr判断客户端属于哪个网段,从对应的subnet池子里分配IP。

麒麟V11上配置DHCP中继使用dhcrelay命令:

dhcrelay -i ens192 192.168.1.10

-i指定中继代理的网卡接口,后面跟DHCP服务器的IP。配置文件/etc/sysconfig/dhcrelay可以固化这些参数,实现开机自启。

实验里会让学员搭建双网段DHC配置——一台DHCP服务器同时对两个VLAN提供地址分配,subnet声明分别对应不同网段,中继代理转发请求。这个实验的价值在于让你理解"客户端怎么知道DHCP服务器在哪"——不是靠配置,而是靠中继代理的转发。

综合实验题里,PXE装机往往需要DHCP配合:next-server指向TFTP、filename指定引导文件。所以考试时PXE和DHCP这两个模块一定会串联考察——DHCP配不好,PXE就走不通。

麒麟V11的DHCP适配要点

麒麟V11上跑DHCP有几个跟传统环境不一样的地方:

  • 服务名称变了systemctl start dhcpd而不是某些老教材上的dhcp,而且必须指定监听的网络接口(/etc/sysconfig/dhcpd里设置DHCPDARGS

  • NetworkManager接管网络:DHCP客户端的行为由NetworkManager控制,不在/etc/sysconfig/network-scripts/里写ifcfg了,客户端侧排障时先看nmcli device show确认DHCP状态

  • 租约文件路径:服务端的租约文件在/var/lib/dhcpd/dhcpd.leases,客户端在/var/lib/dhclient/目录下。麒麟V11上这些路径和权限跟V10一样,不需要额外创建

  • 多架构兼容:x86_64、AArch64、LoongArch64三种架构的dhcp-server包行为一致,但ARM架构下网卡驱动对DHCP广播包的处理可能有微妙差异,实验时建议每种架构都验证一遍

实战排障参考

IPv6 DHCP无法获取地址:messages日志报/var/lib/dhclient--enp3s0.lease: No such file or directory。根因是dhclient租约目录不存在或权限不对。麒麟V11的dhcp包已修复此问题,但自定义分区或最小化安装时仍有概率遇到。排查方法是先确认ls -la /var/lib/dhclient/目录是否存在,没有就手动创建。

NO DHCPOFFERS received:网卡配了DHCP但从没收到过Offer。排查链路分三步——第一、ip link show看物理链路up了没;第二、同网段其他机器能不能拿到IP(排除服务器端问题);第三、防火墙有没有拦截DHCP的67/68端口。最常见的原因是网线没插或者交换机端口VLAN配错了。

防火墙端口:DHCP服务端监听UDP 67端口,客户端使用UDP 68端口。麒麟V11默认Firewalld开启状态下,要放行DHCP服务:firewall-cmd --add-service=dhcp --permanent && firewall-cmd --reload。实验里经常有人配完DHCP客户端拿不到IP,查了一圈才发现防火墙没放行。

三、DNS:最被低估的基础服务

搞技术的人对DNS普遍有一种"轻视感"——不就是域名解析嘛,配几条A记录就行了。但实际上DNS在企业IT基础设施里的地位,用一句大白话说就是"DNS瘫了,什么服务都别想跑"。DNS是所有网络服务的入口,你写的Nginx配置、MySQL连接串、Ansible的inventory文件,背后都在依赖DNS把名字翻译成IP。

课程从DNS核心概念出发,一直讲到生产级的主DNS部署和缓存服务器搭建,覆盖了正向解析、反向解析、多域管理、客户端缓存配置几个关键环节。

DNS查询原理:递归与迭代的本质区别

DNS的查询方式分为两种,这个概念面试和考试里经常考,但很多人死记硬背,不理解它们的实际区别。

迭代查询:客户端向DNS服务器发起请求,服务器不自己跑完全程,而是告诉客户端"你去问问根域名服务器",然后客户端再去问根、再去问顶级域、再去问权威DNS——每一步都是客户端主动发起的。典型场景就是dig +trace命令,你能看到完整的解析链路从根一路走到目标域名的权威服务器。

递归查询:客户端只发起一次请求,DNS服务器替客户端跑完全程——先去问根,拿到顶级域NS记录后再去问顶级域,最后从权威服务器拿到A记录返回给客户端。内部DNS服务器和公共DNS(如114.114.114.114、8.8.8.8)默认都支持递归查询。

企业内部网络的实际拓扑:客户端的/etc/resolv.conf指向内网DNS服务器(递归),内网DNS再配置上游转发器(forwarders)。如果内网DNS缓存了结果直接返回;没缓存就递归查询,拿到结果后再缓存在本地供后续请求使用。

BIND安装与named配置体系

麒麟V11上安装BIND:

dnf install -y bind bind-utils

bind是服务端程序,bind-utils提供dignslookuphost等测试工具。安装好后,DNS服务的核心进程叫named,服务名也叫namedsystemctl start named)。

BIND的配置文件分成两层:

第一层:主配置文件 /etc/named.conf。定义全局选项(options)和zone声明。一个典型的内网主DNS配置结构:

options {
    listen-on port 53 { 192.168.1.10; };     # 监听IP和端口
    allow-query     { 192.168.1.0/24; };      # 允许查询的客户端网段
    forwarders      { 114.114.114.114; };      # 上游转发DNS
    recursion yes;                             # 开启递归查询
};

zone "db.local" IN {
    type master;
    file "db.local.zone";
};

zone "1.168.192.in-addr.arpa" IN {
    type master;
    file "192.168.1.zone";
};

options块里几个关键参数:

  • listen-on port 53:指定named监听的IP和端口。如果服务器有多网卡,只写内网IP避免对外暴露

  • allow-query:ACL访问控制,只允许内网网段查询,防止被外部利用做DNS放大攻击

  • forwarders:不是本zone的域名转发到上游DNS解析

  • recursion:是否开启递归。公网权威DNS必须关掉递归防止被利用做DDOS反射

第二层:zone数据文件。存放在/var/named/目录下,定义了具体的域名和IP映射关系。

Zone文件结构与资源记录详解

zone文件是DNS的灵魂。课程案例要求学员完成多个域名的解析服务配置,所以zone文件的每个字段都得理解透彻。

一个标准正向解析zone文件(以db.local域为例):

$TTL 86400
@   IN  SOA  ns1.db.local.  admin.db.local. (
            2025072001    ; serial(版本号,格式YYYYMMDDNN)
            7200          ; refresh(从服务器多久检查一次更新)
            3600          ; retry(重试间隔)
            604800        ; expire(从服务器过期时间)
            86400 )       ; minimum(否定缓存TTL)

@        IN  NS   ns1.db.local.
ns1      IN  A    192.168.1.10
web      IN  A    192.168.1.20
db       IN  A    192.168.1.50
app      IN  A    192.168.1.30
mail     IN  MX  10 mail.db.local.

每个字段的含义:

  • SOA记录(Start of Authority):zone的起始标记,必须是第一条记录。括号里的五个时间值称为"SOA五元组"。serial是最容易出错的——每次修改zone文件后必须递增递增serial值,否则从服务器不会同步。课程建议用日期+序号格式(如2025072001)方便识别。

  • NS记录:指定该域的权威DNS服务器

  • A记录:域名→IPv4地址映射。ns1webdb这些都是相对于zone域名的简写——web.db.local.的完整写法

  • MX记录:邮件交换记录,后面的数字是优先级(越小越优先)

  • CNAME记录:别名,课程实验里常见场景是www IN CNAME web让www指向web服务器

反向解析zone文件(以192.168.1.zone为例):

$TTL 86400
@   IN  SOA  ns1.db.local.  admin.db.local. (2025072001 7200 3600 604800 86400)
@        IN  NS   ns1.db.local.
10       IN  PTR  ns1.db.local.
20       IN  PTR  web.db.local.
50       IN  PTR  db.db.local.

反向解析用PTR记录,把IP映射回域名。这里10对应的完整IP是192.168.1.10。应用场景不多但考试会考——综合实验题里验证DNS配置是否正确时,直接用dig -x 192.168.1.20测试反向解析。

配置文件语法检查与测试工具

zone文件写好之后,不能直接重启named就完事——语法检查是必须的步骤:

named-checkconf                     # 检查named.conf主配置
named-checkzone db.local /var/named/db.local.zone    # 检查zone文件

一个很常见的坑:named-chroot模式(麒麟V11默认安装不启用chroot)、zone文件属主和SELinux上下文。如果named-checkzone通过但客户端解析不到,十有八九是文件权限问题——zone文件必须属主named:named、权限640

测试工具方面,课程实验里最常用的是:

  • dig:功能最强,直接查询并显示详细应答信息。dig @192.168.1.10 web.db.local指定DNS服务器测试解析

  • nslookup:简单交互式查询,适合快速验证

  • host:最轻量,host web.db.local一行出结果

我习惯在实验完成后让学员跑一遍这三个命令都验证一遍,养成多维度交叉验证的习惯。dig +trace还能看到完整解析链路,是理解DNS查询原理最直观的方式。

DNS缓存机制:客户端和服务端两层

课程专门讲DNS缓存,不是因为它难,而是因为它经常被忽略但出问题时影响面极广。

服务端缓存:BIND默认开启缓存,查询过的域名结果缓存在内存中。TTL值决定了缓存有效期——TTL越短,解析越"实时"但查询负载越高;TTL越长,缓存命中率高但切换时生效慢。课程实验里有专门的缓存服务器搭建案例。

客户端缓存:这个是更隐蔽的坑。麒麟V11的DNS客户端行为由两种机制控制:

  1. systemd-resolved:麒麟V11默认开启(V10可能用的是nscd)。它在本机监听127.0.0.53:53/etc/resolv.confnameserver 127.0.0.53指向的就是这个本地代理。缓存策略通过/etc/systemd/resolved.conf控制。

  2. nscd(Name Service Cache Daemon):老牌DNS缓存服务,在部分麒麟版本里仍在用。缓存文件在/var/db/nscd/

做DNS主备切换演练的时候,这个两层缓存机制就是最大的坑源。即使DNS服务器端的A记录已经改成了新IP,客户端本地缓存还在,就继续往老地址发包。解决方案分两层:服务器端提前降低TTL(比如切之前从86400降到300),客户端侧用systemd-resolve --flush-cachesresolvectl flush-caches强制刷新。

麒麟V11的DNS适配要点

麒麟V11跑BIND整体兼容性良好,但有几个值得注意的地方:

  • named服务管理systemctl start named,不是bind9也不是dns。V11的named版本较新(9.16+),支持dnssec-validation等新特性

  • NetworkManager与DNS的交互:V11默认由NetworkManager管理/etc/resolv.conf,修改DNS配置建议用nmcli con mod命令而不是直接改文件。如果手动改了resolv.conf,NetworkManager重启后可能覆盖掉

  • Firewalld放行:DNS使用UDP 53端口(TCP 53用于大响应和zone传输)。firewall-cmd --add-service=dns --permanent

  • chroot注意事项:如果启用了named-chroot,zone文件实际路径在/var/named/chroot/var/named/下,这是学员最容易搞混的地方

  • 多架构一致:x86_64、AArch64、LoongArch64三种架构的bind包行为完全一致,zone文件语法通用

实战排障参考

DNS主备切换不生效:切换后部分客户端还是解析到老IP。问题出在两层缓存上——服务端zone文件的TTL值设了86400(24小时),切换前没提前降低;再加上客户端systemd-resolved的本地缓存,导致部分应用几小时后才切过去。正确做法:切换前至少提前一个TTL周期把TTL降到300秒;切换后用rndc reload让named重载配置;客户端侧执行resolvectl flush-caches清除本地缓存。

named启动失败无日志:最常见的原因是zone文件语法错误。先用named-checkconf检查主配置,再用named-checkzone逐zone检查。如果检查都通过但还是启动不了,看journalctl -u named的输出,常见错误有:文件权限不对、zone文件路径写错、端口被占用。

解析超时dig返回SERVFAIL或timeout。排查链路:先确认named进程在不在(systemctl status named),再看防火墙有没有放行53端口,然后检查allow-query是不是把当前客户端网段排除了。最后一步才怀疑上游forwarder不通——用dig @114.114.114.114 www.baidu.com直接测试上游联通性。

四、NTP:时钟同步比你想象的重要

时钟同步这个东西,平时不出问题你感觉不到它存在,一旦出问题就是"莫名其妙"级别的事故——主从复制无故中断、分布式事务超时回滚、日志时间线对不上导致排查方向完全跑偏。做过数据库高可用的都懂这个痛。

麒麟V11用Chrony全面替代了传统的ntpd。课程从时钟同步的基本概念讲起,然后到Chrony的安装配置、时间服务器搭建、客户端同步、监控命令详解,覆盖了企业内网时钟同步的完整场景。

为什么需要时钟同步:企业场景中的真实痛点

说几个你大概率遇到过的真实场景:

  • 数据库主从复制:从库的SQL线程比主库快了一秒,binlog里的事务时间戳还是未来的——MySQL直接报错、SQL线程挂掉。DBA半夜爬起来看监控,发现所有从库的Seconds_Behind_Master都是NULL,根因就是NTP没同步

  • 分布式事务:微服务架构下,订单服务和库存服务各跑各的时钟,时间偏差超过心跳超时阈值,分布式锁竞争失败导致订单超时回滚

  • 日志关联分析:三台应用服务器的access log时间戳各差30秒,排查一个跨服务请求时,三份日志根本对不上,看谁的时间线都不对

  • Kerberos认证:时钟偏差超过5分钟(默认),TGT票据直接无效,所有节点认证失败——这才是最坑的,因为你第一反应肯定去查认证配置

课程里把这些场景拆开来讲,不是为了考试,而是让学员理解"为什么你要配这个"。搞清楚why,配how的时候就有感觉了。

Chrony vs ntpd:为什么V11全面切换

Chrony相比传统ntpd有几个核心优势,这也是麒麟V11选择它的原因:

  • 快速收敛:ntpd逐步微调,初始偏差大的时候要花几十分钟才能追平。Chrony用makestep参数可以在启动时直接大步跳转到正确时间,秒级收敛

  • 间歇性网络友好:ntpd假设网络连接持续稳定,断网后恢复慢。Chrony专门为虚拟机、移动设备、间歇联网环境设计,断网期间也能保持较高精度

  • 虚拟机支持更好:虚拟机的系统时钟和硬件时钟不稳定(CPU窃取),ntpd容易误判。Chrony的时钟滤波算法能更好地区分真实漂移和虚拟化抖动

  • 内存占用小:ntpd大约2-3MB,Chrony约1MB左右,对于容器和边缘节点更友好

麒麟V11上安装chrony:dnf install -y chrony,启动:systemctl start chronyd。注意服务名是chronyd带d后缀,不是chrony也不是ntp。

Chrony配置文件结构

Chrony的主配置文件/etc/chrony.conf,一个典型的内网时间服务器的配置长这样:

# 上游NTP服务器
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
server ntp.tencent.com iburst

# 允许本地硬件时钟作为备用源(断网时)
# 第10层表示这是最后的fallback
local stratum 10

# 允许时钟大幅跳变(默认首次启动允许跳1秒)
makestep 1.0 3

# 将系统时钟同步到硬件时钟(RTC)
rtcsync

# 允许内网客户端访问本机作为NTP服务器
allow 192.168.1.0/24

# 漂移文件,记录时钟的漂移率
driftfile /var/lib/chrony/drift

每个关键参数的含义:

  • server:指定上游NTP服务器。iburst加速初始同步——前四次请求间隔2秒而不是默认的60秒,大大缩短首次收敛时间。生产环境建议至少配三台上游服务器,避免单点故障

  • pool:NTP服务器池。pool 2.kylin.pool.ntp.org iburst,pool会自动轮询池内的多台服务器,比单个server的可用性更高。麒麟有自己的NTP池

  • makestep 1.0 3:时钟偏差超过1秒直接跳变(不是逐渐微调),最多执行3次。这个参数在生产环境里要谨慎——已经跑了很久的服务如果突然时钟跳变,可能导致超时逻辑触发。但如果偏差太大(比如几分钟),逐步追平反而更危险

  • rtcsync:定期把系统时钟写回硬件时钟。如果不加这个参数,系统时间和BIOS时间会逐渐脱节,机器重启后可能出现巨大偏差

  • allow:ACL访问控制,指定哪些网段的客户端可以连本机做时间同步。配成0.0.0.0/0等于对外暴露NTP服务,有被利用做反射放大的风险

  • local stratum 10:当所有上游NTP服务器都失联时,chronyd以本地时钟作为时间源,stratum设为10(越大表示时钟质量越差)。这确保内网客户端至少有一个时间基准,不会丢同步

chronyc监控命令:运维的眼睛

配好chrony只是第一步,能不能正常工作要靠监控命令确认。课程重点讲这几个命令:

chronyc tracking:查看当前同步状态。输出示例及解读:

Reference ID    : 5A7628F1 (ntp1.aliyun.com)
Stratum         : 3
Ref time (UTC)  : Wed Jul 22 09:30:15 2026
System time     : 0.000012345 seconds slow of NTP time
Last offset     : -0.000000123 seconds
RMS offset      : 0.000000456 seconds
Frequency       : 2.345 ppm slow
Residual freq   : -0.012 ppm
Skew            : 0.123 ppm
Root delay      : 0.015432101 seconds
Root dispersion : 0.001234567 seconds
Update interval : 64.2 seconds
Leap status     : Normal

重点关注这几个字段:

  • System time:当前系统时钟和NTP时间的偏差。正常情况下应该在微秒或小毫秒级别

  • Last offset:最后一次同步的偏差

  • Frequency:时钟晶振的频率漂移率,单位ppm(百万分之一)。这个值稳定下来说明chrony已经摸清了本机晶振的"脾气"

  • Leap status:闰秒状态,Normal表示正常。不是Normal就得留意了

  • Stratum:当前时间源的层级。直接连原子钟是1,连1层的是2,以此类推

chronyc sources -v:查看所有配置的上游NTP服务器状态:

MS Name/IP address         Stratum Poll Reach LastRx Last sample           
^* ntp1.aliyun.com               2   6   377    45   -123us[ -456us] +/- 15ms
^+ ntp2.aliyun.com               2   6   377    48   +456us[ +789us] +/- 20ms
^- ntp.tencent.com               ?   6     0    -     +0ns[   +0ns] +/-    0ns

状态标志(最左边那列):

  • ^*:当前正在使用的时间源,最优选择

  • ^+:可用但未被选中的时间源(综合评分略低于当前源)

  • ^-:被算法淘汰的源(偏差太大或不稳定)

  • ^?:连接丢失或无法访问

Reach是八进制值,377表示最近8次探测全部成功(二进制11111111=八进制377),0表示全部失败。这个值能直观判断上游服务器可用性。

chronyc sourcestats -v:查看统计信息,包括频率偏差、标准偏差,用于判断时间源的稳定性。

timedatectl与时区管理

麒麟V11使用systemd体系,时间管理用timedatectl统一控制:

timedatectl set-timezone Asia/Shanghai      # 设置时区
timedatectl set-ntp true                      # 启用NTP同步
timedatectl status                            # 查看完整时间状态

输出里关注三个信息:Local time(本地时间)、Universal time(UTC时间)、RTC time(硬件时钟时间)。三个时间不一致说明配置有问题。还有就是NTP enabled: yesNTP synchronized: yes两者都必须是yes——前者是配置启用了,后者是实际同步成功了。

常见的坑:有些老运维习惯直接用hwclock命令操作硬件时钟,但在systemd环境下timedatectl会跟hwclock冲突。统一用timedatectl,别混着用。

麒麟V11的Chrony适配要点

  • 服务名和端口systemctl start chronyd(不是chrony也不是ntpd),监听UDP 123端口

  • ntpd已移除:麒麟V11不再提供ntpd包,不存在chronyd和ntpd共存抢端口的问题。但如果你是从V10升级上来的,可能残留旧ntpd,确认一下rpm -qa | grep ntp

  • timedatectl集成:用timedatectl set-ntp true控制chronyd服务,它会自动管理chronyd.service的启停状态

  • Firewalld放行firewall-cmd --add-service=ntp --permanent,虽然服务是chrony,但Firewalld的预定义服务名仍然叫ntp

  • driftfile路径/var/lib/chrony/drift,权限为chrony用户可写

实战排障参考

时钟同步一直不成功chronyc tracking显示Not synchronised。三步排查——第一、chronyc sources -v看上游reach值是不是0(网络不通或防火墙拦截123端口);第二、检查/etc/chrony.confallow网段有没有配错;第三、看防火墙firewall-cmd --list-services有没有ntp服务。最常见的原因是外网不通或防火墙没放行123/UDP。

数据库主从复制莫名中断:MySQL报错The slave I/O thread stops because master and slave have equal MySQL server ids或者binlog位点对不上。排查第一步不是看MySQL配置,而是chronyc tracking看各节点时钟偏差——偏差超过几秒的从库,binlog时间戳对不上,主从复制直接挂。修好时钟同步后START SLAVE一把恢复。

虚拟机时钟忽快忽慢:虚拟化平台(如KVM、VMware)的CPU时间窃取(steal time)导致系统时钟不稳定。Chrony的应对策略是在chrony.conf里加maxupdateskew 100放宽斜率容限,同时在虚拟机模板里确保rtcsync开启——主机reboot时从硬件时钟恢复初始时间,再靠chrony微调。

五、Nginx:从Web服务器到反向代理网关

Nginx在KYCP课程里占了很重的篇幅,从基础安装一路讲到企业级高并发优化。它的定位不只是一个Web服务器——在企业架构里,Nginx往往扮演着入口网关的角色:HTTPS终结、请求路由、负载均衡、限流限速,流量进来第一脚踩的就是它。

Nginx装坏了或者配错了,后面MySQL、Python站点、PHP应用全挂——所以在整个课程体系中,这章属于"承上启下"的关键节点。

安装部署与核心架构

麒麟V11上装Nginx很简单:dnf install nginx,仓库自带。但课上不能只讲怎么装,得讲清楚Nginx的进程模型——这是理解它高性能的关键。

Nginx启动后会有两类进程:一个master进程(root权限,负责读取配置、管理worker生命周期),多个worker进程(非root,真正处理请求)。worker数量通常设为CPU核数,配置在nginx.confworker_processes auto;。每个worker能处理的并发连接数由worker_connections控制,默认1024,高并发场景要往上调。

还有一个容易被忽略的参数:worker_rlimit_nofile,它控制每个worker进程能打开的最大文件描述符数。在麒麟V11上这个值默认是1024,高并发场景下如果不手动调大,日志里会出现too many open files报错——这不是Nginx的问题,是系统层面的限制。

配置文件三层结构

Nginx的配置遵循严格的层级关系:http → server → location。这个嵌套结构初学容易晕,但一旦理解了就很好用:

  • http块:全局生效,sendfile ongzip onkeepalive_timeout这些放这

  • server块:定义虚拟主机,listen端口 + server_name域名决定哪个server处理请求

  • location块:URL路由规则,/根路径、/api/接口、/static/静态资源各走各的

课上一定会讲location的匹配优先级:=精确匹配 > ^~前缀匹配 > ~正则匹配 > 普通前缀匹配。这个优先级如果不搞清楚,配了五六条规则结果请求全落到第一条/里,排查半天才发现是优先级问题——这种坑我自己踩过。

虚拟主机:一台机器跑多个站点

Nginx支持三种虚拟主机方式:基于端口(listen 80 vs 8080)、基于域名(server_name a.com vs b.com)、基于IP(多网卡场景)。企业里最常用的是域名方式——一台服务器通过不同域名对外提供不同服务。

配置很简单,在主配置文件里用include引用sites-enabled/目录,每个站点一个独立的.conf文件。但麒麟V11上默认配置目录结构跟CentOS略有不同,conf.d/sites-enabled/的分工需要搞清楚:conf.d/放全局模块配置,sites-enabled/放虚拟主机配置(通过软链接到sites-available/管理启用状态)。

反向代理与负载均衡

这两个是Nginx在KYCP课程里最核心的两个考点。

反向代理的基本配置就三行:

location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

但实际企业中远不止这些。proxy_set_header要传的不只是Host和Real-IP——如果后端是HTTPS,还得传X-Forwarded-Protoproxy_read_timeoutproxy_connect_timeout控制超时;proxy_buffering控制缓冲策略。这些参数任何一个配错都可能导致502或504。

负载均衡通过upstream模块实现。课程里讲到了几种调度算法:

  • 轮询(默认):一人一个请求轮流来

  • 加权轮询(weight):性能好的节点多分担

  • ip_hash:同一个客户端IP始终落到同一个后端(解决session问题)

  • least_conn:谁连接少就给谁

做数据库管理平台的时候,前端Nginx+keepalived做高可用入口,反向代理转发到后端应用集群——这是经典的三层架构。如果upstream里后端挂了Nginx还往那打,一半用户看到的就是超时白页。

健康检查upstream的进阶考点。默认情况下Nginx只做被动检查——请求失败了才标记down,不会主动探测后端存活。这意味着一个节点挂了之后,Nginx还会往它发几个请求,这几个请求的用户就遭殃了。生产环境一般会用nginx_upstream_check_module做主动探测,或者配合keepalived做VIP漂移。

Rewrite规则与HTTPS

URL重写在实际工作中用得非常频繁:域名迁移做301跳转、HTTP强制转HTTPS、伪静态化处理SEO友好URL。rewrite指令支持正则和flag(last/break/redirect/permanent),其中lastbreak的区别是个经典考点——last是重新走一遍location匹配,break是停止后续rewrite直接处理当前请求。

HTTPS证书配置在考试综合实验题里很可能跟Nginx联动考察。核心几个步骤:生成私钥(openssl genrsa)、生成CSR、签发证书(自签或CA签)、配置ssl_certificatessl_certificate_key、配置SSL协议版本和加密套件。自签证书浏览器会报警告,但在内网环境够用;正式上线得用CA签发的证书。

麒麟V11上证书文件一般放/etc/pki/tls/下,这个路径跟标准路径一致,但权限管理要注意——私钥文件权限必须是600(chmod 600),否则Nginx启动会报权限错误拒绝加载。

静态资源优化

Nginx处理静态文件的能力是它的看家本事。课上会讲几个关键优化参数:

  • sendfile on:零拷贝传输,绕过用户态直接在内核态把文件发出去

  • tcp_nopush on:跟sendfile配合,等数据包攒满再发,减少网络包数量

  • gzip on:开启压缩,文本类资源能压缩70%以上

  • expires:设置浏览器缓存过期时间,静态资源设久一点减少请求

  • open_file_cache:缓存文件描述符和元数据,减少磁盘IO

这些参数单独看都不起眼,但组合起来效果很明显——一个页面如果有20个静态资源,gzip压缩能省一半流量,expires缓存能减少大量重复请求,sendfile能降低CPU开销。做高并发优化的时候,先把这些基础参数配到位,比上来就加机器靠谱。

企业案例:发布Python站点、高并发优化

课程里有两个硬核企业案例。

第一个是配置Nginx反向代理发布Python站点。典型架构是Nginx做前端接收请求,通过uWSGI协议转发到后端的Python应用(Django/Flask),静态文件直接由Nginx返回不走Python。配置的关键在于uWSGI的socket通信和静态文件的location划分——如果不把/static/拆出来,所有请求都走Python,那并发能力会差两个数量级。

第二个是企业高并发场景下的Nginx优化。从worker_processesworker_connectionskeepalive_timeoutproxy_buffer_sizeclient_max_body_size,系统性地调整参数应对大流量。这个案例从需求分析到配置落地完整走了一遍工程流程,跟综合实验题里那种"给一个场景让你配Nginx"的出题思路很像。

麒麟V11适配要点

几个跟V11有关的注意事项:

  • Nginx包来自麒麟仓库,版本可能不是最新稳定版,要注意模块兼容性

  • 默认日志路径在/var/log/nginx/,配置文件在/etc/nginx/,跟标准路径一致

  • Firewalld要放行80和443端口(firewall-cmd --add-service=http --add-service=https

  • SELinux在企业级部署中要保持开启,Nginx的反向代理需要开放网络连接权限(setsebool -P httpd_can_network_connect 1

实战排障参考:反向代理后端健康检查失效

麒麟交付知识库里有个典型场景:客户用Nginx做反向代理,后端跑了三个应用节点。某天一个节点挂了,但Nginx还在往挂掉的节点转发请求,导致三分之一的用户访问超时。

根因就是upstream没配主动健康检查。Nginx默认只做被动检查(请求失败了才标记down),不会主动探测后端是否存活。解决方案是配置max_failsfail_timeout参数:max_fails=3 fail_timeout=30s表示30秒内失败3次就摘除节点。如果需要更精细的健康探测,得用nginx_upstream_check_module第三方模块。

这个案例我在课堂上一定会讲,因为它直接对应了综合实验题里的考点:不仅要会配反向代理,还得理解后端健康检查的机制,知道什么情况下请求会被打到故障节点、怎么避免。

六、MySQL:DBA的本行

MySQL是我的老本行,但在KYCP课程里,它的定位很明确:系统运维视角下的数据库管理——不深挖InnoDB的B+树和redo log刷盘策略,不纠结SQL执行计划的cost估算,而是聚焦在"怎么在麒麟系统上把MySQL装好、配好、管好、备好"。

这门课不是培养DBA,是培养能管数据库的系统工程师。但说实话,如果你能把课程里讲的安装部署、权限管理、备份恢复、主从复制吃透,基础数据库管理工作已经能应付80%的场景了。

在麒麟V11上安装MySQL

麒麟V11上装MySQL,跟标准CentOS/RHEL的区别不大,但有几个注意点。

安装源有两种选择:可以用MySQL官方的yum仓库,也可以用麒麟仓库自带的MariaDB或社区版MySQL包。课件里引用了MySQL官方文档链接(8.0和8.4两个版本),说明课程以MySQL 8.x为主线。实际推荐用dnf install mysql-server——如果是麒麟仓库自带的版本,包依赖都适配好了,省去手动解决依赖的麻烦。

几个跟麒麟V11相关的安装要点:

  • ARM架构适配:鲲鹏(AArch64)、飞腾平台下安装MySQL 8.x没啥问题,官方社区版对ARM支持已经很成熟了

  • 数据目录:默认/var/lib/mysql/,建议生产环境单独挂一块盘,不要跟系统盘混在一起。这事不是课程要求的,但是企业部署的基本惯例

  • 初始化安全mysql_secure_installation是标配,设置root密码、删除匿名用户、禁用远程root登录、删除test库——这四步一条命令搞定

  • 字符集:安装后第一件事就是确认character_set_servercollation_server,麒麟系统默认通常已经是utf8mb4了,但检查一下不费事

用户与权限管理

课件引用了MySQL官方的权限系统文档(privileges-provided),说明权限管理是核心教学内容。课程里讲的是CREATE USER、GRANT、REVOKE这套基本操作。

但从DBA的角度,权限管理最容易被学员忽略的不是语法,而是最小权限原则怎么落地。比如一个备份账号只需要SELECT和LOCK TABLES权限就够了,很多人图省事直接GRANT ALL——这种习惯在企业里是没办法过安全审计的。

另外MySQL 8.x的认证插件从mysql_native_password改成了caching_sha2_password,有些老客户端连不上就是因为不支持新的认证方式。在麒麟V11实验环境里如果碰到ERROR 2059: Authentication plugin caching_sha2_password cannot be loaded,解决方案是创建用户时指定IDENTIFIED WITH mysql_native_password BY 'password',或者把客户端工具升级到支持新的认证方式。

SQL基础与数据类型

课件里引用了数据类型(data-types)、关键字(keywords)、日期时间(datetime)、运算符优先级(operator-precedence)等官方文档链接,说明课程覆盖了SQL基础语法和数据类型的教学内容。

这部分对系统运维人员来说是"够用就行"的范畴:知道常见的数值型(INT/BIGINT/DECIMAL)、字符串型(VARCHAR/TEXT)、日期型(DATE/DATETIME/TIMESTAMP)的适用场景,能写基本的SELECT/INSERT/UPDATE/DELETE、能建表就能应付大部分日常管理任务。考试不会考复杂多表JOIN或者子查询优化,但基本的DDL和DML语法是综合实验题里的基本功。

值得提一嘴的是课件还引用了MySQL 8.4的sys schema视图文档——这说明课程涉及到了MySQL内置的系统库和监控视图,这对日常巡检和性能排查很有帮助。知道怎么查sys.schema_unused_indexes找冗余索引、用sys.statements_with_full_table_scans定位全表扫描的SQL,这些比会写复杂SQL实用得多。

备份与恢复

这块是KYCP课程里MySQL部分的重头戏,也是我讲课会花最多时间的地方。

逻辑备份(mysqldump):适合小库、跨版本迁移、导出特定表。优点是灵活、可读、可选择性导出,缺点是慢、锁表、恢复时间长。生产环境几百G的库用mysqldump备份不现实,但做个几十G的业务库备份完全够用。一个关键参数:--single-transaction可以保证InnoDB表的一致性备份不锁表,但MyISAM表不行——这个差异学员经常搞混。

物理备份(xtrabackup):适合生产环境大库。速度快、不锁表、支持增量备份,但操作复杂度比mysqldump高一个台阶。课程里讲的是概念和基本操作,不会深入到增量备份的LSN对齐机制——那是专业DBA方向的内容。

我课堂上反复强调的一个点是:备份不校验等于没备份。mysqldump导出的SQL文件看着大小正常,但里面可能全是空的——数据库连不上、权限不够、磁盘满了,脚本没做错误处理就继续跑,最后生成的"备份文件"只有表结构没有任何数据。这种事故在企业里太常见了(我自己刚入行时就踩过这个坑),所以讲备份的时候我一定会强调两点:备份完成后检查退出码、定期做恢复演练(找个测试环境把备份文件还原回去跑一遍业务查询)。

主从复制

课程讲了经典的主从复制原理和基本配置:主库开binlog、创建复制账号、从库CHANGE MASTER TO配置、START SLAVE启动。讲得算是入门,但基本的搭建流程都有了。

作为DBA讲师,这部分我一般会补充几个生产环境里真正会碰到的问题:

  • 主从延迟:监控Seconds_Behind_Master只是第一步,得理解延迟的原因——是网络延迟、从库性能不足、还是主库有大事务阻塞了binlog同步。知道怎么排查比知道怎么搭更重要

  • 复制中断:断点续传的MASTER_AUTO_POSITION配合GTID能自动跳过已执行的binlog事件,但GTID不是默认开启的,手动配置的时候gtid_mode=ONenforce_gtid_consistency=ON要同时设

  • 主从切换:计划内切换用FLUSH TABLES WITH READ LOCK锁主库、等从库追上、然后切流量。紧急切换就没这么体面了——从库提升为主库后,关键一步是检查数据一致性,pt-table-checksumpt-table-sync是常用工具,这些超出课程范围但在项目里一定会用到

这些东西可能在考试里不直接考,但学员出去做项目,只要配过主从就一定会碰到这些问题。课堂上提前打预防针,比让他们在生产环境里摸索强。

麒麟V11适配要点

  • MySQL/MariaDB包的生态比较成熟,麒麟V11上的包依赖问题不多,但要确认架构版本(AArch64 vs x86_64)下载正确的RPM

  • my.cnf配置文件的存放路径跟标准Linux一致:/etc/my.cnf主配置、/etc/my.cnf.d/目录存放独立配置片段

  • SELinux在企业部署中要保持开启,MySQL的数据目录如果自定义了路径,需要semanage fcontext加标签,否则启动权限被SELinux拦截

  • Firewalld放行3306端口只是基本操作,内网环境建议限制来源IP范围,不要直接全开

七、NFS:文件共享的基石

NFS在企业里最常见的场景是集中存放数据库备份和日志归档,但课程讲得比这个基础场景深入得多。从NFS与RPC的底层关系、协议版本差异、到三种挂载方式的选择和权限控制细节,基本覆盖了一个系统工程师日常管理NFS所需的全部知识。

NFS协议原理与RPC

NFS跟普通网络服务不太一样——它不直接监听固定端口,而是通过RPC(Remote Procedure Call)来注册服务端口。这个架构理解起来有个关键点:启动顺序必须是先rpcbind、再NFS服务,因为NFS启动时向rpcbind注册自己的端口号,客户端访问时也是先问rpcbind"NFS服务在哪个端口"再建立连接。

NFS涉及的守护进程有好几个:nfsd处理文件读写请求、mountd处理客户端的挂载请求、statd处理文件锁状态、idmapd做用户UID/GID映射(NFSv4专用)。如果某个守护进程挂了,NFS服务可能"部分可用"——能查到共享列表但挂载失败、能挂载但写不进文件——这种半死不活的状态排查起来最费时间。

showmount -e <server_ip>查看服务端导出的共享列表,rpcinfo -p <server_ip>查看RPC注册的端口信息,这两个命令是NFS排障的基本功。

NFSv3 vs NFSv4

课程会专门讲NFS协议版本的区别,这不是为了凑知识点,而是实际生产环境里版本混用导致的问题太多了。

NFSv3是老牌协议,兼容性最好,几乎所有客户端都支持。但它依赖多个辅助协议:mountd处理挂载、statd处理锁、NLM做网络锁管理——各个端口各管一摊,防火墙放行的时候要把rpcbind、mountd、nfs、nlockmgr一堆端口全打开,比较麻烦。

NFSv4是现代化版本,把挂载、锁、状态管理全部整合到了一个协议里,只需要开放2049一个端口(防火墙配置简化很多)。它还支持有状态连接、ACL权限、Kerberos安全认证、伪文件系统(pseudo filesystem)等高级特性。兼容性方面,主流Linux发行版包括麒麟V11都已经默认支持NFSv4了。

课程里讲的都是NFSv4,但实验的时候会有学员拿旧客户端用NFSv3协议挂载,然后发现某些功能(比如锁机制)表现不一致——所以知道版本差异有助于理解实验中的异常现象。

服务端配置与exportfs

服务端核心配置在/etc/exports文件,格式很简单:/shared_dir client_ip(options)。但选项这块是考点密集区。

常用export选项

  • rw / ro:读写还是只读,基础但容易忘——默认是ro,很多人挂上去发现写不了文件排查半天才发现没写rw

  • sync / async:sync等数据落地磁盘才返回确认(安全但性能差),async先把数据放内存缓存就回复(性能好但断电可能丢数据)。生产环境数据库备份场景建议sync,交易业务如果追求低延迟且有一致性兜底方案可以用async

  • no_root_squash / root_squash / all_squash:用户ID映射策略,这是NFS权限最复杂的部分。默认root_squash会把root映射成nobody,防止客户端root操作服务端文件;no_root_squash取消这个映射(危险但某些应用就是需要);all_squash把所有用户都映射到指定用户

  • anonuid / anongid:配合all_squash使用,指定映射到的具体用户ID

  • no_subtree_check:不做子目录检查,提高性能但降低安全性

修改完/etc/exports后,用exportfs -r重新加载,不要直接重启服务(会中断现有挂载)。很多人犯的错误是改完配置忘了reload,然后来问"为什么我改了配置没生效"——这种情况在生产里遇到太多次了。

客户端挂载:三种方式

课程重点讲了三种挂载方式的区别和适用场景:

手动挂载mount -t nfs server:/path /local_mount,最直接,但重启就没了。适合测试、临时操作。

fstab自动挂载:在/etc/fstab里写server:/path /mnt nfs defaults 0 0,开机自动挂载。优点是简单,缺点是如果NFS服务器不可达,客户端开机时会卡在挂载阶段(等待超时才能跳过),这个坑在生产里影响很大——凌晨服务器重启后半天起不来,就是因为fstab里写了一条NFS挂载指向一台正在维护的存储服务器。

autofs按需挂载:配置/etc/auto.master/etc/auto.nfs映射文件,只有在访问挂载路径时才触发挂载,超时不活动后自动卸载。这是企业里大量客户端挂载NFS的最佳实践——NFS服务器挂了不影响客户端启动,资源只在需要时占用。

mount命令的常用参数也值得记住:-o rw,hard,intr指定读写、硬挂载(挂了会重试而不是报错)、允许中断;-o vers=4指定NFS协议版本;-o rsize=1048576,wsize=1048576调大读写块大小提升性能。

权限控制实战

NFS的权限是两层叠加的:服务端export权限 + 文件系统权限。两个都放行才能真正读写。很多人只注意export配置里的rw/ro,忘了文件系统层面的目录权限——chmod没调对,客户端挂载成功但写文件时报Permission denied。

另一个容易踩的坑是用户ID映射。比如服务端有个文件属于uid=1000的用户,客户端的同一个用户uid也是1000——NFSv3直接透传UID,两个端uid一致才能正常访问。如果两边的uid不一致,NFSv3下就会出现"文件所有权混乱"的问题:客户端ls看到的是服务端的uid号,但客户端上那个uid对应的是完全不同的用户。

NFSv4用idmapd做uid/gid到用户名的双向映射,解决了跨机器uid不一致的问题,但要求两边的用户名(不是uid)一致,而且nfs-idmapd服务必须正常运行。

麒麟V11适配要点

  • 服务包名:nfs-utils(包含NFS服务端和客户端工具),autofs(按需挂载)

  • 启动顺序:必须先systemctl start rpcbindsystemctl start nfs-server,麒麟V11的systemd依赖关系默认处理好了,但手工操作时别搞反

  • Firewalld:NFSv4只需放行2049/tcp;NFSv3需要额外放行rpcbind(111)和mountd(20048),生产环境建议直接用NFSv4省事

  • SELinux:如果共享目录不在默认路径(如/data/nfs_share),需要用semanage fcontext -a -t nfs_t打标签,否则SELinux会拦截NFS服务读写

实战排障参考:fstab NFS挂载导致系统无法启动

麒麟交付知识库专门有案例讲这个:修改fstab后系统卡在启动阶段。典型报错是A start job is running for /etc/rc.d/rc.local compatibility,一等就是好几分钟,严重时直接进emergency mode。

根因一般是fstab里写了NFS自动挂载,结果NFS服务器挂了或网络不通。解决方案是进单用户模式(grub启动时按e,在内核参数那行末尾加singleemergency),把fstab里出错那行注释掉,执行mount -a确认没报错后再重启。这也是为什么课程里要讲autofs——用autofs按需挂载替代fstab自动挂载,从根上避免启动挂死。

知识库里还有个开机卡logo的案例,报错信息也是A start job is running for /etc/rc.d/rc.local compatibility,但根因是rc.local里写了依赖网络的挂载操作——排查方向跟fstab类似,总之就是启动流程里别放依赖网络的阻塞操作。

八、iSCSI:块存储的进阶玩法

NFS解决了文件共享的问题,但如果你要跑数据库、要做集群共享存储,NFS就不够用了——数据库对磁盘的要求是"跟本地盘一样快、一样稳定",文件级别的NFS在并发写和锁机制上天然有劣势。iSCSI来了:它把远程存储模拟成一块本地SCSI磁盘,你可以在这块"虚拟盘"上做分区、建LVM、格式化,数据库直接读写,就跟插了块本地硬盘没区别。

NAS vs SAN:文件存储和块存储的本质区别

课程上来先讲清楚大格局——NAS(Network Attached Storage)和SAN(Storage Area Network)的定位完全不同:

  • NAS:跑的是文件级协议(NFS/SMB/CIFS),客户端看到的是"共享目录",文件系统由服务端管理。适合文件共享、日志归档、备份集中存放。

  • SAN:跑的是块级协议(iSCSI/FC/FCoE),客户端看到的是"裸磁盘",自己分区、自己格式化、自己管理文件系统。适合数据库、虚拟化、集群共享存储。

一句话择决:你到底是"给别人用一个目录"(NAS)还是"给别人用一块盘"(SAN)。KYCP课程里NFS和iSCSI前后衔接,就是要你搞明白这个区别——综合实验题里很可能会出"请为数据库集群配置共享存储"这种需求,你选NFS还是iSCSI?选错了整个方案就不对。

iSCSI协议原理:SCSI over IP

iSCSI的核心思想是把SCSI命令封装进TCP/IP包,通过以太网传输。它有两个角色:

  • Target(目标端):提供存储资源的一方,可以是一整块物理磁盘、一个LVM逻辑卷、甚至一个文件模拟的虚拟盘。

  • Initiator(发起端):使用存储资源的一方,就是客户端。

每个iSCSI节点用IQN(iSCSI Qualified Name)唯一标识,格式固定:iqn.YYYY-MM.com.example:identifier。比如 iqn.2024-08.cn.kylinos:storage.lun0——年月是域名注册时间,后面是自定义标识。Target和Initiator各有一个IQN,认的就是这个名字。

通信流程不复杂:Initiator先做Discovery(发现)——向Target的Portal(IP+端口,默认3260)发请求,问"你有哪些LUN可以给我用";Target返回可用的Target名称列表;Initiator选择目标后做Login(登录),建立起Session;之后就跟本地SCSI盘一样读写了。

Target端配置:从零搭建iSCSI存储服务

这是课程实操的重头戏,分四步走:

第一步:安装并启动服务。 麒麟V11上装targetcli套件,启动target.service。targetcli是个交互式命令行工具,进去之后就是一个树形目录结构,可以用lscdcreatedelete操作——跟操作文件系统一样,直观。

第二步:创建Backstore(后端存储)。 这是iSCSI Target真正"拿出"存储的地方,两种方式:

  • block(块设备):直接拿一块物理磁盘或LVM逻辑卷(如/dev/sdb/dev/vg_iscsi/lv_data)。优点是性能好,没有文件系统层的损耗;缺点是灵活性差,扩容要动底层的LVM或物理设备。

  • fileio(文件模拟):在现有文件系统上创建一个大文件(如/data/iscsi_disk.img),把这个文件模拟成块设备。优点是灵活——创建快照、动态扩容、迁移都方便;缺点是多了一层文件系统开销,性能略低于block。

课程里两种都会让你做一遍,考试的综合实验题里可能指定"使用文件方式创建iSCSI目标",因为fileio方便环境回滚。

第三步:创建Target并绑定Backstore。 给Target分配IQN名称,然后把前面建好的Backstore映射为LUN(Logical Unit Number)。一个Target下可以有多个LUN,每个LUN对应一个Backstore。

第四步:配置ACL(访问控制)。 默认情况下任何Initiator都能登录,这在生产环境里绝对不行。ACL就是白名单——指定哪些Initiator IQN才能访问这个Target。课程还会讲CHAP认证(单向和双向),在ACL基础上再加一层用户名密码校验。

整个配置流程在targetcli里操作完,一定要执行saveconfig保存——否则服务重启配置全丢。这个坑我在课堂上会让学员故意触发一次,印象才深。

Initiator端配置:客户端连接与挂载

Initiator端用iscsiadm工具,工作流分四步:

  1. 发现Targetiscsiadm -m discovery -t sendtargets -p <target_ip>:3260 ——问Target有哪些资源可用。成功后会在/var/lib/iscsi/nodes/目录下生成对应Target的配置子目录。

  2. 登录Targetiscsiadm -m node -T <target_iqn> -p <target_ip>:3260 -l ——建立连接。登录成功后系统内核会识别到新的SCSI设备(/dev/sdb/dev/sdc之类的),用dmesg或者lsblk就能看到。

  3. 分区、格式化、挂载:就跟本地盘一样操作——fdisk分区、mkfs.xfs格式化、mount挂载。

  4. 开机自动连接iscsiadm配的session默认重启就断,要想开机自动重连,需要把对应node的node.startup改为automatic

关键警告:fstab里写iSCSI盘的挂载信息时,必须加_netdev参数(如UUID=xxx /data xfs defaults,_netdev 0 0)。这个参数告诉系统"等网络起来之后再挂载这个盘",否则开机时网络还没通、iSCSI还没连上,系统会卡在挂载阶段超时。

存储扩容:Target端扩了,Initiator端怎么认

这是实操考试的必考点。扩容流程涉及两端操作:

Target端:如果是fileio类型的Backstore,直接扩展img文件的大小;如果是block类型且底层是LVM逻辑卷,先lvextend扩展LV,再刷新Backstore。

Initiator端:Target端扩容后,Initiator不会自动识别到新容量。需要做两件事:

  1. 重新扫描SCSI设备:echo 1 > /sys/class/scsi_device/<device>/device/rescan 或者直接 iscsiadm -m session -R(rescan所有session下的设备)

  2. 扩文件系统:如果是LVM,先用pvresize扩展PV,再lvextend扩LV,最后xfs_growfs或者resize2fs在线扩文件系统——全程不用卸载。

这个流程做完,数据库的数据文件目录就默默"变大"了,业务无感知。能在不中断服务的情况下扩容,这才是iSCSI在企业里真正的价值。

数据库高可用场景中的iSCSI

DBA视角下,iSCSI最常见的使用场景是两个数据库节点共享同一个LUN,配合集群文件系统(如GFS2/OCFS2)实现共享存储HA。MySQL的drbd方案其实也是类似的思路——块设备级别的数据同步。

但实际项目中,iSCSI配错权限导致Initiator连不上的情况很常见:ACL白名单里IQN写错一个字符、CHAP用户名密码不匹配、Portal的IP和端口配置不一致——任何一个环节出问题,数据库集群就起不来。相比NFS的showmount -e一眼看到共享列表,iSCSI的排障链路更长:要确认target服务状态、ACL配置、CHAP认证、Initiator端session状态、SCSI设备识别情况,一层一层查。这种复合排障场景,在综合实验题里太适合考了。

麒麟V11适配要点

  • 包名与依赖targetcli提供target端功能,iscsi-initiator-utils提供initiator端的iscsiadm。两者依赖关系要搞清楚——考试可能问"某台机器上要连iSCSI存储,需要安装哪个包"

  • 服务管理:target端服务名是target.service(不是tgtd,那是老版的),initiator端是iscsid.serviceiscsi.service两个

  • Firewalld端口:iSCSI使用3260/tcp,target端必须放行。如果在非标端口上配置了Portal,记得对应修改防火墙规则

  • SELinux:如果fileio的img文件放在非标路径(如/data/iscsi/images/),SELinux可能阻止target读取,需要用semanage fcontext打标签

  • 多架构支持:麒麟V11支持x86_64、AArch64、LoongArch64三种架构,iSCSI内核模块在所有架构上均已集成,无需额外编译

实战排障参考:设备名漂移与文件系统修复

iSCSI环境里最常见的坑:设备名变化。今天登录的时候内核把iSCSI盘认成/dev/sdb,重启后可能变成/dev/sdc(因为加载顺序变了)——如果fstab里写的是设备名而不是UUID,挂载直接失败。

排查路径很明确:先用lsblkdmesg | grep -i "attached"确认系统识别到了哪些新SCSI盘;再用blkid看每块盘的UUID和文件系统类型;fstab里确保挂载写的UUID。如果文件系统出问题(比如异常断电导致的脏数据),用xfs_repair(XFS)或者e2fsck(ext4)修复。知识库里还提到一个细节:fstab的defaults参数后面加x-gvfs-show才能让图形界面显示这块盘——不是必考点,但讲课时提一句能让学员少踩坑。

麒麟交付知识库还记录过一个iSCSI相关案例:LUN扩容后Initiator端执行iscsiadm -m session -R重新扫描,但lsblk看到的容量没变。排查发现是文件系统类型限制了在线扩容能力——XFS可以xfs_growfs在线扩,ext4必须卸载后才能resize2fs,而某些场景下底层用的LVM需要先pvresize再扩LV再扩文件系统,三步缺一不可。这个案例在考试的综合实验题里就是标准的"存储扩容"完整流程题。

九、Shell脚本:自动化运维的基本功

搞技术的人,迟早要面对一个事实:手工操作是最大的效率瓶颈,也是最大的出错来源。Shell脚本编程不是KYCP里最"炫"的模块——没Nginx那么高大上,没Docker那么时髦——但它是最"横穿"的。你在前面八章学到的东西——PXE装机、DHCP网配、DNS解析、NTP同步、Nginx部署、MySQL运维、NFS挂载、iSCSI连接——哪些不是靠Shell脚本串起来的?备份不是一行mysqldump手动敲,而是写成脚本扔进crontab;批量配置不是一台台SSH上去改,而是写好循环一把梭。

这就是Shell在KYCP课程里的定位:它不是独立的一章,是贯穿全课程的黏合剂。课程从零基础教起,一步步带你写出能真正干活的脚本。

Shell基础概念:解释器、执行方式与环境

Shell本质上是一个命令解释器——你敲的命令、写的脚本,最终都是Shell翻译给内核执行。课程会先让你搞清楚几个基础问题:

常见的Shell解释器sh(最基础,POSIX标准,功能最少)、bash(Linux默认,功能最全)、dash(轻量,Debian系默认的/bin/sh指向)、zsh(交互式王炸但不适合写生产脚本)。KYCP教学和考试统一用bash

Shebang(第一行魔法)#!/bin/bash不是注释,它告诉系统用哪个解释器执行这个脚本。写#!/bin/bash意味着不管用户当前用什么Shell,脚本都跑在bash下。不写Shebang的话,脚本会在当前Shell的子进程中执行,不同Shell行为可能不一致——这是新手最容易踩的坑。麒麟V11上如果写了#!/bin/sh,实际可能指向dash而不是bash,所以教学里统一要求#!/bin/bash

脚本的执行方式:三种方式结果不同:

  • bash script.sh:在新bash子进程中执行,不需要执行权限,脚本里的exit不影响当前Shell

  • ./script.sh:需要chmod +x执行权限,读Shebang决定解释器,同样在子进程中执行

  • source script.sh 或 . script.sh在当前Shell中执行,脚本里的cd、变量赋值会直接影响当前会话。这就是为什么修改.bashrc后要用source ~/.bashrc让它生效

变量与数据类型:一切皆字符串

Shell变量默认都是字符串,这是跟Python/Java最大的思维差异。课程会覆盖四种变量类型:

普通变量name="value",注意等号两边不能有空格——这个规则反直觉,初学者常写成name = "value"然后报command not found。变量引用用$name${name},后者可以避免歧义,比如${name}_backup明确表示name变量后接_backup字符串。

环境变量export NAME="value"导出的变量会被子进程继承。典型的用法是写脚本时export PATH="/usr/local/mysql/bin:$PATH"让后续命令能找到MySQL客户端。注意:子Shell修改变量不影响父Shell,这个单向传递特性经常让新手困惑。

特殊变量:Shell自带的一批"系统变量",每个Shell程序员都必须记:

  • $?:上一条命令的退出码,0表示成功,非0表示失败。这是Shell脚本错误处理的基础

  • $#:传递给脚本的参数个数。用来判断用户有没有给够参数。

  • $@ 和 $*:都表示所有参数,但"$@"会把每个参数独立引用(推荐),"$*"会把所有参数合并成一个字符串。

  • $0:脚本自身的名字,$1$9:第1到第9个位置参数。超过9个要加花括号:${10}

  • $$:当前Shell进程的PID,写锁文件时常用。$!:最后一个后台进程的PID。

数组:bash支持一维数组,arr=(a b c)定义,${arr[0]}取值,${#arr[@]}取长度。多维数组不支持,需要的话用关联数组(declare -A)。

条件判断:if、test和case

Shell的条件判断分两套语法,混着用很容易写出bug。

test命令(等价于[ ]

  • 数值比较:-eq-ne-gt-lt-ge-le。注意-gt不是>[里的>是重定向。

  • 字符串比较:=(等于)、!=(不等于)、-z(长度为0,判断空串)、-n(长度非0)。双引号是灵魂——[ "$var" = "value" ]写成[ $var = "value" ],当var为空时空变量导致语法错误。

  • 文件测试:-f(普通文件)、-d(目录)、-e(存在)、-r/-w/-x(可读写执行)、-s(非空)。写备份脚本第一步就是[ ! -d "$BACKUP_DIR" ] && mkdir -p "$BACKUP_DIR"

[[ ]]增强版:bash专属,支持&&/||逻辑运算直接在括号里写、支持=~正则匹配、变量引用不需要双引号也不会因空值报错。能用[[ ]]就别用[ ]——但考试可能考两个的区别。

if-then-elif-else-fi结构:完整的分支语法。重点记忆:if后面跟的是命令的退出码,不是布尔表达式——if grep "error" logfile; then之所以能工作,是因为grep找到匹配就返回0、找不到返回1。

case-esac多分支匹配:比一长串if-elif优雅得多。常用于处理脚本参数,比如case "$1" in start|stop|restart|status*是默认分支,相当于switch的default。每个分支末尾的两个分号;;不能少。

循环控制:for、while、until

for循环三形态

  1. 列表遍历:for i in 1 2 3 4 5; do ... done 或 for host in $(cat hosts.txt); do ssh $host "uptime"; done。批量操作几十台机器靠的就是这个。

  2. C风格:for ((i=1; i<=10; i++)); do ... done,适合固定次数循环。

  3. 通配展开:for file in /var/log/*.log; do gzip "$file"; done,日志压缩脚本的经典写法。

while循环:条件为真就一直循环。典型场景:while read line; do echo "Processing: $line"; done < file.txt——逐行读文件。注意不要写成cat file | while read line,管道会创建子Shell,循环里的变量修改在循环外不可见。

breakcontinuebreak跳出整个循环,break 2跳出两层。continue跳过本次循环继续下一次。多层嵌套循环里这两个命令配合能精确控制流程。

函数封装:让脚本从"能跑"到"好维护"

函数是让Shell脚本从"揉成一团的命令序列"升级为"结构化的程序"的钥匙。课程会讲:

定义与调用function_name() { commands; }。注意函数必须先定义后调用——Shell是逐行解释执行的,不像Python可以先定义类再实例化。

参数传递:函数内部用$1$2接收参数,跟脚本接收命令行参数一样。$@获取所有参数。典型用法就是一个通用日志函数:log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> /var/log/script.log; },然后在脚本各处调用log "备份开始"log "备份完成"

返回值:函数的return只能返回0-255的整数(退出码),不能返回字符串。要"返回"数据,要么用echo输出配合命令替换result=$(func)接收,要么操作全局变量。

局部变量:函数内用local var=value声明的变量只在函数内可见,不会污染全局命名空间。写大型脚本强烈建议所有函数内变量都加local

输入输出重定向与管道

这部分是Shell最基础也最容易搞混的:

三个标准流:stdin(0,标准输入,默认键盘)、stdout(1,标准输出,默认屏幕)、stderr(2,标准错误,默认屏幕)。重定向就是改变这些流的去向。

常用重定向操作

  • >:覆盖写入stdout。> file 2>&1 或 &> file:stdout和stderr都重定向到同一个文件。

  • >>:追加写入。日志脚本用这个,别用>——覆盖了就什么都找不回来了。

  • 2>/dev/null:丢弃错误信息。装x的时候用,排障的时候千万别用——把错误信息扔了你还查什么。

  • <:从文件读取stdin。while read line; do ... done < input.txt是标准写法。

  • << EOF ... EOF(Here Document):在脚本中嵌入多行文本,写配置文件模板时最方便。cat <<EOF > /etc/nginx/conf.d/site.conf写一堆内容EOF。

管道|:把前一个命令的stdout接到后一个命令的stdin。ps aux | grep nginx | awk '{print $2}' | xargs kill——这条管道链就是Shell哲学的体现:每个命令做一件事,组合起来完成复杂任务。但管道里每个命令在子Shell中执行,记住这个对排查"为什么管道里的变量赋值在外面拿不到"有帮助。

错误处理与调试:让脚本从"跑通"到"靠谱"

脚本写出来"能跑"只是第一步,"靠谱"才是真功夫。课程会覆盖这些关键技巧:

set -e(遇错即停):任何一个命令返回非0退出码,脚本立即退出。但有个坑:set -e在管道命令里不生效——管道的退出码默认取最后一个命令的,即使前面某个命令失败了。

set -o pipefail:配合set -e使用,管道中任何一个命令失败,管道整体就返回失败。set -euo pipefail是生产脚本的标配——-e遇错即停、-u引用未定义变量报错、-o pipefail管道严格模式。

trap信号捕获:脚本收到SIGTERM(kill默认信号)、SIGINT(Ctrl+C)时执行清理操作。写数据库备份脚本最典型的用法:trap "rm -f /tmp/backup.lock; exit 1" INT TERM EXIT——不管脚本正常退出还是被kill,锁文件一定会被清理。

退出码约定:脚本退出码0表示成功、非0表示失败。建议在不同失败点返回不同的非0值(1=权限不足、2=参数错误、3=网络不通……),调用方的排障效率会高很多。

调试三板斧

  1. bash -x script.sh:逐行打印每条命令及其展开后的参数,最常用。

  2. set -x / set +x:在脚本里局部开关调试模式,只打印你关心的那几行。

  3. 关键位置加echo "DEBUG: var=$var"——虽然土,但比任何调试器都管用。

常用文本处理工具:grep、sed、awk三剑客

Shell脚本80%的工作是处理文本——日志分析、配置修改、数据提取。课程会覆盖这三个核心工具:

grep:文本搜索。-i忽略大小写、-v反向匹配(排除)、-n显示行号、-c计数、-A/B/C上下文行数。grep -c "ERROR" app.log一眼看出今天出了多少错误——这是Zabbix自定义监控项最常用的数据采集手段。

sed:流编辑器,逐行处理文本。最常用的是替换:sed 's/old/new/g'g替换所有匹配,不加只替换第一个)、sed -i直接修改文件(macOS上要写sed -i '',Linux上写sed -i,这是个跨平台坑)、sed '/pattern/d'删除匹配行。批量修改配置文件的神器。

awk:文本分析。awk '{print $1, $NF}'打印第1列和最后一列,awk -F: '{print $1}' /etc/passwd用冒号分隔取用户名。最强大的用法是条件过滤+计算:awk '$3 > 1000 {sum+=$3} END {print sum}'——第三字段大于1000的行对第三字段求和。做性能分析时用awk处理iostatvmstat输出的表格数据,比Excel快得多。

其他配套工具:cut -d -f按分隔符切字段、sort排序(-n数字排序、-r逆序、-u去重)、uniq -c统计重复行、wc -l统计行数、find查找文件配合-execxargs批量操作。

麒麟V11适配要点

  • 默认Shell:V11上/bin/sh默认指向bash(而不是某些发行版的dash),但考试和教学统一要求#!/bin/bash显式声明,避免依赖系统默认行为

  • 命令路径差异:某些系统管理命令在V11上的路径可能与传统CentOS不同(如网络配置相关命令),写脚本时建议用which nginxcommand -v nginx动态查找路径,而不是硬编码/usr/sbin/nginx

  • 权限管理:麒麟V11的SELinux和KSAF安全框架默认开启,脚本中如果涉及修改系统配置、操作网络端口等操作,可能被安全策略拦截,需要验证脚本在SELinux enforcing状态下的行为

  • systemd集成:V11全面使用systemd,脚本中操作服务应该用systemctl而不是老式的service命令。写健康检查脚本时可以用systemctl is-active nginx判断服务状态,比ps aux | grep可靠得多

实战排障参考:备份脚本静默失败

这是我在课堂上必讲的案例。一个典型的"看起来没问题"的MySQL备份脚本:

#!/bin/bash
BACKUP_DIR=/backup/mysql
DATE=$(date +%Y%m%d)
mysqldump -u root --all-databases > $BACKUP_DIR/full_$DATE.sql
gzip $BACKUP_DIR/full_$DATE.sql
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
echo "备份完成"

这个脚本至少有四个坑:

  1. MySQL连接失败不报错:如果数据库挂了或者密码不对,mysqldump返回非0退出码,但脚本继续执行,gzip压缩了一个空文件,最后还打印"备份完成"。

  2. 目录不存在不检查$BACKUP_DIR如果不存在,重定向创建文件也会失败。

  3. 没有pipefail:如果mysqldump通过管道传给gzip(mysqldump ... | gzip > file.gz),mysqldump失败了gzip可能正常退出,整体退出一码是0。

  4. find -delete没确认:万一某天$BACKUP_DIR变量为空,find / -name "*.gz" -mtime +7 -delete就是灾难。

修复方案就是加错误处理:

#!/bin/bash
set -euo pipefail
BACKUP_DIR=/backup/mysql
DATE=$(date +%Y%m%d)
[[ -d "$BACKUP_DIR" ]] || { echo "备份目录不存在"; exit 1; }
trap "rm -f /tmp/mysql_backup.lock" EXIT
mysqldump -u root --all-databases | gzip > "$BACKUP_DIR/full_$DATE.sql.gz"
[[ ${PIPESTATUS[0]} -eq 0 ]] || { echo "备份失败"; exit 2; }
find "$BACKUP_DIR" -name "*.gz" -mtime +7 -delete
echo "备份完成"

这个对比案例把错误处理、trap、set选项、变量引号、命令退出码检查全部串起来了——Shell脚本的含金量不在能写出多复杂的逻辑,而在异常路径处理得有多严谨。这也是综合实验题里可能会出现的考点:给你一段有bug的脚本,让你找出来并修复。

十、Zabbix:你看不见的东西才最危险

前面九章从PXE装机到Shell脚本,搭建了整个基础设施层和应用层。但所有这些服务上线之后,有一个灵魂拷问你必须回答:你怎么知道它们还在正常工作?

靠用户报故障吗?那太晚了。靠SSH上去手动topfreedf一条条敲吗?一台机器行,一堆机器你敲得过来吗。这就是KYCP把Zabbix放在倒数第三章的原因——它不是等你搭好所有服务再来事后补票,而是从第一天就应该跑起来的"第三只眼"。

Zabbix架构:Server + Agent + Proxy 三层体系

Zabbix的核心架构分三层,每一个角色都有清晰的定位:

Zabbix Server:大脑。负责收集所有被监控端的数据、执行触发器逻辑、发送告警通知、存储历史和趋势数据。Server端自带Web前端(基于PHP),课程里会从dnf install zabbix-server-mysql zabbix-web-mysql开始一路配到浏览器打开Dashboard。

Zabbix Agent:手脚。装在被监控的每台机器上,负责采集本机数据(CPU、内存、磁盘、网卡、进程等),通过被动检查(Server来拉)或主动检查(Agent自己推)把数据送给Server。Agent用10050端口,配置极其简单:Server=<Zabbix Server IP>指定谁来拉数据就行。

Zabbix Proxy:中继。大型分布式环境里的标配——比如A城市、B城市各有一个机房,机房间网络不稳定,如果每个Agent都直连总部的Server,网络抖动会导致大量数据丢失。Proxy部署在各地机房本地,它先收集本区域所有Agent的数据再批量转发给Server,即使总部到分支的网络断了,本地数据不会丢。考试不一定考Proxy的部署,但架构理解是必考点。

这里有个容易被忽略的认知:Zabbix Agent主动检查和被动检查的区别。被动检查模式下,Server定期去Agent上拉数据,适合小规模环境,网络开销可控。主动检查模式下,Agent自己采集完推给Server,减轻Server轮询压力,适合大规模环境。但主动检查的间隔依赖于Agent的配置而不是Server端的监控项设置——考试里可能会出现"为什么配置了30秒间隔但实际上每60秒才采集一次"的坑,答案就是没看Agent用的是主动还是被动模式。

安装部署:从零到看到Dashboard

Zabbix的安装链路比前面Nginx、MySQL要长一些,因为它涉及多个组件协同:

  1. 安装Zabbix仓库:麒麟V11上推荐用Zabbix官方源,课件基于Zabbix 6.0 LTS版本。rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/8/x86_64/zabbix-release-6.0-4.el8.noarch.rpm

  2. 安装Server和Agentdnf install zabbix-server-mysql zabbix-web-mysql zabbix-agent

  3. 创建并初始化数据库:要手动建库建用户导入schema——zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix

  4. 配置zabbix_server.conf:核心参数就一个DBPassword=填上数据库密码

  5. 配置PHP时区/etc/php.ini里的date.timezone = Asia/Shanghai,这个不配Web安装向导第一步就报红

  6. 启动服务并访问Web前端systemctl start zabbix-server zabbix-agent httpd,浏览器打开http://<IP>/zabbix

七八个步骤走下来,任何一个环节配错都会卡在Web安装向导的"Check of pre-requisites"页面上全是红色Fail。课堂上做实验的时候,最快的学员可能十分钟走完,慢的可能半小时还在跟PHP扩展缺失较劲——讲师的经验就是提前把依赖包一次性装全:dnf install zabbix-server-mysql zabbix-web-mysql zabbix-agent mariadb-server php php-mysqlnd php-ldap php-bcmath php-mbstring php-gd php-xml

监控项(Item)与触发器(Trigger):Zabbix的"采集与判断"

监控项和触发器是Zabbix最核心的两个抽象,搞清楚它们的关系才算入门了:

监控项(Item):定义"采集什么数据"。每个Item有一个唯一的Key(键值),比如system.cpu.load[all,avg1]采集CPU 1分钟平均负载、vm.memory.size[available]采集可用内存、net.if.in[eth0]采集网卡入站流量。Item的配置包括:更新间隔(多久采集一次)、历史保留时间(原始数据存多久)、趋势保留时间(聚合数据存多久)、值类型(数值/字符/文本/日志)。

触发器(Trigger):定义"什么条件下报警"。触发器基于一个或多个监控项的值,用表达式来判断是否触发。比如:{host:system.cpu.load[all,avg1].last()}>5——当CPU最近一次采样的1分钟负载超过5时触发。触发器的严重级别分五个等级:未分类、信息、警告、一般严重、严重、灾难——生产环境里数据库的主从同步断了就该配"灾难"级别,微信短信全渠道轰炸那种。

这里有个实操要点:触发器的表达式语法是Zabbix考试的重要得分点。它有自己的函数体系,last()取最新值、avg(#5)取最近5次平均值、min(#3)取最近3次最小值——不会用这些函数,你写的告警要么误报满天飞,要么该报不报。比如CPU负载,如果用last()>5判断,一个瞬时峰值就能把告警触了;换成avg(#5)>5——最近5次采样的平均值超过5才告警,瞬间打消90%的误报。

告警与动作(Action):从"看到了"到"通知到"

触发器触发了只是Zabbix Server自己知道,还没有通知到人。告警(Action)就是把触发事件转成通知消息的最后一公里。

Action配置的核心三要素:

条件(Conditions):什么情况下触发这个Action?可以按触发器名称、主机组、严重级别等维度过滤。比如数据库主机组的严重级别告警走一条Action,普通测试机组的警告级别走另一条。

操作(Operations):告警发给谁、通过什么媒介发。Zabbix内建的媒介类型包括:邮件、短信、企业微信/钉钉/飞书的Webhook、自定义脚本。邮件是最简单的方案,配置下SMTP服务器和收件人就行。但实际企业里更常用的是Webhook对接企业微信机器人——比邮件快、比短信省钱。课程里讲基础配置,我在课堂上会顺带演示一遍企业微信告警的配置流程,学员自己回去照葫芦画瓢。

恢复和升级(Recovery & Escalation):告警触发了,问题修复后你用不用手动关闭?配好Recovery操作,Zabbix检测到触发器恢复正常后自动发一条"已恢复"消息,不用你手动关。升级机制更实用——严重告警1分钟内没被确认,自动升级到电话通知;5分钟没被确认,升级到值班经理。这个在企业里是硬需求,也是考试里Action配制的加分项。

自定义监控(UserParameter):唯一真正值钱的东西

官方模板给了你CPU、内存、磁盘、网络这些通用监控——有用,但不够。一个MySQL DBA真正关心的是:当前QPS多少、InnoDB缓冲池命中率多少、连接数是不是快满了、主从延迟秒数多少。这些官方模板不提供,你得自己写。

UserParameter就是干这个的。配置写在Agent端的/etc/zabbix/zabbix_agentd.conf(多个自定义参数也可以放/etc/zabbix/zabbix_agentd.d/目录下独立conf文件):

UserParameter=mysql.qps,mysql -uzabbix -p密码 -e "SHOW GLOBAL STATUS LIKE 'Queries'" 2>/dev/null | tail -1 | awk '{print $2}'
UserParameter=mysql.innodb_buffer_pool_hit_rate,mysql -uzabbix -p密码 -e "SHOW GLOBAL STATUS" 2>/dev/null | awk '/Innodb_buffer_pool_read_requests/{read=$2} /Innodb_buffer_pool_reads/{phys=$2} END{if(read>0) print (read-phys)/read*100; else print 0}'
UserParameter=mysql.connections,mysql -uzabbix -p密码 -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'" 2>/dev/null | tail -1 | awk '{print $2}'

写完之后在Server端创建对应的监控项,Key填你定义的mysql.qps,Zabbix就会在每次采集间隔时远程执行这个Shell命令拿返回值。

这里有几个实操要点得说清楚:

第一,安全隔离。密码写在配置文件里是安全隐患,生产环境里应该给Zabbix Agent单独跑脚本的权限,用sudo配合--with-libpcre或者把脚本放特定目录限制AllowRoot=0。更规范的做法是给Agent一个独立的mysql监控账号,只给PROCESSSELECT最小权限,密码放到~/.my.cnf里设600权限。

第二,返回值规范。UserParameter的脚本必须往stdout输出一个干净的值——一个数字或字符串。你不能加echo "当前QPS是: 1234"这种前缀,Zabbix Server只认数字本身。多余的输出会导致数据类型错乱。

第三,超时控制。默认Agent侧执行脚本的超时只有3秒。如果你的采集脚本要连MySQL跑复杂查询,可能超过这个时间。在zabbix_agentd.conf里加大Timeout=10。但别设太大——超时过长会导致一个卡住的采集阻塞整个Agent的调度队列,所有监控项都跟着瘫痪。

我课堂上必讲的案例:学员写UserParameter采集MySQL的数据,Server端配置好了但一直拿不到值。排查链路是:Server端看Latest Data是不是灰色→Agent端手动执行脚本能不能正常输出→Agent日志里有没有报权限错误。99%的情况是Agent执行脚本的用户(zabbix)没有访问MySQL的权限。

模板、主机组与自动发现:规模化管理的引擎

当你只有一台数据库服务器时,手动给它加20个监控项还行。当你有30台的时候呢?每台手动加一遍,谁受得了。

模板(Template)就是解决这个问题的。把一组监控项、触发器、图形打包成模板,往主机上一套,所有配置自动继承。官方模板库(Template OS Linux)就是最好的例子。你基于它复制一份自己的数据库监控模板,把自定义的MySQL监控项和触发器全加进去,下次新加一台MySQL服务器,两步搞定:加主机→套模板。模板里改了监控项阈值参数,所有引用该模板的主机自动生效——这就是"改一次,全生效"的工程价值。

主机组(Host Group)按业务维度分类:数据库服务器一个组、应用服务器一个组、中间件一个组。Action里可以用主机组做告警过滤,"数据库组的严重告警发短信,应用组的警告只发邮件"。

自动发现(Auto Discovery)和自动注册(Auto Registration)是规模化运维的终极武器。自动发现是Server端主动扫描指定IP范围,发现新主机自动添加。自动注册是Agent装好后主动向Server报告"我在这里",Server根据元数据自动归类。PXE批量装完100台机器,如果每台手动加进Zabbix…你得加半天。开自动注册,Agent一装自动上线,同时套上预设模板——这是在企业里真正体现KYCP价值的地方。

图形与仪表盘:让数据会说话

Zabbix的图形(Graph)和仪表盘(Dashboard)功能,用好了比一堆告警邮件管用得多。

简单图形:一个Item的趋势图,比如磁盘使用率过去7天的变化曲线。看曲线斜度就知道什么时候该扩容。

聚合图形:多个Item并排展示。一个屏幕同时看到所有数据库节点的CPU、内存、IO、连接数——出问题不用切来切去,一眼就能看出哪个节点异常、什么指标异常。

Screen和Slide Show:把多个图形组合成一个屏幕布局,支持轮播。运维大屏上投的就是这个——大老板路过看了一眼"嗯不错全是绿的",这就是稳定性的直观体现。

麒麟V11适配要点

  • 数据库选型:Zabbix Server后端支持MySQL或PostgreSQL,课件默认MySQL。麒麟V11上装MariaDB还是MySQL,注意版本兼容性——Zabbix 6.0要求MySQL 8.0 或 MariaDB 10.5+。

  • SELinux:默认会拦截Agent访问某些系统文件(如/proc下一些敏感条目),如果自定义监控项采集数据总返回空,优先检查SELinux是否拦截:grep zabbix_agent /var/log/audit/audit.log | grep denied。不建议关SELinux,应该用audit2allow生成策略放行。

  • Firewalld:Server端开10051,Agent端开10050。注意是tcp。如果装了Proxy,Proxy端两侧端口都要开。

  • PHP时区:麒麟V11的/etc/php.inidate.timezone默认可能是注释的,必须手动配置。这个不改,Web前端直接报"Time zone for PHP is not set"。

  • 多架构:Zabbix官方提供x86_64和AArch64的RPM包,龙芯版本可能需要从源码编译。麒麟V11的AArch64(鲲鹏/飞腾)可以直接用官方AArch64仓库。

实战排障参考:三个高频翻车场景

场景一:Agent采集不到数据

Server界面里监控项的"最新数据"显示灰色(not supported)。排查链路:第一步看Agent端zabbix_agentd -t <key>手动测试能否返回数据;第二步看Agent日志/var/log/zabbix/zabbix_agentd.log有没有权限错误;第三步看Server端/var/log/zabbix/zabbix_server.log有没有连接错误;第四步检查防火墙和SELinux。

场景二:自定义脚本输出含多余换行或空格

这是最隐蔽的坑。Zabbix Server对Item的值类型校验很严格——如果你定义的是"数值(无符号)",但脚本输出是"1234\n"(带换行符),Server端会报"Value should be a number"。用echo -n或者tr -d '\n'把输出清理干净。这个坑我在课堂上讲到UserParameter一定会重点预警。

场景三:告警风暴

一个核心交换机挂了,导致100台服务器的Zabbix Agent全不可达,一瞬间100条告警涌过来。这就是告警风暴——告警本身变成了灾难。对策是配置Action里的"操作条件"加间隔限制:同一条告警最少间隔多久才重复发送。另一个办法是用触发器依赖(Trigger Dependency)——100台服务器的Agent不可达告警,全部依赖于"核心交换机Ping不可达"那个触发器。主触发器触发后,所有依赖它的触发器自动抑制——你只收到一条告警,但所有相关信息在详情里都能看到。

十一、Ansible:从手工到编排的飞跃

前面十章从PXE装机搭到Zabbix监控,所有东西都是单点操作的——你配DNS要SSH到DNS服务器上改named.conf,配Nginx要SSH到Nginx服务器上改nginx.conf。一次两台还行,一百台呢?Ansible就是来解决这个问题的——让你在一台控制节点上,一条命令同时管理成百上千台服务器。

Ansible凭什么不需要装Agent?

Ansible最核心的设计理念:无Agent。控制节点通过SSH连接被控端,把Python模块推过去执行,执行完了清理干净。被控端不装任何Ansible软件、不留任何长驻进程——这对数据库服务器这种对资源敏感的环境是巨大优势。你不需要担心Agent进程吃内存、Agent版本不兼容、Agent自己出bug导致监控数据丢失。

对比一下同类工具:Puppet和SaltStack都需要在被控端装Agent,Zabbix也需要Agent采集数据。Ansible只需要被控端有Python和SSH就够了——而这两样麒麟系统默认就有。

工作流程拆解一下:

  1. 控制节点读取inventory清单,找到要操作的目标主机

  2. 控制节点把模块代码(Python脚本)通过SSH推送到目标主机

  3. 目标主机在~/.ansible/tmp/下执行模块代码

  4. 执行完毕后返回JSON结果给控制节点

  5. 清理临时文件

你看这个过程,其实Ansible就是"一台SSH批量操作工具+一套模块化任务库+一种声明式编排语法(YAML)"的组合。理解了这三点,Ansible就入门了。

环境搭建:控制节点、免密登录、Inventory

控制节点安装:只有控制节点需要装Ansible,被控端不需要。麒麟V11上:dnf install ansible。注意V11可能需要先启用EPEL或PowerTools仓库。验证安装:ansible --version。Ansible在麒麟V11上的版本通常是2.9或更高。

SSH免密登录是前提:Ansible默认通过SSH连接被控端,每次连接如果不配免密,执行一条命令就要输一次密码——那自动化就成了笑话。标准流程:

ssh-keygen -t ed25519                    # 控制节点上生成密钥对
ssh-copy-id root@192.168.1.101           # 把公钥拷到被控端
ansible all -m ping -i '192.168.1.101,'  # 验证连通性

密钥类型选ed25519比RSA更快更安全,麒麟V11的OpenSSH 8.x以上完全支持。我课堂上必做的一个检查:免密配完后先用ssh root@192.168.1.101 'hostname'手动测一次——如果手动SSH都报"Permission denied",Ansible肯定也通不了。在被控端看/var/log/secure日志确认认证失败原因。

Inventory清单的三种写法

最简单的是INI格式静态清单/etc/ansible/hosts

[db_servers]
192.168.1.101
192.168.1.102

[web_servers]
192.168.1.201  ansible_user=admin  ansible_port=2222
192.168.1.202

[all_servers:children]
db_servers
web_servers

支持分组([group_name])、组嵌套(:children后缀)、主机级变量(ansible_user/ansible_port等直接写在主机后面)。YAML格式也可以:

all:
  children:
    db_servers:
      hosts:
        192.168.1.101:
        192.168.1.102:
    web_servers:
      hosts:
        192.168.1.201:
          ansible_user: admin

两种格式等价,习惯哪个用哪个。考试一般用INI格式——简洁,看一眼就知道结构。

Ad-hoc命令:一行搞定批量操作

复杂任务写playbook,简单查询用ad-hoc。Ad-hoc就是命令行直接执行Ansible模块,不需要写文件:

# 批量查看主机名(ping模块实际是测试连通性)
ansible db_servers -m ping

# 批量查看麒麟系统版本
ansible all -m command -a "cat /etc/kylin-release"

# 批量查看内存
ansible all -m shell -a "free -h"

# 批量装软件
ansible web_servers -m dnf -a "name=nginx state=present"

# 批量推文件
ansible db_servers -m copy -a "src=/tmp/my.cnf dest=/etc/my.cnf backup=yes"

# 批量启服务
ansible all -m service -a "name=chronyd state=restarted enabled=yes"

ad-hoc的核心价值在于快速验证和临时操作。你要确认所有节点都能看到LVM卷,三秒钟敲完ansible all -m shell -a "lvs"就知道答案了,比一台台SSH效率高出不知多少倍。

常用模块详解:这七个是基本功

Ansible有几百个模块,但KYCP范围里真正高频使用的就这些:

Ansible有几百个模块,但KYCP范围里真正高频使用的就这些,按功能分几类说:

执行类:

  • command:执行命令,不支持管道/重定向/变量,适合简单命令如hostname

  • shell:执行Shell,支持管道/重定向/变量,适合复杂Shell如ps aux | grep nginx

文件类:

  • copy:拷贝文件,关键参数src/dest/backup/mode,典型场景分发配置文件

  • template:Jinja2模板,关键参数src/dest,按变量动态生成配置

  • file:文件属性管理,关键参数path/state/mode/owner,创建目录/改权限

  • lineinfile:行级修改,关键参数path/regexp/line,追加/修改配置文件某一行

包管理类:

  • dnf/yum:包管理,关键参数name/state(present/latest/absent),批量装软件(V11用dnf)

服务管理类:

  • service:服务管理,关键参数name/state/enabled,批量启停服务

用户管理类:

  • user:用户管理,关键参数name/state/groups/uid,批量创建用户

最重要的概念:幂等性。你用command: useradd zabbix执行第二次会报错"用户已存在"——这是非幂等的。用user: name=zabbix state=present执行第二次,Ansible发现用户已经在就返回绿色ok——这是幂等的。生产环境playbook必须追求幂等性:跑一次和跑一百次结果一致,失败了重跑不会引入额外的副作用。

command vs shell的选择:能用command就别用shell。command模块不经过Shell解释器,所以管道、重定向、$变量都不支持——但它更安全,不会有注入风险。给你一条万能规则:需要用到管道、重定向、通配符、环境变量扩展的时候用shell,否则用command。

Playbook:从"一条命令"到"一本剧本"

Ad-hoc能解决单个操作,但现实中的任务是一组操作的组合:装Nginx→推配置文件→启动服务→验证端口——你不可能每一步都手动敲一条ad-hoc。Playbook就是把一组task组织成"剧本",一次性自动化执行。

YAML语法要点:Playbook是YAML格式,缩进用两个空格(不能用Tab),-表示列表元素,key: value表示键值对。最常犯的错就是缩进不一致或Tab混用——Ansible直接报语法错。

Playbook结构模板

---
- name: 部署Web服务                      # Play名称
  hosts: web_servers                     # 目标主机组
  become: yes                            # 提权到root
  vars:                                  # 定义变量
    nginx_port: 8080
    app_user: webapp
  tasks:                                 # 任务列表
    - name: 安装Nginx
      dnf:
        name: nginx
        state: present
    - name: 推送Nginx配置
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx              # 通知handler
    - name: 启动Nginx
      service:
        name: nginx
        state: started
        enabled: yes
  handlers:                              # 处理器(被notify触发)
    - name: restart nginx
      service:
        name: nginx
        state: restarted

这个Playbook演示了四个核心概念:tasks(任务列表,按顺序执行)、vars(变量定义,{{ nginx_port }}引用)、handlers(仅在被notify时执行,且所有task跑完后集中执行一次)、become(提权)。一个完整的Playbook可以包含多个Play,每个Play可以操作不同的主机组——比如第一个Play针对db_servers装MySQL,第二个Play针对web_servers装Nginx。

变量、条件与循环:让Playbook活起来

变量的三种定义方式

  • Playbook内vars块:最简单,直观

  • Inventory文件里主机/组变量:不同主机用不同值,比如数据库服务器的内存参数

  • ansible-playbook -e "key=value"命令行传参:最灵活,适合CI/CD管道动态注入

变量优先级(从低到高):inventory组变量 → inventory主机变量 → playbook vars → -e命令行传参。-e传参优先级最高,适合覆盖默认值。

条件判断(when):不是所有主机都执行相同操作。麒麟V11的AArch64和x86_64架构,装某些包时版本不一样:

- name: 安装ARM架构专用包
  dnf: name=mariadb-server state=present
  when: ansible_architecture == "aarch64"

when的判断基于Jinja2表达式,常用的:ansible_facts['os_family']ansible_hostname、自定义变量、register注册结果的.rc.stdout判断。

循环:对于重复操作,用loop替代写多个task:

- name: 安装多个包
  dnf:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - mariadb-server
    - chrony
    - zabbix-agent

这样一段task替代了四个独立的dnf task。注意循环变量的引用方式是{{ item }}

register注册:把命令执行的返回值(stdout、rc、stderr)存下来,后续task引用:

- name: 获取nginx状态
  command: systemctl is-active nginx
  register: nginx_status
  ignore_errors: yes
- name: 打印状态
  debug:
    msg: "Nginx状态: {{ nginx_status.stdout }}"

模板(Template):让配置随环境变化

Jinja2模板是Ansible最强大的功能之一。传统方式是你手工写死每台机器的配置文件——Nginx的server_name、MySQL的innodb_buffer_pool_size、Zabbix Agent的Server地址——一百台机器一百份配置,改一处全得重推。

模板解决了这个:配置文件里嵌入变量{{ var_name }},Ansible在执行时用实际值替换。

Nginx模板示例(nginx.conf.j2):

worker_processes {{ ansible_processor_vcpus }};
events {
    worker_connections {{ nginx_max_connections | default(1024) }};
}

ansible_processor_vcpus是Ansible自动采集的fact变量(CPU核心数)——不需要你手动定义,模板直接引用。{{ value | default(1024) }}是Jinja2过滤器,如果变量没定义就用默认值1024——防御性编程的体现。

模板配合for循环动态生成upstream后端列表是最经典的用法:

upstream backend {
{% for host in groups['app_servers'] %}
    server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:8000;
{% endfor %}
}

有了这段模板,你加一台应用服务器只需要在inventory里加一行IP,下次执行playbook自动生成完整的upstream配置——配置文件跟着环境变化自动更新。

角色(Roles)与Vault:从"能跑"到"工程化"

Roles是Ansible的目录级封装。当你写了几十个playbook之后,会发现每个playbook都在重复类似的tasks——装包、推配置、启服务。Roles把相关的tasks、vars、handlers、templates、files按标准目录结构组织成一个可复用的单元:

roles/
  nginx/
    tasks/main.yml       # 主任务
    handlers/main.yml    # 处理器
    templates/           # Jinja2模板
    files/               # 静态文件
    vars/main.yml        # 变量

用的时候一行引入:roles: - nginx。以后每台需要Nginx的机器都复用同一套role,修改一处全部生效。KYCP课件里的综合案例——"编写剧本部署web服务"——用roles组织会让整体结构清晰几个量级。

ansible-vault加密敏感信息。inventory里的密码、SSH私钥、数据库连接串,明文写在YAML里推Git?这是给攻击者送福利。ansible-vault encrypt vars/secrets.yml对文件加密,只有知道密码的人才能解密。执行playbook时用ansible-playbook --ask-vault-pass输入密码自动解密。生产环境的最佳实践:把所有敏感变量放在一个独立的vars/secrets.yml文件里单独加密,普通变量放另一个yaml不加密——加密的文件越小,出问题时排查越快。

生产环境四条铁律

这几条不是书本上的,是踩过坑的教训:

1. --check先模拟,别上来就执行。ansible-playbook site.yml --check做干运行——Ansible会报告哪些task会改变,但不会实际执行。确认无误再摘掉--check真实跑。尤其对于dnf install这种会改系统状态的操作,强推先check。

2. --diff看具体变化。 如果--check说会改某个文件,但你不知道改成什么样,加--diff参数就能看到修改前后的diff。推配置文件改错了一个参数,diff里一眼就能发现。

3. serial控制滚动更新。serial: 1表示每次只在一台机器上执行——一台做完task再下台。数据库集群做滚动升级必须这样:serial: 1一轮一台,停主库前先切到从库,确保服务不中断。如果你不理解serial的意义,想象一下hosts: db_servers包含三台MySQL主库,不加serial等于同时停三台——你就等着钉钉炸吧。

4. ansible-vault加密一切敏感信息。 数据库密码、API密钥、SSL证书私钥——绝对不能明文出现在playbook里。哪怕只是内部测试环境,也要养成vault的习惯。我在课堂上演示过:不加密的playbook推内网GitLab,一周后密码被谁看见了你都不知道。加密之后,除了你没人能解密,安全性提升一个维度。

麒麟V11适配要点

  • Python版本:Ansible依赖Python 3,麒麟V11自带Python 3.9+,完全满足要求。Ansible控制节点需要Python 3.8+。

  • dnf替代yum:V11全面使用dnf,写playbook时用dnf模块而非yum模块(虽然yum模块向后兼容但也建议统一用dnf)。

  • Firewalld:如果被控端开了防火墙,确保SSH端口(默认22)在Ansible控制的网段内放开。

  • SELinux:Ansible推的文件如果放在非标路径(比如/opt/app/config/),SELinux可能阻止服务读取。用file模块设seuser/serole/setype参数,或者提前配好SELinux策略。

  • Python解释器路径:某些ARM架构麒麟V11上/usr/bin/python可能不存在(只有/usr/bin/python3)。在inventory里设ansible_python_interpreter=/usr/bin/python3显式指定解释器。

实战排障参考

场景一:SSH连通性是所有问题的根

课堂实验里最常见的翻车:学员配好免密、写好playbook,一跑全红,UNREACHABLE!。排查三板斧:ssh root@被控端IP能不能直接连上?被控端/var/log/secure有没有Accepted publickey记录?ssh-copy-id是不是拷错用户了?麒麟V11默认/etc/ssh/sshd_configPubkeyAuthentication yes一般默认开启,但PasswordAuthentication可能被安全策略禁了——所以一定要先确认免密通不通再写playbook。

场景二:dnf install报"No package available"

麒麟V11上有些包不在默认仓库里,需要在inventory里或者playbook里先执行dnf install epel-release -y或者启用PowerTools。另一种可能是ARM架构下某些包的名称和x86_64不一样——比如x86_64上叫mariadb-server,AArch64上可能叫mariadb-server.aarch64。用dnf list available | grep 包名确认可用包名。

场景三:SSH升级导致批量推送失败

麒麟交付知识库记录了一个典型案例:SSH版本升级时遇到依赖冲突,原因是历史版本的合包和后续拆包版本冲突,默认包管理器尝试装最高版本但依赖不满足。解决方法是手动下载RPM包用dnf localinstall本地安装。这个案例的核心启示是:Ansible完全依赖SSH,SSH一出问题整条链路全断。做Ansible实验前先确保所有被控端SSH版本一致、免密配置到位。

十二、Docker:从虚拟机到容器的运维思维跃迁

Docker放在最后一章,我觉得编排得很巧妙——前面的PXE装好系统、NFS和iSCSI配好存储、Nginx和MySQL搭好服务、Ansible搞定自动化,最后用Docker把这一切容器化,串联成一条完整的"从裸机到容器"的技术链路。更重要的是,它逼迫学员完成一次思维模式的转换:从"这台机器上配了哪些服务"变成"我用什么镜像、挂什么卷、开什么端口、配什么网络"。

Docker核心三要素:镜像、容器、仓库

课程一上来先讲三个核心概念,但讲法跟网上那些"镜像是模板、容器是实例"的空洞比喻不一样,而是从文件系统的底层视角拆解。

镜像(Image)是一组只读层的堆叠。你用docker pull mysql:8.0拉下来的不是一个文件,而是一叠layer——最底层是系统基础层(比如麒麟V11 base image),往上每一层对应Dockerfile里的一条指令。联合文件系统(OverlayFS)把这些只读层合并成一个"看起来完整"的文件系统。理解了这个模型,你就能搞明白两个关键问题:为什么docker pull有时候只要几十KB(本地已有底层layer),以及为什么同一个基础镜像派生的应用镜像占用空间很小(共享底层layer)。

容器(Container)本质上是镜像上面加了一层可写层(Container Layer)。容器运行时对文件系统的任何修改——写日志、改配置、产生临时文件——都落在这层可写层上。容器删了,可写层就没了。这个机制决定了容器的一个核心设计哲学:容器应该是无状态的,持久化数据必须靠数据卷。

仓库(Registry)是镜像的集中存储和分发中心。课上讲的是Docker Hub公共仓库和私有Registry的搭建方式。在企业里,由于安全合规要求,信创项目通常不直接访问公网Docker Hub,需要在内部搭建Harbor或麒麟仓库做镜像中转。

安装部署与基础操作

麒麟V11上装Docker走官方仓库或麒麟应用商店都行。几个必须掌握的启动后检查动作:

# 确认Docker服务状态
systemctl status docker

# 验证Docker能正常工作
docker run hello-world

# 查看Docker系统信息(Storage Driver/Logging Driver/Cgroup Driver等)
docker info

这个docker info的输出信息量很大——Storage Driver是overlay2还是devicemapper、Cgroup Driver是systemd还是cgroupfs、Docker Root Dir在哪个分区——这些在生产环境排障时都是第一手信息。课堂实验我会让学员先跑一遍docker info,养成"看一眼环境再动手"的习惯。

课程实操覆盖了完整的镜像和容器生命周期管理:

# 镜像管理
docker search nginx          # 搜索镜像
docker pull nginx:1.25       # 拉取指定版本(不写tag默认latest,生产环境大忌)
docker images                # 查看本地镜像(REPOSITORY/TAG/IMAGE ID/SIZE)
docker rmi <image_id>        # 删除镜像
docker save -o nginx.tar nginx:1.25   # 导出镜像为tar包(离线环境必备)
docker load -i nginx.tar     # 从tar包导入镜像

# 容器管理
docker run -d --name web -p 8080:80 nginx:1.25   # 后台运行+命名+端口映射
docker ps -a                 # 查看所有容器(包括已停止的)
docker stop web && docker rm web   # 停止并删除
docker logs -f web           # 实时查看日志
docker exec -it web /bin/bash    # 进入容器内部调试
docker inspect web           # 查看容器完整元数据(Mounts/NetworkSettings/Env等)

Docker网络:bridge/host/none三种模式

Docker网络是实操中的重头戏,课程覆盖了四种网络模式。

bridge桥接模式(默认):Docker在宿主机上创建一个虚拟网桥docker0,每个容器分配一个虚拟网卡连接到docker0,通过NAT访问外网。端口映射(-p 8080:80)本质是在宿主机iptables里加了DNAT规则。bridge模式的局限是跨宿主机的容器无法直接通信,单机部署够用。

host模式:容器直接使用宿主机网络栈,没有网络隔离,-p参数无效。性能最高但端口冲突风险大,适用于对网络延迟敏感的服务。

none模式:容器只有lo回环网卡,没有外网访问能力,一般用于安全敏感的计算任务。

自定义网络(user-defined bridge):这才是企业实践里最常用的模式。通过docker network create创建自定义网络后,同一网络内的容器可以通过容器名互相ping通——Docker内置了DNS服务做服务发现。比如:

docker network create app-net
docker run -d --name mysql --network app-net -e MYSQL_ROOT_PASSWORD=xxx mysql:8.0
docker run -d --name web --network app-net -p 8080:80 nginx:1.25

这时候web容器里ping mysql是可以通的,不用记IP。这在综合实验题里很可能出现:让你配一个自定义网络,然后跑MySQL+Nginx两容器互通。

数据卷:容器持久化的灵魂

前面说了容器的可写层随容器销毁而消失,那数据库文件、配置文件、日志文件怎么办?答案是数据卷(Volume)和绑定挂载(Bind Mount)。

Volume(具名卷)是Docker管理的,存在/var/lib/docker/volumes/下,Docker负责生命周期:

docker volume create mysql-data
docker run -d --name mysql -v mysql-data:/var/lib/mysql mysql:8.0

Bind Mount是把宿主机上的任意目录挂进容器,灵活性更高但路径管理需要人工维护:

docker run -d --name web -v /data/nginx/conf:/etc/nginx/conf.d nginx:1.25

这两个模式的选择有个简单法则:应用产生的持久化数据用Volume,需要频繁修改的配置文件用Bind Mount。比如MySQL的/var/lib/mysql用Volume让Docker自动管理存储路径,Nginx的配置文件用Bind Mount方便你用vi直接改。

数据卷还有个常见坑:MySQL容器第一次启动时,如果挂载了一个空Volume,MySQL会执行初始化脚本创建系统表空间;但如果挂了一个已有数据的Volume,它会直接使用已有数据。这个行为在做数据库迁移和备份恢复的时候容易被忽略——你把一个旧Volume挂到新的MySQL容器上,密码不一致就直接起不来了。

Dockerfile:镜像即代码

Dockerfile是容器化里最有工程价值的技能——把环境依赖、软件安装、配置调整全部写成代码,真正实现"环境即代码"。

课程覆盖了Dockerfile的核心指令,我按执行顺序梳理:

FROM kylin-v11-base:latest          # 基础镜像(FROM必须是第一条指令)
LABEL maintainer="dba@example.com"  # 元数据标注
ENV MYSQL_VERSION=8.0.35           # 环境变量(构建时和运行时都可用)
RUN dnf install -y mysql-server-${MYSQL_VERSION} \    # 构建时执行
    && dnf clean all
COPY my.cnf /etc/my.cnf             # 从构建上下文复制文件进镜像
ADD mysql-init.tar.gz /docker-entrypoint-initdb.d/  # 复制并自动解压
WORKDIR /data/mysql                  # 设置工作目录
EXPOSE 3306                          # 声明容器监听端口(仅文档作用,不实际发布)
VOLUME ["/var/lib/mysql"]           # 声明数据卷挂载点
CMD ["mysqld"]                       # 容器启动时的默认命令(可以被docker run覆盖)
ENTRYPOINT ["docker-entrypoint.sh"]  # 入口点(不能被覆盖,CMD作为参数传入)

几条高频踩坑点:

  • RUN vs CMD:RUN在docker build时执行、结果写入镜像层;CMD在docker run时执行、结果存在容器层。搞混了就会把运行时的操作写进RUN导致镜像体积暴增。

  • 每一条RUN产生一个新layer,所以要把多条命令用&&串成一条RUN(比如dnf install && dnf clean all),减少镜像层数。

  • COPY vs ADD:一般用COPY就够了,ADD只在需要自动解压tar包时使用。不推荐ADD远程URL——缓存失效问题严重。

  • EXPOSE不实际发布端口docker run -P会为所有EXPOSE的端口随机映射,但生产环境用-p显式指定更可靠。

课程最后的Dockerfile练习是"编写一个运行Python Web应用的Dockerfile并构建运行",这就是把前面学的FROM/RUN/COPY/WORKDIR/EXPOSE/CMD全串起来,和综合实验题出题思路一致。

麒麟V11上的Docker适配要点

信创环境下跑Docker有几个要注意的差异:

架构适配:麒麟V11支持AArch64(鲲鹏/飞腾)、x86_64、LoongArch64三种架构。docker pull会自动匹配当前CPU架构的镜像,但前提是镜像仓库里有对应架构的版本。如果你自己在x86上docker build了一个镜像,直接推到ARM机器上是跑不起来的——需要做多架构构建(docker buildx)或者在目标架构上重新构建。

存储驱动:麒麟V11默认用overlay2作为Docker存储驱动,这是目前性能最好的选择。用docker info | grep "Storage Driver"确认。如果发现是devicemapper,要检查内核是否支持overlay——Linux 6.6内核完全支持,但某些定制内核可能没编译相关模块。

Cgroup兼容性docker info确认Cgroup Driver和宿主机systemd一致(都是systemd),否则容器资源限制可能不生效。不一致时改/etc/docker/daemon.json

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "storage-driver": "overlay2"
}

改完systemctl restart docker生效。

SELinux与Firewalld:如果启用了SELinux,Bind Mount需要在目录上设置正确的SELinux上下文(:Z:z参数)。Firewalld需要放行Docker暴露的端口——但要注意,Docker会绕过firewalld直接在iptables里加规则,所以firewall-cmd --list-ports看不到不代表没生效。

实战排障参考

场景一:容器启动立即退出

docker run -d --name test mysql:8.0
docker ps    # 看不到test容器
docker ps -a  # 状态是Exited(1)

排查链路:docker logs test看退出日志,80%的情况是环境变量没配或配置文件有语法错误。MySQL容器必须配MYSQL_ROOT_PASSWORD环境变量,这是最常见的启动失败原因。

场景二:容器间网络不通

两个容器在默认bridge网络下无法通过容器名互访——这是默认bridge和自定义bridge的核心区别。默认bridge只能用IP互访,自定义bridge才支持容器名DNS解析。排查:docker network inspect bridge看是否在同一网络,docker network create创建自定义网络后重新连接。

场景三:数据卷权限问题

Bind Mount的目录在宿主机上属主是root:root,容器内mysql用户(uid=999)写不进去,导致MySQL启动失败。解决方式:chown -R 999:999 /data/mysql,或者在Dockerfile里用USER指令切换运行用户。

场景四:镜像拉取慢或失败

信创环境一般不能直接连Docker Hub。解决方式:配国内镜像加速器(/etc/docker/daemon.json里加registry-mirrors),或者在内网搭建Harbor私有Registry,用docker pull harbor.internal.com/library/mysql:8.0走内网拉取。

2026年8月考试改革:3天变5天,实操才是王道

两个变化必须重视:

第一,时长从3天拉到5天。 这不是简单的时间翻倍——多出来的两天全给了实操。以前讲完一个模块可能就留十分钟让你敲两条命令感受一下,现在每个模块后面都有完整的实验时间,最后还有综合项目演练。

第二,考试新增综合实验题。 以前单选多选判断,刷题还能混过去。现在要你在模拟麒麟环境里完成一组复合型任务——比如"通过PXE完成系统安装,配置DNS解析,部署Nginx反向代理,编写Ansible playbook实现批量部署"。一条链路串下来,哪个环节卡住都丢分。

这两点加起来意味着什么?KYCP的含金量在提高。对企业来说,招一个持证的人,至少能确认他不是纸上谈兵。对学员来说,也别把它当成"背题库就能过"的证书了。

图片

学完KYCP,你能拿到什么价值

说了这么多技术细节,最后聊聊学完这套东西到底值不值。

第一,信创项目的入场券。 国产化替代不是口号,是实打实的项目需求。银行、电信、政务、能源这些行业,信创操作系统是底座,KYCP认证是这块底座上的能力背书。持证至少证明你不是纸上谈兵,PXE装机、DHCP配网、DNS解析、NTP同步、Nginx网关、MySQL数据库、NFS共享、iSCSI存储、Shell自动化、Zabbix监控、Ansible编排、Docker容器化——这十二项技能你亲手搭过、排过错、知道坑在哪。

第二,从"会操作"到"懂原理"。 很多运维人员会敲命令,但不懂背后的协议交互和设计原理。DHCP的DORA四步、DNS的递归迭代、NFS的RPC机制、iSCSI的SCSI over IP——KYCP课程不是教你背命令,是让你理解"为什么这样配"。理解了why,排障的时候才有方向感。

第三,综合实验能力。 2026年8月考试改革后,综合实验题成为考核维度。这意味着你必须能把多个模块串起来——PXE装好系统、配好DNS、部署Nginx、写Ansible playbook批量部署、用Zabbix监控起来、最后用Docker容器化。这种复合型能力,在企业项目里就是"能独当一面"的标准。

第四,信创生态的系统性认知。 麒麟V11基于openEuler,跟传统CentOS/RHEL有差异。NetworkManager接管网络、Chrony替代ntpd、systemd-resolved管理DNS缓存、SELinux和KSAF安全框架——这些差异不是"换个命令"那么简单,是底层架构和设计理念的变化。KYCP课程让你建立对国产操作系统生态的系统性认知,而不是"把CentOS的命令搬到麒麟上试试"。

第五,持续学习的起点。 KYCP不是终点,是起点。操作系统运维这条路,PXE、DHCP、DNS、NTP、Nginx、MySQL、NFS、iSCSI、Shell、Zabbix、Ansible、Docker——这十二项技能吃透了,你才有资格往更高阶的方向走:Kubernetes编排、数据库内核调优、分布式存储、云原生架构。底座稳了,上面盖什么楼都行。

总结

这次麒麟原厂赋能最大的收获,不是学了多少新知识——这些技术栈平时都在用——而是搞清楚了每个模块的教学重点、实验设计和考试评分逻辑。回去讲课的时候,哪些环节要多练、哪些坑要提前预警、哪些知识点在综合实验题里会串联考察,心里更有底了。

如果你在准备KYCP,我的建议很简单:把每个模块在实验环境里亲手搭一遍。从PXE装机到DNS解析、从NFS存储到Ansible自动化、从MySQL数据库到Docker容器化——考试过了拿的是证,实操能力才是你在信创项目里安身立命的本钱。

操作系统是信创的底座,KYCP是这块底座上的能力认证。底座稳了,国产数据库、国产中间件、国产应用才能跑稳。这条路,值得走。

今天话题就聊到这,欢迎留言交流。觉得内容有用,别忘了点赞转发给有需要的朋友,回见!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐