# Installazione su Kubernetes

**URL:** <https://meta.discourse.org/t/installing-on-kubernetes/49329>\
**Category:** Self-hosting\
**Tags:** unsupported-install\
**Created:** [27 Agosto 2016, 10:59pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329 "2016-08-27T22:59:09Z")\
**Posts on this page:** 1\
**Showing post:** 38

<div class="post-metadata">

**Author:** ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)\
**Post date:** [9 Febbraio 2020, 11:24pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/38 "2020-02-09T23:24:23Z")

</div>

OK… Sono riuscito a trovare un modo per farlo. Non ne sono completamente soddisfatto, ma funziona e potrebbe essere interessante per altri che stanno cercando di implementare un’immagine Discourse “monolitica” (che include PostgreSQL, Redis, ecc.) in un singolo contenitore su Kubernetes.

### Il mio approccio

Dopo aver esaminato il processo di avvio, mi è diventato chiaro che, purtroppo, questo mescola due tipi diversi di operazioni: alcune che influenzano solo il contenitore sottostante e altre che interagiscono con l’ambiente circostante, principalmente attraverso il montaggio del volume `/shared` dove risiedono i file di dati di PostgreSQL. Invece di cercare di separare questi passaggi, sembra più ragionevole eseguire semplicemente i passaggi di avvio nell’ambiente in cui il contenitore verrà effettivamente distribuito.

Purtroppo, `launcher bootstrap` vuole creare un contenitore e utilizzare Docker. Quindi, eseguire `launcher` all’interno di un altro contenitore (ad esempio, in un contenitore in esecuzione sul nostro cloud) significa o impelagarsi in una configurazione Docker-in-Docker (fattibile, ma non considerata una buona pratica) o esporre il demone Docker sottostante. Non sono nemmeno sicuro che il secondo approccio funzionerebbe, poiché penso che interpretarebbe un montaggio del volume rispetto al filesystem locale del nodo, mentre nel nostro scenario vogliamo montare `/shared` su un volume Kubernetes persistente. _Forse_ la strada Docker-in-Docker potrebbe funzionare, ma allora avresti anche un bizzarro triplo montaggio del volume: dal contenitore nidificato a quello esterno e da lì al volume Kubernetes persistente. Sembra… poco saggio.

Tuttavia, essenzialmente `launcher bootstrap` crea un unico grande file `.yml` elaborando il valore `templates` in `app.yml` e lo passa all’immagine base di Discourse al termine del processo di avvio. Quindi, se riusciamo a estrarre il file di configurazione, possiamo generarlo su qualsiasi macchina e poi dobbiamo solo capire come passarlo a un contenitore che avvieremo nel cloud.

Quindi, come panoramica, ecco i passaggi che seguiremo:

1. Generare la configurazione di avvio utilizzando una versione modificata di `launcher`
2. Passarla a un’immagine base di Discourse modificata che eseguirà l’avvio (usando `pups`) e poi avvierà Discourse

### Generazione della configurazione di avvio

Ecco la modifica necessaria a `launcher` per supportare un comando `dump` che scrive la configurazione unita sullo STDOUT:

```bash
run_dump() {
  set_template_info
  echo "$input"
}

```

