Commit 240c705a authored by Ruslan Konviser's avatar Ruslan Konviser
Browse files

feat: adding DO k8s deployments (WIP)

parent d43486c1
Loading
Loading
Loading
Loading
+92 −28
Original line number Diff line number Diff line
# API

FROM node:alpine AS development
# Ever Core (API)

ARG NODE_OPTIONS
ARG NODE_ENV
ARG API_BASE_URL
ARG API_HOST
ARG API_PORT
ARG DB_URI
ARG DB_HOST
ARG DB_NAME
ARG DB_PORT
ARG DB_USER
ARG DB_PASS
ARG DB_TYPE
ARG DB_SSL_MODE
ARG DEMO
ARG HOST
ARG PORT

FROM node:alpine AS dependencies

LABEL maintainer="ever@ever.co"

ENV CI=true

RUN apk update \
	&& apk add libexecinfo libexecinfo-dev \
@@ -9,49 +30,92 @@ RUN apk update \
	snappy g++ snappy-dev gcc libgcc libstdc++ linux-headers autoconf automake make nasm python git \
	&& npm install --quiet node-gyp -g \
	&& npm config set python /usr/bin/python \
	&& npm install yarn -g --force
	mkdir /srv/ever && chown -R node:node /srv/ever

RUN mkdir /srv/ever && chown node:node /srv/ever
COPY wait .deploy/core/entrypoint.sh /
RUN chmod +x /wait /entrypoint.sh && dos2unix /entrypoint.sh

USER node
USER node:node

WORKDIR /srv/ever

COPY --chown=node:node package.json yarn.lock lerna.json package.workspaces.json ./
COPY --chown=node:node package.json yarn.lock lerna.json package.workspaces.json tsconfig.base.json ./
COPY --chown=node:node packages/core/package.json ./packages/core/package.json
COPY --chown=node:node .snyk ./.snyk
COPY --chown=node:node packages/core/.snyk ./packages/core/.snyk
COPY --chown=node:node packages/common ./packages/common

RUN yarn install
RUN yarn install --frozen-lockfile && yarn cache clean

FROM node:alpine AS production
FROM node:alpine AS development

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.6.0/wait /wait
USER node:node

RUN npm install cross-env -g \
	&& npm install pm2 -g --unsafe-perm \
	&& chmod +x /wait
WORKDIR /srv/gauzy

RUN mkdir /srv/ever && chown node:node /srv/ever
RUN mkdir -p /srv/ever/packages/core && chown node:node /srv/ever/packages/core
RUN mkdir -p /srv/ever/packages/common && chown node:node /srv/ever/packages/common
COPY --chown=node:node --from=dependencies /wait /entrypoint.sh /
COPY --chown=node:node --from=dependencies /srv/ever .
COPY . .

USER node
FROM node:alpine AS build

WORKDIR /srv/ever

ARG NODE_OPTIONS="--max-old-space-size=2048"
ENV NODE_OPTIONS $NODE_OPTIONS
ENV NODE_ENV production
RUN mkdir dist

COPY --from=development --chown=node:node /srv/ever/node_modules ./node_modules
COPY --from=development --chown=node:node /srv/ever/packages/core/node_modules ./packages/core/node_modules
COPY --chown=node:node package.json yarn.lock lerna.json package.workspaces.json tsconfig.base.json ./
COPY --chown=node:node packages/common ./packages/common
COPY --chown=node:node packages/core ./packages/core
COPY --chown=node:node --from=development /srv/ever .

EXPOSE 5500 5501 5050 5555
ENV NODE_OPTIONS=${NODE_OPTIONS:-"--max-old-space-size=2048"}
ENV NODE_ENV=${NODE_ENV:-production}
ENV DEMO=${DEMO:-false}

CMD /wait \
	&& yarn --cwd ./packages/common build && yarn run:server
 No newline at end of file
ENV IS_DOCKER=true

RUN yarn build:server

FROM node:alpine AS production

WORKDIR /srv/ever

RUN mkdir /srv/ever && chown node:node /srv/ever
RUN mkdir -p /srv/ever/packages/core && chown node:node /srv/ever/packages/core
RUN mkdir -p /srv/ever/packages/common && chown node:node /srv/ever/packages/common

COPY --chown=node:node --from=dependencies /wait ./wait
COPY --chown=node:node --from=dependencies /entrypoint.sh .
COPY --chown=node:node --from=dependencies /srv/ever/node_modules ./node_modules
COPY --chown=node:node --from=build /srv/ever/packages/common/ ./packages/common/
COPY --chown=node:node --from=build /srv/ever/packages/core/ ./packages/core/

RUN npm install cross-env -g \
	&& npm install pm2 -g --unsafe-perm \
	&& touch ormlogs.log && chown node:node ormlogs.log \
	&& chown node:node wait && chmod +x /wait

USER node:node

ENV NODE_OPTIONS=${NODE_OPTIONS:-"--max-old-space-size=2048"}
ENV NODE_ENV=${NODE_ENV:-production}
ENV API_HOST=${API_HOST:-api}
ENV API_PORT=${API_PORT:-5500}
ENV API_BASE_URL=${API_BASE_URL:-http://localhost:5500}
ENV DB_URI=${DB_URI}
ENV DB_HOST=${DB_HOST:-db}
ENV DB_NAME=${DB_NAME:-ever}
ENV DB_PORT=${DB_PORT:-27017}
ENV DB_USER=${DB_USER}
ENV DB_PASS=${DB_PASS}
ENV DB_TYPE=${DB_TYPE:-mongodb}
ENV DB_SSL_MODE=${DB_SSL_MODE}
ENV HOST=${HOST:-0.0.0.0}
ENV PORT=${PORT:-5500}
ENV DEMO=${DEMO:-false}

# 5500 for HTTP
# 5501 for HTTPS
# 5555 for GraphQL
# 5050 for GraphQL Subscriptions

EXPOSE ${PORT} 5501 5050 5555

CMD [ "pm2-runtime", "main.js" ]
 No newline at end of file
+11 −0
Original line number Diff line number Diff line
#!/bin/sh
set -ex

# This Entrypoint used inside Docker Compose only

export WAIT_HOSTS=$DB_HOST:$DB_PORT

# in Docker Compose we should wait other services start
./wait

exec "$@"
 No newline at end of file

.deploy/k8s/README.md

0 → 100644
+50 −0
Original line number Diff line number Diff line
# Kubernetes

Let's assume current k8s kube config file saved as `k8s-ever-kubeconfig.yaml`

## Verify connectivity

kubectl --kubeconfig="k8s-ever-kubeconfig.yaml" get nodes

## Deploy

-   To Deploy

`kubectl --kubeconfig="k8s-ever-kubeconfig.yaml" --context do-sfo2-k8s-ever apply -f k8s-manifest.yaml`

Note: it assume we have context called `do-sfo2-k8s-ever` defined already

-   To Describe Deployment

`kubectl describe deployment --kubeconfig="k8s-ever-kubeconfig.yaml"`

-   To Redeploy (if use latest docker images versions)

`kubectl --kubeconfig="k8s-ever-kubeconfig.yaml" --context do-sfo2-k8s-ever rollout restart -f k8s-manifest.yaml`

## Monitoring

See <https://marketplace.digitalocean.com/apps/kubernetes-monitoring-stack>

`kubectl --kubeconfig="k8s-ever-kubeconfig.yaml" port-forward svc/kube-prometheus-stack-grafana 8090:80 -n kube-prometheus-stack`

Your Grafana instance will now be available at http://localhost:8090.

Default credentials:

Username: admin
Password: prom-operator

Note: change password after login!

`kubectl --kubeconfig="k8s-ever-kubeconfig.yaml" port-forward svc/kube-prometheus-stack-prometheus 9090 -n kube-prometheus-stack`

Your Prometheus instance will now be available at http://localhost:9090.

## Links

-   [DO, WebSockets, Load Balancers](https://medium.com/swlh/how-to-use-web-sockets-socket-io-with-digital-ocean-load-balancers-and-kubernetes-dok8s-with-e4dd5531c67e)
-   [Load Balancers Configs in DO](https://www.digitalocean.com/docs/kubernetes/how-to/configure-load-balancers)
-   [K8s Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-controllers)
-   [K8s Ingress with Nginx + SSL LetsEncrypt](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes)
-   [Github Actions with DO](https://github.com/do-community/example-doctl-action/blob/master/.github/workflows/workflow.yaml)
+150 −0
Original line number Diff line number Diff line
---
kind: Service
apiVersion: v1
metadata:
    name: ever-demo-admin-lb
    annotations:
        service.beta.kubernetes.io/do-loadbalancer-name: 'admindemo.ever.co'
        service.beta.kubernetes.io/do-loadbalancer-protocol: 'http2'
        service.beta.kubernetes.io/do-loadbalancer-http2-ports: '443'
        # Replace with your Certificate Id. You can get a list of Ids with 'doctl compute certificate list'
        service.beta.kubernetes.io/do-loadbalancer-certificate-id: '3e7e0327-dd2d-4ffc-9b98-5c79eb4592d5'
        service.beta.kubernetes.io/do-loadbalancer-size-slug: 'lb-small'
        service.beta.kubernetes.io/do-loadbalancer-hostname: 'admindemo.ever.co'
spec:
    type: LoadBalancer
    selector:
        app: ever-demo-admin
    ports:
        - name: http
          protocol: TCP
          port: 443
          targetPort: 4200

---
kind: Service
apiVersion: v1
metadata:
    name: ever-demo-api-lb
    annotations:
        service.beta.kubernetes.io/do-loadbalancer-name: 'apidemo.ever.co'
        service.beta.kubernetes.io/do-loadbalancer-protocol: 'http2'
        service.beta.kubernetes.io/do-loadbalancer-http2-ports: '443'
        # Replace with your Certificate Id. You can get a list of Ids with 'doctl compute certificate list'
        service.beta.kubernetes.io/do-loadbalancer-certificate-id: '3e7e0327-dd2d-4ffc-9b98-5c79eb4592d5'
        service.beta.kubernetes.io/do-loadbalancer-size-slug: 'lb-small'
        service.beta.kubernetes.io/do-loadbalancer-hostname: 'apidemo.ever.co'
spec:
    type: LoadBalancer
    selector:
        app: ever-demo-api
    ports:
        - name: http
          protocol: TCP
          port: 443
          targetPort: 5500

---
apiVersion: apps/v1
kind: Deployment
metadata:
    name: ever-demo-api
spec:
    replicas: 1
    selector:
        matchLabels:
            app: ever-demo-api
    template:
        metadata:
            labels:
                app: ever-demo-api
        spec:
            containers:
                - name: ever-demo-api
                  image: ghcr.io/ever-co/ever-api:latest
                  env:
                      - name: HOST
                        value: 0.0.0.0                        
                      - name: DEMO
                        value: 'true'                      
                      - name: NODE_ENV
                        value: 'production'
                      - name: HTTPPORT
                        value: '5500'
                      # We use LB, so no need to expose HTTPS, only HTTPS required  
                      # - name: HTTPSPORT
                      #   value: '5501'
                      - name: GQLPORT
                        value: '5555'
                      - name: GQLPORT_SUBSCRIPTIONS
                        value: '5050'
                      - name: DB_URI
                        value: 'mongodb://localhost/ever_development'
                      - name: ADMIN_PASSWORD_RESET
                        value: 'true'
                      - name: FAKE_DATA_GENERATOR
                        value: 'true'
                      - name: LOG_LEVEL
                        value: 'info'
                  ports:
                      - containerPort: 5500
                        protocol: TCP
                      # We use LB, so no need to expose HTTPS, only HTTPS required    
                      # - containerPort: 5501
                      #   protocol: TCP
                      - containerPort: 5555
                        protocol: TCP
                      - containerPort: 5050
                        protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
    name: ever-demo-admin
spec:
    replicas: 1
    selector:
        matchLabels:
            app: ever-demo-admin
    template:
        metadata:
            labels:
                app: ever-demo-admin
        spec:
            containers:
                - name: ever-demo-admin
                  image: ghcr.io/ever-co/ever-admin-angular:latest
                  env:
                      - name: DEMO
                        value: 'true'
                      - name: NODE_ENV
                        value: 'production'
                      - name: HTTPS_SERVICES_ENDPOINT
                        value: 'https://apidemo.ever.co'
                      # We connect via HTTPS, so no need HTTP endpoint address  
                      # - name: SERVICES_ENDPOINT
                      #   value: ''
                      - name: GQL_ENDPOINT
                        value: 'http://apidemo.ever.co:5555/graphql'
                      - name: GQL_SUBSCRIPTIONS_ENDPOINT
                        value: 'ws://apidemo.ever.co:5050/subscriptions'
                      - name: SENTRY_DSN
                        value: 'https://7cd381188b6f446ca0e69185227b9031@o51327.ingest.sentry.io/4397292'
                      - name: CHATWOOT_SDK_TOKEN
                        value: 'jFoSXEjGmqhUhqU3zfgkFfMt'
                      - name: GOOGLE_MAPS_API_KEY
                        value: ''
                      - name: GOOGLE_PLACE_AUTOCOMPLETE
                        value: 'false'
                      - name: DEFAULT_LATITUDE
                        value: '42.6459136'
                      - name: DEFAULT_LONGITUDE
                        value: '23.3332736'
                      - name: CURRENCY_SYMBOL
                        value: '$'
                      - name: DEFAULT_LANGUAGE
                        value: 'en-US'

                  ports:
                      - containerPort: 4200
                        protocol: TCP
+38 −0
Original line number Diff line number Diff line
name: Deploy to DigitalOcean

on:
    workflow_run:
        workflows: ['Build and Publish Docker Images']
        branches: [master]
        types:
            - completed

jobs:
    deploy-do:
        runs-on: ubuntu-latest

        steps:
            - name: Checkout
              uses: actions/checkout@v2

            - name: Install doctl
              uses: digitalocean/action-doctl@v2
              with:
                  token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

            - name: Log in to DigitalOcean Container Registry with short-lived credentials
              run: doctl registry login --expiry-seconds 600

            - name: Save DigitalOcean kubeconfig with short-lived credentials
              run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-ever

            - name: Apply k8s manifests changes in DigitalOcean k8s cluster (if any)
              run: |
                  kubectl --context do-sfo2-k8s-ever apply -f $GITHUB_WORKSPACE/.deploy/k8s/k8s-manifest.demo.yaml                  

            # we need this step because for now we just use :latest tag
            # note: for production we will use different strategy later
            - name: Restart Pods to pick up :latest tag version
              run: |
                  kubectl --context do-sfo2-k8s-ever rollout restart deployment/ever-demo-api
                  kubectl --context do-sfo2-k8s-ever rollout restart deployment/ever-demo-admin