Skip to main content

Kubernetes Cluster com K3s no Homelab

· 10 min read
David Puziol Prata
DevSecOps at @ dLocal

Chega uma hora que a gente enjoa de instalar o cluster Kubernetes com o Kind, até porque o Kind não foi feito pra ser um cluster permanente. Ele é ótimo pra testes locais, mas não escala, não atualiza fácil e não guarda estado entre reinstalações. Se você quiser subir uma nova versão do Kubernetes, tem que destruir tudo, reinstalar e restaurar os backups — uma baita dor de cabeça.

Quando queremos manter várias ferramentas rodando, testar integrações ou usar GitOps de verdade, o ideal é ter um cluster persistente, que você possa formatar o host se precisar, mas manter o cluster em pé ou reerguer ele rapidinho. Aí entra o K3s.

alt text

O K3s é uma distribuição leve e simplificada do Kubernetes, criada pela Rancher, pensada especialmente para ambientes de edge, IoT, homelabs e até produção em clusters menores. Ele se destaca por ser fácil de instalar (um único binário), consumir menos recursos e vir com várias otimizações embutidas, como o uso do containerd no lugar do Docker e a inclusão de componentes essenciais como o traefik e flannel (que podem ser desativados se preferir customizar). Outra grande vantagem é que o K3s reduz a complexidade do setup tradicional do Kubernetes, sem perder compatibilidade com APIs e ferramentas do ecossistema. Ideal pra quem quer agilidade sem abrir mão da robustez.

Por que escolhi o K3s?

  • É leve e otimizado pra ambientes pequenos, ideal pra homelab ou até produção em edge.
  • Já vem com bateria incluída: containerd, flannel, traefik (opcional), servicelb, metrics-server e até o kubectl.
  • Instalação ridiculamente simples: um script resolve tudo, inclusive adicionando workers.
  • Atualizações automáticas (opcional, mas disponíveis).

Menos consumo de memória: ideal pra VMs com 2~4 GB de RAM.

Qual sistema operacional no Host?

Escolhi o Ubuntu Server e não outras opções como Arch, Fedora CoreOS, ou Bottlerocket por alguns motivos bem práticos:

🟢 Ubuntu Server:

  • Documentação clara e fácil de achar solução (Stack Overflow salva).
  • Compatível com praticamente tudo no ecossistema Linux.
  • Atualização simples com apt — sem surpresas.
  • Comunidade gigantesca: qualquer erro, alguém já sofreu por você.
  • A versão mínima é leve o bastante pro K3s, sem perder praticidade.

🔴 Bottlerocket?

  • Fechado demais. Sem apt, sem shell, sem paciência.
  • Difícil de debugar, exige uma mentalidade “GitOps ou nada”.
  • Ótimo no EKS, mas um inferno em homelab ou VM.

🟠 Arch?

  • Pra quem curte viver no limite.
  • Total controle, mas manutenção constante e risco de quebrar as coisas.
  • Não combina com ambientes que pedem estabilidade.

🟡 Fedora CoreOS?

  • Arquitetura imutável, focada em GitOps e segurança.
  • Ótimo pra produção com CI/CD bem definido.
  • Mas pra homelab? É subir ladeira com skate — funcional, mas trabalhoso.

Instalação e Infraestrutura

Criei duas VMs no meu Unraid Server (Meu servidor para HomeLab) ambas utilizando o Ubuntu 24.04 LTS com apenas o openssh-server instalado durante o setup e sem LVM group.

MáquinaFunçãoCPURAMDiscoIPUsuárioLinux
master-1Control-plane2 vCPU3GB30 GB10.0.0.11ubuntuUbuntu Server 24.04 LTS
worker-1Worker node2 vCPU4GB60 GB10.0.0.12ubuntuUbuntu Server 24.04 LTS

alt text

💡 Dica: fixar os IPs no DHCP do seu roteador pra evitar dor de cabeça depois. Use um Ip da sua faixa de rede. Como uso uma rede 10.0.0.0/24 então vou utilizar os Ips entre 10.0.0.11 e 10.0.0.15 para montar o cluster completo no futuro.

alt text

Acesso SSH

Copia tua chave SSH pública pras máquinas pra facilitar o acesso:

ssh-copy-id [email protected]
ssh-copy-id [email protected]

Preparação das Máquinas

Em ambas as máquinas vamos atualizar os pacotes, o sistema operacional e instalar algumas ferramentas úteis. Para acessa as máquinas use ssh [email protected].

sudo apt update && sudo apt upgrade -y
sudo apt dist-upgrade -y
sudo apt-get dist-upgrade
# Iptables é necessário
sudo apt install -y curl vim net-tools bash-completion dnsutils lsof iptables iptables-persistent ebtables arptables

# Adicionando os módulos para carregar na inicialização
echo ip_tables | sudo tee -a /etc/modules-load.d/k3s.conf
echo nf_conntrack | sudo tee -a /etc/modules-load.d/k3s.conf
# Forçando os módulos
sudo modprobe ip_tables
sudo modprobe nf_conntrack

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy

Toda essa stack do iptables é necessária para o calico, o CNI que vamos utilizar.

Por que usar iptables com Calico no nosso cluster Kubernetes ao invés do nftables ou eBPF que são muito mais modernos?

Porque não precisamos dessas tecnologias mais recentes. Nosso cluster é simples, voltado para estudo e homelab, onde estabilidade e compatibilidade são prioridade. O iptables é maduro, amplamente suportado e atende perfeitamente sem complicação. Então, pra esse cenário, é o melhor custo-benefício. Não iríamos ganhar performance suficiente que valha o esforço.

Além disso, instalamos arptables e ebtables porque eles complementam o iptables para controlar o tráfego em camadas específicas da rede:

arptables cuida do filtro de pacotes ARP (camada 2, endereço MAC), essencial para redes locais.

ebtables manipula o tráfego Ethernet, importante para políticas mais granulares em bridges e interfaces virtuais usadas no cluster.

Essas ferramentas juntas garantem que o Calico tenha o controle completo do tráfego, desde a camada 2 até a 4, mantendo a segurança e o isolamento sem precisar de soluções mais complexas.

Instalando o K3s no Master (sem CNI)

O K3s vem com Flannel como CNI por padrão, mas eu prefiro o Calico. O Flannel é simples, mas limita algumas coisas como network policies. O Calico dá mais controle, permite segurança entre pods, e pode até usar eBPF. Então vamos instalar o K3s desativando o CNI padrão:

ubuntu@master-1:~$ curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-backend=none" sh -

Agora, aplicamos o manifest do Calico (sem usar o Operator, pois não vamos escalar isso tanto a ponto de justificar).

ubuntu@master-1:~$ sudo kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

Preparando acesso externo ao cluster

No master-1:

ubuntu@master-1:~$ sudo cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/k3s.yaml
ubuntu@master-1:~$ sudo chown ubuntu:ubuntu /home/ubuntu/k3s.yaml
# Essa etapa é só para adiantar o acesso ao token que vamos precisar depois.
ubuntu@master-1:~$ sudo cat /var/lib/rancher/k3s/server/node-token > ~/token
ubuntu@master-1:~$ cat token
K108xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx::server:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Só conferindo
ubuntu@master-1:~$ sudo kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-1 Ready control-plane,master 69m v1.32.5+k3s1

Na sua máquina pessoal copie o config do master para ~/.kube/config. Lembranco que temos que ter o kubectl para acessa esse cluster.

# Criando o diretório caso não exista
mkdir -p ~/.kube
# Copiando o config
scp [email protected]:/home/ubuntu/k3s.yaml ~/.kube/config
# O config vem com o ip 127.0.0.1 que é o acesso ao cluster localmente, precisamos mudar para apontar para o master.
sed -i 's/127.0.0.1/10.0.0.11/' ~/.kube/config

❯ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-1 Ready control-plane,master 72m v1.32.5+k3s1

Adicionando o Worker

Pronto ja temos um cluster, porém só com o master. Esse processo para adicionar um worker serve para quantos workers quiser adicionar.

No worker-1, para acessa ssh [email protected].

ubuntu@worker-1:~$ curl -sfL https://get.k3s.io | \
K3S_URL=https://10.0.0.11:6443 \
K3S_TOKEN="K108xxxxxxxxxxxxxxxxx::server:xxxxxxxxxxxxxxxxx" \
sh -
❯ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-1 Ready control-plane,master 74m v1.32.5+k3s1
worker-1 Ready <none> 67m v1.32.5+k3s1

Ajustando

O que temos no cluster?

alt text

  • Traefik
  • Metric Server
  • Calico

Temos uma situação específica que não me agrada. O traefik(responsável pelo ingress) rodando no master também. Isso acontece pois o K3s pode rodar somente com um node master sem nenhum worker.

Preciso isolar o master. Não gosto da idéia do traefik no master.

# Vamos colocar uma taint nele
❯ kubectl taint nodes master-1 node-role.kubernetes.io/master=:NoSchedule

# Ao reiniciar o deploy do traefik garantimos que ele não irá rodar dentro do master.
kubectl rollout restart deployment -n kube-system traefik

# Edit o daemonset removendo todo o bloco toleration
kubectl edit daemonsets -n kube-system svclb-traefik-dee52a6c

# tolerations:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master
# operator: Exists
# - effect: NoSchedule
# key: node-role.kubernetes.io/control-plane
# operator: Exists
# - key: CriticalAddonsOnly
# operator: Exists

O calico é necessário rodar no master e o metric server é um serviço crítico então vamos deixá-lo no master sem problemas.

Atualizando o K3s

E para atualizar? Quando instalamos foi utilizada a versão 1.32, mas já temos a 1.33. É só conferir na página de release do K3s.

É importante dessa vez não instalar o Traefik. Mesmo que você reinstale com --disable traefik, o Traefik continua lá ele, pois já esta no cluster, mas se não evitar a instalação irá reinstalar e teremos que novamente remover as tolerations.

No master:

ubuntu@master-1:~$ sudo systemctl stop k3s
ubuntu@master-1:~$ curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.33.1+k3s1" INSTALL_K3S_EXEC="--disable traefik" sh -
ubuntu@master-1:~$ sudo systemctl start k3s

Em cada worker:

ubuntu@worker-1:~$ sudo systemctl stop k3s-agent
ubuntu@worker-1:~$ curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.33.1+k3s1" K3S_URL="https://10.0.0.11:6443" K3S_TOKEN="K108xxxxxxxxxxxxxxxxx::server:xxxxxxxxxxxxxxxxx" sh -

E tudo atualizado.

❯ kubectl get nodes                                            
NAME STATUS ROLES AGE VERSION
master-1 Ready control-plane,master 5h29m v1.33.1+k3s1
worker-1 Ready <none> 5h22m v1.33.1+k3s1

Considerações Finais

Mesmo rodando tudo no mesmo hardware (Unraid), quero expandir o cluster com mais dois nodes pra testar cenários de failover, tolerância a falhas e deploys distribuídos. Não é uma infra "de produção", mas sim um laboratório pra aprender a manter o carro rodando enquanto trocamos as rodas.

No k3s é um pouco diferente do Kubernetes "puro" porque o k3s é tudo empacotado num binário só — incluindo o kube-apiserver, controller-manager, scheduler, etcd.

Para mexer no kube-apiserver no k3s, as opções são meio limitadas, mas podemos passar flags personalizadas via INSTALL_K3S_EXEC ou no arquivo de configuração do k3s que podemos criar em /etc/rancher/k3s/config.yaml. Este arquivo é lido para completar o INSTALL_K3S_EXEC.

Você também pode editar o arquivo /etc/systemd/system/k3s.service (no master) para passar flags extras para o k3s. Por exemplo, para passar flags para o kube-apiserver, usa a flag --kube-apiserver-arg, tipo:

##--kube-apiserver-arg=<flag>=<value>
--kube-apiserver-arg=authorization-mode=RBAC,Node

Criando um arquivo de configuração por exemplo em teríamos

# Desativa o Traefik
disable:
- traefik

# Passa argumentos para o kube-apiserver para melhorar segurança
kube-apiserver-arg:
- "anonymous-auth=false" # Desabilita acesso anônimo
- "authorization-mode=RBAC" # Usa RBAC para autorização
- "enable-admission-plugins=NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota"
- "secure-port=6443" # Porta segura (default)
- "tls-cert-file=/etc/rancher/k3s/ssl/k3s-server.crt"
- "tls-private-key-file=/etc/rancher/k3s/ssl/k3s-server.key"

Pra mexer no kube-apiserver no k3s, o jeito padrão é passar flags customizadas via kube-apiserver-arg no arquivo de config do k3s ou na linha de comando do sistema. Depois reinicia o serviço.

Tendo um arquivo de configuração vamos seguir atualizações com um comando mais simples.