Kubernetes 란 Docker, cri-o 등의 컨테이너 기반 가상화 런타임 위에서 동작하는 소프트웨어로,
가상화 런타임의 클러스터를 만들어 쉬운 Container 배포 및 관리를 가능하게 합니다.
Kubernetes는 클러스터 내에 새로운 컨테이너를 생성할 때 능동적으로 할당 및 관리하며,
여러 노드로 이루어진 클러스터 안에서 다른 노드 안에 있는 컨테이너 간의 네트워킹을 관리 해 주고 (CNI 플러그인이 실제로 통신을 담당),
설정 정보를 포함하여 컨테이너를 배포 할 수 있게 하는 등 편의성 높은 기능을 가지고 있으며,
이 모든 작업을 쉽고 빠르게 클러스터 내에 배포 및 확장 할 수 있는 많은 이점을 가진 고성능 플랫폼 입니다.
이 장점 덕분에 마이크로서비스 아키텍처를 사용하는 클라우드 소프트웨어를 손쉽게 구성 할 수 있게 해 줍니다.
Kubernetes 는 클러스터로 동작하며, Single-node 실행은 가능하나 권장하지 않습니다.
이 튜토리얼에서는 3개의 노드로 Single-Master, Multi-Worker 클러스터를 구성하도록 하겠습니다.
사용한 시스템 :
사용한 소프트웨어 :
Kubernetes 는 위 이미지처럼 etcd, kube-apiserver 등의 컨테이너가 조합되어 동작합니다.
Pod 란 일종의 컨테이너의 집합체 입니다. Kuberenetes 에서 배포할 수 있는 가장 작은 단위의 Runnable Object 이며, 노드에 배포되어 실행됩니다. 여러 Container 를 하나의 Pod 안에 넣을 수 있으나 긴밀하게 연결될 필요가 있는 컨테이너가 아닌 이상 권장되지는 않습니다.
왜냐 하면 하나의 Pod는 각종 컴퓨팅 리소스 (고유 IP, CPU, RAM 등) 를 할당 받는데, 같은 Pod 내의 Container 들은 이 리소스를 공유하기 때문에 유연한 관리가 어려워집니다.
Pod는 휘발성이기 때문에 Pod 가 재시작 되면 담고있던 데이터가 초기화 됩니다. 이는 Volume 을 할당하여 해결합니다.
kubelet 은 각 노드에서 컨테이너 런타임과 연동되어 Kubernetes 의 각종 명령을 실행합니다. Kubernetes Node의 기반이라고도 볼 수 있습니다. 컨테이너가 아닌 각 노드의 시스템 서비스로 동작합니다.
kube-apiserver 는 마스터 노드에서 동작하며 사용자가 kubectl 등의 툴로 내린 명령들을 처리하고 etcd 에 작성합니다. apiserver 라는 명칭에서 볼 수 있듯 HTTPS 를 이용한 API 서버로 동작합니다.
etcd 는 기본적으로 key-value 저장소이나 Kubernetes 에서는 명령을 처리하기 위해 이 저장소를 활용합니다.

예를 들어, kube-apiserver 가 Pod 생성 명령을 받으면 휘하의 워커 노드에게 Pod를 실행시키는 명령을 내리기 위해 이 곳에 명령을 담습니다.
그러면 kube-scheduler 가 etcd를 확인하고 적당한 노드를 선택 해 노드에 Pod를 할당하라는 명령을 kube-apiserver로 보내고, kube-apiserver는 이를 etcd에 다시 담습니다.
해당 노드의 kubelet 은 etcd를 지켜보다가 자신에게 Pod를 실행하라는 명령이 온 것을 확인하고 명령을 처리합니다.
CNI 플러그인 은 Kubernetes 에서 사용하는 네트워킹 플러그인 입니다.
여러 노드로 이루어진 클러스터 구조를 가진 Kubernetes 특성 상 다른 노드에 존재하는 Pod 에 접근하기 어렵기 때문에, 이 플러그인을 통해 Pod가 어느 노드에 있든, 노드가 물리적으로 멀리 떨어져 있든 아무런 문제 없이 쉽게 접근 할 수 있게 합니다. CNI 자체는 표준 인터페이스 이므로 이를 구현 한 여러 플러그인이 존재합니다. 이 예제에서는 calico 를 사용 하겠습니다.
미리 확인할 점
각 노드끼리 Timezone이 일치하는지, 시간은 동기화 되어 있는지 확인합니다.
상술하였듯 kube-apiserver 는 HTTPS 를 통신에 사용합니다. 이 때문에 보안 인증서를 자동으로 발급받게 되는데, Master 와 Node 사이에 시간 차이가 있을 경우 Issue Date 검증 과정에 실패하여 Node Join 이 실패할 수 있습니다.
Docker 설치 (모든 노드)
Kubernetes 는 Docker, cri-o 등 여러 Runtime 과 같이 사용 할 수 있습니다. 여기서는 Docker 를 사용하겠습니다.
# 항상 root 유저로 작업합니다.
su
# Docker CE 설치
# 출처: https://kubernetes.io/ko/docs/setup/production-environment/container-runtimes
## Swap 끄기
swapoff -a
# 그리고 /etc/fstab 파일을 선호하는 편집기로 열어서 swap 영역을 지워줍니다.
# swap 이라고 적혀있는 라인을 지워주면 됩니다.
# 이 작업을 하지 않으면 재부팅시 swap 영역이 살아나 Kubernetes 실행에 실패합니다.
vim /etc/fstab
## 리포지터리 설정
### apt가 HTTPS 리포지터리를 사용할 수 있도록 해주는 패키지 설치
apt-get update && apt-get install -y \
apt-transport-https ca-certificates curl software-properties-common gnupg2
### Docker의 공식 GPG 키 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
### Docker apt 리포지터리 추가.
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
## Docker CE 설치.
apt-get update && apt-get install -y \
containerd.io=1.2.10-3 \
docker-ce=5:19.03.4~3-0~ubuntu-$(lsb_release -cs) \
docker-ce-cli=5:19.03.4~3-0~ubuntu-$(lsb_release -cs)
# 데몬 설정.
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
mkdir -p /etc/systemd/system/docker.service.d
# Docker 재시작.
systemctl daemon-reload
systemctl restart docker
kubeadm toolbox 설치 (모든 노드)
Kubernetes 설치를 위해 사용하는 kubeadm, Kubernetes 관리를 위해 사용하는 kubectl 등을 이용하기 위해 설치합니다.
# 항상 root 유저로 작업합니다.
su
# SELinux 기능이 켜져있는 리눅스 (CentOS 등)의 경우, 비활성화 합니다.
# 우분투의 경우, 별도로 설치하지 않았다면 넘어가셔도 좋습니다.
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
# 출처: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
# 네트워크 브릿지 기능을 켭니다.
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
# 레거시 iptables를 설치합니다.
apt-get install -y iptables arptables ebtables
# 레거시 iptables를 사용하도록 지정합니다.
update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
update-alternatives --set arptables /usr/sbin/arptables-legacy
update-alternatives --set ebtables /usr/sbin/ebtables-legacy
# kubeadm toolbox 를 설치합니다
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
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
Kubernetes 설치 (Master 와 Worker 노드의 설치 방법이 다릅니다)
### Master Node를 초기화 합니다.
# 항상 root 유저로 작업합니다.
su
# calico 에서 사용할 IP CIDR 를 지정하여 초기화 합니다.
# 기본값은 192.168.0.0/16 (192.168.0.1 ~ 192.168.255.254) 이나, 한국에서는 주로 ipTIME 라우터 등에서 사용되는 기본값이므로 IP 충돌 우려가 있어 변경합니다. 네트워크 상황에 따라 수정 해 주십시오.
# 작업은 최대 4분 까지 걸릴 수 있으며, 시스템 사양에 따라 다릅니다.
# 4분 이상 초기화에 시간이 걸린다면 오류가 발생합니다.
kubeadm init --pod-network-cidr=10.96.0.0/16
## 아래와 같은 로그가 출력되면 Master Node 의 초기화가 성공 한 것 입니다.
## 중간의 `To start using your cluster` 부분의 명령어를 Master Node 에서 입력하십시오.
## 이 명령어는 kubectl 을 이용해 Cluster 를 관리 하기 위해 필요합니다.
## 그 후, 맨 아래 kubeadm join 명령어를 각 노드에 root 유저 상태에서 입력하십시오.
##
# ~ 생략 ~
# Your 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/config
#
# You 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.0.218:6443 --token ttq3wo.s6n45xjqpjygx452 \
# --discovery-token-ca-cert-hash sha256:02d0425add47b5a409c39f7e36b6fe51496a47f5c1e56fefb5292bfe3b541945
#
# Kubernetes Admin 정보를 현재 유저 (root)에 적용합니다.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
### Worker Node를 초기화 합니다.
# 항상 root 유저로 작업합니다.
su
# Kubernetes Cluster 에 Worker Node를 Join 합니다.
# 토큰의 유효기간은 24시간 이므로 24시간 이후에 새 노드를 추가하시려면 Token을 신규 발급 하셔야 합니다.
kubeadm join 192.168.0.218:6443 --token ttq3wo.s6n45xjqpjygx452 \
--discovery-token-ca-cert-hash sha256:02d0425add47b5a409c39f7e36b6fe51496a47f5c1e56fefb5292bfe3b541945
## 아래와 같은 로그가 출력되면 Join 에 성공 한 것 입니다.
# ~ 생략 ~
# 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.
### Master Node 에서 작업 합니다.
# Worker Node Join 에 성공하였는지 Master Node 에서 확인합니다.
kubectl get nodes
## 아래와 같이 출력되면 정상적으로 Join 된 것입니다.
# NAME STATUS ROLES AGE VERSION
# kubernetes-218 NotReady master 10m v1.18.0
# kubernetes-219 NotReady <none> 4m14s v1.18.0
# kubernetes-220 NotReady <none> 4m10s v1.18.0
#
# 현재 이 상태에서는 CNI 플러그인이 설치되지 않아 노드끼리 통신이 불가능하여 CoreDNS 라는 Kubernetes Core Module 중 하나가 Running 상태로 진입하지 못하고 있을 것입니다.
# 이는 `kubectl get all --all-namespaces` 명령어와 `kubectl describe pod <Pod 명> -n <namespace>` 명령어로 확인할 수 있습니다.
# CNI 플러그인을 설치 하되, 아까 설정하였던 IP CIDR를 수정해야 한다는 점을 유념하여 작업합니다.
# 우선, CNI 플러그인인 Calico 의 설정 파일을 받아 수정합니다.
curl -O https://docs.projectcalico.org/manifests/calico.yaml
# 선호하는 편집기를 이용해 calico.yaml 을 열어 아래와 같이 수정합니다.
### calico.yaml
## 주석처리 되어 있는 '- name: CALICO_IPV4POOL_CIDR' 부분을 찾아 주석을 해제하고 값을 수정합니다.
# ~ 생략 ~
# The default IPv4 pool to create on startup if none exists. Pod IPs will be
# chosen from this range. Changing this value after installation will have
# no effect. This should fall within `--cluster-cidr`.
- name: CALICO_IPV4POOL_CIDR
value: "10.96.0.0/16"
# ~ 생략 ~
# 수정을 마치고 저장 한 후 Kubernetes Cluster 에 Deploy 합니다.
kubectl apply -f calico.yaml
# 정상적으로 Deploy 되었는지 확인 합니다.
kubectl get all --all-namespaces
## 아래와 같이 출력 로그에 calico 가 보인다면 Deploy 에 성공한 것입니다.
# NAMESPACE NAME READY STATUS RESTARTS AGE
# kube-system pod/calico-kube-controllers-5b8b769fcd-zclkf 0/1 Pending 0 32s
# kube-system pod/calico-node-h8krd 0/1 Init:0/3 0 33s
# kube-system pod/calico-node-js4nn 0/1 Init:0/3 0 33s
# kube-system pod/calico-node-wsdc7 0/1 Init:0/3 0 33s
# kube-system pod/coredns-66bff467f8-nb2x4 0/1 Pending 0 44m
# kube-system pod/coredns-66bff467f8-tbfsb 0/1 Pending 0 44m
# ~ 이하 생략 ~
## 다만 Init 상태나 Pending 상태라면 아직 초기화가 진행중 이므로 이 상태가 해소될 때까지 기다립니다. 몇 분에서 몇 십분 정도 걸릴 수 있습니다.
## 너무 오랜 시간 동안 상태에 변화가 없다면 `kubectl describe pod <Pod 명> -n <namespace>` 명령어로 원인을 찾아 해결을 시도하십시오.
# 노드의 Status 가 Ready 가 되었는지 확인합니다.
kubectl get nodes
# 클러스터의 API Endpoint 주소를 확인합니다.
kubectl cluster-info
축하 드립니다! Kubernetes Cluster 의 설치가 완료 되었습니다!
하지만 Kubernetes 의 정보를 확인하려면 CLI를 사용해야 하는 등 아직 불편한 점이 남아 있습니다. 그래서 Kubernetes Cluster 의 정보를 쉽게 확인할 수 있게 해주는 Kubernetes Dashboard를 설치 하도록 하겠습니다.
# Kubernetes Dashboard 를 설치합니다
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc7/aio/deploy/recommended.yaml
# Kubernetes Dashboard 에 접근하는 방법은 여러 방법이 있습니다. 하지만 명령을 입력한 상태에서 창을 닫지 않아야 한다던가, Kubernetes가 돌아가는 노드에서만 접근이 가능하다던가, 항상 포트를 개방해야 하는 문제가 있습니다.
# 이를 회피하기 위해 Kubernetes Cluster 의 API에 접근 할 수 있는 곳에서만, 권한을 가진 인증서를 설치 한 시스템에서만 접근이 가능하도록 설정 하겠습니다.
# 참고 https://crystalcube.co.kr/199
# 우선 Kubernetes 인증서와 Key 파일을 가져옵니다
grep 'client-certificate-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.crt
grep 'client-key-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.key
# 가져온 인증서와 Key 파일을 이용해 개인 인증서를 만듭니다
# 암호를 물어보는데, 인증서에 사용할 암호입니다. 비워두셔도 좋습니다.
openssl pkcs12 -export -clcerts -inkey kubecfg.key -in kubecfg.crt -out kubecfg.p12 -name "kubernetes-admin"
# Kubernetes 의 CA 인증서를 가져옵니다
cp /etc/kubernetes/pki/ca.crt ./
# 이제 kubecfg.p12 파일과 ca.crt 파일을 *내 컴퓨터*로 가져옵니다.
# 그리고 시스템에 설치합니다
certutil.exe -addstore "Root" ca.crt
certutil.exe -p <암호> -user -importPFX kubecfg.p12
# 그리고 웹 브라우저를 이용해 Kubernetes Dashboard 에 들어갑니다
# IP와 Port는 `kubectl cluster-info` 명령어를 통해 확인 할 수 있습니다
https://<IP>:<PORT>/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# 아래 이미지와 같은 창이 뜨면 성공입니다. 확인을 눌러주세요.

