Traefik 搭建日志

Traefik 是一款开源的反向代理和负载均衡软件,它能够将请求根据不同的条件转发给后端不同的服务,并提供 HTTP/HTTPS/TCP/UDP 等协议支持。要注意 Traefik 并没有 http 服务器的功能,相比 Nginx 它更像是一个网络路由软件。

unRAID搭建Traefik的建议

Traefik 对 Docker 的支持非常强大,其中一个很方便的功能是它能够自主发现后端的 Docker 服务,进行反向代理的配置。这个功能是通过在创建容器的时候添加 “label” 参数(就是键值),Traefik 根据 “label” 字符串自动配置。这样无需像使用 Nginx 时频繁修改配置文件。

本来这是一个很好的功能,label 字符串写在容器的 compose.yaml 文件里很方便。但目前 unRAID 默认没有 docker compose,使用过 unRAID 系统的都知道在容器创建界面里添加各种 label 属性是极其繁琐的,而且能被 Traefik 识别的 label 字符串很长,写起来很难受。

网络上的 Traefik 教程大部分都是结合 docker compose、docker.sock 和 label 来配置,个人感觉在 unRAID 系统下不方便,所以本篇搭建教程主要采用 File 动态文件来配置。我在 unRAID 系统和 Jetson Nano 下都使用过 Traefik,下文将介绍使用 Docker 和编译好的可执行文件两种方式来搭建 Traefik 服务。

初始化配置文件

下载配置示例: traefik.sample.yml https://github.com/traefik/traefik/blob/master/traefik.sample.yml

这是用来测试 Traefik 运行的,只需简单修改配置文件,在下面配置服务章节会使用到它。

Traefik安装(Docker)

打开 unRAID 终端,创建 Traefik 配置文件目录:

1
2
3
4
5
6
# Traefik 主目录
sudo mkdir /mnt/user/appdata/traefik
# 动态配置文件目录
sudo mkdir /mnt/user/appdata/traefik/dynamic-conf
# SSL证书的json文件目录
sudo mkdir /mnt/user/appdata/traefik/acme

静态配置文件

将上面的 traefik.sample.yml 示例上传到主目录 traefik 里,修改文件名为 traefik.yml。这是静态配置文件,先修改下面几段配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
################################################################
# EntryPoints configuration
################################################################
# 入口点,即Traefik的监听端口
entryPoints:
web:
address: ":80"
################################################################
# API and dashboard configuration
################################################################
# 开启Dashboard
api:
# Enable the API in insecure mode
insecure: true
# Enabled Dashboard
dashboard: true
################################################################
# Docker configuration backend
################################################################
# 后端服务配置(禁用Docker,使用File)
providers:
# Enable Docker configuration backend
#docker:
# endpoint: ......
file:
directory: "/etc/traefik/dynamic-conf/" # 动态配置文件目录

创建容器

DOCKER > ADD CONTAINER

1
2
3
4
5
6
7
8
9
10
11
# 这里使用 docker-compose.yml 的格式来说明
services:
Traefik:
image: traefik:v2.9.6
ports:
# The HTTP port
- "80:80"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
volumes:
- /mnt/user/appdata/traefik:/etc/traefik

访问 Traefik 仪表盘

在上文中已经修改了静态配置文件,开启了仪表盘功能,所以访问 http://[IP]:[PORT:8080] 即可打开 Traefik 仪表盘。

Traefik安装(可执行文件)

在 Linux 系统下使用编译好的 Traefik 可执行文件安装,并且以系统服务启动。笔者在 Jetson Nano 上的 Traefik 就是按下面步骤安装的。

First, put the traefik binary in the system wide binary directory and give it appropriate ownership and permissions:

1
2
3
sudo cp ~/Apps/Traefik/traefik /usr/local/bin/
sudo chown root:root /usr/local/bin/traefik
sudo chmod 755 /usr/local/bin/traefik

Give the traefik binary the ability to bind to privileged ports (e.g. 80, 443) as a non-root user:

1
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/traefik

Set up the user, group, and directories that will be needed:

1
2
3
4
5
6
7
8
9
10
sudo groupadd -g 3001 traefik
sudo useradd -g traefik --no-user-group --home-dir /var/www --no-create-home --shell /usr/sbin/nologin --system --uid 3001 traefik
sudo mkdir /etc/traefik
sudo mkdir /etc/traefik/dynamic-conf # Need if use dynamic-configurations
sudo chown -R root:root /etc/traefik
# Need if use acme
sudo mkdir /etc/traefik/acme
sudo touch /etc/traefik/acme/acme.json
sudo chown -R traefik:traefik /etc/traefik/acme
sudo chmod 600 /etc/traefik/acme/acme.json

Place your traefik configuration file (“traefik.yml”) in the proper directory and give it appropriate ownership and permissions:

1
2
3
sudo cp ~/Apps/Traefik/traefik.yml /etc/traefik/
sudo chown root:root /etc/traefik/traefik.yml
sudo chmod 644 /etc/traefik/traefik.yml

Install the systemd service unit configuration file, reload the systemd daemon, and start traefik:

1
2
3
4
5
sudo cp ~/Apps/Traefik/traefik.service /etc/systemd/system/
sudo chown root:root /etc/systemd/system/traefik.service
sudo chmod 644 /etc/systemd/system/traefik.service
sudo systemctl daemon-reload
sudo systemctl start traefik.service

Have the traefik service start automatically on boot if you like:

1
sudo systemctl enable traefik.service

If traefik doesn’t seem to start properly you can view the log data to help figure out what the problem is:

1
journalctl --boot -u traefik.service

If your GNU/Linux distribution does not use journald with systemd then check any logfiles in /var/log.

If you want to follow the latest logs from traefik you can do so like this:

1
journalctl -f -u traefik.service

配置Traefik

Traefik 支持很多种配置方式,网络上的很多教程使用 Docker label 的方式配置,但我发现其有很多功能无法实现,并且在 unRAID 下给每个容器写 label 操作非常繁琐。这里通过静态配置文件和动态配置文件的方式来配置。

静态配置文件

Traefik 启动后默认会读取 /etc/traefik/traefik.yml 进行初始化,这个文件就是它的静态配置文件。Traefik 根据文件内的元素开启监听端口、连接后端服务、启用关闭日志仪表盘等。这个配置文件不经常改动,所以叫做“静态配置文件”,如果修改了该文件的内容,必须重启 Traefik 才能使改动生效。

上文已经根据示例生成过静态配置文件了,其中几个重要元素包括:

1
2
3
4
5
6
7
8
9
################################################################
# EntryPoints configuration
################################################################
entryPoints:
web:
address: ":80"
web2:
address: ":8081"
# 这是Traefik的入口点(entrypoints),即监听端口。
1
2
3
4
5
6
7
8
################################################################
# File configuration backend
################################################################
providers:
file:
directory: "/etc/traefik/dynamic-conf/"
# 这是Traefik的后端服务设置(providers),可以是docker,file等等
# 这里使用file,定义了一个目录,存储所有动态配置文件(见下文)。

动态配置文件

动态配置文件存储在指定的目录中,它配置了从接收到客户端请求到中间件预处理再到后端服务的整个流程。Traefik 提供了丰富的中间件和规则,可以满足对请求的各种处理。

反向代理配置

下面是一个简单的反向代理示例,后端服务是前几篇文章中笔者搭建的 FileBrowserEx 文件管理器。

创建 FileBrowserEx 的动态配置文件:

1
sudo vi /etc/traefik/dynamic-conf/FileBrowserEx.yml

编辑文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
################################################################
# FileBrowserEx Dynamic Configuration
################################################################
http:
################################################################
# Routers Configruation
################################################################
routers:
FBE-http:
entryPoints:
- "web"
rule: "Host(`fbe.example.com`)"
service: "FileBrowserEx"
################################################################
# Services Configruation
################################################################
services:
FileBrowserEx:
loadBalancer:
servers:
- url: "http://192.168.1.110:19001/"
passHostHeader: true

简单说明该配置文件的内容:

动态配置文件默认是 YAML 格式的,按层级阅读起来很清晰。首先,笔者定义了 http 协议下的一个路由 router,命名为 “FBE-http”,监听 “web” 端口(”web”已经在静态文件中定义,即80端口),规则是主机域名 “fbe.example.com”,后端服务是 “FileBrowserEx”(这个服务也在下面定义),所有访问该域名的请求都会转发到 “FileBrowserEx” 服务上。然后,定义这个服务,名称 “FileBrowserEx”,负载均衡服务器 servers 只有一个,就是后端的内网地址 “http://192.168.1.110:19001/"。

这样就完成了简单的反向代理配置。Traefik 的动态配置还有很多的功能,比如:设置访问密码,IP地址黑白名单,网址路径规则等等。可参看:File Configuration Reference

Let’s Encrypt 自动申请

要使用 Let’s Encrypt 自动生成证书,需要使用 ACME 并在静态配置中定义 certificatesResolvers,Traefik 将自动完成证书的申请。

注意!因为 Let’s encrypt 的dns认证新增了对 CNAME 的支持,但在某些情况下可能会出现问题,所以如果 dnsChallenge 出现无法认证的情况,则要额外添加环境变量:LEGO_DISABLE_CNAME_SUPPORT=true

