Chemmy's Blog

chengming0916@outlook.com

1. 打开配置文件目录

进入MSYS2安装目录,例如C:\msys64,打开路径 C:\msys64\etc\pacman.d.

2. 修改镜像源文件

在该目录下找到以下文件,并分别进行修改:

修改 mirrorlist.msys

将以下内容添加到文件顶部:

1
2
3
4
5
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch

Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch

Server = https://mirrors.aliyun.com/msys2/msys/$arch

修改 mirrorlist.mingw32

1
2
3
4
5
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686

Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686

Server = https://mirrors.aliyun.com/msys2/mingw/i686

修改 mirrorlist.mingw64

1
2
3
4
5
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64

Server = http://mirrors.ustc.edu.cn/msys2/mingw/x86_64

Server = https://mirrors.aliyun.com/msys2/mingw/x86_64

(可选)修改其他文件

如果需要支持 ucrt64 或 _clang_,可参考以下示例

1
2
3
4
5
# mirrorlist.ucrt64
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/ucrt64
# mirrorlist.clang64
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/clang64

3. 更新MSYS2

完成上述修改后,运行以下命令更新系统:

1
pacman -Syu

注意事项

  • 确保每个文件的顶部添加了正确的镜像地址。
  • 如果更新过程中遇到问题,可尝试删除 .git 文件夹后重新初始化。
    通过以上步骤,MSYS2 的更新速度将显著提升。

Systemd 服务管理

Systemd 概述

Systemd 简介

Systemd 是一系列工具的集合,其作用也远远不仅是启动操作系统,它还接管了后台服务、结束、状态查询,以及日志归档、设备管理、电源管理、定时任务等许多职责,并支持通过特定事件(如插入特定 USB 设备)和特定端口数据触发的 On-demand(按需)任务。

Systemd 的后台服务还有一个特殊的身份——它是系统中 PID 值为 1 的进程。

  1. 更少的进程

Systemd 提供了服务按需启动的能力,使得特定的服务只有在真正被请求时才启动。

  1. 允许更多的进程并行启动

在 SysV-init 时代,将每个服务项目编号依次执行启动脚本。Ubuntu 的 Upstart 解决了没有直接依赖的启动之间的并行启动。而 Systemd 通过 Socket 缓存、DBus 缓存和建立临时挂载点等方法进一步解决了启动进程之间的依赖,做到了所有系统服务并发启动。对于用户自定义的服务,Systemd 允许配置其启动依赖项目,从而确保服务按必要的顺序运行。

  1. 使用 CGroup 跟踪和管理进程的生命周期

在 Systemd 之前的主流应用管理服务都是使用进程树来跟踪应用的继承关系的,而进程的父子关系很容易通过两次 fork 的方法脱离。

而 Systemd 则提供通过 CGroup 跟踪进程关系,弥补了这个缺漏。通过 CGroup 不仅能够实现服务之间访问隔离,限制特定应用程序对系统资源的访问配额,还能更精确地管理服务的生命周期。

  1. 统一管理服务日志

Systemd 是一系列工具的集合,包括了一个专用的系统日志管理服务:Journald。这个服务的设计初衷是克服现有 Syslog 服务的日志内容易伪造和日志格式不统一等缺点,Journald 用二进制格式保存所有的日志信息,因而日志内容很难被手工伪造。Journald 还提供了一个 journalctl 命令来查看日志信息,这样就使得不同服务输出的日志具有相同的排版格式,便于数据的二次处理。

Systemd 架构

![[Systemd服务管理/IMG-20260205160657597.png]]

Systemd 的 Unit 文件

Systemd 可以管理所有系统资源,不同的资源统称为 Unit(单位)。

在 Systemd 的生态圈中,Unit 文件统一了过去各种不同系统资源配置格式,例如服务的启/停、定时任务、设备自动挂载、网络配置、虚拟内存配置等。而 Systemd 通过不同的文件后缀来区分这些配置文件。

Systemd 支持的 12 种 Unit 文件类型

  • .automount:用于控制自动挂载文件系统,相当于 SysV-init 的 autofs 服务
  • .device:对于 /dev 目录下的设备,主要用于定义设备之间的依赖关系
  • .mount:定义系统结构层次中的一个挂载点,可以替代过去的 /etc/fstab 配置文件
  • .path:用于监控指定目录或文件的变化,并触发其它 Unit 运行
  • .scope:这种 Unit 文件不是用户创建的,而是 Systemd 运行时产生的,描述一些系统服务的分组信息
  • .service:封装守护进程的启动、停止、重启和重载操作,是最常见的一种 Unit 文件
  • .slice:用于表示一个 CGroup 的树,通常用户不会自己创建这样的 Unit 文件
  • .snapshot:用于表示一个由 systemctl snapshot 命令创建的 Systemd Units 运行状态快照
  • .socket:监控来自于系统或网络的数据消息,用于实现基于数据自动触发服务启动
  • .swap:定义一个用户做虚拟内存的交换分区
  • .target:用于对 Unit 文件进行逻辑分组,引导其它 Unit 的执行。它替代了 SysV-init 运行级别的作用,并提供更灵活的基于特定设备事件的启动方式
  • .timer:用于配置在特定时间触发的任务,替代了 Crontab 的功能

Systemd 目录

Unit 文件按照 Systemd 约定,应该被放置指定的三个系统目录之一中。这三个目录是有优先级的,如下所示,越靠上的优先级越高。因此,在三个目录中有同名文件的时候,只有优先级最高的目录里的那个文件会被使用。

  • /etc/systemd/system:系统或用户自定义的配置文件
  • /run/systemd/system:软件运行时生成的配置文件
  • /usr/lib/systemd/system:系统或第三方软件安装时添加的配置文件。
    • CentOS 7:Unit 文件指向该目录
    • ubuntu 16:被移到了 /lib/systemd/system

Systemd 默认从目录 /etc/systemd/system/ 读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录 /usr/lib/systemd/system/,真正的配置文件存放在那个目录。

Unit 和 Target

Unit 是 Systemd 管理系统资源的基本单元,可以认为每个系统资源就是一个 Unit,并使用一个 Unit 文件定义。在 Unit 文件中需要包含相应服务的描述、属性以及需要运行的命令。

Target 是 Systemd 中用于指定系统资源启动组的方式,相当于 SysV-init 中的运行级别。

简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。

Systemd Service Unit

Unit 文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=Hello World
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill busybox1
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop="/usr/bin/docker stop busybox1"
ExecStopPost="/usr/bin/docker rm busybox1"

[Install]
WantedBy=multi-user.target

如下所示,Systemd 服务的 Unit 文件可以分为三个配置区段:

  • Unit 和 Install 段:所有 Unit 文件通用,用于配置服务(或其它系统资源)的描述、依赖和随系统启动的方式
  • Service 段:服务(Service)类型的 Unit 文件(后缀为 .service)特有的,用于定义服务的具体管理和操作方法

Unit 段

  • Description:描述这个 Unit 文件的信息
  • Documentation:指定服务的文档,可以是一个或多个文档的 URL 路径
  • Requires:依赖的其它 Unit 列表,列在其中的 Unit 模板会在这个服务启动时的同时被启动。并且,如果其中任意一个服务启动失败,这个服务也会被终止
  • Wants:与 Requires 相似,但只是在被配置的这个 Unit 启动时,触发启动列出的每个 Unit 模块,而不去考虑这些模板启动是否成功
  • After:与 Requires 相似,但是在后面列出的所有模块全部启动完成以后,才会启动当前的服务
  • Before:与 After 相反,在启动指定的任务一个模块之间,都会首先确证当前服务已经运行
  • Binds To:与 Requires 相似,失败时失败,成功时成功,但是在这些模板中有任意一个出现意外结束或重启时,这个服务也会跟着终止或重启
  • Part Of:一个 Bind To 作用的子集,仅在列出的任务模块失败或重启时,终止或重启当前服务,而不会随列出模板的启动而启动
  • OnFailure:当这个模板启动失败时,就会自动启动列出的每个模块
  • Conflicts:与这个模块有冲突的模块,如果列出的模块中有已经在运行的,这个服务就不能启动,反之亦然

Install 段

这部分配置的目标模块通常是特定运行目标的 .target 文件,用来使得服务在系统启动时自动运行。这个区段可以包含三种启动约束:

  • WantedBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .wants 后缀构成的子目录中,如 “/etc/systemd/system/multi-user.target.wants/“
1
2
3
4
5
6
7
8
9
10
11
# find /etc/systemd/system/* -type d
/etc/systemd/system/default.target.wants
/etc/systemd/system/getty.target.wants
/etc/systemd/system/graphical.target.wants
/etc/systemd/system/multi-user.target.wants
/etc/systemd/system/network-online.target.wants
/etc/systemd/system/paths.target.wants
/etc/systemd/system/shutdown.target.wants
/etc/systemd/system/sockets.target.wants
/etc/systemd/system/sysinit.target.wants
/etc/systemd/system/timers.target.wants
  • RequiredBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .required 后缀构成的子目录中
  • Also:当前 Unit enable/disable 时,同时 enable/disable 的其他 Unit
  • Alias:当前 Unit 可用于启动的别名

SysV-init 运行级别与 Systemd Target 对应的 Unit 文件

SysV-Init Level Systemd Target Note
0 runlevel0.target -> poweroff.target 关闭系统
1 runlevel1.target -> rescue.target 单用户模式
2 runlevel2.target -> multi-user.target 用户定义/域特性运行级别,默认等同于级别3
3 runlevel3.target -> multi-user.target 多用户模式
4 runlevel4.target -> multi-user.target 用户定义/域特定运行级别,默认等同于级别3
5 runlevel5.target -> graphical.target 多用户, 图形模式
6 runlevel6.target -> reboot.target 重启
emergency emergency.target 急救模式

通过 systemctl list-units –type=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
27
# systemctl list-units --type=target
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
cryptsetup.target loaded active active Encrypted Volumes
getty.target loaded active active Login Prompts
graphical.target loaded active active Graphical Interface
local-fs-pre.target loaded active active Local File Systems (Pre)
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.target loaded active active Network
nss-user-lookup.target loaded active active User and Group Name Lookups
paths.target loaded active active Paths
remote-fs-pre.target loaded active active Remote File Systems (Pre)
remote-fs.target loaded active active Remote File Systems
slices.target loaded active active Slices
sockets.target loaded active active Sockets
sound.target loaded active active Sound Card
swap.target loaded active active Swap
sysinit.target loaded active active System Initialization
time-sync.target loaded active active System Time Synchronized
timers.target loaded active active Timers
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
20 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

Service 段

用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。它的主要字段分为服务生命周期和服务上下文配置两个方面。

