본문 바로가기

혼자하는 프로젝트/AWS_쿠버네티스_Docker

AWS 에서 쿠버네티스 클러스터링 구현

728x90
반응형

구성 환경

- AWS 우분투 20.04버전

- 인스턴스 타입 : t2.micro

 

쿠버네티스 클러스터 아키텍처

쿠버네티스 클러스터는 컨트롤 플레인(Control plane) 부분을 담당하는 마스터 노드와 애플리케이션 파드(POD)가 실행되는 워커 노드로 구성된다.

컨트롤 플레인 컴포넌트 (Control plane component)

쿠버네티스 클러스터의 두뇌 역할을 하며 컨테이너 스케줄링, 서비스 관리, API 요청 처리등의 작업을 수행한다.
컨트롤 플레인에 해당하는 컴포넌트들은 마스터 노드에서 실행된다.

#kube-apiserver

  • K8S API를 노출하는 컨트롤 플레인의 프론트엔드
  • 수평 확장 가능
  • etcd
    • 클러스터의 모든 데이터를 보관하는 일관성, 고가용성을 보장하는 키-값 저장소
    • 어떤 노드가 존재하고 클러스터에 어떤 리소스가 존재하는지와 같은 정보
  • kube-scheduler
    • 새로운 POD 생성을 감지하고, 실행시킬 워커 노드를 선택하는 역할
  • kube-controller-manager
    • 디플로이먼트 같은 리소스 컨트롤러를 관리
    • API 서버를 통해 클러스터의 공유된 상태를 감지하고, 현재 상태를 원하는 상태로 이행하는 컨트롤 루프를 관리
  • cloud-controller-manager
    • 클러우드 업체와 연동하여 로드 밸런서나 디스크 볼륨 같은 자원을 관리

# 노드 컴포넌트 (Node component)

POD를 유지시키고 쿠버네티스 런타임 환경을 제공하는 역할을 수행한다.
노드 컴포넌트들은 모든 노드에서 실행될 수 있다.

  • kubelet
    • 각 노드에서 실행되는 에이전트
    • 컨테이너 런타임을 관리하고 상태를 모니터링
    • POD에서 컨테이너가 확실하게 동작하도록 관리
  • kube-proxy
    • 각 노드에서 실행되는 네트워크 프록시
    • Service 개념의 구현부
    • 서로 다른 노드에 있는 POD 간 통신이나 POD와 인터넷 사이의 네트워크 트래픽을 라우팅
  • 컨테이너 런타임
    • 컨테이너를 시작하고, 중지
    • 대표적인 Docker

 

애드온

  • 쿠버네티스 오브젝트(데몬셋, 디플로이먼트 등)를 이용해 클러스터에 추가 기능 제공
  • 클러스터 단위 기능을 제공하기 때문에 kube-system 네임스페이스에 속함

# Kubeadm으로 쿠버네티스 클러스터 만들기

컨테이너 런타임 환경 갖추기 (Docker)

쿠버네티스는 컨테이너로 구성된 애플리케이션을 관리할 수 있게 해주는 시스템이다.
그래서 당연히 컨테이너 실행 환경을 갖춰야 한다.
컨테이너의 대중화에 큰 역할을 한 Docker를 사용하는 것이 일반적이긴 하지만 현재는 더욱 다양한 컨테이너 런타임이 생겨났다.

쿠버네티스의 kubelet은 시스템으로부터 명령을 받아 Docker 런타임을 통해서 컨테이너를 관리하는 방식으로 동작한다.
그런데 여러 컨테이너 런타임 기술이 나오면서 쿠버네티스가 지원해야 하는 부담이 늘어났고, 결국 CRI (Container Runtime Interface)라는 표준 인터페이스가 등장하게 되었다.
이 CRI를 따르는 컨테이너 런타임은 무엇이든 쿠버네티스와 연동할 수 있게 된다.

 

Kubeadm 설치하기

서버 요구 사항

  • CPU 2코어, RAM 2GB 이상
  • 클러스터 내 모든 노드간 네트워크 통신 가능
  • 고유한 Hostname, MAC 주소, product_uuid
    • ip link
    • sudo cat /sys/class/dmi/id/product_uuid
  • .Swap을 사용하지 않는다. 초록색으로 된 명령어는 실행 및 구성을 해줘야 함.
    • swapoff -a : Swap 기능 끔
    • echo 0 > /proc/sys/vm/swappiness : 커널 속성을 변경해 swap을 disable
    • sed -e '/swap/ s/^#*/#/' -i /etc/fstab : Swap을 하는 파일 시스템을 찾아 주석 처리
  • 쿠버네티스 구성 요소가 사용하는 포트에 대한 방화벽 오픈(AWS EC2 보안그룹 수정)
    • 컨트롤 플레인
      • TCP - Inbound - 6443: Kubernetes API Server (used by All)
      • TCP - Inbound - 2379~2380: Etcd server client API (used by kube-apiserver, etcd)
      • TCP - Inbound - 10250: Kubelet API (used by Self, Control plane)
      • TCP - Inbound - 10251: kube-scheduler (used by Self)
      • TCP - Inbound - 10252: kube-controller-manager (used by Self)
    • 워커 노드
      • TCP - Inbound - 10250: Kubelet API (used by Self, Control plane)
      • TCP - Inbound - 30000~32767: NodePort Services (used by All)

 

