Podman Equivalent for Docker Compose
source link: https://mydeveloperplanet.com/2023/06/07/podman-equivalent-for-docker-compose/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
In 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!
1. Introduction
First reactions 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 deploy 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 ressembles 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 build 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.
2. Prerequisites
Prerequisites needed for this blog are:
- Basic Linux knowledge;
- Basic container knowledge;
- Basic Podman knowledge;
- Basic Kubernetes knowledge.
3. Create Pod Locally
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! |
4. 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: { } |
5. 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.
6. 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 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 container mypodmanplanet-1
and remove the port mapping for port 8080. Add the port mapping for port 8080 to 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! |
7. 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.
7.1 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 details, 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.
7.2 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 <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f604.svg" > minikube v1.30.1 on Ubuntu 22.04 <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/2728.svg" > Using the docker driver based on existing profile <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f44d.svg" > Starting control plane node minikube in cluster minikube <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f69c.svg" > Pulling base image ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f3c3.svg" > Updating the running docker "minikube" container ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f433.svg" > Preparing Kubernetes v1.26.3 on Docker 23.0.2 ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/25aa.svg" > Using image gcr.io /k8s-minikube/storage-provisioner :v5 <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f50e.svg" > Verifying Kubernetes components... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f31f.svg" > Enabled addons: storage-provisioner, default-storageclass <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f4a1.svg" > kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A' <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f3c4.svg" > 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 |
7.3 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 |
7.4 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! |
7.5 Cleanup
In order to cleanup, you first stop the local Kubernetes cluster.
$ minikube stop <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/270b.svg" > Stopping node "minikube" ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f6d1.svg" > Powering off "minikube" via SSH ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f6d1.svg" > 1 node stopped. |
Finally, you delete the cluster.
$ minikube delete <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f525.svg" > Deleting "minikube" in docker ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f525.svg" > Deleting container "minikube" ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f525.svg" > Removing /home/ <user>/.minikube /machines/minikube ... <img draggable= "false" role= "img" class= "emoji" alt= "" src= "https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f480.svg" > Removed all traces of the "minikube" cluster. |
8. 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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK