Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker Service deployment (Stack) #515

Open
prologic opened this issue Jan 2, 2019 · 5 comments
Open

Docker Service deployment (Stack) #515

prologic opened this issue Jan 2, 2019 · 5 comments

Comments

@prologic
Copy link

prologic commented Jan 2, 2019

I'd like to see support for running, updating and managing AdGuardHome via docker and more specifically a Docker Swarm set-up using docker stack deploy (//Docker Swarm mode is far far far simpler to use and operationalize than k8s!//).

Somewhat related is #448

I'm happy to contribute to this but it would also require #377 to be resolved with up-to-date upstream images built in some kind of CI. Ideally using Docker Hub's own Automated Build would be nice. #514 would help a lot with this.

A (//not yet tested//) adguardhome.yml (//Docker Stackfile//): would look like this:

version: "3.4"

services:
  adguardhome:
    image: adguardteam/adguardhome:latest
    ports:
      - "53:53/udp"
    networks:
      - bridge
    volumes:
      - adguardhomedata:/data
    deploy:
      replicas: 1

networks:
  bridge:
    external: true

volumes:
  adguardhomedata:
    driver: local
@prologic
Copy link
Author

prologic commented Jan 2, 2019

Your upgrade process would then look like this:

$ docker service update -d --image adguardteam/adguardhome:latest adguardhome_adguardhome

@prologic
Copy link
Author

prologic commented Jan 2, 2019

I now have this running at my home network in my Docker Swarm mode cluster.

Here is the config:

version: "3.4"

services:
  adguardhome:
    image: r.mydomain.com/myuser/adguardhome
    command: -c /data/config.yml
    volumes:
      - adguardhomedata:/data
    networks:
      - bridge
      - traefik
    ports:
      - "53:53/udp"
    deploy:
      placement:
        constraints:
          - "node.hostname == my-master-node"
      labels:
        - "traefik.enable=true"
        - "traefik.port=3000"
        - "traefik.backend=adguardhome"
        - "traefik.docker.network=traefik"
        - "traefik.frontend.rule=Host:adguardhome.mydomain.com"
      replicas: 1

networks:
  bridge:
    external: true
  traefik:
    external: true

volumes:
  adguardhomedata:
    driver: local

However I wasn't able to use Docker Configs for this because the AdGuardHome daemon insists on rewriting the config file? It seems the last-updated timestamp of each filter source is stored in teh config file. This is pretty bad design :/ Can we rethink this? Mutable data like this should well umm go to somewhere mutable like the /data volume?

@ameshkov
Copy link
Member

ameshkov commented Jan 2, 2019

Mutable data like this should well umm go to somewhere mutable like the /data volume?

Yep, that makes sense indeed.

@ameshkov ameshkov added this to the Docker image milestone Jan 2, 2019
@ameshkov
Copy link
Member

It seems the last-updated timestamp of each filter source is stored in teh config file

Resolved in v0.93 btw.

@wmandra
Copy link

wmandra commented Sep 15, 2021

I have successfully managed to get AdGuardHome running replicated on a docker swarm cluster.

Caveats:

  • If using Encryption and other services are running on the swarm using port 443, AdGuardHome will have to use a different port.
  • Configs can't be shared across replicas, however once the first instance is configured you can shutdown the stack and copy the config to the second node.
  • Must have a physical node for each replica that you want to run.

First, create macvlan config on each node that will run AdGuardHome:
Node 1: docker network create --config-only --subnet 192.168.1.0/24 --gateway 192.168.1.1 -o parent=eth0 --ip-range 192.168.1.53/32 dns-config

Node 2: docker network create --config-only --subnet 192.168.1.0/24 --gateway 192.168.1.1 -o parent=eth0 --ip-range 192.168.1.153/32 dns-config

In the above, 192.168.1.53 and 192.168.1.153 are the IP Addresses that AdGuardHome will be available on.

On a manager node create a swarm scoped macvlan network: docker network create -d macvlan --scope swarm --attachable --config-from dns-config dns

Add a label to each node in the swarm that will run an instance of AdGuardHome:

docker node update --label-add dns=true node01
docker node update --label-add dns=true node02

Create shared data volumes for each instance. In my case I have a gluster volume shared across all nodes and mounted to /mnt/data:

mkdir /mnt/data/adguard/ns1/conf
mkdir /mnt/data/adguard/ns1/work
mkdir /mnt/data/adguard/ns2/conf
mkdir /mnt/data/adguard/ns2/work

If you want to have more than 2 replicas running, just create additional directories as ns3, ns4...

Docker compose file (adguard.yml):

version: '3.8'

services:
  ns:
    image: adguard/adguardhome:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "853:853/tcp"
      - "3000:3000/tcp"
    volumes:
      - type: bind
        source: "/mnt/data/adguard/ns{{.Task.Slot}}/conf"
        target: /opt/adguardhome/conf
      - type: bind
        source: "/mnt/data/adguard/ns{{.Task.Slot}}/work"
        target: /opt/adguardhome/work
    networks:
      - dns
    cap_add:
      - NET_ADMIN
    dns:
      - 9.9.9.10
      - 149.112.112.10
    hostname: "ns{{.Task.Slot}}"
    deploy:
      mode: replicated
      replicas: 2
      placement:
        max_replicas_per_node: 1
        constraints: [node.labels.dns == true]
      labels:
        - docker.group=dns

networks:
  dns:
    external: true

Important bits in the above yaml file:

  • "version: '3.8'" this is needed to limit the number of replicas per node
  • using the long method of specifying bind mounts is used so that we can use the {{.Task.Slot}} template which then mounts unique paths to each replica
  • "max_replicas_per_node: 1" this prevents 2 instances from attempting to run on the same node in the event of a failure.
  • "contraints: [node.labels.dns == true]" this ensures the replicas only run on the nodes that we configured with the IP Addresses we want to use.

Deploy the stack: docker stack deploy adguard -c ./adguard.yml

In my deployment I left the "bind_port" in the AdGuardHome.yaml configuration file set to 80, then enabled encryption and set the HTTPS port to 3000. I don't use DNS-over-HTTPS internally just 53/udp so this makes the admin portal available on 3000 with proper certificate included in the config.

Note, for the container instance to be able to communicate with outside work via macvlan network the parent interface of the host must be in promiscuous mode ip link set eth0 promisc on and the default iptables forward rule must be changed from DROP to ACCEPT iptables -P FORWARD ACCEPT.

Finally, if the host needs to use the container for its dns you will also have to create a macvlan shim interface on the host and add a static route:

ip link add macvlan-shim link eth0 type macvlan mode bridge
ip addr add {MAC_VLAN_IP}/32 dev macvlan-shim
ip link set macvlan-shim up
ip route add X.X.X.X/32 dev macvlan-shim

This can easily be added to a script and run on boot via a simple systemd unit file.

Hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants