จดๆ ลงมือทำ Helm Essential > Helm Package

สำหรับเรื่องนี้จะเป็นตอนที่สาม และที่ผมมาลองเรียนตัว Helm Package จากคุณโจโจ้ JumpBox สัปดาห์นี้จะเรียนแบบมืนๆนิดนึง พอดีเกิดปัญหา Infra ราบเรียบสะอาดหมดจรดเลย เลยมายุ่งกู้สภาพนิดนึง

ก่อนเรียนเรารู้ และไม่รู้อะไรบ้าง

เนื่องจากเป็น End-User ที่ดี ดังนั้น เราไม่ได้สร้าง Helm Package หา Install ใช้งานจบ และถ้าพัง มุม Dev อ่านะ ก็ Install ใหม่ 555

แลัวโลกก่อน Helm ถ้าใช้งานจริงทำยังไง

  • ทำมือ
  • เขียน Script อ่านไฟล์ properties แล้วมา replace ตาม KEY ที่เตรียมไว้

Connecting The Dot

คุณโจ้แชร์เทคนิคการเรียน เพื่อให้เข้ามาขึ้น โยกการเชื่อมโยงกับของใกล้ตัว โดยมี 3 ส่วนได้แก่

  • Active Learning to Text - เชื่อมโยงที่เรียนในอดีต หรือ อาจจะไปฟังงาน Event Meetup ก็ได้
  • Active Learning to Self - เชื่อมโยงกับเหตุการณ์ในอดีต
  • Active Learning to World - เชื่อมโยงกับเหตุการณ์รอบตัว

เครื่องมือที่แนะนำ https://daily.dev/ มัน Feed รวมบทความสำหรับงาน IT ที่สนใจ ที่ลองๆกดดูนะ น่าจะเหมือนทำงานแรกที่เมื่อก่อนเราเปิด blognone ทุกเช้า หลังๆตามใน FB / X และ

พี่มาร์ค แอบมองเราอยู่ ระหว่างที่เรียน ads มันก็มา 555555555

Helm Package

Helm Package - หุ้มตัว K8S Object ออกมา อารมณ์แบบการสร้าง Image / Container หุ้มอีกที ทำ abstraction ซ้อนอีกที โดยถ้าเราอยากรู้ข้างในมึอะไร สามารถลอง pull ลงมาได้ เหมือน docker git เลย

helm pull <Path_To_Chart>

helm pull bitnami/nginx          //ดึงมันลงมา
helm pull bitnami/nginx --untar  //ให้มันแตก Tar ออกมาด้วย

เรื่องของชื่อ สำคัญไฉน ?

ถ้านึกไม่ออกว่าจัดการสร้าง Helm ออกมายังไง ให้ลอกคนอื่น โดยคุณโจ้แนะนำ Step คร่าว

  • หาแนวคิดของชาวบ้าน ดูจาก repo open-source ที่สนใจ มาตั้งก่อน เช่น backstage/charts / argo-helm/charts
  • ลองมา map แนวคิดของเค้า กับข้อกำหนดขององค์กร บางทีมันมี naming pattern พวกที่ทำมาตรฐานจะโดน
  • จดไว้ บอกด้วยว่าทำไมตั้งชื่อแบบนี้ ข้างในทำเพื่ออะไร ? มันจะย้อนกลับใน Blog ก่อนหน้า ที่ทำ ADR จดไว้
  • ตกลงรูปแบบการใช้ หรือ การสอน Helm ให้คนที่เกี่ยวข้อง เพื่อป้องกันปัญหา Anti Pattern เช่น
    ปัญหา มี Helm แต่ก็ Copy แยกไปตาม ENV / SITE อยากคตมันจะเกิดการตัดแปะ
    ความเป็นจริง Helm มันสามารถทำ subchart ได้ ถ้ามี Common หรือ การค่ามันต่างไปนิดนึง สามารถกำหนดใน Config value.yaml ได้

Creat First Helm

helm create <YOUR_NAME> [flags]

ที่นี่เราจะได้ Template ที่มันเตรียม ของมาให้เยอะเลย เท่าที่มันเตรียมขอมาให้เหมาะกับพื้นฐานที่ทุกคนใช้ดีนะ มีตั้งแต่ deployment / hpa / ingress / service / serviceAccount (พวก Security เอามายัดบอกตรงนี้ด้วย ใครรันน้า) ถ้าเราลองเข้าไปสักไฟล์ ผมจิ้มตัว deployment มันจะคล้ายกันนะ

  • จุดที่ 1 มี key เดว Helm เอามาทับ แบบที่ผมทำ Replace String ตรงๆ อันนี้ มันมีโครงสร้าง และเราสามารถใส่เงื่อนไขได้นะ เค้าทำมาให้แล้ว ไม่ต้องคิดเอง
  • จุดที่ 2 ถ้าลองดูดีๆ มันกึ่งการเขียน Coding นะ และมันช่วงแก้ปัญหา Anti Pattern เช่น ลูกค้า A มีทำ Auto Scale แต่ B ไม่มี ถ้าเป็นสมัยก่อน Copy แก้ยกชุด อันนี้เห็นว่าตัว Helm มันเปิดให้เราเขียน Logic แต่ละคนที่เอาไปใช้ มากำหนดใน value.yaml ได้ และพอมันกึ่ง Code Folder Test มาให้ เดานะ

ไฟล์มันเยอะ แล้วที่นี่ ถ้าคนที่เริ่มจาก 0 ไม่รอดแน่ๆ เพราะถ้าตัดแปะ มันอารมณ์แบบแปะ Code หมดแล้ว แต่ Compile ไม่ผ่าน เริ่มทีละเล็กน้อย โดยการลบ Template ที่มันให้มาก่อน

  • template ลบออกหมด เหลือไฟล์ NOTE.txt แล้วค่อยๆใส่ที่เราจะใช้ลงไป
  • NOTE.txt เอาไส้มันออกไปครับ
ก่อน
หลัง

จากนั้นเริ่มตัวที่เล็กที่สุด เข้ามาที่ <HELM-CHART-PATH>/templates

kubectl create deployment sampleaspnet --image=mcr.microsoft.com/dotnet/samples:aspnetapp --dry-run=client -o yaml > deployment.yaml

สิ่งที่ต้องระวังบน Windows ถ้าใช้ท่าแบบนี้ บน PowerShell 5 (Default ของ Windows) มันจะสร้างไฟล์ให้ แต่ Encoding จะเป็น UTF-16 LE ต้องแก้เป็น UTF-8 ก่อน

ไม่งั้นจะเจอปัญหาตอน Replace Value และ Error แบบนี้ error converting YAIL to JSON: yaml: invalid map key: map[interface [interface (".Release.Name":interface {}(nil))

ผมตอนแรกแปลกใจเหมือนกัน ลองทำแล้วเจอ เลยหนีไป wsl มันทำได้ เลยลองเทียบไฟล์ที่ได้จาก powershell กับ wsl ดูชัดเจน โดยวางยา ถ้าใครไม่อยากประสบปัญหาใช้ wsl หรือขยับ PowerShell ไป 6++ ซะ เอาจริงๆอีกตัวที่ต้องระวังใน Powershell 5 ที่นึกได้ EncodeBase64 ทำไปแล้ว kube อ่านไม่ได้ 5555

เมื่อก่อนน่าจะแก้โดยการขยับ Powershell แต่พอใช้อีกเครื่องลืมลงเลยเจอ

Helm Predefined  {{ KeyToValue }}

รูปแบบ {{ KeyToValue }} ที่มันเอามา replace มาจาก Pattern ของภาษา Go เช่น Go-Viper ตอนแรกเข้าใจว่าจาก Jenkins โดยที่ค่า Predefined สามารถกำหนดได้เป็นกลุ่มๆ ตามนี้

  • กลุ่ม .Release.xxx มาจากตอนที่เรา helm install ไปครับ
Release.Name: The name of the release (not the chart)
Release.Namespace: The namespace the chart was released to.
...
  • กลุ่ม .Chart.xxx เอาค่ามาจากไฟล์ Chart.yaml
  • กลุ่ม .Capabilities.xxx ดึงค่าบางอย่างของ Kube อารมณ์แบบ Downward API หรือ ตัว Helm มันเอง คล้ายกับตัว
  • กลุ่ม .Value.xxxx มาจาก Value.yaml มันเป็นค่าทื่เราปรับให้ Helm เอาไปใช้
  • กลุ่ม Files ดึงค่าจากไฟล์ไว้
  • ตัว Note อันนี้เป็น Predefined  แต่มันมีลูกเล่น เอาค่ามาจากกุล่มข้างต้นมาแสดงได้นะ

สำหรับข้อมูลเพิ่มเติม ว่าใช้อะไรได้/ไม่ได้ พวก Built-in Objects กลุ่ม .Release / .Capabilities.xxx หรือ Files ให้ดูจาก Helm:Built-in Objects ครับ

ถ้าใครอ่าน blog ผมมาเรื่องตอนนี้เราจะอยู่ที่ <HELM-CHART-PATH>/templates ให้ ถอยออกมาก่อน <HELM-CHART-PATH> จากนั้นลองตรวจสอบก่อน ว่า Deployment ของเราเป็นยังไงบ้างด้วยคำสั่ง

Update 2024-09-28 เพิ่ม helm template <relase_name> <path_to_chart> <option> ตอนแรกผมเข้าใจว่าต้องลงจริง release name ถึงจะขึ้น แต่จริงๆ set ได้

helm template <relase_name> <path_to_chart> <option>
//sample 
helm template .     // . = current directory
helm template mooping .     // . = current directory

ผลที่ได้ มัน print template เราลองออกมาตามรูป

- ลองใช้ predefine

กลับมาที่ deployment ของเรา ผมจะลองแก้ ตามนี้ครับ เพิ่มขึ้นมา เพื่อดูว่ามัน replace ค่าไหม โดยจะลองเพิ่มในส่วนของ label

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: sampleaspnet
    release: {{ .Release.Name }}
    environment: {{ .Values.environment }}
    chartAPIVersion: {{ .Chart.APIVersion }}
    DeployOn: {{ .Capabilities.KubeVersion }}
  name: sampleaspnet
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sampleaspnet
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: sampleaspnet
    spec:
      containers:
      - image: mcr.microsoft.com/dotnet/samples:aspnetapp
        name: samples
        resources: {}
status: {}

ผลการ Run helm template . ครั้งแรก ที่นีเ้เราจะเห็นว่า Enviroment / release มันยังอ่านไม่ได้


Run รอบแรก ผมโดนผีหลอก 555 เดวค่อยมาตรวจอีกทีหลัง

มาเติมค่าที่อ่านไม่ได้ในส่วน Enviroment ที่ไฟล์ Value.yaml แล้วลอง helm template . อีกรอบ

ตอนนี้ยังขาด release-name เราเดาว่า เรายังไม่ได้ติดตั้งไง มันเลยไม่รู้ มาลองกันครับ

  • ลอง helm install sampledotnet . มันจะเอาข้อมูลใน note มาแสดงเพิ่มด้วย เมื่อ helm ls ข้อมูลในส่วนของ chart มันเอามาจาก chart.yaml ครับ
  • ลอง helm template . ยังได้ผลเหมือนเดิมนะ
  • ลองเข้าไปส่อง Deployment / pod
PS D:\2WarRoom\2024Helm\sample> kubectl get deploy,pod
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/sampleaspnet   1/1     1            1           9m26s

NAME                               READY   STATUS    RESTARTS      AGE
pod/sampleaspnet-6b5878787-cgnf7   1/1     Running   0             9m26s
  • ลอง describe deployment ซะหน่อย kubectl describe deploy sampleaspnet

ประเด็นที่สงสัยมานาน
- ถ้ายังไม่ Install มันยังไม่ได้ Apply จริงๆ เลยอาจจะไปส่งค่า Default เดี๋ยวต้องไปหาทางแงะ 555 ถ้าว่าง
- แต่ถ้า Install ไปแล้ว พวกค่าที่มาจากตัวแปรในกลุ่ม .Release.xxx / .Capabilities.xxx มันจะไปดึงค่าจริง จาก Kube / Helm มาให้เลย

และคำสั่ง helm template <path_to_chart> มันช่วยเรา visual / debug ค่าได้จริงๆนะ แต่ติดบางส่วนที่ต้อง install ก่อน ถึงจะได้ค่ามาครับ

Pack Helm Template > Helm Package

หลังจากที่เราได้ Template เรียบร้อบแล้ว ผมติดต่างว่าใช่ได้ 5555 จากนั้นค่อยมาเราต้องการจะส่งให้คนอื่นและ

helm package <Path_to_Chart>
//Sample 
helm package .

ตรงนี้เราจะได้ไฟล์ .tgz มา โดยชื่อไฟล์มา จากชื่อจาก Chart.yaml <name>:<version ของ Chart>.tgz มาให้ ซึ่งไฟล์นี้เราสามารถเอาไปใช้ได้เลย จริงๆตัดแปะ ตัว .tgz เราสามารถ edit และแก้ไข และเองได้นะ เผื่อเคสฉุกเฉิน

Publish Package

- Via GitHub Package

1. เอา Code ขึ้น Git ก่อน กันหายครับ

2. Generate GitHub Token เพื่อให้มีสิทธิเข้าตัว GCHR (Container Registry ของ GitHub) ตอนแรกก็สงสัยทำไม มันใช้แบบใหม่ (Fine Gain) ไม่ได้ ลองหาดูและไปอ่าน doc สรุปต้องใช้ แบบ Classic ไปก่อนนะ

  • Account > Developer Setting > Personal access tokens (classic) เลือก Read/Write/Delete เลยครับ
  • กด Generate และ Copy Token ที่ได้
  • เอามา Login ใช้ docker cli ก็ได้นะ
//Linux
export HELM_PAT="your_personal_access_token"
echo $HELM_PAT | docker login ghcr.io -u <github_account_name> --password-stdin

//Powershell ยังมีความพยายามในการให้ 555
$HELM_PAT = "your_personal_access_token"
$HELM_PAT | docker login ghcr.io -u <github_account_name> --password-stdin

3. ลอง push .tgz ขึ้น GCHR ตามนี้

helm push <Path_To_File.tgz> oci://ghcr.io/<your_github_account_name>/<your_github_repo_name>

สังเกตุว่า มันเป็น protocal oci:// ไม่ใช่ http นะ โดยเจ้า oci เป็นมาตรฐานกลางในการเก็บของดึงของครับ

helm push sampledotnet-0.1.0.tgz oci://ghcr.io/pingkunga/sampledotnet-chart

Pushed: ghcr.io/pingkunga/sampledotnet-chart/sampledotnet:0.1.0
Digest: sha256:0ad7255e45d43a0fcc0eec09830469ed8d6466a93d2b5f942ee97a20047f19b3

ไฟล์ที่เอา push ขึ้นไปต้องเข้า menu package ตามรูปนะ ตอนแรก ผมไปหาที่ repo ไม่เจอ 555 จิตตกไปแปบนึง โดนอีกแล้วเหรอ 55 สัปาดาห์นี้เจอวิกฤติมาเยอะ

จากนั้นมาเปลี่ยน Link กับ repository และแก้ไช package setting และเปลี่ยน Package Repository Visibility ครับ

- Via GitHub Page

วิธีนี้จะต่างกันอันแรก คือ สามารถเพิ่ม repo เข้าไปใน helm repo add ได้

  • มาลอง Add กันก่อน มันจะด่า ทำไม่ได้
PS C:\Users\Chatr> helm repo add sampledotnet-chart  "oci://ghcr.io/pingkung/sampledotnet-chart"

Error: looks like "oci://ghcr.io/pingkung/sampledotnet-chart" is not a valid chart repository or cannot be reached: object required
  • มาสร้าง GitHub Page หลอกมันก่อน โดยตอนนี้เราอยู่ใน Branch เดิมนะ สร้าง orphan branch ขึ้นมา
git checkout --orphan gh-pages
//Delete File
git rm -rf .
//Check current branch
git status  
//Commit & Push Chart 
git commit --allow-empty -m "Feat: Init Chart Repo"
git push --set-upstream origin gh-pages
ตรวจสอบ GitHubPage และจำ url ของ repo เราที่ highlight ครับ
  • จากนั้นเอา package .tgz มาวางใน branch gh-pages
  • จากนั้น ทำ index ด้วยคำสั่ง helm repo index . ซึ่งมันจะไปอ่านข้อมูลใน tgz แล้วแตก สร้างไฟล์ index ขึ้นมาครับ
helm repo index . --url <Your_GitHub_Page_URL>

//ของผมจะประมาณนี้
helm repo index . --url https://pingkunga.github.io/sampledotnet-chart/
  • add index + .tgz แล้ว commit & push ขึ้นไปครับ
  • ลองกลับมาเพิ่ม repo อีกรอบ ทำได้ครับ
helm repo add sampledotnetgh-chart  "https://pingkunga.github.io/sampledotnet-chart/"
  • จากนั้นลอง list ขึ้นมา ว่ามีไหน และถ้า Seach ดูจะพบ Chart ครับ
  • ถ้าอยากลงก็ตามนี้ครับ แต่ต้องระวังด้วยว่ามันชนกับของเดิมไหม
helm install sampledotnetfromgh sampledotnetgh-chart/sampledotnet

ถึงตรงนี้ เราสามารถเอา Step ทำมือตรงนี้ ไปลองผูก Flow กับ GitHubAction ได้ครับ

Update 2024-09-24 นอกจากเอาไปไว้ใน Repo แล้ว ยังสามารถ สร้าง Release โยนไฟล์ .tgz ได้ เราจะล้อตัวอย่าง repo backstage/charts

  1. Create Release และโยนไฟล์ โยนไฟล์ .tgz
ใน GitHubAction มีตัวช่วย Pack แล้วโดยไฟล์เข้า Release น้า
  1. ทดสอบ Link https://github.com/pingkunga/sampledotnet-chart/releases/download/sampledotnet-0.1.0/sampledotnet-0.1.0.tgz ตรงนี้เราจะรู้ pattern
  2. แก้ไขไฟล์ index ให้ชี้ไป Link ใหม่

ถ้าใครใช้ GitHub เป็นหลักก็ดีครับ แต่ถ้าต้องการเก็บของ แล้วใช้หลายอันแบบ docker / nuget / maven / npm / helm ผมแนะนำอีกวิธีด้านล่างครับ

- Via Nexus

อันนี้ผมลองทำเพิ่ม เพราะ ถ้ามาท่า GitHub และโยน tgz เยอะมันน่าจะไม่ดี และไม่แน่ใจว่าอนาคตตัว tgz ใช้ไปนานๆจะบวมไหม ไหนมีโอกาสเล่น nexus ใหม่ (แบบจำเป็น) และลองซ้อมสร้าง Nexus Repo ของเราด้วยคำสั่ง

docker volume create --name nexus-data
docker run -d -p 8081:8081 --name nexus -v nexus-data:/nexus-data sonatype/nexus3

ปกติแล้วตัว nexus จะ default ของ maven (java) / nuget (dotnet) มาให้ ที่นี้เราจะเพิ่ม helm repo เข้าไปครับ ที่นี้มันจะได้ 2 ส่วน helm (เอาไว้เก็บข้อข้างใน) และตัว helm proxy (ให้ nexus มัน cache package จาก public repo มาให้)

สำหรับ Blog นี้ เน้น local helm ส่วน helm proxy step ก็ล้อๆกันครับ

กำหนดสิทธิกันก่อน หลังสร้าง repo เราต้องมาเพิ่มสิทธิให้ Role ที่จะโยนของเข้าไปด้วย ผมใช้ user jenkins และก็กำหนด role cicd ขึ้นมา โดยสิทธิกำหนด nx-repository-view-helm-*-edit / nx-repository-view-helm-*-read คิดว่าน่าจะ least privilleage ที่สุดนะ

ลอง push ขึ้นไป

  • ลอง login + helm push ไม่รอด
  • เหลือ helm push คิดว่ามันจะถาม user pass ไม่รอด
//Not work
helm push sampledotnet-0.1.0.tgz http://localhost:8081/repository/helm/ 
helm push sampledotnet-0.1.0.tgz oci://localhost:8081/repository/helm/
  • ใช้วิธีปกติ curl แต่ของยิงจาก wsl //powershell curl ก็ indy
# Upload chart package
curl -u USERNAME:PASSWORD <NEXUS-URL>/repository/REPONAME/ --upload-file CHART-PACKAGE.tgz

curl -u jenkins:jenkins http://localhost:8081/repository/helm/ --upload-file sampledotnet-0.1.0.tgz
  • ไฟล์ขึ้นไปแล้ว
  • ลอง add repo
helm repo add <REPONAME> <NEXUS-URL>/repository/REPONAME/ --username USERNAME --password PASSWORD

helm repo add nexus-helm http://localhost:8081/repository/helm/ --username jenkins --password jenkins 
  • พอ add สำเร็จก็ ลอง Test โดยตอนเรา add nexus จะไปสร้าง index มาด้วย
  • ลองส่อง repo
  • ถ้าจะ install ได้เหมือนกัน

ถ้าลองวาด Flow น่าจะตามนี้

ภาพเต็มตามนี้ HelmPackageFlowV1

Blog ท่านอื่นๆ

ถ้าอ่านมาถึงตรงนี้ ใน Blog ผม ตัว sample ยังมีแต่ deployment นะครับ และไม่ได้ทำ GitHubAction ด้วย ยกยอดไปสักตอน แค่คิดว่าน่าจะได้เขียน nexus ยาวๆสักอัน


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts sent to your email.