쿠버네티스란?

| kubernetes
줄여서 k8s로 부른다

컨테이너화된 애플리케이션 배포, 확장, 관리를 자동으로 처리해주는 오픈소스 플랫폼
-> 컨테이너가 수십개로 늘어나면서 관리를 해야했고, 이를 관리해주는 플랫폼이라고 생각하면 될 듯하다

| 컨테이너 오케스트레이션이 필요한 이유
물리 서버-> 가상머신 -> 컨테이너 로 발전된 애플리케이션 실행 환경으로 발전하였고,
하지만 컨테이너도 수십개로 증가하며 이를 관리해주는 오케스트레이션이 필요하게 됨

| 클라우드 도입 시 얻는 장점이란?

  1. 자가 치유
  2. 자동 확장
  3. 로드 밸런싱
  4. 자동 롤백

| 자가치유
컨테이너가 실행 중 갑자기 멈추면 쿠버네티스가 이를 자동으로 감지해서 재시작하거나 새 컨테이너로 교체
-> ReplicaSet이 항상 설정한 수의 pod를 유지하므로 일부에 문제가 생겨도 전체 서비스에는 영향이 없음

| ReplicaSet? pod?

| 자동확장
사용자가 갑자기 몰리면, 컨테이너를 자동으로 늘림

=> 트래픽이 줄면, 불필요한 리소스를 줄여 비용을 절약함

| 로드 밸런싱
트래픽을 여러 컨테이너에 골고루 분배함

| 자동 롤백
새로운 버전을 배포할 때. 다양한 무중단 배포 전략 지원
안전한 버전으로 롤백 가능함

쿠버네티스 4가지 핵심 구성요소

  1. 컨트롤 플레인, 워커 노드(control plane. worker node)
  2. pod
  3. deployment
  4. service

| 컨트롤 플레인과 워커 노드, 전체 시스템을 관리하는 두뇌와 일꾼의 구조
클러스터는 여러 개의 node로 구성 + Control plane 과 worker node로 나뉨

| control plane
-> 두뇌의 역할을 수행함

| worker node
-> 일꾼 역할 수행함
control plane이 내린 명령에 따라 실제 컨테이너를 실행하고 애플리케이션을 구동함

| pod
-> 일꾼이 처리하는 최소 단위 업무
쿠버네티스의 최소 배포 단위

| deployment
-> pod를 몇개 유지하라는 명령어
ex)
이 pod는 항상 3개 유지하라면 컨테이너가 죽어도 새로 생성하면서 유지함
내부적으로는 ReplicaSet이 파드 수를 관리하며, 업데이트 관리

| Service
-> 외부 노출과 고정 주소

Docker 와 kubernetes

Online Boutique 기반 쿠버네티스 공부하기

https://github.com/GoogleCloudPlatform/microservices-demo
-> 해당 github를 통해 학습하며 yaml 파일을 공부했습니다

| frontend.yaml 파일

# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Q. kind가 여러 종류 있는데, Deployment는 어떤 역할을 하는 오브젝트일까?
apiVersion: apps/v1
kind: Deployment # pod를 몇개 유지하라는 명령어
metadata:  # 해당 리소스에 대한 식별 정보가 들어있다.
  name: frontend 
  labels:
    app: frontend        # Q. 이 label은 누가, 왜 사용할까? -> frontend라는  label을 사용
spec: # 리소스가 원하는 상태가 무엇인지를 설명해준다. 설계도라고 생각하면 편한다
  selector: # 이 배포가 어떤 pod를 관리할 것인가
    matchLabels: # matchLabels가 frontend인 것을 보아 frontend를 관리하는 것으로 보인다
      app: frontend      # Q. 위의 labels.app: frontend 와 같은 값 — 우연일까 의도일까?
  template:              # Q. template 아래가 실제 Pod 정의 — Deployment와 Pod는 어떤 관계일까?
                        # template는 실제 띄울  pod의 상세 설정을 나타냄
                        # Deployment는 pod를 몇개 유지하라는 명령어이고 metadata에 해당 이름을 넣고
                        # spec에서 어떤 pod를 관리할지 label이 frontend인 것을 관리하는 것으로 적었고
                        # template 에서 실제 pod에 대한 상세한 설명을 작성하는 것이다.
    metadata:
      labels:
        app: frontend  # pod의 label을 명시해주고
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "true" # Istio가 HTTP probe를 HTTPS로 재작성하는 설정 (mTLS와 직접 관련 없음)
    spec:
      serviceAccountName: frontend # 해당 pod가 쿠버네티스 안 에서 가길 identity
      securityContext: # 컨테이너가 실행되는 OS수준 제어
        fsGroup: 1000 # 컨테니어 내부 프로세스 사용자의 수준을 1000으로 고정
        runAsGroup: 1000
        runAsNonRoot: true 
        runAsUser: 1000
      containers: # 이제 어떤 컨테이너를 pod로 실행할지 확인할 수 있음
        - name: server 
          securityContext: 
            allowPrivilegeEscalation: false # 자식 프로세스가 부모 프로세스보다 더 높은 권환을 갖는 것을 방지
            capabilities: # 권한에 대해서 설명
              drop: # 권한에 대해서 drop함
                - ALL 
            privileged: false # 커널 특권 false
            readOnlyRootFilesystem: true # 자신의 파일을 수정할 필요없기에 true. -> 컨테이너의 불변성을 의미함
          image: frontend          # Q. 어떤 Docker 이미지를 쓰는 걸까? # frontend 도커
          ports:
          - containerPort: 8080    # Q. 컨테이너 안에서 열리는 포트 — 외부에서 바로 접근 가능할까?
                                    # 컨테니어 포트는 8080으로 열림 -> 외부에서 접근 불가능
          readinessProbe:          # Q. readiness vs liveness — 둘의 차이는?
                                   #  readniess : 해당 pod가 사용자의 트래픽을 받은 준비가 되었는가?
                                   # 
            initialDelaySeconds: 10 # 컨테이너 시작 후 10초까지는 유예
            httpGet:
              path: "/_healthz" 
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-readiness-probe"
          livenessProbe: # 해당 pod가 살아있는가, 좀비 상태에 있는가 확인
            initialDelaySeconds: 10 # 10초간 유예함
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-liveness-probe"
          env: # 설정
          - name: PORT
            value: "8080"
          - name: PRODUCT_CATALOG_SERVICE_ADDR # 
            value: "productcatalogservice:3550"   # Q. IP 대신 서비스 이름을 쓰는 이유는?
            # 고정된 IP를 할당받는 것이 아니기에 value를 통해 이름을 지정해줌
            # 만약에 product의 catalog를 얻고자 한다면, 해당 서비스에 물어보게 되는 것
          - name: CURRENCY_SERVICE_ADDR
            value: "currencyservice:7000"
          - name: CART_SERVICE_ADDR
            value: "cartservice:7070"
          - name: RECOMMENDATION_SERVICE_ADDR
            value: "recommendationservice:8080"
          - name: SHIPPING_SERVICE_ADDR
            value: "shippingservice:50051"
          - name: CHECKOUT_SERVICE_ADDR
            value: "checkoutservice:5050"
          - name: AD_SERVICE_ADDR
            value: "adservice:9555"
          - name: SHOPPING_ASSISTANT_SERVICE_ADDR
            value: "shoppingassistantservice:80"
          # # ENV_PLATFORM: One of: local, gcp, aws, azure, onprem, alibaba
          # # When not set, defaults to "local" unless running in GKE, otherwies auto-sets to gcp
          # - name: ENV_PLATFORM
          #   value: "aws"
          - name: ENABLE_PROFILER
          # 애플리케이션 성능 분석 도구를 enable할 것인가?
            value: "0"
          # - name: CYMBAL_BRANDING
          #   value: "true"
          # - name: ENABLE_ASSISTANT
          #   value: "true"
          # - name: FRONTEND_MESSAGE
          #   value: "Replace this with a message you want to display on all pages."
          # As part of an optional Google Cloud demo, you can run an optional microservice called the "packaging service".
          # - name: PACKAGING_SERVICE_URL
          #   value: "" # This value would look like "http://123.123.123"
          resources:
            requests:              # 이 pod에게 보장해줄 최소 자원 (스케줄링 기준)
              cpu: 100m            # 100m = 100 millicores = CPU 코어의 0.1개 (1000m = 코어 1개)
              memory: 64Mi
            limits:                # 이 pod가 사용할 수 있는 최대 자원 (초과 시 throttle/kill)
              cpu: 200m
              memory: 128Mi
              # requests: 최소 보장 / limits: 최대 허용
              # 둘 다 설정하는 이유: requests만 있으면 한 pod가 자원을 독점할 수 있고,
              # limits만 있으면 갑자기 자원이 부족해서 pod가 뜨지 않을 수 있음
---
# Q. 같은 파일에 Deployment와 Service가 같이 있는 이유는?
# Q. ClusterIP vs LoadBalancer — 아래 frontend-external과 무엇이 다를까?
apiVersion: v1
kind: Service
# service는 외부 노출과 고정 주소를 담당함
metadata:
  name: frontend
  labels:
    app: frontend
spec:
  type: ClusterIP
  # Cluster 내부에서 사용하는 IP
  selector:
    app: frontend        # Q. Deployment의 labels.app: frontend 와 연결 — 어떻게 Pod를 찾는 걸까?
    # frontend라고 명시되어있는  pod에게 보냄
  ports:
  - name: http
    port: 80             # Q. Service가 받는 포트
    targetPort: 8080     # Q. 실제 Pod로 전달하는 포트 — containerPort와 값이 같아야 할까?
    # targetport는 containerport와 같아야한다고 생각함
    # 80 포트로 받고 8080으로 보냄
    # 80은 클러스터 내부에서 서로 통신할 때 받아서 8080 내부로 보내서 응답을 받아옴
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-external
  labels:
    app: frontend
spec:
  type: LoadBalancer     # Q. ClusterIP와 다르게 외부에서 접근 가능 — 어떤 차이가 있을까?
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080
    # 외부에서 80포트로 들어오면 8080의 내부 포트로 보냄
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: frontend
#  frontend라는 이름의 계정이고 여기서 생성된 pod들은 frontend의 권한을 가지게 됨

# 마지막 service를 보면서 궁금한 점, loadbalancer는 외부에서도 접근할 수 있는 거면, 어떻게 해당 오픈소스 프로젝트의
# 구조가 어떻게 된 것인지 궁금해짐

쿠버네티스 배포 실습

docker ps
  먼저 Docker 켜져 있는지 확인하고.

  1. 클러스터 생성
  kind create cluster --name boutique

  2. 배포
  kubectl apply -f D:\Users\<username>\Desktop\git\microservices-demo\release\kubernetes-manifests.yaml

  3. Pod 상태 확인
  kubectl get pods -w
  전부 Running 될 때까지 대기.

  4. 브라우저 접속
  kubectl port-forward svc/frontend 8080:80
  → http://localhost:8080

| 어떤 pods가 있는지 확인
kubectl get pods

PS C:\Users\<username>  kubectl get pods
NAME                                     READY   STATUS    RESTARTS   AGE
adservice-848c5d6f88-h9p7z               1/1     Running   0          19m
cartservice-59d44fb67-z4qv7              1/1     Running   0          19m
checkoutservice-54475449f4-rsbsz         1/1     Running   0          19m
currencyservice-6bbd8c95f4-x7sb5         1/1     Running   0          19m
emailservice-68dd7ccf64-9mrkh            1/1     Running   0          19m
frontend-6b8fcb997-jdclw                 1/1     Running   0          19m
loadgenerator-8599589654-9z65l           1/1     Running   0          19m
paymentservice-cc458477b-59r5h           1/1     Running   0          19m
productcatalogservice-7d7957447b-dflkd   1/1     Running   0          19m
recommendationservice-84d6f4488d-85fxf   1/1     Running   0          19m
redis-cart-c4fc658fb-tf4lx               1/1     Running   0          19m
shippingservice-69f6756b4d-jtrh8         1/1     Running   0          19m

| 어떤 deployments가 있는지 확인
kubectl get deployments

PS C:\Users\<username>  kubectl get deployments
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
adservice               1/1     1            1           25m
cartservice             1/1     1            1           25m
checkoutservice         1/1     1            1           25m
currencyservice         1/1     1            1           25m
emailservice            1/1     1            1           25m
frontend                1/1     1            1           25m
loadgenerator           1/1     1            1           25m
paymentservice          1/1     1            1           25m
productcatalogservice   1/1     1            1           25m
recommendationservice   1/1     1            1           25m
redis-cart              1/1     1            1           25m
shippingservice         1/1     1            1           25m

| 어떤 services가 있는지 확인
kubectl get services

PS C:\Users\<username> kubectl get services
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
adservice               ClusterIP      10.96.226.166   <none>        9555/TCP       26m
cartservice             ClusterIP      10.96.248.15    <none>        7070/TCP       26m
checkoutservice         ClusterIP      10.96.116.251   <none>        5050/TCP       26m
currencyservice         ClusterIP      10.96.85.70     <none>        7000/TCP       26m
emailservice            ClusterIP      10.96.76.33     <none>        5000/TCP       26m
frontend                ClusterIP      10.96.135.14    <none>        80/TCP         26m
frontend-external       LoadBalancer   10.96.197.139   <pending>     80:30560/TCP   26m
kubernetes              ClusterIP      10.96.0.1       <none>        443/TCP        26m
paymentservice          ClusterIP      10.96.72.177    <none>        50051/TCP      26m
productcatalogservice   ClusterIP      10.96.85.150    <none>        3550/TCP       26m
recommendationservice   ClusterIP      10.96.135.137   <none>        8080/TCP       26m
redis-cart              ClusterIP      10.96.77.140    <none>        6379/TCP       26m
shippingservice         ClusterIP      10.96.29.39     <none>        50051/TCP      26m

| pod의 정보를 확인
kubectl describe pod <pod이름>

PS C:\Users\<username>  kubectl describe pod frontend-6b8fcb997-jdclw
Name:             frontend-6b8fcb997-jdclw
Namespace:        default
Priority:         0
Service Account:  frontend
Node:             boutique-control-plane/172.19.0.2
Start Time:       Wed, 29 Apr 2026 15:07:52 +0900
Labels:           app=frontend
                  pod-template-hash=6b8fcb997
Annotations:      sidecar.istio.io/rewriteAppHTTPProbers: true
Status:           Running
IP:               10.244.0.14
IPs:
  IP:           10.244.0.14
Controlled By:  ReplicaSet/frontend-6b8fcb997
Containers:
  server:
    Container ID:   containerd://e92a89e926d7630449ecbcf94c577c424d6fd34f71a9e321de1115e4a5f791b3
    Image:          us-central1-docker.pkg.dev/google-samples/microservices-demo/frontend:v0.10.5
    Image ID:       us-central1-docker.pkg.dev/google-samples/microservices-demo/frontend@sha256:50ca57c513de1ef22a97b6ef76989a73878d4a3873f85b5314bfc207853d5475
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 29 Apr 2026 15:09:15 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     200m
      memory:  128Mi
    Requests:
      cpu:      100m
      memory:   64Mi
    Liveness:   http-get http://:8080/_healthz delay=10s timeout=1s period=10s #success=1 #failure=3
    Readiness:  http-get http://:8080/_healthz delay=10s timeout=1s period=10s #success=1 #failure=3
    Environment:
      PORT:                             8080
      PRODUCT_CATALOG_SERVICE_ADDR:     productcatalogservice:3550
      CURRENCY_SERVICE_ADDR:            currencyservice:7000
      CART_SERVICE_ADDR:                cartservice:7070
      RECOMMENDATION_SERVICE_ADDR:      recommendationservice:8080
      SHIPPING_SERVICE_ADDR:            shippingservice:50051
      CHECKOUT_SERVICE_ADDR:            checkoutservice:5050
      AD_SERVICE_ADDR:                  adservice:9555
      SHOPPING_ASSISTANT_SERVICE_ADDR:  shoppingassistantservice:80
      ENABLE_PROFILER:                  0
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qrhls (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  kube-api-access-qrhls:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  44m   default-scheduler  Successfully assigned default/frontend-6b8fcb997-jdclw to boutique-control-plane
  Normal  Pulling    44m   kubelet            Pulling image "us-central1-docker.pkg.dev/google-samples/microservices-demo/frontend:v0.10.5"
  Normal  Pulled     43m   kubelet            Successfully pulled image "us-central1-docker.pkg.dev/google-samples/microservices-demo/frontend:v0.10.5" in 5.382s (1m20.11s including waiting). Image size: 11928584 bytes.
  Normal  Created    43m   kubelet            Created container: server
  Normal  Started    43m   kubelet            Started container server
1. kubectl get pods → 상태 확인
  2. kubectl describe pod → Events 확인
  3. kubectl logs → 앱 에러 확인

쿠버네티스 Namespace

| Namespace
클러스터를 논리적으로 격리하는 단위
-> Namespace에 각 리소스를 할당해서 관리할 수 있음

Pod, Service, Deployment 등을 하나로 묶어서 Namespace를 만들 수 있음

07_kubernetes_namespace.png514

참고자료

https://velog.io/@pinion7/Kubernetes-리소스-Namespace에-대해-이해하고-실습해보기

https://whatap.io/ko/blog/what-is-kubernetes-k8s-guide