diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a2b4423..ff2e897 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -32,7 +32,20 @@ jobs: with: kubeconfig: ${{ secrets.KUBE_CONFIG }} - - name: Create and Apply Kubernetes Secrets + - name: Create Namespace (if not exists) + run: | + kubectl get ns phrinifluent || kubectl create ns phrinifluent + + - name: Set up Helm + uses: azure/setup-helm@v1 + + # - name: Add Helm repository + # run: helm repo add myrepo https://mychartrepo.com/charts + + # - name: Update Helm repositories + # run: helm repo update + + - name: Deploy with Helm env: DB_USER: ${{ secrets.DB_USER }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} @@ -43,34 +56,14 @@ jobs: SSL_CERTIFICATE: ${{ secrets.SSL_CERTIFICATE }} SSL_KEY: ${{ secrets.SSL_KEY }} run: | - kubectl create secret generic phrinifluent-db-secrets \ - --from-literal=POSTGRES_USER="$DB_USER" \ - --from-literal=POSTGRES_PASSWORD="$DB_PASSWORD" \ - --from-literal=POSTGRES_DB="$DB_NAME" \ - -o yaml --dry-run=client | kubectl apply -f - - kubectl create secret generic phrinifluent-backend-secrets \ - --from-literal=SECRET_KEY="$SECRET_KEY" \ - --from-literal=ADMIN_USERNAME="$ADMIN_USERNAME" \ - --from-literal=ADMIN_EMAIL="$ADMIN_EMAIL" \ - --from-literal=ADMIN_PASSWORD="$ADMIN_PASSWORD" \ - --from-literal=SSL_CERTIFICATE="$SSL_CERTIFICATE" \ - --from-literal=SSL_KEY="$SSL_KEY" \ - -o yaml --dry-run=client | kubectl apply -f - - echo "$SSL_CERTIFICATE" > cert.pem - echo "$SSL_KEY" > key.pem - kubectl create secret tls phrinifluent-tls-secrets \ - --cert=cert.pem \ - --key=key.pem \ - -o yaml --dry-run=client | kubectl apply -f - - - - name: Update image tags in Kubernetes manifests - run: | - TAG="${{ steps.latest_tag.outputs.tag }}" - sed -i "s|flagro/phrinifluent_web:latest|flagro/phrinifluent_web:$TAG|g" ./k8s/web/deployment.yml - - - name: Deploy to Kubernetes - run: | - kubectl apply -f ./k8s/db/ - kubectl apply -f ./k8s/ingress/ - kubectl apply -f ./k8s/web/ - kubectl apply -f ./k8s/static/ + helm upgrade --install ${{ steps.latest_tag.outputs.tag }} ./helm \ + --namespace phrinifluent \ + --set image.tag=${{ steps.latest_tag.outputs.tag }} \ + --set dbSecrets.user=${{ secrets.DB_USER }} \ + --set dbSecrets.password=${{ secrets.DB_PASSWORD }} \ + --set backendSecrets.secretKey=${{ secrets.SECRET_KEY }} \ + --set backendSecrets.adminUsername=${{ secrets.ADMIN_USERNAME }} \ + --set backendSecrets.adminEmail=${{ secrets.ADMIN_EMAIL }} \ + --set backendSecrets.adminPassword=${{ secrets.ADMIN_PASSWORD }} \ + --set-file tlsSecrets.certFile=<(echo "${{ secrets.SSL_CERTIFICATE }}") \ + --set-file tlsSecrets.keyFile=<(echo "${{ secrets.SSL_KEY }}") diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/.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/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..fc38992 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: helm +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: "1.16.0" diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..f57eb03 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "flatchart.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 "flatchart.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 "flatchart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "flatchart.labels" -}} +helm.sh/chart: {{ include "flatchart.chart" . }} +{{ include "flatchart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "flatchart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "flatchart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "flatchart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "flatchart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/templates/db/config.yaml b/helm/templates/db/config.yaml new file mode 100644 index 0000000..dc72e54 --- /dev/null +++ b/helm/templates/db/config.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "flatchart.fullname" . }}-db-config + labels: + {{- include "flatchart.labels" . | nindent 4 }} +data: + POSTGRES_NAME: "postgres" + POSTGRES_HOST: "{{ include "flatchart.fullname" . }}-db-service" + POSTGRES_PORT: "5432" diff --git a/helm/templates/db/deployment.yaml b/helm/templates/db/deployment.yaml new file mode 100644 index 0000000..7a468ff --- /dev/null +++ b/helm/templates/db/deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "flatchart.fullname" . }}-db-deployment + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.dbDeployment.replicas }} + selector: + matchLabels: + app: phrinifluent-db + {{- include "flatchart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: phrinifluent-db + {{- include "flatchart.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: phrinifluent-db + image: {{ .Values.dbDeployment.phrinifluentDb.image.repository }}:{{ .Values.dbDeployment.phrinifluentDb.image.tag | default .Chart.AppVersion }} + ports: + - containerPort: 5432 + envFrom: + - secretRef: + name: {{ include "flatchart.fullname" . }}-db-secrets + - configMapRef: + name: {{ include "flatchart.fullname" . }}-db-config + - env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + resources: {} + volumeMounts: + - name: phrinifluent-db-storage + mountPath: /var/lib/postgresql/data + volumes: + - name: phrinifluent-db-storage + persistentVolumeClaim: + claimName: {{ include "flatchart.fullname" . }}-db-pvc diff --git a/helm/templates/db/pv.yaml b/helm/templates/db/pv.yaml new file mode 100644 index 0000000..e50eb2a --- /dev/null +++ b/helm/templates/db/pv.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ include "flatchart.fullname" . }}-db-pv + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + capacity: + storage: {{ .Values.pv.dbPv.storageRequest }} + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: standard + hostPath: + path: {{ .Values.pv.dbPv.hostPath | quote }} diff --git a/helm/templates/db/pvc.yaml b/helm/templates/db/pvc.yaml new file mode 100644 index 0000000..20463e2 --- /dev/null +++ b/helm/templates/db/pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "flatchart.fullname" . }}-db-pvc + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.pvc.dbPvc.storageRequest | quote }} + storageClassName: standard diff --git a/helm/templates/db/secrets.yaml b/helm/templates/db/secrets.yaml new file mode 100644 index 0000000..a58c762 --- /dev/null +++ b/helm/templates/db/secrets.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "flatchart.fullname" . }}-db-secrets + labels: + {{- include "flatchart.labels" . | nindent 4 }} +type: Opaque +data: + POSTGRES_USER: {{ .Values.dbSecrets.user | b64enc | quote }} + POSTGRES_PASSWORD: {{ .Values.dbSecrets.password | b64enc | quote }} + POSTGRES_DB: {{ .Values.dbSecrets.dbName | b64enc | quote }} diff --git a/helm/templates/db/service.yaml b/helm/templates/db/service.yaml new file mode 100644 index 0000000..a38c728 --- /dev/null +++ b/helm/templates/db/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "flatchart.fullname" . }}-db-service + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + app: phrinifluent-db + {{- include "flatchart.selectorLabels" . | nindent 4 }} + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 diff --git a/helm/templates/ingress/ingress.yaml b/helm/templates/ingress/ingress.yaml new file mode 100644 index 0000000..e13d6d9 --- /dev/null +++ b/helm/templates/ingress/ingress.yaml @@ -0,0 +1,29 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "flatchart.fullname" . }}-ingress + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: /static + pathType: Prefix + backend: + service: + name: '{{ include "flatchart.fullname" . }}-static-service' + port: + number: 80 + - path: / + pathType: Prefix + backend: + service: + name: '{{ include "flatchart.fullname" . }}-backend-service' + port: + number: 80 + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ include "flatchart.fullname" . }}-tls-secrets diff --git a/helm/templates/ingress/secrets.yaml b/helm/templates/ingress/secrets.yaml new file mode 100644 index 0000000..f68d356 --- /dev/null +++ b/helm/templates/ingress/secrets.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "flatchart.fullname" . }}-tls-secrets + labels: + {{- include "flatchart.labels" . | nindent 4 }} +type: kubernetes.io/tls +data: + tls.crt: {{ .Files.Get .Values.tlsSecrets.certFile | b64enc | quote }} + tls.key: {{ .Files.Get .Values.tlsSecrets.keyFile | b64enc | quote }} diff --git a/helm/templates/static/config.yaml b/helm/templates/static/config.yaml new file mode 100644 index 0000000..b17aa64 --- /dev/null +++ b/helm/templates/static/config.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "flatchart.fullname" . }}-nginx-config + labels: + {{- include "flatchart.labels" . | nindent 4 }} +data: + default.conf: | + server { + listen 80; + server_name _; + + location /static { + alias /usr/share/nginx/html/static; + } + } diff --git a/helm/templates/static/deployment.yaml b/helm/templates/static/deployment.yaml new file mode 100644 index 0000000..8a6716d --- /dev/null +++ b/helm/templates/static/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "flatchart.fullname" . }}-nginx-deployment + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.nginxDeployment.replicas }} + selector: + matchLabels: + app: nginx + {{- include "flatchart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: nginx + {{- include "flatchart.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: nginx + image: {{ .Values.nginxDeployment.nginx.image.repository }}:{{ .Values.nginxDeployment.nginx.image.tag | default .Chart.AppVersion }} + ports: + - containerPort: 80 + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + resources: {} + volumeMounts: + - name: phrinifluent-static-storage + mountPath: /usr/share/nginx/html/static + - name: phrinifluent-nginx-config-storage + mountPath: /etc/nginx/conf.d + readOnly: true + volumes: + - name: phrinifluent-static-storage + persistentVolumeClaim: + claimName: {{ include "flatchart.fullname" . }}-static-pvc + - name: phrinifluent-nginx-config-storage + configMap: + name: {{ include "flatchart.fullname" . }}-nginx-config diff --git a/helm/templates/static/service.yaml b/helm/templates/static/service.yaml new file mode 100644 index 0000000..b4acf86 --- /dev/null +++ b/helm/templates/static/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "flatchart.fullname" . }}-static-service + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + app: nginx + {{- include "flatchart.selectorLabels" . | nindent 4 }} + ports: + - protocol: TCP + port: 80 + targetPort: 80 diff --git a/helm/templates/web/config.yaml b/helm/templates/web/config.yaml new file mode 100644 index 0000000..59ce39d --- /dev/null +++ b/helm/templates/web/config.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "flatchart.fullname" . }}-backend-config + labels: + {{- include "flatchart.labels" . | nindent 4 }} +data: + ENVIRONMENT: {{ .Values.backendConfig.environment | quote }} diff --git a/helm/templates/web/deployment.yaml b/helm/templates/web/deployment.yaml new file mode 100644 index 0000000..b36df20 --- /dev/null +++ b/helm/templates/web/deployment.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "flatchart.fullname" . }}-backend-deployment + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.backendDeployment.replicas }} + selector: + matchLabels: + app: phrinifluent-backend + {{- include "flatchart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: phrinifluent-backend + {{- include "flatchart.selectorLabels" . | nindent 8 }} + spec: + volumes: + - name: phrinifluent-static-storage + persistentVolumeClaim: + claimName: {{ include "flatchart.fullname" . }}-static-pvc + initContainers: + - name: phrinifluent-backend-setup + image: {{ .Values.backendDeployment.phrinifluentBackendSetup.image.repository }}:{{.Values.backendDeployment.phrinifluentBackendSetup.image.tag | default .Chart.AppVersion}} + command: ["/bin/sh", "-c"] + args: {{- toYaml .Values.backendDeployment.phrinifluentBackendSetup.args | nindent 8 }} + envFrom: + - secretRef: + name: {{ include "flatchart.fullname" . }}-backend-secrets + - secretRef: + name: {{ include "flatchart.fullname" . }}-db-secrets + - configMapRef: + name: {{ include "flatchart.fullname" . }}-backend-config + - configMapRef: + name: {{ include "flatchart.fullname" . }}-db-config + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + resources: {} + volumeMounts: + - mountPath: /app/staticfiles/ + name: phrinifluent-static-storage + containers: + - name: phrinifluent-backend + image: {{ .Values.backendDeployment.phrinifluentBackend.image.repository }}:{{.Values.backendDeployment.phrinifluentBackend.image.tag | default .Chart.AppVersion}} + ports: + - containerPort: 8000 + envFrom: + - secretRef: + name: {{ include "flatchart.fullname" . }}-backend-secrets + - secretRef: + name: {{ include "flatchart.fullname" . }}-db-secrets + - configMapRef: + name: {{ include "flatchart.fullname" . }}-backend-config + - configMapRef: + name: {{ include "flatchart.fullname" . }}-db-config + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + resources: {} diff --git a/helm/templates/web/pv.yaml b/helm/templates/web/pv.yaml new file mode 100644 index 0000000..4a1fc6d --- /dev/null +++ b/helm/templates/web/pv.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ include "flatchart.fullname" . }}-static-pv + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + capacity: + storage: {{ .Values.pv.staticPv.storageRequest }} + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: standard + hostPath: + path: {{ .Values.pv.staticPv.hostPath | quote }} diff --git a/helm/templates/web/pvc.yaml b/helm/templates/web/pvc.yaml new file mode 100644 index 0000000..ace4a51 --- /dev/null +++ b/helm/templates/web/pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "flatchart.fullname" . }}-static-pvc + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: {{ .Values.pvc.staticPvc.storageRequest | quote }} + storageClassName: standard diff --git a/helm/templates/web/secrets.yaml b/helm/templates/web/secrets.yaml new file mode 100644 index 0000000..8a954e5 --- /dev/null +++ b/helm/templates/web/secrets.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "flatchart.fullname" . }}-backend-secrets + labels: + {{- include "flatchart.labels" . | nindent 4 }} +type: Opaque +data: + SECRET_KEY: {{ .Values.backendSecrets.secretKey | b64enc | quote }} + ADMIN_USERNAME: {{ .Values.backendSecrets.adminUsername | b64enc | quote }} + ADMIN_EMAIL: {{ .Values.backendSecrets.adminEmail | b64enc | quote }} + ADMIN_PASSWORD: {{ .Values.backendSecrets.adminPassword | b64enc | quote }} diff --git a/helm/templates/web/service.yaml b/helm/templates/web/service.yaml new file mode 100644 index 0000000..585acb7 --- /dev/null +++ b/helm/templates/web/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "flatchart.fullname" . }}-backend-service + labels: + {{- include "flatchart.labels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + app: phrinifluent-backend + {{- include "flatchart.selectorLabels" . | nindent 4 }} + ports: + - protocol: TCP + port: 80 + targetPort: 8000 diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..8f47cf2 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,66 @@ +backendConfig: + environment: production + +kubernetesClusterDomain: cluster.local + +ingress: + host: api.phrinifluent.com + +backendDeployment: + phrinifluentBackend: + image: + repository: ghcr.io/flagro/phrinifluentbackend + tag: latest + phrinifluentBackendSetup: + args: + - | + python manage.py migrate && + python manage.py createadmin && + python manage.py collectstatic --noinput + image: + repository: ghcr.io/flagro/phrinifluentbackend + tag: latest + replicas: 1 + +dbDeployment: + phrinifluentDb: + image: + repository: postgres + tag: latest + replicas: 1 + +nginxDeployment: + nginx: + image: + repository: nginx + tag: latest + replicas: 1 + +pv: + dbPv: + storageRequest: 5Gi + hostPath: /var/lib/docker/volumes/phrinifluent-db-pv/_data + staticPv: + storageRequest: 2Gi + hostPath: /var/lib/docker/volumes/phrinifluent-static-pv/_data + +pvc: + dbPvc: + storageRequest: 5Gi + staticPvc: + storageRequest: 2Gi + +dbSecrets: + user: "" + password: "" + dbName: "" + +backendSecrets: + secretKey: "" + adminUsername: "" + adminEmail: "" + adminPassword: "" + +tlsSecrets: + certFile: "cert.pem" + keyFile: "key.pem"