DEV Community

Cover image for CI/CD Pipeline for a Next.js Application Using GitHub Actions, ArgoCD, and MicroK8s
Ibrahim Okić
Ibrahim Okić

Posted on

CI/CD Pipeline for a Next.js Application Using GitHub Actions, ArgoCD, and MicroK8s

GitOps flow using GitHub Actions, ArgoCD and Microk8s

🔗 Setting up GitHub Actions

1.) Create a GitHub repository

Alright, first things first—let’s create a GitHub repository to house your Next.js app. In my case, I named mine "netflix-next." 😉 Feel free to call yours whatever you want, but I highly recommend a name that inspires fear and respect in your fellow developers.

2.) Create a GitHub repository named infrastructure

3.) Inside it, create the following directories and files

For reference, check my repositories: https://github.com/kaizerpwn/netflix-next and https://github.com/kaizerpwn/infrastructure):

k8s
-- netflix-next
---- deployment.yml
---- service.yml
---- ingress.yml
Enter fullscreen mode Exit fullscreen mode

Here’s what each file does:

  • deployment.yml: Defines the configuration for deploying your app’s Docker container, including the number of replicas and the image to use.
  • service.yml: Sets up the networking layer for your app, ensuring that traffic is directed to the correct pods.
  • ingress.yml: Manages external access to the services, routing HTTP/S traffic to the correct service endpoints.

The contents of these files are as follows:

deployment.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: netflix-next-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netflix-next
  template:
    metadata:
      labels:
        app: netflix-next
    spec:
      containers:
        - name: netflix-next-container
          image: ghcr.io/<YOUR_GITHUB_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:e19d20e9df6de3f64f622c46c4f6315ce37b3097
          ports:
            - containerPort: 3000
      imagePullSecrets:
        - name: regcred

Enter fullscreen mode Exit fullscreen mode

ingress.yml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-staging
  name: netflix-next-ingress
  namespace: default
spec:
  ingressClassName: public
  rules:
    - host: netflix.kaizer.live
      http:
        paths:
          - path: /.well-known/acme-challenge/
            pathType: Prefix
            backend:
              service:
                name: cm-acme-http-solver-service
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: netflix-next-service
                port:
                  number: 80
  tls:
    - hosts:
        - netflix.kaizer.live
      secretName: netflix-next-tls
Enter fullscreen mode Exit fullscreen mode

service.yml:

apiVersion: v1
kind: Service
metadata:
  name: netflix-next-service
spec:
  type: LoadBalancer
  selector:
    app: netflix-next
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
Enter fullscreen mode Exit fullscreen mode

4.) Create a GitHub personal access token (shown in the video).

To allow GitHub Actions to interact with your repositories, create a GitHub personal access token. This token will be used to authenticate the workflow when it pushes updates or checks out code.

5.) Add the codespace secret to the infrastructure repository (shown in the video).

Add your personal access token as a secret to the infrastructure repository. This allows the GitHub Actions workflow to use it securely without exposing your credentials.

6.) Add the actions secrets to the netflix-next repository (shown in the video).

Similarly, add necessary secrets to the netflix-next repository. These might include Docker registry credentials, API keys, or any other sensitive information your workflow needs.

7.) Once this is done, create a workflow file pipeline.yml (shown in the video):

Now, create a pipeline.yml file in the .github/workflows directory of your netflix-next repository. This file will define the CI/CD pipeline for your Next.js app:

pipeline.yml:

name: CI/CD Pipeline for Next.js

on:
  push:
    branches: [ "main" ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'

      - name: Install Dependencies
        run: npm ci

      - name: Build Next.js App
        run: npm run build

      - name: Docker Login
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.PAT_TOKEN }}

      - name: Docker Build and Push
        id: build-push
        uses: docker/build-push-action@v3
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            ghcr.io/<YOUR_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:${{ github.sha }}
            ghcr.io/<YOUR_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:latest

      - name: Get Docker Image Digest
        id: digest
        run: |
          IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/<YOUR_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:${{ github.sha }})
          echo "IMAGE_DIGEST=$IMAGE_DIGEST" >> $GITHUB_ENV

      - name: Checkout Infra Repository
        uses: actions/checkout@v4
        with:
          repository: <YOUR_USERNAME>/infrastructure
          ref: 'main'
          token: ${{ secrets.INFRASTRUCTURE_TOKEN }}
          path: infrastructure

      - name: Verify File Presence
        run: |
          echo "Checking file presence:"
          ls -la infrastructure/k8s/<YOUR_REPOSITORY>/
          cat infrastructure/k8s/<YOUR_REPOSITORY>/deployment.yml

      - name: Update Deployment Manifest
        run: |
          echo "Updating deployment manifest:"
          # Print the file content before changes
          echo "Before update:"
          cat infrastructure/k8s/<YOUR_REPOSITORY>/deployment.yml

          # Update the image tag in the deployment manifest with the digest
          sed -i "s|image: ghcr.io/<YOUR_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:.*|image: ghcr.io/<YOUR_USERNAME>/<YOUR_DOCKER_IMAGE_NAME>:${{ github.sha }}|g" infrastructure/k8s/<YOUR_REPOSITORY>/deployment.yml

          # Print the file content after changes
          echo "After update:"
          cat infrastructure/k8s/<YOUR_REPOSITORY>/deployment.yml

      - name: Stage and Commit Changes
        run: |
          cd infrastructure
          git config user.name "GitHub Actions Bot"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add k8s/<YOUR_REPOSITORY>/deployment.yml
          # Verify the staging
          git status
          # Commit and push changes if there are modifications
          if git diff --cached --quiet; then
            echo "No changes to commit"
          else
            git commit -m "Update image to version ${{ github.sha }}"
            git push origin main
          fi
Enter fullscreen mode Exit fullscreen mode

8.) After committing this file, the first build will start. Click on the Actions tab and wait for it to run.

If everything is correct, the build will be green 🌿, and in the infrastructure repository, the GitHub Actions bot will automatically update the image tag in the deployment.yml, so you can check that. After these steps, we have completed everything related to GitHub.

We can now move on to the server part. Also, if you wish, you can add more pipelines, e.g., one for staging environment and another for production, etc.

⚙ MicroK8s Installation and Configuration Guide

Now I will provide a step-by-step guide to installing MicroK8s, enabling essential services, and setting up Argo CD on your Kubernetes cluster.

1.) Install MicroK8s

To get started, you need to install MicroK8s using snapd. Run the following commands in your terminal:

sudo apt install snapd
sudo snap install microk8s --classic
Enter fullscreen mode Exit fullscreen mode

2.) Enable Required Services

After installing MicroK8s, you need to enable several essential services to ensure your cluster is fully functional. The following commands will enable DNS, storage, registry, MetalLB (Load Balancer), hostpath storage, Ingress, Dashboard, Cert Manager, and MinIO:

microk8s enable dns
microk8s enable storage # (optional)
microk8s enable registry
microk8s enable metallb # Load balancer
microk8s enable hostpath-storage # (optional)
microk8s enable cert-manager # Certificate manager
microk8s enable dashboard # Kubernetes dashboard (optional)
microk8s enable ingress # Routing rules manager
microk8s enable minio # Object storage S3 service - like Amazon S3 (optional)
Enter fullscreen mode Exit fullscreen mode

3.) Install Argo CD

To deploy Argo CD as a service, follow these steps. Ensure you check for the latest version of Argo CD before applying the command:

microk8s.kubectl create namespace argocd
microk8s.kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.12.3/manifests/install.yaml
Enter fullscreen mode Exit fullscreen mode

4.) Expose Argo CD on an External Port

To expose Argo CD on an external port, you have two options. You can either change the Service type to NodePort or LoadBalancer.

Method 1: Using NodePort
microk8s.kubectl edit svc argocd-server -n argocd
Enter fullscreen mode Exit fullscreen mode

Change the spec.type from ClusterIP to NodePort:

spec:
  type: NodePort

ports:
  - name: http
    port: 80
    targetPort: 8080
    nodePort: 32080  # Choose any port within the valid NodePort range (30000-32767)
  - name: https
    port: 443
    targetPort: 8443
    nodePort: 32443  # Choose any port within the valid NodePort range (30000-32767)
Enter fullscreen mode Exit fullscreen mode
Method 2: Using LoadBalancer

Alternatively, change the spec.type to LoadBalancer if you have MetalLB or other LoadBalancer service:

spec:
  type: LoadBalancer
Enter fullscreen mode Exit fullscreen mode

5.) Retrieve the Initial Argo CD Admin Password

To obtain the initial password for logging into the Argo CD dashboard (the username is admin), run the following command:

microk8s.kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
Enter fullscreen mode Exit fullscreen mode

Copy the output and use it to log in to the Argo CD dashboard.

6.) Create a Docker Registry Secret

To create a Docker registry secret for accessing private Docker images (e.g., from GitHub Container Registry), use the following command. Replace GITHUB_USERNAME, GITHUB_PERSONAL_ACCESS_TOKEN, and GITHUB_EMAIL with your GitHub credentials:

microk8s.kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io \
  --docker-username="GITHUB_USERNAME" \
  --docker-password="GITHUB_PERSONAL_ACCESS_TOKEN" \
  --docker-email="GITHUB_EMAIL" \
  -n default
Enter fullscreen mode Exit fullscreen mode

By following these steps, you will have a fully configured MicroK8s cluster with essential services and Argo CD ready for use. Make sure to replace placeholders with your actual information when creating secrets or applying configuration changes.

7.) Access the ArgoCD on the web

To check the port on which ArgoCD is running, use the following command:

microk8s kubectl get svc -n argocd
Enter fullscreen mode Exit fullscreen mode

You will get output like this:

argocd-applicationset-controller          ClusterIP      10.152.183.42    <none>         7000/TCP,8080/TCP            5d19h
argocd-dex-server                         ClusterIP      10.152.183.120   <none>         5556/TCP,5557/TCP,5558/TCP   5d19h
argocd-metrics                            ClusterIP      10.152.183.249   <none>         8082/TCP                     5d19h
argocd-notifications-controller-metrics   ClusterIP      10.152.183.122   <none>         9001/TCP                     5d19h
argocd-redis                              ClusterIP      10.152.183.218   <none>         6379/TCP                     5d19h
argocd-repo-server                        ClusterIP      10.152.183.25    <none>         8081/TCP,8084/TCP            5d19h
argocd-server                             LoadBalancer   10.152.183.26    10.64.140.43   80:32092/TCP,443:31217/TCP   5d19h
argocd-server-metrics                     ClusterIP      10.152.183.202   <none>         8083/TCP                     5d19h
Enter fullscreen mode Exit fullscreen mode

You can now access ArgoCD in your web browser using http://YOUR_SERVER_IP:31217/ or the port that was displayed in the output specific to your setup.

Username: admin
Password: The password is provided in the output of step 5.

8.) Connect repository with ArgoCD

To connect your repository with ArgoCD follow steps shown in the video:

9.) Create and deploy new application

Finally, create and deploy your app using ArgoCD. Follow the steps in the video, and watch as your app springs to life like a phoenix from the ashes—or, you know, like an app from the cloud.

By the end of this adventure, you'll have a fully configured MicroK8s cluster, with all the bells and whistles, ready to handle whatever you throw at it.

Happy coding, and may your builds be ever green! 🌿

Top comments (0)