K8s - DigitalOcean

Page content

Deploy Sampleapp on Kuberentes …

Prerequisite

  • Domain “kubbi.xyz”, ns1.digitalocean.com, ns2, ns3 …
  • Digital Ocean Login

Doku

https://docs.digitalocean.com/products/kubernetes/getting-started/operational-readiness/

Build Kubbi Cluster

Build Cluster with WebGUI or CLI

via CLI

time doctl kubernetes cluster create prod001 --region fra1 --node-pool "size=s-2vcpu-2gb;auto-scale=true;min-nodes=3;max-nodes=5"
  • FRA1
  • 3 Nodes
  • 2 CPU
  • 2 GB RAM
  • 60 GB Disk
  • Costs: 54 USD/Mt!

Connecting and managing this cluster

doctl kubernetes cluster kubeconfig save 4375b470-ebe8-4ccb-925a-345df364dfbd
user@mac % doctl kubernetes cluster kubeconfig save 4375b470-ebe8-4ccb-925a-345df364dfbd


Notice: Adding cluster credentials to kubeconfig file found in "/Users/user/.kube/config"
Notice: Setting current-context to do-fra1-k8s-1-28-2-do-0-fra1-1702031438694

kubectl config get-contexts

kubectl config get-contexts
user@mac % kubectl config get-contexts

CURRENT   NAME                                         CLUSTER                                      AUTHINFO                                           NAMESPACE
*         do-fra1-prod-cluster-01                      do-fra1-prod-cluster-01                      do-fra1-prod-cluster-01-admin

kubectl cluster-info

kubectl cluster-info
user@mac % kubectl cluster-info

Kubernetes control plane is running at https://f179692f-aeac-4f9c-af3b-2422897ea578.k8s.ondigitalocean.com
CoreDNS is running at https://f179692f-aeac-4f9c-af3b-2422897ea578.k8s.ondigitalocean.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

kubectl version

kubectl version
user@mac % kubectl version

Client Version: v1.28.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.2

kubectl get nodes

kubectl get nodes
user@mac % kubectl get nodes

NAME                    STATUS   ROLES    AGE     VERSION
prod-cluster-01-xa856   Ready    <none>   3m59s   v1.28.2
prod-cluster-01-xa85a   Ready    <none>   4m7s    v1.28.2

Install Nginx Ingress Controller

  • via GUI / Marketplace / NGINX Ingress Controller

show lbl status

doctl compute load-balancer list --format IP,ID,Name,Status
user@mac % doctl compute load-balancer list --format IP,ID,Name,Status

IP    ID                                      Name                                Status
      55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    new

no ip yet … wait …

while true; do doctl compute load-balancer list --format IP,ID,Name,Status; sleep 10; done

after a few seconds …

user@mac % while true; do doctl compute load-balancer list --format IP,ID,Name,Status; sleep 10; done

IP    ID                                      Name                                Status
      55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    new
IP    ID                                      Name                                Status
      55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    new
IP    ID                                      Name                                Status
      55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    new
IP    ID                                      Name                                Status
      55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    new
IP             ID                                      Name                                Status
64.225.93.9    55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    active
IP             ID                                      Name                                Status
64.225.93.9    55d7381d-9bcd-4f8f-905e-e90efd09f03e    a6554a9aff1a340e986d48431e19cca9    active

Create Backend Application

kubectl create ns backend
user@mac % kubectl create ns backend

namespace/backend created

Create the Deployment and apply

cat << 'EOF' > echo_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo
  namespace: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: echo
    spec:
      containers:
        - name: echo
          image: jmalloc/echo-server
          ports:
            - name: http
              containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 50Mi
            limits:
              cpu: 200m
              memory: 100Mi
EOF
kubectl apply -f echo_deployment.yaml
deployment.apps/echo created

Create the Service and Apply

cat << 'EOF' > echo_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo
  namespace: backend
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: echo
EOF
kubectl apply -f echo_Service.yaml
service/echo created

Verify Deployment

kubectl get deployments -n backend
user@mac % kubectl get deployments -n backend

NAME   READY   UP-TO-DATE   AVAILABLE   AGE
echo   1/1     1            1           81s

Verify Services

kubectl get services -n backend
user@mac % kubectl get services -n backend

NAME   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
echo   ClusterIP   10.245.228.215   <none>        80/TCP    43s

Configure DNS

doctl compute domain records list kubbi.xyz
user@mac % doctl compute domain records list kubbi.xyz

ID            Type    Name    Data                    Priority    Port    TTL     Weight
1715497047    SOA     @       1800                    0           0       1800    0
1715497048    NS      @       ns1.digitalocean.com    0           0       1800    0
1715497049    NS      @       ns2.digitalocean.com    0           0       1800    0
1715497050    NS      @       ns3.digitalocean.com    0           0       1800    0

add Wildcard Pointing to the Cluster

lbip="64.225.93.9"
doctl compute domain records create "kubbi.xyz" --record-type A --record-data ${lbip} --record-ttl 60 --record-name "*"

confirm Wildcard Record

user@mac % doctl compute domain records list kubbi.xyz

ID            Type    Name    Data                    Priority    Port    TTL     Weight
1715497047    SOA     @       1800                    0           0       1800    0
1715497048    NS      @       ns1.digitalocean.com    0           0       1800    0
1715497049    NS      @       ns2.digitalocean.com    0           0       1800    0
1715497050    NS      @       ns3.digitalocean.com    0           0       1800    0
1715597396    A       *       64.225.93.9             0           0       60      0

Install Cert Manager

via GUI / Marketplace / Cert-Manager

Check Cert Manager

kubectl get all -n cert-manager
user@mac % kubectl get all -n cert-manager

NAME                                           READY   STATUS    RESTARTS   AGE
pod/cert-manager-7b9cbf5b8d-p2bwm              1/1     Running   0          13s
pod/cert-manager-cainjector-6d5f558c69-m79p9   1/1     Running   0          13s
pod/cert-manager-webhook-bd76f6cf9-48bwv       1/1     Running   0          13s

NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/cert-manager-webhook   ClusterIP   10.245.93.140   <none>        443/TCP   13s

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cert-manager              1/1     1            1           13s
deployment.apps/cert-manager-cainjector   1/1     1            1           13s
deployment.apps/cert-manager-webhook      1/1     1            1           13s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/cert-manager-7b9cbf5b8d              1         1         1       13s
replicaset.apps/cert-manager-cainjector-6d5f558c69   1         1         1       13s
replicaset.apps/cert-manager-webhook-bd76f6cf9       1         1         1       13s

Inspect the Cert Manager

kubectl get crd -l app.kubernetes.io/name=cert-manager
user@mac % kubectl get crd -l app.kubernetes.io/name=cert-manager

NAME                                  CREATED AT
certificaterequests.cert-manager.io   2023-12-08T06:18:47Z
certificates.cert-manager.io          2023-12-08T06:18:47Z
challenges.acme.cert-manager.io       2023-12-08T06:18:47Z
clusterissuers.cert-manager.io        2023-12-08T06:18:48Z
issuers.cert-manager.io               2023-12-08T06:18:47Z
orders.acme.cert-manager.io           2023-12-08T06:18:47Z

Create Kubernetes Secret

via GUI / API: https://cloud.digitalocean.com/account/api/tokens

  • mytoken
  • 90 days
  • read/write
dop_v1_60f1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DO_API_TOKEN="dop_v1_60f1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

kubectl create secret generic "digitalocean-dns" \
  --namespace backend \
  --from-literal=access-token="$DO_API_TOKEN"
user@mac % DO_API_TOKEN="dop_v1_60f1exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

kubectl create secret generic "digitalocean-dns" \
  --namespace backend \
  --from-literal=access-token="$DO_API_TOKEN"
secret/digitalocean-dns created

Configure Issuer CRD and apply

  • adapt email
cat << 'EOF' > cert-manager-wcard-issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-dev-wcard
  namespace: backend
spec:
  acme:
    email: certs@user.net
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-dev-wcard-private
    # List of challenge solvers that will be used to solve ACME challenges for the matching domains.
    solvers:
    # Use the DigitalOcean DNS API to manage DNS-01 challenge records.
      - dns01:
          digitalocean:
            # Kubernetes secret that contains the DO API token.
            # Must be in the same namespace as the Issuer CRD.
            tokenSecretRef:
              name: digitalocean-dns
              key: access-token
EOF
kubectl apply -f cert-manager-wcard-issuer.yaml
issuer.cert-manager.io/letsencrypt-dev-wcard created

verify issuer

kubectl get issuer letsencrypt-dev-wcard -n backend
user@mac % kubectl get issuer letsencrypt-dev-wcard -n backend

NAME                    READY   AGE
letsencrypt-dev-wcard   True    25s

Configure Certificate CRD and apply

cat << 'EOF' > cert-manager-wcard-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kubbi.xyz
  namespace: backend
spec:
  secretName: kubbi.xyz
  issuerRef:
    name: letsencrypt-dev-wcard
    kind: Issuer
    group: cert-manager.io
  commonName: "*.kubbi.xyz"
  dnsNames:
    - "kubbi.xyz"
    - "*.kubbi.xyz"
EOF
kubectl apply -f cert-manager-wcard-certificate.yaml
certificate.cert-manager.io/kubbi.xyz created

verify status

kubectl get certificate kubbi.xyz -n backend
user@mac % kubectl get certificate kubbi.xyz -n backend

NAME        READY   SECRET      AGE
kubbi.xyz   False   kubbi.xyz   24s

wait 2:30min

user@mac % kubectl get certificate kubbi.xyz -n backend

NAME        READY   SECRET      AGE
kubbi.xyz   True    kubbi.xyz   3m39s

Describe Secret

kubectl describe secret kubbi.xyz -n backend
user@mac % kubectl describe secret kubbi.xyz -n backend

Name:         kubbi.xyz
Namespace:    backend
Labels:       controller.cert-manager.io/fao=true
Annotations:  cert-manager.io/alt-names: *.kubbi.xyz,kubbi.xyz
              cert-manager.io/certificate-name: kubbi.xyz
              cert-manager.io/common-name: *.kubbi.xyz
              cert-manager.io/ip-sans:
              cert-manager.io/issuer-group: cert-manager.io
              cert-manager.io/issuer-kind: Issuer
              cert-manager.io/issuer-name: letsencrypt-dev-wcard
              cert-manager.io/uri-sans:

Type:  kubernetes.io/tls

Data
====
tls.crt:  5518 bytes
tls.key:  1675 bytes

create Host and apply

cat << 'EOF' > wildcard-host.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-backend
  namespace: backend
spec:
  tls:
    - hosts:
        - "*.kubbi.xyz"
      secretName: kubbi.xyz
  rules:
    - host: echo.kubbi.xyz
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: echo
                port:
                  number: 80
  ingressClassName: nginx
EOF
kubectl apply -f wildcard-host.yaml
ingress.networking.k8s.io/ingress-backend created

show ingress

kubectl get ingress -n backend
user@mac % kubectl get ingress -n backend

NAME              CLASS   HOSTS            ADDRESS       PORTS     AGE
ingress-backend   nginx   echo.kubbi.xyz   64.225.93.9   80, 443   58s

and here we are …

curl -Li http://echo.kubbi.xyz/
user@puffy202 $ curl -Li http://echo.kubbi.xyz/
HTTP/1.1 308 Permanent Redirect
Date: Fri, 08 Dec 2023 11:16:00 GMT
Content-Type: text/html
Content-Length: 164
Connection: keep-alive
Location: https://echo.kubbi.xyz

HTTP/2 200
date: Fri, 08 Dec 2023 11:16:00 GMT
content-type: text/plain
content-length: 334
strict-transport-security: max-age=15724800; includeSubDomains

Request served by echo-67dbbdb49d-dt9f9

GET / HTTP/1.1

Host: echo.kubbi.xyz
Accept: */*
User-Agent: curl/8.4.0
X-Forwarded-For: 10.244.0.87
X-Forwarded-Host: echo.kubbi.xyz
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Scheme: https
X-Real-Ip: 10.244.0.87
X-Request-Id: 1480dae4d90eab8df39db4683c52404b
X-Scheme: https

Any Comments ?

sha256: 2c4814af7931dcb4ff1080d8d406e8ffe603a213d0ed3b34d00defdcdcebb428