Author Archives: pkhamdee

Unknown's avatar

About pkhamdee

Craft IT strategies to drive IT transformation and engineering of cloud-native architecture in cloud computing to build software that people love and rely on.

Install NDK with NKP

Nutanix Data Service for Kubernetes (NDK) เป็น service ที่ติดตั้งบน Kubernetes Cluster เพื่อทำการ backup application และ replicate ข้อมูลที่ถูก backup ไปยัง Kubernetes cluster ปลายทาง เพื่อให้สามารถ recovery ทั้ง application และข้อมูลได้ โดยใช้กับ use case เช่นการทำ DR โดยการ replicate ข้อมูลไปยังปลายทางมากกว่าหนึ่งปลายทาง การย้าย Application (Application migration) หรือ rebalancing application ไปยัง cluster ที่มี resource ที่เหมาะสม การ snapshots และ restore ภายใน cluster โดยสามารสร้าง schedule และรองรับการ replication ในแบบ near realtime ได้

CRD ที่มาด้วยกับ NDK

Application สำหรับกำหนดกลุ่มของ resource ที่จะทำการ snapshot หรือ replicate
ApplicationSnapshot สร้าง application snapshot สำหรับ source ที่ระบุ (application)
Remote จับคู่ของ NKP instance ระหว่างสอง cluster
ReplicationTarget กำหนด cluster และ namespace ของ cluster ปลายทาง
ApplicationSnapshotReplication ทำการ replicate snapshot ระหร่วง cluster
ApplicationSnapshotRestore ทำการ restore snapshot ที่ local cluster หรือ remote cluster
JobScheduler, ProtectionPlan กำหนด snapshot policy และการจัดเก็บ (retention)
StorageCluster กำหนด Infrastructure ที่จะ support การทำ snapshot และ replication ระหว่าง cluster หรือภายใน cluster

การติดตั้ง NDK โดยต้องติดตั้งทั้ง Kuberntes Cluster ต้นทาง และ Kubernetes Cluster ปลายทาง

ขั้นตอนการติดตั้ง NDK ที่ Kubernetes ต้นทาง

เข้าไปที่ Nutanix Portal เพื่อ copy Download Link เพื่อทำการ download install package และทำการจัดเก็บไว้ใน private registry ภายในสำหรับใช้ในการการติดตั้ง

[nutanix@harbor ~]$ curl -o ndk-1.2.0.tar "https://download.nutanix.com/downloads/ndk/1.2.0/ndk-1.2.0.tar?Expires=1752582997&Key-Pair-Id=APKAJTTNCWPEI42QKMSA&Signature=CGOEgIDQHcJ1fTI8nIMbB5mcrM~5jPFcfS~5PyKDFGQyeNGlfBHyookKrzTTearX6L1aLLyEL6psYlkYIZdDlGIghHQuyb5qQBcxVGqiJ2ENuJD2MZKJkBFb6gnJ5s0JynyfkReAwPU5Ls4Vwb9yZXhzROm25Adezn-noLnkQUpLYQkyNl3~3n3X6xWR7qQQhHbo~QH3GEmYylmfsAcfx78WrH6-t9q3AV-2vhOGFNFz8k4gueqbRAjcLiKJBe7pJ-MlQ1KCo2ZYQMg3OACgqx7epi-2t0ImmpKs0I3rGa0lBqccOggX3n0tfaSED1cwTyjFRRpgFGYWZRX0qdrCvQ__"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  526M  100  526M    0     0  22.5M      0  0:00:23  0:00:23 --:--:-- 30.1M

unzip file ที่ทำการ download มา

[nutanix@harbor ~]$ tar -xvf ndk-1.2.0.tar

Load container เข้าไปยังระบบของ docker

[nutanix@harbor ~]$ docker image load -i ndk-1.2.0/ndk-1.2.0.tar

ทำการ tag container ที่ load เข้าไปในระบบและ push เข้าไปยัง container private registry ที่เตรียมไว้

[nutanix@harbor ~]$ docker image tag ndk/manager:1.2.0 10.38.53.107/ndk/manager:1.2.0
[nutanix@harbor ~]$ docker image tag ndk/infra-manager:1.2.0 10.38.53.107/ndk/infra-manager:1.2.0
[nutanix@harbor ~]$ docker image tag ndk/job-scheduler:1.2.0 10.38.53.107/ndk/job-scheduler:1.2.0
[nutanix@harbor ~]$ docker image tag ndk/kube-rbac-proxy:v0.17.0 10.38.53.107/ndk/kube-rbac-proxy:v0.17.0
[nutanix@harbor ~]$ docker image tag ndk/bitnami-kubectl:1.30.3 

[nutanix@harbor ~]$ docker push 10.38.53.107/ndk/manager:1.2.0
[nutanix@harbor ~]$ docker push 10.38.53.107/ndk/infra-manager:1.2.0
[nutanix@harbor ~]$ docker push 10.38.53.107/ndk/job-scheduler:1.2.0
[nutanix@harbor ~]$ docker push 10.38.53.107/ndk/kube-rbac-proxy:v0.17.0
[nutanix@harbor ~]$ docker push 10.38.53.107/ndk/bitnami-kubectl:1.30.3

ทำการ set environment variable เพื่อ install ndk

[nutanix@harbor ~]$ cd ~/ndk-1.2.0
[nutanix@harbor ~]$ export CLUSTER_NAME=<<NKP cluster to install NDK on>>
[nutanix@harbor ~]$ export KUBECONFIG=~/nkp-v2.15.0/cli/${CLUSTER_NAME}.conf
[nutanix@harbor ~]$ export LOCAL_REG=<<registry hostname/IP>>:<<port>>

ตรวจสอบว่ามี Nutanix CSI credentials secret ในระบบแล้ว ทั้งนี้ nutanix-csi-credentials จะมีอยู่แล้วถ้า NKP ถูกติดตั้งใช้งานกับ Nutanix Infra Provider

kubectl get secret -n ntnx-system

ถ้าไม่มีต้องสร้างด้วย yaml โดยที่ key คือ base64 encoding ของ “<<PC hostname/IP>>:9440:<<PC userid>>:<<PC userid password>>”

apiVersion: v1
data:
  key: <<encoded creds>>
kind: Secret
metadata:
  name: nutanix-csi-credentials
  namespace: ntnx-system
type: Opaque

ติดตั้ง ndk ด้วย helm cli

helm install ndk -n ntnx-system chart/ \
--set manager.repository=${LOCAL_REG}/ndk/manager \
--set manager.tag=1.2.0 \
--set infraManager.repository=${LOCAL_REG}/ndk/infra-manager \
--set infraManager.tag=1.2.0 \
--set kubeRbacProxy.repository=${LOCAL_REG}/ndk/kube-rbac-proxy \ 
--set kubeRbacProxy.tag=v0.17.0 \
--set bitnamiKubectl.repository=${LOCAL_REG}/ndk/bitnami-kubectl \ 
--set bitnamiKubectl.tag=1.30.3 \
--set jobScheduler.repository=${LOCAL_REG}/ndk/job-scheduler \
--set jobScheduler.tag=1.2.0 \
--set tls.server.clusterName=${CLUSTER_NAME} \
--set config.secret.name=nutanix-csi-credentials

ตัวอย่างค่าที่ใช้ในการติดตั้งครั้งนี้

[nutanix@harbor ndk-1.2.0]$ helm install ndk -n ntnx-system chart/ \
--set manager.repository=10.38.53.107/ndk/manager \
--set manager.tag=1.2.0 \
--set infraManager.repository=10.38.53.107/ndk/infra-manager \
--set infraManager.tag=1.2.0 \
--set kubeRbacProxy.repository=10.38.53.107/ndk/kube-rbac-proxy \
--set kubeRbacProxy.tag=v0.17.0 \
--set bitnamiKubectl.repository=10.38.53.107/ndk/bitnami-kubectl \
--set bitnamiKubectl.tag=1.30.3 \
--set jobScheduler.repository=10.38.53.107/ndk/job-scheduler \
--set jobScheduler.tag=1.2.0 \
--set tls.server.clusterName=nkp-at-next \
--set config.secret.name=nutanix-csi-credentials

ตรวจสอบการติดตั้งด้วยการ get pod ndk-controller-manager ว่าทำงานได้ปกติหรือไม่

[nutanix@harbor ndk-1.2.0]$ kubectl get pod -n ntnx-system
NAME                                      READY   STATUS    RESTARTS   AGE
ndk-controller-manager-79687849d5-8vxdh   4/4     Running   0          22s

สร้าง StorageCluster ของทั้งสอง Kubernetes Cluster ที่ต้องการทำ replication ระหว่างกัน โดย Kubernetes Cluster ที่สองจะต้องติดตั้ง NDK ให้สมบูรณ์ตามตัวอย่างข้างต้น

cd ~/nkp-v2.15.0/cli
export CLUSTER_NAME=<<NKP cluster name>>
export KUBECONFIG=~/nkp-v2.15.0/cli/${CLUSTER_NAME}.conf
export PE_UUID=<<PE UUID>>
export PC_UUID=<<PC UUID>>

cat << EOF >  ${CLUSTER_NAME}-storagecluster.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: StorageCluster
metadata:
 name: ${CLUSTER_NAME}
spec:
 storageServerUuid: ${PE_UUID}
 managementServerUuid: ${PC_UUID}
EOF

ตัวอย่างการสร้าง storage cluster

[nutanix@harbor ~]$ export KUBECONFIG=/home/nutanix/nkp-at-next.conf

[nutanix@harbor ~]$ cat << EOF >  nkp-at-next-storagecluster.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: StorageCluster
metadata:
 name: nkp-at-next
spec:
 storageServerUuid: 0006391e-0ad0-299a-70dd-ac1f6b3fddde
 managementServerUuid: f2b78299-aa10-43b9-94d5-faa3527e3897
EOF

[nutanix@harbor ~]$ k apply -f nkp-at-next-storagecluster.yaml
storagecluster.dataservices.nutanix.com/nkp-at-next created
[nutanix@harbor ~]$ k get storagecluster
NAME          AVAILABLE
nkp-at-next   true

สร้าง Storage Cluster ที่ Kubernetes Cluster ที่สอง

[nutanix@harbor ~]$ export KUBECONFIG=cluster2-kubeconfig.yaml

[nutanix@harbor ~]$ cat << EOF >  cluster2-storagecluster.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: StorageCluster
metadata:
 name: cluster2
spec:
 storageServerUuid: 0006391e-0ad0-299a-70dd-ac1f6b3fddde
 managementServerUuid: f2b78299-aa10-43b9-94d5-faa3527e3897
EOF

[nutanix@harbor ~]$ k apply -f cluster2-storagecluster.yaml
storagecluster.dataservices.nutanix.com/cluster2 created
[nutanix@harbor ~]$ k get storagecluster
NAME       AVAILABLE
cluster2   true

สร้าง application resource ที่ต้องการ snapshot หรือ replication โดยตัวอย่างนี้จะ backup resource ทั้งหมดที่อยู่ภายใต้ namespace แต่ถ้าต้องการ filter เฉพาะบาง resource สามารถกำหนดเพิ่มเติมได้ตามตัวอย่างที่ https://portal.nutanix.com/page/documents/details?targetId=Nutanix-Data-Services-for-Kubernetes-v1_2:top-app-cr-manual-create-cli-k8s-t.html

[nutanix@harbor ~]$ cat << EOF >  wordpress-app-cr.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: Application
metadata:
  name: wordpress-app-cr
  namespace: application
spec:
  applicationSelector:
EOF

[nutanix@harbor ~]$ k apply -f wordpress-app-cr.yaml
application.dataservices.nutanix.com/wordpress-app-cr created

[nutanix@harbor ~]$ k get application -A
NAMESPACE     NAME               AGE   ACTIVE   LAST-STATUS-UPDATE
application   wordpress-app-cr   20s            20s

สร้าง application snapshot ที่ cluster ต้นทาง

cd ~/nkp-v2.15.0/cli
export CLUSTER_NAME=<<NKP cluster name>>
export KUBECONFIG=~/nkp-v2.15.0/cli/${CLUSTER_NAME}.conf
export APP_NAME=<<application name>>
export APP_NAMESPACE=<<application namespace>>

cat << EOF >  ${APP_NAME}-snap.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshot
metadata:
  name: ${APP_NAME}-snap
  namespace: ${APP_NAMESPACE}
spec:
  source:
    applicationRef:
     name: ${APP_NAME}-app-cr 
  expiresAfter: <<#>>m
EOF

ตัวอย่างค่าที่ใช้ในตัวอย่างนี้

[nutanix@harbor ~]$ cat << EOF >  wordpress-snap.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshot
metadata:
  name: wordpress-snap
  namespace: application
spec:
  source:
    applicationRef:
     name: wordpress-app-cr
  expiresAfter: 120m
EOF
[nutanix@harbor ~]$ k apply -f wordpress-snap.yaml
applicationsnapshot.dataservices.nutanix.com/wordpress-snap created

[nutanix@harbor ~]$ kubectl get applicationsnapshot -n application
NAME             AGE   READY-TO-USE   BOUND-SNAPSHOTCONTENT                      SNAPSHOT-AGE   CONSISTENCY-TYPE
wordpress-snap   69s   true           asc-531ae7ca-7afb-40a6-a4f6-403c8e732cfa   36s

ตรวจสอบสถานะของการ snapshot

[nutanix@harbor ~]$ kubectl get applicationsnapshotcontent
NAME                                       READY-TO-USE   AGE    APPLICATIONSNAPSHOT   SNAPSHOTCONTENT-AGE   CONSISTENCYTYPE
asc-531ae7ca-7afb-40a6-a4f6-403c8e732cfa   true           110s   wordpress-snap        77s

ทดสอบ restore application จาก snapshot ที่สร้างขึ้นภายใน cluster เดียวกัน แต่ต่าง namspace ตามตัวอย่างนี้ โดยต้องสร้าง reference grant

export APP_NAME=<<application name>>
export APP_NAMESPACE=<<application namespace>>
export TGT_NAMESPACE=<<target namespace>>
***NOTE: target namespace must exist on target cluster (create if necessary)***
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cat << EOF >  ${APP_NAME}-snap-rg.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: ${APP_NAME}-snap-rg
 namespace: ${APP_NAMESPACE}
spec:
 from:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshotRestore
   namespace: ${TGT_NAMESPACE}
 to:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshot
   name: ${APP_NAME}-snap
EOF

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
kubectl apply -f ${APP_NAME}-snap-rg.yaml
- verify reference grant exists:
kubectl get referencegrant -n ${APP_NAMESPACE}
--> ${APP_NAME}-snap-rg is in the list of reference grants

ตัวอย่างค่าที่ใช้ในการสร้าง reference grant

[nutanix@harbor ~]$ k create ns newapplication
namespace/newapplication created

[nutanix@harbor ~]$ cat << EOF >  wordpress-snap-rg.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: wordpress-snap-rg
 namespace: application
spec:
 from:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshotRestore
   namespace: newapplication
 to:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshot
   name: wordpress-snap
EOF

[nutanix@harbor ~]$ k apply -f wordpress-snap-rg.yaml
referencegrant.gateway.networking.k8s.io/wordpress-snap-rg created

[nutanix@harbor ~]$ kubectl get referencegrant -n application
NAME                AGE
wordpress-snap-rg   35s

สร้าง application restore

cat << EOF >  ${APP_NAME}-rg-restore.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotRestore
metadata:
  name: ${APP_NAME}-rg-restore
  namespace: ${TGT_NAMESPACE}
spec:
  applicationSnapshotName: ${APP_NAME}-snap
  applicationSnapshotNamespace: ${APP_NAMESPACE}
EOF
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kubectl apply -f ${APP_NAME}-rg-restore.yaml
- verify restore operation completes successfully (can take a few minutes)
kubectl get applicationsnapshotrestore -n ${TGT_NAMESPACE}
--> ${APP_NAME}-rg-restore shows a COMPLETED status of 'true'
- verify all artifacts referenced in snapshot describe operation have been restored on the target namespace:
***For example***
kubectl get all -n ${TGT_NAMESPACE}
kubectcl get pvc -n ${TGT_NAMESPACE}

ตัวอย่างการ restore application

[nutanix@harbor ~]$ cat << EOF >  wordpress-rg-restore.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotRestore
metadata:
  name: wordpress-rg-restore
  namespace: newapplication
spec:
  applicationSnapshotName: wordpress-snap
  applicationSnapshotNamespace: application
EOF
[nutanix@harbor ~]$ k apply -f wordpress-rg-restore.yaml
applicationsnapshotrestore.dataservices.nutanix.com/wordpress-rg-restore created

[nutanix@harbor ~]$ kubectl get applicationsnapshotrestore -n newapplication
NAME                   SNAPSHOT-NAME    COMPLETED
wordpress-rg-restore   wordpress-snap   false

[nutanix@harbor ~]$ kubectl get applicationsnapshotrestore -n newapplication
NAME                   SNAPSHOT-NAME    COMPLETED
wordpress-rg-restore   wordpress-snap   true

[nutanix@harbor ~]$ k get all -n newapplication
NAME                                   READY   STATUS    RESTARTS   AGE
pod/wordpress-84f858d9fd-8mjdp         1/1     Running   0          2m24s
pod/wordpress-mysql-556f6f65cc-bjd8q   1/1     Running   0          2m24s

NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/wordpress         ClusterIP   10.99.164.74   <none>        80/TCP     2m24s
service/wordpress-mysql   ClusterIP   None           <none>        3306/TCP   2m24s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/wordpress         1/1     1            1           2m24s
deployment.apps/wordpress-mysql   1/1     1            1           2m24s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/wordpress-84f858d9fd         1         1         1       2m24s
replicaset.apps/wordpress-mysql-556f6f65cc   1         1         1       2m24s

ตัวอย่างการ restore application snapshot ไปยัง kubernetes cluster ปลายทาง โดยต้องสร้าง Remote cluster ที่ cluster ต้นทางก่อน

- verify ndk intercom service is working on both source & target clusters:
cd ~/nkp-v2.15.0/cli
export SOURCE_NAME=<<NKP source cluster name>>
export TARGET_NAME=<<target cluster name>>
export APP_NAME=<<application name>>
export APP_NAMESPACE=<<application namespace>>
export KUBECONFIG=~/nkp-v2.15.0/cli/${SOURCE_NAME}.conf
kubectl get svc -n ntnx-system
--> load balancer service 'ndk-intercom-service' should exist and have an assigned external IP
kubectl get svc -n ntnx-system --kubeconfig ${TARGET_NAME}.conf
--> load balancer service 'ndk-intercom-service' should exist and have an assigned external IP	
--> cache external IP (needed for the remote cr)
- verify an application snapshot exists and is ready:
kubectl get applicationsnapshot -n ${APP_NAMESPACE}
--> snapshot status for <<app name>>-snap should be "true"
- create remote custom resource:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cat << EOF >  ndk-${TARGET_NAME}-remote.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: Remote
metadata:
  name: ndk-${TARGET_NAME}-remote
spec:
  clusterName: ${TARGET_NAME} 
  ndkServiceIp: <<EXTERNAL IP of target cluster's 'ndk-intercom-service' LB service>> 
  ndkServicePort: 2021
  tlsConfig:
    skipTLSVerify: true
EOF
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--> 'tlsConfig' section may not be needed or may need modified based on how NDK was installed, see following link: https://portal.nutanix.com/page/documents/details?targetId=Nutanix-Data-Services-for-Kubernetes-v1_2:top-remote-cr-create-cli-k8s.html
kubectl apply -f ndk-${TARGET_NAME}-remote.yaml
- verify remote cr is ready:
kubectl get remote
--> ndk-${TARGET_NAME}-remote should be listed and AVAILABLE status set to 'True'

ตัวอย่างการสร้าง Remote cluster

[nutanix@harbor ~]$ cat << EOF >  ndk-cluster2-remote.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: Remote
metadata:
  name: ndk-cluster2-remote
spec:
  clusterName: cluster2
  ndkServiceIp: 10.38.53.17
  ndkServicePort: 2021
  tlsConfig:
    skipTLSVerify: true
EOF
[nutanix@harbor ~]$ k apply -f ndk-cluster2-remote.yaml
remote.dataservices.nutanix.com/ndk-cluster2-remote created
[nutanix@harbor ~]$ k get remote
NAME                  ADDRESS       PORT   AVAILABLE
ndk-cluster2-remote   10.38.53.17   2021   True

สร้าง replication target ที่ต้องการให้ snapshot ทำการ replicate ไป

cat << EOF >  ${APP_NAME}-replicate-target.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ReplicationTarget
metadata:
  name: ${TARGET_NAME}
  namespace: ${APP_NAMESPACE}
spec:
  namespaceName: ntnx-system
  remoteName: ndk-${TARGET_NAME}-remote
  serviceAccountName: default
EOF

ตัวอย่างการสร้าง Replication Target

[nutanix@harbor ~]$ cat << EOF >  wordpress-replicate-target.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ReplicationTarget
metadata:
  name: cluster2
  namespace: application
spec:
  namespaceName: ntnx-system
  remoteName: ndk-cluster2-remote
  serviceAccountName: default
EOF
[nutanix@harbor ~]$ k apply -f wordpress-replicate-target.yaml
replicationtarget.dataservices.nutanix.com/cluster2 created
[nutanix@harbor ~]$ k get replicationtarget -A
NAMESPACE     NAME       REMOTE-NAME           REMOTE-NAMESPACE   AVAILABLE
application   cluster2   ndk-cluster2-remote   ntnx-system        True

สร้าง application snapshot replication ไปยัง cluster ปลายทาง

cat << EOF >  ${APP_NAME}-replicate.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotReplication
metadata:
  name: ${APP_NAME}-replicate
  namespace: ${APP_NAMESPACE}
spec:
  applicationSnapshotName: ${APP_NAME}-snap
  replicationTargetName: ${TARGET_NAME}
EOF

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kubectl apply -f ${APP_NAME}-replicate.yaml
- monitor an application snapshot replication:
kubectl get applicationsnapshotreplication -n ${APP_NAMESPACE}
--> within a few minutes AVAILABLE status for <<app name>>-snap should be "True"
kubectl get applicationsnapshot -n ntnx-system ${APP_NAME}-snap --kubeconfig ${TARGET_NAME}.conf
--> should list snapshot on target cluster with a READY-TO-USE status of "true"

ตัวอย่างการสร้าง application snapshot replication

***NOTE: This enables application snapshots saved in one cluster to be restored to their original namespace on another cluster***
- verify the NDK reference grant CRD exists on the 'target' cluster:
cd ~/nkp-v2.15.0/cli
export TARGET_NAME=<<NKP target cluster name>>
export KUBECONFIG=~/nkp-v2.15.0/cli/${TARGET_NAME}.conf
kubectl get crd | grep 'referencegrants'
--> the 'referencegrants.gateway.networking.k8s.io' cred should be listed
- create a reference grant:
export APP_NAME=<<application name>>
export TGT_NAMESPACE=<<application's namespace on source cluster>>
***NOTE: target namespace must exist on target cluster (create if necessary)***
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cat << EOF >  ${APP_NAME}-snap-rg-${TARGET_NAME}.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: ${APP_NAME}-snap-rg-${TARGET_NAME}
 namespace: ntnx-system
spec:
 from:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshotRestore
   namespace: ${TGT_NAMESPACE}
 to:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshot
   name: ${APP_NAME}-snap
EOF

ตัวอย่างการสร้าง application snapshot replication

[nutanix@harbor ~]$ cat << EOF >  wordpress-replicate.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotReplication
metadata:
  name: wordpress-replicate
  namespace: application
spec:
  applicationSnapshotName: wordpress-snap
  replicationTargetName: cluster2
EOF
[nutanix@harbor ~]$ k apply -f wordpress-replicate.yaml
applicationsnapshotreplication.dataservices.nutanix.com/wordpress-replicate created
[nutanix@harbor ~]$ k get applicationsnapshotreplication -A
NAMESPACE     NAME                  AVAILABLE   APPLICATIONSNAPSHOT   REPLICATIONTARGET   AGE
application   wordpress-replicate   False       wordpress-snap        cluster2            12s

[nutanix@harbor ~]$ k get applicationsnapshotreplication -A
NAMESPACE     NAME                  AVAILABLE   APPLICATIONSNAPSHOT   REPLICATIONTARGET   AGE
application   wordpress-replicate   True        wordpress-snap        cluster2            6m57s

[nutanix@harbor ~]$ kubectl get applicationsnapshot -n ntnx-system
NAME             AGE     READY-TO-USE   BOUND-SNAPSHOTCONTENT                                  SNAPSHOT-AGE
wordpress-snap   9m46s   true           asc-531ae7ca-7afb-40a6-a4f6-403c8e732cfa-1980ca1b120   6m58s

สร้าง reference grant สำหรับการ restore โดยต้องสร้างที่ cluster ปลายทาง