服务生命周期控制相关

  • Type:定义启动时的进程行为,它有以下几种值:
    • Type=simple:默认值,执行ExecStart指定的命令,启动主进程
    • Type=forking:以 fork 方式从父进程创建子进程,创建后父进程会立即退出
    • Type=oneshot:一次性进程,Systemd 会等当前服务退出,再继续往下执行
    • Type=dbus:当前服务通过D-Bus启动
    • Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行
    • Type=idle:若有其他任务执行完毕,当前服务才会运行
  • RemainAfterExit:值为 true 或 false(默认)。当配置为 true 时,Systemd 只会负责启动服务进程,之后即便服务进程退出了,Systemd 也仍然会认为这个服务还在运行中。这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,然后等待消息按需启动的特殊类型服务使用的。
  • ExecStart:启动当前服务的命令
  • ExecStartPre:启动当前服务之前执行的命令
  • ExecStartPost:启动当前服务之后执行的命令
  • ExecReload:重启当前服务时执行的命令
  • ExecStop:停止当前服务时执行的命令
  • ExecStopPost:停止当其服务之后执行的命令
  • RestartSec:自动重启当前服务间隔的秒数
  • Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括 always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
  • TimeoutStartSec:启动服务时等待的秒数,这一配置对于使用 Docker 容器而言显得尤为重要,因其第一次运行时可能需要下载镜像,严重延时会容易被 Systemd 误判为启动失败杀死。通常,对于这种服务,将此值指定为 0,从而关闭超时检测
  • TimeoutStopSec:停止服务时的等待秒数,如果超过这个时间仍然没有停止,Systemd 会使用 SIGKILL 信号强行杀死服务的进程

服务上下文配置相关

  • Environment:为服务指定环境变量
  • EnvironmentFile:指定加载一个包含服务所需的环境变量的列表的文件,文件中的每一行都是一个环境变量的定义
  • Nice:服务的进程优先级,值越小优先级越高,默认为 0。其中 -20 为最高优先级,19 为最低优先级
  • WorkingDirectory:指定服务的工作目录
  • RootDirectory:指定服务进程的根目录(/ 目录)。如果配置了这个参数,服务将无法访问指定目录以外的任何文件
  • User:指定运行服务的用户
  • Group:指定运行服务的用户组
  • MountFlags:服务的 Mount Namespace 配置,会影响进程上下文中挂载点的信息,即服务是否会继承主机上已有挂载点,以及如果服务运行执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值为 shared、slaved 或 private
    • shared:服务与主机共用一个 Mount Namespace,继承主机挂载点,且服务挂载或卸载设备会真实地反映到主机上
    • slave:服务使用独立的 Mount Namespace,它会继承主机挂载点,但服务对挂载点的操作只有在自己的 Namespace 内生效,不会反映到主机上
    • private:服务使用独立的 Mount Namespace,它在启动时没有任何任何挂载点,服务对挂载点的操作也不会反映到主机上
  • LimitCPU / LimitSTACK / LimitNOFILE / LimitNPROC 等:限制特定服务的系统资源量,例如 CPU、程序堆栈、文件句柄数量、子进程数量等

注意:如果在 ExecStart、ExecStop 等属性中使用了 Linux 命令,则必须要写出完整的绝对路径。对于 ExecStartPre 和 ExecStartPost 辅助命令,若前面有个 “-” 符号,表示忽略这些命令的出错。因为有些 “辅助” 命令本来就不一定成功,比如尝试清空一个文件,但文件可能不存在。

Unit 文件占位符和模板

Unit 文件占位符

在 Unit 文件中,有时会需要使用到一些与运行环境有关的信息,例如节点 ID、运行服务的用户等。这些信息可以使用占位符来表示,然后在实际运行被动态地替换实际的值。

  • %n:完整的 Unit 文件名字,包括 .service 后缀名
  • %p:Unit 模板文件名中 @ 符号之前的部分,不包括 @ 符号
  • %i:Unit 模板文件名中 @ 符号之后的部分,不包括 @ 符号和 .service 后缀名
  • %t:存放系统运行文件的目录,通常是 “run”
  • %u:运行服务的用户,如果 Unit 文件中没有指定,则默认为 root
  • %U:运行服务的用户 ID
  • %h:运行服务的用户 Home 目录,即 %{HOME} 环境变量的值
  • %s:运行服务的用户默认 Shell 类型,即 %{SHELL} 环境变量的值
  • %m:实际运行节点的 Machine ID,对于运行位置每个的服务比较有用
  • %b:Boot ID,这是一个随机数,每个节点各不相同,并且每次节点重启时都会改变
  • %H:实际运行节点的主机名
  • %v:内核版本,即 uname -r 命令输出的内容
  • %%:在 Unit 模板文件中表示一个普通的百分号

Unit 模板

在现实中,往往有一些应用需要被复制多份运行。例如,用于同一个负载均衡器分流的多个服务实例,或者为每个 SSH 连接建立一个独立的 sshd 服务进程。

Unit 模板文件的写法与普通的服务 Unit 文件基本相同,不过 Unit 模板的文件名是以 @ 符号结尾的。通过模板启动服务实例时,需要在其文件名的 @ 字符后面附加一个参数字符串。

示例:apache@.service

apache@.service 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=My Advanced Service Template
After=etcd.service docker.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill apache%i
ExecStartPre=-/usr/bin/docker rm apache%i
ExecStartPre=/usr/bin/docker pull coreos/apache
ExecStart=/usr/bin/docker run --name apache%i -p %i:80 coreos/apache /usr/sbin/apache2ctl -D FOREGROUND
ExecStartPost=/usr/bin/etcdctl set /domains/example.com/%H:%i running
ExecStop=/usr/bin/docker stop apache1
ExecStopPost=/usr/bin/docker rm apache1
ExecStopPost=/usr/bin/etcdctl rm /domains/example.com/%H:%i

[Install]
WantedBy=multi-user.target

启动 Unit 模板的服务实例

在服务启动时需要在 @ 后面放置一个用于区分服务实例的附加字符参数,通常这个参数用于监控的端口号或控制台 TTY 编译号。

1
systemctl start apache@8080.service

Systemd 在运行服务时,总是会先尝试找到一个完整匹配的 Unit 文件,如果没有找到,才会尝试选择匹配模板。例如上面的命令,System 首先会在约定的目录下寻找名为 apache@8080.service 的文件,如果没有找到,而文件名中包含 @ 字符,它就会尝试去掉后缀参数匹配模板文件。对于 apache@8080.service,systemd 会找到 apache@.service 模板文件,并通过这个模板文件将服务实例化。

Systemd 的资源管理

Systemctl 命令

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# systemctl --help
systemctl [OPTIONS...] {COMMAND} ...
Query or send control commands to the systemd manager.
-h --help Show this help
--version Show package version
--system Connect to system manager
-H --host=[USER@]HOST
Operate on remote host
-M --machine=CONTAINER
Operate on local container
-t --type=TYPE List units of a particular type
--state=STATE List units with particular LOAD or SUB or ACTIVE state
-p --property=NAME Show only properties by this name
-a --all Show all loaded units/properties, including dead/empty
ones. To list all units installed on the system, use
the 'list-unit-files' command instead.
-l --full Don't ellipsize unit names on output
-r --recursive Show unit list of host and local containers
--reverse Show reverse dependencies with 'list-dependencies'
--job-mode=MODE Specify how to deal with already queued jobs, when
queueing a new job
--show-types When showing sockets, explicitly show their type
-i --ignore-inhibitors
When shutting down or sleeping, ignore inhibitors
--kill-who=WHO Who to send signal to
-s --signal=SIGNAL Which signal to send
--now Start or stop unit in addition to enabling or disabling it
-q --quiet Suppress output
--no-block Do not wait until operation finished
--no-wall Don't send wall message before halt/power-off/reboot
--no-reload Don't reload daemon after en-/dis-abling unit files
--no-legend Do not print a legend (column headers and hints)
--no-pager Do not pipe output into a pager
--no-ask-password
Do not ask for system passwords
--global Enable/disable unit files globally
--runtime Enable unit files only temporarily until next reboot
-f --force When enabling unit files, override existing symlinks
When shutting down, execute action immediately
--preset-mode= Apply only enable, only disable, or all presets
--root=PATH Enable unit files in the specified root directory
-n --lines=INTEGER Number of journal entries to show
-o --output=STRING Change journal output mode (short, short-iso,
short-precise, short-monotonic, verbose,
export, json, json-pretty, json-sse, cat)
--plain Print unit dependencies as a list instead of a tree

Unit Commands:
list-units [PATTERN...] List loaded units
list-sockets [PATTERN...] List loaded sockets ordered by address
list-timers [PATTERN...] List loaded timers ordered by next elapse
start NAME... Start (activate) one or more units
stop NAME... Stop (deactivate) one or more units
reload NAME... Reload one or more units
restart NAME... Start or restart one or more units
try-restart NAME... Restart one or more units if active
reload-or-restart NAME... Reload one or more units if possible,
otherwise start or restart
reload-or-try-restart NAME... Reload one or more units if possible,
otherwise restart if active
isolate NAME Start one unit and stop all others
kill NAME... Send signal to processes of a unit
is-active PATTERN... Check whether units are active
is-failed PATTERN... Check whether units are failed
status [PATTERN...|PID...] Show runtime status of one or more units
show [PATTERN...|JOB...] Show properties of one or more
units/jobs or the manager
cat PATTERN... Show files and drop-ins of one or more units
set-property NAME ASSIGNMENT... Sets one or more properties of a unit
help PATTERN...|PID... Show manual for one or more units
reset-failed [PATTERN...] Reset failed state for all, one, or more
units
list-dependencies [NAME] Recursively show units which are required
or wanted by this unit or by which this
unit is required or wanted

Unit File Commands:
list-unit-files [PATTERN...] List installed unit files
enable NAME... Enable one or more unit files
disable NAME... Disable one or more unit files
reenable NAME... Reenable one or more unit files
preset NAME... Enable/disable one or more unit files
based on preset configuration
preset-all Enable/disable all unit files based on
preset configuration
is-enabled NAME... Check whether unit files are enabled
mask NAME... Mask one or more units
unmask NAME... Unmask one or more units
link PATH... Link one or more units files into
the search path
add-wants TARGET NAME... Add 'Wants' dependency for the target
on specified one or more units
add-requires TARGET NAME... Add 'Requires' dependency for the target
on specified one or more units
edit NAME... Edit one or more unit files
get-default Get the name of the default target
set-default NAME Set the default target

Machine Commands:
list-machines [PATTERN...] List local containers and host

Job Commands:
list-jobs [PATTERN...] List jobs
cancel [JOB...] Cancel all, one, or more jobs

Snapshot Commands:
snapshot [NAME] Create a snapshot
delete NAME... Remove one or more snapshots

Environment Commands:
show-environment Dump environment
set-environment NAME=VALUE... Set one or more environment variables
unset-environment NAME... Unset one or more environment variables
import-environment [NAME...] Import all or some environment variables

Manager Lifecycle Commands:
daemon-reload Reload systemd manager configuration
daemon-reexec Reexecute systemd manager

System Commands:
is-system-running Check whether system is fully running
default Enter system default mode
rescue Enter system rescue mode
emergency Enter system emergency mode
halt Shut down and halt the system
poweroff Shut down and power-off the system
reboot [ARG] Shut down and reboot the system
kexec Shut down and reboot the system with kexec
exit Request user instance exit
switch-root ROOT [INIT] Change to a different root file system
suspend Suspend the system
hibernate Hibernate the system
hybrid-sleep Hibernate and suspend the system

Unit 管理

查看当前系统的所有 Unit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 列出正在运行的 Unit
$ systemctl list-units

# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all

# 列出所有没有运行的 Unit
$ systemctl list-units --all --state=inactive

# 列出所有加载失败的 Unit
$ systemctl list-units --failed

# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service

# 查看 Unit 配置文件的内容
$ systemctl cat docker.service

查看 Unit 的状态

  • enabled:已建立启动链接
  • disabled:没建立启动链接
  • static:该配置文件没有 [Install] 部分(无法执行),只能作为其他配置文件的依赖
  • masked:该配置文件被禁止建立启动链接
1
2
3
4
5
6
7
8
# 显示系统状态
$ systemctl status

# 显示单个 Unit 的状态
$ systemctl status bluetooth.service

# 显示远程主机的某个 Unit 的状态
$ systemctl -H root@rhel7.example.com status httpd.service

Unit 的管理

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
# 立即启动一个服务
$ sudo systemctl start apache.service

# 立即停止一个服务
$ sudo systemctl stop apache.service

# 重启一个服务
$ sudo systemctl restart apache.service

# 杀死一个服务的所有子进程
$ sudo systemctl kill apache.service

# 重新加载一个服务的配置文件
$ sudo systemctl reload apache.service

# 重载所有修改过的配置文件
$ sudo systemctl daemon-reload

# 显示某个 Unit 的所有底层参数
$ systemctl show httpd.service

# 显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service

# 设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500

查看 Unit 的依赖关系

1
2
3
4
5
# 列出一个 Unit 的所有依赖,默认不会列出 target 类型
$ systemctl list-dependencies nginx.service

# 列出一个 Unit 的所有依赖,包括 target 类型
$ systemctl list-dependencies --all nginx.service

服务的生命周期

当一个新的 Unit 文件被放入 /etc/systemd/system/ 或 /usr/lib/systemd/system/ 目录中时,它是不会被自识识别的。

服务的激活

  • systemctl enable:在 /etc/systemd/system/ 建立服务的符号链接,指向 /usr/lib/systemd/system/ 中
  • systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、ExecStart 和 ExecStartPost 命令

服务的启动和停止

  • systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、ExecStart 和 ExecStartPost 命令
  • systemctl stop:依次停止定义在 Unit 文件中的 ExecStopPre、ExecStop 和 ExecStopPost 命令
  • systemctl restart:重启服务
  • systemctl kill:立即杀死服务

服务的开机启动和取消

  • systemctl enable:除了激活服务以外,也可以置服务为开机启动
  • systemctl disable:取消服务的开机启动

服务的修改和移除

  • systemctl daemon-reload:Systemd 会将 Unit 文件的内容写到缓存中,因此当 Unit 文件被更新时,需要告诉 Systemd 重新读取所有的 Unit 文件
  • systemctl reset-failed:移除标记为丢失的 Unit 文件。在删除 Unit 文件后,由于缓存的关系,即使通过 daemon-reload 更新了缓存,在 list-units 中依然会显示标记为 not-found 的 Unit。

Target 管理

Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。

在传统的 SysV-init 启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看当前系统的所有 Target
$ systemctl list-unit-files --type=target

# 查看一个 Target 包含的所有 Unit
$ systemctl list-dependencies multi-user.target

# 查看启动时的默认 Target
$ systemctl get-default

# 设置启动时的默认 Target
$ sudo systemctl set-default multi-user.target

# 切换 Target 时,默认不关闭前一个 Target 启动的进程,systemctl isolate 命令改变这种行为,关闭前一个 Target 里面所有不属于后一个 Target 的进程
$ sudo systemctl isolate multi-user.target

Target 与 SysV-init 进程的主要区别:

  • 默认的 RunLevel(在 /etc/inittab 文件设置)现在被默认的 Target 取代,位置是 /etc/systemd/system/default.target,通常符号链接到graphical.target(图形界面)或者multi-user.target(多用户命令行)。
  • 启动脚本的位置,以前是 /etc/init.d 目录,符号链接到不同的 RunLevel 目录 (比如 /etc/rc3.d、/etc/rc5.d 等),现在则存放在 /lib/systemd/system 和 /etc/systemd/system 目录。
  • 配置文件的位置,以前 init 进程的配置文件是 /etc/inittab,各种服务的配置文件存放在 /etc/sysconfig 目录。现在的配置文件主要存放在 /lib/systemd 目录,在 /etc/systemd 目录里面的修改可以覆盖原始设置。

日志管理

Systemd 通过其标准日志服务 Journald 提供的配套程序 journalctl 将其管理的所有后台进程打印到 std:out(即控制台)的输出重定向到了日志文件。

Systemd 的日志文件是二进制格式的,必须使用 Journald 提供的 journalctl 来查看,默认不带任何参数时会输出系统和所有后台进程的混合日志。

默认日志最大限制为所在文件系统容量的 10%,可以修改 /etc/systemd/journald.conf 中的 SystemMaxUse 来指定该最大限制。

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$ sudo journalctl

# 查看内核日志(不显示应用日志):--dmesg 或 -k
$ sudo journalctl -k

# 查看系统本次启动的日志(其中包括了内核日志和各类系统服务的控制台输出):--system 或 -b
$ sudo journalctl -b
$ sudo journalctl -b -0

# 查看上一次启动的日志(需更改设置)
$ sudo journalctl -b -1

# 查看指定服务的日志:--unit 或 -u
$ sudo journalctl -u docker.servcie

# 查看指定服务的日志
$ sudo journalctl /usr/lib/systemd/systemd

# 实时滚动显示最新日志
$ sudo journalctl -f

# 查看指定时间的日志
$ sudo journalctl --since="2012-10-30 18:17:16"
$ sudo journalctl --since "20 min ago"
$ sudo journalctl --since yesterday
$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$ sudo journalctl --since 09:00 --until "1 hour ago"

# 显示尾部的最新 10 行日志:--lines 或 -n
$ sudo journalctl -n

# 显示尾部指定行数的日志
$ sudo journalctl -n 20

# 将最新的日志显示在前面
$ sudo journalctl -r -u docker.service

# 改变输出的格式:--output 或 -o
$ sudo journalctl -r -u docker.service -o json-pretty

# 查看指定进程的日志
$ sudo journalctl _PID=1

# 查看某个路径的脚本的日志
$ sudo journalctl /usr/bin/bash

# 查看指定用户的日志
$ sudo journalctl _UID=33 --since today

# 查看某个 Unit 的日志
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today

# 实时滚动显示某个 Unit 的最新日志
$ sudo journalctl -u nginx.service -f

# 合并显示多个 Unit 的日志
$ journalctl -u nginx.service -u php-fpm.service --since today

# 查看指定优先级(及其以上级别)的日志,共有 8 级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
$ sudo journalctl -p err -b

# 日志默认分页输出,--no-pager 改为正常的标准输出
$ sudo journalctl --no-pager

# 以 JSON 格式(单行)输出
$ sudo journalctl -b -u nginx.service -o json

# 以 JSON 格式(多行)输出,可读性更好
$ sudo journalctl -b -u nginx.service -o json-pretty

# 显示日志占据的硬盘空间
$ sudo journalctl --disk-usage

# 指定日志文件占据的最大空间
$ sudo journalctl --vacuum-size=1G

# 指定日志文件保存多久
$ sudo journalctl --vacuum-time=1years

Systemd 工具集

  • systemctl:用于检查和控制各种系统服务和资源的状态
  • bootctl:用于查看和管理系统启动分区
  • hostnamectl:用于查看和修改系统的主机名和主机信息
  • journalctl:用于查看系统日志和各类应用服务日志
  • localectl:用于查看和管理系统的地区信息
  • loginctl:用于管理系统已登录用户和 Session 的信息
  • machinectl:用于操作 Systemd 容器
  • timedatectl:用于查看和管理系统的时间和时区信息
  • systemd-analyze:显示此次系统启动时运行每个服务所消耗的时间,可以用于分析系统启动过程中的性能瓶颈
  • systemd-ask-password:辅助性工具,用星号屏蔽用户的任意输入,然后返回实际输入的内容
  • systemd-cat:用于将其他命令的输出重定向到系统日志
  • systemd-cgls:递归地显示指定 CGroup 的继承链
  • systemd-cgtop:显示系统当前最耗资源的 CGroup 单元
  • systemd-escape:辅助性工具,用于去除指定字符串中不能作为 Unit 文件名的字符
  • systemd-hwdb:Systemd 的内部工具,用于更新硬件数据库
  • systemd-delta:对比当前系统配置与默认系统配置的差异
  • systemd-detect-virt:显示主机的虚拟化类型
  • systemd-inhibit:用于强制延迟或禁止系统的关闭、睡眠和待机事件
  • systemd-machine-id-setup:Systemd 的内部工具,用于给 Systemd 容器生成 ID
  • systemd-notify:Systemd 的内部工具,用于通知服务的状态变化
  • systemd-nspawn:用于创建 Systemd 容器
  • systemd-path:Systemd 的内部工具,用于显示系统上下文中的各种路径配置
  • systemd-run:用于将任意指定的命令包装成一个临时的后台服务运行
  • systemd-stdio-bridge:Systemd 的内部工具,用于将程序的标准输入输出重定向到系统总线
  • systemd-tmpfiles:Systemd 的内部工具,用于创建和管理临时文件目录
  • systemd-tty-ask-password-agent:用于响应后台服务进程发出的输入密码请求

systemctl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 重启系统
$ sudo systemctl reboot

# 关闭系统,切断电源
$ sudo systemctl poweroff

# CPU停止工作
$ sudo systemctl halt

# 暂停系统
$ sudo systemctl suspend

# 让系统进入冬眠状态
$ sudo systemctl hibernate

# 让系统进入交互式休眠状态
$ sudo systemctl hybrid-sleep

# 启动进入救援状态(单用户状态)
$ sudo systemctl rescue

systemd-analyze

1
2
3
4
5
6
7
8
9
10
11
# 查看启动耗时
$ systemd-analyze

# 查看每个服务的启动耗时
$ systemd-analyze blame

# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain

# 显示指定服务的启动流
$ systemd-analyze critical-chain atd.service

hostnamectl

1
2
3
4
5
# 显示当前主机的信息
$ hostnamectl

# 设置主机名。
$ sudo hostnamectl set-hostname rhel7

timedatectl

1
2
3
4
5
6
7
8
9
10
# 查看当前时区设置
$ timedatectl

# 显示所有可用的时区
$ timedatectl list-timezones

# 设置当前时区
$ sudo timedatectl set-timezone America/New_York
$ sudo timedatectl set-time YYYY-MM-DD
$ sudo timedatectl set-time HH:MM:SS

loginctl

1
2
3
4
5
6
7
8
# 列出当前 session
$ loginctl list-sessions

# 列出当前登录用户
$ loginctl list-users

# 列出显示指定用户的信息
$ loginctl show-user ruanyf

systemd-ask-password

