在 Traefik Proxy 2.5 中使用/開發私有外掛(Traefik 官方部落格)

2022-06-08 09:00:59

Traefik Proxy 在設計上是一個模組化路由器,允許您將中介軟體放入您的路由中,並在請求到達預期的後端服務目的地之前對其進行修改。 Traefik 內建了許多這樣的中介軟體,還允許您以外掛的形式載入自己的中介軟體。

查詢和安裝中介軟體外掛的最簡單方法是通過 Traefik PilotTraefik Pilot 是一個軟體即服務 (SaaS) 平臺,它為您的所有 Traefik 代理範例提供全球指標和警報系統,並具有免費使用的內建外掛商店。 在商店內,您可以瀏覽所有可用的開源外掛,然後單擊按鈕進行安裝。

隨著 Traefik Proxy v2.5 的釋出,有一種新方法可以直接從本地儲存載入外掛(並且無需啟用 Traefik Pilot)。 只需將您的外掛原始碼放入一個名為 /plugins-local 的新目錄中。(您將相對於當前工作目錄 [從您呼叫 traefik 的位置] 建立此目錄,如果您使用的是 traefik docker 映像,則入口點始終是根目錄 /。) Traefik Proxy 本身將負責構建(解釋 ) 你的外掛,所以你所要做的就是編寫原始碼,並在正確的目錄中提供它以便 Traefik Proxy 載入它。外掛每次啟動僅載入一次(即,每次您希望重新載入外掛原始碼時都必須重新啟動 traefik)。

在以下場景中,您將找到使用 Traefik Proxy v2.5 編寫自己的 Docker 容器映象並將外掛原始碼捆綁到該映象的 /plugins-local 目錄中的範例。在使用 Docker 在開發環境中測試您的外掛之後(並且可能在為其建立持續整合構建之後),您可以將此映象推播到容器 registry,並在生產 Docker 伺服器和/或 Kubernetes 叢集中參照此映象。您可以將映象保密,也可以將其釋出並在任何地方共用您的外掛。

構建 Traefik Proxy 容器映象並捆綁 demo 外掛

這是一個範例 Dockerfile,它重新混合了標準 traefik:v2.5 docker 映像,並新增了一個從可設定的 git 儲存庫自動克隆的外掛。

在某個地方建立一個臨時目錄,並在其中建立一個名為 Dockerfile.demo 的新檔案:

# Dockerfile.demo - Example for Traefik Proxy and a demo plugin from git:
FROM alpine:3
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ARG PLUGIN_GIT_REPO=https://github.com/traefik/plugindemo.git
ARG PLUGIN_GIT_BRANCH=master
RUN apk add --update git && \
    git clone ${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE} \
      --depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH}

FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-local

預設構建引數載入 Traefik Labs 釋出的範例外掛 demo,它本質上是內建 headers.customRequestHeaders 中介軟體的克隆,但作為外掛。

在與 Dockerfile.demo 相同的目錄中,構建映象:

docker build -f Dockerfile.demo --tag traefik-with-demo-plugin .

您現在剛剛構建了一個 docker 映象,其中包含 Traefik v2.5 和演示外掛。您現在可以執行映象來測試它:

docker run --rm -it traefik-with-demo-plugin \
  --log.level=DEBUG \
  --experimental.localPlugins.demo.moduleName=github.com/traefik/plugindemo

紀錄檔將列印顯示外掛已載入且 Traefik 代理將執行的設定。
你可以肯定地測試一下,按 Ctrl-C 停止容器,然後重新執行將 moduleName= 更改為 github.com/something/different 的命令,你會得到一個錯誤,說它不存在並立即退出。

使用您的自定義外掛構建 Traefik Proxy 容器映象

要建立您自己設計的新外掛,請分叉此演示儲存庫。(要直接在 GitHub 上執行此操作,您可以單擊標有 Use this template 的綠色按鈕,或者您可以將儲存庫克隆到另一臺伺服器)。 您可以選擇將此新儲存庫設為公共或私有,但說明會有所不同,具體取決於它是否需要身份驗證才能克隆它,因此將分別介紹每種情況。

將您的分叉儲存庫克隆到您的工作站,並閱讀 readme.md 檔案中的開發說明。 建立您的外掛程式碼,更新 .traefik.yml 中的 import 行以匹配您的儲存庫名稱,將更改提交到 git,然後將更改推播回您的 git 伺服器 (GitHub)。 如果您只想測試範例外掛程式碼,則無需提交任何更改。 此外,Traefik 不需要編譯外掛原始碼:外掛通過原始原始碼載入,並在執行時由 Yaegi 解釋。

從公共儲存庫構建映象

如果您將儲存庫公開,則構建映象很容易。開啟您的 shell 終端,並建立這些臨時環境變數以用作構建引數:

## Create temporary variables for your plugin and git repository details:
## Optionally save these to build-env.sh and run "source build-env.sh" after.
export DOCKER_IMAGE=traefik-with-my-plugin
export PLUGIN_MODULE=github.com/YOUR_NAME/YOUR_REPOSITORY
export PLUGIN_GIT_REPO=https://github.com/YOUR_NAME/YOUR_REPOSITORY.git
export PLUGIN_GIT_BRANCH=master

更改這些變數以適合您的分叉外掛儲存庫:

  • DOCKER_IMAGE 是你的新 Docker 映象的 tag,它將捆綁 Traefik 和你的外掛程式碼。
  • PLUGIN_MODULE 是外掛的 Go 模組的名稱(例如 github.com/traefik/plugindemo)。 使用您自己的伺服器、組織和分叉儲存庫名稱。
  • PLUGIN_GIT_REPO 是外掛儲存庫中心的完整 git clone URL。 (此範例假設使用了公共儲存庫,並且不需要身份驗證,否則請參閱下一節。)
  • PLUGIN_GIT_BRANCH 是您希望克隆和安裝的 git 分支名稱。

在克隆儲存庫的根目錄中,建立一個名為 Dockerfile.public 的新檔案:

## Dockerfile.public - Bundle a Traefik plugin from a public git repository
FROM alpine:3
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ARG PLUGIN_GIT_REPO=https://github.com/traefik/plugindemo.git
ARG PLUGIN_GIT_BRANCH=master
RUN apk update && \
    apk add git && \
    git clone ${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE} \
      --depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH}

FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-local

構建並標記映象,從環境中傳遞引數:

docker build -f Dockerfile.public \
  --tag ${DOCKER_IMAGE} \
  --build-arg PLUGIN_MODULE \
  --build-arg PLUGIN_GIT_REPO \
  --build-arg PLUGIN_GIT_BRANCH .

從私有 git 儲存庫構建映象

從私有 git 儲存庫構建映象更具挑戰性,因為您需要將 SSH 憑據傳遞到 Docker 構建過程,以便按照 Dockerfile 中的指令碼從私有 git 儲存庫進行克隆。

您需要將 Docker 安裝更新到版本 >=18.09,這允許在 docker 映象構建過程中載入與 ssh-agent 通訊和臨時使用工作站使用者帳戶的 SSH 金鑰所需的實驗性 BuildKit 增強功能

在你的 shell 中設定這些環境變數:

## Optionally save these to build-env.sh and run "source build-env.sh" after.
## Docker BuildKit is required for ssh-agent forwarding:
export DOCKER_BUILDKIT=1
## Edit these variables for your plugin and git repository:
export DOCKER_IMAGE=traefik-with-my-plugin
export PLUGIN_MODULE=github.com/YOUR_NAME/YOUR_REPOSITORY
export [email protected]:YOUR_NAME/YOUR_REPOSITORY.git
export PLUGIN_GIT_BRANCH=master

主機 ssh-agent 直通需要修改 Dockerfile。建立一個名為 Dockerfile.private 的新檔案:

# syntax=docker/dockerfile:1.0.0-experimental
# The above line is required to turn on experimental BuildKit features.
# Dockerfile.private - Build Traefik and plugin from a private git repository.
# Loads SSH keys from the host `ssh-agent` to allow git clone.
FROM alpine:3

