TT Lab
开始
学习 学习路径 课程

LFCS — Linux 基金会认证系统管理员

从 systemd 定时器到 podman — 真正搭建服务与网络

在 TT Lab 中继续学习

目标

systemd 服务与定时器、nginx 反向代理、iptables DNAT/DROP、dummy 接口上的桥接与 bond 以及 netplan、chrony 与时区、git 合并、无需注册表的 podman 镜像、永久生效的 sysctl、负载指标——这些在 LFCS 的 Operations 和 Networking 领域中被 Pod 禁用的内容,将在 Ubuntu 24.04 VM 上真实运行。

为什么重要

LFCS 看的是“结果”,而不是“说明”。单元文件写得再好,只要 systemctl is-enabled 的结果是 disabled,就是 0 分;nginx 配置语法即使正确,只要默认站点占着 80 端口,代理就无法工作。本实验的评分器不只检查文件,还会用 systemctl is-active、curl、iptables -S、/sys/class/net、sysctl -n、podman inspect 查看系统当前的状态。

有三条原则会反复出现。第一,enable 与 start 是不同的问题(是否要在开机时启用 / 现在是否已在运行)。第二,写了文件与内核、守护进程知道这件事是两回事(sysctl --system、daemon-reload、nginx reload)。第三,要留出退路——如果把 iptables 的 INPUT 默认策略改成 DROP,或者封掉 8899、22,评分代理就会断开,从下一步开始会出现找不出原因的失败。

会话从 60 分钟开始,最长可延长到 180 分钟,会话结束后 VM 会消失。本实验预计 85 分钟,所以开始时请先延长一次。

步骤

  1. 编写 /usr/local/bin/lfcs-heartbeat.sh,使其把当前时间追加一行到 /var/log/lfcs-heartbeat.log,并在 /etc/systemd/system/ 中把它写成 Type=oneshot 的 lfcs-heartbeat.service,以及每分钟调用该服务的 lfcs-heartbeat.timer。定时器必须同时完成 enable 和 start,并且要手动启动一次服务,使日志中至少有一行。
  2. 在 /srv/backend/index.html 中写入 backend ok 这段文字,把 python3 -m http.server 8000 --directory /srv/backend 做成 systemd 服务 lfcs-backend.service,并 enable、start。然后创建 /etc/nginx/sites-available/lfcs-proxy,让 nginx 在 80 端口反向代理到这个后端,并启用它(停用默认站点)。curl http://127.0.0.1/ 必须返回 backend ok。
  3. 用 iptables 添加两条规则。(1)在 nat 表的 PREROUTING 中,把进入 tcp 8080 的流量转发到 80 的 DNAT(或 REDIRECT)。(2)在 filter 表的 INPUT 中,对所有接口的 tcp 9999 执行 DROP。INPUT 的默认策略保持 ACCEPT 不变,不要动 8899 和 22(评分代理和 SSH)。最后把 iptables-save 的输出保存到 /root/lfcs/net/rules.v4。
  4. 创建 dummy 接口 dummy0、dummy1、dummy2。把 dummy0 加入桥接 br0,并给 br0 分配 192.0.2.1/24。以 active-backup 模式创建 bond bond0,并把 dummy1 与 dummy2 加入其中(不要动 eth0 之类的真实接口)。用 netplan 语法把同样的配置写入 /root/lfcs/net/99-lab.yaml,但不要应用它。
  5. 在 /etc/chrony/chrony.conf 中添加 server time.cloudflare.com iburst 这一行,并重启 chrony。把时区改为 Asia/Seoul。然后把 chronyc sources 保存到 /root/lfcs/time/sources.txt,把 chronyc tracking 保存到 /root/lfcs/time/tracking.txt,把 timedatectl 保存到 /root/lfcs/time/timedatectl.txt。(这台 VM 的 UDP 123 被屏蔽,同步本身可能无法完成——评分的是配置和状态。)
  6. 在 /root/lfcs/repo 中创建 git 仓库(默认分支 main),并提交 README.md。在分支 feature 上添加 feature.txt 并提交,然后回到 main,以保留合并提交的方式(--no-ff)进行合并。工作树必须保持干净。
  7. 把只包含 /bin/busybox(busybox-static)的根文件系统打成 tar,用 podman import 导入,创建 localhost/lfcs-busybox:1 镜像(不从注册表拉取)。用该镜像以 --network none 运行名为 bb1 的容器,让它输出 hello-from-busybox,并把输出保存到 /root/lfcs/bb/run.txt。不要删除容器(退出码必须为 0)。
  8. 在 /etc/sysctl.d/90-lfcs.conf 中写入 net.ipv4.ip_forward = 1、vm.swappiness = 10、net.core.somaxconn = 1024 三个值,并同时应用到当前内核。应用之后,用 sysctl 读取这三个值,保存到 /root/lfcs/sysctl/runtime.txt。
  9. 把 uptime 的输出保存到 /root/lfcs/perf/uptime.txt,把 top -bn1 的前 15 行保存到 /root/lfcs/perf/top.txt。然后把 /root/lfcs/perf/load.txt 写成两行:NPROC=<이 VM 의 CPU 개수>、LOAD1=<1분 로드 평균(/proc/loadavg 첫 값)>(占位符依次为本 VM 的 CPU 数量、1 分钟平均负载,即 /proc/loadavg 的第一个值)。

参考

服务单元与定时器

编写 /usr/local/bin/lfcs-heartbeat.sh,使其把当前时间追加一行到 /var/log/lfcs-heartbeat.log,并在 /etc/systemd/system/ 中把它写成 Type=oneshot 的 lfcs-heartbeat.service,以及每分钟调用该服务的 lfcs-heartbeat.timer。定时器必须同时完成 enable 和 start,并且要手动启动一次服务,使日志中至少有一行。

