LFCS — 리눅스 재단 시스템 관리자 · 서비스와 네트워크를 실제로 — systemd·nginx·iptables·chrony·podman · 이론
enable 과 start 는 다른 질문이다 — systemd·nginx·iptables·bond·chrony·podman
한 줄 요약
리눅스 관리의 절반은 "지금 되는가" 와 "다음에도 되는가" 를 따로 묻는 일이다. systemctl start 와 enable, sysctl -w 와 /etc/sysctl.d, iptables -A 와 iptables-save, ip link 와 netplan — 짝마다 앞은 지금, 뒤는 다음 부팅이다. LFCS 는 둘 다 요구하고, 이 모듈의 실습은 둘 다 채점한다.
왜 이게 필요했나
앞 모듈들(lfcs-operating-systems, lfcs-networking)은 파드에서 돌아 유닛 파일을 쓰고 systemd-analyze verify 로 검증하는 데까지가 전부였다. 타이머가 실제로 도는지, nginx 가 정말 백엔드로 넘기는지, iptables 규칙이 패킷을 버리는지는 볼 수 없었다. 이 모듈은 우분투 24.04 VM 에서 root 로 돈다. 채점기가 systemctl is-active, curl, iptables -S, /sys/class/net, sysctl -n, podman inspect 로 지금 시스템을 본다.
한 가지 경계가 있다. VM 은 밖으로 DNS 와 공인 인터넷 80/443 만 열려 있고 UDP 123 은 막혀 있다. 그래서 chrony 는 서버를 알아도 동기화하지 못하며, 그 단계는 설정·서비스 상태·출력 파일로 채점한다. 그리고 채점 에이전트가 8899/tcp 로 들어오므로 iptables 의 INPUT 기본 정책을 DROP 으로 바꾸거나 8899·22 를 막으면 그 순간부터 채점이 닿지 않는다.
어떻게 동작하나
타이머는 서비스를 부르는 유닛이다. [systemd.timer(5)](https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html) 에 따르면 .timer 유닛은 Unit= 을 생략하면 이름이 같은 .service 를 활성화한다. OnBootSec= 은 부팅 뒤 처음 언제, OnUnitActiveSec= 은 그 서비스가 마지막으로 활성화된 뒤 얼마마다이다. 불리는 쪽은 [systemd.service(5)](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html) 의 Type=oneshot 이어야 "한 번 돌고 끝나는 일" 로 취급된다. 그리고 enable 은 [Install] 절의 WantedBy=timers.target 을 보고 심볼릭 링크를 만드는 일이고, start 는 지금 켜는 일이다. --now 가 둘을 한 번에 한다. is-enabled 와 is-active 가 다른 답을 낼 수 있다는 것이 이 단계의 전부다.
리버스 프록시는 앞에서 받아 뒤로 넘긴다. [ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) 의 proxy_pass 가 그 한 줄이다. 우분투 패키지는 sites-available 에 설정을 두고 sites-enabled 의 심볼릭 링크로 켠다. 기본 사이트도 default_server 로 80 을 잡고 있으므로 지우지 않으면 nginx -t 가 중복을 거부한다. 백엔드는 python3 -m http.server 면 충분하지만 손으로 & 로 띄우면 세션이 끊길 때 함께 죽는다. 유닛으로 만들어 systemd 가 붙들게 한다.
넷필터는 테이블과 체인이다. [iptables(8)](https://man7.org/linux/man-pages/man8/iptables.8.html) 의 nat 테이블은 주소를 바꾸고 filter 테이블은 통과·차단을 정한다. PREROUTING 은 패킷이 들어오자마자(라우팅 전), INPUT 은 이 호스트 앞으로 온 것, OUTPUT 은 이 호스트가 만든 것이다. 그래서 자기 자신에게 보낸 패킷은 PREROUTING 을 지나지 않는다 — curl localhost:8080 으로는 DNAT 를 볼 수 없다. [iptables-extensions(8)](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) 의 DNAT 는 --to-destination 으로 목적지를 바꾸고, REDIRECT 는 그 특수형이다. DROP 은 응답 없이 버리므로 클라이언트는 거부가 아니라 시간 초과를 본다. 규칙은 재부팅하면 사라지고, iptables-save 출력이 다음 부팅의 재료다. 우분투 24.04 의 iptables 는 nf_tables 백엔드 위에서 돈다는 것도 알아 두자.
브리지와 본드. 브리지는 소프트웨어 스위치이고 본드는 여러 링크를 하나로 묶는다. [ip-link(8)](https://man7.org/linux/man-pages/man8/ip-link.8.html) 로 type dummy|bridge|bond 를 만들고 master 로 멤버를 넣는다. [커널 bonding 문서](https://docs.kernel.org/networking/bonding.html) 의 active-backup 은 하나만 쓰고 죽으면 다른 것으로 넘어가는 모드라 스위치 설정이 필요 없고, 슬레이브는 넣기 전에 down 이어야 한다. 실제 NIC 이 하나뿐인 VM 에서 멤버는 dummy 인터페이스로 만든다 — 커널이 보기에는 진짜 인터페이스다. 같은 구성을 선언형으로 적는 것이 [netplan YAML](https://netplan.readthedocs.io/en/stable/netplan-yaml/) 의 bridges·bonds 절이고, 이 실습은 /etc/netplan 밖에 두어 적용하지 않는다. 실제 NIC 을 잃으면 되돌아갈 길이 없기 때문이다.
시각과 시간대는 다른 것이다. 커널 시계는 언제나 UTC 이고 시간대는 표시 규칙이다. [timedatectl](https://www.freedesktop.org/software/systemd/man/latest/timedatectl.html) 의 set-timezone 이 뒤를 바꾸고, [chrony.conf](https://chrony-project.org/doc/4.5/chrony.conf.html) 의 server … iburst 가 앞을 맞춘다. iburst 는 시작 직후 패킷을 몰아 보내 첫 동기화를 앞당기는 옵션이다. [chronyc](https://chrony-project.org/doc/4.5/chronyc.html) 의 sources 첫 열이 ? 이면 아직 응답을 받지 못한 것이고, 이 VM 에서는 그것이 정상이다.
컨테이너 이미지는 tar 다. [podman-import(1)](https://docs.podman.io/en/latest/markdown/podman-import.1.html) 은 tarball 하나를 한 층짜리 이미지로 만든다. Dockerfile 도 레지스트리도 없이 /bin/busybox 하나로 이미지가 된다는 것을 손으로 보면, 이미지가 무엇인지가 남는다. 이 실습의 podman 은 root 로 돌고 [podman-run(1)](https://docs.podman.io/en/latest/markdown/podman-run.1.html) 의 --network none 으로 네트워크 설정 없이 실행한다.
sysctl 은 두 번 한다. [sysctl(8)](https://man7.org/linux/man-pages/man8/sysctl.8.html) 의 -w 는 지금 커널을 바꾸고 [sysctl.d(5)](https://man7.org/linux/man-pages/man5/sysctl.d.5.html) 의 파일은 부팅 때 읽힌다. sysctl --system 이 표준 디렉터리를 순서대로 적용하므로, 파일을 쓴 뒤 그것을 돌리면 지금과 다음 부팅이 함께 맞는다. 로드 평균은 [proc_loadavg(5)](https://man7.org/linux/man-pages/man5/proc_loadavg.5.html) 가 설명하듯 실행 중이거나 실행 대기(그리고 디스크 I/O 대기) 중인 작업 수의 평균이고, CPU 개수와 견줘야 의미가 생긴다.
현장에서 만나는 모습
"cron 에 넣었는데 안 돈다" 의 systemd 판이 "타이머를 start 만 했다" 이다. 재부팅 뒤 systemctl list-timers 에 없으면 is-enabled 부터 본다. 반대로 enable 만 하고 start 를 안 하면 다음 부팅까지 아무것도 안 돈다.
"방화벽 규칙을 넣었는데 재부팅하니 사라졌다" 는 iptables 의 고전이다. 규칙은 커널 메모리에 있고, 저장한 파일을 부팅 때 되살리는 것은 별도의 서비스(우분투는 iptables-persistent 의 rules.v4)의 일이다. 실습의 마지막에 iptables-save 를 파일로 남기는 이유다.
노드가 "느리다" 는 신고를 받으면 uptime 의 로드 평균을 nproc 으로 나눈다. 4코어에서 4.0 은 꽉 찬 것이고 1코어에서 4.0 은 셋이 기다리는 것이다. top 의 wa 가 크면 디스크, st 가 크면 하이퍼바이저 이웃이 CPU 를 먹는 것이다.
다음 실습에서 할 것
oneshot 서비스 + 1분 타이머(enable·start·로그) → python 백엔드 유닛 + nginx 프록시(기본 사이트 비활성) → DNAT 8080→80 과 9999 DROP(8899·22 는 그대로) + iptables-save → dummy 위의 br0·bond0 와 netplan 문서 → chrony 서버·시간대·출력 파일 → git init·branch·--no-ff 병합 → busybox tar 를 podman import 해 실행 → sysctl 파일 + --system → uptime·top·NPROC. 단계마다 "지금" 과 "다음 부팅" 을 둘 다 챙겨라. 채점기가 둘 다 본다.