Deploying Traefik and Pihole on the Swarm home cluster

3 minute read

In the previous post, we setup a Swarm cluster. That’s fine and dandy but that cluster, as far as we’re concerned, is useless. Let’s change that.

Traefik

I’ve talked and played with Traefik previously on this blog and here we go again, with another orchestration technology. As always, we need an ingress to our cluster. Traefik makes a great ingress that’s easily configurable with labels.

Let’s not forget, we’re working with Swarm this time around. Swarm stacks look very similar to docker-compose manifests.

But, before we do that, there is a small piece of information that we need to be aware of. For Traefik to be able to route traffic to our services, both Traefik and the service need to be on the same network. Let’s make this a bit more predictable and manage that network ourselves.

warning

Only leader and manager nodes will allow interaction with the Swarm cluster. The worker nodes will not give you any useful information about the cluster.

Network Configuration

We started with Ansible and we shall continue with Ansible. We begin with creating the network.

---
- name: Create a Traefik Ingress network
  community.docker.docker_network:
    name: traefik-ingress
    driver: overlay
    scope: swarm

Ingress

Once the network is in place, we can go ahead and deploy Traefik.

warning

This setup is not meant to be deploy in a production setting. SSL certificates require extra configuration steps that might come in a future post.

---
- name: Deploy Traefik Stack
  community.docker.docker_stack:
    state: present
    name: Traefik
    compose:
      - version: '3'
        services:
          traefik:
            image: traefik:latest
            restart: unless-stopped
            command:
              - --entrypoints.web.address=:80
              - --providers.docker=true
              - --providers.docker.swarmMode=true
              - --accesslog
              - --log.level=INFO
              - --api
              - --api.insecure=true
            ports:
              - "80:80"
            volumes:
              - "/var/run/docker.sock:/var/run/docker.sock:ro"
            networks:
            - traefik-ingress
            deploy:
              replicas: 1
              resources:
                limits:
                  cpus: '1'
                  memory: 80M
                reservations:
                  cpus: '0.5'
                  memory: 40M
              placement:
                constraints:
                  - node.role == manager

              labels:
                - traefik.protocol=http
                - traefik.docker.network=traefik-ingress
                - traefik.http.routers.traefik-api.rule=Host(`traefik.our-domain.com`)
                - traefik.http.routers.traefik-api.service=api@internal
                - traefik.http.services.taefik-api.loadbalancer.server.port=8080

        networks:
          traefik-ingress:
            external: true

Note

Even though these are Ansible tasks, Swarm stack manifests are not much different as I’m using mostly the raw format.

Let’s talk a bit about what we did.

--providers.docker=true and --providers.docker.swarmMode=true
We configure Traefik to enable both docker and swarm mode providers.
--api and --api-insecure=true
We enable the API which offers the UI and we allow it to run insecure.

The rest, I believe, have been explained in the previous blog post.

If everything went well, and we configured our DNS properly, we should be welcomed by a Traefik dashboard on traefik.our-domain.com.

Pi-hole

Now I know most people install the Pi-hole straight on the Pi. Well, I’m not most people and I’d like to deploy it in a container. I feel it’s easier all around than installing it on the system, you’ll see.

---
- name: Deploy PiHole Stack
  community.docker.docker_stack:
    state: present
    name: PiHole
    compose:
      - version: '3'
        services:
          pihole:
            image: pihole/pihole:latest
            restart: unless-stopped
            ports:
              - "53:53"
              - "53:53/udp"
            cap_add:
              - NET_ADMIN
            environment:
              TZ: "Europe/Vienna"
              VIRTUAL_HOST: pihole.our-domain.com
              VIRTUAL_PORT: 80
            healthcheck:
              test: ["CMD", "curl", "-f", "http://localhost:80/"]
              interval: 30s
              timeout: 20s
              retries: 3
            volumes:
              - /opt/pihole/data/pihole-config:/etc/pihole
              - /opt/pihole/data/pihole-dnsmasq.d:/etc/dnsmasq.d
            networks:
              - traefik-ingress
            deploy:
              replicas: 1
              placement:
                constraints:
                  - node.role == worker
              labels:
                - traefik.docker.network=traefik-ingress
                - traefik.http.routers.pihole-http.entrypoints=web
                - traefik.http.routers.pihole-http.rule=Host(`pihole.our-domain.com`)
                - traefik.http.routers.pihole-http.service=pihole-http
                - traefik.http.services.pihole-http.loadbalancer.server.port=80
                - traefik.http.routers.pihole-http.middlewares=pihole-main
                - traefik.http.middlewares.pihole-main.chain.middlewares=frame-deny,browser-xss-filter
                - traefik.http.middlewares.frame-deny.headers.framedeny=true
                - traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true

        networks:
          traefik-ingress:
            external: true

We make sure to expose port 53 for DNS on all nodes, and configure the proper labels to our service so that Traefik can pick it up.

Once deployed and your DNS is pointing properly then pihole.our-domain.com is waiting for you. This also shows us that the networking between nodes works properly. Let’s test it out.

$ nslookup duckduckgo.com pihole.our-domain.com
Server:		pihole.our-domain.com
Address:	192.168.1.100#53

Non-authoritative answer:
Name:	duckduckgo.com
Address: 52.142.124.215

Alright, seems that our Pi-hole works.

Conclusion

On these small Raspberry Pis, the cluster seems to be working very well. The Pi-hole has been running without any issues for a few days running my internal DNS. There’s a few improvements that can be done to this setup, mainly the deployment of an SSL cert. That may come in the future, time permitting. Stay safe, until the next one !