쿠버네티스(kubernetes) 설치 및 환경 구성하기

How to configure a Kubernetes cluster

ShinChul Bang
finda 기술 블로그
44 min readJan 8, 2020

--

이번 블로그에서는 쿠버네티스 설치부터 기본적인 환경 구성까지 핸즈온 한다.

쿠버네티스는 기본적으로 마스터 노드와 워커 노드로 구성된다.

마스터 노드와 워커 노드는 최소한 1개씩 필요하다.

마스터 노드는 워커 노드에 Pod를 할당하고 Pod 안에 컨테이너를 띄우게 하는 역할을 한다. 또한 쿠버네티스의 상태를 관리하고 여러 Pod 들의 스케줄링도 하는 등 쿠버네티스에서 중추적인 역할을 한다.

워커 노드는 마스터 노드와 통신하면서 Pod를 할당 받고 그 안에 컨테이너를 띄워 유지 및 관리하는 역할을 한다. 또한 네트워크나 볼륨에 대한 기능도 컨트롤한다.

마스터 노드와 워커 노드는 각 1개씩 생성할 것이며, 운영체제는 Ubuntu 18.04 LTS 를 사용하여 구성하도록 하겠다.

구성하려는 각 노드 별 하드웨어 스펙은 아래와 같다.

마스터 노드

  • CPU : 2 core
  • RAM : 3 GB
  • Storage : 30GB

워커 노드

  • CPU : 2 core
  • RAM : 2 GB
  • Storage : 30 GB

How to Setup

Kubernetes의 환경을 구성하는 방법에는 다양한 방법이 있지만 여기에서는 kubedam이라는 도구를 사용하여 구성하겠다.

kubeadm이란, kubernetes에서 제공하는 기본적인 도구이며, kubernetes 클러스터를 가장 빨리 구축하기 위한 다양한 기능을 제공한다.

kubeadm은 다양한 커맨드라인 명령들을 사용하여 클러스터를 구축하는데 대략적인 내용은 아래와 같다.

kubeadm init

  • Kubernetes 컨트롤 플레인 노드를 초기화한다.
  • 즉, 마스터 노드를 초기화한다.

kubeadm join

  • Kubernetes 워커 노드를 초기화하고 클러스터에 연결한다.

kubeadm upgrade

  • Kubernetes 클러스터를 업그레이드 한다.

kubeadm config

kubeadm token

kubeadm reset

  • kubeadm init 혹은 kubeadm join의 변경사항을 최대한 복구한다.

kubeadm version

  • kubeadm 버젼은 보여준다.

kubeadm alpha

  • 정식으로 배포된 기능은 아니지만 kubernetes측에서 사용자 피드백을 얻기 위해 인증서 갱신, 인증서 만료 확인, 사용자 생성, kubelet 설정 등 다양한 기능을 제공하고 있다.

Check List

Kubernetes를 설치하기에 앞서 아래의 사항을 점검한다.

# 필요 운영체제 :

  • Ubuntu 16.04+
  • Debian 9+
  • CentOS 7
  • Red Hat Enterprise Linux 7
  • Fedora 25+
  • HypriotOS v1.0.1+
  • 컨테이너 리눅스 (1800.6.0에서 테스트)

# 컴퓨터 당 2GB 이상의 RAM (이보다 적으면 앱을 위한 공간이 없음)

# CPU 2개 이상

# 클러스터의 모든 시스템 간 완벽한 네트워크 연결

# 모든 노드에 대한 고유한 호스트 이름, 고유한 MAC 주소, 고유한 product_uuid

  • MAC 주소 확인 방법 : ifconfig -a 혹은 ip link
  • product_uuid 확인 방법 : sudo cat /sys/class/dmi/id/product_uuid

# 스왑 메모리 비활성화(중요!)

  • Pod를 할당하고 제어하는 kubelet은 스왑 상황을 처리하도록 설계되지 않았음.
  • 이유는 kubernetes에서 가장 기본이 되는 Pod의 컨셉 자체가 필요한 리소스 만큼만 호스트 자원에서 할당 받아 사용한다는 구조이기 때문이다.
  • 따라서 kubernetes 개발팀은 메모리 스왑을 고려하지 않고 설계했기 때문에 클러스터 노드로 사용할 서버 머신들은 모두 스왑 메모리를 비활성화 해줘야 한다.
  • 스왑 메모리를 비활성화 하기 위해 아래 명령어를 사용한다.
  • swapoff -ased -i '2s/^/#/' /etc/fstab

# iptables 툴이 nftables 백엔드를 사용하지 않아야 함.

  • nftables 백엔드는 현재 kubeadm 패키지와 호환되지 않는다.
  • nftables 백엔드를 사용하면 방화벽 규칙이 중복되어 kube-proxy가 중단된다.

# 마스터 노드에서는 6443, 2379~2380, 10250, 10251, 10252 포트가 사용되고 있지 않아야 한다.

  • 마스터 노드에서 필요한 필수 포트
  • 6443 포트 : Kubernetes API Server / Used By All
  • 2379~2380 포트 : etcd server client API / Used By kube-apiserver, etcd
  • 10250 포트 : Kubelet API / Used By Self, Control plane
  • 10251 포트 : kube-scheduler / Used By Self
  • 10252 포트 : kube-controller-manager / Used By Self

# 워커 노드에서는 10250, 30000~32767 포트가 사용되고 있지 않아야 한다.

# Kubernetes는 v1.6.0 부터는 기본적으로 CRI(Container Runtime Interface)를 사용하도록 해서 상관 없지만 그 하위 버전에서는 컨테이너 런타임이 설치 되어 있어야한다. (CRI-O, Containerd, Docker 등)

# Kubernetes v1.14.0 부터 kubeadm은 잘 알려진 도메인 소켓 목록을 스캔하여 Linux 노드에서 컨테이너 런타임을 자동으로 감지하려고 시도한다. 사용 가능하고 감지가 가능한 컨테이너 런타임 및 소켓 경로는 아래와 같다.

# Docker와 Containerd가 모두 감지되면 Docker가 우선한다.

# 이외에 다른 두 개 이상의 컨테이너 런타임이 감지되면 kubeadm은 적절한 오류 메시지를 출력하고 종료된다.

Before Kubernetes Setup

kubernetes를 설치하기 이전에 작업 해야할 목록이 있다.

그 내용은 아래와 같다.

  • 클러스터의 모든 시스템 간에 완벽한 네트워크 연결. 즉, 마스터 노드와 워커 노드는 네트워크를 통해 통신이 가능해야 한다.
  • 스왑 메모리 비활성화
  • 컨테이너 런타임 설치(Docker)

첫번째 목록인 클러스터의 모든 시스템 간에 완벽한 네트워크 연결은 다양한 변수가 있기 때문에 간단히 ping 테스트가 성공하면 통과한 것으로 확인한다.

두번째 목록인 스왑 메모리 비활성화는 아래 명령어로 가능하다.

  • swapoff -a
  • sed -i '2s/^/#/' /etc/fstab
  • 해당 명령어는 /etc/fstab 파일 내부의 2번째 라인 맨 앞에 #을 붙이라는 명령어임.
  • 스왑메모리에 대한 줄만 주석 처리해줘야함. 만약에 2번째 라인에 루트 파티션에 대한 내용이 있다면 매우 위험하다. 따라서 해당 명령어는 사용하기 전에 꼭 /etc/fstab 파일 내용을 확인 해야하며 권장되지 않음.

세번째 목록인 컨테이너 런타임 설치는 Docker를 설치하는 것으로 해결한다.

우리가 사용하기로 결정한 Ubuntu 18.04 LTS 에서 Docker를 설치하는 방법은 아래와 같다.

Docker 설치가 완료 되었으면, Docker 데몬이 사용하는 드라이버를 cgroupfs 대신 systemd를 사용하도록 설정한다.

왜냐하면 kubernetes에서 권장하는 Docker 데몬의 드라이버는 systemd 이기 때문이고, systemd를 사용하면 kubernetes가 클러스터 노드에서 사용 가능한 자원을 쉽게 알 수 있도록 해주기 때문이다.

아래 명령어를 통해 Docker 데몬의 드라이버를 교체한다.

Kubernetes Setup

kubernetes 클러스터 구성을 위해 모든 노드에 아래의 패키지를 설치 해야 한다.

  • kubeadm : kubernetes 클러스터를 구축하기 위해 사용하는 툴이다.
  • kubelet : 클러스터의 모든 머신에서 실행되며 Pod 및 컨테이너 시작 등의 작업을 수행하는 구성 요소이다.
  • kubectl : 클러스터와 통신하는 커맨드라인 인터페이스 유틸이다.

여기서 의문이 들 수 있다.

마스터 노드는 api server를 통해 워커 노드의 kubelet이랑 통신하며 Pod와 컨테이너를 관리하는 것으로 알고 있는데 그럼 마스터 노드에는 kubelet이 필요 없는 것이 아닌가?