# 이제 Kubernetes Dashboard에 접근은 가능해 졌지만, 로그인 하여 상세 정보를 볼 수는 없습니다.
# 로그인을 하기 위해서는 kubecfg 파일 또는 Secret Token 이 필요합니다. 여기서는 Secret Token 을 생성하겠습니다.
# 이는 RBAC (Role-based Access Control) 이라는 Kubernetes 의 접근 제어 방식을 이용합니다.
# 쉽게 설명하자면,
# ---------------------------------------------------
# | 이 명령서를 가지고 있다면 이 행동을 할 수 있음 (Role) |
# | ↑ |
# | 어떤 사람이 어떤 명령서를 가질지 기록 (RoleBinding) |
# | ↓ |
# | 명령서를 가질 사람 (ServiceAccount) |
# ---------------------------------------------------
# 우선 ServiceAccount 을 생성합니다.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-dashboard-user
namespace: kube-system
EOF
# RoleBinding 을 생성합니다.
# Role은 기본값으로 존재하는 cluster-admin Role 을 사용하기 때문에 별도로 생성하지 않습니다.
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: kube-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kube-dashboard-user
namespace: kube-system
EOF
# ServiceAccount 의 토큰을 가져옵니다. 이 토큰은 잘 저장 해 둡니다.
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kube-dashboard-user | awk '{print $1}')
# 토큰으로 로그인 하면 완료됩니다.

