更好的反向代理工具 Traefik 配置入門——Docker 篇

前言

本人 Ghost 博客以及自建私有云 Nextcloud 均已經穩定運行了一年有餘,期間經歷過兩次服務器遷移和屢次版本升級,均未發生過數據丟失和其餘問題,在遷移過程當中也曾分享過如何將 Docker Volumes 安全的跨服務器遷移。目前博客和網盤服務分別搭建於兩臺服務器,並統一使用另外一臺服務器做反代。web

在反代軟件上,一直使用的都是 Nginx,最近在研究 K8s,也對比較流行的雲原生反向代理工具 Traefik 產生了興趣,因而便花了一點時間將反代服務器上的 Nginx 換成了 Traefik。必定要吐槽的一點是,Traefik 的官方文檔寫得實在太糟糕了,初次接觸的用戶,想經過只看官方文檔把事情搞定,那是至關困難,因此我以爲很是有必要分享一下,讓其餘初次使用的用戶避免踩坑。docker

目前 Traefik 已經更新到了 2.1 版本,其 2.x 版本和 1.x 版本差別較大,本文天然將基於最新版本,新版本增長的自動配置 HTTPS 功能和 TCP 代理功能都十分有價值。apache

主要目標

反向代理服務器的工做,天然就是截獲服務器流量,並將流量轉發至目標服務或地址,在這一基礎上,若是轉發的目標服務或地址是多個,就是負載均衡。Traefik 官方文檔中的圖片都是萌萌的,下面這張圖就比較清晰的說明了 Traefik 或者說通常反代軟件的功能:json

Traefik 功能

本人目前的需求比較簡單,就是未來自於pbeta.mewww.pbeta.me的訪問流量轉發至 博客IP:博客端口,未來自於cloud.pbeta.me的訪問流量轉發至 網盤IP:端口,另外,考慮到演示須要,本文將完成如下目標:api

  • 域名訪問 Traefik Dashboard
  • 將域名訪問轉發至 外部IP:端口
  • 將域名訪問轉發至 Docker 服務
  • 配置 HTTPS 訪問(使用 LetsEncrypt)
  • 使用 Middlewares 實現 HTTP 訪問自動跳轉 HTTPS
  • 使用 Middlewares 爲 Traefik Dashboard 增長密碼驗證

Traefik 配置說明

在安裝以前,我以爲有必要講一下 Traefik 這款工具的配置方式,搞清楚了基本的配置方式,在下面安裝和配置的過程當中纔能有清晰的思路,如下內容主要參考了官方文檔中的Configuration Introduction 頁面安全

配置類型

配置類型

簡單的說,Traefik 的配置包括靜態配置和動態配置兩種,前者是 Traefik 自身的配置,須要重啓才能生效,後者則能夠理解爲被代理服務的配置,能夠經過配置實現爲即時生效。bash

若是配置內容不存在服務差別性,那就能夠統一在靜態配置中,不然就須要在動態配置中爲每個服務中單獨添加,動態配置中的多個服務也能夠共用同一配置內容,好比能夠配置一個basicAuthMiddlewares,由多個服務共用。服務器

配置內容位置

不管靜態配置仍是動態配置,都有兩種配置方式:CLI 形式或者獨立文件形式。網絡

對於靜態配置,一個是 Traefik 鏡像啓動時的啓動參數,另外一個是單獨的配置文件(如 traefik.yml),官方文檔提供示例時,也同時包括這兩種形式,分別對應的是 CLIFile(YAML),不過在查看官方的配置示例時,你會發現還包括 File(TOML),TOML 文件是對 YAML 的一種改進,本文暫時都使用 YAML,讀者能夠自行轉換。另外,官方文檔中也說明了靜態配置能夠經過環境變量實現,本文暫不使用此方式。hexo

配置項的優先級爲配置文件 > 命令行參數 > 環境變量,請注意配置文件與命令行參數是互斥的,若是你選擇使用配置文件,就不能再使用命令行參數,最終的配置並不是兩者的疊加

而對於動態配置,能夠選擇直接在服務的 docker-compose 文件最下方經過 labels 實現,好比這樣:

# yaml
  whoami:
    # A container that exposes an API to show its IP address
    image: containous/whoami
    labels:
      - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
複製代碼

也能夠寫在單獨的配置文件中,這一文件在 Traefik 中通常命名爲 dynamic.yml,你能夠將全部路由配置寫在此文件中,爲了清晰起見,本文統一使用後一種方式。

配置加載方式

經過上面的說明,咱們能夠知道,在 Docker 中使用 Traefik 大體可能會涉及到三個文件:

  • docker-compose.yml
  • traefik.yml(可選)
  • dynamic.yml

再次重複一次,若是使用獨立配置文件來存放靜態配置,那麼 docker-compose.yml 文件中的 command 部分將不會生效。

有了以上的文件,如何加載呢?docker-compose.yml 中的配置天然沒必要考慮這個問題,對於其餘兩個文件的加載則必需要綁定至容器。

traefik.yml 文件的加載

當 Traefik 啓動時,將會在如下位置搜索配置文件,名稱能夠是 traefik.toml、traefik.yml 或者 traefik.yaml:

  • /etc/traefik/
  • $XDG_CONFIG_HOME/
  • $HOME/.config/
  • . (the working directory).

另外,能夠經過添加形式如 --configFile=foo/bar/myconfigfile.toml 的啓動參數覆蓋默認行爲。

根據官方文檔的以上描述,考慮到咱們是在 Docker 環境中運行 Traefik,因此建議將放置配置文件的目錄綁定到容器的 /etc/traefik 目錄便可。

dynamic.yml 文件的加載

動態配置文件的加載,則須要使用 Traefik 的 providers 啓動參數,形式以下:

  • CLI 形式
--providers.file.directory=/etc/traefik
--providers.file.filename=dynamic.yml
--providers.file.watch=true
複製代碼
  • 配置文件形式
providers:
 file:
 directory: "/etc/traefik"
 filename: "dynamic.yml"
 watch: true
複製代碼

這三行啓動參數的含義很是容易理解,在官方文檔中我沒有注意到有默認目錄,因此咱們必定須要指定目錄,指定配置文件名,最後一行的watch=true將開啓動態配置項的即時生效,再也不須要重啓容器。

traefik.yml 文件的加載相似,在 Docker 環境運行 Traefik 的狀況下,咱們須要將存放 dynamic.yml 的目錄綁定至容器的 /etc/traefik 目錄,這裏咱們使用了同一目錄,這樣就沒必要分別綁定兩個目錄了。

經過以上的描述,讀者應該基本明白了 Traefik 整個配置的運做方式,更加高級的用法能夠自行研究官方文檔。

Traefik 的安裝

咱們使用 Docker 安裝 Traefik,下文將以官方文檔中的相關示例爲模板,修正其錯誤,完成初步配置及安裝。

咱們使用的配置文件來自於官方文檔中Docker-compose with let's encrypt: TLS Challenge,內容以下:

version: "3.3"

services:

 traefik:
 image: "traefik:v2.0.0-rc3"
 container_name: "traefik"
 command:
      #- "--log.level=DEBUG"
 - "--api.insecure=true"
 - "--providers.docker=true"
 - "--providers.docker.exposedbydefault=false"
 - "--entrypoints.websecure.address=:443"
 - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      #- "--certificatesresolvers.mytlschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
 - "--certificatesresolvers.mytlschallenge.acme.email=postmaster@mydomain.com"
 - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
 ports:
 - "443:443"
 - "8080:8080"
 volumes:
 - "./letsencrypt:/letsencrypt"
 - "/var/run/docker.sock:/var/run/docker.sock:ro"

 whoami:
 image: "containous/whoami"
 container_name: "simple-service"
 labels:
 - "traefik.enable=true"
 - "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
 - "traefik.http.routers.whoami.entrypoints=websecure"
 - "traefik.http.routers.whoami.tls.certresolver=mytlschallenge"
複製代碼

官方文檔中的這一示例,使用了 CLI 配置靜態配置的方式,但其提供的這一示例主要存在如下幾個問題:

  • 缺乏 80 端口綁定,只能使用 HTTPS
  • 沒有開啓 Dashboard
  • 沒有綁定動態配置文件目錄