1
$ PASSWORD=$(systemd-ask-password "Input Your Password:")

systemd-run

systemd-run 可以将一个指定的操作变成后台运行的服务。它的效果似乎与直接在命令后加上表示后台运行的 & 符号很相似。然而,它让命令成为服务还意味着,它的生命周期将由 Systemd 控制。具体来说,包括以下好处:

  • 服务的生命周期由 Systemd 接管,不会随着启动它的控制台关闭而结束
  • 可以通过 systemctl 工具管理服务的状态
  • 可以通过 journalctl 工具查看和管理服务的日志信息
  • 可以通过 Systemd 提供的方法限制服务的 CPU、内存、磁盘 IO 等系统资源的使用情况。

环境准备

本地安装 Git NodeJS

检查环境

1
2
3
4
5
git -v

node -v

npm -v

切换镜像站,具体参考NPM配置国内源

1
npm config set registry https://registry.npmmirror.com

Hexo环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pnpm install -g hexo-cli                # 安装Hexo cli工具

hexo init # 初始化博客环境
npm install # 安装依赖库

# 插件
npm install hexo-asset-img # 头像
npm install hexo-auto-category # 自动分类
npm install hexo-generator-searchdb # 生成搜索数据库
npm install hexo-backlink # Obsdian链接转换
npm install hexo-deploy-git # git自动发布
npm install hexo-theme-next # hexo NexT主题
npm install hexo-server # hexo服务器
npm install hexo-next-giscus # giscus评论组件
npm install hexo-wordcount # 字数统计

Hexo 配置

参考官方文档

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
...
theme: next # 配置主题next

giscus: # 评论配置
enable: true
repo: # Github repository name
repo_id: # Github repository id
category: # Github discussion category
category_id: # Github discussion category id
# Available values: pathname | url | title | og:title
mapping: title
# Available values: 0 | 1
reactions_enabled: 1
# Available values: 0 | 1
emit_metadata: 1
# Available values: light | light_high_contrast | light_protanopia | light_tritanopia | dark | dark_high_contrast | dark_protanopia | dark_tritanopia | dark_dimmed | preferred_color_scheme | transparent_dark | noborder_light | noborder_dark | noborder_gray | cobalt | purple_dark
theme: light
# Available values: en | zh-CN
lang: zh-CN
# Place the comment box above the comments
input_position: bottom
# Load the comments lazily
loading: lazy

deploy: # 发布配置
- type: git
repo: # 仓库发布地址
branch: main # 发布分支
name: # git用户名 git config user.name <username>
email: # git邮箱 git config user.email <email>
...

注意: 评论部分需要借助Github Discussions, 参考Hexo博客配置Giscus评论

Hexo主题配置

安装主题后从npm_modules/<主题名>/文件夹中复制_config.yml到博客根目录并重命名为_config.next.yml,当博客deploy时回自动应用主题配置,一下主题修改都基于此文件进行。

设置语言

NexT主题支持多种语言,只需要编辑_config.next.yml中的language设置即可

语言 代码 设定示例
English en language: en
简体中文 zh-CN(注:zh-Hans已经无法使用) language: zh-CN
Frangais fr-FR language: fr-FR
Portugues pt language: pt
或者
language:pt-BR
繁體中文 zh-hk
或者
zh-tw
language: zh-hk
Pycckmi 93bIK ru language: ru
Deutsch de language: de
日本語 ja language: ja
Indonesian id language: id
Korean ko language: ko
如果需要添加非内置的字段需要手动添加翻译文件,例如中文的翻译文件路径为node_modules/next/languages/zh-CN.yml

设置关于

source/about/index.md中添加如下内容

1
2
3
4
5
6
---
title: 关于
date: 2025-08-27 00:00:00
---

<个人信息>

选择Scheme

Scheme 是 NexT 提供的一种特性,借助于 Scheme,NexT 为你提供多种不同的外观。同时,几乎所有的配置都可以 在 Scheme 之间共用。目前 NexT 支持三种 Schem

  • Muse - 默认 Scheme
  • Mist - Muse 的紧凑版本
  • Pisces - 双栏 Scheme
  • Gemini

菜单配置

菜单配置包括三个部分,第一是菜单项(名称和链接),第二是菜单项的显示文本,第三是菜单项对应的图标。 NexT 使用的是 Font Awesome 提供的图标, Font Awesome 提供了 600+ 的图标,可以满足绝大的多数的场景,同时无须担心在 Retina 屏幕下 图标模糊的问题。

1
2
3
4
5
6
7
8
menu: home: / || home 
categories: /categories/ || th
archives: /archives/ || archive
tags: /tags/ || tags
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat
about: /about/ || user

NexT 默认的菜单项有(标注 * 的项表示需要手动创建这个页面):

注意: 若站点运行在子目录中,请将链接前缀的 / 去掉。

键值 设定值 显示文本(简体中文)
home home: / 主页
archives archives: /archives 归档页
categories categories: /categories 分类页 *
tags tags: /tags 标签页 *
about about: /about 关于页面*
commonweal commonweal: /404.html 公益 404 !

侧栏配置

默认情况下,侧栏仅在文章页面(拥有目录列表)时才显示,并放置于右侧位置。配置具体如下

1
2
3
4
5
6
7
...

sidbar:
position: left # 配置侧栏居左
display: post # 侧栏显示行为

...

侧栏显示位置支持

  • left: 居左显示
  • right: 居右显示

侧栏显示行为支持

  • post 默认行为,在文章页面(拥有目录列表)时显示
  • always 所有页面都显示
  • hide 在所有页面中都隐藏(可以手动展开)
  • remove 完全移除

注册Github账号,Gitea账号(可选)
[^注] Github由于网络问题会经常无法链接,可使用Gitea作为中转,先将代码提交道Gitea,然后Gitea配置自动推送到Github

设置头像

1
avatar: /images/avatar.jpg

头像地址如果是以/起始则表示头像图片放置在博客发布后的目录下,例如测试博客地址是http://localhost:4000,头像图片地址为http://localhost:4000/images/avatar.jpg
此配置需要在博客的source/images目录中放置头像图片avatar.jpg

侧边栏社交链接

1
2
3
4
5
6
7
8
9
10
social:
#GitHub: https://github.com/<username> || fab fa-github
#E-Mail: <email> || fa fa-envelope
#Weibo: https://weibo.com/yourname || fab fa-weibo
#Twitter: https://twitter.com/yourname || fab fa-twitter
#FB Page: https://www.facebook.com/yourname || fab fa-facebook
#StackOverflow: https://stackoverflow.com/yourname || fab fa-stack-overflow
#YouTube: https://youtube.com/yourname || fab fa-youtube
#Instagram: https://instagram.com/yourname || fab fa-instagram
#Skype: skype:yourname?call|chat || fab fa-skype

next主题默认支持的社交链接 ||符号后是链接的图标

使用已有配置放开注释即可,如果要添加默认不存在链接示例如下

1
2
social:
微信: https://wx.qq.com || weixin

注意: 图标对应的名称是FontAwesom图标的名称(不必带 fa- 前缀)

打赏功能

1
2
3
4
# Reward 
reward:
wechatpay: /images/custom/wechatpay.jpg
alipay: /images/custom/alipay.jpg

放开此部分注释并在source/images中放入收款码图片

站点建立时间

1
2
footer:
since: 2025

订阅微信公众号

1
2
3
4
5
# Wechat Subscriber 
wechat_subscriber:
enabled: true
qcode: /images/wechat-qcode.jpg
description: 欢迎您扫一扫上面的微信公众号,订阅我的博客!

放开此部分注释,并在source/images中放入公众号二维码

注意: 此功能需要NexT版本在5.0.1之后

设置动画

NexT 默认开启动画效果,效果使用 JavaScript 编写,因此需要等待 JavaScript 脚本完全加载完毕后才会显示内容。 如果您比较在乎速度,可以将设置此字段的值为 false 来关闭动画。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Use velocity to animate everything. 
motion:
enable: true
async: true
transition:
# Transition variants:
# fadeIn | fadeOut | flipXIn | flipXOut | flipYIn | flipYOut | flipBounceXIn | flipBounceXOut | flipBounceYIn | flipBounceYOut
# swoopIn | swoopOut | whirlIn | whirlOut | shrinkIn | shrinkOut | expandIn | expandOut
# bounceIn | bounceOut | bounceUpIn | bounceUpOut | bounceDownIn | bounceDownOut | bounceLeftIn | bounceLeftOut | bounceRightIn | bounceRightOut
# slideUpIn | slideUpOut | slideDownIn | slideDownOut | slideLeftIn | slideLeftOut | slideRightIn | slideRightOut
# slideUpBigIn | slideUpBigOut | slideDownBigIn | slideDownBigOut | slideLeftBigIn | slideLeftBigOut | slideRightBigIn | slideRightBigOut
# perspectiveUpIn | perspectiveUpOut | perspectiveDownIn | perspectiveDownOut | perspectiveLeftIn | perspectiveLeftOut | perspectiveRightIn | perspectiveRightOut
post_block: fadeIn
post_header: slideDownIn
post_body: slideDownIn
coll_header: slideLeftIn # Only for Pisces | Gemini.
sidebar: slideUpIn

设置全文阅读

在首页显示一篇文章的部分内容,并提供一个链接跳转到全文页面是一个常见的需求。 NexT 提供三种方式来控制文章在首页的显示方式。

  • 在文章中使用 <!-- more --> 手动进行截断,Hexo 提供的方式 推荐
  • 在文章的 front-matter 中添加 description,并提供文章摘录
  • 自动形成摘要,需要添加如下配置
    1
    2
    3
    4
    5
    # Automatically Excerpt. Not recommend. 
    # Please use <!-- more --> in the post to control excerpt accurately.
    auto_excerpt:
    enable: true
    length: 150

设置字数统计/阅读时长

_config.yml中配置如下

1
2
3
4
5
6
7
8
# Post wordcount display settings 
# Dependencies: https://github.com/willin/hexo-wordcount
post_wordcount:
item_text: true
wordcount: true
min2read: true
totalcount: false
separated_meta: true

加载进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Progress bar in the top during page loading.
pace: true
# Themes list:
#pace-theme-big-counter
#pace-theme-bounce
#pace-theme-barber-shop
#pace-theme-center-atom
#pace-theme-center-circle
#pace-theme-center-radar
#pace-theme-center-simple
#pace-theme-corner-indicator
#pace-theme-fill-left
#pace-theme-flash
#pace-theme-loading-bar
#pace-theme-mac-osx
#pace-theme-minimal
# For example
# pace_theme: pace-theme-center-simple
pace_theme: pace-theme-minimal

搜索服务

_config.yml中配置如下

1
2
3
4
5
6
# hexo-generator-searchdb 
search:
path: search.xml
field: post
format: html
limit: 10000

在_config.next.yml中配置如下

1
2
3
4
5
6
7
8
9
# Local search 
# Dependencies: https://github.com/flashlab/hexo-generator-search
local_search:
enable: true
# if auto, trigger search by changing input
# if manual, trigger search by pressing enter key or search button
trigger: auto
# show top n results per article, show all results by setting to -1
top_n_per_article: 1

