IT/CLOUD(AWS,Azure,GCP,Docker)

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

알콩달콩아빠 2023. 8. 10. 10:44
728x90
반응형

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

 

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

마스터 노드와 워커 노드는 최소한 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 -a  sed -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          | /var/run/docker.sock              | 
| Containerd      | /run/containerd/containerd.sock   | 
| CRI-O           | /var/run/crio/crio.sock           | 
+-----------------+-----------------------------------+

# 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를 설치하는 방법은 아래와 같다.

# 패키지 관리 도구 업데이트
$ sudo apt update
$ sudo apt-get update# 기존 docker 설치된 리소스 확인 후 발견되면 삭제
$ sudo apt-get remove docker docker-engine docker.io# docker를 설치하기 위한 각종 라이브러리 설치
$ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common -y# curl 명령어를 통해 gpg key 내려받기
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -# 키를 잘 내려받았는지 확인
$ sudo apt-key fingerprint 0EBFCD88# 패키지 관리 도구에 도커 다운로드 링크 추가
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"# 패키지 관리 도구 업데이트
$ sudo apt-get update# docker-ce의 버젼을 최신으로 사용하는게 아니라 18.06.2~3의 버젼을 사용하는 이유는 
# kubernetes에서 권장하는 버젼의 범위가 최대 v18.09 이기 때문이다.
$ sudo apt-get install docker-ce=18.06.2~ce~3-0~ubuntu -y# Docker 설치 완료 후 테스트로 hello-world 컨테이너 구동
$ sudo docker run hello-world

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

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

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

$ sudo cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF$ sudo mkdir -p /etc/systemd/system/docker.service.d$ sudo systemctl daemon-reload$ sudo systemctl restart 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)으로 실행시킬 수 있다.

본론으로 들어가자.

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

$ sudo apt-get update
$ sudo apt-get upgrade

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

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

$ curl -s <https://packages.cloud.google.com/apt/doc/apt-key.gpg> | sudo apt-key add -$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb <https://apt.kubernetes.io/> kubernetes-xenial main
EOF$ sudo apt-get update$ sudo apt-get install -y kubelet kubeadm kubectl# 패키지가 자동으로 설치, 업그레이드, 제거되지 않도록 hold함.
$ sudo apt-mark hold kubelet kubeadm kubectl# 설치 완료 확인
$ kubeadm version$ kubelet --version$ kubectl version
 

마스터 노드 세팅

마스터 노드를 실행시키려면 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 서버에 대한 주소값으로 설정할 것이다.

