Nesse post, irei mostrar como disponibilizar um blog com acesso HTTPS em um servidor utilizando Kubernetes, Traefik e CertManager.
Primeiramente precisamos ter o Kubernetes instalado no servidor. Uma boa opção é utilizar o k3s, uma distribuição do kubernetes bastante leve e eficiente.
Antes de inciar a instalação, é recomendado atualizar os pacotes e instalar o curl:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl
O k3s vem com uma versão do Traefik instalada por padrão que não permite algumas configurações necessárias, por isso, precisamos especificar que não queremos ele:
sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/config.yaml > /dev/null <<'EOF'
disable:
traefik
EOF
Agora sim podemos fazer a instalação:
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644" sh -
Para verificar se ele foi instalado corretamente, rode o comando:
sudo systemctl status k3s --no-pager
Para poder executar os comandos do kubernetes usando o kubectl sem o sudo, podemos configurar para o usuário:
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
Com o kubernetes instalado, já podemos rodar os manifestos para criar os nossos deployments, services, etc. Porém, teríamos que ter esses manifestos dentro do servidor, o que não é muito prático. Para poder executar os manifestos de fora do servidor (de forma remota), basta ajustar o arquivo de configuração do kubernetes da máquina onde você quer executar os comandos remotamente.
Primeiro, precisamos pegar o arquivo de configuração do k3s que está dentro do servidor na pasta /etc/rancher/k3s/k3s.yaml. Ele vai ter um conteúdo mais ou menos assim:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRU... (um bloco grande em base64)
server: https://127.0.0.1:6443
name: defaultPrecisamos copiar esses trechos de código dentro do arquivo ~/.kube/config (no windows: C:\Users\<usuario>\.kube\config).
Atenção: não substitua o conteúdo do seu arquivo de configuração com o que está no servidor, e sim adicione os trechos que estão no servidor nos respectivos trechos que estão no arquivo de configuração da sua máquina.
Agora iremos rodar os manifestos de deployment e service da nossa aplicação diretamente no kubernetes dentro do servidor.
Primeiro, como boa prática, vamos rodar o namespace.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: blog
Em seguida, vamos rodar o deployment.yaml, responsável por criar o POD onde a aplicação vai ser executada:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-app-deployment
namespace: blog
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: blog-app
template:
metadata:
labels:
app: blog-app
spec:
containers:
- name: blog-app
image: [suaimagem]:[versao]
ports:
- containerPort: 8080
Agora vamos rodar o service.yml, que irá permitir que o POD seja acessado internamente pelo Ingress Traefik:
apiVersion: v1
kind: Service
metadata:
name: blog-app
namespace: blog
spec:
type: ClusterIP
selector:
app: blog-app
ports:
- protocol: TCP
port: 8080
targetPort: 8080
nodePort: 31001
O Helm é uma ferramenta que ajuda a gerenciar as aplicações no kubernetes. Nesse momento, ele vai nos ajudar a instalar o Traefik e o cert-manager.
Para instalá-lo, basta rodar o comando:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Para verificar se ele foi instalado com sucesso:
helm version
Agora vamos instalar o Traefik. Ele vai funcionar como um proxy reverso, redirecionando as requisições para os devidos pods. Ele é muito útil pois, a partir de um único domínio, podemos configurar diversos subdomínios. Ou seja, supondo que temos o domínio seudominio.com, podemos ter blog.seudominio.com, forum.seudominio.com, etc.
Para instalá-lo utilizando o Helm, primeiro precisamos adicionar o repositório dele:
helm repo add traefik https://traefik.github.io/charts
helm repo update
Vamos criar um arquivo de configuração na pasta ~/kubernets-confs para realizar a instalação do Traefik chamado traefik-values.yaml. Nesse arquivo, vemos expor a porta web por padrão, além de habilitar o dashboard do Traefik, uma ferramenta que ajuda a monitorar o funcionamento da ferramenta:
# Configuração de portas do Traefik
ports:
web:
expose:
default: true # Expõe a porta web por padrão
entryPoints:
- web # Define o entrypoint HTTP padrão
# Configuração do IngressRoute para o dashboard do Traefik
ingressRoute:
dashboard:
enabled: true # Habilita o dashboard do Traefik
entryPoints: [web, websecure] # Permite acesso via HTTP e HTTPS
matchRule: Host(`traefik-dashboard.lucasferraz.blog`) # Domínio para acessar o dashboard
middlewares:
- name: dashboard-auth # Aplica autenticação básica ao dashboard
Por segurança, adicionamos uma autenticação básica no dashboard. Para que isso funcione, precisamos criar também um manifesto para definir esse mecanismo de autenticação:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: dashboard-auth
namespace: traefik
spec:
basicAuth:
secret: dashboard-auth-secret
---
apiVersion: v1
kind: Secret
metadata:
name: dashboard-auth-secret
namespace: traefik
type: Opaque
data:
users: YWRtaW46JGFwcjEkQ0ZNallHT3kkVUF1OGlhN0VvWkpoM0Q5bXJDdThULw== # admin:admin
Em seguida, rodar o comando de instalação:
helm install traefik traefik/traefik -n traefik --create-namespace --set service.type=LoadBalancer -f ~/kubernets-confs/traefik-values.yaml
Para verificar se a instalação funcionou:
kubectl get pods -n traefik
Agora vamos criar os manifestos do Ingress e do IngressRoute para verificar se o Traefik está funcionando realmente.
ingress.yml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog-ingress
namespace: blog
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
rules:
- host: blog.lucasferraz.blog
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blog-app
port:
number: 8080
ingressroute.yml:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: blog-ingressroute
namespace: blog
spec:
entryPoints:
- websecure
routes:
- match: Host(`blog.lucasferraz.cloud`)
kind: Rule
services:
- name: blog-app
port: 8080
Agora já podemos tentar acessar o endereço blog.lucasferraz.com (ou o domínio que você estiver configurando) e verificar se tudo funcionou corretamente. Se sim, você vai ser direcionado para a aplicação, mas com HTTP ao invés de HTTPS.
Para que o Lets Encrypt possa garantir o certificado HTTPS, precisamos que o DNS da aplicação esteja sendo gerenciado pela Cloudflare, que possui uma API que será utilizada para validar o TLS. Não se preocupe, pois a Cloudflare oferece esses serviços gratuitamente e a configuração é simples.
Para isso, crie uma conta na Cloudflare (caso não tenha), clique em Add > Connect a Domain, em seguida insira o domínio e configure os records. No nosso caso, precisamos adicionar um record to tipo A, onde o nome seja blog e o valor seja o IP do servidor.
Após isso, é necessário obter um token de acesso: ícone do perfil > Profile > API Tokens > Create token > Edit Zone DNS > Em zone resources, selecione o DNS > Continue to summary > Confirm.
Pronto, agora copie o token gerado pois será utilizado daqui a pouco.
O cert-manager vai ficar responsável por gerenciar os certificados das aplicações rodando no kubernetes, permitindo que elas sejam acessadas via HTTPS ao invés de HTTP.
Primeiro adicionamos o repositório do Helm:
helm repo add jetstack https://charts.jetstack.io --force-update
Agora vamos definir o arquivo de configurações ~/kubernets-confs/certmanager-values.yaml:
# Define o namespace onde o cert-manager será instalado
namespace: "cert-manager"
# Configuração dos CRDs (Custom Resource Definitions)
crds:
enabled: true # Instala automaticamente os CRDs necessários para o cert-manager
Em seguida, instalamos usando o comando:
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --values ~/kubernets-confs/certmanager-values.yaml
Para verificar se instalou corretamente:
kubectl get pods -n cert-manager
Agora vamos configurar o Issuer e o ClusterIssuer.
Primeiro, criamos o arquivo certmanager-issuer-secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: [token da Cloudflare]
Em seguida, rodamos o certmanager-clusterissuer.yaml:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cloudflare-clusterissuer
spec:
acme:
email: [seu@email]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: cloudflare-clusterissuer-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
Agora vamos configurar um manifesto para a geração do certificado que será utilizado no IngressRoute.
Vamos criar o arquivo certificate.yaml:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: blog-ingressroute-certificate
namespace: blog
spec:
secretName: blog-certificate-secret
issuerRef:
name: cloudflare-clusterissuer # aqui deve ser o mesmo nome do ClusterIssue definido anteriormente
kind: ClusterIssuer
dnsNames:
- blog.lucasferraz.cloud # ou o domínio que você estiver configurando
Agora precisamos ajustar o arquivo do manifesto do IngressRoute para utilizar esse certificado. O arquivo ingressroute.yaml ficará assim:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: blog-ingressroute
namespace: blog
spec:
entryPoints:
- websecure
routes:
- match: Host(`blog.lucasferraz.cloud`)
kind: Rule
services:
- name: blog-app
port: 8080
tls:
secretName: blog-certificate-secret # aqui tem que ser o mesmo nome definido no manifesto do certificado
Após executar o manifesto, já deve ser possível acessar a URL via HTTPS.
Pronto, agora sua aplicação pode ser acessada de forma segura!
→ https://www.youtube.com/watch?v=vJweuU6Qrgo
→ https://cert-manager.io/docs/