บันทึกการเรียนรู้ K3S และ Helm

Nakorn Rientrakrunchai
T. T. Software Solution
12 min readSep 8, 2024

Medium นี้ เป็นบันทึกการเรียนรู้ K3S และ Helm ของผมในสัปดาห์นี้นะครับ โดยแผนการเรียนรู้ของผมมีรายละเอียดตามในรูปด้านล่างนี้ นะครับ

สำหรับท่านที่จะอ่านบทความนี้, เพื่อให้มีพื้นฐาน kube ก่อน, ผมแนะนำให้อ่านอันนี้ก่อนนะครับ
https://medium.com/t-t-software-solution/%E0%B8%AA%E0%B8%A3%E0%B8%B8%E0%B8%9B%E0%B8%88%E0%B8%B2%E0%B8%81%E0%B8%97%E0%B8%B5%E0%B9%88%E0%B9%80%E0%B8%A3%E0%B8%B5%E0%B8%A2%E0%B8%99-kube-free-class-%E0%B9%82%E0%B8%94%E0%B8%A2-jumpbox-6ff1e53acac0

สำหรับรูปน้องซีโน่ เป็นภาพประกอบเฉยๆ ไม่เกี่ยวข้องกับเนื้อหานะครับ ผมเห็นน่ารักดี เลยเอามาใส่

เตรียม VM และติดตั้ง Ubuntu 24

ผมได้เลือกใช้การติดตั้ง Linux บน VM เพราะว่า อนาคตจะลองเล่นให้มีหลายๆ node และจะลองเชื่อมกันนะครับ, แต่บทความนี้ จะเล่นบน Control Plane ที่เดียว นะครับ

ในการใช้งาน VM บน window ผมเลือกใช้ virtualbox นะครับ โดยสามารถ downlaod ได้จากที่นี่ครับ https://www.virtualbox.org/

เมื่อดาวโหลดมาแล้ว ติดตั้งให้เรียบร้อย จากนั้น ไปเอา Ubuntu ISO มาจากที่นี่

จากนั้น ทำการติดตั้ง Ubuntu 24 ลงใน VM ดังนี้ครับ

ในการใช้งาน ให้กำหนดค่า Network เป็น Bridged Adapter นะครับ

เมื่อติดตั้ง และ restart แล้ว, ให้ตรวจสอบ IP โดยใช้คำสั่งนี้นะครับ

ip addr show

ให้ดูตรงแถวๆสีแดง หรืออื่นๆ แล้วแต่ว่า จะได้ IP อะไรมานะครับ

จากนั้นให้ลอง ssh เข้าไป ถ้าโอเค ก็จะได้ตามรูปนี้ครับ, สำหรับผม ได้ IP 192.168.1.74 มานะคับ

ติดตั้ง K3S

ผมได้ดำเนินการติดตั้ง K3S โดยให้ GPT มันช่วยแนะนำ ซึ่งมีขั้นตอนดังนี้

Installing K3s on Ubuntu 24 is a straightforward process. K3s is a lightweight Kubernetes distribution designed to be easy to install, especially for development and edge computing environments. Below is a step-by-step guide on how to install K3s on Ubuntu 24.

Step 1: Prepare the System

Update the System Ensure your system is up to date before starting the installation:

sudo apt update && sudo apt upgrade -y

Disable Swap (Kubernetes requires that swap is disabled) If swap is enabled, you will need to disable it:

sudo swapoff -a

To permanently disable swap, open the /etc/fstab file and comment out any swap entries:

sudo nano /etc/fstab

Comment out the line related to swap by adding a # at the beginning.

Step 2: Install K3s

Install K3s

The easiest way to install K3s is to use the official installation script. It automatically installs the necessary components and sets up the K3s cluster.

Run the following command to install K3s:

curl -sfL https://get.k3s.io | sh -

The script will download and install K3s along with all its dependencies. Once the installation is complete, the K3s service will be started automatically.

Verify the Installation

After installation, K3s should be running as a systemd service. You can check the status with the following command:

sudo systemctl status k3s

To verify that Kubernetes is running correctly and check the nodes, use kubectl (K3s automatically installs kubectl):

sudo kubectl get nodes

You should see your server node listed as Ready.

กำหนดค่า IP ให้กับ K3S

คือสืบเนื่องจาก เราใช้ VM ผ่าน network ของ window ซึ่ง มันอาจจะมีการเปลี่ยน IP ไปมาได้ ดังนั้น เราจึงต้องรู้วิธีการกำหนดให้ Kube มันรู้จัก IP ด้วย, โดยสำหรับ K3S มันกำหนดค่อนข้างง่าย แตกต่างจากรอบที่ผมลงผ่าน Kubeadm ซึ่งผมไม่ทราบวิธีเปลี่ยน IP ด้วยการลงแบบนั้น

Here’s how you can modify the ExecStart command in the k3s.service file to include IP-specific options:

Edit the k3s.service File:

Open the service file with a text editor:

sudo nano /etc/systemd/system/k3s.service

Add IP Configuration:

In the ExecStart line, you can add flags such as -node-ip or -advertise-address to specify which IP address K3s should use. Suppose your new static IP is 192.168.1.100, you might modify the line like this:

ExecStart=/usr/local/bin/k3s \
server \
--node-ip=192.168.1.74 \
--advertise-address=192.168.1.74

Adjust the IP address as per your requirements.

Save and Close the File:

After making the changes, save the file and exit the editor.

Reload and Restart K3s Service:

Reload the systemd configuration and restart K3s to apply the changes:

sudo systemctl daemon-reload
sudo systemctl restart k3s

Verify the Changes:

Check that K3s is using the new IP address:

sudo kubectl get nodes -o wide

This will show the IP addresses being used by the nodes.

ใช้งาน k3s ด้วย sudo su

บางครั้ง ถ้าเจอแบบนี้

nakorn@k3s01:/$ kubectl get pod
error: error loading config file "/etc/rancher/k3s/k3s.yaml": open /etc/rancher/k3s/k3s.yaml: permission denied
nakorn@k3s01:/$ kubectl get node
error: error loading config file "/etc/rancher/k3s/k3s.yaml": open /etc/rancher/k3s/k3s.yaml: permission denied

เหตุผลมันคือ

If you use the k3s setup for anything professional, such as an IOT solution, edge or anything else in an environment where you do not want unwanted access to this machine: keep using the sudo command. With above steps you are removing a layer of security from your setup.

If you use the k3s environment for a quick test lab to prepare for certification, tests, playground etc. Then it is probably fine to use above commands. But please stay aware of the role and the use-case of the machine. If it leaves your supervision, wipe it and set it up again.

reference https://0to1.nl/post/k3s-kubectl-permission/

ดังนั้น ผมจึง ตัดสินใจเลือกใช้ sudo su เมื่อทำงานกับ k3s สำหรับการทดสอบ นะครับ

ติดตั้ง Helm

ในบทความนี้ จะเป็นการทำเพื่อการศึกษาเท่านั้น, ผมจึงรันคำสั่งด้วย root ไปเลยนะครับ โดยทำดังนี้ก่อน

sudo su

จากนั้นจึงติดตั้ง Helm ตามขั้นตอนดังนี้

Here’s a step-by-step guide to install Helm in K3s on Ubuntu 24:

Step 1: Update Ubuntu 24

First, ensure that your system is up to date.

sudo apt update && sudo apt upgrade -y

Step 2: Install Helm

Download Helm binary

To install Helm, first download the latest release.

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

รันคำสั่งนี้

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

Verify Helm Installation

After installation, verify Helm by checking the version:

helm version

You should see output confirming Helm’s installation.

This setup should enable you to manage your Kubernetes cluster on Ubuntu 24 using Helm with K3s.

กำหนดค่า KUBECONFIG environment

ถ้าคุณพบ error จากการรันคำสั่งของ helm เช่นข้างล่างนี้

มันเกิดจาก helm ต้องการเข้าถึง kube โดยมันจะไปอ่านค่าจาก environment variable ชื่อ KUBECONFIG ดังนั้น เราสามารถแก้ไข ได้โดยรันคำสั่งนี้

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

คำอธิบาย

