티스토리 뷰

Kubernetes 이미지

 
 

1. [IaC] vagrant로 virtualbox의 VM 관리하기 (tistory.com)
2. [K8S] Kubernetes 설치 (VirtualBox + Vagrant + K8S:v1.31) (tistory.com)
3. [K8S] Kubernetes Nodeport로 서비스 테스트 하기

 

보시기 전에.. 당부를 드리자면요...
설치 과정을 나름 자세히 설명한다고는 했지만, 생략된 과정이 있을 수 있습니다.

제 블로그의 글들은 실제 작업 과정에서 생성된 결과물들이 있는 경우가 많고,
그 작업과정이 수많은 삽질 끝에 얻어진것들입니다.
그 과정을 일일이 기억할수도, 설명할 수도 없어서 Shell Script 소스코드 몇개만 남았습니다.
최대한 설명하려 애 썼으나, 이해가 어려운 부분도 더러 있으리라 예상됩니다.

질문을 남겨주시거나, 요청을 해 주시면 내용을 보완하도록 하겠습니다.
그리고, 현 시점(2024-08-29) 기준으로는 정상 동작 되는 코드입니다만,
추후 Contaienrd의 버젼이 변하거나, K8S의 Miner Version이 변함에 따라 정상 동작 되지 않을 수 있습니다.

그때 요청을 주시면 확인 후 수정 하도록 하겠습니다.
그럼, 도움이 되시길 바랍니다.

 


A. Container만으로 운영하는 서비스의 한계

IDC에 서버를 구축하고 서비스를 하는 것의 한계는 이미 알고 있습니다.
그래서 Cloud 환경에서 Application을 Container 기반으로 배포 하여 서비스 하고 있습니다.
잘 돌아가고 있는 서비스이지만 운영 과정에서 한계가 있습니다.

아래 예시를 한번 보시죠..


시간이 지나 서비스가 점점 더 거대해 지고, 모듈들이 많아 지면서 Container가 5개 정도였던 서비스가 어느덧 20개를 넘어가고 있습니다. 각 모듈의 갯수도 늘었지만, Replica 갯수가 1개씩 더 늘었더니 증가 속도가 상상을 초월하네요.
엎친데 덮친격으로 다음주에 이벤트를 한다고 합니다. 그래서 ReplicaSet을 Front는 2배로 늘리기로 했어요.
 
Volume Mount 부터 LoadBalancer 같은 네트워크 구성까지 신경쓸게 하나 둘이 아닙니다.
Container를 늘리다 보니, EC2 장비가 더 늘어나게 되었네요...
이벤트를 위해 구성된 어플리케이션 배포까지 완료 하고 준비가 어느정도 되어 갑니다.
 
어찌 어찌 구성을 끝내고 테스트를 다 했습니다.
이벤트도 무사히 끝났네요..
이제 원상복구를 해야 합니다.
 
Loadbalancer 설정을 원복하고, Container를 제거 했습니다.
그리고 VM을 삭제 하는것으로 이벤트 작업을 끝냈습니다.


 
 
Container 갯수가 늘어나는것도 그 자체로 문제가 많습니다.
에러가 발생하는 Container들을 관리하는것도 어렵지만, 배포할때 마다 빈 공간을 찾아 Container들을 분배해서 배포 해야 하고... 제거 하는것도 찾아서 하나씩 지워야 하고, 그럴때 마다 Loadbalanacer, 내지는 Proxy의 구성을 일일이 고쳐 줘야 하고... Port 충돌도 신경써야 하고..... 
 
여러 Container들을 관리할 수 있는 도구들을 찾기 시작 했습니다.  Docker Swarm 이 있었죠..
그런데, 뭔가 좀 아쉬웠습니다. 그래서 사람들은 표준을 만들기 시작했고, 그 표준에 의한 구현체인 Kubernetes가 나오게 되었습니다.
 


B. Kubernetes란?

 
Overview | Kubernetes

Overview

Kubernetes is a portable, extensible, open source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation. It has a large, rapidly growing ecosystem. Kubernetes services, support, and tools

kubernetes.io

 
 
Kubernetes(쿠버네티스)는 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하는 오픈 소스 플랫폼입니다. 구글이 2014년에 오픈 소스로 공개한 이 플랫폼은 컨테이너 오케스트레이션을 통해 애플리케이션의 안정성과 확장성을 보장합니다.

주요 기능은 다음과 같은것들이 있습니다.

  • 서비스 디스커버리와 로드 밸런싱: 쿠버네티스는 DNS 이름이나 자체 IP 주소를 사용하여 컨테이너를 노출할 수 있습니다. 트래픽이 많을 경우 로드 밸런싱을 통해 네트워크 트래픽을 분산시켜 안정적인 배포를 지원합니다

  • 스토리지 오케스트레이션: 로컬 저장소, 공용 클라우드 공급자 등 다양한 저장소 시스템을 자동으로 탑재할 수 있습니다

  • 자동화된 롤아웃과 롤백: 배포된 컨테이너의 원하는 상태를 서술하고, 현재 상태를 원하는 상태로 변경할 수 있습니다. 이를 통해 새로운 컨테이너를 생성하거나 기존 컨테이너를 제거하는 등의 작업을 자동화할 수 있습니다

  • 자동 빈 패킹: 쿠버네티스는 클러스터의 노드에 컨테이너를 효율적으로 배치하여 리소스를 최적화합니다.

  • 자체 치유: 실패한 컨테이너를 재시작하고, 응답하지 않는 컨테이너를 종료하며, 준비되지 않은 컨테이너는 클라이언트에게 노출하지 않습니다.

  • 비밀 및 구성 관리: 비밀번호, OAuth 토큰, SSH 키와 같은 민감한 정보를 안전하게 저장하고 관리할 수 있습니다.

  • 배치 실행: 서비스 외에도 Kubernetes는 원하는 경우 실패한 컨테이너를 교체하여 배치 및 CI 워크로드를 관리할 수 있습니다.

  • 수평 확장: UI를 사용하여 간단한 명령을 사용하거나 CPU 사용량에 따라 자동으로 애플리케이션을 확장 및 축소할 수 있습니다.

  • IPv4/IPv6 듀얼 스택: POD 및 서비스에 IPv4 및 IPv6 주소 할당

  • 확장성을 고려한 설계: 업스트림 소스 코드를 변경하지 않고도 Kubernetes 클러스터에 기능을 추가할 수 있습니다.

 
쿠버네티스는 컨테이너화된 애플리케이션을 대규모로 운영할 때 발생하는 여러 문제를 해결해줍니다. 예를 들어, 컨테이너가 다운되면 자동으로 다른 컨테이너를 시작하여 가동 중지 시간을 최소화합니다. 또한, 애플리케이션의 확장과 장애 조치를 자동으로 처리하여 운영 효율성을 높입니다.

쿠버네티스는 클라우드 환경에서 특히 유용하며, 하이브리드 및 멀티 클라우드 인프라에서도 높은 이식성을 제공합니다. 이를 통해 다양한 환경에서 일관된 애플리케이션 관리를 가능하게 합니다.
 
튜토리얼 | Kubernetes

튜토리얼

쿠버네티스 문서의 본 섹션은 튜토리얼을 포함하고 있다. 튜토리얼은 개별 작업 단위보다 더 큰 목표를 달성하기 위한 방법을 보여준다. 일반적으로 튜토리얼은 각각 순차적 단계가 있는 여러

kubernetes.io

 


C. 설치하기 

그냥 설치 해 봅니다.
일단 처음 하니까, Single Node로 구성 해 보려 합니다.


1. vagrant 구성파일 생성.

vagrantfile이 조금은 복잡해 질것 같습니다.
그러니 vagrantfile의 구성을 vmconfig.yaml 파일로 설정을 분리해서 구성해 보려 합니다.
 

vagrant project 디렉토리 구성
이런 구조로 구성 될거에요.

(1) Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.

# https://github.com/patrickdlee/vagrant-examples/blob/master/example7/Vagrantfile


# --------------------------------------------------------------------------------
# $ # For vagrant < 1.1.5:
# $ # vagrant gem install vagrant-vbguest
#
# $ # For vagrant 1.1.5+ (thanks Lars Haugseth):
# $ vagrant plugin install vagrant-vbguest
# --------------------------------------------------------------------------------


$V_CPU = "2"
$V_MEM = "256"
$V_BOX = 'ubuntu/jammy64'

