00 Podman mimarisi — daemonless ve fork/exec modeli
Docker'ın aksine Podman, sürekli çalışan bir daemon gerektirmez. Her container, kullanıcının oturumundan doğrudan fork/exec ile başlatılır; bu tasarım saldırı yüzeyini dramatik biçimde azaltır.
Docker daemon vs Podman fork/exec
Docker (daemon mimarisi)
─────────────────────────────────────────────────────
docker CLI → dockerd (root daemon) → containerd → runc → container
(her zaman çalışır, root yetkisi)
Podman (daemonless)
─────────────────────────────────────────────────────
podman CLI → conmon (monitor) → crun/runc → container
(her komut ayrı process, kullanıcı yetkisiyle)
Podman, OCI uyumlu libpod kütüphanesini kullanır. Container lifecycle yönetimi doğrudan bu kütüphane aracılığıyla yapılır; araya giren bir daemon yoktur. conmon (container monitor) süreci container'ın stdin/stdout'unu ve çıkış kodunu izler, ancak container silindikten sonra kendisi de sonlanır.
OCI araç zinciri
| Bileşen | Görev | Alternatif |
|---|---|---|
| libpod | Container lifecycle, image, network yönetimi | dockerd |
| conmon | Container I/O monitor, exit code yakalama | containerd-shim |
| crun | OCI runtime — container başlatır (C, hafif) | runc (Go) |
| netavark | Ağ kurulumu — bridge, VLAN, firewall | CNI plugins |
| aardvark-dns | Container DNS çözümlemesi | dnsmasq |
| buildah | OCI image build (Podman build altyapısı) | docker build |
Podman 4.x ve sonrasında varsayılan ağ yığını netavark + aardvark-dns'e geçildi. Eski CNI tabanlı kurulumlar varsa podman network ls ile kontrol edin; geçiş için podman system reset gerekebilir (tüm container/image silinir).
Bu bölümde
- Podman = daemonless, fork/exec; root daemon yoktur
- conmon container I/O izler; crun OCI runtime olarak container başlatır
- Docker CLI ile birebir uyumlu; Dockerfile değişikliği gerekmez
01 Rootless container temelleri — user namespace ve UID mapping
Rootless modda container, host'ta normal bir kullanıcı olarak çalışır. Linux user namespace mekanizması, container içindeki root (uid=0) ile host'taki sub-uid arasında eşleme yapar.
/etc/subuid ve /etc/subgid
Rootless Podman'ın çalışması için her kullanıcıya bir UID/GID alt aralığı tahsis edilmesi gerekir. Bu aralıklar /etc/subuid ve /etc/subgid dosyalarında tanımlanır.
# Kullanıcıya sub-uid/gid aralığı ekle (usermod veya manuel)
sudo usermod --add-subuids 100000-165535 emirhan
sudo usermod --add-subgids 100000-165535 emirhan
# Sonucu doğrula
cat /etc/subuid
# emirhan:100000:65536
# Podman unshare ile namespace içini incele
podman unshare cat /proc/self/uid_map
# container-uid host-uid range
# 0 1000 1 (kullanıcı kendisi → root)
# 1 100000 65536 (container uid 1..65536 → host 100000..165535)
User namespace izolasyonu nasıl çalışır
Host Container (user ns)
──────────────────────────────────────────────────
emirhan (uid=1000) ←map→ root (uid=0)
emirhan+1 (uid=100000) ←map→ uid=1
emirhan+2 (uid=100001) ←map→ uid=2
...
emirhan+N (uid=165535) ←map→ uid=65535
Container içindeki root, host'ta emirhan kullanıcısına eşlendiğinden; container çöküp host dosya sistemine erişmeye çalışsa bile yalnızca emirhan yetkilerine sahip olur. Host root'una erişim yoktur.
# Podman kurulumu (Ubuntu 22.04/Debian 12)
sudo apt-get install -y podman
# Rootless sistem yap — kullanıcı başına migration
podman system migrate
# Rootless olarak container çalıştır
podman run --rm alpine whoami
# root (container içinde root görünür)
# Ama host'ta hangi UID?
podman run --rm alpine sh -c "cat /proc/self/status | grep '^Uid'"
# Uid: 0 0 0 0 (container içinde)
# Bilgi: gerçek host UID'si subuid tablosundan gelir
podman info --format '{{.Host.IDMappings.UIDMap}}'
Storage yapılandırması
Bu bölümde
- Rootless = user namespace + /etc/subuid mapping
- Container içi root → host'ta normal kullanıcı UID'si
- Image ve veriler ~/.local/share/containers altında, root yetkisi gerekmez
02 Rootless networking — slirp4netns ve pasta
Rootless container'lar host ağ namespace'ine doğrudan erişemez. slirp4netns ve yeni nesil pasta araçları, root gerektirmeden sanal ağ arayüzü sağlar.
slirp4netns
slirp4netns, userspace TCP/IP yığını olan bir proxy gibi davranır. Container'ın ağ trafiği, kullanıcı alanında işlenerek host ağına çıkar. Performans tam kernel yığınından düşüktür ama yeterlidir.
# Container içindeki port 80'i host 8080'e yönlendir
podman run -d -p 8080:80 --name webserver nginx:alpine
# Bağlantıyı test et
curl http://localhost:8080
# Ağ bilgisini incele
podman inspect webserver --format '{{.NetworkSettings.Networks}}'
# Rootless modda hangi ağ backend kullanılıyor?
podman info --format '{{.Host.NetworkBackend}}'
pasta (yeni nesil)
Podman 4.4+ ile gelen pasta, slirp4netns'e göre çok daha yüksek performans sunar. Kernel'deki AF_INET/AF_UNIX soketleri doğrudan kullanılır, userspace proxy overhead'i asgari düzeydedir.
[network]
default_rootless_network_cmd = "pasta"
[containers]
# pasta ile DNS otomatik yapılandırılır
dns_servers = []
| Özellik | slirp4netns | pasta |
|---|---|---|
| Performans | Orta — userspace TCP yığını | Yüksek — kernel soket doğrudan |
| IPv6 desteği | Sınırlı | Tam |
| DNS | Manuel yapılandırma | Otomatik host DNS |
| Port forward | -p ile standart | -p ile standart |
| Kernel gereksinimi | >= 4.9 | >= 5.6 |
| Raspberry Pi OS | Bookworm destekler | Bookworm 64-bit destekler |
Host ağ namespace
# Rootless modda --network=host sınırlıdır
# Yalnızca non-privileged portlar (>1024) bind edilebilir
podman run --rm --network=host nginx:alpine
# Rootless <1024 port için: sysctl net.ipv4.ip_unprivileged_port_start=80
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# Kalıcı hale getir:
echo "net.ipv4.ip_unprivileged_port_start=80" \
| sudo tee /etc/sysctl.d/99-podman.conf
Bu bölümde
- slirp4netns: userspace TCP proxy, geniş kernel uyumluluğu
- pasta: Podman 4.4+, daha hızlı, IPv6 tam desteği
- Port <1024 için sysctl ip_unprivileged_port_start ayarı gerekir
03 Temel kullanım — pull, run, exec, stop, rm
Podman'ın günlük kullanımda en sık çalıştırılan komutları: imaj indirme, container başlatma, içine girme, durdurma ve silme.
Image çekme ve listeleme
# Docker Hub'dan imaj çek
podman pull nginx:alpine
podman pull python:3.11-slim
# Birden fazla registry: docker.io, quay.io, ghcr.io
podman pull quay.io/fedora/fedora:latest
podman pull ghcr.io/home-assistant/home-assistant:stable
# Lokal imajları listele
podman images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# docker.io/library/nginx alpine abc123 2 hours ago 42.6MB
# İmaj geçmişini incele
podman history nginx:alpine
Container çalıştırma
# Arka planda çalıştır, port yönlendir, isim ver
podman run -d \
--name webserver \
-p 8080:80 \
-v /home/emirhan/www:/usr/share/nginx/html:ro \
nginx:alpine
# Ortam değişkeni ile çalıştır
podman run -d \
--name myapp \
-e APP_ENV=production \
-e DB_HOST=192.168.1.10 \
-p 5000:5000 \
myapp:latest
# Geçici container (--rm ile otomatik silinir)
podman run --rm -it alpine /bin/sh
# Resource limit ile çalıştır
podman run -d \
--memory 256m \
--cpus 0.5 \
--name limited \
python:3.11-slim python3 -c "while True: pass"
exec, logs, stop, rm
# Çalışan container içine gir
podman exec -it webserver /bin/sh
# Tek komut çalıştır
podman exec webserver nginx -t
# Logları takip et
podman logs -f webserver
# Son 50 satır log
podman logs --tail 50 webserver
# Container durdur (SIGTERM, ardından SIGKILL)
podman stop webserver
podman stop -t 5 webserver # 5 saniye bekle
# Container sil
podman rm webserver
podman rm -f webserver # çalışıyorsa zorla durdur ve sil
# Durmuş tüm containerları temizle
podman container prune
# Sistem temizliği (kullanılmayan image, volume, network)
podman system prune -a
Bu bölümde
- podman pull: docker.io, quay.io, ghcr.io registry'leri destekler
- podman run: -d arka plan, -p port, -v volume, -e env, --memory/--cpus limit
- exec/logs/stop/rm komutları Docker ile birebir uyumludur
04 Podman vs Docker CLI karşılaştırması
Podman, Docker ile API ve CLI uyumluluğunu kasıtlı olarak korur. Mevcut Dockerfile'lar ve komut dosyaları çoğunlukla değişiklik gerektirmez.
Komut eşleştirme tablosu
| İşlem | Docker | Podman | Not |
|---|---|---|---|
| İmaj çek | docker pull img | podman pull img | Birebir uyumlu |
| İmaj oluştur | docker build -t tag . | podman build -t tag . | Buildah kullanır |
| Container başlat | docker run ... | podman run ... | Birebir uyumlu |
| İçine gir | docker exec -it c sh | podman exec -it c sh | Birebir uyumlu |
| Compose çalıştır | docker compose up | podman compose up | podman-compose gerekir |
| Pod yönetimi | Kubernetes gibi değil | podman pod create/start | Podman özgü |
| Systemd entegrasyon | Manuel unit yazma | podman generate systemd / Quadlet | Podman özgü |
| Rootless mod | Deneysel (userns-remap) | Varsayılan | Podman üstündür |
| Registry giriş | docker login | podman login | Birebir uyumlu |
| Secret yönetimi | docker secret (Swarm) | podman secret create | Swarm gerekmez |
Docker alias ile geçiş
# Mevcut script'leri değiştirmeden Podman kullan
alias docker=podman
# ~/.bashrc veya ~/.zshrc'ye ekle
echo "alias docker=podman" >> ~/.bashrc
source ~/.bashrc
# Docker socket emülasyonu (docker-compose uyumluluğu için)
systemctl --user enable --now podman.socket
# DOCKER_HOST ortam değişkeni
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
Önemli farklılıklar
Bu bölümde
- CLI komutları neredeyse birebir; alias docker=podman ile geçiş kolaylaşır
- Rootless, pod ve Quadlet Podman'a özgü güçlü özelliklerdir
- Ağ ve volume izin farklılıklarına dikkat edin
05 Quadlet ile systemd entegrasyonu
Podman 4.4+ ile gelen Quadlet, container'ları doğrudan systemd unit dosyası olarak tanımlamanıza olanak verir. Manuel podman generate systemd scriptine ihtiyaç kalmaz.
Quadlet .container unit dosyası
[Unit]
Description=Nginx Web Sunucusu (Rootless Podman)
After=network-online.target
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=/home/emirhan/www:/usr/share/nginx/html:ro,Z
Environment=NGINX_HOST=example.com
Environment=NGINX_PORT=80
Label=app=webserver
Label=env=production
AutoUpdate=registry
[Service]
Restart=always
TimeoutStartSec=30
TimeoutStopSec=10
[Install]
WantedBy=default.target
# systemd generator'ı yenile (unit dosyası değiştiğinde)
systemctl --user daemon-reload
# Service'i başlat ve etkinleştir
systemctl --user enable --now nginx.service
# Durumu kontrol et
systemctl --user status nginx.service
# Logları izle
journalctl --user -u nginx.service -f
# Service durdur/yeniden başlat
systemctl --user restart nginx.service
systemctl --user stop nginx.service
# Kullanıcı oturumu kapansa da çalışmaya devam et
sudo loginctl enable-linger emirhan
Quadlet .kube ve .network unit dosyaları
[Unit]
Description=My App via Kubernetes YAML
After=network-online.target
[Kube]
Yaml=/home/emirhan/k8s/myapp-pod.yaml
PublishPort=8080:80
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
Bu bölümde
- Quadlet: ~/.config/containers/systemd/*.container → otomatik systemd unit
- systemctl --user enable --now ile rootless container servis olarak çalışır
- loginctl enable-linger: oturum kapansa da servis hayatta kalır
06 Pod konsepti — infra container ve pod yönetimi
Podman'ın pod desteği Kubernetes pod semantiğini yerel ortamda simüle eder. Aynı pod içindeki container'lar ağ ve IPC namespace'i paylaşır; aralarındaki iletişim localhost üzerinden gerçekleşir.
Pod mimarisi
Pod: mypod
┌──────────────────────────────────────────────┐
│ infra container (pause) │
│ (network + IPC namespace sahibi) │
│ │
│ container-1: nginx port 80 │
│ container-2: python-api port 5000 │
│ container-3: log-collector (sidecar) │
└──────────────────────────────────────────────┘
Dışarıya: -p 8080:80 ve -p 5001:5000
# Pod oluştur, dış portları tanımla
podman pod create \
--name webapp \
-p 8080:80 \
-p 5001:5000
# Pod'a container ekle
podman run -d \
--pod webapp \
--name nginx-frontend \
nginx:alpine
podman run -d \
--pod webapp \
--name python-api \
-e API_PORT=5000 \
myapi:latest
# Pod durumunu listele
podman pod list
podman pod inspect webapp
# Pod'daki tüm container'ları listele
podman ps --pod
# Pod başlat/durdur/sil
podman pod stop webapp
podman pod start webapp
podman pod rm -f webapp # tüm container'larla birlikte sil
Container'lar arası iletişim
# Aynı pod içindeki container'lar localhost ile konuşur
# nginx container, python-api'ye şöyle bağlanır:
podman exec nginx-frontend \
wget -qO- http://localhost:5000/health
# Kubernetes YAML'e dönüştür (K3s veya kind ile kullanmak için)
podman generate kube webapp > webapp-pod.yaml
cat webapp-pod.yaml
Bu bölümde
- Pod = shared network/IPC namespace; infra container (pause) namespace'e sahip
- Pod içi iletişim localhost; dışa portlar pod create sırasında tanımlanır
- podman generate kube ile K3s/Kubernetes YAML üretilir
07 ARM64 cross-build ve secret yönetimi
Podman, QEMU user-static ile ARM64/ARMv7 imajlarını x86-64 host üzerinde derleyebilir. Secret yönetimi ise hassas verileri container'a güvenli şekilde iletir.
ARM64 cross-build
# QEMU user-static kurulumu
sudo apt-get install -y qemu-user-static
sudo podman run --rm --privileged \
multiarch/qemu-user-static --reset -p yes
# ARM64 imaj çek ve çalıştır
podman run --rm --platform linux/arm64 \
alpine uname -m
# aarch64
# ARM64 için build et
podman build \
--platform linux/arm64 \
-t myapp:arm64 \
-f Dockerfile .
# Multi-arch manifest oluştur
podman build \
--platform linux/arm64,linux/amd64 \
--manifest myapp:latest \
-f Dockerfile .
# Manifest'i registry'e push et
podman manifest push myapp:latest \
docker://ghcr.io/myorg/myapp:latest
Secret yönetimi
# Dosyadan secret oluştur
echo "s3cr3t_p4ssw0rd" | podman secret create db_password -
# Ortam değişkeninden secret
printf "%s" "$API_KEY" | podman secret create api_key -
# Secret dosyasından oluştur
podman secret create tls_cert /path/to/cert.pem
# Secret listele
podman secret ls
# Container'a secret bağla — /run/secrets/db_password olarak mount edilir
podman run -d \
--secret db_password \
--name mydb-client \
myapp:latest
# Container içinde oku
podman exec mydb-client cat /run/secrets/db_password
# Secret'ı ortam değişkeni olarak da bağlayabilirsin
podman run --rm \
--secret db_password,type=env,target=DB_PASSWORD \
alpine sh -c "echo $DB_PASSWORD"
Bu bölümde
- --platform linux/arm64 ile x86-64 host üzerinde ARM64 imaj derlenir
- podman secret create: değer şifreli saklanır, container'a /run/secrets/ olarak mount edilir
- podman manifest ile tek tag altında çoklu mimari imaj yönetilir
08 Pratik — RPi'da rootless Nginx + Python API pod
Raspberry Pi üzerinde rootless Podman ile Nginx (frontend proxy) ve Flask Python API'yi aynı pod içinde çalıştırıyoruz. Quadlet ile systemd servisi olarak tanımlıyoruz.
Uygulama mimarisi
İstemci :8080
│
▼
Pod: webapp (RPi4, rootless)
┌─────────────────────────────────────────┐
│ nginx:alpine (:80) │
│ /api/* → proxy_pass localhost:5000 │
│ / → /usr/share/nginx/html │
│ │
│ python-api:latest (:5000) │
│ Flask app — GPIO sensor verileri │
│ /api/sensors → JSON response │
└─────────────────────────────────────────┘
Python Flask API
from flask import Flask, jsonify
import time, random
app = Flask(__name__)
@app.route("/api/sensors")
def sensors():
# Gerçek uygulamada GPIO/I2C'den okuma yapılır
return jsonify({
"timestamp": time.time(),
"temperature": 23.4 + random.uniform(-0.5, 0.5),
"humidity": 55.0 + random.uniform(-2.0, 2.0),
"status": "ok"
})
@app.route("/api/health")
def health():
return jsonify({"status": "healthy"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY main.py .
EXPOSE 5000
USER nobody
CMD ["python3", "main.py"]
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location /api/ {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
try_files $uri $uri/ /index.html;
}
}
Quadlet ile deploy
# RPi'da imajı build et (aarch64 native)
podman build -t localhost/python-api:latest ./app
# Quadlet dizinini oluştur
mkdir -p ~/.config/containers/systemd
# Pod unit dosyası
cat > ~/.config/containers/systemd/webapp.pod <<'EOF'
[Unit]
Description=Webapp Pod
[Pod]
PodName=webapp
PublishPort=8080:80
[Install]
WantedBy=default.target
EOF
# Nginx container unit
cat > ~/.config/containers/systemd/nginx.container <<'EOF'
[Unit]
Description=Nginx Frontend
After=webapp-pod.service
[Container]
Image=docker.io/library/nginx:alpine
Pod=webapp.pod
Volume=/home/pi/webapp/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro,Z
Volume=/home/pi/webapp/html:/usr/share/nginx/html:ro,Z
[Service]
Restart=always
EOF
# Python API container unit
cat > ~/.config/containers/systemd/python-api.container <<'EOF'
[Unit]
Description=Python Sensor API
After=webapp-pod.service
[Container]
Image=localhost/python-api:latest
Pod=webapp.pod
[Service]
Restart=always
EOF
# systemd yenile ve servisleri başlat
systemctl --user daemon-reload
systemctl --user enable --now webapp-pod.service
systemctl --user enable --now nginx.service
systemctl --user enable --now python-api.service
# Linger etkinleştir — RPi reboot sonrası otomatik başlar
sudo loginctl enable-linger pi
# Test et
curl http://localhost:8080/
curl http://localhost:8080/api/sensors
# {"timestamp":1712345678.0,"temperature":23.6,"humidity":54.2,"status":"ok"}
# Pod durumu
podman pod ps
podman pod inspect webapp
RPi'da GPIO erişimi için container'ı --device /dev/gpiomem ile çalıştırın. Rootless modda device erişimi kısıtlıdır; libgpiod kullanımı chardev üzerinden daha güvenlidir ve root gerektirmez.
Bu bölümde
- Nginx + Flask API aynı pod içinde localhost ile haberleşir
- Quadlet .pod + .container unit dosyalarıyla systemd servisi tanımlanır
- loginctl enable-linger ile RPi reboot'ta otomatik başlatma sağlanır