ConfigMap & Secret

일반적으로 배포 환경은 1~N개의 환경을 가진다. 개인이 운영하는 서비스에서도 최소한 개발 환경과 운영 환경으로 나눌 필요가 있고 회사에서 담담하는 서비스도 파트에서만 개발계와 검증계 2 가지 환경으로 운영하며 협력하는 파트와 개발계 / 검증계 / 유지보수계 / 운영계 총 4개의 환경으로 운영한다. 각 환경마다 환경 변수나 설정 값들이 다 다를 것인데 이때 쿠버네티스에서 환경 별로 값들을 다르게 할 수 있는 용도로 ConfigMap과 Secret을 제공한다.

1. ConfigMap

ConfigMap은 Key-Value 쌍의 형태로 데이터를 저장하는 데 사용하는 쿠버네티스 오브젝트이다. 보통 컨피그맵을 생성하고 파드에 해당 컨피그맵을 주입하는 방식으로 파드에서 컨피그맵을 사용한다. 파다의 환경변수에 컨피그맵의 값을 바인딩하거나 파드 볼륨을 생성해서 볼륨 마운팅을 통해 파일 형태로 컨피그맵을 사용할 수 있다.

2. Secret

컨피그맵은 평문을 사용하기 때문에 암호, 토큰, 키 등 중요한 정보를 직접 적는 것은 바람직하지 않다. 컨피그맵과 유사하지만 특히 기밀 데이터를 보관하기 위해서 사용된다. 인코딩은 base64를 이용해서 하게 되는데 base64는 누구나 디코딩 할 수 있기 때문에 완벽하게 안전다고 할 수는 없다. 하지만 평문보다는 안전한 것은 맞으며 추가적인 플러그인을 통해서 암호화를 하는 것이 좋다. 기본 데이터 저장소 etcd에 암호화되지 않은 상태로 저장되기 때문에 etcd에 접근을 제한하는 것 역시 중요하다. 특히, 외부에서는 접속하는 것을 제한하는 것 좋다.

ConfigMap & Secret 사용하는 3가지 방법

1. Literal

ConfigMap을 yml파일에 정의하거나 커맨드라인 입력을 통해 생성하는 방법이다.

kubectl create configmap hello-cm --from-literal=language=java

apiVersion: v1
kind: ConfigMap
metadata:
  name: literal-cm
data:
  today: monday
  month: october

2. File

각각의 환경 변수들을 공유할 수 있지만 파일 형태로 만들어서 Pod에 공유하는 방법도 있다.
pod1이 참조하고 있는 file-c.txt의 내용이 바뀌어도 Pod의 환경변수는 변하지 않는다. Pod가 재성성 되어야만 바뀐 ConfigMap이 적용된다.

kubectl create configmap cm-file --from-file=./properties/profile.properties

key는 파일명이며 value는 파일 내용이다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container
    image: example/test
    env:
    - name: config-env
      valueFrom:
        configMapKeyRef:
          name: file-cm
          key: config.txt

3. VolumeMount

Pod생성 시 mount경로를 정의하고, 해당 경로 안에 ConfigMap파일을 연결시켜 주는 방식이다. File을 ConfigMap으로 설정하는 것과 마찬가지로 ConfigMap을 정의해둔다. 생성하고자 하는 Pod의 volumes에 volume의 이름과 사용하고자 하는 configMap의 이름을 적어준다. containers 부분에 마운트할 볼륨과 마운트 경로를 적어주면 된다. 위의 File을 ConfigMap으로 설정하는 것과 달리 파일을 내용이 변하게 되면 Pod의 재생성 없이도 변경된 데이터를 즉시 반영할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  containers:
  - name: container
    image: example/test
    volumeMounts:
    - name: volume1
      mountPath: /mount
  volumes:
  - name: volume1
    configMap:
      name: file-cm

쿠버네티스 기본 오브젝트 -1

Pod

파드는 쿠버네티스에서 생성하고 관리할 수 있는 배포 가능한 가장 작은 컴퓨터 단위이다. 공유 네임스페이스와 공유 파일시스템 볼륨이 있는 컨테이너들의 집합과 비슷하며 일반적으로 파드는 직접 생성하지는 않으며, 대신 워크로드 리소스를 사용하여 생성한다. 파드 안에는 독립적인 서비스를 구동할 수 있는데 서비스 간의 포트가 중복될 수는 없다. 또한, 파드가 생성될 때는 IP는 자동 할당되며 재생성시 변경된다.

1. Container

실행하는 각 컨테이너는 반복 가능하다. 의존성이 포함된 표준화는 어디에서 실행하던지 동일한 동작을 얻는다는 것을 의미한다.

컨테이너는 기본 호스트 인프라에서 애플리케이션을 분리한다. 따라서 다양한 클라우드 또는 OS 환경에서 보다 쉽게 배포할 수 있다.

쿠버네티스 클러스터에 있는 개별 node는 해당 노드에 할당된 파드를 구성하는 컨테이너들을 실행한다. 파드 내부에 컨테이너들은 같은 노드에서 실행될 수 있도록 같은 곳에 위치하고 함께 스케줄된다.

2. Label

모든 객체에 라벨을 연결할 수 있고 라벨 별로 구분 짓기 위함이다.

예를 들어, 이렇게 6개의 Pod가 있을 경우 3개의 Label은 Dev, 다른 3개의 Label은 production으로 환경을 나눠서 운영할 수 있다.

3. Node Schedule

쿠버네티스에서 스케줄링은 Kubelet이 파드를 실행할 수 있도록 파드가 노드에 적합하지 확인하는 것을 말한다. 스케줄링 결정을 위해 고려해야 할 요소에는 개별 및 집단 리소스 요구사항, 하드웨어 / 소프트웨어 / 정책 제한조건, 어피니티 및 안티-어피니티 명세, 데이터 지역성(data locality), 워크로드 간 간섭 등이 포함된다.

Service

실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법이며 쿠버네티스를 사용하면 익숙하지 않은 서비스 디스커버리 메커니즘을 사용하기 위해 애플리케이션을 수정할 필요가 없다. 쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.

1. ClusterIP

서비스를 클러스터-내부 IP에 노출시킨다. 이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다. 이것은 서비스의 type을 명시적으로 지정하지 않았을 때의 기본값이다.

2. NodePort

고정 포트 (NodePort)로 각 노드의 IP에 서비스를 노출시킨다. 노드 포트를 사용할 수 있도록 하기 위해, 쿠버네티스는 type: ClusterIP인 서비스를 요청했을 때와 마찬가지로 클러스터 IP 주소를 구성한다.

3. Load Balancer

클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 서비스를 외부에서 접근 가능하게 하기 위해서 Load Balancer 방식을 사용해야한다.

Volume

쿠버네티스는 다양한 유형의 볼륨을 지원한다. 파드는 여러 볼륨 유형을 동시에 사용할 수 있다. 임시 볼륨 유형은 파드의 수명을 갖지만, 퍼시스턴트 볼륨은 파드의 수명을 넘어 존재한다. 파드가 더 이상 존재하지 않으면, 쿠버네티스는 임시(ephemeral) 볼륨을 삭제하지만, 퍼시스턴트(persistent) 볼륨은 삭제하지 않는다. 볼륨의 종류와 상관없이, 파드 내의 컨테이너가 재시작되어도 데이터는 보존된다.

1. emptyDir

emptyDir 볼륨은 파드가 노드에 할당될 때 처음 생성되며, 해당 노드에서 파드가 실행되는 동안에만 존재한다. 이름에서 알 수 있듯이 emptyDir 볼륨은 처음에는 비어있다. 파드 내 모든 컨테이너는 emptyDir 볼륨에서 동일한 파일을 읽고 쓸 수 있지만, 해당 볼륨은 각각의 컨테이너에서 동일하거나 다른 경로에 마운트될 수 있다. 어떤 이유로든 노드에서 파드가 제거되면 emptyDir 의 데이터가 영구적으로 삭제된다.

emptyDir 의 일부 용도는 다음과 같다.

  • 디스크 기반의 병합 종류와 같은 스크레치 공간
  • 충돌로부터 복구하기위해 긴 계산을 검사점으로 지정
  • 웹 서버 컨테이너가 데이터를 처리하는 동안 컨텐츠 매니저 컨테이너가 가져오는 파일을 보관

2. hostPath

hostPath 볼륨은 호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 마운트 한다. 이것은 대부분의 파드들이 필요한 것은 아니지만, 일부 애플리케이션에 강력한 탈출구를 제공한다.

예를 들어, hostPath 의 일부 용도는 다음과 같다.

  • 도커 내부에 접근할 필요가 있는 실행중인 컨테이너. /var/lib/docker 를 hostPath 로 이용함
  • 컨테이너에서 cAdvisor의 실행. /sys 를 hostPath 로 이용함
  • 파드는 주어진 hostPath 를 파드가 실행되기 이전에 있어야 하거나, 생성해야 하는지 그리고 존재해야 하는 대상을 지정할 수 있도록 허용함

경고:

HostPath 볼륨에는 많은 보안 위험이 있으며, 가능하면 HostPath를 사용하지 않는 것이 좋다. HostPath 볼륨을 사용해야 하는 경우, 필요한 파일 또는 디렉터리로만 범위를 지정하고 ReadOnly로 마운트해야 한다.

3. PVC / PV

persistentVolumeClaim 볼륨은 퍼시스턴트볼륨을 파드에 마운트하는데 사용한다. 퍼시스턴트볼륨클레임은 사용자가 특정 클라우드 환경의 세부 내용을 몰라도 내구성이있는 스토리지 (GCE 퍼시스턴트디스크 또는 iSCSI 볼륨와 같은)를 “클레임” 할 수 있는 방법이다.

퍼시스턴트볼륨 (PV)은 관리자가 프로비저닝하거나 스토리지 클래스를 사용하여 동적으로 프로비저닝한 클러스터의 스토리지이다. 노드가 클러스터 리소스인 것처럼 PV는 클러스터 리소스이다. PV는 Volumes와 같은 볼륨 플러그인이지만, PV를 사용하는 개별 파드와는 별개의 라이프사이클을 가진다. 이 API 오브젝트는 NFS, iSCSI 또는 클라우드 공급자별 스토리지 시스템 등 스토리지 구현에 대한 세부 정보를 담아낸다.

퍼시스턴트볼륨클레임 (PVC)은 사용자의 스토리지에 대한 요청이다. 파드와 비슷하다. 파드는 노드 리소스를 사용하고 PVC는 PV 리소스를 사용한다. 파드는 특정 수준의 리소스(CPU 및 메모리)를 요청할 수 있다.

참고 자료

https://kubernetes.io/ko/

Vagrant를 통해 쿠버네티스(K8S) 구축하기

쿠버네티스 사용하기 위한 몇 가지 방법

1. Virtual Machine에 Master node와 Worker node를 직접 구축
2. AWS, Azure, GCP 등 쿠버네티스 서비스를 사용
3. 웹 형태로 제공되는 쿠버네티스 서비스 이용

두 번째와 세 번째 방법이 편하지만 일정 금액을 지불해야 한다는 단점이 존재합니다. 또한, 쿠버네티스 환경을 잘 모르기 때문에 쿠버네티스 환경을 직접 구축해보면서 어떻게 구성되어 있는지 알아볼 겸 직접 구축하기로 했습니다.

VM에 직접 OS를 설치하고 쿠버네티스 클러스터를 구축하는 것은 많은 시간이 소요가 됩니다. 또한, 설치 순서가 틀린 경우 다시 구축하는 것이 빠를 정도로 순서가 복잡합니다. 그래서 설치 방법을 템플릿화하는 것이 좋다고 생각 되었고 vagrant를 사용해서 쿠버네티스 클러스터를 구축한다면 시간 단축도 되고 일관성 가지면서 클러스터를 구축할 수 있다는 사실을 알았습니다. vagrant는 IaC(Infrastructure as Code)의 줄인 말이고 코드를 사용해서 VM에 OS를 설치하고 서버를 배포할 수 있도록 자동화하는 도구입니다.

Vagrant를 사용해 쿠버네티스 클러스터 구축

1. VM Provider 설치 및 Vagrant 설치

VM Provider로 VirtualBox를 사용하기로 했고 버전은 7.0.14입니다
VirtualBox 다운 링크: https://www.virtualbox.org/wiki/Downloads

Vagrant는 Window 환경으로 다운을 받았고 버전은 2.4.1입니다
Vagrant 다운 링크: https://developer.hashicorp.com/vagrant/install

2. Vagrant Script 작성 및 실행

먼저, 생성할 VM 스펙을 정의합니다. 대표적으로 OS, network, provider (hyper-v, virtual box, vmware), resources(cpu, memory)가 있습니다. master node는 2CPU, 4GB MEM, worker node는 2CPU, 2GB MEM로 자원을 할당할 예정입니다. 저의 PC 사양은 CPU는 11th Gen Intel(R) Core(TM) i7-1165G7, Memory는 16GB입니다. 그 후 설치 스크립트를 작성합니다. 저는 master와 worker 의 공통으로 설치되는 부분을 먼저 정의하고 이후 master node에 필요한 스크립트만 따로 정의했습니다. VM에는 ssh로 접속할 예정이라서 gui 옵션은 껐습니다.

아래는 실제로 사용한 스크립트입니다.

