# Using CI/CD to deploy web applications on Kubernetes with ArgoCD

*Originally published in* [***CircleCI***](https://circleci.com/blog/) *Blog:* [*Using CI/CD to deploy web applications on Kubernetes with ArgoCD*](https://circleci.com/blog/deploy-to-kubernetes-with-argocd/) *by* [***Avik Kundu***](https://circleci.com/blog/author/avik-kundu/)

[GitOps](https://www.weave.works/technologies/gitops/) modernizes software management and operations by allowing developers to declaratively manage infrastructure and code using a single source of truth, usually a Git repository. Many development teams and organizations have adopted GitOps procedures to improve the creation and delivery of software applications.

For a GitOps initiative to work, an orchestration system like [Kubernetes](https://kubernetes.io/) is crucial. The number of incompatible technologies needed to develop software makes Kubernetes a key tool for managing infrastructure. Without Kubernetes, implementing infrastructure-as-code (IaC) procedures is inefficient or even impossible. Fortunately, the wide adoption of Kubernetes has enabled the creation of tools for implementing GitOps.

One of these tools, [ArgoCD](https://argoproj.github.io/cd/), is a Kubernetes-native continuous deployment (CD) tool. It can deploy code changes directly to Kubernetes resources by pulling it from Git repositories instead of an external CD solution. Many of these solutions support only push-based deployments. Using ArgoCD gives developers the ability to control application updates and infrastructure setup from a unified platform. It handles the latter stages of the GitOps process, ensuring that new configurations are correctly deployed to a Kubernetes cluster.

In this tutorial, you will learn how to deploy a Node.js application on Azure Kubernetes Service (AKS) using a CI/CD pipeline and ArgoCD.

## **Prerequisites**

To follow along with this tutorial, you will need a few things first.

Accounts for:

*   [Docker Hub](https://hub.docker.com/)
    
*   [GitHub](https://github.com/)
    
*   [Microsoft Azure](https://azure.microsoft.com/en-in/features/azure-portal/)
    
*   [CircleCI](https://circleci.com/signup/)
    

These tools installed on your system:

*   [Kubectl](https://kubernetes.io/docs/tasks/tools/)
    
*   [ArgoCD CLI](https://argo-cd.readthedocs.io/en/stable/cli_installation/)
    
*   [Azure CLI](https://github.com/Azure/azure-cli)
    
*   [Node JS](https://nodejs.org/)
    
*   [Docker Engine](https://docs.docker.com/get-docker/)
    

After you have all the prerequisites complete you are ready to go to the next section.

## **Cloning the Node.js application**

In this tutorial, the main focus is on deploying the application on Kubernetes. You can directly [clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) the [Node.js application](https://github.com/CIRCLECI-GWP/aks-nodejs-argocd) to your GitHub and continue with the rest of the process.

To clone the project, run:

```bash
git clone https://github.com/CIRCLECI-GWP/aks-nodejs-argocd.git
```

There are 2 branches in this repository:

*   `main` branch contains only the Node.js Application code
    
*   `circleci-project-setup` branch contains the application codes along with all YAML files that you will create
    

Check out to the `main` branch.

The Node.js application lives in the `app.js` file and contains:

```js
const express = require("express");
const path = require("path");
const morgan = require("morgan");
const bodyParser = require("body-parser");
/* eslint-disable no-console */
const port = process.env.PORT || 1337;
const app = express();
app.use(morgan("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: "true" }));
app.use(bodyParser.json({ type: "application/vnd.api+json" }));
app.use(express.static(path.join(__dirname, "./")));
app.get("*", (req, res) => {
  res.sendFile(path.join(__dirname, "./index.html"));
});
app.listen(port, (err) => {
  if (err) {
    console.log(err);
  } else {
    console.log(`App at: http://localhost:${port}`);
  }
});
module.exports = app;
```

The key takeaway from this code is the port number, This is where the application will be running, which is `1337` for this tutorial.

You can run the application locally by first installing the dependencies. In the project’s root, type:

```bash
npm install
```

Then run the application with the command:

```bash
node app.js
```

The application should now be up and running at the address `http://localhost:1337`.

## **Containerizing the Node.js application**

To deploy the application of Kubernetes you need to containerize it. To containerize applications using Docker as the container runtime tool, you will create a [Dockerfile](https://docs.docker.com/engine/reference/builder/). A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

Create a new file in the root directory of the project and name it `Dockerfile`. Copy the following content in the file:

```yaml
# Set the base image to use for subsequent instructions
FROM node:alpine
# Set the working directory for any subsequent ADD, COPY, CMD, ENTRYPOINT,
# or RUN instructions that follow it in the Dockerfile
WORKDIR /usr/src/app
# Copy files or folders from source to the dest path in the image's filesystem.
COPY package.json /usr/src/app/
COPY . /usr/src/app/
# Execute any commands on top of the current image as a new layer and commit the results.
RUN npm install --production
# Define the network ports that this container will listen to at runtime.
EXPOSE 1337
# Configure the container to be run as an executable.
ENTRYPOINT ["npm", "start"]
```

If you have [Docker](https://docs.docker.com/get-docker/) installed, you can build and run the container locally for testing. Later on in this tutorial, you will learn how to automate this process with CircleCI orbs.

To build and tag the container, enter:

```bash
docker build -t aks-nodejs-argocd:latest .
```

Confirm that the image was successfully created by running this command from your terminal:

```bash
docker images
```

Then run the container using the command:

```bash
docker run -it -p 1337:1337 aks-nodejs-argocd:latest
```

The application should now be up and running at the address `http://127.0.0.1:1337`.

Commit and [push](https://circleci.com/blog/pushing-a-project-to-github/) the changes to your GitHub repository.

## **Configuring Kubernetes manifests for deployment**

To deploy containers on Kubernetes, you will have to configure Kubernetes to incorporate all the settings required to run your application. Kubernetes uses [YAML](https://yaml.org/) for configuration.

Create a directory named `manifests` in the root directory of the project. Then create these files in the newly created folder:

*   `namespace.yaml`
    
*   `deployment.yaml`
    
*   `service.yaml`
    
*   `kustomization.yaml`
    

In Kubernetes, [namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) provide a mechanism for isolating groups of resources within a single cluster. The contents of the `namespace.yaml`:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: nodejs
  labels:
    name: nodejs
```

This file would create a namespace named `nodejs` inside the Kubernetes cluster. All the resources would be created in this namespace.

[Kubernetes Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) manage stateless services running on your cluster. Their purpose is to keep a set of identical pods running and upgrade them in a controlled way – performing a rolling update by default. The contents of the `deployment.yaml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs
  namespace: nodejs
  labels:
    app: nodejs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nodejs
  template:
    metadata:
      labels:
        app: nodejs
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": linux
      containers:
        - name: aks-nodejs-argocd
          image: aks-nodejs-argocd
          ports:
            - name: http
              containerPort: 1337
```

The key takeaway from this code is the `containerPort`. This is where the application will be running and where the `container-image` will be pulled and deployed in the namespace on the Kubernetes cluster.

[Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) is an abstraction that defines a logical set of pods and a policy for accessing them. You need the Kubernetes Service type `LoadBalancer` to make the deployment accessible to the outside world.  
The contents of the `service.yaml` are:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nodejs
  namespace: nodejs
  labels:
    app: nodejs
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 1337
  selector:
    app: nodejs
```

The key takeaway from this code is the `targetPort`, `port` and `type`:

*   `targetPort` is the container port
    
*   `port` is where the application will be running
    
*   `type` is the type of service
    

To deploy the latest version of the application on the Kubernetes cluster, the resources have to be customized to maintain the updated information. You can use [Kustomize](https://kustomize.io/), which is a tool for customizing Kubernetes configurations.

The contents of the `kustomization.yaml` are:

```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - namespace.yaml
namespace: nodejs
images:
  - name: aks-nodejs-argocd
    newName: aks-nodejs-argocd
    newTag: v1
```

The key takeaway from this code is `newName` and `newTag`, which will be updated with the latest Docker image information as part of the continuous integration process.

Commit and [push](https://circleci.com/blog/pushing-a-project-to-github/) these files into the `main` branch of the GitHub repository you had cloned earlier.

## **Launching the Azure Kubernetes Service (AKS) cluster**

In this tutorial, you will be deploying the application on the [AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/#overview) cluster. To create the AKS cluster, the Azure CLI should be [connected to your Azure account](https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli).

To launch an AKS cluster using the Azure CLI, create a Resource Group with this command:

```bash
az group create --name NodeRG --location eastus
```

Launch a two-node cluster:

```bash
az aks create --resource-group NodeRG --name NodeCluster --node-count 2 --enable-addons http_application_routing
```

**Note:** *If you generated any SSH keys in your system previously, you need to add the optional* `--generate-ssh-keys` parameter to this command. This auto-generates SSH public and private key files if they are missing. The keys are stored in the `~/.ssh` directory.

The AKS cluster will take 10 to 15 minutes to launch.

## **Installing ArgoCD in the AKS Cluster**

Once the cluster is up and running, you can install ArgoCD inside the cluster. You will use ArgoCD for deploying your application.

To install the application, use the Azure CLI. Configure `kubectl` to connect to AKS using this command:

```bash
az aks get-credentials --resource-group NodeRG --name NodeCluster
```

To install ArgoCD, use these commands:

```bash
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
```

ArgoCD will be installed in the `argocd` namespace. To get all the resources in the namespace enter:

```bash
kubectl get all --namespace argocd
```

### **Exposing the ArgoCD API server**

By default, the ArgoCD API server is not exposed to an external IP. Because you will access the application from the internet during this tutorial, you need to expose the ArgoCD server with an external IP via Service Type Load Balancer.

Change the argocd-server service type to LoadBalancer:

```bash
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
```

**Note:** *You can also use Kubectl port forwarding to connect to the API server without exposing the service. Use this command:* `kubectl port-forward svc/argocd-server -n argocd 8080:443`

You can now access the API server using `https://localhost:8080`.

## **Accessing the ArgoCD Web Portal**

Once you have exposed the ArgoCD API server with an external IP, you can now access the portal with the external IP address you generated.

ArgoCD is installed in the `argocd` namespace. Use this command to get all the resources in the namespace:

```bash
kubectl get all --namespace argocd
```

Copy the `External-IP` corresponding to `service/argocd-server`.

[![External-IP](https://production-cci-com.imgix.net/blog/media/2022-10-21-external-ip.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-external-ip.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

You can access the application at `http://<EXTERNAL-IP>`.  
In my case, that was `http://20.237.108.112/`

[![ArgoCD application](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-application.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-application.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

To log into the portal, you will need the username and password. The username is set as `admin` by default.

To fetch the password, execute this command:

```bash
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
```

Use this username/password combination to log into the ArgoCD portal.

## **Configuring Kubernetes manifests for ArgoCD**

To configure ArgoCD to deploy your application on Kubernetes, you will have to set up ArgoCD to connect the Git Repository and Kubernetes in a declarative way using [YAML](https://yaml.org/) for configuration.

Apart from this method, you can also set up ArgoCD from the Web Portal or use the ArgoCD CLI. Because this tutorial is following GitOps principles, we are using the Git repository as the sole source of truth. Therefore the declarative method using YAML files works best.

One of the key features and capabilities of ArgoCD is to sync via manual or automatic policy for the deployment of applications to a Kubernetes cluster.

To get started, create a directory named `argocd` in the root directory of the project. Create a new file in the new directory and name it `config.yaml`.

### **Manual Sync Policy**

Use this policy to manually synchronize your application by way of your CI/CD pipelines. Whenever a code change is made, the CI/CD pipeline is triggered and calls the ArgoCD server APIs to start the sync process based on the changes you will commit. For communicating with the ArgoCD server APIs, you can use the ArgoCD CLI. You can also use one of the SDKs available for various programming languages.

For setting up the Manual Sync policy for ArgoCD, paste this in the `config.yaml`:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: aks-nodejs-argocd
  namespace: argocd
spec:
  destination:
    namespace: nodejs
    server: "https://kubernetes.default.svc"
  source:
    path: manifests
    repoURL: "https://github.com/Lucifergene/aks-nodejs-argocd"
    targetRevision: circleci-project-setup
  project: default
```

### **Automated Sync policy**

ArgoCD can automatically sync an application when it detects differences between the desired manifests in Git and the live state in the cluster.

A benefit of automatic sync is that CI/CD pipelines no longer need direct access to the ArgoCD API server to perform the deployment. Instead, the pipeline makes a commit and pushes to the Git repository with the changes to the manifests in the tracking Git repo.

If you want to set to the Automated Sync policy, you need to paste this in the `config.yaml`

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: aks-nodejs-argocd
  namespace: argocd
spec:
  destination:
    namespace: nodejs
    server: "https://kubernetes.default.svc"
  source:
    path: manifests
    repoURL: "https://github.com/Lucifergene/aks-nodejs-argocd"
    targetRevision: circleci-project-setup
  project: default
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
```

Commit and [push](https://circleci.com/blog/pushing-a-project-to-github/) these files into the `main` branch of the GitHub repository you cloned earlier.

## **Creating the continuous integration pipeline**

The objective of this tutorial is to show how you can deploy applications on Kubernetes through [continuous integration](https://circleci.com/continuous-integration/) (CI) using CircleCI and [continuous deployment](https://circleci.com/blog/a-brief-history-of-devops-part-iv-continuous-delivery-and-continuous-deployment/) (CD) via ArgoCD. The CI pipeline should trigger the process of building the container and pushing it to Docker Hub, and the CD should deploy the application on Kubernetes.

To create the CI pipeline, you will be using CircleCI integrated with your GitHub account. CircleCI configuration is named `config.yml` and lives in the `.circleci` directory in the project’s root folder. The path to the configuration is `.circleci/config.yml`.

The content `config.yml` is:

```yaml
version: 2.1

orbs:
  docker: circleci/docker@2.1.1
  azure-aks: circleci/azure-aks@0.3.0
  kubernetes: circleci/kubernetes@1.3.0

jobs:
  argocd-manual-sync:
    docker:
      - image: cimg/base:stable
    parameters:
      server:
        description: |
          Server IP of of ArgoCD
        type: string
      username:
        description: |
          Username for ArgoCD
        type: string
      password:
        description: |
          Password for ArgoCD
        type: string
    steps:
      - run:
          name: Install ArgoCD CLI
          command: |
            URL=https://<< parameters.server >>/download/argocd-linux-amd64
            [ -w /usr/local/bin ] && SUDO="" || SUDO=sudo
            $SUDO curl --insecure -sSL -o /usr/local/bin/argocd $URL
            $SUDO chmod +x /usr/local/bin/argocd
      - run:
          name: ArgoCD CLI login
          command: argocd login << parameters.server >> --insecure --username << parameters.username >> --password << parameters.password >>
      - run:
          name: Manual sync
          command: argocd app sync $APP_NAME
      - run:
          name: Wait for application to reach a synced and healthy state
          command: argocd app wait $APP_NAME

  argocd-configure:
    executor: azure-aks/default
    parameters:
      cluster-name:
        description: |
          Name of the AKS cluster
        type: string
      resource-group:
        description: |
          Resource group that the cluster is in
        type: string
    steps:
      - checkout
      - run:
          name: Pull Updated code from repo
          command: git pull origin $CIRCLE_BRANCH
      - azure-aks/update-kubeconfig-with-credentials:
          cluster-name: << parameters.cluster-name >>
          install-kubectl: true
          perform-login: true
          resource-group: << parameters.resource-group >>
      - kubernetes/create-or-update-resource:
          resource-file-path: argocd/config.yaml

  bump-docker-tag-kustomize:
    docker:
      - image: cimg/base:stable
    steps:
      - run:
          name: Install kustomize
          command: |
            URL=https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.2/kustomize_v4.5.2_linux_amd64.tar.gz
            curl -L $URL | tar zx
            [ -w /usr/local/bin ] && SUDO="" || SUDO=sudo
            $SUDO chmod +x ./kustomize
            $SUDO mv ./kustomize /usr/local/bin
      - checkout
      - run:
          name: Bump Docker Tag
          command: |
            cd manifests
            kustomize edit set image $APP_NAME=$DOCKER_LOGIN/$APP_NAME:$CIRCLE_SHA1
      - add_ssh_keys:
          fingerprints:
            - "$SSH_FINGERPRINT"
      - run:
          name: Commit & Push to GitHub
          command: |
            git config user.email "$GITHUB_EMAIL"
            git config user.name "CircleCI User"
            git checkout $CIRCLE_BRANCH           
            git add manifests/kustomization.yaml
            git commit -am "Bumps docker tag [skip ci]"
            git push origin $CIRCLE_BRANCH

workflows:
  Deploy-App-on-AKS:
    jobs:
      - docker/publish:
          image: $DOCKER_LOGIN/$APP_NAME
          tag: $CIRCLE_SHA1,latest
      - bump-docker-tag-kustomize:
          requires:
            - docker/publish
      - argocd-configure:
          cluster-name: $CLUSTER_NAME
          resource-group: $RESOURCE_GROUP
          requires:
            - bump-docker-tag-kustomize
      # Paste the following only when you opt for the ArgoCD manual-sync-policy:
      - argocd-manual-sync:
          server: $ARGOCD_SERVER
          username: $ARGOCD_USERNAME
          password: $ARGOCD_PASSWORD
          requires:
            - argocd-configure
```

The CI workflow consists of three jobs:

*   `docker/publish` builds and pushes the container to Docker Hub.
    
*   `bump-docker-tag-kustomize` updates the Docker Image Tag and generates a consolidated Kubernetes configuration file.
    
*   `argocd-configure` applies the ArgoCD Configuration on the AKS cluster.
    
*   `argocd-manual-sync` is needed only when you will be opting for the manual sync policy. For automatic sync, you can omit this job from the file.
    

In this workflow, we have extensively used [CircleCI orbs](https://circleci.com/orbs/). Orbs are open-source, shareable packages of parameterizable, reusable configuration elements, including jobs, commands, and executors. The orbs have been used directly or are used in creating custom jobs.

Commit and [push](https://circleci.com/blog/pushing-a-project-to-github/) the changes to your GitHub repository.

## **Setting up the project on CircleCI**

The next step to deploying your application to AKS is connecting the application in your GitHub repository to CircleCI.

Go to your [CircleCI dashboard](https://app.circleci.com/) and select the Projects tab on the left panel. Click the **Set Up Project** button for the GitHub repository containing the code (`aks-nodejs-argocd`).

[![CircleCI Dashboard](https://production-cci-com.imgix.net/blog/media/2022-10-21-circleci-dashboard.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-circleci-dashboard.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

When prompted to select your config.yml file, click the **Fastest** option and type `main` the branch name. CircleCI will automatically locate the `config.yml` file. Click **Set Up Project**.

[![Select your config.yml file](https://production-cci-com.imgix.net/blog/media/2022-10-21-yaml-select.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-yaml-select.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

The workflow will run, but will soon display an `status` of `Failed`. This is because you need to set up a user key and configure the environment variables.

To set up the user key, go to Project Settings and click **SSH Keys** from the left panel. In the User Key section, click **Authorize with GitHub**. The user key is needed by CircleCI to push changes to your GitHub account on behalf of the repository owner during the execution of the workflow.

[![User key](https://production-cci-com.imgix.net/blog/media/2022-10-21-user-key.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-user-key.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

To configure the environment variables, click **Environment Variables**. Select the **Add Environment Variable** option. On the next screen, type the environment variable and the value you want to assign to it.

[![Environment Variables](https://production-cci-com.imgix.net/blog/media/2022-10-21-env-vars.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-env-vars.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

The environment variables used in the file are:

*   `APP_NAME` : Container Image Name (aks-nodejs-argocd)
    
*   `ARGOCD_PASSWORD` : ArgoCD portal password
    
*   `ARGOCD_SERVER` : ArgoCD Server IP Address
    
*   `ARGOCD_USERNAME` : ArgoCD portal username (admin)
    
*   `AZURE_PASSWORD` : Azure Account Password
    
*   `AZURE_USERNAME` : Azure Account Username
    
*   `CLUSTER_NAME` : AKS Cluster Name (NodeCluster)
    
*   `DOCKER_LOGIN` : Docker Hub Username
    
*   `DOCKER_PASSWORD` : Docker Hub Password
    
*   `GITHUB_EMAIL` : GitHub Account Email Address
    
*   `RESOURCE_GROUP` : AKS Resource Group (NodeRG)
    
*   `SSH_FINGERPRINT` : SSH Fingerprint of User Key used for pushing the updated Docker tag to GitHub
    

To locate the **SSH Fingerprint**, go to **Project Settings** and select **SSH Keys** from the sidebar. Scroll down to the **User Key** section and copy the key.

Re-run the workflow. This time the `status` will show `Success`.

[![Success workflow](https://production-cci-com.imgix.net/blog/media/2022-10-21-passed-workflow.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-passed-workflow.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

You will also find another pipeline having the `status` as `Not Run`. That is because you have explicitly instructed CircleCI to skip the pipeline by including `[skip ci]` it in the commit message. When CircleCI commits the updated configuration files to GitHub, `[skip ci]` prevents a self-triggering loop of the workflow.

## **Monitoring the application on ArgoCD Dashboard**

A `status` that shows `Success` when the workflow is re-run means that the application has been deployed on the AKS cluster.

To observe and monitor the resources that are currently running on the AKS Cluster, log in to the ArgoCD Web Portal.

Earlier in this tutorial, you learned how to fetch the ArgoCD Server IP, username, and password for logging in to the portal. After logging in, you will be on the Applications page.

[![ArgoCD Application](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-application-page.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-application-page.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

Click the application name. You will be redirected to a page with the tree view of all resources running on the AKS Cluster and their real-time status.

[![ArgoCD App Tree view](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-app-tree-view.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-argocd-app-tree-view.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

## **Accessing the application on AKS**

To access the application, you need the external IP address of the cluster. You can use the Azure CLI to find the `External-IP`.

Configure `kubectl` to connect to AKS using this command:

```bash
az aks get-credentials --resource-group NodeRG --name NodeCluster
```

You created all the resources in the `nodejs` namespace. To get all the resources in that namespace, use this command:

```bash
kubectl get all --namespace nodejs
```

Copy the `External-IP` corresponding to `service/nodejs`.

[![External-IP](https://production-cci-com.imgix.net/blog/media/2022-10-21-external-app-ip.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-external-app-ip.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

You can access the application at `http://<EXTERNAL-IP>`. In my case, that is `http://20.121.253.220/`.

[![Final application](https://production-cci-com.imgix.net/blog/media/2022-10-21-final-application.png?ixlib=rb-3.2.1&w=2000&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data align="left")](https://production-cci-com.imgix.net/blog/media/2022-10-21-final-application.png?ixlib=rb-3.2.1&auto=format&fit=max&q=60&ch=DPR%2CWidth%2CViewport-Width%2CSave-Data&fm=jpg)

## **Conclusion**

In this tutorial, you learned how to deploy your applications continuously on a Kubernetes cluster following GitOps practices with ArgoCD. This included configuring an automated CI pipeline. With the pipeline properly configured, any changes made to the application code are instantly updated on the application URL. Say goodbye to manually configure and deploying applications on Kubernetes.

As a bonus, you can change the values of the environment variables to use the CircleCI configuration file for similar applications.

The complete source code for this tutorial can also be found [here on GitHub](https://github.com/CIRCLECI-GWP/aks-nodejs-argocd).

* * *