# Kubeadm, Kubelet, Kubectl 설치

kubeadm은 kubelet과 kubectl을 설치해주지 않기 때문에 직접 설치해야 한다.

sudo apt-get update && sudo apt-get install -y apt-transport-https curl

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

# 패키지 버전 홀드 (업데이트에서 제외)
sudo apt-mark hold kubelet kubeadm kubectl

설치가 완료 되었다면 kubeadmkubectl 버전을 확인해본자.

$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:12:12Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:14:22Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/amd64"}

마스터 노드 생성(worker node에서 실행할 필요 없음, worker node에서는 join 명령어로 연결 잘해주면 된다.)

참고로, t2.micro 인스턴스 타입은 vCPU 1이기 때문에 클러스터 최소 요구 사항을 만족하지 않는다.
하지만 테스트 환경을 구축하는것이므로 t2.micro 인스턴스로 구성을한다.

kubeadm init을 통해 쿠버네티스 마스터 노드를 초기화 하는 명령어를 실행한다.

--apiserver-advertise-address 파라미터는 다른 노드가 마스터 노드에 접근할 수 있는 IP 주소를 명시한다.

--pod-network-cidr 파라미터는 쿠버네티스에서 사용할 컨테이너의 네트워크 대역을 지정한다. 실제 서버에 할당된 IP와 중복되지 않도록 해야 한다. 다음 단계에서 진행할 네트워크 플러그인 설치 과정에서 Calico를 설치할 계획이라 CIDR 범위를 192.168.0.0/16로 지정했다. 만약 Flannel을 사용한다면 10.244.0.0./16을 사용해야 한다.

--apiserver-cert-extra-sans 파라미터도 중요하다. 이 값에는 쿠버네티스가 생성한 TLS 인증서에 적용할 IP 또는 도메인을 명시할 수 있다.  로컬 환경에서 kubectl을 통해 이 클러스터에 접근하려면 kube-apiserver와 통신할 수 있어야 하기 때문에 마스터 노드가 실행되고 있는 EC2 인스턴스의 퍼블릭 IP 주소를 추가해야 한다. (worker node 에서는 실행할 필요 없음.)

- 명령어를 실행 시 아래의 #주석은 빼고 실행

kubeadm init \
    --apiserver-advertise-address=0.0.0.0 \ #다른 노드가 마스터 노드에 접근할 수 있는 IP주소
    --pod-network-cidr=192.168.0.0/16 \ #쿠버네티스에서 사용할 컨테이너의 네트워크 대역
    --apiserver-cert-extra-sans=10.1.1.10,[ec2-public-ip-address] \ #TLS 인증서에 적용할 IP 또는 도메인을 명시
    --ignore-preflight-errors=NumCPU \ #CPU 최소 사양 무시
    --ignore-preflight-errors=Mem #최소 메모리 무시

- 만약 위 명령어를 실행하면 아래와 같은 WARNING 과 ERROR 가 보인다면 아래를 확인하자.

[init] Using Kubernetes version: v1.17.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/
error execution phase preflight: [preflight] Some fatal errors occurred:
	[ERROR NumCPU]: the number of available CPUs 1 is less than the required 2
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
To see the stack trace of this error execute with --v=5 or higher

WARNING 메시지에서 “detected “cgroupfs” as the Docker cgroup driver..The recommended driver is systemd” 라며 Docker가 사용하는 Cgroup(Control Group) 드라이버를 systemd로 바꾸는 것을 권장하고 있다.

Cgroup은 프로세스에 할당된 리소스를 제한하는데 사용된다. Ubuntu는 init 시스템으로 systemd를 사용하고 있고 systemd가 Cgroup 관리자로써 작동하게 된다.

- 만약 도커가 설치가 필요하다는 안내가 나오면 아래의 명령어로 설치를 진행한다.

apt install docker.io
# Docker가 사용하는 Cgroup driver 확인하기
$ docker info |grep Cgroup

WARNING: No swap limit support
 Cgroup Driver: cgroupfs

Docker 설정에 Cgroup driver를 바꾸려면 /lib/systemd/system/docker.service을 열어서 아래 구문을 찾아 --exec-opt native.cgroupdriver=systemd 파라미터를 추가한 뒤 저장한다.

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --exec-opt native.cgroupdriver=systemd

systemd를 리로드하고 도커를 재시작한다.
kubeadm init 부분만 빼고 여기까지 워커노드에서 마스터 노드와 동일하게 실행 해줘야 한다.

systemctl daemon-reload
systemctl restart docker

실행 결과 마지막 부분에 출력되는 내용에는 kubectl 을 사용하기 위한 설정(config) 파일 복사 명령어와 워커 노드에서 클러스터에 참여(join)하기 위한 명령어를 제공한다.

- 우분투 20.04에서 발생하는 오류

- 해결방법

sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

- 위의 명령어를 실행 후

sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# kubelet가 실행인지 확인
sudo systemctl status kubelet

# 실행중이 아닐 경우
sudo systemctl start kubelet

- 마지막으로 아래 명령어 실행

위에 커맨드까지 끝나고 나서 

kubeadm reset

kubeadm init 을 다시 실행하면 정상적으로 처리 된다.

kubeadm init \
    --apiserver-advertise-address=0.0.0.0 \
    --pod-network-cidr=192.168.0.0/16 \
    --apiserver-cert-extra-sans=10.1.1.10,[ec2-public-ip-address] \
    --ignore-preflight-errors=NumCPU \
    --ignore-preflight-errors=Mem

- 아래 명령어를 실행해야 kubectl 명령어가 실행된다.

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

워커 노드를 추가하기 전 쿠버네티스가 잘 동작하고 있는지 확인해보자.

kubectl get nodes

NAME              STATUS     ROLES    AGE    VERSION
ip-192-168-1-10   NotReady   master   103s   v1.17.3

coredns가 Pending 상태에 있는것을 볼 수 있다. 네트워크 플러그인을 설치하여 문제를 해결해야 한다.

kubectl get po -n kube-system

NAME                                  READY   STATUS    RESTARTS   AGE
coredns-6955765f44-6gmw4                  0/1     Pending   0          3m24s
coredns-6955765f44-bl62s                  0/1     Pending   0          3m24s
etcd-ip-192-168-1-10                      1/1     Running   0          3m37s
kube-apiserver-ip-192-168-1-10            1/1     Running   0          3m37s
kube-controller-manager-ip-192-168-1-10   1/1     Running   0          3m37s
kube-proxy-qh9lg                          1/1     Running   0          3m24s
kube-scheduler-ip-192-168-1-10            1/1     Running   0          3m37s

# 네트워크 플러그인 설치

이전 과정에서 Coredns가 아직 Pending상태인 것을 볼 수 있었다.
Coredns는 쿠버네티스에서 사용하는 DNS 서버이다. Coredns가 정상 동작하려면 네트워크 플러그인을 먼저 설치해야 한다.

네트워크 플러그인은 파드끼리 서로 통신하기 위해서 사용된다.
컨테이너와 컨테이너 네트워크 구현체 사이의 표준을 정의한 CNI(Container Network Interface)를 지원하는 플러그인을 설치해야 한다.

Calico를 설치한다.

kubectl apply -f https://docs.projectcalico.org/v3.19/manifests/calico.yaml

만약 Flannel을 설치하고 싶다면 kubeadm init할 때 지정한 --pod-network-cidr 파라미터를 수정했어야 한다. 왜냐하면 각 네트워크 플러그인마다 파드에서 사용할 기본 CIDR 대역이 다르기 때문이다.

그리고 Flannel을 설치하려면 최소 1개 이상의 Worker 노드가 있어야 하기 때문에 다음 단계에서 진행할 워커 노드를 클러스터에 참여시키는 과정을 먼저 진행해야 한다. 또한 오버레이 네트워크 구성을 위해 모든 노드간 방화벽 설정에 8285, 8472 포트의 UDP 프로토콜을 개방해야 한다.

Calico가 설치되는 과정을 살펴보자.

kubectl get po -n kube-system -w

