이슈
팀장님과 같은 버스를 타고 지하철에서 갈라졌는데, 서비스 장애 모니터링 슬랙 채널에서 알림이 쏟아지기 시작했다.

핸드폰으로 빠르게 영향 범위(Blast Radius) 를 파악했다. 다행히 장애 도메인은 예상보다 작았다. 웹서버 티어(Tier)와 해당 DB 인스턴스에만 국한되었고, 권위 네임서버(Authoritative DNS)와 기타 부가 서비스들은 정상 응답을 유지하고 있었다. 장애 인지까지 MTTD(Mean Time To Detect) 약 3~5분 모니터링 시스템이 역할을 잘 해줘서 일찍 알았다.
용어
- Blast Radius (영향 범위): 장애가 실제로 영향을 미치는 시스템의 범위. 좁을수록 복구가 쉽다.
- MTTD (Mean Time To Detect): 장애가 발생한 시점부터 인지하는 데까지 걸린 평균 시간. 모니터링 시스템의 성능 지표로 쓰인다.
- 권위 네임서버 (Authoritative DNS): 특정 도메인에 대한 DNS 레코드 원본을 보유하고 최종 응답하는 서버. 이게 살아있으면 도메인 접근 자체는 가능하다.
자취방에 도착하자마자 노트북 열고 이슈를 처리하며 결과적으로 MTTR(Mean Time To Recover) 약 20분으로 모든 서비스를 원상 복구했다. 이후 1시간은 연관 서비스 안정성 모니터링 및 Docker Swarm 워크로드 재배치(Redistribution) 작업을 했다.
용어
- MTTR (Mean Time To Recover): 장애 인지 후 서비스가 완전히 복구되기까지 걸린 평균 시간. 운영팀의 대응 역량을 나타내는 핵심 SRE 지표다.
이번 포스팅은 원인 분석과 해결 방법을 정리하는 과정에 작성해봤다.
용어
- 온프레미스 베어메탈 (On-Premises Bare Metal): AWS/GCP 같은 퍼블릭 클라우드가 아닌, IDC(Internet Data Center)에 직접 입주시킨 물리 서버. 하이퍼바이저 없이 OS가 하드웨어 위에 직접 올라간다. 가성비는 좋지만 관리는 직접해야한다.
전체 장애 흐름 한눈에 보기

인프라 구성 개요
본격적인 트러블슈팅에 앞서, 이 서버가 어떤 환경인지 간략히 정리한다.

