반응형

 

작성일: 2024년 3월 11일

 

여담 1:
이전에는 kubeadm 명령 도구를 이용해서 kubernetes cluster를 구축했었는데,
오늘은 만사가 귀찮아서 kubespray를 이용해서 kubernetes cluster를 구축해보기로 했다.
kubespray를 이용하면 containerd, CNI, MetalLB 같은 패키지를 직접 설치하지 않아도 되니까 시간이 많이 절약된다.
Ubuntu 22.04 OS가 설치되어 있다고 가정하면, 대략 아래 절차를 따라했을 때 30분 정도 시간이 소요되는 것 같다.

 

여담 2:
아래 작업을 3가지 CPU 스펙으로 진행해봤는데, 저사양 CPU에서는 kubespray를 수행하는 중간에 실패한다.
특히 kubespray를 실행하는 노드(예를 들어 master-a 노드)의 CPU 스펙이 중요하다.
내가 테스트했던 CPU 스펙과 성공/실패 여부를 정리해보면;
CPU 스펙 Kubespray 동작 결과
Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz (16 쓰레드) K8S 클러스터 구성 성공
Intel(R) Xeon(R) E-2124G CPU @ 3.40GHz (4 쓰레드) K8S 클러스터 구성 실패
Intel(R) Core(TM) i7-11700 @ 2.50GHz (16 쓰레드) K8S 클러스터 구성 성공

 

.

.

.

 

Kubernetes cluster 구축 환경 및 Network 구성 정보  [ K8S 클러스터 설계 ]

주의:
아래 예시는 kubespray를 실행할 장비를 별도로 만들지 않았는데, 금전적으로 여유가 있거나 이미 HW가 넉넉히 있는 경우라면
kubespray 실행용 장비를 따로 가지고 있으면 좋다.
Master node: 3개 (Ubuntu 22.04)
  - CPU: 2 core
  - MEM: 8GB
  - OS: Ubuntu 22.04

Worker node: 3개 (Ubuntu 22.04)
  - CPU: 4 core
  - MEM: 16GB
  - OS: Ubuntu 22.04

##
## 참고: CPU, MEM가 적어서 master-a 장비에서 kubespray를 실행하겠다.  
##      만약, HW 제원이 넉넉하다면 kubespray를 실행할 전용 장비가 있으면 좋다.  
##

Network 구성:
  - master-a: 10.10.1.11
  - master-b: 10.10.1.12
  - master-c: 10.10.1.13
  - worker-a: 10.10.1.101
  - worker-b: 10.10.1.102
  - worker-c: 10.10.1.103

 

 

OS & 접속 계정 설정

Master node 및 Worker node로 사용할 6개의 Ubuntu 22.04 서버를 설치한다.

아래 2가지 중에서 편한 방법으로 구성하면 된다.
  - 물리 장비에 OS를 설치하거나 VM에 OS를 설치. (실제로 상용 서비스를 할 것이라면, 이 방법을 권장)
  - 단순히 학습을 목적으로 Kubernetes cluster를 설치한다면, VM에 OS를 설치하는 것을 권장.

 

[ 작업 대상 장비: master-a 노드 ]
'master-a' 에서 아래와 같이 Private key, Public key를 생성하고
이렇게 생성한 Public key를 모든 kubernetes node에 복사한다.

## 이 명령은 master-a 노드에서 수행해야 한다.

$ ssh-keygen 

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
... 생략 ...

$ ssh-copy-id  sejong@master-a
...

$ ssh-copy-id  sejong@master-b
...

$ ssh-copy-id  sejong@master-c
...

$ ssh-copy-id  sejong@worker-a
...

$ ssh-copy-id  sejong@worker-b
...

$ ssh-copy-id  sejong@worker-c
...

 

 

[ 작업 대상 장비: 모든 노드 ]

Password 없이 Root 권한을 얻기 위해 각 노드에서 아래와 같이 작업을 수행한다. (Ansible이 동작하기 위해 필요한 작업)
$ sudo -
$ echo "sejong ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

 

 

[ 작업 대상 장비: 모든 노드 ]

각 노드의 Firewall 기능이 disable되어 있는지 확인한다.
$ ufw status
Status: inactive
 
 

[ 작업 대상 장비: 모든 노드 ]

각 노드의 /etc/hosts 파일에 아래의 내용을 추가한다.
$ cat /etc/hosts

... 중간 생략 ...

10.10.1.11 master-a
10.10.1.12 master-b
10.10.1.13 master-c

10.10.1.101 worker-a
10.10.1.102 worker-b
10.10.1.103 worker-c

... 중간 생략 ...
 

Kubespray 실행을 위한 준비 작업

[ 작업 대상 장비: master-a 노드 ]

$ sudo -s

$ apt update

$ apt install python3-pip

$ git clone https://github.com/kubernetes-sigs/kubespray.git

$ cd kubespray

$ pip3 install -r requirements.txt

$ cd inventory

$ cp -Rp sample/ mycluster


$ cat hosts.yaml

