Cursus
Elke developer en operations engineer in IT kent Docker voor het bouwen en deployen van applicaties, lokaal of in de cloud. Maar als developer of machine learning operations engineer wil je misschien resources optimaliseren, de beveiliging verbeteren en de systeemintegratie versterken. Podman biedt een overtuigend alternatief. Het is een gratis, open-sourcetool die dient als alternatief voor Docker en Docker Desktop.
In deze tutorial bekijken we wat Podman is, de verschillen tussen Podman en Docker, en hoe je Podman installeert en gebruikt. Daarnaast laten we zien hoe je lokaal machinelearningmodellen kunt trainen, evalueren en deployen met Dockerfile en Podman-commando's.

Afbeelding door auteur
Wat is Podman?
Podman is een open-source tool voor containerbeheer die developers en machine learning engineers een naadloze en veilige ervaring biedt. In tegenstelling tot Docker werkt Podman daemonloos, wat de beveiliging en flexibiliteit verbetert doordat gebruikers containers rootless kunnen draaien. Dankzij deze kernfunctionaliteit kan Podman containers uitvoeren zonder root-rechten, waardoor potentiële kwetsbaarheden worden beperkt.
Podman is volledig compatibel met OCI-standaarden (Open Container Initiative), zodat containers en images die met Podman zijn gemaakt eenvoudig kunnen worden geïntegreerd met andere OCI-compatibele tools en platforms zoals runc, Buildah en Skopeo. Daarnaast ondersteunt Podman het aanmaken en beheren van pods, groepen containers die dezelfde netwerknamespace delen, vergelijkbaar met Kubernetes-pods.
Een van de grootste pluspunten van Podman is dat de ervaring sterk lijkt op die van Docker. De command-line interface is vergelijkbaar met die van Docker en je kunt ook images van Docker Hub pullen. Deze gelijkenis maakt de overstap eenvoudig voor iedereen die al met Docker werkt, terwijl Podman geavanceerde functies biedt die passen bij de steeds veranderende behoeften van het ontwikkelen en deployen van gecontaineriseerde applicaties.
Podman is slechts één tool in het MLOps-arsenaal. Leer over alle soorten tools die in het MLOps-ecosysteem worden gebruikt in de blog 25 Top MLOps-tools die je in 2025 moet kennen.
Vergelijking Docker vs Podman
Docker en Podman zijn toonaangevende tools voor containerbeheer, elk met eigen kenmerken en mogelijkheden. Deze vergelijking bekijkt hun verschillen en helpt je bepalen welke het beste bij je behoeften past.
|
Docker |
Podman |
|
|
Architectuur |
Docker gebruikt een client-serverarchitectuur met een daemonproces genaamd dockerd. |
Podman is daemonloos en gebruikt een fork-execmodel, wat de beveiliging en eenvoud vergroot. |
|
Beveiliging |
Docker draait containers standaard als root, wat veiligheidsrisico's kan opleveren. |
Podman ondersteunt standaard rootless containers, wat veiligheidsrisico's vermindert. |
|
Imagemanagement |
Docker kan containerimages bouwen en beheren met eigen tools. |
Podman gebruikt Buildah voor het bouwen van images en kan images uit Docker-registers draaien. |
|
Compatibiliteit |
Docker wordt breed gebruikt en is geïntegreerd met veel CI/CD-tools. |
Podman biedt een Docker-compatibele CLI, waardoor overstappen zonder workflowwijzigingen eenvoudiger is. |
|
Containerorkestratie |
Docker ondersteunt Docker Swarm en Kubernetes voor orkestratie. |
Podman ondersteunt geen Docker Swarm, maar werkt met Kubernetes via pods. |
|
Platformondersteuning |
Docker draait native op Linux, macOS en Windows (met WSL). |
Podman ondersteunt ook Linux, macOS en Windows (met WSL). |
|
Prestaties |
Docker staat bekend om efficiënt resourcemanagement en snelle deployment. |
Podman is qua performance over het algemeen vergelijkbaar en biedt snellere opstarttijden. |
|
Use cases |
Docker is ideaal voor projecten die gevestigde tools en integraties vereisen. |
Podman is geschikt voor omgevingen met focus op beveiliging en lichtgewicht operaties. Ideaal voor grootschalige deployments. |
De keuze tussen Docker en Podman hangt grotendeels af van de specifieke projectvereisten, vooral op het gebied van beveiliging, compatibiliteit en orkestratie.
Docker blijft een sterke keuze voor gevestigde CI/CD-pijplijnen en uitgebreid containerbeheer, terwijl Podman een veilig, lichtgewicht alternatief biedt voor omgevingen die prioriteit geven aan beveiliging en rootless operaties. Het biedt ook snellere opstarttijden, wat ideaal is voor grootschalige deployments.
Ontdek Docker voor data science door ons inleidende artikel te lezen, inclusief voorbeeldcode en voorbeelden.
Podman installeren
Ga eerst naar het officiële website om het Podman Desktop-pakket te downloaden en te installeren.

Bron: Podman
De installatie is eenvoudig en snel. Binnen enkele minuten kom je op het startscherm, waar je wordt gevraagd om optionele extensies te installeren.
Als je geen WSL op Windows hebt, wordt WSL automatisch geïnstalleerd.

Stel daarna de Podman-machine in.

In vergelijking met Docker hoef je geen machine op te zetten. In Podman kun je echter meerdere machines beheren die gelijktijdig verschillende containers draaien, wat zorgt voor beter resourcemanagement.
Onze machine draait en is klaar om images te maken en containers te starten.

Om te verifiëren dat Podman correct werkt, pullen we een voorbeeldimage van quay.io en voeren we de container uit.
$ podman run quay.io/podman/hello
De Podman-machine heeft het image succesvol gepulld en de container uitgevoerd, waarbij de logs worden getoond.
Trying to pull quay.io/podman/hello:latest...
Getting image source signatures
Copying blob sha256:81df7ff16254ed9756e27c8de9ceb02a9568228fccadbf080f41cc5eb5118a44
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destination
!... Hello Podman World ...!
.--"--.
/ - - \
/ (O) (O) \
~~~| -=(,Y,)=- |
.---. /` \ |~~
~/ o o \~~~~.----. ~~
| =(X)= |~ / (O (O) \
~~~~~~~ ~| =(Y_)=- |
~~~~ ~~~| U |~~
Project: https://github.com/containers/podman
Website: https://podman.io
Desktop: https://podman-desktop.io
Documents: https://docs.podman.io
YouTube: https://youtube.com/@Podman
X/Twitter: @Podman_io
Mastodon: @Podman_io@fosstodon.org
Een MLOps-project bouwen met Podman
In dit MLOps-project automatiseren we de modeltraining en -evaluatie en serveren we het model met Dockerfile en Podman. Dit lijkt op Docker, maar we gebruiken de Podman-CLI om images te bouwen en vervolgens de container te draaien.
Als deze concepten nieuw voor je zijn, leer dan de basis van MLOps in de MLOps Concepts-cursus.
1. Het machinelearningproject opzetten
Om het machinelearningproject op te zetten, maken we een trainings- en een serveerscript, plus een requirements.txt-bestand voor het installeren van Python-packages.
Het Python-trainingsscript laadt de credit score classification-dataset, verwerkt en encodeert die, en traint het model. We voeren ook een modelevaluatie uit. Tot slot slaan we de preprocessing- en trainingspipeline samen met het model op in pickle-formaat.
src/train.py:
# src/train.py
import os
import pickle
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.metrics import (
accuracy_score,
classification_report,
confusion_matrix,
roc_auc_score,
)
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
def load_data():
data_path = "data/train.csv"
df = pd.read_csv(data_path,low_memory=False)
print("Data loaded successfully!")
return df
def preprocess_data(df):
# Drop unnecessary columns
df = df.drop(columns=["ID", "Customer_ID", "SSN", "Name", "Month"])
# Drop rows with missing values
df = df.dropna()
# Convert data types
# Convert the 'Age' column to numeric, setting errors='coerce' to handle non-numeric values
df["Age"] = pd.to_numeric(df["Age"], errors="coerce")
# Filter the DataFrame to include only rows where 'Age' is between 1 and 60
df = df[(df["Age"] >= 1) & (df["Age"] <= 60)]
df["Annual_Income"] = pd.to_numeric(df["Annual_Income"], errors="coerce")
df["Monthly_Inhand_Salary"] = pd.to_numeric(
df["Monthly_Inhand_Salary"], errors="coerce"
)
# Separate features and target
X = df.drop("Credit_Score", axis=1)
y = df["Credit_Score"]
print("Data preprocessed successfully!")
return X, y
def encode_data(X):
# Identify categorical and numerical features
categorical_features = [
"Occupation",
"Credit_Mix",
"Payment_of_Min_Amount",
"Payment_Behaviour",
"Type_of_Loan",
]
numerical_features = X.select_dtypes(include=["int64", "float64"]).columns.tolist()
# Define preprocessing steps
numerical_transformer = SimpleImputer(strategy="median")
categorical_transformer = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore")),
]
)
preprocessor = ColumnTransformer(
transformers=[
("num", numerical_transformer, numerical_features),
("cat", categorical_transformer, categorical_features),
]
)
return preprocessor
def split_data(X, y):
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
return X_train, X_test, y_train, y_test
def train_model(X_train, y_train, preprocessor):
# Create a pipeline with preprocessing and model
clf = Pipeline(
steps=[
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100)),
]
)
# Train the model
clf.fit(X_train, y_train)
# Return the trained model
return clf
def evaluate_model(clf, X_test, y_test):
# Predict and evaluate
y_pred = clf.predict(X_test)
acc = accuracy_score(y_test, y_pred)
report = classification_report(y_test, y_pred, labels=["Poor", "Standard", "Good"])
cm = confusion_matrix(y_test, y_pred, labels=["Poor", "Standard", "Good"])
# Calculate AUC score
y_test_encoded = y_test.replace({"Poor": 0, "Standard": 1, "Good": 2})
y_pred_proba = clf.predict_proba(X_test)
auc_score = roc_auc_score(y_test_encoded, y_pred_proba, multi_class="ovr")
# Print metrics
print("Model Evaluation Metrics:")
print(f"Accuracy: {acc}")
print(f"AUC Score: {auc_score}")
print("Classification Report:")
print(report)
# Plot confusion matrix
plt.figure(figsize=(8, 6))
plt.imshow(cm, interpolation="nearest", cmap=plt.cm.Blues)
plt.title("Confusion Matrix")
plt.colorbar()
tick_marks = np.arange(3)
plt.xticks(tick_marks, ["Poor", "Standard", "Good"], rotation=45)
plt.yticks(tick_marks, ["Poor", "Standard", "Good"])
plt.ylabel("True label")
plt.xlabel("Predicted label")
plt.tight_layout()
cm_path = os.path.join("model", "confusion_matrix.png")
plt.savefig(cm_path)
print(f"Confusion matrix saved to {cm_path}")
def save_model(clf):
model_dir = "model"
os.makedirs(model_dir, exist_ok=True)
model_path = os.path.join(model_dir, "model.pkl")
# Save the trained model
with open(model_path, "wb") as f:
pickle.dump(clf, f)
print(f"Model saved to {model_path}")
def main():
# Execute steps
df = load_data()
X, y = preprocess_data(df)
preprocessor = encode_data(X)
X_train, X_test, y_train, y_test = split_data(X, y)
clf = train_model(X_train, y_train, preprocessor)
evaluate_model(clf, X_test, y_test)
save_model(clf)
if __name__ == "__main__":
main()
Het model-serveerscript laadt de opgeslagen modelpipeline met het modelbestand, maakt een POST-requestfunctie die een lijst met dictionaries van de gebruiker neemt, zet die om naar een DataFrame, geeft die aan het model om voorspellingen te genereren en retourneert vervolgens het voorspelde label. We gebruiken FastAPI als ons API-framework, waarmee we het model met slechts enkele regels code kunnen serveren.
src/app.py:
# src/app.py
import pickle
from fastapi import FastAPI
from pydantic import BaseModel
import pandas as pd
import os
# Load the trained model
model_path = os.path.join("model", "model.pkl")
with open(model_path, "rb") as f:
model = pickle.load(f)
app = FastAPI()
class InputData(BaseModel):
data: list # List of dictionaries representing feature values
@app.post("/predict")
def predict(input_data: InputData):
# Convert input data to DataFrame
X_new = pd.DataFrame(input_data.data)
# Ensure the columns match the training data
prediction = model.predict(X_new)
# Return predictions
return {"prediction": prediction.tolist()}
We moeten een requirements.txt-bestand maken met alle benodigde Python-packages om de bovengenoemde scripts te draaien. Dit bestand gebruiken we om een draaiende omgeving in de Docker-container op te zetten, zodat we de Python-scripts soepel kunnen uitvoeren.
requirements.txt:
fastapi
uvicorn[standard]
numpy
pandas
scikit-learn
pydantic
matplotlib
2. Een Dockerfile maken
Maak een "Dockerfile" en voeg de volgende code toe.
Dit zijn de stappen die deze Dockerfile uitvoert:
- Gebruikt de Python 3.9 slim image als basis
- Stelt /app in als werkdirectory
- Installeert Python-afhankelijkheden uit requirements.txt
- Kopieert broncode en databestanden naar de container
- Maakt een modelmap aan en draait het trainingsscript
- Stelt poort 8000 open voor de API
- Start de FastAPI-app met de uvicorn-server op poort 8000
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application code
COPY ./src/ ./src/
COPY ./data/ ./data/
# Ensure the model directory exists and is copied
RUN mkdir -p model
# Run the training script during the build
RUN python src/train.py
# Expose the port for the API
EXPOSE 8000
# Run the FastAPI app
CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000"]
Je lokale werkruimte zou als volgt moeten zijn ingericht:
- Een map
datamet alle CSV-bestanden - Een map
models - Een map
srcmet de Python-scripts - Plaats in de hoofddirectory een
requirements.txt-bestand en eenDockerfile
De overige bestanden zijn extra componenten voor automatisering en Git-bewerkingen.

3. De Dockerimage bouwen met Podman
Het bouwen van de Dockerimage is eenvoudig: geef het build-commando met je Dockerimagenaam en de huidige directory waar de Dockerfile staat.
$ podman build -t mlops_app .
De buildtool voert alle commando's in de Dockerfile sequentieel uit, van het inrichten van de omgeving tot het serven van de machinelearningapplicatie.
We zien ook in de logs de resultaten van de modelevaluatie. Het model heeft een nauwkeurigheid van 75% en een ROC AUC-score van 0,89, wat gemiddeld is.
STEP 1/11: FROM python:3.9-slim
STEP 2/11: WORKDIR /app
--> Using cache 72ac9e49ae29da1ff19e118653efca17e7a489ae9e7ead917c83d942a3ea4e13
--> 72ac9e49ae29
STEP 3/11: COPY requirements.txt .
--> Using cache 3a05ca95caaf98c448c53a796714328bf9f7cff7896cce348f84a26b8d0dae61
--> 3a05ca95caaf
STEP 4/11: RUN pip install --no-cache-dir -r requirements.txt
--> Using cache 28109d1183449396a5df0006ab603dd5cf2aa2c06a810bdc6bcf0f843f855ee0
--> 28109d118344
STEP 5/11: COPY ./src/ ./src/
--> f814f699c58a
STEP 6/11: COPY ./data/ ./data/
--> 922550900cd0
STEP 7/11: RUN mkdir -p model
--> 36fc01f2d169
STEP 8/11: RUN python src/train.py
Data loaded successfully!
Data preprocessed successfully!
Data encoded successfully!
Data split successfully!
Model trained successfully!
Model Evaluation Metrics:
Accuracy: 0.7546181417149159
ROC AUC Score: 0.8897184704689612
Classification Report:
precision recall f1-score support
Good 0.71 0.67 0.69 1769
Poor 0.75 0.76 0.75 3403
Standard 0.77 0.78 0.78 5709
accuracy 0.75 10881
macro avg 0.74 0.74 0.74 10881
weighted avg 0.75 0.75 0.75 10881
Model saved to model/model.pkl
--> 5d4777c08580
STEP 9/11: EXPOSE 8000
--> 7bb09a613e7f
STEP 10/11: WORKDIR /app/src
--> 06b6394c2e2d
STEP 11/11: CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
COMMIT mlops-app
--> 9a7a42b03664
Successfully tagged localhost/mlops-app:latest
9a7a42b03664f1e4631330cd682cb2de26e513c5d776fa2ce2042b3bb9455e14
Als je de Podman Desktop-app opent en op het tabblad "Images" klikt, zie je dat je mlops_app-image succesvol is gemaakt.

Bekijk de Docker voor data science-cheat sheet om alle relevante Docker-commando's te leren. Vervang gewoon het eerste commando, docker, door podman.
4. De Dockercontainer draaien met Podman
We gebruiken het run-commando om een container met de naam "mlops_container" te starten vanaf het mlops-app-image. Dit gebeurt in detached-modus (-d), waarbij poort 8000 van de container wordt gemapt naar poort 8000 op de hostmachine. Hiermee kun je de FastAPI-applicatie van buiten de container benaderen.
$ podman run -d --name mlops_container -p 8000:8000 mlops-app
Gebruik het logs-commando om alle logs voor "mlops_container" te bekijken.
$ podman logs -f mlops_container
Output:
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 10.88.0.1:36886 - "POST /predict HTTP/1.1" 200 OK
Je kunt ook de Podman Desktop-app openen en op het tabblad "Containers" klikken om de draaiende containers te bekijken.

Om de logs in de Podman Desktop-app te bekijken, klik je op "mlops_container" en vervolgens op het tabblad "Terminal".

5. De ML-inferenceserver testen
We testen nu de gedeployde applicatie via de interactieve Swagger UI op: http://localhost:8000/docs. De Swagger UI biedt een gebruiksvriendelijke interface waarmee je alle beschikbare API-eindpunten kunt verkennen.


We kunnen de API ook testen met het CURL-commando in de terminal.
$ curl -X POST "http://localhost:8000/predict" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"Age": 35,
"Occupation": "Engineer",
"Annual_Income": 85000,
"Monthly_Inhand_Salary": 7000,
"Num_Bank_Accounts": 2,
"Num_Credit_Card": 3,
"Interest_Rate": 5,
"Num_of_Loan": 1,
"Type_of_Loan": "Personal Loan",
"Delay_from_due_date": 2,
"Num_of_Delayed_Payment": 1,
"Changed_Credit_Limit": 15000,
"Num_Credit_Inquiries": 2,
"Credit_Mix": "Good",
"Outstanding_Debt": 10000,
"Credit_Utilization_Ratio": 30,
"Credit_History_Age": 15,
"Payment_of_Min_Amount": "Yes",
"Total_EMI_per_month": 500,
"Amount_invested_monthly": 1000,
"Payment_Behaviour": "Regular",
"Monthly_Balance": 5000
}
]
}'
De FastAPI-server functioneert correct, verwerkt de gebruikersinput succesvol en retourneert een accurate voorspelling.
{"prediction":["Good"]}
6. De container stoppen en verwijderen
Na het experimenteren met de API stoppen we de container met het stop-commando.
$ podman stop mlops_container
We kunnen de container ook verwijderen met het rm-commando, zodat systeemresources vrijkomen. De container moet eerst gestopt zijn voordat je die kunt verwijderen.
$ podman rm mlops_container
7. De image verwijderen
Om de lokaal opgeslagen containerimage met de naam "mlops-app" te verwijderen, gebruiken we het rmi-commando.
$ podman rmi mlops-app
Als je problemen ondervindt bij het draaien van bovenstaande code of bij het maken van je eigen Dockerfile, bekijk dan de GitHub-repository kingabzpro/mlops-with-podman. Deze bevat een gebruikshandleiding en alle benodigde bestanden om de code op je systeem uit te voeren.
De volgende stap in je leertraject is het bouwen van 10 Docker-projectideeën, van beginner tot gevorderd, maar dan met Podman. Zo krijg je een beter begrip van het Podman-ecosysteem.
Conclusie
Podman is in bepaalde use cases een overtuigend alternatief voor Docker, maar veel developers geven nog steeds de voorkeur aan Docker Desktop en CLI. Deze voorkeur komt vooral door Docker's uitgebreide integraties en gebruiksvriendelijke tools.
Voor een eenvoudig MLOps-project kiezen engineers echter mogelijk voor Podman, dat een lichtere en eenvoudigere setup biedt dan Docker Desktop.
In deze tutorial verkennen we Podman, een populaire tool voor containerbeheer, door het te vergelijken met Docker en te laten zien hoe je Podman Desktop installeert. We begeleiden je ook door een MLOps-project met Podman, inclusief het maken van een Dockerfile, het bouwen van een image en het draaien van een container. Aan de slag gaan met Podman is eenvoudig, en als je al bekend bent met Docker, zul je de naadloze overgang waarderen.
Volg de cursus MLOps Deployment and LifeCycling om het moderne MLOps-framework te verkennen, inclusief de lifecycle en deployment van machinelearningmodellen.
Als gecertificeerd data scientist haal ik met passie het maximale uit de nieuwste technologie om innovatieve machinelearning-toepassingen te bouwen. Met een sterke achtergrond in spraakherkenning, data-analyse en -rapportage, MLOps, conversationele AI en NLP heb ik mijn vaardigheden aangescherpt in het ontwikkelen van intelligente systemen die echt impact maken. Naast mijn technische expertise ben ik ook een sterke communicator met een talent om complexe concepten terug te brengen tot heldere, beknopte taal. Daardoor ben ik uitgegroeid tot een veelgelezen blogger over data science, waar ik mijn inzichten en ervaringen deel met een groeiende community van data-professionals. Op dit moment richt ik me op contentcreatie en redactie, waarbij ik met large language models werk aan krachtige en aansprekende content die zowel bedrijven als individuen helpt het beste uit hun data te halen.

