Descomplicando o Kubernetes
DAY-10: Descomplicando Ingress com TLS, Labels, Annotations e o Cert-manager
Conteúdo do Day-10
O que iremos ver hoje?
Agora que você já sabe o que é o ingress e como ele funciona, vamos adicionar o Cert-Manager com o Let's Encrypt ao cluster e criar um certificado SSL para o nosso domínio. Além disso, vamos aprender o que são e como funcionam as Annotations e Labels no Kubernetes.
Conteúdo do Day-10
- Descomplicando o Kubernetes
- O que é o Cert-Manager?
- O que são os Annotations e as Labels no Kubernetes?
- Final do Day-10
O que é o Cert-Manager?
O Cert-Manager é um controlador do Kubernetes que automatiza a solicitação, emissão, renovação e rotação de certificados TLS de uma maneira muito fácil. Ele é capaz de gerenciar certificados de diferentes autoridades de certificação, como o Let's Encrypt, Venafi, Vault, entre outros. Além disso, o Cert-Manager é um projeto open-source membro da Cloud Native Computing Foundation (CNCF).
O Cert-Manager utiliza dois tipos recursos do Kubernetes para gerenciar os certificados TLS: Issuers e ClusterIssuers. Os Issuers são recursos que permitem a emissão de certificados para um único namespace, enquanto os ClusterIssuers permitem a emissão de certificados para todos os namespaces do cluster.
Enquanto estamos desenvolvendo a nossa aplicação, é uma boa prática utilizar um Issuer para o ambiente de desenvolvimento e um ClusterIssuer para o ambiente de produção. Dessa forma, podemos testar a emissão de certificados no ambiente de desenvolvimento e garantir que tudo está funcionando corretamente antes de ir para o ambiente de produção. Já que a utilização de um ClusterIssuer de produção no ambiente de desenvolvimento pode acabar bloqueando a emissão de certificados devido aos limites pré-estabelecidos pela autoridade certificadora.
Vamos utilizar o Let's Encrypt como autoridade de certificação para o nosso ambiente de desenvolvimento, para isso precisamos entender como é feita a verificação do domínio. O Let's Encrypt utiliza o protocolo ACME (Automatic Certificate Management Environment) para verificar a propriedade do domínio. O ACME utiliza dois tipos de desafios para verificar a propriedade do domínio: HTTP-01 e DNS-01.
O desafio HTTP-01 é feito através da criação de um arquivo com um token específico no servidor web que está respondendo pelas requisições do domínio. Já o desafio DNS-01 é feito através da criação de um registro TXT no servidor de DNS do domínio.
Instalando e configurando o Cert-Manager
Para instalar o Cert-Manager no seu cluster, você pode utilizar o Helm ou instalar diretamente com o kubectl. No nosso caso, vamos instalar o Cert-Manager utilizando o Kubectl.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.1/cert-manager.yaml
Vamos confirmar se o Cert-Manager foi instalado corretamente.
kubectl get pods -n cert-manager
Com o Cert-Manager instalado, vamos criar o Issuer para o ambiente de desenvolvimento e o ClusterIssuer para o ambiente de produção.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: example@mail.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: example@mail.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
Aplique os arquivos no seu cluster.
kubectl apply -f issuer-staging.yaml
kubectl apply -f cluster-issuer-prod.yaml
Com os Issuers e ClusterIssuers criados, podemos obter mais informações sobre eles.
kubectl get issuers
kubectl get clusterissuers
kubectl describe issuer letsencrypt-staging
kubectl describe clusterissuer letsencrypt-prod
Configurando o Ingress para usar o Cert-Manager e ter o HTTPS
Já temos o Cert-Manager instalado e configurado, agora precisamos configurar o Ingress para utilizar o Cert-Manager e ter o HTTPS. Para isso, vamos criar um Ingress para a nossa aplicação e adicionar a anotação cert-manager.io/cluster-issuer utilizando o ClusterIssuer que criamos anteriormente.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000
Aplique o arquivo no seu cluster.
kubectl apply -f ingress.yaml
Agora, vamos verificar se o Cert-Manager criou o certificado para o nosso domínio.
kubectl get certificates
kubectl describe certificate giropops-containers-expert-tls
Também é possível ver as Order criadas pelo Cert-Manager para a emissão do certificado.
kubectl get orders
kubectl describe order giropops-containers-expert-tls
Com o Cert-Manager configurado e o Ingress utilizando o ClusterIssuer para a emissão de certificados, a nossa aplicação já está disponível com o HTTPS. Podemos verificar o certificado acessando a aplicação pelo navegador e clicando no cadeado ao lado do domínio.
O que são os Annotations e as Labels no Kubernetes?
As Annotations e Labels são recursos do Kubernetes que permitem adicionar metadados aos recursos do cluster.
As
Annotationssão pares chave-valor que podem ser utilizados para adicionar metadados adicionais aos recursos do cluster. Como por exemplo, adicionar informações sobre a versão da aplicação, parâmetros de configuração, entre outros.As
Labelstambém são pares chave-valor, mas são utilizadas para identificar e selecionar recursos do cluster. Como por exemplo, adicionar uma labelapp: giropopspara identificar todos os recursos relacionados a aplicaçãogiropops.
Explorando um pouco mais as Labels
As Labels são utilizadas para identificar e selecionar recursos do cluster. Elas são utilizadas para identificar recursos de maneira mais fácil e rápida. Por exemplo, podemos adicionar a label jeferson: gostoso para identificar os recursos relacionados a aplicação giropops.
apiVersion: apps/v1
kind: Deployment
metadata:
name: giropops-senhas
labels:
app: giropops
jeferson: gostoso
spec:
replicas: 3
selector:
matchLabels:
app: giropops
template:
metadata:
labels:
app: giropops
spec:
containers:
- name: giropops-senhas
image: containers-expert/giropops-senhas:1.0
ports:
- containerPort: 5000
Se quisermos listar os recursos que possuem a label jeferson: gostoso, podemos utilizar o comando kubectl get com a flag --selector.
kubectl get deployments --selector descomplicando=kubernetes
Nesse exemplo, adicionamos a label jeferson: gostoso ao Deployment da aplicação giropops. Mas e se quisermos adicionar a label jeferson: gostoso aos nossos Pods? Para isso, devemos adicionar a label no campo metadata do Pod, dentro de spec.
apiVersion: apps/v1
kind: Deployment
metadata:
name: giropops-senhas
labels:
app: giropops
jeferson: gostoso
spec:
replicas: 3
selector:
matchLabels:
app: giropops
template:
metadata:
labels:
app: giropops
jeferson: gostoso
spec:
containers:
- name: giropops-senhas
image: containers-expert/giropops-senhas:1.0
ports:
- containerPort: 5000
Agora, se quisermos listar os Pods que possuem a label jeferson: gostoso, podemos utilizar o comando kubectl get com a flag --selector.
kubectl get pods --selector jeferson=gostoso
Ou podemos listar todos os recursos que possuem a label jeferson: gostoso utilizando o comando kubectl get com a flag --selector e o nome do recurso.
kubectl get all --selector jeferson=gostoso
Também podemos adicionar uma label utilizando o comando kubectl label. Vamos então adicionar a label jeferson: lindo ao Pod da aplicação Redis.
kubectl label pods redis jeferson=lindo
E se precisarmos mudar o valor da label jeferson para gostoso? Podemos utilizar o comando kubectl label com a flag --overwrite.
kubectl label pods redis jeferson=gosotoso --overwrite
E podemos remover uma label utilizando o comando kubectl label com a flag -.
kubectl label pods redis jeferson-
Para uma lista de todas as opções disponíveis para o comando kubectl label, você pode utilizar o comando kubectl label --help.
Explorando as Annotations no Kubernetes
As annotation são utilizadas para adicionar metadados adicionais aos recursos do cluster. Elas são utilizadas para adicionar informações sobre a versão da aplicação, parâmetros de configuração, entre outros.
Vamos adicionar uma annotation ao Pod do Redis utilizando o comando kubectl annotate.
kubectl annotate pods redis description="Pod do redis para ser usado com o giropops-senhas"
Assim como subistituir o valor de uma Label, podemos subistituir o valor de uma annotation utilizando a flag --overwrite.
kubectl annotate pods redis description="Pod do redis" --overwrite
E para apagar uma annotation também é tão simples quanto apagar uma Label. Basta utilizar o comando kubectl annotate com a flag -.
kubectl annotate pods redis description-
Podemos utilizar o comando kubectl describe para ver as annotations e Labels de um recurso. Mas e se quisermos uma visão somente das annotations? Podemos utilizar o comando kubectl get com a flag -o com o formato jsonpath.
kubectl get pods redis -o jsonpath='{.metadata.annotations}'
Quando passamos o valor {.metadata.annotations} para a flag -o jsonpath, estamos pedindo para o kubectl retornar somente as annotations que estão no campo metadata do recurso Pod (identificado pelo .).
apiVersion: v1
kind: Pod
metadata:
annotations:
description: Pod do redis para ser usado com o giropops-senhas
Adicionando Autenticação ao Ingress
Vamos adicionar autenticação ao Ingress utilizando as Annotations do Nginx Ingress Controller. Utilizaremos a autenticação do tipo basic e um secret para armazenar os usuários e senhas.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/auth-type: "basic"
nginx.ingress.kubernetes.io/auth-secret: "giropops-senhas-users"
nginx.ingress.kubernetes.io/auth-realm: "Autenicação necessária"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000
Agora, vamos criar o usuário e a senha para a autenticação utilizando o comando htpasswd do Apache.
htpasswd -c auth jeferson
O comando htpasswd irá pedir para você digitar a senha do usuário. Após digitar a senha, o arquivo auth será criado no diretório onde você executou o comando. Agora, vamos criar um secret no Kubernetes com o arquivo auth.
kubectl create secret generic giropops-senhas-users --from-file=auth
Aplique o arquivo no seu cluster.
kubectl apply -f ingress2.yaml
Agora, a nossa aplicação giropops-senhas está protegida com autenticação do tipo basic. Se você tentar acessar a aplicação pelo navegador, será solicitado um usuário e senha.
Configurando Affinity Cookie no Ingress
Algumas vezes, precisamos garantir que um usuário sempre seja direcionado para o mesmo Pod da aplicação. Uma das maneiras de fazer isso é utilizando o Affinity Cookie do Nginx Ingress Controller. O Affinity Cookie é um cookie que é adicionado na resposta do Pod e utilizado para direcionar o usuário para o mesmo Pod na próxima requisição.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# nginx.ingress.kubernetes.io/auth-type: "basic"
# nginx.ingress.kubernetes.io/auth-secret: "giropops-senhas-users"
# nginx.ingress.kubernetes.io/auth-realm: "Autenicação necessária"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "giropops-cookie"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000
Aplique o arquivo no seu cluster.
kubectl apply -f ingress3.yaml
Para testar o Affinity Cookie, vamos utilizar o comando curl com a flag -v para ver os cabeçalhos da resposta.
curl -v https://giropops.containers.expert
Na saída do comando curl você verá a linha set-Cookie com o valor giropops-cookie. Esse é o cookie que será utilizado para direcionar o usuário para o mesmo Pod na próxima requisição.
Configurando Upsream Hashing no Ingress
Além do Affinity Cookie, o Nginx Ingress Controller também suporta o Upsream Hashing. O Upsream Hashing é uma técnica mais avançada que utiliza um valor do cabeçalho da requisição para direcionar o usuário para o mesmo Pod da aplicação. No nosso exemplo, vamos utilizar o $request_uri para identificar o Pod que irá responder a requisição por meio da URI da requisição.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# nginx.ingress.kubernetes.io/auth-type: "basic"
# nginx.ingress.kubernetes.io/auth-secret: "giropops-senhas-users"
# nginx.ingress.kubernetes.io/auth-realm: "Autenicação necessária"
# nginx.ingress.kubernetes.io/affinity: "cookie"
# nginx.ingress.kubernetes.io/session-cookie-name: "giropops-cookie"
nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000
Aplique o arquivo no seu cluster.
kubectl apply -f ingress4.yaml
Além do $request_uri, o Nginx Ingress Controller suporta outros mérodos para o Upsream Hashing. Você pode ver a lista completa na documentação do Nginx Ingress Controller.
Canary Deployments com o Ingress no Kubernetes
Os Canary Deployments são uma técnica de implantação que permite testar uma nova versão da aplicação em um subconjunto de usuários antes de implantar a nova versão para todos os usuários. No Kubernetes, podemos utilizar o Ingress para fazer Canary Deployments utilizando a anotação nginx.ingress.kubernetes.io/canary e nginx.ingress.kubernetes.io/canary-weight.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas-canary
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# nginx.ingress.kubernetes.io/auth-type: "basic"
# nginx.ingress.kubernetes.io/auth-secret: "giropops-senhas-users"
# nginx.ingress.kubernetes.io/auth-realm: "Autenicação necessária"
# nginx.ingress.kubernetes.io/affinity: "cookie"
# nginx.ingress.kubernetes.io/session-cookie-name: "giropops-cookie"
# nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
Como estamos direcionando o tráfego para o Nginx, precisamos criar o nosso Pod e expor ele, vamos utilizar os comandos kubectl run e kubectl expose:
kubectl run nginx --image=nginx --port=80
kubectl expose deployment nginx --port=80
Agora vamos criar o Ingress no nosso cluster que vai ser responsável por gerenciar o tráfego do Canary Deployment para o Pod do Nginx.
kubectl apply -f ingress5.yaml
Acessando a aplicação pelo navegador, você verá que 10% das requisições estão indo para o Pod do Nginx. Isso é o Canary Deployment em ação. Você pode aumentar o peso do Canary Deployment, alterando o valor da anotação nginx.ingress.kubernetes.io/canary-weight para o valor desejado.
Você pode utilizar o Canary Deployment em conjunto com o Affinity Cookie e/ou Upsream Hashing para garantir que o usuário sempre seja direcionado para o mesmo Pod durante o Canary Deployment, garantindo que ele sempre veja a mesma versão da aplicação.
Limitando requisições das nossas aplicações com o Ingress
Outra funcionalidade interessante do Nginx Ingress Controller é a capacidade de limitar o número de requisições que uma aplicação pode receber. Isso é útil para proteger a aplicação de ataques de negação de serviço (DDoS) e garantir que a aplicação sempre esteja disponível para os usuários.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/limit-rps: "2"
nginx.ingress.kubernetes.io/rewrite-target: /
# nginx.ingress.kubernetes.io/auth-type: "basic"
# nginx.ingress.kubernetes.io/auth-secret: "giropops-senhas-users"
# nginx.ingress.kubernetes.io/auth-realm: "Autenicação necessária"
# nginx.ingress.kubernetes.io/affinity: "cookie"
# nginx.ingress.kubernetes.io/session-cookie-name: "giropops-cookie"
# nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- giropops.containers.expert
secretName: giropops-containers-expert-tls
rules:
- host: giropops.containers.expert
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000
Aplique o arquivo no seu cluster.
kubectl apply -f ingress6.yaml
Agora, a nossa aplicação giropops-senhas está limitada a 2 requisições por segundo. Se você tentar fazer mais de 2 requisições por segundo, você verá o erro 503 Service Temporarily Unavailable. Claro que utilizar o valor 2 para o limit-rps pode não ser o ideal, você deve ajustar o valor para o que for mais adequado para a sua aplicação.
Final do Day-10
Durante o Day-10 você aprendeu como adicionar o Cert-Manager ao seu cluster e criar um certificado SSL para o seu domínio. Além disso, vimos o que são e como funcionam as Annotations e Labels no Kubernetes. Como adicionar a autenticação com usuário e senha. O Affinity Cookie e Upsream Hashing para direcionar o usuário para o mesmo Pod sempre que necessário. O Canary Deployments para testar uma nova versão da aplicação em um subconjunto de usuários antes de implantar a nova versão para todos os usuários. E por fim, você aprendeu como limitar as requisições nas suas aplicações com o limit-rps.