all:
  hosts:
    master-a:
      ansible_host: master-a
    master-b:
      ansible_host: master-b
    master-c:
      ansible_host: master-c
    worker-a:
      ansible_host: worker-a
    worker-b:
      ansible_host: worker-b
    worker-c:
      ansible_host: worker-c      
  children:
    kube_control_plane:
      hosts:
        master-a:
        master-b:
        master-c:
    kube_node:
      hosts:
        worker-a:
        worker-b:
        worker-c:
    etcd:
      hosts:
        master-a:
        master-b:
        master-c:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
        calico_rr:
    calico_rr:
      hosts: {}


$ cat  ./group_vars/k8s_cluster/k8s-cluster.yml

... 중간 생략 ...

## Kube cluster를 구축하고 나서, MetalLB를 설치할 때 strict_arp 설정이 필요하다.
## 따라서 MetalLB를 설치하려는 경우라면, 아래 설정을 꼭 'true'로 변경해야 한다.
kube_proxy_strict_arp: true
... 중간 생략 ...
container_manager: containerd
... 중간 생략 ...
kubernetes_audit: true
... 중간 생략 ...

$

 

 

Kubespray 실행하기 (Kubernetes Cluster 생성하기)

[ 작업 대상 장비: master-a 노드 ]

##
## kubespray 소스 폴더의 최상위 폴더로 이동 후 아래 명령을 실행
## (참고) root 계정, 일반 계정 어떤 것으로 ansible-playbook 명령을 실행해도 괜찮다.
##       단, 위에서 public key를 모든 노드에 복사(ssh-copy-id)할 때 어떤 계정의 public key를
##       사용했느냐에 따라 동일한 계정으로 아래 명령을 수행하면 된다.
##

$ ansible-playbook -v -i inventory/mycluster/hosts.yaml --become --become-user=root cluster.yml

... 중간 생략 ...

TASK [network_plugin/calico : Set calico_pool_conf] *****************************************************************************************************************************************
ok: [master-a] => {"ansible_facts": {"calico_pool_conf": {"apiVersion": "projectcalico.org/v3", "kind": "IPPool", "metadata": {"creationTimestamp": "2024-03-08T15:06:52Z", "name": "default-pool", "resourceVersion": "4122", "uid": "ffb5f2e0-85f1-4f3b-9c9d-55fe86be8c97"}, "spec": {"allowedUses": ["Workload", "Tunnel"], "blockSize": 26, "cidr": "10.233.64.0/18", "ipipMode": "Never", "natOutgoing": true, "nodeSelector": "all()", "vxlanMode": "Always"}}}, "changed": false}
Friday 08 March 2024  15:29:07 +0000 (0:00:00.054)       0:19:02.007 **********

TASK [network_plugin/calico : Check if inventory match current cluster configuration] *******************************************************************************************************
ok: [master-a] => {
    "changed": false,
    "msg": "All assertions passed"
}
Friday 08 March 2024  15:29:07 +0000 (0:00:00.060)       0:19:02.067 **********
Friday 08 March 2024  15:29:07 +0000 (0:00:00.037)       0:19:02.104 **********
Friday 08 March 2024  15:29:07 +0000 (0:00:00.045)       0:19:02.150 **********

PLAY RECAP **********************************************************************************************************************************************************************************
master-a                   : ok=676  changed=35   unreachable=0    failed=0    skipped=1164 rescued=0    ignored=2
master-b                   : ok=580  changed=25   unreachable=0    failed=0    skipped=1046 rescued=0    ignored=1
master-c                   : ok=582  changed=25   unreachable=0    failed=0    skipped=1044 rescued=0    ignored=1
worker-a                   : ok=419  changed=8    unreachable=0    failed=0    skipped=695  rescued=0    ignored=1
worker-b                   : ok=419  changed=8    unreachable=0    failed=0    skipped=691  rescued=0    ignored=1
worker-c                   : ok=419  changed=8    unreachable=0    failed=0    skipped=691  rescued=0    ignored=1

Friday 08 March 2024  15:29:07 +0000 (0:00:00.309)       0:19:02.459 **********
===============================================================================
kubernetes/kubeadm : Restart all kube-proxy pods to ensure that they load the new configmap ----------------------------------------------------------------------------------------- 71.14s
network_plugin/calico : Check if calico ready --------------------------------------------------------------------------------------------------------------------------------------- 69.41s
kubernetes-apps/ansible : Kubernetes Apps | Start Resources ------------------------------------------------------------------------------------------------------------------------- 65.75s
network_plugin/calico : Calico | Create Calico Kubernetes datastore resources ------------------------------------------------------------------------------------------------------- 41.63s
kubernetes/control-plane : Upload certificates so they are fresh and not expired ---------------------------------------------------------------------------------------------------- 35.07s
etcd : Configure | Wait for etcd cluster to be healthy ------------------------------------------------------------------------------------------------------------------------------ 29.78s
kubernetes/preinstall : Preinstall | restart kube-apiserver crio/containerd --------------------------------------------------------------------------------------------------------- 27.93s
policy_controller/calico : Start of Calico kube controllers ------------------------------------------------------------------------------------------------------------------------- 26.35s
etcd : Reload etcd ------------------------------------------------------------------------------------------------------------------------------------------------------------------ 21.45s
kubernetes/preinstall : Preinstall | restart kube-controller-manager crio/containerd ------------------------------------------------------------------------------------------------ 20.39s
network_plugin/calico : Calico | Configure calico network pool ---------------------------------------------------------------------------------------------------------------------- 15.41s
network_plugin/calico : Start Calico resources -------------------------------------------------------------------------------------------------------------------------------------- 14.03s
kubernetes/control-plane : Create kubeadm token for joining nodes with 24h expiration (default) ------------------------------------------------------------------------------------- 13.13s
etcd : Wait for etcd up ------------------------------------------------------------------------------------------------------------------------------------------------------------- 12.82s
container-engine/runc : Download_file | Download item ------------------------------------------------------------------------------------------------------------------------------- 12.65s
container-engine/crictl : Download_file | Download item ----------------------------------------------------------------------------------------------------------------------------- 12.60s
container-engine/containerd : Download_file | Download item ------------------------------------------------------------------------------------------------------------------------- 12.17s
kubernetes/control-plane : Check which kube-control nodes are already members of the cluster ---------------------------------------------------------------------------------------- 11.68s
container-engine/nerdctl : Download_file | Download item ---------------------------------------------------------------------------------------------------------------------------- 11.57s
etcd : Backup etcd v2 data ---------------------------------------------------------------------------------------------------------------------------------------------------------- 11.29s

$

 

[ 참고 ]
가끔 위 ansible-playbook 명령이 실패하는 경우가 있다.
그럴 때는 동일하게 한번 더 실행하면 해결되는 경우가 많다. (특히, CPU와 Memory 성능이 낮은 경우)

 

위 Ansible playbook이 정상적으로 수행되었다면,
아래와 같이 root 계정 권한으로 kubectl 명령을 수행해본다.

 

$ sudo -s

$ kubectl get node -o wide

NAME       STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
master-a   Ready    control-plane   35m   v1.29.2   172.17.1.11    <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13
master-b   Ready    control-plane   34m   v1.29.2   172.17.1.12    <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13
master-c   Ready    control-plane   33m   v1.29.2   172.17.1.13    <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13
worker-a   Ready    <none>          31m   v1.29.2   172.17.1.101   <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13
worker-b   Ready    <none>          31m   v1.29.2   172.17.1.102   <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13
worker-c   Ready    <none>          31m   v1.29.2   172.17.1.103   <none>        Ubuntu 22.04.4 LTS   5.15.0-100-generic   containerd://1.7.13


$ kubectl get pod -A

NAMESPACE     NAME                                      READY   STATUS              RESTARTS         AGE
kube-system   calico-kube-controllers-648dffd99-l9pnt   1/1     Running             0                12m
kube-system   calico-node-4q5q9                         1/1     Running             11 (18m ago)     31m
kube-system   calico-node-98pdw                         1/1     Running             14 (5m52s ago)   31m
kube-system   calico-node-nrn9h                         1/1     Running             12 (7m57s ago)   31m
kube-system   calico-node-wgpvw                         1/1     Running             11 (18m ago)     31m
kube-system   coredns-69db55dd76-zkfqn                  1/1     Running             0                10m
kube-system   dns-autoscaler-6f4b597d8c-rt9nk           1/1     Running             0                10m
kube-system   kube-apiserver-master-a                   1/1     Running             4 (18m ago)      37m
kube-system   kube-apiserver-master-b                   1/1     Running             4 (18m ago)      36m
kube-system   kube-apiserver-master-c                   1/1     Running             4 (16m ago)      35m
kube-system   kube-controller-manager-master-a          1/1     Running             8 (6m27s ago)    37m
kube-system   kube-controller-manager-master-c          1/1     Running             10 (7m48s ago)   35m
kube-system   kube-proxy-hgt5x                          1/1     Running             0                15m
kube-system   kube-proxy-j26c8                          1/1     Running             0                15m
kube-system   kube-proxy-ldlb7                          1/1     Running             0                13m
kube-system   kube-proxy-trhgl                          1/1     Running             0                15m
kube-system   kube-proxy-vh6qt                          1/1     Running             0                15m
kube-system   kube-proxy-wv48f                          1/1     Running             0                13m
kube-system   kube-scheduler-master-a                   1/1     Running             7 (10m ago)      37m
kube-system   kube-scheduler-master-b                   1/1     Running             7 (6m15s ago)    36m
kube-system   kube-scheduler-master-c                   1/1     Running             7 (11m ago)      35m
kube-system   nginx-proxy-worker-a                      1/1     Running             0                34m
kube-system   nginx-proxy-worker-b                      1/1     Running             0                34m
kube-system   nginx-proxy-worker-c                      1/1     Running             0                32m
kube-system   nodelocaldns-42f6z                        1/1     Running             0                10m
kube-system   nodelocaldns-5nqjw                        1/1     Running             0                10m
kube-system   nodelocaldns-dsg5w                        1/1     Running             0                10m
kube-system   nodelocaldns-dz9tm                        1/1     Running             0                10m
kube-system   nodelocaldns-mxjxz                        1/1     Running             0                10m
kube-system   nodelocaldns-zqb6h                        1/1     Running             0                10m

$ kubectl get svc -A

NAMESPACE     NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       kubernetes   ClusterIP   10.233.0.1   <none>        443/TCP                  37m
kube-system   coredns      ClusterIP   10.233.0.3   <none>        53/UDP,53/TCP,9153/TCP   12m
$

 

 

 

Kubespray ansible playbook은 11분 42초가 소요되었는데,

조금 더 최신 컴퓨터로 ansible playbook을 실행했다면 10분 이내에 모든 cluster 구축이 완료되지 않을까...

 

[ 참고: 이번 테스트에 사용했던 HW 1 사양 ]

$ lshw -short

H/W path           Device           Class          Description
==============================================================
... 중간 생략 ...
/0/3b                               memory         64GiB System Memory
/0/52                               processor      11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz
/0/100/1/0                          display        GA102 [GeForce RTX 3080 Ti]
/0/100/6/0         /dev/nvme0       storage        Samsung SSD 980 PRO 1TB
... 중간 생략 ...

 

[ 참고: 이번 테스트에 사용했던 HW 2 사양 ]

$  lshw -short

H/W path          Device     Class          Description
=======================================================
/0/3a                      memory         64GiB System Memory
/0/3a/0                    memory         32GiB SODIMM DDR4 Synchronous 3200 MHz (0.3 ns)
/0/3a/2                    memory         32GiB SODIMM DDR4 Synchronous 3200 MHz (0.3 ns)
/0/48                      processor      Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz
... 중간 생략 ...

 


 

예제 Pod, Service를 배포하기

아래 글에 간단하게 Pod, Service 리소스를 생성하는 예제 코드 및 명령이 있다.

https://andrewpage.tistory.com/68

 

 


 

 

 

Troubleshooting, Q&A

 

Kubernetes cluster 초기화 하기

$ ansible-playbook -v -i inventory/mycluster/hosts.yaml --become --become-user=root reset.yml

 

Kubernetes cluster 를 upgrade 하기

Upgrade 시나리오가 다양하기 때문에 아래 글을 자세히 읽고 Upgrade를 수행하는 것을 권장한다.

https://github.com/kubernetes-sigs/kubespray/blob/master/docs/upgrades.md

 

Kubernetes node를 추가, 대체하기 (Join new node, Replace node)

Kubernetes node에 대한 operation이 다양하기 때문에 아래 글을 자세히 읽고 node 관리하는 것을 권장한다.

https://github.com/kubernetes-sigs/kubespray/blob/master/docs/nodes.md

 

 

 

 

 

블로그 작성자: sejong.jeonjo@gmail.com

 

 

반응형

Kubernetes 기술 문서에 대한 한글화가 참 빠르다.

2018년에 처음 Kubernetes 접할 때와 비교해보면, 4년 사이에 빠르게 한글 문서가 잘 구축되었다.

 

원문(영어 문서)로 읽으면 더 좋겠지만, 시간이 없고 급할 때는 다른 사람이 만들어준 한글 문서가 나의 시간을 많이 아껴준다 :)

 

 

쿠버네티스 문서

쿠버네티스는 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하기 위한 오픈소스 컨테이너 오케스트레이션 엔진이다. 오픈소스 프로젝트는 Cloud Native Computing Foundation에서 주관한다.

kubernetes.io

 

반응형

Kubernetes의 Pod Scheduling에 관한 공부를 하다보면,

  • Node Label을 이용한 NodeSelector, Node Affinity
  • Node Taint, Tolerlation

가 꼭 등장한다.

위 2가지를 따로 따로 공부하면, 그렇구나 라고 이해가 되다가 

이 2가지를 같이 놓고 생각해보면, 비슷한 기능을 하는 것 같은데 왜 이렇게 2가지 모두 있는거지 라는 질문이 자연스레 떠오른다.

 

아래 블로그가 이런 질문에 대한 답을 잘 하고 있다.

 

https://fisache.tistory.com/19

 

CKA 준비 (7) - Scheduling 2 (Node Selector, Node Affinity)

본 포스팅은 Udemy Certificated Kubernetes Administrator 강좌를 공부한 내용입니다. Certified Kubernetes Administrator (CKA) Practice Exam Tests Prepare for the Certified Kubernetes Administrators C..

fisache.tistory.com

 

 

Node Taint 및 Toleration에 대해 기초부터 알고 싶다면, 아래의 Kubernetes Web Docs를 보면 좋다.

 

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/taint-and-toleration/

 

테인트(Taints)와 톨러레이션(Tolerations)

노드 어피니티는 노드 셋을 (기본 설정 또는 어려운 요구 사항으로) 끌어들이는 파드의 속성이다. 테인트 는 그 반대로, 노드가 파드 셋을 제외할 수 있다. 톨러레이션 은 파드에 적용되며, 파드

kubernetes.io

 

# TODO

위 Web Docs에 TolerationSeconds에 관한 설명이 나오는데, 

내 생각에는 Node 장애가 발생했을 때, 빠르게 Pod가 장애가 발생한 Node에서 종료 처리되고 다른 Node로 스케쥴링되게 하려면, 300초로 설정된 값을 1초로 줄이면 되지 않을까하는 상상을 해본다. 

시간 여유가 있을 때, 한번 1초로 변경하고 Node를 다운시켜서 테스트해봐야겠다.

 

참고:
쿠버네티스는 사용자나 컨트롤러에서 명시적으로 설정하지 않았다면, 자동으로 node.kubernetes.io/not-ready 와 node.kubernetes.io/unreachable 에 대해 tolerationSeconds=300 으로 톨러레이션을 추가한다.
자동으로 추가된 이 톨러레이션은 이러한 문제 중 하나가 감지된 후 5분 동안 파드가 노드에 바인딩된 상태를 유지함을 의미한다.

 

반응형

 

2019년 3월에 작성된 Web Docs이지만, 오늘(2021년 11월) 따라서 수행해봤는데 잘 동작한다.

 

 

https://kubernetes.io/blog/2019/03/15/kubernetes-setup-using-ansible-and-vagrant/

 

Kubernetes Setup Using Ansible and Vagrant

Author: Naresh L J (Infosys) Objective This blog post describes the steps required to setup a multi node Kubernetes cluster for development purposes. This setup provides a production-like cluster that can be setup on your local machine. Why do we require m

kubernetes.io

 

 

위 블로그를 읽으면서, 나한테 필요한 만큼 playbook을 다시 작성했다.

아래 Playbook의 내용을 간략하게 설명하면,

 

  1. Kubernetes Master Node에서  `kubeadm token create --print-join-command`를 수행하여 새로운 Node Join을 위한 `kubeadm join ....` 명령행을 생성하고,
    이렇게 얻은 kubeadm 명령행을 'join-command' 스크립트 파일에 Dump한다.
  2. 바로 위에서 얻은 'join-command'를 새롭게 추가할 Worker Node의 /tmp/join-command.sh 경로에 복사하고 실행(Run)한다.
    그러면 새로운 Worker Node가 Master Node에 추가(Join)될 것이다.

 

단, 아래 Playbook YAML 파일에서 inventory(즉, hosts를 정의한 항목)을 생략했으니까 그 부분을 감안하고 보면 좋을 듯~~~

 

 

## 위 Web docs에서 중요한 부분만 발췌했다.
## 원문 그대로는 아니고, 내가 필요한 만큼 각색했다.  

## Filename: my-playbook.yaml

- name: Joining Job Step 1 on Master node
  hosts: kubemaster
  tasks:
    - name: Generate join command
      command: kubeadm token create --print-join-command
      register: join_command
    - name: Copy join command to local file
      local_action: copy content="{{ join_command.stdout_lines[0] }}" dest="./join-command"

- name: Joining Job Step 2 on Worker node
  hosts: kubeworker
  tasks:
    - name: Copy the join command to server location
      copy: src=join-command dest=/tmp/join-command.sh mode=0777
    - name: Join the node to cluster
      command: sh /tmp/join-command.sh

 

반응형

 


 

만약, Kubernetes cluster network 외부에서 kubernetes service에 access한다고 가정하면

아래와 같이 kubectl port-forward 명령으로 외부 IP traffic을 허용할 수 있다.

 

 

Example: Jenkins Web Dashboard 접속한다고 가정하고 테스트해보기

  • 구성 예시:
    • kubernetes service의 port는 8080
    • Web browser로 접속할 port는 8180

 

 

 

 

즉, 외부에서 8180으로 접속시도하면, 8080으로 port forward하도록 하려면, 아래와 같이 명령을 수행하면 된다.

 

##
## --addresss=0.0.0.0은 모든 IP Address를 수용하겠다는 뜻이다.
##
## 8180:8080은 Kubernetes Cluster 외부의 Client가 8180 포트로 접속 요청하면
## Kubernetes Cluster 내부의 8080 서비스 포트로 Forward해주겠다는 뜻이다.
##

$ kubectl  port-forward  --address=0.0.0.0 \
     $(kubectl get pod -l app.kubernetes.io/name=jenkins -o jsonpath='{.items[0].metadata.name}') \
     8180:8080

 

위와 같이 설정하고 Web browser에서 bastion node로 접속 시도한다.  (아래 예제 명령처럼 접속한다.)

 

$  curl  http://bastion.my-kube.node:8180/

 

 

위 예시는 Pod Name을 이용한 것이고, Service 이름을 이용해서도 port-forward 설정할 수 있다. (아래 명령을 참고)

##
## Cluster 밖의 Client가 9999 port 번호로 요청하면,
## 80 port 번호로 바꾸어서 almighty 네임스페이스의 almighty 서비스로 forwarding한다.
##

$ kubectl port-forward --address=0.0.0.0 -n almighty service/almighty 9999:80
Forwarding from 0.0.0.0:9999 -> 8080
Handling connection for 9999
...

 

 

 


참고:
kubectl port-forward 명령을 수행한 node가 bastion이라면, web browser가 접속할 Server(Target) 주소도 bastion의 IP address이어야 한다.

 

 

게시물 작성자: sejong.jeonjo@gmail.com

 

 

반응형

Kubernetes cluster(쿠버네티스 클러스터)를 구축하는 것을 도와주는 여러 도구(kubeadm, kubespray, kops)가 있다.

이 문서에는 그런 kubernetes cluster 구축 도구 중에서 kubeadm을 이용하여 kubernetes cluster를 구축하는 방법을 설명한다.

참고로, 이 문서를 작성하는 시점은 2021년 6월 30일이고, 오늘 Kubernetes cluster를 구축하면서 이 문서에 그 구축 과정을 기록한 것이다.

 

Kubernetes Cluster 구축을 위한 계획하기

CentOS 7.9를 설치한 Master node와 Worker node를 준비한다.

Master Node: 1개 (master-0)

Worker Node: 2개 (worker-0, worker-1)

 

 

Master Node와 Worker Node 준비 작업

master node와 worker node 모두에서 아래의 작업을 수행한다.

kubernetes는 iptables를 이용하여 pod간 통신을 가능하게 한다. 따라서 iptables가 정상 동작하도록 하기 위해 아래와 같이 설정한다.

$  sudo modprobe br_netfilter

$  lsmod | grep br_netfilter
br_netfilter           22256  0
bridge                151336  1 br_netfilter

$  cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

$  cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
$  sudo sysctl --system

 

모든 master node, worker node에서 swap 영역을 비활성화한다.

$  sudo sed -i '/swap/d' /etc/fstab
$  sudo swapoff -a
$  free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        1.0G         13G         13M        925M         14G
Swap:            0B          0B          0B
$

 

방화벽(Firewalld)를 비활성화한다.

(원칙은 kubernetes가 사용하는 service port만 allow 설정해야 하지만, 여기서는 간단하게 firewalld를 종료하는 것으로 하겠다)

$  systemctl stop firewalld
$  systemctl disable firewalld

 

Docker Engine 설치 (Installing Container Runtime)

container runtime에는 containerd, CRI-O, Docker 등 다양한 SW가 있지만, 이 문서에서는 Docker 제품을 설치한다.

참고: https://kubernetes.io/docs/setup/production-environment/container-runtimes/#docker

 

Old version의 Docker engine을 제거한다.

$  sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

 

Docker 패키지가 있는 Yum Repository를 추가한다.

 $  sudo yum install -y yum-utils
 
 $  sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

 

Docker engine을 설치하고, 기동한다.

$  sudo yum install -y docker-ce docker-ce-cli containerd.io

$  sudo systemctl start docker

# 아래와 같이 'hello-world' container image를 다운로드해서 docker engine이 정상 동작하는지 확인한다.
$  sudo docker run hello-world

 

Container의 Cgroups에 관한 설정 내용을 추가한다.

$  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", 
# 만약 Private container image registry으로부터 `docker image pull`을 하고 싶다면,
# 아래와 같이 'insecure-registries' 항목에 private container image registry의 주소를 추가한다.
# 이렇게 하면, 별도의 Certificate 없이 docker client가 container image registry에서
# container image를 pulling할 수 있다.
  "insecure-registries": ["10.10.3.33:8080", "my-harbor.sejong.space:8080"]
}
EOF

 

Docker engine을 재시작한다.

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

 

여기까지 설명이  master node, worker node에서 준비해야 할 작업이다.

이 다음 설명부터 실제 kubernetes cluster를 구축하기 위한 작업이다.

 

 


 

kubeadm, kubelet, kubectl 설치

kubeadm: kubernetes cluster를 구축하기 위한 명령 도구

kubelet: master node, worker node에서 데몬 프로세스로 기동되어 있으면서, container와 pod를 생성/삭제/상태를 감시한다.

kubectl: 사용자가 kubernetes cluster에게 작업 요청하기 위한 명령 도구 (예를 들어, 'pod를 생성해달라!'  'pod의 개수를 늘려달라!' 같은 사용자 명령을 kunernetes API server에게 전달)

 

아래의 명령을 따라 하여 kubeadm, kubelet, kubectl 명령 도구를 설치한다. (모든 장비에서 수행해야 함)

# 아래 명령을 모든 'Master node, Worker node'에서 수행해야 한다.

$  cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# Set SELinux in permissive mode (effectively disabling it)
$  sudo setenforce 0

$  sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

$  sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

