>> Apresentação e considerações
ChurrOpers, escrever o primeiro artigo de Traefik me despertou mais curiosidades a respeito do funcionamento, sobre as suas Features e possibilidades.
Pensando nessa linha estou compartilhando a evolução dos meus estudos, e esse artigo terá como base e tradução a documentação oficial dos conceitos básicos de Traefik, e como a documentação será teórico e voltado para o entendimento dessa ferramenta fantástica, espero que apreciem…
Let’s go!
>> Visualizando e entendendo o fluxo de funcionamento
Na figura abaixo temos a representação com o fluxo do funcionamento do Traefik, e a descrições dos passos.
- As requisições entram através dos Entrypoints via HTTP ou HTTPS, caso a requisição venha como HTTP é realizado o Redirect para HTTPS.
- O tráfego é encaminhado para o FrontEnd que possui as rotas dos hosts, paths, cabeçalhos, etc.
- Ao identificar e reconhecer uma rota o frontend encaminha a requisição para o backend que pode ter um ou mais servers.
- E por fim o backend encaminha a requisição para o service ou microservice correspondente.
>> Entrypoints
Entrypoints são os pontos de entrada de rede no Træfik. Eles podem ser definidos usando:
- Portas (80, 443…)
- SSL (Certificados, keys, autenticação com um certificado de cliente assinado por uma CA confiável)
- redirecionamento para outro entrypoint (redirect
HTTP
toHTTPS
)
Exemplo de definição de Entrypoint:
[entryPoints] [entryPoints.http] address = ":80" [entryPoints.http.redirect] entryPoint = "https" [entryPoints.https] address = ":443" [entryPoints.https.tls] [[entryPoints.https.tls.certificates]] certFile = "tests/traefik.crt" keyFile = "tests/traefik.key"
- 2 entrypoints foram definidos
http
ehttps
. http
escuta na porta80
ehttps
na porta443
.- SSL habilitado no
https
com arquivo de certificado e uma chave. - Redirecionamento do tráfego do entrypoint
http
parahttps
.
>> Frontends
Com o frontend podemos definir as diversas regras que vão determinar como as solicitações vindas do Entrypoint vão ser encaminhadas para um backend.
Existem 2 classificações de regras que são os Modifiers
e Matchers
.
Modifiers
Os Modifiers, não tem impacto nas decisões de roteamento, apenas modificam os pedidos através de regras.
Temos as seguintes regras:
AddPrefix: /churrops
: Adiciona o prefixo do path ao caminho de solicitação existente antes de encaminhar a solicitação para o backend.ReplacePath: /serverless-path
: Substitui o caminho e adiciona o caminho antigo ao cabeçalhoX-Replaced-Path
. Útil para mapeamento para AWS Lambda ou Google Cloud Functions..
Matchers
As regras do Matcher determinam se um pedido específico deve ser encaminhado para um backend.
Separar vários valores de regras por ,
(vírgula) para habilitar QUALQUER semântica (ou seja, encaminhar uma solicitação se qualquer regra corresponder). Não funciona para Headers
e HeadersRegexp
.
Separar valores de regras múltiplas por ;
(ponto e vírgula) para permitir TODAS as semânticas (ou seja, encaminhar uma solicitação se todas as regras coincidirem).
A seguir no quadro abaixo temos a lista de regras de matcher existentes, juntamente com exemplos:
Matcher | Description |
---|---|
Headers: Content-Type, application/json |
Match HTTP header. Aceita chave/valor de forma literal separados por vírgula. |
HeadersRegexp: Content-Type, application/(text/json) |
Match HTTP header. Aceita chave/valor separados por vírgula, onde chave é literal e valor pode ser uma expressão literal ou regular |
Host: churrops.io, www.churrops.io |
Match request host. aceita uma sequência de hosts literais. |
HostRegexp: churrops.io, {subdomain:[a-z]+}.churrops.io |
Match request host. Aceita uma sequência de hosts com expressão literal ou regular. |
Method: GET, POST, PUT |
Match request HTTP method. Aceita uma sequência de métodos HTTP. |
Path: /products/, /articles/{category}/{id:[0-9]+} |
Match no caminho exato da requisição. aceita uma sequência de paths de expressão regular e literal. |
PathStrip: /products/ |
Match no path exato e remove o path antes de encaminhar a requisição para o backend. Aceita uma sequênca de paths literais. |
PathStripRegex: /articles/{category}/{id:[0-9]+} |
Match no path exato e remove o path antes de encaminhar a requisição para o backend. Aceita uma sequência de paths literiais e expressões regulares. |
PathPrefix: /products/, /articles/{category}/{id:[0-9]+} |
Match request prefix path. Ele aceita uma seqüência de caminhos de prefixo de expressão regular e literal. |
PathPrefixStrip: /products/ |
Preencha o path do prefixo e retire o prefixo do path antes de encaminhar a solicitação para o backend. Aceita uma seqüência de paths de prefixo literais. Começando com o Traefik 1.3, o caminho de prefixo despojado estará disponível no X-Forwarded-Prefix header. |
PathPrefixStripRegex: /articles/{category}/{id:[0-9]+} |
Match na request prefix path e remove o prefixo do caminho antes de encaminhar a requisição para o backend. Ele aceita uma seqüência de paths de prefixo de expressão regular e literal. Começando com Traefik 1.3, o caminho de prefixo despojado estará disponível no header X-Forwarded-Prefix . |
Query: foo=bar, bar=baz |
Match Query String parameters. Aceita uma sequência de key=value pairs. |
Para usar expressões regulares com correspondentesHost
e Path
, você deve declarar uma variável arbitrariamente chamada seguida da expressão regular separada por dois pontos, toda fechada em chaves curly. Qualquer padrão suportado pelo pacote Go’s regexp package pode ser usado (examplo: /posts/{id:[0-9]+}
).
NOTA: A variável não tem significado especial; no entanto, é exigido pela dependência gorilla/mux que incorpora a expressão regular e define a sintaxe.
Você pode habilitar opcionalmente o passHostHeader
para encaminhar o cabeçalho do Host
cliente para o backend. Você também pode habilitar opcionalmente o passTLSCert
para encaminhar certificados do TLS Client para o backend.
Guia de uso dos PATH MATCHERS
Esta sessão explica quando usar vários path matchers.
Use Path
se o seu backend ouvir somente o caminho exato. Por exemplo, Path: /products
casa com /products
mas não com /products/shoes
.
Use um *Prefix*
matcher se o seu backend escutar um path de base específico, mas também atende solicitações em sub-paths. Por exemplo, PathPrefix: /products
casa com /products
mas também com /products/shoes
e /products/shirts
. Uma vez que o path é encaminhado as-is, seu backend deverá ouvir em /products
.
Use um *Strip
matcher se o seu backend ouvir no path raiz (/
) mais deveria ser roteável para um prefixo específico. Por exemplo, PathPrefixStrip: /products
casa com /products
mas também com /products/shoes
e /products/shirts
.
Uma vez que o path é removido antes do encaminhamento, espera-se que seu backend escutasse no /
.
Se o seu backend estiver servindo assets (e.g., images ou Javascript files), as chances são de que ele deve retornar URLs relativas devidamente construídas.
Continuando o exemplo, o backend deveria retornar /products/shoes/image.png
(e não /images.png
que o Traefik provavelmente não poderá associar com o mesmo backend).
O X-Forwarded-Prefix
header (disponível desde o Traefik 1.3) pode ser consultado para construir essas URLs dinamicamente.
Ao invés de distinguir seus backends apenas por caminhos, você pode adicionar um Host matcher ao mix. Dessa forma, o namespacing de seus backends acontece com base em hosts além de caminhos.
Exemplo da definição de frontends:
[frontends] [frontends.frontend1] backend = "backend2" [frontends.frontend1.routes.test_1] rule = "Host:test.localhost,test2.localhost" [frontends.frontend2] backend = "backend1" passHostHeader = true passTLSCert = true priority = 10 entrypoints = ["https"] # overrides defaultEntryPoints [frontends.frontend2.routes.test_1] rule = "HostRegexp:localhost,{subdomain:[a-z]+}.localhost" [frontends.frontend3] backend = "backend2" [frontends.frontend3.routes.test_1] rule = "Host:test3.localhost;Path:/test"
- Foram definidos 3 frontends:
frontend1
,frontend2
efrontend3
frontend1
encaminhará o tráfego para obackend2
se der match na ruleHost:test.localhost,test2.localhost
frontend2
encaminhará o tráfego para obackend1
se a ruleHost:localhost,{subdomain:[a-z]+}.localhost
der match (encaminhando o clientHost
header para o backend)frontend3
encaminhará o tráfego para obackend2
se as rulesHost:test3.localhost
ANDPath:/test
derem match
Vimos acima que podemos combinar várias regras. No arquivo TOML, podemos utilizar várias rotas
[frontends.frontend3] backend = "backend2" [frontends.frontend3.routes.test_1] rule = "Host:test3.localhost" [frontends.frontend3.routes.test_2] rule = "Path:/test"
Acima o frontend3 encaminha o tráfego para o backend2 se as rules Host:test3.localhost
e Path:/test
derem match.
Você também pode usar a notação usando um separador ;
, com o mesmo resultado:
[frontends.frontend3] backend = "backend2" [frontends.frontend3.routes.test_1] rule = "Host:test3.localhost;Path:/test"
Finalmente, você pode criar uma regra para vincular vários domínios ou paths para um frontend, usando o separador ,
:
[frontends.frontend2] [frontends.frontend2.routes.test_1] rule = "Host:test1.localhost,test2.localhost" [frontends.frontend3] backend = "backend2" [frontends.frontend3.routes.test_1] rule = "Path:/test1,/test2"
Ordem das Rules
Quando combinamos as rules de Modifier e as rules de Matcher, é importante lembrar que as regras de Modifier SEMPRE se aplicam após as regras do Matcher.
As seguintes regras são Matchers e Modifiers, então a parte Matcher da regra será aplicada primeiro e o Modificador será aplicado mais tarde.
PathStrip
PathStripRegex
PathPrefixStrip
PathPrefixStripRegex
Os modificadores serão aplicados em uma ordem pré-determinada independentemente da ordem na seção de configuração da regra.
PathStrip
PathPrefixStrip
PathStripRegex
PathPrefixStripRegex
AddPrefix
ReplacePath
Prioridades
Por padrão, as rotas serão ordenadas (em ordem decrescente): PathPrefix:/12345
vai dar match antes de PathPrefix:/1234
que por sua vez dará match antes do PathPrefix:/1
.
Você pode personalizar a prioridade pelo frontend:
[frontends] [frontends.frontend1] backend = "backend1" priority = 10 passHostHeader = true [frontends.frontend1.routes.test_1] rule = "PathPrefix:/to" [frontends.frontend2] priority = 5 backend = "backend2" passHostHeader = true [frontends.frontend2.routes.test_1] rule = "PathPrefix:/toto"
Aqui, o frontend1 dará match antes do frontend2 (10 > 5).
Custom headers
Custom headers podem ser configurados através dos frontends, para adicionar headers a pedidos ou respostas que correspondam às regras do frontend. Isso permite configurar os headers como um X-Script-Name
para ser adicionado a request, ou custom headers para serem adicionados à resposta.
[frontends] [frontends.frontend1] backend = "backend1" [frontends.frontend1.headers.customresponseheaders] X-Custom-Response-Header = "True" [frontends.frontend1.headers.customrequestheaders] X-Script-Name = "test" [frontends.frontend1.routes.test_1] rule = "PathPrefixStrip:/cheese"
Nesse exemplo, todos os matches para o path /cheese
terão o header X-Script-Name
adicionado à solicitação proxied, e o X-Custom-Response-Header
adicionado à resposta.
Security headers
Security related headers (HSTS headers, SSL redirection, Browser XSS filter, etc) podem ser adicionados e configurados por frontend de forma semelhante aos custom headers acima. Essa funcionalidade permite que algumas features simples de segurança sejam rapidamente configuradas.
Um exemplo de alguns security headers:
[frontends] [frontends.frontend1] backend = "backend1" [frontends.frontend1.headers] FrameDeny = true [frontends.frontend1.routes.test_1] rule = "PathPrefixStrip:/cheddar" [frontends.frontend2] backend = "backend2" [frontends.frontend2.headers] SSLRedirect = true [frontends.frontend2.routes.test_1] rule = "PathPrefixStrip:/stilton"
Neste exemplo, o tráfego roteado através do primeiro frontend terá o header X-Frame-Options
setado para DENY
, e o segundo permitirá somente a solicitação HTTPS, caso contrário retornará um redirecionamento 301 HTTPS.
A documentação detalhada dos security headers podem ser encontradas em: unrolled/secure.
>> Backend
Um backend é responsável por equilibrar a carga do tráfego que vem de um ou mais frontends para um conjunto de servidores http.
Vários métodos de load-balancing são suportados:
wrr
: Weighted Round Robin: Por pesodrr
: Dynamic Round Robin: Aumenta o peso em servidores que possuem uma melhor performance que outros. Também retorna aos pesos originais se os servidores tiverem mudado.
Um circuit breaker ou disjuntor pode ser aplicado ao backend, impedindo altas cargas em servidores com falha. O estado inicial é Standby. O CB (circuit breaker) observa as estatísticas e não modifica o pedido. Caso a condição corresponda, o CB entra no Tripped state, onde responde com um código pré definido ou redireciona para outro frontend. Quando o temporizador Tripped expirar, o CB entra no estado de recovery e reseta todas as estatísticas. Caso a condição não corresponda e o temporizador de recuperação expire, o CB entra no estado de espera.
Pode ser configurado usando:
- Métodos:
LatencyAtQuantileMS
,NetworkErrorRatio
,ResponseCodeRatio
- Operadores:
AND
,OR
,EQ
,NEQ
,LT
,LE
,GT
,GE
Por exemplo:
NetworkErrorRatio() > 0.5
: Assiste o error ratio em uma janela de 10 segundos para um frontendLatencyAtQuantileMS(50.0) > 50
: Assiste a latência em quantile em milissegundos.ResponseCodeRatio(500, 600, 0, 600) > 0.5
: relação dos códigos de resposta no range [500-600) to [0-600)
Para impedir proativamente que os backends sejam sobrecarregados com alta carga, um limite de conexão máximo também pode ser aplicado a cada backend.
As conexões máximas podem ser configuradas especificando um valor inteiro para maxconn.amount
e maxconn.extractorfunc
que é uma estratégia usada para determinar como categorizar solicitações para avaliar as conexões máximas.
Por exemplo:
[backends] [backends.backend1] [backends.backend1.maxconn] amount = 10 extractorfunc = "request.host"
backend1
retornaráHTTP code 429 Too Many Requests
se já houver 10 pedidos em progresso para um mesmo Host header.- Outro valor possível para
extractorfunc
éclient.ip
que classificará as solicitações com base no IP do origem do cliente. - Finalmente o
extractorfunc
pode levar o valor derequest.header.ANY_HEADER
que classificará os pedidos com base emANY_HEADER
que você fornece.
Sticky sessions
Sticky sessions são suportados pelo 2 load balancers.
Quando sticky sessions está habilitado, um cookie é definido na solicitação inicial. O nome padrão do cookie é uma abreviação de um sha1 (ex: _1d52e
). Em pedidos sub sequentes, o cliente será direcionado para o backend armazenado no cookie se estiver saudável. Se não, um novo backend será atribuído.
[backends] [backends.backend1] # Enable sticky session [backends.backend1.loadbalancer.stickiness] # Customize the cookie name # # Optional # Default: a sha1 (6 chars) # # cookieName = "my_cookie"
Forma antiga (deprecated):
[backends] [backends.backend1] [backends.backend1.loadbalancer] sticky = true
Health Check
Um health check pode ser configurado para remover um backend da rotação do LB, desde que ele continue retornando HTTP status codes diferentes de 200 OK
para pedidos HTTP GET periodicamente executados pelo Traefik.
O check é definido por um path anexado à URL do backend e um intervalo (fornecido em um formato compreendido por time.ParseDuration) especificando a frequência com que a verificação de integridade deve ser executada (o padrão é de 30 segundos). Cada backend deve responder o health check dentro de 5 segundos.
Por padrão, a porta do servidor backend é usada, mas isso pode substituído.
Um backend de recuperação que retorna respostas 200 OK novamente, é retornado para o pool de rotação LB.
Por exemplo:
[backends] [backends.backend1] [backends.backend1.healthcheck] path = "/health" interval = "10s"
Para usar uma porta diferente no healthcheck:
[backends] [backends.backend1] [backends.backend1.healthcheck] path = "/health" interval = "10s" port = 8080
Servers
Os servidores são simplesmente definidos usando uma url. Você também pode aplicar um peso personalizado a cada servidor (isso será usado pelo balanceamento de carga).
NOTA: Paths na url
são ignorados. Use o Modifier
para especificar paths ao invés disso.
Aqui está em exemplo de definição de backends e servers:
[backends] [backends.backend1] [backends.backend1.circuitbreaker] expression = "NetworkErrorRatio() > 0.5" [backends.backend1.servers.server1] url = "http://172.17.0.2:80" weight = 10 [backends.backend1.servers.server2] url = "http://172.17.0.3:80" weight = 1 [backends.backend2] [backends.backend2.LoadBalancer] method = "drr" [backends.backend2.servers.server1] url = "http://172.17.0.4:80" weight = 1 [backends.backend2.servers.server2] url = "http://172.17.0.5:80" weight = 2
- Dois backends são definidos:
backend1
ebackend2
backend1
encaminhará o tráfego para 2 servers:http://172.17.0.2:80"
com peso10
ehttp://172.17.0.3:80
com peso1
usando a estratégia default dewrr
load-balancing.backend2
encaminhará o tráfego para 2 servers:http://172.17.0.4:80"
com peso1
ehttp://172.17.0.5:80
com peso2
usando a estratégia dedrr
load-balancing.- Um circuit breaker é adicionado no
backend1
usando a expressãoNetworkErrorRatio() > 0.5
: watch error ratio over 10 second sliding window
Configuração
A configuração da Træfik tem duas partes:
- A configuração estática que é carregada apenas no inicio.
- A configuração dinâmica que pode ser carregado a quente (não é necessário restartar o processo).
Static Træfik configuration
A configuração estática é a configuração global que está configurando conexões para backends e entrypoints.
Træfik pode ser configurado usando muitas fontes de configuração com a seguinte ordem de procedência. Cada item tem procedência sobre o item abaixo:
- Key-value store
- Arguments
- Configuration file
- Default
Isso significa que os argumentos substituem o arquivo de configuração e o armazenamento de chave-valor substitui os argumentos..
NOTA: os parâmetros de argumento que habilitam o provedor (ex, --docker
) definem todos os valores padrão para o provider específico.
Não deve ser usado se uma fonte de configuração com menos precedência desejar definir um valor de provedor não padrão.
Arquivo de configuração
Por padrão, o Træfik irá tentar encontrar um traefik.toml
nos seguintes lugares:
/etc/traefik/
$HOME/.traefik/
.
O diretório de trabalho
Você pode substituir isso com um argumento de configFile
:
traefik --configFile=foo/bar/myconfigfile.toml
Please refer to the global configuration section to get documentation on it.
Arguments
Cada argumento (e comando) é descrito na sessão “help”:
traefik --help
Observe que todos os valores padrão também serão exibidos.
Key-value stores
Træfik tem suporte a vários armazenadores de Key-value (chave-valor):
Consulte a documentação: User Guide Key-value store configuration.
Dynamic Træfik configuration
A configuração dinâmica se concentra em:
Træfik pode fazer o hot-reload das regras que podem ser fornecidades por multiple configuration backends.
Nós precisamos ativar a opção watch
para fazer alterações do backend de configuração do Træfik e gerar as configuraões automaticamente. As rotas para serviços serão criadas e atualizadas instantaneamente em qualquer alteração.
Consulte a documentação de configuration backends.
Commands
traefik
Uso:
traefik [command] [--flag=flag_argument]
Lista dos comandos disponíveis do Træfik com descrição:
version
: Exibe a versãostoreconfig
: Armazena a configuração estática do Traefik com Key-value stores. Consulte a documentação Store Træfik configuration.bug
: A maneira mais fácil de submeter uma issue preenchida ao Traefik GitHub.healthcheck
: Chama o/ping
para o healthcheck.
Cada comando pode ter sinalizadores relacionados.
Todas essas flags relacionadas serão exibidas com:
traefik command --help
Cada comando é descrito no início da seção de ajuda:
traefik --help
Comando: bug
A maneira mais fácil de enviar um issue preenchida ao Træfik GitHub.
traefik bug
Assista esta demo.
Comando: healthcheck
Isso pode ser usado com a instrução Docker HEALTHCHECK ou qualquer outro mecanismo de orquestração de verificação de saúde.
Este comando permite checar a saúde do Traefik. Seu exit status é 0
se o Traefik está saudável e 1
se não está saudável.
Isso pode ser usado com a instrução Docker HEALTHCHECK ou qualquer outro mecanismo de orquestração de verificação de saúde.
Nota: O web
provider precisa estar habilitado para permitir as chamadas /ping
do comando healthcheck
.
traefik healthcheck OK: http://:8082/ping
>> Conclusão
É isso, procurei de fato seguir o que estava na documentação para torná-la mais acessível fazendo com que mais pessoas conheçam um pouco mais dessa ferramenta que tem se destacado bastante no cenário atual da tecnologia.
Gostou? Comente e compartilhe, e nos ajude a divulgar nosso trabalho, isso é importante para a comunidade!
Abraços!
31/05/2019 at 10:18 am
Rodrigo Floriano,
Estou tendo problema para o redirecionamento para instancias de backend que se encontram em Workrs e não em managers. Quando a instancia de uma api se encontra em um dos managers do cluster a requisição retorna numa boa, mas quando esta instancia se encontra em alguma worker do cluster, a requisição finaliza com um timeout. Isso pode ser alguma configuração do traefik? Ele reconhece essas máquinas e apresenta as mesmas em sua console normalmente, mas não completa as requisições. Não estou conseguindo resolver isso, poderia comentar sobre?
CurtirCurtir
31/05/2019 at 9:23 pm
Boa noite Roberto!
Eu não cheguei a evoluir muito com o Traefik, pois pela minha necessidade usando Kubernetes eu optei pelo Haproxy Ingress, então acredito que não serei preciso no auxílio da sua dúvida.
Entre em nosso grupo do Telegram e coloque sua dúvida para que o pessoal do grupo possa te auxiliar!
t.me/churrops
Grato pelo contato, abraço!
CurtirCurtir
19/08/2019 at 9:40 am
Artigo Top demais!!
CurtirCurtido por 1 pessoa