Skip to content

Commit

Permalink
CI
Browse files Browse the repository at this point in the history
  • Loading branch information
Ilya Egorov committed Oct 22, 2024
1 parent 44f74b5 commit afd1a10
Show file tree
Hide file tree
Showing 17 changed files with 430 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/helm/ @Mobyman
/.github/ @Mobyman
130 changes: 130 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Deploy to Kubernetes

on:
push:
branches:
- main
- canary
- staging

jobs:
build:
runs-on: ubuntu-latest
env:
APP_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || github.ref == 'refs/heads/canary' && 'canary' || github.ref == 'refs/heads/staging' && 'staging' || 'unknown' }}
APP_DOMAIN: ${{ github.ref == 'refs/heads/staging' && vars.APP_DOMAIN_STAGING || vars.APP_DOMAIN }}

permissions:
packages: write
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set sha-short
run: echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV

- id: lower-repo
name: Repository to lowercase
run: |
echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ steps.lower-repo.outputs.repository }}
github-token: ${{ secrets.GITHUB_TOKEN }}
tags: |
type=sha
type=sha,format=long
type=ref,event=branch
- name: Build and push Docker image ${{ steps.lower-repo.outputs.repository }}:${{ env.APP_ENV }}
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }},ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.APP_ENV }}
build-args: |
sha=${{ github.sha }}
sha_short=${{ env.GITHUB_SHA_SHORT }}
app_env=${{ vars.APP_ENV }}
- name: Apply AWS k8s config
run: aws eks update-kubeconfig --name ${{ vars.AWS_CLUSTER }} --region ${{ vars.AWS_REGION }}

- name: Create namespace
run: |
kubectl create ns ${{ vars.APP_NAME }}-${{ env.APP_ENV }} || echo "Namespace $EKS_NAMESPACE already exists"
- name: Deploy ${{ vars.APP_NAME }} to Kubernetes
run: |
helm upgrade --install ${{ vars.APP_NAME }} ./helm/app \
--namespace ${{ vars.APP_NAME }}-${{ env.APP_ENV }} \
--values ./helm/app/values.yaml \
--values ./helm/app/values-${{ env.APP_ENV }}.yaml \
--set imageRepo="ghcr.io/${{ steps.lower-repo.outputs.repository }}" \
--set imageTag="${{ env.GITHUB_SHA_SHORT }}" \
--set host=${{ env.APP_DOMAIN }} \
--set appName=${{ vars.APP_NAME }} \
--set ghcrSecret=${{ secrets.GHCR_SECRET }} \
--set secrets.publicProxyKey=${{ secrets.NEXT_PUBLIC_MIXPANEL_TOKEN }} \
--set secrets.publicMixPanelToken=${{ secrets.NEXT_PUBLIC_ANALYTICS_ENABLED }} \
--set secrets.publicProxyKey=${{ secrets.NEXT_PUBLIC_ANALYTICS_ENABLED }}
- name: Verify deployment
run: |
kubectl -n ${{ vars.APP_NAME }}-${{ env.APP_ENV }} rollout status deployment/${{ vars.APP_NAME }}-${{ env.APP_ENV }}
- name: Verify TLS Certificate
run: |
kubectl describe certificate ${{ env.APP_DOMAIN }} -n ${{ vars.APP_NAME }}-${{ env.APP_ENV }}
- name: Telegram Notify
uses: appleboy/[email protected]
if: success() && contains('${{ vars.ENABLE_DEPLOY_BOT }}', 1)
with:
to: ${{ secrets.TELEGRAM_DEPLOY_CHAT_ID }}
token: ${{ secrets.TELEGRAM_DEPLOY_TOKEN }}
format: markdown
message: |
🚂 The application from repository [${{ steps.lower-repo.outputs.repository }}](https://github.com/${{ steps.lower-repo.outputs.repository }}) has been successfully deployed by [${{ github.actor }}](https://github.com/users/${{ github.actor }}) on ${{ env.APP_ENV }}.
🏗️ [GitHub Actions Build](https://github.com/${{ steps.lower-repo.outputs.repository }}/actions/runs/${{ github.run_id }})
🐳 [Image](https://ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }}
🔗 [Link](https://${{ env.APP_DOMAIN }})
- name: Telegram Notify
uses: appleboy/[email protected]
if: failure()
with:
to: ${{ secrets.TELEGRAM_DEPLOY_CHAT_ID }}
token: ${{ secrets.TELEGRAM_DEPLOY_TOKEN }}
format: markdown
message: |
🚨Deploy of the application from repository [${{ steps.lower-repo.outputs.repository }}](https://github.com/${{ steps.lower-repo.outputs.repository }}) on ${{ env.APP_ENV }} has been failed.
🏗️ [GitHub Actions Build](https://github.com/${{ steps.lower-repo.outputs.repository }}/actions/runs/${{ github.run_id }})
🐳 [Image](https://ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }}
🔗 [Link](https://${{ env.APP_DOMAIN }})
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/web-ide.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:21-alpine AS base

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

EXPOSE 3000
CMD ["npm", "start"]
4 changes: 4 additions & 0 deletions helm/app/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v2
name: Node.js Chart
description: A Helm chart for deploying my Node.js application
version: 0.1.2
54 changes: 54 additions & 0 deletions helm/app/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
release: prometheus-stack
spec:
replicas: {{ .Values.defaultReplicaCount }}
strategy:
type: RollingUpdate
selector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
template:
metadata:
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
release: prometheus-stack
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
containers:
- name: {{ .Values.appName }}-{{ .Values.deployEnv}}
image: "{{ .Values.imageRepo }}:{{ .Values.imageTag }}"
env:
- name: APP_ENV
value: {{ .Values.deployEnv }}
- name: APP_VERSION
value: {{ .Values.appVersion | quote }}
- name: NEXT_PUBLIC_PROXY_KEY
value: {{ .Values.secrets.publicProxyKey }}
- name: NEXT_PUBLIC_MIXPANEL_TOKEN
value: {{ .Values.secrets.publicMixPanelToken }}
- name: NEXT_PUBLIC_ANALYTICS_ENABLED
value:{{ .Values.secrets.publicProxyKey }}
ports:
- containerPort: {{ .Values.containerPort }}
resources:
limits:
cpu: {{ .Values.cpuLimit }}
memory: {{ .Values.memoryLimit }}
requests:
cpu: {{ .Values.cpuRequest }}
memory: {{ .Values.memoryRequest }}
imagePullPolicy: Always
imagePullSecrets:
- name: dockerconfigjson-github-com
10 changes: 10 additions & 0 deletions helm/app/templates/ghcr-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
kind: Secret
type: kubernetes.io/dockerconfigjson
apiVersion: v1
metadata:
name: dockerconfigjson-github-com
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
data:
.dockerconfigjson: {{ .Values.ghcrSecret }}
25 changes: 25 additions & 0 deletions helm/app/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
minReplicas: {{ .Values.minReplicas }}
maxReplicas: {{ .Values.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
44 changes: 44 additions & 0 deletions helm/app/templates/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{{ if .Values.publicService }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "{{ .Values.sslRedirect }}"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10s"
nginx.ingress.kubernetes.io/proxy-read-timeout: "15s"
nginx.ingress.kubernetes.io/proxy-send-timeout: "15s"
nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_502 http_503 http_504"
nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3"
cert-manager.io/cluster-issuer: {{ .Values.tlsIssuer }}
{{- if eq .Values.deployEnv "canary" }}
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: {{ .Values.canaryWeight | quote }}
{{- end }}
nginx.ingress.kubernetes.io/server-snippet: |
location ~ ^/(metrics|ready|health)$ {
return 403;
}
labels:
release: prometheus-stack
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
spec:
tls:
- hosts:
- {{ .Values.host }}
secretName: {{ .Values.host }}
rules:
- host: {{ .Values.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Values.appName }}-{{ .Values.deployEnv }}
port:
name: http
{{- end }}
27 changes: 27 additions & 0 deletions helm/app/templates/ns-resource-quota.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: v1
kind: ResourceQuota
metadata:
name: resource-quota
namespace: {{ .Release.Namespace }}
spec:
hard:
{{- if eq .Values.deployEnv "staging" }}
pods: "2"
services: "1"
requests.memory: "256Mi"
limits.cpu: "1"
limits.memory: "16Gi"
persistentvolumeclaims: "0"
{{- end }}
{{- if eq .Values.deployEnv "canary" }}
pods: "10"
limits.cpu: "4"
limits.memory: "8Gi"
persistentvolumeclaims: "0"
{{- end }}
{{- if eq .Values.deployEnv "production" }}
pods: "100"
limits.cpu: "8"
limits.memory: "16Gi"
persistentvolumeclaims: "0"
{{- end }}
19 changes: 19 additions & 0 deletions helm/app/templates/service-monitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
release: prometheus-stack
spec:
selector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
endpoints:
- port: http
interval: 30s
path: /metrics
- port: https
interval: 30s
path: /metrics

17 changes: 17 additions & 0 deletions helm/app/templates/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
release: prometheus-stack
spec:
type: ClusterIP
selector:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
ports:
- name: http
protocol: TCP
port: 80
targetPort: {{ .Values.containerPort }}
Loading

0 comments on commit afd1a10

Please sign in to comment.