$NET_INTERFACE_NAT="eth0"
# $NET_INTERFACE_BRIDGE="eth1"

# 설정 Yaml 파일 load
require 'yaml'
vmconfig = YAML.load_file('vmconfig.yml')

# 초기 값 Load
$VAGRANTFILE_API_VERSION = vmconfig['vagrant']['version']

$default_network_interface = `ip route | awk '/^default/ {printf "%s", $5; exit 0}'`

# Vagrant 설정.....
Vagrant.configure($VAGRANTFILE_API_VERSION) do |config|


  # ===============================================================================================================================
  vmconfig["servers"].each do |server|
    
    
    config.vm.define server['hostname'] do |nodeconfig|

      nodeconfig.vm.box = server["box"] ? server["box"] : $V_BOX
      nodeconfig.vm.hostname = "#{server['hostname']}.box"
      if server.key?("network")
        nodeconfig.vm.network server["network"]["mode"], ip: server["network"]["ip"], bridge: server["network"]["bridge"], use_dhcp_assigned_default_route: true


        # install net-tools
        config.vm.provision "shell", run: "always", inline: "apt update -y && apt upgrade -y && apt install net-tools"

        # ---------------------------------------------------------------------------------------------------
        # https://developer.hashicorp.com/vagrant/docs/networking/public_network#default-network-interface
        # ---------------------------------------------------------------------------------------------------
        if server["network"].key?("interface")
          $interface_nat = server["network"]["interface"]["nat"] ? server["network"]["interface"]["nat"] : $NET_INTERFACE_NAT
          # default router
          config.vm.provision "shell", run: "always", inline: "route add default gw #{server["network"]["gw"]}"
          # delete default gw on eth0(interface of nat)
          config.vm.provision "shell", run: "always", inline: "eval `route -n | awk '{ if ($8 ==\"#{$interface_nat}\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
          # default router
          config.vm.provision "shell", run: "always", inline: "ip route del default via 10.0.2.2 || true"
        end
      end

      # config.vm.provision "shell",
      #     run: "always",
      #     inline: "route add default gw 192.168.0.1"

      if server.key?("services")
        server["services"].each do |service|
          nodeconfig.vm.network "forwarded_port", id: service["id"], guest: service["gport"], host: service["hport"]
        end
      end

      nodeconfig.ssh.insert_key = false
      # ------------------------------------------------------------------------------------
      # nodeconfig.vm
      # > https://developer.hashicorp.com/vagrant/docs/vagrantfile/machine_settings
      # ------------------------------------------------------------------------------------
      nodeconfig.vm.provider :virtualbox do |vb|
        vb.name = "004-#{server['hostname']}"
        # Display the VirtualBox GUI when booting the machine
        vb.gui = server["gui"] ? server["gui"] : false
      
        # Customize the amount of memory on the VM:
        vb.cpus   = server["cpu"] ? server["cpu"] : $V_CPU 
        vb.memory = server["ram"] ? server["ram"].to_s : $V_MEM
      end
      # nodeconfig.vm.disk :disk, size: "100GB", primary: true

      
      # Copy Script Files to VM
      if server.key?("resources")
        server["resources"].each do |resource|
          nodeconfig.vm.provision "file", source: "#{resource["src"]}", destination: "#{resource["dest"]}"
        end
      end
      # -------------------------------------------------------------------------------------------------------------
      #

      # Volume Setting.
      if server.key?("volumes")
        server["volumes"].each do |volume|
          host_path = volume.split(':')[0].strip
          guest_path = volume.split(':')[1].strip
          nodeconfig.vm.synced_folder host_path, guest_path
        end
      end
      

        
      nodeconfig.vm.provision "shell", inline: "timedatectl set-timezone Asia/Seoul"
    #       config.vm.provision "shell", inline: <<-SHELL
    #   cat <<EOF >> /etc/netplan/60-override.yaml
    #   ---
    #   network:
    #     version: 2
    #     renderer: networkd
    #     ethernets:
    #       eth0:
    #         dhcp4: yes
    #         dhcp4-overrides:
    #           use-routes: false
    #       eth1:
    #         dhcp4: true
    #    EOF
    #    netplan apply
    #    sleep 5
    #  SHELL
      #
      # -------------------------------------------------------------------------------------------------------------
      # Execute Shell Script on VM
      if server.key?("init_command")
        nodeconfig.vm.provision "shell", inline: <<-SCRIPT
        echo "###########################################################"
        echo "##    RUN SCRIPT FROM VAGRANT BOX                        ##"
        echo "###########################################################"
        sudo #{server["init_command"]}
        echo "###########################################################"
        echo "##    FINISH SCRIPT FROM VAGRANT BOX                     ##"
        echo "###########################################################"
        SCRIPT
      end
      
    end
  end
  # ===============================================================================================================================
  
