Some time after originally publishing this recipe, I had the opportunity to meet the Portainer team, who are based out of Auckland, New Zealand. We now have an ongoing friendly working relationship. Portainer is my GitHub Sponsor , and in return, I maintain their official Kubernetes helm charts!
Portainer is a lightweight sexy UI for visualizing your docker environment. It also happens to integrate well with Docker Swarm clusters, which makes it a great fit for our stack.
Portainer attempts to take the "geekiness" out of containers, by wrapping all the jargon and complexity in a shiny UI and some simple abstractions. It's a great addition to any stack, especially if you're just starting your containerization journey!
- Docker swarm cluster with persistent shared storage
- Traefik configured per design
- DNS entry for the hostname you intend to use (or a wildcard), pointed to your keepalived IP
- Traefik Forward Auth to secure your Traefik-exposed services with an additional layer of authentication
Setup data locations¶
Create a folder to store portainer's persistent data:
Setup Docker Swarm¶
Create a docker swarm config file in docker-compose syntax (v3), something like this:
I automatically and instantly share (with my sponsors) a private "premix" git repository, which includes necessary docker-compose and env files for all published recipes. This means that sponsors can launch any recipe with just a
git pull and a
docker stack deploy 👍.
🚀 Update: Premix now includes an ansible playbook, so that sponsors can deploy an entire stack + recipes, with a single ansible command! (more here)
version: '3.2' services: portainer: image: portainer/portainer-ce command: -H tcp://tasks.agent:9001 --tlsskipverify ports: - "9000:9000" - "8000:8000" volumes: - /var/data/portainer:/data networks: - internal - traefik_public deploy: mode: replicated replicas: 1 placement: constraints: [node.role == manager] labels: # traefik - traefik.enable=true - traefik.docker.network=traefik_public # traefikv1 - traefik.frontend.rule=Host:portainer.example.com - traefik.port=9000 # uncomment if you want to protect portainer with traefik-forward-auth using traefikv1 # - traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181 # - traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User # - traefik.frontend.auth.forward.trustForwardHeader=true # traefikv2 - "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)" - "traefik.http.routers.portainer.entrypoints=https" - "traefik.http.services.portainer.loadbalancer.server.port=9000" # uncomment if you want to protect portainer with traefik-forward-auth using traefikv2 # - "traefik.http.routers.portainer.middlewares=forward-auth" agent: image: portainer/agent volumes: - /var/run/docker.sock:/var/run/docker.sock - /var/lib/docker/volumes:/var/lib/docker/volumes networks: - internal deploy: mode: global placement: constraints: [node.platform.os == linux] networks: traefik_public: external: true internal: driver: overlay ipam: config: - subnet: 172.16.13.0/24
Setup unique static subnets for every stack you deploy. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See my list here.
Umm.. didn't you just copy these from the official Portainer docs?
Almost word-for-word! I've made a few (opinionated) improvements though:
- Expose Portainer via Traefik with valid LetsEncrypt SSL certs
- Optionally protected Portainer's web UI with OIDC auth via Traefik Forward Auth
- Use filesystem paths instead of Docker volumes for maximum "swarminess" (We want an HA swarm, and HA Docker Volumes are a PITA, so we just use our ceph shared storage)
Launch Portainer stack¶
Launch the Portainer stack by running
docker stack deploy portainer -c <path -to-docker-compose.yml>
Log into your new instance at https://YOUR-FQDN. You'll be prompted to set your admin user/password on first login. Start at "Home", and click on "Primary" to manage your swarm (you can manage multiple swarms via one Portainer instance using the agent):
Chef's notes 📓¶
Tip your waiter (sponsor) 👏¶
Did you receive excellent service? Want to make your waiter happy? (..and support development of current and future recipes!) Sponsor me on Github / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏
Flirt with waiter (subscribe) 💌¶
Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated. (*double-opt-in, no monkey business, no spam)