Rootless Podman 운영 · docker 와의 차이 · 이론
전환에서 실제로 걸리는 것들
한 줄 요약
docker 에서 podman 으로의 전환은 명령어 교체가 아니라 운영 모델의 교체다. 데몬에서 fork-exec 으로, rootful 에서 rootless 로, daemon.json 에서 containers.conf 가족으로.
왜 이게 필요했나
alias docker=podman 으로 대부분의 명령이 그대로 동작한다. 그래서 전환이 쉬워 보인다. 그런데 실제로 옮기면 몇 가지에서 반드시 걸린다. 그 몇 가지를 미리 알면 30분, 모르면 반나절이다.
어떻게 동작하나
구조의 차이
Docker: docker CLI ──REST──▶ dockerd ──▶ containerd ──▶ runc ──▶ 컨테이너Podman: podman CLI ──fork/exec──▶ conmon ──▶ crun ──▶ 컨테이너podman 에는 데몬이 없다. podman run 은 평범한 프로세스처럼 컨테이너를 직접 만든다. 컨테이너마다 작은 감시 프로세스 conmon 이 붙어 stdio 와 exit code 를 관리하고, 실제 생성은 OCI 런타임 crun(C 로 작성, runc 보다 가볍다)이 한다.
이 구조의 함의가 크다.
- 단일 장애점이 없다. dockerd 가 죽거나 업그레이드하면 모든 컨테이너 관리가 멈추지만, podman 은 애초에 중앙 프로세스가 없다.
- 루트 데몬 소켓이라는 공격면이 없다.
- systemd 와 자연스럽게 결합한다. 컨테이너가 평범한 자식 프로세스이므로 systemd 유닛으로 직접 관리할 수 있다.
반드시 걸리는 다섯 가지
1. restart: always 가 다르게 동작한다. 데몬이 없으므로 "부팅 시 자동 시작" 을 의미하지 않는다. podman 식 정답은 Quadlet 이다.
# ~/.config/containers/systemd/web.container[Unit]Description=Web service[Container]Image=docker.io/library/nginx:1.27PublishPort=8080:80Volume=/srv/www:/usr/share/nginx/html:Z[Service]Restart=always[Install]WantedBy=default.target.container 파일을 두면 systemd 가 그것을 읽어 서비스 유닛을 생성한다. podman generate systemd 는 구식이 됐다.
2. SELinux 볼륨 라벨. RHEL/Fedora 에서 볼륨 마운트가 Permission denied 라면 십중팔구 SELinux 다. -v ./data:/data:Z(전용) 또는 :z(공유) 라벨을 붙인다. 우분투에서 넘어온 compose 파일이 가장 자주 걸리는 함정이다.
3. unqualified 이미지 이름. podman pull nginx 가 docker 와 다르게 동작한다. registries.conf 에 unqualified-search-registries = ["docker.io"] 를 넣거나, CI 라면 전체 경로를 쓴다.
4. 저장 위치. rootless 이미지는 ~/.local/share/containers/storage 다. docker system df 감각으로 /var/lib 를 뒤지면 못 찾는다.
5. 1024 미만 포트. rootless 는 특권 포트에 바인딩할 수 없다.
compose 는 어떻게 하나
두 가지 길이 있다.
- 방법 1 (권장) — podman 이 제공하는 Docker API 호환 소켓을 켜고 표준
docker compose를 그대로 쓴다. compose.yaml 을 고칠 필요가 없고 호환성이 가장 높다.
systemctl --user enable --now podman.socket export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock docker compose up -d- 방법 2 —
podman-compose(파이썬 재구현). 소켓 없이 podman 명령을 직접 호출하지만 Compose 스펙의 구석에서 미묘한 차이가 있다.
프로덕션 지향이라면 compose 대신 podman kube play(쿠버네티스 YAML 을 직접 실행)도 선택지다. 로컬과 클러스터의 매니페스트를 통일할 수 있다.
현장에서 만나는 모습
점진 전환이 가능하다. 두 엔진은 스토리지가 분리돼 있어 한 머신에 공존할 수 있다. 서비스 단위로 하나씩 옮기면 된다.
podman-docker 패키지. docker 명령을 podman 으로 연결해 주는 심 패키지다. 스크립트 호환에 유용하지만, 팀원에게 "이 서버의 docker 는 사실 podman" 임을 반드시 공지해야 한다. 안 그러면 docker 문서를 보고 나온 명령이 미묘하게 다르게 동작할 때 원인을 못 찾는다.
다음 실습에서 할 것
이미지를 적재해 컨테이너를 실행하고, 볼륨과 사용자 매핑을 확인하고, 특권 포트 실패를 재현하고, Quadlet .container 파일을 작성한다. 마지막에 docker 와의 차이를 증거와 함께 표로 정리한다.