end

 
뭔가 복잡해졌죠?
제가 쓰는 vagrantfile입니다.
 
내용을 간단히 보면 vmconfig.yaml을 읽어서, servers 로 정의되어 있는 각각의 서버 구성 정보 "server" 라는 변수로 읽은 다음 반복 하면서 VM을 생성 합니다. 생성할때 구성 정보는  "server" 변수에 정의되어 있는 값들로 구성 합니다.
 
조금 더 구체적인 설명은 아래를 펼쳐서 보시고,

더보기
  • 사용할 Box(VM Image) 를 지정합니다.
nodeconfig.vm.box = server["box"] ? server["box"] : $V_BOX

 

  • VM의 Hostname을 지정 합니다.
nodeconfig.vm.hostname = "#{server['hostname']}.box"

 

  • Network 및 IP를 지정합니다. 
      if server.key?("network")
        nodeconfig.vm.network server["network"]["mode"], ip: server["network"]["ip"], bridge: server["network"]["bridge"], use_dhcp_assigned_default_route: true

 여기서 Private Network, Public Network를 지정하게 되는데, 외부에서 접근이 가능하게 할지, 내부에서만 사용하게 할지를 정하게 됩니다.

- Public Network : 네트워크 내에 IP가 충돌되지 않도록 설정 합니다.

- Private Network : 서비스 접근은 Portforwad 구성을 통해 접근 해야 합니다.

 

 

        # install net-tools
        config.vm.provision "shell", run: "always", inline: "apt update -y && apt upgrade -y && apt install net-tools"

        # ---------------------------------------------------------------------------------------------------
        # https://developer.hashicorp.com/vagrant/docs/networking/public_network#default-network-interface
        # ---------------------------------------------------------------------------------------------------
        if server["network"].key?("interface")
          $interface_nat = server["network"]["interface"]["nat"] ? server["network"]["interface"]["nat"] : $NET_INTERFACE_NAT
          # default router
          config.vm.provision "shell", run: "always", inline: "route add default gw #{server["network"]["gw"]}"
          # delete default gw on eth0(interface of nat)
          config.vm.provision "shell", run: "always", inline: "eval `route -n | awk '{ if ($8 ==\"#{$interface_nat}\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
          # default router
          config.vm.provision "shell", run: "always", inline: "ip route del default via 10.0.2.2 || true"
        end
      end
  • Route 설정을 변경 합니다. 

virtualbox는 10.0.2.15 라는 ip를 가지고, nat network를 default gw 로 route 설정되어 있습니다. 그 문제로 k8s을 구성하는 모든 트래픽은 해당 gw 를 통해 통신을 시도 하는데, 이 NAT GW가 통신 설정이 문제가 좀 있습니다.

추후 Cluster 구성 시에는 더욱 문제가 됩니다.(Join 구성이 안됩니다..) 그래서 강제적으로 Default GW를 변경하는 코드를 일부 추가 했습니다.

https://developer.hashicorp.com/vagrant/docs/networking/public_network#default-network-interface

Public Networks - Networking | Vagrant | HashiCorp Developer

Vagrant public networks are less private than private networks, and the exact meaning actually varies from provider to provider, hence the ambiguous definition. The idea is that while private networks should never allow the general public access to your ma