kubeadm은 모든 마스터 노드의 구성요소(etcd, api server, controller manager, scheduler)를 kubelet을 통해 Pod로 실행하려고 한다.

즉, 마스터 노드의 모든 구성요소들을 Pod 및 내부 컨테이너로 띄우고 유지, 관리하기 위해 마스터 노드 내에도 kubelet을 사용하는 것이다.

만약 이게 싫다면 직접 마스터 노드에 구성요소들을 Docker 데몬(systemd)으로 실행시킬 수 있다.

본론으로 들어가자.

먼저 아래 명령어를 통해 패키지 리스트를 업데이트한다.

아래 명령어를 통해 kubeadm, kubelet, kubectl을 설치할 수 있다.

마스터 노드, 워커 노드 모두 아래 명령어를 통해 동일하게 세 개의 패키지를 설치한다.

마스터 노드 세팅

마스터 노드를 실행시키려면 kubeadm init <args> 명령어를 통해 가능하다.

<args>에는 여러가지 옵션 값이 들어가며, 여기서 필요한 옵션 값은 아래와 같다.

  • —-pod-network-cidr : Pod 네트워크를 설정할 때 사용
  • --apiserver-advertise-address : 특정 마스터 노드의 API Server 주소를 설정할 때 사용
  • 만약 고가용성을 위해 마스터 노드를 다중으로 구성했다면 --control-plane-endpoint 옵션을 사용하여 모든 마스터 노드에 대한 공유 엔드포인트를 설정할 수 있다.

Pod 네트워크 설정

우선 세팅할 클러스터에서 Pod가 서로 통신할 수 있도록 Pod 네트워크 애드온을 설치 해야 한다.

kubeadm을 통해 만들어진 클러스터는 CNI(Container Network Interface) 기반의 애드온이 필요하다.

기본적으로 kubernetes에서 제공해주는 kubenet이라는 네트워크 플러그인이 있지만, 매우 기본적이고 간단한 기능만 제공하는 네트워크 플러그인이기 때문에 이 자체로는 크로스 노드 네트워킹이나 네트워크 정책과 같은 고급 기능은 구현되어 있지 않다.

따라서 kubeadm은 kubernetes가 기본적으로 지원해주는 네트워크 플러그인인 kubenet을 지원하지 않고, CNI 기반 네트워크만 지원한다.

CNI 기반 Pod 네트워크 애드온 설치에 관련해서 자세한 정보는 링크를 참고한다.

여기서는 Flannel이라는 Pod 네트워크 애드온을 설치하여 사용할 것이다.

Flannel을 사용하려면 kubeadm init 명령어에 --pod-network-cidr=10.244.0.0/16 이라는 인자를 추가해서 실행해야 한다. 10.244.0.0/16 이라는 네트워크는 Flannel에서 기본적으로 권장하는 네트워크 대역이다.

Pod 네트워크가 호스트 네트워크와 겹치면 문제가 발생할 수 있기 때문에 일부로 호스트 네트워크로 잘 사용하지 않을 것 같은 네트워크 대역을 권장하는 것이다.

만일 호스트 네트워크에서 10.244.0.0/16 네트워크를 사용하고 있다면, --pod-network-cidr 인자 값으로 사용하고 있지 않은 다른 네트워크 대역을 넣어야 한다.

또한 sysctl net.bridge.bridge-nf-call-iptables=1 명령어를 실행하여 /proc/sys/net/bridge/bridge-nf-call-iptables 의 값을 1로 설정 해야한다.

이것은 일부 CNI 플러그인이 작동하기 위한 요구 사항이다. (자세한 내용 참조)

그리고 만약 방화벽을 사용하고 있다면, 방화벽 규칙이 UDP 8285, 8472 포트의 트래픽을 허용하는지 확인해야 한다.

Flannel이 udp 백엔드를 사용하여 캡슐화 된 패킷을 보내기 위해서 UDP 8285 포트를,

커널이 vxlan 백엔드를 사용하여 캡슐화 된 패킷을 보내기 위해서 UDP 8472 포트를 사용한다.

kubernetes에서의 Flannel 세팅에 대해서 더 자세히 알고 싶으면 링크를 참고한다.

API Server 주소 설정

우리는 마스터 노드를 세팅 할 것이기 때문에 이 노드가 Control Plane으로써 동작할 것이라는 것을 kubeadm에게 알려줘야 한다.

우선 자신의 네트워크 인터페이스를 확인한다.

마스터 노드의 네트워크 인터페이스 IPv4 값을 Control Plane의 API 서버에 대한 주소값으로 설정할 것이다.

