SSD Nodes Learn 🎉 VPS $5.50/월부터
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-13

VPS에서 Podman과 Docker의 실질적인 차이점 비교

Podman은 데몬 없이 rootless로 실행되어 Docker와 구조적 차이를 보입니다. VPS 환경에서 compose 파일 사용, systemd를 이용한 자동 시작, 포트 바인딩 및 볼륨 권한 문제를 해결하는 구체적인 방법을 정리했습니다.

Podman과 Docker의 실질적인 차이점

Podman과 Docker는 VPS에서 동일한 OCI(Open Container Initiative) 이미지를 실행하므로, 어떤 소프트웨어를 구동할 수 있는지는 선택의 기준이 되지 않습니다. 차이점은 프로세스 모델에 있습니다. Docker는 모든 컨테이너를 소유하는 root 데몬을 실행하며, docker 명령은 데몬에게 작업을 요청하는 작은 클라이언트일 뿐입니다. Podman은 데몬이 없습니다. podman run은 컨테이너를 호출한 프로세스의 자식 프로세스로서, 사용자의 권한 없는(unprivileged) 계정 하에서 실행합니다.

나머지 모든 차이점은 이 사실 하나에서 비롯됩니다. 자동 시작은 데몬이 아닌 systemd의 역할이 됩니다. 볼륨 소유권은 사용자 네임스페이스를 거치므로, 호스트에서 ls -l로 확인되는 소유자와 컨테이너 내부에서 보이는 소유자가 다릅니다. 1024번 미만의 포트는 커널 설정을 변경하기 전까지 바인딩이 거부됩니다. docker CLI(명령줄 인터페이스)는 래퍼를 통해 계속 작동하지만, Docker 소켓을 요구하는 작업이 발생하면 한계에 부딪힙니다.

데몬 없음: 컨테이너를 시작할 때 실제로 실행되는 것

Docker 호스트에서 pstree -a 명령을 실행하면 dockerd가 루트 권한으로, containerd가 그 옆에 표시되며, 실행 중인 컨테이너마다 containerd-shim-runc-v2가 하나씩 생성됩니다. 귀하의 애플리케이션은 해당 shim의 자식 프로세스이며, shim은 PID 1의 자식 프로세스입니다. 컨테이너를 시작한 셸과 컨테이너를 연결하는 것은 아무것도 없습니다. 데몬을 중지하면 해당 호스트에 있는 모든 컨테이너의 제어 평면을 잃게 되며, 기본 live-restore 설정이 꺼져 있는 경우 systemctl restart docker는 컨테이너도 함께 재시작합니다.

Podman에는 이와 대응하는 프로세스가 없습니다. 컨테이너를 시작하면 명령을 실행한 사용자가 소유하는 conmon(컨테이너 모니터) 프로세스가 컨테이너의 메인 프로세스를 유지합니다.

podman run -d --name web -p 8080:80 docker.io/library/caddy:2
ps -o user,pid,ppid,args -C conmon
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080

ps 명령을 실행하면 루트가 아닌 로그인한 사용자 권한으로 실행 중인 conmon가 나열되어야 하며, curl200를 출력해야 합니다. 중앙 서비스가 컨테이너를 소유하지 않기 때문에 sudo apt upgrade podman 명령을 실행해도 이미 실행 중인 컨테이너는 중지되지 않으며, 특정 컨테이너의 모니터가 충돌하더라도 다른 컨테이너에 영향을 주지 않습니다.

데몬이 없다는 것은 그에 따른 비용도 발생함을 의미합니다. 재부팅 후 컨테이너를 자동으로 시작해 주는 프로세스가 없습니다. Docker의 --restart=always는 부팅 시 데몬이 보장하는 기능이지만, Podman은 이를 systemd로 대체합니다. 아래의 quadlet 섹션이 바로 이를 위한 것입니다.

소켓은 이 이야기의 나머지 절반입니다. /var/run/docker.sock는 루트가 소유한 API(애플리케이션 프로그래밍 인터페이스) 엔드포인트이며, 이 소켓에 쓸 수 있는 모든 프로세스는 호스트 파일 시스템을 마운트하는 권한 있는 컨테이너를 시작할 수 있습니다. 사용자를 docker 그룹에 추가하는 것은 우회적인 방법으로 루트 권한을 부여하는 것과 같으므로, 각 서비스 계정에 필요한 권한만 부여하기 내용을 함께 읽어보는 것이 좋습니다. Podman은 사용자가 요청하지 않는 한 소켓을 노출하지 않으며, 생성된 소켓은 /run/user/<uid>/podman/podman.sock 경로에서 특정 사용자에게 귀속됩니다.

Ubuntu 24.04에 Podman 설치 및 rootless 모드 확인

sudo apt update
sudo apt install -y podman uidmap
podman --version
podman info | grep -i rootless

uidmap 패키지는 newuidmapnewgidmap을 제공합니다. 이들은 일반 사용자가 하위 ID 범위를 할당받을 수 있게 해주는 setuid 헬퍼이며, 이들이 없으면 rootless 컨테이너가 시작되지 않습니다. podman info 명령을 실행하면 rootless: true이 출력되어야 합니다.

2026년 8월 확인 기준으로 Ubuntu 24.04는 Podman 4.9를, Debian 13은 Podman 5.x를 제공합니다. quadlet 파일은 4.4 이상이 필요하고 .pod quadlet 파일은 5.0이 필요하므로 이 버전 차이는 중요합니다. 업스트림 문서의 예제를 복사하기 전에 podman --version를 실행하여 버전을 확인하십시오.

모든 rootless 사용자는 하위 ID 범위가 필요합니다.

grep "$USER" /etc/subuid /etc/subgid

Ubuntu에서 adduser으로 생성된 사용자는 자동으로 범위를 할당받습니다. 반면 useradd -M나 구성 도구로 생성된 사용자는 그렇지 않은 경우가 많으며, 이 경우 다음과 같은 오류가 발생합니다.

Error: cannot find UID/GID for user deploy: no subuid ranges found for user "deploy" in /etc/subuid - check rootless mode in man pages.

범위를 할당한 다음, 새로운 매핑이 적용되도록 해당 사용자의 스토리지를 초기화하십시오.

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 deploy
podman system migrate

첫 실행 시 주의할 점이 하나 더 있습니다. Podman은 기본적으로 Docker Hub를 가정하지 않습니다. 짧은 이미지 이름은 /etc/containers/registries.conf 내의 unqualified-search-registries를 기준으로 해석되며, 터미널이 연결되지 않은 스크립트 환경에서는 short-name resolution enforced but cannot prompt without a TTY 오류와 함께 pull이 실패합니다. 항상 전체 이름을 작성하십시오. nginx 대신 docker.io/library/nginx:1.27을 사용하십시오.

임대 서버에서 루트리스 컨테이너가 실제로 제공하는 이점

루트리스 컨테이너는 사용자 네임스페이스 내부에서 실행됩니다. 이는 프로세스에 고유한 사용자 ID 매핑을 제공하는 커널 기능입니다. 네임스페이스 내부에서 컨테이너의 슈퍼유저는 UID(사용자 ID) 0입니다. 하지만 VPS 외부에서 해당 프로세스는 일반 로그인 사용자와 동일합니다. 컨테이너 내부의 루트가 호스트의 루트는 아닙니다.

이것이 얻을 수 있는 이점의 실질적인 범위입니다. 루트 권한으로 실행되어야 하는 이미지, 원격 코드 실행 버그가 있는 웹 애플리케이션, 외부에서 UID 0이어야 가능한 탈출 시도 등 모든 경우에 해당 프로세스는 시스템 전체 권한이 아닌 비권한 사용자의 권한만을 가지게 됩니다. 루트리스가 보호하지 못하는 것은 커널 버그이며, 탈출한 프로세스가 사용자 본인으로 실행되어 사용자가 읽을 수 있는 모든 파일에 접근할 수 있으므로 사용자 자신의 파일 또한 보호하지 못합니다.

Docker 역시 루트리스로 실행할 수 있습니다. dockerd-rootless-setuptool.sh install은 사용자별 데몬을 설정하며 잘 작동합니다. 차이점은 기본 설정이 어디를 향하고 있느냐입니다. Podman은 별도의 요청 없이도 기본적으로 루트리스를 제공하므로, 서비스가 2년 동안 조용히 루트 권한으로 실행되는 대신 컨테이너가 80번 포트에 바인딩할 수 없다는 첫 번째 실패를 마주하게 됩니다.

볼륨 파일의 소유자가 왜 UID 100999인가요?

사용자 네임스페이스(user namespace) 때문입니다. 컨테이너의 UID 0은 호스트의 UID로 매핑됩니다. 컨테이너의 UID 1은 subuid 범위의 첫 번째 ID로 매핑되며, 그 이후부터는 순차적으로 증가합니다. 범위가 100000에서 시작한다면, 컨테이너의 UID 1000은 호스트에서 100999로 나타납니다.

mkdir -p "$PWD/data"
podman run --rm --user 1000 -v "$PWD/data:/data" docker.io/library/alpine:3 sh -c 'id -u; touch /data/f'
ls -ln "$PWD/data"

컨테이너는 1000을 출력합니다. 호스트의 파일 목록은 소유자로 100999를 표시하는데, 이는 100000에 1000을 더하고 1을 뺀 값이 100999이기 때문입니다. 이는 오류가 아니며, 일반적인 chown 명령으로는 해결되지 않습니다. 권한이 없는 사용자는 네임스페이스 외부에서 파일 소유권을 변경할 수 없기 때문입니다.

해결 방법은 네 가지입니다.

  • podman unshare chown 1000:1000 "$PWD/data"는 동일한 사용자 네임스페이스 내부에서 chown을 실행하므로, 컨테이너가 인식하는 번호 체계에 따라 소유권이 변경됩니다.
  • -v "$PWD/data:/data:U"는 Podman에게 소스 디렉터리의 소유권을 자동으로 수정하도록 요청합니다. 중요한 데이터가 없는 새 디렉터리에만 사용하십시오.
  • --userns=keep-id은 호스트의 UID를 컨테이너 내부의 동일한 UID로 매핑하여, 새로 생성된 파일의 소유자가 사용자 본인이 되도록 합니다.
  • -v appdata:/data과 같은 명명된 볼륨(named volume)을 사용하면 이 문제를 피할 수 있습니다. Podman이 사용자 스토리지 내부에 소유권이 올바르게 설정된 상태로 볼륨을 생성하기 때문입니다.

Docker에서 이 문제를 겪어본 적이 있다면, 이는 한 단계 위에서 발생하는 동일한 문제입니다. 많은 이미지에서 제공하는 PUID 및 PGID 변수는 컨테이너 내부 프로세스가 사용할 UID를 설정하며, 루트리스(rootless) Podman 환경에서는 해당 UID가 한 번 더 매핑됩니다. 루트리스 컨테이너 내부에서 PUID=1000을 실행해도 호스트 파일은 여전히 100999로 소유권이 지정됩니다. 이 이중 매핑을 고려하여 숫자를 선택하거나, 데이터를 명명된 볼륨으로 옮겨 이 문제를 신경 쓰지 않도록 하십시오.

마운트에 관한 두 가지 추가 사항입니다. Fedora 및 RHEL 예제에서 볼 수 있는 :z:Z 플래그는 SELinux 재레이블 옵션이므로, Ubuntu와 같이 AppArmor를 사용하는 환경에서는 아무런 동작을 하지 않습니다. 또한 루트리스 Podman은 사용자가 읽을 수 없는 호스트 디렉터리를 마운트할 수 없는데, 이는 결함이 아니라 보안을 위한 의도된 동작입니다.

루트리스(rootless) Podman에서 80번 포트 게시가 거부되는 이유는 무엇입니까?

1024번 미만의 포트를 바인딩하려면 일반 사용자에게는 없는 권한이 필요하기 때문입니다. 오류 메시지에 해결 방법이 명시되어 있습니다.

Error: rootlessport cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied

두 가지 해결 방법이 있습니다. 호스트 전체의 임계값을 낮추는 방법입니다.

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system
sysctl net.ipv4.ip_unprivileged_port_start

마지막 명령을 실행하면 80이 출력되어야 합니다. 이 설정의 의미를 명확히 이해해야 합니다. 컨테이너를 실행하는 사용자뿐만 아니라 시스템의 모든 사용자가 80번과 443번 포트를 바인딩할 수 있게 됩니다. 관리자가 한 명인 VPS에서는 허용 가능한 수준이지만, 여러 사용자의 계정이 있는 서버에서는 권장하지 않습니다. 다른 방법은 8080번 포트로 게시하고 앞에 리버스 프록시를 두는 것입니다. 이 방식이 certbot을 사용하여 nginx에서 인증서를 발급 및 갱신하는 데에도 적합합니다.