使用 DNSPod 的话要添加环境变量 DNSPOD_API_KEY

(其他域名服务商请查看官方文档:https://doc.traefik.io/traefik/https/acme/#dnschallenge)

1
2
3
4
5
# 以本地用户运行的话,需要添加系统环境变量
sudo vi /etc/profile
# 在文件中添加:
export DNSPOD_API_KEY="*******"
export LEGO_DISABLE_CNAME_SUPPORT=true
1
2
3
# 以服务运行的话,要在 traefik.service 文件里添加:
Environment="DNSPOD_API_KEY=id,Token"
Environment="LEGO_DISABLE_CNAME_SUPPORT=true"

在静态配置文件中添加 certificatesResolvers

1
2
3
4
5
6
7
8
9
certificatesResolvers:
letsencrypt:
acme:
email: ""
# ACME certificates are stored in here. Needs to have a 600 file mode.
storage: "/etc/traefik/acme/acme.json"
dnsChallenge:
provider: "dnspod"
delayBeforeCheck: 30

在动态配置文件中添加 certResolver

1
2
3
4
5
6
7
8
9
10
http:
routers:
appName:
rule: "Host(`appName.com`) && Path(`/appName`)"
tls:
certResolver: "letsencrypt"
domains:
- main: "appName.com"
sans:
- "*.appName.com"

JSON格式的证书转换为CRT普通证书文件

Traefik 自动申请的证书是以 JSON 文本格式存储在 “/etc/traefik/acme/acme.json” 文件中的,笔者还有个群晖NAS,但群晖不支持导入JSON格式的证书,所以必须把 Traefik 的 JSON 证书文件转换成普通证书文件。

这是笔者在 Github 上找到的开源仓库:traefik-certs-dumper

1
2
# 转换当前目录下acme.json文件
traefik-certs-dumper file

同时监听http和https

从 Traefik v2 开始,在一个端口(entryPoints)开启tls后,这个端口就只会接受https流量了,此时通过http访问该端口的请求被忽略。要在同一个端口同时监听http和https则需要配置2个routers。

1
2
3
4
5
6
7
8
9
10
11
12
13
routers:
appDemo:
entryPoints:
- "web7"
rule: "Host(`example.com`)"
service: "appDemo"
tls: {}

appDemo-http:
entryPoints:
- "web7"
rule: "Host(`example.com`)"
service: "appDemo"

路径规则

只匹配 “example.com” 域名下的 “/_matrix” 和 “/_synapse” 前缀的路径规则:

1
2
3
4
5
6
routers:
appName:
entryPoints:
- "websecure"
rule: "Host(`example.com`) && PathPrefix(`/_matrix`, `/_synapse`)"
service: "appName"

MiddleWares

中间件(Middleware)是一种在请求发送到您的服务之前(或在服务的响应传回客户端前)调整请求的方法。Traefik 中有几个可用的中间件,有的可以修改请求、headers,有的负责重定向,有的添加认证等等。使用相同协议的中间件可以组合成链以适应各种场景。

实现url重定向

使用 RedirectRegex 这个中间件方法来实现 url 重定向功能(即 CODE 302)。

示例:

1
2
3
4
5
6
7
http:
#..
middlewares:
mw-redirect-to-https:
redirectRegex:
regex: "^http://(.*)"
replacement: "https://${1}"

该示例使用 RedirectRegex 方法实现将 http 地址转换为 https 地址。

IP白名单(IPWhiteList)

IP白名单中间件是基于客户端IP地址来判断接受或者拒绝该客户端的请求。

示例:

1
2
3
4
5
6
7
8
9
# Accepts request from defined IP
http:
middlewares:
test-ipwhitelist:
ipWhiteList:
sourceRange: # 定义IP地址或IP地址段(CIDR表示法) 只接受源IP在此列表中的客户端请求
- "127.0.0.1/32"
- "192.168.1.7"
- "10.10.0.0/24"

局域网用户通过域名访问 Traefik 代理的服务时,真实的源IP并不是局域网IP段,而是用户自己的公网IP地址,所以即使设置了如 “192.168.0.1/24” 这样的规则也会被拒绝访问。

解决方法:

​ ① 通过 hosts 文件或者本地 DNS 服务器将域名直接解析为本地IP地址。

​ ② 将公网IP地址写入规则。动态IP则需要用脚本去定时修改配置文件。

middlewares 技巧

一些服务可能需要同一个 MiddleWares 规则,不必在每个服务的 “.yml” 文件中都声明一遍 MiddleWares 规则,其实只需要声明一次,MiddleWares 是全局的,在所有的配置文件中都可以使用。

Reference

Traefik Proxy Documentation

systemd Service Unit for Traefik

regular expressions 101