-
-
Notifications
You must be signed in to change notification settings - Fork 350
Example for deploying with traefik in swarm mode #419
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# Docker Registry with Traefik, Redis, and UI on Docker Swarm | ||
|
||
This guide walks you through deploying a private Docker Registry with authentication and a UI, using Traefik as a reverse proxy and Redis for caching, all running in Docker Swarm mode. | ||
|
||
## Prerequisites | ||
|
||
* Docker and Docker Compose installed | ||
* Docker Swarm initialized on your system (use docker swarm init to initialize) | ||
* Traefik setup and working in your Swarm environment | ||
* A domain (e.g., my-registry.example.com, my-registry-ui.example.com, my-traefik.example.com) pointing to your Swarm cluster | ||
|
||
## Deployment Sources | ||
|
||
This deployment consists of four services: | ||
|
||
1. Traefik: A reverse proxy to route requests to the registry and the UI. | ||
2. Docker Registry: The registry for storing and managing Docker images. | ||
3. Redis: A caching service for Docker Registry’s blob descriptors. (OPTIONAL) | ||
4. Docker Registry UI: A web UI for viewing and managing images in your Docker registry. | ||
|
||
|
||
> [!IMPORTANT] | ||
> In all configuration files, be sure to review and update all the lines with `# !!!!CHANGEME!!!!` directives with your specific values. | ||
|
||
### 1. Traefik Reverse Proxy | ||
|
||
The relevant file is `docker-compose-traefik.yml`, which configures Traefik to handle routing and SSL termination for both the Docker registry and the UI. | ||
|
||
* `traefik.http.routers.traefik-public-https.rule=Host(my-traefik.example.com)` used as placeholder for traefik dashboard URL | ||
* `<</PATH/TO/YOUR/HTPASSWD>>` used as a placeholder for your `htpasswd` to be mounted as `/auth/.htpasswd` | ||
* `htpasswd -c /path/to/your/htpasswd my-auth-username` then enter the password you want for basic auth | ||
* Needs `sudo apt-get install apache2-utils` if ubuntu | ||
* Needs `brew install httpd` if OSX | ||
* `<</PATH/TO/YOUR/TRAEFIK/CONFIG/FOLDER/conf.d>>` is the placeholder for [traefik dynamic configuration files](https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-dynamic-configuration) directory, which is mounted on `/conf.d` on traefik container. | ||
* This is optional, you can remove it. If you do so, also remove `--providers.file.directory=/conf.d/` from the `command` section. | ||
* `--certificatesresolvers.le.acme.email=my-le-email.example.com` used as placeholder for let's encrypt email. | ||
|
||
This configuration sets up Traefik to listen for requests on port 80 (HTTP) and 443 (HTTPS), with automatic SSL certificate generation using Let's Encrypt. | ||
|
||
### 2. Docker Registry | ||
|
||
The relevant file is `docker-compose-registry.yml`, to deploy it with Redis for blob descriptor caching, basic authentication with htpasswd, and with necessary `CORS` settings to enable the use of UI. | ||
|
||
* `<</PATH/TO/YOUR/REGISTRY/STORAGE>>` used as placeholder to indicate directory for docker storage, which is mounter on `/var/lib/docker` in the registry container. | ||
* `<</PATH/TO/YOUR/HTTPASSWD>>` used as a placeholder for your `htpasswd` to be mounted as `/auth/.htpasswd` | ||
* You can use the same both for traefik (see above) and registry, create two separate `htpasswd` keys, up to your discretion. | ||
* If you don't need redis, remove the following lines: | ||
```diff | ||
- REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis # OPTIONAL (see docker-compose-redis.yml) remove if not needed | ||
- REGISTRY_REDIS_ADDR: redis:6379 # OPTIONAL remove if not needed | ||
``` | ||
* ` - traefik.http.routers.registry.rule=Host(my-registry.example.com)` used as placeholder for your registry URL. | ||
* `- traefik.http.routers.registry.middlewares=compress@file,large-upload@file,registry-ratelimit@file` some middlewares added to optimize registry. These are OPTIONAL, their role explained below: | ||
* `compress@file` reduces the amount of data transferred between the server and clients (e.g., browsers or API consumers). | ||
* `large-upload@file` to handle large file uploads, such as Docker image layers, to ensure smooth handling and avoid request timeouts or memory overloads. | ||
* `registry-ratelimit@file` to implement rate-limiting on the registry, restricting the number of requests a client can make within a specific period. | ||
* `traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://my-registry-ui.example.com` the UI URL which will be allowed for CORS. | ||
|
||
### 3. Redis | ||
|
||
The relevant file is `docker-compose-redis.yml`. Nothing much to change here other than resource allocation. This is OPTIONAL, if not included in the swarm deployment, make sure to remove it from `docker-compose-registry.yml` as well. | ||
|
||
### 4. UI (finally) | ||
|
||
The relevant configs and settings to deploy `joxit/docker-registry-ui:latest` are in `docker-compose-ui.yml`. | ||
|
||
* `REGISTRY_URL=https://my-registry.example.com` under the `environment` section is used as placeholder for the URL of the docker registry. | ||
* `traefik.http.routers.registry-ui.rule=Host(my-registry-ui.example.com)` under the `label` section used as the placeholder for the URL over which the UI will be served. | ||
|
||
## Deploy the stack | ||
|
||
It is up to you to decide whether you want to deploy all services to the same stack or to individual stacks. The choice depends on how you prefer to organize and manage your services within Docker Swarm. In this guide, I used `my-registry-swarm` as the stack name for deploying all services together, but you can customize this according to your needs. | ||
|
||
Once you edited all the files, deploy your stack to Docker Swarm by running the following commands: | ||
|
||
1. Initialize Docker Swarm (if not already done): | ||
|
||
``` | ||
docker swarm init | ||
``` | ||
|
||
2. Deploy traefik | ||
|
||
``` | ||
docker stack deploy -c docker-swarm-traefik.yml my-registry-swarm | ||
``` | ||
|
||
3. Deploy redis (OPTIONAL) | ||
|
||
``` | ||
docker stack deploy -c docker-swarm-redis.yml my-registry-swarm | ||
``` | ||
|
||
4. Deploy docker registry | ||
|
||
``` | ||
docker stack deploy -c docker-swarm-registry.yml my-registry-swarm | ||
``` | ||
|
||
5. Deploy UI | ||
|
||
``` | ||
docker stack deploy -c docker-swarm-ui.yml my-registry-swarm | ||
``` | ||
|
||
6. Check status | ||
|
||
``` | ||
docker stack ps my-registry-swarm | ||
``` | ||
``` | ||
docker stack services my-registry-swarm | ||
``` | ||
|
||
## Access the services | ||
|
||
* Traefik UI: https://my-traefik.example.com | ||
* Registry: https://my-registry.example.com | ||
* Registry UI: https://my-registry-ui.example.com | ||
|
||
|
||
By following these steps, you now have a private Docker Registry, complete with authentication, caching via Redis, and a web UI, all routed through Traefik in Docker Swarm mode. | ||
|
||
## Contributed by | ||
|
||
[Agah Karakuzu](https://agah.dev) ([@agahkarakuzu](https://github.com/agahkarakuzu)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
services: | ||
redis: | ||
image: redis:alpine | ||
restart: unless-stopped | ||
networks: | ||
- traefik-public | ||
deploy: | ||
resources: | ||
limits: | ||
memory: 512M | ||
reservations: | ||
memory: 32M | ||
command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru | ||
|
||
networks: | ||
traefik-public: | ||
external: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
version: '3.3' | ||
services: | ||
registry: | ||
image: registry:2 | ||
networks: [traefik-public] | ||
volumes: | ||
- <</PATH/TO/YOUR/REGISTRY/STORAGE>>:/var/lib/registry # !!!!CHANGEME!!!! (REQUIRED) | ||
- <</PATH/TO/YOUR/HTTPASSWD>>:/auth/.htpasswd:ro # !!!!CHANGEME!!!! (REQUIRED) | ||
environment: | ||
REGISTRY_AUTH: htpasswd | ||
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm | ||
REGISTRY_AUTH_HTPASSWD_PATH: /auth/.htpasswd | ||
REGISTRY_STORAGE_DELETE_ENABLED: "no" | ||
REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis # OPTIONAL (see docker-compose-redis.yml) remove if not needed | ||
REGISTRY_REDIS_ADDR: redis:6379 # OPTIONAL remove if not needed | ||
deploy: | ||
labels: | ||
- traefik.enable=true | ||
- traefik.docker.network=traefik-public | ||
- traefik.constraint-label=traefik-public | ||
- traefik.http.routers.registry.rule=Host(`my-registry.example.com`) # !!!!CHANGEME!!!! (REQUIRED) | ||
- traefik.http.routers.registry.entrypoints=https | ||
- traefik.http.routers.registry.tls=true | ||
- traefik.http.routers.registry.tls.certresolver=le | ||
- traefik.http.services.registry.loadbalancer.server.port=5000 | ||
- traefik.http.services.registry.loadbalancer.passhostheader=true | ||
- traefik.http.routers.registry.middlewares=compress@file,large-upload@file,registry-ratelimit@file # # !!!!CHANGEME!!!! (OPTIONAL) | ||
# CORS FOR UI | ||
- traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://my-registry-ui.example.com # !!!!CHANGEME!!!! (REQUIRED) | ||
- traefik.http.middlewares.cors.headers.accesscontrolallowmethods=HEAD,GET,OPTIONS # ADD DELETE TO SUPPORT IMAGE DELETION | ||
- traefik.http.middlewares.cors.headers.accesscontrolallowcredentials=true | ||
- traefik.http.middlewares.cors.headers.accesscontrolallowheaders=Authorization,Accept,Cache-Control | ||
- traefik.http.middlewares.cors.headers.accesscontrolexposeheaders=Docker-Content-Digest | ||
- traefik.http.routers.registry.middlewares=cors | ||
networks: | ||
traefik-public: | ||
external: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
version: '3.3' | ||
|
||
services: | ||
|
||
traefik: | ||
image: traefik:v2.11.0 | ||
ports: | ||
- target: 80 | ||
published: 80 | ||
- target: 443 | ||
published: 443 | ||
- target: 222 | ||
published: 222 | ||
deploy: | ||
placement: | ||
constraints: | ||
# Make the traefik service run only on the node with this label | ||
# as the node with it has the volume for the certificates | ||
- node.labels.traefik-public.traefik-public-certificates == true | ||
labels: | ||
# Enable Traefik for this service, to make it available in the public network | ||
- traefik.enable=true | ||
# Use the traefik-public network (declared below) | ||
- traefik.docker.network=traefik-public | ||
- traefik.constraint-label=traefik-public | ||
# admin-auth middleware with HTTP Basic auth | ||
- traefik.http.middlewares.admin-auth.basicauth.usersfile=/auth/.htpasswd | ||
# https-redirect middleware to redirect HTTP to HTTPS | ||
# It can be re-used by other stacks in other Docker Compose files | ||
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https | ||
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true | ||
# traefik-http set up only to use the middleware to redirect to https | ||
# Uses the environment variable DOMAIN | ||
- traefik.http.routers.all-http.rule=HostRegexp(`{host:.+}`) | ||
- traefik.http.routers.all-http.entrypoints=http | ||
- traefik.http.routers.all-http.middlewares=https-redirect | ||
# traefik-https the actual router using HTTPS | ||
# Uses the environment variable DOMAIN | ||
- traefik.http.routers.traefik-public-https.rule=Host(`my-traefik.example.com`) # !!!!CHANGEME!!!! (REQUIRED) | ||
- traefik.http.routers.traefik-public-https.entrypoints=https | ||
- traefik.http.routers.traefik-public-https.tls=true | ||
# Use the special Traefik service api@internal with the web UI/Dashboard | ||
- traefik.http.routers.traefik-public-https.service=api@internal | ||
# Use the "le" (Let's Encrypt) resolver created below | ||
- traefik.http.routers.traefik-public-https.tls.certresolver=le | ||
# Enable HTTP Basic auth, using the middleware created above | ||
- traefik.http.routers.traefik-public-https.middlewares=admin-auth | ||
# Define the port inside of the Docker service to use | ||
- traefik.http.services.traefik-public.loadbalancer.server.port=8080 | ||
volumes: | ||
# Add Docker as a mounted volume, so that Traefik can read the labels of other services | ||
- /var/run/docker.sock:/var/run/docker.sock:ro | ||
- <</PATH/TO/YOUR/HTPASSWD>>:/auth/.htpasswd:ro # !!!!CHANGEME!!!! (REQUIRED) | ||
# Mount the volume to store the certificates | ||
- traefik-public-certificates_new:/certificates | ||
- <</PATH/TO/YOUR/TRAEFIK/CONFIG/FOLDER/conf.d>>:/conf.d # # !!!!CHANGEME!!!! (OPTIONAL) use for dynamic conf. Remove if not needed. | ||
command: | ||
# - --log.level=DEBUG | ||
# Enable Docker in Traefik, so that it reads labels from Docker services | ||
- --providers.docker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In traefik v3, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jnovack I'll make ensure a proper v2 --> v3 migration and will let you know when done. |
||
# Add a constraint to only use services with the label "traefik.constraint-label=traefik-public" | ||
- --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`) | ||
# Do not expose all Docker services, only the ones explicitly exposed | ||
- --providers.docker.exposedbydefault=false | ||
# Enable Docker Swarm mode | ||
- --providers.docker.swarmmode | ||
# file provider | ||
- --providers.file.directory=/conf.d/ # !!!!CHANGEME!!!! (OPTIONAL) see the last entry of volumes. Remove if not needed. | ||
- --providers.file.watch=true | ||
# Create an entrypoint "http" listening on port 80 | ||
- --entrypoints.http.address=:80 | ||
# Create an entrypoint "https" listening on port 443 | ||
- --entrypoints.https.address=:443 | ||
# Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL | ||
- --certificatesresolvers.le.acme.email=my-le-email.example.com # !!!!CHANGEME!!!! (REQUIRED) | ||
# Store the Let's Encrypt certificates in the mounted volume | ||
- --certificatesresolvers.le.acme.storage=/certificates/acme.json | ||
# Use the TLS Challenge for Let's Encrypt | ||
- --certificatesresolvers.le.acme.tlschallenge=true | ||
# Enable the access log, with HTTP requests | ||
- --accesslog | ||
# Enable the Traefik log, for configurations and errors | ||
- --log | ||
- --log.level=INFO | ||
# Enable the Dashboard and API | ||
- --api | ||
networks: | ||
# Use the public network created to be shared between Traefik and | ||
# any other service that needs to be publicly available with HTTPS | ||
- traefik-public | ||
- traefik-external | ||
|
||
volumes: | ||
# Create a volume to store the certificates, there is a constraint to make sure | ||
# Traefik is always deployed to the same Docker node with the same volume containing | ||
# the HTTPS certificates | ||
traefik-public-certificates_new: | ||
|
||
networks: | ||
# Use the previously created public network "traefik-public", shared with other | ||
# services that need to be publicly available via this Traefik | ||
traefik-public: | ||
external: true | ||
traefik-external: | ||
external: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
version: '3.3' | ||
|
||
services: | ||
# Docker Registry UI | ||
registry_ui: | ||
image: joxit/docker-registry-ui:latest | ||
environment: | ||
- REGISTRY_URL=https://my-registry.example.com # !!!!CHANGEME!!!! (REQUIRED) | ||
- SINGLE_REGISTRY=true | ||
- DELETE_IMAGES=false # Disable delete functionality, you may enable | ||
- NGINX_PROXY_PASS_URL=http://registry:5000 # The URL that the UI will proxy to the Docker registry | ||
networks: | ||
- traefik-public | ||
deploy: | ||
replicas: 1 # You can adjust the replica count as needed for scaling | ||
labels: | ||
- traefik.enable=true | ||
- traefik.docker.network=traefik-public | ||
- traefik.constraint-label=traefik-public | ||
- traefik.http.routers.registry-ui.rule=Host(`my-registry-ui.example.com`) # # !!!!CHANGEME!!!! (REQUIRED) make sure that docker-compose-regitry.yml has the same | ||
- traefik.http.routers.registry-ui.entrypoints=https | ||
- traefik.http.routers.registry-ui.tls=true | ||
- traefik.http.routers.registry-ui.tls.certresolver=le | ||
- traefik.http.services.registry-ui.loadbalancer.server.port=80 | ||
- traefik.http.services.registry-ui.loadbalancer.passhostheader=true | ||
|
||
networks: | ||
traefik-public: | ||
external: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v3 has been stable for over a year, please use a stable v3 version.