Docker의 데몬은 root로 실행되어 잠재적인 보안 위험이 있다. 반면 Podman은 Daemonless, Rootless, 최소 권한으로 실행되어 보안 이점이 상당하다.
문제는 Podman은 Docker처럼 unless-stopped, always를 사용해도 컨테이너가 자동으로 시작되지 않는다. Podman 컨테이너를 자동으로 시작되게 하려면 systemd를 사용하여 컨테이너를 서비스로 실행되게 해야 한다.
예전에는 podman generate systemd 명령어를 이용하여 컨테이너를 서비스로 실행되게 했지만, Podman 4.6 버전 이후부터 podman generate systemd 명령어 사용 시 사용 중단(deprecated) 경고 메시지를 표시하며, Quadlet 사용을 권장하고 있다. 즉 Podman Quadlet이 컨테이너를 systemd 서비스로 관리하는 데 있어 표준이자 권장되는 방식이다.
podman generate systemd 대신 Quadlet을 사용한 이점이 궁금하다면 아래 링크를 참고하면 도움이 될 것이다.
이 글에서는 Rootless 환경에서 Podman 컨테이너를 Quadlet을 이용하여 systemd 서비스로 관리하는 방법을 다룬다.
여기에 사용된 컨테이너 예제는 Nginx Proxy Manager이다. 컨테이너마다 Quadlet 유닛 파일 내용이 다르므로 Quadlet을 이해하는 목적으로 보는 것을 추천한다.
Quadlet 유닛 파일이 위치할 디렉토리 생성
.container·.pod·.volume·.network·*.kube 파일을 특정 디렉터리에 두기 위해 ~/.config/containers/systemd 폴더를 생성해야 한다. 아래 명령어를 이용하여 ~/.config에 containers/systemd 디렉토리를 생성한다.
mkdir -p ~/.config/containers/systemd/
*.pod 파일 생성
*.pod 파일 안에 [Pod] 섹션으로 pod(여러 컨테이너가 네트워크/IPC/UTS 등을 공유하는 묶음)를 선언하면, *.pod 파일의 [Pod] 섹션을 부팅/재시작, daemon-reload 시 Systemd generator가 읽어 .service 유닛을 생성한다.
대표 키로 PodName, PublishPort, Network, UserNS, Volume 등을 사용할 수 있다.
*.pod를 ~/.config/containers/systemd에 생성한다.
이 글에서는 npm.pod를 사용한다.
[Unit]
Description=NPM pod (rootless)
[Pod]
PublishPort=8080:80/tcp
PublishPort=8443:443/tcp
PublishPort=8081:81/tcp
# 원본 클라이언트 IP 보존 네트워크
Network=pasta
# pasta가 불가하면 ↓로 교체:
# Network=slirp4netns:allow_host_loopback=true,port_handler=slirp4netns
[Install]
WantedBy=default.target
리버스 프록시 등의 컨테이너를 실행할 예정이고, Proxy Host 대상 서버의 Apache, Nginx 웹 서버 등에 Real IP를 기록하고 싶으면 Network에 pasta를 사용하면 된다.
(Podman 4.4.1부터 pasta를 지원한다. slirp4netns 대신 pasta를 사용하는 것이 효율적이다. 차이점은 아래 링크를 참고하길 바란다.)
https://docs.redhat.com/ko/documentation/red_hat_enterprise_linux/10/html/building_running_and_managing_containers/differences-between-slirp4netns-and-pasta
*.container 파일 생성
*.container 파일을 ~/.config/containers/systemd에 생성한다.
이 글에서는 npm-db.container, npm-app.container가 사용된다.
npm-db.container
[Unit]
Description=MariaDB for NPM
Requires=npm-pod.service
After=npm-pod.service
[Container]
# 중요: .pod까지 써야 pod와 링크된다.
Pod=npm.pod
Image=docker.io/jc21/mariadb-aria:10.11.5
Environment=MYSQL_ROOT_PASSWORD=npm
Environment=MYSQL_DATABASE=npm
Environment=MYSQL_USER=npm
Environment=MYSQL_PASSWORD=npm
Environment=MARIADB_AUTO_UPGRADE=1
Environment=TZ=Asia/Seoul
Volume=%h/npm/mysql:/var/lib/mysql:Z
NoNewPrivileges=true
[Install]
WantedBy=default.target
npm-app.container
[Unit]
Description=Nginx Proxy Manager
Requires=npm-db.service
After=npm-db.service
[Container]
Pod=npm.pod
Image=docker.io/jc21/nginx-proxy-manager:2.12.6
Environment=DB_MYSQL_HOST=127.0.0.1
Environment=DB_MYSQL_PORT=3306
Environment=DB_MYSQL_USER=npm
Environment=DB_MYSQL_PASSWORD=npm
Environment=DB_MYSQL_NAME=npm
Environment=TZ=Asia/Seoul
# 필요 시 IPv6 차단
# Environment=DISABLE_IPV6=true
Volume=%h/npm/data:/data:Z
Volume=%h/npm/letsencrypt:/etc/letsencrypt:Z
Volume=%h/npm/403_html:/var/www/html:Z
NoNewPrivileges=true
[Install]
WantedBy=default.target
Volume에 사용할 디렉토리 생성 (Volume 사용하지 않으면 해당되지 않음)
*.container에서 Volume이 사용된다면 Volume에 사용할 디렉토리를 생성해야 한다.
이 예제에서는 mysql, data, letsencrypt, 403_html이 사용된다.
mkdir -p ~/npm/{mysql,data,letsencrypt,403_html}
컨테이너 이미지 다운로드
컨테이너를 시작하기 전 컨테이너 이미지를 다운로드해야 한다.
e.g.
podman pull docker.io/jc21/mariadb-aria:10.11.5
podman pull docker.io/jc21/nginx-proxy-manager:2.12.6
사용자 레벨의 서비스가 백그라운드에서 계속 실행되도록 허용 (Rootless 서비스)
부팅 시 자동으로 시작되어야 하는 사용자 레벨의 서비스(Rootless 컨테이너 등)를 운영할 때 아래의 명령어를 사용해야 한다.
sudo loginctl enable-linger $USER
systemd 파일 변경 사항 다시 로드
systemctl --user daemon-reload
Quadlet Service 유닛이 정상적으로 생성되었는지 확인
아래의 명령어를 이용하여 Quadlet Service 유닛이 정상적으로 생성되었는지 확인한다.
ls -1 /run/user/$(id -u)/systemd/generator
systemctl --user list-unit-files
service 생성이 안 될 경우 .container 문법 오류/필수 키가 누락되었는지 확인해야 한다. 누락 시 service 생성이 아예 안 된다. 가장 흔한 건 Image= 누락, Pod= 값 오기(예: Pod=npm > Pod=npm.pod) 등이다.
컨테이너 시작
Quadlet이 생성하는 유닛은 systemd 입장에서 “generated/transient”라서 systemctl –user enable를 이용하면 컨테이너 시작이 되지 않고 오류가 발생한다.
systemctl –user start를 이용하여 컨테이너를 시작하면 된다.
e.g.
systemctl --user start npm-pod.service
systemctl --user start npm-db.service
systemctl --user start npm-app.service
글을 마치며

Podman은 Docker의 강력한 대안으로 부상하고 있다. 보안과 시스템 통합을 중요시하면 Docker에서 Podman으로의 전환을 고려해 볼 것을 권장한다.