Harbor na Prática — Parte 8 Integração com Kubernetes
Esta é a última parte da série. Vamos configurar o Kubernetes para fazer pull de imagens privadas do Harbor — usando
imagePullSecrets,ServiceAccountse boas práticas de segurança para ambientes de produção.
O problema
Por padrão, o Kubernetes não sabe como autenticar em um registry privado. Quando um pod tenta puxar uma imagem do Harbor, ele recebe um erro:
Failed to pull image "harbor.empresa.com/backend/api:v1.0.0":
rpc error: code = Unknown desc = failed to pull and unpack image:
failed to resolve reference "harbor.empresa.com/backend/api:v1.0.0":
unexpected status code 401 Unauthorized
A solução é criar um Secret com as credenciais do Harbor e vinculá-lo ao pod.
Pré-requisitos
- Cluster Kubernetes com acesso ao Harbor
- Robot Account com permissão apenas de pull (veja a Parte 6)
- Se o Harbor usa HTTP: containerd configurado com
insecure-registries
Passo 1 — Configurar o containerd para registries HTTP
Se o Harbor não usa HTTPS, configure o containerd em cada node do cluster.
# Em cada node do cluster
sudo mkdir -p /etc/containerd/certs.d/harbor.empresa.com
cat <<EOF | sudo tee /etc/containerd/certs.d/harbor.empresa.com/hosts.toml
server = "http://harbor.empresa.com"
[host."http://harbor.empresa.com"]
capabilities = ["pull", "resolve"]
skip_verify = true
EOF
sudo systemctl restart containerd
Para HTTPS com certificado autoassinado, substitua o certificado:
sudo mkdir -p /etc/containerd/certs.d/harbor.empresa.com
sudo cp ca.crt /etc/containerd/certs.d/harbor.empresa.com/ca.crt
cat <<EOF | sudo tee /etc/containerd/certs.d/harbor.empresa.com/hosts.toml
server = "https://harbor.empresa.com"
[host."https://harbor.empresa.com"]
capabilities = ["pull", "resolve"]
ca = "/etc/containerd/certs.d/harbor.empresa.com/ca.crt"
EOF
sudo systemctl restart containerd
Passo 2 — Criar o Secret com as credenciais do Harbor
O imagePullSecret é um Secret do tipo kubernetes.io/dockerconfigjson que contém as credenciais para autenticar no registry.
Criando com kubectl
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.empresa.com \
--docker-username='robot$backend+kubernetes-pull' \
--docker-password='TOKEN_DO_ROBOT' \
--namespace=minha-app
⚠️ O nome do Robot Account contém
$— entre aspas simples no shell para evitar que o bash interprete como variável.
Criando via manifesto YAML
Mais adequado para GitOps — o secret fica versionado (sem o valor real, que deve vir de um Secret Manager ou Sealed Secrets).
# Gerar o base64 do config.json
AUTH=$(echo -n 'robot$backend+kubernetes-pull:TOKEN_DO_ROBOT' | base64)
cat <<EOF
{
"auths": {
"harbor.empresa.com": {
"auth": "$AUTH"
}
}
}
EOF | base64
Use o resultado no manifesto:
# harbor-pull-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: harbor-pull-secret
namespace: minha-app
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <BASE64_DO_JSON_ACIMA>
kubectl apply -f harbor-pull-secret.yaml
Criando com Sealed Secrets (recomendado para GitOps)
Se você usa Sealed Secrets no cluster:
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.empresa.com \
--docker-username='robot$backend+kubernetes-pull' \
--docker-password='TOKEN_DO_ROBOT' \
--namespace=minha-app \
--dry-run=client -o yaml | \
kubeseal --format yaml > harbor-pull-secret-sealed.yaml
O arquivo harbor-pull-secret-sealed.yaml pode ser commitado no repositório Git com segurança.
Passo 3 — Usar o Secret em um Deployment
Opção A — Referenciar no Deployment diretamente
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: minha-api
namespace: minha-app
spec:
replicas: 2
selector:
matchLabels:
app: minha-api
template:
metadata:
labels:
app: minha-api
spec:
imagePullSecrets:
- name: harbor-pull-secret # referência ao Secret criado
containers:
- name: api
image: harbor.empresa.com/backend/api:v1.0.0
ports:
- containerPort: 8080
Opção B — Vincular ao ServiceAccount (recomendado)
Se você vincula o Secret ao ServiceAccount, todos os pods que usam aquele ServiceAccount herdam automaticamente o acesso. Não precisa repetir imagePullSecrets em cada Deployment.
# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-serviceaccount
namespace: minha-app
imagePullSecrets:
- name: harbor-pull-secret
# deployment.yaml
spec:
template:
spec:
serviceAccountName: app-serviceaccount # sem imagePullSecrets aqui
containers:
- name: api
image: harbor.empresa.com/backend/api:v1.0.0
Qualquer novo Deployment no namespace que usar serviceAccountName: app-serviceaccount vai puxar automaticamente do Harbor sem configuração adicional.
Passo 4 — Verificar que o pull está funcionando
# Aplicar o deployment
kubectl apply -f deployment.yaml
# Acompanhar os eventos do pod
kubectl describe pod -n minha-app <nome-do-pod>
# Se o pull foi bem-sucedido, você verá:
# Successfully pulled image "harbor.empresa.com/backend/api:v1.0.0"
Para verificar que o Secret está correto antes de aplicar:
kubectl get secret harbor-pull-secret -n minha-app -o jsonpath='{.data.\.dockerconfigjson}' | \
base64 -d | jq '.'
Configurando para múltiplos namespaces
O Secret precisa existir em cada namespace onde os pods rodam. Para automatizar isso, você pode usar um script ou um operador como o kubernetes-replicator.
Script simples para propagar o Secret
#!/bin/bash
NAMESPACES=("minha-app" "frontend" "data-pipeline" "monitoring")
for NS in "${NAMESPACES[@]}"; do
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.empresa.com \
--docker-username="robot\$backend+kubernetes-pull" \
--docker-password="TOKEN_DO_ROBOT" \
--namespace="$NS" \
--dry-run=client -o yaml | kubectl apply -f -
echo "✅ Secret criado/atualizado no namespace: $NS"
done
Rotação de credenciais
Robot Accounts expiram. Para rotacionar sem downtime:
# 1. Criar novo Robot Account no Harbor (via UI ou API)
# 2. Atualizar o Secret em todos os namespaces
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.empresa.com \
--docker-username="robot\$backend+kubernetes-pull-v2" \
--docker-password="NOVO_TOKEN" \
--namespace=minha-app \
--dry-run=client -o yaml | kubectl apply -f -
# 3. Os pods existentes continuam rodando (já baixaram a imagem)
# 4. Novos pods já usam as novas credenciais
# 5. Após confirmar que tudo está ok, remover o Robot Account antigo no Harbor
Quadro completo da série
Você chegou ao final da série. Aqui está o fluxo completo que construímos ao longo dos 8 artigos:
Instalação (Parte 1)
│
▼
Projetos e Repositórios (Parte 2)
│
▼
Usuários, LDAP e Robot Accounts (Parte 3)
│
▼
Push e Pull de imagens — máquina local (Parte 4)
│
▼
Proxy Cache — Docker Hub, GCR, ECR (Parte 5)
│
▼
Retenção, Quotas e Token de Pull (Parte 6)
│
▼
GitLab CI — build e push automático (Parte 7)
│
▼
Kubernetes — imagePullSecrets e ServiceAccount (Parte 8)
Próximos passos sugeridos
Agora que o Harbor está integrado ao seu stack, alguns tópicos para explorar:
- Alta Disponibilidade — PostgreSQL externo em HA, Redis Sentinel, registry com RWX
- Replicação entre ambientes — promover imagens de staging para produção via replicação
- Webhooks — notificar o Slack ou disparar pipelines quando uma imagem é publicada
- Harbor como proxy cache no containerd — redirecionar todos os pulls do cluster automaticamente
- Monitoramento — expor métricas do Harbor para o Prometheus e criar dashboards no Grafana
Espero que esta série tenha sido útil! Se te ajudou a implementar o Harbor no seu ambiente, compartilhe com a comunidade. Todo feedback é bem-vindo nos comentários.
Até a próxima série!