# Clone your plugin git repositories:
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ARG [email protected]:traefik/plugindemo.git
ARG PLUGIN_GIT_BRANCH=master
RUN apk add --update git openssh && \
    mkdir -m 700 /root/.ssh && \
    touch -m 600 /root/.ssh/known_hosts && \
    ssh-keyscan github.com > /root/.ssh/known_hosts
RUN --mount=type=ssh git clone \
    --depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH} \
    ${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE} 
    
FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-local

使用額外的 --ssh default 選項構建映象。這將通過連線到執行 ssh-agent 的主機連線到構建過程,以便您可以在構建過程中使用 SSH 金鑰,並克隆私有 git 儲存庫:

docker build -f Dockerfile.private \
  --ssh default --tag ${DOCKER_IMAGE} \
  --build-arg PLUGIN_MODULE \
  --build-arg PLUGIN_GIT_REPO \
  --build-arg PLUGIN_GIT_BRANCH .

注意:由於 docker-compose 中存在一個未解決的問題,您目前無法在 docker-compose 中使用 --ssh 引數(並且與 ssh-agent 的連線將失敗),因此如果您想使用此修改後的 Dockerfile 以及 docker-compose,您必須首先使用上面列出的 docker build 命令手動構建容器映像。 如果您首先以這種方式構建映像,則 docker-compose 可以依賴構建快取或顯式映象名稱,而無需再次構建它。

使用 docker-compose 作為外掛開發環境

你可以使用 docker-compose 作為一個簡單的外掛開發環境。

將您的外掛儲存庫克隆到您的工作站,然後將這些新檔案建立到儲存庫的根目錄中:

建立 Dockerfile

FROM traefik:v2.5
## Default module name (put your setting in .env to override)
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ADD . /plugins-local/src/${PLUGIN_MODULE}

建立 .env 設定檔案:

## Traefik Proxy local plugin .env file
## Configure your plugin name:
PLUGIN_NAME=demo
## Configure your module namespace:
PLUGIN_MODULE=github.com/traefik/plugindemo
## Configure whoami domain name for route testing:
WHOAMI_TRAEFIK_HOST=whoami.example.com
## Configure Email address for Let's Encrypt:
## Uncomment and configure this for production only:
# [email protected]

建立 docker-compose.yaml

# docker-compose.yaml for Traefik Proxy local plugin development
version: "3.3" networks: traefik-proxy: volumes: traefik-proxy:
  
services:
  traefik-proxy:
    build:
      context: .
      args:
        PLUGIN_MODULE: ${PLUGIN_MODULE}
    restart: unless-stopped
    networks:
    - traefik-proxy
    security_opt:
    - no-new-privileges:true
    command:
    #- "--log.level=DEBUG"
    - "--providers.docker=true"
    - "--providers.docker.exposedbydefault=false"
    - "--providers.docker.network=traefik-proxy"
    ## Entrypoints:
    - "--entrypoints.web.address=:80"
    - "--entrypoints.websecure.address=:443"
    - "--entrypoints.traefik.address=:9000"
    ## Automatically redirect HTTP to HTTPS
    - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
    ## ACME TLS config:
    - "--certificatesresolvers.default.acme.storage=/data/acme.json"
    ## Uncomment for production TLS certificates (Let's Encrypt):
    # - "--certificatesresolvers.default.acme.tlschallenge=true"
    # - "--certificatesresolvers.default.acme.caserver=https://acme-v02.api.letsencrypt.org/directory"
    # - "--certificatesresolvers.default.acme.email=${ACME_CA_EMAIL}"
    ## Enable Dashboard available only from the docker localhost:9000
    - "--api.dashboard=true"
    - "--api.insecure=true"
    ## Enable local plugins:
    - "--experimental.localPlugins.${PLUGIN_NAME}.moduleName=${PLUGIN_MODULE}"
    ports:
    - "80:80"
    - "443:443"
    - "127.0.0.1:9000:9000"
    volumes:
    - "traefik-proxy:/data"
    - "/var/run/docker.sock:/var/run/docker.sock:ro"

  ## The whoami container will run the demo plugin for testing purposes:
  whoami:
    image: traefik/whoami
    networks:
      - traefik-proxy
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`${WHOAMI_TRAEFIK_HOST}`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      # Configure the plugin as a new middleware:
      - "traefik.http.routers.whoami.middlewares=whoami-demo"
      # Add a test header to all incoming requests:
      # (the presense of this header in whoami response shows if the plugin works:)
      - "traefik.http.middlewares.whoami-demo.plugin.${PLUGIN_NAME}.headers.DoesPluginWork=YES"
      - "traefik.http.routers.whoami.tls.certresolver=default"

