Skip to content

Instantly share code, notes, and snippets.

@corbtastik
Last active May 11, 2022 19:11
Show Gist options
  • Save corbtastik/b9b7c431d4ce7001c042a5528933aed6 to your computer and use it in GitHub Desktop.
Save corbtastik/b9b7c431d4ce7001c042a5528933aed6 to your computer and use it in GitHub Desktop.
A multi-container experiment with podman

Todo(s) Multi Container with Podman

An experiment running multiple Rootless containers in podman.

4 container application

Source Code

Container Images

1. Grab container images

podman login quay.io
podman login registry.redhat.io
podman pull registry.redhat.io/rhel8/mysql-80
podman pull quay.io/corbsmartin/todos-mysql
podman pull quay.io/corbsmartin/todos-webui
podman pull quay.io/corbsmartin/todos-edge
podman images

Option 1: Port-forwarding

  • Rootless containers lack privileges to create virtual network interfaces (vNICs) in the linux kernel, thus...
    • They're isolated because there's no SDN.
    • They don't have an IP Address and can't directly communicate with other containers.
  • Networking between Rootless containers is accomplished by port-forwarding with the host.

1. Start the Database container

  • Run MySQL container, map hostPort 3306 to containerPort 3306
podman run --name todos-db -d \
  -p 3306:3306 \
  -e MYSQL_USER=user1 \
  -e MYSQL_PASSWORD=mysql123 \
  -e MYSQL_DATABASE=todos \
  -e MYSQL_ROOT_PASSWORD=mysql123 \
  registry.redhat.io/rhel8/mysql-80

2. Start the Service API container

  • Get your HOST_IP
  • Run Todos-MySQL container
    • Map hostPort 8081 to containerPort 8081
    • Set environment variables for MySQL, NOTE use $HOST_IP not localhost.
# First get the IP of your host (not localhost)
# macOS
HOST_IP=$(ipconfig getifaddr `route get default | grep interface | awk '{print $2}'`); echo $HOST_IP
# RHEL/Fedora/Centos
HOST_IP=$(hostname -I | awk '{print $1}'); echo $HOST_IP
# Run todos-mysql container, setting env vars accordingly, especially MYSQL_HOST=${HOST_IP}
podman run --name todos-mysql -d -p 8081:8081 \
  -e "SERVER_PORT=8081" \
  -e "SPRING_PROFILES_ACTIVE=mysql" \
  -e "MYSQL_USER=user1" \
  -e "MYSQL_PASSWORD=mysql123" \
  -e "MYSQL_HOST=${HOST_IP}" \
  -e "MYSQL_DATABASE=todos" \
  quay.io/corbsmartin/todos-mysql

3. Start the UI container

  • Run Todos-WebUI, map hostPort 8080 to containerPort 8080
podman run --name todos-webui -d -p 8080:8080 \
  -e "SERVER_PORT=8080" \
  -e "SPRING_SECURITY_USER_NAME=Podman" \
  -e "TODOS_WEBUI_PLACEHOLDER=Learn podman" \
  quay.io/corbsmartin/todos-webui

4. Start the Routing container

  • Get your HOST_IP
  • Run Todos-Edge container
    • Publish hostPort 8000 and map to containerPort 8000
    • Set environment variables for the UI (Todos-WebUI) and API (Todos-MySQL) endpoints
# First get the IP of your host (not localhost)
# macOS
HOST_IP=$(ipconfig getifaddr `route get default | grep interface | awk '{print $2}'`); echo $HOST_IP
# RHEL/Fedora/Centos
HOST_IP=$(hostname -I | awk '{print $1}'); echo $HOST_IP
# Run and pass in endpoints for the UI and API, map hostPort 8000 to containerPort 8000
podman run --name todos-edge -d -p 8000:8000 \
  -e "SERVER_PORT=8000" \
  -e "API_ENDPOINT=http://${HOST_IP}:8081" \
  -e "UI_ENDPOINT=http://${HOST_IP}:8080" \
  quay.io/corbsmartin/todos-edge

5. Grok

# Get all
curl http://localhost:8081/todos/
# Create todo
curl -X POST http://localhost:8081/todos/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"id":"1003", "title":"Learn podman"}'
# Get todo
curl http://localhost:8081/todos/1003
# Update todo complete
curl -X PATCH http://localhost:8081/todos/1003 \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"id":"1003", "complete":true}'
# Get complete todos
curl http://localhost:8081/todos/complete
# Delete todo
curl -X DELETE http://localhost:8081/todos/1003
# Get all
curl http://localhost:8081/todos/
  • Execute SQL on the database container via podman
