
정리해야할 내용이 너무 많았다. 설명해야 할 부분도 많기에 내가 아무리 yaml파일들을 작성하고 개념 공부를 했더라도 정리해두지 않으면 평가자가 와서 얘기해야 할 시간에 횡설수설 할 게 뻔해서 회고 전 간단한 기록을 남기려고 한다.
문제 자체는 간단하다. 가상머신 내부에서 Vagrant를 이용하여 2개의 가상머신을 띄우고, k3s를 설치한 뒤 한 대는 마스터 노드 (컨트롤 플레인)
한 대는 워커 노드로 이용하여 클러스터를 구성하는 과제이다.
다른 게 어렵다기 보다는 그냥 Vagrant를 구성하기 위한 그 환경 자체에서 시간이 너무너무너무 오래 걸렸다... 막상 Nested VM 설정을 하고 나니 2시간만에 끝나버림... 허무...
Vagrant.configure("2") do |config|
config.vm.define "sejokimS" do |server|
server.vm.box = "ubuntu/bionic64"
server.vm.hostname = "sejokimS"
server.vm.network "private_network", ip: "192.168.56.110"
server.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 1
vb.customize ["modifyvm", :id, "--name", "sejokimS"]
vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
vb.customize ["modifyvm", :id, "--uartmode1", "file", File::NULL]
vb.customize ["modifyvm", :id, "--cableconnected1", "on"]
vb.customize ["modifyvm", :id, "--vram", "128"]
end
server.vm.provision "shell", path: "scripts/server.sh"
end
config.vm.define "sejokimSW" do |worker|
worker.vm.box = "ubuntu/bionic64"
worker.vm.hostname = "sejokimSW"
worker.vm.network "private_network", ip: "192.168.56.111"
worker.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 1
vb.customize ["modifyvm", :id, "--name", "sejokimSW"]
vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
vb.customize ["modifyvm", :id, "--uartmode1", "file", File::NULL]
vb.customize ["modifyvm", :id, "--cableconnected1", "on"]
vb.customize ["modifyvm", :id, "--vram", "128"]
end
worker.vm.provision "shell", path: "scripts/worker.sh"
end
end
보다시피 들여쓰기가 굉장히 빡빡하고 중요하다.
객체지향이나 스크립트 언어로 개발을 조금 해봤던 사람이라면 대충 이해가 갈 것 같은데, modifyvm에서 virtual machine을 커스텀하는 부분은 아무래도 잘 모를 것 같아서 한 줄씩 설명해야겠다.
name : 가상머신의 이름을 설정한다. 너무 쉽다.
--uart1 : UART1(직렬 포트)를 활성화 하고 특정 I/O 포트와 IRQ를 설정한다.
--uartmode1 : UART1 모드를 설정하고 출력을 /dev/null 로 리다이렉션하여 직렬 포트 출력을 파일로 저장하지 않고 무시한다.
--cableconnected1 : 가상 직렬 포트의 케이블이 연결된 것처럼 설정한다. 가상 머신이 직렬 포트를 통해 연결된 것처럼 인식하게 한다.
--vram: 비디오 메모리 크기를 128MB로 설정한다. GUI 환경에서 중요함.
쿠버네티스의 경량화 버전인 k3s는 싱글 바이너리 프로그램이다. 100MB 보다 작으면서, 쿠버네티스를 사용하기 위해 기본적으로 설치해야 하는 다른 바이너리들을 모두 하나로 사용한다. 따라서, 관리의 복잡성이 줄어들고 python과 같은 종속성이 필요하지 않다.
etcd를 사용하는 대신 SQLite를 사용하는데, 리소스가 부족한 환경에서의 사용량을 크게 줄인다.
네트워킹이 단순화 되어있다. k3s 바이너리에 내장된 flannel을 사용하며, 네트워크 오버레이를 제공하기 위해 다양한 백엔드를 지원한다. 또, 추가적인 네트워크 구성요소를 구성하고 관리할 필요성을 줄인다.
작동 방식(flow) :
VMWare, KVM, Hyper-v, Virtualbox, docker같은 다양한 가상화 툴을 이용하여 가상환경을 만들어준다.
- Box: 새로운 가상환경 구축을 위한 템플릿
- Synced folder: 호스트 (Vagrant 돌리는 컴퓨터)와 게스트 (만들어질 가상환경) 의 폴더를 동기화한다. 만약 호스트 시스템에 IDE를 사용하여 개발하고, VM 내부에는 프로그램을 작동시키기만 한다면 유용하다!
- Network: VM과 통신할 수 있도록 네트워크를 제공한다. 사설 네트워크, 전달된 포트 또는 공용 네트워크를 사용하도록 구성이 가능하다.
- Provision을 사용한 부트스트래핑: Vagrant의 실제 운영체제는 스크립트나 configuration file로 구성되고 관리된다. 소프트웨어 설치, 구성, DB 설정 등이 포함된다.
K3s를 도커 환경에서 돌릴 수 있도록 만들어졌다.
기본 시스템에 영향을 주지 않고서 쿠버네티스 클러스터를 빠르게 제작 및 해체할 수 있게 만들어준다. 도커 컨테이너 내부에서 K3s 클러스터를 만드는 것이다
K3s master 및 worker를 Docker container 내부에 생성한다. 클러스터의 각 노드는 Docker container이다.
ArgoCD는 Kubernetes환경에서 사용하는 Desire state의 CD tool이다.
Git repository에 있는 애플리케이션을 클러스터에 배포하는 것을 자동화한다.
Desire state
쿠버네티스와 마찬가지로 선언적 설정을 사용한다. ArgoCD의 상태와 환경이 YAML 또는 JSON 방식으로 Git 저장소에 저장된다.
GitOps work-flow
config 파일과 그 config에 대한 모든 변경사항이 Git repository를 통해 이루어지며, 저장소를 지속적으로 모니터링하다가 git에 저장된 구성과 일치하도록 변경사항을 자동으로 저장한다.
네트워크 인터페이스이다. 컴퓨터와 네트워크 간의 통신지점이며, eth는 LAN(Local Area Network)에서 가장 일반적으로 사용되는 이더넷 네트워크를 나타낸다.
eth0은?
부팅 프로세스 중에 시스템에 의해 발생한 첫 번째 이더넷 인터페이스를 이야기한다.
eth1은?
두 번째 이더넷 인터페이스이다. 가상화된 인터페이스일 수 있다. 물리적 인터페이스는 실제 컴퓨터에 설치된 랜 카드에 해당하지만, 가상 인터페이스는 소프트웨어를 기반으로 만들 수 있다.
Deployment, service, ingress의 개념에 대해 알아야 한다.
Vagrant로 가상머신 한 대를 작동한 후, k3s를 이용하여 마스터노드로 지정, 3개의 같은 이미지를 배포하게 되는데 호스트별로 각기 다른 페이지를 보도록 분산해야 한다.
클러스터 외부에서 클러스터 내부의 서비스로 HTTP / HTTPS 요청을 라우팅하는 API 객체이다. 일종의 게이트웨이 역할을 해주는데, 외부 요청을 받아 적절한 서비스로 보내는데 사용하는 것이다.
Ingress 자체가 객체의 종류를 나타낸다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
spec:
rules:
- host: sejokimapp1.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: app1-service
port:
number: 80
- host: sejokimapp2.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: app2-service
port:
number: 80
- host: sejokimapp3.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: app3-service
port:
number: 80
- host:
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: app3-service
port:
number: 80
metadata: 객체에 대한 메타데이터를 포함한다.
spec: Ingress의 실제 규칙을 명시한다. specification, 즉 명세서다.
보여준 ingress.yaml 파일에서는 rules 배열을 이용하여 도메인 이름에 따라 트래픽을 어떻게 라우팅할지 정의했다.
각 Rule 들은 특정 도메인 이름으로 들어오는 요청들을 각각의 서비스로 보내도록 설정했다.
Ingress는 공개 URL과 나의 애플리케이션을 매칭하고, 복수의 도메인을 갖는 가상 호스트를 이용할 수 있으며
클라이언트의 요청을 여러 POD에 분산할 수 있다.
애플리케이션의 상태를 선언적으로 업데이트하기위해 사용한다. 이게 되게 재밌는 개념이라 나중에 따로 글로 빼서 다루려고 한다.
요청한 개수만큼 Pod를 만들어 기동하는데, Pod의 개수가 줄어들면 새로운 Pod를 만들어 기동한다.
코드 배포를 세밀하게 제어하며, specification (spec)으로 명세한다.
도커 컴포즈와 동일하게 pod 내부는 같은 네트워크를 공유하며, pod 내에서 공유 가능한 볼륨도 설정할 수 있다.
컨테이너 내부의 리소스를 제안할 수도 있고, 환경변수를 넣을 수도 있다
특별할 건 없고 Replicas 가 3개인 게 app2의 특징이다. (app 1과 app 3는 이렇다할 특징이 없다)
쿠버네티스는 배포를 하는 가장 작은 단위로 pod를 사용하는데, 이 pod의 복제본을 레플리카라고 한다.
이 Replica가 쿠버네티스에서 굉장히 중요한 개념이다.
- 고가용성 : 만약, pod이 단일 인스턴스로 배포하고 있는 경우 해당 인스턴스에 문제가 생기면 다른 서비스까지 문제가 생긴다. 레플리카를 만들어 두면 하나의 인스턴스에 문제가 생겨도 다른 인스턴스가 계속 서비스를 진행한다.
- 부하분산: 3개의 레플리카를 통해 들어오는 요청을 모두 분산시킬 수 있다. 각 인스턴스에 과부하가 걸리는 것을 막을 수 있다.
- 확장성: 사용자 수 및 요청이 증가할 때 쉽게 스케일 아웃(더 많은 복제본 추가)을 할 수 있어 서비스가 증가하는 부하를 처리할 수 있다.
쿠버네티스가 실행되는 (배포되는) 최소 단위이다.
하나 이상의 컨테이너 그룹을 뜻하며, 스토리지와 네트워크를 공유한다.
항상 함께 배치되고, 스케줄링되며 공유 콘텍스트에서 실행된다.
파드는 어떻게 여러 컨테이너를 관리하는가?
-> 클러스터의 동일한 물리 또는 가상머신에서 자동으로 같은 위치에 배치되고 함께 스케줄링된다.
사용자가 개별 파드를 만드는 경우는 잘 없다.
배포될 서비스를 정의한다. 여기서 포트를 지정해두어서 Ingress의 포트와 헷갈릴 수 있는데
Service는 내부 네트워크에서 실행중인 여러개의 pod에 대한 안정적인 접근 방식을 제공한다.
파드 집합에서 실행중인 앱을 네트워크 서비스로 노출하는 역할을 수행한다.
내부에서 로드밸런서 역할을 수행하며 service에 정의된 포트로 들어오는 요청을 service에 연결된 pod들 사이에서 균등하게 분배한다.
Ingress는 엮어놓은 컴퓨터들 (클러스터를 이렇게 표현해봤다 ㅎ)의 외부에서 들어오는 요청을 받아 적절한 서비스로 라우팅하는 것이다.
지정된 IP로 생성이 가능하고, 여러 pod를 묶어 로드밸런싱이 가능하다.
고유한 DNS 이름을 가질 수 있다.
모든 쿠버네티스 설정 파일은 크게 3가지로 나뉜다.
metadata: 컴포넌트를 서로 구분지어줄 수 있는 이름과 설명을 입력할 수 있다.
spec(specification) / 명세서 : kind에 적힌 컴포넌트에 대한 구체적인 내용을 적용한다. 포맷은 컴포넌트 종류마다 다르다.
status: 자동으로 생성되고 자동으로 등록된다.
desire state(목표 상태) / actual state(현재 상태) 로 나뉜다.
Desire state를 유지하기 위해 self-healing이 있는 것이다.
Pod를 생성하기 위해서는 Deployment가 있어야 하며, Deployment가 이를 안정적으로 유지해준다.
그렇다면, service와 deployment는 어떠한 방식으로 연결되어 있는가?
-> Deployment metadata labels에 app:nginx가 있다면
내가 만들 spec의 template의 label도 app:nginx로 한다.
spec:
matchlabels:
app: nginx
ETCD는 믿을 수 있는 key-value 저장소이다.
c++ 자료구조처럼 키 값 하나, value 값 하나가 매칭되어 있는 것이다. 환경변수와 비슷하다.
환경변수처럼 저장이 매우매우 간단하고 빠르게 읽고 쓰기가 간편하다.
그런데, 이 저장소가 쿠버네티스와 무슨 관계가 있는 걸까?
우선, etcd는 쿠버네티스 클러스터의 모든 상태 정보와 메타데이터를 관리하고 클러스터의 신뢰성과 장애 내구성을 보장하는 핵심 컴포넌트이다.
etcd는 쿠버네티스의 다음과 같은 정보들을 저장한다.
- nodes : 클러스터링된 노드의 정보를 저장한다. 노드의 상태와 IP 주소 등의 메타데이터도 모두 저장한다.
- pods : 가장 작은 배포의 단위인 pods를 저장한다. 실행중인 파드의 정보와 포함되어있는 컨테이너, 파드의 상태, 스케줄링 정보 등을 저장한다.
- 서비스 정보 : 서비스와 이들이 어떻게 파드 그룹의 정보를 노출하고 있는지를 저장한다.
- 시크릿 : 암호화된 문자열과 구성 파일 및 스크립트를 포함하여 애플리케이션 운영에 필요한 기밀 정보를 저장한다.
- RBAC (역할 기반 접근 제어) : 사용자의 그룹과 권한, 역할 및 권한 부여를 정의한다.
- 네임스페이스 설정 : 리소스를 논리적으로 분리하는데에 사용되는 네임스페이스의 정의와 정보
- 리소스 할당 및 한계 : 각 리소스(파드, 서비스)등에 대한 할당량과 한계를 저장한다.
K3s는 쿠버네티스의 경량화 버전으로, 리소스가 제한된 사물 인터넷이나 엣지 컴퓨팅 환경에서 주로 사용된다.
가장 큰 차이점은 바로 이 etcd를 경량화 하기 위해 sqlite를 사용한다는 점이다.
k3s의 sqlite는 etcd와 마찬가지로 운영에 필수적인 정보를 저장하지만, etcd는 분산 시스템 설계로 매우 복잡하지만 sqlite는 경량화되고 빠른 성능을 자랑한다.
etcd와 sqlite의 차이점은 다음과 같다.
etcd는 네트워크 파티션이 발생하더라도 데이터의 일관성을 유지할 수 있으나, sqlite는 비분산, 파일 기반의 데이터베이스로 단일 인스턴스에서만 실행할 수 있다.
etcd는 수평 확장이 가능하나, sqlite는 단일 인스턴스 (context)로 운영되어 확장이 제한된다.
etcd는 다중 노드 구성을 통해 높은 가용성을 제공하며, 클러스터의 일부 노드가 실패하더라도 시스템은 계속 작동할 수 있다.
쿠버네티스 클러스터 내부를 <논리적으로> 분할한다. 여러 사용자와 응용 프로그램 간에 클러스터 리소스를 분할하는데에 사용한다.
앞서 설명했듯 생성 및 배포되는 가장 작은 단위이다.
범위 및 용도
네임스페이스: 여러 팀이나 프로젝트의 리소스를 격리하여 동일한 클러스터 아래에서 관리하는 데 사용한다. 여러 사용자 사이에서 클러스터 리소스를 분할하는 방식이다.
Pod: 클러스터에서 실행 중인 프로세스를 나타내며 Kubernetes의 기본 배포 가능 개체이다.
운영 수준
네임스페이스: 더 높은 수준에서 작동하여 클러스터를 논리적 그룹으로 구성하고 보안합니다.
포드: 애플리케이션의 패키징 및 런타임 측면을 직접 처리하는 기본적인 수준에서 작동합니다.
라이프사이클
네임스페이스: 일반적으로 프로젝트의 라이프사이클에 필요한 만큼 오래 유지되거나 조직적인 목적을 위해 필요한 만큼 유지됩니다.
네임스페이스를 삭제하면 내부에 포함된 모든 리소스도 삭제됩니다.
포드: 일반적으로 라이프사이클이 더 짧습니다. 응용 프로그램이 확장되거나 업데이트됨에 따라 포드는 자주 생성되고 파괴될 수 있습니다.
리소스 분리 대 애플리케이션 분리:
네임스페이스: 클러스터 수준에서 리소스 격리 및 관리 경계 형식을 제공합니다.
Pod: 노드 내에서 애플리케이션 실행 격리를 제공합니다. 각 Pod는 실행 중인 컨테이너 세트를 가질 수 있습니다.