GitLab Runner로 가상서버(VM) 및 k8s 배포 하기

정보시스템을 구성하는데 있어 모든 IT 인프라를 물리적으로 구성하는 것은 너무도 큰 비용과 시간 그리고 인력의 투입을 필요로 한다. 그렇기에 최근 정보시스템의 구축·운영 패러다임은 가상화다. 그리고 응용프로그램 뿐만 아니라 인프라 조차도 IaC(Infra as a Code)를 통해 가상화 환경으로 배포한다. 그리고 이런 패러다임의 추종은 인프라 및 응용프로그램의 구성이 너무도 복잡해진 테스트 용 홈랩 환경에서도 필수적이다.

필자는 Proxmox VE 클러스터 환경에 홈랩을 구성하였고 Windows 11 PC에 VSCode와 Terraform을 설치한 다음 IaC 코드를 작성하여 Proxmox 클러스터에 쿠버네티스 환경의 테스트를 위한 가상서버 4대를 배포하였다. 그리고 Windows 11의 WSL2 환경에서 Ansible과 Kubespray를 사용해 4대의 가상서버에 마스터노드 1대와 워커 3대를 배포했다. 하지만 이런 방법도 꽤나 복잡하고 한참 시간이 흐르면 그 과정을 기억해내 다시 재현하는 것이 그다지 쉽지 않다. 그래서 보다 쉽게 이러한 과정을 반복 실행할 수 있도록 GitLab 서버를 가상서버로 설치하고 관련 코드들을 모두 프로젝트의 리포지토리에 저장한 다음 GitLab 러너를 사용해 재실행할 수 있도록 자동화하였다.

GitLab의 Infra 프로젝트에 IaC 코드 Publish

Infra 프로젝트에 저장하고 실행할 코드는 모두 3종이다.

  • 가상서버 생성을 위한 Proxmox 가상머신 템플릿 생성하는 코드 (Ubuntu 24.04 LTS)
  • 가상머신 템플릿으로 노드를 배포하는 IaC 코드 (Terraform)
  • Ansible과 Kubespray로 노드에 쿠버네티스를 배포하는 코드

먼저 이 세 코드를 실행하는 GitLab CI/CD 파이프라인 파일을 다음과 같이 작성한다.

# 3단계의 스테이지 정의. GitLab 프로젝트에 push 한 다음 Build - Pipelines 메뉴에 보이는 Status에 들어가면 3단계의 스테이지가 순서대로 보임. 각각 순서대로 실행되며 각 Stage의 when: manual 이면 수동으로 실행할 수 있음
stages:
  - make-template
  - vm-creation
  - k8s-deployment

# 첫 번째 스테이지-  Proxmox 호스트 1대에 설치한 Runner가 create_ubuntu_template.sh를 실행해 템플릿을 생성함 
create_proxmox_template:
  stage: make-template
  tags:
    - proxmox
  when: manual
  script:
    - echo "Starting Proxmox template creation job..."
    - cd make-template
    - chmod +x create_ubuntu_template.sh
    - sudo ./create_ubuntu_template.sh
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "infra_dev"

# 두 번째 스테이지 -  생성한 템플릿을 사용해 VM을 생성함
create_vms:
  stage: vm-creation
  tags:
    - proxmox
  when: manual
  script: |
    echo "Starting Virtual Machine Creation..."
    cd ubuntu24.04-vm-creation
    terraform init \
      -backend-config="address=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/default" \
      -backend-config="lock_address=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/default/lock" \
      -backend-config="unlock_address=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/default/lock" \
      -backend-config="username=gitlab-ci-token" \
      -backend-config="password=${CI_JOB_TOKEN}" \
      -backend-config="lock_method=POST" \
      -backend-config="unlock_method=DELETE" \
      -backend-config="retry_wait_min=5"
    terraform apply -auto-approve -parallelism=1
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "infra_dev"

