Setup Continuous Deployment for Application with Kubernetes and Argo CD

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. It follows the GitOps pattern of using git repositories as the source of truth for defining the desired application state. With ArgoCD, application deployments can be automated and updates to application can be made at the simple git commit events without the need of any complicated Continuous Integration and/or Deployment Pipelines.

This is our first post in the series of blog post on deploying and managing application with Kubernetes and Argo CD. You can find the series index here.

For the purpose of this post, we’ll create a multi node Kubernetes cluster with Kind (created with Docker and WSL2 backend) and learn how to deploy and manage Argo CD and our application along with it. However you can apply the content for any Kubernetes cluster you want.

Argo CD Overview and Architecture

Argo CD is implemented itself as a client-server application and consists of many different components such as api server, repository server, redis cache, metrics, dex server, etc:

Image with courtesy of https://argoproj.github.io/argo-cd/operator-manual/architecture/

As its evident from diagram, some of the important components are as below:

API Server: It is a gRPC/REST server which exposes the API consumed by other components. It is responsible for application management and status reporting, invoking of application operations (sync, rollback, etc.), listener and forwarder for the Git webhook events

Repository Server: It is an internal service responsible for maintaining a local cache of the git repository for application manifests

Application Controller: It is a Kubernetes controller which continuously monitors running applications and compares the current/live state against the desired state and takes any corrective actions.

For more details, please refer official documentation here.

Create Multi Node Kubernetes Cluster

In our previous posts at here and here, we covered the steps to leverage Kind to create multi node Kubernetes clusters in an easy way. Please refer to the same for more details.

We’ll need to use customize our cluster configuration to bind few ports to the Kubernetes services and pods created for the Argo CD itself and the application containers, so that we can try and test them out on our machine. For our purpose, we’ll use below configuration:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 31000
    hostPort: 31000
  - containerPort: 31001
    hostPort: 31001
  - containerPort: 31002
    hostPort: 31002
  - containerPort: 80
    hostPort: 80
    listenAddress: "127.0.0.1"
- role: worker
- role: worker

Let’s spin up a cluster with above configuration with kind create cluster and verify its running:

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kind create cluster --name kind-1a --config argo-cd-kind.yaml
Creating cluster "kind-1a" ...
 ✓ Ensuring node image (kindest/node:v1.20.2) đŸ–ŧ
 ✓ Preparing nodes đŸ“Ļ đŸ“Ļ đŸ“Ļ
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹ī¸
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind-1a"
You can now use your cluster with:

kubectl cluster-info --context kind-kind-1a

Thanks for using kind! 😊

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kubectl get nodes --context kind-kind-1a
NAME                    STATUS   ROLES                  AGE     VERSION
kind-1a-control-plane   Ready    control-plane,master   4m7s    v1.20.2
kind-1a-worker          Ready    <none>                 3m32s   v1.20.2
kind-1a-worker2         Ready    <none>                 3m32s   v1.20.2

Install Argo CD

Argo CD is available as a Kubernetes manifest itself and can be obtained from https://github.com/argoproj/argo-cd. So Argo CD can be installed using below two simple commands:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yamlnamespace/argocd created

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
serviceaccount/argocd-application-controller created
serviceaccount/argocd-dex-server created
serviceaccount/argocd-redis created
serviceaccount/argocd-server created
role.rbac.authorization.k8s.io/argocd-application-controller created
role.rbac.authorization.k8s.io/argocd-dex-server created
role.rbac.authorization.k8s.io/argocd-redis created
role.rbac.authorization.k8s.io/argocd-server created
clusterrole.rbac.authorization.k8s.io/argocd-application-controller created
clusterrole.rbac.authorization.k8s.io/argocd-server created
rolebinding.rbac.authorization.k8s.io/argocd-application-controller created
rolebinding.rbac.authorization.k8s.io/argocd-dex-server created
rolebinding.rbac.authorization.k8s.io/argocd-redis created
rolebinding.rbac.authorization.k8s.io/argocd-server created
clusterrolebinding.rbac.authorization.k8s.io/argocd-application-controller created
clusterrolebinding.rbac.authorization.k8s.io/argocd-server created
configmap/argocd-cm created
configmap/argocd-gpg-keys-cm created
configmap/argocd-rbac-cm created
configmap/argocd-ssh-known-hosts-cm created
configmap/argocd-tls-certs-cm created
secret/argocd-secret created
service/argocd-dex-server created
service/argocd-metrics created
service/argocd-redis created
service/argocd-repo-server created
service/argocd-server created
service/argocd-server-metrics created
deployment.apps/argocd-dex-server created
deployment.apps/argocd-redis created
deployment.apps/argocd-repo-server created
deployment.apps/argocd-server created
statefulset.apps/argocd-application-controller created

As we can see above, it creates a bunch of configuration including service accounts, services, configmaps, roles, etc. Once its installed, lets verify that all components are setup properly:

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kubectl get all -n argocd --context kind-kind-1a
NAME                                      READY   STATUS    RESTARTS   AGE
pod/argocd-application-controller-0       1/1     Running   0          6m15s
pod/argocd-dex-server-5dd657bd9-z8qln     1/1     Running   0          6m15s
pod/argocd-redis-759b6bc7f4-c2q97         1/1     Running   0          6m15s
pod/argocd-repo-server-6c495f858f-blxpj   1/1     Running   0          6m15s
pod/argocd-server-859b4b5578-s2zmx        1/1     Running   0          6m15s

NAME                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/argocd-dex-server       ClusterIP   10.96.114.168   <none>        5556/TCP,5557/TCP,5558/TCP   6m15s
service/argocd-metrics          ClusterIP   10.96.141.100   <none>        8082/TCP                     6m15s
service/argocd-redis            ClusterIP   10.96.116.179   <none>        6379/TCP                     6m15s
service/argocd-repo-server      ClusterIP   10.96.111.201   <none>        8081/TCP,8084/TCP            6m15s
service/argocd-server           ClusterIP   10.96.92.207    <none>        80/TCP,443/TCP               6m15s
service/argocd-server-metrics   ClusterIP   10.96.196.11    <none>        8083/TCP                     6m15s

NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argocd-dex-server    1/1     1            1           6m15s
deployment.apps/argocd-redis         1/1     1            1           6m15s
deployment.apps/argocd-repo-server   1/1     1            1           6m15s
deployment.apps/argocd-server        1/1     1            1           6m15s

NAME                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/argocd-dex-server-5dd657bd9     1         1         1       6m15s
replicaset.apps/argocd-redis-759b6bc7f4         1         1         1       6m15s
replicaset.apps/argocd-repo-server-6c495f858f   1         1         1       6m15s
replicaset.apps/argocd-server-859b4b5578        1         1         1       6m15s

NAME                                             READY   AGE
statefulset.apps/argocd-application-controller   1/1     6m15s

Connect and Login to the Argo CD User Interface

As we can see that argocd-server is installed as ClusterIP type service. So before we can access it outside the Kubernetes cluster, we need to edit the service type to either LoadBalancer or NodePort or use an ingress. In our case, we’ll change it to type NodePort.

At the same time, since we have setup Kubernetes cluster with kind, we need to edit service configuration to one of the exposed ports 31000 with extraPortMappings. This step is not required with a general setup of Kubernetes cluster. Lets edit our service configuration with kubectl edit svc -n argocd argocd-server to as shown:

With normal Kubernetes cluster, you can also use port-forwarding to connect with argocd-server without exposing it:

kubectl port-forward svc/argocd-server -n argocd 8080:443

and then connect it with localhost:8080

With that, we can now open up browser and access the web-interface with https://localhost:31000/. Since its using a self-signed cert, we’ll get a certification warning page. We can skip it and get to the interface:

By default, the username is admin. The initial password for the admin account is auto-generated and stored as clear text in the field password in a secret named argocd-initial-admin-secret in the Argo CD installation namespace. We can retrieve it with below command:

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

Using the user admin and the password retrieved, we can login to the Argo CD:

Connect and Login to the Argo CD CLI

For detailed instructions related to your operating system, you can read here. This is TL;DR version for linux based distributions:

# Get latest version available from github repo
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ export VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')

# Grab binaries with curl
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64


# Assign executable bit to binary
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ chmod +x argocd


# Move binary to /usr/local/bin
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ sudo mv argocd /usr/local/bin


# Verify binary is working
mohitgoyal@desktop:/mnt/d/mohit/src/kind$ argocd version
argocd: v2.0.0+f5119c0
  BuildDate: 2021-04-07T06:00:33Z
  GitCommit: f5119c06686399134b3f296d44445bcdbc778d42
  GitTreeState: clean
  GoVersion: go1.16
  Compiler: gc
  Platform: linux/amd64
FATA[0000] Argo CD server address unspecified

Once this is done, we can login with argocd login:

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ argocd login localhost:31000 --username admin --insecure
Password:
'admin:login' logged in successfully
Context 'localhost:31000' updated

Verifying Version Installed

In the left panel top, we can see version of the Argo CD, which in our case is v2.0.0+f5119c0. If we click on it, we would see a panel in right side of the screen with full version details:

Create an Application from a GitHub Repository

We’ll use a forked copy of the official Argo CD example repository to demonstrate how we can work with it and deploy a sample guestbook application. Our repository is located at https://github.com/goyalmohit/argocd-example-apps.

To start, click the + NEW APP button and fill the upcoming form. After that, we need to submit fields as below:

Application Name – This is the application name inside Argo CD. You can use Argo CD to manage multiple application at once. We’ll set it to guestbook-ui in our case.

Project – This is the project name inside Argo CD. Project can be used to segregate and group applications together. Since this is a new setup for Argo CD, a default project is created for us and we’ll select the same. If you have multiple projects together, you’ll see an auto-populated list and you can choose the same.

Sync Policy – You can choose to auto synchronize the state of application in the Kubernetes with the GitHub repository or you can set it to manual. There are many choices, and we’ll probably discuss it in more details later. For now, leave it as manual.

Argo CD has a thoughtful UI and using auto-population helps in removing manual errors and guesswork. We’ll see the same for many fields listed in various forms.

We now need to provide the GitHub repository details containing the application manifests, in the same form. Submit the details as below:

Repository URL – Provide the url for the GitHub repository containing the application manifests. We can add private as well as public repositories. For private repositories, you’ll need to provide additional details related to the authentication. Since our repository is a public one, we do not need to provide additional details.

Revision – You can choose to provide the specific branch or tag for github repo and sync the same state with Kubernetes details. We’ll leave it as HEAD so that it can get latest commits at various points in the time and compare them with the application state inside the Kubernetes cluster.

Path – This helps in further segregating application manifests inside the GitHub repository. We can ask it to read only specific directories in the repository and read manifests within that path.

Once we have provided source details where we described the desired state, we now need to provide the destination Kubernetes cluster details as below:

Cluster URL – Argo CD can be used to connect and deploy application to multiple Kubernetes clusters. Inside the UI, you’ll not get the option to add and connect a different Kubernetes cluster. This feature is restricted to Argo CD CLI. For our case, we’ll use the default in-cluster (where Argo CD itself is deployed).

Namespace – This can be used to select namespace where manifests will be deployed. You can choose a custom namespace and provide the same. Also, you’ll need to create the namespace on the target Kubernetes cluster before you can deploy manifests to it. Alternatively, you can select the checkbox for ‘AUTO-CREATE NAMESPACE’ in the sync options. We’ll leave it as default in our case.

Note that the namespace specified inside Kubernetes manifests overrides this value.

After filling out the information above, click Create at the top of the UI to create the guestbook-ui application. After this, it will read out all the parameters, read the source Kubernetes manifests. After this, it will go to OutOfSync state since the application has yet to be deployed, and no Kubernetes resources have been created.

Synchronize the Application Manifests / Deploy the Application

As we mentioned above, the application status is initially in OutOfSync state since the application has yet to be deployed, and no Kubernetes resources have been created. To sync (deploy) the application, we can choose the tile and then select SYNC. This will present us with a choice about what we want to synchronize:

For now, we’ll leave the default options and select synchronize button. After this, it will fetch latest state of HEAD before proceeding further and then starts applying the same:

Once the manifests are applied, we can review the application health and resources deployed:

Here we can lot of nice details such as health of resources, their revisions, when they were deployed, sync state etc. There is also 3 dots in front of each tile, which can be used to selectively sync/delete/manage the resource, or view logs, as applicable to the resource type.

We can also view the same details with kubectl:

mohitgoyal@desktop:/mnt/d/mohit/src/kind$ kubectl get all -n default
NAME                                READY   STATUS    RESTARTS   AGE
pod/guestbook-ui-85985d774c-69nrr   1/1     Running   0          6m54s

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/guestbook-ui   ClusterIP   10.96.163.151   <none>        80/TCP    6m55s
service/kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP   155m

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/guestbook-ui   1/1     1            1           6m55s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/guestbook-ui-85985d774c   1         1         1       6m55s

Access the Application outside Kubernetes Cluster

Since service type for guestbook-ui is set to type ClusterIP, we need to change it to either LoadBalancer or NodePort. Alternatively, we can use port-forwarding to access the application.

Also as our setup is with Kind cluster, we need to specify one of the ports i.e. 31001 from extraPortMappings. Lets edit service configuration with kubectl edit svc -n default guestbook-ui and save the changes.

Once its done, we can point the browser to localhost:31001 and view the application:

Note that since we have applied these changes directly on the Kubernetes cluster, and not from the github repo, Argo CD will complain about the same. However, that’s fine for now as it allows us to further discuss application management.

One thought on “Setup Continuous Deployment for Application with Kubernetes and Argo CD

Leave a Reply to Declarative GitOps Continuous Deployment for Application with Kubernetes, Argo CD, Helm, Kustomize and Kind – Index – mohitgoyal.co Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s