定时器需要 [Timer] 中的 OnBootSec/OnUnitActiveSec,以及 [Install] 中的 WantedBy=timers.target。写完单元文件之后,要先执行 daemon-reload。is-enabled 和 is-active 是不同的问题。

nginx 反向代理

在 /srv/backend/index.html 中写入 backend ok 这段文字,把 python3 -m http.server 8000 --directory /srv/backend 做成 systemd 服务 lfcs-backend.service,并 enable、start。然后创建 /etc/nginx/sites-available/lfcs-proxy,让 nginx 在 80 端口反向代理到这个后端,并启用它(停用默认站点)。curl http://127.0.0.1/ 必须返回 backend ok。

关键是 server 块中的 listen 80 default_server,以及 location / { proxy_pass http://127.0.0.1:8000; }。如果不删除 sites-enabled/default,两个 default_server 会冲突。用 nginx -t 确认之后再 reload。

用 iptables 实现 DNAT 与 DROP

用 iptables 添加两条规则。(1)在 nat 表的 PREROUTING 中,把进入 tcp 8080 的流量转发到 80 的 DNAT(或 REDIRECT)。(2)在 filter 表的 INPUT 中,对所有接口的 tcp 9999 执行 DROP。INPUT 的默认策略保持 ACCEPT 不变,不要动 8899 和 22(评分代理和 SSH)。最后把 iptables-save 的输出保存到 /root/lfcs/net/rules.v4。

DNAT 用 -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination <本 VM 的 IP>:80(或 -j REDIRECT --to-ports 80)。DROP 用 -A INPUT -p tcp --dport 9999 -j DROP。发给自己的数据包不会经过 PREROUTING,所以 DNAT 通过规则是否存在来确认,DROP 则通过访问 127.0.0.1:9999 得到的是“超时”而不是“拒绝”来确认。

dummy 上的桥接与 bond,以及 netplan

创建 dummy 接口 dummy0、dummy1、dummy2。把 dummy0 加入桥接 br0,并给 br0 分配 192.0.2.1/24。以 active-backup 模式创建 bond bond0,并把 dummy1 与 dummy2 加入其中(不要动 eth0 之类的真实接口)。用 netplan 语法把同样的配置写入 /root/lfcs/net/99-lab.yaml,但不要应用它。

ip link add <名称> type dummy|bridge|bond。从属接口用 ip link set master 。bond 的从属接口在加入之前必须处于 down 状态。用 /sys/class/net/bond0/bonding/mode 确认 bond 模式。

chrony 与时区

在 /etc/chrony/chrony.conf 中添加 server time.cloudflare.com iburst 这一行,并重启 chrony。把时区改为 Asia/Seoul。然后把 chronyc sources 保存到 /root/lfcs/time/sources.txt,把 chronyc tracking 保存到 /root/lfcs/time/tracking.txt,把 timedatectl 保存到 /root/lfcs/time/timedatectl.txt。(这台 VM 的 UDP 123 被屏蔽,同步本身可能无法完成——评分的是配置和状态。)

timedatectl set-timezone Asia/Seoul。Ubuntu 中 chrony 的服务名称是 chrony。chronyc sources 的第一列为 ? 表示还没有收到响应,在这里是正常的。

git 基础——分支与合并

在 /root/lfcs/repo 中创建 git 仓库(默认分支 main),并提交 README.md。在分支 feature 上添加 feature.txt 并提交,然后回到 main,以保留合并提交的方式(--no-ff)进行合并。工作树必须保持干净。

git init -b main。提交需要 user.name/user.email(git config)。合并提交可以用 git log --merges 看到。如果以 fast-forward 方式合并,就不会有合并提交。

不用注册表,创建并运行 podman 镜像

把只包含 /bin/busybox(busybox-static)的根文件系统打成 tar,用 podman import 导入,创建 localhost/lfcs-busybox:1 镜像(不从注册表拉取)。用该镜像以 --network none 运行名为 bb1 的容器,让它输出 hello-from-busybox,并把输出保存到 /root/lfcs/bb/run.txt。不要删除容器(退出码必须为 0)。

复制 rootfs/bin/busybox,然后 tar -C rootfs -cf bb.tar . → podman import bb.tar localhost/lfcs-busybox:1。运行用 podman run --name bb1 --network none localhost/lfcs-busybox:1 /bin/busybox echo hello-from-busybox。本实验中的 podman 以 root 运行。

让内核参数永久生效

在 /etc/sysctl.d/90-lfcs.conf 中写入 net.ipv4.ip_forward = 1、vm.swappiness = 10、net.core.somaxconn = 1024 三个值,并同时应用到当前内核。应用之后,用 sysctl 读取这三个值,保存到 /root/lfcs/sysctl/runtime.txt。

sysctl --system 会按顺序读取并应用 /etc/sysctl.d/*.conf。只写文件的话,在重启之前内核并不知道。用 sysctl -n <键> 查看当前值。

留下负载指标

把 uptime 的输出保存到 /root/lfcs/perf/uptime.txt,把 top -bn1 的前 15 行保存到 /root/lfcs/perf/top.txt。然后把 /root/lfcs/perf/load.txt 写成两行:NPROC=<이 VM 의 CPU 개수>、LOAD1=<1분 로드 평균(/proc/loadavg 첫 값)>(占位符依次为本 VM 的 CPU 数量、1 分钟平均负载,即 /proc/loadavg 的第一个值)。

top -bn1 以批处理模式只输出一次。/proc/loadavg 的第一个字段是 1 分钟平均值,CPU 数量用 nproc 获取。平均负载必须与 CPU 数量对比才有意义。