본문 바로가기

INFRA/Kubernetes

쿠버네티스_추가 (1)

1. 기본 프로젝트 설정 

1)  백엔드/프론트엔드의 단순한 설정 (SpringBoot/NextJs)

2) 쿠버네티스에 배포 

- ClusterIP, NodePort, LoadBalancer로 배포 

#기본 쿠버네티스 설정 (백엔드 ClusterIp, 프론트엔드 NodePort)
kubectl get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
backend-service    ClusterIP   10.100.0.174    <none>        8080/TCP       3m47s
frontend-service   NodePort    10.99.176.202   <none>        80:30007/TCP   9s
kubernetes         ClusterIP   10.96.0.1       <none>        443/TCP        18m

#NodePort로 설정하여 External IP를 구성한 frontend
minikube service frontend-service
|-----------|------------------|-------------|---------------------------|
| NAMESPACE |       NAME       | TARGET PORT |            URL            |
|-----------|------------------|-------------|---------------------------|
| default   | frontend-service | http/80     | http://192.168.49.2:30007 |
|-----------|------------------|-------------|---------------------------|

#LoadBalancer frontend-service로 배포
kubectl apply -f frontend-service-loadbalancer

kubectl get svc
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
backend-service    ClusterIP      10.100.0.174   <none>        8080/TCP       13m
frontend-service   LoadBalancer   10.97.201.99   <pending>     80:32442/TCP   43s
kubernetes         ClusterIP      10.96.0.1      <none>        443/TCP        28m

kubectl get svc frontend-service -w
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
frontend-service   LoadBalancer   10.97.201.99   <pending>     80:32442/TCP   3m17s

#포트포워드로 Local의 포트와 연결 (프론트와 백엔드) 
kubectl port-forward service/frontend-service 3000:80
kubectl port-forward service/backend-service 8080:8080

 

- minukube tunnel로 external ip 열어 외부에서 접속가능( 현재는 tunnel되지 않아 pending )

 

2. 설정/민감정보 주입_ConfigMap, Secret

 

1) configMap

- value, env, volume의 mountPath로 설정 

#value로 configmap설정, configmap내용생성/조회
k create configmap todo-config --from-litera=GREETING=hi 
k get configmap
NAME               DATA   AGE
greeting-config    1      7m51s

#내용교체 (--dry-run: 실행은 아닌 구성)
kubectl create configmap greeting-config --from-literal=GREETING=안녕 -o yaml --dry-run=client | kubectl replace -f -

#yaml형식으로 configmap 생성 
todo-db-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: todo-db-config
data:
  MYSQL_DATABASE: "tododb"
  MYSQL_USER: "todouser"
  
#deployment에 configmap반영 
todo-db-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-db
  labels:
    app: todo-db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: todo-db
  template:
    metadata:
      labels:
        app: todo-db
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          ports:
            - containerPort: 3306
          envFrom:
            - configMapRef:
                name: todo-db-config
          env:
            - name: MYSQL_PASSWORD
              value: "todo1234"
            - name: MYSQL_ROOT_PASSWORD
              value: "root1234"
              
#minikube에 instance생성              
k apply -f todo-db-configmap.yaml
k apply -f todo-db-deployment.yaml
k get po                               
NAME                       READY   STATUS    RESTARTS   AGE
todo-db-58bf7c64d4-575lk   1/1     Running   0          99s
  
#생성된 instance의 환경변수 조회
kubectl exec <pod이름> -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=todo-db-58bf7c64d4-575lk
MYSQL_DATABASE=tododb
MYSQL_USER=todouser
MYSQL_PASSWORD=todo1234
MYSQL_ROOT_PASSWORD=root1234
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
GOSU_VERSION=1.17
MYSQL_MAJOR=8.0
MYSQL_VERSION=8.0.43-1.el9
MYSQL_SHELL_VERSION=8.0.43-1.el9
HOME=/root

 

2) secret ( 보안이 필요한 민감정보 저장)

- base64로 encode

- value 혹은 env로 생성

# Value로 secret생성 및 조회 
ubectl create secret generic todo-db-secret \
  --from-literal=MYSQL_PASSWORD=todo \
  --from-literal=MYSQL_ROOT_PASSWORD=root1234
  
k get secret
NAME             TYPE     DATA   AGE
todo-db-secret   Opaque   2      

#세부 내용조회 ( base64로 encoding )
k get secret -o yaml 
apiVersion: v1
items:
- apiVersion: v1
  data:
    MYSQL_PASSWORD: dG9kbw==
    MYSQL_ROOT_PASSWORD: cm9vdDEyMzQ=
  kind: Secret
  metadata:
    creationTimestamp: "2025-07-30T06:46:57Z"
    name: todo-db-secret
    namespace: default
    resourceVersion: "6765"
    uid: 4d99a134-bb28-480c-9db6-28a1a0e1a93d
  type: Opaque
kind: List
metadata:
  resourceVersion: ""
  
# configmap, db, backend ressource 차례로 생성 
ls
README.md                       todo-db-configmap.yaml          todo-db-svc.yaml
todo-backend-deployment.yaml    todo-db-deployment.yaml

k apply -f  todo-db-configmap.yaml
k apply -f todo-db-deployment.yaml
k apply -f todo-db-svc.yaml
k apply -f todo-backend-deployment.yaml

 

 

3. Volume Mount 

- 볼륨 마운트 ( 데이터 저장을 위한 volume연결 )

- 내용 변경 시 즉시 적용되나 환경변수는 실시간 업데이트 불가 ( pod재시동하는 로직 연동)

 

1) hostPath 방식 : 특정 호스트머신(노드)에 종속 ( Pod가 다른 노드로 옮겨지면 데이터 연동불가)

2) PV(Persistent Volume) : 볼륨이 노드에 종속되지 않음

3) PVC (Persistent Volume Claim) : 볼륨을 쿠버네티스 전체에서 사용 ( 관련 볼륨이 없으면 생성하여 pod에 배정 )

 

HostPath

#hostPath로 pod에 마운트 정보연결 (pod 매니페스토 내에 볼륨생성)
      volumeMounts:
        - name: my-volume
          mountPath: /data  
volumes:
    - name: my-volume 
      hostPath:     // Volume을 mount(로컬 파일시스템 중에 지정사용)
        path: /tmp/k8s-data # 미니큐브를 쓰면 미니큐브 내의 패스 
        type: DirectoryOrCreate

 

PV & PVC적용 

#pvc.yaml 생성 
apiVersion: v1
kind: PersistentVolumeClaim 
metadata:
  name: my-pvc
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi # 1 기가바이트(GiB)를 요청합니다.
   
   
#Pod의 manifest에 volume-mount
apiVersion: v1
kind: Pod
metadata:
  name: time-check-pod-pvc
spec:
  containers:
    - name: time-check
      image: busybox
      command: ["/bin/sh", "-c"]
      args:
        - >
          mkdir -p /data;
          while true; do
            echo "$(date)" >> /data/time.txt;
            sleep 20;
          done
      volumeMounts:
        - name: my-storage # 아래 volumes에서 정의한 볼륨 이름
          mountPath: /data
  volumes:
    - name: my-storage
      persistentVolumeClaim:
        claimName: my-pvc

#리소스 생성/조회 
k apply -f pvc.yaml
k get pvc
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
my-pvc   Bound    pvc-b54d3b0b-e8b9-452d-bc6b-154790c26f2c   1Gi        RWO            standard       <unset>                 16s

k apply -f pod-with-pvc.yaml
k exec time-check-pod-pvc -- cat /data/time.txt
Thu Jul 31 02:42:53 UTC 2025
Thu Jul 31 02:43:13 UTC 2025

#volume으로 저장되어 출력되는 내용확인 
k delete pod time-check-pod-pvc
k apply -f pod-with-pvc.yaml
k exec time-check-pod-pvc -- cat /data/time.txt
Thu Jul 31 02:42:53 UTC 2025
Thu Jul 31 02:43:13 UTC 2025
Thu Jul 31 02:43:33 UTC 2025
Thu Jul 31 02:43:53 UTC 2025
Thu Jul 31 02:44:13 UTC 2025
Thu Jul 31 02:44:33 UTC 2025
Thu Jul 31 02:44:58 UTC 2025


환경변수 적용시 재시동 필요

kubectl create configmap nginx-conf --from-file=default.conf
configmap/nginx-conf created

kubectl apply -f default.conf -f nginx-pod.yaml
error: error validating "default.conf": error validating data: invalid object to validate; if you choose to ignore these errors, turn validation off with --validate=false
 
 curl localhost:8000
 echo 'server { listen 80; server_name localhost; location / { return 200 "Volume Updated!\n"; } }' > default.conf

 

 

** storage class 

- minikube에는 볼륨 생성을 위한 storage class로 standard가 준비되어 있으며, claim을 반영해 볼륨 생성

k get storageclass
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  22h

 

 

DB와 같이 데이터 영속성이 필수적인 경우에는 StatefulSet 사용 ( Deployment에 볼륨추가만으로는 기능적으로 부족, 유사하지만 추가기능이 보강된 StatefulSet, backup등의 여러가지 보완점을 생각할때 DB를 쿠버네티스로 올리는 경우는 일반적이지는 않음 )

 

4. StatefulSet 

- DB에 적용하기 위한 구성 ( 원본과 Replica의 구분해 고유한 이름으로 생성 ( main과 backup을 구분 )

- 개별적인 영구 저장공간을 보장 (자신의 이름에 맞는 PVC 할당 및 고유한 PVC와의 연결보장 )

- pod의 생성순서, 삭제순서를 보장 

- Headless Service ( 서비스 이름으로 접근해도 pod의 고유한 이름에 따라 접근 )

# Headless-service.yaml 설정(Pod의 고유성 부여)
apiVersion: v1
kind: Service
metadata:
  name: my-headless-svc
spec:
  # clusterIP: None 으로 설정하는 것이 Headless 서비스의 핵심입니다.
  clusterIP: None
  selector:
    # 이 서비스가 어떤 Pod들을 관리할지 라벨로 지정합니다.
    app: nginx-ss
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  
#버전 정보를 가진 DB생성 
statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet # 리소스 종류는 StatefulSet 입니다.
metadata:
  name: nginx-ss
spec:
  serviceName: "my-headless-svc"
  replicas: 3
  selector:
    matchLabels:
      app: nginx-ss
  template:
    metadata:
      labels:
        app: nginx-ss
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
        volumeMounts:
        - name: data 
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard" # ex02에서 확인한 StorageClass
      resources:
        requests:
          storage: 1Gi

#리소스 생성 및 인스턴스 적용 
k apply -f headless-service.yaml
k get svc my-headless-svc 
NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
my-headless-svc   ClusterIP   None         <none>        80/TCP    48s
k apply -f statefulset.yaml

# pod(명) 규칙성있게 배포됨 
k get pod -l app=nginx-ss 
NAME         READY   STATUS    RESTARTS   AGE
nginx-ss-0   1/1     Running   0          45s
nginx-ss-1   1/1     Running   0          36s
nginx-ss-2   1/1     Running   0          35s

#pod당 PVC가 배정되어 있음 (순서유지, 고유명칭으로 접근가능)
k get pvc 
NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-nginx-ss-0   Bound    pvc-615086b2-2b1e-4cbc-9073-6383e3500383   1Gi        RWO            standard       <unset>                 91s
data-nginx-ss-1   Bound    pvc-8b082ae0-b67a-4dcb-bc5f-9ce4768be3ac   1Gi        RWO            standard       <unset>                 82s
data-nginx-ss-2   Bound    pvc-a00f2990-11b5-4e14-be22-bda635d521e7   1Gi        RWO            standard       <unset>                 81s
my-pvc            Bound    pvc-b54d3b0b-e8b9-452d-bc6b-154790c26f2c   1Gi        RWO            standard       <unset>                 49m

#scale보정 
k scale statefulset nginx-ss --replicas=5
k get pod -l app=nginx-ss //증가 혹은 삭제시에도 순서를 지켜 수행됨 
NAME         READY   STATUS    RESTARTS   AGE
nginx-ss-0   1/1     Running   0          3m41s
nginx-ss-1   1/1     Running   0          3m32s
nginx-ss-2   1/1     Running   0          3m31s
nginx-ss-3   1/1     Running   0          20s
nginx-ss-4   1/1     Running   0          19s
k get pod -l app=nginx-ss               
NAME         READY   STATUS    RESTARTS   AGE
nginx-ss-0   1/1     Running   0          4m9s
nginx-ss-1   1/1     Running   0          4m
nginx-ss-2   1/1     Running   0          3m59s

 

 

5. Ingres

- 여러 서비스의 단일 진입점 (Single Entry Point)

 

1) metallb적용

