Podman ships with built-in support for Kubernetes configuration files but not for Docker Compose. As described in Podman Compose, the Podman Compose utility can use Docker Compose files to create Podman containers. However, you might want to migrate to the Kubernetes format, eschewing Podman Compose and Docker Compose entirely. This is what I ended up doing, and I describe the process here.

Tutorial

This tutorial provides the steps necessary to convert a simple Docker Compose file to an equivalent Kubernetes configuration using Podman Compose and Podman. It continues where Podman Compose left off, having created a Podman container from the Docker Compose for the UniFi Controller from the UniFi Controller post. So, complete this tutorial before following the steps below. This tutorial targets Ubuntu 18.04, and you should be familiar with Linux Containers, Docker Compose, Podman, the command-line, and the Kubernetes configuration format.

  1. Change into the directory containing the UniFi Controller’s Docker Compose file.

    cd ~/Projects/unifi-controller
  2. Check for the previously created UniFi Controller pod with podman-pod-ps(1).

    podman pod ps
    POD ID        NAME              STATUS   CREATED      INFRA ID      # OF CONTAINERS
    241f0bf222a3  unifi-controller  Running  2 hours ago  d5eaaf6d5625  2

    Okay, it’s present and accounted for!

  3. To generate the Kubernetes configuration from a Podman container, use podman-kube-generate(1).

    Here I output the configuration to the file unifi-controller.yml using the -f flag. The -s flag produces the necessary network service configuration.

    podman kube generate -s -f unifi-controller.yml unifi-controller
  4. Examine the generated YAML file, reproduced below.

    ~/Projects/unifi-controller/unifi-controller.yml
    # Generation of Kubernetes YAML is still under development!
    #
    # Save the output of this file and use kubectl create -f to import
    # it into Kubernetes.
    #
    # Created with podman-3.0.1
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: "2021-03-14T15:41:03Z"
      labels:
        app: unifi-controller
      name: unifi-controller
    spec:
      containers:
      - command:
        - /init
        env:
        - name: PATH
          value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
        - name: TERM
          value: xterm
        - name: container
          value: podman
        - name: HOME
          value: /root
        - name: LANGUAGE
          value: en_US.UTF-8
        - name: LANG
          value: en_US.UTF-8
        - name: MEM_LIMIT
          value: 1024M
        image: ghcr.io/linuxserver/unifi-controller
        name: unifi-controllerunifi-controller1
        ports:
        - containerPort: 6789
          hostPort: 6789
          protocol: TCP
        - containerPort: 3478
          hostPort: 3478
          protocol: UDP
        - containerPort: 5514
          hostPort: 5514
          protocol: UDP
        - containerPort: 8880
          hostPort: 8880
          protocol: TCP
        - containerPort: 8080
          hostPort: 8080
          protocol: TCP
        - containerPort: 8443
          hostPort: 8443
          protocol: TCP
        - containerPort: 10001
          hostPort: 10001
          protocol: UDP
        - containerPort: 8843
          hostPort: 8843
          protocol: TCP
        - containerPort: 1900
          hostPort: 1900
          protocol: UDP
        resources: {}
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            drop:
            - CAP_MKNOD
            - CAP_NET_RAW
            - CAP_AUDIT_WRITE
          privileged: false
          readOnlyRootFilesystem: false
          seLinuxOptions: {}
        workingDir: /usr/lib/unifi
      dnsConfig: {}
      restartPolicy: Never
    status: {}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      creationTimestamp: "2021-03-14T15:41:03Z"
      labels:
        app: unifi-controller
      name: unifi-controller
    spec:
      ports:
      - name: "6789"
        nodePort: 32062
        port: 6789
        protocol: TCP
        targetPort: 0
      - name: "3478"
        nodePort: 32030
        port: 3478
        protocol: UDP
        targetPort: 0
      - name: "5514"
        nodePort: 30747
        port: 5514
        protocol: UDP
        targetPort: 0
      - name: "8880"
        nodePort: 30295
        port: 8880
        protocol: TCP
        targetPort: 0
      - name: "8080"
        nodePort: 32396
        port: 8080
        protocol: TCP
        targetPort: 0
      - name: "8443"
        nodePort: 32319
        port: 8443
        protocol: TCP
        targetPort: 0
      - name: "10001"
        nodePort: 30786
        port: 10001
        protocol: UDP
        targetPort: 0
      - name: "8843"
        nodePort: 31695
        port: 8843
        protocol: TCP
        targetPort: 0
      - name: "1900"
        nodePort: 31076
        port: 1900
        protocol: UDP
        targetPort: 0
      selector:
        app: unifi-controller
      type: NodePort
    status:
      loadBalancer: {}

    This generated file warrants some additional attention. Most importantly, the generated Kubernetes configuration is conspicuously lacking any volumes.

  5. Add a section for an associated named volume that will hold the persistent data.

    In the Docker Compose file, a volume was created like so.

    version: "2.1"
    services:
      unifi-controller:
      ...
        volumes:
          - data:/config (1)
      ...
    volumes:
      data: (2)
    1 Associate the unifi-controller with the volume dubbed data which is mounted at /config inside the container.
    2 Declare the named volume data which will be created automatically if it doesn’t exist.

    The way to accomplish the same behavior in the Kubernetes YAML is to use a Persistent Volume Claim. Podman has recently added support for using Persistent Volume Claims to associate Podman containers with named Podman volumes. See Podman pull request #8497 for details. This wasn’t in the generated YAML because the functionality to generate the corresponding YAML is still outstanding per Podman issue #5788.

    For the time being, we’ll just have to add this manually.

    spec:
      containers:
      - command:
        - /init
        ...
        volumeMounts: (1)
          - mountPath: /config
            name: unifi-data
      volumes:
        - name: unifi-data (2)
          persistentVolumeClaim:
            claimName: unifi-controller-data
    1 Mount the volume dubbed unifi-data at /config inside the container.
    2 Declare the Persistent Volume Claim, unifi-data, using the claim name unifi-controller-data. Podman associates the claim name with the name of the Podman named volume to use for this particular pod.

    In an attempt to preserve what little sanity remains in my possession in this moment, I named the volume using - as the separator. This is inconsistent with the volume created by Podman Compose which is named unifi-controller_data. Notice that underscore instead of a hyphen at the end? You might already be using the volume unifi-controller_data. If you want to keep using it with the container created from the Kubernetes YAML, change the claim name accordingly.

  6. Optionally, you can remove some of the environment variable cruft in the env section. I reduced this to just the values below.

    env:
      - name: container
        value: podman
      - name: MEM_LIMIT
        value: 1024M
  7. If you want to allow automatic updates of the image, add the appropriate label.

    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: "2021-03-13T17:21:54Z"
      labels:
        app: unifi-controller
        io.containers.autoupdate: image (1)
      name: unifi-controller
    1 Add the label io.containers.autoupdate and set it to image to enable automatic updates for the containers herein.

    This is a bit of a tease for an upcoming blog post which will describe this in more detail. You’ll need to make sure that Podman’s auto-update systemd timer is enabled. Details forthcoming.

  8. Before starting this pod up, use podman-compose to destroy the existing unifi-controller pod.

    podman-compose down
  9. Provide the generated Kubernetes YAML to podman-kube-play(1) to create and launch the pod.

    podman kube play ~/Projects/unifi-controller/unifi-controller.yml
  10. Access the controller’s web console at https://127.0.0.1:8443/.

    fish
    open http://127.0.0.1:8443
    Other shells
    xdg-open http://127.0.0.1:8443

See Also

I have a GitHub repository for this Kubernetes configuration file which you might find helpful. RedHat has several blog posts related to Podman and Kubernetes YAML including Podman can now ease the transition to Kubernetes and CRI-O, From Docker Compose to Kubernetes with Podman, and The podman play kube command now supports deployments.

Conclusion

You should now have a better idea of how the Docker Compose format translates to the Kubernetes format plus how to get the conversion started with Podman and Podman Compose. This also sets the stage for transitioning to using Kubernetes for managing container deployments. Hopefully you’ve found this post helpful. Posts on automatic image updates and setting up a Podman container as a systemd service to follow.