It feels like forever since I wrote my RHEL 8 beta intro to podman. In fact, it’s been quite a while, and a lot has happened since then. For some time now I’ve been planning on moving my Digital Ocean droplet, that hosts this and a few other sites, from the Centos 7 Docker platform that it’s on now, into a Centos 8 Podman platform. I would really love to do something more sophisticated, but being honest, a single host running containers is really all I need. So, I set out to move from the docker-compose world to podman.
No Great docker-compose alternative
I used docker-compose for one simple reason. It allowed me to define my applications in a portable and easy to write yaml file, which gave me the ability to group each site with its dependencies. For instance, this site requires a web server running php, and a database to store its data in. Those are two containers, and managing them separately seemed silly. So I grouped them in a docker-compose file. This allows me to work with the whole stack when I want to do things like stop services, or pull in updates for the container images I chose to use.
Pods
Well, moving to Centos 8 meant losing docker, and replacing it with podman. Podman.. Does not have a docker-compose counterpart. Well, it does, sort of. There’s a project in the works called podman-compose, which is supposed to do the same basic thing as docker-compose. I wanted to find the “right” solution though. Honestly, that was not an easy task. You’d think that doing a google search for the podman counterpart to docker-compose would get you an article about how podman replaces that functionality with something else. I couldn’t find anything though. What I did find was a reference to Pods in podman. Pods are a way of grouping containers together inside of their own namespace, network, and security context. You can even start and stop the whole pod at once. Sounds great! The only thing it doesn’t get me is a clean yaml file to define my services in. Also worth noting, I found this great expample from Red Hat on pods, it even touches on networking. Podman: Managing pods and containers in a local container runtime
Podman play
Podman does, however, let you import kubernetes definitions using the podman play command. Kubernetes definitions are yaml! Sounds like the solution for me! I spent some time trying to learn how these things are written, and eventually came across the “kompose” tool. Kompose is meant to convert your docker-compose files into Kubernetes definitions! PERFECT! except it wasn’t. What I needed specifically was a pod definition, and that’s not what Kompose gave me. I might have been able to make a pod definition out of what I had, but I had another hunch.
Podman generate
Podman will let you generate kube definitions from existing runtime. Like, if you had a container running, you could podman generate a yaml file to define that container. You can also do that with a pod! So I maually defined one of my wordpress sites in podman. Here are a few notes on that process.
Mapping ports
In the docker world, ports are mapped to containers, and that’s true in podman as well. Except when you’re running insdie a pod. See, the pod is like a container of containers. Networking within the pod is more similar to networking within a host OS. Pods reach eachother over localhost, and external networking reaches the pod, not the containers themselves directly. So when you intend to run containers in a pod, you need to map ports on the pod like you would have on the container in docker or docker-compose. I also found that, regardless of the fact that one of the benefits to podman is the ability to run as a standard user… I had to do all of this as root because of some security problems I ran into when I created the pods. Mainly selinux. So I ended up doing all of his as root, like you would have with Docker. I will likely circle back and try to re-do all of this without superuser privs.
So let’s create a pod
[gangrif@batou-lan ~]$ sudo podman pod create --name my-pod -p 8080:80
850425b9c02dc438a04c278196ef645fc9b8a27070a80d2c1d53aca0f1730502
[gangrif@batou-lan ~]$ sudo podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
850425b9c02d my-pod Created 7 seconds ago 1 b525a0511d3e
This creates a pod, that has port 8080 mapped to inside port 80. So if you were to then spin up a container listening on port 80, you’d have connectivity.
Creating a container, in the pod
Now to create a container in the pod, you’d use podman run, like you’d expect, but you don’t map a port. This makes more sense when you have more than one container to work with, so I’m going to create a database, and then a wordpress container.
[gangrif@batou-lan ~]$ sudo podman run \
-d --restart=always --pod=my-pod \
-e MYSQL_ROOT_PASSWORD="myrootpass" \
-e MYSQL_DATABASE="wp" \
-e MYSQL_USER="wordpress" \
-e MYSQL_PASSWORD="w0rdpr3ss" \
--name=wptest-db mariadb
Trying to pull registry.fedoraproject.org/mariadb...
manifest unknown: manifest unknown
Trying to pull registry.access.redhat.com/mariadb...
name unknown: Repo not found
Trying to pull registry.centos.org/mariadb...
manifest unknown: manifest unknown
Trying to pull docker.io/library/mariadb...
Getting image source signatures
Copying blob 42ed51adaf49 done
Copying blob 127c9761dcba done
Copying blob 7e2d48f22ade done
Copying blob a4a2a29f9ba4 done
Copying blob 4039240d2e0b done
Copying blob d13bf203e905 done
Copying blob 6518a50ecb7c done
Copying blob b5bc5a5c2503 done
Copying blob 67412c7f89bc done
Copying blob 58175d975ba9 done
Copying blob 0c6efbafd3cb done
Copying blob 1e18725209e8 done
Copying blob 05202eb0846d done
Copying config 22851c7fe9 done
Writing manifest to image destination
Storing signatures
fed9756de1017a0ab38ff4b687a854af5422ec2c9fd13e175a78283426ccfc04
[gangrif@batou-lan ~]$ sudo podman run \
-d --restart=always --pod=my-pod \
-e WORDPRESS_DB_NAME="wp" \
-e WORDPRESS_DB_USER="wordpress" \
-e WORDPRESS_DB_PASSWORD="w0rdpr3ss" \
-e WORDPRESS_DB_HOST="127.0.0.1" \
--name wptest-web wordpress
Trying to pull registry.fedoraproject.org/wordpress...
manifest unknown: manifest unknown
Trying to pull registry.access.redhat.com/wordpress...
name unknown: Repo not found
Trying to pull registry.centos.org/wordpress...
manifest unknown: manifest unknown
Trying to pull docker.io/library/wordpress...
Getting image source signatures
Copying blob f54006e0dc29 done
Copying blob e0d3d1244592 done
Copying blob eb2d00c10344 done
Copying blob 8559a31e96f4 done
Copying blob e0276193a084 done
Copying blob 3a60f364b0c5 done
Copying blob 8faf60068506 done
Copying blob c59965a5777f done
Copying blob 42c09ef39fe7 done
Copying blob db37570cfdf4 done
Copying blob 3e309988c00b done
Copying blob 2c289722ebb3 done
Copying blob e80a84c5a269 done
Copying blob 491a234e2c26 done
Copying blob b83f4c8507f7 done
Copying blob 944a23d0ea39 done
Copying blob a650de05eb1e done
Copying blob a7780c30584c done
Copying blob 267943a2fe25 done
Copying blob ed59c3cd6acc done
Copying config a5fef19f5a done
Writing manifest to image destination
Storing signatures
a49711540329f4307ff218c277bb5a2848b37bf6ded922dfd2a839c564ddbdf1
Now, notice that I pointed the wordpress_db_host in the env to localhost. That’s because the WP container is going to find the DB container on localhost. Like magic. Now we can see our pod has 3 containers. Yes, I ran two, but one of them is the container that does the pod magic.
[gangrif@batou-lan ~]$ sudo podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
850425b9c02d my-pod Running 12 minutes ago 3 b525a0511d3e
And, as expected, podman ps gives us two containers.
[gangrif@batou-lan ~]$ sudo podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a49711540329 docker.io/library/wordpress:latest apache2-foregroun... 2 minutes ago Up 2 minutes ago 0.0.0.0:8080->80/tcp wptest-web
fed9756de101 docker.io/library/mariadb:latest mysqld 5 minutes ago Up 5 minutes ago 0.0.0.0:8080->80/tcp wptest-db
And, in my browser, I can get to the WordPress setup page in my container, via localhost:8080
So we’re done, right? Nope, Now we want to make a yaml file that defines all of this.
Generating the yaml for our pod
Now we can use podman generate to output our pod as a yaml definition using podman generate kube.
[gangrif@batou-lan ~]$ sudo podman generate kube my-pod >> my-pod.yaml
[gangrif@batou-lan ~]$ cat my-pod.yaml
# 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-1.9.3
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-07-01T20:17:42Z"
labels:
app: my-pod
name: my-pod
spec:
containers:
- command:
- apache2-foreground
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
value: my-pod
- name: PHP_MD5
- name: PHP_VERSION
value: 7.4.7
- name: PHPIZE_DEPS
value: "autoconf \t\tdpkg-dev \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake
\t\tpkg-config \t\tre2c"
- name: APACHE_CONFDIR
value: /etc/apache2
- name: PHP_ASC_URL
value: https://www.php.net/distributions/php-7.4.7.tar.xz.asc
- name: PHP_EXTRA_BUILD_DEPS
value: apache2-dev
- name: PHP_CFLAGS
value: -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
- name: WORDPRESS_VERSION
value: 5.4.2
- name: WORDPRESS_DB_NAME
value: wp
- name: WORDPRESS_DB_HOST
value: 127.0.0.1
- name: PHP_LDFLAGS
value: -Wl,-O1 -pie
- name: APACHE_ENVVARS
value: /etc/apache2/envvars
- name: WORDPRESS_DB_USER
value: wordpress
- name: PHP_CPPFLAGS
value: -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
- name: PHP_URL
value: https://www.php.net/distributions/php-7.4.7.tar.xz
- name: PHP_INI_DIR
value: /usr/local/etc/php
- name: WORDPRESS_DB_PASSWORD
value: w0rdpr3ss
- name: PHP_EXTRA_CONFIGURE_ARGS
value: --with-apxs2 --disable-cgi
- name: PHP_SHA256
value: 53558f8f24cd8ab6fa0ea252ca8198e2650160649681ce5230c1df1dc2b52faf
- name: WORDPRESS_SHA1
value: e5631f812232fbd45d3431783d3db2e0d5670d2d
- name: GPG_KEYS
value: 42670A7FE4D0441C8E4632349E4FDC074A4EF02D 5A52880781F755608BF815FC910DEB46F53EA312
- name: container
value: podman
image: docker.io/library/wordpress:latest
name: wptest-web
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
workingDir: /var/www/html
- command:
- mysqld
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
value: my-pod
- name: MARIADB_MAJOR
value: "10.5"
- name: MARIADB_VERSION
value: 1:10.5.4+maria~focal
- name: MYSQL_ROOT_PASSWORD
value: myrootpass
- name: MYSQL_USER
value: wordpress
- name: GOSU_VERSION
value: "1.12"
- name: GPG_KEYS
value: 177F4010FE56CA3336300305F1656F24C74CD1D8
- name: MYSQL_PASSWORD
value: w0rdpr3ss
- name: MYSQL_DATABASE
value: wp
- name: container
value: podman
image: docker.io/library/mariadb:latest
name: wptest-db
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
workingDir: /
status: {}
I found that this output needed some cleanup. I ended up deleting a bunch of the env that I thought the container images would easily re-propagate, and in fact could cause conflicts if I were to build this again from scratch. Here’s what I ended up with after some clean-up.
# 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-1.9.3
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-07-01T20:17:42Z"
labels:
app: my-pod
name: my-pod
spec:
containers:
- name: wptest-web
env:
- name: WORDPRESS_DB_NAME
value: wp
- name: WORDPRESS_DB_HOST
value: 127.0.0.1
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: w0rdpr3ss
image: docker.io/library/wordpress:latest
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
workingDir: /var/www/html
- name: wptest-db
env:
- name: MYSQL_ROOT_PASSWORD
value: myrootpass
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: w0rdpr3ss
- name: MYSQL_DATABASE
value: wp
image: docker.io/library/mariadb:latest
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
workingDir: /
status: {}
You could directly copy and paste that into your own file, and then use podman to bring up the exact same pod.
Bringing up a pod from the yaml
Now, you can use podman play kube to bring your pod up from the defined yaml!
[gangrif@batou-lan ~]$ sudo podman pod ls
[gangrif@batou-lan ~]$ sudo podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[gangrif@batou-lan ~]$ sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[gangrif@batou-lan ~]$ sudo podman play kube ./my-pod.yaml
Trying to pull docker.io/library/wordpress:latest...
Getting image source signatures
Copying blob 3a60f364b0c5 skipped: already exists
Copying blob e0276193a084 skipped: already exists
Copying blob 8559a31e96f4 skipped: already exists
Copying blob f54006e0dc29 skipped: already exists
Copying blob eb2d00c10344 skipped: already exists
Copying blob e0d3d1244592 skipped: already exists
Copying blob 8faf60068506 skipped: already exists
Copying blob 3e309988c00b skipped: already exists
Copying blob 42c09ef39fe7 skipped: already exists
Copying blob c59965a5777f skipped: already exists
Copying blob 2c289722ebb3 skipped: already exists
Copying blob db37570cfdf4 skipped: already exists
Copying blob 491a234e2c26 skipped: already exists
Copying blob 944a23d0ea39 skipped: already exists
Copying blob b83f4c8507f7 skipped: already exists
Copying blob e80a84c5a269 skipped: already exists
Copying blob 267943a2fe25 [--------------------------------------] 0.0b / 0.0b
Copying blob a7780c30584c [--------------------------------------] 0.0b / 0.0b
Copying blob ed59c3cd6acc [--------------------------------------] 0.0b / 0.0b
Copying blob a650de05eb1e [--------------------------------------] 0.0b / 0.0b
Copying config a5fef19f5a done
Writing manifest to image destination
Storing signatures
Trying to pull docker.io/library/mariadb:latest...
Getting image source signatures
Copying blob 42ed51adaf49 skipped: already exists
Copying blob 127c9761dcba skipped: already exists
Copying blob 4039240d2e0b skipped: already exists
Copying blob 1e18725209e8 skipped: already exists
Copying blob d13bf203e905 skipped: already exists
Copying blob 6518a50ecb7c skipped: already exists
Copying blob 67412c7f89bc skipped: already exists
Copying blob 7e2d48f22ade skipped: already exists
Copying blob a4a2a29f9ba4 skipped: already exists
Copying blob 58175d975ba9 skipped: already exists
Copying blob 0c6efbafd3cb skipped: already exists
Copying blob b5bc5a5c2503 [--------------------------------------] 0.0b / 0.0b
Copying config 22851c7fe9 done
Writing manifest to image destination
Storing signatures
Pod:
a91dc8859e85505b43f82f1be248880f76138f0ae86f12c3ffdea4d309d4eacf
Containers:
87a67588fbacf959c95f8f63f9f26ffa7b4fb3c7f8827aadd65bcd94de526e37
cc3f3e7255c8110826565673b5d30ebc223cbfcefa01597714231e1305382d3c
[gangrif@batou-lan ~]$ sudo podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
a91dc8859e85 my-pod Running 10 seconds ago 3 aa53c9308991
[gangrif@batou-lan ~]$ sudo podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc3f3e7255c8 docker.io/library/mariadb:latest docker-entrypoint... 12 seconds ago Up 11 seconds ago 0.0.0.0:8080->80/tcp wptest-db
87a67588fbac docker.io/library/wordpress:latest docker-entrypoint... 13 seconds ago Up 12 seconds ago 0.0.0.0:8080->80/tcp wptest-web
And if you hit up port 8080, you should get the WP setup page, just as before.
Conclusion
So, I hope this was helpful! It took me some time to figure out, so I thought it was worth sharing! Happy podmanning!
[…] https://www.undrground.org/2020/07/01/moving-from-docker-compose-to-podman-pods/ […]