- 미니 쿠베라서 external IP가 비활성화되므로 이를 활성화 해줌

  ( ingres 미적용시 모든 서비스에 다른 ip가 생성되어 관리하기 어려움 )

#metallb addon설정 
minikube addons enable metallb ( addon받아옴 , 활성화 ip, metal loadbalancer?)
 metallb is a 3rd party addon and is not maintained or verified by minikube maintainers, enable at your own risk.
❗  metallb does not currently have an associated maintainer.
    ▪ Using image quay.io/metallb/speaker:v0.9.6
    ▪ Using image quay.io/metallb/controller:v0.9.6
🌟  The 'metallb' addon is enabled

#서로 다른 서비스 생성 및 확인 
k apply -f service-a.yaml -f service-b.yaml
deployment.apps/hello-deployment created
service/hello-service created
deployment.apps/world-deployment created
service/world-service created

k get all
NAME                                    READY   STATUS              RESTARTS   AGE
pod/hello-deployment-746498bd8f-5qxwf   0/1     ContainerCreating   0          8s
pod/world-deployment-8498cb8866-4qn5p   0/1     ContainerCreating   0          8s

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/hello-service   LoadBalancer   10.97.255.187   <pending>     80:32203/TCP   8s
service/kubernetes      ClusterIP      10.96.0.1       <none>        443/TCP        10m
service/world-service   LoadBalancer   10.96.6.13      <pending>     80:31767/TCP   8s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-deployment   0/1     1            0           8s
deployment.apps/world-deployment   0/1     1            0           8s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/hello-deployment-746498bd8f   1         1         0       8s
replicaset.apps/world-deployment-8498cb8866   1         1         0       8s
(⎈|minikube:default) elena@ijieun-ui-MacBookAir ex01 % k get all
NAME                                    READY   STATUS    RESTARTS   AGE
pod/hello-deployment-746498bd8f-5qxwf   1/1     Running   0          76s
pod/world-deployment-8498cb8866-4qn5p   1/1     Running   0          76s

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/hello-service   LoadBalancer   10.97.255.187   <pending>     80:32203/TCP   76s
service/kubernetes      ClusterIP      10.96.0.1       <none>        443/TCP        11m
service/world-service   LoadBalancer   10.96.6.13      <pending>     80:31767/TCP   76s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-deployment   1/1     1            1           76s
deployment.apps/world-deployment   1/1     1            1           76s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/hello-deployment-746498bd8f   1         1         1       76s
replicaset.apps/world-deployment-8498cb8866   1         1         1       76s

minikube ip 192.168.49.2 // 로드밸런서 타입의 서비스에서 미니 큐브 IP를 지정

#metallb적용을 위한 구성및 리소스생성 ( metallb-config.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.49.50-192.168.49.99

k apply -f metallb-config.yaml