ifconfig 명령어의 출력 결과에서 확인할 수 있듯 해당 마스터 노드의 IPv4 주소는 enp0s8 이더넷의 192.168.99.102 이다.

해당 IPv4 주소를 마스터 노드의 API Server 주소로 사용할 것이다.

--apiserver-advertise-address 라는 옵션을 통해 해당 IPv4 주소를 kubeadm에게 전달할 수 있다.

마스터 노드 생성 및 실행

최종적으로 실행해야 할 kubeadm init 명령어는 아래와 같다.

해당 명령을 실행하고 제대로 마스터 노드가 셋업되면 아래와 같은 메시지를 출력한다.

셋업한 클러스터를 사용하려면 아래의 명령어를 실행해야한다.

해당 명령어는 Root 계정이 아닌 다른 사용자 계정에서 kubectl 커맨드 명령어를 사용하여 클러스터를 제어하기 위해 사용하는 명령어이다.

기본적으로 kubernetes에서는 /etc/kubernetes/admin.conf 파일을 가지고 kubernetes 관리자 Role의 인증 및 인가 처리를 하며, 위 명령어는 사용자 계정의 $HOME/.kube/config 디렉터리에 admin.conf 파일을 복사함으로써 사용자 계정이 kubectl을 사용하면서 관리자 Role의 인증 및 인가 처리를 받을 수 있도록 하는 것이다. 여기서 말한 사용자 계정이란, 마스터 노드의 Shell에 접속한 계정이다.

더 깊게 들어가보면 admin.conf는 클러스터에 접근할 수 있는 인증의 역할만을 하고,

클러스터의 리소스에 접근하여 제어하기 위한 Role에 대한 권한 허용은 해당 계정(admin.conf)에 rolebinding을 통해서 가능하다.

즉, RBAC(Role-based access control) 방식을 통해 인증 및 인가 시스템을 사용한다고 보면 된다.

RBAC는 사용자와 역할을 별개로 생성한 후 서로를 엮어서(binding) 사용자에게 역할에 대한 권한을 부여하는 방식이다.

kubeadm은 기본적으로 안전한 클러스터 구성을 위하여 RBAC 사용을 강제한다.

RBAC말고도 ABAC(Attribute-based access control)이라는 방식도 있는데, 이 방식은 권한에 대한 내용을 파일로 관리하기 때문에 권한을 변경하려면 직접 마스터 노드에 들어가서 파일을 변경하고 api server를 재시작 해주어야 하기 때문에 번거로워 잘 사용하지 않는다.

만약 다른 컴퓨터에서 kubectl을 사용하여 클러스터와 통신하고 싶다면 아래와 같이 admin.conf 파일을 마스터 노드에서 복사해와서 kubectl --kubeconfig 명령어를 통해 가능하다.

또한 클러스터 외부에서 마스터 노드의 API 서버에 연결하고 싶다면 아래와 같이 kubectl proxy 명령어를 사용할 수 있다.

이제 브라우저를 통해 http://<마스터 노드의 IP>:8001/api/v1 에 접속해보면 API 서버에 접속했음을 확인할 수 있다.

그런데 admin.conf는 관리자 Role을 가지고 있으므로 보안상 외부에 노출하기에는 위험하다.

그래서 일반 사용자를 위한 일부 권한을 허용하는 고유한 자격 증명을 새로 생성하여 제공하는 것이 좋다. 참고, 참고2

클러스터에 접근하여 리소스를 사용하기 위한 인증 및 인가 처리는 완료했다.

그 다음은 우리가 사용할 Pod 네트워크 애드온(Flannel)을 사용하기 위해 아래의 명령어를 통해 Pod 네트워크를 클러스터에 배포한다.

명령어를 실행하면 아래와 같은 메시지를 확인할 수 있다.

이제 마스터 노드가 잘 세팅되었는지 아래 명령어를 통해 확인해볼 수 있다.

워커 노드 세팅

마스터 노드를 생성하고 출력된 메시지의 맨 아래에서 아래와 같은 메시지를 확인할 수 있다.

여기서 kubeadm join 명령어를 워커 노드에서 실행 시키기만 하면 해당 워커 노드가 마스터 노드에 결합된다.

워커 노드에서 아래 명령어를 실행한다.