$  sudo systemctl enable --now kubelet

 

Kubernetes Cluster 생성하기

아래 명령을 master-0 node에서 수행한다.

# 반드시 `master-0` node에서만 수행해야 한다.
$  kubeadm init --pod-network-cidr=10.244.0.0/16
...
...

# 위 명령은 2분 ~ 3분 정도 소요된다.
# 꽤 오랜 시간이 걸리니까, 인내심을 가지고 기다려야 한다.
# 
# 그리고 위 명령의 출력 내용 중에서 아래의 내용을 복사하여 명령행에 붙여 넣고 수행해야 한다.
# (참고: 이 문서의 뒷 부분에서, 아래 설정 파일 'admin.conf'를 이용해서 
#       Kubernetes cluster 외부에서 Kubernetes API 서버에 접속하는 방법을 설명할 것이다)

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

위 명령을 수행하고 바로 Container Network Interface(CNI) 설치하는 작업을 수행해야 한다.

 

CNI 설치

CNI에는 많은 종류가 있지만, 사용하기 쉽고 대중적인 Calico 또는 Flannel CNI를 설치하겠다.

(A) CNI로써 Calico를 설치하는 경우  <-- 추천

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

(B) CNI로써 Calico를 설치하는 경우 <-- 나는 개인적으로 별루~  (몇달 운영하다보면, Error  발생한 경험이 있어서)

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

 

위 CNI 설명 명령이 수행된 후, 아래와 같이 Pod의 기동 여부를 확인한다.

$  kubectl get pod -A
NAMESPACE     NAME                                                 READY   STATUS    RESTARTS   AGE
kube-system   coredns-558bd4d5db-4qglz                             1/1     Running   0          117m
kube-system   coredns-558bd4d5db-znw7h                             1/1     Running   0          117m
kube-system   etcd-master-0.kube.namiso.space                      1/1     Running   0          117m
kube-system   kube-apiserver-master-0.kube.namiso.space            1/1     Running   0          117m
kube-system   kube-controller-manager-master-0.kube.namiso.space   1/1     Running   0          117m
kube-system   kube-flannel-ds-wnbjv                                1/1     Running   0          115m
kube-system   kube-proxy-8gsdl                                     1/1     Running   0          117m
kube-system   kube-scheduler-master-0.kube.sejong.space            1/1     Running   0          117m
$

 

※ 참고

위 명령 `kubectl apply -f .......calico.yaml` 을 수행 후 CNI 설치에 문제가 있었다면, Pod 'coredns-xxxxxx'  구동하지 못하고 'pending' 상태로 남게 된다.

그런 경우는 대부분 `kubeadm init --pod-network-cidr=10.244.0.0/16` 명령을 수행했을 때 사용했던 CIDR 값이 master node, worker node의 물리 주소와 겹치는 경우에 문제가 발생한다. 따라서 '10.244.0.0/16' 값이 아닌 다른 값으로 다시 변경해서 kubernetes cluster를 생성해보면 문제가 해결될 수 있다.

 

Worker node joining

위에서 `kubeadm init --pod-network-cidr=10.244.0.0/16` 명령을 수행했을 때, 출력되었던 메시지 중에 `kubeadm join ....` 과 같은 형태의 메시지가 있었을 것이다. 그 메시지를 복사해서  모든 worker node에서 수행한다.

# worker-0, worker-1에서 아래의 명령을 수행한다.
kubeadm join 10.10.12.39:6443 --token pdfjgjas.psdfjbh kajsdjhasdfv \
    --discovery-token-ca-cert-hash sha256:3nasklj46kj234k5lj12k3j4gkfdjjgdsfh51a3a686

위 명령이 수행된 이후에 master-0 node에서 아래의 명령으로 cluster 구축된 결과를 확인한다.

$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   3m     v1.21.2
worker-0.kube.sejong.space   Ready    <none>                 1m     v1.21.2
worker-1.kube.sejong.space   Ready    <none>                 1m     v1.21.2

 

Kubernetes cluster 삭제 (Tear down)

만약 깔끔하게 kubernetes cluster를 지우고, 처음부터 다시 구축하고 싶다면 아래와 같이 cluster를 reset 한다.

$  kubeadm reset;   rm -rf  $HOME/.kube  /etc/kubernetes;   rm -rf /etc/cni

 

 

Bastion Node 설정

위 설명에서는 kubectl 명령을 master-0 node에서 수행했다.

그러나 일반적으로 master-0에 직접 SSH 접속해서 kubectl 명령을 수행하는 것을 권장하지 않는다.

kubernetes cluster node는 운영 node이기 때문에 개발자가 접속하는 것이 바람직하지 않다.

(어쩌면, 보안 규정상 개발자가 master node에 SSH 접속하는 것 자체를 허용하지 않는 회사도 있을 것이다)

따라서 master-0 node가 아닌 본인의 PC(예를 들어 MacBook 같은 PC)에서 접속하는 방법을 사용하는 것을 권장한다.

방법은 간단하다.

