1

EDIT: weird DNS behavior was some kind of transient issue, and now RHEL/podman works the same way as Ubuntu/podman. I can't reproduce the issue, which makes most part (not 100% though) of this question moot.

I am trying to use Podman and docker-compose to create a compose stack with multiple replicas of backend container, and having a hard time with it.

I use Podman because I have to (comes from Red Hat platform), and picked docker-compose because it is familiar and I use in local dev host, too. I know there are alternatives (podman-compose etc). I learned that Podman 4.1 supported Docker Compose so this sounded like a good candidate.

As an example I have docker-compose.yml with one frontend container and 3 backend containers:

version: "3"
services:
  frontend:
    image: "nginx:latest"
    ports:
      - "3000:80"
    depends_on:
      - backend
  backend:
    image: "tomcat:latest"
    ports:
      - "8180-8280:8080"
    scale: 3

Note: this stack is just an example. It's purpose is only to highlight the networking aspects of multi-replica docker-compose . I could use something else than nginx:latest, and using e.g. Traefik can solve some of the problems...but sometimes you wish to connect directly from one container to a service with multiple container replicas.

Docker & docker-compose (Ubuntu)

Running this on host which has docker and docker-compose is straightforward.

Backend containers get assigned random ports from range 8180-8280.

$ docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                                       NAMES
98941117708c   nginx:latest    "/docker-entrypoint.…"   25 seconds ago   Up 22 seconds   0.0.0.0:3000->80/tcp, :::3000->80/tcp       example-docker-compose-frontend-1
3d749e25eaba   tomcat:latest   "catalina.sh run"        26 seconds ago   Up 23 seconds   0.0.0.0:8193->8080/tcp, :::8193->8080/tcp   example-docker-compose-backend-1
854ba8f60cb3   tomcat:latest   "catalina.sh run"        26 seconds ago   Up 23 seconds   0.0.0.0:8192->8080/tcp, :::8192->8080/tcp   example-docker-compose-backend-2
e57e32181e8e   tomcat:latest   "catalina.sh run"        26 seconds ago   Up 23 seconds   0.0.0.0:8194->8080/tcp, :::8194->8080/tcp   example-docker-compose-backend-3

Logging into frontend container, service name backend resolves to all 3 backend containers

dig backend

; <<>> DiG 9.16.27-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31602
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;backend.           IN  A

;; ANSWER SECTION:
backend.        600 IN  A   172.19.0.3
backend.        600 IN  A   172.19.0.2
backend.        600 IN  A   172.19.0.4

curl backend:8080 works

Podman & docker-compose (RHEL 8)

Podman came preinstalled, I added docker-compose (standalone) and podman-docker:

# curl -SL https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
# chmod a+x /usr/local/bin/docker-compose
# sudo yum install podman-docker

And activated rootless podman socket so that podman and docker-compose can talk to each other:

# systemctl --user enable podman.socket
# systemctl --user start podman.socket
# systemctl --user status podman.socket
# export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock

I also switched network backend to netavark, DNS did not work without that change

$ podman info |grep -i networkbackend
  networkBackend: netavark

1. Port ranges are not supported

Podman does not like ports: - "8180-8280:8080" due to this bug: https://github.com/containers/podman/issues/15111

[+] Running 1/0
 ⠿ Network example-docker-compose_default      Created                           0.0s
 ⠋ Container example-docker-compose-backend-3  Creating                          0.0s
 ⠋ Container example-docker-compose-backend-1  Creating                          0.0s
 ⠋ Container example-docker-compose-backend-2  Creating                          0.0s
Error response from daemon: make cli opts(): strconv.Atoi: parsing "8180-8280": invalid syntax

2. Without port range, address already in use conflict

Changed docker-compose.yml to remove port range

ports:
  - "8080:8080"

This results in port conflict, all 3 backend containers try to bind to 8080

Catalina.startup.Catalina.start Server startup in [190] milliseconds
Error response from daemon: rootlessport listen tcp 0.0.0.0:8080: bind: address already in use

3. Scale = 1

Let's try with just one backend container. System starts up