NAME                                   READY   STATUS    RESTARTS   AGE
coredns-6955765f44-c9nzw               0/1     Pending   0          51s
coredns-6955765f44-ck4lh               0/1     Pending   0          51s
etcd-ip-10-1-1-10                      1/1     Running   0          67s
kube-apiserver-ip-10-1-1-10            1/1     Running   0          67s
kube-controller-manager-ip-10-1-1-10   1/1     Running   0          67s
kube-proxy-ztjfb                       1/1     Running   0          51s
kube-scheduler-ip-10-1-1-10            1/1     Running   0          67s
calico-node-m6dzv                      0/1     Pending   0          0s
calico-kube-controllers-5b644bc49c-9wnxl   0/1     Pending   0          0s
calico-node-m6dzv                          0/1     Pending   0          0s
calico-kube-controllers-5b644bc49c-9wnxl   0/1     Pending   0          0s
calico-node-m6dzv                          0/1     Init:0/3   0          0s
calico-node-m6dzv                          0/1     Init:1/3   0          15s
calico-node-m6dzv                          0/1     Init:1/3   0          18s
calico-node-m6dzv                          0/1     Init:2/3   0          21s
calico-kube-controllers-5b644bc49c-9wnxl   0/1     Pending    0          27s
calico-kube-controllers-5b644bc49c-9wnxl   0/1     ContainerCreating   0          28s
coredns-6955765f44-c9nzw                   0/1     Pending             0          118s
coredns-6955765f44-ck4lh                   0/1     Pending             0          118s
coredns-6955765f44-c9nzw                   0/1     ContainerCreating   0          119s
coredns-6955765f44-ck4lh                   0/1     ContainerCreating   0          2m2s
calico-node-m6dzv                          0/1     PodInitializing     0          50s
kube-scheduler-ip-10-1-1-10                0/1     Error               0          2m38s
kube-controller-manager-ip-10-1-1-10       0/1     Error               0          2m38s
kube-scheduler-ip-10-1-1-10                1/1     Running             1          4m3s
kube-controller-manager-ip-10-1-1-10       1/1     Running             1          4m4s
kube-scheduler-ip-10-1-1-10                0/1     Error               1          5m7s
kube-controller-manager-ip-10-1-1-10       0/1     Error               1          5m7s
calico-node-m6dzv                          0/1     Running             0          3m24s
calico-node-m6dzv                          1/1     Running             0          9m22s
kube-controller-manager-ip-10-1-1-10       0/1     CrashLoopBackOff    1          11m
kube-scheduler-ip-10-1-1-10                0/1     CrashLoopBackOff    1          11m
kube-controller-manager-ip-10-1-1-10       1/1     Running             2          11m
kube-scheduler-ip-10-1-1-10                1/1     Running             2          11m
coredns-6955765f44-c9nzw                   0/1     ContainerCreating   0          11m
calico-kube-controllers-5b644bc49c-9wnxl   0/1     ContainerCreating   0          9m46s
coredns-6955765f44-ck4lh                   0/1     ContainerCreating   0          11m
coredns-6955765f44-c9nzw                   0/1     Running             0          11m
coredns-6955765f44-ck4lh                   0/1     Running             0          11m
coredns-6955765f44-c9nzw                   1/1     Running             0          11m
coredns-6955765f44-ck4lh                   1/1     Running             0          11m
calico-kube-controllers-5b644bc49c-9wnxl   0/1     Running             0          9m57s
calico-kube-controllers-5b644bc49c-9wnxl   1/1     Running             0

워커 노드를 클러스터로 참여시키기

위에서 kubeadm init 명령어 실행 결과 밑부분에 나온 명령어를 Worker 노드에서 실행한다.

kubeadm join 10.1.1.10:6443 --token oc1crn.fr0wtn6k8vmxml80 \
    --discovery-token-ca-cert-hash sha256:913c8cfd3249b4c45e61c3ce6dccb13baa086d2b617782b7dcd2ef730cb3dc60

완료되면 Master 노드에서 kubectl로 확인해보자.

kubectl get nodes

NAME              STATUS     ROLES    AGE   VERSION
ip-192-168-1-10   NotReady   master   26m   v1.17.3
ip-192-168-1-20   NotReady   <none>   13s   v1.17.3

# 팁

1. 마스터 노드 토큰 재발급하기

워커 노드가 클러스터에 참여할 때 실행한 join 명령어에 사용된 토큰은 24시간 동안만 유효하다.
토큰을 잃어버렸을 때는 마스터 노드에서 kubeadm token list 명령을 실행해서 확인한다. 새 토큰이 필요한 경우 kubeadm token create을 실행한다.

2.  마스터 노드 생성시 발급된 인증서 다시 만들기

rm /etc/kubernetes/pki/apiserver.*

#만약 실행시 오류 발생하면 \ 빼고 가로로 쭉 늘어뜨려서 명령문을 실행
kubeadm init phase certs all \ 
    --apiserver-advertise-address=0.0.0.0 \
    --apiserver-cert-extra-sans=10.1.1.10,[ec2-pubic-address]

docker rm -f `docker ps -q -f 'name=k8s_kube-apiserver*'`

systemctl restart kubelet

3. 토큰 및 해시값 확인하기

1. Token 생성(확인)

kubeadm token list

kubeadm token create

2. Hash 확인

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

3. Join

kubeadm join <Kubernetes API Server:PORT> --token <2. Token 값> --discovery-token-ca-cert-hash sha256:<3. Hash 값>\

예시 : kubeadm join 10.1.1.10:6443 --token mbq8ym.b8dhx5su7d --discovery-token-ca-cert-hash sha256:f1591a1dba97b7f45bf2015779bd78fdf3

- 최종적으로 마스터 노드와 워커노드가 조회되는 모습이다.

728x90
반응형