master-0 node에 있는 /etc/kubernetes/admin.conf 파일을 내 PC(예를 들어 Macbook)에 복사하기만 하면 된다.

# MacOS를 사용한다고 가정하고 설명하겠다.
$  mkdir -p $HOME/.kube
$  cd $HOME/.kube
$  master-0 node의 '/etc/kubernetes/admin.conf' 파일을 내 PC로 내려받는다.
$  mv  admin.conf  config

# 내 PC에서 Kubernetes cluster의 API 서버로 잘 접속하는지 아래와 같이 명령을 수행해본다.
$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   128m   v1.21.2
worker-0.kube.sejong.space   Ready    <none>                 126m   v1.21.2
worker-1.kube.sejong.space   Ready    <none>                 126m   v1.21.2
$

 


 

이 아래 부분에서 설명하는 작업 절차는 Kubernetes를 운영하는 데 있어서 꼭 필요한 것은 아니고, Web Dashboard로 좀 더 예쁘게 Kubernetes cluster를 모니터링하고 싶은 경우에 아래 Web Dashboard 설정 작업을 해주면 좋다.

 

Kubernetes Web Dashboard 설치 및 설정

내가 참고했던 Web docs(https://waspro.tistory.com/516) 가 있고, 이 문서에서 설명한 3가지 방식 중에서 3번째 방식(Kubernetes API Server 연동 방식)을 사용하는 것을 권장한다.

이 Web Docs에 설명이 잘 되어 있어서 내가 별도 설명할 필요 없을 것이고, 내가 수행했던 명령만 로그로 남겨보겠다.

 

$  kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
$  kubectl proxy   &

# Service Account 생성

$  cat <<EOF | kubectl create -f -
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: admin-user
   namespace: kube-system
EOF
$

# ClusterRoleBinding을 생성

$  cat <<EOF | kubectl create -f -
 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: kube-system
EOF
$

# 사용자 계정의 Token 확인
$  kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') 
Name:         admin-user-token-p9ldd
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=admin-user
              kubernetes.io/service-account.uid=041cb7ec-946a-49b6-8900-6dc90fc08464

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InFmdGVUUnB6QmhoOHhlSDBJLUNLVHlEWWxpd2ZVaDhBVjZOQXE5TElhVWsifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWZ2dG5uIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOGExZTY3MS00NmY1LTQwZjctODNkYy02YTE4N2NiYzkzYmYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.lKKD4yEvlpFJ7-BNuPTyO3YRuFYYdQMgPX5azXwn4bZiki2Y886k1dnNM16L4YuA_SSahrPtitSzPfevlAoeC5msdDg1DKRdFuGuYkkI_u_KvOv7orMopDDgZs0zuXFrHIZa1-qiWbgvHfgokOMvndchCcMHyo8pKD3vdBAq_AxtGlCIPImkfALM_d41FrBKIXEjdoCcHkPu7Cz13UAxNRBRs-d274g2UNz-MUnNiomDhJlcYFXTXeooKjHhUiyoFLCgP-V6Wh_1QSCwdfYZGQ1bF0QcZINQZdwluyOtP43AjXHxdSBrAGIPfaY7qsBR_b2upuUDnQsA1w7qkaQB0g     <== 이 빨간색 token을 Web dashboard login 화면에 붙여 넣고, "Sign-in" 버튼을 누른다.
$

 

위와 같이 ServiceAccount와 ClusterRole을 설정하고, secret을 생성/등록한 후에 Web Browser에서 접속하면 된다.

 

접속 주소 예시:  http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

 

 

 

 

Troubleshooting & How to clear the issue

kubeadm join  명령이 실패하는 경우.

대부분 master node에서 생성한지 1시간이 초과된 token 값을 이용해서 worker node에서 join하려고 하면 

'kubeadm join' 명령이 실패한다.

worker node 1, 2, ... 9 이런 식으로 순차적으로 작업하다가 보면, 거의 끝 부분에 있는 worker node 9는 이미 1 시간이 지난 뒤에

'kubeadm join'을 하게 되므로 종종 실패하게 된다.

그러나 심각한 문제는 아니고, master node에서 'kubeadm token create ...' 명령을 사용해서 다시 token 값을 생성해주기만 하면 된다.

아래와 같이 master node에서 token create하고, worker node에서 새로 만들어진 token 값으로 `kubeadm join'하면 된다.

## On master node.
$ kubeadm token create --print-join-command
kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686


## On worker node.
$ kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686
[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 -o yaml'
[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] Starting the kubelet
[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.
$


## 위와 같이 worker node의 joining이 성공하면, 
## 그 동안 activating (auto-restart) 상태였던 kubelet에 아래와 같이 active(running) 상태로 바뀐다.
## On worker node.
$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Thu 2021-11-11 15:38:54 KST; 17s ago
   ...
   ...
 $

위와 같이 Active: active (running) 상태로 출력되면, 정상적으로 kubelet이 기동되고 Master node와 연동된 것이다.

 

+ Recent posts