k get svc //리소스 IP확인 (외부로 개방, Pending에서 IP로 바뀜)
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
hello-service   LoadBalancer   10.97.255.187   192.168.49.50   80:32203/TCP   8m56s
kubernetes      ClusterIP      10.96.0.1       <none>          443/TCP        18m
world-service   LoadBalancer   10.96.6.13      192.168.49.51   80:31767/TCP   8m56s

 

2) Ingress적용 (enable)

- 미니 큐브로 수행하면 특성상 외부 노출만으로는 접근 불가, 터널링 필요

#inegres enable로 구성 
minikube addons enable ingress
💡  ingress is an addon maintained by Kubernetes. For any concerns contact minikube on GitHub.
You can view the list of minikube maintainers at: https://github.com/kubernetes/minikube/blob/master/OWNERS
💡  After the addon is enabled, please run "minikube tunnel" and your ingress resources would be available at "127.0.0.1"
    ▪ Using image registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
    ▪ Using image registry.k8s.io/ingress-nginx/controller:v1.12.2
    ▪ Using image registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
🔎  Verifying ingress addon...
🌟  The 'ingress' addon is enabled

#controller동작확인 ( Running이면 ingress enable ) 
k get pod -n ingress-nginx  
NAME                                       READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-7gcq2       0/1     Completed   0          71s
ingress-nginx-admission-patch-r7gqw        0/1     Completed   0          71s
ingress-nginx-controller-67c5cb88f-sqsfg   1/1     Running     0          71s

minikube service ingress-nginx-controller --url -n ingress-nginx
http://127.0.0.1:55970
http://127.0.0.1:55971

#인스턴스 생성 
k apply -f .
ingress.networking.k8s.io/my-ingress created
deployment.apps/hello-deployment unchanged
service/hello-service configured
deployment.apps/world-deployment unchanged
service/world-service configured

k get all
NAME                                    READY   STATUS    RESTARTS   AGE
pod/hello-deployment-746498bd8f-5qxwf   1/1     Running   0          29m
pod/world-deployment-8498cb8866-4qn5p   1/1     Running   0          29m

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/hello-service   ClusterIP   10.97.255.187   <none>        80/TCP    29m
service/kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP   39m
service/world-service   ClusterIP   10.96.6.13      <none>        80/TCP    29m

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-deployment   1/1     1            1           29m
deployment.apps/world-deployment   1/1     1            1           29m

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/hello-deployment-746498bd8f   1         1         1       29m
replicaset.apps/world-deployment-8498cb8866   1         1         1       29m

 

- Ingress Controller의 내용을 바꾼 다음 외부에서 사용가능한 포트를 tunnel로 열고 브라우저로 접속해서 확인가능 

- 인그레스 별도의 포트로 (8000번)로 연동하여 NodePort나 LoadBalancer로 노출가능  (별도의 추가설정 필요)

   kubectl port-forward --namespace ingress-nginx service/ingress-nginx-controller 8000:80

 

6. HealthCheck

- liveliness Probe

#로컬에서 실행 
kubectl port-forward <파드네임> 로컬포트:파트포트

#서버설정
const express = require("express");
const app = express();
const PORT = 8080;

let requestCount = 0;

# 동기적으로 CPU를 점유하여 블로킹을 시뮬레이션하는 함수
function sleep(seconds) {
  const waitUntil = new Date().getTime() + seconds * 1000;
  while (new Date().getTime() < waitUntil) {}
}

app.get("/", (req, res) => {
  requestCount++;

  # 5번째 요청마다 30초 동안 서버 전체를 멈추게 하는 버그
  if (requestCount % 5 === 0) {
    console.log(
      `Request #${requestCount}: Simulating a bug... blocking for 30 seconds.`
    );
    sleep(30);
    return res
      .status(200)
      .send(`Request #${requestCount}: Finally responding after a long delay!`);
  }

  console.log(`Request #${requestCount}: Responding normally.`);
  res
    .status(200)
    .send(
      `Hello from the buggy Node.js app! This is request #${requestCount}.`
    );
});

# Liveness Probe가 사용할 헬스 체크 엔드포인트
app.get("/healthz", (req, res) => {
  res.status(200).send("OK");
});

