Skip to main content

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, ServiceAccounts e 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!