Contextator’ı Kubernetes üzerinde çalıştırmanın desteklenen yolu bir Helm chart’ıdır. Bu sayfadaki her
şeyin kaynağı: depodaki charts/contextator/ altındaki chart’ın kendisi ve onun kendi, daha uzun
charts/contextator/README.md’si.
Kurulum
helm repo add contextator https://contextator.github.io/Contextator
helm repo update
helm install contextator contextator/contextator \
--set database.url="postgres://user:pass@host:5432/contextator" \
--set-string image.tag="<version>-slim"
database.url (ya da zaten yönettiğiniz bir Secret için database.existingSecret) ve image.tag
ikisi de zorunludur ve varsayılanı yoktur — ikisinden biri eksikse chart hiçbir şey render etmeden önce
helm install/helm template’i anında başarısız kılar. image.tag bir -slim build’ine işaret etmelidir;
o son ek olmadan gelen tag’ler gömülü PostgreSQL’li tam imajdır, bu chart için yanlış topolojidir. Ya da
chart dizininin checkout edilmiş bir kopyasından doğrudan kurun:
helm install contextator ./charts/contextator -f my-values.yaml — aynı iki değer, değer dosyasında ya
da komut satırında olsun, yine zorunludur.
Tasarım gereği tek replica
replicaCount desteklenen bir değer değildir — chart onu doğrudan reddeder
(additional properties 'replicaCount' not allowed) ve bu bilerek yapılmıştır, bir eksiklik değil:
Contextator kendi indeksleme kuyruğuna karşı tek bir yazıcı çalıştırır, ve onu kümelemek ürünün henüz
sahip olmadığı bir koordinasyon katmanı gerektirirdi (ADR-0078). Pod’un sayısını değil, kendi
kaynaklarını ölçekleyin; aşağıdaki Kaynak boyutlandırma bölümüne bakın.
Veritabanı
Chart’a ya doğrudan bir bağlantı dizesi verin (database.url) ya da zaten yönettiğiniz bir Secret’a
işaret edin (database.existingSecret) — Yapılandırma’nın Kubernetes dışında
DATABASE_URL için anlattığı seçimin aynısı. PostgreSQL’in kendisi bu chart’ın parçası değildir; onu
ayrıca çalıştırın ya da yönetilen bir örneğe işaret edin.
SECRET_KEY: bir kez üretilir, sabit kalır
SECRET_KEY kaynak kimlik bilgilerini durağan halde şifreler — git, Notion ve Confluence tokenları ve
webhook sırları — bu yüzden onu kaybetmek yalnızca herkesi oturumdan atmaz, aynı zamanda o saklanan
kimlik bilgilerini kurtarılamaz hale getirir. Ayarlanmadan bırakıldığında chart ilk kurulumda bir tane
üretir ve — yeni bir tane üretmeye karar vermeden önce çalışan kümenin Secret’ını Helm’in lookup
fonksiyonuyla okuduğu için — siz hiçbir şey yapmadan sonraki her helm upgrade boyunca aynı değeri
korur.
lookup fonksiyonu yalnızca gerçek, erişilebilir bir kümeye karşı çalışır. Chart’ı offline render
etmek — helm template, ya da render edilmiş bir manifestten canlı bir lookup olmadan reconcile eden
bir GitOps controller’ı — her seferinde var olan bir Secret görmez ve her render’da yeni bir anahtar
üretir, ki bu tam olarak çalışan bir örneğin altında değişmemesi gereken değerdir. Böyle bir kurulumda,
anahtarı bir kez kendiniz üretin ve chart’a onu üretmesine izin vermek yerine var olan bir Secret olarak
elden verin:
kubectl create secret generic contextator-secret-key \
--from-literal=SECRET_KEY=$(openssl rand -hex 32)
secretKey:
existingSecret: contextator-secret-key
existingSecretKey: SECRET_KEY
Onu rotasyona sokmak bir değer değişimi değil, iki adımlı bir harekettir. Önce mevcut değeri yeni bir
adla saklayın — contextator-secret-key yeni anahtarla üzerine yazılmak üzeredir, bu yüzden herhangi bir
şey üretmeden önce eskisini kopyalayıp çıkarın:
kubectl -n <namespace> create secret generic contextator-secret-key-previous \
--from-literal=SECRET_KEY="$(kubectl -n <namespace> get secret contextator-secret-key -o jsonpath='{.data.SECRET_KEY}' | base64 -d)"
Sonra yeni anahtarı üretin ve onu contextator-secret-key’e yerinde yazın (kubectl create tek başına
zaten var olan bir Secret’ta başarısız olur):
kubectl -n <namespace> create secret generic contextator-secret-key \
--from-literal=SECRET_KEY="$(openssl rand -hex 32)" \
--dry-run=client -o yaml | kubectl -n <namespace> apply -f -
Yeni anahtar yukarıdaki gibi sabitlenmişken (secretKey.existingSecret/secretKey.existingSecretKey),
sürece eskisini envSecret üzerinden SECRET_KEY_PREVIOUS olarak verin; onun girdileri her zaman
AD: {secretName, secretKey} biçimindedir:
envSecret:
SECRET_KEY_PREVIOUS:
secretName: contextator-secret-key-previous
secretKey: SECRET_KEY
İkisi de mevcutken ve Pod yeniden başlatıldıktan sonra, Deployment adını bulun (<release>-contextator,
release adı zaten contextator içermiyorsa ve fullnameOverride ayarlanmamışsa) ve rotasyonu onun
içinde çalıştırın:
kubectl -n <namespace> get deploy -l app.kubernetes.io/instance=<release>
kubectl -n <namespace> exec deploy/<deployment-name> -- npm run rotate-secret
Eski anahtarın şifrelediğini çözer ve yenisinin altında yeniden şifreler. Dönüştürülecek bir şey
kalmadığını bildirdiğinde, SECRET_KEY_PREVIOUS’ı (envSecret girdisini) kaldırın ve Pod’un tekrar
yeniden başlamasına izin verin — eski anahtarı emekliye ayıran o kaldırmadır. Anahtar tamamen kaybolduysa
ve kurtaracak bir SECRET_KEY_PREVIOUS yoksa, onun altında saklanan kaynak kimlik bilgileri çözülemez —
geri dönüşün tek yolu o kaynakları yeniden eklemektir.
Ingress ve reverse-proxy üçlüsü
Chart’ın önündeki bir Ingress, herhangi bir başka reverse proxy gibidir, ve
Yapılandırma’daki aynı üçlü geçerlidir:
config.trustProxy, config.publicBaseUrl ve env.AUTH_COOKIE_SECURE birlikte hareket eder, yoksa
IP başına oturum açma sınırı, MCP connector yetkilendirmesi ve oturum çerezi her biri kendi sessiz
biçiminde bozulur.
Bir sıçrama sayısı (2, 3, …) reddedilir; 1/true kabul edilir ve her sıçramaya güven anlamına
gelir. Bu, Yapılandırma’nın Kubernetes
dışında anlattığı aynı korumadır: chart config.trustProxy’yi doğrulamadan geçirir, bu yüzden "1" her
sıçramada X-Forwarded-For başlığını kim yazdıysa ona güvenir — yalnızca bu Pod’un portuna ingress
controller’dan başka bir şeyin ulaşmasına izin vermeyen bir NetworkPolicy arkasında güvenlidir. Bunun
yerine proxy’yi adlandırın, onu gerçekten kapsayan en dar aralığı:
config:
publicBaseUrl: "https://contextator.example.com"
trustProxy: "<ingress-controller-pod-ip-or-cidr>"
env:
AUTH_COOKIE_SECURE: "1"
Değer yalnızca socket’in eşine karşı değil her sıçramaya karşı eşleştirilir, bu yüzden tüm pod CIDR’ını yapıştırmak bu Service’e ulaşabilen her diğer pod’a da güvenir — yalnızca ingress controller’dan başka bir şeyin bu pod’un portuna ulaşmasına izin vermeyen bir NetworkPolicy arkasında güvenlidir.
Kalıcılık
İki ReadWriteOnce PersistentVolumeClaim: /data (veritabanına bitişik çalışma dosyaları) ve
/app/.cache/models (indirilen gömme modeli, böylece bir pod yeniden başlatması onu yeniden indirmez).
/data’nın, canlı bir kümede, bir pod yeniden başlatmasından bütün çıktığı doğrulanmıştır.
Probe’lar
Liveness, porttaki salt bir TCP kontrolüdür — veritabanına bağımlı olmadan tıkanmış bir süreci
yakalamaya yeter. Readiness ve startup ikisi de gerçek bir GET /api/health çağrısıdır, bu yüzden bir
pod yalnızca bir bağlantı kabul ettiğinde değil, uygulama gerçekten cevap verebildiğinde ready
işaretlenir. Bir veritabanı kesintisine karşı canlı bir kümede doğrulanmıştır: pod bu süre boyunca
READY 0/1 raporlar, liveness hiçbir zaman veritabanına bağımlı olmadığı için sıfır restart alır,
ve veritabanı geri geldiği anda hiçbir manuel pod restart’ı gerekmeden otomatik olarak ready’e döner.
Security context
Container uid 0 olarak başlamak zorundadır — opsiyonel değildir, ve bunu yasaklayan sertleştirilmiş
bir securityContext, “the container must start as root” hatasıyla başlangıçta başarısız olur. Bu
kısa, yapısal bir gerekliliktir, kalıcı değil: entrypoint o ilk anı root olarak yukarıdaki iki bağlı
volume’un sahipliğini düzeltmek için kullanır (taze bir PVC varsayılan olarak root sahiplidir), sonra
herhangi bir uygulama kodu çalışmadan önce gosu ile ayrıcalıksız bir node kullanıcısına hemen
devreder. Trafiği gerçekten sunan süreç hiçbir zaman root olarak çalışmaz.
İmaj ayrıca wget ya da curl taşımaz — kendi healthcheck’i ve chart testleri, Dockerfile’dan
varsayılmak yerine gerçek bir helm test çalıştırmasıyla doğrulanmış şekilde, Node’un yerleşik
fetch()’iyle birlikte node -e kullanır.
Kaynak boyutlandırma
Aşağıdaki sayılar tahmin edilmemiş, çalışan bir örneğe karşı ölçülmüştür:
| Durum | CPU | Bellek |
|---|---|---|
| Boşta | düşük | ~770–820 MiB RSS |
| İndeksleme patlaması | 2–3 saniye boyunca ~10 çekirdek | — |
| Soğuk başlangıç | — | ~72,4 saniyede hazır |
resources.requests/resources.limits’i boşta değerinin üzerinde bir marjla ve indeksleme
patlamaları için yeterli burst kapasitesiyle ayarlayın; bir patlama sırasında sıkı bir CPU limiti diğer
pod’ları etkilemek yerine yalnızca tek bir indeksleme çalışmasını yavaşlatır, çünkü zaten hep tek bir
replica vardır.
Kaldırma
helm uninstall varsayılan olarak iki PersistentVolumeClaim’i geride bırakır — bunlar bir
helm.sh/resource-policy: keep anotasyonu taşır, bu yüzden indekslenmiş veri ve önbelleğe alınmış
model kazara bir kaldırmadan sağ çıkar. SECRET_KEY Secret’ı böyle bir politika taşımaz ve release ile
birlikte silinir; aynı veriye karşı yeniden kurmayı planlıyorsanız önce anahtarı kaydedin (yukarıdaki
SECRET_KEY: bir kez üretilir, sabit kalır’e bakın) yoksa
hayatta kalan PVC’lerin hâlâ referans verdiği kaynak kimlik bilgilerini çözemezsiniz.
Katı values şeması
Chart, değerlerini katı bir JSON Schema’ya karşı doğrular (ADR-0092) — tanınmayan bir key, sessizce yok
sayılmak yerine helm install/helm upgrade’i anında başarısız kılar. Yanlış yolda aranması kolay
olduğu için adıyla bilinmeye değer iki key: config.confluenceAllowedHosts ve
config.mcpStructuredOutput ikisi de tipli, üst düzey config.* key’leridir, başka bir yerde iç içe
serbest değerler değil.
Yayımlama
Chart release’leri deponun kendi release iş akışını izler ve gh-pages üzerinde barındırılan bir Helm
deposuna yayımlanır, uygulamanın kendi sürümünden bağımsız olarak sürümlenir — tam release ve
sürümleme kurallarını depodaki chart’ın kendi charts/contextator/README.md’sinde bulabilirsiniz.