Podman Equivalent for Docker Compose
Learn how to use Podman with the built-in equivalent for Docker Compose, Podman kube play, and how to deploy your Podman pod to a local Minikube cluster.
Join the DZone community and get the full member experience.
Join For FreeIn this blog, you will learn how to use Podman with the built-in equivalent for Docker Compose. You will learn how to use Podman kube play
and how to deploy your Podman pod to a local Minikube cluster. Enjoy!
Introduction
The first reaction to the short intro will be: “You need to use Podman Compose for that!” However, this blog is not about Podman Compose, but about using the basic concept of Podman by using pods and deploying them to a Kubernetes cluster. Podman Compose is a different concept and deserves its own blog.
So, what will you do and learn in this blog? You will create a pod locally, generate a Kubernetes YAML file for it, and use the YAML file to recreate the pod locally, but also for deploying it to a local Minikube Kubernetes cluster. You will notice that Podman has built-in functionality which resembles the functionality of Docker Compose. In other words, you can accomplish exactly the same thing. So, you might not need something like Podman Compose at all!
Sources used in this blog are available at GitHub and the container image is available at DockerHub. The container image is built in a previous blog, you might want to check it out when you want to know more about Podman compared to Docker. The image contains a basic Spring Boot application with one REST endpoint which returns a hello
message.
Prerequisites
The prerequisites needed for this blog are:
- Basic Linux knowledge
- Basic container knowledge
- Basic Podman knowledge
- Basic Kubernetes knowledge
Create Pod Locally
The first thing to do is to create a Podman pod locally. The pod contains two containers based on the same image.
Create the pod with the following command. The port range 8080 up to and including 8081 is exposed externally. One container will expose the endpoint at port 8080 and the other container at port 8081.
$ podman pod create -p 8080-8081:8080-8081 --name hello-pod
Create both containers. With the environment variable added to container 1, you can configure the Spring Boot application to run on a different port. Otherwise, the default port 8080 is used.
$ podman create --pod hello-pod --name mypodmanplanet-1 --env 'SERVER_PORT=8081' docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT env
$ podman create --pod hello-pod --name mypodmanplanet-2 docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
Start the pod.
$ podman pod start hello-pod
Check the status of the pod. It is in the running
state.
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
bef893686468 hello-pod Running 3 minutes ago 1f0c0ebf2248 3
Verify whether you can access both endpoints. Both endpoints return the same hello
message.
$ curl http://localhost:8080/hello
Hello Podman!
$ curl http://localhost:8081/hello
Hello Podman!
Generate Kubernetes YAML
Based on the local pod you created, you can generate a Kubernetes YAML file which will contain the configuration of your pod. The generate kube
command is used for that, followed by the pod name hello-pod
, followed by the file you want to generate the configuration to.
$ podman generate kube hello-pod -f kubernetes/hello-pod-1-initial.yaml
Take a closer look at the generated Kubernetes YAML file. It contains the pod definition and the two containers that need to run in the pod.
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-3.4.4
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2023-05-13T08:21:40Z"
labels:
app: hello-pod
name: hello-pod
spec:
containers:
- args:
- env
image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
name: mypodmanplanet-1
ports:
- containerPort: 8080
hostPort: 8080
- containerPort: 8081
hostPort: 8081
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
- image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
name: mypodmanplanet-2
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
restartPolicy: Never
status: {}
Recreate the Pod
Now that the configuration is stored in a Kubernetes YAML file, you can verify whether the pod can be recreated based on this file. If this is the case, you can commit this file to a Git repository and you and your colleagues can use it to set up a development environment for example.
First, stop and remove the running pod.
$ podman pod stop hello-pod
$ podman pod rm hello-pod
Verify whether the containers and pod are really removed.
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
$ podman ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Start the pod based on the generated Kubernetes YAML file with the play kube
command.
$ podman play kube kubernetes/hello-pod-1-initial.yaml
Verify the status of the pod. You will notice that the status is degraded
.
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
a7eac7991adc hello-pod Degraded 53 seconds ago 8471932f5741 3
Verify the status of the containers. The status of container hello-pod-mypodmanplanet-2
shows you that something went wrong with this container. It exited for some reason.
$ podman ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8471932f5741 k8s.gcr.io/pause:3.5 About a minute ago Up About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp a7eac7991adc-infra
0f25b7105d2b docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT env About a minute ago Up About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp hello-pod-mypodmanplanet-1
840f307cb67b docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT About a minute ago Exited (1) About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp hello-pod-mypodmanplanet-2
Stop and remove the pod again.
Fix the Kubernetes YAML File
What went wrong here? When you take a closer look at the generated Kubernetes YAML file, you will notice that the generation of the file was a bit messed up for the container mypodmanplanet-1
. The environment variable is not correctly setup and it contains port mappings for port 8080 and for port 8081. The container mypodmanplanet-2
does not contain any port mapping at all.
...
spec:
containers:
- args:
- env
image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
name: mypodmanplanet-1
ports:
- containerPort: 8080
hostPort: 8080
- containerPort: 8081
hostPort: 8081
...
Let’s fix this in file hello-pod-2-with-env.yaml
. Add the environment variable to the container mypodmanplanet-1
and remove the port mapping for port 8080. Add the port mapping for port 8080 to the container mypodmanplanet-2
.
...
spec:
containers:
- env:
- name: SERVER_PORT
value: 8081
image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
name: mypodmanplanet-1
ports:
- containerPort: 8081
hostPort: 8081
resources: {}
...
- image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
name: mypodmanplanet-2
ports:
- containerPort: 8080
hostPort: 8080
resources: {}
...
Start the pod again based on this new configuration.
$ podman play kube kubernetes/hello-pod-2-with-env.yaml
Verify the status of the pod. It is now in the running
state.
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
ea387d67b646 hello-pod Running 41 seconds ago c62a0f7f1975 3
Verify the status of the containers. All are running now.
$ podman ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c62a0f7f1975 k8s.gcr.io/pause:3.5 About a minute ago Up About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp ea387d67b646-infra
97c47b2420cf docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT About a minute ago Up About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp hello-pod-mypodmanplanet-1
16875b941867 docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT About a minute ago Up About a minute ago 0.0.0.0:8080-8081->8080-8081/tcp hello-pod-mypodmanplanet-2
The endpoints are accessible as well.
$ curl http://localhost:8080/hello
Hello Podman!
$ curl http://localhost:8081/hello
Hello Podman!
Minikube
Let’s see whether you can use the generated Kubernetes YAML file in order to run the pod in a Minikube Kubernetes cluster. Minikube allows you to run a Kubernetes cluster locally, mainly used during application development.
Generate Kubernetes YAML
You need to generate the Kubernetes YAML file just like you did before, but this time you need to add the -s
option to the command. This will generate a Kubernetes service, which allows you to access the containers from outside the Kubernetes cluster.
Execute the following command:
$ podman generate kube hello-pod -s -f kubernetes/hello-pod-3-minikube.yaml
Replace the pod part from hello-pod-2-with-env.yaml
in this newly generated YAML file hello-pod-3-minikube.yaml
because the issues with the environment variable and port mapping are again present in this newly generated file.
The generated YAML file contains the following extra service description:
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2023-05-18T07:07:31Z"
labels:
app: hello-pod
name: hello-pod
spec:
ports:
- name: "8081"
nodePort: 32696
port: 8081
targetPort: 8081
- name: "8080"
nodePort: 31435
port: 8080
targetPort: 8080
selector:
app: hello-pod
type: NodePort
---
...
In short, without going into too much detail, this service will map port 8080 to external port 31435 and it will map port 8081 to external port 32696. External means external to the pod.
Before continuing, stop and remove the locally running pod.
Install and Start Minikube
If you have not installed Minikube yet, it is now time to do so. The installation instructions can be found here. The following instructions are executed on a Ubuntu 22.04 OS.
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube
Start Minikube.
$ minikube start
minikube v1.30.1 on Ubuntu 22.04
Using the docker driver based on existing profile
Starting control plane node minikube in cluster minikube
Pulling base image ...
Updating the running docker "minikube" container ...
Preparing Kubernetes v1.26.3 on Docker 23.0.2 ...
Using image gcr.io/k8s-minikube/storage-provisioner:v5
Verifying Kubernetes components...
Enabled addons: storage-provisioner, default-storageclass
kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A'
Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Verify whether the default Minikube Pods are running and whether kubectl
is available. You will need kubectl
to load the Kubernetes YAML file.
$ minikube kubectl -- get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-787d4945fb-qb56z 1/1 Running 1 (86s ago) 2m52s
kube-system etcd-minikube 1/1 Running 2 (85s ago) 3m7s
kube-system kube-apiserver-minikube 1/1 Running 2 (75s ago) 3m7s
kube-system kube-controller-manager-minikube 1/1 Running 2 (85s ago) 3m4s
kube-system kube-proxy-cl5bh 1/1 Running 2 (85s ago) 2m52s
kube-system kube-scheduler-minikube 1/1 Running 1 (91s ago) 3m7s
kube-system storage-provisioner 1/1 Running 0 63s
Create Pod In Minikube
Now that a Minikube cluster is running, you can create the pod based on the Kubernetes YAML file.
$ minikube kubectl -- create -f kubernetes/hello-pod-3-minikube.yaml
service/hello-pod created
Error from server (BadRequest): error when creating "kubernetes/hello-pod-3-minikube.yaml": Pod in version "v1" cannot be handled as a Pod: json: cannot unmarshal number into Go struct field EnvVar.spec.containers.env.value of type string
Unfortunately, this returns an error. The reason is that the environment variable port value must be enclosed with double quotes.
Replace the following snippet:
spec:
containers:
- env:
- name: SERVER_PORT
value: 8081
With the following:
spec:
containers:
- env:
- name: SERVER_PORT
value: "8081"
The new Kubernetes YAML file is hello-pod-4-minikube.yaml
.
Execute the command again, but this time with the new Kubernetes YAML file.
$ minikube kubectl -- create -f kubernetes/hello-pod-4-minikube.yaml
pod/hello-pod created
The Service "hello-pod" is invalid: spec.ports[0].nodePort: Invalid value: 32696: provided port is already allocated
Now an error is returned indicating that the external port 32696 is already allocated.
Verify whether any service is running.
$ minikube kubectl -- get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-pod NodePort 10.99.254.70 <none> 8081:32696/TCP,8080:31435/TCP 4m59s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6m56s
It appears that the Kubernetes service is created although initially, the creation of the pod failed. Also, the pod is created.
$ minikube kubectl -- get pod
NAME READY STATUS RESTARTS AGE
hello-pod 2/2 Running 0 3m8s
Remove the pod and the service.
$ minikube kubectl delete pod hello-pod
pod "hello-pod" deleted
$ minikube kubectl delete svc hello-pod
service "hello-pod" deleted
Final Attempt
Create the pod and the service again based on the hello-pod-4-minikube.yaml
file. This time it is successful.
$ minikube kubectl -- create -f kubernetes/hello-pod-4-minikube.yaml
service/hello-pod created
pod/hello-pod created
Verify the status of the service. The service is created.
$ minikube kubectl -- get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-pod NodePort 10.105.243.28 <none> 8081:32696/TCP,8080:31435/TCP 71s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12m
Check the status of the pod. It is running
.
$ minikube kubectl -- get pods
NAME READY STATUS RESTARTS AGE
hello-pod 2/2 Running 0 118s
But can you access the endpoints?
Retrieve the IP address of the Minikube cluster.
$ minikube kubectl -- describe pods | grep Node:
Node: minikube/192.168.49.2
Verify whether the endpoints can be accessed using the Minikube IP address and the external ports defined in the service. Beware that the external ports can be different when you generated the YAML files yourself.
$ curl http://192.168.49.2:32696/hello
Hello Podman!
$ curl http://192.168.49.2:31435/hello
Hello Podman!
Cleanup
In order to clean up, you first stop the local Kubernetes cluster.
$ minikube stop
Stopping node "minikube" ...
Powering off "minikube" via SSH ...
1 node stopped.
Finally, you delete the cluster.
$ minikube delete
Deleting "minikube" in docker ...
Deleting container "minikube" ...
Removing /home/<user>/.minikube/machines/minikube ...
Removed all traces of the "minikube" cluster.
Conclusion
In this blog, you created a local pod, generated a Kubernetes YAML file for it with Podman, and used this YAML file to recreate the pod locally and to create the pod into a Minikube Kubernetes cluster. It did not work out of the box, but with some minor tweaks, it worked just fine. The Kubernetes YAML file can be stored in a Git repository and shared with your colleagues just like you would do with a Docker Compose file. This way, a development environment can be set up and shared quite easily.
Published at DZone with permission of Gunter Rotsaert, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments