Aug 19, 2024
Get featured on Geeklore.io
Geeklore is in very early stages of development. If you want to help us grow, consider letting us sponsor you by featuring your tool / platform / company here instead of this text. 😊
DigitalOcean is a cloud service provider, just like AWS. The difference is that it is more affordable, and less complex ( due to having less services available at the moment ).
My terraform configuration is organized into three files:
Terraform/
|-- main.tf
|-- container_registry_cluster.tf
|-- variables.tf
!Note you need to allow K8S to access the Container registry in order to pull images from it later on. This can be done via the following command
doctl registry kubernetes-manifest registry.digitalocean.com/$CONTAINER_REG_NAME | kubectl apply -f -
// main.tf
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
data "digitalocean_ssh_key" "terraform" {
name = "ssh_main_key"
}
// container_registry_cluster.tf
resource "digitalocean_container_registry" "container_reg" {
name = "container-registry-test191202"
subscription_tier_slug = "starter"
region = "ams3"
}
resource "digitalocean_kubernetes_cluster" "cluster_test" {
name = "test-cluster"
region = "ams3"
version = "1.30.2-do.0"
node_pool {
name = "worker-pool"
size = "s-2vcpu-2gb"
node_count = 1
}
}
// variables.tf
variable "do_token" {
type = string
description = "DO personal access token"
default = "ENTER_YOUR_PERSONAL_TOKEN_HERE"
}
Note! Anyone with the token will be able to access your account! Having the token exposed like that is bad practice. I only did it for simplicity, and ease of use. My TF files will not be committed to GitHub.
Nginx Ingress Controller allows you to manage HTTP and HTTPS routing for your services within the cluster. By using a single Load Balancer, you can route traffic to different services based on rules defined in the ingress.
Otherwise, without an Ingress you would need a Load Balancer for each service that needs to be accessed by clients. This is insufficient.
Deploying the Nginx is quite simple with the use of Helm:
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
We also need to configure the ingress rules, based on which it will forward the traffic within the K8S cluster.
// node-express-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: node-express-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- djingle.xyz
secretName: nodeapp-tls
ingressClassName: nginx
rules:
- host: djingle.xyz
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: node-express-app-service
port:
number: 80
This configuration allows Nginx to route traffic based on the djingle.xyz domain to the node-express-app-service.
Cert Manager makes our life easier by automating the management and issuance of TLS certificates. This is important for securing our application with HTTPS.
To deploy Cert Manager, use the following commands:
helm repo add jetstack https://charts.jetstack.io --force-update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.15.3 \
--set crds.enabled=true
After deployment, we need to configure the Issuer and Certificate
All cert-manager certificates require a referenced issuer that is in a ready condition to attempt to honor the request. An Issuer defines how certificates should be generated by specifying the certificate authority (CA) and how to verify ownership of a domain (e.g., through ACME for Let's Encrypt as we did in our case).
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-issuer
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: [email protected]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: letsecnrypt-issuer-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
ingressClassName: nginx
A Certificate resource requests a TLS certificate from a specified Issuer and stores the resulting certificate in a Kubernetes Secret. It specifies the domain names to secure and the Issuer to use.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: node-app-cert
spec:
secretName: nodeapp-tls
dnsNames:
- *INSERT_DNS_NAME*
issuerRef:
name: letsencrypt-issuer
# We can reference ClusterIssuers by changing the kind here.
# The default value is Issuer (i.e. a locally namespaced Issuer)
kind: ClusterIssuer
# This is optional since cert-manager will default to this value however
# if you are using an external issuer, change this to that issuer group.
Before I get on with the deployment, here are a few words on ArgoCD and what is so fascinating about it.
A traditional CI/CD pipeline typically follows these steps: First, the code is committed to the repository, triggering automated tests. Once the tests pass, the application is deployed. Next, a Docker image is built, and the corresponding YAML manifest for deployment is updated. Finally, Jenkins (or a similar CI/CD tool) accesses the Kubernetes cluster and applies the new changes using kubectl apply. (This is a simplified overview of a common CI/CD workflow.)
This is not practical for a number of reasons:
Security is compromised since we give an external agent 'Jenkins' access credentials to the cluster.
Even then, then when the new manifest is applied Jenkins has no idea of knowing if everything is still running without any discrepancies. The whole cluster might be down, and Jenkins would not even know a thing.
One of the cool things with ArgoCD is that it's directly deployed in the cluster. Contrary to Jenkins, it has visibility over all the components, and their status.
Install ArgoCD with these commands:
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
!! Note that this only deployes the ArgoCD within the cluster !! It is not yet working properly, as we still need to configure it which will be done later on in step 7.
Before we create the application, we first need to initialize npm, and then install express.
NPM stands for 'Node Package Manager' think of it like a tool which we use to install different dependancies ( libraries ) in order to make our life easier.
In order to initiate npm, and install express as a dependency, we need to run the following commands:
npm init -y
npm install express --save
Creating the nodeJS application is quite simple, as it consists of a few lines of code.
// app.js
import express from 'express'
const app = express()
app.get("/", async (req, res) => {
res.send("Hello World!")
})
app.listen(8080, () => {
console.log('Server is running on port 8080!')
})
In this case, for the workflow I will use GitHub Actions. Github Actions are a set of commands that are executed when a commit is made to a repository.
To configure such actions, we need to create the following directory in the root of the repo -> ./github/workflows
Inside the newly created directory, I have created a 'build.yaml' file which contains the following operations:
// build.yaml
name: Build And Push Docker Container
on:
push:
branches:
- main
jobs:
build_and_push:
runs-on: ubuntu-latest
steps:
-
name: Checkout the repo
uses: actions/checkout@v4
-
name: Build image
run: docker build -t node-express.js .
-
name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
-
name: Log in to DO Container Registry
run: doctl registry login --expiry-seconds 600
-
name: Tag image
run: docker tag node-express.js registry.digitalocean.com/${{ vars.CONTAINER_REGISTRY_NAME }}/node-express.js
-
name: Push image
run: docker push registry.digitalocean.com/${{ vars.CONTAINER_REGISTRY_NAME }}/node-express.js:latest
All this does is build an image and then push it to the Digital Ocean Container Registry.
We need to present the Access Token, which in this case is done via a secret.
For reference my K8S repo for this project can be accessed HERE
The final step is to deploy the app using ArgoCD. The application.yaml manifest instructs ArgoCD to monitor a specific Git repository for changes.
Thats it! From this point on, ArgoCD starts listening to the repo for any changes, and configures the cluster effectively syncing it to the state provided in the GitHub repository.
// application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-argo-application
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/Fedd911/k8S-manifest-argoCD
targetRevision: HEAD
path: dev
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
selfHeal: true
prune: true
Key configurations in the manifest:
ArgoCD has a cool UI which can be accessed locally, by port forwarding the service to a local port.
In order to execute that, we need to run the following command:
kubectl port-forward svc/argocd-server -n argocd 9090:443
Since we are forwarding from port 9090, we need to access the app via that port: http://localhost:9090/
Once presented with the login window, you need to use the following credentials:
Username: admin (by default)
The password is stored locally in a secret on the cluster.
In order to access it we need to run the following command
kubectl get secret argocd-initial-admin-secret -n argocd -o yaml
// This extracts the secret in a .yaml format.
Then the password needs to be decoded.
example password: cGFzc3dvcmQ=
// Take the value of the password and decode it:
echo cGFzc3dvcmQ= | base64 --decode
The output is the password which should be used to login into the ArgoCD UI.
If your cluster is unhealthy or the application is down, you will be able to see it directly in the WebUI.
I had a lot of fun working on this project. It gave me hands-on experience with ArgoCD, Cert Manager, and Nginx Ingress Controller.
The project itself is quite simple. Although this barely scrapes the surface, it is more than sufficient to demonstrate the power of GitOps and the benefits of working with these tools.
ArgoCD Tutorial for Beginners | HERE
Nginx Ingress Controller and Cert Manager setup 2024 | HERE
Getting started with Kustomize | HERE
More From This User
Latest Comments
this post was amazing!
good job on the amazing explanation and wide area of technologies
Thank you for your support!