Skip to content

Kumin-91/mdi-yang-lab

Repository files navigation

Model-Driven Infrastructure: A YANG-Based Laboratory

YANG libyang JSON
Shell Python
Terraform Ansible Jinja2
MinIO Redis JuiceFS
Tailscale K3s

One Logical Infrastructure, Many Physical Realities: A YANG-Driven Hybrid Cloud with K3s

📄 Laboratory Overview

📈 Key Results

🗿 Next Milestone

📑 How to Use This Repository

🛠️ Tools and Use Cases

📄 Laboratory Overview

Model is the Control Plane, not the Reality Plane.
본 프로젝트는 이 한 문장을 증명하기 위한 실험실입니다. 지리적으로 떨어진 AWS (ARM/x86)와 온프레미스 자원이라는 현실 (RAW)을, YANG 모델이라는 정교한 질서 (PURE) 아래 하나의 클러스터로 수렴시키는 과정을 다룹니다.

YANG 모델링을 통한 추상화부터 데이터 검증, 스토리지 통합, 그리고 Terraform/Ansible을 활용한 자동화까지 하이브리드 클라우드 구축의 전 과정을 다룹니다. 파편화된 물리적 자원들을 단일 진실 원천 (SSoT)인 모델을 중심으로 결합하여, 설계가 곧 실제 인프라가 되는 모델-드리븐 인프라 (MDI)를 구현합니다.

📈 Key Results

  • Unified SSoT: YANG 모델링을 통해 이기종 플랫폼 (AWS/On-Premise)의 리소스 명세를 통합하고 플랫폼 독립적인 설계도를 완성했습니다.

  • Mesh Networking Backbone: Tailscale을 활용해 모든 노드를 하나의 오버레이 네트워크로 연결하여 지리적 제약이 없는 일관된 통신 환경을 구축했습니다.

  • Storage Abstraction: JuiceFS를 통해 하이브리드 환경의 스토리지를 논리적으로 통합하여, 모든 노드에서 즉시 접근 가능한 공유 저장소를 확보했습니다.

  • End-to-End MDI Pipeline: YANG 설계도가 Terraform, Shell Script, Ansible을 거쳐 실제 인프라로 자동 전환되는 완전한 인프라 파이프라인을 시연했습니다.

📑 How to Use This Repository

Usage: make [target]
Targets:
    help            - 이 도움말 메시지를 출력합니다.
    all             - 모든 과정을 실행합니다.
    keygen          - [Phase 0] SSH 키 페어를 생성합니다.
    lint            - [Phase 2] YANG 모델 검사 및 JSON 검증을 실행합니다.
    lint-test       - [Phase 2] 에러가 있는 JSON 파일로 YANG 모델 검사를 테스트합니다.
    provision       - [Phase 4] AWS/On-premises 인프라 프로비저닝을 실행합니다.
    bootstrap       - [Phase 5] Ansible playbook을 실행하여 bootstrap을 수행합니다.
    aws-purge       - [Phase 4] AWS infrastructure를 제거합니다.

Implementation Details

Phase 0. Physical Inventory & Resource Specification

Phase 1. Logical Abstraction via YANG Modeling

Phase 2. Data Integrity & Schema Validation

Phase 3. Storage Abstraction: JuiceFS Infrastructure Setup

Phase 4. Automating Node Provisioning with Terraform & Shell Scripting

Phase 5. Ansible-Driven Bootstrapping & Configuration Management

Phase 0. Physical Inventory & Resource Specification

Step 0. Shared Public Key Authentication Setup

모든 노드 (AWS & On-Premise)의 통합 관리를 위해 공통 SSH Key Pair를 생성합니다.

# 로컬에서 SSH Key Pair 생성
mkdir -p ./00-key
ssh-keygen -t ed25519 -f ./00-key/hybrid-cloud -N ""

# 생성된 공개 키 확인
cat ./00-key/hybrid-cloud.pub

Step 1. Hardware Inventory & Compute/Storage Quotas

Location IP Port Compute Arch Burstable Cache Quota
Site A 192.168.1.202 22 4 vCPU / 8GB RAM x86_64 No 30GB NVME
Site B 192.168.1.203 22 4 vCPU / 8GB RAM x86_64 No 20GB SSD
AWS Dynamic 22 t4g.medium arm64 Yes 5GB EBS
AWS Dynamic 22 t3.medium x86_64 Yes 5GB EBS

AWS의 IP 주소는 Terraform 프로비저닝 후 동적으로 할당됩니다.

Step 2. Strategic Role Allocation & Infrastructure Hierarchy

Location Node Name Node Role Bootstrap Role Strategy
Site A site-a-node control-plane join HA을 위한 보조 제어 노드
Site B site-b-node worker join 확장된 워커 노드
AWS t4g-seed control-plane init 클러스터 시드 노드
AWS t4g-a worker join 확장된 워커 노드
AWS t4g-b worker join 확장된 워커 노드
AWS t3-a control-plane join 확장된 보조 제어 노드
AWS t3-b worker join 확장된 워커 노드
AWS t3-c worker join 확장된 워커 노드

Phase 1. Logical Abstraction via YANG Modeling

