레이블이 Kubernetes인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Kubernetes인 게시물을 표시합니다. 모든 게시물 표시

10/22/2024

Kubernetes 배포(Skaffold)

Kubernetes는 컨테이너 오케스트레이션 서비스로 오케스트레이션이라는 단어에서 알 수 있듯이 컨테이너화된 애플리케이션을 하나 이상의 노드로 구성된 클러스트에서 실행한다. 분산 컴퓨팅과 Docker 기반 컨테이너의 확산으로 Kubernetes를 비롯한 많은 컨테이너 오케스트레이션 서비스가 개발되었다. 마이크로서비스 아키텍처의 특성을 고려하면 Kubernetes와 찰떡궁합이라고 말할 수 있다. 이를 적용하면서 배운 내용을 정리한다.


Deployment

디플로이먼트는 포드를 관리하는 오브젝트로 설정 YAML 파일을 작성할 때 여러 속성들 때문에 조금 헷갈렸다. 설정 YAML 파일에서 spec은 생성하려는 오브젝트에 적용하는 속성을 명시하는 부분이다. selector 속성은 생성할 모든 포드를 찾는 방법을 디플로이먼트에 알려주며 matchLabels 속성은 selector 속성이 일치할 레이블을 명시한다.  template 속성은 디플로이먼트가 생성할 각 포드의 설정을 정의한다. 즉, metadata 속성과 spec 속성을 다시 명시하는 부분이다. 리뷰 서비스의 디플로이먼트 YAML 파일은 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: apps/v1
kind: Deployment
metadata:
  name: review
spec:
  replicas: 1
  selector:
    matchLabels:
      app: review
  template:
    metadata:
      labels:
        app: review
    spec:
      containers:
        - name: review
          image: csup96/tour-review
          env:
            - name: NODE_ENV
              value: 'development'
            - name: PORT
              value: '3000'
            - name: MONGO_URI
              value: 'mongodb://review-mongo:27017/review'
            - name: REDIS_HOST
              value: 'review-redis'
            - name: REDIS_PORT
              value: '6379'
            - name: JWT_ACCESS_SECRET
              value: 'personal-tour-project-in-nodejs-typescript-access'
            - name: JWT_REFRESH_SECRET
              value: 'personal-tour-project-in-nodejs-typescript-refresh'
            - name: NATS_URL
              value: 'http://nats:4222'
            - name: NATS_CLUSTER_ID
              value: 'tour'
            - name: NATS_CLIENT_ID
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
 
cs


Service

포드를 관리하는 디플로이먼트 설정 파일을 작성하는데 크게 어려운 점이 없었지만 서비스의 경우 달랐다. 서비스는 오브젝트의 일종으로 서로 다른 포드 간의 통신을 설정하거나 클러스터 외부에서 포드에 접근할 수 있는 URL을 제공한다. 따라서, 네트워킹이나 통신을 생각할 때는 항상 서비스를 염두에 두어야 한다. 다시 말해, 생성하는 거의 모든 포드나 디플로이먼트는 항상 관련된 서비스를 가진다고 볼 수 있다. Kubernetes는 크게 4개의 서비스를 제공한다. Cluster IP는 클러스터 내에서만 접근 가능한 포드에 쉽게 기억할 수 있는 URL을 설정한다. Node Port는 개발 목적으로만 사용되는 경우가 많으며 포드를 클러스터 외부에서 접근 가능하게 한다. Load Balancer도 포드를 클러스터 외부에 노출한다. External Name은 클러스터 내 요청을 CNAME URL로 리다이렉트 한다.

일단 클러스터 내의 포드 간 통신은 당연히 Cluster IP를 사용하면 된다. 서비스 A 포드가 서비스 B 포드와 이벤트로 통신을 할 경우 서비스 A가 이벤트를 방출하면 이벤트 버스 포드의 Cluster IP로 요청을 보내고 이벤트 버스는 서비스 A 포드의 Cluster IP로 응답을 전송한다. 예를 들어, 리뷰 서비스의 서비스 YAML 파일을 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
  name: review
spec:
  selector:
    app: review
  ports:
    - name: review
      protocol: TCP
      port: 3000
      targetPort: 3000
  type: ClusterIP
cs


클러스터 외부와의 통신의 경우 Node Port와 Load Balancer중 하나를 골라야 하는데 Node Port의 큰 단점은 생성 시 기본적으로 무작위 포트를 열기 때문이다. 정확한 포트를 지정할 수 없고 클러스터 내에서 임의의 포트를 할당받게 받기 때문에 Node Port를 변경하게 되면 할당받는 포트가 다를 가능성이 있다. Load Balancer는 이러한 고민을 없애준다. Load Balancer는 클러스터 전체에 대한 단일 진입 지점을 설정하고 내부에서 요청을 적절한 포드로 라우팅 한다. 다시 말하지만 포드로 라우팅한다는 것은 실제로는 포드에 대해 생성할 Cluster IP를 참조하는 것이다. 그렇다면 라우팅은 어떻게 처리할까? 바로 Ingress 포드이다!


Ingress

이제 문제는 Load Balancer로 들어온 요청을 어느 포드로 전달하는 것이다. 즉, 라우팅 규칙을 설정해야 하는데 이를 관리하는 것이 Ingress 포드이다. 여러 Ingress 포드 중에서 Ingress NGINX 포드를 사용하기로 결정했다. 사실 Load Balancer는 AWS, GCP 그리고 Azure와 같은 클라우드 서비스를 통해 제공받아야 하는데 Ingress-Nginx는 Ingress 포드뿐만 아니라 Load Balancer도 지원한다.


호스트 파일

하나의 Kubernetes 클러스터 내에서 여러 다른 애플리케이션을 다양한 도메인에 호스팅할 수 있는데 예를 들어, 애플리케이션 A는 blog-app.com 애플리케이션 B는 tasty-delivery.org에 있을 수 있다. Ingress NGINX는 여러 애플리케이션을 다양한 도메인에서 호스팅할 수 있음을 전제로 설정된다. 그런데 개발 환경에서는 큰 단점이 있다. 바로 모든 실행 중인 서버에 localhost로 접근한다. 그렇다면 라우팅 규칙이나 특정 도메인을 사용하는 아이디어가 Ingress NGINX와 어떻게 맞아 떨어질까? 개발 환경에서는 도메인을 localhost와 동일하게 인식하도록 속여야 한다. 만약 호스트가 blog-app.com이면 연결하려고 할 때 인터넷에 존재하는 실제 blog-app.com이 아닌 현재 컴퓨터에 연결하도록 속이는 것이다. 이를 위해 호스트 파일의 설정을 변경해야 한다. Windows는 C:\\Windows\System32\Drivers\etc\hosts, MacOS와 Linux는 /etc/hosts 파일을 수정한다.


ingress.yaml