루트리스 모드에서 포트를 게시하면 애플리케이션이 인식하는 정보도 달라집니다. Podman 4.x는 기본적으로 rootlesskit 포트 핸들러와 함께 slirp4netns를 사용하며, 전달된 연결은 소스 주소가 재작성되어 들어오므로 접근 로그에는 모든 방문자가 10.0.2.100으로 기록됩니다. Podman 5.0부터는 기본값이 pasta로 변경되어 실제 클라이언트 주소가 유지됩니다. 4.x 버전에서는 --network slirp4netns:port_handler=slirp4netns를 사용하면 처리량은 다소 감소하지만 실제 소스 주소를 복원할 수 있습니다.

여기에는 한 가지 긍정적인 측면이 있습니다. 루트리스로 게시된 포트는 일반 프로세스가 소유한 일반적인 리스닝 소켓이므로 방화벽의 입력 규칙이 그대로 적용됩니다. Docker는 NAT(네트워크 주소 변환) 규칙과 자체 포워딩 허용 규칙을 작성하여 포트를 게시하는데, 이것이 바로 게시된 Docker 포트가 ufw 규칙을 무시하고 개방되는 이유입니다. 루트 권한으로 실행되는 Podman도 유사한 방식을 사용하여 동일한 함정에 빠지지만, 루트리스 모드는 그렇지 않습니다.

Docker Compose 파일을 Podman에서 그대로 사용할 수 있습니까?

대부분의 경우 두 가지 경로를 통해 사용할 수 있습니다. 첫 번째는 podman-compose을 사용하는 것으로, 동일한 파일을 읽어 Podman CLI를 구동하는 별도의 구현체입니다.

sudo apt install -y podman-compose
podman-compose up -d
podman ps

두 번째는 실제 Docker Compose를 사용하여 사용자별 소켓을 통해 Podman의 Docker 호환 API와 통신하는 방식입니다.

systemctl --user enable --now podman.socket
export DOCKER_HOST="unix:///run/user/$(id -u)/podman/podman.sock"
docker compose up -d
podman ps

docker compose pspodman ps은 동일한 컨테이너 세트를 공유하므로 목록도 동일하게 출력되어야 합니다. 이름 해석(Name resolution)도 정상적으로 작동합니다. Podman의 기본 네트워크 백엔드인 netavark가 aardvark-dns를 실행하므로, 사용자 정의 네트워크상의 컨테이너들은 이름으로 서로를 찾을 수 있습니다.

물론 한계점도 존재합니다. /var/run/docker.sock을 마운트하는 모든 설정은 Podman 소켓을 가리키도록 수정하거나 제거해야 합니다. network_mode: host은 사용자 네임스페이스 환경에서 다르게 동작합니다. depends_oncondition: service_healthy의 조합은 podman-compose 버전에 따라 지원 수준이 다릅니다. restart: always는 재부팅 시 자동으로 유지되지 않으며, 이는 다음 섹션에서 해결합니다. Compose는 여전히 단일 파일로 다중 컨테이너 스택을 정의하는 좋은 방법이며, Podman 환경에서는 일종의 변환 계층 역할을 합니다. 수년간 운영할 스택이라면, 두 가지 추상화 계층을 유지하는 대신 quadlet으로 변환하여 관리하는 것이 좋습니다.

Pod: Docker가 해결하지 못하는 개념

Pod는 하나의 네트워크 네임스페이스를 공유하는 컨테이너 그룹입니다. Podman은 작은 infra 컨테이너를 시작하여 해당 네임스페이스를 열어두며, 구성원들은 사용자 정의 네트워크나 서비스 디스커버리 없이 127.0.0.1을 통해 서로 통신합니다.

podman pod create --name app -p 8080:80
podman run -d --pod app --name app-cache docker.io/library/redis:7
podman run -d --pod app --name app-web docker.io/library/nginx:1.27
podman pod ps
podman ps --pod