$pre_install = <<-SCRIPT
  echo ">>>> pre-install <<<<<<"
  echo 'root:password1234' | sudo chpasswd
  sudo echo "192.168.1.100 master" >> /etc/hosts
  sudo echo "192.168.1.101 worker1" >> /etc/hosts
  sudo echo "192.168.1.102 worker2" >> /etc/hosts
  sudo sysctl net.ipv4.ip_forward=1
  sudo modprobe br_netfilter
  sudo swapoff -a
  
  echo ">>>> Install Containerd <<<<<<"
  sudo apt-get update
  sudo apt-get install ca-certificates curl gnupg -y
  sudo install -m 0755 -d /etc/apt/keyrings
  sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  sudo chmod a+r /etc/apt/keyrings/docker.gpg
  sudo echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  sudo apt-get update
  sudo apt-get install containerd.io
  sudo echo "" > /etc/containerd/config.toml
  sudo systemctl restart containerd

  echo ">>>> Install K8s Component <<<<<<"
  sudo apt-get install -y apt-transport-https
  sudo curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
  echo "deb [signed-by=/etc/apt/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
SCRIPT

$mater_config = <<-SCRIPT
  echo ">>>> Master Node Config <<<<<<"
  sudo ssh-keyscan worker1 >> ~/.ssh/known_hosts
  sudo ssh-keyscan worker2 >> ~/.ssh/known_hosts
  sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address 192.168.1.100 2>&1 | tee /root/kubeadm_init_output.txt
  sudo mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config
  export KUBECONFIG=/etc/kubernetes/admin.conf
SCRIPT


Vagrant.configure("2") do |config|
  # Master 노드 설정
  config.vm.define "master" do |master|
    master.vm.box = "bento/ubuntu-22.04"
    master.vm.network "private_network", ip: "192.168.1.100"
    master.vm.provider "virtualbox" do |v|
      v.name = "Master"
      v.gui = false
      v.memory = 4096
      v.cpus = 2
    end
    master.vm.hostname = "master"
    master.vm.provision "shell", inline: $pre_install
    master.vm.provision "shell", inline: $mater_config
  end

  # Worker 노드 설정
  (1..2).each do |i|
    config.vm.define "worker#{i}" do |worker|
      worker.vm.box = "bento/ubuntu-22.04"
      worker.vm.network "private_network", ip: "192.168.1.11#{i}"
      worker.vm.provider "vmware_desktop" do |v|
        v.name = "Worker#{i}"
        v.gui = false
        v.memory = 2048
        v.cpus = 2
      end
      worker.vm.hostname = "worker#{i}"
      worker.vm.provision "shell", inline: $pre_install
    end
  end
end

3. 쿠버네티스 클러스터 구축 마무리

한 대의 master node와 두 대의 worker node가 설치되었다면 worker node를 master node에 join 시켜야 합니다.
master node에서 sudo 권한으로 tail -n 2 kubeadm_init_output.txt 명령을 실행하면 master node에 join할 수 있는 hash 값이 나오게 되는데 해당 값을 그대로 worker node에 적용하면 됩니다.
master node에 join

적용 후 쿠버네티스 노드들을 조회해보면 master node에서 worker node의 상태를 확인할 수 있습니다. 현재는 네트워크 연결이 되어 있지 않기 때문에 NotReady 상태입니다. master node에서 calico를 적용하게 되면 노드 간의 네트워크가 연결됩니다.

마지막으로, master node에서 kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml 명령어를 실행하게 되면 쿠버네티스 클러스가 구축됩니다.


4. 쿠버네티스를 구축할 때 몇 가지 주의할 점

첫번째로 VM을 생성할 때는 최소 사용이 2 CPU, 2 memory를 가져야 한다는 것입니다.
두번째로 swap off를 해줘야 하는데 쿠버네티스는 자신의 자원을 최대한 활용하는 것이 목적이기 때문에 swap에 대한 지원이 따로 없기 때문입니다. 충분한 자원이 없어서 swap를 사용하게 된다면 master 노드에서 아래와 같은 에러를 만날 수 있습니다.

swapoff를 안 했을 경우 오류

충분한 자원을 가지고 쿠버네티스 클러스터를 구축한다면 네트워크를 연결을 제외하면 NotReady인 상태로 클러스터가 구축이 됩니다. 이후 클러스터 간의 네트워크 연결을 하게 된다면 Ready 상태로 Node간의 통신을 할 수 있는 클러스터 환경이 만들어지게 됩니다.

전반적인 쿠버네티스 클러스터 구성하는 방법에 대해서 알아봤으며 다음장부터는 쿠버네티스의 세부적인 내용에 대해서 다룰 예정입니다.

참고 자료

https://alive-wong.tistory.com/67