developer.hashicorp.com

 

 

  • PortForward 설정을 합니다.
        server["services"].each do |service|
          nodeconfig.vm.network "forwarded_port", id: service["id"], guest: service["gport"], host: service["hport"]
        end

 Public Network인 경우에는 필요 없습니다. IP로 직접 VM에 접근이 가능하니까요.

 

  • Provider가 Virtualbox인 경우에 VM의 구성을 합니다.
      nodeconfig.vm.provider :virtualbox do |vb|
        # VM의 이름을 지정합니다.
        vb.name = "#{server['hostname']}"
        
        # VirtualBox의 VM UI 창을 띄울지 여부를 지정 합니다.
        vb.gui = server["gui"] ? server["gui"] : false
      
        # VM의 Resource를 지정해 줍니다.
        vb.cpus   = server["cpu"] ? server["cpu"] : $V_CPU 
        vb.memory = server["ram"] ? server["ram"].to_s : $V_MEM
      end

 

  • File을 VM으로 Copy 합니다.
      if server.key?("resources")
        server["resources"].each do |resource|
          nodeconfig.vm.provision "file", source: "#{resource["src"]}", destination: "#{resource["dest"]}"
        end
      end

그런데, 생각보다 느립니다. VM에서 하는 작업은 일반적으로 다 느립니다. 

그래서 가급적 Volume 구성을 통해 파일을 접근하는 방법과 병행해서 사용하는게 좋습니다.

 

  • Volume 을 설정 합니다.
      if server.key?("volumes")
        server["volumes"].each do |volume|
          host_path = volume.split(':')[0].strip
          guest_path = volume.split(':')[1].strip
          nodeconfig.vm.synced_folder host_path, guest_path
        end
      end

 

  • Shell Script를 실행 합니다.
      # Timezone을 지정해 주고,
      nodeconfig.vm.provision "shell", inline: "timedatectl set-timezone Asia/Seoul"

      # 설치를 위한 Shell Script가 실행 되도록 합니다.
      if server.key?("init_command")
        nodeconfig.vm.provision "shell", inline: <<-SCRIPT
        echo "###########################################################"
        echo "##    RUN SCRIPT FROM VAGRANT BOX                        ##"
        echo "###########################################################"
        sudo #{server["init_command"]}
        echo "###########################################################"
        echo "##    FINISH SCRIPT FROM VAGRANT BOX                     ##"
        echo "###########################################################"
        SCRIPT
      end

 


(2) vmconfig.yml

 
설정 전에 IP 확인이 필요 합니다.

Host PC의 IP 확인

 
저는 IP가 192.168.0.185/16 으로 구성 되어 있고, 실제로는 192.168.0.1 의 GW를 사용합니다.
그러다 보니 192.168.0.156 이라는 IP를 사용하였습니다.
그리고 192.168.0.156이라는 IP는 현재 사용하지 않는 IP입니다.
각자 상황에 맞는 IP를 하나 정해 주시구요.
 

vagrant:
  version: 2

