فهرست منبع

Examples: Docker Swarm with Traefik and container build script.

Example files for setting a complete SNAC + Traefik v3 stack on Docker
Swarm mode, including Let's Encrypt certificates, HTTP security headers,
and caching of */s/* (SNAC static files) for 24 hours with souin.

A script for building the SNAC container with Docker and optionally
pushing it to a registry.
daltux 10 ماه پیش
والد
کامیت
ffaa7aeb80
3فایلهای تغییر یافته به همراه247 افزوده شده و 0 حذف شده
  1. 114 0
      examples/docker-swarm-traefik/snac_stack.yml
  2. 89 0
      examples/docker-swarm-traefik/traefik_config.yml
  3. 44 0
      examples/docker_build_snac.sh

+ 114 - 0
examples/docker-swarm-traefik/snac_stack.yml

@@ -0,0 +1,114 @@
+---
+# Example of a SNAC + Traefik stack on Docker Swarm mode
+# SNAC behind Traefik v3 reverse proxy
+# including Let's Encrypt certificates, HTTP security headers
+# and caching */s/* (SNAC static files) for 24h with souin.
+#
+# docker stack deploy -c snac_stack.yml snac
+
+services:
+  traefik:
+    image: traefik:v3
+    hostname: '{{.Node.Hostname}}'
+    ports:
+      # listen on host ports without ingress network
+      - target: 80
+        published: 80
+        protocol: tcp
+        mode: host
+      - target: 443
+        published: 443
+        protocol: tcp
+        mode: host
+      - target: 443
+        published: 443
+        protocol: udp   # for HTTP3
+        mode: host
+    networks:
+      - proxy
+    volumes:
+      - /opt/docker/traefik/traefik.yml:/etc/traefik/traefik_config.yml  # main Traefik static configuration
+      - /opt/docker/traefik/dynamic:/etc/traefik/dynamic   # for Traefik dynamic configuration files
+      - /opt/docker/acme:/acme    # directory for certificate storage files
+      - /opt/docker/traefik/log:/var/log
+      - /var/run/docker.sock:/var/run/docker.sock:ro
+      - /usr/share/zoneinfo/America/Sao_Paulo:/etc/localtime:ro  # Change this to another or comment/remove to use UTC
+      - /etc/timezone:/etc/timezone:ro    # Comment/remove to use UTC
+    environment:
+      - TZ=America/Sao_Paulo  # Change it to another or comment/remove to use UTC
+    deploy:
+      mode: global
+      placement:
+        constraints:
+          - node.role==manager
+      labels:
+        - traefik.enable=true
+        - traefik.http.routers.mydashboard.rule=Host(`traefik.example.net`)   # for Traefik dashboard
+        - traefik.http.routers.mydashboard.service=api@internal
+        - traefik.http.routers.mydashboard.middlewares=traefik_auth,traefik_retry
+        - traefik.http.services.mydashboard.loadbalancer.server.port=1337
+        - traefik.http.middlewares.traefik_retry.retry.attempts=3
+        - traefik.http.middlewares.traefik_retry.retry.initialinterval=200ms
+        # How to generate a password hash: echo $(htpasswd -nB desired_username) | sed -e s/\\$/\\$\\$/g
+        - traefik.http.middlewares.traefik_auth.basicauth.users=desired_username:PASTE_THE_PASSWORD_HASH_HERE
+      resources:
+        limits:
+          cpus: "2"
+          memory: 1g
+        reservations:
+          memory: 256m
+
+  snac:
+    image: codeberg.org/daltux/snac:2.82-dev_daltux3
+    hostname: '{{.Node.Hostname}}'
+    restart: unless-stopped
+    security_opt:
+      - no-new-privileges:true
+    volumes:
+      - /opt/snac:/data
+    networks:
+      - proxy
+    depends_on:
+      - traefik
+    environment:
+      - TZ=America/Sao_Paulo   # Change this to your own or comment/remove it to use UTC.
+    deploy:
+      mode: replicated
+      replicas: 1
+      labels:
+        - traefik.enable=true
+        - traefik.http.services.snac.loadbalancer.server.port=8001
+        - traefik.http.routers.snac.rule=Host(`snac.example.net`)
+        - traefik.http.routers.snac.priority=1
+        - traefik.http.routers.snac.middlewares=snac_headers@swarm,traefik_retry@swarm
+        - "traefik.http.routers.snac_static.rule=Host(`snac.example.net`) && PathRegexp(`^/.+/s/.+`)"
+        - "traefik.http.routers.snac_static.priority=2"
+        - "traefik.http.routers.snac_static.middlewares=snac_headers@swarm,snac_cache_static@swarm,traefik_retry@swarm"
+        - traefik.http.middlewares.snac_headers.headers.stsSeconds=31536000
+        - traefik.http.middlewares.snac_headers.headers.stsIncludeSubdomains=false
+        - traefik.http.middlewares.snac_headers.headers.stsPreload=false
+        - traefik.http.middlewares.snac_headers.headers.customFrameOptionsValue=SAMEORIGIN
+        - traefik.http.middlewares.snac_headers.headers.contentTypeNosniff=true
+        - traefik.http.middlewares.snac_headers.headers.browserXssFilter=true
+        - traefik.http.middlewares.snac_headers.headers.referrerPolicy=strict-origin-when-cross-origin
+        - "traefik.http.middlewares.snac_headers.headers.contentSecurityPolicy=default-src 'self'; base-uri 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; form-action 'self'; img-src https: data:; font-src 'self'; media-src https: data:; connect-src 'none'; frame-src 'none'; frame-ancestors 'none';"
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.api.souin=true
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.log_level=info
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.default_cache.ttl=1440m
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.default_cache.allowed_http_verbs=GET,HEAD
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.default_cache.stale=1h
+        - traefik.http.middlewares.snac_cache_static.plugin.souin.default_cache.etag=true
+        - "traefik.http.middlewares.snac_cache_static.plugin.souin.default_cache.default_cache_control=public, max-age=86400"
+      resources:
+        limits:
+          cpus: "3"
+          memory: 1g
+        reservations:
+          memory: 256m
+
+networks:
+  proxy:
+    name: proxy
+    driver: overlay
+    attachable: true
+    external: true

+ 89 - 0
examples/docker-swarm-traefik/traefik_config.yml

@@ -0,0 +1,89 @@
+---
+# Traefik main config file
+# e.g. /opt/docker/traefik/traefik_config.yml
+
+entryPoints:
+  web:
+    address: ":80"
+    http:
+      encodeQuerySemicolons: true
+      redirections:
+        entryPoint:
+          to: websecure
+          scheme: https
+          permanent: true
+  websecure:
+    address: ":443"
+    asDefault: true
+    http:
+      encodeQuerySemicolons: true
+      tls:
+        certResolver: letsencrypt
+    http2:
+      maxConcurrentStreams: 100
+    http3: {}
+
+certificatesResolvers:
+  letsencrypt:
+    acme:
+      email: you@example.net
+      storage: "/acme/letsencrypt.json"
+      keyType: EC384
+      httpChallenge:
+        entryPoint: web
+#  buypass:
+#    acme:
+#      email: you@example.net
+#      caServer: "https://api.buypass.com/acme/directory"
+#      storage: "/acme/buypass.json"
+#      keyType: EC256
+#      certificatesDuration: 4320
+#      httpChallenge:
+#        entryPoint: web
+
+ocsp: {}
+
+tls:
+  stores:
+    default:
+      defaultGeneratedCert:
+        resolver: letsencrypt
+        domain:
+          main: snac.example.net
+          # sans:
+          #  - other.example.net
+          #  - another.example.net
+
+providers:
+  file:
+    directory: "/etc/traefik/dynamic"
+    watch: true
+  swarm:
+    network: "proxy"
+    endpoint: "unix:///var/run/docker.sock"
+    exposedByDefault: false
+    watch: true
+    allowEmptyServices: true
+
+api:
+  dashboard: true
+  insecure: false
+  debug: false
+  disabledashboardad: true
+
+log:
+  level: "INFO"
+  filePath: "/var/log/server.log"
+
+accessLog:
+  filePath: "/var/log/traefik-access.log"
+  bufferingSize: 15
+  fields:
+    names:
+      StartUTC: "drop"
+
+experimental:
+  plugins:
+    souin:
+      moduleName: "github.com/darkweak/souin"
+      version: "v1.7.7"

+ 44 - 0
examples/docker_build_snac.sh

@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# docker_build_snac.sh : build a SNAC container image
+# 	and optionally send it to a container registry.
+#
+# Set variables e.g. if you want version to be different from 'latest':
+# 	img_version=2.82 ./docker_build_snac.sh
+
+src_dir=${src_dir:-"$HOME/src/snac2"}
+img_name=${img_name:-'snac'}
+img_version=${img_version:-'latest'}
+#registry=${registry:-'codeberg.org'}
+#reg_user=${reg_user:-'daltux'}
+
+if [ -z "$tag" ] ; then
+	if [ -n "$reg_user" ] && [ -z "$registry" ] ; then
+		echo "Missing container registry name. Set variable \"registry\"." >&2
+		exit 10
+	fi
+
+	if [ -z "$registry" ] ; then
+		tag="$img_name:$img_version"
+	elif [ -z "$reg_user" ] ; then
+		echo "Container registry user unknown. Set variable \"reg_user\"." >&2
+		exit 20
+	else
+		tag="$registry/$reg_user/$img_name:$img_version"
+	fi
+fi
+
+if [ -d "$src_dir" ] ; then
+	echo "Entering directory \"$src_dir\"..."
+	cd "$src_dir" || exit $?
+	docker build --no-cache -f Dockerfile -t "$tag" . || exit $?
+else
+	echo "Invalid directory \"$src_dir\"" >&2
+	exit 30
+fi
+
+if [ -n "$registry" ] ; then
+	#docker login "$registry" || $?
+	docker image push "$tag"
+fi
+