From 83c73f925fed85f4dfe976ca4845305f9ad7efef Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Sat, 5 Nov 2022 23:02:30 -0700 Subject: [PATCH 1/3] add helm chart template --- k8s/helm-chart/.helmignore | 23 ++++++ k8s/helm-chart/Chart.yaml | 24 ++++++ k8s/helm-chart/templates/NOTES.txt | 22 +++++ k8s/helm-chart/templates/_helpers.tpl | 62 ++++++++++++++ k8s/helm-chart/templates/deployment.yaml | 61 ++++++++++++++ k8s/helm-chart/templates/hpa.yaml | 28 +++++++ k8s/helm-chart/templates/ingress.yaml | 61 ++++++++++++++ k8s/helm-chart/templates/service.yaml | 15 ++++ k8s/helm-chart/templates/serviceaccount.yaml | 12 +++ .../templates/tests/test-connection.yaml | 15 ++++ k8s/helm-chart/values.yaml | 82 +++++++++++++++++++ 11 files changed, 405 insertions(+) create mode 100644 k8s/helm-chart/.helmignore create mode 100644 k8s/helm-chart/Chart.yaml create mode 100644 k8s/helm-chart/templates/NOTES.txt create mode 100644 k8s/helm-chart/templates/_helpers.tpl create mode 100644 k8s/helm-chart/templates/deployment.yaml create mode 100644 k8s/helm-chart/templates/hpa.yaml create mode 100644 k8s/helm-chart/templates/ingress.yaml create mode 100644 k8s/helm-chart/templates/service.yaml create mode 100644 k8s/helm-chart/templates/serviceaccount.yaml create mode 100644 k8s/helm-chart/templates/tests/test-connection.yaml create mode 100644 k8s/helm-chart/values.yaml diff --git a/k8s/helm-chart/.helmignore b/k8s/helm-chart/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/k8s/helm-chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/k8s/helm-chart/Chart.yaml b/k8s/helm-chart/Chart.yaml new file mode 100644 index 00000000..054290f5 --- /dev/null +++ b/k8s/helm-chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: codepod +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "v0.4.3-alpha.3" diff --git a/k8s/helm-chart/templates/NOTES.txt b/k8s/helm-chart/templates/NOTES.txt new file mode 100644 index 00000000..472b1d19 --- /dev/null +++ b/k8s/helm-chart/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "codepod.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "codepod.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "codepod.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "codepod.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/k8s/helm-chart/templates/_helpers.tpl b/k8s/helm-chart/templates/_helpers.tpl new file mode 100644 index 00000000..d4b396f6 --- /dev/null +++ b/k8s/helm-chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "codepod.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "codepod.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "codepod.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "codepod.labels" -}} +helm.sh/chart: {{ include "codepod.chart" . }} +{{ include "codepod.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "codepod.selectorLabels" -}} +app.kubernetes.io/name: {{ include "codepod.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "codepod.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "codepod.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/k8s/helm-chart/templates/deployment.yaml b/k8s/helm-chart/templates/deployment.yaml new file mode 100644 index 00000000..e3856a1a --- /dev/null +++ b/k8s/helm-chart/templates/deployment.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "codepod.fullname" . }} + labels: + {{- include "codepod.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "codepod.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "codepod.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "codepod.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/k8s/helm-chart/templates/hpa.yaml b/k8s/helm-chart/templates/hpa.yaml new file mode 100644 index 00000000..3f61f41d --- /dev/null +++ b/k8s/helm-chart/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "codepod.fullname" . }} + labels: + {{- include "codepod.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "codepod.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/k8s/helm-chart/templates/ingress.yaml b/k8s/helm-chart/templates/ingress.yaml new file mode 100644 index 00000000..362bdebe --- /dev/null +++ b/k8s/helm-chart/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "codepod.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "codepod.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/k8s/helm-chart/templates/service.yaml b/k8s/helm-chart/templates/service.yaml new file mode 100644 index 00000000..d74269c7 --- /dev/null +++ b/k8s/helm-chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "codepod.fullname" . }} + labels: + {{- include "codepod.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "codepod.selectorLabels" . | nindent 4 }} diff --git a/k8s/helm-chart/templates/serviceaccount.yaml b/k8s/helm-chart/templates/serviceaccount.yaml new file mode 100644 index 00000000..00e22004 --- /dev/null +++ b/k8s/helm-chart/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "codepod.serviceAccountName" . }} + labels: + {{- include "codepod.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/k8s/helm-chart/templates/tests/test-connection.yaml b/k8s/helm-chart/templates/tests/test-connection.yaml new file mode 100644 index 00000000..cd37fb9e --- /dev/null +++ b/k8s/helm-chart/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "codepod.fullname" . }}-test-connection" + labels: + {{- include "codepod.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "codepod.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/k8s/helm-chart/values.yaml b/k8s/helm-chart/values.yaml new file mode 100644 index 00000000..b038a454 --- /dev/null +++ b/k8s/helm-chart/values.yaml @@ -0,0 +1,82 @@ +# Default values for codepod. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} From b23f3bfbb80d3dc22b128e350c0f98810790d720 Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Sun, 6 Nov 2022 01:43:46 -0700 Subject: [PATCH 2/3] add codepod helm-chart; use RUNTIME_NS env for k8s runtime --- README.md | 8 +- api/src/spawner-k8s.ts | 26 +-- compose/dev/compose.yml | 5 +- compose/prod/compose.yml | 4 +- deploy.md | 24 +-- k8s/helm-chart/Chart.yaml | 2 +- k8s/helm-chart/README.md | 50 +++++ k8s/helm-chart/templates/NOTES.txt | 22 -- k8s/helm-chart/templates/_helpers.tpl | 62 ------ k8s/helm-chart/templates/db.yaml | 85 ++++++++ k8s/helm-chart/templates/deployment.yaml | 204 ++++++++++++++---- k8s/helm-chart/templates/hpa.yaml | 28 --- k8s/helm-chart/templates/ingress.yaml | 92 +++----- k8s/helm-chart/templates/rbac.yaml | 50 +++++ k8s/helm-chart/templates/service.yaml | 15 -- k8s/helm-chart/templates/serviceaccount.yaml | 12 -- .../templates/tests/test-connection.yaml | 15 -- k8s/helm-chart/values.prod.yaml | 1 + k8s/helm-chart/values.staging.yaml | 1 + k8s/helm-chart/values.yaml | 83 +------ package.json | 2 +- ui/src/pages/about.tsx | 2 +- ui/src/pages/index.tsx | 6 - 23 files changed, 415 insertions(+), 384 deletions(-) create mode 100644 k8s/helm-chart/README.md delete mode 100644 k8s/helm-chart/templates/NOTES.txt delete mode 100644 k8s/helm-chart/templates/_helpers.tpl create mode 100644 k8s/helm-chart/templates/db.yaml delete mode 100644 k8s/helm-chart/templates/hpa.yaml create mode 100644 k8s/helm-chart/templates/rbac.yaml delete mode 100644 k8s/helm-chart/templates/service.yaml delete mode 100644 k8s/helm-chart/templates/serviceaccount.yaml delete mode 100644 k8s/helm-chart/templates/tests/test-connection.yaml create mode 100644 k8s/helm-chart/values.prod.yaml create mode 100644 k8s/helm-chart/values.staging.yaml diff --git a/README.md b/README.md index dcc36d66..7b736fea 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,15 @@ Make sure you have the runtime containers images pulled. - TODO use a more generic image tag here, e.g. latest ``` -docker pull lihebi/codepod-kernel-python:v0.4.3-alpha.3 -docker pull lihebi/codepod-runtime:v0.4.3-alpha.3 +docker pull lihebi/codepod-kernel-python:v0.4.3-alpha.4 +docker pull lihebi/codepod-runtime:v0.4.3-alpha.4 ``` Or you can build the images: ``` -docker build -t lihebi/codepod-kernel-python:v0.4.3-alpha.3 ./runtime/kernel -docker build -t lihebi/codepod-runtime:v0.4.3-alpha.3 ./runtime +docker build -t lihebi/codepod-kernel-python:v0.4.3-alpha.4 ./runtime/kernel +docker build -t lihebi/codepod-runtime:v0.4.3-alpha.4 ./runtime ``` ## Usage diff --git a/api/src/spawner-k8s.ts b/api/src/spawner-k8s.ts index 0bd87177..ad31bb9d 100644 --- a/api/src/spawner-k8s.ts +++ b/api/src/spawner-k8s.ts @@ -1,4 +1,5 @@ import * as k8s from "@kubernetes/client-node"; +import * as fs from "fs"; import { ApolloClient, InMemoryCache, gql } from "@apollo/client/core"; @@ -117,15 +118,6 @@ function getServiceSpec(name) { }; } -async function k8s_test() { - try { - let res: any = await k8sApi.listNamespacedPod("codepod-dev"); - console.log(res.body); - } catch (e: any) { - console.log("ERROR", e.body.message); - } -} - export async function spawnRuntime(_, { sessionId }) { let url = `/${sessionId}`; sessionId = sessionId.replaceAll("_", "-").toLowerCase(); @@ -136,9 +128,14 @@ export async function spawnRuntime(_, { sessionId }) { // check if exist? // 1. create a jupyter kernel pod // 2. create a ws pod - // await k8s_test(); console.log("Creating namespaced pod .."); - let ns = "codepod-runtime"; + + let ns = + process.env.RUNTIME_NS || + fs + .readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/namespace") + .toString(); + console.log("Using k8s ns:", ns); try { // TODO if exists, skip // await k8sApi.createNamespacedPod(ns, getPodSpec(k8s_name)); @@ -254,7 +251,12 @@ export async function killRuntime(_, { sessionId }) { // sessionId = "k8s-user-UGY6YAk7TM"; let k8s_name = `k8s-${sessionId}`; console.log("killRuntime", url, k8s_name); - let ns = "codepod-runtime"; + let ns = + process.env.RUNTIME_NS || + fs + .readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/namespace") + .toString(); + console.log("Using k8s ns:", ns); console.log("Killing pod .."); // await k8sApi.deleteNamespacedPod(getPodSpec(k8s_name).metadata.name, ns); await k8sAppsApi.deleteNamespacedDeployment( diff --git a/compose/dev/compose.yml b/compose/dev/compose.yml index 037ca0fc..c83c28b7 100644 --- a/compose/dev/compose.yml +++ b/compose/dev/compose.yml @@ -36,8 +36,8 @@ services: JWT_SECRET: ${JWT_SECRET} KERNEL_NETWORK: "codepod" PROXY_API_URL: "http://proxy:4011/graphql" - ZMQ_KERNEL_IMAGE: "lihebi/codepod-kernel-python:v0.4.3-alpha.3" - WS_RUNTIME_IMAGE: "lihebi/codepod-runtime:v0.4.3-alpha.3" + ZMQ_KERNEL_IMAGE: "lihebi/codepod-kernel-python:v0.4.3-alpha.4" + WS_RUNTIME_IMAGE: "lihebi/codepod-runtime:v0.4.3-alpha.4" ui: image: node:18 @@ -69,7 +69,6 @@ volumes: proxy-node-modules: prisma-node-modules: - networks: default: name: codepod diff --git a/compose/prod/compose.yml b/compose/prod/compose.yml index 250c8d00..b7031587 100644 --- a/compose/prod/compose.yml +++ b/compose/prod/compose.yml @@ -24,8 +24,8 @@ services: JWT_SECRET: ${JWT_SECRET} KERNEL_NETWORK: "codepod" PROXY_API_URL: "http://proxy:4011/graphql" - ZMQ_KERNEL_IMAGE: "lihebi/codepod-kernel-python:v0.4.3-alpha.3" - WS_RUNTIME_IMAGE: "lihebi/codepod-runtime:v0.4.3-alpha.3" + ZMQ_KERNEL_IMAGE: "lihebi/codepod-kernel-python:v0.4.3-alpha.4" + WS_RUNTIME_IMAGE: "lihebi/codepod-runtime:v0.4.3-alpha.4" ui: build: ../../ui diff --git a/deploy.md b/deploy.md index 7f251e6c..e4a1debb 100644 --- a/deploy.md +++ b/deploy.md @@ -3,21 +3,21 @@ Build the docker images: ``` -docker build -t lihebi/codepod-ui:v0.4.3-alpha.3 ./ui -docker build -t lihebi/codepod-api:v0.4.3-alpha.3 ./api -docker build -t lihebi/codepod-proxy:v0.4.3-alpha.3 ./proxy -docker build -t lihebi/codepod-kernel-python:v0.4.3-alpha.3 ./runtime/kernel -docker build -t lihebi/codepod-runtime:v0.4.3-alpha.3 ./runtime +docker build -t lihebi/codepod-ui:v0.4.3-alpha.4 ./ui +docker build -t lihebi/codepod-api:v0.4.3-alpha.4 ./api +docker build -t lihebi/codepod-proxy:v0.4.3-alpha.4 ./proxy +docker build -t lihebi/codepod-kernel-python:v0.4.3-alpha.4 ./runtime/kernel +docker build -t lihebi/codepod-runtime:v0.4.3-alpha.4 ./runtime ``` Push to registry: ``` -docker push lihebi/codepod-ui:v0.4.3-alpha.3 -docker push lihebi/codepod-api:v0.4.3-alpha.3 -docker push lihebi/codepod-proxy:v0.4.3-alpha.3 -docker push lihebi/codepod-kernel-python:v0.4.3-alpha.3 -docker push lihebi/codepod-runtime:v0.4.3-alpha.3 +docker push lihebi/codepod-ui:v0.4.3-alpha.4 +docker push lihebi/codepod-api:v0.4.3-alpha.4 +docker push lihebi/codepod-proxy:v0.4.3-alpha.4 +docker push lihebi/codepod-kernel-python:v0.4.3-alpha.4 +docker push lihebi/codepod-runtime:v0.4.3-alpha.4 ``` Create a cloud VM with docker support. Add DNS from domain name to the cloud @@ -61,8 +61,8 @@ npx prisma migrate dev --name init Pull the kernel image: ``` -docker pull lihebi/codepod-kernel-python:v0.4.3-alpha.3 -docker tag lihebi/codepod-kernel-python:v0.4.3-alpha.3 codepod-kernel-python +docker pull lihebi/codepod-kernel-python:v0.4.3-alpha.4 +docker tag lihebi/codepod-kernel-python:v0.4.3-alpha.4 codepod-kernel-python ``` Now go to diff --git a/k8s/helm-chart/Chart.yaml b/k8s/helm-chart/Chart.yaml index 054290f5..327cc5fd 100644 --- a/k8s/helm-chart/Chart.yaml +++ b/k8s/helm-chart/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.4.3-alpha.3" +appVersion: "v0.4.3-alpha.4" diff --git a/k8s/helm-chart/README.md b/k8s/helm-chart/README.md new file mode 100644 index 00000000..0f423a14 --- /dev/null +++ b/k8s/helm-chart/README.md @@ -0,0 +1,50 @@ +# Helm charts for CodePod + +## Prerequist + +Also need the secrets: + +``` +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: mysecret +data: + POSTGRES_USER: + POSTGRES_PASSWORD: + POSTGRES_DB: + JWT_SECRET: +``` + +Note: the secrets must be base64 encoded with: + + echo -n your-password | base64 + +Need the -n there, otherwise there will be a space when this secret is used in +env variables. Ref: https://github.com/kubernetes/kubernetes/issues/28086 + +## Install + +We need to create one more namespace manually: + + kubectl create ns codepod-staging-runtime + +Install: + + helm install codepod-staging . --namespace codepod-staging --create-namespace --values=./values.staging.yaml + +Upgrade: + + helm upgrade codepod-staging . --namespace codepod-staging --values=./values.staging.yaml + +Optionally initialize the DB (run in one api pod): + + npx prisma migrate dev --name init + +- TODO: automate this +- TODO: restore from backup + +Uninstall: + + helm uninstall codepod-staging diff --git a/k8s/helm-chart/templates/NOTES.txt b/k8s/helm-chart/templates/NOTES.txt deleted file mode 100644 index 472b1d19..00000000 --- a/k8s/helm-chart/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "codepod.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "codepod.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "codepod.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "codepod.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} diff --git a/k8s/helm-chart/templates/_helpers.tpl b/k8s/helm-chart/templates/_helpers.tpl deleted file mode 100644 index d4b396f6..00000000 --- a/k8s/helm-chart/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "codepod.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "codepod.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "codepod.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "codepod.labels" -}} -helm.sh/chart: {{ include "codepod.chart" . }} -{{ include "codepod.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "codepod.selectorLabels" -}} -app.kubernetes.io/name: {{ include "codepod.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "codepod.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "codepod.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/k8s/helm-chart/templates/db.yaml b/k8s/helm-chart/templates/db.yaml new file mode 100644 index 00000000..018c199e --- /dev/null +++ b/k8s/helm-chart/templates/db.yaml @@ -0,0 +1,85 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: postgres-longhorn-pv-claim + labels: + app: postgres +spec: + storageClassName: longhorn + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: database + labels: + app: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + strategy: + type: Recreate + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:10.1 + imagePullPolicy: "IfNotPresent" + ports: + - containerPort: 5432 + env: + # FIXME the valueFromSecrete is not working, and the PVC will not + # update unless delete the /mnt/data on agent node. + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_DB + volumeMounts: + - mountPath: /var/lib/postgresql + name: postgredb + readOnly: false + resources: + limits: + memory: 512Mi + cpu: "1" + requests: + memory: 256Mi + cpu: "0.2" + volumes: + - name: postgredb + persistentVolumeClaim: + claimName: postgres-longhorn-pv-claim + +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres-service +spec: + ports: + - port: 5432 + targetPort: 5432 + protocol: TCP + selector: + app: postgres diff --git a/k8s/helm-chart/templates/deployment.yaml b/k8s/helm-chart/templates/deployment.yaml index e3856a1a..f33badf6 100644 --- a/k8s/helm-chart/templates/deployment.yaml +++ b/k8s/helm-chart/templates/deployment.yaml @@ -1,61 +1,171 @@ +# UI + apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "codepod.fullname" . }} + name: codepod-ui-deployment labels: - {{- include "codepod.labels" . | nindent 4 }} + app: codepod-ui spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} + replicas: 3 selector: matchLabels: - {{- include "codepod.selectorLabels" . | nindent 6 }} + app: codepod-ui template: metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} labels: - {{- include "codepod.selectorLabels" . | nindent 8 }} + app: codepod-ui spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "codepod.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} + - name: codepod-ui + image: lihebi/codepod-ui:{{ .Chart.AppVersion }} ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http + - containerPort: 80 resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} + limits: + memory: 512Mi + cpu: "1" + requests: + memory: 256Mi + cpu: "0.2" + +--- +apiVersion: v1 +kind: Service +metadata: + name: codepod-ui-service +spec: + selector: + app: codepod-ui + ports: + - protocol: TCP + port: 80 + targetPort: 80 + +# API +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: codepod-api-deployment + labels: + app: codepod-api +spec: + replicas: 3 + selector: + matchLabels: + app: codepod-api + template: + metadata: + labels: + app: codepod-api + spec: + serviceAccountName: api-account + containers: + - name: codepod-api + image: lihebi/codepod-api:{{ .Chart.AppVersion }} + ports: + - containerPort: 4000 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: mysecret + key: POSTGRES_DB + - name: DATABASE_URL + value: "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@postgres-service:5432/$(POSTGRES_DB)?schema=public" + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: mysecret + key: JWT_SECRET + - name: PROXY_API_URL + value: "http://codepod-proxy-service:4011/graphql" + - name: ZMQ_KERNEL_IMAGE + value: lihebi/codepod-kernel-python:{{ .Chart.AppVersion }} + - name: WS_RUNTIME_IMAGE + value: lihebi/codepod-runtime:{{ .Chart.AppVersion }} + - name: RUNTIME_SPAWNER + value: k8s + - name: RUNTIME_NS + value: {{ .Release.Namespace }}-runtime + resources: + limits: + memory: 512Mi + cpu: "1" + requests: + memory: 256Mi + cpu: "0.2" + +--- +apiVersion: v1 +kind: Service +metadata: + name: codepod-api-service +spec: + selector: + app: codepod-api + ports: + - protocol: TCP + port: 4000 + targetPort: 4000 + +# Proxy +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: codepod-proxy-deployment + labels: + app: codepod-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: codepod-proxy + template: + metadata: + labels: + app: codepod-proxy + spec: + containers: + - name: codepod-proxy + image: lihebi/codepod-proxy:{{ .Chart.AppVersion }} + ports: + - containerPort: 4010 + - containerPort: 4011 + resources: + limits: + memory: 512Mi + cpu: "1" + requests: + memory: 256Mi + cpu: "0.2" + +--- +apiVersion: v1 +kind: Service +metadata: + name: codepod-proxy-service +spec: + selector: + app: codepod-proxy + ports: + - name: proxy-port + protocol: TCP + port: 4010 + targetPort: 4010 + - name: api-port + protocol: TCP + port: 4011 + targetPort: 4011 diff --git a/k8s/helm-chart/templates/hpa.yaml b/k8s/helm-chart/templates/hpa.yaml deleted file mode 100644 index 3f61f41d..00000000 --- a/k8s/helm-chart/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "codepod.fullname" . }} - labels: - {{- include "codepod.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "codepod.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/k8s/helm-chart/templates/ingress.yaml b/k8s/helm-chart/templates/ingress.yaml index 362bdebe..283490d7 100644 --- a/k8s/helm-chart/templates/ingress.yaml +++ b/k8s/helm-chart/templates/ingress.yaml @@ -1,61 +1,35 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "codepod.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware metadata: - name: {{ $fullName }} - labels: - {{- include "codepod.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} + name: stripprefix spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} + stripPrefix: + prefixes: + - /runtime + - /prisma + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: codepod-ingress +spec: + routes: + - match: "Host(`{{ .Values.hostname }}`) && PathPrefix(`/`)" + kind: Rule + services: + - name: codepod-ui-service + port: 80 + - match: "Host(`{{ .Values.hostname }}`) && PathPrefix(`/graphql`)" + kind: Rule + services: + - name: codepod-api-service + port: 4000 + - match: "Host(`{{ .Values.hostname }}`) && PathPrefix(`/runtime`)" + kind: Rule + middlewares: + - name: stripprefix + services: + - name: codepod-proxy-service + port: 4010 diff --git a/k8s/helm-chart/templates/rbac.yaml b/k8s/helm-chart/templates/rbac.yaml new file mode 100644 index 00000000..190cf87e --- /dev/null +++ b/k8s/helm-chart/templates/rbac.yaml @@ -0,0 +1,50 @@ +# service account for API +# This allow api-account to create/delete resources in codepod-runtime namespace. + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: api-account + +# Runtime spawner in a seperate namespace codepod-runtime +# Ref: https://stackoverflow.com/questions/53960516/can-i-connect-one-service-account-to-multiple-namespaces-in-kubernetes + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }}-runtime + name: runtime-spawner +rules: + - apiGroups: [""] # "" indicates the core API group + resources: ["pods"] + verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] + - apiGroups: ["apps"] # "" indicates the core API group + resources: ["deployments"] + verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["services"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +# This role binding allows "jane" to read pods in the "default" namespace. +# You need to already have a Role named "pod-reader" in that namespace. +kind: RoleBinding +metadata: + name: spawn-runtimes + namespace: {{ .Release.Namespace }}-runtime +subjects: + # You can specify more than one "subject" + - kind: ServiceAccount + name: api-account # "name" is case sensitive + # apiGroup: rbac.authorization.k8s.io + # This should be optional if I'm already in this namespace. + # CAUTION: Well, this is not optional. + namespace: {{ .Release.Namespace }} +roleRef: + # "roleRef" specifies the binding to a Role / ClusterRole + kind: Role #this must be Role or ClusterRole + name: runtime-spawner # this must match the name of the Role or ClusterRole you wish to bind to + apiGroup: rbac.authorization.k8s.io diff --git a/k8s/helm-chart/templates/service.yaml b/k8s/helm-chart/templates/service.yaml deleted file mode 100644 index d74269c7..00000000 --- a/k8s/helm-chart/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "codepod.fullname" . }} - labels: - {{- include "codepod.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "codepod.selectorLabels" . | nindent 4 }} diff --git a/k8s/helm-chart/templates/serviceaccount.yaml b/k8s/helm-chart/templates/serviceaccount.yaml deleted file mode 100644 index 00e22004..00000000 --- a/k8s/helm-chart/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "codepod.serviceAccountName" . }} - labels: - {{- include "codepod.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/k8s/helm-chart/templates/tests/test-connection.yaml b/k8s/helm-chart/templates/tests/test-connection.yaml deleted file mode 100644 index cd37fb9e..00000000 --- a/k8s/helm-chart/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "codepod.fullname" . }}-test-connection" - labels: - {{- include "codepod.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "codepod.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/k8s/helm-chart/values.prod.yaml b/k8s/helm-chart/values.prod.yaml new file mode 100644 index 00000000..fe5bf7bb --- /dev/null +++ b/k8s/helm-chart/values.prod.yaml @@ -0,0 +1 @@ +hostname: "app.codepod.io" \ No newline at end of file diff --git a/k8s/helm-chart/values.staging.yaml b/k8s/helm-chart/values.staging.yaml new file mode 100644 index 00000000..ccdd91bc --- /dev/null +++ b/k8s/helm-chart/values.staging.yaml @@ -0,0 +1 @@ +hostname: "codepod.127.0.0.1.sslip.io" \ No newline at end of file diff --git a/k8s/helm-chart/values.yaml b/k8s/helm-chart/values.yaml index b038a454..ccdd91bc 100644 --- a/k8s/helm-chart/values.yaml +++ b/k8s/helm-chart/values.yaml @@ -1,82 +1 @@ -# Default values for codepod. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} +hostname: "codepod.127.0.0.1.sslip.io" \ No newline at end of file diff --git a/package.json b/package.json index 0f988012..1c6bfb60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codepod", - "version": "0.1.2", + "version": "v0.4.3-alpha.4", "private": true, "main": "./public/electron.js", "homepage": "./", diff --git a/ui/src/pages/about.tsx b/ui/src/pages/about.tsx index 0e3d8d99..d5a5296f 100644 --- a/ui/src/pages/about.tsx +++ b/ui/src/pages/about.tsx @@ -7,7 +7,7 @@ export default function About() { textAlign: "center", }} > - About page + CodePod version v0.4.3-alpha.4 ); } diff --git a/ui/src/pages/index.tsx b/ui/src/pages/index.tsx index b1426fc3..bd46a149 100644 --- a/ui/src/pages/index.tsx +++ b/ui/src/pages/index.tsx @@ -13,12 +13,6 @@ export default function Home() { Coding on a canvas, organized. - - - CodePod v0.1 is on internal testing. The data (user info, repos) - will likely be deleted. - - From 39830a52320cfeae2bab7b6bd434f6ba03532cff Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Sun, 6 Nov 2022 01:08:31 -0800 Subject: [PATCH 3/3] clean up kubectl deployments --- k8s/deployments/codepod-01-secret.yml | 10 -- k8s/deployments/codepod-03-db.yml | 88 ------------- k8s/deployments/codepod-04-prisma.yml | 48 ------- k8s/deployments/codepod-04.5-rbac.yml | 81 ------------ k8s/deployments/codepod-05-app.yml | 169 ------------------------- k8s/deployments/codepod-06-ingress.yml | 35 ----- k8s/helm-chart/README.md | 6 + 7 files changed, 6 insertions(+), 431 deletions(-) delete mode 100644 k8s/deployments/codepod-01-secret.yml delete mode 100644 k8s/deployments/codepod-03-db.yml delete mode 100644 k8s/deployments/codepod-04-prisma.yml delete mode 100644 k8s/deployments/codepod-04.5-rbac.yml delete mode 100644 k8s/deployments/codepod-05-app.yml delete mode 100644 k8s/deployments/codepod-06-ingress.yml diff --git a/k8s/deployments/codepod-01-secret.yml b/k8s/deployments/codepod-01-secret.yml deleted file mode 100644 index b4735179..00000000 --- a/k8s/deployments/codepod-01-secret.yml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: mysecret -data: - POSTGRES_USER: your-base64-encrypted - POSTGRES_PASSWORD: your-base64-encrypted - POSTGRES_DB: your-base64-encrypted - JWT_SECRET: your-base64-encrypted diff --git a/k8s/deployments/codepod-03-db.yml b/k8s/deployments/codepod-03-db.yml deleted file mode 100644 index 24ef3924..00000000 --- a/k8s/deployments/codepod-03-db.yml +++ /dev/null @@ -1,88 +0,0 @@ -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: postgres-longhorn-pv-claim - labels: - app: postgres -spec: - storageClassName: longhorn - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: database - labels: - app: postgres -spec: - replicas: 1 - selector: - matchLabels: - app: postgres - strategy: - type: Recreate - template: - metadata: - labels: - app: postgres - spec: - containers: - - name: postgres - image: postgres:10.1 - imagePullPolicy: "IfNotPresent" - ports: - - containerPort: 5432 - env: - # FIXME the valueFromSecrete is not working, and the PVC will not - # update unless delete the /mnt/data on agent node. - - name: POSTGRES_USER - value: YOUR_USERNAME - # valueFrom: - # secretKeyRef: - # name: mysecret - # key: POSTGRES_USER - - name: POSTGRES_PASSWORD - value: YOUR_PASSWD - # valueFrom: - # secretKeyRef: - # name: mysecret - # key: POSTGRES_PASSWORD - - name: POSTGRES_DB - value: YOUR_DBNAME - # valueFrom: - # secretKeyRef: - # name: mysecret - # key: POSTGRES_DB - volumeMounts: - - mountPath: /var/lib/postgresql - name: postgredb - readOnly: false - resources: - limits: - memory: 512Mi - cpu: "1" - requests: - memory: 256Mi - cpu: "0.2" - volumes: - - name: postgredb - persistentVolumeClaim: - claimName: postgres-longhorn-pv-claim - ---- -apiVersion: v1 -kind: Service -metadata: - name: postgres-service -spec: - ports: - - port: 5432 - targetPort: 5432 - protocol: TCP - selector: - app: postgres diff --git a/k8s/deployments/codepod-04-prisma.yml b/k8s/deployments/codepod-04-prisma.yml deleted file mode 100644 index a34018f5..00000000 --- a/k8s/deployments/codepod-04-prisma.yml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: prisma - labels: - app: prisma -spec: - replicas: 1 - selector: - matchLabels: - app: prisma - strategy: - type: Recreate - template: - metadata: - labels: - app: prisma - spec: - containers: - - name: data-migrations - image: lihebi/codepod-api:v0.4.3-alpha.3 - # Run this manually on-demand: npx prisma migrate dev --name init - # Or do a reset: npx prisma db push - command: ["npx", "prisma", "studio"] - ports: - - containerPort: 5555 - env: - - name: DATABASE_URL - resources: - limits: - memory: 512Mi - cpu: "1" - requests: - memory: 256Mi - cpu: "0.2" - ---- -apiVersion: v1 -kind: Service -metadata: - name: prisma-service -spec: - ports: - - port: 80 - targetPort: 5555 - protocol: TCP - selector: - app: prisma diff --git a/k8s/deployments/codepod-04.5-rbac.yml b/k8s/deployments/codepod-04.5-rbac.yml deleted file mode 100644 index 812a4859..00000000 --- a/k8s/deployments/codepod-04.5-rbac.yml +++ /dev/null @@ -1,81 +0,0 @@ -# service account for API -# This allow api-account to create/delete resources in codepod-runtime namespace. - ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: api-account - -# Runtime spawner in a seperate namespace codepod-runtime -# Ref: https://stackoverflow.com/questions/53960516/can-i-connect-one-service-account-to-multiple-namespaces-in-kubernetes - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - namespace: codepod-runtime - name: runtime-spawner -rules: -- apiGroups: [""] # "" indicates the core API group - resources: ["pods"] - verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] -- apiGroups: ["apps"] # "" indicates the core API group - resources: ["deployments"] - verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] -- apiGroups: [""] - resources: ["services"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - ---- -apiVersion: rbac.authorization.k8s.io/v1 -# This role binding allows "jane" to read pods in the "default" namespace. -# You need to already have a Role named "pod-reader" in that namespace. -kind: RoleBinding -metadata: - name: spawn-runtimes - namespace: codepod-runtime -subjects: -# You can specify more than one "subject" -- kind: ServiceAccount - name: api-account # "name" is case sensitive - # apiGroup: rbac.authorization.k8s.io - # This should be optional if I'm already in this namespace. - # CAUTION: Well, this is not optional. - namespace: codepod-prod -roleRef: - # "roleRef" specifies the binding to a Role / ClusterRole - kind: Role #this must be Role or ClusterRole - name: runtime-spawner # this must match the name of the Role or ClusterRole you wish to bind to - apiGroup: rbac.authorization.k8s.io - - -# In the local namespace ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: localns-runtime-spawner -rules: -- apiGroups: [""] # "" indicates the core API group - resources: ["pods"] - verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] - ---- -apiVersion: rbac.authorization.k8s.io/v1 -# This role binding allows "jane" to read pods in the "default" namespace. -# You need to already have a Role named "pod-reader" in that namespace. -kind: RoleBinding -metadata: - name: localns-spawn-runtimes -subjects: -# You can specify more than one "subject" -- kind: ServiceAccount - name: api-account # "name" is case sensitive - # apiGroup: rbac.authorization.k8s.io -roleRef: - # "roleRef" specifies the binding to a Role / ClusterRole - kind: Role #this must be Role or ClusterRole - name: localns-runtime-spawner # this must match the name of the Role or ClusterRole you wish to bind to - apiGroup: rbac.authorization.k8s.io diff --git a/k8s/deployments/codepod-05-app.yml b/k8s/deployments/codepod-05-app.yml deleted file mode 100644 index 69938ab2..00000000 --- a/k8s/deployments/codepod-05-app.yml +++ /dev/null @@ -1,169 +0,0 @@ -# UI - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: codepod-ui-deployment - labels: - app: codepod-ui -spec: - replicas: 3 - selector: - matchLabels: - app: codepod-ui - template: - metadata: - labels: - app: codepod-ui - spec: - containers: - - name: codepod-ui - image: lihebi/codepod-ui:v0.4.3-alpha.3 - ports: - - containerPort: 80 - resources: - limits: - memory: 512Mi - cpu: "1" - requests: - memory: 256Mi - cpu: "0.2" - ---- -apiVersion: v1 -kind: Service -metadata: - name: codepod-ui-service -spec: - selector: - app: codepod-ui - ports: - - protocol: TCP - port: 80 - targetPort: 80 - -# API ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: codepod-api-deployment - labels: - app: codepod-api -spec: - replicas: 3 - selector: - matchLabels: - app: codepod-api - template: - metadata: - labels: - app: codepod-api - spec: - serviceAccountName: api-account - containers: - - name: codepod-api - image: lihebi/codepod-api:v0.4.3-alpha.3 - ports: - - containerPort: 4000 - env: - - name: POSTGRES_USER - valueFrom: - secretKeyRef: - name: mysecret - key: POSTGRES_USER - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: mysecret - key: POSTGRES_PASSWORD - - name: POSTGRES_DB - valueFrom: - secretKeyRef: - name: mysecret - key: POSTGRES_DB - - name: DATABASE_URL - value: "" - - name: JWT_SECRET - valueFrom: - secretKeyRef: - name: mysecret - key: JWT_SECRET - - name: PROXY_API_URL - value: "http://codepod-proxy-service:4011/graphql" - - name: ZMQ_KERNEL_IMAGE - value: "lihebi/codepod-kernel-python:v0.4.3-alpha.3" - - name: WS_RUNTIME_IMAGE - value: "lihebi/codepod-runtime:v0.4.3-alpha.3" - - name: RUNTIME_SPAWNER - value: k8s - resources: - limits: - memory: 512Mi - cpu: "1" - requests: - memory: 256Mi - cpu: "0.2" - ---- -apiVersion: v1 -kind: Service -metadata: - name: codepod-api-service -spec: - selector: - app: codepod-api - ports: - - protocol: TCP - port: 4000 - targetPort: 4000 - -# Proxy ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: codepod-proxy-deployment - labels: - app: codepod-proxy -spec: - replicas: 1 - selector: - matchLabels: - app: codepod-proxy - template: - metadata: - labels: - app: codepod-proxy - spec: - containers: - - name: codepod-proxy - image: lihebi/codepod-proxy:v0.4.3-alpha.3 - ports: - - containerPort: 4010 - - containerPort: 4011 - resources: - limits: - memory: 512Mi - cpu: "1" - requests: - memory: 256Mi - cpu: "0.2" - ---- -apiVersion: v1 -kind: Service -metadata: - name: codepod-proxy-service -spec: - selector: - app: codepod-proxy - ports: - - name: proxy-port - protocol: TCP - port: 4010 - targetPort: 4010 - - name: api-port - protocol: TCP - port: 4011 - targetPort: 4011 diff --git a/k8s/deployments/codepod-06-ingress.yml b/k8s/deployments/codepod-06-ingress.yml deleted file mode 100644 index 76cda590..00000000 --- a/k8s/deployments/codepod-06-ingress.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: stripprefix -spec: - stripPrefix: - prefixes: - - /runtime - - /prisma - ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRoute -metadata: - name: codepod-ingress -spec: - routes: - - match: Host(`app.codepod.io`) && PathPrefix(`/`) - kind: Rule - services: - - name: codepod-ui-service - port: 80 - - match: Host(`app.codepod.io`) && PathPrefix(`/graphql`) - kind: Rule - services: - - name: codepod-api-service - port: 4000 - - match: Host(`app.codepod.io`) && PathPrefix(`/runtime`) - kind: Rule - middlewares: - - name: stripprefix - services: - - name: codepod-proxy-service - port: 4010 diff --git a/k8s/helm-chart/README.md b/k8s/helm-chart/README.md index 0f423a14..9090b551 100644 --- a/k8s/helm-chart/README.md +++ b/k8s/helm-chart/README.md @@ -24,6 +24,12 @@ Note: the secrets must be base64 encoded with: Need the -n there, otherwise there will be a space when this secret is used in env variables. Ref: https://github.com/kubernetes/kubernetes/issues/28086 +Need to manually apply these secrets: + +``` +kubectl apply -f secrets.yaml +``` + ## Install We need to create one more namespace manually: