Quando um cluster Kubernetes hospeda serviços voltados para a internet e serviços internos ao mesmo tempo, a abordagem mais limpa é dividir o tráfego de entrada no nível do ingress controller. Para isso, você pode implantar duas instâncias separadas do Traefik: uma para lidar com o ingress público e outra para o ingress privado.
Este guia apresenta essa configuração passo a passo. Você vai implantar dois releases Helm do Traefik com IngressClasses diferentes e, em seguida, rotear o tráfego para os serviços públicos e internos.
Duas instâncias independentes do Traefik são executadas no cluster:
traefik-public processa apenas recursos Ingress com ingressClassName: publictraefik-private processa apenas recursos Ingress com ingressClassName: privateCada instância cria seu próprio serviço do tipo LoadBalancer com parâmetros diferentes:
Dessa forma, você pode usar um único cluster tanto para aplicações externas quanto internas, sem misturar seus pontos de entrada.
Antes de começar, certifique-se de que você tem:
As duas instâncias do Traefik são implantadas a partir do mesmo chart Helm, mas com arquivos de values separados.
Crie um arquivo chamado traefik-public-values.yaml com o seguinte conteúdo:
fullnameOverride: traefik-public
ingressClass:
enabled: true
isDefaultClass: false
name: public
service:
enabled: true
type: LoadBalancer
providers:
kubernetesCRD:
enabled: true
allowCrossNamespace: false
ingressClass: public
kubernetesIngress:
enabled: true
ingressClass: public
publishedService:
enabled: true
Esse arquivo configura a instância pública do Traefik para processar recursos ingress da classe public e criar um LoadBalancer padrão com IP externo.
Parâmetros importantes:
ingressClass.name: public define o nome da classe que os recursos Ingress públicos vão referenciarproviders.kubernetesCRD.ingressClass e providers.kubernetesIngress.ingressClass restringem o Traefik apenas aos recursos da sua própria classepublishedService.enabled: true permite a publicação correta do endereço do serviço de ingressCrie um arquivo chamado traefik-private-values.yaml com o seguinte conteúdo:
fullnameOverride: traefik-private
ingressClass:
enabled: true
isDefaultClass: false
name: private
service:
enabled: true
type: LoadBalancer
annotations:
k8s.hostman.com/attached-loadbalancer-no-external-ip: "true"
providers:
kubernetesCRD:
enabled: true
allowCrossNamespace: false
ingressClass: private
kubernetesIngress:
enabled: true
ingressClass: private
publishedService:
enabled: true
Esse arquivo configura a instância privada do Traefik para processar recursos ingress da classe private e criar um load balancer interno sem IP público.
O parâmetro principal aqui é k8s.hostman.com/attached-loadbalancer-no-external-ip: "true", que instrui a plataforma a provisionar um load balancer interno sem endereço IP público.
Adicione o repositório Helm do Traefik:
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
Implante a instância pública:
helm install traefik-public traefik/traefik \
-n traefik-public --create-namespace \
-f traefik-public-values.yaml
Implante a instância privada:
helm install traefik-public traefik/traefik \
-n traefik-public --create-namespace \
-f traefik-public-values.yaml
Após a instalação, verifique se os serviços do tipo LoadBalancer foram criados:
kubectl get svc -n traefik-public
kubectl get svc -n traefik-private
O provisionamento do load balancer pode levar até 10 minutos. A coluna
EXTERNAL-IPpode exibirpendingenquanto o recurso está sendo criado.
O comportamento esperado é diferente para cada instância:
traefik-public deve receber um endereço IP externotraefik-private não receberá IP externo, pois utiliza um load balancer internoVocê também pode acompanhar o status dos load balancers pelo painel de controle da Hostman. Aguarde a conclusão do provisionamento antes de prosseguir.
Nessa etapa, confirme também que as duas classes de ingress estão registradas no cluster:
kubectl get ingressclass
A saída deve listar tanto public quanto private.
Com as duas instâncias do Traefik em execução, basta definir o ingressClassName adequado em cada manifesto Ingress.
Para um serviço público:
spec:
ingressClassName: public
Para um serviço privado:
spec:
ingressClassName: private
Todo o tráfego será roteado automaticamente pelo ingress controller correspondente.
Os manifestos abaixo demonstram a configuração completa. O exemplo inclui:
service1 e service2service3IngressClasses diferentesCrie config-map.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: service-config
namespace: ingress-example
data:
service1.html: |
<html>
<head><title>Service 1</title></head>
<body><h1>Welcome to Service 1!</h1></body>
</html>
service2.html: |
<html>
<head><title>Service 2</title></head>
<body><h1>Welcome to Service 2!</h1></body>
</html>
service3.html: |
<html>
<head><title>Service 3</title></head>
<body><h1>Welcome to Service 3!</h1></body>
</html>
Crie service1-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service1
namespace: ingress-example
spec:
replicas: 2
selector:
matchLabels:
app: service1
template:
metadata:
labels:
app: service1
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /usr/share/nginx/html
volumes:
- name: config-volume
configMap:
name: service-config
items:
- key: service1.html
path: service1.html
---
apiVersion: v1
kind: Service
metadata:
name: service1
namespace: ingress-example
spec:
selector:
app: service1
ports:
- protocol: TCP
port: 80
targetPort: 80
Crie service2-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service2
namespace: ingress-example
spec:
replicas: 2
selector:
matchLabels:
app: service2
template:
metadata:
labels:
app: service2
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /usr/share/nginx/html
volumes:
- name: config-volume
configMap:
name: service-config
items:
- key: service2.html
path: service2.html
---
apiVersion: v1
kind: Service
metadata:
name: service2
namespace: ingress-example
spec:
selector:
app: service2
ports:
- protocol: TCP
port: 80
targetPort: 80
Crie service3-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service3
namespace: ingress-example
spec:
replicas: 2
selector:
matchLabels:
app: service3
template:
metadata:
labels:
app: service3
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /usr/share/nginx/html
volumes:
- name: config-volume
configMap:
name: service-config
items:
- key: service3.html
path: index.html
---
apiVersion: v1
kind: Service
metadata:
name: service3
namespace: ingress-example
spec:
selector:
app: service3
ports:
- protocol: TCP
port: 80
targetPort: 80
Crie ingress-public.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-public-ingress
namespace: ingress-example
spec:
ingressClassName: public
rules:
- host: ingress1.example.com
http:
paths:
- path: /service1
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
- path: /service2
pathType: Prefix
backend:
service:
name: service2
port:
number: 80
Observe que spec.ingressClassName está definido como public, o que significa que este Ingress será processado pelo ingress controller público.
Crie ingress-private.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-private-ingress
namespace: ingress-example
spec:
ingressClassName: private
rules:
- host: ingress2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service3
port:
number: 80
Aqui spec.ingressClassName está definido como private, portanto este Ingress será processado pelo ingress controller privado.
Crie o namespace ingress-example:
kubectl create namespace ingress-example
Aplique todos os manifestos:
kubectl apply -f config-map.yaml
kubectl apply -f service1-deployment.yaml
kubectl apply -f service2-deployment.yaml
kubectl apply -f service3-deployment.yaml
kubectl apply -f ingress-public.yaml
kubectl apply -f ingress-private.yaml
Verifique se os deployments, serviços e recursos de ingress foram criados:
kubectl get deploy,svc,ingress -n ingress-example
Você deve ver três serviços, três deployments e dois recursos de ingress. Se algum estiver faltando, o manifesto correspondente não foi aplicado ou contém um erro.
Com essa configuração:
ingress1.example.com/service1... são roteadas para o service1ingress1.example.com/service2... são roteadas para o service2ingress2.example.com/ são roteadas para o service3, acessível apenas dentro da rede privada via load balancer interno do TraefikAntes de enviar requisições de teste, confirme que cada ingress tem a classe correta atribuída:
kubectl describe ingress example-public-ingress -n ingress-example
kubectl describe ingress example-private-ingress -n ingress-example
Certifique-se de que example-public-ingress exibe Ingress Class: public e example-private-ingress exibe Ingress Class: private. Se a classe não corresponder, o Traefik não vai processar esse recurso.
O exemplo usa
ingress1.example.comeingress2.example.comcomo hostnames fictícios. Você pode utilizá-los comcurl --resolvesem precisar configurar DNS real. Se quiser abrir o ingress público no navegador, substituaingress1.example.compor um domínio real e configure um registro DNS apontando para o IP do load balancer público.
Quando o load balancer público tiver um IP externo, teste as rotas:
curl http://ingress1.example.com/service1.html \
--resolve ingress1.example.com:80:PUBLIC_LB_EXTERNAL_IP
curl http://ingress1.example.com/service2.html \
--resolve ingress1.example.com:80:PUBLIC_LB_EXTERNAL_IP
Onde PUBLIC_LB_EXTERNAL_IP é o IP externo atribuído ao load balancer público.
Se você receber um erro 404, verifique se está usando o hostname ingress1.example.com e os caminhos /service1 ou /service2.
Para testar o serviço privado, execute o comando abaixo a partir de uma máquina dentro da mesma rede privada do cluster:
curl http://ingress2.example.com/ \
--resolve ingress2.example.com:80:PRIVATE_LB_IP
Onde PRIVATE_LB_IP é o IP privado do load balancer interno.
Você deve receber uma página HTML servida pelo service3.
Se a rota privada não responder, confirme que a requisição está partindo de dentro da rede privada. Tráfego externo não consegue alcançar esse ingress, mesmo que os recursos no cluster estejam configurados corretamente.