The KUBECONFIG environment variable is used to specify the location of the kubeconfig file, which is necessary for kubectl to know how to connect to a Kubernetes cluster. The kubeconfig file contains configuration information such as cluster details, credentials, and namespaces, allowing kubectl to authenticate and communicate with the Kubernetes cluster.

Here’s why you need to export KUBECONFIG=/etc/rancher/k3s/k3s.yaml:

  1. Default Config Path: By default, kubectl looks for a kubeconfig file in ~/.kube/config. If your kubeconfig file is located somewhere else, such as /etc/rancher/k3s/k3s.yaml (which is common for K3s), you need to tell kubectl where to find it using the KUBECONFIG environment variable.
  2. Multiple Config Files: If you’re working with multiple clusters, you may have different kubeconfig files for each cluster. Setting the KUBECONFIG variable allows you to switch between configurations easily by pointing to different kubeconfig files.
  3. Non-standard Setup: In some cases, especially in managed Kubernetes environments like K3s, the kubeconfig file is located in a system directory (e.g., /etc/rancher/k3s/k3s.yaml) instead of the default user directory (~/.kube/config). Exporting the KUBECONFIG environment variable ensures that kubectl uses the correct file for authentication and cluster connection.

By running export KUBECONFIG=/etc/rancher/k3s/k3s.yaml, you’re instructing kubectl to use this specific kubeconfig file to interact with the K3s cluster.

อย่างไรก็ตาม หากต้องการให้มันมีค่า KUBECONFIG ทุกครั้งที่ restart เครื่องขึ้นมา ให้รันคำสั่งดังนี้

This way, the KUBECONFIG will be set for every session.

echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc
source ~/.bashrc

ทดสอบ Helm สามารถใช้งานได้

ในขั้นต่อไปนี้ เราจะตรวจสอบว่า Helm สามารถใช้งานได้แล้วหรือยัง โดยทดลองลง nginx ผ่าน Helm ครับ

Step 5: Add Helm Repositories

Helm uses repositories to store charts. You can add the stable repository using the following command:

helm repo add bitnami https://charts.bitnami.com/bitnami

Step 6: Test Helm with K3s

To test that Helm is working with K3s, you can deploy an example chart. For instance, install an NGINX chart:

helm install my-nginx bitnami/nginx

Check the deployed service using:

sudo k3s kubectl get all

ดูดีๆ ว่า Helm มันกำหนด Label อะไรมาให้ pod

ในหัวข้อถัดไปเกี่ยวกับ NodePort ให้ตรวจสอบให้ดีก่อนว่า Label ต่างๆใน pod เป็นอย่างไร เพื่อใช้ในการอ้างอิง

kubectl get pod
kubectl describe pod [pod ของคุณ]

Nginx with NodePort

มาลองใช้ NodePort เพื่อให้ window pc สามารถเข้าถึง nginx ได้ กันครับ

To access your NGINX service via NodePort, you’ll need to expose the NGINX pod as a service using the NodePort type. Here’s how you can do it:

Step 1: Create a NodePort Service for NGINX

Create a YAML file to define the NodePort service for your NGINX deployment.

Create a file called nginx-nodeport-service.yaml:

nano nginx-nodeport-service.yaml

Add the following configuration:

apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
spec:
type: NodePort
selector:
app.kubernetes.io/name: nginx # This should match the label of your nginx pod
ports:
- protocol: TCP
port: 80 # The port the service will expose inside the cluster
targetPort: 8080 # The port your NGINX container is listening on (from your pod description)
nodePort: 30001 # This is the NodePort that will be exposed to the outside (choose a port between 30000-32767)

Save the file and apply the service:

kubectl apply -f nginx-nodeport-service.yaml

You should see the nginx-service listed with type NodePort and the specified nodePort (in this case 30001).

Example output:

kubectl get svc

เมื่อเข้า URL http://192.168.1.74:30001/ จากเครื่อง window คุณก็จะสามารถเข้าถึง nginx ได้ ดังนี้

Nginx with ingress

มาลองใช้ ingress ในการเข้าถึง nginx ผ่าน window กันครับ

