Jérôme Decoster

Jérôme Decoster

3x AWS Certified - Architect, Developer, Cloud Practionner

17 Feb 2022

ArgoCD + Minikube + Ngrok + Github Webhook

The Goal
  • Create a Kubernetes cluster with Minikube
  • Create a private Git repository on Github
  • Install Argo CD in Kubernetes cluster
  • Deploy our application using Argo CD
  • Create a tunnel with Ngrok to make our local cluster accessible from Github
  • Create webhook notification between Github and Argo CD
  • See the almost instantaneous update of our Kubernetes application after another Git push

    architecture.svg

    Setup the project

    Get the code from this github repository :

    download-repo.png

    Update the source / repoURL with your github username :

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: argocd-app
      namespace: argocd
    spec:
      project: default
    
      source:
    -    repoURL: git@github.com:jeromedecoster/argocd-test.git
    +    repoURL: git@github.com:<YOUR_USERNAME>/argocd-test.git
        targetRevision: HEAD
        path: infra
      destination: 
        server: https://kubernetes.default.svc
        namespace: my-app
    
      syncPolicy:
        syncOptions:
        - CreateNamespace=true
    
        automated:
          selfHeal: true
          prune: true
    

    Now create a new Github repository that matches the address you defined above :

    git@github.com:<YOUR_USERNAME>/argocd-test

    Make sure this project is private to best match a real project.

    create-private-repo.png

    Commit your project on Github :

    $ git add .
    $ git commit -m :boom:
    $ git push -u origin master
    

    Install and start Minikube

    Minikube lets you quickly create a Kubernetes cluster for local testing.

    To install or update it you can follow the instructions on this page.

    If you are on Linux like me you can run this one-line install command :

    # one-line command to install minikube on Linux
    $ wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \
        --quiet \
        --show-progress \
        --output-document=/tmp/minikube-linux-amd64 \
        && sudo install /tmp/minikube-linux-amd64 /usr/local/bin/minikube
    

    Kubectl is a cli tool to manage your kubernetes cluster.

    You also need to install or update it to match the version of Minikube.

    If you are on Linux like me you can run this one-line install command :

    # one-line command to install kubectl on Linux
    $ LATEST=$(wget https://storage.googleapis.com/kubernetes-release/release/stable.txt \
        --quiet \
        --output-document=-) \
        && wget https://storage.googleapis.com/kubernetes-release/release/$LATEST/bin/linux/amd64/kubectl \
        --quiet \
        --show-progress \
        --output-document=/tmp/kubectl \
        && chmod +x /tmp/kubectl \
        && sudo mv /tmp/kubectl /usr/local/bin/kubectl
    

    We are now starting Minikube :

    $ minikube start
    

    After a fairly long time to set up, our cluster is working :

    # check the kubectl current context
    $ kubectl config current-context 
    minikube
    
    # ping the cluster by listing the namespaces
    $ kubectl get ns
    NAME              STATUS   AGE
    default           Active   2m00s
    kube-node-lease   Active   2m00s
    kube-public       Active   2m00s
    kube-system       Active   2m00s
    

    install Argo CD in our Kubernetes cluster

    Argo CD is wonderful software that uses GitOps practice to perform continuous deployment specifically for Kubernetes.

    Argo CD is installed within our kubernetes cluster, in a specific namespace.

    Argo CD is a pull-based deployment tool. It watches a remote Git repository for new or updated manifest files and synchronizes those changes with the cluster.

    To install Argo CD we execute this command :

    # create the namespace
    $ kubectl create namespace argocd
    
    # apply the installation manifest
    $ kubectl apply \
        --namespace argocd \
        --filename https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    

    You can browse the install manifest online :

    It is an impressive manifest. More than 10500 lines !

    Let’s wait for all services and pods to be perfectly launched :

    # wait for everything to be launched and running
    $ watch kubectl get all --namespace argocd
    NAME                                     READY   STATUS    RESTARTS   AGE
    pod/argocd-application-controller-0      1/1     Running   0          100s
    pod/argocd-dex-server-59d44b9f99-8kp7d   1/1     Running   0          100s
    pod/argocd-redis-79bdbdf78f-247q5        1/1     Running   0          100s
    pod/argocd-repo-server-b6f8cdc6f-4m8h5   1/1     Running   0          100s
    pod/argocd-server-bdc697879-rdk89        1/1     Running   0          100s
    
    NAME                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
    service/argocd-dex-server       ClusterIP   10.100.46.74    <none>        5556/TCP,5557/TCP,5558/TCP   100s
    service/argocd-metrics          ClusterIP   10.98.170.62    <none>        8082/TCP                     100s
    service/argocd-redis            ClusterIP   10.102.83.160   <none>        6379/TCP                     100s
    service/argocd-repo-server      ClusterIP   10.96.100.93    <none>        8081/TCP,8084/TCP            100s
    service/argocd-server           ClusterIP   10.96.151.61    <none>        80/TCP,443/TCP               100s
    service/argocd-server-metrics   ClusterIP   10.102.166.2    <none>        8083/TCP                     100s
    
    NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/argocd-dex-server    1/1     1            1           100s
    deployment.apps/argocd-redis         1/1     1            1           100s
    deployment.apps/argocd-repo-server   1/1     1            1           100s
    deployment.apps/argocd-server        1/1     1            1           100s
    
    NAME                                           DESIRED   CURRENT   READY   AGE
    replicaset.apps/argocd-dex-server-59d44b9f99   1         1         1       100s
    replicaset.apps/argocd-redis-79bdbdf78f        1         1         1       100s
    replicaset.apps/argocd-repo-server-b6f8cdc6f   1         1         1       100s
    replicaset.apps/argocd-server-bdc697879        1         1         1       100s
    
    NAME                                             READY   AGE
    statefulset.apps/argocd-application-controller   1/1     100s
    

    To be able to access the web interface of Argo CD we use this port-forward command :

    $ kubectl port-forward \
        --namespace argocd \
        svc/argocd-server 8000:443
    Forwarding from 127.0.0.1:8000 -> 8080
    Forwarding from [::1]:8000 -> 8080
    

    You can now open https://localhost:8000 and accept the insecure certificate :

    argocd-welcome.png

    To be able to login we need to get the admin password like this :

    # get admin password
    $ kubectl get secret argocd-initial-admin-secret \
        --namespace argocd \
        --output jsonpath="{.data.password}" \
        | base64 --decode \
        && echo
    

    We connect :

    argocd-signin.png

    We are connected :

    argocd-logged.png

    Connect to a private repository

    Here are the steps to get Argo CD to connect to a private repository

    argocd-connect-repo-01.png

    argocd-connect-repo-02.png

    argocd-connect-repo-03.png

    We will create an SSH key for our project.

    I create a key of type ed25519 using the .pem extension

    $ ssh-keygen -t ed25519 -f ~/.ssh/argocd-test.pem
    Enter passphrase (empty for no passphrase): # <press-enter>
    Enter same passphrase again: # <press-enter>
    Your identification has been saved in /home/xxxxx/.ssh/argocd-test.pem
    Your public key has been saved in /home/xxxxx/.ssh/argocd-test.pem.pub
    

    I rename the generated public file argocd-test.pem.pub to argocd-test.pub

    $ mv ~/.ssh/argocd-test.pem.pub ~/.ssh/argocd-test.pub
    

    Now I display my public key so I can copy-paste it :

    $ cat ~/.ssh/argocd-test.pub
    ssh-ed25519 AAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx username@computer
    

    I will now add this key in my main settings on Github :

    github-add-key-01.png

    I add it :

    github-add-key-02.png

    github-add-key-03.png

    Now I display my private key so I can copy-paste it :

    $ cat ~/.ssh/argocd-test.pem
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3Blxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxQFBgc=
    -----END OPENSSH PRIVATE KEY-----
    

    argocd-connect-repo-04.png

    argocd-connect-repo-05.png

    Deploying the application in Argo CD

    To create an application in Argo CD, we use a manifest of type Application :

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: argocd-app
      namespace: argocd
    spec:
      project: default
    
      source:
        repoURL: git@github.com:jeromedecoster/argocd-test.git
        targetRevision: HEAD
        path: k8s
      destination: 
        server: https://kubernetes.default.svc
        namespace: my-app
    
      syncPolicy:
        syncOptions:
        - CreateNamespace=true
    
        automated:
          selfHeal: true
          prune: true
    

    You can consult the documentation about the options of Automated Sync Policy

    This manifest uses the Kubernetes Custom Resource Definition (CRD). This extends the functionality of Kubernetes.

    We create a resource dedicated to Argo CD by defining it by these 2 properties :

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    

    Custom Resource Definitions are one of the great strengths and flexibility of Kubernetes.

    If you are curious you can see here the YAML code allowing to create Application type manifests :

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      labels:
        app.kubernetes.io/name: applications.argoproj.io
        app.kubernetes.io/part-of: argocd
      name: applications.argoproj.io
    spec:
      group: argoproj.io
      names:
        kind: Application
        listKind: ApplicationList
        plural: applications
        shortNames:
        - app
        - apps
        singular: application
      # ...
    

    To declare our application we execute this command within our project directory :

    $ kubectl apply --filename argocd-app.yaml
    

    argocd-app-01.png

    argocd-app-02.png

    Access our application through the browser

    We list the services running in our Minikube cluster :

    $ minikube service list
    |-------------|-----------------------|--------------|---------------------------|
    |  NAMESPACE  |         NAME          | TARGET PORT  |            URL            |
    |-------------|-----------------------|--------------|---------------------------|
    | argocd      | argocd-dex-server     | No node port |                           |
    | argocd      | argocd-metrics        | No node port |                           |
    | argocd      | argocd-redis          | No node port |                           |
    | argocd      | argocd-repo-server    | No node port |                           |
    | argocd      | argocd-server         | No node port |                           |
    | argocd      | argocd-server-metrics | No node port |                           |
    | default     | kubernetes            | No node port |                           |
    | kube-system | kube-dns              | No node port |                           |
    | my-app      | my-app-svc            |         9000 | http://192.168.49.2:31000 |
    |-------------|-----------------------|--------------|---------------------------|
    

    Another way to display our service URL :

    $ minikube service --url my-app-svc --namespace my-app
    http://192.168.49.2:31000
    

    In a new terminal window you can run this command :

    $ watch curl --silent $(minikube service --url my-app-svc --namespace my-app)
    

    Displays the result of the curl call every 2 seconds :

    Every 2,0s: curl --silent http://192.168.492:31000
    
    version 1-0-0
    

    Our application use http-echo :

      # ...
      spec:
        containers:
        - name: my-app
          image: hashicorp/http-echo
          args:
          - "-text=version 1-1-0"
          ports:
          - containerPort: 5678
    

    http-echo is a simple but useful tool to simply display text content through an HTTP server.

    # let's do a little test ...
    $ docker pull hashicorp/http-echo
    
    $ docker run -p 5000:5678 \
        hashicorp/http-echo \
        -text="Hello..."
    
    # in another terminal window ...
    $ curl localhost:5000
    Hello...
    

    And our message is simply version 1-0-0

    Creating a tunnel with Ngork

    By default Argo CD checks for changes in the Git repository every 3 minutes.

    If we want the deployment of changes to be almost instantaneous, we must set up a Github webhook.

    The webhook system is push based.

    In order for Github.com to send an event to our cluster in localhost, we will create a tunnel using ngrok.

    ngrok.png

    Once the application is installed and authenticated, we create our tunnel very simply :

    $ ngrok http https://localhost:8000
    

    We get some URLs :

    Web Interface                 http://127.0.0.1:4040
    Forwarding                    http://aaaa-bbb-ccc-dd-eee-fff-ggg-hhhh-iiii.ngrok.io -> https://localhost:8000                                     
    Forwarding                    https://aaaa-bbb-ccc-dd-eee-fff-ggg-hhhh-iiii.ngrok.io -> https://localhost:8000 
    

    By copy-pasting the https address obtained, we can access out Argo CD interface from an online URL. It is magic !

    Creation of the webhook

    In the Webhooks tab of our Github repository :

    webhook-add-01.png

    As written in the documentation, we need to :

    • Add the path : /api/webhook
    • Choose the content type : application/json

    So we have somethis like :

    https://aaaa-bbb-ccc-dd-eee-fff-ggg-hhhh-iiii.ngrok.io/api/webhook

    webhook-add-02.png

    It has been added. It is still gray because it has not been used yet :

    webhook-add-03.png

    I edit the deployment file directly on Github.

    I modify the version of our site :

          - name: my-app
            image: hashicorp/http-echo
            args:
    -       - "-text=version 1-0-0"
    +       - "-text=version 1-1-0"
            ports:
            - containerPort: 5678
    

    github-commit.png

    I notice that the synchronization starts immediately in Argo CD :

    argocd-sync-01.png

    argocd-sync-02.png

    argocd-sync-03.png

    And after a few seconds my terminal window shows :

    Every 2,0s: curl --silent http://192.168.492:31000
    
    version 1-1-0
    

    The web hook worked fine, it is no longer grey :

    webhook-green.png

    Our demo is over, we can delete our cluster :

    $ minikube stop