kubeadm으로 단일 노드 Kubernetes 클러스터 만들기

essem
19 min readOct 21, 2019

--

2021–01–13 업데이트: Ubuntu 20.04, Kubernetes 1.23기준으로 업데이트

테스트 목적으로 Kubernetes 클러스터를 생성한다면 microk8s나 minikube 같은 편리한 방법들이 있다. 하지만 클라우드가 아닌 일반 Bare-metal 환경일지라도 확장성을 고려한다거나 좀 더 정밀하게 제어하고 싶다면 kubeadm으로 Kubernetes 클러스터를 생성할 수 있다.

OS 설치

여기서는 Ubuntu 20.04 LTS Server 버전을 사용하였다.

설치 문서에 보면 Swap 기능을 비활성화시키라고 한다. 다음과 같은 명령을 입력하자.

sudo swapoff -a

재부팅을 해도 계속 비활성화되도록 /etc/fstab 에서도 swap 관련 라인이 있다면 제거하자.

Docker 설치

여러 가지 컨테이너 런타임 중에 하나를 설치해야 하는데 가장 익숙한 Docker를 설치해보자. 이 문서를 따라서 다음 명령을 차례대로 수행한다.

sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
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

docker version 명령어나 docker container ls 명령 등으로 문제가 없는지 확인한다.

sudo docker version
sudo docker run hello-world
sudo docker image ls
sudo docker container ls

root 계정 외에도 docker 명령을 사용하도록 하려면 다음 명령을 입력한다.

sudo usermod -aG docker $USER

로그아웃 & 로그인 하면 적용된다.

kubeadm/kubelet/kubectl 설치

이 문서를 따라 다음 명령어를 차례대로 입력한다.

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Kubernetes 클러스터 생성

본격적인 클러스터 생성 전에 Network Addon을 선택해야 한다. 여기서는 Flannel을 선택하였다. 애드온 설명Flannel 설명을 보면 처음 kubeadm init을 할 때 반드시 --pod-network-cidr=10.244.0.0/16 옵션을 지정하라고 되어 있다.

또한 AWS 같은 환경에 설치하면서 외부에서 kubectl로 접속하고 싶은 경우 외부 IP 혹은 도메인 이름을 미리 --apiserver-cert-extra-sans=kube.example.org 처럼 옵션에 추가하는것이 편하다.

이제 준비가 끝났으니 설치 문서에 따라 클러스터를 생성해 보자.

sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-cert-extra-sans=kube.example.org

잠시 시간이 지나면 설치가 완료되고 다음과 같은 가이드 내용이 표시된다.

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
Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.confYou 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 172.31.26.245:6443 --token cml8w5.aem9su86pztp735y \
--discovery-token-ca-cert-hash sha256:15bd2983695a078b8c664e2aee482692da171881789682871f8bcd898c83e2d4

설명해주는 데로 자신의 홈디렉토리 아래 설정 파일을 복사한다.

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 pods --all-namespaces

다음과 같이 현재 생성된 모든 POD가 표시된다.

NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system coredns-64897985d-29kvh 0/1 Pending 0 67s
kube-system coredns-64897985d-d6ptk 0/1 Pending 0 67s
kube-system etcd-ip-172-31-26-245 1/1 Running 0 80s
kube-system kube-apiserver-ip-172-31-26-245 1/1 Running 0 80s
kube-system kube-controller-manager-ip-172-31-26-245 1/1 Running 0 80s
kube-system kube-proxy-52r7c 1/1 Running 0 67s
kube-system kube-scheduler-ip-172-31-26-245 1/1 Running 0 80s

coredns 는 생성이 완료되지 않고 Pending된 것을 볼 수 있다.

이제 다음 명령어로 Flannel Network Addon을 설치해 보자.

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

잠시 기다리면 다음과 같이 kube-flannel POD가 생성되고 corednsRunning 상태가 된다.

NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE
kube-system coredns-5644d7b6d9-c5mn2 1/1 Running 0 8m8s
kube-system coredns-5644d7b6d9-s2rwt 1/1 Running 0 8m8s
kube-system etcd-ubuntu 1/1 Running 0 7m1s
kube-system kube-apiserver-ubuntu 1/1 Running 0 7m1s
kube-system kube-controller-manager-ubuntu 1/1 Running 0 7m4s
kube-system kube-flannel-ds-amd64-hwdfq 1/1 Running 0 93s
kube-system kube-proxy-fsgmj 1/1 Running 0 8m8s
kube-system kube-scheduler-ubuntu 1/1 Running 0 7m25s

단일 노드 클러스터이기 때문에 이 노드에 일반 POD도 띄울 수 있게 다음 명령어를 입력한다.

kubectl taint nodes --all node-role.kubernetes.io/master-

이제 노드 외부에서도 kubectl 명령으로 접속할 수 있도록 .kube/config 파일을 외부에 복사해서 테스트 해보자. 만약 config 파일 안에 서버 주소가 사설 IP 였다면 --apiserver-cert-extra-sans 옵션으로 지정했던 DNS 주소로 교체한다.

Ingress 설치

설치

Bare-metal 용 문서를 따라 NGINX Ingress를 설치해 보자.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/baremetal/deploy.yaml

NodePort 사용

기본 설치는 NodePort를 사용하게 되어 있다. 다음 명령으로 몇 번 포트가 열려 있는지 확인한다.

kubectl -n ingress-nginx get svc

80, 443 포트가 외부 몇 번 포트로 맵핑되었는지 확인한다.

HostNetwork 사용

외부에서 80/443포트를 사용하여 접속하려면 이 부분에서 설명해 주는 것처럼 hostNetwork 옵션을 켜야 한다. 다음 명령어로 직접 수정해 보자.

kubectl edit deploy/nginx-ingress-controller -n ingress-nginx

다음과 같이 파일을 편집한다.

        app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
hostNetwork: true
containers:
- args:

브라우저나 curl 을 사용해 80포트에 접속되는지 확인해 본다.

Dashboard 설치

설치

이 문서를 따라 다음 명령어를 입력하여 Dashboard를 설치한다.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml

테스트 접속

로컬 머신이 kubectl로 접속 가능한 상태라면 다음 명령으로 프록시를 실행한다.

kubectl proxy

그리고 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 주소로 접속하면 대시보드 로그인 화면을 볼 수 있다.

외부 오픈

외부 접속은 https를 통해서만 가능하다. SSL 인증서를 준비하고 다음 명령어로 등록한다.

kubectl create secret tls example.com --key privkey.pem --cert fullchain.pem -n kubernetes-dashboard

다음과 같은 manifest 파일을 생성 후 적용한다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/server-snippet: |
proxy_ssl_verify off;
spec:
tls:
- hosts:
- kube.example.com
secretName: example.com
rules:
- host: kube.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443

계정 생성

이 문서의 내용처럼 접속을 위한 토큰을 얻기 위해 관리자 계정을 만들어 보자.

apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard

위 내용을 파일로 만들어 적용한 후 다음 명령어로 토큰을 확인하자.

kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')

브라우저로 접속하여 토큰으로 로그인이 잘 되는지 확인한다.

Metric Server 설치

노드나 파드들의 CPU, 메모리 사용량 등을 확인할 수 있도록 Metric Server를 설치한다.

kubelet이 공식 인증서를 사용하는 상태가 아니기 때문에 manifest를 바로 적용하지 말고 다운 받아서 수정하다.

curl -LO https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

파일을 열고 아래 부분을 찾아서 --kubelet-insecure-tls옵션을 추가한다.

spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls

수정한 파일을 적용한다.

kubectl apply -f components.yaml

설치하고 시간이 조금 지나면 kubectl top node 등을 사용할 수 있고 대시보드에도 정보가 표시된다.

Rook/Ceph 설치

Persistent Volume을 사용하기 위해서 Rook를 설치해 보자. 1.3 버전 부터는 일반 디렉토리를 사용하지 못 하고 포멧되지 않은 디스크나 파티션을 준비해야 한다.

이 문서에 나오는 것처럼 필요한 manifest들을 모두 적용한다. 단 cluster.yaml을 최소 3개의 노드를 필요로 하므로 테스트 용도인 cluster-test.yaml을 사용하자.

git clone --single-branch --branch v1.8.2 https://github.com/rook/rook.git
cd rook/deploy/examples
kubectl create -f crds.yaml -f common.yaml -f operator.yaml
kubectl create -f cluster-test.yaml
kubectl create -f csi/rbd/storageclass-test.yaml

Dashboard에 들어가서 rook-ceph 네임스페이스에 있는 POD가 생성되길 기다린다. (처음에 operator POD가 생성되고 기다리면 대략 8개 정도의 POD가 생성된다.) 생성이 완료되면 PV를 사용하는 예제를 적용해 보자.

kubectl apply -f mysql.yaml

PV를 생성할 때 매번 storageclassrook-ceph-block로 지정하는것은 불편하므로 다음과 같이 설정을 수정해 보자.

kubectl patch storageclass rook-ceph-block -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

더 자세한 Ceph 상태를 보고 싶다면 이 문서를 따라 Ceph Dashboard를 설치해 보자.

Docker Registry 설치

만약 사용할 수 있는 외부 Docker Registry가 없다면 Kubernetes 안에 간단하게 하나 설치할 수도 있다. 다음 명령어로 Namespace를 만들고 위에서처럼 인증서를 등록한다.

kubectl create ns registry
kubectl create secret tls example.com --key privkey.pem --cert fullchain.pem -n registry

다음과 같은 manifeset 파일을 생성하고 적용해 보자. (Persistent Volume을 사용하지 않았으므로 POD가 재시작 되면 업로드 되었던 이미지는 삭제된다)

apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: registry
spec:
selector:
matchLabels:
app: registry
template:
metadata:
labels:
app: registry
spec:
containers:
- image: registry:2.7
name: registry
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: registry
namespace: registry
spec:
ports:
- port: 5000
targetPort: 5000
selector:
app: registry
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: registry
namespace: registry
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
spec:
tls:
- hosts:
- registry.example.com
secretName: example.com
rules:
- host: registry.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: registry
port:
number: 5000

위 내용 중 proxy-body-size annotation은 업로드 크기 제한을 해제하기 위함이다.

다음과 같이 curl을 사용해 접속이 잘 되는지 확인해 보자.

curl https://registry.example.com/v2/

Registry가 잘 동작하는지 확인하기 위해 커스텀 이미지를 만들어 등록해 보자. Dockerfile 파일을 만들고 다음 내용을 입력한다.

FROM nginx

이미지를 생성해보자.

docker build -t registry.example.com/my-nginx .

생성한 이미지를 Registry에 등록한다.

docker push registry.example.com/my-nginx

다음 명령어로 잘 등록되었는지 확인해 보자.

curl https://registry.example.com/v2/_catalog

--

--

essem
essem

Written by essem

취미 프로젝트와 개발 일기

Responses (1)