용어
- 사설 L2 세그먼트 (Private L2 Segment): 공인 인터넷과 격리된 내부 전용 네트워크. 서버 간 통신(동기화, DB 복제, Swarm heartbeat 등)을 외부 노출 없이 처리한다. 여기서는 별도의 물리 NIC(Network Interface Card)를 통해 구성되어 있다.
- Docker Swarm: Docker 내장 컨테이너 오케스트레이터. k8s보다 운영 복잡도가 낮아 소규모 베어메탈 환경에서 가성비가 좋다.
1단계: dockerd 데몬 크래시 — overlay2 그래프드라이버 마운트 실패
증상
SSH 세션 연결 후 도커 데몬 프로세스 상태를 조회했다.
$ systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled)
Active: failed (Result: exit-code)
Process: ... ExecStart=/usr/bin/dockerd -H fd:// (code=exited, status=1/FAILURE)
용어
- systemd: 현대 리눅스 배포판의 PID 1 프로세스. 서비스의 시작/종료/재시작 정책을 관리한다.
failed상태는 설정된 재시작 횟수를 모두 소진했다는 뜻이다. - dockerd: Docker 데몬 프로세스. 컨테이너 런타임, 이미지 레이어 관리, 네트워크 네임스페이스 등을 총괄하는 백그라운드 서비스다.
journalctl 로그에는 의미있는 정보를 찾기 쉽지 않아서 데몬을 직접 포그라운드 모드(foreground mode) 로 실행해서 풀 스택 트레이스를 확인했다. 이 방법으로 overlay2 스토리지 드라이버 초기화 실패 에러를 확인할 수 있었다.
$ sudo dockerd
# ...
ERRO[0000] failed to start daemon: error initializing graphdriver:
driver not supported: overlay2
용어
- 그래프드라이버 / overlay2 (Graph Driver / overlay2): Docker가 컨테이너 레이어(이미지 + 쓰기 레이어)를 호스트 파일시스템에 마운트하는 방식.
overlay2는 리눅스 커널의 OverlayFS를 사용한다. 이 기능이 커널 레벨에서 지원되지 않으면 Docker는 아예 뜨지 못한다.
원인 진단
현재 부팅된 커널을 확인했다.
$ uname -r
6.8.0-51-generic
6.8.0-51이다. 그런데 이상했다. 분명 표준 Ubuntu Generic 커널인데 왜 모듈이 없는 걸까.
장애 복구 후 서버의 /var/log/apt/history.log를 정밀 분석했더니, 진짜 원인이 드러났다.
# /var/log/apt/history.log — 장애 발생 약 2주 전 기록
# 2026-04-03 06:39: unattended-upgrades가 신규 커널 자동 설치
Install: linux-image-6.8.0-107-generic (6.8.0-107.107, automatic)
linux-modules-6.8.0-107-generic (6.8.0-107.107, automatic)
linux-modules-extra-6.8.0-107-generic (6.8.0-107.107, automatic)
Upgrade: linux-generic (6.8.0-106.106 → 6.8.0-107.107)
# 2026-04-03 06:40: 자동 설치 직후 구 버전 패키지 자동 삭제(!)
Remove: linux-image-6.8.0-106-generic (6.8.0-106.106)
linux-modules-6.8.0-106-generic (6.8.0-106.106)
linux-modules-extra-6.8.0-106-generic (6.8.0-106.106)
unattended-upgrades가 6.8.0-107 커널을 설치하고 구 버전(6.8.0-106) 모듈 패키지를 자동 삭제했다. 여기까지는 정상 동작이다.
문제는 GRUB의 기본 부팅 엔트리에 있었다. GRUB은 업그레이드된 6.8.0-107이 아닌 훨씬 더 오래된 6.8.0-51을 계속 가리키고 있었고, 6.8.0-51용 linux-modules-extra는 서버에 단 한 번도 설치된 적이 없었다. 즉, 재부팅 후 6.8.0-51 커널로 부팅되었으나 해당 버전의 모듈 파일이 디스크에 아예 없는 상태였던 것이다.
[타임라인 정리]
2026-04-03 06:40 unattended-upgrades: 6.8.0-107 설치, 6.8.0-106 모듈 삭제
장애 당일 재부팅 GRUB 기본값: 6.8.0-51 로 부팅 (107도, 106도 아닌 훨씬 오래된 버전)
재부팅 직후 /lib/modules/6.8.0-51-generic/ 디렉토리 내 모듈 파일 없음
→ overlay2, nf_tables 모듈 로드 실패 → Docker·방화벽 동시 기동 실패
용어
- unattended-upgrades: Ubuntu에서 보안 패치를 자동으로 설치하는 데몬. 커널 업데이트 시 새 버전 설치와 구 버전 자동 삭제(autoremove)를 함께 수행한다. 기본 설정은 가장 최신 커널 2개만 디스크에 유지한다.
- GRUB_DEFAULT / GRUB 부팅 엔트리:
/etc/default/grub의GRUB_DEFAULT값이0(최신 커널)이 아닌 특정 버전으로 고정되어 있으면, 아무리 새 커널이 설치되어도 항상 고정된 버전으로 부팅된다. 이 서버는6.8.0-51이 고정되어 있었다.
2단계: 방화벽 서브시스템까지 연쇄 장애
Docker 만 문제가 아니었다. 사설 세그먼트와 퍼블릭 인터페이스 트래픽을 통제하는 방화벽 스크립트(/etc/rc.firewall)를 수동 실행했더니 수십 줄의 에러가 쏟아졌다.
$ sudo /etc/rc.firewall
ip6tables v1.8.9 (nf_tables): Protocol not supported (nft)
ip6tables v1.8.9 (nf_tables): Protocol not supported (nft)
ip6tables v1.8.9 (nf_tables): Protocol not supported (nft)
# .....
용어
- iptables / nftables: 리눅스 커널 내부의 패킷 필터링 프레임워크.
iptables는 구형 인터페이스,nftables는 2014년 이후 도입된 차세대 방화벽 프레임워크다. 현대 리눅스는nftables백엔드를 기본으로 사용하며, 관련 커널 모듈(nf_tables)이 로드되어 있어야 방화벽 명령어 자체가 실행된다. - nf_tables:
nftables의 커널 모듈. 이게 없으면 최신 iptables/ip6tables 명령어 자체가 실행되지 않는다.
스크립트 로직 자체는 이상이 없었다. Docker 문제와 동일한 근본 원인(Root Cause), 즉 6.8.0-51 커널에 맞는 모듈 패키지가 없어 nf_tables 모듈 자체가 로드되지 않은 것이 방화벽 서브시스템에도 영향을 준 것이었다.
이쯤 되니 방향이 명확해졌다. 증상별 우회(Workaround)를 쌓아가는 건 기술 부채만 늘린다. 현재 부팅된 커널(6.8.0-51)에 맞는 모듈 패키지를 재설치하는 근본적 처치(Root Cause Fix) 로 방향을 잡았다.
3단계: 현재 커널 기준 모듈 패키지 강제 재설치
커널 교체가 아니다 — 모듈 패키지가 문제다
uname -r이 6.8.0-51-generic이니 커널 자체는 멀쩡히 부팅되어 있다. 문제는 이 버전에 맞는 모듈 파일이 디스크에 없다는 것이다.
$ sudo modprobe overlay
modprobe: FATAL: Module overlay not found in directory /lib/modules/6.8.0-51-generic
용어
- modprobe: 커널 모듈을 로드/언로드하는 명령어. 모듈 파일 자체가 디스크에 없으면 당연히 로드할 수 없다.
- linux-modules-extra: Ubuntu 커널 패키지 중 OverlayFS, 각종 네트워크 드라이버 등 "확장 모듈"을 담고 있는 패키지. 베어메탈에서는 이게 없으면 Docker, nftables 등이 정상 작동하지 않는다.
6.8.0-107과 6.8.0-106의 모듈 패키지는 unattended-upgrades가 설치·삭제를 관리하는 최신 버전이었다. 하지만 6.8.0-51은 GRUB이 고정해서 부팅만 하는 버전이라 처음부터 linux-modules-extra 패키지가 설치된 적이 없었던 것이다.
# 현재 부팅된 커널 버전(6.8.0-51)에 맞는 모듈 패키지 강제 재설치
# --reinstall: 이미 설치 기록이 없어도 강제로 밀어넣기
$ sudo apt-get install --reinstall \
linux-modules-$(uname -r) \
linux-modules-extra-$(uname -r)
# 모듈 의존성 데이터베이스 재생성
$ sudo depmod -a
용어
- depmod: 커널 모듈 의존성 데이터베이스를 재생성하는 명령어. 새 모듈 패키지를 설치한 후 반드시 실행해야
modprobe가 의존성 순서대로 모듈을 로드할 수 있다.
재부팅 없이 Docker 서비스를 재시작하고 모듈 로드 상태를 확인했다.
$ sudo service docker restart
$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
Active: active (running)
$ lsmod | grep overlay
overlay 155648 0
용어
- lsmod: 현재 커널에 로드된 모듈 목록을 출력하는 명령어.
overlay항목이 보이면 OverlayFS가 활성화된 것이다.
4단계: iptables 백엔드 충돌 해결 + 방화벽 스크립트 현행화
Docker는 살아났는데, 방화벽 스크립트 재실행 시 또 다른 에러가 발생했다.
증상
$ sudo /etc/rc.firewall
iptables: Bad argument '3'
원인 분석 — 두 개의 문제가 겹쳐있었다
첫 번째: iptables 백엔드 불일치. 모듈 재설치 후 nft 백엔드가 활성화되었는데, 그 이전 legacy 백엔드 환경에서 작성된 잔존 룰셋(Stale Ruleset)이 충돌을 일으키고 있었다.
용어
- iptables-legacy vs iptables-nft: 같은
iptables명령어지만 내부적으로 다른 커널 서브시스템을 사용한다. legacy는 구형x_tables, nft는 최신nf_tables백엔드를 쓴다. 두 백엔드의 룰이 동시에 커널에 존재하면 충돌이 발생한다.
두 번째: Docker 체인 구조 변경. 최신 Docker 버전에서 DOCKER-ISOLATION-STAGE-1/2 체인이 DOCKER-USER 체인 기반으로 재설계되었는데, 스크립트 내부의 grep 파싱 로직이 구형 체인명 패턴을 기반으로 작성되어 있어 Bad argument 에러를 유발했다.
용어
- iptables 체인 (Chain): 패킷이 통과하는 규칙 집합의 단위. Docker는 자신의 네트워크 격리(isolation)를 위해 커널 netfilter에 자체 체인을 등록한다.
- DOCKER-USER 체인: 외부 트래픽이 Docker 컨테이너로 진입하기 전에 관리자 정의 규칙을 적용할 수 있는 공식 진입점. Docker가 공식 권장하는 커스텀 방화벽 훅(Hook) 포인트다.
해결 — 잔존 룰셋 플러시 + 스크립트 업데이트
# 모든 테이블(filter, nat, mangle)의 체인/룰 완전 플러시
# 커널 netfilter 상태를 클린슬레이트(Clean Slate)로 리셋
$ sudo iptables -F && sudo iptables -X
$ sudo iptables -t nat -F && sudo iptables -t nat -X
$ sudo iptables -t mangle -F && sudo iptables -t mangle -X
$ sudo ip6tables -F && sudo ip6tables -X
용어
- -F (Flush): 체인 내 모든 규칙 삭제.
- -X (Delete chain): 사용자 정의 체인 삭제. 기본 체인(INPUT, OUTPUT, FORWARD 등)은 삭제 불가.
- Clean Slate: 기존 상태를 완전히 초기화하는 접근법. 잔존 설정이 새 설정과 충돌하는 상황에서 가장 확실한 방법이다.
방화벽 스크립트에서 구형 DOCKER-ISOLATION 체인 파싱 코드를 제거하고, Docker 공식 권장 방식인 DOCKER-USER 체인 기반으로 트래픽 제어 로직을 재작성했다.
# 레거시 방식 — Docker 업데이트 후 동작 보장 안 됨
# DOCKER_CHAIN=$(iptables -L FORWARD | grep -o 'DOCKER-ISOLATION-STAGE-[0-9]')
# iptables -I $DOCKER_CHAIN ...
# 요즘 방식 — DOCKER-USER 체인 직접 사용 (Docker 공식 권장)
# 기본적으로 모든 외부 인바운드 차단
iptables -I DOCKER-USER -s 0.0.0.0/0 -j DROP
# 사설 세그먼트(192.168.0.0/24) 노드 간 통신 허용
iptables -I DOCKER-USER -s 192.168.0.0/24 -j ACCEPT
# 필요한 공인 IP 대역 화이트리스트 추가
# iptables -I DOCKER-USER -s <허용_IP> -j ACCEPT
5단계: 재부팅 후 초기화된 사설 L2 인터페이스 — Netplan 퍼시스턴트 설정
ip link show 로 인터페이스 목록을 확인했더니 사설 세그먼트용 NIC(enp3s0f1np1)가 DOWN 상태에 IP 미할당 상태였다.
$ ip addr show enp3s0f1np1
3: enp3s0f1np1: <BROADCAST,MULTICAST> mtu 9000 qdisc noop state DOWN
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
# IP 없음
원인
이전 설정이 ip addr add와 ip link set up 같은 임시 명령어(Ephemeral Configuration) 로만 적용되었을 가능성이 있는 것 같다. 만약 이렇게 설정했다면 커널 메모리에만 존재하고 재부팅 시 사라진다. Netplan 같은 네트워크 관리 계층(Network Management Layer) 의 영구 설정 파일로 관리했던 기억이 있는데 이상하게 존재하지 않았다.
용어
- Ephemeral Configuration (임시 설정):
ip명령어나ifconfig등으로 직접 적용한 네트워크 설정. 커널의 런타임 상태만 변경하며 재부팅 시 초기화된다. - Netplan: Ubuntu 17.10 이후 기본 네트워크 설정 프론트엔드. YAML 파일로 설정을 선언하면 백엔드(
systemd-networkd또는NetworkManager)가 이를 영구 적용한다.
사실 이 시점에서 이미 Swarm 재가입(docker swarm join)을 여러 번 시도했었다. 당연히 전부 실패. 사설망이 죽어있으니 192.168.0.1로 라우팅 자체가 안 됐던 것이다. ping 192.168.0.1 한 방에 원인이 잡혔다.
$ docker swarm join --token SWMTKN-1-xxx 192.168.0.1:2377
Error response from daemon: manager is unavailable # 계속 실패
# 원인을 찾다가 ping 날려봄
$ ping 192.168.0.1
ping: connect: Network is unreachable # 사설망이 죽어있었다
Swarm보다 사설망부터 살려야 했다.
해결 — Netplan 퍼시스턴트 설정 파일 신규 작성
정상 운영 중인 web01 노드의 Netplan 설정을 참고해서 파일을 작성했다.
# /etc/netplan/60-vrack.yaml — 최종 설정
network:
version: 2
renderer: networkd
ethernets:
enp3s0f1np1:
addresses:
- 192.168.0.3/24 #
routes:
- to: 192.168.0.0/24
via: 192.168.0.254
mtu: 9000
$ sudo chmod 600 /etc/netplan/60-vrack.yaml
$ sudo netplan apply
# 적용 결과 검증
$ ifconfig enp3s0f1np1
enp3s0f1np1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 192.168.0.3 netmask 255.255.255.0 #
# 사설 세그먼트 인접 노드 L3 도달성 검증
$ ping 192.168.0.1
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.4 ms # 사설망 복구
용어
- 서브넷 마스크 /16 vs /24: /16은 192.168.0.0 ~ 192.168.255.255 전체(65534개 호스트), /24는 192.168.0.0 ~ 192.168.0.255(254개 호스트)다. 사설 세그먼트 내 서버들이 192.168.0.x 대역만 쓰는 환경에서는 /24가 올바른 설정이다.
- MTU 9000 (Jumbo Frame): 표준 이더넷 MTU는 1500바이트. 내부 서버 간 통신에서는 9000바이트로 설정해 패킷 오버헤드를 줄이고 처리량을 높인다. 단, 경로상의 모든 네트워크 장비가 지원해야 한다.
- renderer: networkd: Netplan이 설정을 위임할 백엔드. 서버 환경에서는
systemd-networkd를 사용하는 것이 표준이다.
Docker Swarm 복구: 도미노 장애의 마무리
사설 네트워크가 복구되면서 Swarm 클러스터 상태 불일치(State Inconsistency)도 정리가 필요했다.
sequenceDiagram
participant Net as 사설 L2 세그먼트
participant Node as web03 노드
participant Raft as Swarm Raft 합의 레이어
participant Svc as 서비스 스케줄러
Note over Net: 재부팅으로 NIC IP 소실
Net->>Node: 사설망 단절 (네트워크 파티션)
Node->>Raft: Heartbeat 타임아웃 → 노드 Unreachable
Raft->>Svc: 워커 노드 Down 판정
Svc->>Svc: 복제본 수 초과 (2/1) — 다른 노드에 중복 스케줄링
Note over Raft: 관리자 개입: 강제 삭제 후 재가입(Rejoin)
Raft->>Node: 신규 노드 ID 발급
Note over Node: 기존 플레이스먼트 라벨 메타데이터 소실
Node->>Svc: 라벨 기반 Constraint 미충족 → 일부 서비스 미배포
용어
- Raft 합의 알고리즘 (Raft Consensus): Docker Swarm Manager들이 클러스터 상태(노드 목록, 서비스 정의 등)의 일관성을 유지하기 위해 사용하는 분산 합의 알고리즘. 노드가 일정 시간 Heartbeat를 보내지 않으면
Unreachable로 판정한다. - 네트워크 파티션 (Network Partition): 네트워크 장애로 인해 클러스터의 일부 노드들이 서로 통신할 수 없게 되는 상황. CAP 이론에서 다루는 핵심 장애 시나리오다.
- 플레이스먼트 라벨 (Placement Label): Swarm 서비스가 특정 노드에만 배포되도록 제약(Constraint)을 거는 메타데이터. 예:
node.labels.letsencrypt-proxy==true
Swarm 노드 강제 퇴출 및 재가입
# [Manager 노드에서] 비정상 노드 강제 퇴출
$ docker node rm --force web03
# [web03에서] 기존 Swarm 참여 정보 초기화
$ docker swarm leave --force
# [Manager 노드에서] Worker 조인 토큰 발급
$ docker swarm join-token worker
# 출력된 join 명령어 복사
# [web03에서] 클러스터 재가입
$ docker swarm join --token SWMTKN-1-xxxx 192.168.0.1:2377
# 노드 상태 검증
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
aaaaaaaaaaaaa * web01 Ready Active Leader
bbbbbbbbbbbbb web02 Ready Active
ccccccccccccc web03 Ready Active #
플레이스먼트 라벨 재설정
재가입 과정에서 노드 ID가 재발급되어 기존 라벨 메타데이터가 모두 초기화된 상태였다. 이는 Swarm의 정상 동작이다. letsencrypt-proxy 서비스가 node.labels.letsencrypt-proxy==true 플레이스먼트 제약 조건 때문에 web03에 미배포된 것도 이 때문이었다.
# 서비스 배포 제약 조건 확인
$ docker service inspect letsencrypt-proxy \
--format '{{json .Spec.TaskTemplate.Placement}}'
{"Constraints":["node.labels.letsencrypt-proxy==true"]}
# 기존 노드의 라벨 목록 레퍼런스 확인 (정상 노드 기준)
$ docker node inspect web02 \
--format '{{json .Spec.Labels}}'
{"db-slave":"true","general":"true","letsencrypt-proxy":"true"}
# web03에 누락된 라벨 일괄 복구
$ docker node update \
--label-add db-slave=true \
--label-add general=true \
--label-add letsencrypt-proxy=true \
web03
# 서비스 복제본 수 정상화 확인
$ docker service ls
ID NAME MODE REPLICAS IMAGE
xxxxxxxx letsencrypt-proxy global 2/2 ... #
yyyyyyyy web-app replicated 1/1 ... #
사후 분석 (Post-Mortem): 재부팅 한 방에 도미노가 된 이유

