- Published on
เขียน Pipeline ใน Jenkins สำหรับ Kubernetes
บทความนี้ ผมขอข้ามการอธิบาย Docker และ Kubernetes ว่าคืออะไร ทำงานอย่างไร แต่จะ Focus ไปที่การ Setup Jenkins เพื่อ Deploy เท่านั้น
Jenkins
Jenkins คือ เครื่องมือที่ช่วยให้การ Deployment สะดวก รวดเร็ว และง่ายขึ้นใน One click ซึ่งเรียกว่า CI/CD นั่นเอง ในปัจจุบันก็มีหลายตัวมากๆ แต่ตัวที่ได้รับความนิยมนั้นมีไม่กี่ตัว และ Jenkins ก็เป็นหนึ่งในนั้นครับ โดยปกติแล้วการ Deploy เราจะ Manual build เองแล้วนำไฟล์ต่างๆไปวางใน Server ทำเองทุกอย่างทีล่ะขั้นตอน และทำแบบเดิมซ้ำๆทุกครั้งที่มีการ Deploy ในหลายๆโปรเจค นั่นแหล่ะครับ มันก็คงจะดีกว่าถ้าแต่ล่ะโปรเจคสามารถ Deploy ได้ด้วยเพียงแค่ One click และการ Config Jenkins นั้นก็สามารถทำได้หลายวิธีจนไปถึงสามารถเขียน Script เองได้ที่เรียกกันว่า Pipeline นั่นเอง
Prerequire
- Java 11 or 17
- Jenkins (Require Java)
- Mac OS -
brew install jenkins-lts
(Install ผ่าน Homebrew) - Windows - Download Jenkins (เลือก Windows)
- Linux - Install Jenkins
- Mac OS -
- Docker
- minikube (Require Docker)
- kubectl
- Spring-Boot Application (Source Code)
Solution Overview
- Pull code ล่าสุดจาก Github เมื่อเราสั่ง Build
- Build .jar ด้วย Maven
- Build docker image ด้วย Dockerfile ที่อยู่ในโปรเจค
- Push docker image ไปยัง Docker Hub (คล้าย Github แต่ใช้เก็บ Docker image)
- Deploy Kubernetes ด้วย Kubeconfig ที่เรากำหนดไว้ โดย Kubernetes จะไปเอา Docker image จาก Docker Hub ตาม Tag ที่เรากำหนดไว้มา Deploy
ถ้าเราไม่มี Jenkins เราจะต้องทำ 5 ขั้นตอนนี้แบบ Manual เองทุกๆครั้งที่ Deploy
Start Jenkins
Setup Jenkins (First Time)
- เข้า Jenkins UI ด้วย http://localhost:8080 (Default)
cat /Users/[user]/.jenkins/secrets/initialAdminPassword
แล้วจะได้ secret ออกมาเอามาใส่ Administrator password- เลือก Install suggested plugins รอจนเสร็จ
- กรอก Username กำหนด Password และตั้งชื่อ
- เลือก Save and Continue > Save and Finish > Start using Jenkins
Install Plugins
- เข้าไปที่ Dashboard > Manage Jenkins > Plugins > Advanced settings
- Download File (Kubernetes Continuous Deploy PluginVersion1.0.0)
- เลือนไปที่ Deploy Plugin แล้วเลือก Choose File เพื่อ Upload Plugin ที่ Download มา
- เลือก Deploy รอจนเสร็จ
Setup Credentials
เข้าไปที่ Dashboard > Manage Jenkins > Credentials > System > Global credentials (unrestricted) เลือก + Add Credentilas
- Kubeconfig
กำหนด ID และ Description เป็น k8sconfigpwd
ใช้คำสั่ง open .kube/config
(ต้อง Start Minikube ก่อน minikube start
) จะเป็นการเปิด Config ขึ้นมาใน Text ให้ Copy เนื้อหาทั้งหมดในไฟล์มา ให้เอามาใส่ใน Content แล้วเลือก Create
- Docker Hub
Setup Maven
Spring-Boot Application ของผมต้องใช้ Maven ในการ Build ไม่จำเป็นต้องมีในเครื่อง เราจะใช้ Maven ของ Jenkins เอา
ไปที่ "Dashboard > Manage Jenkins > Tools" เลื่อนลงไปที่ Maven installations
File Require in Spring-Boot Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: discovery-service-app
labels:
app: discovery-service-app
spec:
selector:
matchLabels:
app: discovery-service-app
replicas: 2
minReadySeconds: 15
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: discovery-service-app
spec:
containers:
- name: discovery-service-app
image: janescience/discovery-service:latest
imagePullPolicy: Always
ports:
- containerPort: 9001
apiVersion: v1
kind: Service
metadata:
name: discovery-svc
spec:
type : LoadBalancer
ports:
- port: 80
targetPort: 9001
protocol: TCP
selector:
app: discovery-service-app
FROM openjdk:17-jdk
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} discovery.jar
ENTRYPOINT ["java","-jar","discovery.jar"]
EXPOSE 9001
Jenkins Pipeline
- เลือก "Dashboard > + New Item"
- ตั้งชื่อ และเลือก Pipeline
- เลื่อนลงมาที่ Pipeline เลือก Pipeline script แล้วเขียน Pipeline ลงไปใน Script ตามนี้
pipeline {
agent any
tools{
maven 'maven_3_5_0'
}
environment {
DOCKER_HOME = '/usr/local/bin/docker' // Path to the Docker executable
}
stages{
stage('Build Maven'){
steps{
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'https://github.com/Janescience/discovery-service']]])
sh 'mvn clean install'
}
}
stage('Build image'){
steps{
script{
sh '${DOCKER_HOME} build -t discovery-service:latest .'
}
}
}
stage('Push image to Hub'){
steps{
script{
withCredentials([usernamePassword(credentialsId: 'dockerhub-pwd', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh '${DOCKER_HOME} login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}'
sh '${DOCKER_HOME} tag discovery-service:latest janescience/discovery-service:latest'
sh '${DOCKER_HOME} push janescience/discovery-service:latest'
sh '${DOCKER_HOME} rmi -f $(${DOCKER_HOME} images -f "dangling=true" -q)'
}
}
}
}
stage('Deploy to k8s'){
steps{
script{
kubernetesDeploy (configs: 'k8s/deployment.yml',kubeconfigId: 'k8sconfigpwd')
kubernetesDeploy (configs: 'k8s/service.yml',kubeconfigId: 'k8sconfigpwd')
}
}
}
}
}
หรืออีกวิธีคือเลือก Pipeline script from SCM
เป็นการเก็บ Script Pipeline ไว้ใน Project แทน โดยตั้งชื่อไฟล์ว่า Jenkinsfile
และไปเพิ่ม Github Credentials เหมือน Setup Credentials เพื่อให้ Jenkins เข้าถึง Github ได้
Deploy to Kubernetes
เลือก Project และกด Build Now ถ้า Deploy สำเร็จต้องขึ้นสีเขียวหมดแบบนี้ (ต้อง Start minikube ก่อน)
ถ้าอยากดู Logs ดูได้ที่ "Permalinks > Last build > Console Output"
Verify Kubernetes Deployed
~ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/discovery-service-app-6747dd94f9-5bsxs 1/1 Running 0 18m
pod/discovery-service-app-6747dd94f9-w6dhb 1/1 Running 0 18m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/discovery-svc LoadBalancer 10.111.56.231 <pending> 80:31604/TCP 18m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/discovery-service-app 2/2 2 2 18m
NAME DESIRED CURRENT READY AGE
replicaset.apps/discovery-service-app-6747dd94f9 2 2 2 18m
ใน minikube
จะไม่ Support EXTERNAL-IP แต่ถ้าเป็น Kubernetes ของ Cloud Provider เช่น Azure , GCP หรือ AWS ถึงจะมี Public IP เราต้องใช้คำสั่ง minikube service discovery-svc
~ minikube service discovery-svc
|-----------|---------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|---------------------------|
| default | discovery-svc | 80 | http://192.168.49.2:31605 |
|-----------|---------------|-------------|---------------------------|
🏃 Starting tunnel for service discovery-svc.
|-----------|---------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|------------------------|
| default | discovery-svc | | http://127.0.0.1:51232 |
|-----------|---------------|-------------|------------------------|
🎉 Opening service default/discovery-svc in default browser...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
เมื่อใช้คำสั่งนี้จะทำการเปิด Browser ให้อัตโนมัติ และเข้าไปที่ http://127.0.0.1:51232
Load Balance Test
ผมลอง Deploy อีกโปรเจคโดยทำเหมือนกันทุกอย่าง แล้วลอง Request ไปเยอะๆ และมีการ Log เอาไว้ โดยกำหนดเอาไว้ 2 Pods
อันนี้ Pod 1
~ kubectl logs -f pod/search-service-app-5745756985-xgsgk
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.0)
2023-10-29T08:42:27.260Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : Starting SearchApplication v0.0.1 using Java 17.0.2 with PID 1 (/search.jar started by root in /)
2023-10-29T08:42:27.261Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : No active profile set, falling back to 1 default profile: "default"
2023-10-29T08:42:28.007Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-10-29T08:42:28.067Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 35 ms. Found 5 JPA repository interfaces.
2023-10-29T08:42:28.449Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8086 (http)
2023-10-29T08:42:28.453Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-10-29T08:42:28.454Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-10-29T08:42:28.504Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-10-29T08:42:28.505Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1206 ms
2023-10-29T08:42:28.754Z INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-10-29T08:42:28.790Z INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.2.2.Final
2023-10-29T08:42:28.791Z INFO 1 --- [ main] org.hibernate.cfg.Environment : HHH000406: Using bytecode reflection optimizer
2023-10-29T08:42:28.883Z INFO 1 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy
2023-10-29T08:42:28.980Z INFO 1 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2023-10-29T08:42:28.989Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-10-29T08:42:29.462Z INFO 1 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@69c93ca4
2023-10-29T08:42:29.463Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-10-29T08:42:29.525Z INFO 1 --- [ main] org.hibernate.orm.dialect : HHH035001: Using dialect: org.hibernate.dialect.PostgreSQLDialect, version: org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$DialectResolutionInfoImpl@26f96b85
2023-10-29T08:42:30.152Z INFO 1 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy
2023-10-29T08:42:31.363Z INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-10-29T08:42:31.823Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-10-29T08:42:32.350Z INFO 1 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
2023-10-29T08:42:32.663Z WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-10-29T08:42:33.865Z INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoint(s) beneath base path '/actuator'
2023-10-29T08:42:34.167Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8086 (http) with context path ''
2023-10-29T08:42:34.237Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : Started SearchApplication in 7.265 seconds (process running for 7.685)
2023-10-29T08:59:16.311Z INFO 1 --- [nio-8086-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-10-29T08:59:16.313Z INFO 1 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-10-29T08:59:16.320Z INFO 1 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 6 ms
2023-10-29T08:59:22.606Z INFO 1 --- [nio-8086-exec-2] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:00:25.681Z INFO 1 --- [nio-8086-exec-4] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:00:34.175Z INFO 1 --- [nio-8086-exec-5] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:00:35.148Z INFO 1 --- [nio-8086-exec-6] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:09.307Z INFO 1 --- [nio-8086-exec-8] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:11.206Z INFO 1 --- [nio-8086-exec-9] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:12.177Z INFO 1 --- [io-8086-exec-10] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:12.981Z INFO 1 --- [nio-8086-exec-1] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:13.681Z INFO 1 --- [nio-8086-exec-2] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:14.383Z INFO 1 --- [nio-8086-exec-3] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:15.154Z INFO 1 --- [nio-8086-exec-4] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:20.707Z INFO 1 --- [nio-8086-exec-5] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:22.288Z INFO 1 --- [nio-8086-exec-6] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:04:23.427Z INFO 1 --- [nio-8086-exec-7] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
อันนี้ Pod 2
~ kubectl logs -f pod/search-service-app-5745756985-xqxtw
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.0)
2023-10-29T08:42:30.958Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : Starting SearchApplication v0.0.1 using Java 17.0.2 with PID 1 (/search.jar started by root in /)
2023-10-29T08:42:30.959Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : No active profile set, falling back to 1 default profile: "default"
2023-10-29T08:42:32.132Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-10-29T08:42:32.251Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 112 ms. Found 5 JPA repository interfaces.
2023-10-29T08:42:33.741Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8086 (http)
2023-10-29T08:42:33.747Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-10-29T08:42:33.747Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-10-29T08:42:33.863Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-10-29T08:42:33.925Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2827 ms
2023-10-29T08:42:34.560Z INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-10-29T08:42:34.596Z INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.2.2.Final
2023-10-29T08:42:34.597Z INFO 1 --- [ main] org.hibernate.cfg.Environment : HHH000406: Using bytecode reflection optimizer
2023-10-29T08:42:34.682Z INFO 1 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy
2023-10-29T08:42:34.768Z INFO 1 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2023-10-29T08:42:34.784Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-10-29T08:42:35.241Z INFO 1 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@69c93ca4
2023-10-29T08:42:35.243Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-10-29T08:42:35.302Z INFO 1 --- [ main] org.hibernate.orm.dialect : HHH035001: Using dialect: org.hibernate.dialect.PostgreSQLDialect, version: org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$DialectResolutionInfoImpl@46d8f407
2023-10-29T08:42:35.503Z INFO 1 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy
2023-10-29T08:42:36.042Z INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-10-29T08:42:36.295Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-10-29T08:42:36.491Z INFO 1 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
2023-10-29T08:42:36.668Z WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-10-29T08:42:37.041Z INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoint(s) beneath base path '/actuator'
2023-10-29T08:42:37.166Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8086 (http) with context path ''
2023-10-29T08:42:37.184Z INFO 1 --- [ main] c.spring.microservice.SearchApplication : Started SearchApplication in 6.817 seconds (process running for 7.657)
2023-10-29T08:45:38.147Z INFO 1 --- [nio-8086-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-10-29T08:45:38.149Z INFO 1 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-10-29T08:45:38.156Z INFO 1 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms
2023-10-29T08:46:35.605Z INFO 1 --- [nio-8086-exec-3] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:01:34.343Z INFO 1 --- [nio-8086-exec-7] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:01:42.932Z INFO 1 --- [nio-8086-exec-8] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:01:48.836Z INFO 1 --- [nio-8086-exec-9] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:13.476Z INFO 1 --- [io-8086-exec-10] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:20.416Z INFO 1 --- [nio-8086-exec-1] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:22.441Z INFO 1 --- [nio-8086-exec-2] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:23.191Z INFO 1 --- [nio-8086-exec-4] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:24.005Z INFO 1 --- [nio-8086-exec-3] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:25.011Z INFO 1 --- [nio-8086-exec-5] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:26.204Z INFO 1 --- [nio-8086-exec-6] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:27.088Z INFO 1 --- [nio-8086-exec-7] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:27.824Z INFO 1 --- [nio-8086-exec-8] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:28.707Z INFO 1 --- [nio-8086-exec-9] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:29.544Z INFO 1 --- [io-8086-exec-10] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:34.676Z INFO 1 --- [nio-8086-exec-1] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:36.273Z INFO 1 --- [nio-8086-exec-2] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:37.317Z INFO 1 --- [nio-8086-exec-4] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:58.291Z INFO 1 --- [nio-8086-exec-3] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:02:59.882Z INFO 1 --- [nio-8086-exec-5] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:00.810Z INFO 1 --- [nio-8086-exec-6] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:01.956Z INFO 1 --- [nio-8086-exec-7] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:02.808Z INFO 1 --- [nio-8086-exec-8] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:03.150Z INFO 1 --- [nio-8086-exec-9] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:03.604Z INFO 1 --- [io-8086-exec-10] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:03.816Z INFO 1 --- [nio-8086-exec-1] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:04.036Z INFO 1 --- [nio-8086-exec-2] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:04.265Z INFO 1 --- [nio-8086-exec-4] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:04.493Z INFO 1 --- [nio-8086-exec-3] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
2023-10-29T09:03:04.693Z INFO 1 --- [nio-8086-exec-5] c.s.m.c.IndividualSearchController : INDIVIDUAL SEARCH PROFILE CALLED
จะเห็นว่ามีการจัดการแบ่ง Request ให้อัตโนมัติ และถ้าอยากเข้าใจและศึกษาเพิ่มเติมเกี่ยวกับ Kubernetes มากกว่านี้ผมแนะนำบทความนี้ครับ Learn Kubernetes in Under 3 Hours: A Detailed Guide to Orchestrating Containers