LFCS — Linux Foundation認定システム管理者
systemd タイマーから podman まで — サービスとネットワークを実際に作る
한국어 원문으로 표시합니다.
목표
systemd 서비스와 타이머, nginx 리버스 프록시, iptables DNAT/DROP, dummy 인터페이스 위의 브리지·본드와 netplan, chrony 와 시간대, git 병합, 레지스트리 없는 podman 이미지, 영구 sysctl, 부하 지표 — LFCS 의 Operations 와 Networking 도메인에서 파드가 막아 두었던 것을 우분투 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분으로 잡혀 있으니 시작하면서 한 번 연장해 두세요.
단계
/usr/local/bin/lfcs-heartbeat.sh가 현재 시각을/var/log/lfcs-heartbeat.log에 한 줄 덧붙이게 만들고, 이것을Type=oneshot인lfcs-heartbeat.service와 1분마다 그 서비스를 부르는lfcs-heartbeat.timer로/etc/systemd/system/에 작성하세요. 타이머는 enable 과 start 둘 다 되어 있어야 하고, 서비스를 한 번 직접 시작해 로그에 최소 한 줄이 있어야 합니다./srv/backend/index.html에backend ok라는 문구를 넣고,python3 -m http.server 8000 --directory /srv/backend를 systemd 서비스lfcs-backend.service로 만들어 enable·start 하세요. 그런 다음 nginx 가 80 포트에서 이 백엔드로 리버스 프록시하도록/etc/nginx/sites-available/lfcs-proxy를 만들고 활성화(기본 사이트는 비활성화)하세요.curl http://127.0.0.1/이backend ok를 돌려줘야 합니다.- 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에 저장하세요. - 더미 인터페이스
dummy0,dummy1,dummy2를 만드세요. 브리지br0에dummy0을 넣고br0에192.0.2.1/24를 주세요. 본드bond0을active-backup모드로 만들어dummy1과dummy2를 넣으세요(eth0 같은 실제 인터페이스는 건드리지 않습니다). 같은 구성을 netplan 문법으로/root/lfcs/net/99-lab.yaml에 적되 적용하지는 마세요. /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 이 막혀 있어 동기화 자체는 안 될 수 있습니다 — 설정과 상태가 채점 대상입니다.)/root/lfcs/repo에 git 저장소를 만들고(기본 브랜치main)README.md를 커밋하세요. 브랜치feature에서feature.txt를 추가해 커밋한 뒤main으로 돌아와 병합 커밋이 남도록(--no-ff) 병합하세요. 작업 트리는 깨끗해야 합니다./bin/busybox(busybox-static) 하나만 담은 루트 파일시스템을 tar 로 묶어podman import로localhost/lfcs-busybox:1이미지를 만드세요(레지스트리에서 받지 않습니다). 그 이미지로 이름이bb1인 컨테이너를--network none으로 실행해hello-from-busybox를 출력하게 하고, 그 출력을/root/lfcs/bb/run.txt에 저장하세요. 컨테이너는 지우지 마세요(종료 코드가 0 이어야 합니다)./etc/sysctl.d/90-lfcs.conf에net.ipv4.ip_forward = 1,vm.swappiness = 10,net.core.somaxconn = 1024세 값을 적고 지금 커널에도 적용하세요. 적용 뒤 세 값을sysctl로 읽어/root/lfcs/sysctl/runtime.txt에 저장하세요.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 첫 값)>.
참고
- 유닛을 쓰거나 고친 뒤에는
systemctl daemon-reload.systemctl list-timers --all로 타이머가 잡혔는지 봅니다. - nginx:
nginx -t→systemctl reload nginx.sites-enabled/default를 지우지 않으면default_server가 둘이 됩니다. - iptables:
iptables -t nat -S PREROUTING,iptables -S INPUT.-C로 규칙이 이미 있는지 물을 수 있습니다. - 본드 슬레이브는
ip link set dummy1 down뒤에master bond0. 모드 확인은cat /sys/class/net/bond0/bonding/mode. - 이 VM 은 밖으로 DNS 와 80/443 만 열려 있습니다.
chronyc sources의?는 정상이고, podman 은 레지스트리에서 받지 않습니다. - 흔한 실수 1: 타이머를
start만 하고enable을 빠뜨리는 경우 — 재부팅하면 사라집니다. - 흔한 실수 2:
lvextend처럼 sysctl 도 파일만 쓰고sysctl --system을 빼서 커널 값이 그대로인 경우. - 흔한 실수 3: 병합을 fast-forward 로 해서 병합 커밋이 없는 경우.
서비스 유닛과 타이머
/usr/local/bin/lfcs-heartbeat.sh 가 현재 시각을 /var/log/lfcs-heartbeat.log 에 한 줄 덧붙이게 만들고, 이것을 Type=oneshot 인 lfcs-heartbeat.service 와 1분마다 그 서비스를 부르는 lfcs-heartbeat.timer 로 /etc/systemd/system/ 에 작성하세요. 타이머는 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 하세요. 그런 다음 nginx 가 80 포트에서 이 백엔드로 리버스 프록시하도록 /etc/nginx/sites-available/lfcs-proxy 를 만들고 활성화(기본 사이트는 비활성화)하세요. 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 위의 브리지와 본드, 그리고 netplan
더미 인터페이스 dummy0, dummy1, dummy2 를 만드세요. 브리지 br0 에 dummy0 을 넣고 br0 에 192.0.2.1/24 를 주세요. 본드 bond0 을 active-backup 모드로 만들어 dummy1 과 dummy2 를 넣으세요(eth0 같은 실제 인터페이스는 건드리지 않습니다). 같은 구성을 netplan 문법으로 /root/lfcs/net/99-lab.yaml 에 적되 적용하지는 마세요.
ip link add <이름> type dummy|bridge|bond. 슬레이브는 ip link set master . 본드 슬레이브는 넣기 전에 down 이어야 합니다. 본드 모드는 /sys/class/net/bond0/bonding/mode 로 확인합니다.
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. 우분투의 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 이미지를 만드세요(레지스트리에서 받지 않습니다). 그 이미지로 이름이 bb1 인 컨테이너를 --network none 으로 실행해 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 첫 값)>.
top -bn1 은 배치 모드로 한 번만 찍습니다. /proc/loadavg 의 첫 필드가 1분 평균이고, CPU 개수는 nproc 입니다. 로드 평균은 CPU 개수와 견줘야 의미가 있습니다.