Print
카테고리: [ Cloud Computing & MSA ]
조회수: 4167

순식간에 Kubernetes에 대해 알고가자.


1. 소개

Kubernetes란 무엇인지 알아보자.

쿠버네티스라고 부른다. 혹은 큐브(Kube)라고 부르기도 한다. 그리스어로 조타수, 항해사라는 뜻을 가지고 있다.


2. 의미

마이크로 서비스 아키텍처는 비즈니스 민첩성(Agility), 유연한 확장성(Scalability), 강한 복원력(Resiliency) 등을 제공한다. 하지만 단지 응용 프로그램을 마이크로 단위로 구현한다고 해서 이러한 이점을 얻을 수 있는 것은 아니다. 각 마이크로 서비스를 위한 독립된 실행 환경 확보 및 자동화된 배포(CI/CD) 환경이 뒷받침되어야 한다.

우선 각 마이크로 서비스들을 실행할 수 있는 독립된 환경이 필요하다. 또 마이크로 서비스 단위로 부하에 따라 빠르고 유연하게 확장/축소할 수 있어야 한다. 하지만 물리 서버 기반에서는 각 서비스별로 리소스 할당이 자유롭지 못하기 때문에 가상화 기법을 사용하는 것이 일반적이다. 가상화는 VM 가상화와 컨테이너 가상화로 구분할 수 있는데, 컨테이너 가상화는 VM 가상화에 비해 Guest OS 영역에 대한 오버헤드가 존재하지 않아 가볍고, 작고 독립적인 단위로 분할할 수 있기 때문에 확장성 측면에서도 장점이 있다.

여기서 잠깐,

(1) 각 컨테이너에는 자체 운영 체제 인스턴스가 있습니다.

컨테이너는 가상 머신보다 훨씬 빠르게 시작하고 더 적은 리소스를 사용합니다. 각 컨테이너에는 자체 운영 체제 인스턴스가 없기 때문입니다.

(2) 컨테이너는 환경에 느슨하게 연결됩니다.

컨테이너 가상화는 Docker가 대표적이다. 그런데 서버의 수와 컨테이너의 수가 증가하게 되면 그 관리가 쉽지 않으며, 서비스 단위 관리, 서비스 간 연결 방안도 필요하다. 이러한 요구사항을 충족하기 위하여 컨테이너 오케스트레이션 도구가 등장하게 되었는데 춘추전국시대를 거쳐 현재는 Kubernetes가 사실상의 표준으로 자리잡았다.


3. 역사

2014년 발표되었으니 역사가 그렇게 오래된 것은 아니다. 처음 만든 곳은 구글이다. 원래 구글에는 Borg라는 솔루션(?)이 있었는데 이것이 발전했다고 보면 된다. 위키에 따르면 첫 코드 네임은 "Seven"이었다고 하는데 이는 스타트랙의 1등 항해사임. 하지만 쿠버네티스가 정말 단기간에 만들어진 것은 아니다. 10년 이상의 구글 노하우가 집약되어 있다고 보면 된다.

Docker 등장 이후 시장에는 여러 Orchestration Tool들이 등장하였는데 2018년 정도에 쿠버네티스가 천하통일했다고 봐도 무방하다.

리눅스에서 컨테이너 기술은 namespace와 cgroup을 통해 가능해졌다고 할 수 있다.


4. 특징

4.1. 특징

4.2. VM과 비교

흔히 VM에 대비한 컨테이너 기술은 Pet(애완동물) vs. Cattle(가축)으로 비유된다. VM(애완동물)에는 이름이 있고 생명주기가 길지만 컨테이너(가축)는 이름도 없고 일찍 죽는다..

멀티 테넌시를 구현할 수 있는데 1) namespace를 이용하거나, 2) 클러스터 분리를 통해 가능하다.

근데 컨테이너가 모든 것이 좋은 것은 아니다.


5. Node

노드에서는 마스터 노드와 워커 노드가 있다. 마스터 노드는 전체적으로 관장하는 노드이고 워커 노드는 일을 하는 노드이다. (당연한 말..)

5.1. 마스터 노드

클러스터의 상태를 관리하고, API 서비스를 제공하는 노드이다.

API Server, etcd, Scheduler(kube-scheduler), Controller 등으로 구성된다.

5.1.1. API Server

5.1.2. etcd

5.1.3. Scheduler

5.1.4. Contoller

컨트롤러는 노드 관리, 내부 정보 생성 및 업데이트, 상태 변경 등을 수행한다.

5.1.5. High Availability

5.2. 워커 노드

kubelet, kube-proxy 등으로 구성된다. 

5.2.1. kubelet

kubelet은 각 워커 노드에서 실행된다. 노드 에이전트 역할이라고 보면 된다. Pod를 생성하기 위해서는 kubelet에 컨테이너 런타임 환경이 필요한데 기본적으로는 도커를 많이 사용한다. 물론 다른 컨테이너 런타임 환경을 사용할 수도 있다. 이와 관련해서는 이 문서를 확인한다.

워커 노드 설계 시에는 적정 수량을 고민해야 하는데 워커 노드가 너무 많으면 마스터의 API Server가 부담이 되고, 소수의 워커 노드에 Pod가 너무 많으면 kubelet이 부담이 된다.

5.2.2. kube-proxy

kube-proxy는 호스트의 NIC를 컨테이너에 브리지한다.


6. 클러스터 접근

6.1. kubectl

kubectl이 사용하는 위치 정보, 인증 정보는 kubectl config view 로 확인 가능하다.

6.2. REST API

HTTP 클라이언트로 REST API에 접근하고자 한다면, 위치 정보와 인증을 위한 몇가지 방법이 있다.

6.2.1. kubectl proxy

kubectl proxy --port=8080 처럼 하면 curl http://localhost:8080/api/ 처럼 접근 가능하다.

6.2.2. kubectl describe secret

grep과 cut의 조합으로 얻어낼 수 있다.

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
SECRET_NAME=$(kubectl get secrets | grep ^default | cut -f1 -d ' ')
TOKEN=$(kubectl describe secret $SECRET_NAME | grep -E '^token' | cut -f2 -d':' | tr -d " ")

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

혹은 jsonpath를 사용할 수도 있다.

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
SECRET_NAME=$(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}')
TOKEN=$(kubectl get secret $SECRET_NAME -o jsonpath='{.data.token}' | base64 --decode)

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

다만 --insecure 옵션은 MITM 공격의 여지가 있다. 

6.2.3. 프로그래밍 접근

Go, Python 용 클라이언트 라이브러리가 지원된다.

6.2.4. Pod에서 접근

Pod에서 API 접근 시에는 apiserver의 위치 지정과 인증이 다르다.

Pod에서 apiserver 위치 지정은 kubernetes.default.svc DNS 사용이 권장된다. 이 DNS 이름은 apiserver로 라우팅되는 서비스 IP가 resolve된다.

apiserver 인증에 추천하는 방식은 서비스 어카운트 인증을 사용하는 것이다.

접근 방식은 다음과 같다.


7. Pods


8. Service

각 Pod들이 고유 IP를 갖고 있지만 Service의 도움 없이는 클러스터 외부로 노출될 수 없다.

Service는 크게 4종류가 있다.

8.1. ClusterIP

가장 기본적인 Service이고, 클러스터 내부에서 사용 가능하다. 다시 말해 클러스터 외부에서는 사용할 수 없다.

apiVersion: v1
kind: Service
metadata:
  name: my-cip-service
spec:
  type: ClusterIP
  selector:
    app: metrics
    department: sales
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

8.2. NodePort

각 노드의 지정된 포트를 할당한다. 노드의 포트를 사용하기 때문에 클러스터 내부와 외부에서도 접근 가능하다. 특이한 점이 하나 있는데 예를 들어 포트가 8080이고 Pod가 node1에만 있고 node2에는 없을 때 node2:8080으로 접근하더라도 node1의 Pod에 연결된다.

apiVersion: v1
kind: Service
metadata:
  name: my-np-service
spec:
  type: NodePort
  selector:
    app: metrics
    department: engineering
  ports:
  - protocol: TCP
    port: 80
    targetPort: 50000

8.3. LoadBalancer

GCP, AWS 등 퍼블릭 클라우드 서비스를 사용할 때 가능한 옵션이다. Pod를 클라우드가 제공하는 로드 밸런서를 통해 외부로부터 접근이 가능하게 해준다.

apiVersion: v1
kind: Service
metadata:
  name: my-lb-service
spec:
  type: LoadBalancer
  selector:
    app: products
    department: sales
  ports:
  - protocol: TCP
    port: 60000
    targetPort: 50001

8.4. ExternalName

Service를 externalName의 값과 매치한다. 주로 클러스터 내부에서 외부로 접근할 때 사용한다. 외부로 접근 시 주로 사용하기 때문에 설정할 때 셀렉터가 필요 없다. 이 서비스로 접근 시에는 설정된 CNAME 값으로 연결되어 클러스터 외부에서 접근할 수 있다.

apiVersion: v1
kind: Service
metadata:
  name: my-xn-service
spec:
  type: ExternalName
  externalName: example.com

아래 설명할 Ingress를 사용하지 않는 경우에도 NodePort 등을 사용하면 외부 요청을 처리할 수 있음을 알 수 있다.


9. Ingress

쿠버네티스 외부 -> 내부로 들어오는 트래픽을 처리하기 위한 규칙 세트로 Layer 7의 요청을 처리할 수 있다.

기능은 다음과 같다.

외부 요청 처리 측면에서 어느 정도는 NodePort로도 가능은 하지만, 세부적인 기능을 애플리케이션 레벨에서 구현 시에는 꽤나 복잡하게 될 것이다.

Ingress가 위와 같은 개념이라고 하면 실제 구현체는 Ingress Controller이다. 쿠버네티스가 공식적으로 제공하는 Ingress Controller는 ingress-gce, ingress-nginx (https://github.com/kubernetes/ingress-nginx)이다.

다음과 같은 yaml 파일을 한번 보자.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: sarcio.com
    http:
      paths:
      - path: /top
        backend:
          serviceName: svc1
          servicePort: 80
      - path: /main
        backend:
          serviceName: svc2
          servicePort: 80
  - host: iosarc.com
    http:
      paths:
      - backend:
          serviceName: svc2
          servicePort: 80

해석하면 다음과 같다.


10. CI/CD

여러가지 방안이 있다.

10.1. 쿠버네티스에서 Jenkins 구축

10.1.1. 환경

kubeadm와 테라폼으로 구성한 3개 워커 노드 환경을 만들었다고 하자. 쿠버네티스 클러스터는 AWS EBS와 연동이 된다고 하자.

(작성중)


11. EKS

AWS가 제공하는 관리형 Kubernetes 서비스이다.

11.1. 소개

11.2. 전환 전략