参考
官方文档
Hexo 博客使用 Next 主题及美化 | Jiz4oh’s Life

前言

作为技术博主,博客的高效维护与部署一直是我关注的重点。近期在维护博客时,我遇到了两个核心问题:

  1. 内容管理混乱:草稿箱文件堆积,缺乏分类标准,甚至因误操作破坏了原有配置;
  2. 兼容性局限:计划将文章同步至 FastGPT 等 AI 知识库时,发现官方推荐的 Hexo 部署方案(源码与静态文件混存)中,冗余的 public 目录会干扰 RAG 系统提取内容,且源码与发布产物耦合易引发冲突。

为解决这些问题,我采用了源码与发布分离的部署架构:将 Markdown 源文件单独存放在一个仓库,通过 GitHub Actions 自动在另一个仓库构建并发布静态文件。这种方式的优劣对比如下:

方案 优点 缺点
官方混仓部署 支持本地手动 / 自动发布,预览方便,配置简单 仓库体积大,源码与产物混合,不利于二次利用
本文分离部署 源码纯净、产物独立,兼容 AI 知识库,自动构建 本地预览需搭测试环境,配置较复杂(双仓库 + 鉴权)

部署核心思路

核心逻辑:当源码仓库收到推送时,GitHub Actions 自动将源文件检出到 source/_posts,并从 _hexo 目录复制配置文件还原 Hexo 环境,最终执行构建与发布。

文件结构设计(源码仓库):

1
2
3
4
5
6
7
8
|-- _hexo/ # Hexo 核心配置目录 
| |-- _config.yml # Hexo 主配置
| |-- _config.next.yml # NexT 主题配置
| |-- package.json # Node 环境依赖
| |-- scaffolds/ # 文章模板(draft/page/post.md)
| |-- static/ # 静态资源(头像、支付码等)
|-- .github/workflows/ # GitHub Actions 工作流配置
|-- .obsidian/ # Obsidian 编辑器配置(可选)

详细部署步骤

1. 生成 SSH 密钥对(用于仓库间鉴权)

需要生成一对 SSH 密钥,用于源码仓库向发布仓库推送构建结果:

1
ssh-keygen -t rsa -C "<github 注册邮箱>"

执行后会在以下路径生成两个文件:

  • 私钥:~/.ssh/id_rsa(Linux/Mac)或 C:\Users\<用户名>\.ssh\id_rsa(Windows)
  • 公钥:~/.ssh/id_rsa.pub(同上路径)

注意:.ssh为隐藏目录,需要修改系统设置显示此文件夹

2. 准备两个仓库