# 세 번째 스테이지 - 사전에 python3, python3-pip, python3-venv 가 설치되어 있어야 함. Ansible은 git clone으로 내려받은 kubespray의 코드의 requirements.txt에 의해 설치됨
deploy_k8s:
  stage: k8s-deployment
  tags:
    - k8s
  when: manual
  variables:
    ANSIBLE_HOST_KEY_CHECKING: "False"
  script: |
    echo "Deploying Kubernetes Cluster with Kubespray..."
    
    # 1. SSH Key Setup
    mkdir -p ~/.ssh
    chmod 700 ~/.ssh
    # Hardcoded key for testing. 테스트 용이며 이 SSH key는 추후 GitLab의 Variable로 이전해야 함.
    cat <<EOF > ~/.ssh/id_rsa
    -----BEGIN RSA PRIVATE KEY-----
    MIIEogIBAAKCAQEAmP+Pf3GZ04r2j0OGNBUJloSsuyxQPKlqfJIcij1ng7oNXNVv
    T/tZXJ/hkAkVaVwhSwGgKKNTTt7o+QmnIBleRSWBHNQ8woaVAV0IVvQ6T8URp5uO
    ....
    F1QfwcgRWAvUGYffOFkP7AGapCvZb9qa8hyEjhUug1+EySzBV6oQ2hZXUnrr5AzC
    uCRQdZP0fqgNTPukcoMzacZAdGgDJR6wXwao/z7C7BvUvEswyGE=
    -----END RSA PRIVATE KEY-----
    EOF
    chmod 600 ~/.ssh/id_rsa
    
    # 2. Kubespray Setup
    if [ -d "kubespray" ]; then rm -rf kubespray; fi
    git clone --branch v2.26.0 https://github.com/kubernetes-sigs/kubespray.git
    cd kubespray
    
    # 3. Python Venv & Requirements
    python3 -m venv venv
    source venv/bin/activate
    pip install -U -r requirements.txt
    
    # 4. Inventory Generation
    mkdir -p inventory/mycluster
    cp -rfp inventory/sample/group_vars inventory/mycluster/
    cat <<EOF > inventory/mycluster/inventory.ini
    [all]
    k8s-m ansible_host=172.16.0.10 ip=172.16.0.10 etcd_member_name=etcd1
    k8s-w1 ansible_host=172.16.0.11 ip=172.16.0.11
    k8s-w2 ansible_host=172.16.0.12 ip=172.16.0.12
    k8s-w3 ansible_host=172.16.0.13 ip=172.16.0.13

    [kube_control_plane]
    k8s-m

    [etcd]
    k8s-m

    [kube_node]
    k8s-w1
    k8s-w2
    k8s-w3

    [calico_rr]

    [k8s_cluster:children]
    kube_control_plane
    kube_node
    calico_rr
    EOF
    
    # 5. Run Ansible Playbook
    ansible-playbook -i inventory/mycluster/inventory.ini \
      --user=taeho \
      --private-key=~/.ssh/id_rsa \
      --become \
      --become-user=root \
      -e "calico_ipip_mode=Never" \
      -e "calico_vxlan_mode=Never" \
      -e "calico_network_backend=bird" \
      -e "resolvconf_mode=host_resolvconf" \
      cluster.yml
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "infra_dev"

다음은 두 번째 스테이지에서 실행될 IaC 코드다. 먼저 Terraform이 읽어 실행할 main.tf 파일이다.

다음은 main.tf에서 사용되는 변수가 포함된 terraform.tfvars 파일이다. 실제 생성할 가상머신들의 정보와 어느 플랫폼에 생성을 요청할 것인지를 정의하는 url이 보인다. 여기서는 Proxmox VE가 되겠다.

생성할 VM 정보를 추가하거나 변경할 수 있다. 필요시 재생성에도 사용될 수 있다.

이 코드들을 GitLab의 infra 프로젝트에 Push 했다.

GitLab의 CI/CD 파이프라인 실행

앞의 화면에서 처럼 infra 프로젝트의 리포지토리에 있는 infra_dev 브랜치에 IaC 코드가 Push되었다. 코드가 Push되면 아래에 있는 Build 메뉴의 Pipelines 메뉴로 이동한다.

Status를 보면 Push 될 때마다 Skipped 된 파이프라인이 보인다. 파이프라인이 실행되지 않고 Skipped 상태로 표시되는 이유는 앞의 .gitlab-ci.yml 파일의 내용 중 Stage 별로 정의된 when: manual 이라는 설정 때문이다. 스테이지가 manual로 설정되어 있기 때문에 자동으로 실행되지 않는다. 파이프라인을 실행하기 위해서는 이 화면으로 들어온 다음 Skipped를 클릭하면 보여지는 다음 화면의 각 스테이지를 수동으로 실행하면 된다.

위 화면은 k8s-deployment 스테이지만 별도로 실행한 화면이다.


Proxmox에서 가상머신이 배포되는 화면

앞의 CI/CD 파이프라인 중 vm-creation 스테이지가 실행되면 다음과 같이 Proxmox VE 관리자 페이지에서 실시간으로 확인이 가능하다. 먼저 템플릿을 이용해 생성할 가상머신을 템플릿이 있는 Proxmox 호스트에 생성한다.

생성 중이기 때문에 자물쇠가 표시된다. 그리고 나서 Terraform IaC 코드에서 정의한 대로 각각의 Proxmox 호스트로 마이그레이션 해준다.

k8s-w1은 prxmx 호스트에, k8s-w2는 prxmx2 호스트로 이전했다. 그리고 자물쇠가 사라지고 중지된 상태로 변했다. 아직 k8s-w3는 배prxmx3 호스트로 이전되기 전이다.

최종적으로 가상머신 4대가 모두 생성되고 배포된 화면이다.

배포가 완료되면 실행까지 자동으로 이루어진다.

그리고 CI/CD 파이프라인의 마지막 단계인 k8s-deployment가 실행되고 나면 다음과 같이 정상적으로 구동된 쿠버네티스 환경을 확인할 수 있다.

이제 필요할 때 K8S 테스트를 위한 가상서버 4대와 쿠버네티스 설치까지 버튼 클릭 몇 번으로 손쉽게 재-구축할 수 있게 되었다.

#GitLab #IaC #k8s배포

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다