Development with Pod

During development, it is recommended that contributors

  1. load dependencies from the same container image and

  2. mount the source code as a volume.

The reasoning here is

  1. to avoid different behaviour caused by the use of different versions of the dependencies and

  2. to avoid the time costly process of create new container image.

Workflow

  1. Build the container image as described in Building Images.

  2. Run a pod as described in Pod with Volume.

  3. Rebuild the container image as described in Building Images.

  4. Delete and recreate the pod as described in Pod with Volume.

Example

Important

The commands are run from the root of the project.

Given the following Dockerfile

Dockerfile
FROM docker.io/alpine:3.21.3

RUN apk add --no-cache \
    python3 \
    py3-pip \
    py3-virtualenv

ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN python3 -m pip install \
    Flask==3.1.0

WORKDIR /var/app

EXPOSE 5000

ENTRYPOINT [ \
    "flask", \
    "run", \
    "--host", \
    "0.0.0.0", \
    "--port", \
    "5000", \
    "--reload", \
    "--debugger" \
]

running

podman build \
--tag my-tutorial/my-flask-app:dev \
.

creates a container image named my-tutorial/my-flask-app:dev that has Flask installed.

Given the following Kubernetes manifest file

play.yml
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-5.3.1

# NOTE: If you generated this yaml from an unprivileged and rootless podman container on an SELinux
# enabled system, check the podman generate kube man page for steps to follow to ensure that your pod/container
# has the right permissions to access the volumes added.
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: flask
  name: flask
spec:
  containers:
  - image: my-tutorial/my-flask-app:dev
    name: flask
    ports:
    - containerPort: 5000
      hostPort: 5000
    volumeMounts:
    - mountPath: /var/app
      name: flask-src
  volumes:
  - hostPath:
      # This is the path in the host machine running Kubernetes or Podman.
      # When using WSL2, the Windows C disk is mounted into /mnt/c.
      path: ./src
      type: Directory
    name: flask-src

running

podman kube play ./play.yml

creates a pod named flask with a container named flask running the image my-tutorial/my-flask-app:dev with a volume mounted.

http://localhost:5000/ should show

No module named ‘requests’

The Dockerfile can be modified to provide the library requests like in

--- /home/docs/checkouts/readthedocs.org/user_builds/rse-podman-tutorial/checkouts/latest/source/examples/flask0/Dockerfile
+++ /home/docs/checkouts/readthedocs.org/user_builds/rse-podman-tutorial/checkouts/latest/source/examples/flask1/Dockerfile
@@ -9,7 +9,8 @@
 RUN python3 -m venv $VIRTUAL_ENV
 ENV PATH="$VIRTUAL_ENV/bin:$PATH"
 RUN python3 -m pip install \
-    Flask==3.1.0
+    Flask==3.1.0 \
+    requests==2.32.3
 
 WORKDIR /var/app
 

Running

podman build \
--tag my-tutorial/my-flask-app:dev \
.

replaces the container image named my-tutorial/my-flask-app:dev with a new container image that has Flask and the library requests installed.

And running

podman kube play --replace ./play.yml

replaces the pod named flask with a container named flask running the new image my-tutorial/my-flask-app:dev, now with the library requests installed, with a volume mounted.

http://localhost:5000/ should show

Hello, World! Let’s read …

Project Structure

Podman can automatically match the container from the Kubernetes manifest file with the Dockerfile if the following project structure is followed.

my-project/
├── .git
├── play.yaml
└── server
    └── Dockerfile

and play.yaml includes

apiVersion: v1
kind: Pod
metadata:
...
spec:
containers:
- name: container
    image: server
...

Run

podman kube play --build ./play.yml

is equivalent to run

podman build \
--tag server \
./server

followed by

podman kube play ./play.yml