ingress มันจะชี้ไปที่ service ดังนั้น ให้เราตรวจสอบก่อนว่า เราจะให้ ingress มันชี้ไปที่ service ดังนี้

kubectl get svc

จากในรูปข้างล่าง เราจะให้มันชี้ไปที่ my-nginx กันครับ

Create an Ingress resource:

Create a file called nginx-ingress.yaml:

nano nginx-ingress.yaml

เขียนเนื้อหานี้ลงไป

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-nginx-ingress
namespace: default
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: nginx.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-nginx
port:
number: 80
tls:
- hosts:
- nginx.local
secretName: my-nginx-tls

Apply the Ingress Resource: Run the following command to apply the ingress file:

kubectl apply -f nginx-ingress.yaml

ตรวจสอบว่ามี ingress ที่สร้างขึ้น เกิดขึ้นหรือไม่

kubectl get ingress

ตัวอย่างการรรัน เพื่อตรวจสอบ

จากนั้น แก้ไข host file ใน window

Update hosts on your local machine: On your Windows PC, open the /etc/hosts file (usually located in C:\Windows\System32\drivers\etc\hosts) and add the following line:

192.168.1.74 nginx.local

เมื่อสำเร็จ ให้ลองเข้า http://nginx.local จากเครื่อง window ก็จะสามารถเข้าถึงได้

รู้จักกับ artifacthub.io

ผมให้ GPT มันช่วยอธิบายเป็นภาษาไทยให้ แต่ดูมันน่าจะยังไม่เก่งภาษาไทยมาก ลองอ่านกันดูนะครับ, ไว้วันหลังจะลองใช้ Claude AI ที่มีคนสปอยมาว่า มันเก่งภาษาไทยมากกว่า

Artifact Hub เป็นแพลตฟอร์มที่ใช้สำหรับการค้นหา การติดตั้ง และการจัดการแพ็คเกจสำหรับโครงการต่าง ๆ ใน Kubernetes หรือโครงการที่เกี่ยวข้องกับระบบคลาวด์ โดยเฉพาะการใช้ Kubernetes และ Helm Charts เป็นหลัก

การใช้งาน Artifact Hub

ค้นหาแพ็คเกจ: Artifact Hub ช่วยให้คุณค้นหาและตรวจสอบแพ็คเกจหรือโปรเจ็กต์ต่าง ๆ ที่คุณสามารถนำมาใช้ในระบบของคุณได้ ตัวอย่างเช่น Helm Charts ซึ่งเป็นแพ็คเกจที่ช่วยจัดการการติดตั้งแอปพลิเคชันใน Kubernetes โดยตอนค้นหา ก็ติกตรงสีแดง ด้วยก็ดีครับ

ดูคำสั่งติดตั้งแพ็คเกจ: คุณสามารถใช้ Artifact Hub เพื่อดาวน์โหลดและติดตั้งแพ็คเกจในระบบของคุณ โดยใช้คำสั่งและขั้นตอนที่มีการระบุไว้ในแต่ละโปรเจ็กต์ ตัวอย่างเช่น คำสั่ง helm install สำหรับติดตั้ง Helm Chart.

Artifact Hub เป็นเครื่องมือที่มีประโยชน์มากสำหรับนักพัฒนาและผู้ดูแลระบบที่ทำงานกับ Kubernetes ช่วยให้การจัดการแพ็คเกจง่ายขึ้นและสะดวกยิ่งขึ้น

ติดตั้ง pgadmin

ให้ทำการค้นหา pgadmin จากใน artifacthub.io สำหรับผมได้เลือกอันข้างล่างนี้มาครับ

https://artifacthub.io/packages/helm/arunalakmal/pgadmin

จากนั้น ทำการเปิดดูคำสั่งที่ใช้ติดตั้ง

จากนั้น เราก็มาลองกันเลยครับ, ก็คือรันตามกรอบแดงนะครับ เสร็จแล้ว ก็จะได้ แบบนี้

มาตรวจสอบกันว่า มีอะไรใหม่ โผล่มาบ้าง ลองรันคำสั่งดังนี้

kubectl get pod | grep pgadmin
kubectl get svc | grep pgadmin