Step 1. Base Type Definitions: nodes/common-types.yang

  • Platform (AWS/On-Premise), Arch, K3s Role 등에 대한 표준 데이터 타입을 정의합니다.

  • AWS 인스턴스 타입은 정규표현식을 통해 medium 이하 규격으로 엄격히 제한됩니다.

Step 2. Compute Resource Abstraction: nodes/res-compute.yang

  • 플랫폼별 연산 자원 명세를 담당하며, Platform 값에 따라 입력 항목을 동적으로 강제합니다.

  • AWS: instance-type 기반의 규격화된 자원 할당.

  • On-Premise: vCPU 및 Memory 범위를 직접 지정하여 하드웨어 제약 내에서 자원을 선언합니다.

Step 3. Network Perimeter & Policy Modeling: nodes/res-network.yang

  • 초기 프로비저닝 (Underlay)을 위한 접속 계정과 포트 정보를 정의합니다.

  • AWS의 Late Binding 전략과 On-Premise의 Early Binding 전략을 구분하여 모델링합니다.

Step 4. Distributed Storage Logic Modeling: nodes/res-storage.yang

  • 분산 스토리지 (JuiceFS) 환경 구성을 위한 엔드포인트와 캐시 정책을 정의합니다.

  • 자격 증명과 같은 민감 데이터를 YANG 명세에서 배제하고 앤서블의 동적 주입 방식으로 전환하여, 설계도의 범용성을 높이고 보안 사고를 원천 차단합니다.

Step 5. Holistic Cluster Integration: nodes/hybrid-cloud.yang

  • 개별 리소스 모델을 통합하여 하이브리드 클러스터의 단일 진실 원천 (SSoT)을 구축합니다.

  • 각 노드의 역할 할당 (control-plane, worker)과 부트스트랩 전략 (init, join)을 명세하여, 클러스터 부트스트래핑과 확장 시나리오를 지원합니다.

Step 6. Provider-Specific Extensions: providers/aws-provider.yang

  • AWS 프로비저닝에 특화된 YANG 모델로, AWS 고유의 자원 명세와 제약 조건을 포함합니다.

  • Region, VPC, Subnet, Security Group 등 AWS 인프라 구성 요소에 대한 세부적인 모델링을 통해, Terraform 코드와의 완벽한 매핑을 지원합니다.

Phase 2. Data Integrity & Schema Validation

Step 0. Environment Setup (macOS with Homebrew): CESNET/libyang

# 레포지토리 클론
git clone https://github.com/CESNET/libyang.git

# 필요 패키지 설치
brew install cmake pcre2 pkg-config

# 빌드 및 설치
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local \
      -DCMAKE_INSTALL_RPATH="/usr/local/lib" \
      -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
make -j$(sysctl -n hw.ncpu)
sudo make install
# 설치 확인
yanglint -version
# yanglint 4.2.2

Step 1. Hierarchical Schema Visualization & Structural Audit

  • YANG 모델의 계층적 구조와 논리적 일관성을 검증하기 위해 yanglint의 트리 출력 기능을 활용합니다.

    yanglint -f tree ./01-schema/nodes/hybrid-cloud.yang
    yanglint -f tree ./01-schema/providers/aws-provider.yang
  • YANG 모델에 에러가 있는 경우, yanglint가 상세한 오류 메시지를 제공하여 문제를 쉽게 파악할 수 있습니다.

    libyang err : Invalid keyword ";" as a child of "leaf". (Line number 37.)
    libyang err : Importing "res-compute" module failed.
    YANGLINT[E]: Processing schema module from ./01-schema/nodes/hybrid-cloud.yang failed.
    
  • YANG 모델의 계층적 구조는 다음과 같이 시각적으로 표현됩니다.

    module: hybrid-cloud
    +--rw cluster
        +--rw node* [name]
            +--rw name                   string
            +--rw k8s-role-assignment
            |  +--rw node-role?        ct:k8s-node-role
            |  +--rw bootstrap-role?   ct:k8s-bootstrap-role
            +--rw compute
            |  +--rw platform           ct:node-platform
            |  +--rw arch               ct:cpu-arch
            |  +--rw (platform-spec)
            |     +--:(aws)
            |     |  +--rw instance-type    ct:aws-instance-type
            |     |  +--rw ebs-size         uint16
            |     +--:(on-premise)
            |        +--rw vcpu      uint8
            |        +--rw memory    uint8
            +--rw network
            |  +--rw ssh-user                string
            |  +--rw ssh-port                inet:port-number
            |  +--rw (bootstrap-strategy)
            |     +--:(aws-strategy)
            |     |  +--rw public-ip-required?   boolean
            |     |  +--rw use-eip?              boolean
            |     +--:(on-premise-strategy)
            |        +--rw bootstrap-ip    inet:ip-address
            +--rw storage
            +--rw s3-endpoint?      string
            +--rw redis-endpoint?   string
            +--rw cache-size?       uint32
    
    module: aws-provider
    +--rw aws-config
        +--rw region?                string
        +--rw vpc-cidr?              inet:ipv4-prefix
        +--rw public-subnet-cidr?    inet:ipv4-prefix
        +--rw private-subnet-cidr?   inet:ipv4-prefix
        +--rw public-key-dir?        string

