Split View: 쿠버네티스를 로컬로 가져오는 기술: Podman이 선사하는 5가지 반전의 혁신
쿠버네티스를 로컬로 가져오는 기술: Podman이 선사하는 5가지 반전의 혁신
- 1. 서론: Docker의 빛과 그림자, 그리고 Podman으로의 전환
- 2. [혁신 1] 데몬리스 아키텍처: 단일 장애점의 종언
- 3. [혁신 2] 루트리스(Rootless) 보안: 권한의 패러다임 전환
- 4. [혁신 3] play kube: "개발과 운영의 언어를 일치시키다"
- 5. [혁신 4] Pod 단위 관리: 진정한 Pod Manager
- 6. [혁신 5] Pasta 네트워킹: NAT 없는 루트리스 네트워킹
- 7. Podman Desktop: GUI로 만나는 Podman의 모든 것
- 8. Docker에서 Podman으로: 실전 마이그레이션 가이드
- 9. Podman vs Docker: 종합 비교표
- 10. 결론: 컨테이너의 미래는 '뺄셈'에 있다
- 11. 참고 자료 (References)
1. 서론: Docker의 빛과 그림자, 그리고 Podman으로의 전환
1.1 Docker가 바꿔놓은 세계
2013년, dotCloud의 Solomon Hykes가 PyCon에서 시연한 5분짜리 데모가 소프트웨어 산업의 지형을 완전히 바꿔놓았다. "It works on my machine"이라는 개발자의 영원한 변명을 역사의 뒤안길로 보낸 것이다. Docker는 Linux 컨테이너(LXC)를 누구나 쓸 수 있는 도구로 만들어냈고, 이미지 레이어링, Dockerfile, Docker Hub라는 삼위일체로 컨테이너 생태계의 사실상(de facto) 표준이 되었다.
하지만 모든 독점적 표준에는 구조적 취약점이 뒤따른다.
1.2 Docker Desktop 라이선스의 전환점
2021년 8월, Docker Inc.는 Docker Desktop의 라이선스 정책을 변경했다. 직원 수 250명 이상 또는 연매출 1,000만 달러 이상인 기업은 유료 구독(Pro, Team, Business)이 필수가 되었다. 이 변화는 단순한 가격 정책 이상의 의미를 지닌다:
- 공급망 의존성: 핵심 개발 도구가 단일 벤더의 라이선스 정책에 종속
- 규정 준수 리스크: 무단 사용 시 법적 리스크 발생
- 기업 내 대안 탐색 가속화: Red Hat, SUSE, Google 등이 대안 생태계 투자 확대
1.3 공급망 보안이라는 시대적 화두
2020년 SolarWinds 해킹, 2021년 Log4Shell, 2024년 XZ Utils 백도어에 이르기까지, **소프트웨어 공급망 보안(Software Supply Chain Security)**은 이제 선택이 아닌 필수가 되었다. 컨테이너 런타임은 이 공급망의 가장 하부 계층에 위치하며, 그 보안 모델은 전체 스택의 신뢰 기반(Trust Foundation)을 결정한다.
Docker 데몬이 root 권한으로 동작하는 구조는 이 맥락에서 근본적인 재검토 대상이 되었다.
1.4 Podman으로의 시선 전환
이 세 가지 흐름 — 라이선스 리스크, 공급망 보안, 아키텍처 한계 — 이 교차하는 지점에서 Red Hat이 주도하고 오픈소스 커뮤니티가 함께 만들어가는 Podman이 떠오르고 있다.
Podman은 단순한 "Docker 대체제"가 아니다. 그것은 컨테이너 기술의 근본 전제를 재설계한 프로젝트다. 2026년 현재, Podman 5.x 시리즈는 TLS/mTLS 기반 원격 연결 암호화, OCI Artifact 관리 안정화, macOS Hypervisor Framework 네이티브 통합, podman farm build를 통한 멀티플랫폼 빌드까지 지원하며 엔터프라이즈 수준의 컨테이너 플랫폼으로 성숙했다.
이 글에서는 Podman이 제시하는 5가지 근본적 혁신을 아키텍트의 시각에서 심층 분석한다:
| 혁신 | 핵심 키워드 | 해결하는 문제 |
|---|---|---|
| 혁신 1 | 데몬리스 아키텍처 | 단일 장애점(SPOF) 제거 |
| 혁신 2 | 루트리스 보안 | 컨테이너 탈출 위험 근절 |
| 혁신 3 | play kube | 개발/운영 언어 이중성 해소 |
| 혁신 4 | Pod 단위 관리 | 쿠버네티스 네이티브 워크플로우 |
| 혁신 5 | Pasta 네트워킹 | NAT 오버헤드 제거, 성능 도약 |
2. [혁신 1] 데몬리스 아키텍처: 단일 장애점의 종언
2.1 Docker Daemon의 구조적 문제
Docker의 아키텍처를 이해하려면, 그 중심에 있는 dockerd(Docker Daemon)를 이해해야 한다. 모든 Docker CLI 명령은 REST API를 통해 dockerd에 전달되고, dockerd가 containerd를 호출하며, containerd가 다시 runc를 통해 실제 컨테이너를 생성한다.
┌─────────────────────────────────────────────────────────────────┐
│ Docker 아키텍처 (Client-Server) │
│ │
│ ┌──────────┐ REST API ┌──────────────────────────┐ │
│ │ docker │ ─────────────────▶│ dockerd │ │
│ │ CLI │ /var/run/ │ (Docker Daemon) │ │
│ └──────────┘ docker.sock │ ┌──────────────────┐ │ │
│ │ │ containerd │ │ │
│ ┌──────────┐ REST API │ │ ┌────────────┐ │ │ │
│ │ docker │ ─────────────────▶│ │ │ runc │ │ │ │
│ │ CLI │ /var/run/ │ │ └────────────┘ │ │ │
│ └──────────┘ docker.sock │ └──────────────────┘ │ │
│ │ │ │
│ ┌──────────┐ REST API │ ┌─────┐ ┌─────┐ │ │
│ │ docker │ ─────────────────▶│ │ C1 │ │ C2 │ ... │ │
│ │ CLI │ │ └─────┘ └─────┘ │ │
│ └──────────┘ └──────────────────────────┘ │
│ ▲ │
│ │ │
│ ★ 단일 장애점(SPOF) ★ │
│ dockerd가 죽으면 │
│ 모든 컨테이너 관리 불가 │
└─────────────────────────────────────────────────────────────────┘
이 구조의 핵심 문제점은 세 가지다:
첫째, 단일 장애점(SPOF): dockerd가 비정상 종료되면 실행 중인 모든 컨테이너의 관리가 불가능해진다. 컨테이너 자체는 계속 실행되지만, 로그 수집, 헬스체크, 리소스 제한 변경 등 관리 기능을 완전히 상실한다.
둘째, root 권한 요구: dockerd는 기본적으로 root 권한으로 실행된다. /var/run/docker.sock에 접근할 수 있는 모든 사용자는 사실상 root와 동등한 권한을 행사할 수 있다. 이는 **권한 상승(Privilege Escalation)**의 대표적 공격 벡터다.
셋째, 자원 오버헤드: 컨테이너가 하나도 실행되지 않는 유휴 상태에서도 dockerd와 containerd는 메모리를 점유하며 상시 대기한다.
2.2 Podman의 Fork-Exec 모델
Podman은 이 모든 문제를 데몬 자체를 제거하는 것으로 해결했다. Podman의 이름 자체가 Pod Manager의 약자이며, 그 아키텍처는 근본적으로 다른 철학 위에 세워져 있다.
┌─────────────────────────────────────────────────────────────────┐
│ Podman 아키텍처 (Daemonless Fork-Exec) │
│ │
│ ┌──────────┐ fork/exec ┌───────┐ ┌─────────────────┐ │
│ │ podman │ ──────────────▶│ conmon │───▶│ Container 1 │ │
│ │ CLI │ └───────┘ └─────────────────┘ │
│ └──────────┘ │
│ │ │
│ │ fork/exec ┌───────┐ ┌─────────────────┐ │
│ └─────────────────────▶│ conmon │───▶│ Container 2 │ │
│ │ └───────┘ └─────────────────┘ │
│ │ │
│ │ fork/exec ┌───────┐ ┌─────────────────┐ │
│ └─────────────────────▶│ conmon │───▶│ Container 3 │ │
│ └───────┘ └─────────────────┘ │
│ │
│ ★ 데몬 없음: 각 컨테이너가 독립 프로세스 │
│ ★ Podman CLI 종료해도 컨테이너 영향 없음 │
│ ★ conmon: 경량 컨테이너 모니터 (로그, exit code 관리) │
│ ★ OCI 런타임(crun/runc) 직접 호출 │
└─────────────────────────────────────────────────────────────────┘
Podman이 컨테이너를 실행할 때의 프로세스 흐름은 다음과 같다:
- 사용자가
podman run명령을 실행한다 - Podman CLI가 OCI 런타임(기본값:
crun, 또는runc)을 직접 fork/exec한다 conmon(Container Monitor)이 생성되어 컨테이너 프로세스의 표준 출력/에러, 종료 코드를 관리한다- Podman CLI 프로세스는 역할을 마치고 종료된다
- 컨테이너는 독립적인 프로세스로 계속 실행된다
"The best daemon is no daemon at all. Podman proves that container management doesn't need a privileged, always-on service — it needs a tool that does its job and gets out of the way." — Dan Walsh, Red Hat Container Security Architect
2.3 systemd 통합: 운영체제의 일등 시민이 된 컨테이너
Podman의 데몬리스 아키텍처가 가진 가장 강력한 장점 중 하나는 systemd와의 자연스러운 통합이다. Docker에서는 dockerd라는 별도의 서비스 관리자가 컨테이너의 생명주기를 관리했지만, Podman에서는 Linux의 PID 1인 systemd가 직접 컨테이너를 관리한다.
이는 컨테이너가 운영체제의 **일등 시민(First-class citizen)**으로 승격되었음을 의미한다:
- 자동 시작: 시스템 부팅 시 컨테이너 자동 시작
- 의존성 관리:
After=network-online.target등 systemd의 의존성 그래프 활용 - 로깅 통합:
journalctl로 컨테이너 로그 통합 조회 - 리소스 관리: systemd의 cgroup slice를 통한 자원 할당
- 헬스체크:
WatchdogSec=기반의 정밀한 상태 감시
# /etc/containers/systemd/webapp.container (Quadlet 형식)
[Unit]
Description=Production Web Application
After=network-online.target
Wants=network-online.target
[Container]
Image=registry.example.com/webapp:v2.1.0
PublishPort=8080:8080
Environment=NODE_ENV=production
Environment=DB_HOST=db.internal
Volume=/data/webapp:/app/data:Z
Network=webapp.network
AutoUpdate=registry
HealthCmd=/bin/curl -f http://localhost:8080/health || exit 1
HealthInterval=30s
HealthRetries=3
HealthStartPeriod=10s
[Service]
Restart=always
RestartSec=5
TimeoutStartSec=120
WatchdogSec=60
[Install]
WantedBy=multi-user.target default.target
2.4 Quadlet: systemd 네이티브 컨테이너 관리의 결정판
Podman 4.4에서 도입된 Quadlet은 컨테이너를 systemd 유닛 파일의 확장으로 정의하는 혁신적인 방식이다. 기존의 podman generate systemd가 생성하는 긴 명령줄 대신, .container, .volume, .network, .pod, .kube, .build 확장자를 가진 선언적 유닛 파일로 컨테이너를 관리한다.
Quadlet의 핵심 이점:
| 구분 | 기존 방식 (generate systemd) | Quadlet |
|---|---|---|
| 파일 형태 | 자동 생성된 긴 ExecStart 명령 | 선언적 .container 파일 |
| 유지보수 | 이미지 업데이트 시 재생성 필요 | 선언적 수정 후 daemon-reload |
| 가독성 | 한 줄 명령에 모든 옵션 나열 | 섹션별 구조화 |
| 파일 위치 (root) | /etc/systemd/system/ | /etc/containers/systemd/ |
| 파일 위치 (rootless) | ~/.config/systemd/user/ | ~/.config/containers/systemd/ |
멀티 컨테이너 스택 예시 — 데이터베이스와 애플리케이션:
# ~/.config/containers/systemd/app-network.network
[Network]
Subnet=10.89.1.0/24
Gateway=10.89.1.1
# ~/.config/containers/systemd/postgres.container
[Unit]
Description=PostgreSQL Database
[Container]
Image=docker.io/library/postgres:16-alpine
Volume=pgdata.volume:/var/lib/postgresql/data:Z
Network=app-network.network
Environment=POSTGRES_DB=appdb
Environment=POSTGRES_USER=appuser
Secret=pg-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U appuser
HealthInterval=10s
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target
# ~/.config/containers/systemd/pgdata.volume
[Volume]
# Quadlet 적용
systemctl --user daemon-reload
# 서비스 시작
systemctl --user start postgres.service
# 상태 확인
systemctl --user status postgres.service
# 로그 확인
journalctl --user -u postgres.service -f
Quadlet은 **Infrastructure as Code(IaC)**의 정신을 컨테이너 관리에 완벽히 녹여낸 결과물이다. Ansible, Terraform과 같은 도구로 .container 파일을 배포하면, 별도의 컨테이너 오케스트레이터 없이도 systemd가 곧 컨테이너 오케스트레이터가 된다.
3. [혁신 2] 루트리스(Rootless) 보안: 권한의 패러다임 전환
3.1 컨테이너 탈출: 왜 root가 위험한가
Docker의 기본 실행 모델에서 컨테이너 내부의 root(UID 0)는 호스트의 root(UID 0)와 동일한 UID를 공유한다. 물론 Linux Capabilities, seccomp, AppArmor/SELinux 등의 보안 계층이 이를 제한하지만, 커널 취약점이 발견될 때마다 이 제한은 우회될 수 있다.
대표적인 컨테이너 탈출(Container Escape) 사례:
| CVE | 취약점 | 영향 |
|---|---|---|
| CVE-2019-5736 | runc 취약점 | 컨테이너에서 호스트 runc 바이너리 덮어쓰기 |
| CVE-2020-15257 | containerd Shim API | 호스트 네트워크 네임스페이스 접근 |
| CVE-2022-0185 | 커널 heap overflow | 비특권 컨테이너에서 호스트 root 획득 |
| CVE-2024-21626 | runc 파일 디스크립터 유출 | 컨테이너에서 호스트 파일시스템 접근 |
이 모든 취약점의 공통점은 컨테이너 프로세스가 호스트에서 root 권한으로 실행된다는 전제를 악용한다는 것이다.
3.2 User Namespace와 UID 매핑: Podman의 해답
Podman의 루트리스 모드는 Linux User Namespace를 활용하여 이 전제 자체를 무력화한다. 핵심 원리는 단순하다: 컨테이너 내부의 UID 0(root)를 호스트의 비특권 UID로 매핑하는 것이다.
┌─────────────────────────────────────────────────────────────────┐
│ User Namespace UID 매핑 다이어그램 │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ 컨테이너 내부 │ │ 호스트 시스템 │ │
│ │ (User Namespace) │ │ │ │
│ │ │ │ │ │
│ │ UID 0 (root) ─────┼──────┼──▶ UID 1000 (youruser) │ │
│ │ UID 1 ─────┼──────┼──▶ UID 100000 │ │
│ │ UID 2 ─────┼──────┼──▶ UID 100001 │ │
│ │ UID 3 ─────┼──────┼──▶ UID 100002 │ │
│ │ ... │ │ ... │ │
│ │ UID 65535 ─────┼──────┼──▶ UID 165535 │ │
│ │ │ │ │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
│ │
│ ★ 컨테이너의 root가 탈출해도 호스트에서는 UID 1000 │
│ ★ 비특권 사용자 → 시스템 파일 접근 불가 │
│ ★ 커널 취약점 악용 범위 대폭 축소 │
└─────────────────────────────────────────────────────────────────┘
이 매핑은 /etc/subuid와 /etc/subgid 파일로 설정된다:
# /etc/subuid — 사용자별 하위 UID 범위 할당
youruser:100000:65536
# /etc/subgid — 사용자별 하위 GID 범위 할당
youruser:100000:65536
# 의미: youruser는 UID 100000부터 65536개의 UID를 사용할 수 있음
실제 매핑을 확인하는 방법:
# 컨테이너를 루트리스 모드로 실행
podman run -d --name test-rootless alpine sleep 3600
# 컨테이너 내부에서 UID 확인
podman exec test-rootless id
# uid=0(root) gid=0(root) ← 컨테이너 내부에서는 root로 보임
# 호스트에서 실제 UID 확인
podman top test-rootless huser
# HUSER
# 1000 ← 호스트에서는 일반 사용자 (UID 1000)
3.3 User Namespace 모드: 세밀한 보안 제어
Podman은 --userns 옵션을 통해 다양한 User Namespace 모드를 지원한다:
| 모드 | 설명 | 사용 시나리오 |
|---|---|---|
auto | 자동 UID/GID 범위 할당 (기본값) | 일반적인 루트리스 컨테이너 |
keep-id | 호스트 사용자 UID를 컨테이너 내부에도 동일하게 매핑 | 바인드 마운트 파일 권한 유지 |
nomap | 호스트 사용자 UID를 컨테이너에 매핑하지 않음 | 최대 보안 격리 |
host | User Namespace 비활성화 | 레거시 호환성 (비권장) |
# keep-id 모드: 호스트 UID를 컨테이너 내부에서도 유지
# → 바인드 마운트된 파일의 소유권 문제 해결
podman run --userns=keep-id -v $HOME/project:/workspace:Z \
-it node:20-alpine sh
# nomap 모드: 호스트 사용자 UID를 아예 매핑하지 않음
# → 컨테이너가 호스트 사용자 소유 파일에 접근 불가 (world permission 제외)
podman run --userns=nomap alpine cat /proc/self/uid_map
3.4 컴플라이언스: PCI-DSS, FedRAMP, NIST 규정 준수
루트리스 컨테이너는 단순한 기술적 개선을 넘어 규정 준수(Compliance) 측면에서 결정적 이점을 제공한다:
PCI-DSS v4.0 (Payment Card Industry Data Security Standard):
- 요구사항 7.2: "최소 권한 원칙에 따라 시스템 구성요소에 대한 접근을 제한할 것"
- 루트리스 컨테이너는 root 권한 없이 동작하므로 이 요구사항을 구조적으로 충족한다
FedRAMP (Federal Risk and Authorization Management Program):
- AC-6 (Least Privilege): "시스템 기능 수행에 필요한 최소한의 권한만 부여"
- Podman의 루트리스 모드는 이 통제항목의 기술적 구현 자체다
NIST SP 800-190 (Application Container Security Guide):
- "컨테이너를 비root 사용자로 실행하여 호스트 운영체제에 대한 위험을 줄일 것"
- Podman은 이를 기본 동작으로 구현했다
Linux Capabilities 비교 — Docker vs Podman:
Podman은 기본적으로 Docker(14개)보다 적은 11개의 커널 Capabilities만을 컨테이너에 부여한다. 이는 최소 권한 원칙(Principle of Least Privilege)의 구체적 구현이다:
# Podman 기본 Capabilities 확인
podman run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80425fb
# Docker 기본 Capabilities 확인
docker run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80435fb
# → Docker가 더 많은 Capabilities 부여 (NET_RAW 등 추가 포함)
보안 아키텍트의 원칙: "보안은 추가하는 것이 아니라 불필요한 것을 제거하는 것이다." Podman의 루트리스 모델은 이 원칙의 교과서적 구현이다.
4. [혁신 3] play kube: "개발과 운영의 언어를 일치시키다"
4.1 Docker Compose vs Kubernetes YAML: 이중성의 고통
클라우드 네이티브 환경에서 개발자들이 겪는 가장 흔한 마찰(friction) 중 하나는 로컬 개발 환경과 프로덕션 배포 환경의 언어가 다르다는 점이다.
┌─────────────────────────────────────────────────────────────────┐
│ 개발/운영 언어 이중성 문제 │
│ │
│ 개발자 노트북 프로덕션 클러스터 │
│ ┌───────────────────┐ ┌──────────────────────┐ │
│ │ docker-compose.yml│ │ deployment.yaml │ │
│ │ │ ──────▶ │ service.yaml │ │
│ │ version: "3.8" │ 변환 필요 │ configmap.yaml │ │
│ │ services: │ │ ingress.yaml │ │
│ │ web: │ │ │ │
│ │ image: app │ │ apiVersion: apps/v1 │ │
│ │ ports: │ │ kind: Deployment │ │
│ │ - "8080:80" │ │ spec: │ │
│ └───────────────────┘ │ replicas: 3 │ │
│ └──────────────────────┘ │
│ │
│ ★ 두 가지 문법, 두 가지 도구, 두 배의 학습 비용 │
│ ★ 변환 과정에서 미묘한 차이로 인한 배포 실패 │
│ ★ "내 노트북에서는 됐는데..." 문제의 새로운 변종 │
└─────────────────────────────────────────────────────────────────┘
Kompose, Docker Compose to Kubernetes 변환 도구들이 있지만, 이는 근본적 해결이 아닌 문제 위에 레이어를 하나 더 얹는 것에 불과하다.
4.2 play kube: 쿠버네티스 YAML을 로컬에서 실행하다
Podman의 play kube(이전 podman play kube, 현재 podman kube play)는 이 이중성을 원천적으로 해소한다. 쿠버네티스 YAML 파일을 그대로 로컬 Podman 환경에서 실행하는 것이다.
지원되는 Kubernetes 리소스 타입:
- Pod
- Deployment
- DaemonSet
- ConfigMap
- Secret
- PersistentVolumeClaim
단계별 실습: 마이크로서비스 스택을 play kube로 실행하기
Step 1 — Kubernetes YAML 작성 (microservices-stack.yaml):
# microservices-stack.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: 'postgres'
DATABASE_PORT: '5432'
DATABASE_NAME: 'appdb'
APP_LOG_LEVEL: 'info'
---
apiVersion: v1
kind: Pod
metadata:
name: microservices
labels:
app: microservices-stack
environment: development
spec:
# Init 컨테이너: 데이터베이스 마이그레이션
initContainers:
- name: db-migration
image: docker.io/library/flyway/flyway:10
command: ['flyway', 'migrate']
env:
- name: FLYWAY_URL
value: 'jdbc:postgresql://localhost:5432/appdb'
- name: FLYWAY_USER
value: 'appuser'
- name: FLYWAY_PASSWORD
value: 'secret123'
volumeMounts:
- name: migration-scripts
mountPath: /flyway/sql
containers:
# 데이터베이스
- name: postgres
image: docker.io/library/postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: 'appdb'
- name: POSTGRES_USER
value: 'appuser'
- name: POSTGRES_PASSWORD
value: 'secret123'
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
# API 서버
- name: api-server
image: docker.io/library/node:20-alpine
ports:
- containerPort: 3000
hostPort: 3000
env:
- name: DATABASE_HOST
value: 'localhost'
- name: DATABASE_PORT
value: '5432'
command: ['node', 'server.js']
volumeMounts:
- name: app-source
mountPath: /app
# Redis 캐시
- name: redis
image: docker.io/library/redis:7-alpine
ports:
- containerPort: 6379
command: ['redis-server', '--maxmemory', '256mb']
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: pgdata-pvc
- name: migration-scripts
hostPath:
path: ./migrations
- name: app-source
hostPath:
path: ./src
Step 2 — 스택 실행:
# YAML 파일로 전체 스택 생성
podman kube play microservices-stack.yaml
# 실행 상태 확인
podman pod ps
# POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
# a1b2c3d4e5f6 microservices Running 10 seconds ago 9f8e7d6c5b4a 4
# 개별 컨테이너 확인
podman ps --pod
# CONTAINER ID IMAGE STATUS POD ID NAMES
# ... docker.io/library/postgres:16 Up 10 seconds a1b2c3d4e5f6 microservices-postgres
# ... docker.io/library/node:20 Up 8 seconds a1b2c3d4e5f6 microservices-api-server
# ... docker.io/library/redis:7 Up 8 seconds a1b2c3d4e5f6 microservices-redis
# API 서버 로그 확인
podman logs microservices-api-server
Step 3 — 스택 종료:
# --down 플래그로 전체 스택 한 번에 종료 및 정리
podman kube play --down microservices-stack.yaml
# 확인
podman pod ps
# (비어 있음 — 모든 리소스가 정리됨)
4.3 관례 기반 이미지 빌드(Convention-based Build)
play kube의 숨겨진 강력한 기능 중 하나는 관례 기반 자동 이미지 빌드이다. YAML에서 참조하는 이미지 이름과 동일한 이름의 디렉터리가 현재 경로에 존재하고, 그 안에 Containerfile 또는 Dockerfile이 있으면 자동으로 빌드한다.
프로젝트 디렉터리 구조:
├── my-stack.yaml ← image: my-api 참조
├── my-api/ ← 자동 빌드 대상
│ ├── Containerfile
│ └── src/
│ └── server.js
└── my-frontend/ ← image: my-frontend 참조
├── Containerfile
└── dist/
└── index.html
# --build 플래그로 이미지 자동 빌드 + 실행
podman kube play --build my-stack.yaml
# 이미 빌드된 이미지 교체(강제 빌드)
podman kube play --replace --build my-stack.yaml
이 기능은 CI/CD 파이프라인과 로컬 개발 환경을 동일한 YAML로 구동할 수 있게 하며, "Develop locally, deploy globally"라는 Podman의 비전을 구체화한다.
4.4 Init 컨테이너: Always와 Once
Podman은 Kubernetes의 Init Container 개념을 로컬에서도 충실히 구현한다. 특히 Podman만의 확장인 always와 once 타입 구분은 실무적으로 매우 유용하다:
| 타입 | 동작 | 사용 시나리오 |
|---|---|---|
once (기본값) | Pod 시작 시 한 번만 실행 후 삭제 | DB 마이그레이션, 시크릿 초기화 |
always | Pod 시작 시 매번 실행 (삭제되지 않음) | 헬스체크 대기, 설정 파일 갱신 |
# Init 컨테이너 타입 지정 (Podman 어노테이션)
metadata:
annotations:
io.podman.annotations.init.container.type/wait-for-db: 'always'
spec:
initContainers:
- name: wait-for-db
image: docker.io/library/busybox:latest
command:
['sh', '-c', 'until nc -z localhost 5432; do echo "Waiting for DB..."; sleep 2; done']
always 타입의 Init 컨테이너는 podman kube generate에도 포함되므로, 로컬에서 검증한 초기화 로직을 그대로 Kubernetes 클러스터로 가져갈 수 있다.
5. [혁신 4] Pod 단위 관리: 진정한 Pod Manager
5.1 Podman의 본질: Pod를 관리하는 도구
Podman의 이름은 Pod Manager에서 유래했다. 이것은 단순한 네이밍 센스가 아니라, Kubernetes의 Pod 개념을 로컬 환경에서 일급 시민(first-class citizen)으로 구현하겠다는 설계 의도의 표현이다.
Docker에서 여러 컨테이너를 함께 실행하려면 Docker Compose라는 별도 도구가 필요하지만, Podman에서는 Pod가 기본 제공 기능이다.
┌─────────────────────────────────────────────────────────────────┐
│ Podman Pod 아키텍처 │
│ │
│ ┌─── Pod ──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ Infra │ ← pause 컨테이너 │ │
│ │ │Container │ ← 네트워크 네임스페이스 소유자 │ │
│ │ │ (pause) │ ← Pod 생명주기의 앵커 │ │
│ │ └──────────┘ │ │
│ │ │ │ │
│ │ │── 공유: Network Namespace │ │
│ │ │── 공유: IPC Namespace │ │
│ │ │── 공유: UTS Namespace (hostname) │ │
│ │ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ App │ │ Sidecar │ │ Logging │ │ │
│ │ │Container │ │ (envoy) │ │ (fluentd)│ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ :8080 │ │ :15001 │ │ :24224 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ ★ 모든 컨테이너가 localhost로 서로 통신 │ │
│ │ ★ Kubernetes Pod와 동일한 네트워크 모델 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Podman Pod의 핵심 특성:
- Infra 컨테이너: Kubernetes의
pause컨테이너와 동일한 역할. 네트워크 네임스페이스를 소유하고, Pod 내 모든 컨테이너가 이를 공유 - localhost 통신: Pod 내 컨테이너들은
localhost로 서로 통신 (127.0.0.1) - 네임스페이스 공유: Network, IPC, UTS 네임스페이스를 기본 공유
- 일괄 관리:
podman pod start/stop/rm으로 Pod 단위 생명주기 관리
5.2 사이드카 패턴 구현
클라우드 네이티브 아키텍처에서 가장 널리 쓰이는 **사이드카 패턴(Sidecar Pattern)**을 Podman Pod로 로컬에서 완벽하게 구현할 수 있다:
# 1. Pod 생성 (포트 매핑은 Pod 수준에서 설정)
podman pod create --name my-app \
--publish 8080:8080 \
--publish 9090:9090
# 2. 메인 애플리케이션 컨테이너
podman run -d --pod my-app \
--name app-main \
my-registry.io/my-app:v1.0
# 3. 사이드카: Envoy 프록시
podman run -d --pod my-app \
--name app-envoy \
docker.io/envoyproxy/envoy:v1.31
# 4. 사이드카: 로그 수집기
podman run -d --pod my-app \
--name app-logger \
docker.io/fluent/fluentd:v1.17
# 5. 사이드카: 메트릭 수집기
podman run -d --pod my-app \
--name app-metrics \
docker.io/prom/node-exporter:v1.8
# Pod 상태 확인
podman pod inspect my-app
이 패턴은 Kubernetes에서 실행할 서비스 메쉬 구성(Envoy + 앱 + 로깅)을 로컬 개발 환경에서 동일한 네트워크 토폴로지로 테스트할 수 있게 한다.
5.3 generate kube: 로컬에서 클러스터로 가는 다리
Podman의 generate kube(현재 podman kube generate)는 로컬에서 실행 중인 컨테이너나 Pod를 Kubernetes YAML로 내보내는 역방향 워크플로우를 지원한다.
완전한 워크플로우 예시:
# Step 1: 로컬에서 Pod 구성
podman pod create --name webapp --publish 8080:80
podman run -d --pod webapp \
--name webapp-nginx \
-v ./html:/usr/share/nginx/html:Z \
docker.io/library/nginx:alpine
podman run -d --pod webapp \
--name webapp-redis \
docker.io/library/redis:7-alpine
# Step 2: Kubernetes YAML 생성
podman kube generate webapp -f webapp-k8s.yaml
# Step 3: 생성된 YAML 확인
cat webapp-k8s.yaml
생성되는 YAML 예시:
# webapp-k8s.yaml (podman kube generate가 자동 생성)
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: '2026-03-01T09:00:00Z'
labels:
app: webapp
name: webapp
spec:
containers:
- name: webapp-nginx
image: docker.io/library/nginx:alpine
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html-host
- name: webapp-redis
image: docker.io/library/redis:7-alpine
volumes:
- name: html-host
hostPath:
path: /home/user/project/html
type: Directory
# Step 4: 생성된 YAML을 Kubernetes 클러스터에 적용
kubectl apply -f webapp-k8s.yaml
# 또는 동일한 YAML을 다른 Podman 호스트에서 실행
podman kube play webapp-k8s.yaml
generate kube의 추가 옵션:
# Service 객체도 함께 생성
podman kube generate -s webapp -f webapp-with-service.yaml
# Deployment 타입으로 생성 (replicas 지정 가능)
podman kube generate --type deployment --replicas 3 webapp
# DaemonSet 또는 Job 타입으로도 생성 가능
podman kube generate --type job webapp
이 양방향 워크플로우(play kube ↔ generate kube)는 "Develop locally, deploy globally"를 기술적으로 가능하게 만든 Podman의 핵심 혁신이다:
┌───────────────────────────────────────────────────────────┐
│ Podman ↔ Kubernetes 양방향 워크플로우 │
│ │
│ 로컬 개발 Kubernetes 클러스터│
│ ┌─────────┐ ┌─────────────┐ │
│ │ podman │ podman kube generate │ kubectl │ │
│ │ pod │ ───────────────────────▶│ apply -f │ │
│ │ create │ │ │ │
│ │ + run │ podman kube play │ Deployment │ │
│ │ │ ◀───────────────────────│ export │ │
│ └─────────┘ └─────────────┘ │
│ │
│ ★ 동일한 YAML, 동일한 의미론(Semantics) │
│ ★ 환경 간 전환 시 변환(Translation) 불필요 │
└───────────────────────────────────────────────────────────┘
6. [혁신 5] Pasta 네트워킹: NAT 없는 루트리스 네트워킹
6.1 루트리스 네트워킹의 딜레마
루트리스 컨테이너의 가장 큰 기술적 과제 중 하나는 네트워킹이다. 일반 사용자는 네트워크 인터페이스를 생성하거나 iptables 규칙을 조작할 권한이 없다. 이 문제를 해결하기 위해 Podman이 처음 채택한 기술은 slirp4netns였다.
slirp4netns의 동작 원리:
slirp4netns는 사용자 공간(User Space)에서 TCP/IP 스택을 에뮬레이션한다. 컨테이너의 모든 네트워크 트래픽은 다음 경로를 거친다:
컨테이너 → TAP 장치 → slirp4netns (사용자 공간 NAT) → 호스트 네트워크
이 접근법은 동작은 하지만, 모든 패킷이 사용자 공간에서 NAT 변환을 거쳐야 하므로 상당한 지연시간(latency)과 CPU 오버헤드가 발생한다.
6.2 Pasta: 패러다임의 전환
**Pasta(Pack A Subtle Tap Abstraction)**는 passt 프로젝트의 일부로, slirp4netns의 NAT 기반 접근법을 L2-L4 소켓 매핑으로 대체한 차세대 루트리스 네트워킹 솔루션이다.
Podman 5.0부터 기본 루트리스 네트워크 드라이버로 채택되었으며, RHEL 9.5에서도 기본값으로 설정되어 있다.
┌─────────────────────────────────────────────────────────────────┐
│ slirp4netns vs Pasta 아키텍처 비교 │
│ │
│ ┌─ slirp4netns (레거시) ─────────────────────────────────────┐ │
│ │ │ │
│ │ 컨테이너 사용자 공간 호스트 │ │
│ │ ┌───────┐ ┌──────────────────┐ ┌──────────┐ │ │
│ │ │ eth0 │───▶│ slirp4netns │───▶│ 호스트 │ │ │
│ │ │ (TAP) │ │ ┌────────────┐ │ │ 네트워크 │ │ │
│ │ └───────┘ │ │ User-space │ │ └──────────┘ │ │
│ │ │ │ TCP/IP │ │ │ │
│ │ │ │ + NAT │ │ ← 모든 패킷 NAT 변환 │ │
│ │ │ └────────────┘ │ ← CPU 오버헤드 높음 │ │
│ │ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Pasta (Podman 5.0+ 기본값) ───────────────────────────────┐ │
│ │ │ │
│ │ 컨테이너 커널 공간 호스트 │ │
│ │ ┌───────┐ ┌──────────────────┐ ┌──────────┐ │ │
│ │ │ eth0 │───▶│ L2→L4 소켓 매핑 │───▶│ 호스트 │ │ │
│ │ │ (TAP) │ │ ┌────────────┐ │ │ 네트워크 │ │ │
│ │ └───────┘ │ │ splice(2) │ │ │ 설정 복사│ │ │
│ │ │ │ sendmmsg() │ │ └──────────┘ │ │
│ │ │ │ recvmmsg() │ │ │ │
│ │ │ └────────────┘ │ ← NAT 없음 │ │
│ │ └──────────────────┘ ← 직접 소켓 전달 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Pasta의 핵심 기술적 혁신:
- 호스트 네트워크 설정 복사: 호스트의 네트워크 구성을 컨테이너 네임스페이스에 직접 복사하여 NAT 자체를 불필요하게 만듦
- L2-L4 소켓 매핑: Layer-2 TAP 인터페이스의 패킷을 Layer-4 소켓(TCP/UDP/ICMP)으로 직접 매핑
- splice(2) 시스템콜: 로컬 연결의 경우 커널의
splice(2)시스템콜을 사용하여 제로카피(Zero-copy) 데이터 전송 - recvmmsg/sendmmsg 최적화: 여러 패킷을 한 번의 시스템콜로 처리하여 syscall 오버헤드 감소
- Tap Bypass Path: 로컬 목적지 패킷은 L2 변환을 건너뛰고 L4 소켓 간 직접 전달
6.3 성능 비교: slirp4netns vs Pasta
| 지표 | slirp4netns | Pasta | 개선율 |
|---|---|---|---|
| 컨테이너 시작 시간 | ~1.2초 | ~0.8초 | 33% 향상 |
| 메모리 사용량 | ~100MB | ~85MB | 15% 절감 |
| TCP 처리량 (1 연결) | 기준 | +15~30% | 유의미한 개선 |
| TCP 지연시간 (p50) | 기준 | -20~40% | 대폭 개선 |
| UDP 처리량 | 기준 | +10~20% | 개선 |
| 병렬 연결 (≤8) | 기준 | 우위 | Pasta 유리 |
| 병렬 연결 (>8) | 우위 | 기준 | slirp4netns 유리* |
| CPU 오버헤드 (유휴) | 높음 | 낮음 | 유의미한 개선 |
*참고: 높은 병렬성(parallelism > 8)에서는 slirp4netns가 더 나은 성능을 보이는 경우도 보고되었다. 이는 Pasta의 소켓 매핑 방식이 극도로 높은 동시성에서 경합(contention)을 유발할 수 있기 때문이다. 다만, 일반적인 개발 환경과 대부분의 프로덕션 워크로드에서는 Pasta가 전반적으로 우수하다.
6.4 Pasta 네트워킹 설정 및 활용
# Podman 5.0+ 에서는 Pasta가 기본값
# 명시적으로 지정하려면:
podman run --network=pasta -d nginx
# slirp4netns로 폴백하려면:
podman run --network=slirp4netns -d nginx
# Pasta 포트 포워딩 (특정 포트만 노출)
podman run --network=pasta:-T,8080,-U,5353 -d my-app
# Pasta 네트워킹 상태 확인
podman inspect --format '{{.HostConfig.NetworkMode}}' <container>
6.5 네트워크 모드 선택 가이드
| 시나리오 | 권장 네트워크 모드 | 이유 |
|---|---|---|
| 일반 루트리스 개발 | pasta (기본값) | 최적의 성능/호환성 균형 |
| 레거시 호환성 필요 | slirp4netns | 오래된 커널에서 안정적 |
| 루트 컨테이너 | bridge (CNI/netavark) | 루트 모드에서는 전통적 브릿지 사용 |
| 호스트 네트워크 직접 사용 | host | 네트워크 격리 불필요 시 |
| Pod 내 컨테이너 간 통신 | pod 공유 네임스페이스 | localhost 통신 |
7. Podman Desktop: GUI로 만나는 Podman의 모든 것
7.1 Podman Desktop 개요
2025년은 Podman Desktop에게 결정적인 해였다. 300만 다운로드를 돌파하며 Docker Desktop의 대안으로서의 입지를 확고히 했다. 2026년 1월에는 CNCF(Cloud Native Computing Foundation) 프로젝트로도 등재되었다.
Podman Desktop 핵심 기능:
| 기능 | 설명 |
|---|---|
| 컨테이너 관리 | 생성, 시작, 정지, 삭제, 로그 조회, 터미널 접속 |
| 이미지 관리 | 빌드, 풀, 푸시, 검색, 프룬(미사용 이미지 정리) |
| Pod 관리 | Pod 생성, Kubernetes YAML 배포, Pod 로그 검색 |
| 볼륨/네트워크 | 볼륨 생성/삭제, 네트워크 가시성 및 관리 |
| Kubernetes 통합 | Kind, Minikube, OpenShift 클러스터 연동 |
| 확장 에코시스템 | BootC, Headlamp, AI Lab 등 확장 지원 |
| Compose 지원 | Docker Compose 파일 호환 실행 |
| 대시보드 커스터마이징 | 위젯 배치, 테이블 열 커스터마이징 |
7.2 Enterprise Podman Desktop
Red Hat은 Red Hat build of Podman Desktop을 출시하여 기업 환경에 특화된 기능을 제공한다:
- Fleet-wide Settings: 레지스트리 미러, HTTP 프록시 등 조직 단위 설정 관리
- OpenShift 통합: OpenShift 클러스터 직접 배포 지원
- BootC 워크플로우: 컨테이너를 부팅 가능한 OS 이미지로 변환
- 보안 정책 적용: 기업 보안 표준에 맞는 기본 설정 배포
8. Docker에서 Podman으로: 실전 마이그레이션 가이드
8.1 마이그레이션이 간단한 이유
Podman은 Docker CLI와 명령어 수준의 호환성을 유지하도록 설계되었다. 대부분의 docker 명령은 podman으로 직접 대체 가능하다.
8.2 명령어 매핑표
| Docker 명령 | Podman 명령 | 차이점 |
|---|---|---|
docker run | podman run | 동일 |
docker build | podman build | 동일 |
docker pull | podman pull | 동일 |
docker push | podman push | 동일 |
docker ps | podman ps | 동일 |
docker exec | podman exec | 동일 |
docker logs | podman logs | 동일 |
docker images | podman images | 동일 |
docker compose | podman compose | 별도 설치 또는 podman-compose |
docker swarm | ❌ 미지원 | Kubernetes 사용 권장 |
| ❌ 해당 없음 | podman pod | Podman 고유 기능 |
| ❌ 해당 없음 | podman kube play | Podman 고유 기능 |
| ❌ 해당 없음 | podman kube generate | Podman 고유 기능 |
8.3 단계별 마이그레이션
Step 1 — 설치:
# macOS
brew install podman
podman machine init --cpus 4 --memory 8192 --disk-size 60
podman machine start
# RHEL/CentOS/Fedora
sudo dnf install -y podman
# Ubuntu/Debian
sudo apt-get install -y podman
# Windows
winget install RedHat.Podman
Step 2 — Docker 호환 alias 설정 (선택 사항):
# ~/.bashrc 또는 ~/.zshrc
alias docker=podman
# Docker Socket 호환 (Docker API를 사용하는 도구를 위해)
# macOS의 경우:
podman machine stop
podman machine set --rootful
podman machine start
# Linux의 경우 (rootless):
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
Step 3 — Docker Compose 파일 마이그레이션:
# 방법 1: podman compose (Podman 5.x 내장 지원)
podman compose up -d
# 방법 2: podman-compose (Python 기반 독립 도구)
pip install podman-compose
podman-compose up -d
# 방법 3: Kubernetes YAML로 전환 (권장)
# docker-compose.yml → Kubernetes YAML 변환 후
podman kube play my-stack.yaml
Step 4 — CI/CD 파이프라인 업데이트:
# GitHub Actions 예시
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Build Image
run: podman build -t my-app:${{ github.sha }} .
- name: Push to Registry
run: |
podman login -u ${{ secrets.REGISTRY_USER }} \
-p ${{ secrets.REGISTRY_PASS }} \
registry.example.com
podman push my-app:${{ github.sha }} \
registry.example.com/my-app:${{ github.sha }}
8.4 마이그레이션 시 주의사항
| 영역 | Docker | Podman | 대응 방안 |
|---|---|---|---|
| 데몬 상태 | 항상 실행 중 | 데몬 없음 | systemd 서비스로 자동 시작 구성 |
| Docker Socket | /var/run/docker.sock | Podman Socket 활성화 필요 | podman.socket 서비스 활성화 |
| Docker Swarm | 지원 | 미지원 | Kubernetes로 마이그레이션 |
| 이미지 저장소 | /var/lib/docker | ~/.local/share/containers (rootless) | 기존 이미지 재풀 필요 |
| 네트워크 드라이버 | bridge (dockerd 관리) | Netavark + Pasta (rootless) | 호환 가능, 설정 확인 |
| 빌드 캐시 | Docker BuildKit | Podman Buildah 내장 | 호환 가능 |
9. Podman vs Docker: 종합 비교표
| 비교 항목 | Docker | Podman |
|---|---|---|
| 아키텍처 | 클라이언트-서버 (dockerd 데몬) | 데몬리스 (Fork-Exec) |
| 기본 실행 모드 | root (rootless는 실험적) | rootless (기본값) |
| Pod 지원 | 없음 (Compose로 대체) | 네이티브 Pod 지원 |
| Kubernetes YAML 실행 | 별도 도구 필요 | podman kube play 내장 |
| systemd 통합 | 제한적 | Quadlet을 통한 네이티브 통합 |
| 컨테이너 시작 시간 | ~1.2초 | ~0.8초 |
| 유휴 메모리 | ~100MB (데몬 포함) | ~85MB |
| 유휴 CPU | 데몬 상시 실행 | 0% (데몬 없음) |
| 커널 Capabilities | 14개 (기본) | 11개 (기본) |
| OCI 호환 | 완전 호환 | 완전 호환 |
| 이미지 호환 | Docker Hub 기본 | Docker Hub + 모든 OCI 레지스트리 |
| 빌드 도구 | BuildKit | Buildah 내장 |
| 루트리스 네트워킹 | slirp4netns | Pasta (기본값) |
| Docker Compose | 네이티브 | 호환 지원 (podman compose) |
| Docker Swarm | 지원 | 미지원 (Kubernetes 권장) |
| GUI 도구 | Docker Desktop (유료*) | Podman Desktop (무료/오픈소스) |
| 라이선스 | Apache 2.0 (Engine) / 유료 (Desktop) | Apache 2.0 (전체 무료) |
| 멀티플랫폼 빌드 | buildx | podman farm build |
| 시크릿 관리 | Docker Secrets (Swarm) | podman secret |
| TLS/mTLS 원격 접속 | 지원 | 5.x에서 강화 |
| OCI Artifact 관리 | 제한적 | podman artifact (5.x 안정화) |
*Docker Desktop: 직원 250인 이상 또는 연매출 $10M 이상 기업에서 유료 구독 필수
10. 결론: 컨테이너의 미래는 '뺄셈'에 있다
Podman이 제시하는 5가지 혁신을 다시 정리하면, 거기에는 일관된 철학이 관통한다:
10.1 보안을 기본값으로
Docker에서 보안은 "추가하는 것"이었다. rootless 모드를 활성화하고, seccomp 프로파일을 작성하고, AppArmor 정책을 설정해야 했다. Podman에서 보안은 **"이미 켜져 있는 것"**이다. 루트리스가 기본이고, 데몬이 없으며, 최소 Capabilities가 기본 적용된다.
뺄셈의 보안: 데몬을 빼고(데몬리스), root를 빼고(루트리스), 불필요한 권한을 빼는(최소 Capabilities) 것. 추가하는 보안은 잊혀지지만, 기본으로 제공되는 보안은 항상 동작한다.
10.2 개발과 운영의 언어 일치
Docker Compose로 개발하고 Kubernetes YAML로 배포하는 이중성은 **번역 비용(Translation Cost)**이라는 보이지 않는 세금을 개발 조직에 부과해왔다.
Podman의 play kube와 generate kube는 이 세금을 제거한다. 개발자가 작성하는 YAML이 곧 운영 환경의 YAML이 되는 **단일 언어 원칙(Single Language Principle)**을 실현한다.
10.3 자원 효율성
데몬 없는 아키텍처는 유휴 시 CPU 사용량 제로, Pasta 네트워킹은 NAT 오버헤드 제거, systemd 통합은 별도 오케스트레이터의 자원 비용 절감을 의미한다. 이는 단일 개발 머신에서는 체감하기 어려울 수 있지만, 수백 대의 에지 노드, 리소스 제약이 있는 IoT 환경, 비용 최적화가 중요한 클라우드 인프라에서는 유의미한 차이를 만든다.
10.4 최종 정리
┌─────────────────────────────────────────────────────────────────┐
│ Podman의 5가지 혁신 요약 │
│ │
│ ┌─────────────┐ 데몬을 뺐다 → 안정성 확보 │
│ │ 혁신 1 │ SPOF 제거 → 개별 컨테이너 독립성 │
│ │ Daemonless │ systemd 통합 → OS 네이티브 관리 │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ root를 뺐다 → 공격 표면 최소화 │
│ │ 혁신 2 │ User Namespace → UID 격리 │
│ │ Rootless │ 최소 Capabilities → 컴플라이언스 충족 │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ 번역을 뺐다 → 개발/운영 언어 통일 │
│ │ 혁신 3 │ K8s YAML 직접 실행 → 환경 간 일관성 │
│ │ play kube │ 관례 기반 빌드 → 워크플로우 단순화 │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ 추상화를 뺐다 → K8s와 동일한 단위 │
│ │ 혁신 4 │ 네이티브 Pod → 사이드카 패턴 로컬 구현 │
│ │ Pod 관리 │ generate kube → 양방향 워크플로우 │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ NAT를 뺐다 → 지연시간 감소 │
│ │ 혁신 5 │ L2-L4 소켓 매핑 → 처리량 향상 │
│ │ Pasta │ splice(2) → 제로카피 전송 │
│ └─────────────┘ │
│ │
│ ★ 공통 철학: 불필요한 것을 제거하여 본질적 가치를 극대화 │
└─────────────────────────────────────────────────────────────────┘
Podman은 Docker를 부정하는 것이 아니다. Docker가 열어놓은 컨테이너 혁명의 토대 위에서, 보안, 표준 준수, 자원 효율성이라는 현대적 요구사항에 맞게 컨테이너 기술을 재설계한 것이다.
2026년의 클라우드 네이티브 환경에서, 컨테이너 도구를 선택하는 기준은 "무엇을 할 수 있는가"가 아니라 **"무엇을 하지 않는가"**가 되어야 한다. 불필요한 데몬을 실행하지 않고, 불필요한 권한을 부여하지 않으며, 불필요한 번역 계층을 두지 않는 것. 그것이 Podman이 제시하는 컨테이너의 미래다.
11. 참고 자료 (References)
공식 문서
- Podman 공식 문서
- Podman GitHub 릴리스 노트
- Podman Desktop 공식 사이트
- Podman Quadlet 문서
- podman-kube-play 문서
- podman-kube-generate 문서
- passt/pasta 프로젝트
Red Hat 블로그 및 기술 문서
- Podman 5.0 Unveiled — Red Hat Blog
- Understanding rootless Podman's user namespace modes
- Make systemd better for Podman with Quadlet
- Build Kubernetes pods with Podman play kube
- 5 underused Podman features to try now
커뮤니티 및 기술 분석
- Podman 5 X Deep Dive — hrdtr.dev
- Podman vs Docker: Complete 2026 Comparison — Xurrent
- From Podman to Kubernetes — Better Stack
- Rootless network performance discussion — GitHub
- Podman Desktop 2025 Journey
- How does rootless Podman work? — Opensource.com
- Migrating from Docker — Podman Desktop Docs
보안 및 컴플라이언스
Bringing Kubernetes to Your Local Machine: 5 Game-Changing Innovations from Podman
- 1. Introduction: The Light and Shadow of Docker, and the Shift to Podman
- 2. [Innovation 1] Daemonless Architecture: The End of Single Points of Failure
- 3. [Innovation 2] Rootless Security: A Paradigm Shift in Privileges
- 4. [Innovation 3] play kube: "Unifying the Language of Development and Operations"
- 5. [Innovation 4] Pod-Native Management: A True Pod Manager
- 6. [Innovation 5] Pasta Networking: NAT-Free Rootless Networking
- 7. Podman Desktop: Podman's Full Experience Through a GUI
- 8. From Docker to Podman: Practical Migration Guide
- 9. Podman vs Docker: Comprehensive Comparison
- 10. Conclusion: The Future of Containers Lies in Subtraction
- 11. References
- Quiz
1. Introduction: The Light and Shadow of Docker, and the Shift to Podman
1.1 The World Docker Changed
In 2013, a 5-minute demo by Solomon Hykes of dotCloud at PyCon completely reshaped the software industry landscape. It sent the developer's eternal excuse — "It works on my machine" — to the dustbin of history. Docker made Linux containers (LXC) accessible to everyone, and with the trinity of image layering, Dockerfile, and Docker Hub, it became the de facto standard of the container ecosystem.
But every monopolistic standard carries structural vulnerabilities.
1.2 The Turning Point of Docker Desktop Licensing
In August 2021, Docker Inc. changed the Docker Desktop licensing policy. Companies with 250 or more employees or annual revenue exceeding $10 million are now required to have a paid subscription (Pro, Team, Business). This change carries significance beyond a simple pricing policy:
- Supply chain dependency: A critical development tool became subject to a single vendor's licensing policy
- Compliance risk: Unauthorized use creates legal exposure
- Accelerated search for alternatives: Red Hat, SUSE, Google, and others increased investment in alternative ecosystems
1.3 Software Supply Chain Security as an Imperative
From the 2020 SolarWinds hack, the 2021 Log4Shell, to the 2024 XZ Utils backdoor, Software Supply Chain Security has become mandatory, not optional. Container runtimes sit at the lowest layer of this supply chain, and their security model determines the trust foundation of the entire stack.
Docker daemon running with root privileges became a fundamental subject of re-evaluation in this context.
1.4 The Shift to Podman
At the intersection of these three trends — licensing risk, supply chain security, and architectural limitations — Podman, led by Red Hat and built with the open-source community, is emerging.
Podman is not simply a "Docker replacement." It is a project that redesigns the fundamental premises of container technology. As of 2026, the Podman 5.x series supports TLS/mTLS-based remote connection encryption, stabilized OCI Artifact management, native macOS Hypervisor Framework integration, and multi-platform builds via podman farm build, maturing into an enterprise-grade container platform.
This article provides an in-depth analysis of the 5 fundamental innovations Podman brings, from an architect's perspective:
| Innovation | Key Theme | Problem Solved |
|---|---|---|
| Innovation 1 | Daemonless Architecture | Eliminates single point of failure (SPOF) |
| Innovation 2 | Rootless Security | Eradicates container escape risk |
| Innovation 3 | play kube | Resolves dev/ops language duality |
| Innovation 4 | Pod-Native Management | Kubernetes-native workflow |
| Innovation 5 | Pasta Networking | Removes NAT overhead, performance leap |
2. [Innovation 1] Daemonless Architecture: The End of Single Points of Failure
2.1 The Structural Problem of Docker Daemon
To understand Docker's architecture, you must understand dockerd (Docker Daemon) at its center. All Docker CLI commands are passed to dockerd via REST API, which calls containerd, which in turn creates actual containers through runc.
+-------------------------------------------------------------+
| Docker Architecture (Client-Server) |
| |
| +----------+ REST API +------------------------+ |
| | docker | ----------------->| dockerd | |
| | CLI | /var/run/ | (Docker Daemon) | |
| +----------+ docker.sock | +------------------+ | |
| | | containerd | | |
| +----------+ REST API | | +------------+ | | |
| | docker | ----------------->| | | runc | | | |
| | CLI | /var/run/ | | +------------+ | | |
| +----------+ docker.sock | +------------------+ | |
| | | |
| +----------+ REST API | +-----+ +-----+ | |
| | docker | ----------------->| | C1 | | C2 | ... | |
| | CLI | | +-----+ +-----+ | |
| +----------+ +------------------------+ |
| ^ |
| | |
| * Single Point of Failure * |
| If dockerd dies, |
| all container management lost |
+-------------------------------------------------------------+
Three core problems with this structure:
First, Single Point of Failure (SPOF): If dockerd terminates abnormally, management of all running containers becomes impossible. The containers themselves continue running, but all management functions — log collection, health checks, resource limit changes — are completely lost.
Second, Root privilege requirement: dockerd runs with root privileges by default. Any user with access to /var/run/docker.sock effectively has root-equivalent privileges. This is a classic Privilege Escalation attack vector.
Third, Resource overhead: Even when no containers are running, dockerd and containerd occupy memory in standby mode.
2.2 Podman's Fork-Exec Model
Podman solved all these problems by eliminating the daemon entirely. The name Podman itself is short for Pod Manager, and its architecture is built on a fundamentally different philosophy.
+-------------------------------------------------------------+
| Podman Architecture (Daemonless Fork-Exec) |
| |
| +----------+ fork/exec +-------+ +--------------+ |
| | podman | -------------->| conmon |--->| Container 1 | |
| | CLI | +-------+ +--------------+ |
| +----------+ |
| | |
| | fork/exec +-------+ +--------------+ |
| +--------------------->| conmon |--->| Container 2 | |
| | +-------+ +--------------+ |
| | |
| | fork/exec +-------+ +--------------+ |
| +--------------------->| conmon |--->| Container 3 | |
| +-------+ +--------------+ |
| |
| * No daemon: each container is an independent process |
| * Exiting Podman CLI does not affect containers |
| * conmon: lightweight container monitor (logs, exit codes) |
| * Direct OCI runtime (crun/runc) invocation |
+-------------------------------------------------------------+
The process flow when Podman runs a container:
- The user executes
podman run - The Podman CLI directly fork/execs the OCI runtime (default:
crun, orrunc) conmon(Container Monitor) is created to manage the container process's stdout/stderr and exit code- The Podman CLI process finishes its role and exits
- The container continues running as an independent process
"The best daemon is no daemon at all. Podman proves that container management doesn't need a privileged, always-on service — it needs a tool that does its job and gets out of the way." — Dan Walsh, Red Hat Container Security Architect
2.3 systemd Integration: Containers as First-Class Citizens of the OS
One of the most powerful advantages of Podman's daemonless architecture is its natural integration with systemd. In Docker, a separate service manager called dockerd managed container lifecycles, but in Podman, Linux's PID 1, systemd, directly manages containers.
This means containers have been promoted to first-class citizens of the operating system:
- Auto-start: Containers start automatically on system boot
- Dependency management: Leveraging systemd's dependency graph with
After=network-online.target, etc. - Logging integration: Unified container log viewing via
journalctl - Resource management: Resource allocation through systemd cgroup slices
- Health checks: Precise health monitoring based on
WatchdogSec=
# /etc/containers/systemd/webapp.container (Quadlet format)
[Unit]
Description=Production Web Application
After=network-online.target
Wants=network-online.target
[Container]
Image=registry.example.com/webapp:v2.1.0
PublishPort=8080:8080
Environment=NODE_ENV=production
Environment=DB_HOST=db.internal
Volume=/data/webapp:/app/data:Z
Network=webapp.network
AutoUpdate=registry
HealthCmd=/bin/curl -f http://localhost:8080/health || exit 1
HealthInterval=30s
HealthRetries=3
HealthStartPeriod=10s
[Service]
Restart=always
RestartSec=5
TimeoutStartSec=120
WatchdogSec=60
[Install]
WantedBy=multi-user.target default.target
2.4 Quadlet: The Definitive systemd-Native Container Management
Quadlet, introduced in Podman 4.4, is an innovative approach that defines containers as extensions of systemd unit files. Instead of the long command lines generated by the legacy podman generate systemd, it manages containers through declarative unit files with .container, .volume, .network, .pod, .kube, and .build extensions.
Key Benefits of Quadlet:
| Category | Legacy (generate systemd) | Quadlet |
|---|---|---|
| File format | Auto-generated long ExecStart | Declarative .container files |
| Maintenance | Regeneration needed on image update | Declarative edit + daemon-reload |
| Readability | All options on a single command line | Structured by section |
| File location (root) | /etc/systemd/system/ | /etc/containers/systemd/ |
| File location (rootless) | ~/.config/systemd/user/ | ~/.config/containers/systemd/ |
Multi-container stack example — database and application:
# ~/.config/containers/systemd/app-network.network
[Network]
Subnet=10.89.1.0/24
Gateway=10.89.1.1
# ~/.config/containers/systemd/postgres.container
[Unit]
Description=PostgreSQL Database
[Container]
Image=docker.io/library/postgres:16-alpine
Volume=pgdata.volume:/var/lib/postgresql/data:Z
Network=app-network.network
Environment=POSTGRES_DB=appdb
Environment=POSTGRES_USER=appuser
Secret=pg-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U appuser
HealthInterval=10s
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target
# ~/.config/containers/systemd/pgdata.volume
[Volume]
# Apply Quadlet
systemctl --user daemon-reload
# Start service
systemctl --user start postgres.service
# Check status
systemctl --user status postgres.service
# View logs
journalctl --user -u postgres.service -f
Quadlet perfectly embodies the spirit of Infrastructure as Code (IaC) in container management. Deploy .container files with tools like Ansible or Terraform, and without a separate container orchestrator, systemd itself becomes the container orchestrator.
3. [Innovation 2] Rootless Security: A Paradigm Shift in Privileges
3.1 Container Escape: Why Root Is Dangerous
In Docker's default execution model, root (UID 0) inside a container shares the same UID as root (UID 0) on the host. Of course, security layers like Linux Capabilities, seccomp, and AppArmor/SELinux restrict this, but every time a kernel vulnerability is discovered, these restrictions can be bypassed.
Notable container escape cases:
| CVE | Vulnerability | Impact |
|---|---|---|
| CVE-2019-5736 | runc vulnerability | Overwrite host runc binary from container |
| CVE-2020-15257 | containerd Shim API | Access host network namespace |
| CVE-2022-0185 | Kernel heap overflow | Gain host root from unprivileged container |
| CVE-2024-21626 | runc file descriptor leak | Access host filesystem from container |
The common thread in all these vulnerabilities is the exploitation of the premise that container processes run with root privileges on the host.
3.2 User Namespace and UID Mapping: Podman's Answer
Podman's rootless mode uses Linux User Namespace to neutralize this premise entirely. The core principle is simple: map UID 0 (root) inside the container to an unprivileged UID on the host.
+-------------------------------------------------------------+
| User Namespace UID Mapping Diagram |
| |
| +---------------------+ +---------------------------+ |
| | Inside Container | | Host System | |
| | (User Namespace) | | | |
| | | | | |
| | UID 0 (root) ----+------+--> UID 1000 (youruser) | |
| | UID 1 ----+------+--> UID 100000 | |
| | UID 2 ----+------+--> UID 100001 | |
| | UID 3 ----+------+--> UID 100002 | |
| | ... | | ... | |
| | UID 65535 ----+------+--> UID 165535 | |
| | | | | |
| +---------------------+ +---------------------------+ |
| |
| * Even if container root escapes, it's UID 1000 on host |
| * Unprivileged user -> no access to system files |
| * Kernel exploit scope drastically reduced |
+-------------------------------------------------------------+
This mapping is configured via /etc/subuid and /etc/subgid files:
# /etc/subuid - subordinate UID range per user
youruser:100000:65536
# /etc/subgid - subordinate GID range per user
youruser:100000:65536
# Meaning: youruser can use 65536 UIDs starting from UID 100000
How to verify the actual mapping:
# Run a container in rootless mode
podman run -d --name test-rootless alpine sleep 3600
# Check UID inside the container
podman exec test-rootless id
# uid=0(root) gid=0(root) <- Appears as root inside container
# Check actual UID on the host
podman top test-rootless huser
# HUSER
# 1000 <- Regular user (UID 1000) on the host
3.3 User Namespace Modes: Fine-Grained Security Control
Podman supports various User Namespace modes through the --userns option:
| Mode | Description | Use Case |
|---|---|---|
auto | Automatic UID/GID range allocation (default) | General rootless containers |
keep-id | Maps host user UID identically inside the container | Preserving bind mount file permissions |
nomap | Does not map host user UID to the container | Maximum security isolation |
host | Disables User Namespace | Legacy compatibility (not recommended) |
# keep-id mode: Maintain host UID inside the container
# -> Solves bind-mounted file ownership issues
podman run --userns=keep-id -v $HOME/project:/workspace:Z \
-it node:20-alpine sh
# nomap mode: Does not map host user UID at all
# -> Container cannot access host user-owned files (except world permissions)
podman run --userns=nomap alpine cat /proc/self/uid_map
3.4 Compliance: PCI-DSS, FedRAMP, NIST Regulatory Adherence
Rootless containers provide decisive advantages beyond technical improvements in terms of compliance:
PCI-DSS v4.0 (Payment Card Industry Data Security Standard):
- Requirement 7.2: "Restrict access to system components according to the principle of least privilege"
- Rootless containers operate without root privileges, structurally satisfying this requirement
FedRAMP (Federal Risk and Authorization Management Program):
- AC-6 (Least Privilege): "Grant only the minimum privileges required for system functions"
- Podman's rootless mode is the technical implementation of this control itself
NIST SP 800-190 (Application Container Security Guide):
- "Run containers as non-root users to reduce risks to the host operating system"
- Podman implements this as default behavior
Linux Capabilities Comparison — Docker vs Podman:
Podman grants only 11 kernel Capabilities to containers by default, fewer than Docker's 14. This is a concrete implementation of the Principle of Least Privilege:
# Check Podman default Capabilities
podman run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80425fb
# Check Docker default Capabilities
docker run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80435fb
# -> Docker grants more Capabilities (includes NET_RAW, etc.)
Security Architect's Principle: "Security is not about adding things — it's about removing what's unnecessary." Podman's rootless model is a textbook implementation of this principle.
4. [Innovation 3] play kube: "Unifying the Language of Development and Operations"
4.1 Docker Compose vs Kubernetes YAML: The Pain of Duality
One of the most common frictions developers face in cloud native environments is the language for local development and production deployment being different.
+-------------------------------------------------------------+
| Dev/Ops Language Duality Problem |
| |
| Developer Laptop Production Cluster |
| +-------------------+ +--------------------+ |
| | docker-compose.yml| | deployment.yaml | |
| | | -------> | service.yaml | |
| | version: "3.8" | Translation| configmap.yaml | |
| | services: | Required | ingress.yaml | |
| | web: | | | |
| | image: app | | apiVersion: apps/v1| |
| | ports: | | kind: Deployment | |
| | - "8080:80" | | spec: | |
| +-------------------+ | replicas: 3 | |
| +--------------------+ |
| |
| * Two syntaxes, two tools, double the learning cost |
| * Deployment failures due to subtle conversion differences |
| * A new variant of "It worked on my laptop..." |
+-------------------------------------------------------------+
Tools like Kompose and Docker Compose to Kubernetes converters exist, but they are adding another layer on top of the problem, not a fundamental solution.
4.2 play kube: Running Kubernetes YAML Locally
Podman's play kube (previously podman play kube, now podman kube play) eliminates this duality at its source. It runs Kubernetes YAML files as-is in your local Podman environment.
Supported Kubernetes resource types:
- Pod
- Deployment
- DaemonSet
- ConfigMap
- Secret
- PersistentVolumeClaim
Step-by-Step: Running a Microservices Stack with play kube
Step 1 — Write Kubernetes YAML (microservices-stack.yaml):
# microservices-stack.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: 'postgres'
DATABASE_PORT: '5432'
DATABASE_NAME: 'appdb'
APP_LOG_LEVEL: 'info'
---
apiVersion: v1
kind: Pod
metadata:
name: microservices
labels:
app: microservices-stack
environment: development
spec:
# Init container: database migration
initContainers:
- name: db-migration
image: docker.io/library/flyway/flyway:10
command: ['flyway', 'migrate']
env:
- name: FLYWAY_URL
value: 'jdbc:postgresql://localhost:5432/appdb'
- name: FLYWAY_USER
value: 'appuser'
- name: FLYWAY_PASSWORD
value: 'secret123'
volumeMounts:
- name: migration-scripts
mountPath: /flyway/sql
containers:
# Database
- name: postgres
image: docker.io/library/postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: 'appdb'
- name: POSTGRES_USER
value: 'appuser'
- name: POSTGRES_PASSWORD
value: 'secret123'
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
# API Server
- name: api-server
image: docker.io/library/node:20-alpine
ports:
- containerPort: 3000
hostPort: 3000
env:
- name: DATABASE_HOST
value: 'localhost'
- name: DATABASE_PORT
value: '5432'
command: ['node', 'server.js']
volumeMounts:
- name: app-source
mountPath: /app
# Redis Cache
- name: redis
image: docker.io/library/redis:7-alpine
ports:
- containerPort: 6379
command: ['redis-server', '--maxmemory', '256mb']
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: pgdata-pvc
- name: migration-scripts
hostPath:
path: ./migrations
- name: app-source
hostPath:
path: ./src
Step 2 — Run the stack:
# Create the entire stack from the YAML file
podman kube play microservices-stack.yaml
# Check running status
podman pod ps
# POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
# a1b2c3d4e5f6 microservices Running 10 seconds ago 9f8e7d6c5b4a 4
# Check individual containers
podman ps --pod
# CONTAINER ID IMAGE STATUS POD ID NAMES
# ... docker.io/library/postgres:16 Up 10 seconds a1b2c3d4e5f6 microservices-postgres
# ... docker.io/library/node:20 Up 8 seconds a1b2c3d4e5f6 microservices-api-server
# ... docker.io/library/redis:7 Up 8 seconds a1b2c3d4e5f6 microservices-redis
# Check API server logs
podman logs microservices-api-server
Step 3 — Tear down the stack:
# Tear down and clean up the entire stack with the --down flag
podman kube play --down microservices-stack.yaml
# Verify
podman pod ps
# (empty - all resources cleaned up)
4.3 Convention-Based Image Build
One of the hidden powerful features of play kube is convention-based automatic image building. If a directory with the same name as an image referenced in the YAML exists in the current path, and it contains a Containerfile or Dockerfile, it builds automatically.
Project directory structure:
+-- my-stack.yaml <- references image: my-api
+-- my-api/ <- auto-build target
| +-- Containerfile
| +-- src/
| +-- server.js
+-- my-frontend/ <- references image: my-frontend
+-- Containerfile
+-- dist/
+-- index.html
# Auto-build images + run with --build flag
podman kube play --build my-stack.yaml
# Replace already-built images (force build)
podman kube play --replace --build my-stack.yaml
This feature enables running CI/CD pipelines and local development environments with the same YAML, bringing Podman's vision of "Develop locally, deploy globally" to life.
4.4 Init Containers: Always and Once
Podman faithfully implements the Kubernetes Init Container concept locally. The Podman-specific always and once type distinction is particularly useful in practice:
| Type | Behavior | Use Case |
|---|---|---|
once (default) | Runs once at Pod start then removed | DB migration, secret initialization |
always | Runs every time Pod starts (not removed) | Health check waiting, config refresh |
# Specifying Init container type (Podman annotation)
metadata:
annotations:
io.podman.annotations.init.container.type/wait-for-db: 'always'
spec:
initContainers:
- name: wait-for-db
image: docker.io/library/busybox:latest
command:
['sh', '-c', 'until nc -z localhost 5432; do echo "Waiting for DB..."; sleep 2; done']
always-type Init containers are also included in podman kube generate output, so initialization logic validated locally can be carried directly to the Kubernetes cluster.
5. [Innovation 4] Pod-Native Management: A True Pod Manager
5.1 Podman's Essence: A Tool That Manages Pods
Podman's name comes from Pod Manager. This is not just clever naming — it expresses the design intent to implement the Kubernetes Pod concept as a first-class citizen in the local environment.
In Docker, running multiple containers together requires Docker Compose, a separate tool, but in Podman, Pods are a built-in feature.
+-------------------------------------------------------------+
| Podman Pod Architecture |
| |
| +--- Pod ------------------------------------------------+ |
| | | |
| | +----------+ | |
| | | Infra | <- pause container | |
| | |Container | <- network namespace owner | |
| | | (pause) | <- Pod lifecycle anchor | |
| | +----------+ | |
| | | | |
| | |-- Shared: Network Namespace | |
| | |-- Shared: IPC Namespace | |
| | |-- Shared: UTS Namespace (hostname) | |
| | | | |
| | +----------+ +----------+ +----------+ | |
| | | App | | Sidecar | | Logging | | |
| | |Container | | (envoy) | | (fluentd)| | |
| | | | | | | | | |
| | | :8080 | | :15001 | | :24224 | | |
| | +----------+ +----------+ +----------+ | |
| | | |
| | * All containers communicate via localhost | |
| | * Same network model as Kubernetes Pods | |
| +--------------------------------------------------------+ |
| |
+-------------------------------------------------------------+
Key characteristics of Podman Pods:
- Infra container: Same role as Kubernetes
pausecontainer. Owns the network namespace, shared by all containers in the Pod - localhost communication: Containers within a Pod communicate via
localhost(127.0.0.1) - Namespace sharing: Network, IPC, and UTS namespaces shared by default
- Batch management: Pod-level lifecycle management via
podman pod start/stop/rm
5.2 Implementing the Sidecar Pattern
The most widely used Sidecar Pattern in cloud native architecture can be perfectly implemented locally with Podman Pods:
# 1. Create Pod (port mapping is set at the Pod level)
podman pod create --name my-app \
--publish 8080:8080 \
--publish 9090:9090
# 2. Main application container
podman run -d --pod my-app \
--name app-main \
my-registry.io/my-app:v1.0
# 3. Sidecar: Envoy proxy
podman run -d --pod my-app \
--name app-envoy \
docker.io/envoyproxy/envoy:v1.31
# 4. Sidecar: Log collector
podman run -d --pod my-app \
--name app-logger \
docker.io/fluent/fluentd:v1.17
# 5. Sidecar: Metrics collector
podman run -d --pod my-app \
--name app-metrics \
docker.io/prom/node-exporter:v1.8
# Check Pod status
podman pod inspect my-app
This pattern allows you to test service mesh configurations destined for Kubernetes (Envoy + app + logging) in your local development environment with the same network topology.
5.3 generate kube: The Bridge from Local to Cluster
Podman's generate kube (now podman kube generate) supports the reverse workflow of exporting locally running containers or Pods to Kubernetes YAML.
Complete Workflow Example:
# Step 1: Configure Pod locally
podman pod create --name webapp --publish 8080:80
podman run -d --pod webapp \
--name webapp-nginx \
-v ./html:/usr/share/nginx/html:Z \
docker.io/library/nginx:alpine
podman run -d --pod webapp \
--name webapp-redis \
docker.io/library/redis:7-alpine
# Step 2: Generate Kubernetes YAML
podman kube generate webapp -f webapp-k8s.yaml
# Step 3: Inspect generated YAML
cat webapp-k8s.yaml
Generated YAML example:
# webapp-k8s.yaml (auto-generated by podman kube generate)
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: '2026-03-01T09:00:00Z'
labels:
app: webapp
name: webapp
spec:
containers:
- name: webapp-nginx
image: docker.io/library/nginx:alpine
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html-host
- name: webapp-redis
image: docker.io/library/redis:7-alpine
volumes:
- name: html-host
hostPath:
path: /home/user/project/html
type: Directory
# Step 4: Apply generated YAML to Kubernetes cluster
kubectl apply -f webapp-k8s.yaml
# Or run the same YAML on another Podman host
podman kube play webapp-k8s.yaml
Additional generate kube options:
# Generate Service object as well
podman kube generate -s webapp -f webapp-with-service.yaml
# Generate as Deployment type (specify replicas)
podman kube generate --type deployment --replicas 3 webapp
# Can also generate as DaemonSet or Job type
podman kube generate --type job webapp
This bidirectional workflow (play kube <-> generate kube) is the core Podman innovation that makes "Develop locally, deploy globally" technically possible:
+-----------------------------------------------------------+
| Podman <-> Kubernetes Bidirectional Workflow |
| |
| Local Development Kubernetes Cluster |
| +---------+ +-------------+ |
| | podman | podman kube generate| kubectl | |
| | pod | --------------------->| apply -f | |
| | create | | | |
| | + run | podman kube play | Deployment | |
| | | <---------------------| export | |
| +---------+ +-------------+ |
| |
| * Same YAML, same semantics |
| * No translation needed between environments |
+-----------------------------------------------------------+
6. [Innovation 5] Pasta Networking: NAT-Free Rootless Networking
6.1 The Rootless Networking Dilemma
One of the biggest technical challenges of rootless containers is networking. Regular users do not have permission to create network interfaces or manipulate iptables rules. The technology Podman first adopted to solve this was slirp4netns.
How slirp4netns works:
slirp4netns emulates a TCP/IP stack in user space. All network traffic from the container follows this path:
Container -> TAP device -> slirp4netns (user-space NAT) -> Host network
This approach works, but since every packet must undergo NAT translation in user space, it incurs significant latency and CPU overhead.
6.2 Pasta: A Paradigm Shift
Pasta (Pack A Subtle Tap Abstraction) is part of the passt project, a next-generation rootless networking solution that replaces slirp4netns's NAT-based approach with L2-L4 socket mapping.
It was adopted as the default rootless network driver starting with Podman 5.0, and is also set as the default in RHEL 9.5.
+-------------------------------------------------------------+
| slirp4netns vs Pasta Architecture Comparison |
| |
| +- slirp4netns (Legacy) ----------------------------------+|
| | ||
| | Container User Space Host ||
| | +-------+ +------------------+ +----------+ ||
| | | eth0 |--->| slirp4netns |--->| Host | ||
| | | (TAP) | | +------------+ | | Network | ||
| | +-------+ | | User-space | | +----------+ ||
| | | | TCP/IP | | ||
| | | | + NAT | | <- All packets NAT'd||
| | | +------------+ | <- High CPU overhead ||
| | +------------------+ ||
| +----------------------------------------------------------+|
| |
| +- Pasta (Podman 5.0+ default) ---------------------------+|
| | ||
| | Container Kernel Space Host ||
| | +-------+ +------------------+ +----------+ ||
| | | eth0 |--->| L2->L4 socket |--->| Host | ||
| | | (TAP) | | mapping | | Network | ||
| | +-------+ | +------------+ | | cfg copy | ||
| | | | splice(2) | | +----------+ ||
| | | | sendmmsg() | | ||
| | | | recvmmsg() | | ||
| | | +------------+ | <- No NAT ||
| | +------------------+ <- Direct socket fwd ||
| +----------------------------------------------------------+|
| |
+-------------------------------------------------------------+
Pasta's Core Technical Innovations:
- Host network configuration copying: Copies the host's network config directly to the container namespace, making NAT itself unnecessary
- L2-L4 socket mapping: Directly maps Layer-2 TAP interface packets to Layer-4 sockets (TCP/UDP/ICMP)
- splice(2) system call: Uses the kernel's
splice(2)system call for zero-copy data transfer on local connections - recvmmsg/sendmmsg optimization: Processes multiple packets in a single system call, reducing syscall overhead
- Tap Bypass Path: Packets destined for local addresses skip L2 conversion and transfer directly between L4 sockets
6.3 Performance Comparison: slirp4netns vs Pasta
| Metric | slirp4netns | Pasta | Improvement |
|---|---|---|---|
| Container start time | ~1.2s | ~0.8s | 33% faster |
| Memory usage | ~100MB | ~85MB | 15% less |
| TCP throughput (1 conn) | Baseline | +15-30% | Significant |
| TCP latency (p50) | Baseline | -20-40% | Major improvement |
| UDP throughput | Baseline | +10-20% | Improved |
| Parallel connections (8 or fewer) | Baseline | Advantage | Pasta wins |
| Parallel connections (more than 8) | Advantage | Baseline | slirp4netns wins* |
| CPU overhead (idle) | High | Low | Significant |
*Note: In high-parallelism scenarios (parallelism more than 8), slirp4netns may show better performance. This is because Pasta's socket mapping approach can cause contention under extremely high concurrency. However, for typical development environments and most production workloads, Pasta is generally superior.
6.4 Pasta Networking Configuration and Usage
# Pasta is the default in Podman 5.0+
# To explicitly specify:
podman run --network=pasta -d nginx
# To fall back to slirp4netns:
podman run --network=slirp4netns -d nginx
# Pasta port forwarding (expose specific ports only)
podman run --network=pasta:-T,8080,-U,5353 -d my-app
# Check Pasta networking status
podman inspect --format '{{.HostConfig.NetworkMode}}' <container>
6.5 Network Mode Selection Guide
| Scenario | Recommended Network Mode | Reason |
|---|---|---|
| General rootless development | pasta (default) | Best performance/compatibility balance |
| Legacy compatibility needed | slirp4netns | Stable on older kernels |
| Root containers | bridge (CNI/netavark) | Traditional bridge in root mode |
| Direct host network access | host | When network isolation not needed |
| Inter-container communication in Pod | pod shared namespace | localhost communication |
7. Podman Desktop: Podman's Full Experience Through a GUI
7.1 Podman Desktop Overview
2025 was a decisive year for Podman Desktop. It surpassed 3 million downloads, solidifying its position as an alternative to Docker Desktop. In January 2026, it was also listed as a CNCF (Cloud Native Computing Foundation) project.
Podman Desktop Key Features:
| Feature | Description |
|---|---|
| Container management | Create, start, stop, delete, view logs, terminal access |
| Image management | Build, pull, push, search, prune (unused image cleanup) |
| Pod management | Pod creation, Kubernetes YAML deployment, Pod log search |
| Volumes/Networks | Volume create/delete, network visibility and management |
| Kubernetes integration | Kind, Minikube, OpenShift cluster integration |
| Extension ecosystem | BootC, Headlamp, AI Lab extensions |
| Compose support | Docker Compose file compatible execution |
| Dashboard customization | Widget placement, table column customization |
7.2 Enterprise Podman Desktop
Red Hat has released Red Hat build of Podman Desktop with features tailored for enterprise environments:
- Fleet-wide Settings: Organization-level settings management for registry mirrors, HTTP proxies, etc.
- OpenShift integration: Direct OpenShift cluster deployment support
- BootC workflow: Converting containers to bootable OS images
- Security policy enforcement: Deploying default settings aligned with enterprise security standards
8. From Docker to Podman: Practical Migration Guide
8.1 Why Migration Is Simple
Podman is designed to maintain command-level compatibility with the Docker CLI. Most docker commands can be directly replaced with podman.
8.2 Command Mapping Table
| Docker Command | Podman Command | Difference |
|---|---|---|
docker run | podman run | Identical |
docker build | podman build | Identical |
docker pull | podman pull | Identical |
docker push | podman push | Identical |
docker ps | podman ps | Identical |
docker exec | podman exec | Identical |
docker logs | podman logs | Identical |
docker images | podman images | Identical |
docker compose | podman compose | Separate install or podman-compose |
docker swarm | Not supported | Use Kubernetes instead |
| N/A | podman pod | Podman-exclusive feature |
| N/A | podman kube play | Podman-exclusive feature |
| N/A | podman kube generate | Podman-exclusive feature |
8.3 Step-by-Step Migration
Step 1 — Installation:
# macOS
brew install podman
podman machine init --cpus 4 --memory 8192 --disk-size 60
podman machine start
# RHEL/CentOS/Fedora
sudo dnf install -y podman
# Ubuntu/Debian
sudo apt-get install -y podman
# Windows
winget install RedHat.Podman
Step 2 — Docker compatibility alias setup (optional):
# ~/.bashrc or ~/.zshrc
alias docker=podman
# Docker Socket compatibility (for tools that use the Docker API)
# macOS:
podman machine stop
podman machine set --rootful
podman machine start
# Linux (rootless):
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
Step 3 — Docker Compose file migration:
# Method 1: podman compose (Podman 5.x built-in support)
podman compose up -d
# Method 2: podman-compose (Python-based standalone tool)
pip install podman-compose
podman-compose up -d
# Method 3: Convert to Kubernetes YAML (recommended)
# After converting docker-compose.yml to Kubernetes YAML
podman kube play my-stack.yaml
Step 4 — CI/CD pipeline update:
# GitHub Actions example
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Build Image
run: podman build -t my-app:${{ github.sha }} .
- name: Push to Registry
run: |
podman login -u ${{ secrets.REGISTRY_USER }} \
-p ${{ secrets.REGISTRY_PASS }} \
registry.example.com
podman push my-app:${{ github.sha }} \
registry.example.com/my-app:${{ github.sha }}
8.4 Migration Considerations
| Area | Docker | Podman | Mitigation |
|---|---|---|---|
| Daemon state | Always running | No daemon | Configure auto-start via systemd |
| Docker Socket | /var/run/docker.sock | Podman Socket must be enabled | Enable podman.socket service |
| Docker Swarm | Supported | Not supported | Migrate to Kubernetes |
| Image storage | /var/lib/docker | ~/.local/share/containers (rootless) | Existing images must be re-pulled |
| Network driver | bridge (dockerd managed) | Netavark + Pasta (rootless) | Compatible, verify configuration |
| Build cache | Docker BuildKit | Podman Buildah built-in | Compatible |
9. Podman vs Docker: Comprehensive Comparison
| Comparison Item | Docker | Podman |
|---|---|---|
| Architecture | Client-server (dockerd daemon) | Daemonless (Fork-Exec) |
| Default execution mode | root (rootless is experimental) | rootless (default) |
| Pod support | None (Compose as substitute) | Native Pod support |
| Kubernetes YAML execution | Requires separate tools | podman kube play built-in |
| systemd integration | Limited | Native via Quadlet |
| Container start time | ~1.2s | ~0.8s |
| Idle memory | ~100MB (includes daemon) | ~85MB |
| Idle CPU | Daemon always running | 0% (no daemon) |
| Kernel Capabilities | 14 (default) | 11 (default) |
| OCI compliance | Fully compliant | Fully compliant |
| Image compatibility | Docker Hub default | Docker Hub + all OCI registries |
| Build tool | BuildKit | Buildah built-in |
| Rootless networking | slirp4netns | Pasta (default) |
| Docker Compose | Native | Compatible (podman compose) |
| Docker Swarm | Supported | Not supported (Kubernetes recommended) |
| GUI tool | Docker Desktop (paid*) | Podman Desktop (free/open-source) |
| License | Apache 2.0 (Engine) / Paid (Desktop) | Apache 2.0 (entirely free) |
| Multi-platform build | buildx | podman farm build |
| Secret management | Docker Secrets (Swarm) | podman secret |
| TLS/mTLS remote access | Supported | Enhanced in 5.x |
| OCI Artifact management | Limited | podman artifact (5.x stabilized) |
*Docker Desktop: Paid subscription required for companies with 250+ employees or annual revenue exceeding $10M
10. Conclusion: The Future of Containers Lies in Subtraction
Looking back at the 5 innovations Podman presents, a consistent philosophy runs through them all:
10.1 Security as the Default
In Docker, security was something you "added." You had to enable rootless mode, write seccomp profiles, and configure AppArmor policies. In Podman, security is "already on." Rootless is the default, there is no daemon, and minimal Capabilities are applied by default.
Security through subtraction: Removing the daemon (daemonless), removing root (rootless), removing unnecessary privileges (minimal Capabilities). Added security gets forgotten, but security provided by default always works.
10.2 Unifying the Language of Development and Operations
The duality of developing with Docker Compose and deploying with Kubernetes YAML has imposed an invisible tax — the translation cost — on development organizations.
Podman's play kube and generate kube eliminate this tax. They realize the Single Language Principle where the YAML developers write becomes the YAML for the production environment.
10.3 Resource Efficiency
A daemonless architecture means zero CPU usage at idle; Pasta networking means NAT overhead elimination; systemd integration means savings on the resource cost of a separate orchestrator. While this may be hard to notice on a single development machine, it makes a meaningful difference across hundreds of edge nodes, resource-constrained IoT environments, and cost-optimized cloud infrastructure.
10.4 Final Summary
+-------------------------------------------------------------+
| Podman's 5 Innovations Summary |
| |
| +-------------+ Removed the daemon -> Stability |
| | Innovation 1| Eliminated SPOF -> Container autonomy|
| | Daemonless | systemd integration -> OS-native mgmt |
| +-------------+ |
| |
| +-------------+ Removed root -> Minimized attack surface |
| | Innovation 2| User Namespace -> UID isolation |
| | Rootless | Minimal Capabilities-> Compliance |
| +-------------+ |
| |
| +-------------+ Removed translation -> Dev/Ops language unity |
| | Innovation 3| Direct K8s YAML exec-> Cross-env consistency |
| | play kube | Convention builds -> Workflow simplification |
| +-------------+ |
| |
| +-------------+ Removed abstraction -> Same unit as K8s |
| | Innovation 4| Native Pods -> Local sidecar patterns |
| | Pod mgmt | generate kube -> Bidirectional workflow |
| +-------------+ |
| |
| +-------------+ Removed NAT -> Reduced latency |
| | Innovation 5| L2-L4 socket mapping-> Increased throughput |
| | Pasta | splice(2) -> Zero-copy transfer |
| +-------------+ |
| |
| * Common philosophy: Maximize essential value by removing |
| the unnecessary |
+-------------------------------------------------------------+
Podman does not deny Docker. Built on the foundation of the container revolution Docker started, it redesigns container technology to meet modern requirements of security, standards compliance, and resource efficiency.
In the 2026 cloud native landscape, the criterion for choosing a container tool should not be "what can it do" but "what does it not do." Not running an unnecessary daemon, not granting unnecessary privileges, and not adding an unnecessary translation layer. That is the future of containers that Podman envisions.
11. References
Official Documentation
- Podman Official Documentation
- Podman GitHub Release Notes
- Podman Desktop Official Site
- Podman Quadlet Documentation
- podman-kube-play Documentation
- podman-kube-generate Documentation
- passt/pasta Project
Red Hat Blogs and Technical Documentation
- Podman 5.0 Unveiled — Red Hat Blog
- Understanding rootless Podman's user namespace modes
- Make systemd better for Podman with Quadlet
- Build Kubernetes pods with Podman play kube
- 5 underused Podman features to try now
Community and Technical Analysis
- Podman 5 X Deep Dive — hrdtr.dev
- Podman vs Docker: Complete 2026 Comparison — Xurrent
- From Podman to Kubernetes — Better Stack
- Rootless network performance discussion — GitHub
- Podman Desktop 2025 Journey
- How does rootless Podman work? — Opensource.com
- Migrating from Docker — Podman Desktop Docs
Security and Compliance
- NIST SP 800-190: Application Container Security Guide
- Podman Rootless Tutorial — GitHub
- Controlling access to rootless Podman for users — Red Hat
Quiz
Q1: What is the main topic covered in "Bringing Kubernetes to Your Local Machine: 5 Game-Changing Innovations from Podman"?
From Podman daemonless architecture, rootless security model, running K8s YAML locally with play kube, Pod-native management, to Pasta networking — an in-depth analysis from an architect perspective of 5 innovations that have made Podman the new standard for cloud native developm...
Q2: Describe the [Innovation 1] Daemonless Architecture: The End of Single Points of Failure.
2.1 The Structural Problem of Docker Daemon To understand Docker's architecture, you must understand dockerd (Docker Daemon) at its center. All Docker CLI commands are passed to dockerd via REST API, which calls containerd, which in turn creates actual containers through runc.
Q3: Explain the core concept of [Innovation 2] Rootless Security: A Paradigm Shift in Privileges.
3.1 Container Escape: Why Root Is Dangerous In Docker's default execution model, root (UID 0) inside a container shares the same UID as root (UID 0) on the host.
Q4: What are the key aspects of [Innovation 3] play kube: "Unifying the Language of Development and Operations"?
4.1 Docker Compose vs Kubernetes YAML: The Pain of Duality One of the most common frictions developers face in cloud native environments is the language for local development and production deployment being different.
Q5: How does [Innovation 4] Pod-Native Management: A True Pod Manager work?
5.1 Podman's Essence: A Tool That Manages Pods Podman's name comes from Pod Manager. This is not just clever naming — it expresses the design intent to implement the Kubernetes Pod concept as a first-class citizen in the local environment.