# K8s와 OpenStack은 어떻게 연계돼 있나 OpenStack-Helm 은 모든 OpenStack 컴포넌트를 Kubernetes 리소스(Deployment / DaemonSet / StatefulSet / Job)로 띄운다. 전부 `openstack` 네임스페이스에서 동작하며, 이 랩은 단일 노드라 컨트롤 플레인과 데이터 플레인이 같은 노드에 함께 올라온다. ## 전체 구조 ```mermaid flowchart TB User(["사용자 / openstack CLI"]) --> KS subgraph CP["컨트롤 플레인 · Deployment"] KS["Keystone
인증·토큰"] GL["Glance
이미지"] NV["Nova API
scheduler·conductor"] NE["Neutron server
네트워크 API"] PL["Placement
리소스 배치"] end subgraph DP["데이터 플레인 · DaemonSet (컴퓨트 노드)"] NC["nova-compute"] LV["libvirt / QEMU"] OVS["Open vSwitch"] AG["neutron L3·DHCP·metadata 에이전트"] end subgraph INF["인프라 · StatefulSet"] DB[("MariaDB")] MQ[("RabbitMQ")] MC[("memcached")] end NV --> PL NV --> NC --> LV NE --> AG --- OVS CP --> DB CP --> MQ KS --> MC ``` 각 K8s 리소스 종류가 맡는 역할: | K8s 리소스 | 무엇을 띄우나 | 개수(이 랩) | |---|---|---| | **Deployment** | 상태 없는 컨트롤 플레인 API·스케줄러·컨덕터 | 11 | | **DaemonSet** | 컴퓨트 노드에 1:1로 배치되는 데이터 플레인(hypervisor·network 에이전트·OVS) | 8 | | **StatefulSet** | 영구 상태가 있는 인프라(DB·MQ·캐시) | 3 | | **Job** | 일회성 부트스트랩(DB 스키마, keystone 등록) | ~38 | `kubectl -n openstack get {deploy,ds,sts,jobs}` 로 자기 클러스터의 수치를 확인한다. ## 컨트롤 플레인 — Deployment 상태가 없고 어느 노드에 올라와도 되며, 보통 1 replica 다(HA 구성 시 늘린다). `openstack-control-plane=enabled` 라벨이 붙은 노드에 배치된다. | 서비스 | Deployment | 역할 | |---|---|---| | **Keystone** | `keystone-api` | identity·토큰 발급. 모든 다른 서비스의 인증 기반 | | **Glance** | `glance-api` | 이미지 저장·조회(PVC `glance-images` 에 qcow2 보관) | | **Nova** | `nova-api-osapi` | 사용자가 입력하는 `openstack server ...` 를 수신 | | | `nova-api-metadata` | VM 안에서 169.254.169.254 로 호출하는 메타데이터 서버 | | | `nova-conductor` | DB 접근 중개(compute 가 DB 에 직접 접근하지 않음) | | | `nova-scheduler` | 어느 하이퍼바이저에 VM 을 배치할지 결정 | | | `nova-novncproxy` | 브라우저에서 VM 콘솔 접속 | | **Neutron** | `neutron-server` | network·subnet·port API | | | `neutron-rpc-server` | 에이전트와의 RPC 분리(성능용) | | **Placement** | `placement-api` | 리소스 인벤토리·할당(Nova 가 빈 자리를 placement 에 질의) | | (MariaDB) | `mariadb-controller` | MariaDB Galera 상태 머신 컨트롤러 | ## 데이터 플레인 — DaemonSet 노드에 1:1로 배치되는 것(가상화 호스트, OVS 브리지, 네트워크 에이전트). 라벨 셀렉터로 어느 노드에 올릴지 결정하며, 이 랩은 단일 노드라 모두 같은 노드에 하나씩 올라온다. | DaemonSet | 라벨 셀렉터 | 역할 | |---|---|---| | `libvirt-libvirt-default` | `openstack-compute-node=enabled` | QEMU/KVM hypervisor 데몬. host `/var/lib/libvirt` 마운트 | | `nova-compute-default` | `openstack-compute-node=enabled` | nova-compute(libvirt 와 통신해 실제 VM 생성) | | `openvswitch` | `openvswitch=enabled` | OVS vswitchd·ovsdb-server. br-int / br-ex 생성 | | `neutron-ovs-agent-default` | `openvswitch=enabled` | VM port 가 생성되면 OVS 에 attach | | `neutron-l3-agent-default` | `l3-agent=enabled` | 가상 라우터(네트워크 간 라우팅) | | `neutron-dhcp-agent-default` | `dhcp-agent=enabled` | VM 에 IP 할당(dnsmasq) | | `neutron-metadata-agent-default` | `metadata-agent=enabled` | VM 의 169.254.169.254 요청을 nova-api-metadata 로 프록시 | | `neutron-netns-cleanup-cron-default` | (compute) | 사용하지 않는 netns 정리 cron | `osh/deploy.sh` Step 5 에서 노드에 다음 라벨을 모두 부여해, 단일 노드가 컨트롤·데이터 플레인 역할을 동시에 한다. ```text openstack-control-plane, openstack-compute-node, openvswitch, linuxbridge, ovs-host, l3-agent, dhcp-agent, metadata-agent ``` ## 인프라 의존성 — StatefulSet 영구 상태가 있고 ordering 이 중요한 인프라다. | StatefulSet | 역할 | 의존하는 쪽 | |---|---|---| | `mariadb-server` | OpenStack 의 모든 메타데이터 DB | 거의 모든 서비스 | | `rabbitmq-rabbitmq` | RPC 메시지 큐(oslo.messaging) | nova·neutron·placement·glance 에이전트 | | `memcached-memcached` | keystone 토큰 캐시 | keystone(성능) | ## Jobs — 일회성 부트스트랩 OpenStack 은 DB 가 비어 있으면 스키마부터 만들고, keystone 에 자기 자신을 등록해야 동작한다. OSH 는 이를 모두 K8s Job 으로 만든다. | Job 이름 패턴 | 무엇을 | |---|---| | `*-db-init`, `*-db-sync` | DB 사용자 생성, 스키마 마이그레이션 | | `*-rabbit-init` | RabbitMQ 사용자·vhost 생성 | | `*-ks-user`, `*-ks-service`, `*-ks-endpoint` | keystone 에 user·service·endpoint 등록 | | `keystone-fernet-setup`, `keystone-credential-setup` | 토큰 서명 키 생성 | | `nova-bootstrap`, `nova-cell-setup` | placement 에 hypervisor 등록 대기·nova cell 초기화 | | `glance-metadefs-load` | glance metadata definitions | `kubectl -n openstack get jobs` 에서 `Completions 1/1` 이면 성공이며, 다시 실행되지 않는다. ## 자격증명 흐름 각 서비스가 keystone 에 admin 으로 호출할 때 쓸 자격증명을 Secret 으로 보관한다. ```text secret/keystone-keystone-admin ← 실제 cloud admin secret/glance-keystone-admin secret/neutron-keystone-admin secret/nova-keystone-admin secret/placement-keystone-admin ``` `keystone-keystone-admin` Secret 이 담고 있는 키는 그대로 OpenStack CLI 환경변수가 된다. ```text OS_AUTH_URL OS_USERNAME OS_PASSWORD OS_PROJECT_NAME OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_DEFAULT_DOMAIN OS_REGION_NAME OS_INTERFACE ``` `osc` 파드의 Pod spec 에 다음이 있어서 `openstack` CLI 를 별도 source 없이 바로 쓴다(`osh/cirros-boot.sh` 12-27행 참조). ```yaml envFrom: - secretRef: name: keystone-keystone-admin ``` ## 서비스 디스커버리 — K8s Service + cluster.local `openstack endpoint list` 에 나오는 URL 은 전부 K8s Service FQDN 이다. ```text keystone public http://keystone.openstack.svc.cluster.local keystone internal http://keystone-api.openstack.svc.cluster.local:5000/v3 nova public http://nova.openstack.svc.cluster.local/v2.1/%(tenant_id)s neutron public http://neutron.openstack.svc.cluster.local glance public http://glance.openstack.svc.cluster.local placement public http://placement-api.openstack.svc.cluster.local:8778/ ``` 즉 서비스 간 통신은 모두 K8s in-cluster DNS 로 이뤄진다. 클러스터 밖에서 호출하려면 ingress 나 NodePort 가 필요한데, 이 랩은 학습용이라 두지 않았다. ## 스토리지 | 컴포넌트 | StorageClass | 크기 | 용도 | |---|---|---|---| | `glance-images` PVC | `general` | 2Gi | 업로드된 OS 이미지(qcow2) | | MariaDB PV | `local-path` | (chart 기본) | DB 데이터 | | RabbitMQ PV | `local-path` | (chart 기본) | MQ 상태 | 실제 provisioner 는 둘 다 같은 `rancher.io/local-path` 다. 일부 차트가 `class_name: general` 을 하드코딩하고 있어서, 같은 provisioner 를 가리키는 alias `general` 을 추가로 만들어 둔다([함정](../operations/troubleshooting.md) 참고). ## 가상화·네트워크 흐름 VM 이 부팅될 때 일어나는 일이다. ```mermaid flowchart TB A["openstack server create"] --> B["nova-api"] B --> C["nova-scheduler
어느 hypervisor 인가"] C --> P["placement-api
빈 자리 질의"] P --> C C --> D["nova-compute
선택된 노드의 DaemonSet"] D --> E["libvirt → QEMU 프로세스 = VM"] D --> F["neutron-server
port 생성·IP 할당"] F --> G["dhcp·ovs-agent → br-int"] G --> H["br-ex → provider1 (dummy)"] ``` - **br-int** = integration bridge. 모든 VM port 가 여기에 묶인다. - **br-ex** = external bridge. tenant network 가 외부로 나가는 출구다. - **provider1** = 본래 host NIC 여야 하지만, 단일 노드 랩이라 `ip link add provider1 type dummy` 로 만든 가짜 인터페이스다. neutron 의 `auto_bridge_add: br-ex: provider1` 을 만족시키는 용도다. - **QEMU 모드(nested KVM 아님)** — m5 인스턴스는 nested KVM 이 안 되므로 `virt_type=qemu` 소프트웨어 에뮬레이션을 쓴다. 컴퓨트가 CPU 를 많이 쓰는 이유다.