app.listen(PORT, () => {
  console.log(`Buggy Node.js app listening on port ${PORT}`);
});


#apiVersion: v1
kind: Pod
metadata:
  name: buggy-app-with-probe
  labels:
    app: buggy-fixed
spec:
  containers:
    - name: buggy-container      
      image: captainyun/buggy-node-app:v1.0 
      ports:
        - containerPort: 8080
      livenessProbe:
        httpGet:
          path: /healthz # app.js에 만든 health check 경로
          port: 8080
        initialDelaySeconds: 5 # Pod 시작 후 5초 뒤부터 검사 시작
        periodSeconds: 5 # 5초마다 검사
        failureThreshold: 1 # 1번 실패하면 바로 재시작 (의도적으로 낮게 설정하여 빠른 감지를 위함)

#새 터미널로 watch와 port-forward
watch kubectl get pod 
k port-forward pot/파드이름 8080:8080

 

 

- Readiness Probe ( Rolling Update로 무중단 구현시 활용 ) 

#서비스 구성 
apiVersion: v1
kind: Service
metadata:
  name: readiness-demo-service
spec:
  type: NodePort
  selector:
    app: readiness-app
  ports:
  - protocol: TCP
    port: 80 
    targetPort: 8080 
    nodePort: 30007
    
#Probe없는 경우 
const express = require('express');
const app = express();
const PORT = 8080;

let isReady = false;

# 시작 시 15초 동안 "준비 중" 상태를 시뮬레이션
console.log("Application starting... It will be ready in 15 seconds.");
setTimeout(() => {
  isReady = true;
  console.log("Application is now ready to accept traffic!");
}, 15000); // 15초 딜레이

app.get('/', (req, res) => {
  if (isReady) {
    res.status(200).send(`Welcome version 2 ! The application is ready. Served by ${process.env.HOSTNAME}`);
  } else {
    res.status(503).send('Service is not ready yet.');
  }
});

# Readiness Probe가 사용할 헬스 체크 엔드포인트
app.get('/healthz', (req, res) => {
  if (isReady) {
    res.status(200).send('OK');
  } else {
    res.status(503).send('Not Ready');
  }
});

app.listen(PORT, () => {
  console.log(`Slow-starting app listening on port ${PORT}`);
});

watch kubectl get po
minikube service <서비스 이름> --url //접속 가능한 Path 출력

 

- 준비되지 않은 상태에서 접속이 되어 오류가 발생하지 않도록 구성함, RollingUpdate에 적용 ( 정상 배포 시 추가배포되는 방식으로 구성할때 서비스 중단 방지) 

 

 

- StartUp Probe

- 시작 시간이 오래 걸리는 프로그램이나 프레임워크를 대기하도록 설정가능 

   ( liveness Probe는 시작 시간이 오래 걸리는 프로그램을 기다리지 못하고 restart만 반복 )

- Startup Probe적용시 대기 후 Startup성공시 liveness Probe가 구동됨 

 

7. Metric

- 자원의 사용을 제어 ( 메모리나 CPU사용량을 모니터 )

- Guranteed, Burstable, Best Effort순으로 안정성 보장

 

metric설정 및 지나친 자원요구로 인한 Instancer구동 테스트 시나리오 

#metric관련 addon을 설치해야 가능
minikube addmons enable metrics-servce 
watch kubectl top pod

#포트포워드로 pod 구동
k port-forward pod/pod이름 8080:8080

#pathVariable로 들어오는 값을 잡아서 실행을 시킴 (구동 프로그램)
const express = require('express');
const app = express();
const PORT = 8080;

let memoryHog = [];

app.get('/', (req, res) => {
  res.status(200).send(`
    Hello! This is a memory consumer app.
    Use /consume?mb=[number] to allocate memory.
    Current memory usage: ${Math.round(process.memoryUsage().rss / 1024 / 1024)} MB
  `);
});

#consume?mb=100 와 같이 요청하면 해당 크기의 메모리를 할당
app.get('/consume', (req, res) => {
  const mbToConsume = parseInt(req.query.mb, 10);

  if (isNaN(mbToConsume) || mbToConsume <= 0) {
    return res.status(400).send('Please provide a valid number for "mb" query parameter.');
  }

  # 1MB = 1024 * 1024 bytes. 각 element가 1 byte 이므로 해당 크기의 배열 생성
  const newAllocation = new Array(mbToConsume * 1024 * 1024).fill('x');
  memoryHog.push(newAllocation);

  const currentRssMb = Math.round(process.memoryUsage().rss / 1024 / 1024);
  
  console.log(`Allocated ${mbToConsume}MB. Current total memory usage: ~${currentRssMb}MB`);
  res.status(200).send(`Successfully allocated ${mbToConsume}MB. Current RSS: ${currentRssMb}MB`);
});

app.listen(PORT, () => {
  console.log(`Memory consumer app listening on port ${PORT}`);
});

# 메모리 증가함에 따라 pod구동의 경향성이 나타남 
localhost:8080/consume?mb=10 //mega단위라지만 사실상 더 많이 증가, watch에서 메모리 사용량 증가확인
localhost:8080/consume?mb=20 //급증함
localhost:8080/consume?mb=30 //급증함
localhost:8080/consume?mb=50 //이 정도에서 거의 멈추듯이 느려짐 

# limit을 주는 옵션의 설정 추가( metric에 대해 request에 대해 대응) 
apiVersion: v1
kind: Pod
metadata:
  name: memory-eater-with-limits
spec:
  containers:
  - name: memory-eater-container    
    image: captainyun/resource-app:v1.0
    ports:
    - containerPort: 8080
    resources:
      limits:
        memory: "128Mi" # 메모리 사용을 128 Mebibytes 로 제한
        cpu: "500m"     # CPU 사용을 0.5 core 로 제한
      requests:
        memory: "64Mi"  # 최소 64 Mebibytes 의 메모리를 보장 요청
        cpu: "250m"     # 최소 0.25 core 의 CPU를 보장 요청

 

 

8. Helm

#helm 설치 및 확인 
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

helm version
version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}

#차트생성 및 문법오류확인 
helm create my-first-chart
helm lint ./my-first-chart/
==> Linting ./my-first-chart/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed

#template내의 내용들은 {{}}으로 정의되어 있으며 완전한 resource로 helm이 만들어주는것 
helm template my-first-release ./my-first-chart/  //object를 살펴보는 명령 

--- 관련 세부내용
#Source: my-first-chart/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-first-release-my-first-chart
  labels:
    helm.sh/chart: my-first-chart-0.1.0
    app.kubernetes.io/name: my-first-chart
    app.kubernetes.io/instance: my-first-release
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
automountServiceAccountToken: true
---
# Source: my-first-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-first-release-my-first-chart
  labels:
    helm.sh/chart: my-first-chart-0.1.0
    app.kubernetes.io/name: my-first-chart
    app.kubernetes.io/instance: my-first-release
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: my-first-chart
    app.kubernetes.io/instance: my-first-release
---
# Source: my-first-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-first-release-my-first-chart
  labels:
    helm.sh/chart: my-first-chart-0.1.0
    app.kubernetes.io/name: my-first-chart
    app.kubernetes.io/instance: my-first-release
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: my-first-chart
      app.kubernetes.io/instance: my-first-release
  template:
    metadata:
      labels:
        helm.sh/chart: my-first-chart-0.1.0
        app.kubernetes.io/name: my-first-chart
        app.kubernetes.io/instance: my-first-release
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: my-first-release-my-first-chart
      containers:
        - name: my-first-chart
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
---
# Source: my-first-chart/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "my-first-release-my-first-chart-test-connection"
  labels:
    helm.sh/chart: my-first-chart-0.1.0
    app.kubernetes.io/name: my-first-chart
    app.kubernetes.io/instance: my-first-release
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['my-first-release-my-first-chart:80']
  restartPolicy: Never
  
//install 
helm install my-first-release ./my-first-chart/
NAME: my-first-release
LAST DEPLOYED: Mon Aug  4 14:35:33 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=my-first-chart,app.kubernetes.io/instance=my-first-release" -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
  
# helm으로 한번에 생성가능 (서비스, replicaset, pod생성)  
helm list 
NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
my-first-release        default         1               2025-08-04 14:35:33.222913 +0900 KST    deployed        my-first-chart-0.1.0    1.16.0   

#삭제
helm uninstall my-first-release  
release "my-first-release" uninstalled

 

 

 

9. Monitoring 

- Prometheus (코어 역할, 데이터 자체를 추출) + Grafana (식별하기 어려운 Metric데이터 시각화) 조합

- 이용자수 ( 예: 시간별 이용자수를 체크 ), 상품 ( 판매된 상품의 갯수, 총 가격의 합 등 ) , Alarm등의 연동 가능

 

1) 프로메테우스 

- 기본 설정에 대한 내용이 출력 ( 개별 구성한 Application의 Metric은 별도의 설정필요 )

 

설치 및 활용  

#namespace생성 
kubectl create namespace monitor

#repository추가 (helm chart를 받아옴)
helm repo add prometheus-community https://prometheus-community.github.io/helm-chartshelm repo update

#repo최신화 
helm repo update  

#namespace에 설치 
helm install kube-prom-stack prometheus-community/kube-prometheus-stack --namespace monitor 
NAME: kube-prom-stack
LAST DEPLOYED: Wed Aug  6 10:34:10 2025
NAMESPACE: monitor
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
  kubectl --namespace monitor get pods -l "release=kube-prom-stack"
Get Grafana 'admin' user password by running:
  kubectl --namespace monitor get secrets kube-prom-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Access Grafana local instance:
  export POD_NAME=$(kubectl --namespace monitor get pod -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=kube-prom-stack" -oname)
  kubectl --namespace monitor port-forward $POD_NAME 3000
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.

#Pod 확인 
kubectl --namespace monitor get pods -l "release=kube-prom-stack"
NAME                                                   READY   STATUS    RESTARTS   AGE
kube-prom-stack-kube-prome-operator-6f6857b96f-6kbcp   1/1     Running   0          2m45s
kube-prom-stack-kube-state-metrics-6c4dc9d54-wn9sv     1/1     Running   0          2m45s
kube-prom-stack-prometheus-node-exporter-9tqlg         1/1     Running   0          2m45s

혹은 
kubectl --namespace monitor get all
 
#Dash보드 접속 (기본 포트 9090)
kubectl port-forward service/kube-prom-stack-kube-prome-prometheus 9090:9090 -n monitor
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090

 

포트포워드 후 Local의 9090포트로 접속 (down되어 있다고 해도 실제로는 조금 다를수 있음)

 

- Query를 통해 조회 (  1로 표시된 경우 가동중 )

 

CPU 사용량 (node_cpu, 5분 측정후 평균)

        100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

       -> 이러한 쿼리를 grafana에서 활용하여 시각화 ( 시계열 메트릭 수집이 가능한 구조 )

 

System의 메모리 사용 (Application과 기초적인 pod들이 구동되고 있음)

 

 

2) 그라파나  (시각화)

kubectl port-forward svc/kube-prom-stack-grafana 3000:80 -n monitor

기본 dashboard

 

 

 

-----------------------------------------------------------------------------

미니큐브를 이용한 쿠버네티스 설정을 연습했다.

추가 1의 코드는 쿠버네티스의 각 구성을 익히기 위해 nginx로 구성된 연습용이며,

뒤이은 추가 2는 2개의 간단한 프로젝트를 구현해서 CI/CD로 구성하는 실습까지 추가로 진행하여

그 일부를 기록으로 남긴다.

'INFRA > Kubernetes' 카테고리의 다른 글

쿠버네티스_추가 (2, CI/CD실습 )  (7) 2025.08.10