config.toml 최상단의 두 값은 역할이 완전히 다르다.
| 설정 | 기본값 | 내용 | 성격 |
|---|---|---|---|
root |
/var/lib/containerd |
이미지 레이어 · 스냅샷 · 메타데이터 DB | 영속. 용량을 차지하는 쪽 |
state |
/run/containerd |
소켓 · shim 상태 · FIFO | tmpfs. 재부팅하면 사라진다 |
디스크가 부족해 옮기려는 대상은 root 다. state 는 메모리 기반이고 보통 수십에서 수백 MB 에 그친다. 여기가 1 GB 를 넘는다면 종료되지 못한 shim 프로세스가 쌓였는지 의심한다.
root 아래 구조는 이렇다.
/var/lib/containerd/
├── io.containerd.content.v1.content/ # 레지스트리에서 받은 원본 blob
├── io.containerd.snapshotter.v1.overlayfs/ # 컨테이너 파일 시스템. 대부분의 용량
├── io.containerd.metadata.v1.bolt/meta.db # 이미지·컨테이너 매핑
└── io.containerd.runtime.v2.task/
config.toml 의 root 만 고치고 재시작하면 containerd 는 빈 디렉터리에서 새로 시작한다. 기존 이미지가 전부 사라진 것처럼 보이고, Kubernetes 노드라면 kubelet 이 모든 이미지를 다시 받으려 하면서 파드가 무더기로 재시작한다. 폐쇄망이면 그대로 멈춘다.
데이터를 옮기지 않아도 되는 경우는 하나뿐이다 — 아직 클러스터에 조인하지 않은 새 노드다. 이때는 경로만 바꾸고 시작하면 된다.
# 1. kubelet 을 먼저 멈춘다. 순서가 중요하다
systemctl stop kubelet
systemctl stop containerd
# 2. 데이터 이동 (권한·속성 보존)
mkdir -p /data/containerd
rsync -aHAX --numeric-ids /var/lib/containerd/ /data/containerd/
# 3. 설정 변경
# /etc/containerd/config.toml
version = 2
root = "/data/containerd"
state = "/run/containerd"
# 4. 기동
systemctl daemon-reload
systemctl start containerd
systemctl start kubelet
# 5. 확인 — 옮기기 전의 이미지가 보여야 한다
ctr -n k8s.io images ls | head
crictl images
kubectl get pod -A -o wide | grep <노드>
옮긴 뒤 원본은 바로 지우지 말고 며칠 두었다가 지운다. SELinux 가 enforcing 인 노드라면 새 경로에 문맥을 맞춰야 컨테이너가 뜬다.
semanage fcontext -a -e /var/lib/containerd /data/containerd
restorecon -R /data/containerd
state 를 기본값에서 옮기면 소켓 경로가 바뀐다. kubelet 의 --container-runtime-endpoint 와 /etc/crictl.yaml 을 함께 바꿔야 하므로, 특별한 이유가 없으면 state 는 그대로 둔다.
이미 클러스터에 붙어 있는 노드라면 작업 중 그 노드의 파드가 모두 멈춘다. 먼저 비운다.
kubectl cordon <노드>
kubectl drain <노드> --ignore-daemonsets --delete-emptydir-data
# 작업 수행
kubectl uncordon <노드>
비우지 않고 진행하면 CNI 관련 상태가 어긋나 파드 샌드박스가 깨지기도 한다. 최악의 경우 노드를 다시 조인해야 한다.