NGINX는 와일드카드를 지원하지 않기 때문에 정규 표현식을 경로에 사용해야 한다. 경로의 순서는 매우 중요하다. 위에서부터 밑으로 경로를 순서대로 확인하기 때문에 OAuth 2.0 HTML 파일과 일치하는 정규 표현식이 가장 밑에 위치해야 한다. 만약 이 경로를 맨 위에 두면 경로 일치가 미리 발생해서 백엔드 경로를 사용할 수 없다. 또한, 리뷰나 예약과 같이 여행을 지칭하는 tours로 시작하는 API의 경우 tours 경로보다 위에 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tour-ingress
  annotations:
    nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
  ingressClassName: nginx
  rules:
    - host: tour.xyz
      http:
        paths:
          - path: /api/v1/auth/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: auth
                port:
                  number: 3000
          - path: /api/v1/oauth2/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: auth
                port:
                  number: 3000
          - path: /api/v1/users/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: auth
                port:
                  number: 3000
          - path: /api/v1/admin/users/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: auth
                port:
                  number: 3000
          - path: /api/v1/tours/?(.*)/bookings
            pathType: ImplementationSpecific
            backend:
              service:
                name: booking
                port:
                  number: 3000
          - path: /api/v1/bookings/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: booking
                port:
                  number: 3000
          - path: /api/v1/admin/bookings/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: booking
                port:
                  number: 3000
          - path: /api/v1/payments/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: payment
                port:
                  number: 3000
          - path: /api/v1/tours/?(.*)/reviews
            pathType: ImplementationSpecific
            backend:
              service:
                name: review
                port:
                  number: 3000
          - path: /api/v1/reviews/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: review
                port:
                  number: 3000
          - path: /api/v1/admin/reviews/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: review
                port:
                  number: 3000
          - path: /api/v1/tours/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: tour
                port:
                  number: 3000
          - path: /api/v1/admin/tours/?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: tour
                port:
                  number: 3000
          - path: /?(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: client
                port:
                  number: 3000
cs


Skaffold

애플리케이션을 실제 운영 환경에서 배포할 경우 애플리케이션 수정이 발생하면 Docker 이미지를 빌드하고 Docker Hub에 푸시한 후 kubectl rollout restart 명령어를 사용해서 배포를 진행한다. 하지만 개발 환경에서 이 과정은 너무 번거로운데 이를 도와주는 도구가 바로 Skaffold이다. Skaffold는 Kubernetes 개발 환경에서 다양한 작업을 자동으로 처리해주는 CLI 도구이다. 실행 중인 포드의 애플리케이션 코드를 매우 쉽게 수정할 수 있고 프로젝트와 관련된 모든 오브젝트들을 매우 빠르게 생성하고 삭제할 수 있다. Skaffold를 다운로드하고 설정하는 것은 홈페이지에서 쉽게 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: skaffold/v4beta3
kind: Config
manifests:
  rawYaml:
    - ./k8s/**/*
build:
  local:
    push: false
  artifacts:
    - image: csup96/tour-auth
      context: auth
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-booking
      context: booking
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-expiration
      context: expiration
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-payment
      context: payment
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-review
      context: review
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-tour
      context: tour
      docker:
        dockerfile: Dockerfile
    - image: csup96/tour-client
      context: client
      docker:
        dockerfile: Dockerfile
cs

update: 2024.10.22

12/20/2023

Kubernetes

Kubernetes

Kubernetes는 인기 있는 컨테이너 오케스트레이터(orchestrator)이다. 오케스트레이터는 컨테이너와 서버/노드 집합을 가져와서 컨테이너 워크로드를 노드 사이에 어떻게 실행할지 결정하는 역할을 수행한다. 간단하게 말하면, 여러 대의 서버를 하나처럼 동작하도록 만든다. 2014년 구글에서 공개된 여러 오케스트레이터 중 하나였으며 현재는 전 세계의 오픈 소스 커뮤니티에 의해 관리된다.

Kubernetes는 본질적으로 컨테이너 내의 애플리케이션에서 실행되는 일련의 API로 서버 집합을 관리하고 Docker에서 컨테이너를 실행한다. 기본적으로 Docker 위에서 실행된다. 따라서 컨테이너 런타임을 제거하지는 않으며 다중 노드 시스템을 관리하는 일련의 컨테이너가 컨테이너 런타임 위에 실행되는 것이다. 즉, Kubernetes는 컨테이너 내에서 실행되는 일련의 API로서 Docker 위에서 동작한다. Kubernetes는 API 집합과 명령 줄 도구를 제공하여 Swarm과 유사한 서버 인프라를 배포하고 유지 관리할 수 있게 해준다. 주로 사용하는 도구 중 하나는 cube control/Kubectl이다.

Kubernetes를 사용하려면 수많은 옵션이 있는데 가장 일반적인 방법 중 하나는 클라우드 공급업체를 통해 Kubernetes를 얻는 것이다. 클라우드 공급업체는 컨테이너를 실행할 Kubernetes를 서비스로 제공한다. 다른 클라우드 서비스와 마찬가지로 Kubernetes 종단점 및 API를 제공하며 로컬 도구를 사용하거나 클라우드 인터페이스에 내장된 GUI를 사용하여 Kubernetes로 제어되는 컨테이너를 배포하고 유지 관리할 수 있다. 인프라 공급업체가 Kubernetes를 패키지로 제공한다. 이를 Kubernetes 배포판(distribution)라고 부른다. Linux 배포 버전의 개념과 유사하다. Linux 세계에서는 모든 다양한 배포판에서 동일한 Linux 커널이 실행된다. 커널은 약간 다르게 구성되거나 다른 버전이지만 기본적으로는 동일하다. Ubuntu, CentOS, Amazon Linux 등 다양한 배포판은 각자 선호하는 도구 집합을 패키지로 제공한다. Kubernetes도 마찬가지다. 클라우드나 온프레미스 데이터 센터용 Kubernetes 배포판 중에서 요구 사항, 기존 공급업체 계약 또는 운영 체제 또는 공급업체 선호도를 기반으로 선택한다.


Kubernetes vs. Swarm

Kubernetes와 Swarm 둘 다 컨테이너 런타임 위에서 실행되는 컨테이너 오케스트레이터이다. Docker 컨테이너 런타임 이외에도 containerd와 CRI-O와 같은 컨테이너 런타임이 존재한다. 두 플랫폼 모두 공급업체 지원이 뒷받침되는 견고한 플랫폼이다.

요약하면, Swarm은 구축하기 쉽고 노드를 추가하기 쉽고 시작하기 쉽고 관리하기 쉽다. 그래서, Swarm에 대해 말할 때 '쉬움'이라는 한 단어로 요약할 수 있다. 하지만 Swarm은 모든 경우에 Kubernetes만큼 다양한 용도로 활용되지는 않는다. Kubernetes는 훨씬 더 다양한 기능과 유연성, 맞춤화 기능을 가지고 있다. 더 많은 문제를 더 다양한 방식으로 해결할 수 있으며 일반적으로 더 많은 곳에서 사용되고 지원된다. 만약 DevOps 커뮤니티에 속해 있고 이 도구를 사용해 비즈니스 문제를 해결하려 한다면 두 플랫폼을 설치하고 애플리케이션을 배포하고 애플리케이션을 배포하는 기본 기능을 알아야 한다.

일반적으로, Swarm은 처음 시작하고 작은 규모로 시작하는 사람들에게 추천하는 툴이며 Kubernetes를 반드시 사용해야 한다는 확신이 없는 한 먼저 Swarm을 추천한다. 하지만 Swarm의 한계에 도달한 것 같고 원하는 기능을 충분히 얻지 못할 것 같다고 느끼면 Kubernetes를 고려한다.

Swarm

  • Docker와 함께 제공되는 단일 공급업체에서 지원하는 솔루션이다. 즉, 컨테이너 런타임과 오케스트레이터를 개발한 회사가 동일하다. 복잡성과 실행에 필요한 자원이 상대적으로 작다. Docker 위에서 Swarm을 실행하는데 필요한 자원은 매우 작다.
  • 배포하고 관리하기 쉽다. 수백 개에서 수천 개의 노드로 쉽게 확장할 수 있으며, 관리하는 데 큰 팀이 필요하지 않을 수 있다.
  • 80/20 규칙을 따른다고 말할 수 있다. Kubernetes의 기능 중 20%를 가지고 있지만, 컨테이너와 오케스트레이션을 위한 80%의 사용 사례를 해결한다. 물론 이는 정확한 과학적 비교가 아니다. Swarm은 대부분의 웹 애플리케이션, 장기 실행 솔루션을 실행하는 데 사용 사례의 다수를 해결하며 클라우드나 데이터 센터, 작은 기기에서도 잘 작동한다.
  • Docker가 실행되는 곳이라면 어디에서든 실행된다. Docker 엔진은 어느 툴보다도 더 많은 장소에서 실행된다. 리눅스뿐만 아니라 윈도우에서, 메인프레임에서도 실행된다. 또한, IoT, 라스베리파이와 같은 ARM 디바이스에서도 실행된다.
  • 기본적으로 안전하다. Swarm을 만들고 노드를 추가할 때 상호 TLS 인증을 보장한다. 제어판을 암호화하고 데이터베이스를 암호화하여 필요한 경우 비밀을 안전하게 보호한다.  
  • Swarm은 요소가 적기 때문에 문제 해결이 더 쉽고 관리해야 할 것이 더 적다. Docker에 기본으로 내장되어 있기 때문에 Docker의 문제 해결 방법을 알고 있다면 아마도 Swarm의 문제 해결 방법도 알 수 있다. 동일한 데몬 로그를 사용하며 애플리케이션에 대해 동일한 Docker 로그 명령을 사용한다. 노드 및 Swarm 자체의 이벤트를 확인하기 위해 동일한 Docker 이벤트 명령을 사용한다.

Kubernetes

  • 가장 넓은 클라우드 및 공급업체 지원을 제공한다. 모든 클라우드 공급업체가 Kubernetes을 제공하며 직접 실행하지 않고도 시스템을 시작하고 Kubernetes 배포 파일을 사용하여 애플리케이션을 실행할 수 있다. 
  • VMware, Red Hat을 포함한 인프라 공급업체들도 자체 Kubernetes 배포판을 가지고 있다.
  • 지원하는 사용 사례 범위가 가장 넓다. 많은 기능과 제삼자 옵션을 가지고 있어서 Swarm보다 더 많은 경우의 수를 다룬다.
  • 이미 사용 중인 공급업체가 이미 Docker의 Swarm 제품을 고려하기 전에 Kubernetes를 지원한다. 특히, 산업 전체적으로 Kubernetes를 지원해야 한다는 태도를 취하고 있기 때문에 이미 사용 중인 공급업체들이 Kubernetes를 지원하는 배포판이나 솔루션을 이미 가지고 있을 수 있다. 
  • "IBM 제품을 사면 절대로 해고 당하지 않는다." 과거 IBM이 컴퓨팅에서 가장 큰 공급업체였고 하드웨어, 메인프레임, PC를 외주했을 때 인기를 끌었던 시절에 널리 사용되었다. 기술적으로 어떤 공급업체를 선택할지 결정하지 못할 때 안전한 길을 선택하면 IBM을 선택하는 것이었다. 전반적으로, 현재 Kubernetes 플랫폼에 대해서도 그러한 상황이 되었다.


Architecture


다중 마스터 설정에서는 Swarm과 마찬가지로 홀수 개의 마스터가 필요한데 Swarm과 마찬가지로 RAFT 프로토콜을 사용하여 합의를 유지하기 때문이다. 마스터(master)는 제어판이라고도 불리는데 제어판은 클러스터를 관리하는 컨테이너 모음으로 API 서버, 스케줄러, 컨트롤러 매니저, etcd 등을 포함한다. 제어판(control plane)은 마스터 노드가 의사 결정을 내리는 것을 의미한다. Swarm의 일꾼 노드를 Kubernetes에서는 노드(node)라고 한다. 노드도 컨테이너를 실행할 수 있지만 일반적으로 애플리케이션은 노드에 Kubernetes 관리 시스템은 마스터에 유지한다. 노드와 마스터 모두 Docker나 containerd, CRI-O와 같은 다른 컨테이너 런타임 위에서 실행된다.

마스터 내부에서는 시스템을 제어하기 위해 여러 컨테이너를 실행해야 하는데 그 중 하나가 etcd이다. etcd는 키-값 쌍을 위한 분산 저장 시스템이다. Swarm에서 사용되는 RAFT 알고리즘 시스템과 매우 유사하다. etcd는 etcd라는 별도의 제품으로 Kubernetes 없이도 설치할 수 있고 구성 데이터를 저장한다. 또한 동일한 RAFT 프로토콜을 사용하기 때문에 동일한 규칙이 적용된다. 홀수 개수가 필요하다. 한 개로 시작할 수 있지만, 실제 내고장성(fault tolerance)을 위해서는 세 개가 필요한다. 또한, 데이터를 etcd 내부에 저장하고 클러스터를 관리하는 여러 Kubernetes 컨테이너를 실행해야 한다. 

API 서버는 클러스터와 대화하고 클러스터에 명령을 내릴 수 있는 방법이다. 스케줄러(scheduler)는 컨테이너가 노드 상에 포드(pod)라고 불리는 객체 내에서 어디에 어떻게 배치되는지를 제어한다. 

컨트롤러 매니저(controller manager)는 전체 클러스터의 상태와 실행 중인 모든 것을 살펴보고 API를 통해 이를 수행한다. 명령이나 명세를 받아들이고 원하는 작업과 실제로 일어나고 있는 작업 사이의 차이를 결정한다. 기본적으로는 시스템 전체를 살펴보고 현재 일어나고 있는 모든 것을 요청된 작업과 동일하게 만들어내는 루프이다. 

DNS를 제어하기 위한 것이 필요한데 기본적으로 coreDNS가 내장되어 있다. 나중에 네트워크 또는 스토리지와 같은 추가 기능을 얻으면 여기에 더 많은 컨테이너가 실행된다. 모든 노드에서는 에이전트(agent)가 실행되어야 한다. 이는 kubelet이라고 불린다. 또한, 처음에는 네트워크를 제어하기 위해 kube-proxy가 필요하다. 

Docker와 마찬가지로 이 모든 것들은 각각의 역할에 대해 반복된다. 다중 마스터를 사용할 경우 마스터를 동일한 컨테이너들에 모두 설정해야 한다. 워커 노드로 설정한 각 노드도 최소 두 개의 컨테이너(kubelet, kube-proxy)를 갖는다.

update: 2023.12.20