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.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: [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: 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: [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: 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
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.
As Annotations
e Labels
são recursos do Kubernetes que permitem adicionar metadados aos recursos do cluster.
-
As
Annotations
sã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
Labels
também são pares chave-valor, mas são utilizadas para identificar e selecionar recursos do cluster. Como por exemplo, adicionar uma labelapp: giropops
para 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: 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
.
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
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.
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.
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
.
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.
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.
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
.