Kuryr는 Kubernetes의 Pod와 Service를 OpenStack Neutron 자원으로 직접 매핑하는 CNI 솔루션입니다. 따라서 클러스터 초기화 시 Pod/Service 서브넷을 OpenStack 네트워크 대역과 충돌 없이 일치시키는 것이 구축의 핵심입니다.
의존성 설치: Kubernetes 노드 역할에 맞는 컨테이너 런타임(containerd 등) 및 필수 패키지(kubeadm, kubelet, kubectl)를 설치합니다.
k8s 구축해보기에 따라 설치합니다.
k8s 구축해보기
●-기본 구축 마스터노드1, 워커노드1 최소사양 cpu 2 GB RAM코어 2Centos 7 -선수조건 ● k8s 가 설치 되는 경우 cri (container runtime interfece) 는 contaierd 와 호환 되므로 모든 노드에 container runtime 은 cont
drogva.tistory.com
OS는 centos7 , 버전은 k8s 1.29 입니다.
각 각 마스터 서버와 워커 서버의 /etc/hosts에 마스터 서버, 워커 서버, openstack controller 노드의 hostname을 입력합니다.

kubeadm 기본 설정 파일을 생성한 후, Kuryr 연동에 필요한 네트워크 파라미터를 수정합니다.
kubeadm config print init-defaults > kubeadm-init.yaml
Kuryr 환경에 맞춰 아래 3가지 핵심 섹션을 수정합니다. certSANs는 제어 평면(Controller)과 노드 간 통신을 위한 FQDN 및 IP를 모두 포함해야 합니다.
apiVersion: kubeadhttp://m.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.100.138 # 마스터 노드 IP
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: master.novalocal # 마스터 노드의 hostname
taints: null
# 1. API Server 접속을 위한 인증 정보 (CertSANs 추가)
apiServer:
certSANs:
- "kubernetes"
- "kubernetes.default"
- "kubernetes.default.svc"
- "kubernetes.default.svc.cluster.local"
- "master.novalocal"
- "192.168.100.138" # 마스터 노드 IP
- "192.168.0.234" # Public API 접속용 VIP (선택 사항)
# 2. 클러스터 제어 평면 설정
kind: ClusterConfiguration
kubernetesVersion: 1.29.0
...
# 3. Kuryr 연동을 위한 네트워크 설정
networking:
dnsDomain: cluster.local
serviceSubnet: 10.2.0.0/17 # OpenStack 내부 서비스용 대역- openstack 에서 생성한 service 네트워크 대역과 일치시킴
podSubnet: 10.1.0.0/16 # Pod별 Neutron 포트 할당 대역 - openstack 에서 생성한 pod 네트워크 대역과 일치시킴
예시)
apiVersion: kubeadhttp://m.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.100.138
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: master.novalocal
taints: null
---
apiVersion: kubeadhttp://m.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.29.15
clusterName: kubernetes
imageRepository: registry.k8s.io
apiServer:
timeoutForControlPlane: 15m0s
certSANs:
- "kubernetes"
- "kubernetes.default"
- "kubernetes.default.svc"
- "kubernetes.default.svc.cluster.local"
- "master.novalocal"
- "192.168.100.138"
- "192.168.0.234" # (선택) public으로 API 붙일 계획이면 유지
networking:
dnsDomain: cluster.local
serviceSubnet: 10.2.0.0/17
podSubnet: 10.1.0.0/16
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
시스템 최적화: 커널 모듈 로드(overlay, br_netfilter) 및 IP 포워딩 설정 등 K8s 실행을 위한 리눅스 커널 파라미터 튜닝을 진행합니다.
펌 : Ubuntu 22.04 + 쿠버네티스 1.29 설치
<모든 노드에서 실행>
1.29 버전에 맞게 모듈을 로드합니다.
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# 재부팅 후에도 값이 유지
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
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 재부팅하지 않고 sysctl 파라미터 적용
sudo sysctl --system
echo '1' > /proc/sys/net/ipv4/ip_forward
# 실행시 1 반환 확인
cat /proc/sys/net/ipv4/ip_forward
# containerd 에서 사용할 커널 모듈에 대한 정의 및 containderd 에서 참조할 conf 파일 생성
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
# 커널에 생성한 모듈을 등록하여 사용가능하도록 설정
modprobe overlay
modprobe br_netfilter
명령어 : kubeadm init --config kubeadm.yaml


설정을 적용하기 전 다음 항목들이 준비되었는지 확인하세요.
https://github.com/drogva/kuryr-kubernetes.git
GitHub - drogva/kuryr-kubernetes
Contribute to drogva/kuryr-kubernetes development by creating an account on GitHub.
github.com
본 설정은 Kuryr가 OpenStack의 자원(Neutron, Octavia)을 제어하기 위한 필수 파라미터입니다. [...]로 표시된 각 항목을 본인의 환경에 맞게 입력하십시오. (해당 github의 config_map 을 수정합니다.)
apiVersion: v1
kind: ConfigMap
metadata:
name: kuryr-config
namespace: kube-system
data:
kuryr.conf: |
[DEFAULT]
debug = true
[kubernetes]
api_root = https://[마스터_노드_IP_또는_VIP]:6443
token_file = /var/run/secrets/kubernetes.io/serviceaccount/token
ssl_ca_crt_file = /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
endpoints_driver_octavia_provider = amphora
enabled_handlers = vif,endpoints,service,kuryrloadbalancer,kuryrport
pod_vif_driver = nested-vlan
pod_subnets_driver = default
[octavia_defaults]
member_mode = L3
lb_algorithm = LEAST_CONNECTIONS
[neutron]
auth_url = http://[Controller_IP]:5000/v3
username = [neutron_서비스_계정명]
password = [neutron_서비스_계정_비밀번호]
project_name = service # neutorn 설치시 연동한 프로젝트 이름
project_domain_name = Default # neutorn 설치시 연동한 프로젝트 도메인 이름
user_domain_name = Default # neutorn 설치시 연동한 유저 도메인 이름 --- admin 이므로
auth_type = password
[neutron_defaults]
ovs_bridge = br-int # neutron 스위치를 설치한 네트워크 OR 컴퓨트 노드에서 설정한 br-int
service_subnet = [Service_Subnet_UUID]
pod_security_groups = [Security_Group_UUID]
pod_subnet = [Pod_Subnet_UUID]
external_svc_net = [External_Network_UUID]
external_svc_subnet = [External_Subnet_UUID]
project = [service 프로젝트 uuid]
[namespace_subnet]
pod_subnet_pool = [Pod_Subnet_UUID]
[cni_daemon]
docker_mode = false #런타임은 containerd 이므로
netns_proc_dir = /host_proc
[vif_plug_ovs_privileged]
helper_command = privsep-helper
[vif_plug_linux_bridge_privileged]
helper_command = privsep-helper
[pod_vif_nested]
worker_nodes_subnets = [Worker_Nodes_Subnet_UUID]
[binding]
driver = kuryr.lib.binding.drivers.vlan
link_iface = [노드_NIC_인터페이스명] # 워커 노드(서버)의 메인 이더넷 이름
[health_server]
port = 8082
[cni_health_server]
port = 8090
max_memory_usage = 4096
[]로 표시된 부분은 실제 OpenStack 및 K8S 환경에 맞춰 입력해야 하는 핵심 파라미터입니다.
이 값들은 openstack subnet list, openstack port list, openstack project list 명령어로 확인한 UUID를 넣어야 합니다.
Kuryr는 Kubernetes 환경에서 Pod와 Service를 OpenStack의 Neutron 포트 및 Octavia 로드밸런서로 직접 매핑합니다. 이를 통해 오버레이 네트워크 오버헤드를 제거하고 물리 망 수준의 성능을 구현할 수 있습니다.
Kuryr는 Kubernetes API를 확장하여 OpenStack 자원을 제어하므로, 반드시 다음 순서대로 리소스를 적용해야 합니다.
CNI 바이너리는 노드의 호스트 경로(/opt/cni/bin, /etc/cni/net.d)에 직접 설치되어야 합니다. DaemonSet에서 hostPath를 사용하여 이를 마운트함으로써 파드가 호스트의 CNI 환경을 제어할 수 있도록 구성합니다.
예시)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kuryr-cni-ds
namespace: kube-system
labels:
tier: node
app: kuryr-cni
spec:
selector:
matchLabels:
app: kuryr-cni
template:
metadata:
labels:
tier: node
app: kuryr-cni
spec:
hostNetwork: true
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoSchedule"
serviceAccountName: kuryr-cni
containers:
- name: kuryr-cni
image: drogva/kuryr-cni # OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(1) 에서 빌드한 도커 이미지
imagePullPolicy: IfNotPresent
command: [ "cni_ds_init" ]
env:
- name: KUBERNETES_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: KURYR_CNI_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
securityContext:
privileged: true
volumeMounts:
- name: bin
mountPath: /opt/cni/bin
- name: net-conf
mountPath: /etc/cni/net.d
- name: config-volume
mountPath: /etc/kuryr
- name: proc
mountPath: /host_proc
- name: var-pci
mountPath: /var/pci_address
- name: var-run
mountPath: /var/run
mountPropagation: HostToContainer
volumes:
- name: bin
hostPath:
path: /opt/cni/bin
- name: net-conf
hostPath:
path: /etc/cni/net.d
- name: config-volume
configMap:
name: kuryr-config
- name: var-run
hostPath:
path: /var/run
- name: proc
hostPath:
path: /proc
- name: var-pci
hostPath:
path: /var/pci_address
예시)
kuryr_controller.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
name: kuryr-controller
name: kuryr-controller
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
name: kuryr-controller
template:
metadata:
labels:
name: kuryr-controller
name: kuryr-controller
spec:
serviceAccountName: kuryr-controller
automountServiceAccountToken: true
hostNetwork: true
containers:
- image: drogva/kuryr-controller # OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(1) 에서 빌드한 도커 이미지
imagePullPolicy: IfNotPresent
name: controller
terminationMessagePath: "/dev/termination-log"
volumeMounts:
- name: config-volume
mountPath: "/etc/kuryr"
readOnly: true
volumes:
- name: config-volume
configMap:
name: kuryr-config
restartPolicy: Always
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoSchedule"
Kuryr는 Kubernetes 환경에서 파드(Pod)와 서비스(Service)를 OpenStack의 Neutron 네트워크와 직접 매핑합니다. 이를 통해 오버레이 네트워크 오버헤드를 제거하고 물리 망 수준의 고성능 네트워킹을 구현합니다.
Kuryr는 Kubernetes API를 확장하여 OpenStack 자원을 제어하므로, 반드시 다음 순서대로 리소스를 적용해야 합니다.
kubectl apply -f kuryr-kubernetes/cni_service_account.yml
kubectl apply -f kuryr-kubernetes/controller_service_account.yml
OpenStack 자원을 Kubernetes 객체로 관리하기 위한 필수 자원을 생성합니다.
kubectl apply -f kuryr-kubernetes/kuryrport.yaml
kubectl apply -f kuryr-kubernetes/kuryrnetwork.yaml
kubectl apply -f kuryr-kubernetes/kuryrnetworkpolicy.yaml
kubectl apply -f kuryr-kubernetes/networkattachment.yaml
kubectl apply -f kuryr-kubernetes/kuryrloadbalancer.yaml
Kuryr 컨트롤러와 CNI가 사용할 OpenStack 인증 정보 및 네트워크 UUID를 주입합니다. 이 단계가 누락되면 파드가 실행되지 않습니다.
# kube-system 네임스페이스에 ConfigMap 적용
kubectl apply -f kuryr-kubernetes/kuryr/config_map.yml -n kube-system
# 각 노드에 CNI 바이너리 배포
kubectl apply -f kuryr-kubernetes/cni_ds.yml
# Kuryr 컨트롤러 배포
kubectl apply -f kuryr-kubernetes/controller_deployment.yml

마스터 노드에서 생성된 토큰을 사용하여 워커 노드를 클러스터에 편입시키고, 네트워크 상태를 점검합니다.
kubeadm join [마스터_IP]:6443 \
--token [토큰] \
--discovery-token-ca-cert-hash [해시값]
구축 완료 후 다음 명령어로 파드 상태를 반드시 점검하십시오.
# 전체 파드 상태 확인
kubectl get pods -A
# Kuryr 컨트롤러 로그 확인 (실패 시 원인 분석용)
kubectl logs -n kube-system -l name=kuryr-controller
# 파드 강제 재생성 (ConfigMap 수정 후 적용 시)
kubectl delete pod -n kube-system -l name=kuryr-controller
kubectl delete pod -n kube-system -l app=kuryr-cni


kube-dns가 이제 단순 쿠버네티스 내부 파드 IP를 넘어, OpenStack 가상 네트워크에서도 관리되는 공인(혹은 사설) VIP가 되었음을 보여줍니다. SVC - Pod (ClusterIP) 연결로 인하여 클러스터 내부 파드들이 일상적인 DNS 통신을 할 수 있습니다.

☞ 왜 풀(Pool)이 3개나 생성?
쿠버네티스 서비스 매니페스트(kube-dns)에는 **여러 포트(53/UDP, 53/TCP, 9153/TCP)**가 정의되어 있습니다. OpenStack Octavia(로드밸런서)는 서비스의 각 포트를 독립적인 **'풀(Pool)'**로 관리합니다.





☞ OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(2) 에서 트렁크 기능을 활성화하고 트렁크 포트로 워커 노드를 생성한 이유가 이것입니다.


# service.yaml
apiVersion: v1
kind: Service
metadata:
name: spring-boot-service
namespace: kpop
spec:
type: LoadBalancer
selector:
app: spring-boot-load
ports:
- protocol: TCP
port: 80 # 외부에서 접속할 포트
targetPort: 8080 # 컨테이너 내부 애플리케이션 포트
-------------------------------------------------------------
# deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-load
namespace: kpop
spec:
replicas: 3
selector:
matchLabels:
app: spring-boot-load
template:
metadata:
labels:
app: spring-boot-load
spec:
containers:
- name: spring-boot-load
image: drogva/oh
ports:
- containerPort: 8080
resources:
limits:
cpu: 1
memory: 512Mi
requests:
cpu: 500m
memory: 256Mi
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
kubectl apply -f deploy.yaml -n kpop
kubectl apply -f service.yaml -n kpop





| OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(4) (0) | 2026.04.21 |
|---|---|
| OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(3) (0) | 2026.04.20 |
| OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(2) (0) | 2026.04.19 |
| OpenStack Zed 수동 구축 - 11. kuryr-kubernetes(1) (0) | 2026.04.19 |
| OpenStack Zed 수동 구축 - 10. Load Balancer 서비스 (Octavia) 사용해보기 (0) | 2026.04.16 |