일반적으로 public cloud 를 많이 사용하지만 어쩔 수 없이 망을 분리 하여 사용하기 위해 private cloud 를
검토해야 할 경우가 있습니다.
이런 경우 저희의 구축 및 운영 경험을 기반으로 한 Architeture 와 구축 방법을 공유해 보고자 합니다.

Cluster api 는 k8s cluster 를 custom resource 로 관리 하는 진일보한 방식이기는 하지만 addon 확장이 가능은 하나 제한적입니다.
Public cloud 를 사용하지 않는 offline private cloud 환경에서 어떤 hardware 나 software 를 사용해야 할지 모르는 상황에서는 컨트롤이 쉽고 레퍼런스가 더 많은 kubespray 가 낫다고 판단했습니다.
5. kubespray 를 통한 k8s provisioning

(https://github.com/kubernetes-sigs/kubespray/tree/master/contrib/offline 를 참고 하시면 됩니다.)
shell> ssh-keygen -t rsa -b 2048
shell> ssh-copy-id root@node1
shell> ssh-copy-id root@node2
shell> ssh-copy-id root@node3
shell> ssh-copy-id root@node4
…
해당 file과 image 리스트를 다운받아서 사내 웹서버 또는 사내 repository 에서 k8s 가 설치 될 서버가 http 로 다운 받을 수 있는 환경을 구성 해주면 됩니다.
다만 그런 구성이 힘들 경우 아래의 스크립트를 추가 실행 시켜서 bootstrap 서버에 임시로 image repository, file repository 를 구성할 수 있습니다.
registry_host: "[bootstrap서버IP:5000]"
files_repo: "[bootstrap서버IP:8080]"
#아래는 OS 설정에 맞게 처리
ubuntu_repo: "[bootstrap서버IP:8080]"
#그 아래는 필요한 부분에 대해서 주석 해제 해주면 됩니다. (위의 global 변수 설정을 가져오므로 주석 해제만으로 처리됩니다.)
- prefix: [bootstrap서버IP:5000]
mirrors:
- host: http://[image[bootstrap서버IP:5000]
capabilities: ["pull", "resolve"]
skip_verify: true #https 가 아닌 http 를 사용하기 위해
server = "http://{{ item.prefix }}" 로 변경 해줘야 합니다. (kubespray 2.24.1 기준)
gcr_image_repo: "{{ registry_host }}"
github_image_repo: "{{ registry_host }}"
docker_image_repo: "{{ registry_host }}"
quay_image_repo: "{{ registry_host }}"
을
kube_image_repo: "{{ registry_host }}/registry.k8s.io"
gcr_image_repo: "{{ registry_host }}/gcr.io"
github_image_repo: "{{ registry_host }}/ghcr.io"
docker_image_repo: "{{ registry_host }}/docker.io"
quay_image_repo: "{{ registry_host }}/quay.io"
로 변경 해줍니다.

[all]
master1 ansible_host=[master1아이피] ip=[master1아이피] etcd_member_name=etcd1
master2 ansible_host=[master2아이피] ip=[master2아이피] etcd_member_name=etcd2
master3 ansible_host=[master3아이피] ip=[master3아이피] etcd_member_name=etcd3
proxy ansible_host=[proxy아이피] ip=[proxy아이피]
worker1 ansible_host=[worker1아이피] ip=[worker1아이피]
worker2 ansible_host=[worker2아이피] ip=[worker2아이피]
[kube_control_plane]
master1
master2
master3
[etcd]
master1
master2
master3
[kube_node]
master1
master2
master3
proxy
worker1
worker2
[proxy-node]
proxy
[worker-node]
worker1
worker2
[kube_control_plane:vars]
node_labels={"node-role.kubernetes.io/master" : "", "master" : "true", "node-role.kubernetes.io/control-plane" : "", "control-plane" : "true"}
[proxy-node:vars]
node_labels={"node-role.kubernetes.io/proxy" : "", "proxy" : "true"}
[worker-node:vars]
node_labels={"node-role.kubernetes.io/worker" : "", "worker" : "true"}
[calico_rr]
[k8s_cluster:children]
kube_control_plane
kube_node
calico_rr
ingress_nginx_enabled: true
ingress_publish_status_address: ""
ingress_nginx_nodeselector:
node-role.kubernetes.io/proxy : ""
ingress_nginx_configmap:
proxy-read-timeout : "300"
proxy-send-timeout : "300"
worker-shutdown-timeout : "60m"
proxy-body-size : "0"

kubectl
kubeadm
파일을 가져다 놓습니다.
그 다음
roles/kubespray-defaults/defaults/main/checksums.yml 파일에서 checksum 내용을 타겟 버전으로 kubelet, kubeadm, kubectl 을 추가 합니다.
(업그레이드 하려는 버전의 kubespray 가 있다면 가져오면 됩니다.)
registry.k8s.io/kube-controller-manager
registry.k8s.io/kube-proxy
registry.k8s.io/kube-scheduler
when: kube_network_plugin_multus
tags:
- multus
주석 처리
when: kube_network_plugin_multus
tags:
- multus
을 주석 처리로 변경 해 줍니다.
그 다음 업그레이드 명령을 수행하면 됩니다.
shell> ansible-playbook -i inventory/baremetal/inventory.ini upgrade-cluster.yml --become --become-user=root -e kube_version=v[타겟버전] --limit [타겟노드] | tee kubespray_upgrade_yyyymmdd.txt

위와 같이 kubespray 로 kubernetes 를 구성, 삭제, 추가, 업그레이드 하는 방법을 살펴 보았습니다.
위의 내용을 따라 하신 다면 ansible 에 대한 이해가 크게 없으셔도 충분히 따라서 구축 및 운영에 도움이 되실 거라고 생각합니다.
이 글로 private cloud 의 offline 설치에 좀 더 자신감을 가지실 수 있었으면 합니다.
상세 내용을 실제 코드 상에서 확인 하고 싶으시다면
https://github.com/YunchaeJung/kubespray/tree/main/2.24.1
를 참고 하세요
검토해야 할 경우가 있습니다.
이런 경우 저희의 구축 및 운영 경험을 기반으로 한 Architeture 와 구축 방법을 공유해 보고자 합니다.
- 이 글에서 알아 갈 수 있는 것
- 실무에서 설치,운영되는 Private k8s Architecture (망분리 된 자사 IDC 나 private 환경)
- 실제 k8s설치 방법 (kubespray 사용, kubespary 버전은 2.24.1, k8s 버전은 1.29.2)
- 사전 필요 지식
- 기본 k8s 구성
- Linux 기본 이해
- Ansible 기본 사용법
- Private k8s Archtecture
- 이론적으로 k8s cluster 는 control plane 이라고 하는 master node 와 worker node 로 나누지만 실무적으로는 외부와 통신을 주로 담당하는 proxy node 를 두는 것이 좋습니다.
- 외부 L4 의 역할은 여러 대의 master node, proxy node 에 대한 대표IP 제공(vip), Service 의 loadbalancer type 을 제공 해야 하는 경우 해당 L4 를 통해 서비스를 제공 할 수 있습니다.(metallb 가 없는 경우 직접 external ip 를 제공)
- K8s provisioning 방법 소개
- Private k8s 를 설치 하기위해서는 아래와 같은 여러 가지 방법이 있습니다.
- kubeadm : 단일 클러스터, 온프라미스에서 k8s 를 설치하기 위한 기초적인 방법 (대규모 클러스터에 적합하지 않음)
- kubespray : ansible 을 이용한 k8s 클러스터 설치, 다양한 옵션제공 및 설정 파일 편집이 직관적임
- kops : public cloud 환경에서 간단한 설치 및 관리
- cluster api : operator 기반으로 멀티 k8s 클러스터 환경, 선언적 클러스터 관리가 가능
- 이 중에 여기에서는 kubespray 를 통한 k8s 설치에 대해 알아 보겠습니다.
Cluster api 는 k8s cluster 를 custom resource 로 관리 하는 진일보한 방식이기는 하지만 addon 확장이 가능은 하나 제한적입니다.
Public cloud 를 사용하지 않는 offline private cloud 환경에서 어떤 hardware 나 software 를 사용해야 할지 모르는 상황에서는 컨트롤이 쉽고 레퍼런스가 더 많은 kubespray 가 낫다고 판단했습니다.
5. kubespray 를 통한 k8s provisioning
- 실제 설치를 위해서는 위의 architeture 상태로 구성을 해야 합니다.
- 위의 구성에 대해서 설명을 하자면
- Bootstrap : ansible 을 실행 하고자 하는 노드 (외부 인터넷이 가능 하거나 인터넷에서 다운 받은 파일을 자유롭게 업로드 할 수 있는 서버 필요)
- Node 1,2,3,4 … : 실제 k8s 가 설치 될 노드 (실제 k8s 가 구성 될 서버)
- Image repository : container image 를 pull 받을 수 있는 repository
- File repository : container runtime 이나 기타 필요한 파일들을 다운 받을 수 있는 구성
(https://github.com/kubernetes-sigs/kubespray/tree/master/contrib/offline 를 참고 하시면 됩니다.)
- 이제 실제로 kubespray 를 통해 k8s cluster 를 3번에서 설명한 대로 구성해 보겠습니다. (metallb 구성은 여기서는 제외하고 설치 합니다.)
- Bootstrap 서버에서 root 계정으로 아래의 명령어들을 실행 하여 ssh key 교환을 합니다.
shell> ssh-keygen -t rsa -b 2048
shell> ssh-copy-id root@node1
shell> ssh-copy-id root@node2
shell> ssh-copy-id root@node3
shell> ssh-copy-id root@node4
…
- shell> git clone -b v(버전) https://github.com/kubernetes-sigs/kubespray.git 명령어로 원하는 kubespray 버전을 다운받습니다.
- shell> cd kubespray
- shell> pip3 install -r requirements.txt 를 통해 prerequisitions 를 설치 합니다. (사전에 python, pip 는 설치 되어 있어야 합니다.)
- shell> cp -rpf inventory/sample/ inventory/testcluster 를 통해 sample 로 구성된 클러스터 정보를 설치 할 testcluster 로 복사합니다.
- 본 문서는 망분리가 되어 있는 offline 설치를 기본으로 하므로 offline 설치에 대한 개략적인 설명을 하겠습니다.
- shell> cd contrib/offline
- shell > ./generate_list.sh 명령을 통해 k8s 설치 되는데 필요한 파일과 container image 리스트를 얻을 수 있습니다.
해당 file과 image 리스트를 다운받아서 사내 웹서버 또는 사내 repository 에서 k8s 가 설치 될 서버가 http 로 다운 받을 수 있는 환경을 구성 해주면 됩니다.
다만 그런 구성이 힘들 경우 아래의 스크립트를 추가 실행 시켜서 bootstrap 서버에 임시로 image repository, file repository 를 구성할 수 있습니다.
- shell> ./manage-offline-files.sh 를 통해 k8s 를 위해 설치될 파일들을 다운로드 받고 file repository (8080 포트) 를 기동시킵니다.
- shell> ./manage-offline-container-images.sh create
- shell> ./manage-offline-container-images.sh register 를 통해 설치될 image 들을 다운로드 받고 image repository(5000 포트) 를 기동시킵니다.
- shell> vi ~/kubespray/inventory/testcluster/group_vars/all/offline.yml 에서
registry_host: "[bootstrap서버IP:5000]"
files_repo: "[bootstrap서버IP:8080]"
#아래는 OS 설정에 맞게 처리
ubuntu_repo: "[bootstrap서버IP:8080]"
#그 아래는 필요한 부분에 대해서 주석 해제 해주면 됩니다. (위의 global 변수 설정을 가져오므로 주석 해제만으로 처리됩니다.)
- shell> vi ~/kubespray/inventory/testcluster/group_vars/all/containerd.yml
- prefix: [bootstrap서버IP:5000]
mirrors:
- host: http://[image[bootstrap서버IP:5000]
capabilities: ["pull", "resolve"]
skip_verify: true #https 가 아닌 http 를 사용하기 위해
- shell> vi ~/kubespray/roles/container-engine/containerd/templates/hosts.toml.j2 부분을
server = "http://{{ item.prefix }}" 로 변경 해줘야 합니다. (kubespray 2.24.1 기준)
- shell> vi ~/kubespray/inventory/testcluster/group_vars/all/offline.yml 의
gcr_image_repo: "{{ registry_host }}"
github_image_repo: "{{ registry_host }}"
docker_image_repo: "{{ registry_host }}"
quay_image_repo: "{{ registry_host }}"
을
kube_image_repo: "{{ registry_host }}/registry.k8s.io"
gcr_image_repo: "{{ registry_host }}/gcr.io"
github_image_repo: "{{ registry_host }}/ghcr.io"
docker_image_repo: "{{ registry_host }}/docker.io"
quay_image_repo: "{{ registry_host }}/quay.io"
로 변경 해줍니다.
- k8s 클러스터를 구성 하고자 하는 모습은 아래 그림과 같습니다.
- shell> vi inventory/testcluster/inventory.ini 를 통해 k8s 클러스터가 설치 될 서버들에 대한 정보를 기술 합니다. (etcd 는 모든 master node 에 기동 되도록 구성합니다.)
[all]
master1 ansible_host=[master1아이피] ip=[master1아이피] etcd_member_name=etcd1
master2 ansible_host=[master2아이피] ip=[master2아이피] etcd_member_name=etcd2
master3 ansible_host=[master3아이피] ip=[master3아이피] etcd_member_name=etcd3
proxy ansible_host=[proxy아이피] ip=[proxy아이피]
worker1 ansible_host=[worker1아이피] ip=[worker1아이피]
worker2 ansible_host=[worker2아이피] ip=[worker2아이피]
[kube_control_plane]
master1
master2
master3
[etcd]
master1
master2
master3
[kube_node]
master1
master2
master3
proxy
worker1
worker2
[proxy-node]
proxy
[worker-node]
worker1
worker2
[kube_control_plane:vars]
node_labels={"node-role.kubernetes.io/master" : "", "master" : "true", "node-role.kubernetes.io/control-plane" : "", "control-plane" : "true"}
[proxy-node:vars]
node_labels={"node-role.kubernetes.io/proxy" : "", "proxy" : "true"}
[worker-node:vars]
node_labels={"node-role.kubernetes.io/worker" : "", "worker" : "true"}
[calico_rr]
[k8s_cluster:children]
kube_control_plane
kube_node
calico_rr
- shell> vi inventory/testcluster/group_vars/k8s_cluster/addons.yml 에서 주석 처리 되어 있는 nginx 설정을 아래와 같이 변경 합니다.
ingress_nginx_enabled: true
ingress_publish_status_address: ""
ingress_nginx_nodeselector:
node-role.kubernetes.io/proxy : ""
ingress_nginx_configmap:
proxy-read-timeout : "300"
proxy-send-timeout : "300"
worker-shutdown-timeout : "60m"
proxy-body-size : "0"
- shell> ansible all -m ping -i inventory/testcluster/inventory.ini 를 통해 설치를 진행할 노드와의 연결이 정상인지 확인 해 봅니다.
- Shell> ansible-playbook --forks 50 -i inventory/testcluster/inventory.ini cluster.yml -b --become-user root -e ansible_ssh_timeout=50 -vvv | tee kubespray_install_yyyymmdd.log 를 통해 설치를 진행 합니다.
- Master node 에서 shell> kubectl get node 로 정상적으로 node 들이 Ready 상태인지 확인 합니다.
- 클러스터에서 특정 node 를 제거하는 명령어는 다음과 같습니다.
- 클러스터에서 특정 node 를 추가하는 명령어는 다음과 같습니다.
- K8S 클러스터 전체를 제거 하는 명령어는 아래와 같습니다.
- K8S 클러스터에 대한 개별 노드에 대해 upgrade 를 진행 하기 위해서는
- wget 으로 contrib/offline/offline-files/dl.k8s.io/release/v[업그레이드 타겟버전]/bin/linux/amd64 경로에
kubectl
kubeadm
파일을 가져다 놓습니다.
그 다음
roles/kubespray-defaults/defaults/main/checksums.yml 파일에서 checksum 내용을 타겟 버전으로 kubelet, kubeadm, kubectl 을 추가 합니다.
(업그레이드 하려는 버전의 kubespray 가 있다면 가져오면 됩니다.)
- image repository 에 upgrade 하려는 버전의 아래의 이미지들을 push 합니다.
registry.k8s.io/kube-controller-manager
registry.k8s.io/kube-proxy
registry.k8s.io/kube-scheduler
- roles/network_plugin/meta/main.yml 에서
when: kube_network_plugin_multus
tags:
- multus
주석 처리
- roles/kubernetes-apps/network_plugin/meta/main.yml 에서
when: kube_network_plugin_multus
tags:
- multus
을 주석 처리로 변경 해 줍니다.
그 다음 업그레이드 명령을 수행하면 됩니다.
shell> ansible-playbook -i inventory/baremetal/inventory.ini upgrade-cluster.yml --become --become-user=root -e kube_version=v[타겟버전] --limit [타겟노드] | tee kubespray_upgrade_yyyymmdd.txt
위와 같이 kubespray 로 kubernetes 를 구성, 삭제, 추가, 업그레이드 하는 방법을 살펴 보았습니다.
위의 내용을 따라 하신 다면 ansible 에 대한 이해가 크게 없으셔도 충분히 따라서 구축 및 운영에 도움이 되실 거라고 생각합니다.
이 글로 private cloud 의 offline 설치에 좀 더 자신감을 가지실 수 있었으면 합니다.
상세 내용을 실제 코드 상에서 확인 하고 싶으시다면
https://github.com/YunchaeJung/kubespray/tree/main/2.24.1
를 참고 하세요