podman pod ps 명령을 실행하면 인프라 컨테이너를 포함하여 3개의 컨테이너가 있는 Pod Running이 표시되어야 합니다. 이제 웹 컨테이너는 app-cache:6379이 아닌 127.0.0.1:6379에서 Redis에 접근합니다. 공유 네임스페이스로부터 두 가지 규칙이 도출됩니다. 포트는 Pod 단위로 게시해야 하며 개별 구성원 단위로 게시해서는 안 됩니다. 또한, 두 구성원이 동일한 포트에서 수신 대기할 수 없습니다.

이는 Kubernetes 모델이며, Podman은 이를 적극적으로 수용합니다. podman kube generate app > app.yaml은 실행 중인 상태로부터 Kubernetes 매니페스트를 작성하며(이전 패키지에서는 podman generate kube로 표기됨), podman kube play app.yaml은 다른 호스트에서 이를 재현합니다. Quadlet에는 이러한 파일을 systemd 서비스로 실행하는 .kube 유닛 유형이 있습니다. 이는 서비스를 그룹화하는 완전히 다른 방식이며, 향후 Kubernetes 도입을 고려한다면 Podman을 선택해야 하는 가장 강력한 이유입니다.

데몬 없이 자동 시작하기: Quadlet 유닛

Quadlet은 systemd 생성기입니다. 컨테이너를 설명하는 짧은 파일을 부팅 시점에 실제 systemd 서비스로 변환합니다. 파일은 루트리스 사용자의 경우 ~/.config/containers/systemd/에, 루트 사용자의 경우 /etc/containers/systemd/에 위치합니다.

~/.config/containers/systemd/caddy.container:

[Unit]
Description=Caddy web server
After=network-online.target

[Container]
Image=docker.io/library/caddy:2
ContainerName=caddy
PublishPort=8080:80
Volume=caddy-data.volume:/data
Environment=TZ=UTC
AutoUpdate=registry

[Service]
Restart=always
MemoryMax=512M

[Install]
WantedBy=default.target

~/.config/containers/systemd/caddy-data.volume은 섹션 헤더가 볼륨을 생성하므로 거의 비어 있어도 됩니다:

[Volume]
systemctl --user daemon-reload
systemctl --user start caddy
systemctl --user status caddy
journalctl --user -u caddy -n 50

서비스 이름은 파일 이름에서 결정됩니다. caddy.containercaddy.service이 됩니다. systemctl --user enable caddy을 실행하지 마십시오. 생성된 유닛은 활성화할 수 없으며, systemd는 Failed to enable unit: Unit /run/user/1000/systemd/generator/caddy.service is transient or generated.라고 응답합니다. [Install] 섹션은 부팅 시 컨테이너를 시작하는 역할을 하며, daemon-reload는 파일을 수정한 후 유닛을 다시 생성하는 명령어입니다.

이제 거의 모든 사용자가 겪는 설정 관련 주의 사항입니다:

sudo loginctl enable-linger deploy
loginctl show-user deploy --property=Linger

Linger=yes를 예상하십시오. linger 설정이 없으면 마지막 SSH 연결이 종료될 때 systemd가 사용자 세션 전체를 정리하므로, 모든 루트리스 컨테이너가 함께 중지되고 부팅 시 다시 시작되지 않습니다. 로그아웃 시 컨테이너가 사라지는 현상은 항상 이 설정 때문입니다.

컨테이너가 일반 서비스 유닛의 메인 프로세스이므로 systemd의 제어 기능이 직접 적용됩니다. [Service] 섹션의 MemoryMax=CPUQuota=systemd로 관리하는 다른 모든 서비스와 동일하게 동작합니다. 이는 Ubuntu 22.04부터 기본으로 사용되는 cgroup v2(control group version 2)가 필요합니다. podman info | grep -i cgroup로 확인하십시오.

업데이트에는 일치하는 메커니즘이 있습니다. AutoUpdate=registrysystemctl --user enable --now podman-auto-update.timer을 함께 사용하면 동일한 태그의 더 최신 이미지가 있는지 레지스트리를 확인하고, 유닛을 재시작하며, 새 컨테이너 시작에 실패할 경우 이전 이미지로 롤백합니다. 변경 사항을 미리 보려면 podman auto-update --dry-run를 먼저 실행하십시오. 이전의 podman generate systemd 명령어는 여전히 존재하지만 더 이상 권장되지 않으므로, 새로운 작업에는 quadlet을 작성하십시오.

docker 별칭이 유효한 경우와 그렇지 않은 경우

sudo apt install -y podman-docker
sudo touch /etc/containers/nodocker
docker ps

podman-docker는 Podman을 호출하는 /usr/bin/docker 래퍼를 설치합니다. nodocker 파일이 없으면 모든 호출 시 먼저 Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.가 출력됩니다. 이 래퍼는 일상적으로 사용하는 다음 명령들을 처리합니다: run, ps, logs, exec, build, pull, push, inspect, cp, volume, network.

적용되지 않는 기능은 더 짧고 명확합니다. Swarm 모드는 대응하는 기능이 없으므로 Swarm 스택을 배포할 수 없습니다. Docker 소켓과 통신하는 도구들은 Podman 소켓을 내보내야 하며, 일부 도구는 여전히 차이를 감지합니다. Traefik의 Docker 프로바이더는 /run/user/<uid>/podman/podman.sock를 가리키면 작동하지만, Watchtower는 podman auto-update가 해당 역할을 수행하므로 사용할 수 없습니다. 저장소가 분리되어 있어 Podman은 Docker로 이미 내려받은 이미지를 볼 수 없으며, 사용 중인 Docker 호스트의 podman images는 처음에는 비어 있는 상태로 시작됩니다.

실행 중인 스택을 단계별로 마이그레이션하기

  1. 컨테이너를 소유할 권한 없는 사용자를 생성하거나 선택하고, 해당 사용자가 /etc/subuid에 범위를 가지고 있는지 확인합니다.
  2. 레지스트리에서 가져온 모든 항목을 정규화된 이름을 사용하여 다시 가져옵니다(pull). Podman은 자체 이미지 저장소를 사용하므로 Docker의 저장소를 읽지 않습니다.
  3. docker save app:1.4 | podman load을 사용하여 로컬에서 빌드된 이미지를 이동합니다.
  4. Docker 컨테이너를 중지하고, /var/lib/docker/volumes/<name>/_data에서 각 볼륨의 내용을 복사한 뒤 podman unshare chown -R 1000:1000 <path>를 사용하여 소유권을 수정합니다.
  5. 포트 문제를 해결합니다. 리버스 프록시 뒤에서 1024 이상의 포트를 게시하거나 net.ipv4.ip_unprivileged_port_start을 설정합니다.
  6. 컨테이너당 하나의 quadlet 파일을 작성하고, systemctl --user daemon-reload을 실행한 뒤 각 서비스를 시작합니다.
  7. sudo loginctl enable-linger <user>을 실행하고 VPS를 재부팅한 다음, 다시 로그인하여 podman ps에 모든 서비스가 다시 나열되는지 확인합니다.

두 엔진은 이미지 저장소와 네트워크를 별도로 관리하므로 공유하는 것이 없습니다. 따라서 마이그레이션하는 동안 두 엔진을 모두 실행할 수 있으며, 유일한 충돌 가능성은 호스트 포트 번호뿐입니다. 한 서비스를 이동하고 하루 동안 모니터링한 뒤 다음 서비스를 이동하십시오.

Podman 대 Docker: VPS에 무엇을 선택해야 할까?

스택이 다른 사람들도 유지보수하는 compose 파일로 구성되어 있거나, Docker 소켓과 통신하는 도구에 의존한다면 Docker를 유지하십시오. 다른 사람들이 작성한 코드와의 호환성은 실질적인 기능이며, Docker는 이 점에서 더 우위에 있습니다. 팀원들의 노트북이 모두 Docker를 실행 중이라면, 운영 환경에서도 동일한 엔진을 실행함으로써 얻는 구체적인 이점이 있습니다.

VPS에서 직접 제어하는 소수의 서비스를 실행하거나, 각 애플리케이션을 docker 그룹 없이 별도의 비권한 사용자 계정으로 실행하고 싶다면 Podman으로 전환하십시오. 배포판과의 일치성도 중요합니다. RHEL과 그 파생 배포판들은 Podman을 지원되는 엔진으로 제공하므로, 해당 시스템에서는 Podman을 사용하는 것이 예기치 않은 문제를 줄이는 방법입니다. 이미 다른 모든 것을 systemd 유닛으로 관리하고 있다면, quadlet은 새로 배워야 할 도구라기보다 빠져 있던 조각이 채워지는 느낌을 줄 것입니다.

중간 단계의 선택지도 하나 있습니다. Rootful Podman은 Docker와 매우 유사하게 동작하며, 래퍼를 통해 docker 명령어를 유지하면서도 항상 실행 중인 데몬을 제거합니다. 다만 이는 보안 환경을 변화시키는 핵심 요소인 rootless 기능을 포기하는 것이므로, 전환 과정의 경유지로 간주하십시오.

아직 첫 번째 컨테이너 호스트를 구축하는 중이라면, 새 VPS에서의 Docker 설정 및 보안 강화 경로가 더 짧은 길이며, 여기서 얻은 지식은 무엇 하나 헛되지 않습니다. 이미지와 볼륨은 두 엔진 모두에서 동일한 객체이므로, 나중에 전환하더라도 서비스 관리 방식만 바뀔 뿐 다른 부분은 거의 변하지 않습니다.

FAQ

Podman은 Docker를 완전히 대체할 수 있습니까?

명령어 수준에서는 거의 그렇습니다. podman-docker를 설치하면 /usr/bin/docker 래퍼가 제공되며, run, ps, build, logsexec은 동일하게 동작합니다. 하지만 데몬을 완전히 대체하는 것은 아닙니다. Swarm에 대응하는 기능은 없으며, /var/run/docker.sock에 연결하는 도구는 사용자별 Podman 소켓을 가리키도록 설정해야 합니다. 또한 Docker로 가져온 이미지는 두 도구가 별도의 저장소를 사용하므로 Podman에서 보이지 않습니다.

SSH 로그아웃 시 루트리스(rootless) Podman 컨테이너가 중지되는 이유는 무엇입니까?

마지막 로그인이 종료되면 systemd가 사용자 세션과 해당 세션의 모든 사용자 서비스를 중지하기 때문입니다. sudo loginctl enable-linger <user>을 실행한 뒤 loginctl show-user <user> --property=Linger의 결과가 Linger=yes로 출력되는지 확인하십시오. Linger를 설정하면 활성 세션이 없어도 해당 사용자의 systemd 인스턴스가 계속 실행되며, 이는 재부팅 후 컨테이너가 자동으로 다시 시작되게 하는 원리이기도 합니다.

볼륨 내 파일의 소유자가 UID 100999로 표시되는 이유는 무엇입니까?

루트리스 Podman은 컨테이너의 UID 0을 호스트 사용자로 매핑하고, 컨테이너의 UID 1부터는 subuid 범위로 매핑합니다. 범위가 100000부터 시작하는 경우, 컨테이너의 UID 1000은 호스트에서 100999가 됩니다. 네임스페이스 내부에서 podman unshare chown 1000:1000 /path/to/data을 사용하여 수정하거나, 처음 실행 시 :U 플래그를 사용하여 마운트하거나, --userns=keep-id를 사용하여 컨테이너의 UID가 사용자의 UID와 일치하도록 설정하십시오.

Podman에서 docker-compose.yml을 계속 사용할 수 있습니까?

네, 두 가지 방법이 있습니다. podman-compose은 파일을 읽어 Podman CLI를 직접 구동합니다. 또는 systemctl --user enable --now podman.socket로 호환성 소켓을 활성화하고 DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock을 설정한 뒤, 실제 docker compose를 해당 소켓에 연결하여 실행할 수 있습니다. network_mode: host 관련 문제, Docker 소켓을 마운트하는 서비스, 그리고 재부팅 후에도 유지하기 위해 quadlet unit과 linger가 필요한 restart: always 사용 시에는 일부 제약이 있을 수 있습니다.

루트리스(rootless)를 사용하면 컨테이너가 정말 더 안전해집니까?

특정 위험을 제거할 수 있습니다. 루트리스 컨테이너를 탈출한 프로세스는 root 권한이 아닌 사용자의 비권한 권한을 갖게 됩니다. 이는 충분히 가치 있는 보안 조치이며, 루트 권한과 동등한 docker 그룹이 루트리스 Podman에 존재하지 않는 이유이기도 합니다. 다만 커널 취약점까지 막아주지는 않으며, 사용자가 읽을 수 있는 파일까지 보호해주지는 않으므로 서버에서 수행하는 일반적인 보안 강화 조치를 병행해야 합니다.