쿠버네티스 기초 개념 정리
쿠버네티스를 잘 이해하기 위해선 우선, 도커를 알고 있어야한다.
도커(Docker)란?
Node.js로 서버를 구축하기 위해서는 우선, Node.js가 서버(혹은 로컬 컴퓨터)에 설치되어있어야 한다.
실제 서비스 서버에서는 다양한 라이브러리, 환경이 구축되어있는데,
만약 서비스를 접속자 수가 늘어나
Scale out를 하려면 똑같은 환경을 다른 서버에 구축해야하는데 똑같이 구축하기에는 꽤 어렵다.
도커는 이 문제를 깔끔하게 처리해준다.
도커는 서비스를 위해 필요한 환경, 파일과 설정값 등 모든 정보를 "이미지(Image)"란 형태로 박제해서 저장한다.
어디서든 동일한 환경을 재현할 수 있도록 도커 이미지들을 도커 허브(Docker hub)를 통해 공유되고 다운받을 수 있다.
이 도커 이미지는 컨테이너(Container)에서 실행할 수 있다.
우리가 아는 컨테이너처럼 일종의 격리되고 독립된 공간으로 생각하면 간단하다.
즉, 컨테이너는 서비스를 위한 실행 환경을 독립적으로 운영할 수 있는 기반 환경 및 기술이다.
그럼 격리된 가상환경이란 점에서 동일한 VM(가상 머신)과는 어떤 차이가 있을까?
VM vs. 컨테이너
VM은 HW 인프라(Infrastructure)가 존재하고 그 위에 Hypervisor(Vmware, VirtualBox)를 설치하고 그 안에 독립적인 OS를 가지고 있는 가상 머신(VM)을 생성한다. (하드웨어 스택 가상화)
컨테이너는 다수의 컨테이너를 OS 커널에서 직접 구동하여 커널을 공유하여 Docker 플랫폼 위에 Application을 올릴 수 있다. (운영체제 수준 가상화)
불필요한 OS 없이 소스코드와 베이스 환경(애플리케이션)만 들어가있어 VM보다 훨씬 가벼워 확장/축소가 빠르고 배포/롤백도 간단하다.
컨테이너에서 워크로드를 격리하는 기능은 Linux의 일부 기술을 조합한 것이다.
- Linux process : 리눅스 프로세스마다 서로 분리된 고유의 가상 메모리 주소 공간이 있고, 빠르게 생성/삭제 가능
- Linux namespace : 리눅스 네임스페이스를 사용하여 애플리케이션에 제공할 항목인 프로세스 ID, directory tree, ip address 등을 제어. (단, Linux namespace != Kubernetes namespace)
- Linux cgroup : 애플리케이션이 사용할 수 있는 CPU 시간, 메모리, I/O 대역폭 기타 리소스의 최대 사용량 제어
- 유니온 파일 시스템 : 애플리케이션과 종속 항목을 간결한 최소 layer의 모음으로 효율적으로 캡슐화
Container Image는 여러 layer로 구성되어 있으며, '컨테이너 매니페스트' 파일 (ex. Dockerfile)을 통해 빌드(레이어 구성)된다.
컨테이너 실행시, 가장 최상위 레이어(=Container layer)가 생성되고 그 외는 모두 Read-only layer로 구성되어있다.
Container layer
- 실행 중인 컨테이너에 대한 새 파일 쓰기, 기존 파일 수정 및 파일 삭제와 같은 모든 변경사항을 기록
- 모두 임시 변경사항이므로 컨테이너가 삭제되면 레이어 내용도 영구 삭제
레이어 구성 방법
- From 문을 통해 기본 layer를 공개 저장소에서 가져와 생성
- Copy 명령어로 새 레이어 추가
- Run 명령어는 make 명령어를 사용하여 애플리케이션을 빌드하고 빌드 결과를 세 번째 레이어에 배치
- 마지막 레이어는 실행 시 컨테이너 내에 실행할 명령어 지정
- 만약 이미지에서 새 컨테이너를 만들면, 컨테이너 런타임에서 쓰기 가능한 layer를 추가
layer 특징
- 각 레이어는 이전 레이어와 일정 부분만 차이가 있음
- 변경할 가능성이 가장 낮은 레이어에서 변경할 가능성이 가장 높은 레이어로 구성
근데 만약 도커 자체가 다운되면 스케일 아웃이 무슨 소용이지?
그래서 나온게 멀티호스트 도커 플랫폼이다.
멀티호스트 도커 플랫폼
도커를 여러 대를 띄워 서비스를 분산되게 운영시키는 것이다.
여기서 만약 도커 하나가 다운되어도 시스템이 정상 동작할 수 있다.
그럼 여러 개면 환경을 어떻게 매번 관리할까?
한 시스템에 여러 노드로 나눠져 있고 실행을 위해서 각 노드를 하나하나 접속해서 배포, 실행(docker stop, run)을 해야한다.
버전 업할 때도 똑같이 하나하나 버전 업. 롤백할 때도 하나하나 해야하는데 어떡하나?
그래서 나온 개념이 컨테이너 오케스트레이션이다.
컨테이너 오케스트레이션 (Container Orchestration)
컨테이너 오케스트레이션은 "중앙 관리자"로
복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구이다.
컨테이너 오케스트레이션은 아래와 같은 기능을 제공해준다.
1. 상태 관리
2. 배포 관리 : 유휴 리소스를 관리하여 신규 컨테이너 생성시 알맞은 서버에 배치
3. 배포 버전 관리 : 배포/롤백시 중앙에서 자동으로 진행
4. 서비스 등록 및 조회 : 새로운 서비스가 추가된 경우 자동으로 해당 서비스에 대한 설정이 추가
쿠버네티스(K8S)
컨테이너 오케스트레이션 중 하나가 바로, 쿠버네티스이다.
"컨테이너화된 애플리케이션을 자동으로 배포, 스케일링 및 관리해주는 오픈소스 시스템"
※ 설치 없이 쿠버네티스 써보기
- 카타코다 쿠버네티스 플레이그라운드 (1시간 사용가능, Master, node1 구성)
https://www.katacoda.com/courses/kubernetes
- Play with Kubernetes (4시간, Master/worker node 직접 구성 가능)
https://labs.play-with-k8s.com
※ 직접 설치 방법
- kubeadm : 쿠버네티스에서 공식 제공하는 클러스터 생성/관리 도구
- kubespray : 쿠버네티스 클러스터를 배포하는 오픈소스 프로젝트 (멀티 master 구현 간편)
CNI(Container Network Interface)란?
말그대로 Container간 통신을 지원하는 인터페이스
다양한 종류의 플러그인이 존재한다. (flannel, calico...)
쿠버네티스 클러스터 구성
- Master node : 워커 노드들의 상태를 관리하고 제어
- etcd : key-value 타입의 저장소. worker node, 쿠버네티스의 상태정보들을 가지고 있음
- kube-apiserver : k8s API를 사용하도록 요청을 받고 요청이 유효한지 검사. kubectl 명령어로 된 요청을 받고 etcd저장소에서 정보를 받아 scheduler에게 요청
- kube-scheduler : apiserver가 준 etcd저장소 정보들을 통해 파드를 실행할 노드를 판단하여 선택
- kube-controller-manager : 파드를 관찰하며 개수를 보장
- Worker node : 도커 플랫폼을 통해 컨테이너를 동작하며 실제 서비스 제공
- 컨테이너 런타임 : 컨테이너를 실행하는 엔진. ex. docker, containerd, runc
- kubelet : 모든 노드에서 실행되는 k8s 에이전트. 데몬 형태로 동작. kube-apiserver가 pod를 시작하려고 하면 해당 노드의 kubelet에 연결되어, kubelet은 컨테이너 런타임을 사용하여 pod를 시작하고 수명 주기를 모니터링하여 kube-apiserver에 다시 보고.
- kube-proxy : k8s의 네트워크 동작을 관리(클러스터에서 pod 간 네트워크 연결을 유지). iptables rule의 방화벽 기능 사용하며 이는 Linux 커널에 내장.
쿠버네티스에서 컨테이너 동작 Flow
컨테이너를 쿠버네티스 플랫폼에 올려서 실행하기 위한 Flow를 알아보자
개발자가 여러 컨테이너(main ui, login, pay...)를 빌드한다(1)
해당 컨테이너를 Docker hub에 저장한다(2) 그리고 이 컨테이너가 실행되도록(deploy) 요청한다(3)
해당 command를 master node에게 보내고 master node는 REST API Server를 통해 요청을 받을 수 있다(4)
우측에 다양한 worker node 중 어느 노드에 배치하면 좋을지 REST API Server가 Scheduler에게 요청하고 Sceduler는 상태를 보고 응답해준다(5)
REST API Server는 worker node1에게 실행해달라고 요청한다(6)
worker node1의 kublet은 요청을 받고 도커 명령어로 바꿔서 Docker nginx에게 실행요청을 한다(7)
Docker demon은 Docker hub에서 서치하고 받아와서 컨테이너로 실행해준다.
이렇게 동작되는 컨테이너는 Pod라는 단위로 관리하게 된다.
※ 클러스터 로깅 : 컨테이너 로그, k8s 운영 로그들을 수집해서 중앙화
ex. ELK(ElasticSearch, Logstash, Kibana), EFK(ElasticSearch, Fluented, Kibana), DataDog
Namespace란?
namespace는 K8s API 종류 중 하나인데 클러스터 하나를 여러 개의 논리적인 단위로 나눠서 사용할 수 있도록 한다.
만약, blue, orange, green namespace를 생성했을 때 내부에 Pod, service가 나눠져있다.
kubectl에서 namespace 지정을 하면 API를 통해 해당 Pod에만 적용할 수 있다.
보통 용도에 따라 실행해야 하는 앱을 구분할 때 사용한다.
Pod란?
컨테이너를 표현하는 K8S API(쿠버네티스는 Pod라는 단위를 통해 컨테이너를 동작시킴)의 최소 단위로 Pod에는 하나 또는 여러 개의 컨테이너가 포함될 수 있다.
Worker 노드에서 실행되는 컨테이너의 집합이며, 하나의 Pod에는 한 개 이상의 서비스로 지정될 수 있다.
Service : Label Selector로 선택하여 하나의 endpoint로 노출되는 pod의 집합 (L4 로드밸런서)
- 종류 : ClusterIP, NodePort, LoadBalancer, ExternalName
Pod 별로 고유한 IP가 할당되고 Pod 내에서 PID namespace, network, hostname, 로컬 디스크을 공유한다.
Pod 동작 Flow
웹서버를 실행하기 위한 kubectl 커맨드를 입력하면
Master node의 API는 etcd 정보를 꺼내와서 Scheduler에게 어디에 실행하면 좋을지 요청한다.
응답 받은 뒤 직전의 Pod 상태를 Pending이 되고
배치를 받으면 Running 상태가 된다.
Controller란?
Master node 내 컴포넌트로 Pod의 개수를 보장한다.
웹서버를 실행하기 위한 kubectl 커맨드를 입력하고 API가 Scheduler에게 응답 받고
Controller에게 3개 보장하라고 요청을 준다.
Controller는 3개의 Pod를 주시 중이다가
그 중 1개가 장애가 나면 Controller는 API에게 Pod 한 개를 더 할당해야한다고 알려준다.
Controller 종류
Deployment는 ReplicaSet을 컨트롤해서 Pod수를 조절하고 Rolling Update를 위해 만들어졌다.
※ Rolling Update : Pod instance를 점진적으로 새로운 것으로 업데이트하여 배포가 서비스 중단 없이 이루어짐
Ingress란?
HTTP나 HTTPS를 통해 클러스터 내부의 서비스를 외부에서 접속할 수 있도록 지원해주는 것 (L7 로드밸런서)
즉, URI 기반으로 Client(사용자)를 쿠버네티스 내부 서비스와 연결시켜주는 역할을 한다.
보통 Service(L4 로드밸런서) 앞 단에 위치한다.
기능
- Service에 외부 URL을 제공
- 트래픽을 로드밸런싱
- SSL 인증서 처리
- Virtual hosting을 지정
동작방식
main ui, login, order 등 다양한 Pod가 나눠져있는데
Client가 접속할 단일 진입점을 Ingress Controller가 생성해준다.
특정 URL로 접속하면 이런 Service로 연결시켜줘! 하는 Ingress Rule을 Ingress Controller에 등록해준다.
Ingress Controller는 URL에 맞게 Service를 연결시켜준다.
Secret 이란?
컨테이너에서 읽혀지거나 사용되는 소량의 설정 및 민감한 정보이다.
배포 과정
기존
1. 개발자는 본인이 개발한 소스코드를 Git 저장소 Master branch에 병합한다.
2. Gihub actions, jenkins 등을 이용해서 빌드, 테스트, 배포까지 자동화 시켜준다.
빌드는 개발 코드와 코드 내 사용하는 라이브러리를 Gradle, Maven에서 받아와 패키징하여 App으로써 실행가능한 파일로 만드는 과정이다. 빌드, 테스트 과정을 Gihub actions, jenkins 등을 이용해서 자동화시켜준다.
3. 서버에 배포하기 위해서 기본 구조는 기본적으로 물리서버에 OS가 설치되어있고 개발언어에 따라 개발 언어를 실행해주는 런타임이 존재한다. 그리고 런타임에 의해 App이 바로 돌아가긴 하지만 여러 앱을 올리거나 앱 위에서 돌아가는 트랜잭션 등을 관리하기 위해 WAS 위에 APP을 올린다.
컨테이너 배포 방식
1. 젠킨스를 이용해서 깃헙 소스를 들고와서 빌드까지 하고 컨테이너를 만들기 위해 Docker를 이용하여 컨테이너 저장소에 이미지를 Push한다.
※ 컨테이너 빌드 : 컨테이너 환경에서 앱이 실행되려면 WAS와 Runtime 포함하여 Pod를 만들어야하는데 이런 컨테이너를 생성하여 저장소에 올리는 과정
2. 기존 : 쿠버네티스 리소스를 배포하기 위한 툴로 kubectl을 사용하여 쿠버네티스는 Pod를 생성한다.
helm 사용 : (2) Heml chart 저장소에서 pull 받아 (3)쿠버네티스에 배포한다.
※ 컨테이너 배포 : Pod 생성 명령어를 받으면 컨테이너 저장소에서 컨테이너를 다운받는 과정
4. Pod 생성과정에서 컨테이너 저장소에서 컨테이너 이미지를 다운받는다.
참고 : 따배쿠, 대세는 쿠버네티스, IBM 밋업(쿠버네티스 차근차근 다지기), 구글에게 듣는 쿠버네티스를 활용한 개발부터 프로덕션까지