회사에서 EKS 를 유지보수해야 할 일이 생겨, 미뤄두었던 쿠버네티스를 공부하기로 마음먹었다..
5장부터 시작하려고 한다.
05장. 쿠버네티스 설치
그리스어로 조타수라는 뜻의 쿠버네티스는 오늘날 사실상 표준 (de facto standard) 으로 사용되고 있는 컨테이너 오케스트레이션 도구입니다. 구글에서 2014년에 오픈소스로 공개한 이후로, 오픈시프트(Openshift), 렌처(Rancher) 와 같은 클라우드 플랫폼을 비롯한 많은 회사들이 쿠버네티스를 실제 서비스 운영에 도입해 사용하고 있습니다.
쿠버네티스는 다른 오픈소스 오케스트레이션 툴과 비교해 매우 많은 장점이 있습니다.
서버 지원 클러스터링, 마이크로서비스 구조의 컨테이너 배포, 서비스 장애 복구 등 컨테이너 기반의 서비스 운영에 필요한 대부분의 오케스트레이션 기능을 폭넓게 지원합니다.
구글, 레드햇을 비롯한 많은 오픈소스 진영에서 쿠버네티스의 소스코드에 기여하고 있기 때문에 성능과 안정성 면에서 신뢰받고 있습니다.
영속적 볼륨 (Persistent Volume), 스케줄링, 장애 복구, 오토 스케일링, 서비스 디스커버리 및 인그레스 (Ingress) 등 컨테이너 기반의 클라우드를 운영할 때 필요한 대부분의 기능과 컴포넌트를 사용자가 직접 커스터마이징할 수 있습니다.
CNCF (Cloud Native Computing Foundation) 및 다른 클라우드 운영 도구들과 쉽게 연동되므로 확장성이 높습니다.
CNCF 는 리눅스 재단 (Linux Foundation) 산하의 단체입니다. 쿠버네티스는 CNCF에 소속된 오픈소스이며, 도커 컨테이너의 핵심 중 하나인 containerd, 컨테이너의 데이터를 수집하기 위한 프로메테우스 (Prometheus) 등이 함께 CNCF 에 소속돼 있습니다.
5.1 쿠버네티스 설치 환경의 종류
개발 용도의 쿠버네티스 설치
Minikube
Docker Desktop for Mac/Windows 에 내장된 쿠버네티스
서비스 테스트 또는 운영 용도의 쿠버네티스 설치
kops
kubespray
kubeadm
EKS, GKE 등의 매니지드(Managed) 서비스
Docker Desktop for Mac 이나 for Windows 를 설치했다면 쿠버네티스를 함께 사용할 수 있으므로 별도의 설치 과정이 필요하지 않습니다. 또는 Minikube 를 사용하면 버추얼 박스 등의 가상화 환경에서 쿠버네티스를 손쉽게 설치할 수 있습니다. 그러나 이러한 개발 용도의 쿠버네티스는 로컬 노드를 스탠드얼론 (standalone) 모드로 사용하기 때문에 쿠버네티스의 기능들을 완벽하게 사용해보기에는 적합하지 않다는 단점이 있습니다.
간편하게 설치해 사용할 수 있는 대신, 기본 기능의 테스트 및 로컬 개발 용도로 제한된다는 한계점이 있습니다.
개발 용도가 아닌, 실제 서비스 테스트 또는 운영 용도로 쿠버네티스를 사용하려면 어떠한 환경에서 쿠버네티스를 설치할 것인지를 먼저 결정해야 합니다. 첫 번째는 AWS, GKE 등의 클라우드 플랫폼 환경이고, 두 번째는 자체적으로 보유한 온프레미스(on-premise) 서버 환경입니다.
자체 서버 환경에서 쿠버네티스 설치
자체 서버 환경에서는 kubespray, kubeadm 등의 도구를 이용해 쿠버네티스를 설치할 수 있습니다.
클라우드 플랫폼에서 쿠버네티스 설치
서버 인스턴스만을 사용해 쿠버네티스를 설치할지, 쿠버네티스 자체를 서비스로서 제공하는 매니지드 서비스를 사용할지 선택해야 합니다.
AWS 의 EC2 인스턴스를 생성한 다음, 그 위에 직접 쿠버네티스를 설치하는 방법이 될 수 있습니다.
이때 사용할 수 있는 쿠버네티스 설치 도구는 kubespray, kubeadm, kops 등이 있습니다.
쿠버네티스 자체를 클라우드 서비스로서 사용
처음 시작하는 단계에 있다면 곧바로 매니지드 서비스를 이용하기보다는 직접 쿠버네티스를 설치해 전체적인 개념을 파악하는 것이 좋습니다.
쿠버네티스는 클라우드 플랫폼에서만 사용할 수 있는 기능이 일부 포함돼 있습니다. 따라서 로컬 개발 환경이나 온프레미스 서버에서 쿠버네티스를 설치해 사용할 경우 로드 밸런서(LoadBalancer) 또는 퍼시스턴트 볼륨(Persistent Volume) 등의 기능을 사용하지 못할 수도 있습니다.
5.2 쿠버네티스 버전 선택
운영 단계의 쿠버네티스를 고려하고 있다면 쿠버네티스 버전을 신중히 선택하는 것이 좋습니다.
2020년 9월 기준으로 사용할 수 있는 최신 쿠버네티스 버전은 1.18 버전입니다.
5.3 개발 용도의 쿠버네티스 설치
5.3.1 Docker Desktop for Mac / Windows 에서 쿠버네티스 사용
(책이 오래되다보니 책에 나오는 UI 와 도커 데스크탑 UI 가 많이 바뀌었다.. )
일단 싱글노드로 만들어보자.
버전확인
kubectl version
만들어졌다..
5.3.2 Minikube 로 쿠버네티스 설치
Minikube 는 로컬에서 가상 머신이나 도커 엔진을 통해 쿠버네티스를 사용할 수 있는 환경을 제공합니다.
Minikube 는 가상 머신 또는 도커를 통해 쿠버네티스를 설치하기 때문에 버추얼 박스 또는 도커 엔진이 미리 설치돼 있어야 합니다. 이번 절에서는 기본 설정을 이용해 버추얼 박스로 Minikube 를 설치하는 방법을 먼저 살펴보고, 리눅스 서버에서 가상 머신 없이 도커만으로 Minikube 를 설치하는 방법을 살펴보겠습니다.
버추얼박스를 설치했다면 다음 명령어로 minikube 가상 머신을 생성할 수 있습니다. minikube 는 자동으로 minikube 의 ISO 파일을 내려받아 설치합니다.
특정 버전의 쿠버네티스를 설치하려면 다음과 같이 --kubernetes-version 옵션을 추가합니다.
minikube start --kubernetes-version v1.13.5
리눅스 서버에서 가상 머신 없이 도커 엔진만으로 minikube 설치
도커 엔진을 미리 설치해 둔 리눅스에서는 가상 머신을 생성하지 않고도 Minikube 를 이용해 쿠버네티스를 설치할 수 있습니다. 앞의 "기본 설정을 이용해 버추얼 박스로 minikube 설치" 에서 "2. minikube, kubectl 내려받기" 를 참고해 minikube 와 kubectl 을 내려받은 뒤, 다음 명령어를 실행합니다.
minikube start --vm-driver=none
설치가 완료된 뒤에는 다음 명령어로 쿠버네티스가 정상적으로 설치됐는지 확인합니다.
kubectl version --short
Minikube 를 삭제하려면 minikube delete 명령어를 사용합니다.
minikube delete
5.4 여러 서버로 구성된 쿠버네티스 클러스터 설치
이번 절에서는 여러 개의 서버를 이용해 쿠버네티스 클러스터를 설치하는 방법인 kubeadm, kops, GKS 에 대해서 알아봅니다. 사용할 서버의 개수는 각자의 환경에 맞게 준비하면 되지만, 쿠버네티스의 각종 기능을 제대로 사용하려면 최소한 3대 이상의 서버를 준비하는 것이 좋습니다. 따라서 이번 절에서는 1개의 마스터와 3개의 워커 노드로 구성된 테스트용 쿠버네티스 클러스터를 설치하는 방법을 설명합니다.
각 서버에서 아래의 항목들이 준비됐는지 확인한 후에 설치를 진행해야 합니다.
모든 서버의 시간이 ntp 를 통해 동기화돼 있는지 확인합니다.
모든 서버의 맥(MAC) 주소가 다른지 확인합니다. 가상 머신을 복사해 사용할 경우, 같은 맥 주소를 가지는 서버가 존재할 수 있습니다.
모든 서버가 2GB 메모리, 2 CPU 이상의 충분한 자원을 가지고 있는지 확인합니다.
다음 명령어를 사용해 모든 서버에서 메모리 스왑 (Swap) 을 비활성화합니다. 메모리 스왑이 활성화돼 있으면 컨테이너의 성능이 일관되지 않을 수 있기 때문에 대부분의 쿠버네티스 설치 도구는 메모리 스왑을 허용하지 않습니다.
swapoff -a
kubeadm, kops, GKE 는 모두 서비스 운영 환경에서 사용할 수 있는 좋은 방법이지만, 이번 장에서 설명하는 설치 및 클러스터 구성은 쿠버네티스의 기능을 테스트하기 위한 간소화된 방법이라는 점에 유의해야 합니다.
5.4.1 kubeadm 으로 쿠버네티스 설치
쿠버네티스 설치 도구입니다.
kubeadm 은 온프레미스 환경, 클라우드 인프라 환경에 상관없이 일반적인 리눅스 서버라면 모두 사용할 수 있습니다. 이번 예제에서는 별도의 설정 없이 AWS 에서 우분투 18.04 의 EC2 서버를 4개 생성한 다음에 진행한다고 가정합니다. 그러나 가상 머신, 베어 메탈 서버 환경 등에서 동일하게 따라 해도 상관없습니다.
aws iam create-group --group-name kops # IAM User group 생성
# Permissions policies 추가
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonSQSFullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEventBridgeFullAccess --group-name kops
aws iam create-user --user-name kops # User 생성
aws iam add-user-to-group --user-name kops --group-name kops # User 를 User group 에 추가
aws iam create-access-key --user-name kops # Access key 생성
# configure the aws client to use your new IAM user
aws configure # Use your new access and secret key here
# Because "aws configure" doesn't export these vars for kops to use, we export them now
export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)
3. S3 버킷에 쿠버네티스 클러스터의 설정 정보 저장
kops 는 쿠버네티스의 설정 정보를 S3 버킷에 저장하기 때문에 kops 가 사용할 S3 버킷을 미리 생성해 둬야 합니다.
참고: S3는 us-east-1 이외의 지역에 대해 --create-bucket-configuration LocationConstraint=<region>이 필요합니다. 참고: 이전 상태 저장소를 되돌리거나 복구해야 하는 경우를 대비하여 S3 버킷을 버전화하는 것이 좋습니다.
버켓이름은 전세계에서 유일해야 합니다. 중복이 안되니, --bucket 을 적절하게 수정하세요.
마지막으로 클러스터 구성하려면: kops update cluster --name myclluster.k8s.local --yes --admin
생성된 클러스터 확인
kops get cluster
4. 쿠버네티스 클러스터 옵션 변경
마스터와 워커 노드의 인스턴스 타입이나 워커 노드의 개수 등을 조절할 수 있습니다. 다음 명령어를 입력해서 워커 노드의 옵션을 수정해 보겠습니다.
먼저 인스턴스 그룹 이름을 확인한다.
kops get ig --name $NAME
kops edit ig {INSTANCE_GROUP_NAME} --name $NAME
필자는 아래와 같이 입력했다.
kops edit ig nodes-ap-northeast-2a --name $NAME
노드의 개수는 maxSize 와 minSize 를 수정해 변경할 수 있고, 워커의 CPU 와 메모리 크기는 인스턴스 타입을 수정해 변경할 수 있습니다. 이번 예시에서는 3개의 워커 노드를 생성하도록 설정해 보겠습니다. 다음과 같이 maxSize 와 minSize 를 3으로 변경합니다.
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: "2025-12-18T14:29:00Z"
labels:
kops.k8s.io/cluster: mycluster.k8s.local
name: nodes-ap-northeast-2a
spec:
image: 099720109477/ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20251126
machineType: t3.micro
maxSize: 1
minSize: 1
role: Node
subnets:
- ap-northeast-2a
kops 가 자동으로 생성하는 AWS 의 오토 스케일링 그룹에서 사용되는 숫자입니다.
다음은 마스터 노드의 설정을 변경해 보겠습니다. 마스터 노드는 1개의 노드만을 사용하되, 인스턴스 타입을 워커와 동일한 종류인 t2.medium 으로 변경하겠습니다.
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: "2025-12-18T13:07:11Z"
labels:
kops.k8s.io/cluster: mycluster.k8s.local
name: control-plane-ap-northeast-2a
spec:
image: 099720109477/ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20251126
machineType: t2.medium
maxSize: 1
minSize: 1
role: Master
subnets:
- ap-northeast-2a
5. 쿠버네티스 클러스터 생성
다음 명령어를 입력하면 kops 가 자동으로 서버 인스턴스, 네트워크 리소스 등을 생성해 쿠버네티스를 설치합니다.
kops update cluster --name $NAME --yes --admin
쿠버네티스 클러스터의 준비에는 약 5~10분 정도가 소요됩니다. 쿠버네티스 설치 진행 상황은 kops validate cluster 명령어를 사용해 확인할 수 있습니다.
kOps has set your kubectl context to mycluster.k8s.local W1218 22:25:14.263041 87927 update_cluster.go:439] Exported kubeconfig with no user authentication; use --admin, --user or --auth-plugin flags with `kops export kubeconfig`
Cluster is starting. It should be ready in a few minutes.
Suggestions: * validate cluster: kops validate cluster --wait 10m * list nodes: kubectl get nodes --show-labels * ssh to a control-plane node: ssh -i ~/.ssh/id_rsa ubuntu@ * the ubuntu user is specific to Ubuntu. If not using Ubuntu please use the appropriate user based on your OS. * read about installing addons at: https://kops.sigs.k8s.io/addons.
kOps 가 kubectl 컨텍스트를 mycluster.k8s.local 로 설정함 사용자 인증 없이 하려면 --admin, --user or --auth-plugin 플래그를 'kops export kubeconfig' 와 함께 쓰면 된다.
- 클러스터 검증: kops validate cluster --wait 10m - 노드 목록: kubectl get nodes --show-labels - SSH to control-plane node: ssh -i ~/.ssh/id_rsa ubuntu@ - 우분투 유저는 우분투 OS 전용이다. 다른 OS 면 적절한 유저 사용하세요. - 애드온 설치에 대해 읽어보세요. https://kops.sigs.k8s.io/addons
잠시 후, 다음 명령어로 노드의 목록과 쿠버네티스 버전을 출력해 설치가 정상적으로 완료됐는지 확인합니다.
kops get all [CLUSTER]
kops get [CLUSTER] 는 deprecated 라고 한다.
kubectl version --short
kops 는 쿠버네티스 클러스터를 AWS 의 DNS 서비스인 Route53 에 등록된 도메인, 서브 도메인과 연동함으로써 핵심 컴포넌트 간에 DNS 로 접근할 수 있는 기능을 제공합니다. 기존에 구입한 도메인이 Route53 의 AWS 네임 서버에 등록돼 있다면 클러스터 이름 환경변수를 다음과 같이 설정한 뒤 설치를 진행함으로써 쿠버네티스 클러스터를 도메인에 등록할 수 있습니다.
export NAME=alicek106.com
kops 로 생성한 쿠버네티스 클러스터를 삭제하려면 다음 명령어를 입력합니다. 단, S3 에 저장된 쿠버네티스 클러스터의 설정 파일은 삭제되지 않습니다.
kops delete cluster $NAME --yes
Trouble shooting
# 1. ELB scheme 확인 (이미 OK)
aws elbv2 describe-load-balancers
Validate 자꾸 실패해서 보니, Master 인스턴스 타입이 t3.micro 라 api server 조차 정상실행되지 않는다.
명령어로 다시 인스턴스 타입 높여서 띄우는 중..
kops edit ig control-plane-ap-northeast-2a --name $NAME # 인스턴스 타입 변경
kops update cluster --yes # 설정 적용
# 인스턴스 강제 재시작
kops rolling-update cluster \
--name mycluster.k8s.local \
--yes \
--cloudonly
어휴.. 왜 아프니 친구야..
이번엔.. 워커 노드가 아픈 것 같다.. 워커 노드도 그냥 올려주자..
CoreDNS 의 replica 가 2 인데, 워커노드가 1개라서 안되는 문제였다. 해결 후 validate 성공
06장. 쿠버네티스 시작하기
6.1 쿠버네티스를 시작하기 전에
모든 리소스는 오브젝트 형태로 관리됩니다.
쿠버네티스에서 사용할 수 있는 오브젝트에는 어떤 것이 있는지 kubectl api-resources 명령어를 사용해 확인해 보겠습니다. 꽤 많은 종류의 오브젝트를 사용할 수 있음을 알 수 있습니다.
kubectl api-resources
쿠버네티스 공식 문서에서 대부분의 리소스 오브젝트의 사용 방법을 친절하게 설명하고 있기 때문에 처음 보는 오브젝트라도 당황하지 말고 필요에 따라 배우고 익히면 됩니다.
특정 오브젝트의 간단한 설명을 보고 싶다면 kubectl explain 명령어를 사용합니다.
kubectl explain pod
쿠버네티스는 명령어로도 사용할 수 있지만, YAML 파일을 더 많이 사용합니다.
쿠버네티스에서 YAML 파일의 용도는 컨테이너뿐만 아니라 거의 모든 리소스 오브젝트들에 사용될 수 있다는 것이 가장 큰 특징입니다. 예를 들어 컨테이너 자체는 물론이고, 컨테이너의 설정값 (ConfigMap), 비밀값 (Secrets) 등도 모두 YAML 파일로 정의해 사용합니다. 그리고 쿠버네티스에서 실제로 서비스를 배포할 때에도 kubectl 명령어가 아닌 여러 개의 YAML 파일을 정의해 쿠버네티스에 적용시키는 방식으로 동작할 것입니다.
쿠버네티스는 여러 개의 컴포넌트로 구성돼 있습니다.
쿠버네티스 노드의 역할은 크게 마스터와 워커로 나뉘어 있습니다. 마스터 노드는 쿠버네티스가 제대로 동작핧 수 있게 클러스터를 관리하는 역할을 담당하며, 워커 노드에는 애플리케이션 컨테이너가 생성됩니다.
쿠버네티스는 도커를 포함한 매우 많은 컴포넌트들이 실행됩니다. 예를 들어, 마스터 노드에서는 API 서버 (kube-apiserver), 컨트롤러 매니저 (kube-controller-manager), 스케줄러 (kube-scheduler), DNS 서버 (coreDNS) 등이 실행되며, 모든 노드에서는 오버레이 네트워크 구성을 위해 프록시 (kube-proxy) 와 네트워크 플러그인 (calico, flannel 등) 이 실행됩니다.
이러한 컴포넌트들은 기본적으로 도커 컨테이너로서 실행되고 있습니다. 마스터 노드에 SSH 로 접속해 docker ps 명령어를 실행해 보면 매우 많은 컨테이너가 실행되고 있을 것입니다.
Docker desktop 에서 show system containers 항목을 체크하면 쿠버네티스의 컴포넌트 컨테이너를 확인할 수 있습니다.
그리고 쿠버네티스 클러스터 구성을 위해 kubelet 이라는 에이전트가 모든 노드에서 실행됩니다. kubelet 은 컨테이너의 생성, 삭제뿐만 아니라 마스터와 워커 노드 간의 통신 역할을 함께 담당하는 매우 중요한 에이전트입니다. 따라서 kubelet 이 정상적으로 실행되지 않으면 해당 노드는 쿠버네티스와 제대로 연결되지 않을 수도 있습니다.
쿠버네티스의 입장에서 보면 도커 데몬 또한 하나의 컴포넌트입니다. 따라서 쿠버네티스에서 반드시 도커를 사용해야 하는 것은 아니며, OCI (Open Container Initiative) 라는 컨테이너의 런타임 표준을 구현한 CRI (Container Runtime Interface) 를 갖추고 있다면 어떠한 컨테이너를 써도 문제는 없습니다.
CRI 는 kubelet 과 통신하기 위한 인터페이스를 의미 도커 컨테이너의 경우 runC 컨테이너 런타임을 제어하는 containerd (컨테이너-디) 는 자체적으로 CRI 플러그인을 내장하고 있으므로 도커 엔진만 설치해도 쿠버네티스와 문제없이 연결해 사용할 수 있습니다. CRI 를 구현하는 다른 오픈소스에는 cri-i 등이 있습니다.
6.2 포드(Pod) : 컨테이너를 다루는 기본 단위
알아야 할 몇가지 오브젝트
Pod
Replica Set
Service
Deployment
6.2.1 포드 사용하기
컨테이너 애플리케이션의 기본 단위를 포드 (Pod) 라고 부르며, 포드는 1개 이상의 컨테이너로 구성된 컨테이너의 집합입니다.
포드의 개념을 좀 더 정확히 이해하기 위해서 Nginx 컨테이너로 구성된 포드를 직접 생성해보겠습니다.
쿠버네티스의 YAML 파일은 일반적으로 apiVersion, kind, metadata, spec 네 가지 항목으로 구성됩니다.
apiVersion: YAML 파일에서 정의한 오브젝트의 API 버전을 나타냅니다. 오브젝트의 종류 및 개발 성숙도에 따라 apiVersion 설정값이 달라질 수 있다는 것만 알고 넘어가겠습니다.
kind: 이 리소스의 종류를 나타냅니다. 위의 YAML 파일에서 생성하려고 하는 것이 포드이기 때문에 Pod 를 입력했습니다. kind 항목에서 사용할 수 있는 리소스 오브젝트 종류는 kubectl api-resources 명령어의 KIND 항목에서 확인할 수 있습니다.
metadata: 라벨, 주석 (Annotation), 이름 등과 같은 리소스의 부가 정보들을 입력합니다. 위 예시에서는 name 항목에서 포드의 고유한 이름을 my-nginx-pod 로 설정했습니다.
spec: 리소스를 생성하기 위한 자세한 정보를 입력합니다. 위 예시에서는 포드에서 실행될 컨테이너 정보를 정의하는 containers 항목을 작성한 뒤, 하위 항목인 image 에서 사용할 도커 이미지를 지정했습니다. name 항목에서는 컨테이너의 이름을, ports 항목에서는 Nginx 컨테이너가 사용할 포트인 80을 입력했습니다.
작성한 YAML 파일은 kubectl apply -f 명령어로 쿠버네티스에 생성할 수 있습니다. 다음 명령어를 사용해 새로운 포드를 생성합니다.
kubectl apply -f nginx-pod.yaml
kubectl get pods
kubectl get <오브젝트 이름> 을 사용하면 특정 오브젝트의 목록을 확인할 수 있습니다.
이 Nginx 포드를 생성할 때, YAML 파일에 사용할 포트 (containerPort) 를 정의하긴 했지만, 아직 외부에서 접근할 수 있도록 노출된 상태는 아닙니다. 따라서 포드를 Nginx 서버로 요청을 보내려면 포드 컨테이너의 내부 IP 로 접근해야 합니다.
kubectl describe 명령어를 사용하면 생성된 리소스의 자세한 정보를 얻어올 수 있습니다.
kubectl describe pods my-nginx-pod
IP 가 있으나, 이 IP 는 외부에서 접근할 수 있는 IP 가 아니다.
쿠버네티스 외부 또는 내부에서 포드에 접근하려면 서비스 (service) 라고 하는 쿠버네티스 오브젝트를 따로 생성해야 하지만, 지금은 서비스 오브젝트 없이 IP 만으로 Nginx 포드에 접근해 보겠습니다.
클러스터의 노드 중 하나에 접속한 뒤 다음과 같이 Nginx 포드의 IP 로 요청을 전송합니다.
EXTERNAL IP 확인
kubectl get nodes -o wide
SSH 접근
ssh -i ~/.ssh/kops_id_rsa ubuntu@<EXTERNAL_IP>
master node 에 접속해 nginx 포드에 접근
테스트용 포드 생성해 임시 사용
kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never bash
그렇다면 이번에는 포드 컨테이너 내부로 직접 들어가 보겠습니다.
kubectl exec -it my-nginx-pod -- bash
책에 있는 내용과 다르게 명령어가 바뀌었다. - before: kubectl exec -it my-nginx-pod bash - after: kubectl exec -it my-nginx-pod -- bash
로그 확인
kubectl logs my-nginx-pod
쿠버네티스의 오브젝트는 kubectl delete -f 명령어로 쉽게 삭제할 수 있습니다.
kubectl delete -f nginx-pod.yaml
kubectl delete pod <POD 이름> # 또는 이 방법을 사용해도 됩니다.
pod 확인
kubectl get pods
삭제되어 아래와 같은 문구가 나온다. No resources found in default namespace.
6.2.2 포드 vs. 도커 컨테이너
포드는 컨테이너 IP 주소를 가지고 있어 쿠버네티스 클러스터 내부에서 접근할 수 있고, kubectl exec 명령어로 포드 컨테이너 내부로 들어갈 수도 있으며, kubectl logs 명령어로 포드의 로그를 확인할 수도 있습니다. 그렇다면 쿠버네티스는 왜 '도커 컨테이너' 가 아니라 굳이 '포드' 라는 새로운 개념을 사용하는 것일까요?
쿠버네티스가 포드를 사용하는 이유는 컨테이너 런타임의 인터페이스 제공 등 여러 가지가 있지만, 그 이유 중 하나는 여러 리눅스 네임스페이스 (namespace) 를 공유하는 여러 컨테이너들을 추상화된 집합으로 사용하기 위해서입니다.
그렇다면 이번에는 Nginx 포드에 새로운 우분투 컨테이너를 추가해 보겠습니다. 이전에 사용한 YAML 파일을 확장해 아래 내용으로 새로운 YAML 파일을 작성합니다. curl 이 미리 설치된 우분투 이미지인 alicek106/rr-test:curl 을 사용했으며, 이 컨테이너는 종료되지 않기 위해 tail -f /dev/null 이라는 단순한 동작만을 실행합니다.
YAML 에서 대시 (-) 를 사용하는 항목은 여러 개의 항목을 정의할 수 있음을 의미합니다.
포드의 YAML 파일에서 사용되는 command 와 args 는 컨테이너 내부에서 가장 먼저 실행될 프로세스를 지정합니다. YAML 파일에서 command 를 설정하면 도커 컨테이너의 Entrypoint로, 포드에서 args 를 설정하면 도커 컨테이너의 Cmd(커맨드)로 치환된다고 이해하면 쉽습니다. tail -f /dev/null 이 실행된다고 보면 됩니다.
READY 가 2/2 로 생성되었다.
kubectl exec, logs 등과 같은 명령어를 사용할 때는 -c 옵션을 이용해 포드의 어떤 컨테이너에 대해 명령어를 수행할지 명시할 수 있습니다.
포드 내의 컨테이너들이 네트워크 네임스페이스 등과 같은 리눅스 네임스페이스를 공유해 사용하기 때문에 접근이 가능합니다.
네트워크 네임스페이스는 컨테이너의 고유한 네트워크 환경을 제공해주는 역할을 담당합니다. 예를 들어 docker run 명령어로 docker0 브리지에 연결된 컨테이너가 생성됐다면, 그 컨테이너는 자기 자신만의 고유한 네트워크 네임스페이스를 가지게 됩니다. 그렇기 때문에 호스트 및 다른 컨테이너와 다른 고유한 IP 를 유지할 수 있는 것입니다.
1개의 포드에 포함된 컨테이너들은 여러 개의 리눅스 네임스페이스를 공유합니다.
지금은 "포드 내부의 컨테이너들은 네트워크와 같은 리눅스 네임스페이스를 공유한다"라는 것만 알고 넘어가면 됩니다.
6.2.3 완전한 애플리케이션으로서의 포드
한가지 유의해야 할 점은 '하나의 포드는 하나의 완전한 애플리케이션' 이라는 점입니다.
Nginx 컨테이넌가 실행되기 위해 부가적인 기능을 필요로 한다면 어떨까요? 예를 들어, Nginx 의 설정 파일의 변경사항을 갱신해주는 설정 리로더(reloader) 프로세스나 로그를 수집해주는 프로세스는 Nginx 컨테이너와 함께 실행돼야 할 수 있습니다. 이런 경우 포드의 주 컨테이너를 Nginx 로 하되, 기능 확장을 위핞 추가 컨테이너를 함께 포드에 포함시킬 수 있습니다. 이렇게 포드에 정의된 부갖거인 컨테이너를 사이드카 (sidecar) 컨테이너라고 부르며, 사이드카 컨테이너는 포드 내에 다른 컨테이너와 네트워크 환경 등을 공유하게 됩니다. 때문에 포드에 포함된 컨테이너들은 모두 같은 워커 노드에서 함께 실행됩니다.
6.3 레플리카셋(Replica Set) : 일정 개수의 포드를 유지하는 컨트롤러
6.3.1 레플리카셋을 사용하는 이유
앞서 생성했던, 2개의 컨테이너가 담겨 있는 Nginx 포드는 다음의 두 가지 방법을 삭제할 수 있습니다.
kubectl delete -f nginx-pod-with-ubuntu.yaml
kubectl delete 명령어로 포드를 삭제하면 그 포드의 컨테이너 또한 삭제된 뒤 쿠버네티스에서 영원히 사라지게 됩니다. 이처럼 YAML 파일에 포드만 정의해 생성할 경우 해당 포드는 오직 쿠버네티스 사용자에 의해 관리됩니다. 단순히 포드의 기능을 테스트하는 등의 간단한 용도로는 이렇게 포드를 사용할 수 있을지도 모릅니다. 그렇다면 실제로 외부 사용자의 요청을 처리해야 하는 마이크로 서비스 구조의 포드라면 이러한 방식을 사용하기 어렵습니다. 스웜 모드에서 다뤘던 것처럼 마이크로서비스에서는 여러 개의 동일한 컨테이너를 생성한 뒤 외부 요청이 각 컨테이너에 적절히 분배될 수 있어야 합니다.
YAML 파일은 ---를 구분자로 사용해 여러 개의 리소스를 정의할 수 있습니다.
포드가 어떠한 이유로든지 삭제되거나, 포드가 위치한 노드에 장애가 발생해 더 이상 포드에 접근하지 못하게 됐을 때, 어려분이 직접 포드를 삭제하고 다시 생성하지 않는 한 해당 포드는 다시 복구되지 않습니다.
아래의 예시에서는 kubectl get pods 에 -o wide 옵션을 붙여 포드가 실행중인 워커 노드를 확인한 뒤, 직접 워커 노드 서버를 종료해 봤습니다. 포드가 생성된 노드에 장애가 발생하더라도 포드는 다른 노드에서 다시 생성되지 않으며, 포드는 단지 종료된 상태로 남아있을 뿐입니다.
kubectl apply -f nginx-pod.yaml
워커 노드 서버를 종료하니, pods 가 노출되지 않는다.
이처럼 포드만 YAML 파일에 정의해 사용하는 방식은 여러 가지 한계점이 있습니다. 따라서 쿠버네티스에서 포드만 정의해 사용하는 경우는 거의 없으며, 이러한 한계점을 해결해주는 레플리카셋 (replicaset) 이라는 쿠버네티스 오브젝트를 함께 사용하는 것이 일반적입니다. 레플리카셋이 수행하는 역할을 간단하게 설명하면 다음과 같습니다.
정해진 수의 동일한 포드가 항상 실행되도록 관리합니다.
노드 장애 등의 이유로 포드를 사용할 수 없다면 다른 노드에서 포드를 다시 생성합니다.
따라서 동일한 Nginx 포드를 안정적으로 여러개 실행할 수도 있고, 워커 노드에 장애가 생기더라도 정해진 개수의 포드를 유지할 수 있습니다. 이처럼 레플리카셋이 여러분 대신 포드를 관리하기 때문에 앞으로 포드를 여러분이 직접 관리할 일은 거의 없을 것입니다.
6.3.2 레플리카셋 사용하기
이번에는 Nginx 포드를 생성하는 레플리카셋을 만들어 보겠습니다. 다음과 같은 내용으로 replicaset-nginx.yaml 파일을 작성합니다.
spec.tempate 아래의 내용들 : 포드를 어떻게 생성할 것인지 명시. 이를 보통 포드 스펙 또는 포드 템플릿이라고 말합니다.
kubectl apply -f replicaset-nginx.yaml
2개의 워커 노드에 한개씩 생성된 것을 볼 수 있다.
이 2개의 포드는 위에서 생성된 레플리카셋에 의해 생성된 것입니다. 따라서 레플리카셋을 삭제하거나 수정하면 포드에 변경사항이 반영됩니다.
kubectl 명령어를 사용할 때, 이름을 줄여 쓸 수 있는 몇 가지 쿠버네티스 오브젝트가 있습니다. 예를 들어 pods 대신 po를, replicasets 대신 rs 를 사용할 수 있습니다. 물론 오브젝트 이름을 줄여서 사용해도 kubectl 은 동일하게 동작합니다. 사용할 수 있는 줄임말의 목록은 kubectl api-resources 명령어의 SHORTNAMES 항목에서 확인할 수 있습니다.
엄~.. 청나게 많다.
이번에는 레플리카셋에 정의된 포드의 개수를 늘려 3개의 포드가 실행되게 해 보겠습니다. 그러나 레플리카셋의 포드 개수를 변경하기 위해서 이미 생성된 레플리카셋을 삭제하고 다시 생성할 필요는 없습니다. 쿠버네티스에서는 이미 생성된 리소스의 속성을 변경하는 제공하기 때문입니다. 이를 위해 jubectl edit, kubectl patch 등 여러 방법을 사용할 수 있지만, 지금은 간단히 YAML 파일에서 숫자만 바꿔 사용해 보겠습니다.
레플리카셋을 삭제하려면 마찬가지로 kubectl delete -f 명령어를 사용하거나, kubectl delete rs 명령어를 사용하면 됩니다. 레플리카셋에 의해 생성된 포드 또한 함께 삭제될 것입니다.
kubectl delete rs replicaset-nginx
6.3.3 레플리카셋의 동작 원리
레플리카셋은 포드와 연결돼 있지 않습니다. 오히려 느슨한 연결 (loosely coupled) 을 유지하고 있으며, 이러한 느슨한 연결은 포드와 레플리카셋의 정의 중 라벨 셀렉터 (Label Slector) 를 이용해 이뤄집니다.
라벨 셀렉터를 이해하기 위해 레플리카셋을 생성했을 때 사용한 YAML 파일 내용의 일부를 다시 살펴보겠습니다.
이전에 포드를 생성할 때, metadata 항목에서는 리소스의 부가적인 정보를 설정할 수 있다고 설명했었습니다. 그 부가 정보 중에는 리소스의 고유한 이름뿐만 아니라 주석(애노테이션 : Annotation), 라벨 등도 포함됩니다. 특히 라벨은 포드 등의 쿠버네티스 리소스를 분류할 때 유용하게 사용할 수 있는 메타데이터입니다.
라벨은 쿠버네티스 리소스의 부가적인 정보를 표현할 수 있을 뿐만 아니라, 서로 다른 오브젝트가 서로를 찾아야 할 때 사용되기도 합니다. 예를 들어 레플리카셋은 spec.selector.matchLabel 에 정의된 라벨을 통해 생성해야 하는 포드를 찾습니다.
레플리카셋과 포드의 라벨은 고유한 키-값 쌍이어야만 합니다. 위의 예시는 이해를 돕기 위한 것일 뿐, 레플리카셋과 동일한 라벨을 가지는 포드를 직접 생성하는 것은 바람직하지 않습니다.
kubectl edit pods replicaset-nginx-2fhd7
kubectl edit 명령어는 포드뿐만 아니라 모든 종류의 쿠버네티스 오브젝트에 사용할 수 있으며, YAML 에서 설정하지 않았던 상세한 리소스의 설정까지 확인할 수 있습니다. 예를 들어 레플리카셋으로부터 생성된 포드는 레플리카셋의 이름을 ownerReferences 라는 항목에 저장하고 있습니다.
kubectl delete rs replicaset-nginx # 레플리카셋 삭제
kubectl get pods # pods 확인
kubectl delete pods replicaset-nginx-2fhd7 # pod 삭제
여기서 오해하지 말아야 할 점은 레플리카셋의 목적이 '포드를 생성하는 것'이 아닌, '일정 개수의 포드를 유지하는 것' 이라는 점입니다.
6.3.4 레플리케이션 컨트롤러 vs. 레플리카셋
이전 버전의 쿠버네티스에서는 레플리카셋이 아닌 레플리케이션 컨트롤러라는 오브젝트를 통해 포드의 개수를 유지했었습니다.
레플리카셋이 레플리케이션 컨트롤러와 다른 점 중 하나는 "표현식 (matchExpression)" 기반의 라벨 셀렉터를 사용할 수 있다는 것입니다. 예를 들어 레플리카셋의 YAML 파일에서 selector 항목은 다음과 같이 표현식으로 정의할 수도 있습니다.
위 예시는 키가 app인 라벨을 가지고 있는 포드들 중에서 values 항목에 정의된 값들이 존재 (In) 하는 포드들을 대상으로 하겠다는 의미입니다.
macheExpression 은 matchLabels 와 함께 쓰일 수 있으며, operator 에는 In 외에도 Notin, Exists 등을 사용할 수 있습니다.
6.4 디플로이먼트(Deployment) : 레플리카셋, 포드의 배포를 관리
6.4.1 디플로이먼트 사용하기
레플리카셋만 사용해도 충분히 마이크로서비스 구조의 컨테이너를 구성할 수 있을 것 같지만, 실제 쿠버네티스 운영 환경에서 레플리카셋을 YAML 파일에서 사용하는 경우는 거의 없습니다. 대부분은 레플리카셋과 포드의 정보를 정의하는 디플로이먼트 (Deployment)라는 이름의 오브젝트를 YAML 파일에 정의해 사용하며, 디플로이먼트는 앞으로 여러분이 포드와 함께 가장 많이 보게 될 오브젝트이기도 합니다.
디플로이먼트는 레플리카셋의 상위 오브젝트이기 때문에 디프로이먼트를 생성하면 해당 디플로이먼트에 대응하는 레플리카셋도 함께 생성됩니다. 따라서 디플로이먼트를 사용하면 포드와 레플리카셋을 직접 설정핧 필요가 없습니다. 간단한 예시를 통해 디플로이먼트를 직접 생성해 보겠습니다.
생성된 디플로이먼트의 목록은 포드나 레플리카셋에서 목록을 출력했던 것과 동일하게 kubectl get deployment 명령어로 출력할 수 있습니다.
kubectl get deployment
kubectl get replicasets
kubectl get pods
디플로이먼트를 삭제하면 레플리카셋과 포드 또한 함께 삭제됩니다.
kubectl delete deploy my-nginx-deployment
6.4.2 디플로이먼트를 사용하는 이유
디플로이먼트를 사용하는 핵심적인 이유 중 하나는 애플리케이션의 업데이트와 배포를 더욱 편하게 만들기 위해서입니다. 애플리케이션을 업데이트할 때 레플리카셋의 변경 사항을 저장핳는 리비전(revision) 을 남겨 롤백을 가능하게 해주고, 무중단 서비스를 위해 포드의 롤링 업데이트의 전략을 지정할 수도 있습니다.
좀 더 쉬운 이해를 위해 디플로이먼트를 이용해 애플리케이션의 버전을 업데이트해 배포하는 간단한 예시를 알아보겠습니다. 이전과 동일한 YAML 파일인 deployment-nginx.yaml 파일로 디플로이먼트를 생성하되, 이번에는 --record 라고 하는 족므 특수한 옵션을 추가합니다.
kubectl apply -f deployment-nginx.yaml --record
record flag 는 곧 삭제될 것이라고 한다.
디플로이먼트에서 생성된 포드의 이미지를 변경할 때는 kubectl set image 명령어를 사용할 수 있습니다.
nginx=nginx:1.11
포드 템플릿에 정의된 containers 항목에서 ngnx 라는 이름을 가지는 컨테이너의 이미지를 nginx:1.11 으로 변경합니다.
kubectl set image deployment my-nginx-deployment nginx=nginx:1.11 --record
kubectl set image 명령어 대신 YAML 파일에서 직접 image 항목을 nginx:1.11 으로 변경한 다음 kubectl apply -f 명령어로 적용해도 동일하게 변경됩니다. 또는 이전에 사용해 보았던 kubectl edit 명령어를 사용해도 됩니다.
새로운 nginx 버전으로 pod 한개씩 옮겨가는 것을 볼 수 있다.
디플로이먼트는 포드의 정보를 업데이트함으로써 새로운 레플리카셋과 포드를 생성했음에도 불구하고 이전 버전의 레플리카셋을 삭제하지 않고 남겨두고 있습니다. 즉, 디플로이먼트는 포드의 정보가 변경되어 업데이트가 발생했을 때, 이전의 정보를 리비전으로서 보존합니다. 이러한 리비전 정보는 다음 명령어로 더욱 자세히 확인할 수 있습니다.
kubectl rollout history deployment my-nginx-deployment
--record=true 옵션으로 디플로이먼트를 변경하면 변경 사항을 위와 같이 디플로이먼트에 기록함으로써 해당 버전의 레플리카셋을 보존합니다.
--record 옵션을 사용하지 않아도 이전의 레플리카셋은 보존되지만, 어떤 명령어를 통해 변경됐는지 기록하는 CHANGE-CAUSE 항목에 <NONE> 으로 표시됩니다. 따라서 가능하다면 --record 를 사용하는 것이 좋습니다.
만약 이전 버전의 레플리카셋으로 되돌리는 롤백을 하고 싶다면 다음 명령어를 사용할 수 있습니다. --to-revision 에는 되돌리려는 리비전의 번호를 입력하면 됩니다.
쿠버네티스 리소스의 자세한 정보를 출력하느 kubectl describe 명령어를 사용해 디플로이먼트의 정보를 출력해 보면 현재의 레플리카셋 리비전 정보와 활성화된 레플리카셋 이름을 확인할 수 있습니다.
kubectl describe deploy my-nginx-deployment
디플로이먼트를 사용하면 이러한 레플리카셋의 리비전 관리뿐만 아니라 다양한 포드의 롤링 업데이트 정책을 사용할 수도 있다는 장점이 있습니다. 따라서 쿠버네티스에서도 공식적으로 디플로이먼트를 사용할 것을 권장하고 있습니다.
디플로이먼트의 기능을 이용한 롤링 업데이트 전략은 11.3절에서 다시 설명할 것입니다.
리소스 정리
kubectl delete deployment,pod,rs --all
6.5 서비스(Service) : 포드를 연결하고 외부에 노출
이전 예제에서 kubectl describe 명령어로 포드의 내부 IP 를 직접 확인한 뒤 포드로 직접 접근할 수는 있었지만, 이 방법은 로컬 개발 환경 또는 쿠버네티스 클러스터 내부에서만 사용할 수 있었습니다.
게다가 도커 컨테이너와 마찬가지로 포드의 IP 는 영속적이지 않아 항상 변할 수 있다는 점도 유의해야 합니다. 여러 개의 디플로이먼트를 하나의 완벽한 애플리케이션으로 연동하려면 포드 IP가 아닌, 서로를 발견(Discovery) 할 수 있는 다른 방법이 필요합니다.
도커 컨테이너 접근
- (publish) 옵션
오버레이 네트워크
도커 사용자 정의 네트워크
docker run --link
docker run -itd --name myapp2 --link myapp:nginx ubuntu:16.04
docker network create mynetwork
docker run mycontainer --network mynetwork --net-alias mycon ubuntu:16.04
이 포트를 외부로 노출해 사용자들이 접근하거나, 다른 디플로이먼트의 포드들이 내부적으로 접근하려면 서비스(service) 라고 부르는 별도의 쿠버네티스 오브젝트를 생성해야 합니다. 서비스는 포드에 접근하기 위한 규칙을 정의하기 때문에 쿠버네티스에서 애플리케이션을 배포하기 위해서는 반드시 알아야 할 오브젝트입니다.
여러 개의 포드에 쉽게 접근할 수 있도록 고유한 도메인 이름을 부여합니다.
여러 개의 포드에 접근할 때, 요청을 분산하는 로드 밸런서 기능을 수행합니다.
클라우드 플랫폼의 로드 밸런서, 클러스터 노드의 포트 등을 통해 포드를 외부로 노출합니다.
쿠버네티스를 설치할 때 기본적으로 calico, flannel 등의 네트워크 플러그인을 사용하도록 설정되기 때문에 자동으로 오버레이 네트워크를 통해 각 포드끼리 통신할 수 있습니다. 단, 어떠한 네트워크 플러그인을 사용하느냐에 따라서 네트워크 기능 및 성능에 차이가 있을 수 있습니다.
6.5.1 서비스(Service)의 종류
아래의 YAML 파일을 이용해 먼저 디플로이먼트를 생성합니다. 이번에는 컨테이너(포드)의 호스트 이름을 반환하는 간단한 웹 서버 이미지를 사용합니다.
ClusterIP 타입: 쿠버네티스 내부에서만 포드들에 접근할 때 사용합니다. 외부로 포드를 노출하지 않기 때문에 쿠버네티스 클러스터 내부에서만 사용되는 포드에 적합합니다.
NodePort 타입: 포드에 접근할 수 있는 포트를 클러스터의 모든 노드에 동일하게 개방합니다. 따라서 외부에서 포드에 접근할 수 있는 서비스 타입입니다. 접근할 수 있는 포트는 랜덤으로 정해지지만, 특정 포트로 접근하도록 설정할 수도 있습니다.
LoadBalancer 타입: 클라우드 플랫폼에서 제공하는 로드 밸런서를 동적으로 프로비저닝해 포드에 연결합니다. NodePort 타입과 마찬가지로 외부에서 포드에 접근할 수 있는 서비스 타입입니다. 그렇지만 일반적으로 AWS, GCP 등과 같은 클라우드 플랫폼 환경에서만 사용할 수 있습니다.
spec.selector : selector 항목은 이 서비스에서 어떠한 라벨을 가지는 포드에 접근할 수 있게 만들 것인지 결정합니다.
레플리카셋이나 서비스의 selector 처럼 두 리소스 간의 라벨이 동일할 때에만 쿠버네티스의 기능을 온전히 사용할 수 있는 경우가 앞으로 자주 등장할 것입니다.
spec.ports.port : 생성된 서비스는 쿠버네티스 내부에서만 사용할 수 있는 고유한 IP (ClusterIP) 를 할당받습니다. port 항목에는 서비스의 IP 에 접근할 때 사용할 포트를 설정합니다. 이에 대해서는 뒤에서 다시 설명합니다.
spec.ports.targetPort : selector 항목에서 정의한 레벨에 의해 접근 대상이 된 포드들이 내부적으로 사용하고 있는 포트를 입력합니다. deployment-hostname.yaml 파일의 containerPort 항목에서 포드가 사용할 포트를 80으로 선언했기 때문에 위의 ports.targetPort 항목을 80으로 설정했습니다. 즉, 포드 템플릿에 정의된 containerPort 와 같은 값으로 설정해야 합니다.
spec.type : 이 서비스가 어떤 타입인지 나타냅니다. 서비스의 종류에는 ClusterIP, NodePort, LoadBalancer 등을 설정할 수 있습니다.
kubectl apply -f hostname-svc-clusterip.yaml
생성한 적이 없는데도 kubernetes 라는 이름의 서비스가 미리 생성돼 있습니다. 이 서비스는 포드 내부에서 쿠버네티스의 API 에 접근하기 위한 서비스로, 지금 당장 자세하게 알 필요는 없습니다. 10장에서 다시 다룹니다.
kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never -- bash
로드밸런싱 되는 것을 볼 수 있다.
ClusterIP 타입의 서비스를 생성해 포드에 접근하는 과정을 다시 정리해 보겠습니다.
특정 라벨을 가지는 포드를 서비스와 연결하기 위해 서비스의 YAML 파일에 selector 항목을 정의합니다.
포드에 접근할 때 사용하는 포트(포드에 설정된 containerPort) 를 YAML 파일의 targetPort 항목에 정의합니다.
서비스를 생성할 때, YAML 파일의 port 항목에 8080을 명시해 서비스의 Cluster IP 와 8080 포트로 접근할 수 있게 설정합니다.
kubectl apply -f 명령어로 ClusterIP 타입의 서비스가 생성되면 서비스는 쿠버네티스 클러스터 내부에서만 사용할 수 있는 고유한 내부 IP 를 할당받습니다.
쿠버네티스 클러스터에서 서비스의 내부 IP 또는 서비스 이름으로 포드에 접근할 수 있습니다.
단, 위에서 생성한 서비스는 ClusterIP 타입이기 때문에 외부에서는 접근할 수 없다는 점에 유의해야 합니다.
서비스의 라벨 셀렉터(selector)와 포드의 라벨이 매칭돼 연결되면 쿠버네티스는 자동으로 엔드포인트 (endpoint)라고 부르는 오브젝트를 별도로 생성합니다. 서비스와 동일한 이름으로 존재하고 있습니다.
kubectl apply -f hostname-svc-nodeport.yaml
kubectl get services
kubectl get nodes -o wide
curl {IP}:{PORT} --silent | grep Hello
단 AWS 에서 Security Group 에 별도의 Inbound 규칙을 추가하지 않으면 NodePort 로 통신이 실패할 수 있습니다.
Security group 에 해당 port 를 open 해주니 pod 로 요청이 가는 것을 볼 수 있다.
각 노드에서 개방되는 포트는 기본적으로 30000~32768 포트 중에 랜덤으로 선택되지만, YAML 파일에 nodePort 항목을 정의하면 원하는 포트를 선택할 수도 있습니다.
기본적으로 NodePort 가 사용할 수 있는 포트 범위는 30000~32768이지만, API 서버 컴포넌트의 실행 옵션을 변경하면 원하는 포트 범위를 설정할 수 있습니다. 포트 범위를 직접 지정하려면 API 서버의 옵션을 다음과 같이 추가하거나 수정합니다. --service-node-port-range=30000-35000 너무 낮은 포트 번호는 시스템에 의해 예약된 포트일 수 있기 때문에 가능하면 기본적으로 설정된 30000번 이상의 포트를 사용하는 것이 좋습니다.
NodePort 서비스 그 자체를 통해 서비스를 외부에 제공하기보다는 인그레스(Ingress)라고 부르는 쿠버네티스의 오브젝트에서 간접적으로 사용되는 경우가 많습니다.
특정 클라이언트가 같은 포드로부터만 처리되게 하려면 서비스의 YAML 파일에서 sessionAffinity 항목을 ClientIP 로 설정합니다.
6.5.4 클라우드 플랫폼의 로드 밸런서와 연동하기 - LoadBalancer 타입의 서비스
NodePort 를 사용할 때는 각 노드의 IP를 알아야만 포드에 접근할 수 있었지만, 이번에 사용해 볼 LoadBalancer 타입의 서비스는 클라우드 플랫폼으로부터 도메인 이름과 IP를 할당받기 때문에 NodePort보다 더욱 쉽게 포드에 접근할 수 있습니다.
여기서 눈여겨봐야 할 것은 EXTERNAL-IP 항목입니다. 이 주소는 클라우드 플랫폼인 AWS 로부터 자동으로 할당된 것이며, 이 주소와 80 포트 (YAML 파일의 ports.port) 를 통해 포드에 접근할 수 있습니다.
로드밸런서가 pod 의 30338 PORT 로 연결되는 것을 볼 수 있다.
LoadBallcer 타입을 명시해 서비스를 생성했지만, NodePort 의 간접적인 기능 또한 자동으로 사용할 수 있는 셈입니다.
AWS 로드 밸런서 관리 페이지에서 유형을 확인해보면 'classic' 으로 되어 있는 것을 볼 수 있습니다. 쿠버네티스 1.18.0 버전을 기준으로 서비스의 YAML 파일에서 아무런 설정을 하지 않으면 AWS 의 클래식 로드 밸런서를 생성합니다. 원한다면 NLB (네트워크 로드 밸런서) 를 생성할 수도 있습니다.
쿠버네티스에서 주석 (annotations) 은 라벨처럼 해당 리소스의 추가적인 정보를 나타내기 위한 키-값 쌍으로 이뤄져 있습니다. 그렇지만 리소스의 종류에 따라 특정 용도로 사용할 수 있게 쿠버네티스에 미리 정의된 몇 가지 주석이 있습니다. 이 주석들은 리소스에 특별한 설정값을 부여하기 위해 사용됩니다. 미리 정의된 특별한 주석은 앞으로 가끔씩 등장할 것이기 때문에 라벨과 함께 익숙해지는 것이 좋습니다.
로드 밸런서의 사용을 마쳤다면 서비스를 삭제합니다.
kubectl delete -f hostname-svc-lb.yaml
생성한 로드밸런서가 삭제되었다.
온프레미스 환경에서 LoadBalancer 타입의 서비스 사용하기
필요하다면 여러분이 직접 보유하고 있는 온프레미스 서버에서도 LoadBalancer 타입을 사용할 수 있습니다. 단, 쿠버네티스가 이 기능을 직접 제공하는 것은 아니며, MetalLB 나 오픈스택과 같은 특수한 환경을 직접 구축해야만 합니다.
6.5.5 트래픽의 분배를 결정하는 서비스 속성 : external TrafficPolicy
LoadBalancer 타입의 서비스를 사용하면 외부로부터 들어온 요청은 각 노드 중 하나로 보내지며, 그 노드에서 다시 포드 중 하나로 전달됩니다. 마찬가지로 NodePort 타입을 사용했을 때도 각 노드로 들어오는 요청은 다시 포드 중 하나로 전달됩니다.
노드간의 리다이렉트가 발생하면 트래픽의 출발지 주소가 바뀌는 SNAT 가 발생하게 되고, 이로 인해 클라이언트의 IP 주소 또한 보존되지 않는다는 단점이 있습니다.
이러한 요청 전달 매커니즘은 서비스의 속성 중 externalTrafficPolicy 항목에 정의돼 있습니다. kubectl get -o yaml 명령어로 서비스의 모든 속성을 출력해 보면 externalTrafficPolicy 가 Cluster 로 설정된 것을 알 수 있습니다.
kubectl get svc hostname-svc-nodeport -o yaml
kubectl get 에서 -o 옵션을 이용해 리소스의 정보를 yaml, json 등의 형식으로 출력할 수 있습니다. -o 옵션으로 출력하면 쿠버네티스에 의해 자동으로 설정된 상세 항목들까지 모두 확인할 수 있습니다.
externalTrafficPolicy 를 Local 로 설정하면 포드가 생성된 노드에서만 포드로 접근할 수 있으며, 로컬 노드에 위치한 포드 중 하나로 요청이 전달됩니다. 즉, 추가적인 네트워크 홉이 발생하지 않으며, 전달되는 요청의 클라이언트 IP 또한 보존됩니다.
서비스를 생성할 때 externalTrafficPolicy 를 Local 로 설정했기 때문에 i-0d360505efc9a06ca 노드에서만 이 포드에 접근할 수 있을 것입니다. 개방된 포트 번호를 kubectl get services 명령어로 확인한 다음, 각 노드로 요청을 보내 보면 이를 알 수 있습니다.
pod 의 포트는 31127 이다.
서비스는 기본적으로 externalTrafficPolicy 속성이 Cluster 로 설정됩니다. 단, 노드 간에 요청이 리다이렉트 되어 NAT 가 발생하므로 클라이언트의 IP 를 보존할 수 없습니다.
Local 로 설정하면 포드가 위치한 노드만 랜덤한 포드를 개방합니다. 로드밸런서는 포드가 위치한 노드로만 요청을 전달하며, 해당 노드 내의 포드에서만 요청이 분산됩니다.
Local 로 설정하는 것이 무조건 좋은 것은 아닙니다. 각 노드에 포드가 고르지 않게 스케줄링됐을 때, 요청이 고르게 분산되지 않을 수도 있기 때문입니다.
11.2절에서 다뤄볼 쿠버네티스 스케줄링 기능 중 PodAntiAffinity 등을 사용하면 포드를 최대한 클러스터 노드에 고르게 분배할 수 있으며, External TrafficPolicy: Local 의 단점을 어느 정도 해결할 수 있습니다.
불필요한 네트워크 홉으로 인한 레이턴시나 클라이언트의 IP. 보존이 중요하지 않다면 Cluster 를 사용해도 됩니다.
6.5.6 요청을 외부로 리다이렉트하는 서비스 : ExternalName
여러분이 앞으로 서비스를 사용할 때는 앞서 언급한 ClusterIP, NodePort, LoadBalancer 세 가지만 알아도 충분하지만, 쿠버네티스를 외부 시스템과 연동해야 할 때는 ExternalName 타입의 서비스를 사용할 수도 있습니다.
ExternalName 타입을 사용해 서비스를 생성하면 서비스가 외부 도메인을 가리키도록 설정할 수 있습니다. 예를 들어 아래의 설정은 쿠버네티스 내부의 포드들이 externalname-svc 라는 이름으로 요청을 보낼 경우, 쿠버네티스의 DNS는 my.database.com 으로 접근할 수 있도록 CNAME 레코드를 반환합니다. ExtenrnalName 타입의 서비스는 쿠버네티스와 별개로 존재하는 레거시 시스템에 연동해야 하는 상황에서 유용하게 사용할 수 있습니다.
이번 장에서는 효율적으로 애플리케이션을 관리하기 위해 자주 사용되는 네임스페이스 (Namespace), 컨피그맵 (ConfigMap), 시크릿 (Secret) 의 사용 방법을 알아보겠습니다.
7.1 네임스페이스(Namespace) : 리소스를 논리적으로 구분하는 장벽
간단히 생각해 네임스페이스는 포드, 레플리카셋, 디플로이먼트, 서비스 등과 같은 쿠버네티스 리소스들이 묶여 있는 하나의 가상 공간 또는 그룹이라고 이해하면 됩니다.
예를 들어 모니터링을 위한 모든 리소스들은 monitoring 이라는 이름의 네임스페이스에서 생성할 수 있고, 테스트를 위한 리소스들은 testbed 라는 네임스페이스에서 생성할 수 있습니다. 또는 여러 개발 조직이 하나의 쿠버네티스 클러스터를 공유해 사용해야 한다면 조직별로 네임스페이스를 사용하도록 구성할 수도 있습니다. 이처럼 여러 개의 네임스페이스를 사용하면 마치 하나의 클러스터에서 여러 개의 가상 클러스터를 동시에 사용하는 것 처럼 느겨질 것입니다.
네임스페이스 기본 개념 이해
kubectl get namespaces
kubectl get ns
여러분이 네임스페이스를 생성하지 않았더라도 기본적으로 3개의 네임스페이스가 존재합니다. 각 네임스페이스에는 포드, 레플리카셋, 서비스와 같은 리소스가 따로 존재합니다.
kubectl get pods --namespace default
이번에는 kube-system 이라는 네임스페이스의 포드를 확인해보겠습니다.
kubectl get pods -n kube-system
여러분이 생성한 적 없는 포드가 여러 개 실행되고 있습니다.
kube-system 네임스페이스는 쿠버네티스에 대한 충분한 이해 없이는 건드리지 않는 것이 좋습니다.
kube-system 네임스페이스에는 쿠버네티스의 포드, 서비스 등을 이름으로 찾을 수 있도록 하는 DNS 서버의 서비스가 미리 생성돼 있습니다.
kubectl get service -n kube-sytem
난 아무것도 안나오네..
앞으로 여러분이 네임스페이스를 사용하는 경우는 대부분 모니터링, 로드 밸런싱 인그레스(Ingress) 등의 특정 목적을 위한 용도가 대부분일 것입니다.
각 네임스페이스의 리소스들은 논리적으로만 구분된 것일 뿐, 물리적으로 격리된 것이 아니라는 점을 알아둬야 합니다. 예를 들어 서로 다른
네임스페이스에서 생성된 포드가 같은 노드에 존재할 수도 있습니다.
kubectl get pods -l app=webserver
네임스페이스는 라벨보다 더욱 넓은 용도로 사용할 수 있습니다. 예를 들어 ResourceQuota 라는 오브젝트를 이용해 특정 네임스페이스에서 생성되는 포드의 자원 사용량을 제한하거나, 애드미션 컨트롤러라는 기능을 이용해 특정 네임스페이스에 생성되는 포드에서 항상 사이드카 컨테이너를 붙이도록 설정할 수 있습니다. (11.1절에서 다시 설명합니다)
네임스페이스 사용하기
네임스페이스는 지금까지 사용했던 방식처럼 YAML 파일에 정의해 사용할 수 있습니다.
production-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
kubectl get 명령어에 --all-namespaces 옵션을 사용하면 모든 네임스페이스의 리소스를 확인할 수도 있습니다.
production 이라는 namespace 에 pod 하나가 보인다.
네임스페이스의 서비스에 접근하기
쿠버네티스 클러스터 내부에서는 서비스 이름을 통해 포드에 접근할 수 있다고 설명했습니다. 이는 정확히 말하자면 '같은 네임스페이스 내의 서비스' 에 접근할 때에만 서비스 이름만으로 접근할 수 있다는 뜻입니다.
그렇지만 다른 네임스페이스에 존재하는 서비스에는 서비스 이름만으로 접근할 수 없습니다.
하지만 <서비스 이름>.<네임스페이스 이름>.svc 처럼 서비스 이름 뒤에 니음스페이스 이름을 붙이면 다른 네임스페이스의 서비스에도 접근할 수 있습니다. 예를 들어 production 네임스페이스에 있는 hostname-svc-clusterip-ns 라는 이름의 서비스에는 다음과 같이 접근할 수 있습니다.
서비스의 DNS 이름에 대한 FQND (Fully Qualifed Domain Name) 은 일반적으로 다음과 같은 형식으로 구성돼 있습니다. <서비스 이름>.<네임스페이스 이름>.svc.cluster.local
네임스페이스를 삭제하면 네임스페이스에 존재하는 모든 리소스 또한 함께 삭제되기 때문에 네임스페이스를 삭제하기 전에 다시 한번 리소스 목록을 확인해 보는 것이 좋습니다.
kubectl delete namespace production
네임스페이스에 종속되는 쿠버네티스 오브젝트와 독립적인 오브젝트
모든 리소스가 네임스페이스에 의해 구분되는 것은 아닙니다.
포드의 경우 오브젝트가 네임스페이스에 속한다 (namespaced) 라고 표현합니다. 네임스페이스에 속하는 오브젝트의 종류는 다음 명령어로 확인할 수 있습니다.
kubectl api-resources --namespaced=true
노드 또한 쿠버네티스의 오브젝트 중 하나이지만, 네임스페이스에 속하지 않는 대표적인 오브젝트 중 하나이기도 합니다.
노드가 있다.
클러스터의 관리를 위한 저수준의 오브젝트들은 네임스페이스에 속하지 않을 수도 있다는 것만 알아두면 됩니다.
기본적으로 사용하도록 설정되어 있는 네임스페이스는 default 이지만, 이는 쿠버네티스의 설정을 담고 있는 kubeconfig 라는 파일을 수정함으로써 변경할 수 있습니다. kubeconfig 는 10.5 절에서 다시 다룰것이기 때문에 지금 당장은 설명하지 않지만, 필요하다면 kubens 라는 오픈 소스 스크립트를 이용해 kubectl 의 기본 네임스페이스를 편리하게 변경할 수 있습니다. (github.com/ahmetb/kubectx, https://github.com/ahmetb/kubectx)
kubens kube-system
7.2 컨피그맵(Configmap),. 시크릿(Secret) : 설정값을 포드에 전달
포드를 정의하는 YAML 파일에 환경변수를 직접 적어 놓는 하드 코딩 방식을 사용할 수도 있습니다.
상황에 따라서는 환경변수의 값만 다른, 동일한 여러 개의 YAML 이 존재할 수도 있습니다. 만약 운영 환경과 개발 환경에서 각각 디플로이먼트를 생성해야 한다면 환경 변수가 서로 다르게 설정된 두 가지 버전의 YAML 파일이 따로 존재해야 하기 때문입니다.
7.2.1 컨피그맵 (Configmap)
컨피그맵 사용 방법 익히기
네임스페이스별로 컨피그맵이 존재합니다.
YAML 파일을 사용해 컨피그맵을 생성해도 되지만, kubectl create configmap 명령어를 사용하면 쉽게 컨피그맵을 생성할 수 있습니다. 예를 들어 다음 ㅁ여령어는 --from-literal 이라는 옵션을 이용해 LOG_LEVEL 키의 값이 DEBUG 인 컨피그맵을 생성하며, 컨피그맵의 이름은 log-level-configmap 이 됩니다.
컨피그맵에 저장된 설정값은 kubectl describe configmap 명령어나 kubectl get configmap -o yaml 명령어로 확인할 수 있습니다.
kubectl get cm
kubectl describe configmap log-level-configmap
생성된 컨피그맵을 포드에서 사용하려면 디플로이먼트 등의 YAML 파일에서 포드 템플릿 항목에 컨피그맵을 사용하도록 정의하면 됩니다.
컨피그맵을 포드에서 사용하는 방법은 크게 두 가지가 있습니다. 여러분의 애플리케이션이 소스코드 내부에서 어떻게 설정값을 읽는지에 따라 적절한 방법을 선택해야 합니다.
컨피그맵의 값을 컨테이너의 환경 변수로 사용
셸에서 echo $LOG_LEVEL 과 같은 방식으로도 값을 확인할 수 있습니다.
컨피그맵의 값을 포드 내부의 파일로 마운트해 사용
컨피그맵을 /etc/config/log_level 이라는 파일로 마운트하면 log_level 파일에는 INFO 라는 값이 저장됩니다. 이때 파일이 위치할 경로는 별도로 설정할 수 있습니다. 여러분의 애플리케이션이 nginx.conf 등의 파일을 통해 설정값을 읽어 들인다면 이 방법을 사용하는 것이 좋습니다.
실제 운영 환경에서는 대부분의 경우 디플로이먼트를 사용하지만, 앞으로 사용해 볼 쿠버네티스의 기능들은 대부분 포드를 정의하는 YAML 파일을 기준으로 설명할 것입니다. 포드를 사용하는 쿠버네티스 오브젝트에 적용해 사용할 수 있다는 것을 의미합니다. 아직 다루지는 않았지만, 스테이트풀셋, 데몬셋 등의 쿠버네티스 오브젝트 또한 포드를 기본 단위로 사용하기 때문에 포드의 YAML 설정을 똑같이 사용할 수 있습니다. 포드를 사용하는 다른 오브젝트의 사용방법은 13장에서 자세히 설명합니다.
위의 YAML 파일 내용 중 중요한 부분은 envFrom 과 configMapRef 항목입니다.
포드를 생성한 뒤 포드 내부에서 환경 변수의 목록을 출력해 보겠습니다. env 는 환경 변수를 출력하는 명령어입니다.
kubectl apply -f all-env-from-configmap.yaml
kubectl exec container-env-example -- env
pod 에 주입된 환경변수가 보인다.
쿠버네티스가 자동으로 서비스에 대한 접근 정보를 컨테이너의 환경 변수로 설정합니다. KUBERNETES_ 로 시작하는 환경 변수는 default 네임스페이스에 존재하는 kubernetes 라는 서비스에 대한 것이며, 여러분이 이전에 생성해 뒀던 서비스 리소스가 있다면 해당 서비스의 클러스터 내부 IP 및 포트 또한 환경 변수에 등록돼 있을 것입니다.
envFrom.configMapRef vs valueFrom.configMapKeyRef
그렇다면 이번에는 다른 방법으로 포드를 생성해 보겠습니다.
valueFrom.configMapKeyRef 를 사용하면 여러 개의 키-값 쌍이 들어 있는 컨피그맵에서 특정 데이터만을 선택해 환경 변수로 가져올 수도 있습니다.
selective-env-from-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: container-selective-env-example
spec:
containers:
- name: my-container
image: busybox
args: ['tail', '-f', '/dev/null']
env:
- name: ENV_KEYNAME_1 # (1.1) 컨테이너에 새롭게 등록될 환경 변수 이름
valueFrom:
configMapKeyRef:
name: log-level-configmap
key: LOG_LEVEL
- name: ENV_KEYNAME_2 # (1.2) 컨테이너에 새롭게 등록될 환경 변수 이름
valueFrom:
configMapKeyRef:
name: start-k8s # (2) 참조할 컨피그맵의 이름
key: k8s # (3) 가져올 데이터 값의 키
# 최종 결과 -> ENV_KEYNAME_2=$(k8s 키에 해당하는 값)
# ENV_KEYNAME_2=kubernetes
시크릿을 포드의 환경 변수나 볼륨 파일로서 가져오면 base64 로 디코딩된 원래의 값을 사용하게 됩니다.
이미지 레지스트리 접근을 위한 docker-registry 타입의 시크릿 사용하기
시크릿은 컨피그맵처럼 단순 문자열이나 설정 파일 등을 저장할 때 사용할 수도 있지만, 사용 목적에 따라 여러 종류의 시크릿을 사용할 수도 있습니다. kubectl get secrets 명령어로 시크릿의 목록을 확인했을 때 볼 수 있었던 Type 항목이 시크릿의 종류에 해당합니다.
여러분이 생성해봤던 시크릿은 모두 Opaque (불투명한) 로 설정돼 있습니다. 별도 시크릿의 종류를 명시하지 않으면 자동으로 설정되는 타입으로, 사용자가 정의하는 데이터를 저장할 수 있는 일반적인 목적의 시크릿입니다. 시크릿을 생성할 때 generic 이라고 명시했던 것이 바로 Opaque 타입에 해당하는 종류입니다.
시크릿은 사용 용도에 따라 여러 종류를 설정할 수 있으며, 그중 하나가 지금 다뤄 볼 비공개 레지스트리 (Private Registry) 에 접근할 때 사용하는 인증 설정 시크릿입니다.
여러분의 사설 레지스트리 또는 도커 허브, GCR(구글), ECR(AWS) 등의 클라우드 레지스트리를 사용하고 있다면 로그인 등과 같은 인증 절차가 필요합니다.
이전의 2.3.4 절에서 배웠던 사설 레지스트리에 접근하는 방법을 기억하고 있을 것입니다. 단일 서버에서 도커 데몬을 사용할 때는 다음과 같이 docker login 명령어를 사용할 수 있었습니다.
쿠버네티스에서는 docker login 명령어 대신 레지스트리의 인증 정보를 저장하는 별도의 시크릿을 생성해 사용합니다. 레지스트리 인증을 위해 시크릿을 생성하는 방법은 두 가지가 있습니다. 첫 번째는 docker login 명령어로 로그인에 성공했을 때 도커 엔진이 자동으로 생성하는 ~/.docker/config.json 파일을 사용하는 것입니다. (기본적으로 리눅스 사용자의 홈 디렉토리에 위치) config.json 파일에는 인증을 위한 정보가 담겨 있기 때문에 이를 그대로 시크릿으로 가져오면 됩니다.
--docker-server 는 필수 옵션이 아니며, 필요에 따라 사용하면 됩니다. --docker-server 옵션을 사용하지 않으면 기본적으로 도커 허브 (docker.io) 를 사용하도록 설정되지만, 다른 사설 레지스트리를 사용하려면 --docker-server 옵션에 해당 서버의 주소 또는 도메인 이름을 입력하면 됩니다.
위 명령어로 생성된 시크릿은 kubenetes.io/dockerconfigjson 이라는 타입으로 설정됩니다.
이 시크릿은 디플로이먼트 또는 포드 등에서 사설 레지스트리로부터 이미지를 받아올 때 사용할 수 있습니다. 예를 들어 도커 허브의 프라이빗 저장소 (Private Repository) 에 저장된 이미지를 통해 포드를 생성하려면 다음과 같이 YAML 파일에서 imagePullSecret 항목을 정의합니다.
기본적으로 YAML 파일에 명시된 도커 이미지가 워커 서버에 존재하지 않을 때만 이미지를 받아오도록 설정돼 있지만, imagePullPolicy 항목을 통해 이미지를 받아오는 설정을 변경할 수 있습니다. imagePullPolicy 의 자세한 사용 방법은 쿠버네티스 공식 문서를 참고할 수 있습니다.
TLS 키를 저장할 수 있는 tls 타입의 시크릿 사용하기
시크릿은 TLS 연결에 사용되는 공개키, 비밀키 등을 쿠버네티스 자체적으로 저장할 수 있도록 tls 타입을 지원합니다. 따라서 포드 내부의 애플리케이션이 보안 연결을 위해 인증서나 비밀키 등을 가져와야 할 때 시크릿의 값을 포드에 제공하는 방식으로 사용할 수 있습니다.
tls 타입의 시크릿을 사용하는 방법은 매우 간단합니다. 보안 연결에 사용되는 키 페어가 미리 준비돼 있다면 kubectl create secret tls 명령어로 쉽게 생성할 수 있습니다. 이전에 사용했던 명령어와 조금 다른 점은 시크릿의 종류로 generic 이나 docker-registry eotls tls 를 사용하고, --cert 와 --key 옵션을 사용해 인증서와 키를 직접 명시해 준다는 것입니다.
생성된 시크릿의 정보를 확인해보면 cert.crt 와 cert.key 파일의 내용이 tls.crt 와 tls.key 라는 키로 저장돼 있음을 알 수 있습니다. 각 데이터는 모두 base64 로 인코딩되어 저장됩니다.
좀 더 쉽게 컨피그맵과 시크릿 리소스 배포하기
kubectl create secret 명령어를 사용해도 되지만, 이를 YAML 파일로 배포하려면 시크릿의 데이터를 YAML 파일에 함께 저장해 둬야 합니다. 예를 들어 tls 타입의 시크릿을 인증서와 함께 배포하려면 kubectl create 의 --dry-run 명령어에서 출력되는 YAML 파일 내용을 저장해 사용할 수 있습니다.
그러나 시크릿의 데이터가 많아질수록 YAML 파일에 직접 시크릿의 데이터를 저장하는 것은 바람직한 방법이 아닙니다. 위 명령어에서도 tls.crt 의 데이터가 매우 길어 가독성이 좋지 않을 뿐만 아니라, YAML 파일과 데이터가 분리되지 않아 데이터를 관리하기에도 불편하기 때문입니다.
이러한 단점을 해결하면서 시크릿이나 컨피그맵을 배포하기 위해 YAML 파일을 작성할 때, 데이터를 YAML 파일로부터 분리할 수 있는 kustomize 기능을 사용할 수 있습니다. kustomize 는 kubectl 명령어 1.14 버전부터 사용할 수 있는 기능으로, 자주 사용되는 YAML 파일의 속성을 별도로 정의해 재사용하거나 여러 YAML 파일을 하나로 묶는 등 다양한 용도로 사용할 수 있는 기능입니다.
secretGenerator: # 시크릿을 생성하기 위한 지시문
- name: kustomize-secret
type: kubernetes.io/tls # tls 타입의 시크릿을 생성
files:
- tls.crt=cert.crt # tls.crt 라는 키에 cert.crt 파일의 내용을 저장
- tls.key=cert.key # tls.key 라는 키에 cert.key 파일의 내용을 저장
지금까지 봐왔던 YMAL 파일과 완전히 다른 형식이지만, 사실 위 내용은 시크릿을 생성하기 위해 사용했던 방법들과 크게 다르지 않습니다. 시크릿을 생성하기 전에 kustomize 로부터 생성될 시크릿의 정보를 미리 확인하려면 kubectl kustomize 명령어를 사용합니다. 이는 kubectl create 명령어에서 --dry-run 옵션을 사용했던 것과 비슷합니다.
kubectl kustomize ./
앞서 작성한 kustomization.yaml 파일로부터 시크릿을 생성하려면 해당 파일이 위치한 디렉터리에서 kubectl apply -k ./ 명령어를 사용합니다. 지금까지 YAML 파일을 사용해왔던 것과 동일하게 kustomization.yaml 파일로부터 쿠버네티스 리소스를 생성하거나 삭제할 수 있습니다.
kubectl apply -k ./
리소스가 잘 생성되었다.
kubectl delete -k ./
만약 컨피그맵을 kustomize 로부터 생성하고 싶다면 kustomization.yaml 파일에서 secretGenerator 대신 configmapGenerator 를 사용하면 됩니다. 컨피그맵은 종류가 존재하지 않으므로 type 항목을 정의하지 않습니다.
kustomize 로부터 생성된 컨피그맵이나 시크릿의 이름 뒤에는 컨피그맵, 시크릿에 저장된 데이터로부터 추출된 해시값이 자동으로 추가됩니다. kubectl 명령어를 사용할 때도 --append-hash 옵션을 이용해 리소스의 이름 뒤에 해시값을 추가할 수 있습니다. --append-hash 를 사용하면 데이터를 시크릿이나 컨피그맵의 이름에 명확히 나타낼 수 있다는 장점이 있기 때문에 설정값을 업데이트할 설정값을 업데이트할 때 유용하게 사용할 수 있습니다.
컨피그맵이나 시크릿을 업데이트해 애플리케이션의 설정값 변경하기
설정값을 변경해야 한다면 값을 kubectl edit 명령어로 수정해도 되고, YAML 파일을 변경한 뒤 다시 kubectl apply 명령어를 사용해도 됩니다. 지금까지 사용해 본 적은 없지만 Kubectl patch 라는 명령어도 사용할 수 있습니다.
cat my-configmap.yaml
kubectl apply -f my-configmap.yaml
sed -i -e 's/myvalue/yourvalue/g' my-configmap.yaml # 컨피그맵의 데이터를 변경
kubectl apply -f my-configmap.yaml
지금까지 컨피그맵이나 시크릿을 포드에 제공하는 방법으로 크게 두 가지 방법을 설명했습니다. 첫 번째는 환경 변수로 포드 내부에 설정값을 제공하는 방법이고, 두 번째는 볼륨 파일로 포드 내부에 마운트하는 방법이었습니다. 첫 번째 방법으로 설정된 값은 컨피그맵이나 시크릿 값을 변경해도 자동으로 재설정되지 않으며, 디플로이먼트 포드를 다시 생성해야만 합니다. 그러나 파일로 포드 내부에 마운트된 설정파일은 컨피그맵이나 시크릿을 변경하면 파일의 내용 또한 자동으로 갱신됩니다.
변경된 파일을 다시 읽어 들이도록 컨테이너의 프로세스에 별도의 시그널(SIGHUP) 을 보내는 사이드카 컨테이너를 포드에 포함시킬 수도 있습니다. 또는 애플리케이션의 소스코드 레벨에서 쿠버네티스의 API를 통해 컨피그맵, 시크릿의 데이터 변경에 대한 알림(Watch) 을 받은 뒤, 자동으로 리로드하는 로직을 생각해 볼 수도 있습니다. 이러한 방법에 뚜렷한 정답은 없지만, 이는 여러분이 쿠버네티스 애플리케이션을 설계할 때 반드시 알아둬야 할 부분 중 하나임을 잊지 말아야 합니다.
단, 서비스 어카운트와 연결된 시크릿은 삭제해도 쿠버네티스에 의해 자동으로 다시 복구됩니다. 서비스 어카운트와 시크릿의 관계에 대해서는 10장에서 자세히 설명합니다.
08장. 인그레스 (Ingress)
앞으로는 쿠버네티스를 좀 더 알차게 활용하기 위해 인그레스(Ingress), 영구 볼륨(Persistent Volume: PV), 권한 관리(Role Based Access Control : RBAC), 자원 사용량 제한 방법, 스케줄링, 포드의 라이프사이클, 애플리케이션 업데이트 방법 등에 대해서 알아봅니다.
인그레스(Ingress) 는 일반적으로 외부에서 내부로 향하는 것을 지칭하는 단어입니다. 예를 들어 인그레스 트래픽은 외부에서 서버로 유입되는 트래픽을 의미하며, 인그레스 네트워크는 인그레스 트래픽을 처리하기 위한 네트워크를 의미합니다.
이전에 사용해 봤던 서비스 오브젝트가 외부 요청을 받아들이기 위한 것이었다면 '인그레스'는 외부 요청을 어떻게 처리할 것인지 네트워크 7계층 레벨에서 정의하는 쿠버네티스 오브젝트입니다. 인그레스 오브젝트가 담당할 수 있는 기본적인 기능만 간단히 나열해보면 다음과 같습니다.
외부 요청의 라우팅 : /apple, /apple/red 등과 같이 특정 경로로 들어온 요청을 어떠한 서비스로 전달할지 정의하는 라우팅 규ㅜ칙을 설정할 수 있습니다.
가상 호스트 기반의 요청 처리 : 같은 IP 에 대해 다른 도메인 이름으로 요청이 도착했을 때, 어떻게 처리할 것인지 정의할 수 있습니다.
SSL/TLS 보안 연결 처리 : 여러 개의 서비스로 요청을 라우팅할 때, 보안 연결을 위한 인증서를 쉽게 적용할 수 있습니다.
인그레스 자체의 기능은 비교적 정해져 있더라도 인그레스의 요청을 처리할 서버로 무엇을 선택하느냐에 따라 기능이 조금씩 달라집니다.
8.1 인그레스를 사용하는 이유
쿠버네티스가 제공하는 인그레스 오브젝트를 사용하면 URL 엔드포인트를 단 하나만 생성함으로써 이러한 번거로움을 쉽게 해결할 수 있습니다.
중요한 점은 라우팅 정의나 보안 연결 등과 같은 세부 설정은 서비스와 디플로이먼트가 아닌 인그레스에 의해 수행된다는 것입니다.
host : 해당 도메인 이름으로 접근하는 요청에 대해서 처리 규칙을 적용합니다. 위 예시에서는 alicek106.example.com 이라는 도메인으로 접근하는 요청만 처리하지만, 여러 개의 host 를 정의해 사용할 수도 있습니다.
path : 해당 경로로 들어온 요청을 어느 서비스로 전달할 것인지 정의합니다. 위 예시에서는 /echo-hostname 이라는 경로의 요청을 backend 에 정의된 서비스로 전달합니다. 여러 개의 path 를 정의해 경로를 처리할 수도 있습니다.
serviceName, servicePort : path 로 들어온 요청이 전달될 서비스와 포트입니다. 즉, 위 예시에서는 /echo-hostname 이라는 경로로 들어온 요청을 hostname-service 서비스의 80 포트로 전달합니다.
인그레스를 정의하는 YAML 파일 중에서 annotation 항목을 통해 인그레스의 추가적인 기능을 사용할 수 있으며, 위의 YAML 파일에서도 두 가지의 주석을 사용했습니다. 이 기능에 대해서는 뒤에서 다시 설명할 것이기 때문에 지금은 인그레스 자체의 기능을 먼저 살펴보겠습니다.
ingress-example.yaml 파일로 인그레스를 생성한 다음, 인그레스의 목록을 확인해 보겠습니다.
kubectl apply -f ingress-example-k8s-latest.yaml
kubectl get ingress
ingress-example 이라는 이름의 인그레스를 생성했지만, 이것만으로는 아무 일도 일어나지 않습니다. 외부 요청을 받아들일 수 있는 실제 서버가 아닙니다. 인그레스는 인그레스 컨트롤러(Ingress Controller) 라고 하는 특수한 서버에 적용해야만 그 규칙을 사용할 수 있습니다. 즉, 실제로 외부 요청을 받아들이는 것은 인그레스 컨트롤러 서버이며, 이 서버가 인그레스 규칙을 로드해 사용합니다.
따라서 쿠버네티스의 인그레스는 반드시 인그레스 컨트롤러라는 서버와 함께 사용해야 됩니다. 여러 종류가 있으며, 필요에 따라 하나를 골라서 사용하면 됩니다. 대표적으로는 쿠버네티스 커뮤니티에서 활발히 사용되고 있는 Nginx 웹 서버 인그레스 컨트롤러가 있습니다. 그 외에도 Kong 이라는 API 게이트웨이나 GKE 등의 클라우드 플랫폼에서 제공되는 인그레스 컨트롤러가 있지만, 이번 장에서는 쉽고 간단하게 사용해 볼 수 있는 Nginx 인그레스 컨트롤러를 사용해 보겠습니다.
Nginx 인그레스 컨트롤러는 쿠버네티스에서 공식적으로 개발되고 있기 때문에 설치를 위한 YAML 파일을 공식 깃허브 저장소에서 내려받을 수 있습니다.
namespace, serviceaccount, rbac, configmap, service, deployment, job 등이 생성되었다.
Nginx 인그레스 컨트롤러의 깃허브 주소: https://github.com/kubernetes/ingress-nginx 설치를 위한 공식 문서: https://kubernetes.github.io/ingress-nginx/deploy/
명렁어의 출력 결과에서 알 수 있듯이 Nginx 인그레스 컨트롤러를 설치하기 위해 다양한 쿠버네티스 리소스를 한 번에 생성합니다. 우선 ingress-nginx 라는 네임스페이스에 Nginx 웹 서버 디플로이먼트를 생성하고, 그와 관련된 설정들을 컨피그맵으로 생성하는 것을 알 수 있습니다.
시간이 어느 정도 지난 뒤 ingress-nginx 네임스페이스의 디플로이먼트와 포드를 확인해보면 Nginx 웹 서버가 생성돼 있을 것입니다.
kubectl get pods,deployment -n ingress-nginx
외부에서 Nginx 인그레스 컨트롤러에 접근하기 위한 서비스도 생성됐을 것입니다.
kubectl get svc -n ingress-nginx
Nginx 인그레스 컨트롤러를 설치하면 자동으로 생성되는 서비스는 LoadBalancer 타입이며, 위 예시는 AWS 에서 생성된 예시입니다. 실제 운영 환경이라면 LoadBalancer 타입에 DNS 이름을 할당함으로써 Nginx 인그레스 컨트롤러에 접근하는 것이 일반적이지만 일단 지금은 자동으로 부여된 DNS 이름 (a20..2.elb.amazonaws.com) 을 사용해 보겠습니다.
하지만 가상 머신처럼 클라우드가 아닌 환경에서 인그레스를 테스트하고 싶다면 LoadBalancer 대신 NodePort 타입의 서비스를 생성해 사용해도 됩니다. 이 경우에는 각 노드의 랜덤한 포트로 Nginx 인그레스 컨트롤러에 접근할 수 있습니다.
혹은 온프레미스에서의 운영 단계를 계획하고 있다면 MetalLB (https://metallb.io/) 나 오픈스택의 로드 밸런서를 사용할 수도 있습니다.
이로써 인그레스, Nginx 인그레스 컨트롤러 및 Nginx 포드에 접근하기 위한 서비스의 준비가 완료됐습니다. 하지만 아직 인그레스의 종착점이 될 테스트용 디플로이먼트와 서비스를 생성하지 않았으므로(echo-hostname) 이를 생성해 최종적으로 인그레스 동작 여부를 확인해보겠습니다. Nginx 인그레스 컨트롤러로 들어오는 요청은 이 디플로이먼트들의 포드들로 분산될 것입니다.
kubectl apply -f hostname-deployment.yaml
전에 테스트한 데몬셋을 삭제를 안했네..
kubectl apply -f hostname-service.yaml
인그레스 컨트롤러에 의해 요청이 최종적으로 도착할 디플로이먼트의 서비스는 어떤 타입이든지 상관이 없습니다. 다만 굳이 외부에 노출할 필요가 없다면 ClusterIP 타입을 사용하는 것이 좋습니다.
Nginx 인그레스 컨트롤러의 /echo-hostname 으로 요청을 전송해 보겠습니다. AWS 를 사용하고 있다면 DNS 이름으로, GKE 라면 IP 이름으로 접근합니다.
ingress 오브젝트의 spec.rules.host 값을 AWS 로드밸런서 DNS 로 변경하니 잘 동작한다.
지금은 인그레스의 기능을 하나씩 사용해보기 위해 YAML 파일에 host, path 항목 등을 모두 명시했지만, 이는 반드시 정의해야 하는 것은 아닙니다. 인그레스를 최소한의 설정만으로 생성하려면 아래와 같이 서비스 이름만을 YAML 파일에 명시할 수도 있습니다. 예를 들어 아래의 YAML 파일은 Nginx 에 접근하는 도메인 이름(host 항목) 에 상관없이 hostname-service 서비스로 요청을 전달합니다.
Nginx 인그레스 컨트롤러로 들어온 요청은 인그레스 규칙에 따라 적절한 서비스로 전달됩니다.
3번에서 인그레스를 생성하면 인그레스 컨트롤러는 자동으로 인그레스를 로드해 Nginx 웹 서버에 적용합니다. 이를 위해 Nginx 인그레스 컨트롤러는 항상 인그레스 리소스의 상태를 지켜 보고 있으며, 기본적으로 모든 네임스페이스의 인그레스 리소스를 읽어와 규칙을 적용합니다.
쿠버네티스의 API 에는 특정 오브젝트의 상태가 변화하는 것을 확인할 수 있는 Watch 라는 API 가 있으며, 인그레스 컨트롤러 또한 인그레스 리소스에 의해 Watch API 를 사용합니다. Watch 는 리소스에 생성, 삭제, 수정 등의 이벤트가 발생했을 때 이를 알려주는 기능으로, kubectl get 명령어에서도 -w 옵션을 붙여 사용할 수 있습니다.
kubectl get pods -w
요청은 hostname-service 라는 서비스로 전달되는 것은 아니며, 서비스에 의해 생성된 엔드포인트로 요청을 직접 전달합니다. 즉 서비스의 ClusterIP 가 아닌 엔드포인트의 실제 종착 지점들로 요청이 전달되는 셈입니다. 이러한 동작을 쿠버네티스에서는 바이패스(bypass) 라고 부릅니다.
8.3 인그레스의 세부 기능 : annotation 을 이용한 설정
인그레스는 YAML 파일의 주석 항목을 정의함으로써 다양한 옵션을 사용할 수 있습니다. 이전에 사용해봤던 두 개의 주석 항목을 다시 살펴보겠습니다.
kubernetes.io/ingress.class 는 해당 인그레스 규칙을 어떤 인그레스 컨트롤러에 적용할 것인지를 의미합니다. GKE 에서 쿠버네티스를 사용하고 있다면 GKE에서 인그레스를 생성할 때 만약 annotation 항목에서 kubernetes.io/ingress.class 를 사용하지 않으면 GKE 의 인그레스 컨트롤러를 자동으로 생성해 사용하게 됩니다.
이번에는 nginx.ingress.kubernetes.io/rewrite-target 이라는 주석에 대해서 알아보겠습니다.
이 주석은 Nginx 인그레스 컨트롤러에서만 사용할 수 있는 기능입니다. 이 주석은 인그레스에 정의된 경로로 들어오는 요청을 rewrite-target 에 설정된 경로로 전달합니다. 예를 들어, Nginx 인그레스 컨트롤러로 /echo-hostname 으로 접근하면 hostname-service 에는 / 경로로 전달됩니다.
사실 rewrite-target 은 Nginx 의 캡처 그룹 (Captured groups) 과 함께 사용할 때 유용한 기능입니다. 캡처 그룹이란 정규 표현식의 형태로 요청 경로 등의 값을 변수로서 사용할 수 있는 방법입니다.
ingress-rewrite-target-k8s-latest.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 # path의 (.*) 에서 획득한 경로로 전달합니다.
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: <여러분이 Nginx 컨트롤러에 접근하기 위한 도메인 이름을 입력합니다>
#- host: a2cbfefcfbcfd48f8b4c15039fbb6d0a-1976179327.ap-northeast-2.elb.amazonaws.com
http:
paths:
- path: /echo-hostname(/|$)(.*) # (.*) 을 통해 경로를 얻습니다.
pathType: Prefix
backend:
service:
name: hostname-service
port:
number: 80
rewrite-target 과 path 를 조금 수정했지만, 아주 어려운 설정은 아닙니다. path 항목에서 (.*) 이라는 Nginx 의 정규 표현식을 통해 /echo-hostname/ 뒤에 오는 경로를 얻은 뒤, 이 값을 rewrite-target 에서 사용한 것뿐입니다. 즉, /echo-hostname/ 으로 접근하면 이전과 동일하게 / 로 전달되지만, /echo-hostname/color 는 /color 로, /echo-hostname/color/red 는 /color/red 로 전달됩니다.
그 외에도 루트 경로(/) 로 접근했을 때 특정 path 로 리다이렉트하는 app-root 주석이나 SSL 리다이렉트를 위한 ssl-redirect 주석 등을 사용할 수 있습니다. Nginx 인그레스 컨트롤러에서 사용할 수 있는 주석은 공식 사이트에서 확인할 수 있으며, 여러분의 필요에 따라 적절히 선택해 사용하면 됩니다.
주석을 사용해도 별도의 기능을 사용할 수 있지만, 필요하다면 Nginx 인그레스 컨트롤러와 함께 생성된 컨피그맵을 수정해 직접 Nginx 의 옵션을 설정할 수도 있습니다. 사용 가능한 옵션들은 Nginx 인그레스 컨트롤러의 깃허브에서 확인할 수 있습니다.
8.4 Nginx 인그레스 컨트롤러에 SSL/TLS 보안 연결 적용
인그레스의 장점 중 하나는 쿠버네티스의 뒤쪽에 있는 디플로이먼트와 서비스가 아닌, 앞쪽에 있는 인그레스 컨트롤러에서 편리하게 SSL/TLS 보안 연결을 설정할 수 있다는 것입니다. 즉, 인그레스 컨트롤러 지점에서 인증서를 적용해두면 요청이 전달되는 애플리케이션에 대해 모두 인증서 처리를 할 수 있습니다. 따라서 인그레스 컨트롤러가 보안 연결을 수립하기 위한 일종의 관문 (Gateway) 역할을 한다고도 볼 수 있습니다.
AWS 와 같은 클라우드 환경에서 LoadBalancer 타입의 서비스를 사용할 계획이라면 클라우드 플랫폼 자체에서 관리해주는 인증서를 인그레스 컨트롤러에 적용할 수도 있습니다. 예를 들어 AWS 의 ACM(AWS Certificate Manager) 을 LoadBalancer 타입의 서비스에 제공하는 방법을 생각해 볼 수 있습니다. 그렇지만 이번 장에서는 직접 서명한 루트 인증서를 통해 Nginx 인그레스 컨트롤러에 적용하는 기초적인 방법을 알아봅니다.
/CN 에는 여러분이 Nginx 인그레스 컨트롤러에 접근하기 위한 Public DNS 이름을 입력해야 합니다. 위 예시에서는 Nginx 인그레스 컨트롤러와 연결된 서비스에 alicek106.example.com 이라는 도메인으로 접근한다고 가정한 것이며, 인증서 생성 옵션은 여러분의 환경에 맞게 적절히 변경해 사용해야 합니다.
# curl 의 -k 옵션은 신뢰할 수 없는 인증서로 보안 연결을 하기 위함입니다.
curl https://adb50d7c6b9604b5dbbfad33b2b9f726-caed224370e6d3ec.elb.ap-northeast-2.amazonaws.com -k
https 로 접근했을 때에도 정상적으로 데이터를 반환하는 것을 확인할 수 있습니다. 단, 인증서를 통해 보안 연결을 설정했을 때는 http 로 접근해도 자동으로 https 로 리다이렉트됩니다. 이는 특정 인그레스에 SSL/TLS 가 적용됐을 때, Nginx 인그레스 컨트롤러가 https 로 리다이렉트하는 주석 (annotation) 기능인 ssl-redirect 를 자동으로 true 로 설정하기 때문입니다.
8.5 여러 개의 인그레스 컨트롤러 사용하기
하나의 쿠버네티스 클러스터에서 반드시 하나의 인그레스 컨트롤러를 사용해야 하는 것은 아닙니다. Nginx 인그레스 컨트롤러는 기본적으로 nginx 라는 이름의 클래스를 가지고 있으며, 이 설정을 변경함으로써 여러 개의 Nginx 인그레스 컨트롤러를 사용할 수도 있고, 인그레스 규칙을 선택적으로 적용할 수도 있습니다.
이번에는 YAML 파일을 내려받은 뒤 직접 옵션을 아래와 같이 수정해 보겠습니다. --ingress-class 라는 옵션을 추가했고, alicek106-nginx 라는 값을 설정했습니다.
이 외에도 Nginx 에 적용할 주석 접두어를 설정하는 옵션이나(--annotations-prefix, 기본값은 nginx.ingress.kubernestes.io), Nginx 의 설정을 저장하는 컨피그맵을 지정하는 옵션(--configmap, 기본값은 ingress-nginx 네임스페이스의 nginx-configuration) 등을 수정해 여러분이 원하는 구성을 완성할 수 있습니다. (https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/) 간단히 기능을 테스트하는 용도라면 기본 옵션 설정으로도 충분하지만, 실제 운영 환경을 고려하고 있다면 여러 옵션의 커스터마이징이 필요함을 알아두기 바랍니다.
리소스 정리
kubectl delete -f ./
09장. 퍼시스턴트 볼륨 (PV) 과 퍼시스턴트 볼륨 클레임 (PVC)
지금까지 사용해봤던 간단한 테스트용 디플로이먼트는 모두 상태가 없는 (stateless) 애플리케이션이었습니다. 데이터베이스처럼 포드 내부에서 특정 데이터를 보유해야 하는, 상태가 있는 (stateful) 애플리케이션의 경우에는 데이터를 어떻게 관리할지 고민해야 합니다.
네트워크로 연결해 사용할 수 있는 퍼시스턴트 볼륨의 대표적인 예로는 NFS, AWS 의 EBS (Elastic Block Store), Ceph, GlusterFS 등이 있습니다.
9.1 로컬 볼륨 : hostPath, emptyDir
볼륨을 간단히 사용해 볼 수 있는 hostPath 와 emptyDir 두 가지 종류의 볼륨을 먼저 알아봅니다. hostPath 는 호스트와 볼륨을 공유하기 위해서 사용하고, emptyDir 은 포드의 컨테이너 간에 볼륨을 공유하기 위해서 사용합니다. 비록 이 두가지는 자주 사용되는 볼륨 종류는 아니지만, YAML 파일에서 볼륨을 정의하고 사용하는 것에 익숙해진다는 생각으로 먼저 사용해 보겠습니다.
9.1.1 워커 노드의 로컬 디렉터리를 볼륨으로 사용 : hostPath
포드의 데이터를 보존할 수 있는 가장 간단한 방법은 호스트의 디렉터리를 포드와 공유해 데이터를 저장하는 것 입니다.
kubectl get pods -o wide # 워커노드 확인
kubectl debug nodes/i-0d360505efc9a06ca -it --image=busybox # 노드 들어가기
워커노드에 잘 생성된 것을 볼 수 있다.
그렇지만 이러한 방식의 데이터 보존은 바람직하지 않습니다. hostPath 방식의 볼륨을 반드시 사용해야 한다면 11.2절에서 설명하는 스케줄링을 이용해 특정 노드에만 포드를 배치하는 방법도 생각해 볼 수 있지만, 이 방법 또한 호스트 서버에 장애가 생기면 데이터를 잃게 된다는 단점이 있습니다.
그렇지만 hostPath 볼륨은 모든 노드에 배치해야 하는 특수한 포드의 경우에 유용하게 사용할 수 있습니다. 이전에 2.5.4.3 절에서 사용해 봤던 모니터링 툴인 CAdvisor 는 호스트의 디렉터리와 도커 소켓 (/var/run/docker.sock) 등을 컨테이너 내부로 공유해 모니터링 데이터를 수집했습니다.
9.1.2 포드 내의 컨테이너 간 임시 데이터 공유 : emptyDir
emptyDir 볼륨은 포드의 데이터를 영속적으로 보존하기 위해 외부 볼륨을 사용하는 것이 아닌, 포드가 실행되는 도중에만 필요한 휘발성 데이터를 각 컨테이너가 함께 사용할 수 있도록 임시 저장 공간을 생성합니다. 비어있는 상태로 생성되며 포드가 삭제되면 emptyDir 에 저장돼 있떤 데이터도 함께 삭제됩니다.
400 page
9.1.
9.1.
9.1.
9.
9.
9.
10장. 보안을 위한 인증과 인가 : ServiceAccount 와 RBAC
11장. 애플리케이션 배포를 위한 고급 설정
12장. 커스텀 리소스와 컨트롤러
13장. 포드를 사용하는 다른 오브젝트들
13.1
13.2 데몬셋(DaemonSets)
데몬셋(DaemonSets) 은 쿠버네티스의 모든 노드에 동일한 포드를 하나씩 생성하는 오브젝트입니다. 데몬셋은 로깅, 모니터링, 네트워킹 등을 위한 에이전트를 각 노드에 생성해야 할 때 유용하게 사용할 수 있습니다.
kubectl get daemonsets -n kube-system
daemonset-example.yaml
apiVersion: apps/v1
kind: DaemonSet # [1]
metadata:
name: daemonset-example
spec:
selector:
matchLabels:
name: my-daemonset-example # [2.1] 포드를 생성하기 위한 셀렉터 설정
template:
metadata: # [2.2] 포드 라벨 설정
labels:
name: my-daemonset-example
spec:
tolerations: # [3] 마스터 노드에도 포드를 생성
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: daemonset-example
image: busybox # 테스트를 위해 busybox 이미지 사용
args: ["tail", "-f", "/dev/null"]
resources: # [4] 자원 할당량을 제한
limits:
cpu: 100m
memory: 200Mi
마스터노드에도 데몬셋을 생성하기 위한 설정
tolerations: # [3] 마스터 노드에도 포드를 생성
- key: node-role.kubernetes.io/master
effect: NoSchedule
Guaranteed 클래스 설정이 바람직하다. 필수는 아님.
kubectl apply -f daemonset-example.yaml
kubectl get daemonsets
kubectl get ds
만약 특정 노드에만 데몬셋의 포드를 생성하고 싶다면 11.2 절에서 사용해봤던 노드 셀렉터 (nodeSelector) 나 Node Affinity 를 포드에 적용할 수도 있습니다.