本项目的目的是建立一个Docker应用管理框架,可以方便地对Docker应用进行管理、监控和部署。经过广泛调研,本项目采用广为流行的Traefik反向代理工具,结合Adminer数据库管理工具、Portainer容器管理工具和Let’s Encrypt证书管理工具,构建了一个开箱即用的Docker应用工具集。源代码可以在我的GitHub上下载

关于Treafik

Traefik与 Nginx 一样,是一款优秀的反向代理工具,或者叫 Edge Router。与Nginx不同的是,Traefik专为Docker应用设计,管理和配置非常方便,广受欢迎。其主要特点包括:

  • 轻量设计,对Docker应用的支持非常友好,更新配置后无需重启
  • 实现服务自动发现与负载均衡
  • 已在全球范围内用户生产环境,并经过了严格的测试和基准测试
  • 自带仪表盘面板
  • 方面和第三方Docker监控应用如Prometheus集成,提供metrics支持
  • 与Docker Swarm和k8s等容易集成

本项目着重Treafik的具体应用而非Treafik本身的介绍,Treafik的详细介绍请参考官网

前提

  • 有自己的VPS并安装了Docker Engine、docker-compose和git工具
  • 将用到的域名和子域名指向了上述VPS的IP地址

项目包含的组件和工具

Traefik is a popular Docker-aware reverse proxy. One of its important feature is the integration with Let’s Encryption functionality, which enables automatically generate and renew ssl certificates for Docker applications under its management.

In this project, we will set up a basic Docker app management environment using Traefik together with commonly used tools including Adminer and Portainer.

Adminer (formerly known as PHPMyAdmin) is a tool for managing content in MySQL databases. It is a light-weight database managing tool. Adminer also supports SQLite, PostgreSQL, Oracle, etc. and it is distributed under GPL v2 license in the form of a single PHP file (around 430KB in size). Adminer development priorities are: 1. Security, 2. User experience, 3. Performance, 4. Feature set, 5. Size. Here are some discussions on why Adminer is better than phpMyAdmin.

Portainer is a lightweight open-source management UI which allows you to easily manage your different Docker environments.Portainer makes it easier for you to manage your Docker containers, it allows you to manage containers, images, networks, and volumes from the web-based Portainer dashboard.

Let’s Encrypt enables HTTPS on your websites by using ACME protocol to generate and renew SSL certificates for you.

Cats Demo shows you some interesting cats gif pictures as a demo of how to deploy a Docker app.

项目特点

  • 可以用于生产环境,用来方便地部署、管理和监控Docker应用
  • 为你的Docker应用自动申请和更新SSL证书,再也不用为使用HTTPS烦恼了
  • 将自动重定向HTTP请求到HTTPS
  • 为 Traefik和Adminer仪表盘设置了基本的访问控制(用配置的用户名和密码进行访问)
  • 通过Adminer控制面板管理数据库
  • 通过Portainer控制面板管理Docker应用。还可以使用Portainer的应用模板快速部署新的Docker应用,例如Wordpress, Redmine, Jenkins, Magento 2, 等等
  • 提供了一个叫 cats 的测试APP,会播放猫的gif动画

定制项目参数

项目使用 .env 文件设置各项参数. docker-compose 命令会读取 .env 文件并设置相关环境变量, 这些变量将用在 docker-compose.yml 文件中.

项目提供了一个 .env 的模板文件 .env_template. 最终的 .env 文件看起来是这个样子的:

APP_DOMAIN=example.com
ACME_EMAIL=alphacodinghub@example.com

ACME_EMAIL 是你的 email 地址,Let’s Encrypt 在产生和更新SSL证书时需要用到.

基本的访问控制(用户名和密码)是在文件 middlewares.yml 中(位于 ./config/traefik/config). 缺省的 user/passwordadmin/password. 密码可以用htppassd生成,也可以在线生成:https://hostingcanada.org/htpasswd-generator/

快速部署

以root身份登录VPS, 然后将本项目克隆到某文件夹,例如 /app; 根据 .env_template 生成一个 .env 文件,就可以运行项目了(注意:需要将用到的域名和子域名提前指向VPS,不然的话Let’s Encrypt在申请SSL证书时会失败):

- mkdir /app
- cd /app
- git clone https://github.com/alphacodinghub/traefik-docker-manager.git
- cd traefik-docker-manager
- cp .env_template .env
- vim .env
- # set your APP_DOMAIN and ACMS_EMAIL, and save and quit the .env file
- docker network create backend
- docker network create web
- docker-compose up -d

访问仪表盘

注意将下面的 example.com 替换成你自己的域名

Access to Traefik dashboard: https://traefik.example.com

Access to Aminer dashboard: https://adminer.example.com

Access to Portainer dashboard: https://portainer.example.com

Access to cats demo: https://cats.example.com

