RFC 0002 — CDN
| Campo | Valor |
|---|---|
| Status | Proposed |
| Data | 2026-03-16 |
| Afeta | charts/kelbi-app, halk-en-cdn, halk-jp-cdn, kelbi-cdn |
| Depende de | RFC 0001 — Storage |
Contexto
Existem dois padrões de CDN no cluster, ambos hoje usando nginx.conf embutido em values.yaml:
| App | Padrão | Descrição |
|---|---|---|
halk-en-cdn, halk-jp-cdn | Proxy reverso | Encaminha para API; API decide quais arquivos servir via X-Accel-Redirect |
kelbi-cdn | Arquivo estático | Serve arquivos diretamente de um storage |
Ambos compartilham o mesmo problema: o desenvolvedor precisa conhecer e escrever nginx para declarar intenção de negócio.
Problema
Acoplamento ao provedor
# valores atuais — nginx embutido nos dois padrões
configmapData:
contents:
nginx.conf: |
server {
location / { proxy_pass http://...; } # halk-cdn
location /assets { alias /data/assets; } # kelbi-cdn
Se o provedor mudar (nginx → Caddy, Envoy, Traefik), todos os values.yaml de todos os apps CDN precisariam ser reescritos. A troca de implementação vaza para o domínio do desenvolvedor.
Duplicação entre halk-en e halk-jp
Os dois apps diferem em apenas 4 campos, mas carregam 80 linhas de nginx.conf duplicadas em cada values.yaml. Qualquer mudança precisa ser replicada manualmente.
Outros problemas
| Problema | App | Detalhe |
|---|---|---|
| DNS hardcoded | halk-*-cdn | prod-halk-en-api.apps.svc.cluster.local:4000 em string multiline |
| HPA inútil | halk-*-cdn | minReplicas: 1 / maxReplicas: 1 — nunca age |
| Typo | kelbi-cdn | app.name: kelbi-cdb |
Princípio de design
O desenvolvedor declara o que quer entregar. A plataforma decide como entregar.
| Camada | Responsável | Exemplo |
|---|---|---|
| Intenção | Desenvolvedor | "proxy para o serviço X" ou "serve arquivos de /assets" |
| Implementação | Plataforma (chart) | nginx proxy_pass, X-Accel-Redirect, alias, sendfile |
| Infraestrutura | Plataforma (ops) | DNS interno, namespace |
values.yamlnginx, proxy_pass, server_name, alias, sendfile, DNS de cluster (*.svc.cluster.local).
Proposta
O novo bloco cdn unifica os dois padrões sob um vocabulário intencional. O tipo é declarado por mode.
Modo: proxy
Para apps que encaminham tráfego para uma API com suporte a file serving via X-Accel-Redirect.
cdn:
- mode: proxy
host: release.halk-en.arcamh.com
backend:
service: halk-en-api # plataforma resolve o DNS
port: 4000
fileServing: # intenção: API redireciona downloads para storage local
- mountPath: /_release/
storage: patch-data # referência ao storage do RFC 0001
subPath: release/mhfdat
- mountPath: /_base/
storage: patch-data
subPath: base/mhfdat
- mode: proxy
host: pre-release.halk-en.arcamh.com
backend:
service: halk-en-api
port: 4001
fileServing:
- mountPath: /_pre_release/
storage: patch-data
subPath: pre-release/mhfdat
- mountPath: /_base/
storage: patch-data
subPath: base/mhfdat
Modo: static
Para apps que servem arquivos diretamente de um storage.
cdn:
- mode: static
path: /assets # path público de acesso
storage: static-assets # referência ao storage do RFC 0001
autoindex: true
Vocabulário
| Campo | Modo | Significado | O que evita |
|---|---|---|---|
cdn[].mode | ambos | proxy ou static | mistura de conceitos num único bloco |
cdn[].host | proxy | Virtual host | server_name (nginx) |
cdn[].backend.service | proxy | Nome do serviço Kubernetes | DNS hardcoded de cluster |
cdn[].backend.port | proxy | Porta do serviço | proxy_pass :port (nginx) |
cdn[].fileServing[].mountPath | proxy | Path de acesso interno | location + internal (nginx) |
cdn[].fileServing[].storage | proxy | Storage lógico do RFC 0001 | alias /path/ (nginx) |
cdn[].fileServing[].subPath | proxy | Subdiretório dentro do storage | path absoluto no container |
cdn[].path | static | Path público de acesso | location /assets (nginx) |
cdn[].storage | static | Storage lógico do RFC 0001 | alias /data/assets (nginx) |
cdn[].autoindex | static | Listagem de diretório | autoindex on (nginx) |
O que o chart gera
- Modo proxy (nginx.conf)
- Modo static (nginx.conf)
- Troca de provedor
# gerado pelo chart — o dev nunca vê isso
server {
listen 80;
server_name release.halk-en.arcamh.com;
location / {
proxy_pass http://halk-en-api.apps.svc.cluster.local:4000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /_release/ {
internal;
alias /app/data/release/mhfdat/;
sendfile on;
}
location /health {
access_log off;
add_header Content-Type text/plain;
return 200 "healthy\n";
}
}
# gerado pelo chart — o dev nunca vê isso
server {
listen 80;
location /assets {
alias /data/assets/;
autoindex on;
}
location /health {
access_log off;
add_header Content-Type text/plain;
return 200 "healthy\n";
}
}
Para migrar de nginx para Caddy, a plataforma:
- Substitui o template
_cdn-nginx.tplpor_cdn-caddy.tpl - Atualiza a imagem padrão em
values.yaml - Faz bump de versão do chart
Os values.yaml dos apps não mudam. O desenvolvedor não sabe que houve troca.
Impacto nos apps
halk-en-cdn
- Antes (~119 linhas)
- Depois (~35 linhas)
configmapData:
name: prod-halk-en-cdn
contents:
nginx.conf: |
events { ... }
http {
server {
listen 80;
server_name release.halk-en.arcamh.com;
location / {
proxy_pass http://prod-halk-en-api.apps.svc.cluster.local:4000/;
...
}
location /_release/ { internal; alias /data/release/mhfdat/; sendfile on; }
# ... mais 40 linhas
}
}
volumes:
- name: data
hostPath:
path: /root/volumes/halk-patch-server/en
type: Directory
- name: nginx-conf
configMap:
name: prod-halk-en-cdn
volumeMounts:
- name: data
mountPath: /app/data
- name: nginx-conf
mountPath: /etc/nginx
readOnly: true
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 1
app:
name: "halk-en-cdn"
image: "nginx:1.29-alpine"
config:
port: 80
network:
expose: true
domain: "halk-en.arcamh.com"
health_check:
path: /health
autoscaling:
enabled: false
storage:
- name: patch-data
claim: halk-en-patch-data
mountPath: /app/data
cdn:
- mode: proxy
host: release.halk-en.arcamh.com
backend:
service: halk-en-api
port: 4000
fileServing:
- mountPath: /_release/
storage: patch-data
subPath: release/mhfdat
- mountPath: /_base/
storage: patch-data
subPath: base/mhfdat
- mode: proxy
host: pre-release.halk-en.arcamh.com
backend:
service: halk-en-api
port: 4001
fileServing:
- mountPath: /_pre_release/
storage: patch-data
subPath: pre-release/mhfdat
- mountPath: /_base/
storage: patch-data
subPath: base/mhfdat
halk-jp-cdn segue o mesmo padrão trocando en → jp e claim: halk-jp-patch-data.
kelbi-cdn
- Antes (~94 linhas)
- Depois (~40 linhas)
app:
name: "kelbi-cdb" # typo
image: "nginx:1.29-alpine"
volumes:
- name: cache
emptyDir: {}
- name: static
hostPath:
path: /root/volumes/kelbi/static
type: Directory
- name: nginx-conf
configMap:
name: prod-kelbi-cdn-kelbi-cdb
volumeMounts:
- name: static
mountPath: /data/assets
readOnly: true
- name: nginx-conf
mountPath: /etc/nginx
readOnly: true
- name: cache
mountPath: /var/cache/nginx
configmapData:
name: prod-kelbi-cdn-kelbi-cdb
contents:
nginx.conf: |
http {
server {
listen 80;
location /assets {
alias /data/assets;
autoindex on;
}
location /health { ... }
}
}
app:
name: "kelbi-cdn" # typo corrigido
image: "nginx:1.29-alpine"
config:
port: 80
network:
expose: true
domain: "cdn.arcamh.com"
health_check:
path: /health
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
podSecurityContext:
runAsNonRoot: true
runAsUser: 101
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
add: [NET_BIND_SERVICE]
storage:
- name: static-assets
claim: kelbi-static-assets
mountPath: /data/assets
readOnly: true
- name: cache
emptyDir: true
mountPath: /var/cache/nginx
- name: tmp
emptyDir: true
mountPath: /tmp
- name: run
emptyDir: true
mountPath: /run
cdn:
- mode: static
path: /assets
storage: static-assets
autoindex: true
Arquitetura futura — Nginx centralizado
Requer um controller/reloader que ainda não existe no cluster. A interface cdn[] é idêntica entre a arquitetura atual (nginx por app) e a futura (nginx centralizado). Migrar não exige nenhuma mudança nos values.yaml.
Mudanças no chart (kelbi-app 3.3.0)
| Arquivo | Mudança |
|---|---|
templates/_helpers.tpl | Helper kelbi-app.cdnConf — gera nginx.conf a partir de cdn[] e storage |
templates/cdn-configmap.yaml | Novo — cria ConfigMap <fullname>-cdn-conf quando cdn é definido |
templates/deployment.yaml | Injeta volume e volumeMount do cdn-conf automaticamente |
values.yaml | Adiciona cdn: [] com comentário de exemplo |
Compatibilidade
cdn é um novo bloco opcional. Apps sem esse campo continuam funcionando sem nenhuma alteração.
- Chart version:
3.3.0(minor bump, junto com RFC 0001) - Apps que migram:
halk-en-cdn,halk-jp-cdn,kelbi-cdn - Apps sem mudança: todos os outros
Pré-requisitos
Os PVCs precisam existir antes da migração. Requer RFC 0001 — Storage implementado.
halk-en-patch-datahalk-jp-patch-datakelbi-static-assets
Fora do escopo
- Abstração de volumes — RFC 0001 — Storage
- Nginx centralizado — arquitetura futura, sem data definida
- Headers de cache customizados por path
- Rate limiting declarativo
- Helper de DNS entre serviços — roadmap
Critério de aceitação
-
cdn[].mode: proxygera nginx.conf comproxy_passe locações internas corretas -
cdn[].mode: staticgera nginx.conf comaliaseautoindexcorretos - DNS resolvido pelo chart a partir de
backend.service+ namespace do release -
fileServing[].storage+subPathresolve para o alias correto usandostorage[].mountPath - Volume e volumeMount do cdn-conf injetados automaticamente no Deployment
- Apps sem
cdncontinuam renderizando sem alteração -
halk-en-cdnehalk-jp-cdnmigrados — sem nginx.conf raw, sem DNS hardcoded -
kelbi-cdnmigrado — sem nginx.conf raw, typo corrigido - Testes: ambos os modos, DNS correto, alias calculado, sem cdn = sem ConfigMap