В данной статье мы рассмотрим, как настроить три мощных инструмента для управления вашим сервером: Traefik, Portainer и Keycloak. В итоге вы получите готовый файл docker-compose.yaml
, который позволит вам развернуть все эти сервисы на вашем VPS сервере.
Предварительные условия
- VPS сервер: Убедитесь, что у вас есть доступ к VPS серверу с установленной операционной системой Linux. Если у вас ещё нет VPS, её можно арендовать у провайдера, например таких как aeza, beget, timeweb.
- Docker и Docker Compose: Убедитесь, что Docker и Docker Compose установлены на вашем сервере. Если они не установлены, следуйте инструкциям на официальном сайте Docker для установки.
Сервисы и их предназначение
- Traefik — это современный HTTP reverse proxy и load balancer, разработанный для автоматизации работы с контейнерами. Он автоматически обнаруживает контейнеры и их настройки, облегчая маршрутизацию и управление трафиком. Он будет доступен по адресу
traefik.example.com
. - Portainer — это удобный веб-интерфейс для управления Docker. С его помощью можно легко создавать, обновлять и удалять контейнеры, а также управлять Docker-образами и сетями. Он будет доступен по адресу
portainer.example.com
. - Keycloak — это сервер управления идентификацией и доступом с открытым исходным кодом. Он предоставляет функции единого входа (SSO), федерации идентификационных данных и управления пользователями. Он будет доступен по адресу
auth.example.com
.
docker-compose.yaml
и .env
Файлы Для настройки данных сервисов на вашем сервере, используйте следующий файл docker-compose.yaml
:
version: "3.8"
volumes:
letsencrypt:
portainer_data:
keycloakdata:
networks:
intranet:
name: intranet
services:
traefik:
container_name: traefik
image: "traefik:latest"
command:
- "--api.insecure=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entryPoints.ssh.address=:${SHELL_SSH_PORT}"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=intranet"
- "--log.level=DEBUG"
- "--certificatesresolvers.leresolver.acme.httpchallenge=true"
- "--certificatesresolvers.leresolver.acme.email=${ACME_EMAIL}"
- "--certificatesresolvers.leresolver.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--metrics.prometheus=true"
- "--api.dashboard=true"
ports:
- "${SHELL_SSH_PORT}:${SHELL_SSH_PORT}"
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "${LETSENCRYPT_DIR}:/letsencrypt"
networks:
- intranet
labels:
- "traefik.enable=true"
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.tls.certresolver=leresolver"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
- "traefik.http.routers.traefik.middlewares=forwardauth"
traefik-forward-auth:
image: mesosphere/traefik-forward-auth
container_name: traefik-forward-auth
restart: on-failure
depends_on:
- traefik
- keycloak
environment:
- TZ=${TZ}
- SECRET=${FORWARD_AUTH_SECRET}
- PROVIDER_URI=${FORWARD_AUTH_PROVIDER_URI}
- CLIENT_ID=${FORWARD_AUTH_CLIENT_ID}
- CLIENT_SECRET=${FORWARD_AUTH_CLIENT_SECRET}
- ENCRYPTION_KEY=${FORWARD_AUTH_ENCRYPTION_KEY}
# - COOKIE_DOMAIN=https://emxample.com
# - DISABLE_SSL_VERIFICATION=true # might be unnecessary
# - INSECURE_COOKIE=1
- SCOPE=profile email openid
networks:
- intranet
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181"
- "traefik.http.routers.traefik-forward-auth.entrypoints=websecure"
- "traefik.http.routers.traefik-forward-auth.middlewares=forwardauth"
- "traefik.http.middlewares.forwardauth.forwardauth.address=http://traefik-forward-auth:4181"
- "traefik.http.middlewares.forwardauth.forwardauth.authResponseHeaders=X-Forwarded-User"
- "traefik.http.middlewares.forwardauth.forwardauth.trustForwardHeader=true"
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
command: -H unix:///var/run/docker.sock
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
networks:
- intranet
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`${PORTAINER_DOMAIN}`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
- "traefik.http.routers.portainer.service=portainer"
- "traefik.http.routers.portainer.tls.certresolver=leresolver"
keycloakdb:
image: postgres:16.2-alpine
container_name: keycloakdb
environment:
- POSTGRES_DB=${KEYCLOAK_DB_NAME}
- POSTGRES_USER=${KEYCLOAK_DB_USER}
- POSTGRES_PASSWORD=${KEYCLOAK_DB_PASSWORD}
- POSTGRES_ROOT_PASSWORD=${KEYCLOAK_DB_ROOT_PASSWORD}
networks:
- intranet
volumes:
- keycloakdata:/var/lib/postgresql/data
labels:
- "traefik.enable=false"
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: keycloak
hostname: keycloak
environment:
- KC_HOSTNAME_STRICT=false
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://keycloakdb/keycloak
- KC_DB_URL_PORT=5432
- KC_DB_USERNAME=${KEYCLOAK_DB_USER}
- KC_DB_PASSWORD=${KEYCLOAK_DB_PASSWORD}
- KC_DB_SCHEMA=public
- KC_LOG_LEVEL=info
- KC_FEATURES=docker
- KEYCLOAK_ADMIN=${KEYCLOAK_ADMIN}
- KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
- KC_PROXY=edge
networks:
- intranet
depends_on:
- traefik
- keycloakdb
volumes:
- ${KEYCLOAK_DIR}/themes:/opt/keycloak/themes
- ${KEYCLOAK_DIR}z/standalone/deployments:/opt/keycloak/standalone/deployments
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`${KEYCLOAK_DOMAIN}`)"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.routers.keycloak.tls.certresolver=letsencrypt"
entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev"]
Переменные окружения находятся в файле .env
, в нем необходимо изменить значения на свои доменные имена, данные для входа, пароли и т.д.:
TZ="Europe/Moscow"
ACME_EMAIL=mail@example.com
LETSENCRYPT_DIR="./data/letsencrypt"
SHELL_SSH_PORT=2222
TRAEFIK_DOMAIN=traefik.example.com
TRAEFIK_DIR="./data/traefik"
FORWARD_AUTH_SECRET=123456
FORWARD_AUTH_PROVIDER_URI=https://auth.example.com/realms/traefik
FORWARD_AUTH_CLIENT_ID=traefik
FORWARD_AUTH_CLIENT_SECRET=123456
FORWARD_AUTH_ENCRYPTION_KEY=123456
PORTAINER_DOMAIN=portainer.example.com
KEYCLOAK_DOMAIN=auth.example.com
KEYCLOAK_DB_NAME=keycloak
KEYCLOAK_DB_USER=keycloak
KEYCLOAK_DB_PASSWORD=password
KEYCLOAK_DB_ROOT_PASSWORD=password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_DIR="./data/keycloak"
Пошаговая настройка
- Создание сети Docker:
Для того чтобы Traefik мог взаимодействовать с другими контейнерами, создайте внешнюю сеть:
docker network create intranet
- Создание
docker-compose.yaml
и.env
файлов
Копируем приведенные выше файлы в директорию проекта и изменяем данные на валидные. Данные с префиксом FORWARD_AUTH
пока не трогаем, их нужно добавить после настройки авторизации.
-
Настройка доменных имен:
Убедитесь, что у вас настроены DNS записи для доменов
traefik.example.com
,portainer.example.com
иauth.example.com
, указывающие на ваш VPS сервер. Рекомендую подождать минут 15 после смены A записи доменов, чтобы настройки dns записей успели примениться. -
Запуск Docker Compose:
Перейдите в директорию проекта, где находится файл docker-compose.yaml
и выполните команду:
docker-compose up -d
На данном этапе должны уже успешно работать домены portainer.example.com
и auth.example.com
.
Перейдем к настройке авторизации.
Настройка авторизации через keycloak
Для создания нового пользовательского Realm, пользователя и клиента в Keycloak, следуйте пошаговой инструкции, основанной на официальной документации Keycloak.
Запуск Keycloak с использованием Docker Compose
Убедитесь, что ваш Keycloak контейнер запущен с использованием Docker Compose, как было описано ранее. Введите команду для запуска контейнера, если он еще не запущен:
docker-compose up -d
Доступ к Keycloak Admin Console
- Откройте браузер и перейдите на URL вашего Keycloak, например:
http://auth.example.com
. - Войдите в административную консоль, используя учетные данные администратора, которые вы задали в файле
.env
.
Создание нового пользовательского Realm
- После входа в административную консоль Keycloak, в верхнем левом углу выберите текущий Realm (обычно это "Master").
- Нажмите на "Add Realm" (Добавить Realm) в выпадающем меню.
- Заполните форму:
- Name: Введите имя для нового Realm, например,
myrealm
.
- Name: Введите имя для нового Realm, например,
- Нажмите "Create" (Создать).
Создание нового пользователя в Realm
- Перейдите в только что созданный Realm, выбрав его в верхнем левом углу.
- В левом меню выберите "Users" (Пользователи), затем нажмите "Add user" (Добавить пользователя).
- Заполните форму:
- Username: Введите имя пользователя, например,
newuser
. - Остальные поля можно оставить пустыми или заполнить по необходимости.
- Username: Введите имя пользователя, например,
- Нажмите "Save" (Сохранить).
- После создания пользователя вы будете перенаправлены на страницу профиля пользователя. Перейдите на вкладку "Credentials" (Учетные данные).
- Установите пароль для пользователя:
- Password: Введите пароль.
- Password confirmation: Подтвердите пароль.
- Снимите флажок "Temporary" (Временный), если вы хотите, чтобы пароль не требовал смены при первом входе.
- Нажмите "Set Password" (Установить пароль).
Добавление нового клиента
- В левом меню выберите "Clients" (Клиенты), затем нажмите "Create" (Создать).
- Заполните форму:
- Client ID: Введите идентификатор клиента, например,
myclient
. - Client Protocol: Выберите протокол (например,
openid-connect
).
- Client ID: Введите идентификатор клиента, например,
- Нажмите "Save" (Сохранить).
- На странице настроек клиента заполните следующие поля:
- Valid Redirect URIs: Введите URL вашего приложения, сейчас авторизацию использует только
traefik.example.com
так что добавляем его,https://traefik.example.com/*
. - Web origins: Вводим домены для которых разрешаются CORS запросы.
https://traefik.example.com
- Остальные поля можно настроить по вашему усмотрению.
- Valid Redirect URIs: Введите URL вашего приложения, сейчас авторизацию использует только
- Нажмите "Save" (Сохранить).
После этого заполняем переменные в файле .env
с префиксом FORWARD_AUTH
.
Перезапускаем контейнеры:
docker-compose down
docker-compose up -d
Заходим на сайт traefik.example.com
и проверяем что он доступен только после ввода логин пароля.
Проверка работы сервисов
После успешного запуска контейнеров, откройте браузер и перейдите по следующим адресам для проверки:
- Traefik:
http://traefik.example.com
- Portainer:
http://portainer.example.com
- Keycloak:
http://auth.example.com
Теперь рассмотрим пример, как добавить docker-compose
конфигурацию через Portainer, сайт которой будет открываться только после авторизации через keycloak.
Добавлению Stack в Portainer
Для примера развернем сервис dufs
- файловый менеджер доступный через веб-сервер, облегчающий доступ и управлению файлами через HTTP.
Для этого будем использовать следующую конфигурацию:
version: "3.8"
networks:
intranet:
external: true
services:
dufs:
image: sigoden/dufs
container_name: dufs
volumes:
- ${DATA_PATH}:/data
restart: unless-stopped
command: /data -A
env_file: stack.env
extra_hosts:
host.docker.internal: host-gateway
networks:
- intranet
environment:
USER_UID: 1000
USER_GID: 1002
labels:
- "traefik.enable=true"
- "traefik.http.routers.files.rule=Host(`files.example.com`)" # Домен по которому будет доступен сайт
- "traefik.http.services.files.loadbalancer.server.port=5000" # Порт на котором отдаётся веб сайт
- "traefik.http.routers.files.entrypoints=websecure"
- "traefik.http.routers.files.tls.certresolver=leresolver"
- "traefik.http.routers.files.middlewares=forwardauth" # Удалить если не нужно защитить доступ к сайту через авторизацию
Откройте браузер и перейдите на URL вашего Portainer, например: http://portainer.yourdomain.com
.
Войдите в систему с использованием ваших учетных данных администратора.
Добавление нового Stack
- На главной странице Portainer выберите вашу Docker окружение (например, локальный Docker).
- В левом меню выберите "Stacks".
- Нажмите на кнопку "Add stack" (Добавить стек).
Конфигурация Stack
- В поле "Name" введите имя для вашего Stack, например,
dufs
. - В поле "Web editor" вставьте Docker Compose конфигурацию, приведенную выше.
- В нижней части страницы вы увидите опцию "Environmental variables" (Переменные окружения). Нажмите на "Add environment variable" и добавьте переменную
DATA_PATH
со значением пути к директории в которой будут лежать файлы доступные через данный сервис.
Развертывание Stack
- Нажмите "Deploy the stack" (Развернуть стек).
- Portainer начнет развертывание Stack на вашем Docker окружении. Вы сможете отслеживать статус развертывания на странице Stack.
Проверка развертывания
После успешного развертывания Stack, откройте браузер и перейдите по адресу http://files.example.com
, чтобы убедиться, что сервис dufs
успешно запущен и доступен через Traefik, а также, что он доступен только после авторизации.