服务环境搭建-Traefik网关服务
1. 说明
Traefik网关服务
用于提供一个实现反向代理、中间件鉴权、服务负载均衡、与服务发现的环境。
2. 反向代理
2.1 基本概念
EntryPoints:入口点是进入Traefik的网络入口点。它们定义了接收数据包的端口,以及是侦听TCP
还是UDP
。入口点是静态配置的一部分,它们可以通过使用文件(TOML或YAML)或CLI参数来定义。
Routers:路由器负责将传入的请求连接到能够处理它们的服务。在这个过程中,路由器可能会使用一些中间件来更新请求,或者在将请求转发给服务之前采取行动。
Rule:Rule
是一组配置有值的匹配器,这些值确定特定请求是否匹配特定条件。 如果该规则得到验证,则路由器将变为活动状态,调用中间件,然后将请求转发到服务。
转发到
Host
为example.com
的服务
rule = "Host(`example.com`)"
转发到
Host
为example.com
或者Host
为example.org
并且Path
为/traefik
的服务
rule = "Host(`example.com`) || (Host(`example.org`) && Path(`/traefik`))"
Rule所有可用的配置器
可以使用
AND(&&)
和OR(||)
运算符组合多个匹配器。 也可以使用括号。
Rule | Description |
---|---|
Headers(key , value ) | Check if there is a key key defined in the headers, with the value value |
HeadersRegexp(key , regexp ) | Check if there is a key key defined in the headers, with a value that matches the regular expression regexp |
Host(example.com , …) | Check if the request domain (host header value) targets one of the given domains |
HostHeader(example.com , …) | Check if the request domain (host header value) targets one of the given domains |
HostRegexp(example.com , {subdomain:[a-z]+}.example.com , …) | Check if the request domain matches the given regexp |
Method(GET , …) | Check if the request method is one of the given methods (GET , POST , PUT , DELETE , PATCH , HEAD ) |
Path(/path , /articles/{cat:[a-z]+}/{id:[0-9]+} , …) | Match exact request path. It accepts a sequence of literal and regular expression paths |
PathPrefix(/products/ , /articles/{cat:[a-z]+}/{id:[0-9]+} ) | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths |
Query(foo=bar , bar=baz ) | Match Query String parameters. It accepts a sequence of key=value pairs |
2.2 Traefik监听Docker服务
简单例子
配置
docker
、部署/公开服务
启用docker提供程序
--providers.docker=true
将标签附加到容器(在docker-compose文件中)
version: "3"
services:
my-container:
# ...
labels:
- traefik.http.routers.my-container.rule=Host(`example.com`)
为容器指定自定义端口:将对
http://example.com
的请求转发到http:// <容器的私有IP>:12345
:
version: "3"
services:
my-container:
# ...
labels:
- traefik.http.routers.my-container.rule=Host(`example.com`)
# Tell Traefik to use the port 12345 to connect to `my-container`
- traefik.http.services.my-service.loadbalancer.server.port=12345
为每个容器指定多个路由器和服务
将请求转发到容器上的多个端口需要使用路由器上的service
参数引用服务负载均衡器端口定义。
在此示例中,除了将http://example-a.com
请求转发到http://<private IP of container>:8000
外,还将http://example-b.com
的请求转发到http://<private IP of container>:9000
version: "3"
services:
my-container:
# ...
labels:
- traefik.http.routers.www-router.rule=Host(`example-a.com`)
- traefik.http.routers.www-router.service=www-service
- traefik.http.services.www-service.loadbalancer.server.port=8000
- traefik.http.routers.admin-router.rule=Host(`example-b.com`)
- traefik.http.routers.admin-router.service=admin-service
- traefik.http.services.admin-service.loadbalancer.server.port=9000
3. 中间件
连接到路由器上的中间件是在请求被发送到服务(或在服务的回答被发送到客户机之前)之前调整请求的一种方法。通常用来做一些权限校验与调整请求。
在Traefik中有几个可用的中间件,一些可以修改请求、头,一些负责重定向,一些添加身份验证,等等。
3.1 ForwardAuth
Traefik提供的ForwardAuth
中间件可以使用外部服务转发身份验证。FrwardAuth
中间件将身份验证委派给外部服务。 如果服务以2XX
响应码回答,则将授予访问权限,并执行原始请求。 否则,将返回来自身份验证服务器的响应。我们用此中间件来完成服务的鉴权:
3.1.1 使用实例
将身份验证转发到
http://example.com/auth
labels:
- "traefik.http.middlewares.test-auth.forwardauth.address=http://example.com/auth"
3.1.2 转发请求头
以下请求属性以X-Forwarded- headers
的形式提供给forward-auth
目标端点。
Property | Forward-Request Header |
---|---|
HTTP Method | X-Forwarded-Method |
Protocol | X-Forwarded-Proto |
Host | X-Forwarded-Host |
Request URI | X-Forwarded-Uri |
Source IP-Address | X-Forwarded-For |
3.1.3 配置项
address
address
定义认证服务器的地址
labels:
- "traefik.http.middlewares.test-auth.forwardauth.address=https://example.com/auth"
trustForwardHeader
将trustForwardHeader
选项设置为true
以信任所有X-Forwarded-*
头。
labels:
- "traefik.http.middlewares.test-auth.forwardauth.trustForwardHeader=true"
authResponseHeaders
authResponseHeaders
选项是要从身份验证服务器响应中复制并根据转发的请求进行设置的标头列表,以替换所有现有的冲突标头。
labels:
- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeaders=X-Auth-User, X-Secret"
authResponseHeadersRegex
authResponseHeadersRegex
选项是正则表达式,用于匹配要从身份验证服务器响应中复制的标头,并在转发与正则表达式匹配的所有标头后,根据转发的请求进行设置。 它允许将正则表达式与标题键进行部分匹配。 字符串(^
)的开头和字符串($
)的结尾应用于确保与标题键完全匹配。
labels:
- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeadersRegex=^X-"
authRequestHeaders
authRequestHeaders
选项是要从请求复制到身份验证服务器的标头列表。 它允许过滤不应传递到身份验证服务器的标头。 如果未设置或为空,则传递所有请求标头。
labels:
- "traefik.http.middlewares.test-auth.forwardauth.authRequestHeaders=Accept,X-CustomHeader"
3.2 Add Prefix
AddPrefix中间件在转发请求之前添加前缀更新请求的路径。
3.2.1 使用实例
给原请求的path添加前缀
# Prefixing with /foo
labels:
- "traefik.http.middlewares.add-foo.addprefix.prefix=/foo"
3.2.2 配置项
prefix
prefix
是要在请求的URL中的当前路径之前添加的字符串,它应该包含一个斜杠(/)。
3.3 StripPrefix
从URL路径中删除指定的前缀。
3.3.1 使用实例
删除原请求path的前缀
# Strip prefix /foobar and /fiibar
labels:
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar,/fiibar"
3.3.2 配置项
General
StripPrefix
中间件将匹配的路径前缀去除,并将其存储在X - Forwarded - Prefix
头文件中。
prefixes
prefixes
选项定义从请求URL中去除的前缀。
forceSlash
Optional, Default=true
forceSlash
选项可通过在必要时将其替换为/
来确保所得的剥离路径不是空字符串。
添加这个选项是为了保持这个中间件的初始(非直观)行为,以避免引入破坏性更改。
建议显式设置forceSlash为false。
forceSlash的行为示例
forceSlash=true
Path | Prefix to strip | Result |
---|---|---|
/ | / | / |
/foo | /foo | / |
/foo/ | /foo | / |
/foo/ | /foo/ | / |
/bar | /foo | /bar |
/foo/bar | /foo | /bar |
forceSlash=false
Path | Prefix to strip | Result |
---|---|---|
/ | / | empty |
/foo | /foo | empty |
/foo/ | /foo | / |
/foo/ | /foo/ | empty |
/bar | /foo | /bar |
/foo/bar | /foo | /bar |
3.4 Chain
Chain中间件使您能够定义其他中间件的可重用组合, 这使得重用相同的组更加容易。
3.4.1 使用实例
以下是包含
WhiteList
,BasicAuth
和RedirectScheme
的Chain的示例。
labels:
- "traefik.http.routers.router1.service=service1"
- "traefik.http.routers.router1.middlewares=secured"
- "traefik.http.routers.router1.rule=Host(`mydomain`)"
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
- "traefik.http.middlewares.known-ips.ipwhitelist.sourceRange=192.168.1.7,127.0.0.1/32"
- "http.services.service1.loadbalancer.server.port=80"
4. 负载均衡
负载平衡器能够在程序的多个实例之间负载平衡请求,每个服务都有一个负载均衡器,即使只有一台服务器可以转发流量。
目前只支持轮询负载均衡
使用两个服务器声明服务(使用负载平衡)
http:
services:
my-service:
loadBalancer:
# 在以下服务中实现负载均衡
servers:
- url: "http://private-ip-server-1/"
- url: "http://private-ip-server-2/"
5. 服务发现
Traefik可以自动发现服务。其服务发现是通过 providers来实现的, providers是基础设置组件,其思想是Traefik查询提供程序的API,以查找有关路由的相关信息,并且当Traefik检测到更改时,它会动态更新路由。
使用Docker作为Traefik的提供商
指定docker作为Traefik的providers
--providers.docker=true
Traefik需要访问docker套接字来获取它的动态配置。你可以用endpoint
来指定要使用哪个Docker API端点。
--providers.docker.endpoint=unix:///var/run/docker.sock
使Traefik监听Docker事件
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
使用docker部署服务时,通过labels
标签附加信息,告诉Traefik服务可以处理的请求的特征,这意味着当部署服务时,Traefik会立即检测到该服务并实时更新路由规则。反之亦然:当您从基础设施中删除服务时,路由将随之消失。
如下例子:
whoami:
image: traefik/whoami
container_name: whoami
networks:
- proxy
labels:
# 通知Traefik路由的名称为routers,规则为:访问Host为example.com、path为/whoami的请求转发到whoami的服务
- "traefik.http.routers.whoami.rule=Host(`example.com`) && Path(`/whoami`)"
6. 综合实例
6.1 实现目标
使用Traefik监听Docker服务,自动发现服务与自动负载均衡。启动两个whoami的docker服务,http://example.com/web
的请求转发到whoami_web服务;http://example.com/svv_runtime
与http://example.com/svv/...
的请求转发到whoami_svv服务的根路径下。同时请求这两个服务之前进行鉴权,当鉴权成功时才可以顺利访问服务。
6.2 实现方式
# docker-compose.yml
version: '3'
services:
# traefik服务
traefik:
image: traefik:v2.4
container_name: traefik
restart: always
# 禁止容器进程获取新的权限
security_opt:
- no-new-privileges:true
# traefik服务监听80与8080两个端口
ports:
- 80:80
- 8080:8080
command:
- "--api=true"
# 启用Web UI并监听docker
- "--api.insecure=true"
# 开启控制台(默认开启)
- "--api.dashboard=true"
# 入口点定义
- "--entrypoints.web.address=:80"
# 指定traefik的提供商为docker
- "--providers.docker=true"
# 访问docker套接字获取动态配置
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
# 指定docker网络为proxy
- "--providers.docker.network=proxy"
# 使Traefik监听Docker事件
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# 创建docker自定义网络为proxy
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# 访问traefik的后台的Host为example.com
- "traefik.http.routers.traefik.rule=Host(`example.com`)"
# whoami服务
whoami_web:
image: traefik/whoami
container_name: whoami_web
networks:
- proxy
labels:
# 创建名为whoami_web的路由,将path为/web请求转发到该服务
- "traefik.http.routers.whoami_web.rule=Path(`/web`)"
# 将名为whoami_web的路由与test-auth的鉴权中间件进行连接
- "traefik.http.routers.whoami_web.middlewares=test-auth@docker"
# 将请求转发到address为http://ip:port/auth的鉴权服务,若服务响应2xx的状态码,则授权访问,并执行原始请求。否则,将返回鉴权服务器的响应
- "traefik.http.middlewares.test-auth.forwardauth.address=http://ip:port/auth"
whoami_svv:
image: traefik/whoami
container_name: whoami_svv
networks:
- proxy
labels:
# 创建名为whoami_svv的路由,将path前缀为/svv_runtime请求转发到该服务
- "traefik.http.routers.whoami_svv.rule=PathPrefix(`/svv_runtime`)"
# 创建名为middlewareschain的中间件链路
- "traefik.http.routers.whoami_svv.middlewares=middlewareschain"
# middlewareschain中间件链路包含鉴权中间件test-auth与删除前缀中间件test-stripprefix
- "traefik.http.middlewares.middlewareschain.chain.middlewares=test-auth@docker,test-stripprefix@docker"
# 将请求转发到address为http://ip:port/auth的鉴权服务,若服务响应2xx的状态码,则授权访问,并执行原始请求。否则,将返回鉴权服务器的响应
- "traefik.http.middlewares.test-auth.forwardauth.address=http://ip:port/auth"
# 将原请求Path删除前缀svv_runtime
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/svv_runtime"
networks:
proxy:
external: true