명령어에서 확인할 수 있는 것과 같이 워커 노드가 마스터 노드에 결합(join)하기 위해서는 아래 인자들이 필요하다.

  • 마스터 노드의 IP:6443 (6443 포트는 kubernetes api server 프로세스의 기본 포트이다. 만약 마스터 노드의 api server 포트가 6443이 아니라면 해당 마스터 노드의 api server 포트 번호를 기입해주면 된다.)
  • --token (token은 기본적으로 24시간 뒤 만료된다.)
  • --discovery-token-ca-cert-hash

마스터 노드의 IP와 포트는 쉽게 알 수 있지만, 만약 --token 값과 --discovery-token-ca-cert-hash 값을 잊어버렸다면 아래와 같이 다시 찾을 수 있다.

  • --token 찾는 명령어
  • --discovery-token-ca-cert-hash 찾는 명령어

만약 token이 24시간이 지나서 만료가 되었을 때에는 아래의 명령어를 통해 새로 생성이 가능하다.

kubeadm join 명령어를 실행하고 연결에 성공하면 아래와 같은 메시지를 확인할 수 있다.

이제 마스터 노드에서 아래의 명령어를 실행하면 워커 노드가 잘 결합된 것을 확인할 수 있다.

명령어 실행 결과로는 아래와 같은 메시지로 확인할 수 있다.

Kubernetes Deploy Example

자 이제 마스터 노드와 워커 노드가 구성되었으니 기본적인 hello world 프로젝트를 배포하여 테스트해보겠다.

우리가 구축한 kubernetes 구조는 아래와 비슷하다. 다른 점은 우리는 워커 노드가 1개 라는 것 뿐이다.

기본적인 kubernetes 구조 다이어그램

만약에 우리가 Hello world 프로젝트를 배포하고 싶다면 어떻게 해야할까?

마스터 노드의 API Server에 kubectl 명령어 도구를 사용하여 Hello world 프로젝트를 배포하라는 명령을 전달하면 된다.

아래는 구글에서 제공하는 Hello world 샘플 프로젝트를 kubectl create deployment 명령어로 배포하는 예제이다. (deployment.yaml 명세서를 작성하지 않고 바로 생성한다)

마스터 노드에서 위 명령어를 실행하면 아래와 같은 메시지를 확인할 수 있다.

우리는 마스터 노드의 API Server에게 샘플 프로젝트를 생성 하라고 명령을 전달했으니 마스터 노드의 API Server가 워커 노드의 kubelet을 통해 명령을 전달하여 워커 노드가 Pod를 할당받고 그 안에 컨테이너를 배포 했을 것이라고 생각할 수 있다.

과연 예상대로 동작했는지 확인해보자.

먼저 마스터 노드에서 배포한 deployment와 이에 따라 생성된 pod를 확인해보자.

deployment 확인

pod 확인

Pod 확인 결과 Pod의 IP가 10.244.1.2 이다.

즉, 10.224.1.xxx 네트워크를 사용하고 있는 것이다.

마스터 노드의 ifconfig를 조회해보자.

우리가 앞에서 마스터 노드를 세팅했을 때 설치한 Pod 네트워크 애드온인 Flannel이 보인다.

이 Flannel의 IP가 10.244.0.0 으로써 네트워크 인터페이스 역할을 하는 것을 확인할 수 있다.

그리고 cni0 이라는 가상 이더넷 네트워크 엔드포인트가 생겼다. 이 엔드포인트는 10.244.0.1 라는 IP 주소를 사용한다.

즉, Flannel의 10.244.0.xxx 대역을 통해 노드 내부에서 각 Pod 끼리 찾아 서로 통신할 수 있도록 네트워크가 구성된 것이다.

그런데 우리가 생성한 Pod의 IP 주소는 10.244.1.2 이다.

마스터 노드 내 Flannel의 10.244.0.xxx 대역과 다르다.

그럼 Pod는 어디서 구동되고 있을까?

이번엔 워커 노드의 ifconfig를 조회해보자.

워커 노드의 Flannel 네트워크 IP 주소는 10.244.1.0 이고,

cni0 가상 이더넷 네트워크의 IP 주소는 10.244.1.1 이다.

따라서 10.244.1.2의 IP 주소를 가진 Pod는 여기 워커 노드와 같은 네트워크 대역을 사용하고 있으므로 Pod는 워커 노드에 생성 되었다는 것을 확인할 수 있다.

Pod 내부에 떠있는 Hello world 프로젝트 프로세스가 사용하고 있는 포트 번호는 8080이다.

워커 노드에 접속해서 curl 명령어를 통해 Pod 내부에 떠있는 컨테이너로 통신을 시도해보자.

curl 명령어의 결과로 아래와 같은 메시지를 확인할 수 있다.

--

--