จะพบผลลัพท์ ดังนี้

อธิบาย kubectl port-forward command

ข้อสังเกตุหนึ่ง จากการติดตั้ง pgadmin ผ่าน helm, เราจะพบคำแนะนำตามกรอบแดง

นี่คือ ชุดคำสั่งตามที่เขาแนะนำมา

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=pgadmin,app.kubernetes.io/instance=my-pgadmin" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

เมื่อลองทำตามดู จะพบดังนี้

เมื่อรันคำสั่ง kubectl port-forward มันจะทำการสร้าง tunnel ชั่วคราวขึ้นมาให้ ซึ่งสามารถเข้าถึงได้ภายใน local mechine เท่านั้น ตามสีเขียว, เมื่อเราปิดมันโดย Ctrl+c เราก็จะเข้าไม่ได้แล้ว ตามสีแดง, ข้างล่างนี้ คือคำอธิบายจาก GPT

The command kubectl -n default port-forward $POD_NAME 8080:$CONTAINER_PORT is used to forward a port from your local machine to a specific port on a container running inside a Kubernetes pod.

the port-forward command does not create a Kubernetes Service for you. It simply forwards traffic from a port on your local machine to a port on a specific pod, allowing you to interact with the application running inside the pod as if it were running locally.

ดังนั้น การใช้คำสั่ง kubectl port-forward จึงไม่ใช่วิธีสำหรับการใช้งานถาวรนะครับ

จัดทำ NodePort ให้ pgadmin

ตรวจสอบก่อนว่า label ต่างๆของ pgadmin service เป็นอย่างไร

kubectl get svc | grep pgadmin
kubectl describe svc my-pgadmin

To create a NodePort service for your my-pgadmin deployment, you can use the following YAML file. The current service is of type ClusterIP, and we will modify it to be of type NodePort to expose it outside the cluster.

nano my-pgadmin-nodeport.yaml

Here’s the service definition for your my-pgadmin service:

my-pgadmin-nodeport.yaml:

apiVersion: v1
kind: Service
metadata:
name: my-pgadmin
namespace: default
spec:
type: NodePort
selector:
app.kubernetes.io/name: pgadmin
app.kubernetes.io/instance: my-pgadmin
ports:
- protocol: TCP
port: 80 # The port exposed by the service
targetPort: 80 # The port exposed by the container (inside the pod)
nodePort: 30002 # The port on the node where the service will be accessible

Run the following command to apply the service:

kubectl apply -f my-pgadmin-nodeport.yaml

เมื่อรันแล้วจะพบว่า มี my-pgadmin service ที่เป็น NodePort แสดงขึ้นมาดังรูป

เมื่อเข้าผ่าน browser จาก window จะเข้าได้ดังนี้

แล้วจะเอารหัสผ่านจากที่ไหน?

ตอนที่เข้ามาที่ http://192.168.1.74:30002/ ผมก็สับสนว่าจะเอารหัสผ่านจากที่ไหนดี สุดท้ายให้กลับไปดูที่ artifacthub.io มันจะมีส่วนที่เป็น DEFAULT VALUES อยู่ครับ

เมื่อเรากดดู และเลื่อนดูเรื่อยๆ ก็จะพบ default credential ดังนี้

เมื่อลองใช้งาน ก็จะใช้เข้าไปได้ ดังนี้

ติดตั้ง Postgres Database

ผมได้ลองหา postgres และตัดสินใจใช้ตัวนี้นะครับ

อนึ่ง, จริงๆแล้ว ตอนแรก ผมใช้ postgres ของ binami ครับ แต่ปรากฏว่า ติดปัญหาการใช้งาน ซึ่งยังแก้ไขไม่ได้, เลยมาใช้ตัวนี้ก่อนครับ

เมื่อดู INSTALL ของมันแล้ว ก็มาลุยกันเลยครับ

เมื่อตรวจสอบ pod และ service พบดังนี้ครับ

เมื่อดู DEFAULT VALUE จากใน Artifact Hub จะเจอดังนี้ครับ

เข้าไปดู Postgres ด้วย PGadmin

