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.
- Descomplicando o Kubernetes
- O que é o Cert-Manager?
- O que são os Annotations e as Labels no Kubernetes?
- Final do Day-10
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.
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.yamlVamos confirmar se o Cert-Manager foi instalado corretamente.
kubectl get pods -n cert-managerCom 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: [email protected]
# 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: nginxapiVersion: 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: [email protected]
# 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: nginxAplique os arquivos no seu cluster.
kubectl apply -f issuer-staging.yaml
kubectl apply -f cluster-issuer-prod.yamlCom os Issuers e ClusterIssuers criados, podemos obter mais informações sobre eles.
kubectl get issuers
kubectl get clusterissuerskubectl describe issuer letsencrypt-staging
kubectl describe clusterissuer letsencrypt-prodJá 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: 5000Aplique o arquivo no seu cluster.
kubectl apply -f ingress.yamlAgora, vamos verificar se o Cert-Manager criou o certificado para o nosso domínio.
kubectl get certificates
kubectl describe certificate giropops-containers-expert-tlsTambé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-tlsCom 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.
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.
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: 5000Se 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=kubernetesNesse 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: 5000Agora, 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=gostosoOu 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=gostosoTambé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=lindoE 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 --overwriteE 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.
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" --overwriteE 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-senhasVamos 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: 5000Agora, vamos criar o usuário e a senha para a autenticação utilizando o comando htpasswd do Apache.
htpasswd -c auth jefersonO 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=authAplique o arquivo no seu cluster.
kubectl apply -f ingress2.yamlAgora, 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.
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: 5000Aplique o arquivo no seu cluster.
kubectl apply -f ingress3.yamlPara 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.expertNa 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.
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: 5000Aplique o arquivo no seu cluster.
kubectl apply -f ingress4.yamlAlé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.
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: 80Como 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=80kubectl expose deployment nginx --port=80Agora 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.yamlAcessando 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.
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: 5000Aplique o arquivo no seu cluster.
kubectl apply -f ingress6.yamlAgora, 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.
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.