Step 2. Data Instance Modeling: Node-specific JSON Manifests

Step 3. Schema Compliance Verification & Data Integrity Audit

for f in 02-inventory/nodes/*.json; do \
	yanglint -p 01-schema/nodes 01-schema/nodes/hybrid-cloud.yang "$f" && \
	echo "✅ YANG Lint Pass: $f" || { echo "❌ YANG Lint Fail: $f" && break; }; \
done
✅ YANG Lint Pass: 02-inventory/nodes/site-a-node.json
✅ YANG Lint Pass: 02-inventory/nodes/site-b-node.json
✅ YANG Lint Pass: 02-inventory/nodes/t3-a.json
✅ YANG Lint Pass: 02-inventory/nodes/t3-b.json
✅ YANG Lint Pass: 02-inventory/nodes/t3-c.json
✅ YANG Lint Pass: 02-inventory/nodes/t4g-a.json
✅ YANG Lint Pass: 02-inventory/nodes/t4g-b.json
✅ YANG Lint Pass: 02-inventory/nodes/t4g-seed.json
for f in 02-inventory/providers/*.json; do \
	yanglint -p 01-schema/providers 01-schema/providers/aws-provider.yang "$f" && \
	echo "✅ YANG Lint Pass: $f" || { echo "❌ YANG Lint Fail: $f" && break; }; \
done
✅ YANG Lint Pass: 02-inventory/providers/aws.json

Step 4. Exception Handling & Constraint Enforcement Scenarios

JSON 데이터에 에러가 있는 경우, yanglint가 상세한 오류 메시지를 제공하여 문제를 쉽게 파악할 수 있습니다.

  • 예시: instance-typet4g.medium을 초과하는 경우

    libyang err : Unsatisfied pattern - "t4g.large" does not conform to "[tcrm][1-8][a-z]*\.(nano|micro|small|medium)". (Schema location /hybrid-cloud:cluster/node/compute/platform-spec/aws/instance-type, data location /hybrid-cloud:compute, line number 19.)
    YANGLINT[E]: Failed to parse input data file "02-inventory/t4g-seed.json".
    ❌ YANG Lint Fail: 02-inventory/t4g-seed.json
    
  • 예시: platforminstance-type 간의 불일치

    libyang err : Architecture mismatch detected: 'arm64' platform requires 'g' instance types, while 'x86_64' platform cannot use 'g' instance types. (/hybrid-cloud:cluster/node[name='t3-a']/compute/instance-type)
    YANGLINT[E]: Failed to parse input data file "02-inventory/t3-a.json".
    ❌ YANG Lint Fail: 02-inventory/t3-a.json
    
    libyang err : Architecture mismatch detected: 'arm64' platform requires 'g' instance types, while 'x86_64' platform cannot use 'g' instance types. (/hybrid-cloud:cluster/node[name='t4g-a']/compute/instance-type)
    YANGLINT[E]: Failed to parse input data file "02-inventory/t4g-a.json".
    ❌ YANG Lint Fail: 02-inventory/t4g-a.json
    
  • 예시: On-Premise 노드에 public-ip-requiredtrue로 설정된 경우

    libyang err : Data for both cases "aws-strategy" and "on-premise-strategy" exist. (Schema location /hybrid-cloud:cluster/node/network/bootstrap-strategy, data location /hybrid-cloud:network, line number 27.)
    YANGLINT[E]: Failed to parse input data file "02-inventory/site-a-node.json".
    ❌ YANG Lint Fail: 02-inventory/site-a-node.json
    

Step 5. Negative Testing with Intentionally Invalid JSON Files

make lint-test 명령을 통해 의도적으로 오류가 있는 JSON 파일을 검사하여, YANG 모델이 예상대로 제약 조건을 강제하는지 검증할 수 있습니다.

  • error/01-arm-x86-instance.json: arm64 아키텍처 노드에 g가 없는 x86_64 전용 인스턴스 타입 (t3.medium)이 지정되어 must 구문을 위반한 경우

  • error/02-x86-arm-instance.json: x86_64 아키텍처 노드에 Graviton (arm64) 전용 인스턴스 타입 (t4g.small)이 지정되어 must 구문을 위반한 경우

  • error/03-invalid-platform.json: common-types에 정의되지 않은 플랫폼 값 (azure)을 입력하여 enumeration 타입 제약을 위반한 경우

  • error/04-ebs-type-mismatch.json: uint16 타입인 ebs-size 필드에 숫자가 아닌 문자열 값 ("100GB")을 입력하여 데이터 타입 검증에 실패한 경우

  • error/05-wrong-case-data.json: 플랫폼이 aws임에도 불구하고 on-premise 케이스 전용 필드 (vcpu, memory)를 포함하여 when 조건부 로직을 위반한 경우

  • error/06-missing-mandatory-choice.json: mandatory: true로 설정된 platform-spec 초이스 내의 필수 리프 (instance-type)를 누락한 경우

  • error/07-vcpu-out-of-range.json: on-premise 설정에서 vcpu 값을 허용 범위 (1..8)를 초과하는 값 (64)으로 설정하여 range 제약을 위반한 경우

  • error/08-instance-regex-mismatch.json: AWS 인스턴스 명명 규칙 패턴 ([tcrm][1-8]...)에 맞지 않는 인스턴스 타입 (p3.2xlarge)을 입력하여 re-match 검증에 실패한 경우

  • error/09-missing-arch.json: compute 컨테이너 내에서 반드시 존재해야 하는 arch 리프를 누락하여 mandatory 제약을 위반한 경우

  • error/10-empty-node.json: 노드 정의 내에 필수 컨테이너인 computenetwork 섹션을 모두 누락하여 모델의 최소 구조 요건을 충족하지 못한 경우

Phase 3. Storage Abstraction: JuiceFS Infrastructure Setup

컴퓨트 노드와 완전히 격리된 독립형 스토리지 엔진을 구축합니다. S3 호환 API (MinIO)와 고성능 메타데이터 엔진 (Redis)을 추상화된 자원으로 제공하여 하이브리드 클러스터의 데이터 일관성을 보장합니다.

Step 1. Containerized Storage Backend Deployment

  • docker-compose.yml 을 활용하여 스토리지 백엔드 구성을 코드화합니다.

  • Host OS 환경에 의존하지 않고, 컨테이너 기술을 통해 엔진의 배포와 버전 관리를 단순화합니다.

Step 2. Storage Provider Specs & Technical Highlights

Component Service Port Backend Storage
Metadata Engine Redis 4279 Docker Volume (on NVME)
Object Storage MinIO 4200 (S3 API) / 4201 (Dashboard) ZFS Dataset
  • MinIO의 데이터 영속성을 위해 ZFS Storage Pool을 직접 매핑하여 데이터 안정성과 성능을 극대화했습니다.

  • 기존 서비스 및 시스템 서비스와의 포트 간섭을 원천 차단하기 위해 전용 포트를 할당했습니다.

Phase 4. Automating Node Provisioning with Terraform & Shell Scripting

Step 1. Terraform for AWS Node Provisioning

AWS 자원 생성에만 집중하며, user_data 스크립트를 완전히 배제하여 인프라 프로비저닝과 노드 부트스트래핑의 경계를 명확히 분리합니다. 이는 가장 순수한 형태의 IaC로서, 플랫폼에 종속되지 않는 하이브리드 클라우드 구축의 기반이 됩니다.

  • JSON 인벤토리를 jsondecode로 해석하여 YANG 모델이 정의한 명세를 동적으로 추출합니다.

  • 설계도의 변경 사항이 인프라 설정에 즉시 반영되는 유연한 데이터 파이프라인을 구축합니다.

  • local.region을 통해 실행 리전을 동적으로 선언하고 pathexpand로 SSH 키 경로의 호환성을 확보합니다.
  • VPC와 퍼블릭 서브넷을 구축하여 외부 통신 기반을 마련하고 인터넷 게이트웨이를 통해 트래픽 흐름을 완성합니다.

  • 관리용 SSH와 하이브리드 메시 네트워크를 위한 Tailscale 포트만을 선별적으로 개방하여 보안을 극대화합니다.

  • 생성된 VPC, 보안 그룹, 키 페어 등의 핵심 식별자를 출력하여 후속 단계의 동적 참조를 지원합니다.

  • aws-node 프로비저닝과 Ansible 인벤토리 구성 시 실시간 인프라 정보를 제공하는 핵심 지점이 됩니다.

  • terraform_remote_state를 활용하여 aws-base에서 생성된 VPC, 서브넷, 보안 그룹 정보를 실시간으로 참조합니다.

  • 주입된 JSON 매니페스트를 분석하여 아키텍처, 인스턴스 유형, EBS 크기 등 개별 노드의 물리적 사양을 결정합니다.

  • 노드 명세의 아키텍처 정보를 필터로 사용하여 해당 규격에 최적화된 최신 Amazon Linux 2023 AMI를 동적으로 선택합니다.

  • 베이스 레이어와 동일한 리전에 프로바이더를 배치하고, 생성된 노드의 퍼블릭 IP를 출력하여 후속 자동화 단계의 엔드포인트를 제공합니다.

  • user_data를 완전히 배제하여 인프라 프로비저닝과 소프트웨어 부트스트래핑의 관리 영역을 엄격하게 격리합니다.

  • source_dest_check를 비활성화하여 인스턴스가 하이브리드 메시 네트워크 상의 패킷 라우팅 중계지 역할을 수행할 수 있도록 설정합니다.

Step 2. Unified Orchestration with Shell Script: dispatcher.sh

설계도 (JSON)가 인프라 실재로 이어지는 경로를 최소화한 경량 디스패처입니다. 모델 중심의 데이터를 기반으로 Terraform과 Shell Bridge를 동적으로 실행하여 하이브리드 노드 구축의 투명성과 실행 속도를 극대화했습니다.

1. Direct-Path Orchestration: Zero-Layer Abstraction

  • 기존 Python 기반의 복잡한 객체 생성 과정을 제거하고, Shell과 jq만으로 구성된 Direct-Path 구조를 채택했습니다.

  • 중간 추상화 레이어를 최소화하여, YANG 모델 (JSON)에서 실제 인프라 명령 (Terraform/SSH)으로 이어지는 데이터 흐름을 투명하고 직관적으로 개선했습니다.

  • set -e와 실시간 에러 핸들링을 통해 프로비저닝 과정 중 발생하는 사소한 결함도 즉시 감지하여 인프라 오염을 원천 차단합니다.

2. jq-Powered Metadata Routing

  • jq를 활용해 YANG 모델의 계층 구조를 직접 탐색하고, 노드별 플랫폼 (aws, on-premise) 및 네트워크 속성을 실시간으로 추출합니다.

  • 추출된 메타데이터를 기반으로 Terraform의 -chdir 경로와 -var 주입 값을 동적으로 결정하여, 하나의 스크립트로 이기종 환경을 통합 관리합니다.

  • 모델에서 정의된 기본값을 jq의 Fallback 연산자 (//)로 구현하여 데이터 누락 시에도 안정적인 실행을 보장합니다.

3. Isolated State & Scalable Execution

  • 모든 AWS 노드에 대해 고유한 .tfstate 파일을 생성함으로써, 특정 노드의 변경이나 삭제가 전체 클러스터 상태에 영향을 주지 않는 수평적 확장성을 확보했습니다.

  • realpath를 통한 절대 경로 추적 방식을 도입하여, 프로젝트 내 어느 디렉토리에서 실행하더라도 인벤토리와 프로비저닝 코드를 정확하게 결합합니다.

  • Cloud (Terraform)와 On-Premise (SSH Bridge) 간의 실행 분기를 단일 case 문으로 통합하여, 향후 신규 플랫폼 추가 시에도 코드 수정 범위를 최소화했습니다.

Step 3. Automated Infrastructure De-provisioner: purge_march.sh

YANG 설계도와 Terraform의 상태 파일 (.tfstate)을 동적으로 결합하여, 모델이 정의한 인프라의 시작과 끝을 일관성 있게 관리합니다.

1. Cost-Optimization & Resource Recovery

  • 사용하지 않는 AWS 인프라를 즉시 회수하여 불필요한 과금을 방지하고 클라우드 자원을 효율적으로 관리합니다.

  • terraform destroy 명령을 자동화하여 수동 작업 시 발생할 수 있는 자원 누락 문제를 원천 차단합니다.

2. State-Aware De-provisioning

  • 각 노드별로 분리된 .tfstate 파일을 기반으로 동작하므로, 전체 클러스터에 영향을 주지 않고 특정 노드의 자원만 안전하게 제거할 수 있습니다.

  • 실제 terraform destroy 명령을 내리기 전 해당 노드의 상태 파일 존재 여부를 먼저 검사하여, 이미 제거된 자원에 대한 중복 실행 에러를 방지하는 방어적 로직을 갖췄습니다.

3. Execution Consistency

  • 프로비저닝 단계와 동일한 매니페스트 경로 및 SSH 키 경로 변수를 Terraform에 주입합니다.

  • 이를 통해 Terraform이 파괴 시점에도 동적 자원 이름을 정확히 계산하여, 이름 충돌 없이 대상 자원을 식별할 수 있도록 보장합니다.

Phase 5. Ansible-Driven Bootstrapping & Configuration Management

Step 1. Dynamic Inventory Generation: inventory/resolver.py

수동으로 hosts 파일을 수정하는 전통적인 방식에서 벗어나, 코드가 인프라의 변화를 스스로 감지하고 환경 설정을 업데이트하는 Self-Healing Inventory를 구현했습니다.

1. Multi-State Data Aggregator

  • .tfstate 파일을 탐색하여 AWS에서 프로비저닝된 노드들의 공인 IP 주소를 하나의 Map으로 집계합니다.

  • 노드가 추가되거나 제거되더라도, 실행 시점의 Terraform 상태를 반영하므로 항상 최신의 인벤토리를 유지할 수 있습니다.

2. Architectural Intent & Design Principles

  • YANG 매니페스트와 Terraform의 상태 파일을 동시에 참조하여 Ansible이 이해할 수 있는 HostVars 구조로 변환합니다.

  • 설계도에 정의된 role-assignment, storage, compute 등의 메타데이터를 개별 노드의 변수로 완벽하게 이식합니다.

3. Standardized Ansible JSON Inventory Format

  • Ansible의 Dynamic Inventory 규격에 맞춘 JSON 출력을 생성하여, _meta 정보를 포함한 데이터를 생성합니다.

  • aws, on_premise, server, agent 등의 논리적 그룹핑을 자동으로 생성하여, 플레이북에서 유연하게 타겟팅할 수 있도록 지원합니다.

4. Operational Transparency

  • 처리 과정과 디버깅 로그는 sys.stderr로 출력하여, Ansible이 JSON 데이터를 표준 출력 (sys.stdout)으로만 인식하도록 설계되었습니다.

  • 이를 통해 Ansible 파싱 에러를 방지하면서, 터미널로 실시간 수집 현황을 모니터링할 수 있도록 했습니다.

Step 2. Establishing the Baseline: Core Ansible Configuration & Common Playbook

Ansible이 하이브리드 클라우드의 다양한 환경에서 일관된 방식으로 작동할 수 있도록, 핵심 설정과 공통 작업을 정의하는 단계입니다. 이는 이후의 플레이북들이 안정적으로 실행될 수 있는 기반을 마련합니다.

1. Ansible Core Configuration: ansible.cfg

  • 동적 리졸버와의 연동, 보안 인증, 그리고 하이브리드 환경의 지연 시간을 극복하기 위한 성능 최적화 설정을 정의합니다.

    [defaults]
    # 인벤토리 스크립트 경로 설정
    inventory = ./inventory/resolver.py
    # SSH 개인 키 파일 경로 설정
    private_key_file = ../00-key/hybrid-cloud
    # Vault 파일 경로 설정
    vault_password_file = ./.vault_pass
    # SSH 호스트 키 검증 비활성화
    host_key_checking = False
    # Python 인터프리터 자동 감지 설정 (불필요한 경고 메시지 제거)
    interpreter_python = auto_silent
    # 출력 형식 설정 (YAML)
    stdout_callback = ansible.builtin.default
    result_format = yaml
    # 콜백 플러그인 설정 (YAML)
    bin_ansible_callbacks = True
    # Ansible이 수집한 사실을 변수로 주입하지 않도록 설정
    inject_facts_as_vars = False
    
    [ssh_connection]
    # SSH 연결 최적화 설정
    pipelining = True
    # SSH 연결 재사용 설정
    ssh_args = -o ControlMaster=auto -o ControlPersist=60s

2. Common Infrastructure Baseline: roles/common/tasks/main.yml

  • 모든 노드가 갖춰야 할 최소 요건을 구성합니다.

  • node_spec 기반의 호스트네임 설정 및 /etc/hosts 정비를 통해 sudo 지연과 이름 해석 에러를 원천 차단합니다.

  • 외부 저장소 및 API 통신을 위해 신뢰할 수 있는 DNS 설정을 강제 주입하여 apt/dnf 업데이트의 안정성을 확보합니다.

  • os_familydistribution 팩트를 활용하여 OS별로 roles/common/vars/main.yml에 정의된 패키지를 설치합니다.

  • Amazon Linux 대응

    • Amazon Linux 2023의 기본 패키지인 curl-minimal은 일반 curl 설치 시 의존성 충돌을 일으킵니다.

    • Amazon Linux 환경을 별도로 감지하여, allowerasing: yes 옵션을 통해 기존 패키지를 안전하게 교체하고 표준 도구 세트를 완성합니다.

Step 3. Ansible Playbook for Tailscale Mesh Network Setup: roles/tailscale/tasks/main.yml

모든 노드가 하나의 Overlay 네트워크로 연결하기 위한 Tailscale 설치 및 초기 설정을 담당합니다.

1. Tailscale 설치 스크립트 실행 & 현재 상태 체크

  • Tailscale 공식 설치 스크립트를 활용하여, Amazon Linux (ARM64)와 Debian (x86_64) 등 서로 다른 플랫폼과 아키텍처에 상관없이 일관된 설치 과정을 제공합니다.

  • creates: /usr/sbin/tailscale 옵션을 통해 이미 설치된 노드에서의 중복 실행을 방지하여 멱등성을 확보합니다.

  • tailscale status --json 명령을 통해 현재 노드의 연결 상태를 JSON 형식으로 파악합니다.

  • 응답 JSON의 BackendState 필드가 "Running"이 아닌 경우에만 tailscale up을 실행하도록 조건화하여, 이미 네트워크에 가입된 노드에 불필요한 가입 요청을 방지합니다.

2. Tailscale 서비스 활성화 및 시작 & IPv4 주소 확인

  • ansible-vault로 암호화된 인증 키를 활용하며, no_log: true 설정을 통해 민감한 키 정보가 앤서블 실행 로그에 남지 않도록 보안을 강화했습니다.

  • BackendState != "Running" 조건에 따라 선택적으로 tailscale up 명령을 실행하여, 노드를 Tailscale 네트워크에 연결합니다.

  • --auth-key {{ tailscale_auth_key }} 옵션을 주입하여 수동 인증 과정 없는 완전 자동화된 네트워크 가입을 실현합니다.

  • 리졸버에서 추출한 {{ node_spec.name }}을 호스트네임으로 지정하여, Tailscale 대시보드 내에서 노드 식별을 명확히 합니다.

  • 네트워크 가입 후 할당된 100.64.0.0/10 대역의 사설 IP 주소를 tailscale ip -4 명령으로 실시간 확인합니다.

  • 연결이 완료될 때까지 최대 5회 재시도를 수행하여, 네트워크 인터페이스가 활성화되는 데 필요한 물리적 시간을 충분히 확보합니다.

  • ansible.builtin.set_fact 모듈을 활용하여, 노드의 Tailscale IP 주소를 ts_ip 변수로 저장한 후 이후 플레이북에서 참조할 수 있게 합니다.

3. 커널 수준 성능 최적화 및 프로세스 가용성 강화

  • 네트워크 가속 및 하드닝: 하이브리드 클라우드의 대역폭 성능을 극대화하기 위해 커널 수준에서 BBR 혼잡 제어 및 UDP/NIC Offload 튜닝을 자동 적용합니다.

  • 프로세스 안정성: OOMScoreAdjustNice 값 조정을 통해 시스템 리소스 부족 시에도 네트워크 연결을 최우선으로 보호합니다.

Step 4. Ansible Playbook for JuiceFS Setup: roles/juicefs/tasks/main.yml

JuiceFS 클라이언트 설치 및 S3/Redis 엔드포인트 연결을 담당합니다.

1. JuiceFS 설치 스크립트 실행 & 마운트 디렉토리 생성

  • JuiceFS 공식 설치 스크립트를 활용하여, Amazon Linux (ARM64)와 Debian (x86_64) 등 서로 다른 플랫폼과 아키텍처에 상관없이 일관된 설치 과정을 제공합니다.

  • juicefs_bin_path 변수와 Ansible의 creates 옵션을 결합하여, 이미 바이너리가 존재하는 노드에서의 불필요한 재설치를 방지합니다.

  • 설치 완료 후 {{ juicefs_bin_path }} --version 명령을 실행하여 JuiceFS 바이너리가 정상적으로 설치되었는지 검증합니다.

  • ansible_user를 기준으로 마운트 디렉토리를 생성하고, 소유권을 사전에 설정하여 이후 프로세스에서 발생할 수 있는 권한 충돌을 예방합니다.

2. JuiceFS 파일 시스템 포맷

  • JSON 인벤토리의 S3/Redis 정보를 기반으로 juicefs format 명령을 실행합니다:

    {{ juicefs_bin_path }} format \
        --storage s3 \
        --bucket "{{ storage_info.s3_url }}/jfs" \
        --access-key "{{ minio_root_user }}" \
        --secret-key "{{ minio_root_password }}" \
        "redis://:{{ redis_password }}@{{ storage_info.redis_url }}/0" \
        "{{ jfs_name | default('hybrid-cloud-jfs') }}"
    • --storage s3: MinIO S3 호환 스토리지 백엔드 지정

    • --bucket "{{ storage_info.s3_url }}/jfs": S3 버킷 경로 설정

    • --access-key "{{ minio_root_user }}": MinIO 접근 키 주입

    • --secret-key "{{ minio_root_password }}": MinIO 비밀 키 주입

    • redis://:{{ redis_password }}@{{ storage_info.redis_url }}/0: Redis 메타데이터 엔진 연결 URI (DB 0)

    • {{ jfs_name | default('hybrid-cloud-jfs') }}: 파일 시스템 이름 (기본값: hybrid-cloud-jfs)

  • failed_when 조건을 상세화하여, "already exists" 메시지가 포함된 에러는 정상으로 간주함으로써 기존 파일 시스템을 보호하고 멱등성을 유지합니다.

  • no_log: true 설정을 통해 Ansible 실행 로그에 민감한 S3/Redis 인증 정보가 노출되는 것을 차단합니다.

  • run_once: true 옵션을 사용하여 클러스터 전체에서 단 한 번만 포맷 명령이 실행되도록 보장함으로써 메타데이터의 무결성을 지킵니다.

3. FUSE: 타 사용자 접근 허용 설정

  • /etc/fuse.confallow_other 옵션을 주입하여 root 이외의 일반 사용자나 애플리케이션 프로세스가 마운트된 볼륨에 접근할 수 있도록 설정합니다.

  • 해당 설정은 다중 사용자 환경에서 JuiceFS를 애플리케이션 스토리지로 활용하기 위한 필수 전제 조건입니다.

4. Jinja2 서비스 템플릿 배포 & JuiceFS 마운트 서비스 시작 및 활성화

  • roles/juicefs/templates/juicefs-mount.service.j2를 통해 각 노드별 환경 변수와 경로가 주입된 유닛 파일을 생성합니다.

  • 생성된 서비스를 시스템에 등록 및 시작하여, 노드 재부팅 시에도 JuiceFS 볼륨이 자동으로 마운트되도록 구성합니다.

  • Tailscale 서비스에 대한 의존성 (After/Wants)을 부여하여 네트워크 준비 전 마운트 시도로 인한 오류를 원천 차단합니다.

  • Ansible의 Ternary Operator 로직을 통해 템플릿이나 FUSE 설정이 변경될 때만 서비스를 선별적으로 재시작함으로써 운영 효율성을 높입니다.

5. JuiceFS 상태 및 연결 확인

  • timeout 5 df -h 명령을 사용하여 스토리지 응답 지연 시 태스크가 무한 대기하는 현상을 방지하고, 안정적으로 마운트 여부를 확정합니다.

  • 마운트 서비스 시작 후 물리적 시간이 필요하므로, 최대 5회 재시도 (5초 간격)를 설정하여 안정적인 검증을 수행합니다.

6. JuiceFS 마운트 후 소유권 강제 재적용

  • FUSE 마운트 프로세스 중에 소유권이 root로 변경되는 시스템적 현상을 방지하기 위해, Ansible file 모듈을 활용하여 마운트 완료 후에도 ansible_user 소유권과 권한 (0770)을 재강제합니다.

  • 이는 마운트 전후 양쪽에서 소유권을 검증하는 이중화 전략으로 권한 충돌을 완전히 차단합니다.

Step 5. Ansible Playbook for K3s Cluster Bootstrapping

MDI (Model-Driven Infrastructure) 설계에 따라 분류된 그룹 (control_plane, worker)과 전략 (init, join)을 바탕으로, 하이브리드 환경에 최적화된 K3s 클러스터를 선언적으로 구축합니다.

0. Network Integration Strategy: Tailscale Backbone

물리적 위치 (On_Premise/AWS)에 관계없이 모든 노드는 Tailscale Mesh 네트워크를 통해 하나의 논리적 서브넷으로 통합됩니다. 이를 위해 모든 부트스트래핑 과정에서 다음 설정을 강제합니다.

  • 고정 엔드포인트 (ts_ip): 리졸버가 추출한 tailscale0 인터페이스의 IP를 모든 노드의 대표 주소로 활용합니다.

  • 통신 인터페이스 표준화: K3s의 내부 통신 및 Flannel CNI가 물리 인터페이스가 아닌 tailscale0를 바라보도록 설정하여 하이브리드 환경에서의 통신 안정성을 확보합니다.

  • 노드 역할별 최적화 템플릿: Seed, Join, Worker 등 각 역할에 맞는 Jinja2 템플릿을 동적으로 렌더링합니다.

    • Control Plane에는 API 인증서에 Tailscale IP를 바인딩하는 tls-san 추가

    • Worker 노드에는 불필요한 Kubeconfig 생성 옵션 제거

  • 핵심 구성 플래그: 모든 노드의 설정 파일에는 네트워크 경계를 허물고 보안을 강화하기 위한 다음 파라미터가 공통으로 포함됩니다.

    # K3s 노드 통신 및 인터페이스 고정 설정
    node-ip: "{{ ts_ip }}"
    node-external-ip: "{{ ts_ip }}"
    flannel-iface: "tailscale0"

1. Playbook for Controller: roles/k3s-controller/tasks/main.yml

  • Cluster Seed (k8s_init): roles/k3s-controller/templates/k3s-init.yaml.j2

    • AWS (t4g-seed)를 클러스터의 기점으로 삼아 내장 etcd를 초기화합니다.

    • --cluster-init 플래그를 통해 고가용성 아키텍처의 초석을 다지며, 부트스트래핑 성공 후 생성된 Cluster Token을 안전하게 추출 (slurp)하여 전역 변수화합니다.

  • HA Expansion (k8s_join): roles/k3s-controller/templates/k3s-join.yaml.j2

    • AWS (t3-*, t4g-*)/ On-Premise (site-a-node) 등 추가 컨트롤 플레인 노드를 시드 노드에 합류시킵니다.

    • etcd Quorum 보호를 위해throttle: 1 전략을 채택하여, 추가 컨트롤 플레인 노드들이 순차적으로 안전하게 합류하도록 제어합니다.

    • hostvars를 통해 공유된 시드 노드의 Tailscale IP와 Token을 활용하여 물리적 위치에 관계없이 논리적 제어 계층을 확장합니다.

2. Playbook for Worker: roles/k3s-worker/tasks/main.yml

  • MDI 기반 컴퓨팅 확장: roles/k3s-worker/templates/k3s-worker.yaml.j2

    • 컨트롤 플레인 구축 직후, Seed 노드의 Tailscale IP와 보안 토큰을 hostvars에서 동적으로 참조하여 연산 전용 노드를 클러스터에 안전하게 가입시킵니다.

    • 모든 에이전트 옵션을 Jinja2 템플릿으로 관리하여, 설정 변경 시 시스템 서비스가 이를 즉시 감지하고 재시작되도록 멱등성을 확보했습니다.

  • Agent-Only Mode

    • k3s agent 명령어를 통해 제어 오버헤드를 최소화하고 순수 연산 자원만 확보합니다.

    • 모든 Pod 간 통신은 tailscale0 인터페이스를 강제하여 인터넷 망 노출 없이 안전한 하이브리드 데이터 플레인을 형성합니다.

    • ansible_facts 기반의 자원 예약 계산식을 통해 하이브리드 노드별 (AWS/On-Premise) 맞춤형 하드닝을 수행합니다.

Step 6. Master Orchestration Entrypoint: site.yml

개별적으로 작성된 모든 Playbook을 논리적 선후 관계에 따라 순차 실행하여, 하이브리드 클라우드의 완전한 부트스트래핑을 달성하는 단일 진입점입니다.

1. Infrastructure Startup & Network Fabrication

  • commontailscale 플레이북을 동시 실행하여 모든 노드를 메시 네트워크로 묶습니다.

  • resolverhas_storage 메타데이터에 따라 JuiceFS 마운트 여부를 동적으로 결정하며 기초 구성을 완료합니다.

2. Building the Control Plane: Seed Node Initialization & HA Expansion

  • k3s-controller 플레이북을 실행합니다. 이 단계의 핵심은 데이터의 흐름입니다.

  • k8s_init 노드가 생성한 클러스터 기점 (Seed) 정보를 hostvars로 추출하여, 지연 실행되는 k8s_join 노드들에게 안전하게 전달합니다.

3. Expanding the Compute Layer: Worker Node Integration

  • 완성된 Control Plane 위로 Worker 노드들을 정렬시킵니다.

About

One Logical Infrastructure, Many Physical Realities: A YANG-Driven Hybrid Cloud with K3s

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors