Træfɪk自定义中间件
Træfɪk 是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。 它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 来自动化、动态的应用它的配置文件设置。
Traefik是golang写的,与docker,k8s深度集成,支持服务的自动发现与热部署。
从Traefik2.0版本开始,其加入了中间件,功能更丰富,但是目前(v2.2)官方还不支持以插件的形式自定义中间件。因此,如果要自定义中间件的话,需要在源码上做改动。
比如,我们要添加一个简单的中间件,来把所有的请求都加上一个自定义的请求头:Hello: World。
前置条件
- 安装golang 1.14及以上版本
- 安装git
clone Traefik源码
首先,克隆Traefik的源码,并切换到自己的开发分支进行开发。
| 1 | 克隆 | 
自定义中间件
- 首先,在 - pkg/middlewares/包下新建一个包,我们就命名为- hello,然后新建- hello.go。内容如下:- 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
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46- package hello 
 import (
 "context"
 "net/http"
 "github.com/containous/traefik/v2/pkg/config/dynamic"
 "github.com/containous/traefik/v2/pkg/log"
 "github.com/containous/traefik/v2/pkg/middlewares"
 "github.com/containous/traefik/v2/pkg/tracing"
 "github.com/opentracing/opentracing-go/ext"
 )
 const (
 typeName = "Hello"
 )
 type hello struct {
 next http.Handler
 name string
 }
 // New creates a new handler.
 func New(ctx context.Context, next http.Handler, config dynamic.Hello, name string) (http.Handler, error) {
 log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
 var result *hello
 result = &hello{
 next: next,
 name: name,
 }
 return result, nil
 }
 func (a *hello) GetTracingInformation() (string, ext.SpanKindEnum) {
 return a.name, tracing.SpanKindNoneEnum
 }
 func (a *hello) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), a.name, typeName))
 logger.Infoln("Hello World")
 req.Header.Add("Hello", "World")
 a.next.ServeHTTP(rw, req)
 }- 其中两个函数New和ServeHttp两个函数是必须的,要根据自己的业务重写。New函数是创建一个新的handle,ServeHttp函数就是具体的中间件的处理函数。 
 我们要做的就是在头部添加一个Hello:World的header。
- New函数的第三个参数,config是 dynamic.Hello类型,这个需要我们新定义,实际就是从配置文件读取的当前中间件的配置信息。 
 在- pkg/config/dynamic/middlewares.go文件中新建一个Hello的结构体:- 1 
 2- type Hello struct { 
 }- 我们这个中间件很简单,只是向请求新加一个自定义的请求头,所以这里就没定义属性。根据自己业务来。 
 然后,再在Middleware这个结构体(pkg/config/dynamic/middlewares.go)中添加刚才新建的属性:- 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- type Middleware struct { 
 AddPrefix *AddPrefix `json:"addPrefix,omitempty" toml:"addPrefix,omitempty" yaml:"addPrefix,omitempty"`
 StripPrefix *StripPrefix `json:"stripPrefix,omitempty" toml:"stripPrefix,omitempty" yaml:"stripPrefix,omitempty"`
 StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty" toml:"stripPrefixRegex,omitempty" yaml:"stripPrefixRegex,omitempty"`
 ReplacePath *ReplacePath `json:"replacePath,omitempty" toml:"replacePath,omitempty" yaml:"replacePath,omitempty"`
 ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty" toml:"replacePathRegex,omitempty" yaml:"replacePathRegex,omitempty"`
 Chain *Chain `json:"chain,omitempty" toml:"chain,omitempty" yaml:"chain,omitempty"`
 IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty" toml:"ipWhiteList,omitempty" yaml:"ipWhiteList,omitempty"`
 Headers *Headers `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
 Errors *ErrorPage `json:"errors,omitempty" toml:"errors,omitempty" yaml:"errors,omitempty"`
 RateLimit *RateLimit `json:"rateLimit,omitempty" toml:"rateLimit,omitempty" yaml:"rateLimit,omitempty"`
 RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty" toml:"redirectRegex,omitempty" yaml:"redirectRegex,omitempty"`
 RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty" toml:"redirectScheme,omitempty" yaml:"redirectScheme,omitempty"`
 BasicAuth *BasicAuth `json:"basicAuth,omitempty" toml:"basicAuth,omitempty" yaml:"basicAuth,omitempty"`
 DigestAuth *DigestAuth `json:"digestAuth,omitempty" toml:"digestAuth,omitempty" yaml:"digestAuth,omitempty"`
 ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty" toml:"forwardAuth,omitempty" yaml:"forwardAuth,omitempty"`
 InFlightReq *InFlightReq `json:"inFlightReq,omitempty" toml:"inFlightReq,omitempty" yaml:"inFlightReq,omitempty"`
 Buffering *Buffering `json:"buffering,omitempty" toml:"buffering,omitempty" yaml:"buffering,omitempty"`
 CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" toml:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
 Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty"`
 PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty"`
 Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty"`
 ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty"`
 Hello *Hello `json:"hello,omitempty" toml:"hello,moitempty" yaml:"hello,omitempty"`
 }- 其中最后一行就是我们新定义的Hello配置的结构体。 
- 好了,现在我们要启用这个中间件。在 - pkg/server/middleware/middlewares.go中的- buildConstructor函数中,添加初始化这个中间件的代码:- 1 
 2
 3
 4
 5
 6
 7
 8- if config.Hello != nil { 
 if middleware != nil {
 return nil, badConf
 }
 middleware = func(next http.Handler) (http.Handler, error) {
 return hello.New(ctx, next, *config.Hello, middlewareName)
 }
 }
好了,到现在,最简单的一个中间件就定义完了。下面我们来编译一下二进制文件。
编译
- 先进入webui目录,编译前端dashboard。- cd webui
- npm install
- npm run build
 
- 下载依赖。go mod download
- 安装 go-bindata: go get github.com/containous/go-bindata/...
- 将一些非代码组件打到二进制:go generate
- 编译二进制:go build ./cmd/traefik
编译得到二进制文件,就可以测试了。
启用自定义中间件
在traefik的配置文件中启用该中间件:
| 1 | [http] | 
这样,所以经过web这个入口点的请求,都会在请求上加上一个Hello的请求头。
到这里,简单的中间件的自定义过程就结束了。
总结
Traefik的中间件自定义过程还是挺简单的,无非就下面几个步骤,整理一下:
- 克隆源码到本地,并从最新分支切换一个自己的开发分支出来。
- 下载依赖。go mod download
- 安装go-bindata:go get github.com/containous/go-bindata/...
- 修改代码,加中间件的处理代码
- 编译前端
- 编译后端
这样定义的中间件,耦合太高,改动了源码。还是希望官方尽快将中间件以插件的形式分离出来,这样我们只需要开发插件并启用插件即可。
当然,在这之前,如果是很通用的中间件,也可以提给官方,合并到官方。