This tutorial shows how to install K3s with nginx, cert manager and ArgoCD
- K3s
- Lightweight, easy to install Kubernetes distribution, Docs: https://k3s.io/
- Use latest Debian/Ubuntu Image as base
Basics (K3s, K9s, Helm)
-
☸️ K3s (without traefik)
K3s comes with traefik per default, but we want to use nginx ingress
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable=traefik" sh # needed for K9s and helm export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
-
🔧 K9s (fany K8s management tool)
curl -sS https://webinstall.dev/k9s | bash source ~/.config/envman/PATH.env
-
🪖 Helm (K8s package manager)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Nginx Ingress & Cert Manager
-
🔒 Cert Manager (K8s add-on automates management & renewal of TLS certificates)
https://cert-manager.io/docs/installation/ (find latest version)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.1/cert-manager.yaml
-
🔒 Cluster Issuer (for Cert Manager)
# letsencrypt-prod.yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: # The ACME server URL server: https://acme-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: **your@mail.com** # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-prod # Enable the HTTP-01 challenge provider solvers: - http01: ingress: ingressClassName: nginx
kubectl apply -f letsencrypt-prod.yaml
-
🚪 Nginx Ingress (manages external access to K8s services)
https://kubernetes.github.io/ingress-nginx/deploy/
The last line sets Nginx as default ingress class (different to installation guide from official docs)
helm upgrade --install ingress-nginx ingress-nginx \ --repo https://kubernetes.github.io/ingress-nginx \ --namespace ingress-nginx --create-namespace \ **--set controller.ingressClassResource.default=true**
-
Output with example ingress yaml
Release "ingress-nginx" does not exist. Installing it now. NAME: ingress-nginx LAST DEPLOYED: Sat Nov 16 18:06:51 2024 NAMESPACE: ingress-nginx STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The ingress-nginx controller has been installed. It may take a few minutes for the load balancer IP to be available. You can watch the status by running '**kubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch**' An example Ingress that makes use of the controller: **apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example namespace: foo spec: ingressClassName: nginx rules: - host: www.example.com http: paths: - pathType: Prefix backend: service: name: exampleService port: number: 80 path: / # This section is only required if TLS is to be enabled for the Ingress tls: - hosts: - www.example.com secretName: example-tls** If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: **apiVersion: v1 kind: Secret metadata: name: example-tls namespace: foo data: tls.crt: <base64 encoded cert> tls.key: <base64 encoded key> type: kubernetes.io/tls**
-
Argo
-
🐙 ArgoCD (GitOps-based Continuous Delivery tool)
kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
-
🔑 Argo initial admin password
The initial admin password is stored in a secret, we need to get it and decode with base64. The default username is
admin
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
You can also use K9s to get the password. Select all namespaces or the argocd namespace to see it.
-
🕸️ Argo Ingress (to access the argo web interface)
argo-ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd namespace: argocd annotations: cert-manager.io/cluster-issuer: **letsencrypt-prod** spec: ingressClassName: nginx rules: - host: **argocd.your-domain.io** http: paths: - pathType: Prefix backend: service: name: argocd-server port: number: 80 path: / tls: - hosts: - **argocd.your-domain.io** secretName: argocd-tls
kubectl apply -f argo-ingress.yaml
Check status of Certificate Request
Let’s check the progress of the Letsencrypt ACME challenge. Use K9s or this command:
kubectl get certificaterequests -A -o wide
NAMESPACE NAME APPROVED DENIED READY ISSUER REQUESTER STATUS AGE argocd argocd-tls-1 True True letsencrypt-prod system:serviceaccount:cert-manager:cert-manager **Certificate fetched from issuer successfully** 20m
ERR_TOO_MANY_REDIRECTS
You try to access the page and get an
ERR_TOO_MANY_REDIRECTS
error. What happened?If you search the web, you will quickly find the solution: https://github.com/argoproj/argo-cd/issues/2953#issuecomment-602898868
We have to set the insecure config to true and restart the deployment
kubectl patch configmap argocd-cmd-params-cm -n argocd --type=merge -p '{"data":{"server.insecure":"true"}}' kubectl rollout restart deployment argocd-server -n argocd # it will take some time, maybe run the restart command again
Access the page
Now you can access the page with the domain you specified
-
GitLab/GitHub Repo anlegen
brew install argocd argocd login argocd.your-domain.com
~/.bashrc
-
🐚 Shell
To enable autocompletion for the
kubectl
command, usek
as alias and set theKUBECONFIG
environment variable (for K9s and helm)nano ~/.bashrc
# Enable bash completion (remove comments) if [ -f /etc/bash_completion ] && ! shopt -oq posix; then . /etc/bash_completion fi # Kubectl (completion, k alias) source <(kubectl completion bash) alias k=kubectl complete -F __start_kubectl k # Helm export KUBECONFIG=/etc/rancher/k3s/k3s.yaml source <(helm completion bash)
Deploy Hello World App (without ArgoCD)
You need a domain for the ingress. Just point one to the external IP of the VM. If you don’t have a domain, you can use https://nip.io/
-
hello-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: hello-world labels: app: hello-world spec: replicas: 3 selector: matchLabels: app: hello-world template: metadata: labels: app: hello-world spec: containers: - name: hello-world image: nginx:latest ports: - containerPort: 80
-
hello-service.yaml
apiVersion: v1 kind: Service metadata: name: hello-world-service spec: selector: app: hello-world ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIP
-
hello-ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hello-world-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: hi.example.com # Replace with your domain http: paths: - path: / pathType: Prefix backend: service: name: hello-world-service port: number: 80
-
kubectl apply all the yaml files
# all files in folder kubectl apply -f ./ # or one by one kubectl apply -f hello-deployment.yaml
Note: In the ingress yaml is no
ingressClassName
specified, so it will use the default ingress class. To implecitly set the ingress class, use this ingress.yaml:-
hello-ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hello-world-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: **ingressClassName: nginx # Explicitly specify nginx IngressClass** rules: - host: hi.example.com http: paths: - path: / pathType: Prefix backend: service: name: hello-world-service port: number: 80
kubectl apply -f hello-ingress.yaml
-
You can now visit the Nginx Hello World Page via HTTP e.g. with curl
curl -v hi.**you-domain.com**
# ...
# <!DOCTYPE html>
# <html>
# <head>
# <title>**Welcome to nginx**!</title>
# ...