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