diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..a5d1ae0 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,57 @@ +# Kubernetes Deployment Workflow + +This workflow automates the process of building a Docker image for the public-wiki application, pushing it to the GitHub Container Registry (ghcr.io), and deploying it to a Kubernetes cluster. + +## How it Works + +The workflow is defined in `deploy.yml` and consists of two main jobs: + +1. **`build-and-push`**: This job is responsible for: + * Checking out the repository's code. + * Logging into the GitHub Container Registry. + * Building the Docker image using the `Dockerfile` in the root of the project. + * Pushing the built image to the GitHub Container Registry, tagged with the Git SHA of the commit. + +2. **`deploy`**: This job depends on the successful completion of `build-and-push` and is responsible for: + * Checking out the repository's code. + * Setting up `kubectl` with the credentials to access your Kubernetes cluster. + * Updating the `k8s/deployment.yml` to use the newly built Docker image. + * Applying the Kubernetes manifests (`deployment.yml`, `service.yml`, and `ingress.yml`) located in the `k8s/` directory to the cluster. + +## Triggers + +The workflow is automatically triggered on any `push` event to the following branches: + +* `main` +* `dev` + +## Prerequisites + +Before this workflow can run successfully, you must configure a secret in your GitHub repository. + +### `KUBE_CONFIG_DATA` + +This secret is required for the `deploy` job to authenticate with your Kubernetes cluster. It should contain the base64-encoded content of your `kubeconfig` file. + +To create this secret: + +1. Go to your GitHub repository's **Settings**. +2. Navigate to **Secrets and variables** > **Actions**. +3. Click on **New repository secret**. +4. Name the secret `KUBE_CONFIG_DATA`. +5. For the value, you need to provide the base64-encoded version of your `kubeconfig` file. You can get this by running the following command in your terminal: + + ```bash + cat ~/.kube/config | base64 + ``` + + If you are on Windows using PowerShell, you can use: + + ```powershell + [Convert]::ToBase64String([System.IO.File]::ReadAllBytes("$env:USERPROFILE\.kube\config")) + ``` + +6. Copy the output of the command and paste it into the "Value" field of the secret. +7. Click **Add secret**. + +Once the secret is configured, the workflow will be able to deploy your application to your Kubernetes cluster whenever you push changes to the `main` or `dev` branches. diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..a9f8829 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,95 @@ +name: Deploy to Kubernetes + +on: + push: + branches: + - main + - dev + +env: + IMAGE_NAME: ghcr.io/${{ github.repository }}:${{ github.sha }} + +jobs: + build-and-push: + runs-on: self-hosted + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker tags + id: docker_tags + run: | + BRANCH="${GITHUB_REF_NAME}" + SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) + IMAGE_BASE="ghcr.io/${{ github.repository }}" + TAGS="" + if [ "$BRANCH" = "main" ]; then + TAGS+="${IMAGE_BASE}:main,${IMAGE_BASE}:latest,${IMAGE_BASE}:main-${SHORT_SHA}" + elif [ "$BRANCH" = "dev" ]; then + TAGS+="${IMAGE_BASE}:dev,${IMAGE_BASE}:dev-${SHORT_SHA}" + else + TAGS+="${IMAGE_BASE}:${BRANCH},${IMAGE_BASE}:${BRANCH}-${SHORT_SHA}" + fi + echo "tags=$TAGS" >> $GITHUB_OUTPUT + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ${{ steps.docker_tags.outputs.tags }} + + deploy: + runs-on: ubuntu-latest + needs: build-and-push + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Kubeconfig + env: + KUBECONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + run: | + mkdir -p ~/.kube + + # Decode and write kubeconfig + echo "${KUBECONFIG_DATA}" | base64 -d > ~/.kube/config + + # Verify kubeconfig was decoded properly + if [ ! -s ~/.kube/config ]; then + echo "❌ ERROR: Kubeconfig file is empty after decoding!" + echo "Please verify that KUBECONFIG_DATA secret contains valid base64 encoded kubeconfig" + exit 1 + fi + + # Set proper permissions + chmod 600 ~/.kube/config + + # Verify connection + echo "Testing connection to Kubernetes cluster..." + kubectl cluster-info + + - name: Deploy to Kubernetes + run: | + if [ "${{ github.ref_name }}" = "main" ]; then + OVERLAY=main + elif [ "${{ github.ref_name }}" = "dev" ]; then + OVERLAY=dev + else + echo "Branch is not main or dev, skipping deployment." + exit 1 + fi + cd k8s/overlays/$OVERLAY + kustomize build . | kubectl apply --prune -l app=public-wiki --prune-allowlist=core/v1/Namespace --prune-allowlist=apps/v1/Deployment --prune-allowlist=core/v1/Service --prune-allowlist=networking.k8s.io/v1/Ingress -f - diff --git a/k8s/base/deployment.yml b/k8s/base/deployment.yml new file mode 100644 index 0000000..fb3d231 --- /dev/null +++ b/k8s/base/deployment.yml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: public-wiki + namespace: wiki + labels: + app: public-wiki +spec: + replicas: 3 # default, will be overridden by overlays + selector: + matchLabels: + app: public-wiki + template: + metadata: + labels: + app: public-wiki + spec: + containers: + - name: public-wiki + image: IMAGE_TAG # will be replaced by kustomize + ports: + - containerPort: 3000 diff --git a/k8s/base/ingress.yml b/k8s/base/ingress.yml new file mode 100644 index 0000000..99fec47 --- /dev/null +++ b/k8s/base/ingress.yml @@ -0,0 +1,21 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: public-wiki-ingress + namespace: wiki + labels: + app: public-wiki + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: PLACEHOLDER_HOST + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: public-wiki + port: + number: 80 diff --git a/k8s/base/kustomization.yml b/k8s/base/kustomization.yml new file mode 100644 index 0000000..133a1d5 --- /dev/null +++ b/k8s/base/kustomization.yml @@ -0,0 +1,11 @@ +resources: + - namespace.yml + - deployment.yml + - service.yml + - ingress.yml + +images: + - name: public-wiki + newName: IMAGE_TAG + +namespace: wiki diff --git a/k8s/base/namespace.yml b/k8s/base/namespace.yml new file mode 100644 index 0000000..adf0a9e --- /dev/null +++ b/k8s/base/namespace.yml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: wiki + labels: + app: public-wiki diff --git a/k8s/base/service.yml b/k8s/base/service.yml new file mode 100644 index 0000000..229de96 --- /dev/null +++ b/k8s/base/service.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: public-wiki + namespace: wiki + labels: + app: public-wiki +spec: + selector: + app: public-wiki + ports: + - protocol: TCP + port: 80 + targetPort: 3000 diff --git a/k8s/deployment.yml b/k8s/deployment.yml new file mode 100644 index 0000000..fb3d231 --- /dev/null +++ b/k8s/deployment.yml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: public-wiki + namespace: wiki + labels: + app: public-wiki +spec: + replicas: 3 # default, will be overridden by overlays + selector: + matchLabels: + app: public-wiki + template: + metadata: + labels: + app: public-wiki + spec: + containers: + - name: public-wiki + image: IMAGE_TAG # will be replaced by kustomize + ports: + - containerPort: 3000 diff --git a/k8s/ingress.yml b/k8s/ingress.yml new file mode 100644 index 0000000..99fec47 --- /dev/null +++ b/k8s/ingress.yml @@ -0,0 +1,21 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: public-wiki-ingress + namespace: wiki + labels: + app: public-wiki + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: PLACEHOLDER_HOST + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: public-wiki + port: + number: 80 diff --git a/k8s/kustomization.yml b/k8s/kustomization.yml new file mode 100644 index 0000000..2e58f59 --- /dev/null +++ b/k8s/kustomization.yml @@ -0,0 +1,10 @@ +resources: + - namespace.yml + - deployment.yml + - service.yml + - ingress.yml +images: + - name: public-wiki + newName: IMAGE_TAG + +namespace: wiki diff --git a/k8s/namespace.yml b/k8s/namespace.yml new file mode 100644 index 0000000..adf0a9e --- /dev/null +++ b/k8s/namespace.yml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: wiki + labels: + app: public-wiki diff --git a/k8s/overlays/dev/kustomization.yml b/k8s/overlays/dev/kustomization.yml new file mode 100644 index 0000000..b593024 --- /dev/null +++ b/k8s/overlays/dev/kustomization.yml @@ -0,0 +1,25 @@ +resources: + - ../../base + +namespace: wiki + +patches: + - target: + kind: Deployment + name: public-wiki + patch: |- + - op: replace + path: /spec/replicas + value: 1 + - target: + kind: Ingress + name: public-wiki-ingress + patch: |- + - op: replace + path: /spec/rules/0/host + value: dev.wiki.coregame.de + +images: + - name: IMAGE_TAG + newName: ghcr.io/42core-team/wiki + newTag: dev diff --git a/k8s/overlays/main/kustomization.yml b/k8s/overlays/main/kustomization.yml new file mode 100644 index 0000000..63214dc --- /dev/null +++ b/k8s/overlays/main/kustomization.yml @@ -0,0 +1,25 @@ +resources: + - ../../base + +namespace: wiki + +patches: + - target: + kind: Deployment + name: public-wiki + patch: |- + - op: replace + path: /spec/replicas + value: 3 + - target: + kind: Ingress + name: public-wiki-ingress + patch: |- + - op: replace + path: /spec/rules/0/host + value: wiki.coregame.de + +images: + - name: IMAGE_TAG + newName: ghcr.io/42core-team/wiki + newTag: main diff --git a/k8s/service.yml b/k8s/service.yml new file mode 100644 index 0000000..229de96 --- /dev/null +++ b/k8s/service.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: public-wiki + namespace: wiki + labels: + app: public-wiki +spec: + selector: + app: public-wiki + ports: + - protocol: TCP + port: 80 + targetPort: 3000