apiVersion: apps/v1 kind: Deployment metadata: name: presentation namespace: astrololo spec: replicas: 1 selector: { matchLabels: { app: presentation } } template: metadata: { labels: { app: presentation } } spec: # LOG-33: pody nie rozmawiają z API Kubernetesa, więc token konta # serwisowego jest im niepotrzebny — a zamontowany byłby gotowym # punktem wyjścia do klastra dla kogoś, kto przejmie kontener. automountServiceAccountToken: false imagePullSecrets: [{ name: gitea-registry }] containers: - name: presentation image: gitea.czernobog.pl/gitea/astrololo-presentation:9323803c ports: [{ containerPort: 8000 }] env: - name: LOGIC_URL value: "http://logic:8001" # Logowanie do aplikacji (LOG-32). Bez tych sekretów aplikacja stoi # OTWARTA — a wystawia treść baz interpretacyjnych. Sekret tworzony # POZA repo (jak gitea-registry) — patrz README.md. - name: APP_PASSWORD valueFrom: secretKeyRef: { name: astrololo-auth, key: APP_PASSWORD } - name: INTERNAL_TOKEN valueFrom: secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } - name: APP_USER value: "astrololo" # Klucz podpisu ciasteczek sesji (LOG-34). WYMAGANY — pod bez niego # celowo nie wstaje: usługa z kontami, ale bez klucza, nie umiałaby # odróżnić ważnej sesji od podrobionej. Rotacja tego klucza WYLOGOWUJE # WSZYSTKICH, i tak ma być — to jest awaryjny wyłącznik. - name: SESSION_SECRET valueFrom: secretKeyRef: { name: astrololo-auth, key: SESSION_SECRET } - name: RATE_LIMIT_PER_MIN value: "120" # 0 = bez limitu # Aplikacja stoi za Ingressem, więc bezpośrednim rozmówcą jest zawsze # pod Traefika. Bez tego limit żądań liczyłby WSZYSTKICH do jednego # wiadra i pierwsza osoba, która go wyklika, odcięłaby pozostałych. # Włączać wyłącznie gdy ruch faktycznie idzie przez proxy — inaczej # nagłówek staje się furtką do ominięcia limitu. - name: TRUST_PROXY value: "true" # Szyfrowanie łącza między warstwami (PRE-16). OSOBNY klucz na każdą # parę rozmówców: przejęcie klucza prezentacji nie otwiera warstwy # danych, gdzie leżą całe bazy. Sekret tworzony POZA repo — patrz README. - name: LINK_KEY_PRESENTATION_LOGIC valueFrom: secretKeyRef: { name: astrololo-link, key: LINK_KEY_PRESENTATION_LOGIC } # Bez klucza usługa ma NIE wstać, a klient ma nie wysłać niczego. # Sam serwer broni się fail-closed, ale to za mało: klient bez klucza # zdążyłby wypuścić zapytanie jawnym tekstem, zanim dostanie odmowę. # Usługa składająca PDF (PRE-24) — osobny komponent. - name: RENDER_URL value: "http://render:8004" # discovery po nazwie Service # Trzeci klucz łącza: prezentacja ↔ render. Osobny, bo tym łączem # idzie CAŁY raport (dane urodzeniowe + opisy z baz). - name: LINK_KEY_PRESENTATION_RENDER valueFrom: secretKeyRef: { name: astrololo-link, key: LINK_KEY_PRESENTATION_RENDER } - name: LINK_ENCRYPTION_REQUIRED value: "true" # Konta zakładane z ekranu „Konta" (PRE-26). Konto administracyjne # zostaje w APP_USER/APP_PASSWORD powyżej — celowo, bo dzięki temu # NIE DA SIĘ go skasować ani ograniczyć z aplikacji. - name: ACCOUNTS_FILE value: "/app/state/accounts.json" volumeMounts: # OSOBNY UDZIAŁ, nie podkatalog udziału z bazami. Pierwsza wersja # montowała /mnt/Tank1/astrololo z subPath — i nie wstała: # „failed to create subPath directory”. Powód był podwójny i oba razy # ten sam brak: udział z bazami jest wyeksportowany `ro: true` # z `root_squash` (DAN-25), więc (1) kubelet nie mógł utworzyć # podkatalogu, a (2) gdyby nawet mógł, aplikacja i tak nie zapisałaby # tam pliku kont. # # Osobny udział rozwiązuje to bez naruszania DAN-25: udział z bazami # ZOSTAJE tylko do odczytu, a konta mają własne, małe miejsce. # Przy okazji znika subPath, czyli znika potrzeba, żeby kubelet # cokolwiek zakładał — katalog istnieje, bo jest korzeniem udziału. - name: state mountPath: /app/state resources: requests: { cpu: "100m", memory: "128Mi" } limits: { cpu: "300m", memory: "256Mi" } volumes: - name: state nfs: server: 192.168.1.34 # Udział WYŁĄCZNIE na stan prezentacji (konta z PRE-27). Wymaga # utworzenia na TrueNAS — patrz README-stan-prezentacji.md. path: /mnt/Tank1/astrololo-state --- apiVersion: v1 kind: Service metadata: name: presentation namespace: astrololo spec: # Wejście z przeglądarki idzie teraz WYŁĄCZNIE przez Ingress po https # (ingress.yaml). NodePort był drugą, nieszyfrowaną drogą do środka — czyli # obejściem całego PRE-16 — więc znika. type: ClusterIP selector: { app: presentation } ports: [{ port: 8000, targetPort: 8000 }]