이번 장애의 근본 원인을 계층별로 정리하면 이렇다.
커널 계층 (Kernel Layer): unattended-upgrades가 6.8.0-107 커널을 자동 설치하고 6.8.0-106 모듈 패키지를 자동 삭제했다. 그러나 GRUB 부팅 기본값은 훨씬 오래된 6.8.0-51로 고정되어 있었고, 이 버전의 linux-modules-extra는 디스크에 없었다. 커스텀 커널이 문제가 아니라, GRUB 기본값 관리 부재 + unattended-upgrades의 자동 삭제가 결합된 것이 진짜 원인이었다.
네트워크 계층 (Network Layer): 사설 세그먼트 NIC 설정이 Ephemeral 명령어로만 관리되고 Netplan에 퍼시스턴트 선언이 없었다. 재부팅 시 초기화는 당연한 결과.
오케스트레이션 계층 (Orchestration Layer): 네트워크 파티션 → Swarm 노드 이탈 → 재가입 시 노드 ID 재발급 → 라벨 소실로 이어지는 도미노. 각각은 Swarm의 정상 동작이지만, 라벨 관리가 IaC로 코드화되어있지 않아 수동 복구가 필요했다.
결론: [GRUB 고정 커널 + unattended-upgrades 자동 삭제] + [Ephemeral 네트워크 설정] 이 두 개의 기술 부채가 재부팅 한 번에 전부 현실화된 케이스다.
운영 체크리스트
이번 장애를 계기로 추가하거나 업데이트한 리스트다.
커널 관리
- GRUB 기본 부팅 커널을
/etc/default/grub에서GRUB_DEFAULT=0(항상 최신)으로 설정하고update-grub적용. 특정 버전으로 고정하면unattended-upgrades와 충돌한다 unattended-upgrades정책 재검토: 커널 자동 업그레이드 후 구 버전 자동 삭제(autoremove)가 GRUB 고정 커널 버전의 모듈까지 지우는 일이 없도록 버전 고정 또는 삭제 정책 명시- 커널 업그레이드는 반드시 스테이징(Staging) 환경에서
depmod -a+ Docker/방화벽 기동 테스트 후 프로덕션 적용
네트워크 영구화
- 모든 NIC 설정은 Netplan YAML 파일에 선언적으로 기록. 임시
ip명령어는 디버깅 용도로만 사용 netplan apply후 반드시reboot으로 재부팅 테스트까지 해야 진짜 설정
방화벽 관리
rc.local기반의 방화벽 스크립트는systemd서비스 유닛으로 전환해 부팅 타이밍 보장- Docker 메이저 업데이트 시 방화벽 스크립트의 체인명 호환성 검증을 릴리즈 노트와 함께 확인
Swarm 노드 메타데이터 관리
- 노드 라벨 목록을 Ansible Playbook 또는 IaC 스크립트로 코드화.
docker node update수동 실행은 근절 - Swarm 재가입 절차(Rejoin Runbook)를 사전에 문서화하여 장애 시 즉시 실행 가능하도록 준비
용어
- IaC (Infrastructure as Code): 인프라 구성을 코드로 선언하고 버전 관리하는 방법론. Ansible, Terraform 등이 대표적. 수동 명령어 대신 코드가 인프라 상태의 단일 진실 공급원(Single Source of Truth)이 된다.
- Runbook: 특정 작업이나 장애 대응 절차를 단계별로 문서화한 운영 가이드. "이 상황이 오면 이렇게 해라"를 미리 적어두는 것.
요약
unattended-upgrades 가 커널을 올리고 구 버전 모듈을 지웠다. GRUB 부팅 기본값을 0(항상 최신)으로 설정해두지 않으면, 자동 업그레이드와 부팅 커널이 따로 놀다가 재부팅으로 이렇게 되었다. 참고로 최초 재부팅 원인은 IDC 의 파워선을 교체했다고 한다. 업데이트만 하지 않았더라도 괜찮았을텐데 업데이트로 인해 쉽지 않은 상황이었다.
'실무 경험 > 실무 개발 & 협업' 카테고리의 다른 글
| CVE-2026-31431 "Copy Fail": 732바이트 파이썬으로 리눅스 루트 탈취 이슈 (0) | 2026.05.04 |
|---|---|
| [블록체인] - 솔라나 메인넷 노드, RPC 트러블슈팅 (0) | 2026.04.01 |
| [트러블슈팅] - 유지보수가 중단된 클라이언트와의 TLS 핸드셰이크 (0) | 2026.02.02 |
| 휴먼에러를 방지하는 방법 1 - git add -p(git add partial (or patch) (0) | 2025.04.24 |
| [블록체인] - Integer overflow 와 underflow (0) | 2025.04.21 |