한 호스트에 서비스 여러 벌이 컨테이너 31개로 올라가 있다. 도커 보안 업데이트가 밀려 있었는데 손이 잘 안 나갔다. 데몬을 올리면 컨테이너가 전부 재시작한다고 생각하면, apt 한 줄에 사이트 몇 개가 같이 내려가는 셈이 된다.
실제로는 그렇지 않았다. daemon.json에 live-restore가 켜져 있으면 dockerd가 내려가도 컨테이너 프로세스는 계속 돈다. 업그레이드를 걸고 로그를 따라가 보니 데몬이 비어 있던 시간은 6초였고, 그 사이 컨테이너 쪽에서는 아무 일도 벌어지지 않았다.
apt 기록이 남긴 버전 네 줄
apt 기록을 보면 명령이 시작된 시각이 14:11:20, 끝난 시각이 14:11:31이다. 11초 동안 docker-ce와 docker-ce-cli, docker-ce-rootless-extras가 5:29.7.2에서 5:29.8.1로, containerd.io가 2.3.3에서 2.3.5로 올라갔다. 패키지 넷이 한 트랜잭션에 묶여 있었다.
컨테이너 런타임까지 같이 올라갔다는 점이 중요하다. dockerd만 재시작하는 상황과 containerd까지 교체되는 상황은 위험도가 다르다. 전자는 감독자만 잠깐 자리를 비우는 것이고, 후자는 컨테이너를 실제로 붙들고 있는 쪽이 바뀌는 것이기 때문이다.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3",
"compress": "true"
},
"live-restore": true
} 코드 발췌 · 이 한 줄이 데몬 교체와 서비스 중단을 분리한다.
복원했다는 문장과, 아무 일도 없던 6초
journal을 보면 14:11:24에 Stopping docker.service가 찍히고 같은 초에 Daemon shutdown complete가 나온다. 새 dockerd는 14:11:25에 Starting up, 곧Restoring containers: start를 남긴다. Loading containers: done이 14:11:28, Started docker.service가 14:11:30이다. 데몬이 없던 구간은 대략 6초다.
그 구간에 컨테이너 이벤트가 하나도 없다. task-delete도 restarting container도 나오지 않고, 다음 컨테이너 이벤트는 1분 가까이 지난 뒤 재시작 정책을 가진 큐 워커가 스스로 빠졌다 들어오면서 생긴 것이다. 새 데몬은 컨테이너를 다시 만든 게 아니라 이미 돌고 있던 것을 되찾았다.
재미있는 문장이 하나 더 있었다. "there are running containers, updated network configuration will not take affect". 도는 컨테이너가 있어서 네트워크 설정 변경은 반영하지 않겠다는 말이다. 컨테이너를 살려 두는 대가로 포기하는 것이 무엇인지 데몬이 직접 알려 준 셈이다.
live-restore가 지켜 주지 않는 것들
첫째, 데몬 API는 그 6초 동안 없다. docker ps도 compose도 헬스체크 조회도 그 사이에는 실패한다. 배포 스크립트가 컨테이너 상태를 폴링하는 중이었다면 컨테이너는 멀쩡해도 스크립트는 죽는다. 그래서 업그레이드를 배포와 겹치지 않는 시간에 돌렸다.
둘째, 데몬이 없는 동안은 재시작 정책이 동작하지 않는다. 컨테이너를 다시 띄워 주는 주체가 dockerd이기 때문이다. 하필 그 6초 안에 프로세스가 죽으면 데몬이 돌아올 때까지 그대로 멈춰 있다.
셋째, 재부팅은 완전히 다른 이야기다. 같은 날 오후에 호스트를 실제로 재부팅했을 때는 컨테이너가 전부 새로 떴다. live-restore는 데몬 교체를 무중단으로 만들어 줄 뿐 호스트 재시작을 무중단으로 만들지 않는다. 이 둘을 같은 안전장치로 묶어서 생각하면 언젠가 크게 틀린다.
데몬 교체와 호스트 재부팅은 같은 안전장치로 덮이지 않는다.
데몬이 내려가도 컨테이너는 내려가지 않는다
- live-restore를 켜 두면 데몬과 컨테이너 런타임 교체가 서비스 중단과 분리된다
- 데몬이 비는 몇 초 동안 API와 재시작 정책은 함께 멈춘다
- 도는 컨테이너가 있으면 네트워크 설정 변경은 반영되지 않는다
- 업그레이드는 배포 스크립트가 컨테이너 상태를 읽는 시간과 겹치지 않게 잡는다