(Tieni presente che questo comando è disponibile nel [nostro fork di `discourse_docker`](https://github.com/cs125-illinois/discourse_docker).)

Quindi, il primo passaggio è utilizzare il nuovo comando `launcher dump` aggiunto sopra per creare la nostra configurazione di avvio:

```bash
# Sostituisci con il nome della tua configurazione del contenitore per app
./launcher dump app > bootstrap.yml

```

### Creazione iniziale del contenitore

Successivamente, abbiamo bisogno di un contenitore che sappia eseguire `pups` per avviare il contenitore prima di avviarsi tramite `/sbin/boot`. Ho utilizzato il seguente Dockerfile per apportare una piccola modifica all’immagine base di Discourse:

```Dockerfile
FROM discourse/base:2.0.20191219-2109
COPY scripts/bootstrap.sh /
CMD bash bootstrap.sh

```

Dove `scripts/bootstrap.sh` contiene:

```bash
cd /pups/ && /pups/bin/pups --stdin < /bootstrap/bootstrap.yml && /sbin/boot

```

Ho pubblicato questo come `geoffreychallen:discourse_base:2.0.20191219-2109`. (Tieni presente che probabilmente potresti ottenere lo stesso risultato modificando il comando di avvio dell’immagine Docker base di Discourse, ma avevo difficoltà a farlo funzionare con la reindirizzazione della shell necessaria per far leggere a `pups` il file di configurazione.)

### Configurazione Kubernetes

Ora abbiamo bisogno della nostra configurazione Kubernetes. La mia è così:

```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: kotlin-forum-pvc
  namespace: ikp
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 64Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kotlin-forum-deployment
  namespace: ikp
spec:
  replicas: 0
  selector:
    matchLabels:
      app: kotlin-forum
  template:
    metadata:
      labels:
        app: kotlin-forum
    spec:
      volumes:
      - name: kotlin-forum
        persistentVolumeClaim:
          claimName: kotlin-forum-pvc
      - name: bootstrap
        configMap:
          name: kotlin-forum-bootstrap
      containers:
      - name: kotlin-forum
        image: geoffreychallen/discourse_base:2.0.20191219-2109
        imagePullPolicy: Always
        volumeMounts:
        - name: kotlin-forum
          mountPath: /shared/
        - name: bootstrap
          mountPath: /bootstrap/
        ports:
        - containerPort: 80
        env:
        - name: TZ
          value: "America/Chicago"
        - name: LANG
          value: en_US.UTF-8
        - name: RAILS_ENV
          value: production
        - name: UNICORN_WORKERS
          value: "3"
        - name: UNICORN_SIDEKIQS
          value: "1"
        - name: RUBY_GLOBAL_METHOD_CACHE_SIZE
          value: "131072"
        - name: RUBY_GC_HEAP_GROWTH_MAX_SLOTS
          value: "40000"
        - name: RUBY_GC_HEAP_INIT_SLOTS
          value: "400000"
        - name: RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR
          value: "1.5"
        - name: DISCOURSE_DB_SOCKET
          value: /var/run/postgresql
        - name: DISCOURSE_DEFAULT_LOCALE
          value: en
        - name: DISCOURSE_HOSTNAME
          value: kotlin-forum.cs.illinois.edu
        - name: DISCOURSE_DEVELOPER_EMAILS
          value: challen@illinois.edu
        - name: DISCOURSE_SMTP_ADDRESS
          value: outbound-relays.techservices.illinois.edu
        - name: DISCOURSE_SMTP_PORT
          value: "25"
---
apiVersion: v1
kind: Service
metadata:
  name: kotlin-forum
  namespace: ikp
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 80
  selector:
    app: kotlin-forum
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: ikp
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: kotlin-forum-ingress
spec:
  rules:
  - host: kotlin-forum.cs.illinois.edu
    http:
      paths:
      - backend:
          serviceName: kotlin-forum
          servicePort: 80

```

La tua sarà diversa. Tieni presente che sto terminando l’HTTPS a monte, da qui le modifiche alla configurazione Ingress. Mi piace anche mettere tutto in un unico file, cancellare i pezzi che non funzionano mentre itero, e poi lasciare che Kubernetes salti i duplicati al prossimo `kubectl create -f`. Nota anche che ho impostato `replicas: 0` in modo che la distribuzione non inizi non appena viene configurata. Questo perché abbiamo un ulteriore elemento di configurazione da completare.

Ho copiato l’elenco delle variabili d’ambiente da ciò che ho visto passare al contenitore tramite `launcher start`. Non so se tutti questi siano necessari e altri potrebbero mancare a seconda della tua configurazione. C’è chi ne usa di più e chi di meno (YMMV).

Nota che abbiamo due mappature di volumi che puntano al contenitore: la prima è per PostgreSQL, configurata come volume persistente che sopravviverà ai riavvii dei pod. La seconda è una mappatura di configurazione creata in questo modo:

```bash
kubectl create configmap kotlin-forum-bootstrap --from-file=bootstrap.yml=<path/to/bootstrap.yml>

```

Dove `kotlin-forum-bootstrap` deve corrispondere alla tua configurazione Kubernetes e `path/to/bootstrap.yml` è il percorso del file bootstrap.yml creato sopra utilizzando `launcher dump`.

Una volta che il tuo `configmap` è in posizione, dovresti essere in grado di scalare la tua distribuzione a una replica e vedere Discourse avviarsi ed eseguire lo stesso processo di avvio che `launcher bootstrap` avrebbe eseguito. Ci vogliono alcuni minuti. Quando questo è completato, la tua installazione di Discourse si avvierà.

### Altri dettagli di configurazione

Alcune altre note che ho incontrato lungo la strada per ottenere questa configurazione (almeno per ora) completamente funzionante:

- Qualsiasi proxy a monte _deve_ inoltrare le intestazioni `X-Forwarded`, inclusi sia `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Port`. Non farlo comporterà strani errori di autenticazione quando si tenta di utilizzare l’accesso con Google e probabilmente altri provider di accesso.
- Il tuo controller ingress `nginx` deve essere configurato per passare le intestazioni impostando `use-forwarded-headers` nella configurazione globale del configmap. Ci è voluto un po’ per farlo funzionare correttamente, poiché almeno diverse volte ho modificato il configmap sbagliato e poi mi aspettavo che i miei container ingress si riavviassero quando il configmap cambiava. (Non lo hanno fatto.)

### Aggiornamento

Per aggiornare l’installazione distribuita, rigeneri il nuovo file `bootstrap.yml`, aggiorni il configmap e poi riavvii il contenitore (più facilmente scalando a 0 e poi di nuovo a 1 replica).

Questo comporta un po’ di downtime poiché l’avvio avviene prima che il contenitore sia costruito. Ma questo mi sembra inevitabile nei casi in cui è necessario aggiornare la configurazione e/o cambiare l’immagine base. `launcher rebuild` è documentato come `stop; bootstrap; start`, il che significa che il processo di avvio causerà comunque downtime anche se eseguito utilizzando lo script launcher.

### Commenti

Questo modello di distribuzione monolitica di Discourse sarebbe molto più semplice da supportare se lo script `launcher` separasse più chiaramente (a) i passaggi di avvio che potrebbero essere eseguiti offline e influenzare solo i file nel contenitore e (b) i passaggi di avvio che modificano o necessitano accesso al database o ad altro stato esterno al contenitore. L’approccio descritto sopra è un po’ frustrante perché vedi tutti i tipi di offuscamento JS, minimizzazione delle risorse e altre cose che potrebbero essere fatte con la distribuzione precedente in esecuzione… ma sono semplicemente troppo mescolate con altre cose (come le migrazioni del database) che non possono essere eseguite senza accesso al database. Ho pensato brevemente di creare un contenitore che eseguisse solo i passaggi in `templates/postgres.yml`, ma poi ho notato che le migrazioni del database venivano eseguite dal template web, ho pensato ai plugin e poi ho semplicemente rinunciato 🙂.

Con una migliore separazione, la ridistribuzione per i contenitori monolitici potrebbe funzionare in questo modo:

- Costruire un nuovo contenitore monolitico offline eseguendo tutti i passaggi interni al contenitore
- Pubblicare quel contenitore
- Quando si è pronti per l’aggiornamento, fermare il contenitore precedente, avviare il nuovo contenitore monolitico e lasciarlo completare eventuali passaggi che necessitano accesso al database. In base ai miei esperimenti, questi sembrano più veloci di alcuni degli altri passaggi di avvio.

Questo comporterebbe un po’ meno downtime. Probabilmente non vale la pena del solo sforzo per questo motivo, ma posso immaginare che questo potrebbe anche semplificare scenari di distribuzione più complessi che coinvolgono database condivisi o altro.

---

_[View the full topic](https://meta.discourse.org/t/installing-on-kubernetes/49329)._