上述访问Adminer面板时还没有数据库可以管理。感兴趣的话可以用Traefik一键安装Wordpress(参照:https://github.com/alphacodinghub/wordpress-fpm ),然后就可以进行数据库管理了。

附:docker-compose.yml 文件

version: '3.7'
############################################################
####        Tools to manage all services on the host   #####
############################################################
services:
  ################################################
  ####        Traefik Proxy Setup           #####
  ###############################################
  traefik:
    image: traefik:v2.2.1
    restart: always
    container_name: traefik
    ports:
      - '80:80' # <== http
      - '443:443' # <== https
    command:
      #### Traefik CLI commands to configure Traefik! ####
      ## API Settings - https://docs.traefik.io/operations/api/, endpoints - https://docs.traefik.io/operations/api/#endpoints ##
      - --api.insecure=false # <== DisEnabling insecure api. Default is ture.
      - --api.dashboard=true # <== Enabling the dashboard to view services, middlewares, routers, etc...
      - --api.debug=true # <== Enabling additional endpoints for debugging and profiling
      ## Log Settings (options: ERROR, DEBUG, PANIC, FATAL, WARN, INFO) - https://docs.traefik.io/observability/logs/ ##
      - --log.level=WARN # <== Setting the level of the logs from traefik
      ## Provider Settings - https://docs.traefik.io/providers/docker/#provider-configuration ##
      - --providers.docker=true # <== Enabling docker as the provider for traefik
      - --providers.docker.exposedbydefault=false # <== Don't expose every container to traefik, only expose enabled ones
      ## file provider for auth middleware and canary loadbalance
      - --providers.file.directory=/config/
      - --providers.file.watch
      #- --providers.file.filename=/dynamic.yaml # <== Referring to a dynamic configuration file
      - --providers.docker.network=web # <== Operate on the docker network named web on frontend
      ## Entrypoints Settings - https://docs.traefik.io/routing/entrypoints/#configuration ##
      - --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named web
      - --entrypoints.web-secured.address=:443 # <== Defining an entrypoint for https on port :443 named web-secured
      ## Certificate Settings (Let's Encrypt) -  https://docs.traefik.io/https/acme/#configuration-examples ##
      - --certificatesresolvers.mytlschallenge.acme.tlschallenge=true # <== Enable TLS-ALPN-01 to generate and renew ACME certs
      - --certificatesresolvers.mytlschallenge.acme.email=${ACME_EMAIL} # <== Setting email for certs
      - --certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json # <== Defining acme file to store cert information
    volumes:
      - ./config/traefik/config:/config # <== folder for file provider
      - ./config/traefik/letsencrypt:/letsencrypt # <== Volume for certs (TLS)
      - /var/run/docker.sock:/var/run/docker.sock # <== Volume for docker admin
      #- ./dynamic.yaml:/dynamic.yaml # <== Volume for dynamic conf file, corresponding to providers.file.filename
    networks:
      - web # <== Placing traefik on the network named web
    labels:
      #### Labels define the behavior and rules of the traefik proxy for this container ####
      traefik.enable: true # <== Enable traefik on itself to view dashboard and assign subdomain to view it

      #redirecting ALL HTTP to HTTPS
      traefik.http.routers.http_catchall.rule: hostregexp(`{host:.*}`)
      traefik.http.routers.http_catchall.entryPoints: web
      traefik.http.routers.http_catchall.middlewares: redirect_https # <== apply redirect_https middleware which is defined in the below

      #dashboard
      traefik.http.routers.traefik.rule: Host(`traefik.${APP_DOMAIN}`) # <== Setting the domain for the dashboard
      traefik.http.routers.traefik.entryPoints: web-secured
      traefik.http.routers.traefik.tls: true
      traefik.http.routers.traefik.tls.certresolver: mytlschallenge
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.middlewares: auth@file

      #to define middlewares
      traefik.http.middlewares.redirect_https.redirectscheme.scheme: https # <== define a https redirection middleware

  ################################################
  ####         portainer - docker mngt      #####
  ##############################################
  portainer: # <== we aren't going to open :80 here because traefik is going to serve this on entrypoint 'web'
    ## :80 is already exposed from within the container ##
    image: portainer/portainer
    restart: always
    container_name: portainer
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./run/portainer:/data # persist portainer data.
    networks:
      - web
      - backend
    labels:
      #### Labels define the behavior and rules of the traefik proxy for this container ####
      traefik.enable: true # <== Enable traefik to proxy this container

      traefik.http.routers.portainer.rule: Host(`portainer.${APP_DOMAIN}`) # <== Your Domain Name for the https rule
      traefik.http.routers.portainer.entrypoints: web-secured # <== Defining entrypoint for https, **ref: line 31
      traefik.http.routers.portainer.tls.certresolver: mytlschallenge # <== Defining certsresolvers for https

  ################################################
  ####         adminer on traefik        #####
  ##############################################
  # Navigate to http://example.com/adminer/ to manage your MySQL DB.
  # more info - official: https://github.com/vrana/adminer/
  # unofficial src files: https://github.com/dehy/docker-adminer
  # More info: https://www.adminer.org/
  # Dockerfile: https://github.com/TimWolla/docker-adminer/blob/0e4ae464e25d44f23e476a398a13bfd7704dc7d0/4/Dockerfile
  db-adminer:
    # https://hub.docker.com/r/dehy/adminer
    image: dehy/adminer
    container_name: adminer
    restart: always
    networks:
      - backend
      - web
    labels:
      traefik.enable: true # <== Enable traefik to proxy this container

      traefik.http.routers.db-adminer.rule: Host(`adminer.${APP_DOMAIN}`) # && PathPrefix(`/adminer/`)  # <== Your Domain Name for the https rule
      traefik.http.routers.db-adminer.entrypoints: web-secured # <== Defining entrypoint for https
      traefik.http.routers.db-adminer.tls.certresolver: mytlschallenge # <== Defining certsresolvers for https
      traefik.http.routers.db-adminer.middlewares: auth@file

  ################################################
  ####         an example - cats        #####
  ##############################################
  cats-demo:
    image: mikesir87/cats
    container_name: cats
    restart: always
    networks:
      - web
    labels:
      traefik.enable: true # <== Enable traefik to proxy this container

      traefik.http.routers.cats.rule: Host(`cats.${APP_DOMAIN}`)
      traefik.http.routers.cats.entrypoints: web-secured # <== Defining entrypoint for https
      traefik.http.routers.cats.tls.certresolver: mytlschallenge # <== Defining certsresolvers for https

networks:
  web:
    external: true
  backend:
    external: true