Openstack Private Cloud 구축 -2

SangYeon Min·2025년 2월 13일
0

PROJECT-WHEN-WILL-WE-MEET

목록 보기
5/12
post-thumbnail

Controller Node Backup

### CONTROLLER
shutdown -h now

Compute Node를 설치하기 이전 현재 Rocky Linux Host 컴퓨터가 오래된 만큼 Controller Node를 안정적인 Window Host에 완전하게 백업할 필요성을 느껴 아래와 같은 과정으로 통해 백업하였다.

virsh dumpxml controller > /var/lib/libvirt/images/controller.xml

Controller Node VM을 그대로 다른 서버에서 복원하려면 디스크 파일뿐만 아니라 XML 설정도 필요하기 때문에 우선 virsh dumpxml을 통해 controller KVM의 XML 파일을 생성해주었다.

$ virsh snapshot-info controller --snapshotname Controller-Node-Complete
Name:           Controller-Node-Complete
Domain:         controller
Current:        yes
State:          disk-snapshot
Location:       external
Parent:         Installing-Nova
Children:       0
Descendants:    0
Metadata:       yes

이후 controller의 스냅샷이 external 임을 확인할 수 있었는데

Libvirt(virsh)의 스냅샷은 2가지 방식으로 저장된다.

  1. Internal Snapshot
  • 스냅샷이 qcow2 파일 내부에 저장됨
  • 즉, 디스크 파일(controller.qcow2)만 백업하면 스냅샷도 같이 보존됨
  • virsh snapshot-list에서 나오는 스냅샷들이 qcow2 안에 포함됨
  • 이 경우에는 스냅샷 하나만 백업해도 복구가 가능
  1. External Snapshot (외부 스냅샷)
  • 스냅샷이 별도 파일로 저장됨
  • /var/lib/libvirt/images/controller.Controller-Node-Complete 같은 파일이 외부 스냅샷일 가능성이 높음
  • 이 경우에는 관련된 모든 파일을 백업해야 복구가 가능
  • 일반적으로 External 방식이 OpenStack 환경에서 사용됨
$ ls /var/lib/libvirt/images
...
controller.Controller-Node-Complete
controller.environment-snapshot
controller.initial-snapshot
controller.Installing-Nova
controller.qcow2
controller.qcow2.bak
controller.xml
Rocky-9.2-x86_64-minimal.iso

따라서 이전까지 생성했던 모든 스냅샷과 Controller Node 관련 파일들을 모두 확인해주었고

tar -cvzf /home/judemin/controller_backup.tar.gz -C /var/lib/libvirt/images \
    controller.Controller-Node-Complete \
    controller.environment-snapshot \
    controller.initial-snapshot \
    controller.Installing-Nova \
    controller.qcow2 \
    controller.qcow2.bak \
    controller.xml \
    Rocky-9.2-x86_64-minimal.iso

/home/judemin/controller_backup.tar.gz 파일로 Controller Node와 관련된 모든 파일을 압축하였으며

### WINDOWS
scp judemin@<LinuxIP>:/home/judemin/controller_backup.tar.gz C:\Users\<WindowUserName>\Openstack-Backup

위와 같이 scp 명령어를 통해 Linux의 압축 파일을 Window로 복사하고

### WINDOWS
tar -xvzf C:\Users\<WindowUserName>\Openstack-Backup\controller_backup.tar.gz -C C:\Users\<WindowUserName>\Openstack-Backup\
x controller.Controller-Node-Complete
x controller.environment-snapshot
x controller.initial-snapshot
...

Window에서 tar 명령어를 통해 해당 압축 파일의 압축을 해제하여 백업을 완료할 수 있었다.

또한 해당 파일들을 Window 파일 탐색기를 통해 확인할 수 있다.

Trouble Shooting: passwd authentication token manipulation error

client_loop: send disconnect: Connection reset

judmin@xxx.xxx.xxx.xxx password: 
Permission denied, please try again.

root@xxx.xxx.xxx.xxx password: 
Permission denied, please try again.

Compute Node를 설정하기 위해 SSH를 통해 접속하려고 하자, 이전에 로그인된 상태에서 Connection reset 오류가 발생하고 이후 이전의 User 비밀번호들이 모두 사용 불가인 것을 확인하였다.

$ passwd
...
passwd authentication token manipulation error

따라서 passwd로 비밀번호 변경을 시도하였으나 위와 같은 오류가 발생하여

mount -o remount,rw /

root (/) 파일 시스템이 읽기 전용 (ro) 상태라면, 비밀번호 변경이나 시스템 수정이 불가능하므로

root (/) 파일 시스템을 읽기/쓰기 (rw) 모드로 다시 마운트하고

$ mount
/dev/mapper/rl-root on /proc/8401 type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)

이를 확인해준 후

$ lsattr /etc/passwd
---------------------- /etc/passwd

$ lsattr /etc/shadow
--------a------------- /etc/shadow

lsattr/etc/passwd/etc/shadow를 확인해본 결과 /etc/shadow 파일에 append-only (a) 속성이 설정되어 해당 파일에 기존 내용을 덮어쓰거나 삭제할 수 없는 문제로 판단해

$ chattr -a /etc/shadow
$ lsattr /etc/shadow
---------------------- /etc/shadow

/etc/shadow 파일의 append-only 속성을 제거하고

$ passwd judemin
Changing password for user judemin.
New password: 
Retype new password:
passwd: all authentication tokens updated successfully.

passwd로 비밀번호를 변경한 뒤 다시 정상적으로 접근할 수 있었다.

Trouble Shooting: Rocky Host dhcpd mallware

$ ps aux --sort=-%cpu | head -20
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root        2060  595  0.0 2449196 7168 ?        SNsl Feb09 632:19 /bin/dhpcd

이후 구축을 진행하던 도중 컴퓨터의 온도가 비정상적으로 높고 CPU의 사용량이 너무 높아 /bin/dhpcd 프로세스가 작동중인 것을 확인할 수 있었다.

Virsh(libvirt)에서 가상 네트워크를 사용할 때 기본적으로 dnsmasq가 DHCP 서버 역할을 하기에 정상적인 환경에서는 dnsmasq가 실행 중이어야 하고, dhpcd는 실행되지 않아야 한다.

 ps aux | grep dnsmasq
dnsmasq     1840  0.0  0.0  10432  2320 ?        S    Feb09   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/opstnat.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
root        1842  0.0  0.0  10328  1424 ?        S    Feb09   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/opstnat.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
dnsmasq     1894  0.0  0.0  10432  2184 ?        S    Feb09   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
root        1895  0.0  0.0  10328   904 ?        S    Feb09   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
root        6405  0.0  0.0   6408  2176 pts/4    S+   00:33   0:00 grep --color=auto dnsmasq

따라서 dnsmasq가 정상적으로 작동하고 있는지를 우선 확인하였다

$ ls -lah /bin/dhpcd
-rwxr-xr-x. 1 root root 2.3M Feb  7 02:32 /bin/dhpcd

이후 /bin/dhpcd 파일이 실제로 존재하는지 확인하고

$ file /bin/dhpcd
/bin/dhpcd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header

/bin/dhpcd의 정보를 출력해보았다.

정상적인 리눅스 실행 파일이라면 "dynamically linked" 또는 ELF 헤더가 포함되어 있어야 하는데, dhpcd는 이상한 실행 파일 패턴을 보이고 있기 때문에 멀웨어 파일이라고 판단하였다.

항목설명
Statically Linked모든 라이브러리를 내부에 포함하여 분석을 어렵게 만듦 (루트킷, 멀웨어 특성)
No Section Header정상적인 ELF 파일이 아니며, 분석을 어렵게 하기 위한 난독화(Obfuscation) 가능성 큼
/bin 경로/bin에 DHCP 관련 실행 파일이 존재하는 것은 매우 비정상적
$ sudo crontab -l | grep dhpcd
23 * * * * /bin/dhpcd >/dev/null 2>/dev/null

또한 crontab에서도 해당 파일을 실행하는 명령어가 존재하여

sudo crontab -e

우선 이를 제거해주었고

$ cat /etc/rc.local | grep dhpcd
/bin/dhpcd >/dev/null 2>/dev/null

rc.local 파일에서 해당 멀웨어 파일에 대한 실행 명령을 삭제해주었다.

sudo kill -9 2060
sudo pkill -9 dhpcd

이후 해당 프로세스를 제거한 후

sudo rm -f /bin/dhpcd

/bin/dhpcd 실행파일 또한 삭제해주었다.

Trouble Shooting: Rocky Host /bin/systemd-worker mallware

$ ps aux --sort=-%cpu | head -20
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         961  180  1.6 2490116 263892 ?      Ssl  Feb09 205:29 /bin/systemd-worker 43.226.237.68 212.113.112.44 138.124.19.102 148.72.168.29 154.86.156.69 61.74.135.124 221.179.57.254 8.219.93.186 67.159.24.26 173.231.184.126 35.247.243.10 180.97.195.28 36.41.172.79 192.210.241.208 134.209.188.18 54.159.112.99 119.84.66.98 87.120.165.243 84.21.173.166 45.128.99.140 213.159.67.143 119.84.66.55 41.225.238.233 103.124.101.54 129.227.58.70 50.2.36.119 178.128.125.32 209.141.57.99 212.132.93.112 103.145.145.79 170.84.39.236 223.75.204.39 79.120.74.12 14.199.52.62 45.95.146.8 103.195.101.126 125.124.83.191 23.94.194.210 61.53.69.210 45.61.185.64 139.159.102.236 185.181.210.57 188.235.173.12 207.244.252.183 43.239.110.69 125.124.99.83 107.155.37.142 84.21.173.97 57.128.179.23 45.95.147.221 64.227.163.222

이후에도 systemd-worker 라는 여러개의 호스트와 연결된 프로세스가 가동중인 것을 확인하고

$ sudo netstat -tulnp | grep systemd-worker
tcp6       0      0 :::1919                 :::*                    LISTEN      961/systemd-worker

systemd-worker가 외부의 포트와 연결되어 있는 것을 추가적으로 확인한 후

 systemctl list-units --type=service | grep systemd-worker
  systemd-worker.service                                 loaded active running systemd-worker.service

데몬에까지 포함되어 있는 것을 확인하여

sudo kill -9 961
sudo pkill -9 systemd-worker

해당 프로세스를 kill한 이후

sudo systemctl disable systemd-worker
sudo systemctl stop systemd-worker
sudo rm -f /etc/systemd/system/systemd-worker.service

최종적으로 데몬을 제거하고 데몬 파일 또한 삭제하여 이후 메모리와 CPU 사용량이 정상으로 돌아오는 것을 확인하였다.

Rootkit Check

루트킷(rootkit)은 악의적인 목적을 가진 공격자가 시스템에 침투하여 사용권한을 얻어낸 후 백도어 프로세스나 파일 등을 심어놓고 시스템 상에서 정상적인 관리자가 흔적을 볼 수 없도록 하는 프로그램을 의미
루트킷에 감염된 시스템은 심각한 경우 복구가 불가한 사태가 벌어질 수 있어, 예방차원에서 주기적으로 rkhunter를 이용한 점검이 필요

또한 이후 Openstack Host로의 공격을 효과적으로 탐지하고, 조치하기 위하여

sudo dnf install -y epel-release
sudo dnf install -y rkhunter

rkhunter 패키지를 설치하여 루트킷 프로그램을 탐지해내고자 한다

sudo rkhunter --update

패키지 설치 후 최신 보안 데이터베이스를 업데이트하고

sudo rkhunter --check

rkhunter를 통해 시스템 전체 검사를 실행하여 루트킷, 악성코드, 백도어가 있는지 확인한다.

sudo cat /var/log/rkhunter/rkhunter.log | grep -i warning

또한 검사 결과는 /var/log/rkhunter.log 파일에 저장되어 이를 확인해주고

$ sudo crontab -e
0 12 * * * /usr/bin/rkhunter --check --sk

cron을 설정하여 시스템을 주기적으로 검사할 수 있도록 하였다.

passwd root
passwd judemin

또한 avast의 Password Generator로 생성된 비밀번호로 현존하는 사용자의 비밀번호를 모두 변경해주어 추가적인 Brute Force 공격을 방지한다.

http://avast.com/random-password-generator#pc

Trouble Shooting: There was 200 failed login attempt since the last successful login

이후 다량의 로그인 실패 시도를 포착하여 Brute-forcing이나 자동 로그인 시도를 막기 위하여

sudo yum install fail2ban -y

Fail2Ban을 설치하여 여러 번 로그인 시도한 IP를 차단할 수 있도록 하였다

$ vim /etc/fail2ban/jail.conf
[sshd]
enabled = true
maxretry = 10
findtime = 600
bantime = 86400

설치 이후 /etc/fail2ban/jail.conf를 위와 같이 수정하였다

- maxretry = 10
실패한 로그인 시도가 10번 초과되면 차단

  • findtime = 600
    600초(10분) 동안 maxretry 횟수를 초과하는 로그인 실패가 감지되면 차단

  • bantime = 86400
    IP 차단 시간이 86400초(1일) 동안 지속

sudo systemctl enable fail2ban --now
sudo systemctl status fail2ban

이후 위와 같이 fail2ban 데몬을 실행하고

$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     385
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session
`- Actions
   |- Currently banned: 6
   |- Total banned:     6
   `- Banned IP list:   139.59.127.12 217.182.249.2 87.120.113.119 182.215.66.232 64.226.110.156 196.251.70.115

현재 호스트 머신을 공격중인 IP를 위 명령어로 확인할 수 있었다.


Compute Node Configuration

KVM Configuration

virt-install \
  --name compute \
  --ram 7000 \
  --vcpus 6 \
  --cpu host-passthrough \
  --disk path=/home/libvirt/images/compute.qcow2,size=100,format=qcow2,bus=virtio,cache=none \
  --os-variant rocky9.0 \
  --location /var/lib/libvirt/images/Rocky-9.2-x86_64-minimal.iso \
  --extra-args "inst.text console=ttyS0,115200n8" \
  --network network=opstnat,model=virtio \
  --graphics none

우선 설계와 같이 CPU와 MEM, Disk를 할당하여 KVM을 생성한다.

이때, CPU 성능을 극대화하기 위해 --cpu host-passthrough 옵션을 사용해 VM이 host CPU의 기능을 그대로 활용할 수 있도록 하였다.

df -h
Filesystem           Size  Used Avail Use% Mounted on
devtmpfs             4.0M     0  4.0M   0% /dev
tmpfs                7.7G     0  7.7G   0% /dev/shm
tmpfs                3.1G  9.4M  3.1G   1% /run
/dev/mapper/rl-root   70G   30G   41G  43% /
/dev/nvme0n1p1      1014M  595M  420M  59% /boot
/dev/mapper/rl-home  398G  2.9G  396G   1% /home
tmpfs                1.6G  4.0K  1.6G   1% /run/user/0
tmpfs                1.6G  4.0K  1.6G   1% /run/user/1000

또한 여유 공간이 충분한 /home 파티션에 디스크 파일 경로 지정하여 설치를 진행하였다.

$ pwd
/home/libvirt/images

이때 /home/libvirt/images 해당 디렉터리를 생성해주어야 한다.

[root@compute ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:01:56:bd brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.102/24 brd 192.168.100.255 scope global dynamic noprefixroute enp1s0
       valid_lft 3592sec preferred_lft 3592sec
    inet6 fe80::5054:ff:fe01:56bd/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

이전 포스팅에서와 같이 Rocky Linux를 Text 모드로 설치하고 virsh net을 통해 Static IP 설정을 위와 같이 완료해주었다.

### COMPUTE
[root@compute ~]# ping controller
PING controller (192.168.100.101) 56(84) bytes of data.
64 bytes from controller (192.168.100.101): icmp_seq=1 ttl=64 time=0.411 ms
64 bytes from controller (192.168.100.101): icmp_seq=2 ttl=64 time=0.812 ms
### CONTROLLER
[root@controller ~]# ping compute
PING compute (192.168.100.102) 56(84) bytes of data.
64 bytes from compute (192.168.100.102): icmp_seq=1 ttl=64 time=0.436 ms
64 bytes from compute (192.168.100.102): icmp_seq=2 ttl=64 time=0.254 ms

또한 Compute Node와 Conrtoller Node에서 각각의 Hostname을 통해 네트눠크 상에서 접근이 가능한 것을 확인할 수 있었다.

Prerequisite

SQL Database, Message Queue, Memcached, Etcd 등은 Controller Node에서만 실행되고 Compute NodeController Node와 통신해서 필요한 데이터를 가져오는 방식이므로 개별적인 설치가 필요 없다.

또한 Cinder는 선택적으로 설치 가능하지만 현재 Controller Node에서 관리하기 때문에 Compute Node에서는 openstack-nova-computeneutron-openvswitch-agent만 설치하면 된다.

NTP Configuration

$ yum install -y chrony
$ vim /etc/chrony.conf
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
server controller iburst

또한 NTP 동기화를 위해 위와 같이 chrony를 설치하고 conf 파일을 수정한다.

systemctl enable chronyd.service
systemctl start chronyd.service

이후 chronyd.service 데몬을 재시작하면

$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^? controller                    0   6     0     -     +0ns[   +0ns] +/-    0ns

chronyc 명령어를 통해 NTP의 소스를 controller로 잘 반영하고 있는 것을 볼 수 있다.

OpenStack Repository

dnf install -y libvirt qemu-kvm virt-install virt-manager

우선 Libvirt와 관련 패키지를 설치하고

dnf config-manager --enable crb
dnf install -y centos-release-openstack-zed
dnf config-manager --set-enabled centos-openstack-zed
dnf repolist | grep openstack

이전 Controller Node와 같이 OpenStack Zed 저장소 추가한다.

dnf clean all
dnf makecache
dnf update -y

이후 패키지 목록을 업데이트하면 Openstack Service들을 설치할 준비가 끝난다.

Nova Configuration

yum install -y openstack-nova-compute

openstack-nova-compute 패키지를 설치해준 후

vim /etc/nova/nova.conf

/etc/nova/nova.conf를 아래 공식 문서에 맞게 수정하고

https://docs.openstack.org/nova/2023.1/install/compute-install-rdo.html

$ egrep -c '(vmx|svm)' /proc/cpuinfo
12

위 명령어를 통해 현재 vcpu 지원 현황을 알 수 있다.

systemctl enable libvirtd.service openstack-nova-compute.service
systemctl start libvirtd.service openstack-nova-compute.service

또한 이후 libvirtdopenstack-nova-compute 데몬을 실행하여준다.

Trouble Shooting: ERROR oslo.messaging._drivers.impl_rabbit Connection failed: [Errno -3] Lookup timed out

만약 위와 같은 오류가 난다면

### CONTROLLER
firewall-cmd --permanent --add-port=5672/tcp
firewall-cmd --reload
firewall-cmd --list-ports | grep 5672

Controller의 RabbitMQ 서버로의 접근이 불가하여 발생할 수 있기에 Controller의 방화벽에서 5672 포트를 연다.

$ telnet controller 5672
Trying 192.168.100.101...
Connected to controller.
Escape character is '^]'.

아후 telnet을 통해 정상적으로 Controller의 RabbitMQ 서버 (:5672)로 접근이 가능한 것을 확인한다.

$ vim vi /etc/nova/nova.conf
# transport_url = rabbit://openstack:<RABBIT_PASS>@controller
transport_url = rabbit://openstack:<RABBIT_PASS>@controller:5672/

공식 문서와는 다르게 /etc/nova/nova.conf를 위와 같이 수정하고

$ rabbitmqctl list_users
Listing users ...
user    tags
openstack       []
guest   [administrator]

rabbitmqctl을 통해 openstack User의 권한을 확인해주었을 때 admin이 아닌 것을 확인해

rabbitmqctl set_permissions openstack ".*" ".*" ".*"
rabbitmqctl set_user_tags openstack administrator
$ rabbitmqctl list_users
user    tags
openstack       [administrator]

openstack User의 Permission과 태그를 변경하여준다.

$ rabbitmqctl list_vhosts
Listing vhosts ...
name
/
rabbitmqctl add_vhost openstack
rabbitmqctl set_permissions -p openstack openstack ".*" ".*" ".*"

또한 이후 RabbitMQ의 vhost를 추가하여주고

systemctl restart rabbitmq-server.service

rabbitmq-server를 재시작한다.

$ vi /etc/nova/nova.conf
...
auth_url = 

이후 /etc/nova/nova.conf에서 auth_url를 이전에 Controller Node와 같이 설정하고

### CONTROLLER
openstack role add --project admin --user nova admin

nova User의 권한 문제를 방지하기 위해 admin 권한을 추가해주고

### CONTROLLER
firewall-cmd --permanent --add-port=5000/tcp
firewall-cmd --reload

추가적으로 Keystone에 접근할 수 있는 5000번 포트를 방화벽에서 허용한다.

### COMPUTE
$ vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.101  controller

최종적으로 Compute Nodecontroller 호스트를 추가하고

### COMPUTE
systemctl restart openstack-nova-compute

openstack-nova-compute 데몬을 재시작하여 문제를 해결할 수 있었다.

Trouble Shooting: ERROR Compute driver option required, but not specified

$ vim /etc/nova/nova.conf
[DEFAULT]
...
compute_driver = libvirt.LibvirtDriver

이후 위와 같은 오류가 발생하면 compute_driver 관련 설정을 추가하여 해결할 수 있다.

$ systemctl status openstack-nova-compute
● openstack-nova-compute.service - OpenStack Nova Compute Server
     Loaded: loaded (/usr/lib/systemd/system/openstack-nova-compute.service; en>
     Active: active (running) since Mon 2025-02-10 02:28:15 EST; 7s ago
   Main PID: 53926 (nova-compute)
      Tasks: 23 (limit: 48950)
     Memory: 126.2M
        CPU: 1.830s
     CGroup: /system.slice/openstack-nova-compute.service
             └─53926 /usr/bin/python3 /usr/bin/nova-compute

Trouble Shooting: FileNotFoundError: No such file or directory: '/usr/lib/python3.9/site-packages/instances

mkdir -p /var/lib/nova/instances
chown -R nova:nova /var/lib/nova/instances
chmod 755 /var/lib/nova/instances

또한 만약 위와 같은 오류가 발생한다면 /var/lib/nova/instances에 대한 접근 권한이나 해당 디렉터리 자체가 존재하지 않는다는 것이므로 이를 생성해주고

$ ls -ld /var/lib/nova/instances
drwxr-xr-x. 2 nova nova 6 Apr 26  2024 /var/lib/nova/instances

/var/lib/nova/instances 디렉터리를 확인해준 이후

systemctl restart openstack-nova-compute

데몬을 재시작하여 이를 해결할 수 있었다.

Compute Node Cell 추가

$ su -s /bin/sh -c "nova-manage cell_v2 discover_hosts --verbose" nova
...
Getting computes from cell 'cell1': 4c904549-f28f-4d89-b47d-b1de8135152a

이후 새로운 Compute 노드를 추가할 때, 해당 Compute 노드를 등록하려면 Controller 노드에서 위 명령을 실행해야 하기 때문에 해당 명령어를 실행해주면

$ openstack compute service list
+--------------------------------------+----------------+------------+----------+---------+-------+----------------------------+
| ID                                   | Binary         | Host       | Zone     | Status  | State | Updated At                 |
+--------------------------------------+----------------+------------+----------+---------+-------+----------------------------+
| 799ac5c1-3d7a-4517-b113-d39398c2f0c3 | nova-scheduler | controller | internal | enabled | up    | 2025-02-10T07:29:41.000000 |
| 28892e63-324f-48ff-8813-1f7b4a4bee91 | nova-conductor | controller | internal | enabled | up    | 2025-02-10T07:29:37.000000 |
| 259f0bee-aa49-4511-a110-9d077e8f5032 | nova-compute   | compute    | nova     | enabled | up    | 2025-02-10T07:29:40.000000 |
+--------------------------------------+----------------+------------+----------+---------+-------+----------------------------+

정상적으로 compute 호스트가 추가되고 이를 확인할 수 있다.

Neutron Configuration

yum install -y openstack-neutron-openvswitch

Neutron을 설정하기 위해 우선 openstack-neutron-openvswitch 패키지를 설치하고

$ vim /etc/neutron/neutron.conf

공식 문서에 맞게 /etc/neutron/neutron.conf 파일을 수정한다

https://docs.openstack.org/neutron/2023.1/install/compute-install-rdo.html

ovs-vsctl add-br br-ex

이후 br-ex 브릿지를 추가하고

vim /etc/neutron/plugins/ml2/openvswitch_agent.ini

/etc/neutron/plugins/ml2/openvswitch_agent.ini 또한 동식 문서에 맞게 수정한 뒤

https://docs.openstack.org/neutron/2023.1/install/compute-install-option2-rdo.html

$ vim /etc/nova/nova.conf
auth_url = http://controller:5000
auth_type = password
project_domain_name = Default
user_domain_name = Default
region_name = RegionOne
project_name = admin
username = neutron
password = NEUTRON_PASS

/etc/nova/nova.conf에 Neutron 관련 설정 파일을 추가한다

systemctl restart openstack-nova-compute.service
systemctl enable neutron-openvswitch-agent.service
systemctl start neutron-openvswitch-agent.service

이후 관련 데몬들을 재시작하거나, 시작해주면 된다.

Trouble Shooting: OpenVswitch PermissionError: [Errno 13] Permission denied

$ tail -n 50 /var/log/neutron/openvswitch-agent.log
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/cmd/eventlet/plugins/ovs_neutron_agent.py", line 27, in main
2025-02-10 03:15:32.769 54568 ERROR neutron     agent_main.main()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/main.py", line 38, in main
2025-02-10 03:15:32.769 54568 ERROR neutron     of_main.main()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/main.py", line 34, in main
2025-02-10 03:15:32.769 54568 ERROR neutron     app_manager.AppManager.run_apps([
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/os_ken/base/app_manager.py", line 371, in run_apps
2025-02-10 03:15:32.769 54568 ERROR neutron     hub.joinall(services)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/os_ken/lib/hub.py", line 112, in joinall
2025-02-10 03:15:32.769 54568 ERROR neutron     t.wait()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/eventlet/greenthread.py", line 181, in wait
2025-02-10 03:15:32.769 54568 ERROR neutron     return self._exit_event.wait()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/eventlet/event.py", line 132, in wait
2025-02-10 03:15:32.769 54568 ERROR neutron     current.throw(*self._exc)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/eventlet/greenthread.py", line 221, in main
2025-02-10 03:15:32.769 54568 ERROR neutron     result = function(*args, **kwargs)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/os_ken/lib/hub.py", line 74, in _launch
2025-02-10 03:15:32.769 54568 ERROR neutron     raise e
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/os_ken/lib/hub.py", line 69, in _launch
2025-02-10 03:15:32.769 54568 ERROR neutron     return func(*args, **kwargs)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_oskenapp.py", line 44, in agent_main_wrapper
2025-02-10 03:15:32.769 54568 ERROR neutron     LOG.exception("Agent main thread died of an exception")
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/oslo_utils/excutils.py", line 227, in __exit__
2025-02-10 03:15:32.769 54568 ERROR neutron     self.force_reraise()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/oslo_utils/excutils.py", line 200, in force_reraise
2025-02-10 03:15:32.769 54568 ERROR neutron     raise self.value
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_oskenapp.py", line 41, in agent_main_wrapper
2025-02-10 03:15:32.769 54568 ERROR neutron     ovs_agent.main(bridge_classes)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py", line 2948, in main
2025-02-10 03:15:32.769 54568 ERROR neutron     agent = OVSNeutronAgent(bridge_classes, ext_mgr, cfg.CONF)       
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py", line 156, in __init__
2025-02-10 03:15:32.769 54568 ERROR neutron     self.ovs = ovs_lib.BaseOVS()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/agent/common/ovs_lib.py", line 144, in __init__
2025-02-10 03:15:32.769 54568 ERROR neutron     self.ovsdb = impl_idl.api_factory()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/agent/ovsdb/impl_idl.py", line 37, in api_factory
2025-02-10 03:15:32.769 54568 ERROR neutron     _idl_monitor = n_connection.OvsIdlMonitor()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/agent/ovsdb/native/connection.py", line 109, in __init__
2025-02-10 03:15:32.769 54568 ERROR neutron     super(OvsIdlMonitor, self).__init__()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/agent/ovsdb/native/connection.py", line 84, in __init__
2025-02-10 03:15:32.769 54568 ERROR neutron     helper = self._get_ovsdb_helper(self._ovsdb_connection)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/neutron/agent/ovsdb/native/connection.py", line 99, in _get_ovsdb_helper
2025-02-10 03:15:32.769 54568 ERROR neutron     helpers.enable_connection_uri(connection)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/oslo_privsep/priv_context.py", line 269, in _wrap
2025-02-10 03:15:32.769 54568 ERROR neutron     self.start()
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/oslo_privsep/priv_context.py", line 283, in start
2025-02-10 03:15:32.769 54568 ERROR neutron     channel = daemon.RootwrapClientChannel(context=self)
2025-02-10 03:15:32.769 54568 ERROR neutron   File "/usr/lib/python3.9/site-packages/oslo_privsep/daemon.py", line 348, in __init__
2025-02-10 03:15:32.769 54568 ERROR neutron     listen_sock.bind(sockpath)
2025-02-10 03:15:32.769 54568 ERROR neutron PermissionError: [Errno 13] Permission denied
2025-02-10 03:15:32.769 54568 ERROR neutron

하지만 위와 같은 오류가 발생하여

sudo mkdir -p /var/run/neutron
sudo chown neutron:neutron /var/run/neutron
sudo chmod 755 /var/run/neutron

우선 /var/run/neutron 디렉터리를 생성해주고

$ ls -l /var/run/openvswitch/db.sock
srwxr-x---. 1 openvswitch hugetlbfs 0 Feb 10 03:22 /var/run/openvswitch/db.sock

Open vSwitch의 데이터베이스 소켓이 정상적으로 생성되었는지 확인한 뒤

sudo chown openvswitch:openvswitch /var/run/openvswitch/db.sock
sudo chmod 660 /var/run/openvswitch/db.sock

해당 소켓의 소유자와 권한을 변경해준다.

setenforce 0

이후 혹시나 SELinux의 문제일 수도 있어 Permissive 모드로 변경한 후

sudo systemctl restart openvswitch
sudo systemctl restart neutron-openvswitch-agent

관련 데몬을 재시작하여

$ openstack network agent list
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+
| ID                                   | Agent Type         | Host       | Availability Zone | Alive | State | Binary                    |
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+
| 1f9257e6-e17c-44f7-847e-7b42611db8af | Open vSwitch agent | compute    | None              | :-)   | UP    | neutron-openvswitch-agent |
| 5714217b-89ba-416f-8c16-ee556ed3e8e2 | Open vSwitch agent | controller | None              | :-)   | UP    | neutron-openvswitch-agent |
| 70b36a3e-68fd-4f1b-8ee4-cb5645140281 | Metadata agent     | controller | None              | :-)   | UP    | neutron-metadata-agent    |
| df911242-e20c-4f08-ac59-45a4e0dc2e9f | L3 agent           | controller | nova              | :-)   | UP    | neutron-l3-agent          |
| f4726158-927e-434c-8591-ca340534d40b | DHCP agent         | controller | nova              | :-)   | UP    | neutron-dhcp-agent        |
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+

위와 같이 compute 호스트의 neutron-openvswitch-agent를 확인할 수 있었다.


Network Reconfiguration

Configure Management(Internal) Network with VXLAN Tunnel

1. VXLAN이란?

VXLAN(Virtual eXtensible LAN)은 L3 네트워크 위에서 L2 네트워크를 확장할 수 있도록 하는 오버레이 네트워크 기술이다.

이는 OpenStack, Kubernetes 같은 클라우드 환경에서 가상 네트워크를 격리하고 확장하는 데 많이 활용된다.

특히, 데이터센터에서는 각 노드가 별도의 관리 IP(예: 10.0.0.x)를 사용하여 VXLAN 터널을 구성한다.

2. VXLAN 내부망(오버레이 네트워크) 구조

VXLAN을 사용하면 물리적 L3 네트워크(관리 네트워크) 위에서 가상 L2 네트워크를 생성할 수 있다.
이를 위해 각 노드는 관리 네트워크의 IP(10.0.0.x)를 사용하여 VXLAN 터널을 설정하게 된다.

예를 들어, OpenStack 환경에서 다음과 같은 물리적 L3 네트워크(관리 네트워크)를 가질 수 있다.

Controller Node: 10.0.0.1
Compute Node 1: 10.0.0.2
Compute Node 2: 10.0.0.3
Network Node: 10.0.0.4

관리 네트워크를 통해 VXLAN 터널을 설정하고, 각 노드는 서로 캡슐화된 패킷을 주고받게 된다.

3. VXLAN 터널링 과정

VXLAN을 통한 네트워크 통신은 VTEP(VXLAN Tunnel Endpoint)라는 개념을 이용해 이루어진다.

1) VTEP 설정`

  • Compute Node, Network Node 등은 VTEP 역할을 수행
  • VTEP관리 네트워크(10.0.0.x)에서 IP를 할당받고, 이를 통해 VXLAN 패킷을 송수신

2) VNI(VXLAN Network Identifier) 할당

  • VXLAN에서는 네트워크를 구분하기 위해 VNI(24bit)를 사용
  • 예를 들어, 특정 테넌트(사용자 그룹)마다 다른 VNI를 할당 가능
    Tenant A: VNI 1001
     Tenant B: VNI 1002
  • 같은 VNI를 가진 가상머신(VM)끼리만 VXLAN 터널을 통해 통신할 수 있음

3) VXLAN 캡슐화 및 터널링

  • 3-1) Compute Node에서 VM이 다른 Compute NodeVM과 통신하려고 하면, 패킷은 VXLAN을 통해 캡슐화
  • 3-2) 캡슐화된 패킷은 L3 네트워크(10.0.0.x 관리 네트워크)를 통해 다른 Compute NodeVTEP으로 전달
  • 3-3) 목적지 VTEPVXLAN 헤더를 해제하고 원래 L2 프레임VM에게 전달

4. VXLAN을 사용하는 이유

  • L3 네트워크 위에서 L2 네트워크를 확장할 수 있어, 데이터센터 간 가상 네트워크 구축이 가능
  • 각 테넌트별로 VNI를 다르게 할당해 보안성을 높일 수 있음
  • 기존 VLAN(4096개 제한)보다 훨씬 많은 16M(2^24) 개의 네트워크를 지원할 수 있다
  • 물리적인 네트워크 구조와 상관없이 유연하게 가상 네트워크를 구성할 수 있음

Overlay Network란 기존 네트워크를 바탕으로 그 위에 구성된 또 다른 네트워크, 기존의 네트워크 위에 별도의 노드들(nodes)과 논리적 링크들(logical links)을 구성하여 이루어진 가상 네트워크
오버레이 네트워크를 구성하기 위해 오버레이 네트워크의 기반 네트워크인 언더레이 네트워크(Underlay Network)가 필요하다

Open vSwitch(OVS)

1. Open vSwitch(OVS)란?

  • Open vSwitch(OVS)가상화 환경에서 네트워크 스위칭 기능을 제공하는 오픈소스 가상 스위치
  • 물리적인 네트워크 스위치처럼 동작하면서도, SDN(Software-Defined Networking) 및 가상 네트워크를 지원하는 것이 특징
  • OVS는 OpenStack, Kubernetes, VMware, KVM, Xen 등의 가상화 환경에서 많이 사용되며, VXLAN, GRE, Geneve 등의 터널링을 통해 오버레이 네트워크를 구성

2. Open vSwitch의 주요 기능

1) L2 스위칭 기능

  • OVS는 물리적 스위치처럼 동작하면서 가상 네트워크에서 L2 패킷을 처리 가능
  • VLAN 태깅, MAC 주소 학습, Spanning Tree Protocol(STP) 지원

2) SDN 지원 (OpenFlow)

  • OpenFlow 프로토콜을 통해 SDN 컨트롤러에서 OVS를 원격으로 관리 가능
  • 대표적인 OpenFlow 컨트롤러: OVN, OpenDaylight, Ryu, ONOS

3) 오버레이 네트워크 지원 (VXLAN, GRE, Geneve)

  • OVS는 VXLAN, GRE, Geneve와 같은 오버레이 터널링을 지원
  • 이를 통해 가상 네트워크를 L3 네트워크 위에서 확장 가능

4) 고급 QoS 및 트래픽 관리

  • QoS(Quality of Service) 기능을 통해 네트워크 트래픽을 제어할 수 있음
  • Rate Limiting, Traffic Shaping, Queue Management 지원

5)고급 패킷 필터링 및 보안

  • OVS는 iptables, eBPF, Conntrack 등을 사용해 방화벽 및 보안 정책을 설정 가능
  • DDoS 방어, ACL(Access Control List) 지원

3. Open vSwitch의 아키텍처

OVS는 크게 커널 모듈, 유저 스페이스 데몬, OpenFlow 프로토콜로 구성

1) 커널 모듈 (openvswitch.ko)

  • OVS는 리눅스 커널 모듈(openvswitch.ko)을 통해 L2 스위칭을 처리
  • 커널 공간에서 동작하여 고속 패킷 포워딩을 지원

2) 유저 스페이스 데몬 (ovs-vswitchd, ovsdb-server)

  • ovs-vswitchd: OVS의 핵심 프로세스로, OpenFlow 규칙을 기반으로 패킷을 처리
  • ovsdb-server: OVS 데이터베이스(OVSDB)를 관리하며, 설정 정보를 저장

3) OpenFlow 프로토콜 (SDN 컨트롤러 연동)

  • OVS는 OpenFlow를 사용하여 SDN 컨트롤러와 통신 가능
  • 이를 통해 네트워크 정책을 중앙에서 관리하고, 동적으로 플로우를 설정할 수 있음

Scenario

1) 가상 머신 간 통신

  • 같은 호스트 내에서 VM끼리 통신할 경우
  • 예를 들어, Host 1 내의 VM1VM2가 통신하려면, OVS 브릿지(br0)가 패킷을 포워딩
  • 이 과정에서 패킷은 물리 네트워크를 거치지 않고, 브릿지를 통해 전달

2) 다른 호스트 간 통신

  • 서로 다른 호스트의 VM끼리 통신하려면
  • 예를 들어, VM1(Host 1)에서 VM3(Host 2)로 패킷을 보낼 경우
    1. VM1의 패킷이 OVS 브릿지 br0를 통해 캡슐화됨
    2. 캡슐화된 패킷은 물리 네트워크 인터페이스(eth0)를 통해 데이터 네트워크로 전송
    3. Host 2OVS 브릿지가 패킷을 수신하고, 캡슐화를 해제한 뒤 VM3로 전달

3) 관리 네트워크

  • 각 호스트관리 네트워크(Management Network)를 통해 통신하며, SDN 컨트롤러 또는 관리 서버와 연동
  • 이 네트워크는 OVS의 설정 및 상태 관리를 위한 통신에 주로 사용

Self Service Network Reconfiguration

Self Service Network(tenant 네트워크) 환경에서는 인스턴스오버레이 네트워크(ex. VXLAN을 통해 192.168.200.0/24 같은 별도 대역)를 사용하지만, 외부와 통신하려면 Provider 네트워크(앞서 nmclibr-ex192.168.100.x를 할당한 flat 네트워크)를 연결해 주어야 한다.

즉, 두 네트워크를 Neutron Router로 연결하여 인스턴스는 내부 self-service 네트워크의 이점을 누리면서도, Floating IP를 통해 외부 통신이 가능하도록 구성하고자 한다.

1.virsh net 설정

$ virsh net-edit opstnat
<network>
  <name>opstnat</name>
  <uuid>bf2cbc9d-43a4-4249-b430-a02b5ba3acc1</uuid>
  <forward mode='nat'/>
  <bridge name='virbr10' stp='off' delay='0'/>
  <mac address='52:54:00:8c:9b:3c'/>
  <ip address='192.168.100.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.100.100' end='192.168.100.200'/>
      <host mac='c6:c0:9b:4d:33:42' name='controller' ip='192.168.100.101'/>
      <host mac='6a:66:48:b9:1f:43' name='compute' ip='192.168.100.102'/>
    </dhcp>
  </ip>
</network>

우선 위와 같이 virsh의 opstnat의 MAC 주소를 현재 br-ex의 주소로 변경한다.

2. Provider 네트워크 구성 (Compute Node)

2-0. NetworkManager OVS 플러그인 설치

dnf install -y epel-release
dnf install -y openvswitch NetworkManager-ovs

이후 NetworkManager에서 OVS를 활용하기 위해 위 패키지들을 설치한다.

network-manager-openvswitch 라는 패키지 이름이 RHEL/CentOS 계열 기본 저장소에는 없거나 이름이 다를 수 있기 때문에 epel-release 레파지토리를 우선적으로 설치해주어야 한다.

sudo systemctl restart NetworkManager
sudo systemctl restart openvswitch

이후 해당 데몬들을 재시작해주고

$ vim /etc/NetworkManager/NetworkManager.conf
[main]
plugins=keyfile,ifcfg-rh,ovs

NetworkManager.conf에서 ovs 플러그인을 활성화한다.

sudo dnf install -y dhcp-client

이후 추후 br-ex의 IP 주소를 opstnat의 DHCP 서버로부터 받아오기 위한 dhcp-client를 설치한다.

2-1. enp1s0의 기존 IP 설정 제거

nmcli connection modify "enp1s0" ipv4.method disabled
nmcli connection down "enp1s0"

이후 enp1s0에 할당된 IP를 비활성화합니다.
(“Wired connection 1”와 같이 인터페이스가 아니라 Connection Name을 활용해야 한다)

2-2. OVS 브릿지(br-ex) 및 포트 구성 (ovs-vsctl 이용)

sudo ovs-vsctl add-br br-ex

이후 OVS 브릿지를 생성해주고

sudo ovs-vsctl add-port br-ex enp1s0

enp1s0br-ex의 포트로 추가한다.

즉, OVSenp1s0의 트래픽을 직접 제어할 수 있도록 enp1s0br-export로 연결하는 것이다.

2-3. nmcli로 OVS 브릿지에 IP 할당

nmcli connection add type ovs-bridge ifname br-ex con-name br-ex-ip

OVS 브릿지에 외부 IP를 할당하려면, nmcliovs-bridge 타입의 Connection을 생성해야 한다.
(이때 Connection Name은 br-ex-ip를 사용한다)

nmcli connection modify br-ex-ip \
  ipv4.method manual \
  ipv4.addresses "192.168.100.102/24" \
  ipv4.gateway "192.168.100.1" \
  ipv4.dns "8.8.8.8 8.8.4.4"

br-ex-ip 연결에 IP, 게이트웨이, DNS 등을 수동으로 설정한다.

Compute Node이기 때문에 192.168.100.102를 할당한다.

nmcli connection up br-ex-ip

이제 br-ex 인터페이스에 192.168.100.102/24가 할당되고, 기본 게이트웨이가 192.168.100.1로 설정된다.

2-4.neutron 연결

$ vim /etc/neutron/plugins/ml2/openvswitch_agent.ini
...
[ovs]
bridge_mappings = provider:br-ex

[vxlan]
local_ip = 192.168.100.102
l2_population = true

이후 neutron 서비스에 연결하기 위해 /etc/neutron/plugins/ml2/openvswitch_agent.inilocal_ip 설정을 변경하고

2-5. libvirt 도메인 XML 편집

<interface type='network'>
  <mac address='6a:66:48:b9:1f:43'/>
  <source network='opstnat'/>
  <model type='virtio'/>
  <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>

libvirt 도메인의 network interface 설정을 위와 같이 변경한다.

6a:66:48:b9:1f:43br-ex에 할당된 MAC 주소이다

sudo dhclient -v br-ex

이후 인터페이스에서 DHCP 요청을 강제로 재시도한다.

  • dhclient (독립 실행형 DHCP 클라이언트)
    • dhclient는 단순히 해당 인터페이스에 대해 직접 DHCP 요청을 보내고 응답을 받아 IP를 할당한다
    • NetworkManager의 복잡한 상태 관리나 연결 프로파일 검증 없이 동작하므로, 인터페이스가 DHCP 요청을 받을 수 있으면 바로 IP를 얻는다

따라서, Compute 노드에서 nmcli로 연결을 올리면 NetworkManager가 내부 구성이나 다른 활성 연결 때문에 정상적으로 진행되지 않아 타임아웃이 발생할 수 있지만 수동으로 dhclient를 실행하면 그런 제약 없이 직접 DHCP 요청을 보내 IP를 받기 때문에 성공할 수 있다.

[root@compute ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP group default qlen 1000
    link/ether 6a:66:48:b9:1f:43 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::f38:74f3:29bf:d0c5/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether b6:12:c1:fd:d5:c7 brd ff:ff:ff:ff:ff:ff
4: br-ex: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 6a:66:48:b9:1f:43 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.102/24 brd 192.168.100.255 scope global dynamic br-ex
       valid_lft 3658sec preferred_lft 3658sec
5: br-int: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000        
    link/ether f6:5a:ee:fe:e6:47 brd ff:ff:ff:ff:ff:ff

[root@compute ~]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=31.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=32.2 ms

이후 위와 같이 Compute Node에서 br-ex를 통해 Internet으로 정상적으로 라우팅 되는 모습을 볼 수 있다.

enp1s0br-ex의 MAC 주소가 동일한 이유는 enp1s0Open vSwitch(OVS)의 브릿지 br-ex에 추가된 포트로 설정되었기 때문이며 Open vSwitch에서 브릿지와 포트는 기본적으로 동일한 MAC 주소를 공유한다.

따라서 현 구성에서는 중복된 MAC 주소는 OVS의 정상 동작 방식으로 판단하여도 된다.

2-6. /etc/systemd/system/dhclient-br-ex.service

하지만 이러한 방식으로는 KVM을 재부팅 할 때마다 수동으로 DHCP 요청을 매번 보내주어야 하는 번거로움이 있었다.

$ vim /etc/systemd/system/dhclient-br-ex.service 
[Unit]
Description=DHCP Client for br-ex interface
After=openvswitch.service NetworkManager.service
Requires=openvswitch.service NetworkManager.service
Before=network.target

[Service]
Type=oneshot
ExecStart=/sbin/dhclient -v br-ex
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable dhclient-br-ex.service

따라서 KVM이 재부팅 될때마다 dhclient 실행해줄 수 있는 데몬을 생성하였다.

또한 dhclient-br-ex.service unit 파일의 [Unit] 섹션에 Open vSwitchNetworkManager 서비스에 대한 의존성을 추가하여, 이들 서비스가 시작된 후에 dhclient를 실행하도록 설정하였다.

[  OK  ] Started Network Manager.
         Starting Network Manager Wait Online...
         Starting DHCP Client for br-ex interface...
[  OK  ] Started Network Manager Script Dispatcher Service.
[FAILED] Failed to start Network Manager Wait Online.
See 'systemctl status NetworkManager-wait-online.service' for details.
[  OK  ] Finished DHCP Client for br-ex interface.

하지만 위와 같이 NetworkManager-wait-online.service를 대기하여 부팅 시간이 비정상적으로 길어지고, 최종적으로 해당 데몬의 실행은 불가한 문제를 발하였다.

NetworkManager-wait-online.service는 NetworkManager가 관리하는 네트워크가 "온라인" 상태가 될 때까지 시스템 부팅이나 특정 서비스의 시작을 지연시키는 역할을 한다.

설정된 타임아웃 내에 네트워크 연결이 완전히 수립되지 않으면, 서비스는 실패로 처리되고 이후의 네트워크 의존 서비스들은 별도로 처리된다.

sudo systemctl disable NetworkManager-wait-online.service
sudo systemctl mask NetworkManager-wait-online.service

따라서 클라우드 환경이나, Open vSwitch, NetworkManager 등 다른 네트워크 관리 도구로 네트워크 인터페이스를 별도로 제어하는 경우에는 이 서비스가 불필요하거나 오히려 부팅 지연 및 오류를 유발할 수 있기 때문에 굳이 이 서비스를 활성화할 필요가 없으므로 비활성화하여 부팅 속도를 개선할 수 있었다.

또한 마스킹을 통해 다른 서비스가 의존성을 통해 실행하려 할 때도 해당 서비스가 실행되지 않도록 하였다.

$ ip route
192.168.100.0/24 dev br-ex proto kernel scope link src 192.168.100.102

이후 라우팅 테이블에서 192.168.100.1로의 Default 라우트가 누락되어 있는 것을 발견하여

$ vim /etc/systemd/system/dhclient-br-ex.service 
[Unit]
Description=DHCP Client for br-ex interface
After=openvswitch.service NetworkManager.service
Requires=openvswitch.service NetworkManager.service
Before=network.target

[Service]
Type=oneshot
ExecStart=/sbin/dhclient -v br-ex
ExecStartPost=/bin/sh -c 'if ! ip route | grep -q "^default"; then ip route add default via 192.168.100.1 dev br-ex; fi'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

데몬의 ExecStartPost: dhclient을 통해 실행이 끝난 후, ip route 명령으로 기본 경로가 없는 경우에만 ip route add 명령으로 default gateway를 추가하도록 변경하여 이를 수정하였다.

3. Provider Network 구성 (Controller Node)

Controller Node의 경우 IP 주소만 192.168.100.101로 변경하여 Compute Node와 동일하게 구성하였다.

Trouble Shooting: No DHCPOFFERS received. No working leases in persistent database - sleeping.

[  OK  ] Started Network Manager Script Dispatcher Service.
[FAILED] Failed to start DHCP Client for br-ex interface.
See 'systemctl status dhclient-br-ex.service' for details.

하지만 위와 같이 정상적으로 네트워크를 설정한 이후 Host PC를 리부트하고 난 뒤 위와 같이 부팅 중 dhclient-br-ex.service가 실패하는 모습을 볼 수 있었다

$ dhclient -v br-ex
...
DHCPDISCOVER on br-ex to 255.255.255.255 port 67 interval 9 (xid=0x51ba5b6f)
No DHCPOFFERS received.
No working leases in persistent database - sleeping.

따라서 위와 같이 수동으로 다시 dhclient를 작동시켰을 때 No working leases in persistent database와 같은 오류 메세지를 볼 수 있었다.

### COMPUTE
$ sudo ovs-vsctl get Bridge br-ex fail_mode
secure

우선 OVSbr-exfail_mode를 점검하였다.

OVS에서 br-ex → enp1s0로 트래픽이 이동하려면, br-exfail_mode가 STP를 활성화하지 않고 정상적인 포워딩 상태여야 한다

위와 같이 결과가 "secure"이면, OVS가 자동으로 트래픽을 차단하는 상태이다

sudo ovs-vsctl set Bridge br-ex fail_mode=standalone

따라서 fail_modestandalone로 변경해주었다

  • fail_mode=secure

    • OVS OpenFlow Controller가 반드시 있어야 네트워크 트래픽을 처리할 수 있음
    • 컨트롤러가 없거나 비활성화되면, OVS는 모든 패킷을 block 함
    • 기본적으로 새로운 플로우를 자동으로 처리하지 않음 (컨트롤러가 명시적으로 허용한 트래픽만 전달)
    • 보안적으로 더 강력하지만, 컨트롤러가 작동하지 않으면 네트워크가 정지할 위험이 있음
    • OpenFlow 컨트롤러(ex. OpenDaylight, Ryu, ONOS)를 이용해 정밀한 트래픽 제어를 할 때나 컨트롤러를 통한 네트워크 정책 제어가 필수적일 때 해당 모드를 사용한다
  • fail_mode=standalone

    • OpenFlow Controller 없이도 OVS 브리지가 일반적인 L2 스위치처럼 동작
    • 기본적으로 모든 트래픽을 포워딩하고, OVS OpenFlow Controller가 있더라도 컨트롤러 연결이 끊어져도 계속 작동
    • 새로운 트래픽을 자동으로 처리하며, STP(Spanning Tree Protocol) 같은 네트워크 제어 기능을 사용할 수 있음
    • OpenFlow 컨트롤러 없이 OVS를 L2 스위치처럼 사용하고 싶을 때나 컨트롤러 연결이 불안정하거나, OVS가 기본적인 L2 브리지처럼 동작해야 할 때 사용한다
### HOST
ps aux | grep dnsmasq
sudo systemctl restart libvirtd

또한 dnsmasq가 실행 중인지 확인하고

### HOST
ps aux | grep dnsmasq
sudo systemctl restart libvirtd

DHCP 포트(67) Listen 여부를 확인해주었다

### HOST
$ virsh domiflist compute
 Interface   Type      Source    Model    MAC
-------------------------------------------------------------
 vnet0      network   opstnat   virtio   6a:66:48:b9:1f:43

이후 compute의 인터페이스로 vnet0가 붙어있음을 확인하여주고

sudo tcpdump -i vnet0 port 67 or port 68 -n
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on vnet0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
03:13:54.962220 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 6a:66:48:b9:1f:43, length 300
...
$ sudo tcpdump -i virbr10 port 67 or port 68 -n

tcpdump를 통해 패킷을 검사해보았다.

이때, vnet0으로는 패킷이 정상적으로 도달하지만 KVM의 NAT 네트워크인 opstnatvirbr10로는 패킷이 도달하지 않음을 확인하고

$ sudo brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.525400330d65       yes
virbr10         8000.5254008c9b3c       no

brctl을 통해 virbr10 브리지가 존재하지만, 인터페이스 목록이 없음, 즉 vnet0이 연결되지 않음을 확인하였다.

vnet0이 없으면 VM의 네트워크 패킷이 브리지를 통해 전달되지 않는다

$ sudo brctl addif virbr10 vnet0
$ sudo brctl addif virbr10 vnet1
$ sudo brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.525400330d65       yes
virbr10         8000.5254008c9b3c       no              vnet0
                                                        vnet1

따라서 vnet0,vnet1virbr10에 수동으로 추가하고

cat /var/lib/libvirt/dnsmasq/opstnat.hostsfile 
36:4c:6a:07:2e:4d,192.168.100.101,controller
6a:66:48:b9:1f:43,192.168.100.102,compute

호스트에서 현재 DHCP Lease 확인해준 후

rm /var/lib/libvirt/dnsmasq/opstnat.hostsfile
sudo systemctl restart libvirtd
sudo systemctl restart dnsmasq

DHCP Lease 파일을 제거하여 DHCP 서버를 초기화하고, 새로운 요청을 받을 준비를 마쳤다

### COMPUTE
### CONTROLLER
sudo dhclient -r br-ex
sudo dhclient -v br-ex

이후 위와 같이 VM 내에서 -r 옵션을 통해 dhclient를 reload해주고 다시 DHCP 요청을 보내

$ sudo tcpdump -i virbr10 port 67 or port 68 -n
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on virbr10, link-type EN10MB (Ethernet), snapshot length 262144 bytes
03:23:22.715149 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 6a:66:48:b9:1f:43, length 300
03:23:22.715719 IP 192.168.100.1.bootps > 192.168.100.102.bootpc: BOOTP/DHCP, Reply, length 301

DHCP 요청 패킷이 virbr10까지 정상적으로 도달하는 것을 확인하였고, br-ex의 IP 또한 이전과 동일하게 부여받은 이후 Openstack 서비스들의 동작을 확인할 수 있었다.

br-ex -> enp1s0 -> vnet0(VM이 연결된 인터페이스) -> virbr10 -> opstnat

Compute VM에서 DHCP 요청을 보내는 경로는 위와 같다


Final Summary

Multi Compute Node Self Service Network Scenario

일반적인 Multi Compute Node Self Service Network 시나리오는 위와 같이 구성된다.

OVS 브릿지 및 포트 구조

해당 시나리오의 Open vSwitch(OVS) 네트워크는 OpenStack Neutron에서 흔히 사용되는 구조이며, 세 개의 브릿지(br-ex, br-int, br-tun)와 여러 개의 포트가 연결되어 있다.

포트명풀네임소속 브릿지역할
enp1s0Physical Interface 1br-ex물리 인터페이스, 외부 네트워크 연결
br-exExternal Bridgebr-ex외부 네트워크 브릿지
phy-br-exPhysical Bridge - Externalbr-exbr-intbr-ex를 연결하는 패치 포트
int-br-exInternal Bridge - Externalbr-intbr-intbr-ex를 연결하는 패치 포트
br-intIntegration Bridgebr-int내부 가상 네트워크 스위칭
patch-tunPatch Tunnel Portbr-intbr-intbr-tun을 연결하는 패치 포트
patch-intPatch Integration Portbr-tunbr-intbr-tun을 연결하는 패치 포트
br-tunTunnel Bridgebr-tunVXLAN/GRE 터널을 통해 Compute Node 간 가상 네트워크 연결

포트 설명 및 연결 관계

1) br-ex (External Bridge)

  • 주요 포트

    • enp1s0 (물리 네트워크 인터페이스)
      • 실제 물리 네트워크 카드(NIC)
      • 외부 네트워크(인터넷)과 연결됨
    • phy-br-ex (br-exbr-int 연결을 위한 패치 포트)
      • int-br-ex와 연결되는 패치 포트
      • br-exbr-int를 연결하여 내부 네트워크가 외부 네트워크로 나갈 수 있도록 함
  • 연결 관계

    br-ex  <->  enp1s0 (물리 네트워크 연결)
     br-ex  <->  phy-br-ex  <->  int-br-ex  <->  br-int
    • enp1s0는 물리 네트워크와 연결되어 외부와 통신
    • phy-br-ex를 통해 br-int와 연결되어 내부 네트워크의 VM이 외부 네트워크와 통신 가능

2) br-int (Integration Bridge)

  • 주요 포트

    • int-br-ex (br-intbr-ex 연결을 위한 패치 포트)
      • phy-br-ex와 연결되어 내부 네트워크의 트래픽이 외부 네트워크로 나가도록 함
    • patch-tun (br-intbr-tun 연결을 위한 패치 포트)
      • patch-int와 연결되어 오버레이 네트워크(VXLAN/GRE)와 통신
  • 연결 관계

    br-int  <->  int-br-ex  <->  phy-br-ex  <->  br-ex
     br-int  <->  patch-tun  <->  patch-int  <->  br-tun
    • br-int는 VM들이 직접 연결되는 주요 브릿지
    • int-br-ex를 통해 br-ex와 연결되어 외부 네트워크와 통신
    • patch-tun을 통해 br-tun과 연결되어 다른 Compute Node의 VM과 통신

3) br-tun (Tunnel Bridge)

  • 주요 포트

    • patch-int (br-tunbr-int 연결을 위한 패치 포트)
      • patch-tun과 연결되어 VXLAN/GRE 터널을 통해 Compute Node 간 가상 네트워크 트래픽을 전달
  • 연결 관계

    br-tun  <->  patch-int  <->  patch-tun  <->  br-int
    • br-tun은 오버레이 네트워크(VXLAN/GRE)를 관리하는 브릿지
    • patch-int를 통해 br-int와 연결되어 VM 간 통신을 담당

Tap 인터페이스(TAP Device)

  • VMTap 인터페이스(TAP Device) 를 통해 OVS 브릿지(br-int)와 연결된다
  • 해당 Tap 인터페이스는 OpenStack의 Neutron과 libvirt(KVM)에 의해 자동으로 생성되며, VM의 가상 NIC(VNIC) 역할을 수행한다

인스턴스가 br-int와 연결되는 과정

  1. 인스턴스 생성 시 Tap 인터페이스 생성

    • OpenStack에서 VM을 생성하면, libvirtTap 인터페이스(TAP Device) 를 생성
    • 해당 Tap 인터페이스는 qemu-kvm에 의해 VM의 가상 네트워크 인터페이스(VNIC) 로 인식
  2. Tap 인터페이스를 br-int에 연결

    • ovs-vsctl을 사용하여 Tap 인터페이스를 br-int의 포트로 추가
    • ex. tap12345678-xx라는 Tap 인터페이스가 VM과 연결됨
      $ ovs-vsctl show
       ...
          Bridge br-int
              Port tap12345678-xx
                  Interface tap12345678-xx
  3. Neutron이 OVS 플로우 테이블을 설정

    • OpenStack NeutronSDN 컨트롤러 역할을 하며, br-int의 OpenFlow 룰을 설정함
    • 이는 인스턴스 간의 네트워크 트래픽을 제어하는 데 사용됨

인스턴스 네트워크 트래픽 흐름

  1. 인스턴스 간 통신 (br-int)
    • 같은 Compute Node 내 인스턴스 간 트래픽은 br-int에서 직접 처리됨
Instance1 -> br-int -> Instance2
  1. 다른 Compute Node의 인스턴스와 통신 (br-tun)
    a. 인스턴스가 br-int에서 patch-tun을 통해 br-tun으로 패킷 전송
    b. br-tun에서 VXLAN/GRE 캡슐화를 수행하여 다른 Compute Node로 패킷 전달
    c. 상대 Compute Node의 br-tun이 패킷을 수신
    d. patch-int를 통해 br-int로 전달
    e. br-int에서 대상 VM에게 패킷 전달
Instance1 -> br-int -> patch-tun -> patch-int -> br-tun (Compute Node 1)->
		-> VXLAN/GRE 터널 ->
        -> br-tun(Compute Node 2) -> patch-int -> patch-tun -> br-int -> Instance4
  1. 외부 네트워크와 통신 (br-ex)
    a. 인스턴스가 br-int로 패킷 전송
    b. br-int에서 int-br-ex를 통해 br-ex로 전달
    c. br-ex에서 enp1s0(물리 인터페이스)로 패킷 전달
    d. enp1s0를 통해 외부 네트워크로 송출
Instance1 -> br-int -> int-br-ex -> phy-br-ex -> br-ex -> enp1s0 -> 외부 네트워크

OpenStack 서비스와 OVS와의 관계

  • OpenStack 서비스OVS는 직접적인 관계가 없다.
  • OVS는 네트워크 패킷을 전달하는 역할을 하고, OpenStack 서비스들은 API와 데이터 통신을 위해 br-ex 또는 다른 내부 네트워크를 사용한다

1). OpenStack 서비스들이 OVS를 통해 통신하는 방식

  1. OpenStack 내부 서비스 간 통신
    • OpenStack의 주요 서비스들은 일반적으로 controller 노드에서 실행되며, IP(192.168.100.101)를 기반으로 API 통신을 수행
    • 이 통신은 OVS와 직접적인 관계가 없음, OVS를 거치지 않고 br-ex의 IP(192.168.100.101)로 직접 API 통신을 수행
    • 예제:
      • nova-apikeystone 인증 요청 (http://192.168.100.101:5000)
      • neutron-serverrabbitmq 메시지 전달 (amqp://192.168.100.101:5672)
      • glance-apikeystone 인증 요청 (http://192.168.100.101:5000)

2) OpenStack의 가상 네트워크(Neutron)에서 OVS를 활용하는 경우

OpenStack NeutronOVS를 사용하여 가상 네트워크를 관리

  1. OpenStack Neutron은 OVS 브릿지(br-int, br-tun, br-ex)를 사용하여 VM의 네트워크 트래픽을 처리

    • VM 내부 통신br-int
    • VM 간 오버레이 네트워크 통신(VXLAN, GRE)br-tun
    • VM이 외부 네트워크와 통신br-ex
  2. 가상 머신(VM)이 외부 네트워크(192.168.100.0/24)로 나갈 때

    • VM의 패킷은 br-int에서 br-ex로 전달
    • br-ex는 물리 인터페이스(enp1s0)를 통해 외부 네트워크로 송출
  3. Floating IP 설정 시

    • OpenStack에서는 VM이 외부 네트워크와 통신할 수 있도록 Floating IP(공인 IP)를 사용
    • br-ex는 외부 네트워크를 담당하고 있으므로, Floating IP는 br-ex를 통해 외부 네트워크와 연결됨

결론

  • br-ex외부 네트워크와 연결 (물리 인터페이스)
  • br-int가상 머신 간 통신 및 L2 네트워크 스위칭
  • br-tunCompute Node 간 VXLAN/GRE 터널링
  • 패치 포트 (phy-br-ex, int-br-ex, patch-tun, patch-int) 를 통해 각 브릿지가 서로 연결
항목OVS 사용 여부설명
OpenStack API 통신OpenStack 서비스끼리 API 통신을 수행 (192.168.100.101)
VM 간 내부 통신br-int를 통해 같은 Compute Node 내 VM 간 통신
VM 간 다른 Compute Node 통신VXLAN/GRE 터널(br-tun)을 통해 전달
VM이 외부 네트워크 통신br-ex를 통해 외부 네트워크로 송출

Current Scenario (Self-Service Network)

Self-Service Network

  • “Self-Service”라는 용어는 테넌트(사용자)가 별도의 물리적 네트워크 구성이나 관리자 개입 없이, 오버레이 기술을 통해 가상 네트워크를 생성하고 관리할 수 있음을 의미
    • 테넌트 자율성: 사용자가 네트워크, 서브넷, 라우터 등 네트워크 리소스를 직접 생성·관리할 수 있다
    • 추상화: 복잡한 터널링과 오버레이 구성은 Controller가 담당하므로, Compute Node에서는 단순 연결(포트 연결)만 처리하게 되어 관리 부담이 줄어든다

Scenario 네트워크 구성

  • OVS 브릿지 br-ex외부 네트워크 연결 담당
    • 192.168.100.101 할당됨 (외부 네트워크에서 접근 가능한 IP)
    • enp1s0(물리 인터페이스)를 통해 외부 네트워크와 연결됨
  • Controller 노드 (controller)
    • 192.168.100.101 → OpenStack의 주요 서비스가 실행되는 호스트
    • OpenStack의 API 서비스, 데이터베이스, 메시지 큐 등이 실행됨
    • OpenStack 네트워크 서비스(Neutron), 가상 머신(VM), 내부 네트워크를 관리해야 함

현 시나리오에서는 동일한 IP/인터페이스ManagementOverlay 트래픽을 같이 처리
하지만 Production 환경에서는 트래픽 분산 및 보안 등을 위해 별도 NIC를 사용하는 것이 일반적인 권장사항
따라서 추후 ManagementOverlay 트래픽을 분리할 예정

Controller Node

컨트롤러 노드는 Neutron 서버ML2 플러그인을 통해 오버레이 네트워크(예: VXLAN 또는 GRE 터널)를 생성하고 관리

### CONTROLLER
$ ovs-vsctl show
e066f1d7-b888-4e29-8cb6-484dc0a0c83d
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Bridge br-ex
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-ex
            Interface br-ex
                type: internal
        Port enp1s0
            Interface enp1s0
        Port phy-br-ex
            Interface phy-br-ex
                type: patch
                options: {peer=int-br-ex}
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-int
            Interface br-int
                type: internal
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}
    ovs_version: "3.1.3"

Compute Node

Self-service 네트워크 시나리오에서는 Compute NodeML2 플러그인을 별도로 설정할 필요가 없으며, Neutron 에이전트(neutron-openvswitch-agent)를 통해 인스턴스의 네트워크 인터페이스를 br-exbr-int에만 연결

### COMPUTE
$ ovs-vsctl show
85ccefa0-dd2d-4696-8ffe-bed0ece90fa1
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Bridge br-ex
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-ex
            Interface br-ex
                type: internal
        Port enp1s0
            Interface enp1s0
        Port phy-br-ex
            Interface phy-br-ex
                type: patch
                options: {peer=int-br-ex}
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port br-int
            Interface br-int
                type: internal
    ovs_version: "3.1.3"

결론적으로, 현재 시나리오인 Compute NodeML2 설정을 하지 않고 br-ex, br-int만 사용하는 구성은 Self-Service Network의 기본 취지, 즉 테넌트가 오버레이 기반의 가상 네트워크를 직접 사용할 수 있도록 하면서도 인프라의 복잡한 설정은 중앙(Controller)에서 처리하도록 하는 설계와 부합한다.

또한 단일 Compute Node에서는 인스턴스 간 트래픽이 모두 로컬 br-int에서 처리되므로 터널 브리지(br-tun)가 필요 없다


Instance Deployment Test

Network Configuration

openstack network create ext-net \
  --external \
  --provider-physical-network provider \
  --provider-network-type flat

--external 옵션을 사용하여 외부 네트워크임을 지정합니다.
--provider-physical-network provider와 --provider-network-type flat 옵션은 Neutron의 물리 네트워크(br-ex와 enp1s0 매핑) 설정에 맞춰 조정합니다.

openstack network create private-net

인스턴스가 속하게 될 내부 네트워크를 생성합니다.

openstack subnet create private-subnet \
  --network private-net \
  --subnet-range 10.0.0.0/24 \
  --allocation-pool start=10.0.0.10,end=10.0.0.200 \
  --gateway 10.0.0.1

--subnet-range는 tenant 네트워크의 IP 범위를 정의합니다.
--allocation-pool은 인스턴스에 할당 가능한 IP 범위입니다.
--gateway는 서브넷의 기본 게이트웨이로, 나중에 라우터와 연결할 때 사용됩니다.

openstack router create router1

tenant 네트워크와 외부 네트워크 간 라우팅을 위해 라우터를 생성합니다.

openstack router set router1 --external-gateway ext-net

라우터에 외부 네트워크(ext-net)를 연결하여, 내부 인스턴스가 외부로 나갈 수 있는 경로를 제공합니다.

openstack router add subnet router1 private-subnet

tenant 네트워크의 서브넷(private-subnet)을 라우터에 연결하여, 네트워크 간 트래픽이 라우팅되도록 합니다.

$  ovs-vsctl show
...
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-int
            Interface br-int
                type: internal
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port qg-1bbdb6e1-37
            tag: 2
            Interface qg-1bbdb6e1-37
                type: internal
        Port tapa182dc24-7a
            tag: 1
            Interface tapa182dc24-7a
                type: internal
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port qr-b69bdbee-ee
            tag: 1
            Interface qr-b69bdbee-ee
                type: internal

Port qg-1bbdb6e1-37

  • tag: 2 → VLAN 태그 2번을 사용
  • type: internal → 내부 가상 인터페이스
  • 네트워크 노드에서 외부 라우터와 연결되는 인터페이스
  • qg-로 시작하는 이름 → "qg"는 Quantum Gateway (현재 Neutron Gateway) 포트를 의미
  • 이 포트는 외부 네트워크(external network)와 연결되어 Floating IP 트래픽을 처리

Port tapa182dc24-7a

  • tag: 1 → VLAN 태그 1번을 사용
  • type: internal → 내부 가상 인터페이스
  • tap-로 시작하는 포트는 VM(가상 머신)과 연결된 인터페이스
  • VM 내부의 vNIC(가상 네트워크 인터페이스)와 연결됨
  • tag: 1이므로 네트워크 세그먼트 1에 속한 네트워크

Port qr-b69bdbee-ee

  • tag: 1 → VLAN 태그 1번을 사용
  • type: internal → 내부 가상 인터페이스
  • qr-로 시작하는 포트는 Neutron Router의 내부 인터페이스 (Quantum Router)
  • VM이 속한 서브넷과 라우터를 연결하는 인터페이스
  • tag: 1이므로 VLAN 세그먼트 1과 연결됨

Flavor Configuration

# t2.nano (1 vCPU, 0.5GB RAM)
openstack flavor create t2.nano \
  --vcpus 1 \
  --ram 512 \
  --disk 8 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.micro (1 vCPU, 1GB RAM)
openstack flavor create t2.micro \
  --vcpus 1 \
  --ram 1024 \
  --disk 8 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.small (1 vCPU, 2GB RAM)
openstack flavor create t2.small \
  --vcpus 1 \
  --ram 2048 \
  --disk 20 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.medium (2 vCPUs, 4GB RAM)
openstack flavor create t2.medium \
  --vcpus 2 \
  --ram 4096 \
  --disk 40 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.large (2 vCPUs, 8GB RAM)
openstack flavor create t2.large \
  --vcpus 2 \
  --ram 8192 \
  --disk 40 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.xlarge (4 vCPUs, 16GB RAM)
openstack flavor create t2.xlarge \
  --vcpus 4 \
  --ram 16384 \
  --disk 80 \
  --swap 0 \
  --ephemeral 0 \
  --public

# t2.2xlarge (8 vCPUs, 32GB RAM)
openstack flavor create t2.2xlarge \
  --vcpus 8 \
  --ram 32768 \
  --disk 160 \
  --swap 0 \
  --ephemeral 0 \
  --public
$ openstack flavor list
+--------------------------------------+------------+-------+------+-----------+-------+-----------+
| ID                                   | Name       |   RAM | Disk | Ephemeral | VCPUs | Is Public |
+--------------------------------------+------------+-------+------+-----------+-------+-----------+
| 050dd2b1-4d42-4a77-9e23-b56e59b83f94 | t2.medium  |  4096 |   40 |         0 |     2 | True      |
| 1f3d5bab-46f7-4b37-b63e-96c8709988d8 | t2.nano    |   512 |    8 |         0 |     1 | True      |
| 6d40e04f-2897-4966-9b27-b5866d6a9d2c | t2.small   |  2048 |   20 |         0 |     1 | True      |
| b4f7f1cc-c987-4168-ada7-a40392c5209e | t2.xlarge  | 16384 |   80 |         0 |     4 | True      |
| de3a8ace-937c-4c26-bcd0-4dbc4a9f7550 | t2.2xlarge | 32768 |  160 |         0 |     8 | True      |
| fbf44a9d-0e44-4098-8ea1-c2abacad93bc | t2.large   |  8192 |   40 |         0 |     2 | True      |
| ff81773d-27ad-490d-976f-fb9f58caf697 | t2.micro   |  1024 |    8 |         0 |     1 | True      |
+--------------------------------------+------------+-------+------+-----------+-------+-----------+

이후 인스턴스를 생성해주기 위해 위와 같이 flavor를 생성한다.

Flavor NamevCPUsRAMRoot DiskSwapEphemeral Disk
t2.nano1512MB8GB0GB0GB
t2.micro11GB8GB0GB0GB
t2.small12GB20GB0GB0GB
t2.medium24GB40GB0GB0GB
t2.large28GB40GB0GB0GB
t2.xlarge416GB80GB0GB0GB
t2.2xlarge832GB160GB0GB0GB

이때, AWS의 flavor의 스펙에 맞게 생성해주었다.

또한 현재 AWS에서 배포되고 있는 서버는 t2.micro임을 확인하였다.

Image Selection

OS크기장점비고
Alpine Linux~5MB초경량, 빠른 부팅, 최소 패키지기본적으로 glibc 미포함 (musl libc 사용)
Ubuntu Minimal (20.04/22.04 LTS)~29MB안정적, LTS 지원, 보안 업데이트 우수표준 Ubuntu보다 가벼움
Debian Slim (11/12)~22MB안정적, 패키지 호환성 우수보안 패치가 빠름
Rocky Linux Minimal (9.x)~50MBRHEL 계열, CentOS 대체, 안정적CentOS 계열 선호 시 선택 가능

이후 인스턴스 생성을 위한 이미지를 선택하기 위해 위와 같이 사용이 가능한 이미지를 비교하였다.

wget https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2 -O rocky-linux-9.qcow2

현재 Rocky Linux로 Host와 KVM을 구동하고 있는 만큼 물리 서버 설치용 ISO 이미지와 다르게, 클라우드 플랫폼에서 VM을 쉽게 배포할 수 있도록 사전 설정된 Rocky Linux Generic Cloud 이미지를 사용하도록 하였따.

  • Generic Cloud 이미지
    • Pre-installed 클라우드 에이전트
    • OpenStack, AWS, Azure, GCP 같은 클라우드 플랫폼에서 실행될 수 있도록 cloud-init이 기본 포함
    • 인스턴스 첫 부팅 시 자동으로 SSH 키 등록, 사용자 계정 설정, 네트워크 설정 가능
    • 최소한의 패키지만 포함
    • 핵심 패키지만 유지하여 보안성과 성능을 극대화.
$ ls -alh
-rw-r--r--  1 root root 582M Nov 18 22:53 rocky-linux-9.qcow2

우선 위와 같이 wget을 통해 이미지를 다운로드 받고

openstack image create "Rocky Linux 9 Generic Cloud" \
  --file /root/images/rocky-linux-9.qcow2 \
  --disk-format qcow2 \
  --container-format bare \
  --public \
  --property os_type=linux \
  --property architecture=x86_64 \
  --property hypervisor_type=kvm

이미지를 생성하였따.

Trouble Shooting: HttpException: 500: Server Error for url

HttpException: 500: Server Error for url: http://controller:9292/v2/images, The server has either erred or is incapable of performing the requested operation.: 500 Internal Server Error

하지만 이때 위와 같은 오류가 발생하였다.

$ journalctl -u openstack-glance-api.service --no-pager  | tail -n 20
...
Feb 11 08:06:52 controller glance-api[27776]: 2025-02-11 08:06:52.818 27776 ERROR oslo.limit.limit [None req-1cda880a-e48e-4748-b340-c32d8e20fde5 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] Unable to initialize OpenStackSDK session: An auth plugin is required to determine endpoint URL: keystoneauth1.exceptions.auth_plugins.MissingAuthPlugin: An auth plugin is required to determine endpoint URL
Feb 11 08:06:52 controller glance-api[27776]: 2025-02-11 08:06:52.819 27776 ERROR glance.common.wsgi [None req-1cda880a-e48e-4748-b340-c32d8e20fde5 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] Caught error: Can't initialise OpenStackSDK session: An auth plugin is required to determine endpoint URL.: oslo_limit.exception.SessionInitError: Can't initialise OpenStackSDK session: An auth plugin is required to determine endpoint URL.

따라서 로그를 확인해보니 An auth plugin is required to determine endpoint URL 에러 로그를 확인할 수 있었고

$ openstack endpoint list --service glance
+----------------------------------+-----------+--------------+--------------+---------+-----------+------------------------+
| ID                               | Region    | Service Name | Service Type | Enabled | Interface | URL          
          |
+----------------------------------+-----------+--------------+--------------+---------+-----------+------------------------+
| 5882de4449b24b73a2b11835df9db71d | RegionOne | glance       | image        | True    | public    | http://controller:9292 |
| bc020a0587104bff898dc0dcd41ca0ed | RegionOne | glance       | image        | True    | admin     | http://controller:9292 |
| e18153d6e3924d268112ea67d1544f42 | RegionOne | glance       | image        | True    | internal  | http://controller:9292 |
+----------------------------------+-----------+--------------+--------------+---------+-----------+------------------------+

우선적으로 glance의 엔드포인트 리스트와

$ openstack user list
+----------------------------------+-----------+
| ID                               | Name      |
+----------------------------------+-----------+
| 12529d0a6bfa4cb1894172f0fe354de9 | admin     |
| 83110e40de9742ce87f8d83f02133151 | glance    |
| 597d8fb3ab914e0783bf6eaecef35963 | placement |
| bfdfdbc65be14efb9bddbafdd6c489a6 | nova      |
| 0114d4f75c8d44bd8a38ec2b77377d9d | neutron   |
| 1fec3efa30104587b9dbf1e4bd6c41bf | judemin   |
| 8673de9e4098497eb131fa319ba733a4 | cinder    |
+----------------------------------+-----------+

openstack의 user를 확인해주었다.

openstack role add --project admin --user glance admin

Authorization의 문제일 수도 있어 우선 glance user의 권한을 admin으로 변경하고

$ curl -i -X POST http://controller:5000/v3/auth/tokens      -H "Content-Type: application/json"      -d '{
           "auth": {
             "identity": {
               "methods": ["password"],
               "password": {
                 "user": {
                   "name": "glance",
                   "domain": { "id": "default" },
                   "password": "glance"
                 }
               }
             },
             "scope": {
               "project": {
                 "name": "admin",
                 "domain": { "id": "default" }
               }
             }
           }
         }'

{"token": {"methods": ["password"], "user": {"domain": {"id": "default", "name": "Default"}, ...

직접적으로 Keystone에 요청이 정상적으로 이루어지는 것을 확인한 후

$ vim /etc/glance/glance-api.conf
[oslo_limit]
auth_type = password
...
endpoint_id = 5882de4449b24b73a2b11835df9db71d

[glance_store]
filesystem_store_datadir = /var/lib/glance/images/

[keystone_authtoken]
service_token_roles_required = True

glance-api.conf에서 아래 두 가지 설정을 수정해주었다.

  • /images -> /images/
  • auth_type = password

이는 oslo_limit 섹션이 Keystone 인증 정보를 제대로 찾지 못해 발생하는 전형적인 문제인 것을 확인할 수 있었고

systemctl restart openstack-glance-api.service

Glance 데몬을 재시작하여 해당 문제를 해결할 수 있었다.

Trouble Shooting: 413 Request Entity Too Large

HttpException: 413: Client Error for url: http://controller:9292/v2/images, The request returned a 413 Request Entity Too Large. This generally means that rate limiting or a quota threshold was breached.: The response body:: 413 Request Entity Too Large: Project 0636a6ee7b2242e7ba9faf5e7d1daed4 is over a limit for [Resource image_count_total is over limit of 0 due to current usage 0 and delta 1]

하지만 이후 image_count_total 관련 Quota 오류가 발생하여

$ openstack quota show admin
+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field                 | Value                                                                                   
                                                                                          |
+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| backup-gigabytes      | 1000                                                                                    
                                                                                          |
| backups               | 10                                                                                      
                                                                                          |
| cores                 | 20                                                                                      
                                                                                          |
| fixed-ips             | -1                                                                                      
                                                                                          |
| floating-ips          | 50                                                                                      
                                                                                          |
| gigabytes             | 1000                                                                                    
                                                                                          |
| gigabytes___DEFAULT__ | -1                                                                                      
                                                                                          |
| groups                | 10                                                                                      
                                                                                          |
| injected-file-size    | 10240                                                                                   
                                                                                          |
| injected-files        | 5                                                                                       
                                                                                          |
| injected-path-size    | 255                                                                                     
                                                                                          |
| instances             | 10                                                                                      
                                                                                          |
| key-pairs             | 100                                                                                     
                                                                                          |
| location              | Munch({'cloud': '', 'region_name': '', 'zone': None, 'project': Munch({'id': '0636a6ee7b2242e7ba9faf5e7d1daed4', 'name': 'admin', 'domain_id': None, 'domain_name': 'Default'})}) |
| networks              | 100                                                                                     
                                                                                          |
| per-volume-gigabytes  | -1                                                                                      
                                                                                          |
| ports                 | 500                                                                                     
                                                                                          |
| project               | 0636a6ee7b2242e7ba9faf5e7d1daed4                                                        
                                                                                          |
| project_name          | admin                                                                                   
                                                                                          |
| properties            | 128                                                                                     
                                                                                          |
| ram                   | 51200                                                                                   
                                                                                          |
| rbac_policies         | 10                                                                                      
                                                                                          |
| routers               | 10                                                                                      
                                                                                          |
| secgroup-rules        | 100                                                                                     
                                                                                          |
| secgroups             | 10                                                                                      
                                                                                          |
| server-group-members  | 10                                                                                      
                                                                                          |
| server-groups         | 10                                                                                      
                                                                                          |
| snapshots             | 10                                                                                      
                                                                                          |
| snapshots___DEFAULT__ | -1                                                                                      
                                                                                          |
| subnet_pools          | -1                                                                                      
                                                                                          |
| subnets               | 100                                                                                     
                                                                                          |
| volumes               | 10                                                                                      
                                                                                          |
| volumes___DEFAULT__   | -1                                                                                      
                                                                                          |
+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

quota를 확인해주었지만 현재 image_count_total 관련 설정이 존재하지 않음을 확인하였다.

openstack registered limit create \
--service glance --default-limit 10 --region RegionOne image_count_total

이후 image_count_total 에 대한 제한 등록 아래 레퍼런스에 근거하여 시도하였는데

https://docs.redhat.com/ko/documentation/red_hat_openstack_services_on_openshift/18.0/pdf/customizing_persistent_storage/Red_Hat_OpenStack_Services_on_OpenShift-18.0-Customizing_persistent_storage-ko-KR.pdf

Trouble Shooting: You are not authorized to perform the requested action: identity:create_registered_limits

$ openstack registered limit create \
--service glance --default-limit 10 --region RegionOne image_count_total
You are not authorized to perform the requested action: identity:create_registered_limits. (HTTP 403) (Request-ID: req-93452b92-8c23-4cf2-b803-24d185f44c88)

현재 ./admin_rc를 통해 admin user로 해당 명령을 실행했음에도 You are not authorized와 같은 오류가 발생하여

$ cat /etc/keystone/policy.json
{}
$ vim /etc/keystone/policy.yaml
identity:create_registered_limits: "role:admin"

$ systemctl restart httpd

/etc/keystone/policy.json 설정 파일과 policy.yaml 파일이 비어 있는 것을 확인하고 수동으로 create_registered_limits을 추가할 수 있는 권한 정책을 추가하였다.

$ openstack registered limit create \
--service glance --default-limit 10 --region RegionOne image_count_total
+---------------+----------------------------------+
| Field         | Value                            |
+---------------+----------------------------------+
| default_limit | 10                               |
| description   | None                             |
| id            | 243bbd072193480cb092c80e930857a7 |
| region_id     | RegionOne                        |
| resource_name | image_count_total                |
| service_id    | 6048f1fd11cb4dcda5de41d1251a0296 |
+---------------+----------------------------------+

이후 registered limit이 정상적으로 수행되는 것을 확인하고

$ vim /etc/keystone/policy.yaml
identity:create_registered_limits: "role:admin"
identity:get_registered_limits: "role:admin"
identity:update_registered_limits: "role:admin"
identity:delete_registered_limits: "role:admin"

/etc/keystone/policy.yaml에 이미지를 생성하기 위해 필요한 다른 정책들을 추가한 후

openstack registered limit create \
--service glance --default-limit 10000 --region RegionOne image_size_total

업로드할 수 있는 최대 이미지 크기 설정 (10GB, 1000MB)

openstack registered limit create \
--service glance --default-limit 10 --region RegionOne image_count_uploading

동시에 업로드 가능한 이미지 개수 증가

openstack registered limit create \
--service glance --default-limit 10000 --region RegionOne image_stage_total

Staging 영역(임시 저장 공간) 크기 설정 (10GB, 1000MB)

위와 같이 registered limit를 생성해준 후

systemctl restart openstack-glance-api

이미지 데몬을 재시작한 후

openstack image list
+--------------------------------------+-----------------------------+--------+
| ID                                   | Name                        | Status |
+--------------------------------------+-----------------------------+--------+
| 73c6e7ee-3b6c-40dd-bdc5-96058e183faa | Rocky Linux 9 Generic Cloud | active |
+--------------------------------------+-----------------------------+--------+

이미지 생성을 정상적으로 완료할 수 있었다.

Instance Deployment

openstack keypair create mykey > mykey.pem
chmod 600 mykey.pem

우선 인스턴스를 생성하기 위해 openstack keypair create mykey 명령어를 통해 SSH key pair를 생성하였다.

생성된 프라이빗 키는 mykey.pem 파일에 저장되었으며, chmod 600 명령어로 적절한 권한 설정을 진행하였다.

ls -l
...
-rw-------  1 root root 1676 Feb 13 05:38 mykey.pem

이후 해당 키에 대한 권한을 확인해준 후

openstack security group create test_sg --description "Test security group allowing port 8888"

테스트용 Security Group을 생성하였다.

openstack security group rule create --proto tcp --dst-port 22 --remote-ip 0.0.0.0/0 test_sg

openstack security group rule create --proto tcp --dst-port 80 --remote-ip 0.0.0.0/0 test_sg

openstack security group rule create --proto tcp --dst-port 8888 --remote-ip 0.0.0.0/0 test_sg

해당 Security Group
TCP 프로토콜에 대해 포트 22, 80, 8888로의 외부 접근을 허용하고 --remote-ip 0.0.0.0/0 옵션을 통해 모든 IP에서의 접근을 허용하도록 하였다.

openstack server create \
  --flavor t2.micro \
  --image "Rocky Linux 9 Generic Cloud" \
  --nic net-id=$(openstack network list --name private-net -f value -c ID) \
  --security-group test_sg \
  --key-name mykey \
  test-instance

또한 --nic net-id=... 옵션으로 생성한 내부 네트워크(private-net)가 인스턴스의 NIC에 연결될 수 있도록 이를 생성하였다,

Trouble Shooting: keystoneauth1.exceptions.discovery.DiscoveryFailure

Unexpected API Error. Please report this at http://bugs.launchpad.net/nova/ and attach the Nova API log if possible.
<class 'keystoneauth1.exceptions.discovery.DiscoveryFailure'> (HTTP 500) (Request-ID: req-dc5133ac-7750-4263-b426-d8df9cc0577e)

하지만 위와 같은 오류가 발생하여 추가적으로 트러블 슈팅을 진행하였다.

$ journalctl -u openstack-nova-api --no-pager | tail -n 50
...
Feb 13 05:42:11 controller nova-api[1761]: 2025-02-13 05:42:11.751 1761 ERROR nova.api.openstack.wsgi keystoneauth1.exceptions.discovery.DiscoveryFailure: Could not find versioned identity endpoints when attempting to authenticate. Please check that your auth_url is correct. Unable to establish connection to https://controller/identity: HTTPSConnectionPool(host='controller', port=443): Max retries exceeded with url: /identity (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7ff1e73edaf0>: Failed to establish a new connection: [Errno 111] ECONNREFUSED'))
Feb 13 05:42:11 controller nova-api[1761]: 2025-02-13 05:42:11.751 1761 ERROR nova.api.openstack.wsgi
Feb 13 05:42:11 controller nova-api[1761]: 2025-02-13 05:42:11.757 1761 INFO nova.api.openstack.wsgi [None req-ffd94076-91ed-463b-b65f-ec39b5d1ab80 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] HTTP exception thrown: Unexpected API Error. Please report this at http://bugs.launchpad.net/nova/ and attach the Nova API log if possible.
Feb 13 05:42:11 controller nova-api[1761]: <class 'keystoneauth1.exceptions.discovery.DiscoveryFailure'>

로그에서 Nova APIKeystone에 접근할 수 없는 즉, auth_urlhttps://controller/identity로 설정되어 있는데, 해당 엔드포인트에 접근할 수 없다는 오류가 발생하였다.

### CONTROLLER
### COMPUTE
$ vim /etc/nova/nova.conf
...
[service_user]
# auth_url = https://controller/identity
auth_url = http://controller:5000/identity

따라서 Controller와 Compute Node의 /etc/nova/nova.confauth_url를 위와 같이 변경해주었고

openstack role add --user nova --project admin admin
openstack role add --user neutron --project admin admin

권한과 관련된 오류를 방지하기 위해 nova, neutron 사용자의 권한을 admin으로 변경하고

systemctl restart openstack-nova-api.service

데몬을 재시작해주었다.

Trouble Shooting: nova.exception.NeutronAdminCredentialConfigurationInvalid

Feb 13 06:29:09 controller nova-api[3353]: 2025-02-13 06:29:09.027 3353 ERROR nova.network.neutron [None req-3dc37ce7-d4cf-4fa2-8d06-c4d476675eca 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] Neutron client was not able to generate a valid admin token, please verify Neutron admin credential located in nova.conf: neutronclient.common.exceptions.Unauthorized: 401-{'error': {'code': 401, 'title': 'Unauthorized', 'message': 'The request you have made requires authentication.'}}
Feb 13 06:29:09 controller nova-api[3353]: 2025-02-13 06:29:09.028 3353 ERROR nova.api.openstack.wsgi [None req-3dc37ce7-d4cf-4fa2-8d06-c4d476675eca 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] Unexpected exception in API method: nova.exception.NeutronAdminCredentialConfigurationInvalid: Networking client is experiencing an unauthorized exception.

하지만 여전히 nova-api에서 Neutron API에 접근할 때 Neutron client was not able to generate a valid admin token 오류가 발생하였다.

이는 nova.conf의 [neutron] 섹션에서 잘못된 인증 정보가 설정되어있을 확률이 높아 발생한 문제라 예상하였다.

$ openstack role assignment list --user neutron --project admin --names
+-------+-----------------+-------+---------------+--------+--------+-----------+
| Role  | User            | Group | Project       | Domain | System | Inherited |
+-------+-----------------+-------+---------------+--------+--------+-----------+
| admin | neutron@Default |       | admin@Default |        |        | False     |
+-------+-----------------+-------+---------------+--------+--------+-----------+

따라서 우선 neutron user의 권한과 존재 유무를 확인하고

openstack user set --password neutron neutron

neutron user의 비밀번호를 재설정해준 이후

$ TOKEN=$(openstack token issue -c id -f value)
$ curl -i -H "X-Auth-Token: $TOKEN" http://controller:8774/v2.1/os-networks
HTTP/1.1 500 Internal Server Error
Openstack-Api-Version: compute 2.1
X-Openstack-Nova-Api-Version: 2.1
Vary: OpenStack-API-Version
Vary: X-OpenStack-Nova-API-Version
Content-Type: application/json; charset=UTF-8
Content-Length: 231
X-Openstack-Request-Id: req-92268784-e75e-4251-b138-fa96e43ec0af
X-Compute-Request-Id: req-92268784-e75e-4251-b138-fa96e43ec0af
Date: Thu, 13 Feb 2025 11:37:42 GMT

{"computeFault": {"code": 500, "message": "Unexpected API Error. Please report this at http://bugs.launchpad.net/nova/ and attach the Nova API log if possible.\n<class 'nova.exception.NeutronAdminCredentialConfigurationInvalid'>"}}

직접적으로 Curl을 통해 요청해보았다.

하지만 동일한 오류가 발생하여 nova.conf[neutron] 섹션을 다시 점검하였다.

$ vim /etc/nova/nova.conf
[neutron]
# auth_url = http://controller:5000
auth_url = http://controller:5000/v3
endpoint_override = http://controller:9696

이후 auth_url/v3를 추가해주었으며 publicURL을 사용하고 있다면 Nova가 잘못된 URL로 호출하는 것일 수 있기 때문에 internalURL을 강제 지정하였다.

systemctl restart openstack-nova-api
systemctl restart neutron-server

이후 데몬을 재시작하였음에도

Feb 13 08:47:43 controller nova-api[13471]: 2025-02-13 08:47:43.814 13471 ERROR nova.network.neutron [None req-2158797e-50db-4d47-9b3a-78757468df2d 12529d0a6bfa4cb1894172f0fe354de9 0636a6ee7b2242e7ba9faf5e7d1daed4 - - default default] Neutron client was not able to generate a valid admin token, please verify Neutron admin credential located in nova.conf: neutronclient.common.exceptions.Unauthorized: 401-{'error': {'code': 401, 'title': 'Unauthorized', 'message': 'The request you have made requires authentication.'}}

동일한 오류가 발생하여

$ vim /etc/neutron/neutron.conf
[keystone_authtoken]
www_authenticate_uri = http://controller:5000
# auth_url = http://controller:5000
auth_url = http://controller:5000/v3
...
service_token_roles = admin
service_token_roles_required = True

/etc/neutron/neutron.confauth_url를 위와 같이 수정하고 service_token_rolesservice_token_roles_required를 추가하여 문제를 해결할 수 있었다.

Trouble Shooting: neutron-metadata-agent: ACCESS_REFUSED - Login was refused using authentication mechanism AMQPLAIN

$ tail -n 50 /var/log/neutron/metadata-agent.log
...
2025-02-13 08:28:18.229 10728 ERROR neutron.agent.metadata.agent     raise error_for_code(reply_code, reply_text, 
2025-02-13 08:28:18.229 10728 ERROR neutron.agent.metadata.agent amqp.exceptions.AccessRefused: (0, 0): (403) ACCESS_REFUSED - Login was refused using authentication mechanism AMQPLAIN. For details see the broker logfile.       
2025-02-13 08:28:18.229 10728 ERROR neutron.agent.metadata.agent

하지만 neutron-metadata-agent의 로그를 확인해본 결과 위와 같은 오류가 발생하였다.

rabbitmqctl list_users
Listing users ...
user    tags
guest   [administrator]

이에 rabbitmqctl를 통해 RabbitMQ User를 검사해보았는데, 이전에 생성한 openstack 사용자가 삭제되었음을 발견하고

rabbitmqctl add_user openstack openstack
rabbitmqctl set_permissions -p / openstack ".*" ".*" ".*"

새롭게 user를 생성하고 권한을 부여한 후

rabbitmqctl change_password openstack <설치 시 설정한 RabbitMQ 비밀번호>

해당 user의 비밀번호를 다시 설정해주었다.

systemctl restart rabbitmq-server
systemctl restart neutron-metadata-agent

이후 각 데몬들을 재시작하여

rabbitmqctl list_connections
Listing connections ...
user    peer_host       peer_port       state
openstack       192.168.100.101 36842   running
openstack       192.168.100.102 57656   running
openstack       192.168.100.101 36848   running
openstack       192.168.100.102 57672   running
openstack       192.168.100.101 36858   running
openstack       192.168.100.102 57674   running
...

RabbitMQ로 각 Openstack 서비스들의 요청이 정상적으로 들어오는 것을 확인하고 문제를 해결할 수 있었다.

Trouble Shooting: nova-compute FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3.9/site-packages/instances'

$  openstack hypervisor list

이후 인스턴스를 위한 하이퍼바이저를 확인하는 해당 명령어를 실행했을 때, Compute Node가 등록되어 있어야 정상 작동하는데 아무것도 출력되지 않아 Compute NodeNova에 정상적으로 등록되지 않았음을 확인하였다.

### COMPUTE
$ journalctl -u openstack-nova-compute --no-pager | grep ERROR
...
FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3.9/site-packages/instances'

nova.exception_Remote.ComputeHostNotFound_Remote: Compute host compute could not be found.

따라서 Compute Nodeopenstack-nova-compute 로그를 살펴본 결과 Nova가 인스턴스를 저장해야 할 디렉터리를 찾지 못하고 있음을 확인하였다.

기본적으로 Nova/var/lib/nova/instances 경로를 사용해야 하지만, 잘못된 경로를 참조하고 있음을 확인하고

$ grep instances_path /etc/nova/nova.conf
#instances_path=$state_path/instances
#instances_path_share =
#snapshots_directory=$instances_path/snapshots
# :oslo.config:option:`DEFAULT.instances_path` is on different disk partition

/etc/nova/nova.confinstances_path가 주석 처리된 것을 확인하고

mkdir -p /var/lib/nova/instances
chown -R nova:nova /var/lib/nova/instances
chmod 755 /var/lib/nova/instances

인스턴스를 위한 디렉터리를 생성한 후 적절한 권한을 설정한 뒤

$ vim /etc/nova/nova.conf
instances_path=/var/lib/nova/instances

/etc/nova/nova.confinstances_path를 위와 같이 업데이트 해준 후

systemctl restart openstack-nova-compute

데몬을 재시작하였다.

### CONTROLLER
$ nova-manage cell_v2 discover_hosts --verbose
...
Found 2 cell mappings.
Skipping cell0 since it does not contain hosts.
Getting computes from cell 'cell1': 4c904549-f28f-4d89-b47d-b1de8135152a
Checking host mapping for compute host 'compute': bef94020-5be0-422b-9af6-a14464b0a209
Creating host mapping for compute host 'compute': bef94020-5be0-422b-9af6-a14464b0a209
Found 1 unmapped computes in cell: 4c904549-f28f-4d89-b47d-b1de8135152a

이후 Compute Node 등록을 강제로 실행한 뒤

$ openstack hypervisor list
+----+---------------------+-----------------+-----------------+-------+
| ID | Hypervisor Hostname | Hypervisor Type | Host IP         | State |
+----+---------------------+-----------------+-----------------+-------+
|  1 | compute             | QEMU            | 192.168.100.102 | up    |
+----+---------------------+-----------------+-----------------+-------+

성공적으로 Hypervisor 목록을 확인할 수 있었고

$ openstack server create \
  --flavor t2.small \
  --image "Rocky Linux 9 Generic Cloud" \
  --nic net-id=$(openstack network list --name private-net -f value -c ID) \
  --security-group test_sg \
  --key-name mykey \
  test-instance
+-------------------------------------+--------------------------------------------------------------------+
| Field                               | Value                                                              |      
+-------------------------------------+--------------------------------------------------------------------+      
| OS-DCF:diskConfig                   | MANUAL                                                             |      
| OS-EXT-AZ:availability_zone         |                                                                    |      
| OS-EXT-SRV-ATTR:host                | None                                                               |      
| OS-EXT-SRV-ATTR:hypervisor_hostname | None                                                               |      
| OS-EXT-SRV-ATTR:instance_name       |                                                                    |      
| OS-EXT-STS:power_state              | NOSTATE                                                            |      
| OS-EXT-STS:task_state               | scheduling                                                         |      
| OS-EXT-STS:vm_state                 | building                                                           |      
| OS-SRV-USG:launched_at              | None                                                               |      
| OS-SRV-USG:terminated_at            | None                                                               |      
| accessIPv4                          |                                                                    |      
| accessIPv6                          |                                                                    |      
| addresses                           |                                                                    |      
| adminPass                           | 9iyMDjLi6XGD                                                       |      
| config_drive                        |                                                                    |
| created                             | 2025-02-13T14:11:08Z                                               |      
| flavor                              | t2.small (6d40e04f-2897-4966-9b27-b5866d6a9d2c)                    |      
| hostId                              |                                                                    |      
| id                                  | 3fa3d02e-28fa-4067-bd5c-e58e4276a361                               |      
| image                               | Rocky Linux 9 Generic Cloud (73c6e7ee-3b6c-40dd-bdc5-96058e183faa) |      
| key_name                            | mykey                                                              |      
| name                                | test-instance                                                      |      
| progress                            | 0                                                                  |      
| project_id                          | 0636a6ee7b2242e7ba9faf5e7d1daed4                                   |      
| properties                          |                                                                    |      
| security_groups                     | name='2510e7cb-95b2-4a26-acdf-e72a2d793a69'                        |      
| status                              | BUILD                                                              |      
| updated                             | 2025-02-13T14:11:08Z                                               |      
| user_id                             | 12529d0a6bfa4cb1894172f0fe354de9                                   |
| volumes_attached                    |                                                                    |      
+-------------------------------------+--------------------------------------------------------------------+ 

인스턴스가 정상적으로 생성되는 것을 확인할 수 있었다.

Trouble Shooting: nova-conductor: Exhausted all hosts available for retrying build failures for instance

$ openstack server list
+--------------------------------------+---------------+--------+----------+-----------------------------+----------+
| ID                                   | Name          | Status | Networks | Image                       | Flavor   |
+--------------------------------------+---------------+--------+----------+-----------------------------+----------+
| 3fa3d02e-28fa-4067-bd5c-e58e4276a361 | test-instance | ERROR  |          | Rocky Linux 9 Generic Cloud | t2.small |
+--------------------------------------+---------------+--------+----------+-----------------------------+----------+

하지만 인스턴스 빌드 도중 오류가 발생하여

{'code': 500, 'created': '2025-02-13T14:11:10Z', 'message': 'Exceeded maximum number of retries. Exhausted all hosts available for retrying build failures for instance 3fa3d02e-28fa-4067-bd5c-e58e4276a361.', 'details': 'Traceback (most recent call last):\n  File "/usr/lib/python3.9/site-packages/nova/conductor/manager.py", line 703, in build_instances\n    raise exception.MaxRetriesExceeded(reason=msg)\nnova.exception.MaxRetriesExceeded: Exceeded maximum number of retries. Exhausted all hosts available for retrying build failures for instance 3fa3d02e-28fa-4067-bd5c-e58e4276a361.\n'}

위와 같이 nova-conductorExhausted all hosts available for retrying build failures for instance 오류임을 확인할 수 있었다.

openstack hypervisor stats show
+----------------------+-------+
| Field                | Value |
+----------------------+-------+
| count                | 1     |
| current_workload     | 0     |
| disk_available_least | 57    |
| free_disk_gb         | 61    |
| free_ram_mb          | 7171  |
| local_gb             | 61    |
| local_gb_used        | 0     |
| memory_mb            | 7683  |
| memory_mb_used       | 512   |
| running_vms          | 0     |
| vcpus                | 6     |
| vcpus_used           | 0     |
+----------------------+-------+

우선 하이퍼바이저의 문제를 점검하기 위해 하이퍼바이저의 status를 확인해준 뒤

$ journalctl -u openstack-nova-scheduler.service --no-pager -n 50
...
Feb 13 23:37:27 controller nova-scheduler[1862]: 2025-02-13 23:37:27.287 1862 ERROR nova keystoneauth1.exceptions.discovery.DiscoveryFailure: Could not find versioned identity endpoints when attempting to authenticate. Please check that your auth_url is correct.

nova-scheduler에서 위와 같이 Keystone 인증 문제가 발생하는 것을 볼 수 있었다.

$ vim /etc/nova/nova.conf
[keystone_authtoken]
www_authenticate_uri = http://controller:5000
auth_url = http://controller:5000/v3
memcached_servers = controller:11211
auth_type = password
project_domain_name = Default
user_domain_name = Default
project_name = admin
username = nova
password = nova
service_token_roles = admin
service_token_roles_required = True

따라서 위와 같이 auth_url을 수정하고 service_token_rolesservice_token_roles_required를 추가한 뒤

sudo systemctl restart openstack-nova-api
sudo systemctl restart openstack-nova-scheduler

관련 서비스 데몬들을 재시작하여 문제를 해결할 수 있었다

Trouble Shooting: nova-conductor: nova.exception.PortBindingFailed

https://www.facebook.com/groups/openstack.kr/posts/2814338211913645/

$ journalctl -u openstack-nova-conductor.service --no-pager -f
...
nova.exception.PortBindingFailed: Binding failed for port 21d585de-0654-4359-a1af-61d283e8b138, please check neutron logs for more information.

하지만 NeutronNova에 네트워크 포트를 바인딩하지 못하는 오류가 발생하였다.

새롭게 생성한 해당 인스턴스는 네트워크 인터페이스를 설정할 수 없으며, nova-scheduler가 포트 바인딩을 다시 시도하지만 결국 실패(MaxRetriesExceeded)한 것을 확인할 수 있었다.

$ openstack port list --network private-net
+--------------------------------------+------+-------------------+--------------------------------------------------------------------------+--------+
| ID                                   | Name | MAC Address       | Fixed IP Addresses                            
                           | Status |
+--------------------------------------+------+-------------------+--------------------------------------------------------------------------+--------+
| a182dc24-7ae3-4e0e-a647-0ce9f804dfd7 |      | fa:16:3e:3a:cf:69 | ip_address='10.0.0.10', subnet_id='fb81c0eb-dca9-4ef5-9092-f241945c0199' | ACTIVE |
| b69bdbee-ee61-40e7-87bb-33b3cfc5af90 |      | fa:16:3e:a7:0a:af | ip_address='10.0.0.1', subnet_id='fb81c0eb-dca9-4ef5-9092-f241945c0199'  | ACTIVE |
+--------------------------------------+------+-------------------+--------------------------------------------------------------------------+--------+

우선 Neutron의 포트 상태를 확인해준 뒤

$ openstack port show a8d61a9e-cad9-4c7c-a939-30207be461f1
No Port found for a8d61a9e-cad9-4c7c-a939-30207be461f1

Neutron에서 Compute Node에 포트를 바인딩할 수 없어서 자동으로 포트를 삭제하여 로그의 포트가 존재하지 않음을 확인하고

$ virsh net-dumpxml opstnat
<network>
  <name>opstnat</name>
  <uuid>bf2cbc9d-43a4-4249-b430-a02b5ba3acc1</uuid>
  <forward mode="bridge"/>
  <bridge name="br-opstnat"/>
</network>
### HOST
<network>
  <name>opstnat</name>
  <uuid>bf2cbc9d-43a4-4249-b430-a02b5ba3acc1</uuid>
  <forward mode="nat">
    <nat>
      <port start="1024" end="65535"/>
    </nat>
  </forward>
  <bridge name="virbr10" stp="off" delay="0"/>
  <mac address="52:54:00:8c:9b:3c"/>
  <ip address="192.168.100.1" netmask="255.255.255.0">
    <dhcp>
      <range start="192.168.100.100" end="192.168.100.200"/>
      <host mac="36:4c:6a:07:2e:4d" name="controller" ip="192.168.100.101"/>
      <host mac="6a:66:48:b9:1f:43" name="compute" ip="192.168.100.102"/>
    </dhcp>
  </ip>
  <portgroup name="vxlan">
    <forward mode="nat"/>
    <protocol family="ipv4">
      <port start="4789" end="4789"/>
    </protocol>
  </portgroup>
</network>

private-netvxlan 타입의 네트워크이기 때문에 기존 opstnat는 NAT 모드(forward mode='nat')에서 동작하며, NAT 방식에서는 일반적으로 VXLAN 트래픽(UDP 4789)이 차단기에 4789 포트를 KVM에서 포트포워딩할 수 있도록 설정하였다.

### CONTROLLER
### COMPUTE
$ vim /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
type_drivers = flat,vlan,vxlan
# tenant_network_types = vxlan
tenant_network_types = flat,vxlan
# mechanism_drivers = openvswitch,l2population
mechanism_drivers = openvswitch
# extension_drivers = port_security

이후 /etc/neutron/plugins/ml2/ml2_conf.ini에서 위와 같이 설정을 변경해주었는데

  • l2population은 여러 Compute Node 간 VXLAN 트래픽을 최적화하는 기능이라, 단일 노드에서는 불필요할 수도 있다
  • VXLAN 네트워크tenant_network로 설정되었던 것을 flat을 지원하는 ext-net을 사용하고 있으므로, flat도 추가하였다
  • extension_drivers : 포트 보안 (Security Groups) 관련 설정으로 일반적으로 문제는 없지만, Neutron 포트 바인딩이 실패할 경우 보안 그룹 문제일 가능성 있어서 테스트를 위해 포트 보안을 임시로 비활성화하였다.
systemctl restart neutron-server neutron-openvswitch-agent

이후 관련 데몬들을 재시작하였지만

$ cat /var/log/neutron/openvswitch-agent.log | grep ERROR
2025-02-14 05:21:35.511 2732 ERROR neutron.agent.common.async_process [-] Error received from [ovsdb-client monitor tcp:127.0.0.1:6640 Interface name,ofport,external_ids --format=json]: None
2025-02-14 05:22:43.336 6066 ERROR neutron_lib.rpc [None req-e9439895-d0c9-4f4d-9d6b-a816cf9caca8 - - - - - -] Timeout in RPC method has_alive_neutron_server. Waiting for 9 seconds before next attempt. If the server is not down, consider increasing the rpc_response_timeout option as Neutron server(s) may be overloaded and unable to respond quickly enough.: oslo_messaging.exceptions.MessagingTimeout: Timed out waiting for a reply to message ID 9e20aa54bd494bee9b3b904fdb6349ec

/var/log/neutron/openvswitch-agent.log 로그에서 OVSDB (Open vSwitch Database)와 통신하는 ovsdb-client가 데이터를 가져오지 못하는 문제를 파악하였다.

해당 오류의 원인일 수 있는 요소들을 아래와 같다

  • Neutron 서버가 과부하 상태이거나 중단됨
  • RabbitMQ 메시지 큐가 중단됨
  • Nova와 Neutron 간의 RPC 설정 문제

따라서 위 요소들을 모두 점검하였지만 여전히 문제가 발생하여

$ cat /var/log/neutron/server.log | grep ERROR
2025-02-14 05:23:08.345 6093 ERROR neutron.plugins.ml2.managers [None req-b30b9088-5637-4ed0-835d-455db08e36f4 0114d4f75c8d44bd8a38ec2b77377d9d 0636a6ee7b2242e7ba9faf5e7d1daed4 - - - -] Failed to bind port d5d48599-1e43-43fa-9a59-7bc2679503b4 on host compute for vnic_type normal using segments [{'id': 'f4990c6a-89db-4523-8820-764717960458', 'network_type': 'vxlan', 'physical_network': None, 'segmentation_id': 336, 'network_id': '3d2a325a-b057-45ec-ade0-97849ce0a1f0'}]
2025-02-14 05:23:08.371 6093 ERROR neutron.plugins.ml2.managers [None req-b30b9088-5637-4ed0-835d-455db08e36f4 0114d4f75c8d44bd8a38ec2b77377d9d 0636a6ee7b2242e7ba9faf5e7d1daed4 - - - -] Failed to bind port d5d48599-1e43-43fa-9a59-7bc2679503b4 on host compute for vnic_type normal using segments [{'id': 'f4990c6a-89db-4523-8820-764717960458', 'network_type': 'vxlan', 'physical_network': None, 'segmentation_id': 336, 'network_id': '3d2a325a-b057-45ec-ade0-97849ce0a1f0'}]
2025-02-14 05:23:08.402 6093 ERROR neutron.plugins.ml2.managers [None req-b30b9088-5637-4ed0-835d-455db08e36f4 0114d4f75c8d44bd8a38ec2b77377d9d 0636a6ee7b2242e7ba9faf5e7d1daed4 - - - -] Failed to bind port d5d48599-1e43-43fa-9a59-7bc2679503b4 on host compute for vnic_type normal using segments [{'id': 'f4990c6a-89db-4523-8820-764717960458', 'network_type': 'vxlan', 'physical_network': None, 'segmentation_id': 336, 'network_id': '3d2a325a-b057-45ec-ade0-97849ce0a1f0'}]
2025-02-14 05:23:08.428 6093 ERROR neutron.plugins.ml2.managers [None req-b30b9088-5637-4ed0-835d-455db08e36f4 0114d4f75c8d44bd8a38ec2b77377d9d 0636a6ee7b2242e7ba9faf5e7d1daed4 - - - -] Failed to bind port d5d48599-1e43-43fa-9a59-7bc2679503b4 on host compute for vnic_type normal using segments [{'id': 'f4990c6a-89db-4523-8820-764717960458', 'network_type': 'vxlan', 'physical_network': None, 'segmentation_id': 336, 'network_id': '3d2a325a-b057-45ec-ade0-97849ce0a1f0'}]

/var/log/neutron/server.log 에서 'network_type': 'vxlan', 'physical_network': None...설정을 확인하였다.

단일 Compute Node 환경에서는 모든 인스턴스들이 br-int를 통해 통신하기 때문에 VXLAN 터널링이 필요하지 않아 현재 VXLAN 때문에 바인딩 실패가 발생하는 것을 확인하고, 터널링을 아예 제거하고 flat + vlan로 구성하여 문제를 해결하고자 하였다.

### CONTROLLER
$ sudo vi /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
type_drivers = flat,vlan,vxlan
tenant_network_types = flat,vlan,vxlan
mechanism_drivers = openvswitch

[ml2_type_vlan]
network_vlan_ranges = provider:100:200

따라서 /etc/neutron/plugins/ml2/ml2_conf.ini를 위와 같이 변경하고

systemctl restart neutron-openvswitch-agent.service

neutron-openvswitch-agent 데몬을 재시작한 뒤 기존의 네트워크를 삭제해주었다.

openstack network create \
  --provider-network-type vlan \
  --provider-physical-network provider \
  --provider-segment 100 \
  private-net

이후 위와 같이 새롭게 private-net를 생성해주었다.

  • provider-network-type vlan: VLAN 사용
  • provider-segment 100: VLAN ID 100 사용 (다른 VLAN과 충돌하지 않도록 설정)

이때 VLAN을 사용하면 같은 provider 네트워크를 공유하면서도 private-net을 별도로 생성 가능하다.

openstack subnet create \
  --network private-net \
  --subnet-range 10.0.0.0/24 \
  --gateway 10.0.0.1 \
  --dns-nameserver 8.8.8.8 \
  private-subnet

이후 VLAN 서브넷(private-subnet)을 생성하고

openstack router create my-router
openstack router set my-router --external-gateway ext-net
openstack router add subnet my-router private-subnet

라우터를 생성한 뒤 ext-netprivate-subnet를 연결해주면

최종적인 네트워크 토폴로지를 위와 같이 구성할 수 있으며

openstack server create \
  --flavor t2.micro \
  --image "Rocky Linux 9 Generic Cloud" \
  --nic net-id=$(openstack network list --name private-net -f value -c ID) \
  --security-group test_sg \
  --key-name mykey \
  test-instance
+-------------------------------------+--------------------------------------------------------------------+
| Field                               | Value                                                              |      
+-------------------------------------+--------------------------------------------------------------------+      
| OS-DCF:diskConfig                   | MANUAL                                                             |      
| OS-EXT-AZ:availability_zone         |                                                                    |      
| OS-EXT-SRV-ATTR:host                | None                                                               |      
| OS-EXT-SRV-ATTR:hypervisor_hostname | None                                                               |      
| OS-EXT-SRV-ATTR:instance_name       |                                                                    |      
| OS-EXT-STS:power_state              | NOSTATE                                                            |      
| OS-EXT-STS:task_state               | scheduling                                                         |      
| OS-EXT-STS:vm_state                 | building                                                           |      
| OS-SRV-USG:launched_at              | None                                                               |      
| OS-SRV-USG:terminated_at            | None                                                               |      
| accessIPv4                          |                                                                    |      
| accessIPv6                          |                                                                    |      
| addresses                           |                                                                    |
| adminPass                           | x9qWSLG2Sg3E                                                       |      
| config_drive                        |                                                                    |      
| created                             | 2025-02-14T10:52:11Z                                               |      
| flavor                              | t2.micro (f72a3898-3018-4c8c-8bb1-90b92f788115)                    |      
| hostId                              |                                                                    |      
| id                                  | a0b2abe3-8d4b-4c4c-aed3-1dbbb4c28181                               |      
| image                               | Rocky Linux 9 Generic Cloud (73c6e7ee-3b6c-40dd-bdc5-96058e183faa) |      
| key_name                            | mykey                                                              |      
| name                                | test-instance                                                      |      
| progress                            | 0                                                                  |      
| project_id                          | 0636a6ee7b2242e7ba9faf5e7d1daed4                                   |
| properties                          |                                                                    |      
| security_groups                     | name='2510e7cb-95b2-4a26-acdf-e72a2d793a69'                        |      
| status                              | BUILD                                                              |      
| updated                             | 2025-02-14T10:52:11Z                                               |      
| user_id                             | 12529d0a6bfa4cb1894172f0fe354de9                                   |      
| volumes_attached                    |                                                                    |      
+-------------------------------------+--------------------------------------------------------------------+ 

인스턴스가 정상적으로 생성된 뒤

위와 같이 정상적으로 private-net에 의해 고정 IP가 할당된 것을 확인할 수 있으며

openstack server list
+--------------------------------------+---------------+--------+------------------------+-----------------------------+----------+
| ID                                   | Name          | Status | Networks               | Image                  
     | Flavor   |
+--------------------------------------+---------------+--------+------------------------+-----------------------------+----------+
| a0b2abe3-8d4b-4c4c-aed3-1dbbb4c28181 | test-instance | ACTIVE | private-net=10.0.0.145 | Rocky Linux 9 Generic Cloud | t2.micro |
+--------------------------------------+---------------+--------+------------------------+-----------------------------+----------+

해당 인스턴스가 정상적으로 구동되고

[root@compute ~]# ovs-vsctl show
85ccefa0-dd2d-4696-8ffe-bed0ece90fa1
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Bridge br-ex
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-ex
            Interface br-ex
                type: internal
        Port phy-br-ex
            Interface phy-br-ex
                type: patch
                options: {peer=int-br-ex}
        Port enp1s0
            Interface enp1s0
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port tapbaa2f08c-ad
            tag: 1
            Interface tapbaa2f08c-ad
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port br-int
            Interface br-int
                type: internal
    ovs_version: "3.1.3"

Compute Node에서 tapbaa2f08c-ad 인터페이스가 br-int의 포트로 추가된 모습을 확인할 수 있었다.

Floating IP Configuration

openstack subnet create \
  --network ext-net \
  --subnet-range 192.168.100.0/24 \
  --allocation-pool start=192.168.100.10,end=192.168.100.90 \
  --gateway 192.168.100.1 \
  --no-dhcp \
  ext-subnet

또한 이후 Floating IP 생성을 위해 ext-net의 서브넷을 생성해준 뒤

$ openstack floating ip create ext-net
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| created_at          | 2025-02-14T10:57:30Z                 |
| description         |                                      |
| dns_domain          | None                                 |
| dns_name            | None                                 |
| fixed_ip_address    | None                                 |
| floating_ip_address | 192.168.100.29                       |
| floating_network_id | 4f9841b9-dd23-4050-8bdd-09d79f8df5f9 |
| id                  | 6bca66bd-0d4a-41d8-9199-7b5174dc7f76 |
| name                | 192.168.100.29                       |
| port_details        | None                                 |
| port_id             | None                                 |
| project_id          | 0636a6ee7b2242e7ba9faf5e7d1daed4     |
| qos_policy_id       | None                                 |
| revision_number     | 0                                    |
| router_id           | None                                 |
| status              | DOWN                                 |
| subnet_id           | None                                 |
| tags                | []                                   |
| updated_at          | 2025-02-14T10:57:30Z                 |
+---------------------+--------------------------------------+
openstack server add floating ip test-instance 192.168.100.29 

Floatinf IP를 생성하고 이를 test-instance에 할당해주어

$ ssh -i mykey.pem rocky@192.168.100.29
The authenticity of host '192.168.100.29 (192.168.100.29)' can't be established.
ED25519 key fingerprint is SHA256:dkWUo1kqCa1RpKEkquJ+wFpS3w2dZHtskeEafaNe++c.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.100.29' (ED25519) to the list of known hosts.
[rocky@test-instance ~]$ 

ssh를 통해 해당 인스턴스에 접속할 수 있었으며

### test-instance
[rocky@test-instance ~]$ uname -r
5.14.0-503.14.1.el9_5.x86_64

[rocky@test-instance ~]$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=37.7 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=56 time=35.8 ms

[rocky@test-instance ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether fa:16:3e:d8:4a:aa brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    altname ens3
    inet 10.0.0.145/24 brd 10.0.0.255 scope global dynamic noprefixroute eth0
       valid_lft 85975sec preferred_lft 85975sec
    inet6 fe80::f816:3eff:fed8:4aaa/64 scope link
       valid_lft forever preferred_lft forever

네트워크 설정 또한 정상적으로 구성된 것을 확인할 수 있었다.

최종적인 네트워크 토폴로지는 위와 같으며 이로써 Compute Node까지 구성이 완료되었다.

이후에는 현재 물리적으로 스위치를 통해 연결된 다른 kubernetes-hostK8s를 설정하고 테스트해본 뒤 최종적으로 현재 구축한 Openstack Private CloudOrchestration 서비스로서 동작하게 할 계획이다.

0개의 댓글