$ ifconfigenp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::a00:27ff:fe6b:e889  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:6b:e8:89  txqueuelen 1000  (Ethernet)
        RX packets 33304  bytes 43907211 (43.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13078  bytes 878704 (878.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.99.102  netmask 255.255.255.0  broadcast 192.168.99.255
        inet6 fe80::a00:27ff:fe41:2971  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:41:29:71  txqueuelen 1000  (Ethernet)
        RX packets 261  bytes 25133 (25.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 163  bytes 22616 (22.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 124  bytes 9164 (9.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 124  bytes 9164 (9.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

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

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

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

마스터 노드 생성 및 실행

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

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.99.102

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

[init] Using Kubernetes version: v1.16.3
[preflight] Running pre-flight checks
	[WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at <https://kubernetes.io/docs/setup/cri/>
	[WARNING SystemVerification]: this Docker version is not on the list of validated versions: 19.03.4. Latest validated version: 18.09
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
...
...
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxyYour Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user:  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/configYou should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  <https://kubernetes.io/docs/concepts/cluster-administration/addons/>Then you can join any number of worker nodes by running the following on each as root:kubeadm join 192.168.99.102:6443 --token fnbiji.5wob1hu12wdtnmyr \
    --discovery-token-ca-cert-hash sha256:701d4da5cbf67347595e0653b31a7f6625a130de72ad8881a108093afd06188b

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

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

해당 명령어는 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 명령어를 통해 가능하다.

scp root@<마스터 노드 IP>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf get nodes

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

scp root@<마스터 노드 IP>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf proxy

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

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

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

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

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

$ kubectl apply -f <https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml>

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

podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created

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

# 노드 확인
$ kubectl get nodes
NAME              STATUS   ROLES    AGE     VERSION
bsc-kube-master   Ready    master   5m41s   v1.16.3# 노드 내의 kube-system 네임스페이스를 가진 컨테이너 확인(마스터 노드 관리 컨테이너)
$ kubectl get pod --namespace=kube-system -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP            NODE               NOMINATED NODE   READINESS GATES
coredns-5644d7b6d9-wdt7j                  1/1     Running   4          7d17h   10.244.0.11   bsc-kube-master    <none>           <none>
coredns-5644d7b6d9-x97vf                  1/1     Running   4          7d17h   10.244.0.10   bsc-kube-master    <none>           <none>
etcd-bsc-kube-master                      1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>
kube-apiserver-bsc-kube-master            1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>
kube-controller-manager-bsc-kube-master   1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>
kube-flannel-ds-amd64-n7n7q               1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>
kube-flannel-ds-amd64-qbcwp               1/1     Running   5          7d17h   10.0.2.15     bsc-kube-worker    <none>           <none>
kube-flannel-ds-amd64-qts5l               1/1     Running   1          3d22h   10.0.2.15     bsc-kube-worker2   <none>           <none>
kube-proxy-9sw4k                          1/1     Running   5          7d17h   10.0.2.15     bsc-kube-worker    <none>           <none>
kube-proxy-b5hqc                          1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>
kube-proxy-m5npl                          1/1     Running   1          3d22h   10.0.2.15     bsc-kube-worker2   <none>           <none>
kube-scheduler-bsc-kube-master            1/1     Running   4          7d17h   10.0.2.15     bsc-kube-master    <none>           <none>

워커 노드 세팅

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

Then you can join any number of worker nodes by running the following on each as root:kubeadm join 192.168.99.102:6443 --token fnbiji.5wob1hu12wdtnmyr \
    --discovery-token-ca-cert-hash sha256:701d4da5cbf67347595e0653b31a7f6625a130de72ad8881a108093afd06188b

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

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

$ kubeadm join 192.168.99.102:6443 --token fnbiji.5wob1hu12wdtnmyr \
    --discovery-token-ca-cert-hash sha256:701d4da5cbf67347595e0653b31a7f6625a130de72ad8881a108093afd06188b

명령어에서 확인할 수 있는 것과 같이 워커 노드가 마스터 노드에 결합(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 찾는 명령어
$ kubeadm token list
  • --discovery-token-ca-cert-hash 찾는 명령어
$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

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

$ kubeadm token create

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

[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.16" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

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

$ kubectl get nodes

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

NAME              STATUS   ROLES    AGE     VERSION
bsc-kube-master   Ready    master   10m     v1.16.3
bsc-kube-worker   Ready    <none>   2m32s   v1.16.3

Kubernetes Deploy Example

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

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

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

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

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

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

$ kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1

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

deployment.apps/kubernetes-bootcamp created

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

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

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

deployment 확인

$ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           11s

pod 확인

$ kubectl get pods -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP           NODE              NOMINATED NODE   READINESS GATES
kubernetes-bootcamp-69fbc6f4cf-wn7h2   1/1     Running   0          23s   10.244.1.2   bsc-kube-worker   <none>           <none>

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

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

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

$ ifconfig
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.0.1  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::3442:25ff:fe8b:8109  prefixlen 64  scopeid 0x20<link>
        ether 36:42:25:8b:81:09  txqueuelen 1000  (Ethernet)
        RX packets 56012  bytes 4071357 (4.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 74173  bytes 22202090 (22.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:6d:be:62:41  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::a00:27ff:fe6b:e889  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:6b:e8:89  txqueuelen 1000  (Ethernet)
        RX packets 57793  bytes 79499099 (79.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5172  bytes 332290 (332.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.99.102  netmask 255.255.255.0  broadcast 192.168.99.255
        inet6 fe80::a00:27ff:fe41:2971  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:41:29:71  txqueuelen 1000  (Ethernet)
        RX packets 46174  bytes 4462046 (4.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 41216  bytes 21524823 (21.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.0.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::4800:79ff:feff:94f9  prefixlen 64  scopeid 0x20<link>
        ether 4a:00:79:ff:94:f9  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 19 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 1482983  bytes 273143753 (273.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1482983  bytes 273143753 (273.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0veth37fe471f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet6 fe80::2c20:3ff:fe80:8b38  prefixlen 64  scopeid 0x20<link>
        ether 2e:20:03:80:8b:38  txqueuelen 0  (Ethernet)
        RX packets 27953  bytes 2424489 (2.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 37163  bytes 11106478 (11.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0vethcd4f8a8e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet6 fe80::e0ab:32ff:fe57:d2f9  prefixlen 64  scopeid 0x20<link>
        ether e2:ab:32:57:d2:f9  txqueuelen 0  (Ethernet)
        RX packets 28059  bytes 2431036 (2.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 37070  bytes 11099876 (11.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

우리가 앞에서 마스터 노드를 세팅했을 때 설치한 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를 조회해보자.

$ ifconfig
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.1  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::1490:55ff:fe68:a5f0  prefixlen 64  scopeid 0x20<link>
        ether 16:90:55:68:a5:f0  txqueuelen 1000  (Ethernet)
        RX packets 1  bytes 28 (28.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11  bytes 906 (906.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:e2:9b:59:a8  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::a00:27ff:fee4:e1cc  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:e4:e1:cc  txqueuelen 1000  (Ethernet)
        RX packets 191778  bytes 228795142 (228.7 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 43487  bytes 3897620 (3.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.59.4  netmask 255.255.255.0  broadcast 192.168.59.255
        inet6 fe80::a00:27ff:fefc:af46  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:fc:af:46  txqueuelen 1000  (Ethernet)
        RX packets 900  bytes 135065 (135.0 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 198  bytes 29033 (29.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::f:b7ff:fe93:2fee  prefixlen 64  scopeid 0x20<link>
        ether 02:0f:b7:93:2f:ee  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 19 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 210  bytes 17402 (17.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 210  bytes 17402 (17.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0veth58d9d4ce: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet6 fe80::4819:f3ff:fe70:18ca  prefixlen 64  scopeid 0x20<link>
        ether 4a:19:f3:70:18:ca  txqueuelen 0  (Ethernet)
        RX packets 1  bytes 42 (42.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 20  bytes 1584 (1.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

워커 노드의 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 http://10.244.1.2:8080

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

Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-69fbc6f4cf-wn7h2 | v=1

출처 : 쿠버네티스(kubernetes) 설치 및 환경 구성하기. How to configure a Kubernetes cluster | by ShinChul Bang | finda 기술 블로그 | Medium

728x90
반응형