建立 .dockerignore 以從映象構建中排除 .git 目錄:

# .dockerignore file exludes files from the image:
.git

構建映象並啟動測試範例:

docker-compose up

編輯您的 /etc/hosts 檔案(或您的本地 DNS 伺服器)並新增 whoami 路由域:

# ... excerpt from /etc/hosts
# Domain names for Traefik:
# Point to the IP address of your docker server:
127.0.0.1 whoami.example.com app1.example.com app2.example.com

使用 curl 測試您的 DNS 是否正常工作,以及外掛是否已生效(使用與您為 WHOAMI_TRAEFIK_HOST/etc/hosts 設定的域名相同的域名):

curl -k https://whoami.example.com

您應該得到 whoami 響應,並在輸出中顯示此測試頭:

Doespluginwork: YES

這是外掛設定為注入請求的相同頭和值,並從 whoami 回顯。 如果你看到它,你就知道你的外掛設定成功了。

為常規開發工作設定本地 DNS 服務

當你需要測試大量不同的子域和 Traefik Proxy Host 路由器規則時,一個更好的 DNS 解決方案,而不是不斷編輯你的 /etc/hosts 檔案,是在你的工作站上執行 dnsmasq 作為本地 DNS 伺服器,它會響應到萬用字元 DNS A 記錄查詢,用於整個根域或子域名。

dnsmasq 的設定是可選的,是對 /etc/hosts 檔案的補充。dnsmasq 的安裝說明取決於您的作業系統,但可以從大多數包管理器中獲得。 dnsmasq 將使您的開發工作更加順暢,並且是清理 /etc/hosts 檔案的好方法。這是一個範例 /etc/dnsmasq.conf 組態檔,用於設定具有萬用字元域的本地 DNS 服務。您還需要按照註釋中的說明編輯您的 /etc/resolv.conf

# /etc/dnsmasq.conf
# Use this if you are tired of editing your /etc/hosts file.
# This is a local DNS service bound only to the looback device on localhost.
# To use this requires an /etc/resolv.conf file 
# with a single line (no leading space): nameserver 127.0.0.1
# To prevent any changes to the host DNS config,
# run: sudo chattr +i /etc/resolv.conf
#      (now all of your DNS queries will go through dnsmasq)
interface=lo
listen-address=::1,127.0.0.1
bind-interfaces
cache-size=1000
# Use cloudflare upstream DNS servers:
server=1.1.1.1
server=1.0.0.1
# Example wildcard domain names
# All *.example.com names point to a single docker server IP address:
address=/example.com/127.0.0.1
# dnsmasq also loads your /etc/hosts file, so those host names still work.

檢查您的作業系統說明以啟用 dnsmasq 服務,但通常使用 Systemd:

sudo systemctl enable --now dnsmasq.service

編輯 /etc/resolv.conf 以將 dnsmasq 伺服器用於所有系統 DNS 查詢:

domain your.domain.example.com
search domain.example.com
nameserver 127.0.0.1

有時其他服務(systemd-resolved)想要覆蓋這個檔案,你可以通過在檔案上應用不可變標誌來防止這種情況:

# This prevents editing the file, use -i to re-enable editing:
chattr +i /etc/resolv.conf

您可以使用 digdrillnslookup 實用程式測試 DNS 伺服器是否處於活動狀態:

# dig or drill:
dig test.example.com | grep -A1 "ANSWER SECTION"
# or nslookup:
nslookup test.example.com

任何這些工具的輸出都應該報告您的 docker 主機的正確 IP 地址,現在您可以在 Traefik 代理路由中使用您想要的任何子域。

參照