下面咱們對其針對性的修改,另外爲了清晰,咱們將示例中提供的 whoami 應用在後面做爲單獨應用運行。

根據前文對 Traefik 配置方式的講解,咱們知道咱們既能夠選擇在 docker-compose.yml 文件中配置,也可使用獨立的 traefik.yml 配置,本人建議使用獨立的 traefik.yml 配置,下文也只提供這一種方式,熟悉了之後,讀者能夠自行切換。

靜態配置裏能夠有哪些內容,能夠參考官方文檔

咱們的配置文件內容以下:

  • docker-compose.yml 文件
version: "3.3"
services:
 traefik:
 image: "traefik:latest" # 咱們直接部署最新版本,可自行調整
 container_name: "traefik"
 ports:
 - "80:80"
 - "443:443"
 - "8080:8080"
 volumes:
      # 自動申請的證書存放位置,咱們須要在當前目錄建立 letsencrypt 目錄
 - "./letsencrypt:/letsencrypt"
 - "/var/run/docker.sock:/var/run/docker.sock:ro"
      # 綁定咱們的配置目錄
 - "./config:/etc/traefik"
複製代碼
  • ./config/traefik.yml 文件
providers:
 docker: {}
 file:
 directory: "/etc/traefik"
 filename: "dynamic.yml"
 watch: true

log:
 level: DEBUG

entryPoints:
 web:
 address: ":80"
 websecure:
 address: ":443"

certificatesResolvers:
 mytlschallenge:
 acme:
 email:  "yourname@domain"
 storage:  "/letsencrypt/acme.json"
 tlsChallenge: {}

api:
 dashboard: true
複製代碼
  • ./config/dynamic.yml 文件

因爲咱們暫時不轉發任何服務,此文件暫時爲空。

啓動咱們的 docker-compose.yml 文件:

docker-compose up -d
複製代碼

稍等片刻,就能看到服務建立成功,使用 docker ps 查看,能夠看到容器正常運行。

traefik-docker-1.png

配置域名訪問 Traefik Dashboard

Traefik 的 Dashboard 其實是其 API 功能的圖形化展現,因此咱們須要開啓配置中的 API 參數和 Dashboard 參數,參考官方文檔:

If you enable the API, a new special service named api@internal is created and can then be referenced in a router. And then define a routing configuration on Traefik itself with the dynamic configuration.

若是開啓了 api,就會建立一個叫作 api@internal 的服務,咱們上面所使用的 traefik.yml 配置中,已經打開了 api,也打開了 dashboard

官方配置中有一行 - "--api.insecure=true" 被咱們無視掉了,有興趣的朋友閱讀這裏

