Kubernetes 기초 (一)

Kubernetes 기초

# 인증 토큰 생성
kubectl create token admin --namespace kubernetes-dashboard

1. 네임스페이스

Kubernetes를 시작하면 네 개의 초기 네임스페이스가 자동으로 생성됩니다.

  • default: 새로운 클러스터에서 네임스페이스를 생성할 필요 없이 즉시 사용 가능한 기본 네임스페이스입니다.
  • kube-node-lease: 각 노드와 연관된 Lease(임대) 객체를 포함합니다. 노드 임대를 통해 kubelet이 하트비트를 전송할 수 있어 컨트롤 플레인이 노드 장애를 감지할 수 있습니다.
  • kube-public: 인증되지 않은 클라이언트를 포함한 모든 클라이언트가 읽을 수 있습니다. 클러스터 전체에서 표시되어야 하는 특정 리소스를 위해 예약되어 있으며, 클러스터 사용을 위한 것입니다.
  • kube-system: Kubernetes 시스템이 생성한 객체에 사용됩니다.
주요 명령어
# 네임스페이스 조회
[root@k8s-master ~]# kubectl get ns
NAME                   STATUS   AGE
default                Active   46h
kube-node-lease        Active   46h
kube-public            Active   46h
kube-system            Active   46h
kubernetes-dashboard   Active   46h

# 특정 네임스페이스下的 파드 조회 (미지정 시 default)
kubectl get -n kube-system pod

# 네임스페이스 생성
kubectl create ns myproject

# 네임스페이스 삭제 (하위 리소스도 함께 삭제됨 - 주의 필요)
kubectl delete ns myproject
YAML을 사용한 생성
[root@k8s-master ~]# vim myproject.yaml
[root@k8s-master ~]# cat myproject.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: myproject
[root@k8s-master ~]# kubectl apply -f myproject.yaml
namespace/myproject created
YAML 파일 활용
# YAML 파일로 리소스 생성
# 차이점: create는 일회성 생성, apply는 수정 후 재적용 가능
kubectl create -f xxx.yaml
kubectl apply -f xxx.yaml

# YAML 파일로 리소스 삭제
kubectl delete -f xxx.yaml

# 리소스의 YAML 형식 정보 조회
kubectl get resource-name -o yaml

# 리소스의 YAML 파일을 직접 수정하고 적용
# 참고: 적용이 안 될 때는 delete 후 apply 사용

2. 파드

파드는 실행 중인 컨테이너 그룹입니다. 파드는 Kubernetes에서 가장 작은 배포 단위이며, 컨테이너화된 애플리케이션을 실행하는 리소스 객체입니다. 다른 리소스들은 모두 파드 기능을 지원하거나 확장하기 위해 사용됩니다.

참고사항:
각 파드에는 "Pause" 컨테이너라는 특수한 루트 컨테이너가 있습니다. 이 Pause 컨테이너의 이미지는 Kubernetes에 속하며, Kubernetes는 컨테이너를 직접 관리하지 않고 파드를 관리합니다.
애플리케이션 파드는 다중 프로세스 설계로, 하나의 파드에 여러 컨테이너가 있고 각 컨테이너가 플랫폼의 일부를 실행합니다.

파드 생성 및 사용
[root@k8s-master ~]# kubectl run webserver --image=nginx
pod/webserver created
[root@k8s-master ~]# kubectl get pod
NAME        READY   STATUS              RESTARTS   AGE
webserver   0/1     ContainerCreating   0          18s

[root@k8s-master ~]# kubectl describe pod webserver
Name:             webserver
Namespace:        default
Priority:         0
Service Account:  default
Node:             k8s-node2/192.168.13.66
Start Time:       Thu, 11 Apr 2024 15:18:52 +0800
Labels:           run=webserver
Annotations:      cni.projectcalico.org/containerID: 988a58cf1c04de31abadf12c9440fd8e19857be9a5bd79440b0893a70f153c2b
                  cni.projectcalico.org/podIP: 192.168.169.130/32
                  cni.projectcalico.org/podIPs: 192.168.169.130/32
Status:           Running
IP:               192.168.169.130
IPs:
  IP:  192.168.169.130
Containers:
  webserver:
    Container ID:   docker://1a3ce0a1b4f470ba78c99bfae7f7558eb8c5aeebc180465011bd47bd36ecc173
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:b72dad1d013c5e4c4fb817f884aa163287bf147482562f12c56368ca1c2a3705
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Thu, 11 Apr 2024 15:20:46 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qszgb (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-qszgb:
    Type:                    Projected
    TokenExpirationSeconds: 3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
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  3m4s  default-scheduler  Successfully assigned default/webserver to k8s-node2
  Normal  Pulling    3m2s  kubelet            Pulling image "nginx"
  Normal  Pulled     70s   kubelet            Successfully pulled image "nginx" in 1m52.287s (1m52.287s including waiting)
  Normal  Created    70s   kubelet            Created container webserver
  Normal  Started    70s   kubelet            Started container webserver

# 대시보드에서도 상세 정보 확인 가능

# 로그 확인
kubectl logs webserver
YAML로 파드 생성
[root@k8s-master ~]# vim web-cre.yaml
[root@k8s-master ~]# cat web-cre.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webserver02
  labels:
    run: webserver02
spec:
  containers:
  - image: nginx
    name: webserver02
[root@k8s-master ~]# kubectl apply -f web-cre.yaml
파드 내 애플리케이션 접근
# 파드 상세 정보 조회
# 기본적으로 default 네임스페이스의 파드 조회, -a 옵션으로 전체 조회
[root@k8s-master ~]# kubectl get pod -owide
NAME         READY   STATUS    RESTARTS   AGE    IP                NODE        NOMINATED NODE   READINESS GATES
webserver    1/1     Running   0          23m    192.168.169.130   k8s-node2   <none>           <none>
webserver02  1/1     Running   0          9m2s   192.168.36.66     k8s-node1   <none>           <none>

[root@k8s-master ~]# curl 192.168.169.130
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

# IP와 포트로 파드 내 애플리케이션 접근

# 컨테이너 내부 접속 (docker 명령어와 유사)
kubectl exec -it webserver -- /bin/bash
하나의 파드에서 여러 컨테이너 실행
[root@k8s-master ~]# vim multi-container.yaml
[root@k8s-master ~]# kubectl apply -f multi-container.yaml
pod/multi-pod created
[root@k8s-master ~]# cat multi-container.yaml
apiVersion: v1
kind: Pod
metadata:
  name: multi-pod
  labels:
    run: multi-pod
spec:
  containers:
    - image: nginx
      name: nginx
    - image: tomcat:8.5.92
      name: tomcat
[root@k8s-master ~]# kubectl get pod -owide
NAME         READY   STATUS    RESTARTS      AGE   IP                NODE        NOMINATED NODE   READINESS GATES
webserver    1/1     Running   0             44m   192.168.169.130   k8s-node2   <none>           <none>
webserver02  1/1     Running   0             29m   192.168.36.66     k8s-node1   <none>           <none>
multi-pod    2/2     Running   2 (64s ago)   11m   192.168.169.131   k8s-node2   <none>           <none>

# 192.168.169.131:80 (nginx) 및 192.168.169.131:8080 (tomcat) 접근 가능

파드는 작은 Linux 환경과 같습니다.

3. 디플로이먼트

여러 파드를 관리하기 위한 컨트롤러

서비스 오케스트레이션 문제를 해결하기 위해 Kubernetes 1.2 버전부터 디플로이먼트 컨트롤러가 도입되었습니다. 이 컨트롤러는 파드를 직접 관리하지 않고, 레플리카셋을 통해 간접적으로 관리합니다. 즉, 디플로이먼트가 레플리카셋을 관리하고 레플리카셋이 파드를 관리합니다.

디플로이먼트를 사용하면 파드에 다중 복제본, 자동 복구, 스케일링 등의 기능을 제공할 수 있습니다.

자동 복구 기능
# 디플로이먼트 생성
[root@k8s-master ~]# kubectl create deployment app-backend --image=nginx
deployment.apps/app-backend created

# 자동 복구 기능 확인
# app-backend 파드를 삭제하면 새 접미사의 파드가 자동 생성됨
[root@k8s-master ~]# kubectl get pod -owide
NAME                        READY   STATUS    RESTARTS      AGE   IP                NODE        NOMINATED NODE   READINESS GATES
app-backend-5c48c5c8cd-mjvqn   1/1     Running   0             32s   192.168.36.67     k8s-node1   <none>           <none>
[root@k8s-master ~]# kubectl delete pod app-backend-5c48c5c8cd-mjvqn
pod "app-backend-5c48c5c8cd-mjvqn" deleted
[root@k8s-master ~]# kubectl get pod -owide
NAME                        READY   STATUS    RESTARTS      AGE    IP                NODE        NOMINATED NODE   READINESS GATES
app-backend-5c48c5c8cd-88tlv   1/1     Running   0             102s   192.168.36.68     k8s-node1   <none>           <none>

# 디플로이먼트를 통해 삭제해야 함
[root@k8s-master ~]# kubectl get deploy -n default
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
app-backend  1/1     1            1           6m29s
[root@k8s-master ~]# kubectl delete deploy app-backend -n default
deployment.apps "app-backend" deleted
다중 복제본 기능
[root@k8s-master ~]# kubectl create deployment replica-app --image=nginx --replicas=3
deployment.apps/replica-app created
[root@k8s-master ~]# kubectl get deploy
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
replica-app   3/3     3            3           20s

# 각 파드는 고유한 IP를 가짐
[root@k8s-master ~]# kubectl get pod -owide
NAME                       READY   STATUS    RESTARTS      AGE    IP                NODE        NOMINATED NODE   READINESS GATES
replica-app-6cc66f8b5f-2d4gl   1/1     Running   0             111s   192.168.36.70     k8s-node1   <none>           <none>
replica-app-6cc66f8b5f-7lzfj   1/1     Running   0             111s   192.168.36.69     k8s-node1   <none>           <none>
replica-app-6cc66f8b5f-skd92   1/1     Running   0             111s   192.168.169.132   k8s-node2   <none>           <none>

# 디플로이먼트와 파드의 매핑 방식
# 레이블의 해시 값을 통해 매핑
[root@k8s-master ~]# kubectl get replicaset --show-labels
NAME                   DESIRED   CURRENT   READY   AGE    LABELS
replica-app-6cc66f8b5f   3         3         3       5m8s   app=replica-app,pod-template-hash=6cc66f8b5f
[root@k8s-master ~]# kubectl get pod --show-labels
NAME                       READY   STATUS    RESTARTS      AGE     LABELS
replica-app-6cc66f8b5f-2d4gl   1/1     Running   0             5m15s   app=replica-app,pod-template-hash=6cc66f8b5f
replica-app-6cc66f8b5f-7lzfj   1/1     Running   0             5m15s   app=replica-app,pod-template-hash=6cc66f8b5f
replica-app-6cc66f8b5f-skd92   1/1     Running   0             5m15s   app=replica-app,pod-template-hash=6cc66f8b5f
YAML으로 디플로이먼트 생성
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    run: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
        - image: nginx
          name: nginx
# 사용 가능한 API 버전 목록
[root@k8s-master ~]# kubectl api-versions
admissionregistration.k8s.io/v1
apiextensions.k8s.io/v1
apiregistration.k8s.io/v1
apps/v1
authentication.k8s.io/v1
authorization.k8s.io/v1
autoscaling/v1
autoscaling/v2
batch/v1
certificates.k8s.io/v1
coordination.k8s.io/v1
crd.projectcalico.org/v1
discovery.k8s.io/v1
events.k8s.io/v1
flowcontrol.apiserver.k8s.io/v1beta2
flowcontrol.apiserver.k8s.io/v1beta3
networking.k8s.io/v1
node.k8s.io/v1
policy/v1
rbac.authorization.k8s.io/v1
scheduling.k8s.io/v1
storage.k8s.io/v1
v1
스케일링
[root@k8s-master ~]# kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           5h11m
[root@k8s-master ~]# kubectl scale deploy/nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled
[root@k8s-master ~]# kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/5     5            3           5h12m
자동 복구

worker 노드의 머신이 장애가 발생하면, 다른 머신에서 해당 서비스를 자동으로 시작합니다.
Kubernetes는 약 5분 동안 자동 장애 복구를 수행합니다.

롤링 업데이트 및 롤백
# 실시간 모니터링
kubectl get pod -w

# 업그레이드 또는_down그레이드
[root@k8s-master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.19.2 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deployment image updated
[root@k8s-master ~]#

# 롤백
# kubectl rollout --help

# 배포 히스토리 확인
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment/nginx-deployment nginx=nginx:1.19.2 --record=true
3         kubectl set image deployment/nginx-deployment nginx=nginx:1.19.10 --record=true

# 특정 리비전 상세 정보
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment --revision=2
deployment.apps/nginx-deployment with revision #2
Pod Template:
  Labels:       app=nginx-deployment
        pod-template-hash=6fbbbfffd8
  Annotations:  kubernetes.io/change-cause: kubectl set image deployment/nginx-deployment nginx=nginx:1.19.2 --record=true
  Containers:
   nginx:
    Image:      nginx:1.19.2
    Port:       <none>
    Host Port:  <none>
    Environment:        <none>
    Mounts:     <none>
  Volumes:      <none>

# 이전 버전 또는 지정된 버전으로 롤백
[root@k8s-master ~]# kubectl rollout undo deployment/nginx-deployment --revision=2
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment --to-revision=2

4. 서비스

지금까지 배포한 모든 애플리케이션은 브라우저에서 접근할 수 없습니다.
이전章节에서 설명한 바와 같이, 파드의 수명이 짧고 "생성 후 소멸"이라는 특성으로 인해 서비스 제공 파드의 IP 주소가频繁하게 변경됩니다. 서비스 접근 시 안정적인 IP 주소가 필요합니다. 이러한 요구 사항과 파드의 특성이 충돌하면서 서비스가 필요합니다.
서비스: 파드의 서비스 디스커버리 및 로드 밸런싱

# 3개의 nginx 생성 및 인덱스 파일 수정하여 구분
[root@k8s-master ~]# kubectl create deployment web-app --image=nginx --replicas=3
deployment.apps/web-app created
[root@k8s-master ~]# kubectl get pod -owide
NAME                    READY   STATUS    RESTARTS   AGE     IP                NODE        NOMINATED NODE   READINESS GATES
web-app-5f989946d-76vll   1/1     Running   0          3m49s   192.168.36.76     k8s-node1   <none>           <none>
web-app-5f989946d-pwxk2   1/1     Running   0          3m49s   192.168.169.144   k8s-node2   <none>           <none>
web-app-5f989946d-tgk6q   1/1     Running   0          3m49s   192.168.169.145   k8s-node2   <none>           <none>
[root@k8s-master ~]# curl 192.168.36.76
pod 111
[root@k8s-master ~]# curl 192.168.169.144
pod 222
[root@k8s-master ~]# curl 192.168.169.145
pod 333
서비스 노출 - kubectl expose
[root@k8s-master ~]# kubectl expose deploy web-app --port=80 --target-port=80
service/web-app exposed
[root@k8s-master ~]# kubectl get service
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   3d22h
web-app      ClusterIP   10.96.31.78   <none>        80/TCP    44s

# 서비스 IP가 3개의 nginx에 매핑됨
[root@k8s-master ~]# curl 10.96.31.78
pod 222
[root@k8s-master ~]# curl 10.96.31.78
pod 222
[root@k8s-master ~]# curl 10.96.31.78
pod 222
[root@k8s-master ~]# curl 10.96.31.78
pod 333
[root@k8s-master ~]# curl 10.96.31.78
pod 111
[root@k8s-master ~]# curl 10.96.31.78
pod 333
[root@k8s-master ~]# curl 10.96.31.78
pod 333
로드 밸런싱 원리

레이블의 app 매개변수가 일치하는 파드를 찾아 로드 밸런싱을 수행합니다.

[root@k8s-master ~]# kubectl get pod --show-labels
NAME                    READY   STATUS    RESTARTS   AGE   LABELS
web-app-5f989946d-76vll   1/1     Running   0          13m   app=web-app,pod-template-hash=5f989946d
web-app-5f989946d-pwxk2   1/1     Running   0          13m   app=web-app,pod-template-hash=5f989946d
web-app-5f989946d-tgk6q   1/1     Running   0          13m   app=web-app,pod-template-hash=5f989946d
[root@k8s-master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   3d22h
web-app      ClusterIP   10.96.31.78   <none>        80/TCP    5m36s
[root@k8s-master ~]# kubectl describe svc web-app
Name:              web-app
Namespace:         default
Labels:            app=web-app
Annotations:       <none>
Selector:          app=web-app
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.31.78
IPs:               10.96.31.78
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         192.168.169.144:80,192.168.169.145:80,192.168.36.76:80
Session Affinity:  None
Events:            <none>
kubectl delete svc web-app
외부 노출

웹 서비스는 외부에 노출해야 하지만, Redis, SQL, MQ 등은 내부에서만 접근해야 합니다.

# type 기본값 - Kubernetes 내부 네트워크에서만 접근 가능 (ClusterIP)
# --type 옵션:
#   ClusterIP, NodePort, LoadBalancer, ExternalName
#   기본값은 'ClusterIP'

# NodePort 타입으로 외부 노출
# 랜덤 IP가 할당됨 (다음章节에서 Ingress를 통해 통합 게이트웨이 설정)
[root@k8s-master ~]# kubectl expose deploy web-app --port=80 --target-port=80 --type=NodePort
service/web-app exposed
[root@k8s-master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        3d22h
web-app      NodePort    10.96.50.186   <none>        80:31242/TCP   6s

# 컨테이너 접근용 도메인 (마이크로서비스에서 도메인으로 직접 접근 가능)
# curl web-app-default.svc
# 서비스 IP는 변경될 수 있지만 Kubernetes 도메인은 변경되지 않음

태그: kubernetes k8s namespace Pod deployment

7월 1일 18:30에 게시됨