จากใน pgadmin ให้เข้าไปเพิ่ม server ดังนี้ครับ

หลังจากนั้น ก็ใส่รายละเอียดลงไป, หากสำเร็จ มันก็จะกดบันทึก แล้วแสดงด้านซ้ายได้ ก็แปลว่า pgadmin มันเชื่อมกับ postgres ได้แล้ว, โดย 10.43.219.50 มันคือ ClusterIP ของ postgres นะครับ

อย่างไรก็ตาม, ตรง Hostname/address เราจะใส่ my-postgres ก็ connect ได้ เช่นกัน

เป็นอันว่า เราเชื่อมต่อไปยัง postgres จาก pgadmin ได้เรียบร้อยแล้ว นะครับ

ClusterIP และ DNS ใน Kube

ในการที่ pgadmin และ postgres มันสามารถคุยกันได้นั้น, มันมีเหตุผลมาจาก ClusterIP และ Service Name Resolution (DNS นั่นแหละ) ใน Kube ซึ่ง GPT มันได้อธิบายมาให้ดังนี้, กำหนดให้ใน kube มีรายการอ้างอิง ดังต่อไปนี้

The ClusterIP and service resolution in Kubernetes, like in your case with PostgreSQL (my-postgres) and pgAdmin, work through internal DNS provided by Kubernetes. Here's how it works:

  1. ClusterID (ClusterIP): Every service in your Kubernetes cluster is automatically assigned a ClusterIP, which is an internal IP address that is only accessible within the cluster. In your case:
my-postgres has a ClusterIP of 10.43.219.50.
my-pgadmin has a ClusterIP of 10.43.185.63.

2. These IPs allow internal pods and services to communicate with each other. You can access my-postgres from pgAdmin using this ClusterIP (10.43.219.50).

3. Service Name Resolution: Kubernetes has an internal DNS system, which means services can be accessed by their names as well. In your case, PostgreSQL is exposed as a service called my-postgres, and pgAdmin can resolve this name to the corresponding ClusterIP (10.43.219.50).

4. When you’re using my-postgres, Kubernetes' DNS automatically resolves this to the correct internal IP (10.43.219.50), making it easier to manage services without dealing directly with IPs. This is why you can access PostgreSQL using either 10.43.219.50 (the direct IP) or my-postgres (the service name).

Summary:

  • ClusterIP is an internal IP for a service accessible only within the cluster.
  • Service Name (my-postgres) is resolved by Kubernetes' DNS to its corresponding ClusterIP, making it possible to access services by name or IP.

Helm common command

ผมได้ขอให้ GPT มันช่วยสรุป Helm common command มาให้ เพื่อจะเก็บไว้ใช้เร็วๆ ในอนาคต, มันก็สรุปมาให้แบบนี้นะครับ

สรุปถึงจุดนี้

สิ่งที่ได้เรียนรู้จากการศึกษารอบนี้

  1. ติดตั้ง K3S
  2. ติดตั้ง Helm
  3. ติดตั้ง nginx ผ่าน helm
  4. รู้จักกับ Artifect Hub
  5. ติดตั้ง pgadmin ผ่าน helm
  6. ติดตั้ง postgres ผ่าน helm
  7. ทำความเข้าใจสิ่งต่างๆใน kube ได้แก่ Ingress, Service, NodePort, PortForward, Helm, Configuration บางอย่างที่เกี่ยวกับ Kube

อย่างไรก็ตาม มีสิ่งตกค้าง ที่ทำไม่สำเร็จในรอบนี้ได้แก่

  1. การใช้ Traefik เพื่อรันเป็น HTTPS โดยใช้ let’s encrypt ทำไม่สำเร็จ ติดปัญหา the router default-nginx-ingress-nginx-local@kubernetes uses a non-existent resolver: letsencrypt
  2. ใช้ postgres ที่มาจาก binami ไม่สำเร็จ เพราะ assign credential เข้าไปไม่ได้

ไว้เดี๊ยวมาลองใหม่ โอกาศหน้าครับ, ขอฝากคำคมจาก Jumpbox ที่ผมชอบ เพื่อเป็นกำลังใจให้ทุกคนนะครับ

--

--