Migrating from docker to containerd

Good day.

I am currently running docker with docker-compose and using traefik v3 as my routing service. I now want to migrate away from docker and make use of nerdctl, containerd and still use traefik v3 as my routing service. I am struggling to browse to my domain name when using this set up and it appears as though traefik is not routing requests to my container. Please advise if traefik has support for containerd or if I have to either make use of docker or Kubernetes?

AFAIK there is no provider for containerd, simple container Configuration Discovery only works with Docker containers and the Docker socket (Doc).

You can test if provider.docker is compatible with /run/containerd/containerd.sock.

Otherwise you would need to manually create Traefik routers and services for conteinerd containers.

Alternatively you could implement the conteinerd provider and do a pull request to Traefik.

Thank you for your advice. I did try provider.docker=true and set the volume to /run/containerd/containerd.sock but that didn't resolve the issue. Would you be able to further advise as to how I can manually create the routers or services for traefik? Or alternatively do a pull request to traefik? I'm trying to migrate the following docker compose file to use containerd and nerdctl to manage the yaml file

version: "3"

services:

  traefik:
    image: traefik:v3.0
    container_name: traefik
    volumes:       
     - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.http.routers.traefik-http.entrypoints=web"
      - "traefik.enable=true"
    command:
      - "--api.insecure=true"
      - "--log.level=DEBUG"
      - "--api=true"
      - "--entrypoints.web.address=:80"
      - "--providers.file.directory=/etc/traefik"
      - "--providers.file.watch=true"
      - "--providers.docker=true"
    ports:
      - 80:80
      - 443:443

  apache:
    image: httpd:latest
    container_name: apache
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.apache.rule=Host(`example.com`)"
      - "traefik.http.routers.apache.entrypoints=web"
      - "traefik.http.routers.apache-http.rule=Host(`example.com`)"

I tested these labels, but it didn't resolve the issue 

volumes: 
 -/run/containerd/containerd.sock:/run/containerd/containerd.sock

 - "--providers.docker.endpoint=unix:///var/run/containerd/containerd.sock"

If I'm not mistaken I have already specified routers and services within the labels in my yaml file above?

The dynamic config from labels need to go into a separate file, which you load with providers.file in static config. See "Example with a File Provider" (doc).

I attempted the following set up with no prevail.

static yaml file:


version: '3'

services: 

  apache:
    image: httpd:latest
    container_name: apache
    restart: unless-stopped
    volumes:
      - /var/run/containerd/containerd.sock:/var/run/containerd/containerd.sock
      - /opt/dynamic.yml:/etc/traefik/dynamic.yml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.apache-http.entrypoints=web"
      - "traefik.http.routers.apache-http.rule=Host(`example.com`)"
      - "traefik.http.routers.apache.rule=Host(`example.com`)"

  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: always
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    volumes:
      - /var/run/containerd/containerd.sock:/var/run/containerd/containerd.sock
      - /opt/dynamic.yml:/etc/traefik/dynamic.yml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik-http.entrypoints=web"
    command:
        - "--providers.file.directory=/opt"
        - "--providers.file.watch=true"
        - "--entrypoints.web.address=:80"
        - "--api=true"
        - "--log.level=DEBUG"

dynamic yaml file

http:
  routers:
    apache:
      rule: "Host(`example.com`)"
      service: apache
  services:
    apache:
      loadBalancer:
        servers:
          - url: "http://apache:80"

Please format your config with 3 backticks before and after, or select and press </>. It makes it more readable and in yaml every space matters.

Not sure if containerd has the same dynamic DNS functionality that Docker provides, making services available by their service name.

And "no prevail" is not really an extensive error description. Can you connect, do you get an error status or error message?

What is Traefik debug log and Traefik dashboard telling you?

I have formatted the code above as requested to make readability easier. There are no error messages in the traefik or apache logs. Both the containers start up and run successfully, they are on the same subnet and I can ping the apache container from within the traefik container which indicates the containers can communicate.

But as soon as I browse to my site example.com, the browser just states the site can't be reached and I can't see any requests reaching my container in the logs. The dns resolution for my domain points to my server and I have no firewall rules blocking any requests.

So it appears as though the routing is not working correctly when I try and access my site externally from the server. Hope this info provides more clarity. If containerd cannot discover services based on their names, I'm not sure how else I can set up the routing to work as expected.

Great description, thank you!

The usual breaking points:

  • Network not working
  • Not in the same Docker network
  • DNS resolution not working
  • Config discovery not working
  • Target port not correct

Ah, it seems you didn’t enable providers.docker, so no config discovery via container labels.

But you do have a dynamic config file and providers.file. Check in the Traefik debug log if it is loaded.

Instead of ping you can try wget http://apache inside Traefik container.

I have tried enabling providers.docker and used the docker socket as well as the containerd socket but this doesn't resolve the issue.

I am able to run wget http://apache from within the traefik container and download apache's default index.html file so the containers can communicate.

I have traefiks log level set to debug but there is nothing in the logs. In the apache logs I can see apache starting up, but no connections to the container when I try browse to my site. Not sure if you have any further suggestions? Thank you for your feedback thus far, really appreciate it

It seems you have a mismatch in directory:

    volumes:
      - /opt/dynamic.yml:/etc/traefik/dynamic.yml

    command:
        - "--providers.file.directory=/opt"

Should be

    command:
        - "--providers.file.directory=/etc/traefik"

Even after the above change I still cannot reach my site. Thank you for your suggestions thus far. I understand you have your own things you need to attend to so I don't expect another response but if you do have any further suggestions or tests I can perform to try and pin point the issue it would be much appreciated.

Enable and check Traefik debug log (doc) and dashboard (doc).

Is the dynamic config file loaded, are there any errors?

I am convinced traefik doesn't work with containerd. I don't see any errors in my traefik logs and I can't even enable the dashboard, I'm guessing because the routing isn't working correctly. I have set api.dashboard=true, mounted port 8080 to 8080 on the host and browsed to my servers IP:8080/dashboard and the dashboard doesn't come up.

None of the changes I've made resolves the issue but as soon as I use docker to spin up the containers it works fine.

That seems strange. When using api.dashboard=true in traefik.yml or command, then it’s static config, the whole dynamic config part (labels, dynamic files) doesn’t matter, it should just work.

Note that you can not mix static config in traefik.yml and command (except --configFile=). Make sure the config file is correctly mounted and accessible.

In Docker you can use docker exec -it <c-id> sh, don’t know about containerd.