仓库 1:源码仓库(存放 Markdown 与配置)

  • 新建仓库(例如命名为 hexo-source
  • 进入仓库设置:Settings → Secrets and variables → Actions → New repository secret
  • 添加一个名为 HEXO_DEPLOY_KEY 的密钥,值为私钥 id_rsa 的内容(用记事本打开复制)

仓库 2:发布仓库(存放静态文件,用于 GitHub Pages)

  • 仓库名必须为 <你的 GitHub 用户名>.github.io(固定格式,否则 GitHub Pages 无法生效)
  • 权限需设为公开,并开启 Discussions 功能(进入仓库设置 → Features 勾选)
  • 配置部署密钥:Settings → Deploy keys → Add deploy key
    • Title 填 HEXO_DEPLOY_PUB
    • Key 填入公钥 id_rsa.pub 的内容,并勾选 Allow write access(允许推送权限)

3. 配置 Hexo 环境文件

在源码仓库中创建 _hexo 目录,放入以下核心文件(可从本地 Hexo 环境中复制, 参考Hexo-博客配置):

  • _config.yml:Hexo 主配置(需修改部署相关配置,见步骤 4)
  • _config.next.yml:NexT 主题配置(其他主题同理)
  • package.json:依赖配置(需包含 hexohexo-deployer-git 等核心依赖)
  • scaffolds/:文章模板(draft.md/page.md/post.md
  • 静态资源:如头像(avatar.jpg)、关于页(about.md)等,按实际需求存放

4. 配置部署与工作流文件

① Hexo 部署配置(_hexo/_config.yml

在配置文件中添加部署规则,指向发布仓库:

1
2
3
4
5
6
7
8
# Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
- type: git
repo: git@github.com:<username>/<username>.github.io.git
branch: master
name: <username>
email: <email>

② GitHub Actions 工作流(.github/workflows/hexo-deploy.yml

创建工作流文件,实现自动构建部署:

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
name: hexo-deploy  # 工作流名称

# 触发条件:向 master 分支推送时执行
on:
push:
branches: ["master"]

jobs:
build:
runs-on: ubuntu-latest # 使用 Ubuntu 环境
steps:
# 1. 配置时区(避免时间显示异常)
- name: Setup Timezone
uses: szenius/set-timezone@v2.0
with:
timezoneLinux: "Asia/Shanghai"

# 2. 拉取源码仓库内容到 source/_posts
- uses: actions/checkout@v3
with:
path: source/_posts

# 3. 安装 Node.js(需与本地开发环境版本一致,这里用 20.x)
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: '20'

# 4. 缓存 NPM 依赖(加速构建)
- name: Cache NPM dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.OS }}-npm-cache
restore-keys: |
${{ runner.OS }}-npm-cache

# 5. 配置 SSH 密钥(用于向发布仓库推送)
- name: Setup Git
env:
ACTION_DEPLOY_KEY: ${{ secrets.HEXO_DEPLOY_KEY }} # 引用源码仓库的私钥
run: |
mkdir -p ~/.ssh/
echo "$ACTION_DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa # 严格权限,否则 SSH 会拒绝使用
ssh-keyscan github.com >> ~/.ssh/known_hosts # 信任 GitHub 主机

# 6. 拉取主题(以 NexT 为例,其他主题修改仓库地址即可)
- name: Use Themes
uses: actions/checkout@v3
with:
repository: next-theme/hexo-theme-next
path: themes/next

# 7. 还原 Hexo 环境(从 _hexo 目录复制配置文件)
- name: Setup Hexo
run: |
npm install -g hexo-cli # 全局安装 Hexo 命令行工具
# 复制核心配置文件
cp source/_posts/_hexo/_config.yml .
cp source/_posts/_hexo/_config.next.yml .
cp source/_posts/_hexo/package.json .
# 复制文章模板
mkdir scaffolds
cp source/_posts/_hexo/scaffolds/* scaffolds/
# 复制静态页面(关于页、分类页等,按实际需求调整)
mkdir -p source/about source/categories source/tags source/images
cp source/_posts/_hexo/about.md source/about/index.md
cp source/_posts/_hexo/categories.md source/categories/index.md
cp source/_posts/_hexo/tags.md source/tags/index.md
cp source/_posts/_hexo/*.jpg source/images/ # 复制图片资源
# 安装依赖
npm install

# 8. 缓存部署目录(加速后续构建)
- name: Cache Deploy
uses: actions/cache@v4
with:
path: .deploy_git
key: ${{ runner.OS }}-deploy-cache
restore-keys: |
${{ runner.OS }}-deploy-cache

# 9. 构建并发布
- name: Deploy
run: |
cd .deploy_git && git pull # 拉取最新发布内容,避免冲突
cd ..
hexo clean # 清理缓存
hexo generate # 生成静态文件
hexo deploy # 部署到发布仓库

验证与使用

  1. 将上述文件提交到源码仓库的 master 分支,GitHub Actions 会自动触发工作流;
  2. 进入源码仓库的 Actions 标签页,查看工作流执行状态,若显示绿色对勾则部署成功;
  3. 访问 https://<你的用户名>.github.io,即可看到最新发布的博客。

注意事项

  1. 私钥 HEXO_DEPLOY_KEY 是敏感信息,切勿泄露或提交到仓库;
  2. 发布仓库名必须严格为 <用户名>.github.io,否则 GitHub Pages 无法正常访问;
  3. 若主题是自定义修改过的,建议将主题 fork 到自己的仓库,再在工作流中拉取自己的 fork 版本;
  4. 本地预览时,可在源码仓库中手动搭建 Hexo 环境(复制 _hexo 目录文件,执行 hexo server)。

参考

Hexo官方提供的Github Actions部署示例

由于没有自己的云服务器,所以我之前选择博客工具的时候排除了Typora、Wordpress…转而选择了 Hexo,但其实相较于前者,Hexo 的云端写作体验一直很糟糕。   随着近两年 CI/CD、DevOps 这些概念的流行,很多工具都火了起来,像 Jenkins、Github的好基友Travis等等,但这些都不太适用我们的情况,Jenkins 也需要自己的服务器,而TravisCI我也测试了一下,本来是适用的,也很方便,但是官方宣布后续不再免费,只赠送 10000 积分用完即止,开通付费版则要 69刀/月 [俺支持不起,倒不如整一个云服务器,大佬请随意!]。   但是我偶然了解到全球最大的同性交友网站丢出了一个重磅炸弹-Github Actions,我发现利用此功能可以完美解决 Hexo 静态博客自动部署的问题,并且免费版每月赠送2000分钟的时长,完美!

  本篇博文就来浅谈一下 Github Actions 的原理,以及使用他简单实现 Hexo 静态博客的自动部署(即每次我们 push 源代码后,自动生成静态文件,并上传到我们的仓库或者云存储中;Github 本身可以开启运行结果邮件通知功能,有条件的也可以设置 WebHooks 来进行通知。),好了那么话不多说,我们直接开始,欢迎大佬批评指正。

Github Actions

简介

  前面我们有提到 CI/CD、DevOps 这些名词,其实就是我们一般开发完成后,需要进行测试、打包、发布等操作,这些动作其实都是可以自动完成的,之前提到的 Jenkins 就可以做到,但是需要有自己的服务器。

  而 Github Actions 服务,就是用来帮助我们完成这些动作,他既可以使用自己的服务器也可以使用 Github 的服务器(支持多种环境与语言)。

使用

  Github Actions 和其他工具一样,通过脚本文件来进行一系列复杂的操作,他也有自己的语法规则-官方文档。

  由于很多操作在不同项目里面是类似的,完全可以共享。GitHub 注意到了这一点,想出了一个很妙的点子,允许开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用,官方市场、github/actions 仓库。

  其他基础使用,推荐查看阮一峰老师的介绍。

原理(个人了解)

  其实 Github Actions 就是当我们完成触发条件后(例如:push/pull等),Github 通过我们编写的脚本文件把应该在本地运行的命令,放到他的服务器(也可以设置自己的服务器)上自动运行,大大减少我们的操作。

  经过博主的测试,Github 提供的服务器上预装了多种语言及一些常见的运行环境等,所以我们编写脚本其实很简单,只需按语法要求添加以下固定内容即可:

  • 提供脚本基本信息
  • 指定运行环境、触发条件
  • 编写任务、步骤、动作

  添加这些内容后,当匹配触发条件时,Github 就会读取我们的脚本文件,在服务器上的指定环境中运行我们预先写好的任务、步骤、动作。

好了,经过这些简单的了解后,我们开始配置 Github Actions吧。

Hexo 静态博客自动部署

建立博客源代码仓库

  因为我们需要 Hexo 源代码才能生成静态文件,所以我们需要建立一个私有仓库来保存我们的源代码,当然如果你觉得麻烦也可以建立一个分支来保存,此处就不介绍了。

Ps: 如果您还不会搭建 Hexo 博客,可以参考本站之前的 Hexo 搭建教学。

  • 具体操作如图所示

  仓库建立后,我们可以先把自己的源代码通过 Git 提交上去,这里就不介绍了,也可以参考之前的博客搭建教学。

Ps: 如果碰到 Github 连线失败的情况,建议禁用代理 git config --global --unset http.proxy,或者直接使用 open ssh 进行连线推送。

Hexo 简单配置与介绍

Github 的链接形式

  Github 这种网站的代码仓库地址常见有三种形式,适用于不同的情况,下面简单介绍一下。

  • 普通链接,一般在使用账号密码登录后或者ssh传输时使用。

代码语言:javascript

AI代码解释

复制

1
2
3
// 这种地址可以直接在仓库中复制
https://github.com/pandaoh/biugle.git
git@github.com:pandaoh/biugle.git
  • 账号密码链接,这种适用于自己调用 Github 的数据或者当 Api 使用等情形。

代码语言:javascript

AI代码解释

复制

1
https://{username}:{password}@github.com/pandaoh/biugle.git
  • token 链接,在 Github Settings 中生成 token 后,可以直接放到仓库地址中,这样就可以直接访问有权限的仓库,方便我们自动部署。

代码语言:javascript

AI代码解释

复制

1
https://{token}@github.com/pandaoh/biugle.git
生成 Github Token

  了解完 Github 这些链接形式后,我们就可以开始配置了,因为我们决定使用 Token 这种链接形式来进行连线推送等操作,所以首先就是生成 Github Token

  • 打开我们自己的 Github Settings,选择 Developer settings => Personal access tokens

  • 生成 token 后,此信息只会展示一次,我们先保存下来,因安全问题后文我统一将此 token 称为 $GH_TOKEN
修改 config.yml

  大部分人之前应该都是在本地进行博客编写,所以连接 Github 的方式应该都是使用的 ssh,那么前面我们为了方便后续自动部署,需要把 config.yml 文件中的 deploy->repository->github 值改成 token url 的形式。

代码语言:javascript

AI代码解释

复制

1
2
3
4
5
6
deploy:
- type: git
repository:
github: https://{$GH_TOKEN}@github.com/pandaoh/pandaoh.github.io.git,master
...
// 注意此处的 {$GH_TOKEN} 请替换成我们之前生成的 token 内容,此仓库地址是我们博客静态文件最终存放的仓库地址,即搭建教学中开通 Github Pages 服务的那个仓库地址。

添加 GitHub Actions 脚本

  配置完 Hexo,我们开始编写 Github 的脚本文件,GitHub Actions 的配置文件叫做 workflow 文件,存放在源代码仓库的 .github/workflows 目录。   workflow 文件采用 YAML 格式,文件名可以任意取,但是后缀名统一为 .yml,比如 test.yml。一个库可以有多个 workflow 文件。   GitHub 在我们完成预设触发条件时,只要发现 .github/workflows 目录里面有 .yml 文件,就会自动读取运行该文件。

添加步骤
  • 我们可以直接手动建立此文件,或者通过源代码仓库点击 Actions => 选择 Setup Node创建,但最终同样都需 push 到远端源代码仓库中。

参数介绍

建议阅读完前面给出的官方文档再来进行此处的了解 ^_^

  • 建立文件后,我们修改其配置如下。

代码语言:javascript

AI代码解释

复制

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
name: DoubleAm's Blog CI/CD # 脚本 workflow 名称

on:
push:
branches: [main, master] # 当监测 main,master 的 push
paths: # 监测所有 source 目录下的文件变动,所有 yml,json 后缀文件的变动。
- '*.json'
- '**.yml'
- '**/source/**'

jobs:
blog: # 任务名称
timeout-minutes: 30 # 设置 30 分钟超时
runs-on: ubuntu-latest # 指定最新 ubuntu 系统
steps:
- uses: actions/checkout@v2 # 拉取仓库代码
- uses: actions/setup-node@v2 # 设置 node.js 环境
- name: Cache node_modules # 缓存 node_modules,提高编译速度,毕竟每月只有 2000 分钟。
uses: actions/cache@v2 # 亲测 Github 服务器编译速度比我自己电脑都快,如果每次构建按5分钟计算,我们每个月可以免费部署 400 次,Github yyds!!!
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Init Node.js # 安装源代码所需插件
run: |
npm install
echo "init node successful"
- name: Install Hexo-cli # 安装 Hexo
run: |
npm install -g hexo-cli --save
echo "install hexo successful"
- name: Build Blog # 编译创建静态博客文件
run: |
hexo clean
hexo g
echo "build blog successful"
- name: Deploy DoubleAm's Blog # 设置 git 信息并推送静态博客文件
run: |
git config --global user.name "doubleam"
git config --global user.email "admin@biugle.cn"
hexo deploy

- run: echo "Deploy Successful!"

验证结果

Hexo 与 Github Actions 均配置完成后,我们将这两个文件变动都推送至源代码仓库中。

推送内容

完成以上操作后,我们每修改并 push 一次监测的文件,就可以触发脚本运行。

查看 Github Actions 运行日志与结果

触发后我们可以查看运行日志与结果,如下图所示。

  • 基础日志

  • 详细日志

添加 WebHooks 通知

  脚本运行完成一般都有邮件通知,但如果我们需要在 push 后添加其他通知,例如钉钉机器人、QQ等,我们可以添加 WebHooks 来进行通知。

  • 可以直接在仓库添加 WebHooks,选择触发条件后,输入接口地址【POST】与 Secret (可选),Github 会在我们完成触发条件时,携带此次操作的信息数据包请求一次这个 POST 接口,至于后面的处理就可以自定义啦。

本文是一份关于 Locust 性能测试工具的全面配置与运行指南,涵盖环境变量、配置文件、命令行参数、无头模式、分布式运行以及自定义扩展等核心内容。

一、 配置方式总览

Locust 支持多种配置方式,优先级从低到高依次为:

  1. ~/locust.conf (用户主目录)
  2. ./locust.conf (当前目录)
  3. --config 参数指定的文件
  4. 环境变量 (格式:LOCUST_<OPTION_NAME>)
  5. 命令行参数 (最高优先级)
1.1 通过环境变量配置
1
2
3
4
5
6
# Linux/macOS
LOCUST_LOCUSTFILE=custom_locustfile.py locust

# Windows (cmd)
set LOCUST_LOCUSTFILE=custom_locustfile.py
locust
1.2 通过配置文件配置

创建 locust.confmaster.conf 文件:

1
2
3
4
5
6
7
8
9
# master.conf 示例
locustfile = locust_files/my_locust_file.py
headless = true
master = true
expect-workers = 5
host = http://target-system
users = 100
spawn-rate = 10
run-time = 10m

使用配置文件运行:

1
locust --config=master.conf

二、 核心运行模式

2.1 基础命令与常用选项
命令行参数 环境变量 描述
-f, --locustfile LOCUST_LOCUSTFILE 指定 Locust 测试脚本文件或目录。
-H, --host LOCUST_HOST 设置待测试系统的基准 URL。
-u, --users LOCUST_USERS 模拟的最大并发用户数。
-r, --spawn-rate LOCUST_SPAWN_RATE 每秒孵化的用户数。
--web-host, --web-port -P LOCUST_WEB_HOST, LOCUST_WEB_PORT 指定 Web UI 绑定的主机和端口。
2.2 无头模式运行 (Headless)

禁用 Web UI,直接通过命令行控制测试。

1
locust -f locustfile.py --headless -u 100 -r 5
  • 设置运行时间:使用 -t, --run-time 参数(例如 1h30m, 300s)。
  • 允许任务完成:使用 -s, --stop-timeout 参数,在停止前等待任务完成迭代(例如 --stop-timeout 10s)。
  • 动态调整用户数:即使在无头模式下,也可按 w/W(增加1/10用户)或 s/S(减少1/10用户)实时调整。
2.3 分布式运行

**主节点 (Master)**:负责协调和收集数据。

1
locust -f locustfile.py --master --expect-workers 3
  • --expect-workers:指定期望连接的工作节点数,连接齐备后才开始测试。
  • --master-bind-host/--master-bind-port:绑定主节点的接口和端口(默认 *:5557)。

**工作节点 (Worker)**:执行实际的负载生成。

1
locust -f locustfile.py --worker --master-host=192.168.1.100
  • --master-host/--master-port:指向主节点的地址。

无头模式下的分布式:主节点必须指定 --expect-workers 以等待工作节点就绪。

2.4 使用多个 Locustfile
  1. 指定目录:Locust 会递归扫描目录下的 .py 文件(忽略 locust.py 和以 _ 开头的文件)。
    1
    locust -f locustfiles/
  2. 指定多个文件:用逗号分隔。
    1
    locust -f locustfile1.py,locustfile2.py,locustfile3.py
2.5 用户类选择器 (Class Picker)

使用 --class-picker 参数启动,可在 Web UI 中选择本次运行要使用的特定 User 类或 Shape 类,而不是运行所有类。

1
locust -f locustfiles/ --class-picker

Web UI Class Picker

三、 高级功能与定制

3.1 自定义命令行参数

通过 init_command_line_parser 事件钩子添加自定义参数,这些参数可自动同步到工作节点并在 Web UI 中显示。

1
2
3
4
5
6
7
8
9
10
11
from locust import events

@events.init_command_line_parser.add_listener
def _(parser):
parser.add_argument("--my-argument", type=str, env_var="LOCUST_MY_ARGUMENT", default="", help="自定义参数说明")
# 设置 include_in_web_ui=False 可在 Web UI 中隐藏此参数
parser.add_argument("--hidden-arg", include_in_web_ui=False, default="invisible")

@events.test_start.add_listener
def _(environment, **kw):
print(f"自定义参数值: {environment.parsed_options.my_argument}")
3.2 定制统计信息设置

通过修改 locust.stats 模块的常量来调整统计行为。

1
2
3
4
5
6
7
8
import locust.stats

# 调整控制台输出间隔为15秒
locust.stats.CONSOLE_STATS_INTERVAL_SEC = 15
# 调整 CSV 文件写入间隔为5秒
locust.stats.CSV_STATS_INTERVAL_SEC = 5
# 调整要报告的百分位数
locust.stats.PERCENTILES_TO_REPORT = [0.5, 0.95, 0.99]

可定制参数

  • STATS_NAME_WIDTH:控制台输出中请求名称列的宽度。
  • CURRENT_RESPONSE_TIME_PERCENTILE_WINDOW:计算当前响应时间百分位数的窗口大小(秒)。
3.3 自定义退出条件与退出码

通过 quitting 事件监听器,可以基于测试结果(如失败率、响应时间)自定义进程退出码,便于集成到 CI/CD 流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
from locust import events
import logging

@events.quitting.add_listener
def _(environment, **kw):
if environment.stats.total.fail_ratio > 0.01:
logging.error("失败率超过1%")
environment.process_exit_code = 1
elif environment.stats.total.avg_response_time > 200:
logging.error("平均响应时间超过200ms")
environment.process_exit_code = 1
else:
environment.process_exit_code = 0

注意:默认情况下,如果有任何请求失败,Locust 会以退出码 1 结束。可使用 --exit-code-on-error 参数改变此行为。

四、 其他实用配置选项速查

命令行参数 环境变量 用途简述
--autostart LOCUST_AUTOSTART 自动开始测试(保留 Web UI)。
--autoquit LOCUST_AUTOQUIT 测试结束后自动退出(需与 --autostart 同用)。
--web-auth LOCUST_WEB_AUTH 为 Web UI 设置基础认证(格式:username:password)。
-T, --tags LOCUST_TAGS 仅执行带有指定标签的任务。
-E, --exclude-tags LOCUST_EXCLUDE_TAGS 排除带有指定标签的任务。
--csv LOCUST_CSV 将统计数据保存为 CSV 文件(生成 *_stats.csv, *_stats_history.csv, *_failures.csv)。
--html LOCUST_HTML 将 HTML 报告保存到指定文件。
--print-stats LOCUST_PRINT_STATS 在控制台定期打印统计信息。
--only-summary LOCUST_ONLY_SUMMARY 在无头模式下只打印最终摘要,禁止定期打印。
--loglevel -L LOCUST_LOGLEVEL 设置日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)。
--logfile LOCUST_LOGFILE 指定日志文件路径。

一、核心原理与前置条件

iptables 端口转发(也称为 DNAT/SNAT)是在 Linux 内核层面进行的网络数据包重定向。它比应用层转发(如 SSH 隧道)效率更高,因为它直接修改数据包的 IP 头和端口信息。

1.1 开启系统路由转发功能

这是实现跨网段端口转发(尤其是转发到其他主机)的必备前提。系统默认禁止转发数据包。

临时开启(重启失效):

1
2
3
echo 1 > /proc/sys/net/ipv4/ip_forward
# 或
sysctl -w net.ipv4.ip_forward=1

永久开启:
编辑 /etc/sysctl.conf 文件,添加或修改以下行:

1
net.ipv4.ip_forward = 1

然后执行 sysctl -p 使配置立即生效。

1.2 理解关键链与表

iptables 的 nat 表是实现端口转发的核心,主要涉及两个链:

  • PREROUTING:数据包进入本机、经过路由判断之前。在此链进行 DNAT(目标地址转换),即修改数据包的“目标IP:端口”。
  • POSTROUTING:数据包经过路由判断、即将发送出网卡之前。在此链进行 SNAT(源地址转换),即修改数据包的“源IP”,确保回包能正确返回。

简单记忆PREROUTING 改“去哪”,POSTROUTING 改“从哪来”。


二、实战场景与配置命令

2.1 场景一:本机端口转发(本地转发)

将发往本机 A 端口的数据,转发到本机的 B 端口。

示例:将本机 7777 端口转发到 6666 端口。

1
2
3
4
5
# 外部访问本机时生效
iptables -t nat -A PREROUTING -p tcp --dport 7777 -j REDIRECT --to-port 6666

# 本机自己访问自己时生效(如 localhost:7777)
iptables -t nat -A OUTPUT -p tcp --dport 7777 -j REDIRECT --to-port 6666

说明

  • -t nat:指定操作 nat 表。
  • -A PREROUTING:在 PREROUTING 链末尾追加规则。
  • -p tcp:匹配 TCP 协议。
  • --dport 7777:匹配目标端口为 7777。
  • -j REDIRECT:执行重定向动作。
  • --to-port 6666:重定向到端口 6666。

2.2 场景二:转发到另一台主机(最常用)

将发往本机(转发机) A 端口的数据,转发到另一台内网/外网主机 BC 端口。

网络拓扑

  • **转发机 (FW)**:IP 192.168.1.168,接收来自客户端的 6666 端口请求。
  • **目标机 (TARGET)**:IP 192.168.1.8,实际提供服务在 7777 端口。

在转发机 (FW) 上执行:

1
2
3
4
5
6
7
8
# 1. 开启路由转发(必须)
sysctl -w net.ipv4.ip_forward=1

# 2. DNAT:修改目标地址 (PREROUTING链)
iptables -t nat -A PREROUTING -p tcp --dport 6666 -j DNAT --to-destination 192.168.1.8:7777

# 3. SNAT:修改源地址,确保回包能经过本机 (POSTROUTING链)
iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.8 --dport 7777 -j SNAT --to-source 192.168.1.168

命令解析

  • -j DNAT --to-destination:执行目标地址转换。
  • -j SNAT --to-source:执行源地址转换,--to-source 通常应设置为转发机的内网IP。如果转发机有公网IP且用于转发公网流量,这里也应填内网IP

2.3 场景三:公网IP主机间的转发

当两台主机都有公网IP时,转发规则与场景二类似,但 SNAT --to-source 必须设置为转发机的内网IP(私网IP),而非公网IP。

错误示例(会导致转发失败):

1
iptables -t nat -A POSTROUTING -p tcp -d <目标机公网IP> --dport XXXX -j SNAT --to-source <转发机公网IP> # 错误!

数据包进入主机后,其目的地址已是内网IP。SNAT 为公网IP会导致回包路径异常。

2.4 场景四:UDP 端口转发

UDP 转发与 TCP 转发命令结构相同,只需将协议 -p tcp 改为 -p udp

UDP 转发完整示例(将本机 100 端口转发到 192.168.1.1:80):

1
2
3
4
5
6
7
# DNAT
iptables -t nat -A PREROUTING -d 10.10.10.1 -p udp --dport 100 -j DNAT --to-destination 192.168.1.1:80
# SNAT (注意参数顺序与TCP示例略有不同,但原理一致)
iptables -t nat -A POSTROUTING -s 192.168.1.1 -p udp --dport 80 -j SNAT --to-source 10.10.10.1:100
# 允许转发 (FORWARD链)
iptables -A FORWARD -o eth0 -d 192.168.1.1 -p udp --dport 80 -j ACCEPT
iptables -A FORWARD -i eth0 -s 192.168.1.1 -p udp --sport 80 -j ACCEPT

三、规则管理与持久化

3.1 查看规则

1
2
3
4
# 查看 nat 表的所有规则(带行号)
iptables -t nat -nL --line-numbers
# 查看 filter 表的 FORWARD 链规则
iptables -L FORWARD --line-numbers

3.2 删除规则

通过规则序号删除是最高效的方式。

1
2
3
4
# 删除 nat 表 PREROUTING 链的第 1 条规则
iptables -t nat -D PREROUTING 1
# 删除 filter 表 FORWARD 链的第 2 条规则
iptables -D FORWARD 2

3.3 保存规则(永久生效)

配置的规则默认在重启后丢失,需要保存。

  • CentOS 6 / RHEL 6
    1
    service iptables save
    规则将保存到 /etc/sysconfig/iptables
  • CentOS 7 / RHEL 7
    首先安装 iptables-services
    1
    2
    3
    yum install iptables-services
    systemctl enable iptables
    systemctl start iptables
    然后使用相同命令保存:service iptables saveiptables-save > /etc/sysconfig/iptables

3.4 清空与重置

1
2
3
4
5
6
7
8
# 清除 nat 表所有链的规则
iptables -t nat -F
# 清除 filter 表所有链的规则
iptables -F
# 清除所有用户自定义链
iptables -X
# 计数器归零
iptables -Z

警告:在生产环境谨慎使用 -F-X,可能导致网络中断。


四、综合示例与排错

4.1 完整示例:实现内网穿透式访问

目标:让客户端能通过转发机 A 访问到后端服务器 B 的 Web 服务。

  • **转发机 (A)**:IP 1.1.1.1(公网),2.2.2.2(转发机内网IP,用于SNAT)
  • **后端服务器 (B)**:IP 192.168.1.100(内网),运行在 80 端口。

在转发机 A 上配置:

1
2
3
4
5
6
7
8
9
# 开启路由转发
echo 1 > /proc/sys/net/ipv4/ip_forward

# 添加转发规则
iptables -t nat -A PREROUTING -p tcp --dport 999 -j DNAT --to-destination 192.168.1.100:80
iptables -t nat -A POSTROUTING -d 192.168.1.100 -p tcp --dport 80 -j SNAT --to-source 2.2.2.2

# 保存规则
service iptables save

结果:客户端访问 http://1.1.1.1:999,实际获取到的是 http://192.168.1.100:80 的内容。

4.2 常见问题与排查

  1. 转发不生效

    • 检查是否已开启 net.ipv4.ip_forward = 1
    • 检查 iptables -t nat -nL 规则是否添加成功。
    • 检查目标服务器的防火墙是否放行了来自转发机 IP 的流量。
    • 使用 tcpdump 在转发机抓包,观察数据包是否被正确修改和转发。
      1
      tcpdump -i any port [源端口] or [目标端口] -nn
  2. 本机无法访问自己的转发端口
    确保添加了 OUTPUT 链的 REDIRECT 规则(见场景一)。

  3. CentOS 7 没有 service iptables save 命令
    安装 iptables-services 包。


五、总结

iptables 端口转发是一项强大而高效的内核级网络功能。掌握其核心步骤:

  1. 开启路由转发 (ip_forward=1)。
  2. 添加 DNAT 规则 (PREROUTING 链),修改“去哪”。
  3. 添加 SNAT 规则 (POSTROUTING 链),修改“从哪来”,确保回包路径正确。
  4. 保存规则,确保重启后生效。

对于简单的本机端口重定向,使用 REDIRECT 动作更为便捷。对于复杂的网络环境,理解 DNAT/SNAT 的原理和 PREROUTING/POSTROUTING 链的工作时机是成功配置的关键。

官方文档(中文)

节点名称 节点IP 配置 系统版本
VIP 192.168.50.220 虚拟IP
k8s-master-221 192.168.50.221 4核 2G debian 11
k8s-master-222 192.168.50.222 4核 2G debian 11
k8s-master-223 192.168.50.223 4核 2G debian 11
k8s-node-224 192.168.50.224 4核 2G debian 11
k8s-node-225 192.168.50.225 4核 2G debian 11

主机配置

时间同步

1

配置 hostname

注意节名称不能重复

1
hostnamectl --static set-hostname k8s-master-221

配置防火墙

1
2
3
4
5
service iptables stop 

iptables -F

systemctl stop firewalld && systemctl disable firewalld

如果需要打开防火墙,执行以下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# master节点执行
ufw allow 6443/tcp
ufw allow 2379/tcp
ufw allow 2380/tcp
ufw allow 10250/tcp
ufw allow 10251/tcp
ufw allow 10252/tcp
ufw allow 10255/tcp
ufw reload

# worker节点执行
ufw allow 10250/tcp
ufw allow 30000:32767/tcp
ufw reload

关闭交换分区

1
2
swapoff -a
set -ri 's/.*swap.*/#&/' /etc/fstab

若需允许交换分区参考官方文档 交换分区的配置

配置hosts

1
2
3
4
5
6
7
cat >> /etc/hosts << EOF
192.168.50.221 k8s-master-221
192.168.50.222 k8s-master-222
192.168.50.223 k8s-master-223
192.168.50.224 k8s-worker-224
192.168.50.225 k8s-worker-225
EOF

开启 bridge 网桥过滤功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 桥接的ipv4流量转到iptables
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 设置所需的 sysctl 参数,参数在重新启动后保持不变
cat << EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1 # 开启网桥模式(必须)
net.bridge.bridge-nf-call-ip6tables = 1 # 开启网桥模式(必须)
net.ipv4.ip_forward = 1 # 转发模式(默认开启)
vm.panic_on_oom = 0 # 开启OOM(默认开启)
vm.swappiness  = 0 # 禁止使用swap空间
vm.overcommit_memory = 1 # 不检查物理内存是否够用
EOF

# 应用 sysctl 参数而不重新启动
sudo sysctl --system

配置 IPVS

1
2
3
4
5
6
7
8
9
10
11
modprobe br_netfilter

cat > /etc/sysconfig/modules/ipvs.modules << EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv
EOF

安装工具

安装 Containerd

1
2
3
4
5
6
7
# 安装
apt update
apt install -y containerd

# 导出默认配置
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1

设置cgroupdriversystemd,编辑 /etc/containerd/config.toml 文件,找到 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 部分,添加一行内容:SystemdCgroup = true

1
sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

重启containerd并设置开机启动

1
2
systemctl restart containerd
systemctl enable containerd

安装 keadm,kubelete,kubectl

1
2
3
4
5
6
# 添加安装源

# 安装
apt update
apt install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

部署高可用(仅 master 节点)

安装

1
apt install keepalived haproxy

修改haproxy配置

/etc/haproxy/haproxy.cfg

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
global
maxconn 2000
ulimit-n 16384
log 127.0.0.1 local0 err
stats timeout 30s

defaults
log global
mode http
option httplog
timeout connect 5000
timeout client 50000
timeout server 50000
timeout http-request 15s
timeout http-keep-alive 15s

frontend monitor-in
bind *:33305
mode http
option httplog
monitor-uri /monitor

frontend k8s-master
bind 0.0.0.0:16443
bind 127.0.0.1:16443
mode tcp
option tcplog
tcp-request inspect-delay 5s
default_backend k8s-master

backend k8s-master
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server k8s-master1 172.16.12.111:6443 check
server k8s-master2 172.16.12.112:6443 check
server k8s-master3 172.16.12.113:6443 check

配置 keepalived

interface # 网卡名称
mcast_src_ip # 节点ip
virtual_ipaddress # vip地址

k8s-master-221配置文件/etc/keepalived/keepalived.conf

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
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh" #健康检查脚本
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER #高可用主1
interface eth0 #网卡名称
mcast_src_ip 192.168.50.221 #该节点 IP
virtual_router_id 51
priority 100 #设置最高级优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

k8s-master-222配置文件/etc/keepalived/keepalived.conf

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
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP #高可用 从1
interface ens33 #网卡名称
mcast_src_ip 192.168.50.222 #该节点 IP
virtual_router_id 51
priority 50 #设置优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

k8s-master-222配置文件/etc/keepalived/keepalived.conf

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
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP #高可用从2
interface ens33 #网卡名称
mcast_src_ip 192.168.50.223 #该节点 IP
virtual_router_id 51
priority 49 #设置优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

健康检查脚本 /etc/keepalived/check_apiserver.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
err=0
for k in $(seq 1 3);do
check_code=$(pgrep haproxy)
if [[ $check_code == "" ]]; then
err=$(expr $err + 1)
sleep 1
continue
else
err=0
break
fi
done

if [[ $err != "0" ]]; then
echo "systemctl stop keepalived"
/usr/bin/systemctl stop keepalived
exit 1
else
exit 0
fi

给监测脚本添加执行权限

1
chmod +x /etc/keepalived/check_apiserver.sh

启动keepalive和haproxy

1
2
3
4
5
6
systemctl daemon-reload
# 启动并设置开机启动
# systemctl enable --now haproxy
systemctl start haproxy && systemctl enable haproxy
# systemctl enable --now keepalived
systemctl start keepalived && systemctl enbale keepalived

测试vip漂移

1
2
3
4
5
# 查看ip与vip
hostname -I

# 测试vip的16443端口是否通
nc -v 192.168.50.220 16443

初始化集群

拉取镜像

1
2
3
4
5
# 查看需要的镜像文件
kubeadm config images list

# 拉取镜像
kubeadm config images pull

master 节点初始化

1
2
3
4
5
# 导出默认初始化配置
kubeadm config print init-defaults > kubeadm-config.yaml

# token过期后生成信息token
kubeadm token create --print-join-command

master 节点加入集群

1
2
3
4
5
6
# master节点需要生成certificate-key
kubeadm init --control-plane-endpoint=192.168.50.220:16443

kubeadm join 192.168.50.220:16443 --token {token} \
--discovery-token-ca-cert-hash {} \
--control-plane --certificate-key {}

worker 节点加入集群

1
2
kubeadm join 192.168.50.220:16643 --token {token} \
--discovery-token-ca-cert-hash {}

从集群种移除节点

1
kubectl delete node {node-name}

配置环境变量,用于访问集群

1
2
3
4
5
cat << EOF >> ~/.bashrc
export KUBECONFIG=/etc/kubernetes/admin/conf
EOF

source ~/.bashrc

查看集群节点状态

1
2
3
4
5
6
# 查看节点状态
kubectl get nodes

# 查看系统组件
kubectl get all -n kube-system -o wide

安装网络组件(只在master-221节点操作)

Calico
Flannel

去除 master节点污点

如果你打算让Master节点也参与到平常的Pod调度(生产环境一般不会这样做,以保证master节点的稳定性),那么你需要使用以下命令将Master节点上的 taint(污点标记)解除

1
kubectl taint nodes --all node-role.kubernetes.io/master-

最后我们使用以下命令查看当前集群的状态,发现Scheduler和Controller Manager组件处理不健康状态:

1
kubectl get cs

解决上述问题需要将每个Master节点上的 /etc/kubernetes/manifests/kube-scheduler.yaml 和 /etc/kubernetes/manifests/kube-controller-manager.yaml 文件中的- –port=0注释掉,然后重启一下各Master节点上的kubelet即可.

测试集群

1
2
3
4
kubectl create deployment nginx --image nginx --replicas 2
kubectl expose deployment nginx --name nginx --type NodePort --port 80 --target-port 80 --node-port 8080

curl http://192.168.50.220:8080

参考
如何用 Kubeadm 在 Debian 11 上安装 Kubernetes 集群 | Linux 中国 - 知乎 (zhihu.com)
Kubernetes多主多从高可用集群部署 - 个人文章 - SegmentFault 思否
搭建多主节点k8s高可用集群(三主两从一VIP)_kubernetes部署多主多从集群-CSDN博客
github - 基于Ubuntu22.04部署KubeEdge-v1.18.0环境 - 云原生_KubeEdge - SegmentFault 思否

概述

Bitwarden 是一款开源免费的密码管理器,支持不限数量的密码存储、跨平台同步和自建服务器部署。核心优势包括完全免费、功能完整、开源透明。


一、核心功能

1.1 密码生成与管理

  • 多类型存储:账号密码、TOTP两步验证、自定义笔记/字段
  • 智能分类:一级文件夹结构(不支持嵌套)
  • 高级密码生成
    • 长度:最长128位
    • 自定义:数字/特殊字符最小数量
    • 可视化:不同字符类型彩色显示
    • 密码短语:最多20个单词,支持分隔符/大小写/数字配置
    • 历史记录:保存所有生成过的密码

1.2 数据迁移

  • 导入支持:Chrome、Firefox、1Password、LastPass等主流管理器
  • 导出格式:JSON、CSV(便于备份和迁移)

1.3 安全报告

  • 泄露检测:对比公开泄露数据库
  • 重复使用检测:标记重复密码
  • 弱密码识别:发现易破解密码
  • HTTPS检查:提醒非HTTPS域名
  • 2FA建议:提示未启用两步验证的网站

1.4 账户保护

  • 指纹短语:5个固定英文单词组成的唯一标识(用于验证服务器/组织成员)
  • KDF强化:增加主密码暴力破解难度
  • 基础防护:后台自动锁定、禁止截图

二、自建部署

2.1 环境准备

CentOS

1
2
3
4
5
6
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Ubuntu

1
2
3
4
5
6
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose
sudo systemctl enable --now docker

2.2 部署方案

官方镜像

  1. Bitwarden官网 申请 installation idkey
  2. 执行安装脚本:
    1
    2
    3
    curl -Lso bitwarden.sh https://go.btwrdn.co/bw-sh && chmod +x bitwarden.sh
    ./bitwarden.sh install # 按提示输入域名和密钥
    ./bitwarden.sh start

第三方镜像(推荐)

使用 bitwarden_rs(Rust重写版):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 拉取镜像
docker pull bitwardenrs/server:latest

# 测试运行
docker run -d --name bitwarden -v /bw-data/:/data/ -p 80:80 bitwardenrs/server:latest

# 生产配置(关键参数)
docker run -d --name bitwarden \
-v /bw-data/:/data/ \
-e SIGNUPS_ALLOWED=false \ # 禁止注册(先注册账号!)
-e ADMIN_TOKEN=复杂随机字符串 \ # 启用管理页面
-e SHOW_PASSWORD_HINT=false \ # 隐藏密码提示
-e WEBSOCKET_ENABLED=true \ # 启用通知
-e DOMAIN=https://your-domain.com \ # 必须设置域名
-p 80:80 -p 3012:3012 \ # 通知端口
bitwardenrs/server:latest

2.3 Nginx反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
server_name your-domain.com;
client_max_body_size 128M;

location / {
proxy_pass http://localhost:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /notifications/hub {
proxy_pass http://localhost:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /notifications/hub/negotiate {
proxy_pass http://localhost:80;
}

listen 80;
}

注意:生产环境需配置SSL证书(推荐certbot自动配置)


三、安全最佳实践

  1. 首次部署:先注册个人账号再禁用注册(SIGNUPS_ALLOWED=false
  2. 管理页面:设置高强度ADMIN_TOKEN并启用HTTPS
  3. 邮件服务(可选):
    1
    2
    3
    4
    5
    6
    -e SMTP_HOST=smtp.domain.tld \
    -e SMTP_FROM=bitwarden@domain.tld \
    -e SMTP_PORT=587 \
    -e SMTP_SSL=true \
    -e SMTP_USERNAME=username \
    -e SMTP_PASSWORD=password

总结

Bitwarden 通过开源免费、功能完整和自建支持三大优势,成为个人密码管理的理想选择。自建部署时推荐使用第三方Rust镜像,配合Nginx反向代理和严格的安全配置,可兼顾便利性与数据隐私。

0%