Installing on Kubernetes

In whichever environment we need discourse (example Staging or Production), we have to bootstrap discourse on one of the cluster’s nodes (AWS cluster created with KOPS), after having upgraded docker on the nodes to satisfy requirement for bootstrapping discourse image.
We bootstrap on one of the cluster’s nodes so that we’re in the VPC with access to pre-existing RDS Database (Postgres) and Elasticache (Redis).

I start with copying samples/web_only.yml to containers/discourse.yml

And I have to add SSL + Letsencrypt because if I disable force_https and make ELB http only origin policy, Google oauth in discourse will load over http, and discourse will mix loading assets over http/https which causes problems (ex: preview post stops working with errors in browser console about assets loaded over insecure http while connection is over https), having configured Cloudfront to redirect HTTP to HTTPS as the viewer policy.

templates:
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  - "templates/web.ssl.template.yml"
  - "templates/web.letsencrypt.ssl.template.yml"

Then define all vars needed in the “env” section, such as:
DISCOURSE_SMTP_*
DISCOURSE_DB_* (only leave _SOCKET blank since we’re using remote RDS DB)
DISCOURSE_REDIS_HOST

Also if your RDS database is newer version, say 9.6.x, discourse backups will not work because of older pg_dump version in the image, so I also edit the template to include (at the end):

run:
  - exec: apt-get -y purge postgresql-client-9.5 postgresql-9.5 postgresql-contrib-9.5
  - exec: apt-get -y install postgresql-client-9.6

Then the procedure to bootstrap with the above template (discourse.yml) and push to docker hub is easy:

# rm -rf /var/discourse

# git clone https://github.com/discourse/discourse_docker.git /var/discourse

# cd /var/discourse

# cp /home/admin/discourse/discourse.yml containers/

# ./launcher destroy discourse

# ./launcher bootstrap discourse

# docker tag $(docker images local_discourse/discourse -q) myorg/discourse:PROD-20171228

# docker login

# docker push myorg/discourse:PROD-20171228

If using EBS for the /shared volume, you’ll need to first make PV & PVC for a ebs volume:

kubectl apply -f discourse_ebs.yml

apiVersion: v1
items:
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: ebs-discourse
  spec:
    capacity:
      storage: 100Gi
    accessModes:
      - ReadWriteOnce
    storageClassName: ebs-discourse
    awsElasticBlockStore:
      volumeID: vol-xxxxxxxxxxxxxxx
      fsType: ext4
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ebs-discourse
  spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 100Gi
    storageClassName: ebs-discourse
kind: List
metadata: {}

Finally we can deploy, using a yaml similar to the following:

apiVersion: v1
items:
- apiVersion: v1
  kind: Service
  metadata:
    name: discourse
  spec:
    ports:
    - name: discourse-http
      port: 80
      targetPort: 80
    - name: discourse-https
      port: 443
      targetPort: 443
    selector:
      app: discourse
    type: LoadBalancer
    selector:
      app: discourse
    type: LoadBalancer
- apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    name: discourse
    labels:
      app: discourse
  spec:
    replicas: 1
    strategy:
      type: Recreate
    template:
      metadata:
        labels:
          app: discourse
      spec:
        containers:
        - image: myorg/discourse:PROD-20171228
          name: discourse
          command: ["/sbin/boot"]
          imagePullPolicy: Always
          ports:
          - containerPort: 80
          resources:
            requests:
              memory: "1Gi"
          env:
            - name: DISCOURSE_HOSTNAME
              value: forum.myorg.ngo
            - name: DISCOURSE_SITE_NAME
              value: 'MyOrg Forum'
            - name: DISCOURSE_DEVELOPER_EMAILS
              value: 'bla@myorg.ngo,bla2@myorg.ngo'
            - name: DISCOURSE_DB_HOST
              value: production.xxxxxxxxx.eu-central-1.rds.amazonaws.com
            - name: DISCOURSE_DB_SOCKET
              value: ''
            - name: DISCOURSE_DB_NAME
              value: 'discourse'
            - name: DISCOURSE_DB_USERNAME
              value: discoursedbuser
            - name: DISCOURSE_DB_PASSWORD
              value: LongSecurePasswordHere
            - name: DISCOURSE_REDIS_HOST
              value: redis-production.xxxxxx.xx.0001.euc1.cache.amazonaws.com
            - name: DISCOURSE_REDIS_PORT
              value: '6379'
            - name: DISCOURSE_SMTP_ADDRESS
              value: 'email-smtp.eu-west-1.amazonaws.com'
            - name: DISCOURSE_SMTP_PORT
              value: '587'
            - name: DISCOURSE_SMTP_USER_NAME
              value: xxxxxxxxxxxxxx
            - name: DISCOURSE_SMTP_PASSWORD
              value: xxxxxxxxxxxxxx
          volumeMounts:
          - mountPath: /shared
            name: discourse-discourse-data
        restartPolicy: Always
        volumes:
        - name: discourse-discourse-data
          persistentVolumeClaim:
            claimName: ebs-discourse
        imagePullSecrets:
        - name: services-secret
kind: List
metadata: {}

Finally go into the pod (kubectl exec -ti discourse-xxxx bash), run rails c, and at the prompt run SiteSetting.force_https = true and finally exit

Now you have HTTPS-ready ELB , so you can create a CloudFront distribution with origin as that ELB using HTTPS as the origin policy, and “Redirect HTTP to HTTPS” for the viewer policy.

Important notes:
You cannot build in one environment (ex: staging) and deploy in another (ex: production). Letsencrypt template builds into the image the discourse_hostname & letsencrypt_email used at build time (which you defined in containers/discourse.yml)

Cheers

8 Likes