servers:

  - hostname: k8s

    resources: 
      - src: ./res-root/k8s
        dest: script

    init_command: chmod +x ./script/*.sh && ./script/setup.sh ./script

    # VM UI를 띄울지 여부
    gui: false

    # VM에서 사용할 Base Image.
    box: ubuntu/jammy64
    network:
      interface:
        nat: "enp0s3"
        bridge: "enp0s8"
      mode: public_network
      # 실제 사용하는 Network Interface의 ID
      bridge: wlo1
      # 설정할 고정 IP
      ip: 192.168.0.156
      # Gateway IP
      gw: 192.168.0.1
    # 리소스 설정
    cpu: 2
    ram: 2048

 
여기에 IP를 수정해 주시면 됩니다.
 

더보기
vagrant:
  version: 2

Vagrant API Version을 정의 합니다.

servers:
  - hostname: k8s

k8s 라는 hostname을 가지는 VM에 대한 설정을 정의 합니다.

이 hostname을 2개로 늘리면, VM이 2개가 생성되게 됩니다. 

    resources: 
      - src: ./res-root/k8s
        dest: script

 copy 하는 resource들을 정의 합니다.

./res-root/k8s 를 VM안에 script 라는 경로로 복사하게 됩니다.

    init_command: chmod +x ./script/*.sh && ./script/setup.sh ./script

시작할 command 를 정의 합니다.

"./script/setup.sh ./script"  라는 명령을 실행 시킵니다.

    # VM UI를 띄울지 여부
    gui: false

VirtualBox의 Guest UI를 띄울지 여부를 지정합니다.

저는 귀찮더라구요. 그래서 띄우지 않습니다. 

    # VM에서 사용할 Base Image.
    box: ubuntu/jammy64

 Box의 이미지를 지정합니다. ubuntu 22.04 버젼을 사용할 예정입니다.

    network:
      interface:
        nat: "enp0s3"
        bridge: "enp0s8"
      mode: public_network
      # 실제 사용하는 Network Interface의 ID
      bridge: wlo1
      # 설정할 고정 IP
      ip: 192.168.0.156
      # Gateway IP
      gw: 192.168.0.1

bridge network로 띄울겁니다.

vagrant에서 정의하는 public_network가  virtualbox 에서 bridge network 입니다.

ip는 통신이 가능한 ip로 설정 합니다. 즉, 공유기에 붙어 있는 IP와 충돌 되지 않는 IP를 지정해 주세요

 

  • network.interface.nat : nat network의 interface를 지정합니다.(기본은 eth0 입니다.)
  • network.interface.bridge : bridge network의 interface를 지정합니다.(기본으로 eth1 입니다.)

이 2개의 설정은 Box 이미지에 따라 달라질수 있습니다. 

실행 후 확인 한 다음 수정 해 주시면 됩니다.

network interface 확인 화면
Vagrant Box를 실행 후 vagrant ssh로 들어가면 확인 가능 합니다.

 

    # 리소스 설정
    cpu: 2
    ram: 2048

VM의 Resource를 지정합니다.

최소 이정도는 지정 해 주셔야 K8S가 올라 갈겁니다.

 
 


(3) 설치를 위한 Shell Script 파일들.

  • res-root/k8s/setup.sh

 
실행할 Script입니다.

#!/usr/bin/bash

export SCRIPT_PATH=${1:-~/script}
export K8S_VERSION=${2:-1.31}
export K8S_IP=${3:-192.168.0.156}
export K8S_HOST=${4:-master.k8s}

# master.k8s 라는 도메인으로 hosts 파일에 등록 함.
sudo sh -c "echo $K8S_IP $K8S_HOST >> /etc/hosts"

# Install Containerd & K8S
sudo apt-get update && sudo apt-get dist-upgrade -y
sudo $SCRIPT_PATH/install-containerd.sh
sudo $SCRIPT_PATH/install-k8s.sh $K8S_VERSION

# K8S Node Init
sudo systemctl restart containerd
sudo systemctl restart kubelet
cat << EOF > conf.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: "${K8S_IP}"
  bindPort: 6443
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: containerd
EOF
sudo kubeadm init --config=conf.yaml --v=5 > kubeinit.log && cat kubeinit.log

# Copy K8S Config
echo export KUBECONFIG=/etc/kubernetes/admin.conf >> $HOME/.bashrc

# Install Calico
sudo $SCRIPT_PATH/install-calico.sh

 
 
 
이 코드에서 export 한 변수 값들을 확인 합니다.

export SCRIPT_PATH=${1:-~/script}
export K8S_VERSION=${2:-1.31}
export K8S_IP=${3:-192.168.0.156}
export K8S_HOST=${4:-master.k8s}

 

  • SCRIPT_PATH: vmconfig.yaml 에서 정의한 경로와 맞춰 주시면 됩니다.(수정할 필요 없습니다.)
  • K8S_VERSION:  오늘(2024-08-29) 시점으로 최신 버젼이 1.31 입니다.
    이 버젼은 상황에 맞게 맞춰 주시면 됩니다.
  • K8S_IP : VM에서 사용할 IP를 넣어 주시면 됩니다.
  • K8S_HOST : K8S 내부에서 사용할 HostName 입니다.(수정할 필요 없습니다.)

 
그리고, CNI는 Calico를 사용하였습니다.
CNI(Container Network Interface)는 K8S에서 네트워크 모델을 구현하는 방법을 구현한 플러그인 으로 이해 하시면 될것 같습니다.
클러스터 네트워킹 | Kubernetes

클러스터 네트워킹

네트워킹은 쿠버네티스의 중심적인 부분이지만, 어떻게 작동하는지 정확하게 이해하기가 어려울 수 있다. 쿠버네티스에는 4가지 대응해야 할 네트워킹 문제가 있다. 고도로 결합된 컨테이너 간

kubernetes.io

 
네트워크 플러그인 | Kubernetes

네트워크 플러그인

쿠버네티스 1.31 버전은 클러스터 네트워킹을 위해 컨테이너 네트워크 인터페이스(CNI) 플러그인을 지원한다. 사용 중인 클러스터와 호환되며 사용자의 요구 사항을 충족하는 CNI 플러그인을 사용

kubernetes.io

 
 


  • res-root/k8s/install-container.sh

Install Docker Engine on Ubuntu | Docker Docs

Install Docker Engine on Ubuntu

Jumpstart your client-side server applications with Docker Engine on Ubuntu. This guide details prerequisites and multiple methods to install Docker Engine on Ubuntu.

docs.docker.com

#!/usr/bin/bash


# Containerd 설치
sudo apt-get install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.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 -y
sudo apt-get install -y containerd.io containernetworking-plugins
sudo systemctl enable containerd --now

sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.orig
containerd config default | sudo tee /etc/containerd/config.toml

sudo ssystemctl restart containerd

  • res-root/k8s/install-k8s.sh

kubeadm 설치하기 | Kubernetes

kubeadm 설치하기

이 페이지에서는 kubeadm 툴박스 설치 방법을 보여준다. 이 설치 프로세스를 수행한 후 kubeadm으로 클러스터를 만드는 방법에 대한 자세한 내용은 kubeadm으로 클러스터 생성하기 페이지를 참고한다.

kubernetes.io

#!/usr/bin/bash

export PARAM_K8S_VERSION=${1:-1.31}
export K8S_VERSION_LATEST=`curl -L -s https://dl.k8s.io/release/stable-$PARAM_K8S_VERSION.txt`
# export K8S_VERSION_LATEST=`curl -L -s https://dl.k8s.io/release/stable-1.31.txt`
# export K8S_VERSION_LATEST='v1.31.0'

export K8S_VERSION=${K8S_VERSION_LATEST:0:5}
#export K8S_VERSION=v1.31



# ---------------------------------------------------------------
# IPv4를 포워딩하여 iptables가 브리지된 트래픽을 보게 하기
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 필요한 sysctl 파라미터를 설정하면, 재부팅 후에도 값이 유지된다.
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

sudo echo '1' | sudo tee /proc/sys/net/ipv4/ip_forward

sysctl --system


# SWAP 제거
sudo swapoff -a
sudo sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab

# iptables 설정
SOURCE_FILE="/etc/sysctl.conf"
LINE_INPUT="net.bridge.bridge-nf-call-iptables = 1"
grep -qF "$LINE_INPUT" "$SOURCE_FILE"  || echo "$LINE_INPUT" | sudo tee -a "$SOURCE_FILE"

# 방화벽 등록
sudo ufw allow 179/tcp
sudo ufw allow 4789/udp
sudo ufw allow 5473/tcp
sudo ufw allow 443/tcp
sudo ufw allow 6443/tcp
sudo ufw allow 2379/tcp
sudo ufw allow 4149/tcp
sudo ufw allow 10250/tcp
sudo ufw allow 10255/tcp
sudo ufw allow 10256/tcp
sudo ufw allow 9099/tcp
sudo ufw allow 6443/tcp
# ---------------------------------------------------------------


# ---------------------------------------------------------------
# apt 패키지 인덱스를 업데이트하고, Kubernetes apt 저장소가 필요로 하는 패키지를 설치합니다.
sudo apt-get update -y
sudo apt-get install -y apt-transport-https ca-certificates curl

# 구글 클라우드의 공개 사이닝 키를 다운로드 한다.
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

# 쿠버네티스 apt 리포지터리를 추가한다.
curl -fsSL https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# apt 패키지를 업데이트하고, kubelet, kubeadm, kubectl을 설치합니다.
sudo apt-get update -y
sudo apt-get install -y kubelet kubeadm kubectl

# 그리고 kubelet, kubeadm, kubectl이 자동으로 업그레이드 되는 일이 없게끔 버전을 고정합니다.
sudo apt-mark hold kubelet kubeadm kubectl
# ---------------------------------------------------------------
 

 # conteaienrnetworking-plugins 설치
sudo apt install containernetworking-plugins

# Node-Shell (https://github.com/kvaps/kubectl-node-shell)
curl -LO https://github.com/kvaps/kubectl-node-shell/raw/master/kubectl-node_shell
chmod +x ./kubectl-node_shell
sudo mv ./kubectl-node_shell /usr/local/bin/kubectl-node_shell
# ----------------------------------------------------------



 sudo systemctl restart kubelet

 


  • install-calico.sh
#!/usr/bin/bash

curl https://calico-v3-25.netlify.app/archive/v3.25/manifests/calico.yaml -O --insecure
sed -i -e "s|# - name: CALICO_IPV4POOL_CIDR|- name: CALICO_IPV4POOL_CIDR|g" calico.yaml
kubectl apply -f calico.yaml
rm -f ./calico.yaml

 


2. 설치 (vagrant up)

 
이제 준비가 다 되었습니다.
vm을 기동하고 k8s 설치를 하면 됩니다.
 

(1) VM 기동

vagrant up
vagrant box 실행 (Network interface 선택)

 
이 과정에서 network interface를 선택해야 하는 경우도 있습니다.
제 환경은 유선랜과 무선랜 2개가 설치되어 있어서 이런 질문이 나옵니다.
이런 경우 실제 인터넷을 사용하는, 통신을 해야 하는 Network의 Interface를 선택 해 주면 됩니다.
Network Interface가 1개라면 이 화면은 나오지 않을겁니다.
 

vagrant 로 shell을 실행 시키는 화면
Shell Script 실행을 정의한 코드 부분

앞에서 정의한 Script가 실행 되는 모습이 보입니다.
 
조금 기다리면 기동이 끝납니다.
이제 K8S를 초기화 해 주면 됩니다.
 

(2)  K8S 설치 확인

vagrant shell로 들어갑니다.

vagrant ssh
vagrant ssh

 
kubectl, kubeadm의 버젼을 확인 해 봅니다.

kubeadm version
kubectl version

 

kubeadm version
kubectl version

 
 

sudo systemctl status containerd

 

sudo systemctl status containerd

 

sudo systemctl status kubelet

 

sudo systemctl kubelet

 
잘 설치 된것 같습니다.

kubeinit.log

 
홈 경로에 보면 kubeinit.log 라는 파일이 있습니다.
이 파일은 setup.sh 에서 kubeadm init 명령을 실행한 로그를 남긴 파일입니다.
 
 

setup.sh 의 kubeadm init 명령 부분

파일 내용을 보면,  설정파일을 복사하는 명령이 있습니다.
 

cat kubeinit.log

 

더보기

여기서 잠시 확인할 사항이 있습니다.

 

AWS EC2나 On-Premise 환경에서 직접 설치 하면, kubeadm init 명령만으로 cluster가 생성이 정상적으로 됩니다.

 

그런데 정확한 이유는 모르겠으나 VirtualBox에서 설치하면 설치 후 POD가 정상 기동이 되지 않는 현상이 있습니다.

그래서 부득이 conf.yaml 을 만들어서 설치 하였습니다.

 

사실 이 부분은 VM 특성같은데 제가 정확하게 원인 파악은 안되었고, 회피 방법만 알려 드립니다.

 

kubeconfig 설정 명령

 
그 부분을 실행 해 줍니다.

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

 
그 다음 kubectl 명령으로 node를 조회 해 봅니다.

kubectl get no
node 상태 조회

 

kubectl get pod -A
pod 상태 조회

 
정상적으로 잘 기동되고 있습니다.
 
calico가 기동되어 coredns도 정상적으로 Running 상태로 동작 하고 있습니다.
참고로 cni 가 설치 되지 않거나 접근 가능한 DNS가 없다면 coredns가 pending 상태가 됩니다.
만약 coredns가 pending 이라면 이 2가지를 먼저 확인 해 보시기 바랍니다.
 
다음에 POD와 Service를 기동하고 접근하는 테스트를 해 보겠습니다. 
 


1. [IaC] vagrant로 virtualbox의 VM 관리하기 (tistory.com)
2. [K8S] Kubernetes 설치 (VirtualBox + Vagrant + K8S:v1.31) (tistory.com)
3. [K8S] Kubernetes Nodeport로 서비스 테스트 하기