이로서 Kubernetes Dashboard 의 설치가 완료 되었습니다. 다만 Kubernetes Dashboard 는 현재 존재하는 리소스 정보를 모니터링 하는데에는 좋으나, Kubernetes 의 성능 및 로그 분석에 사용하기는 어렵습니다. 이를 위해선 Prometheus Operator 또는 Elastic Cloud on Kubernetes (ECK) 의 도입을 고려해 보시기 바랍니다.
### Prometheus Operator 설치
# 많은 구성 요소의 조합으로 이루어진 Prometheus Operator 특성 상 직접 모든 구성 요소를 설치하고 설정하기 어려우니, Kubernetes의 Package Manager 인 Helm 을 통하여 한번에 설치를 하도록 하겠습니다.
# 우선 Helm 을 설치합니다
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
# Helm의 Chart Repository 를 추가합니다.
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
# monitoring 이라는 네임스페이스를 만들어 이 안에 Prometheus Operator를 설치 해 줍니다.
kubectl create monitoring
# helm install <instance 명> <helm chart> -n <namespace>
helm install pro-op stable/prometheus-operator -n monitoring
# 잘 Deploy 되었는지 확인합니다.
kubectl get all -n monitoring
# Prometheus Operator 의 Dashboard 인 Grafana를 외부에서 접근 할 수 있게 노출 합니다.
# 이 설정중에서 selector 의 값은 현재 동작중인 Grafana 의 Pod 명이 실행 환경마다 다르기 때문에 Metadata 에 지정한 값을 검색하여 맞는 Pod만을 서비스와 연결하기 위해 지정된 것입니다.
# 그렇기 때문에 helm 으로 설치 하였을 때 instance 값을 변경하였다면 이 곳에도 반영해야 합니다.
# ports 아래의 값은 각각 다음과 같습니다.
# - port: 클러스터 내에서 노출 할 포트
# - targetPort: 연결 될 Pod의 포트 (Grafana는 3000번 포트)
# - nodePort: 노드에서 외부에 노출 할 포트 (30000-32767 사이)
# nodePort 는 모든 노드에 대해서 적용되기 때문에 Kubernetes의 어떤 노드라도 해당 포트를 입력하면 Grafana에 접근할 수 있습니다.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: pro-op-grafana-service
namespace: monitoring
spec:
selector:
app.kubernetes.io/instance: pro-op
app.kubernetes.io/name: grafana
ports:
- port: 3000
protocol: TCP
targetPort: 3000
nodePort: 30000
type: NodePort
EOF
# 아래 명령어로도 같은 동작이 가능하지만, nodeport를 지정 할 수 없고 자동으로 할당 됩니다.
# 이 명령은 Deployment 의 값을 참조로 생성되기 때문에 Grafana 의 Web Port 인 3000 번 뿐만이 아니라 80번 포트도 개방됩니다.
# 위의 명령을 입력하였다면 아래 명령은 넘기십시오.
kubectl expose deployment pro-op-grafana --type=NodePort --name=pro-op-grafana-service -n monitoring
# 포트 확인
kubectl get svc pro-op-grafana-service -n monitoring
# 노드 IP (Master, Worker 아무거나 가능) 로 접속 (id: admin, pw: prom-operator)
http://192.168.0.230:30000/