# Select all
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "SELECT * FROM todos.todos;"'
# Insert todo
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "INSERT INTO todos.todos(id, title, complete) VALUE (1005, \"Learn podman exec\", FALSE);"'
# Select todo
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "SELECT * FROM todos.todos WHERE id=1005;"'
# Update todo
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "UPDATE todos.todos SET complete=TRUE WHERE id=1005;"'
# Select complete todos
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "SELECT * FROM todos.todos WHERE complete=TRUE;"'
# Delete todo
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "DELETE FROM todos.todos WHERE id=1005;"'
# Select all
podman exec -it todos-db /bin/bash \
  -c 'mysql -uuser1 -pmysql123 -e "SELECT * FROM todos.todos;"'

Option 2: Podman pods

As mentioned above, Rootless containers don't run with enough privileges to create virtual network interfaces. This is often sighted as a "limitation" of Rootless containers but another view is it's a secure-by-default posture.

That's all well and good but we're still left with the problem of connecting containers, Option 1 - highlighted port-forwarding with the host interface. This works in small batches but a downside is each container becomes accessible from the host. Typically, not all containers need to be accessible from the outside, they require simple east-to-west container-to-container connectivity, where a smaller number (1 or 2) require north-to-south connectivity. That is to say, expose themselves externally to clients, in this case the host.

With the sample Todo apps, the only one that requires external exposure is the one users interact with. The rest can get by with east-to-west connectivity, and thus be fully encapsulated from a networking perspective in a "pod". Podman supports such a setup and thus gives Container developers tools to architect "well formed" deployment patterns. Incidentally, this is where the "pod" in Podman surfaces, which is similar to the "pod" abstraction in K8s. We simply toss all containers in a pod where they can communicate on localhost, and allow external traffic by poking a hole in it. Let's grok this idea...

1. Create a pod and map hostPort 8000 to podPort 8000

# Create a pod and publish containerPort 8000 to hostPort 8000
podman pod create --name todos-pod -p 8000:8000

2. Run all the apps in the pod

# Create a mysql container, no need to publish ports,
# 3306 is accessible by other containers in the pod.
podman run --name todos-db --pod todos-pod -d \
  -e "MYSQL_USER=user1" \
  -e "MYSQL_PASSWORD=mysql123" \
  -e "MYSQL_DATABASE=todos" \
  -e "MYSQL_ROOT_PASSWORD=mysql123" \
  registry.redhat.io/rhel8/mysql-80

# Create the todos-mysql Service API container, again no need to publish ports,
# this container can reach mysql on localhost:3306 by virtue of being in the same pod.
podman run --name todos-mysql --pod todos-pod -d \
  -e "SERVER_PORT=8081" \
  -e "SPRING_PROFILES_ACTIVE=mysql" \
  -e "MYSQL_USER=user1" \
  -e "MYSQL_PASSWORD=mysql123" \
  -e "MYSQL_DATABASE=todos" \
  quay.io/corbsmartin/todos-mysql

# Create the todos-webui container, you got it...no port publishing, this container
# can be accessed by others in the pod.
podman run --name todos-webui --pod todos-pod -d \
  -e "SERVER_PORT=8080" \
  -e "SPRING_SECURITY_USER_NAME=Podman" \
  -e "TODOS_WEBUI_PLACEHOLDER=Learn podman pods" \
  quay.io/corbsmartin/todos-webui

# Create the todos-edge container, this is the point of entry for external clients,
# as this container binds to containerPort 8000 and port 8000 is published on the pod.
# The default config for todos-edge is to proxy the UI on localhost:8080,
# and the API on localhost:8081, thus we don't need to explicitly set as before.
podman run --name todos-edge --pod todos-pod -d \
  -e "SERVER_PORT=8000" \
  quay.io/corbsmartin/todos-edge

3. Grok

# follow container logs in the pod
podman pod logs -f -c todos-db todos-pod
podman pod logs -f -c todos-mysql todos-pod
podman pod logs -f -c todos-webui todos-pod
podman pod logs -f -c todos-edge todos-pod

podman pod ps --ctr-names --ctr-status
podman pod stats todos-pod
podman pod top todos-pod

References

  1. Getting started with Podman Networking
  2. Basic Networking tutorial
  3. Configuring container networking with Podman
  4. How Podman runs on macOS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment