feat(aula-10): migrar para Hetzner Object Storage + melhorias
Mudanças principais: - Substituir MinIO bundled por Hetzner Object Storage (S3) - Secrets YAML com envsubst para credenciais S3 - Anti-affinity preferencial (adapta a qualquer topologia) - Detecção automática de cert-manager existente - Menu TLS simplificado (Let's Encrypt/CloudFlare/HTTP) - Perguntar hostname do registry separadamente - Exibição unificada de TLS (não mais CloudFlare: true/false)
This commit is contained in:
@@ -29,11 +29,21 @@ Deploy do GitLab em Kubernetes usando Helm chart oficial, com Container Registry
|
|||||||
│ │ (Git Storage) │ │
|
│ │ (Git Storage) │ │
|
||||||
│ └──────────────────────┘ │
|
│ └──────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────┐ ┌───────┐ ┌──────────┐ │
|
│ ┌──────────┐ ┌───────┐ │
|
||||||
│ │PostgreSQL│ │ Redis │ │ MinIO │ │
|
│ │PostgreSQL│ │ Redis │ │
|
||||||
│ │ (10Gi) │ │(10Gi) │ │ (10Gi) │ │
|
│ │ (10Gi) │ │(10Gi) │ │
|
||||||
│ └──────────┘ └───────┘ └──────────┘ │
|
│ └──────────┘ └───────┘ │
|
||||||
└─────────────────────────────────────────┘
|
└──────────────┬──────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ Hetzner Object │
|
||||||
|
│ Storage (S3) │
|
||||||
|
│ - uploads │
|
||||||
|
│ - artifacts │
|
||||||
|
│ - registry images │
|
||||||
|
│ - lfs objects │
|
||||||
|
└─────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pré-requisitos
|
## Pré-requisitos
|
||||||
@@ -41,8 +51,11 @@ Deploy do GitLab em Kubernetes usando Helm chart oficial, com Container Registry
|
|||||||
1. **Cluster Talos na Hetzner** (aula-08) com:
|
1. **Cluster Talos na Hetzner** (aula-08) com:
|
||||||
- Hetzner CSI Driver (StorageClass: hcloud-volumes)
|
- Hetzner CSI Driver (StorageClass: hcloud-volumes)
|
||||||
- NGINX Ingress Controller com LoadBalancer
|
- NGINX Ingress Controller com LoadBalancer
|
||||||
2. **kubectl** instalado
|
2. **Hetzner Object Storage** (bucket + credenciais):
|
||||||
3. **Helm** 3.x instalado
|
- Criar bucket em: https://console.hetzner.cloud → Object Storage
|
||||||
|
- Gerar Access Key e Secret Key
|
||||||
|
3. **kubectl** instalado
|
||||||
|
4. **Helm** 3.x instalado
|
||||||
|
|
||||||
## Contexto do Cluster
|
## Contexto do Cluster
|
||||||
|
|
||||||
@@ -71,6 +84,7 @@ O script é interativo e pergunta:
|
|||||||
1. **Hostname do GitLab** (ex: `git.kube.quest`) - FQDN completo
|
1. **Hostname do GitLab** (ex: `git.kube.quest`) - FQDN completo
|
||||||
2. **Usa CloudFlare?** (com proxy/CDN) - pode herdar da aula-09
|
2. **Usa CloudFlare?** (com proxy/CDN) - pode herdar da aula-09
|
||||||
3. **Ativar Let's Encrypt?** (se não usar CloudFlare)
|
3. **Ativar Let's Encrypt?** (se não usar CloudFlare)
|
||||||
|
4. **Hetzner Object Storage** - endpoint, bucket e credenciais
|
||||||
|
|
||||||
### Opções de TLS
|
### Opções de TLS
|
||||||
|
|
||||||
@@ -128,43 +142,51 @@ Com isso, o Workhorse confia nos headers `X-Forwarded-*` vindos do CloudFlare e
|
|||||||
| Gitaly | 512Mi | 1Gi | 10Gi |
|
| Gitaly | 512Mi | 1Gi | 10Gi |
|
||||||
| PostgreSQL | 512Mi | 1Gi | 10Gi |
|
| PostgreSQL | 512Mi | 1Gi | 10Gi |
|
||||||
| Redis | 256Mi | 512Mi | 10Gi |
|
| Redis | 256Mi | 512Mi | 10Gi |
|
||||||
| MinIO | 128Mi | 256Mi | 10Gi |
|
|
||||||
| Shell | 64Mi | 128Mi | - |
|
| Shell | 64Mi | 128Mi | - |
|
||||||
| Toolbox | 256Mi | 512Mi | - |
|
| Toolbox | 256Mi | 512Mi | - |
|
||||||
| **Total** | ~5Gi | ~8Gi | 40Gi |
|
| **Total** | ~5Gi | ~8Gi | 30Gi |
|
||||||
|
|
||||||
## Pod Anti-Affinity (Distribuicao Inteligente)
|
**Object Storage (externo):**
|
||||||
|
- Hetzner Object Storage (S3-compatible)
|
||||||
|
- Uploads, artifacts, LFS, registry images
|
||||||
|
- Custo: €0.006/GB/mês (paga por uso)
|
||||||
|
|
||||||
O GitLab configura **antiAffinity** para garantir que webservice e sidekiq rodem em nos diferentes:
|
## Distribuição Inteligente de Pods
|
||||||
|
|
||||||
|
O GitLab usa **anti-affinity preferencial** para se adaptar automaticamente ao cluster:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
gitlab:
|
gitlab:
|
||||||
webservice:
|
webservice:
|
||||||
affinity:
|
affinity:
|
||||||
podAntiAffinity:
|
podAntiAffinity:
|
||||||
requiredDuringSchedulingIgnoredDuringExecution:
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
- labelSelector:
|
- weight: 100
|
||||||
matchLabels:
|
podAffinityTerm:
|
||||||
app: sidekiq
|
labelSelector:
|
||||||
topologyKey: kubernetes.io/hostname
|
matchLabels:
|
||||||
|
app: sidekiq
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
```
|
```
|
||||||
|
|
||||||
### Por que isso importa?
|
### Comportamento Adaptativo
|
||||||
|
|
||||||
Sem antiAffinity, webservice (~2.5Gi) + sidekiq (~2Gi) = 4.5Gi no mesmo no CAX11 (4GB):
|
| Cenário | Comportamento |
|
||||||
- **Resultado**: OOMKilled constantes, servico instavel
|
|---------|---------------|
|
||||||
|
| 1 nó grande (8GB+) | Tudo roda junto no mesmo nó |
|
||||||
|
| Múltiplos nós pequenos | Distribui automaticamente |
|
||||||
|
| Sem recursos suficientes | Cluster Autoscaler cria nós novos |
|
||||||
|
|
||||||
Com antiAffinity:
|
### Por que preferencial ao invés de obrigatório?
|
||||||
- Kubernetes detecta que nao pode agendar no mesmo no
|
|
||||||
- Pod fica **Pending**
|
|
||||||
- Cluster Autoscaler cria um novo no automaticamente
|
|
||||||
- Pods distribuidos = servico estavel
|
|
||||||
|
|
||||||
### Licao do Workshop
|
- **Obrigatório** (`required`): pods ficam Pending se não houver nós separados
|
||||||
|
- **Preferencial** (`preferred`): funciona com qualquer topologia, otimiza quando possível
|
||||||
|
|
||||||
> "Kubernetes nao e so rodar containers - e **orquestracao inteligente**.
|
### Lição do Workshop
|
||||||
> Quando configuramos antiAffinity, o scheduler entendeu que precisava de mais
|
|
||||||
> recursos e o autoscaler provisionou automaticamente."
|
> "Configurações adaptativas são melhores que rígidas. O anti-affinity preferencial
|
||||||
|
> permite que o GitLab funcione em um nó grande ou distribua em vários pequenos,
|
||||||
|
> sem intervenção manual."
|
||||||
|
|
||||||
## Padrão de Domínios
|
## Padrão de Domínios
|
||||||
|
|
||||||
@@ -191,6 +213,13 @@ DOMAIN=kube.quest
|
|||||||
USE_CLOUDFLARE=true
|
USE_CLOUDFLARE=true
|
||||||
USE_LETSENCRYPT=false
|
USE_LETSENCRYPT=false
|
||||||
LETSENCRYPT_EMAIL=
|
LETSENCRYPT_EMAIL=
|
||||||
|
|
||||||
|
# Hetzner Object Storage (use a mesma região do cluster)
|
||||||
|
S3_ENDPOINT=nbg1.your-objectstorage.com
|
||||||
|
S3_ACCESS_KEY=xxxxx
|
||||||
|
S3_SECRET_KEY=xxxxx
|
||||||
|
S3_BUCKET=gitlab-workshop
|
||||||
|
S3_REGION=nbg1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Acesso
|
## Acesso
|
||||||
@@ -321,15 +350,21 @@ kubectl run test --rm -it --image=busybox -- wget -qO- http://gitlab-registry:50
|
|||||||
|
|
||||||
## Custos
|
## Custos
|
||||||
|
|
||||||
|
| Cenário | Workers | Custo/mês |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 1x CAX21 (8GB) | 1 nó | ~$8.49 |
|
||||||
|
| 2x CAX11 (4GB) | 2 nós (distribuído) | ~$9.18 |
|
||||||
|
|
||||||
| Recurso | Custo/mês |
|
| Recurso | Custo/mês |
|
||||||
|---------|-----------|
|
|---------|-----------|
|
||||||
| Workers (2x CAX11 - antiAffinity) | ~$9.18 |
|
| Volumes (3x 10Gi = 30Gi) | ~$1.45 |
|
||||||
| Volumes (4x 10Gi = 40Gi) | ~$1.94 |
|
| Object Storage (~10GB uso) | ~€0.06 |
|
||||||
| LoadBalancer (compartilhado) | ~$1.80 (1/3) |
|
| LoadBalancer (compartilhado) | ~$1.80 (1/3) |
|
||||||
| **Total GitLab** | ~$12.92 |
|
|
||||||
|
|
||||||
**Nota:** O antiAffinity requer 2 workers minimos (webservice e sidekiq em nos separados).
|
**Nota:** O anti-affinity preferencial permite ambos os cenários.
|
||||||
O Cluster Autoscaler provisiona automaticamente quando necessario.
|
O Cluster Autoscaler provisiona nós automaticamente quando necessário.
|
||||||
|
|
||||||
|
**Vantagem Object Storage:** Sem volume MinIO (economia de ~$0.50/mês) + storage ilimitado.
|
||||||
|
|
||||||
## Referências
|
## Referências
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ echo ""
|
|||||||
|
|
||||||
log_warn "ATENÇÃO: Isso vai remover os recursos da aula 10:"
|
log_warn "ATENÇÃO: Isso vai remover os recursos da aula 10:"
|
||||||
echo " - GitLab e todos os componentes"
|
echo " - GitLab e todos os componentes"
|
||||||
echo " - Volumes persistentes (PostgreSQL, Redis, MinIO, Gitaly)"
|
echo " - Volumes persistentes (PostgreSQL, Redis, Gitaly)"
|
||||||
echo " - cert-manager (se instalado)"
|
echo " - cert-manager (se instalado)"
|
||||||
echo " - Arquivo .env"
|
echo " - Arquivo .env"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ nginx-ingress:
|
|||||||
|
|
||||||
gitlab:
|
gitlab:
|
||||||
# Webservice (Rails app - UI e API)
|
# Webservice (Rails app - UI e API)
|
||||||
# NOTA: antiAffinity garante que webservice e sidekiq rodem em nós diferentes
|
# Anti-affinity preferencial: distribui se possível, mas não obriga
|
||||||
# Isso evita OOM quando ambos competem por memória no mesmo nó CAX11 (4GB)
|
# - 1 nó grande (8GB): tudo roda junto
|
||||||
|
# - Múltiplos nós pequenos: distribui automaticamente
|
||||||
|
# - Sem recursos: autoscaler cria nós novos
|
||||||
webservice:
|
webservice:
|
||||||
minReplicas: 1
|
minReplicas: 1
|
||||||
maxReplicas: 1
|
maxReplicas: 1
|
||||||
@@ -93,17 +95,18 @@ gitlab:
|
|||||||
threads:
|
threads:
|
||||||
min: 1
|
min: 1
|
||||||
max: 2
|
max: 2
|
||||||
# Anti-affinity: não rodar no mesmo nó que sidekiq
|
# Anti-affinity preferencial: tenta separar de sidekiq, mas não obriga
|
||||||
affinity:
|
affinity:
|
||||||
podAntiAffinity:
|
podAntiAffinity:
|
||||||
requiredDuringSchedulingIgnoredDuringExecution:
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
- labelSelector:
|
- weight: 100
|
||||||
matchLabels:
|
podAffinityTerm:
|
||||||
app: sidekiq
|
labelSelector:
|
||||||
topologyKey: kubernetes.io/hostname
|
matchLabels:
|
||||||
|
app: sidekiq
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
|
||||||
# Sidekiq (background jobs)
|
# Sidekiq (background jobs)
|
||||||
# Anti-affinity: não rodar no mesmo nó que webservice
|
|
||||||
sidekiq:
|
sidekiq:
|
||||||
minReplicas: 1
|
minReplicas: 1
|
||||||
maxReplicas: 1
|
maxReplicas: 1
|
||||||
@@ -117,13 +120,16 @@ gitlab:
|
|||||||
# Desabilitar memory watchdog interno do GitLab (deixa o OOM killer do K8s gerenciar)
|
# Desabilitar memory watchdog interno do GitLab (deixa o OOM killer do K8s gerenciar)
|
||||||
memoryKiller:
|
memoryKiller:
|
||||||
maxRss: 2000000000 # 2GB - maior que o limite para evitar kills prematuros
|
maxRss: 2000000000 # 2GB - maior que o limite para evitar kills prematuros
|
||||||
|
# Anti-affinity preferencial: tenta separar de webservice, mas não obriga
|
||||||
affinity:
|
affinity:
|
||||||
podAntiAffinity:
|
podAntiAffinity:
|
||||||
requiredDuringSchedulingIgnoredDuringExecution:
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
- labelSelector:
|
- weight: 100
|
||||||
matchLabels:
|
podAffinityTerm:
|
||||||
app: webservice
|
labelSelector:
|
||||||
topologyKey: kubernetes.io/hostname
|
matchLabels:
|
||||||
|
app: webservice
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
|
||||||
# Gitaly (Git storage)
|
# Gitaly (Git storage)
|
||||||
gitaly:
|
gitaly:
|
||||||
@@ -213,38 +219,31 @@ redis:
|
|||||||
storageClass: hcloud-volumes
|
storageClass: hcloud-volumes
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# MINIO (Object Storage)
|
# OBJECT STORAGE (Hetzner Object Storage - S3 compatible)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# NOTA: As imagens padrão do GitLab chart não suportam ARM64.
|
# Usamos o Hetzner Object Storage ao invés do MinIO bundled.
|
||||||
# Usamos as imagens oficiais multi-arch do MinIO.
|
# Vantagens:
|
||||||
minio:
|
# - Sem volume persistente (economia de $0.50/mês)
|
||||||
install: true
|
# - Sem pod MinIO (economia de recursos)
|
||||||
image: minio/minio
|
# - Storage ilimitado (paga por uso: €0.006/GB)
|
||||||
imageTag: RELEASE.2024-06-13T22-53-53Z
|
# - Alta disponibilidade gerenciada pela Hetzner
|
||||||
minioMc:
|
#
|
||||||
image: minio/mc
|
# Pré-requisito: criar bucket e credenciais na Hetzner Console
|
||||||
tag: RELEASE.2024-06-12T14-34-03Z
|
# O setup.sh cria o Secret gitlab-object-storage automaticamente
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 128Mi
|
|
||||||
cpu: 50m
|
|
||||||
limits:
|
|
||||||
memory: 256Mi
|
|
||||||
cpu: 200m
|
|
||||||
persistence:
|
|
||||||
size: 10Gi
|
|
||||||
storageClass: hcloud-volumes
|
|
||||||
|
|
||||||
# Ou usar object storage externo (S3, etc):
|
minio:
|
||||||
# global:
|
install: false
|
||||||
# minio:
|
|
||||||
# enabled: false
|
global:
|
||||||
# appConfig:
|
minio:
|
||||||
# object_store:
|
enabled: false
|
||||||
# enabled: true
|
appConfig:
|
||||||
# connection:
|
object_store:
|
||||||
# secret: gitlab-object-storage
|
enabled: true
|
||||||
# key: connection
|
proxy_download: true
|
||||||
|
connection:
|
||||||
|
secret: gitlab-object-storage
|
||||||
|
key: connection
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# REGISTRY (Container Registry)
|
# REGISTRY (Container Registry)
|
||||||
@@ -261,7 +260,10 @@ registry:
|
|||||||
limits:
|
limits:
|
||||||
memory: 256Mi
|
memory: 256Mi
|
||||||
cpu: 200m
|
cpu: 200m
|
||||||
# Storage usa MinIO bundled automaticamente quando minio.install=true
|
# Storage usa Hetzner Object Storage (configurado via global.appConfig.object_store)
|
||||||
|
storage:
|
||||||
|
secret: gitlab-registry-storage
|
||||||
|
key: config
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# COMPONENTES DESABILITADOS (economia de recursos)
|
# COMPONENTES DESABILITADOS (economia de recursos)
|
||||||
|
|||||||
22
aula-10/object-storage-secret.yaml
Normal file
22
aula-10/object-storage-secret.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Secret para GitLab Object Storage (Hetzner S3)
|
||||||
|
# =============================================================================
|
||||||
|
# Usado por: uploads, artifacts, lfs, packages, etc.
|
||||||
|
#
|
||||||
|
# Variáveis substituídas pelo setup.sh via envsubst:
|
||||||
|
# - S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY, S3_ENDPOINT
|
||||||
|
#
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: gitlab-object-storage
|
||||||
|
namespace: gitlab
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
connection: |
|
||||||
|
provider: AWS
|
||||||
|
region: ${S3_REGION}
|
||||||
|
aws_access_key_id: ${S3_ACCESS_KEY}
|
||||||
|
aws_secret_access_key: ${S3_SECRET_KEY}
|
||||||
|
endpoint: https://${S3_ENDPOINT}
|
||||||
|
path_style: true
|
||||||
24
aula-10/registry-storage-secret.yaml
Normal file
24
aula-10/registry-storage-secret.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Secret para GitLab Registry Storage (Hetzner S3)
|
||||||
|
# =============================================================================
|
||||||
|
# Usado por: Container Registry
|
||||||
|
# Formato diferente do object-storage (registry usa config YAML)
|
||||||
|
#
|
||||||
|
# Variáveis substituídas pelo setup.sh via envsubst:
|
||||||
|
# - S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY, S3_REGION, S3_ENDPOINT
|
||||||
|
#
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: gitlab-registry-storage
|
||||||
|
namespace: gitlab
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
config: |
|
||||||
|
s3:
|
||||||
|
bucket: ${S3_BUCKET}
|
||||||
|
accesskey: ${S3_ACCESS_KEY}
|
||||||
|
secretkey: ${S3_SECRET_KEY}
|
||||||
|
region: ${S3_REGION}
|
||||||
|
regionendpoint: https://${S3_ENDPOINT}
|
||||||
|
v4auth: true
|
||||||
207
aula-10/setup.sh
207
aula-10/setup.sh
@@ -46,6 +46,13 @@ USE_CLOUDFLARE=""
|
|||||||
USE_LETSENCRYPT=""
|
USE_LETSENCRYPT=""
|
||||||
LETSENCRYPT_EMAIL=""
|
LETSENCRYPT_EMAIL=""
|
||||||
|
|
||||||
|
# Hetzner Object Storage
|
||||||
|
S3_ENDPOINT=""
|
||||||
|
S3_ACCESS_KEY=""
|
||||||
|
S3_SECRET_KEY=""
|
||||||
|
S3_BUCKET=""
|
||||||
|
S3_REGION=""
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# FUNÇÕES DE CONFIGURAÇÃO
|
# FUNÇÕES DE CONFIGURAÇÃO
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -60,6 +67,13 @@ DOMAIN=${DOMAIN}
|
|||||||
USE_CLOUDFLARE=${USE_CLOUDFLARE}
|
USE_CLOUDFLARE=${USE_CLOUDFLARE}
|
||||||
USE_LETSENCRYPT=${USE_LETSENCRYPT}
|
USE_LETSENCRYPT=${USE_LETSENCRYPT}
|
||||||
LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
|
LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
|
||||||
|
|
||||||
|
# Hetzner Object Storage
|
||||||
|
S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
|
S3_ACCESS_KEY=${S3_ACCESS_KEY}
|
||||||
|
S3_SECRET_KEY=${S3_SECRET_KEY}
|
||||||
|
S3_BUCKET=${S3_BUCKET}
|
||||||
|
S3_REGION=${S3_REGION}
|
||||||
EOF
|
EOF
|
||||||
log_success "Configuração salva em .env"
|
log_success "Configuração salva em .env"
|
||||||
}
|
}
|
||||||
@@ -102,11 +116,19 @@ collect_user_input() {
|
|||||||
|
|
||||||
# Se já tem GITLAB_HOST configurado, oferecer usar
|
# Se já tem GITLAB_HOST configurado, oferecer usar
|
||||||
if [[ -n "$GITLAB_HOST" ]]; then
|
if [[ -n "$GITLAB_HOST" ]]; then
|
||||||
|
# Determinar tipo de TLS para exibição
|
||||||
|
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
||||||
|
TLS_TYPE="Let's Encrypt"
|
||||||
|
elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
||||||
|
TLS_TYPE="CloudFlare"
|
||||||
|
else
|
||||||
|
TLS_TYPE="Sem TLS (HTTP)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "Configuração existente encontrada:"
|
echo -e "Configuração existente encontrada:"
|
||||||
echo -e " GitLab: ${GREEN}${GITLAB_HOST}${NC}"
|
echo -e " GitLab: ${GREEN}${GITLAB_HOST}${NC}"
|
||||||
echo -e " Registry: ${GREEN}${REGISTRY_HOST}${NC}"
|
echo -e " Registry: ${GREEN}${REGISTRY_HOST}${NC}"
|
||||||
echo -e " CloudFlare: ${GREEN}${USE_CLOUDFLARE}${NC}"
|
echo -e " TLS: ${GREEN}${TLS_TYPE}${NC}"
|
||||||
echo -e " Let's Encrypt: ${GREEN}${USE_LETSENCRYPT}${NC}"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "Deseja usar esta configuração?"
|
echo -e "Deseja usar esta configuração?"
|
||||||
echo -e " 1) Sim, continuar com a configuração existente"
|
echo -e " 1) Sim, continuar com a configuração existente"
|
||||||
@@ -128,18 +150,33 @@ collect_user_input() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Derivar Domain e Registry automaticamente
|
# Extrair domínio base
|
||||||
DOMAIN=$(extract_domain "$GITLAB_HOST")
|
DOMAIN=$(extract_domain "$GITLAB_HOST")
|
||||||
REGISTRY_HOST="registry.${DOMAIN}"
|
|
||||||
|
# Perguntar hostname do registry (sugerir padrão)
|
||||||
|
DEFAULT_REGISTRY="registry.${DOMAIN}"
|
||||||
|
echo -n "Digite o hostname do Registry (enter para ${DEFAULT_REGISTRY}): "
|
||||||
|
read -r REGISTRY_HOST
|
||||||
|
if [[ -z "$REGISTRY_HOST" ]]; then
|
||||||
|
REGISTRY_HOST="$DEFAULT_REGISTRY"
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Registry será: ${REGISTRY_HOST}"
|
log_info "Registry será: ${REGISTRY_HOST}"
|
||||||
|
|
||||||
# Se já tem configuração de TLS, oferecer usar
|
# Se já tem configuração de TLS, oferecer usar
|
||||||
if [[ -n "$USE_CLOUDFLARE" ]]; then
|
if [[ -n "$USE_CLOUDFLARE" || -n "$USE_LETSENCRYPT" ]]; then
|
||||||
|
# Determinar tipo de TLS para exibição
|
||||||
|
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
||||||
|
TLS_TYPE="Let's Encrypt"
|
||||||
|
elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
||||||
|
TLS_TYPE="CloudFlare"
|
||||||
|
else
|
||||||
|
TLS_TYPE="Sem TLS (HTTP)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "Configuração de TLS encontrada:"
|
echo -e "Configuração de TLS encontrada:"
|
||||||
echo -e " CloudFlare: ${GREEN}${USE_CLOUDFLARE}${NC}"
|
echo -e " TLS: ${GREEN}${TLS_TYPE}${NC}"
|
||||||
echo -e " Let's Encrypt: ${GREEN}${USE_LETSENCRYPT}${NC}"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "Deseja usar esta configuração de TLS?"
|
echo -e "Deseja usar esta configuração de TLS?"
|
||||||
echo -e " 1) Sim"
|
echo -e " 1) Sim"
|
||||||
@@ -152,44 +189,104 @@ collect_user_input() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Perguntar sobre CloudFlare
|
# Perguntar sobre TLS
|
||||||
echo ""
|
echo ""
|
||||||
echo "Você usa CloudFlare para DNS?"
|
echo "Como deseja configurar TLS/HTTPS?"
|
||||||
echo " 1) Sim (com proxy/CDN ativado - ícone laranja)"
|
echo " 1) Let's Encrypt (recomendado - certificado automático)"
|
||||||
echo " 2) Não"
|
echo " 2) CloudFlare (proxy/CDN - NOTA: SSH usa IP direto, não proxy)"
|
||||||
echo -n "Escolha [1/2]: "
|
echo " 3) Sem TLS (apenas HTTP - dev/workshop)"
|
||||||
|
echo -n "Escolha [1/2/3]: "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
if [[ "$choice" == "1" ]]; then
|
case "$choice" in
|
||||||
USE_CLOUDFLARE=true
|
1)
|
||||||
USE_LETSENCRYPT=false
|
|
||||||
log_info "CloudFlare irá gerenciar TLS automaticamente na edge"
|
|
||||||
else
|
|
||||||
USE_CLOUDFLARE=false
|
|
||||||
|
|
||||||
# Perguntar sobre Let's Encrypt
|
|
||||||
echo ""
|
|
||||||
echo "Deseja ativar HTTPS com Let's Encrypt?"
|
|
||||||
echo " 1) Sim (recomendado para produção)"
|
|
||||||
echo " 2) Não (apenas HTTP)"
|
|
||||||
echo -n "Escolha [1/2]: "
|
|
||||||
read -r choice
|
|
||||||
|
|
||||||
if [[ "$choice" == "1" ]]; then
|
|
||||||
USE_LETSENCRYPT=true
|
USE_LETSENCRYPT=true
|
||||||
|
USE_CLOUDFLARE=false
|
||||||
echo ""
|
echo ""
|
||||||
echo -n "Digite seu email para Let's Encrypt: "
|
echo -n "Digite seu email para Let's Encrypt: "
|
||||||
read -r LETSENCRYPT_EMAIL
|
read -r LETSENCRYPT_EMAIL
|
||||||
|
|
||||||
if [[ -z "$LETSENCRYPT_EMAIL" ]]; then
|
if [[ -z "$LETSENCRYPT_EMAIL" ]]; then
|
||||||
log_error "Email é obrigatório para Let's Encrypt"
|
log_error "Email é obrigatório para Let's Encrypt"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
;;
|
||||||
|
2)
|
||||||
|
USE_CLOUDFLARE=true
|
||||||
USE_LETSENCRYPT=false
|
USE_LETSENCRYPT=false
|
||||||
|
log_info "CloudFlare irá gerenciar TLS na edge"
|
||||||
|
log_warn "NOTA: Git via SSH (porta 22) não passa pelo proxy CloudFlare"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
USE_CLOUDFLARE=false
|
||||||
|
USE_LETSENCRYPT=false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Coletar configuração do Hetzner Object Storage
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${CYAN} Hetzner Object Storage${NC}"
|
||||||
|
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "O GitLab usa Hetzner Object Storage para armazenar:"
|
||||||
|
echo " - Uploads de usuários"
|
||||||
|
echo " - Artifacts de CI/CD"
|
||||||
|
echo " - Container Registry images"
|
||||||
|
echo " - LFS objects"
|
||||||
|
echo ""
|
||||||
|
echo "Crie um bucket e credenciais em:"
|
||||||
|
echo " https://console.hetzner.cloud → Object Storage"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Se já tem configuração, oferecer usar
|
||||||
|
if [[ -n "$S3_ENDPOINT" && -n "$S3_ACCESS_KEY" ]]; then
|
||||||
|
echo -e "Configuração existente encontrada:"
|
||||||
|
echo -e " Endpoint: ${GREEN}${S3_ENDPOINT}${NC}"
|
||||||
|
echo -e " Bucket: ${GREEN}${S3_BUCKET}${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "Deseja usar esta configuração?"
|
||||||
|
echo -e " 1) Sim"
|
||||||
|
echo -e " 2) Não, reconfigurar"
|
||||||
|
echo -n "Escolha [1/2]: "
|
||||||
|
read -r choice
|
||||||
|
if [[ "$choice" == "1" ]]; then
|
||||||
|
save_config
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo -n "Endpoint (ex: nbg1.your-objectstorage.com): "
|
||||||
|
read -r S3_ENDPOINT
|
||||||
|
if [[ -z "$S3_ENDPOINT" ]]; then
|
||||||
|
log_error "Endpoint não pode ser vazio"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extrair região do endpoint (fsn1, nbg1, etc)
|
||||||
|
S3_REGION=$(echo "$S3_ENDPOINT" | cut -d. -f1)
|
||||||
|
|
||||||
|
echo -n "Bucket name (ex: gitlab-workshop): "
|
||||||
|
read -r S3_BUCKET
|
||||||
|
if [[ -z "$S3_BUCKET" ]]; then
|
||||||
|
log_error "Bucket não pode ser vazio"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "Access Key: "
|
||||||
|
read -r S3_ACCESS_KEY
|
||||||
|
if [[ -z "$S3_ACCESS_KEY" ]]; then
|
||||||
|
log_error "Access Key não pode ser vazia"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "Secret Key: "
|
||||||
|
read -rs S3_SECRET_KEY
|
||||||
|
echo ""
|
||||||
|
if [[ -z "$S3_SECRET_KEY" ]]; then
|
||||||
|
log_error "Secret Key não pode ser vazia"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Salvar configuração
|
# Salvar configuração
|
||||||
save_config
|
save_config
|
||||||
}
|
}
|
||||||
@@ -238,6 +335,19 @@ EOF
|
|||||||
log_success "ClusterIssuer 'letsencrypt-prod' criado"
|
log_success "ClusterIssuer 'letsencrypt-prod' criado"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_object_storage_secrets() {
|
||||||
|
log_info "Criando Secrets para Hetzner Object Storage..."
|
||||||
|
|
||||||
|
# Exportar variáveis para envsubst
|
||||||
|
export S3_REGION S3_ACCESS_KEY S3_SECRET_KEY S3_ENDPOINT S3_BUCKET
|
||||||
|
|
||||||
|
# Aplicar secrets usando envsubst para substituir variáveis
|
||||||
|
envsubst < "$SCRIPT_DIR/object-storage-secret.yaml" | kubectl apply -f -
|
||||||
|
envsubst < "$SCRIPT_DIR/registry-storage-secret.yaml" | kubectl apply -f -
|
||||||
|
|
||||||
|
log_success "Secrets criados"
|
||||||
|
}
|
||||||
|
|
||||||
show_dns_instructions() {
|
show_dns_instructions() {
|
||||||
# Obter IP do LoadBalancer
|
# Obter IP do LoadBalancer
|
||||||
LB_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller \
|
LB_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller \
|
||||||
@@ -383,7 +493,15 @@ kubectl create namespace gitlab --dry-run=client -o yaml | kubectl apply -f -
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 6. INSTALAR GITLAB VIA HELM
|
# 6. CRIAR SECRETS DO OBJECT STORAGE
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
create_object_storage_secrets
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 7. INSTALAR GITLAB VIA HELM
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
log_info "=== Instalando GitLab (isso pode levar 10-15 minutos) ==="
|
log_info "=== Instalando GitLab (isso pode levar 10-15 minutos) ==="
|
||||||
@@ -391,11 +509,18 @@ log_info "=== Instalando GitLab (isso pode levar 10-15 minutos) ==="
|
|||||||
# Construir argumentos do Helm dinamicamente
|
# Construir argumentos do Helm dinamicamente
|
||||||
HELM_ARGS=""
|
HELM_ARGS=""
|
||||||
|
|
||||||
|
# Detectar cert-manager já instalado (ex: aula-09)
|
||||||
|
# Se existe, desabilitar o bundled para evitar conflito de CRDs
|
||||||
|
if kubectl get crd certificates.cert-manager.io &> /dev/null; then
|
||||||
|
log_info "cert-manager detectado - usando instalação existente"
|
||||||
|
HELM_ARGS="$HELM_ARGS --set installCertmanager=false"
|
||||||
|
fi
|
||||||
|
|
||||||
# Configurar hosts
|
# Configurar hosts
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.domain=${DOMAIN}"
|
HELM_ARGS="$HELM_ARGS --set global.hosts.domain=${DOMAIN}"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.gitlab.name=${GITLAB_HOST}"
|
HELM_ARGS="$HELM_ARGS --set global.hosts.gitlab.name=${GITLAB_HOST}"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.registry.name=${REGISTRY_HOST}"
|
HELM_ARGS="$HELM_ARGS --set global.hosts.registry.name=${REGISTRY_HOST}"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.minio.name=minio.${GITLAB_HOST}"
|
# MinIO desabilitado - usando Hetzner Object Storage
|
||||||
|
|
||||||
# Configurar TLS
|
# Configurar TLS
|
||||||
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
||||||
@@ -409,6 +534,8 @@ if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
|||||||
elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
||||||
# CloudFlare: TLS na edge, backend HTTP
|
# CloudFlare: TLS na edge, backend HTTP
|
||||||
# Workhorse precisa confiar nos IPs do CloudFlare para X-Forwarded-For
|
# Workhorse precisa confiar nos IPs do CloudFlare para X-Forwarded-For
|
||||||
|
HELM_ARGS="$HELM_ARGS --set global.ingress.configureCertmanager=false"
|
||||||
|
HELM_ARGS="$HELM_ARGS --set certmanager-issuer.install=false"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.ingress.tls.enabled=false"
|
HELM_ARGS="$HELM_ARGS --set global.ingress.tls.enabled=false"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.https=true"
|
HELM_ARGS="$HELM_ARGS --set global.hosts.https=true"
|
||||||
# CloudFlare IPv4 CIDRs (https://www.cloudflare.com/ips-v4)
|
# CloudFlare IPv4 CIDRs (https://www.cloudflare.com/ips-v4)
|
||||||
@@ -416,6 +543,8 @@ elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
|||||||
HELM_ARGS="$HELM_ARGS --set-json gitlab.webservice.workhorse.trustedCIDRsForXForwardedFor='${CLOUDFLARE_CIDRS}'"
|
HELM_ARGS="$HELM_ARGS --set-json gitlab.webservice.workhorse.trustedCIDRsForXForwardedFor='${CLOUDFLARE_CIDRS}'"
|
||||||
else
|
else
|
||||||
# Apenas HTTP
|
# Apenas HTTP
|
||||||
|
HELM_ARGS="$HELM_ARGS --set global.ingress.configureCertmanager=false"
|
||||||
|
HELM_ARGS="$HELM_ARGS --set certmanager-issuer.install=false"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.ingress.tls.enabled=false"
|
HELM_ARGS="$HELM_ARGS --set global.ingress.tls.enabled=false"
|
||||||
HELM_ARGS="$HELM_ARGS --set global.hosts.https=false"
|
HELM_ARGS="$HELM_ARGS --set global.hosts.https=false"
|
||||||
fi
|
fi
|
||||||
@@ -513,15 +642,23 @@ echo " - Gitaly (Git storage)"
|
|||||||
echo " - GitLab Shell (SSH)"
|
echo " - GitLab Shell (SSH)"
|
||||||
echo " - PostgreSQL"
|
echo " - PostgreSQL"
|
||||||
echo " - Redis"
|
echo " - Redis"
|
||||||
echo " - MinIO (object storage)"
|
echo " - Hetzner Object Storage (S3)"
|
||||||
echo " - Container Registry"
|
echo " - Container Registry"
|
||||||
echo ""
|
echo ""
|
||||||
|
# Determinar tipo de TLS para exibição
|
||||||
|
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
|
||||||
|
TLS_TYPE="Let's Encrypt"
|
||||||
|
elif [[ "$USE_CLOUDFLARE" == "true" ]]; then
|
||||||
|
TLS_TYPE="CloudFlare"
|
||||||
|
else
|
||||||
|
TLS_TYPE="Sem TLS (HTTP)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Configuração:"
|
echo "Configuração:"
|
||||||
echo " GitLab: ${GITLAB_HOST}"
|
echo " GitLab: ${GITLAB_HOST}"
|
||||||
echo " Registry: ${REGISTRY_HOST}"
|
echo " Registry: ${REGISTRY_HOST}"
|
||||||
echo " Domínio: ${DOMAIN}"
|
echo " Domínio: ${DOMAIN}"
|
||||||
echo " CloudFlare: ${USE_CLOUDFLARE}"
|
echo " TLS: ${TLS_TYPE}"
|
||||||
echo " Let's Encrypt: ${USE_LETSENCRYPT}"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "URLs:"
|
echo "URLs:"
|
||||||
echo " Web: ${PROTOCOL}://${GITLAB_HOST}"
|
echo " Web: ${PROTOCOL}://${GITLAB_HOST}"
|
||||||
|
|||||||
Reference in New Issue
Block a user