CONTAINER ID  IMAGE                            COMMAND               CREATED        STATUS            PORTS                   NAMES
217c3e726431  docker.io/library/tomcat:latest  catalina.sh run       7 seconds ago  Up 6 seconds ago  0.0.0.0:8080->8080/tcp  example-docker-compose-backend-1
9c3f86676bde  docker.io/library/nginx:latest   nginx -g daemon o...  6 seconds ago  Up 6 seconds ago  0.0.0.0:3000->80/tcp    example-docker-compose-frontend-1

DNS from frontend server looks weird. What are all these different backend IP addresses 10.89.0.3 - 10.89.0.12? Only the last of them works when I curl 10.89.0.x. Still, curl backend:8080 works fine?

4. Scale up from command line

I remove ports and scale from docker-compose.yml and start compose stack with scale=3 option:

version: "3"
services:
  frontend:
    image: "nginx:latest"
    ports:
      - "3000:80"
    depends_on:
      - backend
  backend:
    image: "tomcat:latest"

$ docker-compose up --scale backend=3
[+] Running 4/4
 ⠿ Container example-docker-compose-backend-2   Recreated                        0.3s
 ⠿ Container example-docker-compose-backend-3   Recreated                        0.2s
 ⠿ Container example-docker-compose-backend-1   Recreated                        0.3s
 ⠿ Container example-docker-compose-frontend-1  Recreated                        0.3s
Attaching to example-docker-compose-backend-1, example-docker-compose-backend-2, example-docker-compose-backend-3, example-docker-compose-frontend-1

Now compose stack starts nicely with 3 backend containers

$ docker ps
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID  IMAGE                            COMMAND               CREATED         STATUS             PORTS                 NAMES
b81c44246ae7  docker.io/library/tomcat:latest  catalina.sh run       22 minutes ago  Up 22 minutes ago                        example-docker-compose-backend-3
814dc5d307f7  docker.io/library/tomcat:latest  catalina.sh run       22 minutes ago  Up 22 minutes ago                        example-docker-compose-backend-2
fb0a5090a456  docker.io/library/tomcat:latest  catalina.sh run       22 minutes ago  Up 22 minutes ago                        example-docker-compose-backend-1
c0219d7fded4  docker.io/library/nginx:latest   nginx -g daemon o...  22 minutes ago  Up 22 minutes ago  0.0.0.0:3000->80/tcp  example-docker-compose-frontend-1

DNS from frontend has even more entries

# dig backend

; <<>> DiG 9.16.27-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29526
;; flags: qr rd ad; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 410008265c2b0edb (echoed)
;; QUESTION SECTION:
;backend.           IN  A

;; ANSWER SECTION:
backend.        86400   IN  A   10.89.0.3
backend.        86400   IN  A   10.89.0.4
backend.        86400   IN  A   10.89.0.6
backend.        86400   IN  A   10.89.0.7
backend.        86400   IN  A   10.89.0.15
backend.        86400   IN  A   10.89.0.16
backend.        86400   IN  A   10.89.0.18
backend.        86400   IN  A   10.89.0.19
backend.        86400   IN  A   10.89.0.20
backend.        86400   IN  A   10.89.0.21
backend.        86400   IN  A   10.89.0.22

And curl backend:8080 does not work (not sure which port I should use now)

Questions

  1. What's going on here?
  2. Can I achieve a setup of 3 backend containers, so that DNS name backend would resolve to those, with podman & docker-compose?
  3. Podman seems to support docker-compose (or vice versa), but only to a degree. Is there some documentation which tells what docker-compose features are supported on Podman, and which are not?
Janne Mattila
  • 598
  • 7
  • 20
  • After restarting the server, weirdness related to DNS is mostly gone, and dig returns sane results (without bunch of extra IP addresses) for backend, also curl backend:8080 works. – Janne Mattila Sep 21 '22 at 10:13
  • "mostly gone", since dig backend returns correct IP address, but host backend and nslookup backend can't find the server - which works on Ubuntu/docker – Janne Mattila Sep 21 '22 at 10:15

1 Answers1

1

Podman is my container runtime of choice...unless I'm working with docker-compose, in which case I have found it to be "close but not quite" in terms of its docker API support.

However, for what you're trying to do, you could replace Nginx with Traefik, and let Traefik handle the load balancing. Traefik is a dynamic proxy that uses the Docker API and container labelling to discover containers and configure the proxy rules.

For example:

version: "3"
services:
  frontend:
    image: "docker.io/traefik:v2.8"
    ports:
      - "3000:80"
      - "127.0.0.1:3080:8080"
    command:
      - --api.insecure=true
      - --providers.docker
    volumes:
      - /run/user/$UID/podman/podman.sock:/var/run/docker.sock

  backend:
    labels:
      traefik.http.routers.backend.rule: Host(`localhost`)
    image: "quay.io/larsks/demoserver:latest"
    scale: 3

Here we're mapping all requests for Host: localhost to to our backend containers. This is just for the purposes of a demonstration (since I'll be running curl localhost/...); a more realistic configuration would use specific hostnames, or paths, etc. You can read more in the Routing configuration section of Traefik's Docker documentation, and also in the general Router documentation.

With this configuration, we see the following containers running:

$ podman ps
CONTAINER ID  IMAGE                             COMMAND               CREATED        STATUS            PORTS                                           NAMES
c29de1d137e2  docker.io/library/traefik:v2.8    --api.insecure=tr...  5 seconds ago  Up 6 seconds ago  0.0.0.0:3000->80/tcp, 127.0.0.1:3080->8080/tcp  demoserver_frontend_1
f4fac24cb494  quay.io/larsks/demoserver:latest  /usr/local/bin/st...  5 seconds ago  Up 6 seconds ago                                                  demoserver_backend_2
d9d388202be2  quay.io/larsks/demoserver:latest  /usr/local/bin/st...  5 seconds ago  Up 5 seconds ago                                                  demoserver_backend_1
5a3e6330739d  quay.io/larsks/demoserver:latest  /usr/local/bin/st...  5 seconds ago  Up 5 seconds ago                                                  demoserver_backend_3

And we can see that requests on port 3000 cycle between the available backends. Running this script:

for i in {1..10}; do
  curl http://localhost:3000/hostname
done

Produces as output:

5a3e6330739d
d9d388202be2
f4fac24cb494
5a3e6330739d
d9d388202be2
f4fac24cb494
5a3e6330739d
d9d388202be2
f4fac24cb494
5a3e6330739d
larsks
  • 277,717
  • 41
  • 399
  • 399
  • Thanks! Sounds like a really great fit for this sort of routing use case, I need study those links in detail. – Janne Mattila Sep 20 '22 at 14:52
  • Still, I would be like to understand why podman + docker-compose networking works like this, so I think I will keep this question open. – Janne Mattila Sep 20 '22 at 14:56
  • You've already identified the relevant bug. You *know* why the original config doesn't work in Podman. What else are you looking for? – larsks Sep 20 '22 at 15:44
  • For question 1, I would like to understand why DNS "backend" resolves to 11 different IP addresses on podman+docker-compose (and why I cannot curl any of them). For 2, any workaround would be nice, but I guess answer is "no, not until the bug is fixed", or is it? For 3, I would like to know if there's any source of documentation that describes the features that are supported by podman's docker-compose compatibility, instead of experimenting and using trial-and-error – Janne Mattila Sep 21 '22 at 05:49
  • Clarification: on the last, scaled up from command line, example backend resolves to 11 addresses. On the 3rd example (just one backend container) it for some reason resolves to 3 IP addresses (out of which only the last one works when I curl x.x.x.x with IP address) – Janne Mattila Sep 21 '22 at 05:55
  • Update: I retested and can't reproduce the weird DNS behavior with lots of extra backend IP addresses. It definitely happened, but I don't experience that anymore. dig resolves DNS names on RHEL/podman just like it does on Ubuntu/docker (although host and nslookup behave differently, but I guess there's a reason for that). This makes most of my question content noise. Even though the question is now bad, I guess this is as close to a correct answer as can be. – Janne Mattila Sep 21 '22 at 10:08
  • Tried, and traefik works nicely. I had to add privileged: true to the frontend container to prevent "Provider connection error Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock:" errors though. I am unsure about this, is running privileged container the correct way to go? I am running podman rootless on RHEL8. @larsks – Janne Mattila Sep 21 '22 at 13:13
  • You shouldn't require a privileged container in order to access the podman socket. It might be better to open a new question for that issue, because these comments are already quite long. – larsks Sep 21 '22 at 13:18
  • Certainly. I opened a new question at https://stackoverflow.com/questions/73814619/permission-denied-trying-to-use-rootless-podman-docker-compose-traefik-with – Janne Mattila Sep 22 '22 at 12:28