- create a reference grant:
export APP_NAME=<<application name>>
export TGT_NAMESPACE=<<application's namespace on source cluster>>
***NOTE: target namespace must exist on target cluster (create if necessary)***
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cat << EOF >  ${APP_NAME}-snap-rg-${TARGET_NAME}.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: ${APP_NAME}-snap-rg-${TARGET_NAME}
 namespace: ntnx-system
spec:
 from:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshotRestore
   namespace: ${TGT_NAMESPACE}
 to:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshot
   name: ${APP_NAME}-snap
EOF

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kubectl apply -f ${APP_NAME}-snap-rg-${TARGET_NAME}.yaml
- verify reference grant exists:
kubectl get referencegrant -n ntnx-system
--> ${APP_NAME}-snap-rg-${TARGET_NAME} is in the list of reference grants

ตัวอย่างการสร้าง reference grant

[nutanix@harbor ~]$ cat << EOF >  wordpress-snap-rg-cluster2.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: wordpress-snap-rg-cluster2
 namespace: ntnx-system
spec:
 from:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshotRestore
   namespace: application
 to:
 - group: dataservices.nutanix.com
   kind: ApplicationSnapshot
   name: wordpress-snap
EOF
[nutanix@harbor ~]$ k apply -f wordpress-snap-rg-cluster2.yaml
referencegrant.gateway.networking.k8s.io/wordpress-snap-rg-cluster2 created
[nutanix@harbor ~]$ k get referencegrant -A
NAMESPACE     NAME                         AGE
ntnx-system   wordpress-snap-rg-cluster2   9s

ทำการ restore application ที่ cluster ปลายทาง

cat << EOF >  ${APP_NAME}-rg-restore-${TARGET_NAME}.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotRestore
metadata:
  name: ${APP_NAME}-rg-restore-${TARGET_NAME}
  namespace: ${TGT_NAMESPACE}
spec:
  applicationSnapshotName: ${APP_NAME}-snap
  applicationSnapshotNamespace: ntnx-system
EOF

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kubectl apply -f ${APP_NAME}-rg-restore-${TARGET_NAME}.yaml
- verify restore operation completes successfully (can take a few minutes)
kubectl get applicationsnapshotrestore -n ${TGT_NAMESPACE}
--> ${APP_NAME}-rg-restore-${TARGET_NAME} shows a COMPLETED status of 'true'
- verify all artifacts referenced in snapshot describe operation have been restored on the target namespace:
***For example***
kubectl get all -n ${TGT_NAMESPACE}
kubectcl get pvc -n ${TGT_NAMESPACE}

ตัวอย่างการ restore application

[nutanix@harbor ~]$ cat << EOF >  wordpress-rg-restore-cluster2.yaml
apiVersion: dataservices.nutanix.com/v1alpha1
kind: ApplicationSnapshotRestore
metadata:
  name: wordpress-rg-restore-cluster2
  namespace: application
spec:
  applicationSnapshotName: wordpress-snap
  applicationSnapshotNamespace: ntnx-system
EOF
[nutanix@harbor ~]$ k apply -f wordpress-rg-restore-cluster2.yaml
applicationsnapshotrestore.dataservices.nutanix.com/wordpress-rg-restore-cluster2 created
[nutanix@harbor ~]$ k get applicationsnapshotrestore -A
NAMESPACE     NAME                            SNAPSHOT-NAME    COMPLETED
application   wordpress-rg-restore-cluster2   wordpress-snap   false

[nutanix@harbor ~]$ k get applicationsnapshotrestore -A
NAMESPACE     NAME                            SNAPSHOT-NAME    COMPLETED
application   wordpress-rg-restore-cluster2   wordpress-snap   true
[nutanix@harbor ~]$ k get pod -n application
NAME                               READY   STATUS    RESTARTS   AGE
wordpress-84f858d9fd-zqtrt         1/1     Running   0          78s
wordpress-mysql-556f6f65cc-s2h8m   1/1     Running   0          78s
[nutanix@harbor ~]$

Update NKP using NKP cli

ในการ upgrade นี้จะใช้วิธีแบบ air gapped โดยต้อง download bundle จาก nutanix portal ด้วยการ copy Download link ของ NKP Airgapped Bundle (Version v2.15.0)

ใช้ curl ในการ download หรือ tools อื่น หรือจะ click download ก็ได้ ตัวอย่างนี้จะใช้ curl

[nutanix@harbor ~]$ curl -o nkp-air-gapped-bundle_v2.15.0_linux_amd64.tar.gz "https://download.nutanix.com/downloads/nkp/v2.15.0/nkp-air-gapped-bundle_v2.15.0_linux_amd64.tar.gz?Expires=1751840997&Key-Pair-Id=APKAJTTNCWPEI42QKMSA&Signature=MWumafkGQqAazC31HnaDyHlqfjAggj8q7c5~viJDgbJ1vb2-Xv1bHJwshl4m2gILPcf8emPtZU4VKk0sOMJ-t7lc9PopCKGhd~T1LPTt6wSJx29MEEdcvE7p3jDQZI1uuI5qflzmwaljOiwXThgiIgnIw3WcVa97a7GT3BH1O~fmg0sbiSFh1LG6lUKMCZayDwlbbSfBQA1feE8WfTolwgXNu6WMtZBn2Rl4l0bffuWVcGDGFn2ywBJSt5V5iPTU4bgjzLOqSB6E3ZkvtBZvIHqNwhrUEPJpKyTTvUZh7wbP56gU~hThlJZBOs6BkC5n6h~E0H2aohDUjUjos396XA__"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 16.2G  100 16.2G    0     0  41.8M      0  0:06:36  0:06:36 --:--:-- 40.9M

ทำการ unzip file ที่ download มา

[nutanix@harbor ~]$ tar -xvf nkp-air-gapped-bundle_v2.15.0_linux_amd64.tar.gz
[nutanix@harbor ~]$ cd nkp-v2.15.0
[nutanix@harbor nkp-v2.15.0]$ ls
application-charts        cli               kib                                 kubectl                              NOTICES
application-repositories  container-images  konvoy-bootstrap-image-v2.15.0.tar  nkp-image-builder-image-v2.15.0.tar

push kommander container ซึ่งเป็นระบบจัดการ (nkp management) การใช้งานต่างๆ ของ NKP ไปยัง harbor internal registry

[nutanix@harbor nkp-v2.15.0]$ nkp push bundle --bundle ./container-images/kommander-image-bundle-v2.15.0.tar \
--to-registry=https://10.38.16.169/mirror \
--to-registry-username=admin \
--to-registry-password=Harbor12345 \
--to-registry-insecure-skip-tls-verify
 ✓ Creating temporary directory
 ✓ Unarchiving image bundle "./container-images/kommander-image-bundle-v2.15.0.tar"
 ✓ Parsing image bundle config
 ✓ Starting temporary Docker registry
 ✓ Pushing bundled images [================================>135/135] (time elapsed 53s)

push nkp convoy ซึ่งเป็น cluster life cycle management ของ nkp

[nutanix@harbor nkp-v2.15.0]$ nkp push bundle --bundle ./container-images/konvoy-image-bundle-v2.15.0.tar \
--to-registry=https://10.38.16.169/mirror \
--to-registry-username=admin \
--to-registry-password=Harbor12345 \
--to-registry-insecure-skip-tls-verify
 ✓ Creating temporary directory
 ✓ Unarchiving image bundle "./container-images/konvoy-image-bundle-v2.15.0.tar"
 ✓ Parsing image bundle config
 ✓ Starting temporary Docker registry
 ✓ Pushing bundled images [================================>110/110] (time elapsed 52s)

update nkp cli เป็น version 2.15 โดยการ copy workdload link และ install cli

curl -fsSL https://raw.githubusercontent.com/nutanixdev/nkp-quickstart/main/scripts/get-nkp-cli | bash

run nkp update command เพื่อ update kommander

*** ต้อง upgrade kommander ก่อนเสมอ เพราะการ upgrade nkp management cluster ด้วย version ใหม่ก่อน อาจจะส่งผลให้ kommander ทำงานมีปัญหาได้จากการเปลี่ยนแปลง version ของ kubernetes

[nutanix@harbor nkp-v2.15.0]$ nkp upgrade kommander --charts-bundle ./application-charts/nkp-kommander-charts-bundle-v2.15.0.tar.gz \
--kommander-applications-repository ./application-repositories/kommander-applications-v2.15.0.tar.gz \
--kubeconfig /home/nutanix/nkp-at-next.conf \
--request-timeout 0 \
-v 6

หลังจาก install kommander เสร็จแล้วทำการ update nkp management cluster โดยการ list cluster ทั้งหมด และใช้ nkp cli ตามตัวอย่าง

[nutanix@harbor ~]$ k get cluster -A
NAMESPACE                     NAME          CLUSTERCLASS          PHASE         AGE   VERSION
default                       nkp-at-next   nkp-nutanix-v2.14.0   Provisioned   42h   v1.31.4
kommander-default-workspace   cluster2      nkp-nutanix-v2.14.0   Provisioned   38h   v1.31.4
nkp upgrade cluster nutanix \
--cluster-name nkp-at-next  \
--vm-image nkp-rocky-9.5-release-1.32.3-20250430150550.qcow2 \
--kubeconfig /home/nutanix/nkp-at-next.conf  \
--timeout 0 -v 6

หลังจาก upgrade management cluster เสร็จแล้ว สามารถ upgrade workload cluster ตามด้วย nkp cli ตามตัวอย่าง

[nutanix@harbor ~]$ nkp upgrade cluster nutanix \
--cluster-name cluster2 \
--namespace kommander-default-workspace  \
--vm-image nkp-rocky-9.5-release-1.32.3-20250430150550.qcow2 \
--kubeconfig /home/nutanix/nkp-at-next.conf  \
--timeout 0 -v 6

ตรวจสอบว่า cluster ถูก upgrade ไปยัง version ใหม่แล้วด้วย cli

[nutanix@harbor ~]$ k get cluster -A
NAMESPACE                     NAME          CLUSTERCLASS          PHASE         AGE   VERSION
default                       nkp-at-next   nkp-nutanix-v2.15.0   Provisioned   43h   v1.32.3
kommander-default-workspace   cluster2      nkp-nutanix-v2.15.0   Provisioned   39h   v1.32.3

Create Self-Sign for Kubernetes Ingress (TLS)

การออกกใบรับรอง Digital Certificate จะออกโดย Certificate Authority (CA) ซึ่งเป็นหน่วยงานกลางที่ได้รับความน่าเชื่อถือ เช่น GlobalSign, DigiCert, GoDaddy, Let’s Encrypt หรือถ้าหน่วยงานที่มี Certificate Server ภายในไว้ออก digital certificate โดยมี Certificate Server เช่น Microsoft Certificate Services (Windows Server), Hashicorp Vault เป็นต้น

รูปแบบของ Certificate จะแบ่งเป็นลำดับชั้น (Chain) ดังนี้

Root CA
├── Intermediate CA 1 (signed by Root CA)
│   ├── End-entity certificate (signed by Intermediate CA 1)
│   └── End-entity certificate (signed by Intermediate CA 1)
└── Intermediate CA 2 (signed by Root CA)
    └── End-entity certificate (signed by Intermediate CA 2)

Root CA เป็น Top-level ของ Certificate hierarchy สำหรับ Public Key Infrastructure (PKI) มีความน่าเชื่อถือสูง และมักถูกเก็บไว้แบบ offline เพื่อความปลอดภัย เช่น trust store ของเบราว์เซอร์ต่างๆ และใช้เพื่อออก intermediate certificate หรือใบรับรองอื่นๆ

Intermediate CA เป็น CA ที่อยู่ระหว่าง root และ end-entity certificate ซึ่งใช้ในการออก certificate ให้กลับผู้ร้องขอของระบบย่อยลงไป หรือผู้ใช้งาน (end-entity certificate) เช่นเว็บไซต์ อีเมลล์​ ช่วยลดความเสี่ยงในการถูกโจมตี เพราะหาก intermediate certificate ถูกโจมตี จะสามารถเพิกถอนได้ง่ายกว่า Root Certificate

Root Certificate จึงเป็นรากฐานของความน่าเชื่อถือในระบบ PKI (public key infrastructure) ในขณะที่ Intermediate Certificate ทำหน้าที่เป็นสะพานเชื่อระหว่าง Root Certificate และใบรับรองอื่นๆ ที่ใช้งานจริง

ขั้นตอนสร้าง Self-Sign ฉertificate จาการสร้าง CA เองมีขั้นตอนดังนี้

สร้าง CA key

openssl genrsa -out ca.key 4096

สร้าง CA Certificate จาก CA key ที่สร้างก่อนหน้า

openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=TH/ST=TH/L=BK/O=IT/OU=IT/CN=corp.com" \
-key ca.key \
-out ca.crt

สร้าง Server key หรือใบรับรองของผู้ร้องขอ Digital Certificate (end-entity certificate)

openssl genrsa -out app.key 4096

ทำการสร้าง CSR (Certificate Signing Requests) เพื่อส่งให้กับ CA ทำการ sign หรือออก Digital Certificate ให้

openssl req -sha512 -new \
-subj "/C=TH/ST=TH/L=BK/O=IT/OU=IT/CN=*.app.corp.com" \
-key app.key \
-out app.csr

กระบวนการออก certificate ของ CA จะสร้างส่วนของ SAN เพื่อที่จะมีข้อมูลของ server เช่น server fqdn หรือ ip ในการระบุตัวตนที่ออกโดย CA

[~/repos/lab/test-secret] cat v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.0=app.corp.com
DNS.0=*.app.corp.com
IP.0=10.38.16.141

CA ทำการออก Digital Certificate โดยการ sign ด้วย Certificate ของ CA จาก Certificate Signing Request (CSR) ของผู้ร้องขอ Digital Certificate

openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in app.csr \
-out app.crt
Certificate request self-signature ok
subject=C=TH, ST=TH, L=BK, O=IT, OU=IT, CN=*.app.corp.com

จากนั้น CA จะส่ง Certificate ที่สร้างขึ้นส่งกลับไปยังผู้ร้องขอ ซึ่งก็คือ app.crt ตามตัวอย่างเพื่อใช้งานต่อไป เช่นไปใช้กับ kubernetes Ingress หรือ Application Server เป็นต้น

Nutanix NKP Backup With Velero

ทำการ enable velero ที่ nkp dashboard เนื่องจาก velero ต้องการ s3 storage เพื่อเก็บข้อมูล backup จึงต้อง enable rookceph และ rookceph cluster โดยจะต้องรอจนกว่าจะ running สมบูรณ์ก่อนจะ enable velero

เพิ่ม custom config เพื่อให้ Velero สามารถ backup persistent volume ได้

configuration:
  backupStorageLocation:
    - bucket: dkp-velero
      config:
        region: dkp-object-store
        s3Url: https://10.38.16.132:8085
        s3ForcePathStyle: "true"
        insecureSkipTLSVerify: true
        profile: default
      provider: aws
      credential:
        key: aws
        name: dkp-velero-ceph
  features: EnableCSI
  uploaderType: kopia
  volumeSnapshotLocation:
    - bucket: dkp-velero
      config:
        region: dkp-object-store
        s3Url: https://10.38.16.132:8085
      provider: aws
deployNodeAgent: true
initContainers:
  - image: velero/velero-plugin-for-aws:v1.11.1
    imagePullPolicy: IfNotPresent
    name: velero-plugin-for-aws
    volumeMounts:
      - mountPath: /target
        name: plugins
nodeAgent:
  annotations:
    secret.reloader.stakater.com/reload: dkp-velero-ceph
  priorityClassName: dkp-critical-priority
  resources:
    limits: null

ข้อมูล custom configuration ต้องการ s3 bucket และ secret key เพื่อ connect ไปยัง s3 bucket โดยสร้าง secret key ได้ตามตัวอย่างดังนี้

apiVersion: v1
kind: Secret
metadata:
  name: dkp-velero-ceph
  namespace: kommander-default-workspace
type: Opaque
stringData:
  aws: |
    [default]
    aws_access_key_id = 2N8IL5CIUIWHR2MC287L
    aws_secret_access_key = bNzQDPy23GzdWKDuokZOtlT1fO7oR01YcOTicBCM

หลังจาก enable แล้วตรวจสอบว่า velero pod ทำงานได้ปกติ รวมถึงตรวจสอบ backup location และ volume snapshot location

~/repos/note  k get pod -A | grep velero
kommander                           object-bucket-claims-check-dkp-velero-229gs                       0/1     Completed          0                10d
kommander                           velero-54db8ff6f8-vcvfw                                           1/1     Running            0                12m
kommander                           velero-backup-storage-location-updater-5d849446c9-kt4h8           1/1     Running            0                11m
kommander                           velero-pre-install-wt6nv                                          0/1     Completed          0                13m

~/repos/lab  k get backupstoragelocations -A
NAMESPACE   NAME      PHASE       LAST VALIDATED   AGE   DEFAULT
kommander   default   Available   1s               25m   true

 ~/repos/lab  k get volumesnapshotlocations -A
NAMESPACE   NAME      AGE
kommander   default   26m

ติดตั้ง Velero cli สำหรับการใช้งาน

# For Apple Silicon Macs (M1/M2/M3) Download ARM64 version
curl -LO https://github.com/vmware-tanzu/velero/releases/latest/download/velero-darwin-arm64.tar.gz

# Extract and install
tar -xzf velero-darwin-arm64.tar.gz
sudo mv velero-*/velero /usr/local/bin/
chmod +x /usr/local/bin/velero
rm -rf velero-* velero-darwin-arm64.tar.gz

#Ubuntu/Debian (APT)
# Add Velero repository
curl -s https://packagecloud.io/install/repositories/vmware/velero/script.deb.sh | sudo bash

# Install Velero
sudo apt-get install velero

#CentOS/RHEL/Fedora (YUM/DNF)
# Add Velero repository
curl -s https://packagecloud.io/install/repositories/vmware/velero/script.rpm.sh | sudo bash

# Install Velero (CentOS/RHEL)
sudo yum install velero

# Or for Fedora
sudo dnf install velero

ตรวจสอบ version ของ Velero หลังจากติดตั้ง

~/repos/note  velero version
Client:
	Version: v1.15.2
	Git commit: 804d73c4f2349f1ca9bd3d6c751956e1d2021c01

Velero จะใช้ kubeconfig file เพื่อ connect ไปยัง kubernetes จึงต้อง export kubeconfig environment ก่อน จากนั้นตรวจสอบว่า velero เห็น backup location หรือไม่

~/repos/lab  export KUBECONFIG=/Users/pongsak.khamdee/repos/lab/cluster2-kubeconfig.yaml

~/repos/lab  velero get backup-location -n kommander
NAME      PROVIDER   BUCKET/PREFIX   PHASE       LAST VALIDATED                  ACCESS MODE   DEFAULT
default   aws        dkp-velero      Available   2025-07-03 21:54:38 +0700 +07   ReadWrite     true

 ~/repos/lab  velero get snapshot-location -n kommander
NAME      PROVIDER
default   aws

ติดตั้ง wordpress เพื่อใช้ในการทดสอบ backup และ restore โดยติดตั้งที่ namespace application ตามตัวอย่าง yaml ดังนี้

apiVersion: v1
kind: Secret
metadata:
  name: mysql-pass
type: Opaque
stringData:
  password: nutanix
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
        - image: mysql:8.0
          name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-pass
                  key: password
            - name: MYSQL_DATABASE
              value: wordpress
            - name: MYSQL_USER
              value: wordpress
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-pass
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          persistentVolumeClaim:
            claimName: mysql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
        - image: wordpress:6.4-apache
          name: wordpress
          env:
            - name: WORDPRESS_DB_HOST
              value: wordpress-mysql
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-pass
                  key: password
            - name: WORDPRESS_DB_USER
              value: wordpress
            - name: WORDPRESS_DB_NAME
              value: wordpress
            - name: WORDPRESS_DEBUG
              value: "1"  
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wp-pv-claim
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wordpress-ingress
spec:
  ingressClassName: kommander-traefik
  rules:
  - host: wordpress.10.38.204.16.sslip.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress
            port:
              number: 80            

ตรวจสอบว่า wordpress ทำงานได้ปกติ และเพิ่ม page เข้าไปใน wordpress เพื่อใช้ในการตรวจสอบการ restore

~/repos/lab  k get pod -n application
NAME                               READY   STATUS    RESTARTS   AGE
wordpress-57ff4b4485-6tl9g         1/1     Running   0          10h
wordpress-mysql-6dd978bc8b-jr2gq   1/1     Running   0          10h

ทำการ backup wordpress ด้วย velero cli

[nutanix@harbor ~]$ velero backup create wordpress10 --include-namespaces application --snapshot-volumes=true -n kommander --wait
Backup request "wordpress10" submitted successfully.
Waiting for backup to complete. You may safely press ctrl-c to stop waiting - your backup will continue in the background.
.............
Backup completed with status: Completed. You may check for more information using the commands `velero backup describe wordpress10` and `velero backup logs wordpress10`.

ตรวจสอบข้อมูลที่ velero ทำการ backup ได้จาก cli ดังนี้

[nutanix@harbor ~]$ velero backup describe wordpress10 --insecure-skip-tls-verify -n kommander --details
Name:         wordpress10
Namespace:    kommander
Labels:       velero.io/storage-location=default
Annotations:  velero.io/resource-timeout=10m0s
              velero.io/source-cluster-k8s-gitversion=v1.31.4
              velero.io/source-cluster-k8s-major-version=1
              velero.io/source-cluster-k8s-minor-version=31

Phase:  Completed


Namespaces:
  Included:  application
  Excluded:  <none>

Resources:
  Included:        *
  Excluded:        <none>
  Cluster-scoped:  auto

Label selector:  <none>

Or label selector:  <none>

Storage Location:  default

Velero-Native Snapshot PVs:  true
Snapshot Move Data:          false
Data Mover:                  velero

TTL:  720h0m0s

CSISnapshotTimeout:    10m0s
ItemOperationTimeout:  4h0m0s

Hooks:  <none>

Backup Format Version:  1.1.0

Started:    2025-07-06 08:13:24 +0000 UTC
Completed:  2025-07-06 08:13:37 +0000 UTC

Expiration:  2025-08-05 08:13:24 +0000 UTC

Total items to be backed up:  29
Items backed up:              29

Backup Item Operations:
  Operation for volumesnapshots.snapshot.storage.k8s.io application/velero-wp-pv-claim-hwnp8:
    Backup Item Action Plugin:  velero.io/csi-volumesnapshot-backupper
    Operation ID:               application/velero-wp-pv-claim-hwnp8/2025-07-06T08:13:32Z
    Items to Update:
              volumesnapshots.snapshot.storage.k8s.io application/velero-wp-pv-claim-hwnp8
              volumesnapshotcontents.snapshot.storage.k8s.io /snapcontent-7e8eaf1f-5c59-46ec-9356-887e65b15769
    Phase:    Completed
    Created:  2025-07-06 08:13:32 +0000 UTC
    Started:  2025-07-06 08:13:32 +0000 UTC
    Updated:  2025-07-06 08:13:37 +0000 UTC
  Operation for volumesnapshots.snapshot.storage.k8s.io application/velero-mysql-pv-claim-s6q97:
    Backup Item Action Plugin:  velero.io/csi-volumesnapshot-backupper
    Operation ID:               application/velero-mysql-pv-claim-s6q97/2025-07-06T08:13:37Z
    Items to Update:
              volumesnapshots.snapshot.storage.k8s.io application/velero-mysql-pv-claim-s6q97
              volumesnapshotcontents.snapshot.storage.k8s.io /snapcontent-e8de1f6e-4bce-43e7-9894-c7a180c9dd2d
    Phase:    Completed
    Created:  2025-07-06 08:13:37 +0000 UTC
    Started:  2025-07-06 08:13:37 +0000 UTC
    Updated:  2025-07-06 08:13:37 +0000 UTC
Resource List:
  apiextensions.k8s.io/v1/CustomResourceDefinition:
    - ciliumendpoints.cilium.io
  apps/v1/Deployment:
    - application/wordpress
    - application/wordpress-mysql
  apps/v1/ReplicaSet:
    - application/wordpress-7456589d7f
    - application/wordpress-mysql-7cf779d5dd
  cilium.io/v2/CiliumEndpoint:
    - application/wordpress-7456589d7f-8s8fk
    - application/wordpress-mysql-7cf779d5dd-c6mpk
  discovery.k8s.io/v1/EndpointSlice:
    - application/wordpress-mysql-hcrbc
    - application/wordpress-zpdjc
  networking.k8s.io/v1/Ingress:
    - application/wordpress-ingress
  snapshot.storage.k8s.io/v1/VolumeSnapshot:
    - application/velero-mysql-pv-claim-s6q97
    - application/velero-wp-pv-claim-hwnp8
  snapshot.storage.k8s.io/v1/VolumeSnapshotClass:
    - nutanix-snapshot-class
  snapshot.storage.k8s.io/v1/VolumeSnapshotContent:
    - snapcontent-7e8eaf1f-5c59-46ec-9356-887e65b15769
    - snapcontent-e8de1f6e-4bce-43e7-9894-c7a180c9dd2d
  v1/ConfigMap:
    - application/kube-root-ca.crt
  v1/Endpoints:
    - application/wordpress
    - application/wordpress-mysql
  v1/Namespace:
    - application
  v1/PersistentVolume:
    - pvc-32abc23d-9637-4343-af61-86e24dbbab5d
    - pvc-65ca02e4-4396-45fc-b91d-f7f915db0095
  v1/PersistentVolumeClaim:
    - application/mysql-pv-claim
    - application/wp-pv-claim
  v1/Pod:
    - application/wordpress-7456589d7f-8s8fk
    - application/wordpress-mysql-7cf779d5dd-c6mpk
  v1/Secret:
    - application/mysql-pass
  v1/Service:
    - application/wordpress
    - application/wordpress-mysql
  v1/ServiceAccount:
    - application/default

Backup Volumes:
  Velero-Native Snapshots: <none included>

  CSI Snapshots:
    application/mysql-pv-claim:
      Snapshot:
        Operation ID: application/velero-mysql-pv-claim-s6q97/2025-07-06T08:13:37Z
        Snapshot Content Name: snapcontent-e8de1f6e-4bce-43e7-9894-c7a180c9dd2d
        Storage Snapshot ID: NutanixVolumes-079a8061-ad99-451e-aace-195ba16a72dd:88d833af-04d3-4f7e-b1d5-671cf7ec4656
        Snapshot Size (bytes): 21474836480
        CSI Driver: csi.nutanix.com
        Result: succeeded
    application/wp-pv-claim:
      Snapshot:
        Operation ID: application/velero-wp-pv-claim-hwnp8/2025-07-06T08:13:32Z
        Snapshot Content Name: snapcontent-7e8eaf1f-5c59-46ec-9356-887e65b15769
        Storage Snapshot ID: NutanixVolumes-29fd7391-322f-4fa3-bf35-a857011e0991:8d300ddc-de02-4650-9b88-baa4afb35376
        Snapshot Size (bytes): 21474836480
        CSI Driver: csi.nutanix.com
        Result: succeeded

  Pod Volume Backups: <none included>

HooksAttempted:  0
HooksFailed:     0

ตรวจสอบสถานะการ backup

 ~/repos/lab  velero get backup -n kommander-default-workspace
NAME                            STATUS      ERRORS   WARNINGS   CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
23:39:26 +0700 +07   29d       default            <none>
wordpress10                     Completed   0        0          2025-07-06 

ทำการ ลบ wordpress application

 ~/repos/lab  k  delete ns application
namespace "application" deleted

ทำการ restore wordpress application ซึ่งตัวอย่างนี้เป็นการ restore จากอีก cluster โดยให้ทั้งสอง cluster ใช้ S3 bucket เดียวกัน

 ~/repos/lab  velero restore create wordpress-restore-10 --from-backup wordpress10 --restore-volumes=true  -n kommander-default-workspace
Restore request "wordpress-restore-10" submitted successfully.
Run `velero restore describe wordpress-restore-10` or `velero restore logs wordpress-restore-10` for more details.

เรียกใช้ wordpress เพื่อตรวจสอบว่าข้อมูลมีการ restore กลับมาครบถ้วน

Nutanix NKP Authentication With LDAP

เตรียม nkp service user โดยเข้าไปที่เมนู Active Directory Users and Computers

Click Users เลือก New และ User

ตั้งชื่อ ตัวอย่างนี้ใช้ nkpsvc

หลังจากสร้าง user ใหม่แล้วเก็บ DN ไว้สำหรับใช้ในการ setup ใน NKP โดย click ที่ user เลือก Advanced Feature

click ที่ nkpsvc และเลือก Properties

click เลือก Attribute Editor และ copy ค่า distinguishedName สำหรับใช้ในการ config ที่ nkp

เปิดหน้า nkp-dashboard และเข้าไปที่เมนู Identity Providers

click Add Identity Provider และเลือก LDAP จากนั้นเลือก Workspace ที่ต้องการจะให้ Identity Provider นี้ถูกนำไปใช้ เลือก All Workspace หมายถึง Identity Provider นี้จะถูกใช้กับทุกๆ Workspace

หาค่า User Search Base DN โดย click ที่ Users เลือก Properties

ไปที่ Attribute Editor แล้วเลื่อนหา distinguishedName และทำการ copy ค่านี้เพื่อใช้สำหรับ User Search Base DN

ค่า User Search Username โดย click ที่ user nkpsvc เลือก Properties ไปที่ Attribute Editor แล้วเลื่อนไปที่ sAMAccountName

นำค่าที่ได้กรอกใน User Search Base DN และ User Search Username

กรอกค่าอื่นๆ ตามตัวอย่าง

จากนั้นกด save เพื่อบันทึก

ทำการสร้าง group เพื่อ authentication เข้ามายัง nkp platform จากนั้นสร้าง user และ assign ให้เข้ามาเป็น member ของ group ที่สร้างขึ้น

จากนั้นสร้าง Groups โดยเข้าไปที่เมนู Identity Providers และ click Add Identity Provider

ตั้งชื่อ group name และ group โดยต้องขึ้นต้นด้วย oidc: ตามด้วย group name ที่สร้างใน ldap

เข้าไปที่เมนู Access Control เพื่อให้ group NKP Admins มีสิทธิ์​เป็น Platform Admin ของระบบ NKP ที่สามารถเข้าถึงและจัดการ NKP Management Cluster และ NKP workload clusters ที่อยู่ในะรบบ

เลือก Cluster Role Bindings เลือก Add Roles ที่ group NKP Admins จากนั้น assign role Cluster Admin Role และ dkp-kommander-admin

Log out จากระบบ หน้า Log in จะมี option ให้เลือก login ได้มากกว่าหนึ่งช่องทาง ทดสอบ login ด้วย LDAP โดยเลือกที่ Log in with ntnxlab.local.AD

ตัวอย่างการสร้าง Role อื่นๆ เช่น

Workspace Admin มีสิทธิ์ Adminstrator สำหรับ NKP Workload Cluster โดยสามารถเข้าถึง NKP UI และเข้าถึง workspace ที่ถูก assign ให้เข้าถึงเท่านั้น โดยจะต้อง assign role “Workspace Admin Role” และ “Kommander Workspace Admin Role”

Project Deployer ที่มีสิทธิ์อยู่ภายใต้ namespace ของ NKP workload cluster และเข้าถึง kubernetes dashboad ได้ซึ่งตรงกับความต้องการพื้นฐานของทีม DevOps โดยจะต้อง assign role “App Deployer”

โครงสร้างการสร้าง NKP, Workspace และ Project

Nutanix NKP Installation

ขั้นตอนการติดตั้ง NKP ระบบจะสร้าง kubernetes ชั่วคราวสำหรับการติดตั้งเฉพาะ (bootstrap cluster) โดยจะ load package ที่จำเป็นคือ cluster-api ทำงานใน kubernetes ชั่วคราว อยู่ภายใต้ package ที่ชื่อว่า convoy โดย cluster-api จะสร้าง nkp management cluster ที่ infra platform ปลายทางจากข้อมูลที่ได้จาก NKP cli

cluster-api เป็น controller ที่ทำงานบน kubernetes ทำ infra auto provisioning ไปยัง platform ต่างๆ เนื่องจากการ provision kubernetes บน platform ที่แตกต่างกันจะต้องอาศัย api ที่แตกต่างกัน การใช้ kubernetes api จึงเป็นวิธีการที่ทำให้ไม่ว่า platform อะไรก็สามารถเรียกใช้ได้ด้วย api เดียวกัน cluster-api จึงเป็น standard ในการสร้าง kubernetes บน platform ที่แตกต่างกันได้ โดยมี vendor จากค่ายต่างๆ เข้าร่วมพัฒนาการเชื่อมต่อไปยัง platform ตัวเองจำนวนมาก เช่น nutanix, vmware, aws, google, azure เป็นต้น ทำให้ทำการสร้าง kubernetes เป็นเรื่องง่าย โดยไม่ต้องพัฒนาส่วนการเชื่อมต่อกับ platform ปลายทางเอง เพียงแค่ระบุรูปแบบ kubernetes cluster (yaml) จากนั้น cluster-api จะสร้าง kubernetes บน platform ปลายทางให้

การติดตั้ง Nutanix NKP จะต้องเตรียม docker และ kubectl ที่เครื่อง jumphost หรือเครื่องที่จะใช้ในการติดตั้ง ตัวอย่างนี้จะเป็นการติดตั้งโดยไม่ access internet และใช้ Rocky Linux

ตรวจสอบ docker ที่เครื่องที่ใช้ในการติดตั้ง สามารถทำได้ตามขั้นตอน Harbor Container Registry Installation

[nutanix@harbor nkp]$ docker version
Client: Docker Engine - Community
 Version:           28.3.0
 API version:       1.51
 Go version:        go1.24.4
 Git commit:        38b7060
 Built:             Tue Jun 24 15:45:22 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.3.0
  API version:      1.51 (minimum version 1.24)
  Go version:       go1.24.4
  Git commit:       265f709
  Built:            Tue Jun 24 15:43:38 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.27
  GitCommit:        05044ec0a9a75232cad458027ca83437aae3f4da
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

การติดตั้งจำเป็นต้องมี Container Registry ที่มี package bundle ของ nkp ขั้นตอนนี้ทำการทดสอบ login จากเครื่องที่ใช้ในการติดตั้ง

[nutanix@harbor nkp]$ docker login 10.55.10.75
Username: admin
Password:

WARNING! Your credentials are stored unencrypted in '/home/nutanix/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

ติดตั้ง kubectl สำหรับเข้าถึง kubernetes

[nutanix@harbor nkp-v2.14.0]$ sudo curl -Lo /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 53.7M  100 53.7M    0     0  74.2M      0 --:--:-- --:--:-- --:--:-- 74.3M
[nutanix@harbor nkp-v2.14.0]$ sudo chmod +x /usr/local/bin/kubectl

ติดตั้ง nkp cliโดย copy Download Link จาก nutanix portal จากหน้า download software

ขั้นตอนการติดตั้ง nkp cli

curl -fsSL https://raw.githubusercontent.com/nutanixdev/nkp-quickstart/main/scripts/get-nkp-cli | bash

#When prompted, enter the copy Downloadd Link

[nutanix@harbor nkp]$ curl -fsSL https://raw.githubusercontent.com/nutanixdev/nkp-quickstart/main/get-nkp-cli | bash
Enter 'NKP for Linux' download link: https://download.nutanix.com/downloads/nkp/v2.14.0/nkp_v2.14.0_linux_amd64.tar.gz?Expires=1751224588&Key-Pair-Id=APKAJTTNCWPEI42QKMSA&Signature=HcZFo-J4aqVeBFxBH-6x4zsKbD-EYdEzg-zPylsLxOBbIQ6j5XmExGFEV~fCR8UyPwEpN4xw0TY~4D2MybSgcJfQ4Hm-uB-mIP2IaJ3xDmSaHcQTg-9pR5bg36HbRf~bscjnuMe7feKPj5getHXcFA3jpeMukz9KqID5ZAaDrI76KM~uv4oklIoHTIAZ2JoOb3nbmRZlGMblhB8AGTvrg8lMIozvqEYZdiqU9icpBL~uDEOFrfPFJo~jveT7dymHdmD33j--wzNNS9hPZMO1snMja6iObh~6q2G3J4STx9f6~cwXaCQE2nzXNn74eTPWi9GeZ9F~GdWpLcFZ5ARepQ__
NKP CLI installed successfully!
[nutanix@harbor nkp]$ nkp version
diagnose: v0.10.1
imagebuilder: v0.22.3
kommander: v2.14.0
konvoy: v2.14.0
mindthegap: v1.16.0
nkp: v2.14.0

Download NKP Airgapped Bundle สำหรับการติดตั้ง โดยการ copy Download Link จากหน้า nutanix portal

ขั้นตอนการ Download ผ่าน curl cli

[nutanix@harbor nkp]$ curl -o nkp-air-gapped-bundle_v2.14.0_linux_amd64.tar.gz "https://download.nutanix.com/downloads/nkp/v2.14.0/nkp-air-gapped-bundle_v2.14.0_linux_amd64.tar.gz?Expires=1751229414&Key-Pair-Id=APKAJTTNCWPEI42QKMSA&Signature=FPMLrX1BDFrqGfpMk6haH8TFwSoRkgMH27U9J6QtLO7kbldjss6UcIYtr0sngnT4AvxeX7j4T0B-1TsMXFgdNFyNpvycNCJEp1z-nG-7VcJfm9K2ZNdcsp-ZqLD42RuhRx~uS0UR7kw3ugwcxKgu9vcBNlDq1gh6VRVH8b9QtA6fp16DKQT6CiHyptVqqL-X1wRHPM7ti3LxMNaNhlrndF9D4v89ajxWfaNAICPRpbLjpLGxoviendqjd~sRd2ZsJUdKs6f78YMWCXHhGUH5Y0U38GwVFolQethXmQfvoDF5QvreOPKa6E1bZhQbVDcb3BfVqyPj0x-Zn7rl8Ww5wA__"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 15.4G  100 15.4G    0     0  42.0M      0  0:06:17  0:06:17 --:--:-- 38.7M

Download Konvoy Image โดยการ Copy Download Link จาก nutanix portal

ขั้นตอนการ download จาก curl cli

[nutanix@harbor nkp]$ curl -o konvoy-image-bundle-v2.23.2_linux_amd64.tar.gz "https://download.nutanix.com/downloads/nkp/v2.14.0/konvoy-image-bundle-v2.22.2_linux_amd64.tar.gz?Expires=1751229375&Key-Pair-Id=APKAJTTNCWPEI42QKMSA&Signature=MvjKv3bi87AHpof4rmTgCoAmcR1qxs6y-y5C9Yhd7QvxJFDM8PuemyKdfV7hfObO5Q60e1vo9P9LPgI59gzCgrGa5OZp3df13d6N86drQpThuORkI7uD6LUMF30hqki9XEW8jDRIgn00QrTE3VWoD5KXfunoKCtLGlbDMSJt46w5pjspmFy0237qSobbgo9NfDOORnFiRqi8mBknrH122op8YzZ-kjk0zbHAFdSxouGRZBUEOXlqQ5MavLb9wzZq9PulvHCw9eDMx5L-TmYbXifMlyuh4TKvoqjRxKYCNh5ntPOYdSpgLsHN3Uh3YU1yW175fjsvZquK~YHRcjS~hw__"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  330M  100  330M    0     0  83.2M      0  0:00:03  0:00:03 --:--:-- 83.2M

File ที่ download จากขั้นต้น

[nutanix@harbor nkp]$ ll
total 16674272
-rw-r--r--. 1 nutanix nutanix   346426397 Jun 29 10:36 konvoy-image-bundle-v2.23.2_linux_amd64.tar.gz
-rw-r--r--. 1 nutanix nutanix 16633123312 Jun 29 10:43 nkp-air-gapped-bundle_v2.14.0_linux_amd64.tar.gz

ทำการ unzip file nkp-air-gapped-bunndle และทำการ load docker image เข้าไปยังระบบ docker ของเครื่อง จากนั้นทำการ upload image ไปยัง docker registry ที่เตรียมไว้ด้วย nkp cli

[nutanix@harbor nkp]$ tar -xvf nkp-air-gapped-bundle_v2.14.0_linux_amd64.tar.gz

[nutanix@harbor nkp]$ cd nkp-v2.14.0/
[nutanix@harbor nkp-v2.14.0]$ docker load -i konvoy-bootstrap-image-v2.14.0.tar
[nutanix@harbor nkp-v2.14.0]$ docker image ls | grep konvoy-bootstrap
mesosphere/konvoy-bootstrap   v2.14.0   6692d49a959f   3 months ago   2.08GB

[nutanix@harbor nkp-v2.14.0]$ nkp push bundle --bundle ./container-images/konvoy-image-bundle-v2.14.0.tar --to-registry=10.55.10.75/mirror --to-registry-username=admin --to-registry-password=Harbor12345 --to-registry-insecure-skip-tls-verify

[nutanix@harbor nkp-v2.14.0]$ nkp push bundle --bundle ./container-images/kommander-image-bundle-v2.14.0.tar --to-registry=10.55.10.75/mirror --to-registry-username=admin --to-registry-password=Harbor12345 --to-registry-insecure-skip-tls-verify

สร้าง ssh key สำหรับ access VM ที่สร้างขึ้นโดยระบบ

[nutanix@harbor ~]$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/nutanix/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/nutanix/.ssh/id_ed25519
Your public key has been saved in /home/nutanix/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256://XGZ4k3rrFq2BC+QWmPk3NJNt972vQ5vHrnSuOyTm0 nutanix@harbor.local
The key's randomart image is:
+--[ED25519 256]--+
|                 |
|                 |
|          .      |
|         = +     |
|        S B + .  |
|         X + o . |
|          @ ..E.+|
|         o =.*o&X|
|          .o=*XX%|
+----[SHA256]-----+

ตัวอย่างนี้ใช้ ed25519 สำหรับการสร้าง key ซึ่งมีข้อดีดังนี้

Ed25519 offers several key advantages over RSA:

Performance: Ed25519 is significantly faster for both signing and verification operations. It uses elliptic curve cryptography which requires less computational overhead than RSA’s integer factorization approach.

Key size: Ed25519 uses 256-bit keys that provide security equivalent to 3072-bit RSA keys. This means much smaller key sizes for the same security level, reducing storage and transmission overhead.

Security design: Ed25519 was designed from the ground up to avoid many implementation pitfalls that have plagued RSA. It’s resistant to timing attacks, doesn’t require careful random number generation during signing (unlike RSA), and uses deterministic signatures.

Simplicity: The algorithm has fewer parameters and configuration options, reducing the chance of implementation errors. RSA requires choosing padding schemes, key sizes, and other parameters that can introduce vulnerabilities if done incorrectly.

Side-channel resistance: Ed25519 is designed to be resistant to side-channel attacks like timing and power analysis attacks, whereas RSA implementations often leak information through timing variations.

Future-proofing: While both are considered secure today, Ed25519’s elliptic curve foundation generally scales better as security requirements increase over time.

The main trade-off is that RSA is more widely supported in legacy systems, but for new applications, Ed25519 is generally the better choice due to its superior performance and security characteristics.

ติดตั้ง NKP ด้วย cli โดยจะต้อง export user และ password variable ก่อนจะ run nkp command

[nutanix@harbor nkp-v2.14.0]$ export NUTANIX_USER="admin"
export NUTANIX_PASSWORD='nx2Tech581!'
[nutanix@harbor nkp-v2.14.0]$ nkp create cluster nutanix --cluster-name nkp-at-next \
--control-plane-prism-element-cluster DM3-POC010 \
--worker-prism-element-cluster DM3-POC010 \
--control-plane-subnets primary-DM3-POC010 \
--worker-subnets primary-DM3-POC010 \
--control-plane-endpoint-ip 10.55.10.10 \
--csi-storage-container default \
--endpoint https://10.55.10.7:9440 \
--control-plane-vm-image nkp-rocky-9.5-release-1.31.4-20250214003015.qcow2 \
--worker-vm-image nkp-rocky-9.5-release-1.31.4-20250214003015.qcow2 \
--kubernetes-service-load-balancer-ip-range 10.55.10.11-10.55.10.14 \
--registry-mirror-url https://10.55.10.75/mirror \
--registry-mirror-username admin \
--registry-mirror-password Harbor12345 \
--registry-mirror-cacert /home/nutanix/harbor.crt \
--ssh-public-key-file /home/nutanix/.ssh/id_ed25519.pub \
--ssh-username nutanix \
--airgapped \
--insecure \
--verbose 5 \
--timeout 0 \
--self-managed

กรณี airgap ต้องระบุ registry mirror เพื่อใช้ในการติดตั้ง ตามขั้นตอน upload konvoy และ kommander image ในขั้นตอนก่อนหน้า

ข้อมูล harbor.crt ได้มาจากขั้นตอนการสร้าง harbor หรือ สามารถ export ได้ด้วย cli

openssl s_client -connect 10.55.10.75:443 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM > /home/nutanix/harbor.crt

กรณีที่การติดตั้งสามารถ access internet ได้ไม่จำเป็นต้องใช้ option –airgapped และระบบจะทำการติดตั้งโดยใช้ค่า default ถ้าไม่ระบุใน cli เช่น ขนาดของ VM และจำนวน VM เช่น Control plane จำนวน 3 VMs และ Worker จำนวน 4 VMs ดังตัวอย่าง options อื่นๆ ดังนี้

𝗘𝗡𝗩𝗜𝗥𝗢𝗡𝗠𝗘𝗡𝗧 𝗩𝗔𝗥𝗜𝗔𝗕𝗟𝗘𝗦
export CLUSTER_NAME="wskn-mgmt-ag" # Name of the Kubernetes cluster
export NUTANIX_PC_FQDN_ENDPOINT_WITH_PORT="https://10.168.100.4:9440" # Nutanix Prism Central endpoint URL with port
export CONTROL_PLANE_IP="10.168.102.30" # IP address for the Kubernetes control plane
export IMAGE_NAME="nkp-rocky-9.4-release-1.29.6-20240816215147" # Name of the VM image to use for cluster nodes
export PRISM_ELEMENT_CLUSTER_NAME="wskn-nongpu" # Name of the Nutanix Prism Element cluster
export SUBNET_NAME="non-gpu-airgap" # Name of the subnet to use for cluster nodes
export PROJECT_NAME="default" # Name of the Nutanix project
export CONTROL_PLANE_REPLICAS="3" # Number of control plane replicas
export CONTROL_PLANE_VCPUS="4" # Number of vCPUs for control plane nodes
export CONTROL_PLANE_CORES_PER_VCPU="1" # Number of cores per vCPU for control plane nodes
export CONTROL_PLANE_MEMORY_GIB="16" # Memory in GiB for control plane nodes
export WORKER_REPLICAS="3" # Number of worker node replicas
export WORKER_VCPUS="8" # Number of vCPUs for worker nodes
export WORKER_CORES_PER_VCPU="1" # Number of cores per vCPU for worker nodes
export WORKER_MEMORY_GIB="32" # Memory in GiB for worker nodes
export NUTANIX_STORAGE_CONTAINER_NAME="default-container-xxx" # Name of the Nutanix storage container
export CSI_FILESYSTEM="ext4" # Filesystem type for CSI volumes
export CSI_HYPERVISOR_ATTACHED="true" # Whether to use hypervisor-attached volumes for CSI
export LB_IP_RANGE="10.168.102.31-10.168.102.31" # IP range for load balancer services
export SSH_KEY_FILE="/root/.ssh/id_rsa.pub" # Path to the SSH public key file
export NUTANIX_USER="admin" # Nutanix PrismCentral username (left blank for security)
export NUTANIX_PASSWORD="" # Nutanix PrismCentral password (left blank for security)
export REGISTRY_URL="https://registry.wskn-ag.local/library" # URL for the private container registry
export REGISTRY_USERNAME="admin" # Username for authenticating with the private registry (left blank for security)
export REGISTRY_PASSWORD="" # Password for authenticating with the private registry (left blank for security)
export REGISTRY_CA="/root/wskn-ag-certs/server.crt" # Path to the CA certificate for the private registry

𝗜𝗡𝗦𝗧𝗔𝗟𝗟𝗔𝗧𝗜𝗢𝗡 𝗖𝗢𝗠𝗠𝗔𝗡𝗗
nkp create cluster nutanix --cluster-name $CLUSTER_NAME \
    --endpoint $NUTANIX_PC_FQDN_ENDPOINT_WITH_PORT\
    --control-plane-endpoint-ip $CONTROL_PLANE_IP \
    --control-plane-vm-image $IMAGE_NAME \
    --control-plane-prism-element-cluster $PRISM_ELEMENT_CLUSTER_NAME \
    --control-plane-subnets $SUBNET_NAME \
    --control-plane-pc-project $PROJECT_NAME \
    --control-plane-replicas $CONTROL_PLANE_REPLICAS \
    --control-plane-vcpus $CONTROL_PLANE_VCPUS \
    --control-plane-cores-per-vcpu $CONTROL_PLANE_CORES_PER_VCPU \
    --control-plane-memory $CONTROL_PLANE_MEMORY_GIB \
    --worker-vm-image $IMAGE_NAME \
    --worker-prism-element-cluster $PRISM_ELEMENT_CLUSTER_NAME \
    --worker-subnets $SUBNET_NAME \
    --worker-pc-project $PROJECT_NAME \
    --worker-replicas $WORKER_REPLICAS \
    --worker-vcpus $WORKER_VCPUS \
    --worker-cores-per-vcpu $WORKER_CORES_PER_VCPU \
    --worker-memory $WORKER_MEMORY_GIB \
    --ssh-public-key-file $SSH_KEY_FILE \
    --csi-storage-container $NUTANIX_STORAGE_CONTAINER_NAME \
    --csi-file-system $CSI_FILESYSTEM \
    --csi-hypervisor-attached-volumes=$CSI_HYPERVISOR_ATTACHED \
    --kubernetes-service-load-balancer-ip-range $LB_IP_RANGE \
    --insecure \
    --self-managed \
    --airgapped \
    --registry-mirror-url $REGISTRY_URL \
    --registry-mirror-cacert $REGISTRY_CA \
    --registry-mirror-username=$REGISTRY_USERNAME \
    --registry-mirror-password=$REGISTRY_PASSWORD

หลังจาก nkp cli ทำงาน ระบบจะสร้าง kubernetes ชั่วคราวโดยใช้ KIND (kubernetes in docker) โดย load package ที่ได้จาก konvoy-bootstrap-image ระบบจะสร้าง kubeconfig ตาม format ชื่อ cluster สามารถใช้ access KIND cluster เพื่อตรวจสอบสถานะการทำงานได้ดังนี้

[nutanix@harbor nkp-v2.14.0]$ export KUBECONFIG=nkp-at-next-bootstrap.conf

ทำการ list logs ระหว่างการติดตั้งได้จาก container capx-controller-manager สำหรับ nutanix platform สำหรับ platform อื่นๆ สังเกตุจาก digit ที่ 4 คือ x = nutanix, a=aws, g=google, pp = preprovision, v=vmware, vcd=vmware cloud director, z=azure

[nutanix@harbor nkp-v2.14.0]$ kubectl logs -f capx-controller-manager-745948468d-dkjf5 -n capx-system

ตรวจสอบว่ามี container อะไรบ้างที่ถูกใช้ในการติดตั้ง

[nutanix@harbor nkp-v2.14.0]$ kubectl get pods -A -o yaml | grep image: | sort -h | uniq
      image: docker.io/kindest/kindnetd:v20240813-c6f155d6
      image: docker.io/kindest/local-path-provisioner:v20240813-c6f155d6
      image: docker.io/mesosphere/cluster-api-preprovisioned-controller:v0.34.2
      image: docker.io/mesosphere/kommander2-runtime-extension:v1.2.0
      image: ghcr.io/mesosphere/cluster-api-addon-provider-helm/cluster-api-helm-controller:v0.2.4-d2iq.0
      image: ghcr.io/mesosphere/cluster-api-controller:v1.9.3-d2iq.1
      image: ghcr.io/mesosphere/kubeadm-bootstrap-controller:v1.9.3-d2iq.1
      image: ghcr.io/mesosphere/kubeadm-control-plane-controller:v1.9.3-d2iq.1
      image: ghcr.io/mesosphere/mindthegap:v1.17.0
      image: ghcr.io/nutanix-cloud-native/cluster-api-provider-nutanix/controller:v1.5.3
      image: ghcr.io/nutanix-cloud-native/cluster-api-runtime-extensions-helm-chart-bundle-initializer:v0.27.4
      image: ghcr.io/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix:v0.27.4
      image: mcr.microsoft.com/k8s/azureserviceoperator:v2.8.0
      image: quay.io/jetstack/cert-manager-cainjector:v1.16.1
      image: quay.io/jetstack/cert-manager-controller:v1.16.1
      image: quay.io/jetstack/cert-manager-webhook:v1.16.1
      image: registry.k8s.io/cluster-api-aws/cluster-api-aws-controller:v2.7.1
      image: registry.k8s.io/cluster-api-azure/cluster-api-azure-controller:v1.17.2
      image: registry.k8s.io/cluster-api-gcp/cluster-api-gcp-controller:v1.8.1
      image: registry.k8s.io/cluster-api-vsphere/cluster-api-vsphere-controller:v1.12.0
      image: registry.k8s.io/coredns/coredns:v1.11.3
      image: registry.k8s.io/etcd:3.5.15-0
      image: registry.k8s.io/kube-apiserver-amd64:v1.31.4
      image: registry.k8s.io/kube-apiserver:v1.31.4
      image: registry.k8s.io/kube-controller-manager-amd64:v1.31.4
      image: registry.k8s.io/kube-controller-manager:v1.31.4
      image: registry.k8s.io/kube-proxy-amd64:v1.31.4
      image: registry.k8s.io/kube-proxy:v1.31.4
      image: registry.k8s.io/kube-scheduler-amd64:v1.31.4
      image: registry.k8s.io/kube-scheduler:v1.31.4

รอจนกว่าระบบจะติดตั้งสมบูรณ์ โดยขั้นตอนการติดตั้งระบบจะลบ KIND และย้าย configuration ไปยัง cluster ปลายทาง โดยจะสร้าง kubeconfig ไว้ให้เพื่อ access ไปยัง cluster ที่สร้างขึ้นใหม่ ใช้เวลาประมาณ 10-20 นาที

Cluster default/nkp-at-next kubeconfig was written to to the filesystem.
You can now view resources in the new cluster by using the --kubeconfig flag with kubectl.
For example: kubectl --kubeconfig="/home/nutanix/nkp/nkp-v2.14.0/nkp-at-next.conf" get nodes

Starting kommander installation
 ✓ Deploying Flux
 ✓ Deploying Ingress certificate
 ✓ Creating kommander-overrides ConfigMap
 ✓ Deploying Git Operator
 ✓ Creating GitClaim for management GitRepository
 ✓ Creating GitClaimUser for accessing management GitRepository
 ✓ Configuring HelmRepositories for airgapped environment
 ✓ Deploying Flux configuration
 ✓ Deploying Kommander Operator
 ✓ Creating KommanderCore resource
 ✓ Cleaning up kommander bootstrap resources
Got a Retry-After 1s response for attempt 1 to https://10.55.10.10:6443/apis/dkp.d2iq.io/v1alpha1/kommandercores?watch=true
 ✓ Deploying ChartMuseum
 ✓ Deploying Gatekeeper
 ✓ Deploying Kommander AppManagement
 ✓ Creating Core AppDeployments
 ✓ 4 out of 13 core applications have been installed (waiting for dex, dex-k8s-authenticator and 7 more)
 ✓ 6 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, kommander and 5 more)
 ✓ 7 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, kommander and 4 more)
 ✓ 8 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, kommander and 3 more)
 ✓ 9 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, kommander-ui and 2 more)
 ✓ 10 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, kubefed and 1 more)
 ✓ 11 out of 13 core applications have been installed (waiting for dex-k8s-authenticator, traefik-forward-auth-mgmt)
 ✓ 12 out of 13 core applications have been installed (waiting for traefik-forward-auth-mgmt)
 ✓ Creating cluster-admin credentials

Cluster was created successfully! Get the dashboard details with:
nkp get dashboard --kubeconfig="/home/nutanix/nkp/nkp-v2.14.0/nkp-at-next.conf"

login ไปยัง nkp management platform โดยใช้ nkp cli เพื่อ generate user และ password รวมถึง dashboard url

[nutanix@harbor nkp-v2.14.0]$ nkp get dashboard --kubeconfig="/home/nutanix/nkp/nkp-v2.14.0/nkp-at-next.conf"
Username: happy_gauss
Password: PuK76Q4AUKRCEALcrU9mAW1SNMsMMxelIexQv8Ngw3ONAQ4dPTNzEl5xBcAr4Uln
URL: https://10.55.10.11/dkp/kommander/dashboard

Access dashboard ตามข้อมูลที่ได้

Harbor Container Registry Installation

Harbor เป็น container registry ที่ได้รับความนิยมมากในปัจจุบัน เนื่องจากมีความสามารถที่ช่วยให้เก็บ container image ไว้ภายในองค์กรแล้วยังมีความสามารถอื่นๆ เช่น การทำ proxy ไปยัง container registry ภายนอกเช่น docker hub เพื่อลดจำนวน request ไม่ให้ติดปัญหา request limit ของ docker hub การทำ replication ระหว่าง Harbor ด้วยกันกรณี DC/DR use case และ integrate กับ container scanning เช่น Trivy, Clair และ Notary สำหรับการทำ image signed เพื่อป้องกันการแก้ไขเปลี่ยนแปลง image

ในการติดตั้ง Harbor ต้องการ Docker และ Docker Compose ขั้นตอนการ install docker ขึ้นอยู่กับ OS โดยตัวอย่างนี้ติดตั้งบน Linux Rocky

#!/bin/bash
# Docker Installation Script for Rocky Linux

# 1. Update system packages
sudo dnf update -y

# 2. Install required packages
sudo dnf install -y dnf-utils device-mapper-persistent-data lvm2

# 3. Add Docker's official repository
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 4. Install Docker Engine
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 5. Start and enable Docker service
sudo systemctl start docker
sudo systemctl enable docker

# 6. Add current user to docker group (to run docker without sudo)
sudo usermod -aG docker $USER

# 7. Verify Docker installation
sudo docker --version
sudo docker run hello-world

# 8. Check Docker service status
sudo systemctl status docker

# 9. Optional: Install Docker Compose (if not using plugin version)
# sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose

echo "Docker installation completed!"
echo "Please log out and log back in for group changes to take effect."
echo "Then you can run 'docker ps' without sudo."

กรณีที่ enable firewall จะต้องทำการ allow service docker ด้วย

# Open Docker ports if firewall is enabled
sudo firewall-cmd --permanent --add-service=docker
sudo firewall-cmd --reload

กรณีนี้ติดตั้งด้วยการใช้ self-signed certificate จำเป็นต้องสร้างไฟล์​ daemon.json และใส่ค่า insecure โดยระบุทั้ง host name, host domain และ IP ถ้าไม่ได้สร้าง record บน DSN server

[nutanix@harbor harbor]$ more /etc/docker/daemon.json
{
    "insecure-registries": ["harbor.local", "10.55.10.75"]
}

ต้อง restart docker หลังจากสร้างไฟล์​ daemon.json เพื่อให้ค่า configuration ถูกนำไปใช้

[nutanix@harbor harbor]$ sudo systemctl restart docker

ทำการ download harbor installation binary และสร้างไฟล์ที่เกี่ยวข้อง

[nutanix@harbor harbor]$ HARBOR_VERSION=$(curl -s https://api.github.com/repos/goharbor/harbor/releases/latest | grep 'tag_name' | cut -d\" -f4)

[nutanix@harbor harbor]$ echo $HARBOR_VERSION
v2.13.1

[nutanix@harbor harbor]$ wget "https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz"

[nutanix@harbor harbor]$ tar xvf harbor-offline-installer-${HARBOR_VERSION}.tgz

[nutanix@harbor harbor]$ cd harbor
[nutanix@harbor harbor]$ cp harbor.yml.tmpl harbor.yml

[nutanix@harbor harbor]$ mkdir certs
[nutanix@harbor harbor]$ mkdir data
[nutanix@harbor harbor]$ mkdir generate-cert

ทำการ generate certificate สำหรับ Harbor ตัวอย่างนี้จะตั้ง host name เป็น harbor.local โดยเป็น self-signed certificate


# Generate CA Key
[nutanix@harbor harbor]$ cd generate-cert
[nutanix@harbor generate-cert]$ openssl genrsa -out ca.key 4096

# Generate CA certificate
[nutanix@harbor generate-cert]$ openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=TH/ST=TH/L=BK/O=IT/OU=IT/CN=harbor.local" \
-key ca.key \
-out ca.crt

#Generate Server key
[nutanix@harbor generate-cert]$ openssl genrsa -out harbor.local.key 4096

# Generate CSR (Certificate Signing Requests)
[nutanix@harbor generate-cert]$ openssl req -sha512 -new \
-subj "/C=TH/ST=TH/L=BK/O=IT/OU=IT/CN=harbor.local" \
-key harbor.local.key \
-out harbor.local.csr

# Create file v3.ext
[nutanix@harbor generate-cert]$ vi v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.0=harbor.local
IP.0=10.55.10.75 #harbor IP address

# Generate Server Certificate signed with above CA generated
[nutanix@harbor generate-cert]$ openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in harbor.local.csr \
-out harbor.local.crt

# Optional - Convert the certificate to PEM format for docker
[nutanix@harbor generate-cert]$ openssl x509 -inform PEM -in harbor.local.crt -out harbor.local.cert

#copy harbor.local.crt and harbor.local.key for harbor server
[nutanix@harbor generate-cert]$ cp harbor.local.crt ../certs
[nutanix@harbor generate-cert]$ cp harbor.local.key ../certs

แก้ไขไฟล์ harbor.yml โดยมีค่าที่จำเป็นต้องแก้ดังนี้

hostname:
certificate:
private_keys:
data_volume:

hostname เป็น domain name ที่ map ไว้ที่ DNS server แต่ถ้าไม่มี DNS server สามารถใช้ IP แทนได้ดังตัวอย่างนี้

Certificate และ private_keys ได้จากขึ้นตอนสร้าง certificate ข้างต้น

data_volume คือข้อมูลระบบและ docker image ที่จัดเก็บใน harbor จำเป็นในการ backup กรณีระบบมีปัญหา และต้องการ recover

run install command เพื่อติดตั้ง harbor ทั้งนี้สามารถติดตั้ง components อื่นๆ ร่วมด้วยโดยใช้ options –with-trivy –with-notary –with-clair –with-chartmuseum

trivy และ clair สำหรับ vulnerability scanner และ notary สำหรับ container signing และ chartmuseum เพื่อให้ harbor สามารถเก็บ helm chart ได้ ตัวอย่างนี้จะติดตั้งเฉพาะ harbor

[nutanix@harbor harbor]$ sudo ./install.sh

หลังจากติดตั้งเสร็จ ทดสอบ login

[nutanix@harbor harbor]$ docker login 10.55.10.75
Username: admin
Password:

WARNING! Your credentials are stored unencrypted in '/home/nutanix/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

กรณีที่ต้องการทำ registry proxy จะต้องไปสร้าง enpoint ที่ต้องการ connect ก่อน โดยเข้าไปที่เมนู Registries และ click +NEW ENDPOINT

กรณีนี้เลือก Docker Hub จากนั้นกรอกข้อมูล Access ID และ Access Secret เพื่อ connect ไปยัง Docker hub โดยต้องไปสร้าง access token ที่ Docker Hub ก่อน สามารถทำทดสอบการเชื่อมต่อโดย click TEST CONNECTION กด OK เพื่อเพิ่ม Endpoint

สร้าง Projects โดยเข้าไปที่เมนู Projects และ click + NEW PROJECT

กรอกข้อมูล Project name เลือก Access Level เลือก Proxy Cache และเลือก endpoint ที่สร้างในขั้นต้น และ click OK เพื่อสร้าง Project

สร้าง Projects อื่นๆ ตัวอย่างนี้สร้าง Project mirror สำหรับติดตั้ง NKP

กรอกข้อมูล Project

Project ที่สร้างขึ้น

BIAN – The Banking Industry Architecture Network

BIAN เป็นมาตรฐานกลางที่ธนาคารกำหนดขึ้นมาเพื่อให้เป็น common framework ของระบบธนาคาร โดยกำหนดคำกำจัดความของบริการ (service definition) ของระบบ Banking IT Services เพื่อการสื่อสารที่ตรงกัน ทำให้เกิดการทำงานร่วมกันระหว่างธนาคารได้ง่าย (Interoperability issue) รวมทั้งเป็นเป็น reference กลางของระบบธนาคาร และมาตรฐานการให้บริการด้วย APIs

BIAN นำเสนอโครงสร้างของการบริการธนาคารเป็นในรูปแบบลำดับชั้น ประกอบด้วย

Business Area กลุ่มของ Function งานต่างๆ ในระบบธนาคาร

Business Domain เป็นกลุ่มของส่วนงานภายใน Business Area

Service Domain เป็นส่วนของงานภายใน Business Domain เพื่อทำงานอย่างใดอย่างหนึ่งเช่น Service Domain ชื่อ SavingAccount ก็จะสื่อถืองานเกี่ยวกับ Service Account ที่ provide API เพื่อทำงานเกี่ยวกับ Saving Account

BIAN APIs ได้กำหนดข้อกำหนดมาตรฐานของ API โดยให้แบ่งเป็นแต่ละ Layer

Experience APIs เป็น APIs  สำหรับ user interact ซึ่งจะ specific สำหรับกลุ่มของการใช้งาน

Process APIs เป็น process ของแต่ละ APIs ที่อาจจะมีการเรียกใช้หลาย APIs เพื่อประมวลผลและแสดงผลลัพธ์ที่ต้องการ (Orchestrator)

System APIs เป็น APIs ตามข้อกำหนดของ BIAN และ BIAN Object Model ในการเข้าถึง System of Record (SOR) อื่นๆ เพื่อไม่ให้มีการเรียกใช้ Resource ได้โดยตรง ป้องกันไม่ให้เกิด dependency กับระบบรอบข้าง

ตัวอย่างการ Develop APIs ด้วย BIAN

ตัวอย่าง BIAN Architecture layer

ข้อมูลเพิ่มเติมอื่นๆ

BIAN Architecture

BIAN Service Landscape – Matrix view

BIAN Service Landscape – Value chain view

Semantic APIs Practitioner guide

Semantic APIs

C4 Architecture Model

C4 Model เป็นรูปแบบในการใช้ diagram เพื่ออธิบาย software architecture สำหรับสือสารภายในและภายนอกของทีม software development โดยแบ่งเป็นระดับตามจุดประสงค์ของการสื่อสารใน 4 ระดับ

System Context เป็น high-level ของระบบ software system ที่ให้บริการกับ User หรือ system ด้วยกันเอง และแสดงรายเอียดของ system อื่นๆ ที่เกี่ยวข้องด้วย (system dependency) มีการใช้ term อย่างเช่น application, product, service เป็นต้น scope ของ software system สามารถมองได้หลายมุม เช่น ในระดับ ทีม development, code repository หรือ กลุ่มของ application ที่ต้อง deployment ไปด้วยกัน

Container กลุ่มของ application และ data store ที่ประกอบอยู่ใน software system เพื่อให้ระบบทำงานได้ เช่น web application, mobile app, serverless function, database, file system, shell script เป็นต้น

Component กลุ่มของ function ที่เกี่ยวข้องภายใน Container ที่ประกอบกันเพื่อให้บริการกับภายนอก components อาจจะมีมากกว่าหนึ่ง component ในหนึ่ง container แต่ทั้งหมดจะมี deploy unit เดียวกัน หรือทำงานใน process เดียวกัน

Code เป็น implementation detail ของ component โดยแสดงในรูปแบบ UML class diagram, entity relationship diagram ปกติ code diagram จะถูก generate จาก development tool

K8S Sizing and Key Consideration

แนวทางเบื้องต้นเพื่อใช้ในการ Sizing ขนาดของ Kubernetes Cluster สำหรับ Application ใช้งานใน Kubernetes Environment สามารถใช้แนวทางดังนี้ในการพิจารณา

ต้องพิจารณาปัจจัยหลักๆ ประกอบด้วย 4 ปัจจัยคือ

Pods per node density

คือจำนวนของ Pod ซึ่งเป็น deployable unit (containers) ที่สามารถทำงานได้ต่อหนึ่งเครื่อง (Node) โดยเราต้องมีข้อมูล

  • vCPU ที่ pod ใช้งาน อาจจะใช้ cpu limit (ปกติจะต้องทำ cpu limit ไว้ เพื่อไม่ให้ pod ใช้ cpu ของเครื่องหมดจนส่งผลกระทบทั้ง cluster)
  • vCPU ของเครื่อง (Node) สำหรับใช้ในการ run pod
  • vCPU ทั้งหมดของเครื่อง (Node)
  • เปอร์เซ็นต์ของ cpu ที่จะ reserve ไว้สำหรับงานอื่นๆ เช่น system process

จากนั้นก็จะสามารถหาค่า pod per node density ได้จากการหาว่ามี vCPU สำหรับใช้ได้จริงเท่าไหร่ แล้ว หารด้วยค่าเฉลี่ยของ vCPU ที่ pod ใช้งาน เช่น

Node VM มี 8 vCPU และต้องการ reserve vCPU ไว้ 15% สำหรับ system process โดยค่าเฉลี่ยที่ pod ใช้งาน cpu core คือ 0.2 vCPU ต่อ Pod ก็จะหา pod per node density ได้ดังนี้

vCPU ที่ใช้งานได้ = 8 – (8*15/100) = 6.8 vCPU

Pod per node density = 6.8/0.2 = 34

** การกำหนดค่าเฉลี่ย vCPU ที่ pod ใช้งาน ต้องพิจารณา pods per CPU core จากคำแนะนำของ Kubernetes ที่ 1 CPU core จะสามารถรองรับได้ที่มากที่สุด 10 pods และจำนวน pod ต่อ Node ไม่ควรเกิน 110 pods – https://kubernetes.io/docs/setup/best-practices/cluster-large/

Master Node’s hardware config

ใช้ข้อมูลจาก kubernetes.io สำหรับเลือกขนาดของ Master Node

และต้องพิจารณา size ของเครื่องเพื่อรองรับ etcd คือ

ซึ่งถ้าต้องการ high availability จะต้องมีจำนวน node เป็นเลขคี่ตั้งแต่ 3 ขึ้นไป (Quorum)

ตัวอย่างเช่น ถ้าเรามี Worker node อยู่ 11 Node และต้อง support client จำนวน 500 client เราต้องใช้ Master node ด้วย spec 4vCPU memory 15 GBs

Number of Master Node in Cluster

เพื่อให้ระบบทำงานได้แม้จะมี Master node บางตัวไม่สามารถทำงานได้ ต้องพิจารณาจำนวนของ Master node ดังนี้

ตัวอย่างเช่น ถ้าต้องการให้ระบบยังคงทำงานได้ กรณีที่มี Master Node 2 ตัวเกิดปัญหา เราต้องเลือกจำนวน Master Node เป็น 5

High Availability and Fault Tolerance

ปัจจัยที่ต้องใช้ในการพิจารณามีดังนี้

  • จำนวน Node ที่มีปัญหา ณ​ เวลาใดเวลาหนึ่งพร้อมกัน
  • Node ที่ต้อง reserve ไว้ในกรณีที่มีการ update ระบบ
  • Pod per node density
  • จำนวน Node ที่เหลืออยู่หลังจาก Failure
  • จำนวน Capacity ที่เหลือสำหรับให้ Pod ทำงานได้หลังจาก Failure
  • จำนวน CPU core ที่เหลืออยู่หลังจาก Pod ทำงานเรียบร้อย หลังจาก Failure (rescheduling)

การหาจำนวน Node เช่น ถ้าเรามี 100 pods และต้องการให้ run 34 pods ต่อ node โดยสามารถ fail ได้ 2 Node ต่อเนื่อง และรองรับกรณี upgrade ระบบ ก็จะคำนวณ sizing ของ Node ได้คือ

100/34 + 2 + 1 = 6 worker node

การหา capacity ที่เหลือ และ pod per node density หลังจาก failure

เช่น มี pod จำนวน 100 pods ทำงานกระจายใน 6 node เมื่อมี 2 nodes ไม่สามารถให้บริการได้

จำนวน pod per node density เบื้องต้น = 100/6 = 16.6

จำนวน node ที่เหลืออยู่หลังจาก failure = 4 Node ซึ่งก็คือ resource หายไป 33% ของทั้งหมด

ทำให้ pod per node density เพิ่มขึ้นเป็น = 100/4 = 25

หลังจากได้ pods per node density แล้วต้องคำนวณด้วย cpu core ที่ pod ใช้งานถึงจะ sizing ขนาดของ node ได้ว่าจะต้องเผื่อ cpu และ memory ไว้ที่เท่าไหร่ถึงจะเหมาะสม

** Kubernetes support cluster auto scaling ได้ในกรณีที่ pod ไม่สามารถ scheduling ได้เนื่องจาก resource มีไม่เพียงพอ https://kubernetes.io/docs/concepts/cluster-administration/cluster-autoscaling/ แต่ก็จะมีช่วงเวลาที่ pod ไม่สามารถทำงานได้ในระหว่างที่มีการสร้าง node ใหม่เพื่อ join เข้ามาใน cluster

Workload Characteristic

เพื่อที่จะได้ค่า cpu และ memory ที่ pod ต้องการใช้จะต้องมีข้อมูลการทำงานจริงของ pod แล้วนำมาหาค่าเฉลี่ยเพราะแต่ละ pod ประกอบด้วย container ที่ application ถูกสร้างด้วย technology, architecture, algorithm ที่มี load หรือ usage แตกต่างกันในแต่ละช่วงเวลาการใช้งาน ตัวอย่าง memory ต่อ container ของแต่ละภาษา เช่น

  • Legacy JEE. 2Gb+ mem per container
  • SpringBoot 1Gb+ mem per container
  • NodeJS. 256Mb+ mem per container
  • Go 256Mb+ mem per container

Case Study

หา Sizing ของ Kubernetes Cluster เพื่อรองรับ 100 Springboot containers โดยมีข้อมูลดังนี้

  • Pod ทำงานโดยใช้ 0.2 vCPU และ Memory 0.4 GB
  • จำนวน pod per node density เท่ากับ 34
  • สามารถ fail ติดต่อกันได้ 2 ครั้ง
  • มี node ไว้ reserve สำหรับการ upgrade
  • ต้องมีการสำรอง resource ไว้ 15% สำหรับระบบ