본 글은 ‘쿠버네티스 클러스터 구성을 위한 Hyper-v Provisioning’ 이후 진행함
0. 개요
- 1개의 Master와 3개의 Worker로 구성된 쿠버네티스 클러스터를 구축하는 것이 목표
- 환경
- Host OS: Window11
- Hyper-v OS: Rocky-9.1-x86_64-minimal.iso
Customize vim / promptline (if nessesary)
1
| sudo dnf install -y vim git ctags ShellCheck
|
1
| curl -fLo "${HOME}/.vim/autoload/plug.vim" --create-dirs 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
|
1
| cat << EOF > "${HOME}/.vimrc"
|
.vimrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
| set term=xterm-256color
set nu
set ai
set si
set cindent
set shiftwidth=4
set tabstop=4
set hlsearch
set ruler
set title
set showmatch
set wmnu
set expandtab
set background=dark
set nocompatible
set fileencodings=utf-8,euc-kr
set bs=indent,eol,start
set history=1000
set laststatus=2
set foldenable
set foldlevelstart=10
set foldnestmax=10
set foldmethod=indent
set autoread
set cursorline
filetype plugin on
filetype indent on
call plug#begin('~/.vim/plugged')
Plug 'morhetz/gruvbox'
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'nathanaelkane/vim-indent-guides'
Plug 'itchyny/vim-cursorword'
Plug 'tpope/vim-surround'
Plug 'vim-scripts/WhiteWash'
Plug 'roxma/vim-paste-easy'
Plug 'bitc/vim-bad-whitespace'
Plug 'mtdl9/vim-log-highlighting'
Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
Plug 'edkolev/promptline.vim'
Plug 'godlygeek/tabular'
Plug 'preservim/tagbar'
Plug 'preservim/nerdcommenter'
Plug 'dense-analysis/ale'
Plug 'vim-scripts/AutoComplPop'
Plug 'mg979/vim-visual-multi'
call plug#end()
syntax on
colorscheme gruvbox
map <C-e> :NERDTreeToggle <CR>
map <C-t> :Tagbar <CR>
inoremap <S-Tab> <C-d>
nnoremap <S-Tab> <<
nnoremap <space> za
nnoremap <tab> zM
nnoremap \ zR
nnoremap <F1> :noh <CR>
nnoremap <F2> :set nonumber! <CR>
nnoremap <F3> :set mouse=a <CR>
nnoremap <F4> :set mouse-=a <CR>
nnoremap <F5> :set list! <CR>
nnoremap <F6> :%!python -m json.tool <CR>
nnoremap <F7> :!vi -u NONE % <CR>
nnoremap <F12> :!cat % <CR>
let g:gruvbox_contrast_dark = 'hard'
let g:airline#extensions#tabline#enabled = 1
let g:airline#extensions#tabline#left_sep = ' '
let g:airline#extensions#tabline#left_alt_sep = '|'
let g:airline#extensions#tabline#formatter = 'unique_tail_improved'
let g:airline_theme = 'hybrid'
let g:airline_powerline_fonts = 1
let g:indent_guides_enable_on_vim_startup = 1
let g:indent_guides_start_level = 2
let g:indent_guides_guide_size = 1
let g:promptline_theme = 'airline'
let git_sha_slice = {
\'function_name': 'git_sha',
\'function_body': [
\'function git_sha {',
\' local sha',
\' sha=$(git rev-parse --short HEAD 2>/dev/null) || return 1',
\' printf "%s" "$sha"',
\'}']}
let g:promptline_preset = {
\'a' : [ '\h' ],
\'b' : [ '\u' ],
\'c' : [ '\w' ],
\'warn' : [ promptline#slices#vcs_branch(), git_sha_slice ]
\}
let g:promptline_powerline_symbols = 1
command! -nargs=0 WriteWithSudo :w !sudo tee % > /dev/null
cnoreabbrev w!! WriteWithSudo
|
1
| printf '\n' | vim +PlugInstall +qall &> /dev/null
|
1
| printf '\n' | vim +"PromptlineSnapshot ${HOME}/.promptline airline" +qall &> /dev/null
|
1
| printf 'source "${HOME}/.promptline.sh" &> /dev/null\nfunction vi { vim "${@}"; }\n' >> "${HOME}/.bashrc" && source "${HOME}/.bashrc"
|
1. 클러스터 설정
Host file (모든 노드에서 설정)
1
2
3
4
5
| cat << EOF | sudo tee /etc/hosts
192.168.25.220 woobin-vm1
192.168.25.221 woobin-vm2
192.168.25.222 woobin-vm3
EOF
|
환경변수 설정
1
2
| export CLUSTER_HOSTS=(woobin-vm1 woobin-vm2 woobin-vm3)
export HOME_DIR_PATH=/home/$USER
|
SSH key sharing
각 서버에 접근할 때 비밀번호 없이 로그인하기 위해 host 계정 ssh key를 생성하고 각 서버에 배포
1
2
3
4
5
6
7
8
9
10
11
| # sshd_config 변경
for i in ${!CLUSTER_HOSTS[*]}; do ssh woobin@${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo dnf install -y sshpass && sudo sed -i -e 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config && sudo systemctl restart sshd" &> /dev/null; done
# ssh key 생성
echo -e ">> Enter Password:"; read -s PASSWD; for i in ${!CLUSTER_HOSTS[*]}; do echo "i[$i]: [${CLUSTER_HOSTS[$i]}]"; sshpass -p $PASSWD ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no woobin@${CLUSTER_HOSTS[$i]} "mkdir -p $HOME_DIR_PATH/.ssh; echo -e 'n\n' | ssh-keygen -t rsa -f $HOME_DIR_PATH/.ssh/id_rsa -q -N ''; cp $HOME_DIR_PATH/.ssh/id_rsa.pub $HOME_DIR_PATH/.ssh/authorized_keys; chmod 700 $HOME_DIR_PATH/.ssh" &> /dev/null; done
# ssh key 공유
echo -e ">> Enter Password:"; read -s PASSWD; for i in ${!CLUSTER_HOSTS[*]}; do echo "i[$i]: [${CLUSTER_HOSTS[$i]}]"; for j in ${!CLUSTER_HOSTS[*]}; do echo "j[$j]: [${CLUSTER_HOSTS[$i]}] -> [${CLUSTER_HOSTS[$j]}]"; sshpass -p $PASSWD ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no woobin@${CLUSTER_HOSTS[$i]} "sshpass -p $PASSWD ssh-copy-id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no woobin@${CLUSTER_HOSTS[$j]} &> /dev/null" &> /dev/null; done; done
# 체크
for i in ${!CLUSTER_HOSTS[*]}; do ssh ${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "ls -al .ssh"; done
|
Disable Firewall
1
| for i in ${!CLUSTER_HOSTS[*]}; do ssh woobin@${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo systemctl stop firewalld && sudo systemctl disable firewalld && sudo systemctl mask --now firewalld"; done
|
systemctl stop firewalld
: 현재 기동중인 방화벽 중지systemctl disable firewalld
: 시스템 기동시 방화벽 기동 비활성화systemctl mask --now firewalld
: 다른 서비스에서 방화벽 시작 방지 (Created symlink /etc/systemd/system/firewalld.service → /dev/null.)mask
: mask
는 서비스를 “마스크”하거나 비활성화하는 기능을 합니다. 마스크된 서비스는 수동으로 시작할 수 없으며, 다른 서비스나 사용자의 요청에 의해서도 자동으로 시작되지 않습니다.-now
: -now
옵션은 서비스 상태를 변경하는 동시에 즉시 실행하도록 지시하는 옵션입니다.
Install initial Package
1
| for i in ${!CLUSTER_HOSTS[*]}; do ssh woobin@${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo dnf install -y epel-release; sudo dnf update -y"; done
|
2. Docker
Install docker
현재 구성하는 클러스터에서 도커는 컨테이너 빌드 도구로 사용할 예정. 컨테이너 런타임으로는 containerd를 사용
- 참고) 도커 엔진은 컨테이너 런타임이 쿠버네티스와 호환되기 위한 요구 사항인 CRI를 만족하지 않는다. 쿠버네티스 v1.24 이전 릴리스는 도커심이라는 구성 요소를 사용하여 도커 엔진과의 직접 통합을 지원했다. 이는 더 이상 쿠버네티스에 포함되지 않는다.
1
| for i in ${!CLUSTER_HOSTS[*]}; do ssh woobin@${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin"; done
|
Bash command completion for the docker client
쉘 세션 재시작 시 적용됨
1
| for h in ${CLUSTER_HOSTS[*]}; do ssh ${h} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo dnf -y install bash-completion && sudo curl -L https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker"; done
|
Ensure docker for Operation User
docker 그룹에 사용자를 추가하여 docker 명령어를 실행할 수 있도록 권한을 부여
1
| for h in ${CLUSTER_HOSTS[@]}; do ssh ${h} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo usermod -aG docker $USER"; done
|
kubelet과 컨테이너 런타임이 같은 cgroup 드라이버를 사용해야 하며 구성도 동일해야 함. 본 포스팅에서는 cgroup 드라이버로 systemd 사용
https://kubernetes.io/ko/docs/setup/production-environment/container-runtimes/#cgroup-드라이버
1
2
3
4
5
6
7
8
| for h in ${CLUSTER_HOSTS[@]}; do ssh ${h} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no 'cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"hosts": ["fd://","tcp://0.0.0.0:2375"],
"containerd": "/run/containerd/containerd.sock",
"storage-driver": "overlay2"
}
EOF'; done
|
"exec-opts": ["native.cgroupdriver=systemd"]
: 컨테이너 런타임의 cgroup 드라이버로 cgroupfs 대신 systemd를 사용"hosts": ["fd://","tcp://0.0.0.0:2375"]
: Docker 데몬이 수신 대기할 호스트와 포트를 지정. "fd://"
는 유닉스 도메인 소켓을 통한 연결, "tcp://0.0.0.0:2375"
는 모든 IP 주소에서 2375 포트를 통한 TCP 연결을 허용함"containerd": "/run/containerd/containerd.sock"
: 컨테이너 런타임 “containerd”의 소켓 경로를 지정"storage-driver": "overlay2"
: overlay2"
는 컨테이너 레이어를 효율적으로 관리하기 위해 사용되는 드라이버
1
2
3
| for h in ${CLUSTER_HOSTS[@]}; do ssh ${h} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo sed -i '/ExecStart/s/^/# /' /usr/lib/systemd/system/docker.service && sudo sed -i '/ExecStart/a ExecStart=/usr/bin/dockerd' /usr/lib/systemd/system/docker.service"; done
# AS-IS: ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
|
Run Docker(d)
1
| for h in ${CLUSTER_HOSTS[@]}; do ssh ${h} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo systemctl daemon-reload && sudo systemctl start docker && sudo systemctl enable docker"; done
|
3. Kubernetes
Disable SELinux (Security-Enhanced Linux)
setenforce 0
및 sed ...
를 실행하여 permissive 모드로 SELinux를 설정하면 효과적으로 비활성화된다. 컨테이너가 호스트 파일시스템(예를 들어, 파드 네트워크에 필요한)에 접근하도록 허용하는 데 필요하다. kubelet에서 SELinux 지원이 개선될 때까지 이 작업을 수행해야 한다.
1
| for i in ${!CLUSTER_HOSTS[*]}; do ssh woobin@${CLUSTER_HOSTS[$i]} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo setenforce 0 && sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config"; done
|
Disable Memory Swap
메모리 스왑이 활성화돼 있으면 컨테이너의 성능이 일관되지 않을 수 있기 때문
1
2
| # 현재 활성화된 swap 정보 확인
cat /proc/swaps
|
1
| for host in ${CLUSTER_HOSTS[@]}; do ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo swapoff -a && sudo sed -i '/ swap / s/^/#/' /etc/fstab && sudo mount -a"; done
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| for host in ${CLUSTER_HOSTS[@]}; do
ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
"
done
|
Install kubelet, kubeadm, kubectl
1
| for host in ${CLUSTER_HOSTS[@]}; do ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes && sudo systemctl enable --now kubelet"; done
|
kubeadm
: Kubernetes 클러스터를 부트스트랩(시작)하는 데 사용kubelet
: Kubernetes 클러스터의 모든 노드에서 실행되는 구성 요소. pod 및 컨테이너를 시작하는 등 클러스터 내의 모든 노드에서 필요한 작업을 수행kubectl
: Kubernetes 클러스터의 모든 노드에서 실행되는 구성 요소. 이는 pod 및 컨테이너를 시작하는 등 클러스터 내의 모든 노드에서 필요한 작업을 수행-disableexcludes=kubernetes
: Kubernetes 관련 패키지를 설치할 때 다른 패키지 제외 설정을 무시하고 모든 관련 패키지를 설치하도록 지시하는 것
1
2
3
4
5
6
7
| for host in ${CLUSTER_HOSTS[@]}; do
ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no '
sudo containerd config default | sudo tee /etc/containerd/config.toml &&
sudo sed -i "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml &&
sudo systemctl restart containerd
'
done
|
참고) error: The connection to the server 192.168.x.x:6443 was refused - did you specify the right host or port?
kubeadm init
이후에 kubectl get node
를 했을 때 처음에는 결과가 잘 나타나다가 시간이 조금 지나면 위와 같은 에러가 나타나기 시작한다.6443
은 쿠버네티스 API에서 사용하는 포트이고 192.168.x.x는 localhost니깐 위의 에러는 자기 자신에 연결할 수 없다는 이야기가 된다. 6443 포트를 통해서 자기 내부에서 돌고 있는 쿠버네티스 시스템과 통신을 하게 되는데 그게 불가능하다는 것은 쿠버네티스가 제대로 돌고 있지 않다는 것이다- 컨테이너 런타임(containerd)의 cgroup 드라이버를 설정해줘서 해결
Initializing control-plane node
세팅할 클러스터에서 Pod가 서로 통신할 수 있도록 Pod 네트워크 애드온을 설치 해야 한다. kubeadm을 통해 만들어진 클러스터는 CNI(Container Network Interface) 기반의 애드온이 필요하다. 본 글에서는 Calico를 설치하여 사용할 것이다.
1
| sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=192.168.25.220
|
—-pod-network-cidr
: Pod 네트워크를 설정할 때 사용192.168.0.0/16
네트워크는 Calico에서 기본적으로 권장하는 네트워크 대역
-apiserver-advertise-address
: 특정 마스터 노드의 API Server 주소를 설정할 때 사용ifconfig
명령를 통해 마스터 노드의 IPv4 주소를 확인
- 만약 고가용성을 위해 마스터 노드를 다중으로 구성했다면
--control-plane-endpoint
옵션을 사용하여 모든 마스터 노드에 대한 공유 엔드포인트를 설정할 수 있다.
참고) [ERROR CRI]: container runtime is not running
- /etc/containerd/config.toml에서 disabled_plugins 라인을 비활성화하여 CRI 인터페이스를 활성화
1
| for host in ${CLUSTER_HOSTS[@]}; do ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "sudo sed -i '/^disabled_plugins/s/^/#/' /etc/containerd/config.toml && sudo systemctl restart containerd"; done
|
Additional
1
2
| # maser node
mkdir -p $HOME/.kube && sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config && sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
1
2
3
4
5
6
|
# worker node
for host in ${CLUSTER_HOSTS[@]}; do
ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
"mkdir -p \$HOME/.kube && scp woobin@woobin-vm1:${HOME}/.kube/config \$HOME/.kube/"
done
|
- root 계정이 아닌 다른 사용자 계정에서
kubectl
명령어를 사용하여 클러스터를 제어하기 위함
Install calico
https://docs.tigera.io/calico/latest/getting-started/kubernetes/quickstart
1
2
3
| kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/custom-resources.yaml
|
Join nodes
1
2
3
| sudo kubeadm join 192.168.25.220:6443 \
--token ielh36.8k2eiq8m5rk0u12d \
--discovery-token-ca-cert-hash sha256:6a4e73b78661ecbe9d7a43694ae5809c2601309461a14fed6f595fdb13796edc
|
Unset
1
| for host in ${CLUSTER_HOSTS[@]}; do ssh ${host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "unset CLUSTER_HOSTS"; done
|
Reference
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/