而後按官方文檔說明,爲 Traefik 自身添加這一動態路由,因此咱們修改 dynamic.yml 文件:

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.domain.com`)"
 service: api@internal
複製代碼

Rule 的配置除了 Host 還有 PathPathPrefix 等,配置邏輯運算符來實現配置,具體能夠查看這裏。另外,記得將域名提早解析到服務器 IP,若是是本地虛擬機測試,請自行修改 hosts 文件。

以後從新運行 docker-compose up -d 便可,此時訪問綁定的域名就能看到 Traefik 的 Dashboard 了:

traefik-docker-2-dashboard.png
traefik-docker-3-dashboard2.png

配置域名轉發至 Docker 服務

當咱們啓動一個 Docker 服務後,Traefik 就能發現服務併爲其建立默認路由,須要注意的一點是,若是 Traefik 要可以與新的 Docker 服務進行通訊,必須將其加入到同一網絡,咱們使用 docker-compose.yml 啓動 Traefik 時,就建立了 traefik_default 網絡,新加入的容器須要手動加入該網絡。

加入網絡的方式不少,能夠自行搜索,使用 docker run 時經過 --network traefik_default,或者使用docker-compose.yml 時添加:

networks:
 - traefik_default
複製代碼

下面咱們使用 Traefik 提供的 whoami 應用進行測試,使用 docker run 啓動應用:

docker run -d -P --name whoami --net traefik_default containous/whoami
複製代碼

以後咱們查看 Dashboard,就會發現 Traefik 已經自動發現了服務,服務名爲 whoami@docker 。下面咱們爲該服務配置路由,打開咱們的 ./config/dynamic.yml 文件,在下方增長 whoami 路由配置內容:

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 whoami:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
 service: whoami@docker
複製代碼

配置完成並保存後,在 Dashboard 就能夠看到新添加的路由了,此時咱們訪問 http://traefik.pbeta.cn/whoami 就能訪問到咱們的服務了:

traefik-docker-4.png

你會發現咱們並無告訴要轉發服務的哪一個端口,這是由於 Traefik 智能的進行了處理,若是服務只暴露一個端口,Traefik 就會自動選擇該端口,但若是服務暴露多個端口,你必須手動指定,具體請參考官方文檔。

配置轉發外部服務

咱們使用本機來模擬這一需求,在咱們的服務器上運行一個 Ghost 服務,可是並不加入 traefik_default 網絡,而是在 Traefik 中經過 服務器互聯網IP : 服務端口 來轉發。

啓動一個 Ghost 容器:

docker run -d -p 2368:2368 --name ghost  ghost
複製代碼

咱們暴露了服務器的 2368 端口,此時直接在外部訪問 服務器IP:端口 就能訪問到網站(若是你防火牆開放了 2368 端口的話)

traefik-docker-5.png

下面咱們就配置 Traefik 將 traefik.pbeta.cn/ghost 轉發至該地址,繼續修改咱們的 dynamic.yml ,分別添加一個服務和一個路由,完整的 dynamic.yml 文件以下:

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 whoami:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
 service: whoami@docker
 ghost:
 entryPoints:
 - "web"
 rule: "Host(`ghost.pbeta.cn`) "
 service: ghost

 services:
 ghost:
 loadBalancer:
 servers:
 - url: "http://47.74.154.202:2368/"
複製代碼

保存以後訪問 http://ghost.pbeta.cn 便可打開咱們剛啓動的 Ghost 網站:

traefik-docker-6.png

配置 HTTPS 訪問

前面咱們一直使用的是 web 這一入口,經過簡單配置便可實現 HTTPS 訪問服務。官方文相關頁面有較多示例,可供參考。本文將只講解使用 Let's Encrypt 自動配置 HTTPS。

前面提供的 traefik.yml 中已經包括了相關的配置:

certificatesResolvers:
 mytlschallenge:
 acme:
 email:  "yourname@domain"
 storage:  "/letsencrypt/acme.json"
 tlsChallenge: {}
複製代碼

咱們爲這一 certificatesRsolver 命名爲了 mytlschallenge,以後在路由配置中添加 tls 部分使用該 resolver 便可,因此咱們打開 dynamic.yml,進行編輯,爲服務額外添加一條路由配置:

api-tls:
  entryPoints:
    - "websecure"
  rule: "Host(`traefik.pbeta.cn`)"
  service: api@internal
  tls:
    certResolver: "mytlschallenge"
複製代碼

保存以後稍等片刻,就已經配置好了 HTTPS:

traefik-docker-7.png

能夠在多個網站使用同一個 certificatesResolver,但爲了不衝突,支持經過 option 來進行區分,一個完整的配置示例以下:

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 api-tls:
 entryPoints:
 - "websecure"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 tls:
 certResolver: "mytlschallenge"
 options: traefik

 whoami:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
 service: whoami@docker
 whoami-tls:
 entryPoints:
 - "websecure"
 rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
 service: whoami@docker
 tls:
 certResolver: "mytlschallenge"
 options: traefik
 ghost:
 entryPoints:
 - "web"
 rule: "Host(`ghost.pbeta.cn`)"
 service: ghost
 ghost-tls:
 entryPoints:
 - "websecure"
 rule: "Host(`ghost.pbeta.cn`)"
 service: ghost
 tls:
 certResolver: "mytlschallenge"
 options: ghost

 services:
 ghost:
 loadBalancer:
 servers:
 - url: "http://47.74.154.202:2368/"
tls:
 options:
 traefik: {}
 ghost: {}
複製代碼

這樣咱們的三個路由的 HTTPS 訪問都就配置好了,你能夠注意到咱們使用了2個 options,由於 traefik.pbeta.cnghost.pbeta.cn 須要不一樣的證書,因此必須以此進行區分,options 中也能夠自行根據文檔說明自行添加配置內容。

使用 Middlewares

Traefik 提供了很是多的中間件,這些中間件能實現你須要的大部分功能,這裏分別以簡單權限認證和 HTTP 自動跳轉 HTTPS 爲例進行介紹。

爲了不配置文件過長,下面只以 Dashboard 進行示例。

實現自動跳轉 HTTPS

Middlewares 的使用很是簡單,在動態配置文件中的 http (或tcp)下添加 middlewares,以後在每個路由中使用便可,咱們添加一個自動跳轉 HTTPS 的 Middleware:

 middlewares:
 redirect:
 redirectScheme:
 scheme: https
複製代碼

以後咱們在以 web 爲入口的服務添加這一 middleware

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 middlewares:
 - redirect
 api-tls:
 entryPoints:
 - "websecure"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 tls:
 certResolver: "mytlschallenge"
 options: traefik

 middlewares:
 redirect:
 redirectScheme:
 scheme: https

tls:
 options:
 traefik: {}
複製代碼

保存文件以後,訪問 http://traefik.pbeta.cn 將會自動跳轉至 https://traefik.pbeta.cn

實現密碼認證

這裏須要使用的中間件是 BasicAuth,具體的使用方式能夠參閱官方文檔

在 K8s 中咱們使用 Secret 資源來管理密碼等,但在 Docker 中沒法經過這種方式,須要使用 users 或者 usersFile 參數管理密碼,這裏咱們使用 users

密碼的建立須要藉助於 htpasswd,安裝方式以下:

  • CentOS
yum -y install httpd-tools
複製代碼
  • Ubuntu
apt install apache2-utils
複製代碼

咱們使用 htpasswd 命令來生成一用戶名爲test 密碼 爲test1234 的密碼對:

echo $(htpasswd -nb test test1234)
複製代碼

獲得的信息是 test:$apr1$k.xiBHjv$VdavfNvly69vNZvkKpB2j0

須要注意一點,因爲咱們是在單獨配置文件中使用,因此無須進行轉義,若是是以CLI 形式使用的話,全部 $ 符號必須寫兩次,可使用官方文檔中的命令來生成轉義後的密碼 echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g

下面咱們就使用此密碼對來建立咱們的 BasicAuth Middleware,打開 dynamic.yml 文件進行修改,在上面建立的 redirect 下添加以下內容:

test-auth:
 basicAuth:
 users:
 - "test:$apr1$k.xiBHjv$VdavfNvly69vNZvkKpB2j0"
複製代碼

以後在前面的路由下的 middlewares 下添加 test-auth,完整的 dynamic.yml 文件以下:

http:
 routers:
 api:
 entryPoints:
 - "web"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 middlewares:
 - redirect
 - test-auth
 api-tls:
 entryPoints:
 - "websecure"
 rule: "Host(`traefik.pbeta.cn`)"
 service: api@internal
 middlewares:
 - test-auth
 tls:
 certResolver: "mytlschallenge"
 options: traefik

 middlewares:
 redirect:
 redirectScheme:
 scheme: https
 test-auth:
 basicAuth:
 users:
 - "test:$apr1$k.xiBHjv$VdavfNvly69vNZvkKpB2j0"
tls:
 options:
 traefik: {}
複製代碼

保存文件後,再次訪問 http://traefik.pbeta.cn,就會發現須要密碼登陸了:

traefik-docker-9.png

輸入密碼後便可正常訪問。

總結

經過以上內容,咱們對 Traefik 的配置方式有了比較深刻的瞭解,並實現了咱們的目標,Traefik 的官方文檔雖然雜亂,但不失詳盡,有了本文提供的基礎知識,再結合官方文檔,使用 Traefik 實現各類高級功能應該並不是難事。

在配置過程當中遇到任何問題,能夠在評論區中提問,本人將盡力協助。

參考文檔

  1. Traefik 官方文檔
  2. What api.insecure do exactly?
  3. Comparison of Traefik 1.7 and Traefik 2.0 with Docker
相關文章
相關標籤/搜索