diff --git a/lectures/ch2/ch2_4_10_lec.qmd b/lectures/ch2/ch2_4_10_lec.qmd new file mode 100644 index 0000000..4e6a64b --- /dev/null +++ b/lectures/ch2/ch2_4_10_lec.qmd @@ -0,0 +1,69 @@ +--- +title: "2-4장. SQL Database (MariaDB/MySQL)" +--- + +# 5. **SQL Database (MariaDB/MySQL)** + +### **공식 문서** + +- 환경 > SQL DB (Ubuntu): + + https://docs.openstack.org/ko_KR/install-guide/environment-sql-database-ubuntu.html + + +[개념 정리] + +- 거의 모든 OpenStack 서비스 상태가 **공통 DB(보통 controller)** 에 쌓임. +- 여기서는 **MariaDB**를 controller에 설치하고, + + **bind-address를 controller 관리 IP(10.100.100.11)** 로 맞춰줌. + + +### **5-1. controller 노드에서만** + +### **① 패키지 설치** + +```yaml +apt install -y mariadb-server python3-pymysql +``` + +### **② 설정 파일 생성** + +문서 기준 경로: /etc/mysql/mariadb.conf.d/99-openstack.cnf + +```yaml +cat >/etc/mysql/mariadb.conf.d/99-openstack.cnf << 'EOF' +[mysqld] +bind-address = 10.100.100.11 + +default-storage-engine = innodb +innodb_file_per_table = on +max_connections = 4096 +collation-server = utf8_general_ci +character-set-server = utf8 +EOF +``` + +여기서 bind-address 를 **문서의 10.0.0.11 대신 10.100.100.11** 로 바꿔준 것만 주의. + +**③ 서비스 재시작 + secure 설치** + +```yaml +service mysql restart + +mysql_secure_installation +# 여기서 DB root 암호 = 아까 Security 단계에서 적어둔 DB_ROOT_PASS + +# 질문에 대한 답변 +Set root password? [Y/n] -> Y +New password: -> DB_ROOT_PASS +Remove anonymous users? [Y/n] -> Y +Disallow root login remotely? [Y/n] -> Y (원격 root 필요 없음) +Remove test database and access to it? [Y/n] -> Y +Reload privilege tables now? [Y/n] -> Y +``` + +> 요 단계가 끝나면, 나중에 각 서비스 설치할 때 +mysql -u root -p 로 DB 만들고, KEYSTONE_DBPASS 같은 걸 써서 계정 만들어 줄 수 있음. +> + diff --git a/lectures/ch2/ch2_4_11_lec.qmd b/lectures/ch2/ch2_4_11_lec.qmd new file mode 100644 index 0000000..036b61f --- /dev/null +++ b/lectures/ch2/ch2_4_11_lec.qmd @@ -0,0 +1,47 @@ +--- +title: "2-4장. Message Queue (RabbitMQ)" +--- + +# 6. **Message Queue (RabbitMQ)** + +### **공식 문서** + +- 환경 > 메시지 큐 > Ubuntu: + + https://docs.openstack.org/ko_KR/install-guide/environment-messaging-ubuntu.html + + +[개념 정리] + +- 서비스들 사이에서 “이 작업 해라 / 결과 이렇다” 같은 걸 + + 중앙에서 중계하는 **버스** 역할. RabbitMQ 사용. + +- 보통 controller에만 띄우고, 각 서비스 transport_url 에 접속 정보 넣는다. + +### **controller에서만** + +```yaml +apt install -y rabbitmq-server +``` + +1. 사용자 **`openstack`** 을 추가한다: + +```yaml +rabbitmqctl add_user openstack RABBIT_PASS +`Creating user "openstack" ... +**RABBIT_PASS**`적절한 비밀번호로 바꾸세요 . +``` + +1. 사용자 **`openstack`** 에게 구성/쓰기/읽기 권한을 부여한다. + +```yaml +rabbitmqctl set_permissions openstack ".*" ".*" ".*" + +Setting permissions for user "openstack" in vhost "/" ... +``` + +> 나중에 keystone/nova/neutron 설정할 때 +transport_url = rabbit://openstack:RABBIT_PASS@controller +이런 식으로 계속 재사용함. +> diff --git a/lectures/ch2/ch2_4_12_lec.qmd b/lectures/ch2/ch2_4_12_lec.qmd new file mode 100644 index 0000000..db4c37a --- /dev/null +++ b/lectures/ch2/ch2_4_12_lec.qmd @@ -0,0 +1,46 @@ +--- +title: "2-4장. Memcached" +--- + +# 7. **Memcached** + +### **공식 문서** + +- Memcached (Ubuntu): https://docs.openstack.org/install-guide/environment-memcached-ubuntu.html + +[개념 정리] + +- Keystone 토큰/세션 같은 걸 메모리에 캐싱해서 성능 올리는 용도. +- controller에만 두고, 다른 노드들이 여기에 붙는다. + +### **controller에서만** + +### **① 패키지 설치** + +```yaml +apt install -y memcached python3-memcache +``` + +### **② bind 주소를 관리망 IP로 변경** + +/etc/memcached.conf 편집: + +![image.png](images/ch2_4_img_01.png) + +```yaml +# 원래는 -l 127.0.0.1 이런 줄이 있음 +# 그 줄을 아래처럼 교체 +-l 10.100.100.11 +``` + +**③ 재시작** + +```yaml +service memcached restart +``` + +> 이렇게 하면 compute 같은 다른 노드에서도 +10.100.100.11:11211 으로 Keystone 캐시를 공유하게 된다. +> + +--- diff --git a/lectures/ch2/ch2_4_13_lec.qmd b/lectures/ch2/ch2_4_13_lec.qmd new file mode 100644 index 0000000..2d3ef0a --- /dev/null +++ b/lectures/ch2/ch2_4_13_lec.qmd @@ -0,0 +1,62 @@ +--- +title: "2-4장. Etcd" +--- + +# **8. Etcd** + +### **공식 문서** + +- Etcd (Ubuntu): https://docs.openstack.org/install-guide/environment-etcd-ubuntu.html + +[개념 정리] + +- Nova/Neutron 등에서 **분산 락 / 서비스 상태 / 설정** 등을 저장하는 + + 경량 key-value 스토어. + +- 역시 controller에 하나만 띄우는 구조. + +### **controller에서만 (Ubuntu 24.04 기준)** + +### **① 패키지 설치** + +``` +apt install -y etcd-server +``` + +(Ubuntu 24.04는 etcd-server 패키지 이름이 맞다고 문서에 명시.) + +**②/etc/default/etcd 수정** + +문서 예제에서 10.0.0.11 되어 있는 부분을 **10.100.100.11** 로 바꿔서 넣어주면 됨: + +초기 파일은 아래와 같은 형태다. 기존 내용을 확인한 뒤, 아래 설정을 추가한다. + +![image.png](images/ch2_4_img_02.png) + +``` +cat >/etc/default/etcd << 'EOF' +ETCD_NAME="controller" +ETCD_DATA_DIR="/var/lib/etcd" +ETCD_INITIAL_CLUSTER_STATE="new" +ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-01" + +ETCD_INITIAL_CLUSTER="controller=http://10.100.100.11:2380" +ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.100.100.11:2380" +ETCD_ADVERTISE_CLIENT_URLS="http://10.100.100.11:2379" +ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380" +ETCD_LISTEN_CLIENT_URLS="http://10.100.100.11:2379" +EOF +``` + +![image.png](images/ch2_4_img_03.png) + +### **③ 서비스 enable + 재시작** + +``` +systemctl enable etcd +systemctl restart etcd +systemctl status etcd #상태확인 +``` + +--- diff --git a/lectures/ch2/ch2_4_14_lec.qmd b/lectures/ch2/ch2_4_14_lec.qmd new file mode 100644 index 0000000..0cae33c --- /dev/null +++ b/lectures/ch2/ch2_4_14_lec.qmd @@ -0,0 +1,20 @@ +--- +title: "2-4장. 여기까지 체크 포인트" +--- + +## **여기까지 체크 포인트** + +이제 환경 챕터 기준으로는: + +- Security – 비밀번호 변수 정의, 랜덤 암호 준비 +- Host networking – controller/compute IP/라우팅/hosts 설정 +- NTP – chrony로 시간 동기화 +- OpenStack packages – cloud-archive:epoxy 활성화 + openstackclient 설치 +- SQL DB – controller에 MariaDB 설치 + bind-address 10.100.100.11 +- Message queue – controller에 RabbitMQ + openstack 유저 생성 +- Memcached – controller에 설치, 10.100.100.11 로 바인딩 +- Etcd – controller에 etcd-server 설정 + +이 상태면 설치 가이드 목차에서 다음으로 + +**“OpenStack 서비스 설치(Install OpenStack services)” → Minimal deployment for 2025.1 (Epoxy)** 단계로 진행한다. diff --git a/lectures/ch2/ch2_4_15_lec.qmd b/lectures/ch2/ch2_4_15_lec.qmd new file mode 100644 index 0000000..46395c4 --- /dev/null +++ b/lectures/ch2/ch2_4_15_lec.qmd @@ -0,0 +1,37 @@ +--- +title: "2-4장. Epoxy 버전 OpenStack 서비스 설치 순서" +--- + +# 9. 이제 본격적으로 에폭시 버전 오픈스택 설치! + +https://docs.openstack.org/install-guide/openstack-services.html + +**OpenStack 서비스 설치 (Epoxy)** + +### **Epoxy 최소 구성 설치 순서 (공식)** + +1. Identity – **Keystone** 설치 +2. Image – **Glance** 설치 +3. **Placement** 설치 +4. Compute – **Nova** 설치 +5. Networking – **Neutron** 설치 + +그 다음 권장 컴포넌트는 Horizon(대시보드), Cinder(블록스토리지)이다. + +이번 답변에서는 **1번 Keystone을 공식 문서 순서대로** 순서대로 진행한다 + +(Keystone까지 완전히 돼야 Glance/Nova/Neutron에서 openstack CLI가 제대로 먹음.) + +--- + +[Keystone 설치](ch2_4_17_lec.qmd) + +[Glance 설치](ch2_4_18_lec.qmd) + +[Placement](ch2_4_19_lec.qmd) + +[Nova](ch2_4_20_lec.qmd) + +[Neutron](ch2_4_21_lec.qmd) + +[Horizon](ch2_4_22_lec.qmd) diff --git a/lectures/ch2/ch2_4_16_lec.qmd b/lectures/ch2/ch2_4_16_lec.qmd new file mode 100644 index 0000000..bbaed7c --- /dev/null +++ b/lectures/ch2/ch2_4_16_lec.qmd @@ -0,0 +1,24 @@ +--- +title: "2-4장. 최종 확인" +--- + +# 10. 최종확인 + +```bash +source /root/admin-openrc.sh + +# Nova / compute 상태 +openstack compute service list +openstack hypervisor list + +# Neutron 에이전트 상태 +openstack network agent list + +# 이미지 / flavor / 네트워크가 있는지 +openstack image list +openstack flavor list +openstack network list +``` + +![image.png](images/ch2_4_img_04.png) + diff --git a/lectures/ch2/ch2_4_17_lec.qmd b/lectures/ch2/ch2_4_17_lec.qmd new file mode 100644 index 0000000..225f96b --- /dev/null +++ b/lectures/ch2/ch2_4_17_lec.qmd @@ -0,0 +1,344 @@ +--- +title: "2-4장. Keystone 설치" +--- + +# Keystone 설치 + +## **0. Keystone 공식 문서 링크 (Ubuntu)** + +- Epoxy 기준 Keystone 설치 (Ubuntu): + + https://docs.openstack.org/keystone/2025.1/install/keystone-install-ubuntu.html + + +이 페이지에 나오는 순서를 기준으로, + +**우리 환경(컨트롤러=controller, DB/MQ/Memcached/Etcd 이미 설치 완료, 10.100.100.11)** 에 맞춰 번역해서 설명할게. + +> 앞으로 나오는 모든 Keystone 관련 명령은 **controller 노드에서 root로** +> + +--- + +## **1. Keystone DB 만들기 (MariaDB 쪽)** + +**공식 문서의 “Prerequisites” 1~4번** 단계. + +**목적** + +Keystone도 자기 상태/유저/토큰 메타데이터를 DB에 저장하니까 + +먼저 keystone 데이터베이스랑 계정을 만들어 줘야 함. + +관련 문서: https://docs.openstack.org/keystone/2025.1/install/get-started-ubuntu.html + +### **1-1. controller에서 MariaDB 접속** + +``` +sudo -i # 아직 root 아니면 +mysql +``` + +### **1-2. keystone DB 생성** + +``` +CREATE DATABASE keystone; +``` + +### **1-3. keystone용 DB 계정/권한 부여** + +KEYSTONE_DBPASS 자리에 **아까 Security 단계에서 적어둔 keystone DB 비밀번호** 넣기. + +``` +MariaDB [(none)]> GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' \ +IDENTIFIED BY 'KEYSTONE_DBPASS'; +MariaDB [(none)]> GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' \ +IDENTIFIED BY 'KEYSTONE_DBPASS'; +``` + +> **`KEYSTONE_DBPASS`**적절한 비밀번호로 바꾸세요 . +여기서 '%' 는 “어떤 호스트에서 접속하든 다 허용”이라는 뜻인데, +> + +> PoC/랩 환경에서는 이대로 사용 가능하며, 운영 서비스에서는 보안 정책을 더 강화해야 한다. +> + +마지막으로: +데이터베이스 액세스 클라이언트를 종료한다. + +``` +FLUSH PRIVILEGES; +EXIT; +``` + +이러면 데이터베이스 탈출! + +--- + +## **2. Keystone 패키지 설치** + +**공식 문서 “Install and configure components” 1번**. + +Ubuntu 24.04 + Epoxy에서: + +``` +# controller, root +apt update +apt install -y keystone +``` + +> Apache2 + mod_wsgi는 keystone 패키지가 의존성으로 같이 깔린다고 문서에 나와 있음. +> + +(예전에 rocky 문서처럼 apache2 libapache2-mod-wsgi-py3를 따로 깔던 시절이 있었는데, + +Epoxy 문서 기준으로는 apt install keystone만으로 처리.) + +--- + +## **3. /etc/keystone/keystone.conf 설정** + +**공식 문서 “Install and configure components” 2번**. + +### **3-1. [database] 섹션** + +``` +vi /etc/keystone/keystone.conf +``` + +[database] 섹션을 찾아서 이렇게 맞춰줘: + +``` +[database] +# ... +connection = mysql+pymysql://keystone:KEYSTONE_DBPASS@controller/keystone +``` + +- KEYSTONE_DBPASS → 아까 만든 keystone DB 비밀번호 +- @controller → /etc/hosts 에서 controller -> 10.100.100.11 로 매핑했으므로 이 이름을 그대로 사용해도 된다. + +**중요:** + +[database] 안에 예전 기본값 connection = sqlite:////... 같은 거 있으면 **주석 처리하거나 삭제**. + +### **3-2. [token] 섹션 (Fernet 토큰)** + +같은 파일에서 [token] 섹션 쪽에: + +``` +[token] +# ... +provider = fernet +``` + +이렇게 넣어줘. (없으면 섹션 새로 만들어도 됨) + +> [개념] +> + +> Keystone이 발급하는 토큰 포맷을 Fernet으로 쓰겠다는 의미. +> + +> 요게 최신 기본이기도 하고, 공식 가이드가 이걸 기준으로 설명함. +> + +저장 후 종료. + +--- + +## **4. Keystone DB 동기화** + +**공식 문서 “Populate the Identity service database”** 부분. + +``` +# controller, root +su -s /bin/sh -c "keystone-manage db_sync" keystone +``` + +- su -s /bin/sh : keystone 시스템 유저 권한으로 +- db_sync : 방금 설정한 MariaDB에 테이블 생성/마이그레이션 수행 + +에러 없이 끝나면 OK. + +--- + +## **5. Fernet/credential 키 저장소 초기화** + +**공식 문서 4번 “Initialize Fernet key repositories”**. + +``` +keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone +keystone-manage credential_setup --keystone-user keystone --keystone-group keystone +``` + +- /etc/keystone/fernet-keys/, /etc/keystone/credential-keys/ 디렉토리에 키가 생성된다. +- 나중에 HA 구성에서는 이 키들을 노드 간에 동기화해야 하지만, 현재는 단일 controller 구성이므로 추가 고려하지 않아도 된다. + +--- + +## **6. Keystone bootstrap (admin 계정/엔드포인트 생성)** + +**공식 문서 5번 “Bootstrap the Identity service”**. + +이 단계가 가장 중요한 핵심 포인트이다. + +여기서 **admin 유저, admin 프로젝트, service 프로젝트, API endpoint(내부/공개)** 를 일괄 생성한다. + +``` +keystone-manage bootstrap --bootstrap-password ADMIN_PASS \ + --bootstrap-admin-url http://controller:5000/v3/ \ + --bootstrap-internal-url http://controller:5000/v3/ \ + --bootstrap-public-url http://controller:5000/v3/ \ + --bootstrap-region-id RegionOne +``` + +- ADMIN_PASS → 이후 openstack CLI에서 admin 로그인 시 사용할 관리자 비밀번호(실습 환경에 맞춰 별도로 생성해 사용) +- URL은 다 http://controller:5000/v3/ (우리는 아직 SSL 안 씀) + +> 여기까지 끝나면 Keystone 내부에: +> +- admin user +- admin project +- admin role +- service project +- 기본 RegionOne + + > 이 기본 세트로 구성된다. + > + +--- + +## **7. Apache 설정 (ServerName)** + +**공식 문서 “Configure the Apache HTTP server”**. + +``` +vi /etc/apache2/apache2.conf +``` + +맨 아래쪽이나 적당한 곳에 한 줄 추가: (저거 없어서 걍 추가했삼!) + +``` +ServerName controller +``` + +- controller → hostnamectl에서 설정한 이름 (/etc/hosts 에 10.100.100.11 controller 매핑돼 있어야 함) + +저장 후: + +``` +service apache2 restart +``` + +> Apache가 5000 포트에서 Keystone WSGI를 서비스하게 되고, +> + +> http://controller:5000/v3 로 API가 열린 상태가 된다. +> + +--- + +## **8. admin용 환경 변수 (admin-openrc.sh 만들기)** + +**공식 문서 “Finalize the installation” 2번**에서는 `export` 사용 방법을 설명한다. + +우리는 매번 타이핑하기 귀찮으니까 파일로 만들어 두자. + +### **8-1.** + +### **/root/admin-openrc.sh** + +### **생성** + +``` +cat >/root/admin-openrc.sh << 'EOF' +export OS_USERNAME=admin +export OS_PASSWORD=ADMIN_PASS +export OS_PROJECT_NAME=admin +export OS_USER_DOMAIN_NAME=Default +export OS_PROJECT_DOMAIN_NAME=Default +export OS_AUTH_URL=http://controller:5000/v3 +export OS_IDENTITY_API_VERSION=3 +EOF + +chmod 600 /root/admin-openrc.sh +``` + +- ADMIN_PASS → bootstrap 때 쓴 그 비밀번호로 수정 +- controller 이름이랑 URL은 그대로. + +### **8-2. 적용 + 테스트** + +``` +source /root/admin-openrc.sh +openstack token issue +``` + +- 토큰 정보가 JSON 형식으로 출력되면 Keystone이 정상 동작 중인 상태다. +- 여기까지가 **Keystone 설치 + 기본 admin 계정/엔드포인트까지 완료**. + +## OpenStack Token Issue 오류가 발생한 경우 + +grep -n "^\[database\]" -n /etc/keystone/keystone.conf +grep -n "^connection = " /etc/keystone/keystone.conf +710:[database] +711:connection = mysql+pymysql://keystone:KEYSTONE_DBPASS@controller/keystone +root@controller:~# vi /etc/keystone/keystone.conf + +알고보니 ..keystone.conf 파일에서 비번 써야하는 부분을 그대로 복붙했다 .. + +굉장히 사소해서 부끄럽다 … + +결국 비번이 계속 안맞아서 비밀번호 찾는 과정을 진행했다. .. + +그결과.. 비밀번호는 YES… + +```yaml +mysql -ukeystone -p -h controller keystone +Enter password: +ERROR 1045 (28000): Access denied for user 'keystone'@'controller' (using password: YES) +``` + +--- + +## **9. 공식 문서 기준, 다음 단계 예고** + +openstack-services.html 기준으로 이제 다음 순서는: + +1. **Glance (Image 서비스)** – https://docs.openstack.org/glance/2025.1/install/ +2. **Placement** – https://docs.openstack.org/placement/2025.1/install/ +3. **Nova** – https://docs.openstack.org/nova/2025.1/install/ +4. **Neutron** – https://docs.openstack.org/neutron/2025.1/install/ + +이 서비스들 설치 문서를 보면 전부: + +- “먼저 admin으로 openstack user create, openstack role add, openstack service create, openstack endpoint create …” +- 그 다음 각 서비스 패키지 설치/설정 + +이 흐름으로 되어 있어서, + +**현재 Keystone admin CLI가 동작하는 상태가 필수 요구사항**이다. + +--- + +## **정리 체크리스트 (Keystone 편)** + +controller에서: + +- keystone DB + keystone@localhost/% 계정 생성 (KEYSTONE_DBPASS) +- apt install keystone +- /etc/keystone/keystone.conf + - [database].connection = mysql+pymysql://keystone:KEYSTONE_DBPASS@controller/keystone + - [token].provider = fernet +- keystone-manage db_sync +- keystone-manage fernet_setup ... +- keystone-manage credential_setup ... +- keystone-manage bootstrap --bootstrap-password ADMIN_PASS ... +- /etc/apache2/apache2.conf 에 ServerName controller 추가 + service apache2 restart +- /root/admin-openrc.sh 작성 후 source 하고 openstack token issue 성공 + +이 체크 다 통과하면, + +이제 Glance 설치로 바로 진행할 수 있는 상태이다. + +[에러를 처리해요!](ch2_4_25_lec.qmd) diff --git a/lectures/ch2/ch2_4_18_lec.qmd b/lectures/ch2/ch2_4_18_lec.qmd new file mode 100644 index 0000000..e6f1f62 --- /dev/null +++ b/lectures/ch2/ch2_4_18_lec.qmd @@ -0,0 +1,377 @@ +--- +title: "2-4장. Glance 설치" +--- + +# Glance 설치 + +https://docs.openstack.org/glance/2025.1/install/install-ubuntu.html + +기준 문서는 **Epoxy Glance Ubuntu 설치 문서**다: + +> 모든 작업은 controller 노드에서, root 쉘에서 한다고 가정 +> + +> (sudo -i 하고 작업하면 편함) +> + +--- + +## **0. 전제 체크** + +Glance 설치 들어가기 전에: + +- Keystone까지 끝냈고 +- admin-openrc도 잘 먹는 상태여야 해. + +``` +source /root/admin-openrc.sh +openstack token issue +``` + +여기서 토큰 정보가 잘 나오면 OK. + +--- + +## **1. Glance용 DB 만들기 (MariaDB)** + +**공식 문서: Prerequisites → 1. To create the database** + +[어디서] + +- controller, root + +[명령] + +``` +mysql -u root -p +``` + +DB 안에서: + +``` +CREATE DATABASE glance; + +GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' + IDENTIFIED BY 'GLANCE_DBPASS'; + +GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' + IDENTIFIED BY 'GLANCE_DBPASS'; + +FLUSH PRIVILEGES; +EXIT; +``` + +- GLANCE_DBPASS → **Glance DB 전용 비밀번호**로 정한다. (예시 값 대신 실습 환경에서 정한 값을 사용하고 메모해 둔다) + +--- + +## **2. Glance 서비스 계정/엔드포인트 만들기 (Keystone)** + +**공식 문서: Prerequisites 2~4번** + +### **2-1. admin 자격 로드** + +[어디서] + +- controller + +``` +source /root/admin-openrc.sh +``` + +--- + +### **2-2. (필요하면) service 프로젝트 생성** + +Glance 문서에서는 service 프로젝트가 이미 있다고 가정하는데, + +keystone-users-ubuntu 문서에서 아직 생성하지 않았다면, 미리 생성해 두는 것이 안전하다. + +``` +openstack project show service || \ +openstack project create --domain default --description "Service Project" service +``` + +- 이미 있으면 show 결과가 나오고, +- 없으면 create가 실행된다. + +--- + +### **2-3. glance 유저 생성** + +``` +openstack user create --domain default --password-prompt glance +``` + +여기서 입력하는 비밀번호를 **GLANCE_PASS**라고 부를게. + +> 이는 “Keystone 내부에서 glance가 자기 자신을 인증할 때 사용하는 패스워드”이다. +> + +> DB 비번(GLANCE_DBPASS)랑은 **다른 값** +> + +--- + +### **2-4. service 프로젝트에 admin 역할 부여** + +``` +openstack role add --project service --user glance admin +``` + +- 출력 안 나오면 정상. + +--- + +### **2-5. glance 서비스 등록** + +``` +openstack service create --name glance \ + --description "OpenStack Image" image +``` + +- type이 image인 서비스 엔트리 하나 생김. + +--- + +### **2-6. Glance API 엔드포인트 3개 생성** + +``` +openstack endpoint create --region RegionOne \ + image public http://controller:9292 + +openstack endpoint create --region RegionOne \ + image internal http://controller:9292 + +openstack endpoint create --region RegionOne \ + image admin http://controller:9292 +``` + +- controller → /etc/hosts에 10.100.100.11로 묶여 있으므로 그대로 사용해도 된다. + +--- + +(등록된 limit / [oslo_limit] 연동은 **초기 스터디 단계에서는 생략 가능**하므로, 여기서는 최소 구성으로 진행한다. 필요하면 이후 quota 연동을 추가하면 된다.) + +--- + +## **3. Glance 패키지 설치** + +**공식 문서: Install and configure components → 1. Install the packages** + +[어디서] + +- controller, root + +[명령] + +``` +apt update +apt install -y glance +``` + +--- + +## **4. /etc/glance/glance-api.conf 설정** + +**공식 문서: Install and configure components → 2. Edit glance-api.conf** + +[어디서] + +- controller, root + +``` +vi /etc/glance/glance-api.conf +``` + +### **4-1. [database] – Glance DB 연결** + +[database] 섹션 찾아서 이렇게 맞춰: + +``` +[database] +# ... +connection = mysql+pymysql://glance:GLANCE_DBPASS@controller/glance +``` + +- GLANCE_DBPASS → 1단계에서 DB 만들 때 쓴 그 비번. + +--- + +### **4-2. [keystone_authtoken], [paste_deploy] – Keystone 연동** + +문서 예시 그대로, 다만 GLANCE_PASS만 네 값으로: + +``` +[keystone_authtoken] +# ... +www_authenticate_uri = http://controller:5000 +auth_url = http://controller:5000 +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = glance +password = GLANCE_PASS + +[paste_deploy] +# ... +flavor = keystone +``` + +- GLANCE_PASS → 아까 openstack user create ... glance 때 입력한 비밀번호. +- [keystone_authtoken] 안에 다른 기존 옵션들 있으면 **주석 처리하거나 삭제**하라고 문서에서 말함. + +--- + +### **4-3. [DEFAULT], [glance_store], [fs] – 로컬 파일 스토어** + +**Epoxy 설치 가이드도 “로컬 파일 시스템에 저장”을 예제로 씀.** + +``` +[DEFAULT] +# ... +enabled_backends = fs:file + +[glance_store] +# ... +default_backend = fs + +[fs] +filesystem_store_datadir = /var/lib/glance/images/ +``` + +- 이렇게 하면 Glance 이미지가 컨트롤러 로컬 디렉터리 /var/lib/glance/images/에 저장된다. +- [fs] 항목은 원문에서 누락된 것으로 보여, 실습 일관성을 위해 보완하여 추가하였다. + +--- + +### **4-4. [oslo_limit] / quota는 일단 스킵 (선택)** + +공식 문서에 [oslo_limit] + registered limits 설정이 나오는데, 이건 **per-tenant quota**까지 쓰고 싶을 때 옵션이라 처음에는 안 건드려도 돼. + +- 지금은 [oslo_limit] / use_keystone_limits=True **안 넣고** +- 나중에 Neutron/Nova까지 익숙해지면, quota/limit까지 같이 붙여보는 식으로 확장하면 좋을 듯. + +(현재 단계에서는 개념 과부하를 줄이기 위해 생략한다.) + +--- + +## **5. Glance DB 마이그레이션** + +**공식 문서: 3. Populate the Image service database** + +[어디서] + +- controller, root + +[명령] + +``` +su -s /bin/sh -c "glance-manage db_sync" glance +``` + +[에러를 고쳐봐요!](ch2_4_26_lec.qmd) + +- 에러 없이 끝나면 OK. +- Deprecation warning 같은 건 신경 안 써도 된다고 문서에 적혀 있음. + +--- + +## **6. 서비스 재시작** + +**공식 문서: Finalize installation** + +``` +service glance-api restart +``` + +(혹은 systemctl restart glance-api) + +--- + +## **7. Glance 동작 확인 (cirros 이미지 업로드)** + +**공식 문서: Verify operation** + +[어디서] + +- controller + +### **7-1. admin 자격 로드** + +``` +source /root/admin-openrc.sh +``` + +### **7-2. 테스트용 cirros 이미지 다운로드** + +``` +apt install -y wget # 안 깔려 있으면 +wget http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img +``` + +### **7-3. Glance에 이미지 등록** + +문서 예시는 glance CLI를 쓰니까 그대로 따라가면: + +``` +glance image-create --name "cirros" \ + --file cirros-0.4.0-x86_64-disk.img \ + --disk-format qcow2 --container-format bare \ + --visibility=public +``` + +[오류를 고쳐봐욥!](ch2_4_27_lec.qmd) + +![image.png](images/ch2_4_img_05.png) + +- glance 명령이 없으면: + +``` +apt install -y python3-glanceclient +``` + +- 한 뒤 다시 실행. + +성공하면 표 형태로 이미지 정보가 주르륵 나온다. + +### **7-4. 이미지 리스트 확인** + +``` +glance image-list +``` + +![image.png](images/ch2_4_img_06.png) + +`cirros` 가 `active` 상태로 보이면 Glance 설치가 성공한 상태이다. + +--- + +## **요약 체크리스트** + +컨트롤러에서: + +- glance DB + glance 유저/권한 (GLANCE_DBPASS) +- source admin-openrc.sh 후 +- Keystone에 + - glance 유저 생성 (GLANCE_PASS) + - service 프로젝트에 admin 롤 부여 + - glance 서비스/엔드포인트 3개 생성 (9292) +- apt install glance +- /etc/glance/glance-api.conf + - [database].connection = mysql+pymysql://glance:GLANCE_DBPASS@controller/glance + - [keystone_authtoken] / [paste_deploy] → Keystone 연동 + - [DEFAULT] / [glance_store] / [fs] → local 파일 스토어 +- glance-manage db_sync +- service glance-api restart +- cirros 업로드 + glance image-list 로 active 확인 + +여기까지 완료되면 **이미지 서비스 레이어는 준비된 상태**이다. + +다음 단계는 **Placement → Nova → Neutron** 순서이다. + +원하면 Glance 끝난 시점 기준으로 + +Placement도 동일하게 “어느 노드에서 / 어떤 명령 / 왜 수행하는지” 형식으로 이어서 정리한다. diff --git a/lectures/ch2/ch2_4_19_lec.qmd b/lectures/ch2/ch2_4_19_lec.qmd new file mode 100644 index 0000000..86db258 --- /dev/null +++ b/lectures/ch2/ch2_4_19_lec.qmd @@ -0,0 +1,310 @@ +--- +title: "2-4장. Placement 설치" +--- + +# Placement + +https://docs.openstack.org/placement/2025.1/install/ + +Placement는 한 줄로 말하면: + +> “Nova가 인스턴스 스케줄링할 때 쓰는 리소스(코어/메모리/디스크) 재고 관리용 API” +> + +이라서, Nova 설치 전에 반드시 올라가 있어야 함. + +기준 문서: **Install and configure Placement for Ubuntu (2025.1 / Epoxy)** + +--- + +## **전체 흐름 요약** + +전부 **controller 노드**에서 작업한다고 보면 돼. + +1. MariaDB에 placement DB + 계정 만들기 +2. Keystone에 placement 유저/서비스/엔드포인트 등록 +3. placement-api 패키지 설치 +4. /etc/placement/placement.conf 설정 +5. placement-manage db sync 로 DB 마이그레이션 +6. Apache 재시작 + +이제 섹션 순서대로 풀어볼게. + +--- + +## **1. Prerequisites – DB / User / Endpoint 준비** + +### **1-1. placement DB 생성 (MariaDB)** + +**어디서?** controller, root + +**문서: “Create Database” 섹션** + +``` +mysql -u root -p +``` + +DB 안에서: + +``` +CREATE DATABASE placement; + +GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'localhost' + IDENTIFIED BY 'PLACEMENT_DBPASS'; + +GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%' + IDENTIFIED BY 'PLACEMENT_DBPASS'; + +FLUSH PRIVILEGES; +EXIT; +``` + +- PLACEMENT_DBPASS : **placement DB용 비밀번호** 하나 정해서 넣고, 별도로 메모해두기. +- Keystone 때처럼 %/localhost 둘 다 열어놓는 이유는 어디서 접속해도 편하게 쓰려고. + +--- + +### **1-2. placement 유저/서비스/엔드포인트 (Keystone)** + +**어디서?** controller, admin-openrc 로드 후 + +**문서: “Configure User and Endpoints” 섹션** + +1. admin 권한 로드 + +``` +source /root/admin-openrc.sh +``` + +1. placement 유저 생성 (Identity) + +``` +openstack user create --domain default --password-prompt placement +``` + +- 여기서 입력하는 비밀번호를 **PLACEMENT_PASS**라고 부를게. +- 이는 “Keystone 내부에서 placement 유저가 자기 인증에 사용하는 비밀번호”이다. (DB 비밀번호와 별개) +1. service 프로젝트에 admin role 부여 + +``` +openstack role add --project service --user placement admin +``` + +- 출력 없음이 정상. +1. placement 서비스 등록 + +``` +openstack service create --name placement \ + --description "Placement API" placement +``` + +- type = placement 인 서비스가 catalog에 생김. + +![image.png](images/ch2_4_img_07.png) + +1. placement 엔드포인트 3개 생성 + +문서 예시는 port 8778 기준이라, 그대로 쓰자: + +``` +openstack endpoint create --region RegionOne \ + placement public http://controller:8778 + +openstack endpoint create --region RegionOne \ + placement internal http://controller:8778 + +openstack endpoint create --region RegionOne \ + placement admin http://controller:8778 +``` + +- 나중에 Nova가 Placement API 부를 때 이 URL을 사용함. + +--- + +## **2. Install and configure components – 패키지 + 설정** + +### **2-1. package 설치** + +**어디서?** controller, root + +**문서: “Install the packages”** + +``` +apt update +apt install -y placement-api +``` + +--- + +### **2-2./etc/placement/placement.conf** + +### **설정** + +**문서: “Edit the /etc/placement/placement.conf file…”** + +``` +vi /etc/placement/placement.conf +``` + +### **(a) [placement_database] – DB 연결** + +``` +[placement_database] +# ... +connection = mysql+pymysql://placement:PLACEMENT_DBPASS@controller/placement +``` + +- 여기 PLACEMENT_DBPASS 는 **1-1에서 DB 만들 때 쓴 비번**이랑 같아야 함. +- @controller 는 /etc/hosts 에서 controller → 10.100.100.11 매핑해둔 걸 그대로 사용하는 거라 OK. + +### **(b) [api] / [keystone_authtoken] – Keystone 연동** + +``` +[api] +# ... +auth_strategy = keystone + +[keystone_authtoken] +# ... +auth_url = http://controller:5000/v3 +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = placement +password = PLACEMENT_PASS +``` + +- PLACEMENT_PASS = 1-2에서 openstack user create placement 할 때 입력한 그 비번. +- 문서에도 나오는 것처럼, [keystone_authtoken] 안에 기존에 들어있던 옵션들은 **주석 처리하거나 삭제**하는 것을 권장한다. +- project_name = service / username = placement / Default 도메인이 Keystone 설정과 맞아야 한다고 강조되어 있음. + +여기까지 하면: + +- Placement가 MariaDB에 붙어서 placement DB를 쓰고 +- Keystone의 placement 유저 권한으로 토큰 검증을 수행하는 구조가 완성된다. + +--- + +## **3. placement DB 마이그레이션** + +**어디서?** controller, root + +**문서: “Populate the placement database”** + +``` +su -s /bin/sh -c "placement-manage db sync" placement +``` + +- keystone 때랑 패턴 똑같이, placement 시스템 계정으로 DB 스키마 생성. +- Deprecation warning 같은 건 무시하라고 문서에 써 있음. + +DB 비번이 틀렸으면 여기서 아까처럼 1045 에러가 뜨니까, + +해당 경우에는 `mysql -uplacement -p -h controller placement` 로 직접 접속해 보면 원인 파악이 수월하다. + +--- + +## **4. Finalize – Apache 재시작** + +Placement도 Keystone처럼 Apache WSGI로 붙기 때문에, 설정 반영을 위해 Apache를 재시작해야 함. + +**어디서?** controller, root + +**문서: “Finalize installation”** + +``` +service apache2 restart +``` + +이제 Placement API는 http://controller:8778 에서 응답할 준비가 된 상태. + +--- + +## **5. 설치 확인 간단 체크** + +공식 문서엔 간단한 verify 섹션이 없지만, 최소한 이 정도는 테스트해볼 수 있어: + +``` +# 1) Placement API가 살아있는지 +curl -v http://controller:8778/ +``` + +- 보통은 404나 간단한 JSON이 오더라도, **적어도 500이 아니라는 것**만 확인하면 된다. + +나중에 Nova 설치하고 나면: + +``` +source /root/admin-openrc.sh +openstack resource class list --sort-column name +``` + +처럼 openstack CLI로 Placement API를 두들겨볼 수도 있음(Nova 설치 이후). + +- 모든 항목이 정상인지 확인하기 위해 상태 점검을 수행한다. + +```yaml +placement-status upgrade check +``` + +![image.png](images/ch2_4_img_08.png) + +- Placement API에 대해 몇 가지 명령을 실행한다. + - [osc-placement](https://docs.openstack.org/osc-placement/latest/) 플러그인을 설치한다: + + [이 예제에서는 PyPI](https://pypi.org/) 와 [pip](https://docs.openstack.org/placement/2025.1/install/from-pypi.html#about-pip)를 사용한다. 배포판 패키지를 사용하는 경우에는 해당 저장소에서 패키지를 설치할 수 있다. Python3 환경에서는 **pip3** 를 사용하거나 배포판의 **python3-osc-placement** 패키지를 설치한다. + + `pip3 install osc-placement` + + - 사용 가능한 리소스 클래스와 특성을 나열한다. + + ```yaml + openstack --os-placement-api-version 1.2 resource class list --sort-column name + +----------------------------+ + | name | + +----------------------------+ + | DISK_GB | + | IPV4_ADDRESS | + | ... | + + openstack --os-placement-api-version 1.6 trait list --sort-column name + +---------------------------------------+ + | name | + +---------------------------------------+ + | COMPUTE_DEVICE_TAGGING | + | COMPUTE_NET_ATTACH_INTERFACE | + | ... | + ``` + + + ![image.png](images/ch2_4_img_09.png) + + +--- + +## **요약 체크리스트 (Placement 편)** + +controller에서: + +1. **DB** + - placement DB 생성 + - placement@localhost / placement@% 에 PLACEMENT_DBPASS 부여 +2. **Keystone** + - openstack user create placement (PLACEMENT_PASS) + - openstack role add --project service --user placement admin + - openstack service create placement + - endpoint 3개 (public/internal/admin) → http://controller:8778 +3. **패키지 / 설정** + - apt install placement-api + - /etc/placement/placement.conf + - [placement_database].connection = mysql+pymysql://placement:PLACEMENT_DBPASS@controller/placement + - [api].auth_strategy = keystone + - [keystone_authtoken] 블록에 Keystone 접속 정보 + password = PLACEMENT_PASS +4. **마이그레이션 / 웹서버** + - placement-manage db sync (에러 없음) + - service apache2 restart + +여기까지 완료되면 Nova가 Placement에 연결되어 “어디에 얼마만큼의 vCPU/메모리/디스크가 남았는지” 조회할 준비가 완료된 상태이다. + +이제 다음 타이밍에는 **Nova 설치(Compute)** 를 같은 스타일로 순서대로 진행하면 된다 diff --git a/lectures/ch2/ch2_4_20_lec.qmd b/lectures/ch2/ch2_4_20_lec.qmd new file mode 100644 index 0000000..ae169d2 --- /dev/null +++ b/lectures/ch2/ch2_4_20_lec.qmd @@ -0,0 +1,1142 @@ +--- +title: "2-4장. Nova 설치" +--- + +# Nova + + 이제 “오픈스택의 핵심 서비스”인 Nova 구성 단계이다. + +문서 트리 기준으로 Nova install 가이드는 크게 네 파트다: + +1. **Overview** – 전체 OpenStack PoC 아키텍처 설명 +2. **Compute service overview** – Nova 내부 구조/컴포넌트 설명 +3. **Install and configure controller node** – 컨트롤러에서 nova-api/스케줄러 등 설치 +4. **Install and configure a compute node** – 컴퓨트 노드에서 nova-compute 설치 + +지금 질문은 1–2번처럼 개요/아키텍처 위주라서, 거기부터 차근차근 풀어볼게. + +--- + +## **1. Overview: 이 가이드 자체가 어떤 그림을 상정하냐** + +### **1-1. 이 설치 가이드의 목적** + +Overview 페이지가 먼저 강조하는 핵심은 다음과 같다: + +- 이 가이드는 **학습용 PoC**를 위한 거고 +- **프로덕션 아키텍처 그대로 따라 쓰라고 만든 게 아니다** +- “리눅스 좀 만질 줄 아는 사람” 기준으로, + + OpenStack 주요 서비스 하나씩 설치하면서 구조를 이해하게 하는 게 목적. + + +따라서 현재 2노드 Proxmox 실습 환경과 정확히 대응된다: + +- 컨트롤러 1개 + 컴퓨트 1개 → 최소 VM 부팅 가능한 구조 + +--- + +### **1-2. 예제 아키텍처 – 어떤 노드들이 나오냐** + +Overview 페이지의 “Example architecture” 섹션에서 설명하는 기본 구조: + +1. **Controller 노드** + - Keystone (Identity) + - Glance (Image) + - Nova의 “관리 쪽 컴포넌트” (API, scheduler, conductor, novncproxy) + - Neutron의 “관리 + 에이전트” + - Horizon (Dashboard) + - 그리고 공통 인프라: + - MariaDB, RabbitMQ, Memcached, Etcd, NTP 등 + - 현재 실습 랩: 해당 구성은 controller VM에 집중 배치되어 있다. +2. **Compute 노드** + - Nova hypervisor 부분(nova-compute) + - Neutron 에이전트 (OVS/OVN/bridge agent) + - 인스턴스가 실제로 떠 있는 곳 (KVM/QEMU 하이퍼바이저) + - 현재 실습 랩: compute1 VM. +3. **Block Storage 노드 (선택)** + - Cinder, Manila용 디스크 올려놓는 노드 + - PoC에서는 컨트롤러에 같이 올리거나, 아예 안 쓸 수도 있음. +4. **Object Storage 노드 (선택)** + - Swift용 오브젝트 디스크 + - PoC 가이드에서도 “이건 별도 2노드 이상 필요”라고 말함. + +**중요:** 이 예제 아키텍처는 **프로덕션 미니멈**이 아니라, “개념 학습용 미니멈”이라고 강조함. + +프로덕션 가면 보통: + +- 네트워크 노드 따로, +- 스토리지 노드 따로, +- 컨트롤러 다중(HA), +- overlay/스토리지 트래픽을 별도 네트워크로 분리 + +이런 식으로 더 쪼개진다고 설명해. + +--- + +### **1-3. 이 PoC 아키텍처가 프로덕션이랑 뭐가 다른지** + +Overview에서는 특히 두 가지를 강조한다: + +1. **네트워크 에이전트가 컨트롤러에 다 올라가 있다** + - 프로덕션: L3/DHCP/LBaaS 같은 Neutron 에이전트는 보통 “Network node” 로 분리 + - PoC: 기능을 controller에 집중 배치한다. (현재 실습 방식) +2. **Self-service overlay 트래픽이 management 네트워크를 같이 쓴다** + - 프로덕션: overlay(터널) 트래픽은 별도 “데이터 네트워크” 씀 + - PoC: 관리망(10.x/172.x 같은) 하나에 관리+터널 같이 태움 + +→ 현재 실습 랩은 management/provider를 Proxmox `vmbr0` 한 인터페이스에 통합했으므로, + +따라서 “진짜 PoC 아키텍처”에 해당한다고 볼 수 있다. + +--- + +### **1-4. 네트워크 옵션 두 개** + +Overview 맨 아래 “Networking” 부분에서는 두 가지 옵션을 내놔: + +1. **Option 1: Provider networks** + - 가장 단순한 구성 + - Neutron은 사실상 L2 VLAN/bridge만 담당 + - 라우팅(L3), NAT 같은 건 **물리 네트워크 장비/외부 라우터**에 맡김 + - 특징: + - 인스턴스 네트워크 = 거의 그대로 물리망 + - self-service(테넌트 private) 네트워크 X + - LBaaS, FWaaS 같은 advanced 기능 X + - PoC에서 “외부 네트워크에 직접 연결된 VM을 빠르게 검증”하려는 경우 이 방식을 사용하기도 한다. +2. **Option 2: Self-service networks** + - Provider 위에 **self-service(tenant) 네트워크**를 하나 더 올리는 옵션 + - VXLAN 같은 overlay + L3 라우터 네임스페이스로 + + private 네트워크 만들고, 라우터 통해 외부(provider)로 NAT. + + - 특징: + - 사용자가 자체 네트워크/Subnet/Router 생성 가능 + - LBaaS/FWaaS 같은 고급 서비스 가능 + - 현실적인 OpenStack 느낌이 나는 구성 + +지금 네 스터디 목적(“OpenStack를 제대로 배우고 싶다”)이면, + +**Option 2 (self-service)** 까지 진행하는 구성을 권장한다. + +Nova 설치 자체는 두 옵션 공통이고, Neutron 설정에서 갈라져. + +--- + +## **2. Compute service overview: Nova 내부 구조** + +이제 Nova 자체 아키텍처를 설명하는 페이지. + +### **2-1. Nova가 OpenStack에서 맡는 역할** + +한 문장으로: + +> Nova = “Compute(인스턴스) 관리 서비스” +> + +> → IaaS에서 VM을 어디에/어떻게 띄울지 결정하고, 하이퍼바이저를 제어하는 핵심. +> + +그리고 Nova는 다른 서비스랑 이렇게 얽힌다고 문서에 나온다: + +- Keystone: 인증/권한 +- Placement: **자원 인벤토리 + 스케줄링 대상 선택** +- Glance: 인스턴스 부팅할 때 가져오는 image 저장소 +- Neutron: 인스턴스 NIC/네트워크/VIP 등 네트워킹 +- Horizon: 사용자가 Nova를 조작하는 GUI + +즉, “Nova 혼자만” 있는 건 아니고 + +**Nova가 orchestration 중심에서 나머지 서비스들을 묶어준다** 느낌. + +--- + +### **2-2. Nova 컴포넌트들** + +문서에서 나오는 주요 데몬들 정리: + +1. **nova-api** + - 역할: 외부에서 오는 Compute API 요청(인스턴스 생성/삭제 등)을 받아서 처리 시작 + - 하는 일: + - Keystone 토큰 검증 + - 정책(enforce policy) + - 인스턴스 생성 요청을 메시지 큐로 보내고, orchestration 시작 +2. **nova-api-metadata** + - 역할: 인스턴스 내부에서 오는 metadata 요청 응답 + - cloud-init이 부팅할 때 사용자 데이터/메타데이터 받아갈 때 여기로 옴. +3. **nova-compute** + - 역할: **각 compute 노드에서 하이퍼바이저를 직접 제어**하는 워커 + - 하는 일: + - 메시지 큐에서 “이 인스턴스 여기서 띄워라” 명령을 받아서 + - KVM(libvirt)/VMware 등 하이퍼바이저 API 호출 + - 인스턴스 상태를 DB/Placement에 업데이트 +4. **nova-scheduler** + - 역할: 인스턴스를 “어느 compute 노드에서 띄울지” 결정 + - Placement에서 자원 사용량/가용성을 보고 적절한 호스트를 골라줌. +5. **nova-conductor** + - 역할: nova-compute ↔ DB 사이의 미들맨 + - 이유: + - compute 노드가 DB를 직접 때리지 않게 해서 보안/구조 개선 + - 특징: + - 수평 스케일 가능 (컨트롤러 측에서 여러 개 띄울 수 있음) + - compute 노드에는 올리지 말라고 문서에서 강조. +6. **nova-novncproxy** + - 역할: VNC 콘솔 프록시 (Horizon에서 “콘솔 열기” 할 때 중계) + - 브라우저 기반 noVNC 클라이언트가 여기에 연결 → 여기서 다시 하이퍼바이저 VNC로 튕김. +7. **nova-spicehtml5proxy** (옵션) + - VNC 대신 SPICE 콘솔 쓸 때 사용하는 프록시. +8. **메시지 큐 (RabbitMQ 등)** + - 역할: Nova 데몬끼리 메시지 주고받는 중앙 허브 + - 예: nova-api → nova-scheduler → nova-compute 순서로 “인스턴스 생성 요청” 메시지가 큐를 통해 전달된다. +9. **SQL 데이터베이스** + - 역할: + - 인스턴스 목록/상태 + - flavor(인스턴스 타입) + - 프로젝트/네트워크 정보 등 Nova가 알아야 할 상태 저장 + - 실제로는 nova_api DB / cell DB로 나뉘는데, 그건 설치 섹션에서. + +이를 합치면 “인스턴스 1개를 생성하는 흐름”은 다음과 같다: + +1. 유저: Horizon or CLI → nova-api에 “VM 하나 만들어줘” +2. nova-api: 요청 유효성 검증 → 메시지 큐에 job 넣음 +3. nova-scheduler: placement 보고 적절한 compute 노드를 골라서 큐에 “이 호스트에 배치”라고 올림 +4. 해당 nova-compute: job 받아서 + - 이미지(Glance) 다운로드 + - 네트워크(Neutron) 포트 셋업 + - 하이퍼바이저(KVM)에 실제 VM 생성 + - 상태를 DB/Placement에 업데이트 +5. 유저가 콘솔 보고 싶으면 nova-novncproxy 통해 접속 + +이런 구조라고 보면 돼. + +--- + +## **3. Install & configure controller node (Ubuntu) – Nova 컨트롤러 쪽 설계** + +이제 설치 가이드 본편에서 “컨트롤러에 Nova를 어떻게 올릴지” 부분. + +(현재는 구조 설명 중심으로 이해하고, 이후 실습에서 명령어를 단계별로 분리해 적용하면 된다.) + +### **3-1. 컨셉: 컨트롤러에서 Nova가 하는 역할** + +컨트롤러에는 보통 이런 Nova 프로세스들이 올라간다고 가이드에서 가정해: + +- nova-api +- nova-scheduler +- nova-conductor +- nova-novncproxy +- (환경에 따라 `nova-api-metadata`를 사용하거나 Neutron metadata proxy만 사용하는 구성도 있다.) + +즉: + +- **API/스케줄링/상태 관리/콘솔 프록시**는 컨트롤러 +- **실제 VM 생성/삭제(nova-compute)는 compute 노드** + +로 분리. + +--- + +### **3-2. 설치 가이드에서 실제로 하는 일(컨트롤러)** + +대략적인 단계: + +1. **nova용 DB 2개 생성** + - nova_api DB + - nova (cell0/cell1) DB + + → Nova Cell v2 구조 때문에 DB가 둘로 나뉨. + +2. **Keystone에 nova 유저/서비스/엔드포인트 등록** + - openstack user create nova + - openstack role add --project service --user nova admin + - openstack service create --name nova compute + - openstack endpoint create ... nova public/internal/admin http://controller:8774/v2.1 +3. **패키지 설치** + - nova-api, nova-conductor, nova-scheduler, nova-novncproxy 패키지들. +4. **/etc/nova/nova.conf 설정** + - [DEFAULT] + - transport_url = rabbit://... (RabbitMQ 연결) + - my_ip = 10.100.100.11 (컨트롤러 IP) + - use_neutron = True, firewall_driver = nova.virt.firewall.NoopFirewallDriver (네트워크는 Neutron에게) + - [api_database] / [database] + - connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova_api + - connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova + - [keystone_authtoken] + - Keystone 토큰 검증 정보 (auth_url, memcached, project_name=service, username=nova, password=NOVA_PASS) + - [glance], [neutron], [placement], [vnc], [oslo_messaging_rabbit] 등 + + Nova가 다른 서비스랑 이야기하는 설정들이 들어감. + +5. **DB & cell 초기화** + - nova-manage api_db sync + - nova-manage cell_v2 map_cell0 + - nova-manage cell_v2 create_cell --name cell1 + - nova-manage db sync + + → 이 작업으로 “셀” 개념을 구성한다고 볼 수 있다. (여러 데이터센터/클러스터를 이후 cell로 분리할 수 있음) + +6. **서비스 enable + start** + - systemctl enable --now nova-api nova-scheduler nova-conductor nova-novncproxy +7. **초기 상태 확인** + - nova-status upgrade check + - openstack compute service list (컨트롤러 쪽 서비스들이 up인지) + +--- + +## **4. Install & configure a compute node (Ubuntu) – Nova 컴퓨트 쪽 설계** + +Compute install 가이드는 “각 컴퓨트 노드에서 nova-compute를 어떻게 세팅하냐” 설명. + +### **4-1. 컴퓨트 노드가 맡는 역할** + +- 하이퍼바이저(KVM/QEMU, 혹은 VMware 등)를 직접 제어 +- VM(인스턴스)의 라이프사이클(생성, 삭제, migrate)을 수행 +- Neutron 에이전트와 함께 인스턴스 네트워크 인터페이스를 연결 + +컨트롤러는 **Control Plane**, + +컴퓨트 노드는 **Data Plane(실제 워크로드)** 으로 이해하면 된다. + +--- + +### **4-2. 설치 가이드에서 하는 일 (컴퓨트 노드)** + +1. **패키지 설치** + - nova-compute (필수) + - libvirt/qemu-kvm 등 하이퍼바이저 패키지는 OS 쪽에서 설치. +2. **/etc/nova/nova.conf 설정** + - [DEFAULT] + - my_ip = 10.100.100.31 (compute 노드 IP) + - use_neutron = True + - [oslo_messaging_rabbit] + - 컨트롤러 쪽 RabbitMQ에 붙도록 transport_url 지정 + - [keystone_authtoken] + - controller와 동일하게 nova 유저로 Keystone 인증 + - [vnc] + - enabled = True + - server_listen = 0.0.0.0 + - server_proxyclient_address = 10.100.100.31 (자기 IP) + - novncproxy_base_url = http://controller:6080/vnc_auto.html (컨트롤러 novncproxy) + - [glance], [neutron], [placement] 도 컨트롤러와 유사하게 맞춤. + - [libvirt] + - virt_type = kvm 혹은 nested 문제 있으면 qemu + - CPU 모드 등 튜닝 옵션. +3. **서비스 시작** + - systemctl enable --now nova-compute +4. **컨트롤러에서 서비스 등록 확인** + - openstack compute service list 를 컨트롤러에서 실행하면: + - nova-compute@compute1 같은 항목이 up 으로 떠야 함. + - openstack hypervisor list 에도 compute 노드가 보일 것. + +--- + +## **5. 전체 Nova 설치 페이지 구조 요약** + +Nova install 섹션 전체를 요약하면: + +1. **Overview** + - 이 가이드는 PoC용이다 + - 최소 2노드 아키텍처(controller + compute) + - Block/Object storage는 선택 + - 네트워크 옵션: Provider vs Self-service +2. **Compute service overview** + - Nova가 IaaS에서 맡는 역할 + - 주요 컴포넌트(nova-api, scheduler, conductor, compute, novncproxy 등)와 메시지 큐/DB 구조 + - Keystone/Placement/Glance/Neutron과의 연계 +3. **Install & configure controller node (Ubuntu/RHEL 별도 문서)** + - nova DB 생성 + - Keystone에 nova 유저/서비스/엔드포인트 등록 + - nova-api, nova-scheduler, nova-conductor, nova-novncproxy 설치 및 설정 + - cell v2 DB 초기화 + - 서비스 시작 +4. **Install & configure compute node (Ubuntu/RHEL)** + - 각 compute 노드에서 nova-compute 설치 + - /etc/nova/nova.conf 에 my_ip / hypervisor / RabbitMQ / Keystone / Placement / VNC / Neutron 등 설정 + - 서비스 시작 → 컨트롤러에서 인식되는지 확인 +5. **Verify operation** (별 페이지) + - nova-status upgrade check + - openstack compute service list, openstack hypervisor list + - 나중에는 Neutron까지 붙여서 실제 인스턴스 부팅까지 확인 + +--- + + 이제 Nova 컨트롤러 구성까지 한 번에 정리한다. + +아래는 **공식 문서 controller-install-ubuntu(Flamingo/32.1.1)를 기준으로, 실습 환경(컨트롤러 IP 10.100.100.11, 앞서 구성한 Keystone/Glance/Placement/RabbitMQ 구조)**에 맞게 정리한 실행 순서 버전이다. + +> 전제 +> +- 노드: **controller** +- 유저: root (sudo -i 상태) +- IP: 10.100.100.11 +- RabbitMQ: openstack / RABBIT_PASS 이미 만들어 둔 상태 +- Keystone admin: /root/admin-openrc.sh 정상 작동 + +아래 순서를 위에서부터 차례대로 진행하면 된다. + +--- + +## **1. Nova용 DB 3개 만들기 (nova_api, nova, nova_cell0)** + +**공식: Prerequisites 1번** + +``` +mysql -u root -p +``` + +SQL: + +``` +CREATE DATABASE nova_api; +CREATE DATABASE nova; +CREATE DATABASE nova_cell0; + +GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' + IDENTIFIED BY 'NOVA_DBPASS'; +GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' + IDENTIFIED BY 'NOVA_DBPASS'; + +GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' + IDENTIFIED BY 'NOVA_DBPASS'; +GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' + IDENTIFIED BY 'NOVA_DBPASS'; + +GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' + IDENTIFIED BY 'NOVA_DBPASS'; +GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' + IDENTIFIED BY 'NOVA_DBPASS'; + +FLUSH PRIVILEGES; +EXIT; +``` + +- NOVA_DBPASS 는 **Nova DB 전용 비밀번호**로 하나 정해 사용하면 된다. +- 나중에 /etc/nova/nova.conf 의 DB connection이랑 반드시 같아야 함. + +--- + +## **2. admin 권한 불러오기** + +**공식: Prerequisites 2번** + +``` +source /root/admin-openrc.sh +``` + +이제부터 나오는 openstack ... 커맨드는 전부 admin 권한으로 실행된다. + +--- + +## **3. Keystone에 nova 유저/서비스/엔드포인트 만들기** + +**공식: Prerequisites 3~4번** + +### **3-1. nova 유저 생성 (Identity)** + +``` +openstack user create --domain default --password-prompt nova +``` + +→ 여기서 입력하는 비밀번호 = **NOVA_PASS** (Keystone용 nova 계정 비번). + +이건 DB 비번(NOVA_DBPASS)과 달라도 된다. + +### **3-2. service 프로젝트에 admin 역할 부여** + +``` +openstack role add --project service --user nova admin +``` + +(출력 없음이 정상) + +### **3-3. nova 서비스 등록** + +``` +openstack service create --name nova \ + --description "OpenStack Compute" compute +``` + +### **3-4. nova API 엔드포인트 3개 생성** + +``` +openstack endpoint create --region RegionOne \ + compute public http://controller:8774/v2.1 + +openstack endpoint create --region RegionOne \ + compute internal http://controller:8774/v2.1 + +openstack endpoint create --region RegionOne \ + compute admin http://controller:8774/v2.1 +``` + +이제 service catalog에 compute 타입의 nova API가 등록된다. + +*(문서 5번 “Placement 설치”는 이미 끝냈으니까 스킵)* + +--- + +## **4. Nova 컨트롤러 컴포넌트 패키지 설치** + +**공식: Install and configure components 1번** + +``` +apt update +apt install -y nova-api nova-conductor nova-novncproxy nova-scheduler +``` + +이 네 개가 컨트롤러에서 돌아가는 Nova의 핵심 프로세스들. + +--- + +## **5.** + +## **/etc/nova/nova.conf** + +## **설정** + +**공식: Install and configure components 2번** + +``` +vi /etc/nova/nova.conf +``` + +아래 섹션들을 차례대로 맞추면 된다. + +--- + +### **5-1. [api_database], [database] – Nova DB 연결** + +``` +[api_database] +# ... +connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova_api + +[database] +# ... +connection = mysql+pymysql://nova:NOVA_DBPASS@controller/nova +``` + +- NOVA_DBPASS = 1단계에서 DB 만들 때 썼던 그 비번. + +--- + +### **5-2. [DEFAULT] – RabbitMQ / my_ip 등** + +RabbitMQ는 이미 openstack 유저 + RABBIT_PASS 로 만들어둔 상태라고 가정: + +``` +[DEFAULT] +# ... +transport_url = rabbit://openstack:RABBIT_PASS@controller:5672/ +my_ip = 10.100.100.11 +use_neutron = True +firewall_driver = nova.virt.firewall.NoopFirewallDriver +``` + +- RABBIT_PASS = RabbitMQ에서 openstack 계정 만들 때 쓴 비번. +- my_ip 는 **controller 관리 IP** 인 10.100.100.11 로 바꿈. (문서 기본은 10.0.0.11) + +> 문서에 있는 log_dir = /var/log/nova 옵션은 +> +> +> **패키징 버그 때문에 지우라고** +> + +> 만약 [DEFAULT] 안에 log_dir 가 있다면 그 줄은 삭제/주석 처리. +> + +--- + +### **5-3. [keystone_authtoken] – Keystone 인증 정보** + +``` +[keystone_authtoken] +# ... +www_authenticate_uri = http://controller:5000/ +auth_url = http://controller:5000/ +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = nova +password = NOVA_PASS +``` + +- NOVA_PASS = 3-1에서 nova 유저 만들 때 썼던 비번. +- 기존 [keystone_authtoken] 안에 잡다한 옵션 많으면 **다 지우고 위 블록만 남기는 느낌**으로. + +--- + +### **5-4. [service_user] – 서비스 토큰** + +Flamingo 문서 기준으로 새로 들어간 부분: Nova가 다른 서비스 호출할 때 “service user token”을 같이 보내도록 설정. + +HTTPS 예제지만, 우리 환경은 HTTP라 auth_url만 맞춰서 쓰자: + +``` +[service_user] +send_service_user_token = true +auth_url = http://controller:5000/v3 +auth_type = password +project_domain_name = Default +project_name = service +user_domain_name = Default +username = nova +password = NOVA_PASS +``` + +- 역시 `NOVA_PASS` 값을 그대로 사용한다. + +--- + +### **5-5. [vnc] – VNC 프록시 설정** + +컨트롤러에서 novncproxy가 떠 있고, $my_ip = 10.100.100.11 이므로: + +``` +[vnc] +enabled = true +# ... +server_listen = $my_ip +server_proxyclient_address = $my_ip +``` + +- 컴퓨트 노드는 나중에 novncproxy_base_url = http://controller:6080/vnc_auto.html 까지 넣을 건데, + + 컨트롤러 쪽은 문서처럼 listen 주소만 맞추면 된다. + + +--- + +### **5-6. [glance] – 이미지 서비스 위치** + +``` +[glance] +# ... +api_servers = http://controller:9292 +``` + +- 앞에서 Glance를 http://controller:9292 로 열었으니 그대로 사용. + +--- + +### **5-7. [oslo_concurrency] – 락 경로** + +``` +[oslo_concurrency] +# ... +lock_path = /var/lib/nova/tmp +``` + +디렉터리는 패키지 설치 시 자동 생성되어 있을 가능성이 높으며, 없으면 만들어도 된다. + +--- + +### **5-8. [placement] – Placement 서비스 연결** + +앞에서 설치한 Placement(8778) + Keystone placement 유저 정보: + +``` +[placement] +# ... +region_name = RegionOne +project_domain_name = Default +project_name = service +auth_type = password +user_domain_name = Default +auth_url = http://controller:5000/v3 +username = placement +password = PLACEMENT_PASS +``` + +- PLACEMENT_PASS = Placement 설치 때 openstack user create placement 에서 썼던 비번. +- 기존 [placement]에 다른 옵션 있으면 다 지우고 위만 남김. + +--- + +### **5-9. [neutron] – Neutron 연동 (지금은 TODO 표시 해두고, Neutron 설치 때 채우기)** + +문서는 여기서 Neutron 섹션도 채우라고 하는데, + +실제 내용은 Neutron 설치 가이드에 맞춰야 하므로 **우선 뼈대만 구성하고, Neutron 파트에서 함께 보완하는 방식**을 권장한다. + +예를 들면: + +``` +[neutron] +# 나중에 Neutron 설치할 때 여기 채움 +``` + +(상세 설정은 `url = http://controller:9696`, `auth_url`, `username = neutron` 등 Neutron 가이드에서 다룬다.) + +--- + +## **6. Nova Cell v2 DB 초기화 작업들** + +**공식: Install and configure components 3~6번** + +모두 controller에서 root로 실행: + +### **6-1. nova-api DB 동기화** + +``` +su -s /bin/sh -c "nova-manage api_db sync" nova +``` + +→ nova_api DB에 스키마 생성. + +### **6-2. cell0 매핑** + +``` +su -s /bin/sh -c "nova-manage cell_v2 map_cell0" nova +``` + +→ nova_cell0 DB를 cell0로 등록. + +### **6-3. cell1 생성** + +``` +su -s /bin/sh -c "nova-manage cell_v2 create_cell --name=cell1 --verbose" nova +``` + +→ 실제 compute 자원들이 들어갈 기본 cell. + +### **6-4. nova 메인 DB 동기화** + +``` +su -s /bin/sh -c "nova-manage db sync" nova +``` + +→ nova DB에 스키마/데이터 생성. + +### **6-5. cell 목록 확인** + +``` +su -s /bin/sh -c "nova-manage cell_v2 list_cells" nova +``` + +![image.png](images/ch2_4_img_10.png) + +--- + +## **7. Nova 컨트롤러 서비스 재시작** + +**공식: Finalize installation** + +``` +service nova-api restart +service nova-scheduler restart +service nova-conductor restart +service nova-novncproxy restart +``` + +혹은: + +``` +systemctl restart nova-api nova-scheduler nova-conductor nova-novncproxy +``` + +여기까지가 **“컨트롤러의 Nova 측 구성이 준비 완료된 상태”**이다. + +--- + +## compute1 노드 Nova 구성 +아래는 **공식 문서 compute-install-ubuntu(2025.1 Epoxy) 내용을 실습 환경에 맞게 순서대로 정리한 버전**이다. + +전제 다시 정리: + +- 컨트롤러: controller / **10.100.100.11** +- 컴퓨트: compute1 / **10.100.100.31** +- RabbitMQ: openstack / RABBIT_PASS +- Keystone nova 유저: NOVA_PASS +- Keystone placement 유저: PLACEMENT_PASS +- Glance/Placement/Nova 컨트롤러 쪽은 이미 OK 상태 + +--- + +## **0. 이 장에서 어디서 뭘 치는지** + +- **compute1 노드** + - 패키지 설치, /etc/nova/nova.conf 편집 + - nova-compute 재시작, libvirt 타입 설정 +- **controller 노드** + - cell에 compute 호스트 등록 (nova-manage cell_v2 discover_hosts) + +이거만 머리에 두고, 순서대로 진행하면 된다. + +--- + +## **1. compute1에 Nova 컴퓨트 패키지 설치** + +**공식: “Install the packages”** + +**어디서?** compute1 (루트 or sudo) + +``` +sudo -i # 아직 root 아니면 +apt update +apt install -y nova-compute +``` + +- 이 패키지가 nova-compute 서비스 + 기본 설정 파일(/etc/nova/nova.conf, /etc/nova/nova-compute.conf) 을 깔아줌. +- 아직 서비스가 완전히 동작하는 단계는 아니며, 설정 완료 후 재시작한다. + +--- + +## **2.** + +## **/etc/nova/nova.conf** + +## **설정 (compute1)** + +**공식: “Edit /etc/nova/nova.conf and…”** + +``` +vi /etc/nova/nova.conf +``` + +### **2-1. [DEFAULT] – RabbitMQ + my_ip** + +문서 기준: + +``` +[DEFAULT] +# ... +transport_url = rabbit://openstack:RABBIT_PASS@controller +my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS +``` + +이를 **우리 환경 기준으로** 이렇게 맞춰: + +``` +[DEFAULT] +# ... +transport_url = rabbit://openstack:RABBIT_PASS@controller +my_ip = 10.100.100.31 +use_neutron = True +firewall_driver = nova.virt.firewall.NoopFirewallDriver +``` + +- RABBIT_PASS → 컨트롤러에서 RabbitMQ openstack 계정 만들 때 썼던 비번. +- my_ip → compute1 관리 IP (10.100.100.31) +- `use_neutron = True`, `firewall_driver` 는 컨트롤러와 일관되게 맞추는 구성을 권장한다. + +--- + +### **2-2. [api] + [keystone_authtoken] – Keystone 연동** + +문서 예제: + +``` +[api] +# ... +auth_strategy = keystone + +[keystone_authtoken] +# ... +www_authenticate_uri = http://controller:5000/ +auth_url = http://controller:5000/ +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = nova +password = NOVA_PASS +``` + +우리도 그대로 쓰되, NOVA_PASS만 실제 값으로 넣자: + +``` +[api] +# ... +auth_strategy = keystone + +[keystone_authtoken] +# ... +www_authenticate_uri = http://controller:5000/ +auth_url = http://controller:5000/ +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = nova +password = NOVA_PASS +``` + +- NOVA_PASS = 컨트롤러에서 openstack user create nova 했을 때 입력한 비번. + +**중요:** 기존 [keystone_authtoken] 안에 있던 잡다한 옵션들은 **주석 처리하거나 지우라고** 문서에서 말함. + +--- + +### **2-3. [service_user] – 서비스 토큰** + +Flamingo/Epoxy에서 추가된 “서비스 유저 토큰” 부분. + +문서는 HTTPS 예제를 쓰지만, 우리는 HTTP 환경이라 아래처럼 쓰면 됨: + +``` +[service_user] +send_service_user_token = true +auth_url = http://controller:5000/v3 +auth_strategy = keystone +auth_type = password +project_domain_name = Default +project_name = service +user_domain_name = Default +username = nova +password = NOVA_PASS +``` + +- 여기서도 NOVA_PASS 동일. + +--- + +### **2-4. [neutron] – 나중에 Neutron 설치하면서 채울 자리** + +compute-install 문서에선 [neutron]도 채우라고 하는데, + +실제 내용은 **Neutron 설치 가이드**에 맞춰야 하니까 지금은 TODO로 비워두자. + +예를 들어: + +``` +[neutron] +# TODO: Neutron 설치할 때 url/auth_url/username=neutron 등 채우기 +``` + +나중에 Neutron 파트 들어갈 때 여기 다시 돌아오자. + +--- + +### **2-5. [vnc] – 콘솔 설정** + +문서 예제: + +``` +[vnc] +# ... +enabled = true +server_listen = 0.0.0.0 +server_proxyclient_address = $my_ip +novncproxy_base_url = http://controller:6080/vnc_auto.html +``` + +우리도 그대로: + +``` +[vnc] +# ... +enabled = true +server_listen = 0.0.0.0 +server_proxyclient_address = $my_ip +novncproxy_base_url = http://controller:6080/vnc_auto.html +``` + +- server_listen = 0.0.0.0 → compute1에서 모든 IP로 VNC 듣기 +- server_proxyclient_address = $my_ip → 내부적으로는 10.100.100.31 +- novncproxy_base_url → 컨트롤러에 있는 nova-novncproxy 위치 + +--- + +### **2-6. [glance] – 이미지 서비스 위치** + +``` +[glance] +# ... +api_servers = http://controller:9292 +``` + +Glance 컨트롤러 설치 때와 동일. + +--- + +### **2-7. [oslo_concurrency] – 락 경로** + +``` +[oslo_concurrency] +# ... +lock_path = /var/lib/nova/tmp +``` + +문서에서도 그대로 쓰라고 되어 있음. + +--- + +### **2-8. [placement] – Placement API 연동** + +문서 예제: + +``` +[placement] +# ... +region_name = RegionOne +project_domain_name = Default +project_name = service +auth_type = password +user_domain_name = Default +auth_url = http://controller:5000/v3 +username = placement +password = PLACEMENT_PASS +``` + +- PLACEMENT_PASS = Placement 설치 때 openstack user create placement 에서 쓴 비번. +- 기존 [placement] 옵션 있으면 **지우고 이걸로 교체**하라고 나와 있음. + +--- + +## **3. 하드웨어 가상화 지원 여부에 따라 libvirt 설정** + +**공식: “Finalize installation” 1번** + +compute1에서: + +``` +egrep -c '(vmx|svm)' /proc/cpuinfo +``` + +- **결과 ≥ 1**: CPU가 VT-x/AMD-V 지원 + + → 보통 KVM을 그대로 쓸 수 있고, virt_type = kvm (기본값)이라 **추가 설정 필요 없음**. + +- **결과 0**: 하드웨어 가상화 미지원 or Proxmox에서 nested 안 켜짐 + + → libvirt 를 QEMU 모드로 바꿔야 함. + + +하드웨어 가속 안 되는 경우: + +``` +vi /etc/nova/nova-compute.conf +``` + +``` +[libvirt] +# ... +virt_type = qemu +``` + +→ 이렇게 설정하면 pure QEMU로 돌아가는데, **느리긴 해도 PoC/랩엔 충분**함. + +(너네 Proxmox에서 nested KVM 켜놨으면 아마 1 이상 뜰 가능성이 큼.) + +--- + +## **4. nova-compute 서비스 재시작 (compute1)** + +**공식: “Restart the Compute service”** + +``` +systemctl restart nova-compute +systemctl enable nova-compute +systemctl status nova-compute +``` + +- active (running) 이면 일단 OK. +- 만약 failed 상태면 /var/log/nova/nova-compute.log 에서 에러 확인. + - 문서 예시 에러: AMQP server on controller:5672 is unreachable + + → RabbitMQ 포트/방화벽/비번 체크. + + +--- + +## **5. controller에서 cell에 compute 호스트 등록** + +**공식: “Add the compute node to the cell database”** + +> 주의: +> +> +> **꼭 controller에서** +> + +### **5-1. nova-compute 서비스 올라왔는지 확인** + +컨트롤러에서: + +``` +source /root/admin-openrc.sh + +openstack compute service list --service nova-compute +``` + +예상 결과(비슷한 형식): + +![image.png](images/ch2_4_img_11.png) + +- Host 가 compute +- State = up, Status = enabled 이면 잘 붙은 것. + +--- + +### **5-2. cell_v2 에 compute1 등록** + +컨트롤러에서 root: + +``` +su -s /bin/sh -c "nova-manage cell_v2 discover_hosts --verbose" nova +``` + +문서 예시 출력: + +``` +Found 2 cell mappings. +Skipping cell0 since it does not contain hosts. +Getting compute nodes from cell 'cell1': ad5a5985-a719-4567-98d8-8d148aaae4bc +Found 1 computes in cell: ad5a5985-a719-4567-98d8-8d148aaae4bc +Checking host mapping for compute host 'compute1': fe58ddc1-... +Creating host mapping for compute host 'compute1': fe58ddc1-... +``` + +![image.png](images/ch2_4_img_12.png) + +이와 같이 “host mapping 생성” 로그가 출력되면 cell 등록이 완료된 상태이다. + +> 새 compute 노드를 추가할 때마다 이 명령을 다시 실행해야 하며, 자동화를 원하면 `/etc/nova/nova.conf` 의 `[scheduler]` 섹션에 `discover_hosts_in_cells_interval = 300` 을 설정해 주기적으로 discover 하도록 구성할 수 있다. +> + +``` +[scheduler] +discover_hosts_in_cells_interval = 300 +``` + +(이건 선택 옵션) + +--- + +## **6. 현재 구성 상태 요약** + +지금까지 한 걸 한 줄로 요약하면: + +- 컨트롤러: + - Nova API/스케줄러/컨덕터/novncproxy + Placement + Glance + Keystone 정상 + - cell1 설정 완료 +- 컴퓨트: + - nova-compute@compute1 가 up 상태 + - Placement에 자원 보고할 준비 완료 +- controller에서: + +``` +source /root/admin-openrc.sh +openstack compute service list +openstack hypervisor list +``` + +이 두 출력에 `compute1` 이 보이면 **Nova 레벨 준비가 완료된 상태**이다. + +https://docs.openstack.org/nova/2025.1/install/verify.html + +문서 기준으로 Nova 설치를 확인한다. + +다음 핵심 단계는 **Neutron 설치 + 네트워크 구성(Provider / Self-service)** 이다. + +그게 끝나면: + +1. 네트워크 만들고 +2. flavor / keypair 만들고 +3. 우리가 Glance에 올린 cirros 이미지로 +4. `openstack server create ...` 를 실행해 실제 인스턴스 부팅까지 진행한다. diff --git a/lectures/ch2/ch2_4_21_lec.qmd b/lectures/ch2/ch2_4_21_lec.qmd new file mode 100644 index 0000000..0057da9 --- /dev/null +++ b/lectures/ch2/ch2_4_21_lec.qmd @@ -0,0 +1,1326 @@ +--- +title: "2-4장. Neutron 설치" +--- + +# Neutron + + 이번 절에서는 핵심 네트워크 서비스인 **Neutron**을 체계적으로 정리한다. + +질문대로 **공식 문서 흐름 그대로** 해서: + +1. 개요 / 아키텍처 +2. Neutron 개념 +3. Ubuntu 기준 설치 흐름 (컨트롤러 / 컴퓨트) +4. 설치 완료 후 검증 (Provider / Self-service 둘 다) + +로 풀어볼게. + +--- + +## **1. Neutron 설치 가이드 전체 구조** + +Neutron 설치 가이드는 이렇게 생겼어: + +- **Overview** +- **Networking service overview** +- **Networking (neutron) concepts** +- **Install and configure for Ubuntu** + - Host networking + - Install & configure controller node + - Install & configure compute node + - Verify operation (Option 1: Provider / Option 2: Self-service) + +우리가 지금까지 한 OpenStack 설치 가이드랑 똑같이, + +**“교육용 2노드 PoC 아키텍처”** 기준이다. Production용이 아니라는 점도 문서에 명시되어 있다. + +--- + +## **2. Overview: 예제 아키텍처 + 네트워크 옵션** + +### **2-1. 예제 아키텍처 (컨트롤러 / 컴퓨트 / 스토리지)** + +Overview에서 설명하는 “예제 아키텍처”는 현재 실습 구조와 거의 1:1로 대응된다: + +- **Controller** + - Keystone, Glance + - Nova의 API/스케줄러/컨덕터/novncproxy + - Neutron 서버 + 여러 에이전트(L3, DHCP, metadata 등) + - DB, MQ, Memcached, NTP 등 공통 인프라 +- **Compute** + - KVM 하이퍼바이저 부분 (nova-compute) + - 인스턴스에 붙는 Neutron L2 agent (보통 linuxbridge/OVS/OVN) +- (옵션) Block / Object Storage 노드 + +그리고 PoC 아키텍처의 특징 두 가지를 강조한다: + +1. 네트워크 에이전트가 별도 네트워크 노드가 아니라 **컨트롤러에 합쳐져 있음** +2. self-service overlay(VXLAN 등) 트래픽이 **전용 “데이터 네트워크”가 아니라 management 네트워크를 같이 씀** + +→ 현재 Proxmox에서 단일 NIC를 vmbr0에 연결한 구조와 같은 맥락으로 이해할 수 있다. + +--- + +### **2-2. 두 가지 네트워크 옵션** + +Overview 맨 아래에 **Option 1 / Option 2** 두 개가 있어: + +### **Option 1: Provider networks** + +- 최대한 심플하게: + - Neutron은 거의 L2 브릿지 + VLAN만 담당 + - L3 라우팅은 **물리 스위치/라우터**가 함 +- 특징: + - 인스턴스 네트워크가 물리망에 거의 바로 붙음 + - self-service(테넌트 private) 네트워크 없음 + - Floating IP, NAT, Router 같은 L3 기능 / FWaaS 같은 advanced 기능 제한 +- 장점: 개념 단순, 트래픽 디버깅 쉬움 +- 단점: 클라우드스러움(테넌트 독립 네트워크, 라우터 등)이 떨어짐 + +### **Option 2: Self-service networks** + +- Option 1에 **L3 Router + Overlay(VXLAN)** 를 얹은 버전 +- 구조: + - Provider network : 외부(물리)랑 이어지는 네트워크 (보통 flat/VLAN) + - Self-service network : 테넌트 내부에서 만드는 private 네트워크 (VXLAN 등) + - Router : self-service ↔ provider 사이에 NAT + 라우팅 +- 장점: + - 프로젝트마다 네트워크/서브넷/라우터 자유롭게 생성 + - Floating IP, FWaaS, LBaaS 같은 기능까지 확장 가능 +- 단점: + - 구조 복잡, 디버깅 난이도 ↑ +- *스터디 목적 + “실제 OpenStack 흐름 이해”** 기준이라면 Option 2가 더 적합하다. + +우리는 Nova/Glance까지 이미 self-service 기준으로 따라왔으니까, + +**Neutron도 Option 2 기준으로 이해하는 걸 기본으로 둘게.** + +--- + +## **3. Networking service overview: Neutron이 하는 일** + +이 페이지는 한 줄로 이렇게 시작해: + +> “OpenStack Networking(Neutron)은 다른 OpenStack 서비스가 관리하는 인터페이스들을 네트워크에 붙이게 해준다.” +> + +조금 풀어보면: + +- Neutron은 **“Virtual Network Infra (VNI) + 물리 네트워크 Access Layer”** 를 관리하는 서비스이다. +- Nova, Cinder 같은 서비스가 “포트 하나 만들어줘”, “VM NIC 붙여줘” 하면 + + → Neutron이 VLAN/VXLAN/브릿지/라우터/DHCP 세팅 담당. + + +구성 요소로는: + +1. **neutron-server** + - REST API 받는 메인 프로세스 + - API 요청을 적절한 플러그인/드라이버에게 넘김 (ML2, OVN 등) +2. **플러그인 + 에이전트들** + - L2 에이전트(OVS, Linuxbridge, OVN 등) + - L3 에이전트 (라우팅/NAT) + - DHCP 에이전트 (서브넷의 IP 할당) + - 기타 vendor-specific 에이전트 + - 작업: 포트 plug/unplug, 네트워크/서브넷 생성, IP 할당 등 +3. **Message Queue (RabbitMQ 등)** + - neutron-server ↔ 각 agent 사이에 메시지를 중계 + - 일부 플러그인은 MQ를 내부 상태 “DB”처럼 함께 사용하기도 함 +4. **연동 서비스** + - Nova: 인스턴스 NIC port 관리 + - Keystone: 인증/엔드포인트 + - Placement: (고급 네트워킹에서 리소스 표현) + - 기타: LBaaS, FWaaS 등 확장 서비스 + +--- + +## **4. Networking (neutron) concepts: 핵심 개념들** + +이 페이지는 약간 “네트워크 입문서”에 가까움. 포인트만 뽑으면: + +1. **VNI + PNI** + - Neutron은: + - VNI (Virtual Networking Infrastructure): VXLAN, VLAN, 가상 스위치/라우터 등 + - PNI (Physical Networking Infrastructure)의 access layer: VLAN tag, 외부망 uplink 등 + + 둘 다를 조정해서 “OpenStack에서의 네트워크”를 완성. + +2. **Network / Subnet / Router** + - 네트워크: L2 broadcast domain (가상 스위치) + - 서브넷: 그 위에서 쓰는 IP 대역 + - 라우터: + - 여러 서브넷을 L3로 연결 + - Gateway 인터페이스는 external network에 붙음 + - 동작은 물리 네트워크랑 똑같은 모델. +3. **External vs Internal network** + - **External network**: + - 단순 가상 네트워크가 아니라, + - 실제 물리 외부망(예: 학교/집 라우터 뒷단)의 “슬라이스”를 표현 + - 여기 IP를 floating IP 등으로 할당하면 외부에서 접근 가능 + - **Internal/self-service network**: + - VXLAN 같은 overlay로 만들어지는 테넌트 전용 private 네트워크 + - VM끼리는 직접 통신, 외부와 통신하려면 Router+NAT 필요 +4. **Router + Floating IP** + - 라우터: + - 인터널 네트워크 인터페이스들 + - 외부 네트워크에 Gateway 하나 + - Floating IP: + - external network의 IP를 internal 쪽 포트와 “1:1 매핑” + - 외부에서 VM에 접속할 때 이 주소로 들어옴 +5. **Port** + - “무언가가 subnet에 연결된 것” = Port + - VM NIC, 로드밸런서 VIP, 라우터 인터페이스 등 + - Floating IP는 이 포트에 붙는 것 +6. **Security Group** + - 논리적인 방화벽 rule set + - 인스턴스는 여러 SG에 속할 수 있고, + + → inbound/outbound 포트, IP, 프로토콜 단위로 필터링. + + +--- + +## **5. Install & configure for Ubuntu: 큰 흐름** + +Ubuntu 설치 파트 목차는 이렇게 구성돼 있어: + +1. Host networking + - Controller node + - Compute node + - (Optional) Block storage node + - Verify connectivity +2. Install & configure controller node + - Prerequisites (DB, Keystone 인증, endpoint) + - Configure networking options (Option 1 / 2) + - Configure metadata agent + - Nova가 Neutron을 쓰도록 설정 + - Finalize (서비스 재시작) +3. Install & configure compute node + - 패키지 설치 + - 공통 부분(neutron.conf) 설정 + - 네트워크 옵션에 맞는 L2 agent 설정 + - Nova-compute에 Neutron 연동 +4. Verify operation + - Option 1: Provider networks + - Option 2: Self-service networks + +실제 명령/파일은 Queens/Train 시절부터 거의 안 바뀌고, + +Epoxy에서도 구조는 동일해. + +아래는 “**어떤 단계에서 무슨 일을 왜 하는지**” 기준으로 정리한 거고, + +이미 컨트롤러/컴퓨트 네트워크를 구성해 둔 상태라 + +**Host networking**은 핵심 항목 위주로 확인해도 된다. + +--- + +## **6. Host networking (Ubuntu) – 공식 가이드가 요구하는 것** + +### **6-1. Controller host networking** + +가이드의 Host networking에서 요구하는 요점: + +- 컨트롤러에 최소 2 NIC (mgmt + provider) + + → 현재 실습은 “VM NIC 2개 + Proxmox 브리지 1개” 방식으로 통합 구성되어 있다. + +- management NIC: + - DB/MQ/API 통신용 + - IPv4 고정 IP (예: 10.100.100.11) +- provider NIC: + - 외부 네트워크와 브릿지 + - Option 2에서 external network의 uplink 용 + +이미 너가: + +- controller: my_ip = 10.100.100.11 +- compute1: my_ip = 10.100.100.31 + + 로 Nova까지 맞춰놓은 상태라, 여기서 요구하는 건 이미 충족한 셈. + + +--- + +### **6-2. Compute host networking** + +가이드는 compute도 동일하게 최소 2 NIC를 요구해: + +- management NIC: 컨트롤러와 통신 +- provider NIC: 인스턴스에서 외부로 나가는 트래픽 브릿지/OVS에 붙음 + +너는 지금 둘 다 vmbr0에 연결해둔 상태인데, + +이건 “물리 1 NIC + 가상 NIC 2개” 구조라서 논리적으로 동일한 구성으로 볼 수 있다. + +결론: Host networking 챕터는 **“IP/브릿지/라우팅 잘 잡았냐 체크용”**이라, + +지금까지만 잘 동작하면 넘어가도 된다. + +--- + +## **7. Controller node: Neutron 설치 및 설정 흐름** + +### **7-1. Prerequisites (DB + Keystone + Endpoint)** + +컨트롤러에서 먼저 해야 할 것들: + +1. **neutron DB 생성** + +``` +CREATE DATABASE neutron; + +GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' + IDENTIFIED BY 'NEUTRON_DBPASS'; +GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' + IDENTIFIED BY 'NEUTRON_DBPASS'; +``` + +- NEUTRON_DBPASS = Neutron DB용 별도 비번. +1. **Keystone에 neutron 유저/서비스/엔드포인트 등록** + +``` +openstack user create --domain default --password-prompt neutron +openstack role add --project service --user neutron admin + +openstack service create --name neutron \ + --description "OpenStack Networking" network + +openstack endpoint create --region RegionOne \ + network public http://controller:9696 + +openstack endpoint create --region RegionOne \ + network internal http://controller:9696 + +openstack endpoint create --region RegionOne \ + network admin http://controller:9696 +``` + +- 여기서 NEUTRON_PASS = Keystone용 neutron 유저 비번. + +![image.png](images/ch2_4_img_13.png) + +![image.png](images/ch2_4_img_14.png) + +--- + +### **7-2. Neutron 서버/에이전트 패키지 설치** + +Option 2(self-service) + ML2 + Linuxbridge 예제로 보면, 컨트롤러에서 보통: + +``` +apt install \ + neutron-server neutron-plugin-ml2 \ + neutron-linuxbridge-agent neutron-l3-agent \ + neutron-dhcp-agent neutron-metadata-agent +``` + +- neutron-server: API 서버 +- neutron-plugin-ml2: ML2 플러그인 +- linuxbridge-agent: L2 에이전트 +- l3-agent: Router/NAT +- dhcp-agent: 각 self-service subnet 에서 DHCP 제공 +- metadata-agent: 인스턴스 메타데이터 전달 + +(OVS/OVN 쓸 거면 여기서 패키지가 조금 달라짐) + +--- + +### **7-3.** + +### **/etc/neutron/neutron.conf** + +### **– 공통 설정** + +핵심 섹션: + +1. [database] – neutron DB 연결 + +``` +[database] +connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron +``` + +1. [DEFAULT] – 코어 플러그인, 서비스 플러그인, RabbitMQ 등 + +``` +[DEFAULT] +core_plugin = ml2 +service_plugins = router +allow_overlapping_ips = true + +transport_url = rabbit://openstack:RABBIT_PASS@controller + +notify_nova_on_port_status_changes = true +notify_nova_on_port_data_changes = true +``` + +1. [keystone_authtoken] – Keystone 인증 + +``` +[keystone_authtoken] +www_authenticate_uri = http://controller:5000 +auth_url = http://controller:5000 +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = neutron +password = NEUTRON_PASS +``` + +1. [nova] – Nova에게 포트 변경 알림 줄 때 사용할 인증 정보 + +``` +[nova] +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = nova +password = NOVA_PASS +``` + +--- + +### **7-4. ML2 플러그인 설정 (** + +### **/etc/neutron/plugins/ml2/ml2_conf.ini** + +### **)** + +Option 2 + Linuxbridge 예시: + +``` +[ml2] +type_drivers = flat,vlan,vxlan +tenant_network_types = vxlan +mechanism_drivers = linuxbridge +extension_drivers = port_security + +[ml2_type_flat] +flat_networks = provider + +[ml2_type_vxlan] +vni_ranges = 1:1000 + +[securitygroup] +enable_ipset = true +``` + +- tenant_network_types = vxlan → self-service 네트워크는 VXLAN으로 만든다. +- flat_networks = provider → provider 네트워크를 “flat” 타입으로 정의 (physical_net 이름이 provider). +- 나중에 네트워크 만들 때 --provider:physical_network provider 라고 쓸 수 있음. + +--- + +### **7-5. L2 agent (Linuxbridge) 설정 (** + +### **linuxbridge_agent.ini** + +### **)** + +예시: + +``` +[linux_bridge] +physical_interface_mappings = provider:PROVIDER_NIC_NAME + +[vxlan] +enable_vxlan = true +local_ip = MANAGEMENT_IP_OF_CONTROLLER +l2_population = true + +[securitygroup] +enable_security_group = true +firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver +``` + +- PROVIDER_NIC_NAME → external/provider로 쓸 NIC/브릿지 이름 (너는 vm NIC 2개를 vmbr0에 물려뒀으니, 거기 맞게 설정) +- local_ip → VXLAN 터널에서 사용할 IP (보통 management IP = 10.100.100.11) + +--- + +### **7-6. L3 agent (** + +### **l3_agent.ini** + +### **) / DHCP agent (** + +### **dhcp_agent.ini** + +### **)** + +- l3_agent.ini 에는 interface_driver = linuxbridge / external_network_bridge = (보통 비워둠) +- dhcp_agent.ini 에는 interface_driver / dhcp_driver / enable_isolated_metadata 등. + +(문서 그대로 옮기면 되는데, 개념은 “라우터용 네임스페이스 인터페이스 드라이버 + DHCP 설정” 정도.) + +--- + +### **7-7. Metadata agent (** + +### **metadata_agent.ini** + +### **)** + +Self-service에서 **VM → 메타데이터 → Nova** 흐름을 위해: + +``` +[DEFAULT] +nova_metadata_host = controller +metadata_proxy_shared_secret = METADATA_SECRET +``` + +- METADATA_SECRET = 임의의 문자열 (Nova [neutron] 섹션에서도 동일하게 써야 함) + +--- + +### **7-8. Nova가 Neutron을 쓰도록 설정 (컨트롤러의** + +### **/etc/nova/nova.conf** + +### **)** + +이미 Nova 컨트롤러 설치 때 이 부분 얘기했는데, Neutron 가이드에서도 다시 한 번 강조함: + +``` +[neutron] +url = http://controller:9696 +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = neutron +password = NEUTRON_PASS + +service_metadata_proxy = true +metadata_proxy_shared_secret = METADATA_SECRET +``` + +- service_metadata_proxy = true + METADATA_SECRET = metadata agent와 Nova 사이의 shared secret. + +설정 바꾸고 나면: + +``` +service nova-api restart +``` + +--- + +### **7-9. 서비스 재시작 (컨트롤러)** + +문서에서는 이런 느낌으로 마무리해: + +``` +service neutron-server restart +service neutron-linuxbridge-agent restart +service neutron-dhcp-agent restart +service neutron-metadata-agent restart +service neutron-l3-agent restart +``` + +(또는 systemctl restart ...) + +여기까지가 **컨트롤러 쪽 Neutron 완전체**. + +--- + +## **8. Compute node: Neutron 설치 흐름** + +Compute node는 “인스턴스의 L2 연결 + SG” 담당. + +### **8-1. 패키지 설치** + +linuxbridge 기준에서: + +``` +apt install -y neutron-linuxbridge-agent +``` + +(OVS 사용 시 neutron-openvswitch-agent) + +--- + +### **8-2.** + +### **/etc/neutron/neutron.conf** + +### **– 공통 설정** + +컨트롤러와 거의 동일하지만, DB는 안 쓰고 MQ/Keystone만: + +``` +[DEFAULT] +transport_url = rabbit://openstack:RABBIT_PASS@controller +core_plugin = ml2 +service_plugins = router +allow_overlapping_ips = true + +[keystone_authtoken] +... (컨트롤러와 동일, NEUTRON_PASS) + +[nova] +... (컨트롤러와 동일, NOVA_PASS) +``` + +--- + +### **8-3.** + +### **linuxbridge_agent.ini** + +### **– compute용** + +컨트롤러와 거의 같지만, local_ip / physical_interface_mappings 부분만 compute1에 맞게: + +``` +[linux_bridge] +physical_interface_mappings = provider:PROVIDER_NIC_NAME + +[vxlan] +enable_vxlan = true +local_ip = 10.100.100.31 # compute1 management IP +l2_population = true + +[securitygroup] +enable_security_group = true +firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver +``` + +설정 후: + +``` +systemctl restart neutron-linuxbridge-agent +systemctl enable neutron-linuxbridge-agent +``` + +--- + +### **8-4. Nova compute에서 Neutron 연동 확인** + +/etc/nova/nova.conf 의 [neutron] 섹션은 컨트롤러와 동일해야 해: + +``` +[neutron] +url = http://controller:9696 +auth_url = http://controller:5000 +... +project_name = service +username = neutron +password = NEUTRON_PASS +service_metadata_proxy = true +metadata_proxy_shared_secret = METADATA_SECRET +``` + +수정 후: + +``` +systemctl restart nova-compute +``` + +--- + +## **9. 설치 후 확인 (Verify operation)** + +문서에서는 **Option 1 / 2 별로 “검증 시나리오”**를 나눠서 보여줘. + +### **9-1. 공통 기초 체크** + +컨트롤러에서: + +``` +source /root/admin-openrc.sh + +openstack network agent list +``` + +- L3 agent, DHCP agent, linuxbridge-agent(controller/compute), metadata-agent 가 모두 alive / Up 인지 확인. + +--- + +### **9-2. Option 1: Provider networks 검증** + +(간단히만 정리) + +1. Provider 네트워크 생성: + +``` +openstack network create --share --external \ + --provider-network-type flat \ + --provider-physical-network provider provider +``` + +1. Provider 서브넷: + +``` +openstack subnet create --network provider \ + --allocation-pool start=EXTERNAL_START_IP,end=EXTERNAL_END_IP \ + --dns-nameserver 8.8.8.8 \ + --gateway EXTERNAL_GW \ + --subnet-range EXTERNAL_CIDR provider-subnet +``` + +1. 인스턴스 생성: +- Nova/Glance 준비된 상태에서 --nic net-id= 로 인스턴스 생성 +- 보안 그룹에 ICMP/SSH 허용 +1. 외부에서 ping/ssh 테스트 + +--- + +### **9-3. Option 2: Self-service networks 검증 (우리가 갈 방향)** + +공식 설치가이드의 “Self-service networks” 챕터랑 흐름이 거의 동일해: + +1. **External(provider) 네트워크 + 서브넷** (위와 동일) +2. **프로젝트 전용 self-service 네트워크 + 서브넷 생성** + +``` +# demo 프로젝트 환경 로드 +source demo-openrc.sh # 또는 admin에서 project 지정 + +openstack network create selfservice +openstack subnet create --network selfservice \ + --dns-nameserver 8.8.8.8 \ + --subnet-range 10.0.0.0/24 selfservice-subnet +``` + +1. **Router 생성 + 인터페이스/게이트웨이 설정** + +``` +openstack router create router1 + +# router와 selfservice 네트워크 연결 +openstack router add subnet router1 selfservice-subnet + +# provider 네트워크로 게이트웨이 지정 +openstack router set router1 --external-gateway provider +``` + +1. **보안 그룹 / 키페어 / 인스턴스 생성** + +``` +openstack security group rule create --proto icmp default +openstack security group rule create --proto tcp --dst-port 22 default + +openstack keypair create demo-key > demo-key.pem +chmod 600 demo-key.pem + +openstack server create --flavor m1.small \ + --image cirros \ + --nic net-id=$(openstack network show selfservice -f value -c id) \ + --security-group default \ + --key-name demo-key demo-instance +``` + +1. **Floating IP 할당** + +``` +openstack floating ip create provider +openstack server add floating ip demo-instance FLOATING_IP +``` + +1. **연결 테스트** +- 외부에서 ping FLOATING_IP +- ssh cirros@FLOATING_IP (cirros는 기본 계정/비번) + +해당 단계까지 완료되면 **Neutron + Nova + Glance + Keystone 전체 경로가 완성된 상태이다.** + +--- + +## **10. 요약** + +정리하면, Neutron 설치 가이드는: + +- **개요(Overview)**: + - 2노드 PoC 아키텍처, 컨트롤러/컴퓨트 롤, + - Provider vs Self-service 옵션 정의 +- **서비스/개념**: + - neutron-server + 플러그인/에이전트 구조, + - Network/Subnet/Router/Port/Floating IP/Security Group 개념 잡기 +- **Ubuntu 설치**: + - Host networking (이미 대부분 구성한 부분) + - Controller: neutron DB/Keystone/Endpoint → 패키지 → neutron.conf + ml2_conf.ini + 각 agent 설정 → nova.conf [neutron] → 서비스 재시작 + - Compute: L2 agent + neutron.conf 공통부분 → ml2 에이전트 설정 → nova-compute [neutron] 설정 → 서비스 재시작 +- **Verify**: + - openstack network agent list로 agent 상태 확인 + - Provider / Self-service 시나리오별로 네트워크/서브넷/라우터/인스턴스 생성 및 ping/ssh 검증 + +이제 여기서부터는 + +**“우리가 만든 2노드 Proxmox 구조 + self-service Option 2 + ML2+Linuxbridge(or OVS)”** 기준으로 + +실제 파일들(/etc/neutron/*.conf, /etc/nova/nova.conf)을 하나씩 채워 넣는 단계로 넘어가면 돼. + +원하면 다음 턴에서: + +- “우리 지금 IP/브리지 상황 기준으로 Neutron 컨트롤러 설정을 실제 파일 예시로 상세히 써줘” +- “Self-service용 provider/self-service 네트워크/라우터를 CLI로 만드는 스크립트 템플릿 만들어줘” + +같은 방식으로 실전 설정 예시를 이어서 정리한다 + + 이제 Neutron 실전 설정 단계로 진행한다. + +(Option 2: self-service 네트워크 + ML2 + Linuxbridge 기준으로 갈게.) + +아래는 **“현재 실습 환경(IP, 노드 구성)”에 맞춘 실제 config 예시 + 네트워크 생성 스크립트**이다. + +--- + +## **0. 전제 정리 (이 기준으로 설정 파일을 작성한다)** + +- 컨트롤러 노드 + - 호스트명: controller + - 관리 IP: 10.100.100.11 +- 컴퓨트 노드 + - 호스트명: compute1 + - 관리 IP: 10.100.100.31 +- RabbitMQ + - 유저: openstack + - 패스워드: RABBIT_PASS (실습 환경에서 정한 값) +- DB + - neutron DB: neutron + - DB 유저: neutron + - DB 패스워드: NEUTRON_DBPASS +- Keystone + - neutron 유저: neutron / 패스: NEUTRON_PASS + - nova 유저: nova / 패스: NOVA_PASS +- Metadata shared secret + - METADATA_SECRET = meta-secret-123 라고 예시로 둘게 (원하는 값으로 바꿔도 됨) +- NIC 이름 + - 관리 NIC: ens18 (172.32.0.x 물린 것) + - provider NIC: ens19 (IP 없이 up 상태인 NIC) + + 실제 NIC 이름이 다르면 해당 부분만 수정하면 된다. + + +--- + +## **1. 컨트롤러: Neutron 설치 순서 & 설정 파일들** + +### **1-1. 패키지 설치 (이미 안 했다면)** + +``` +sudo -i + +apt update +apt install -y \ + neutron-server neutron-plugin-ml2 \ + neutron-linuxbridge-agent neutron-l3-agent \ + neutron-dhcp-agent neutron-metadata-agent +``` + +--- + +### **1-2.** + +### **/etc/neutron/neutron.conf** + +### **(controller)** + +핵심 섹션만 정리한다. 나머지 기본값은 유지하면 된다. + +``` +[DEFAULT] +core_plugin = ml2 +service_plugins = router +allow_overlapping_ips = true + +transport_url = rabbit://openstack:RABBIT_PASS@controller + +# 로그 경로 강제로 안 박는 게 좋음 (문서에서도 log_dir 제거하라고 함) +# log_dir = /var/log/neutron # 필요하면 사용 + +# Neutron이 참조하는 auth 방식 +auth_strategy = keystone + +[database] +connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron + +[keystone_authtoken] +www_authenticate_uri = http://controller:5000 +auth_url = http://controller:5000 +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = neutron +password = NEUTRON_PASS + +[nova] +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = nova +password = NOVA_PASS + +[oslo_concurrency] +lock_path = /var/lib/neutron/tmp +``` + +- RABBIT_PASS, NEUTRON_DBPASS, NEUTRON_PASS, NOVA_PASS 는 실제 사용 중인 값으로 치환한다. + +--- + +### **1-3.** + +### **/etc/neutron/plugins/ml2/ml2_conf.ini** + +### **(ML2 + vxlan self-service)** + +``` +[ml2] +type_drivers = flat,vlan,vxlan +tenant_network_types = vxlan +mechanism_drivers = linuxbridge +extension_drivers = port_security + +[ml2_type_flat] +flat_networks = provider + +[ml2_type_vxlan] +vni_ranges = 1:1000 + +[securitygroup] +enable_ipset = true +``` + +- flat_networks = provider → provider라는 이름의 물리 네트워크로 flat 네트워크를 매핑. +- self-service 네트워크는 VXLAN으로 생성. + +--- + +### **1-4.** + +### **/etc/neutron/plugins/ml2/linuxbridge_agent.ini** + +### **(controller)** + +``` +[linux_bridge] +# provider 라는 물리 네트워크 이름을 ens19 NIC에 매핑 +physical_interface_mappings = provider:ens19 + +[vxlan] +enable_vxlan = true +local_ip = 10.100.100.11 +l2_population = true + +[securitygroup] +enable_security_group = true +firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver +``` + +- ens19 → controller에서 provider 용으로 쓸 NIC (IP 없는 그 NIC). +- local_ip = 터널용 IP (management IP 그대로) + +--- + +### **1-5.** + +### **/etc/neutron/l3_agent.ini** + +``` +[DEFAULT] +interface_driver = linuxbridge +external_network_bridge = +agent_mode = legacy +``` + +- external_network_bridge 는 빈 값 → 별도 브릿지 이름 사용 안 함. +- interface_driver = linuxbridge → 라우터 네임스페이스에서 linuxbridge 사용. + +--- + +### **1-6.** + +### **/etc/neutron/dhcp_agent.ini** + +``` +[DEFAULT] +interface_driver = linuxbridge +dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq +enable_isolated_metadata = true +``` + +- self-service 네트워크에 DHCP 제공. + +--- + +### **1-7.** + +### **/etc/neutron/metadata_agent.ini** + +``` +[DEFAULT] +nova_metadata_host = controller +metadata_proxy_shared_secret = METADATA_SECRET +``` + +- METADATA_SECRET는 아래 Nova 설정과 반드시 동일해야 함. + +--- + +### **1-8. 컨트롤러** + +### **/etc/nova/nova.conf** + +### **의 [neutron] 섹션** + +이미 어느 정도 써놨겠지만, Neutron 가이드 기준으로 맞춰주면: + +``` +[neutron] +url = http://controller:9696 +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = neutron +password = NEUTRON_PASS + +service_metadata_proxy = true +metadata_proxy_shared_secret = METADATA_SECRET +``` + +수정 후: + +``` +service nova-api restart +``` + +--- + +### **1-9. 컨트롤러 Neutron 서비스 재시작** + +``` +service neutron-server restart +service neutron-linuxbridge-agent restart +service neutron-dhcp-agent restart +service neutron-metadata-agent restart +service neutron-l3-agent restart +``` + +systemctl status ... 로 다 active (running) 인지만 한 번 확인. + +--- + +## **2. 컴퓨트 노드: Neutron 설정 파일** + +### **2-1. 패키지 설치** + +``` +sudo -i +apt update +apt install -y neutron-linuxbridge-agent +``` + +--- + +### **2-2.** + +### **/etc/neutron/neutron.conf** + +### **(compute1)** + +컨트롤러보단 단순하지만, MQ/Keystone/Nova 섹션은 같이 맞춰줘야 한다. + +``` +[DEFAULT] +transport_url = rabbit://openstack:RABBIT_PASS@controller +core_plugin = ml2 +service_plugins = router +allow_overlapping_ips = true +auth_strategy = keystone + +[keystone_authtoken] +www_authenticate_uri = http://controller:5000 +auth_url = http://controller:5000 +memcached_servers = controller:11211 +auth_type = password +project_domain_name = Default +user_domain_name = Default +project_name = service +username = neutron +password = NEUTRON_PASS + +[nova] +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = nova +password = NOVA_PASS + +[oslo_concurrency] +lock_path = /var/lib/neutron/tmp +``` + +--- + +### **2-3.** + +### **/etc/neutron/plugins/ml2/linuxbridge_agent.ini** + +### **(compute1)** + +``` +[linux_bridge] +physical_interface_mappings = provider:ens19 + +[vxlan] +enable_vxlan = true +local_ip = 10.100.100.31 +l2_population = true + +[securitygroup] +enable_security_group = true +firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver +``` + +- ens19 → compute1에서 provider망에 붙인 NIC +- local_ip = compute1 관리 IP + +--- + +### **2-4. 컴퓨트** + +### **/etc/nova/nova.conf** + +### **의 [neutron]** + +컨트롤러랑 완전히 똑같이 맞추자: + +``` +[neutron] +url = http://controller:9696 +auth_url = http://controller:5000 +auth_type = password +project_domain_name = Default +user_domain_name = Default +region_name = RegionOne +project_name = service +username = neutron +password = NEUTRON_PASS + +service_metadata_proxy = true +metadata_proxy_shared_secret = METADATA_SECRET +``` + +--- + +### **2-5. 서비스 재시작 (compute1)** + +``` +systemctl restart neutron-linuxbridge-agent +systemctl restart nova-compute + +systemctl enable neutron-linuxbridge-agent +``` + +--- + +## **3. 설치 검증 1단계 – 에이전트 상태 확인** + +컨트롤러에서: + +``` +source /root/admin-openrc.sh + +openstack network agent list +``` + +정상 예시 느낌: + +``` ++--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ +| ID | Agent Type | Host | Availability Zone | Alive | State | Binary | ++--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ +| ... | DHCP agent | controller | nova | :-) | UP | neutron-dhcp-agent | +| ... | Linux bridge agent | controller | None | :-) | UP | neutron-linuxbridge-agent| +| ... | Linux bridge agent | compute1 | None | :-) | UP | neutron-linuxbridge-agent| +| ... | L3 agent | controller | nova | :-) | UP | neutron-l3-agent | +| ... | Metadata agent | controller | None | :-) | UP | neutron-metadata-agent | ++--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ +``` + +모두 Alive = :-), State = UP 면 최소 세팅은 성공. + +--- + +## **4. Self-service 네트워크 구성용 스크립트 템플릿** + +다음 단계는 **provider + self-service + router + floating IP 구성**이다. + +### **4-1. 전제 (예시 값)** + +외부(provider) 네트워크는 실습 환경 기준으로 다음과 같이 가정한다. (필요 시 수정) + +- external/provider 물리 대역: 10.100.100.0/24 (지금 vmbr0 대역) +- 외부 gateway: 10.100.100.1 (집/랩 라우터 or Proxmox 게이트웨이) +- Floating IP 풀: 172.32.200.100 ~ 172.32.200.200 (예시) + +이건 환경에 따라 다르므로 **나중에 정확한 external 대역으로 바꾸면 된다.** + +--- + +### **4-2. admin용 네트워크 생성 스크립트 (provider + router까지)** + +컨트롤러에서 `/root/neutron-setup-admin.sh` 형태로 작성할 수 있는 템플릿은 다음과 같다: + +``` +#!/bin/bash +set -e + +source /root/admin-openrc.sh + +# 1) Provider/external 네트워크 생성 +openstack network create provider \ + --share \ + --external \ + --provider-network-type flat \ + --provider-physical-network provider + +# 2) Provider 서브넷 생성 +openstack subnet create provider-subnet \ + --network provider \ + --subnet-range 172.32.200.0/24 \ + --allocation-pool start=172.32.200.100,end=172.32.200.200 \ + --gateway 172.32.200.1 \ + --dns-nameserver 8.8.8.8 +``` + +> 주의: 여기의 `172.32.200.0/24`, 게이트웨이, allocation pool 값은 +> +> +> **현재 외부망 환경에 맞게 반드시 수정해야 한다.** +> + +> 만약 외부망이 192.168.0.0/24라면 그걸로 교체. +> + +실행: + +``` +chmod +x /root/neutron-setup-admin.sh +/root/neutron-setup-admin.sh +``` + +--- + +### **4-3. demo 프로젝트용 self-service 네트워크 + 라우터 스크립트** + +demo 프로젝트가 있다 치고 (없으면 admin으로 해도됨): + +``` +#!/bin/bash +set -e + +# demo-openrc가 있다면: +# source /root/demo-openrc.sh +# 없으면 admin으로 계속 해도 테스트는 가능 + +source /root/admin-openrc.sh + +# 1) self-service 네트워크 + 서브넷 +openstack network create selfservice + +openstack subnet create selfservice-subnet \ + --network selfservice \ + --subnet-range 10.0.0.0/24 \ + --dns-nameserver 8.8.8.8 \ + --gateway 10.0.0.1 + +# 2) 라우터 생성 후 self-service 네트워크에 연결 +openstack router create router1 + +openstack router add subnet router1 selfservice-subnet + +# 3) 라우터 외부 게이트웨이를 provider 네트워크로 설정 +openstack router set router1 --external-gateway provider +``` + +이걸 /root/neutron-setup-selfservice.sh 로 저장해서: + +``` +chmod +x /root/neutron-setup-selfservice.sh +/root/neutron-setup-selfservice.sh +``` + +--- + +### **4-4. 테스트 인스턴스 + Floating IP** + +1. SG/키페어/인스턴스: + +``` +source /root/admin-openrc.sh # 또는 demo-openrc + +openstack security group rule create --proto icmp default +openstack security group rule create --proto tcp --dst-port 22 default + +openstack keypair create demo-key > demo-key.pem +chmod 600 demo-key.pem + +SELF_NET_ID=$(openstack network show selfservice -f value -c id) + +openstack server create demo-instance \ + --flavor m1.small \ + --image cirros \ + --nic net-id=$SELF_NET_ID \ + --security-group default \ + --key-name demo-key +``` + +1. Floating IP 붙이기: + +``` +FIP=$(openstack floating ip create provider -f value -c floating_ip_address) +echo "FLOATING IP = $FIP" + +openstack server add floating ip demo-instance "$FIP" +``` + +1. 노트북/호스트에서: + +``` +ping $FIP +ssh cirros@$FIP # 비번: gocubsgo (cirros 기본) +``` + +여기까지 완료되면 Neutron+Nova+Glance+Keystone+Placement 풀 스택 동작 상태다 + +--- + +## **5. 정리** + +- 위에 적어준 config들은 “**Epoxy + ML2 + Linuxbridge + self-service Option 2**” 공식 가이드 흐름을 + + 위 구성은 실습 환경(IP/호스트명/인터페이스)에 맞춰 상세히 정리한 것이다. + +- 실제로는: + 1. 컨트롤러 파일들 채우고 서비스 재시작 + 2. 컴퓨트 파일들 채우고 서비스 재시작 + 3. openstack network agent list 로 UP 상태 확인 + 4. 스크립트로 provider/self-service/라우터/인스턴스까지 생성 +- 중간에 서비스 실패나 에러 로그가 나오면, 해당 로그 기준으로 디버깅하면 된다. + +이제부터는 “어디까지 올라갔는지” 확인하는 맛이니까, + +config 적용하면서 network agent list, server list, console log show 같은 거 찍어보다가 문제가 발생하면 관련 로그를 함께 확인한다! + +[ovs 스위치](ch2_4_28_lec.qmd) diff --git a/lectures/ch2/ch2_4_22_lec.qmd b/lectures/ch2/ch2_4_22_lec.qmd new file mode 100644 index 0000000..25ba032 --- /dev/null +++ b/lectures/ch2/ch2_4_22_lec.qmd @@ -0,0 +1,325 @@ +--- +title: "2-4장. Horizon 설치" +--- + +# Horizon + +좋은 타이밍에 Horizon까지 왔다 + +이제부터는 “클라우드가 정상 동작하는지”를 CLI가 아닌 **웹 화면에서 확인하는 단계**이다. + +--- + +## **1. Horizon이 뭐 하는 애냐** + +한 줄 정의: + +> Horizon = OpenStack 공식 대시보드(web UI) +> + +> Nova, Neutron, Cinder, Swift, Keystone 같은 서비스들을 브라우저에서 클릭으로 관리하게 해주는 프론트엔드. +> + +특징을 정리하면: + +- **Keystone 기반 로그인** + - 사용자는 Keystone 계정(admin, demo, 프로젝트 유저 등)으로 로그인 + - 로그인 후 “어떤 프로젝트로 들어갈지” 선택 +- **주요 작업을 전부 UI로** + - 인스턴스 생성 / 삭제 (Nova) + - 이미지 업로드 (Glance) + - 네트워크 / 서브넷 / 라우터 / Floating IP 설정 (Neutron) + - 볼륨 생성/Attach (Cinder) + - Object Storage 컨테이너/오브젝트 관리 (Swift) +- **두 가지 시점** + - 사용자 대시보드 (프로젝트 내에서 리소스 보는 화면) + - 관리자 대시보드 (모든 프로젝트/사용자/쿼터 보는 화면) +- **플러그인 구조** + - Magnum, Octavia, Designate 등 각 서비스용 Horizon plugin을 붙이면 메뉴가 늘어나는 구조 + +아키텍처적으로 보면: + +- Horizon은 **Django 웹앱**이고, +- **REST API를 써서 Keystone/Nova/Neutron 등에 직접 붙는 클라이언트** 한 계층으로 이해하면 된다. +- 컨트롤러 노드에서 Apache + mod_wsgi로 서비스하는 게 기본 패턴. + +--- + +## **2. 설치 가이드가 전제로 깔고 있는 것들** + +Horizon 설치 가이드 첫 문장부터 이렇게 적혀 있어: + +- “이 섹션은 **컨트롤러 노드에 대시보드를 설치/구성하는 방법**을 설명한다.” +- “**유일하게 필수인 코어 서비스는 Identity(Keystone)**이다.” +- 하지만 보통은 Image / Compute / Networking이 같이 있어야 UI에서 할 게 많음. +- 전제: + - Keystone이 **Apache HTTPD + Memcached** 로 이미 잘 돌아가고 있어야 한다. + +즉, 지금 네 상태처럼: + +- Keystone / Glance / Placement / Nova / Neutron이 다 올라간 뒤, +- 마지막에 “Dashboard – Horizon”을 설치하는 흐름이 공식 권장 순서다. + +--- + +## **3. 설치 가이드 구조 (큰 그림)** + +Horizon 설치 문서 메인 페이지를 보면 이렇게 나눠져 있어: + +1. **System Requirements** + - 브라우저 지원, Python/Django 버전, DB/캐시(memcached) 등 기본 요구사항 +2. **Installing from Packages** + - Debian 계열 (Debian, Ubuntu) + - RHEL 계열 + - openSUSE / SLES + - 각자 “패키지 + 설정 파일 수정 + Apache 재시작 + 동작 확인” 구조 +3. **Installing from Source (Manual installation)** + - git clone해서 pip로 설치 + - local_settings.py 직접 작성 + - collectstatic / compress / 웹 서버 설정 +4. **Horizon plugins** + - “Plugin Registry” 링크 + - 플러그인 설치하면 Horizon 메뉴에 기능이 추가됨 + +너는 Ubuntu 24.04 + Epoxy라서 **“Install and configure for Ubuntu”** 섹션이 메인 타깃. + +--- + +## **4. Ubuntu 기준 설치 흐름 요약** + +문서 내용 그대로, controller에서 하는 작업만 정리해볼게. + +### **4-1. 패키지 설치** + +컨트롤러에서: + +``` +apt install openstack-dashboard +``` + +패키지 설치가 완료되면 `/etc/openstack-dashboard` 아래에 Django 프로젝트(`horizon + openstack_dashboard`)가 배치된다. + +Apache site config까지 같이 제공해 줌. + +--- + +### **4-2.** + +### **local_settings.py** + +### **핵심 설정 포인트** + +문서는 /etc/openstack-dashboard/local_settings.py 를 열고 다음을 꼭 손보라고 함: + +1. **Keystone 위치** + +``` +OPENSTACK_HOST = "controller" +``` + +- Keystone endpoint가 http://controller:5000 기준이라고 가정. +- 포트까지 지정하려면 OPENSTACK_KEYSTONE_URL에서 조정. +1. **ALLOWED_HOSTS** + +``` +ALLOWED_HOSTS = ['one.example.com', 'two.example.com'] +# 또는 개발용으로 ['*'] + +이 설정은 `controller` 또는 `10.100.100.11`로 접속하도록 맞춰야 한다. 따라서 아래와 같이 설정한다. +ALLOWED_HOSTS = ['10.100.100.11', 'controller', 'localhost'] +``` + +- Django 기본 보안 설정. 허용할 호스트 이름들 나열. +- 문서에서는 “Ubuntu 섹션의 ALLOWED_HOSTS는 수정하지 않고, 대시보드 설정 섹션 값을 수정하라”고 안내한다. +1. **memcached 세션 스토리지** + +``` +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'LOCATION': 'controller:11211', + } +} +``` + +- “다른 세션 설정은 주석 처리해라”라고 문서에 나와 있음. +- 이미 Keystone도 memcached 쓰고 있기 때문에, 여기서도 memcached를 공유. +1. **Keystone v3 API 활성화** + +``` +OPENSTACK_KEYSTONE_URL = "http://%s/identity/v3" % OPENSTACK_HOST +# 또는 keystone 5000포트 쓴다면: +# OPENSTACK_KEYSTONE_URL = "http://%s:5000/identity/v3" % OPENSTACK_HOST +``` + +- 최신 OpenStack는 Keystone v3가 기본이므로, 이 URL이 중요하다. +1. **도메인 지원** + +``` +OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True +OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "Default" +``` + +- 우리가 Keystone 부트스트랩할 때 썼던 기본 도메인과 매칭. +1. **API 버전 설정** + +``` +OPENSTACK_API_VERSIONS = { + "identity": 3, + "image": 2, + "volume": 3, +} +``` + +- Epoxy 기준으로 Identity v3, Glance v2, Cinder v3가 기본. +1. **Neutron 옵션 1(Provider)일 땐 L3 기능 끄기 (선택)** + +문서에서는 “설치 가이드의 networking option 1(Provider only)인 경우 L3 기능을 비활성화하라”고 안내한다: + +``` +OPENSTACK_NEUTRON_NETWORK = { + ... + 'enable_router': False, + 'enable_quotas': False, + 'enable_ipv6': False, + 'enable_distributed_router': False, + 'enable_ha_router': False, + 'enable_fip_topology_check': False, +} +``` + +- self-service(L3) 옵션까지 구성할 경우, 이 부분은 굳이 False로 내릴 필요 없이 default 기능을 사용하면 된다. +1. **TIME_ZONE 설정 (옵션)** + +``` +TIME_ZONE = "Asia/Seoul" +``` + +- 문서에 “TIME_ZONE을 적절한 값으로 바꿔라”라고 나와 있고, + + 예시는 위키 타임존 리스트 링크 달아놓음. + + +--- + +### **4-3. Apache 설정 보정** + +Ubuntu 문서에는 아래 한 줄 추가를 안내한다: + +``` +echo 'WSGIApplicationGroup %{GLOBAL}' >> /etc/apache2/conf-available/openstack-dashboard.conf +``` + +혹은 파일 열어서 수동으로 추가. + +이건 Django/memcached/모듈들이 같은 프로세스 그룹에서 동작하도록 보장하는 설정. + +--- + +### **4-4. 마무리** + +``` +systemctl reload apache2 +``` + +여기까지가 Ubuntu 설치 가이드 범위이다. + +이제 웹 브라우저에서: + +- URL: http://controller/horizon (또는 /dashboard) +- 로그인: admin / ADMIN_PASS, 혹은 demo / DEMO_PASS + +하면 **Epoxy 클러스터가 웹 UI에 표시된다.** + +--- + +## **5. “Installing from Source” 흐름 (개념만)** + +패키지 방식 대신 Git + pip 기반 설치를 원하면 “Manual installation” 문서를 참고한다: + +대략적인 흐름은: + +1. Horizon git clone + +``` +git clone https://opendev.org/openstack/horizon -b stable/ --depth=1 +cd horizon +``` + +1. Python 모듈 설치 + +``` +sudo pip install -c https://opendev.org/openstack/requirements/raw/branch/stable//upper-constraints.txt . +``` + +1. 설정 +- openstack_dashboard/local/local_settings.py.example 복사해서 local_settings.py 만들고 +- 위에서 언급한 OPENSTACK_HOST, ALLOWED_HOSTS, CACHES, OPENSTACK_KEYSTONE_URL 등 설정 +1. Django static 파일/압축 + +``` +python manage.py collectstatic +python manage.py compress +``` + +1. Apache/Nginx + mod_wsgi(or uWSGI)로 서비스 +- WSGI entrypoint: openstack_dashboard.wsgi:application +- VirtualHost에 적절히 붙이고, endpoint 공개 + +이 루트는 **개발/커스텀 테마/플러그인 작업할 때** 많이 쓰고, + +단순 Epoxy PoC엔 패키지 설치가 훨씬 편함. + +--- + +## **6. Horizon plugins** + +설치 가이드 마지막에는 “Horizon plugins” 섹션이 있고, Plugin registry 링크만 달려 있어: + +- 예: + - heat-dashboard (Orchestration) + - magnum-ui (Container Infra) + - octavia-dashboard (LBaaS) +- 방식: + - 해당 플러그인 패키지 설치 + - INSTALLED_APPS 또는 enabled 파일에 등록 + - 웹 서버 재시작 → 메뉴에 새 탭/패널이 추가 + +향후 AOD나 상용 서비스 환경으로 확장하면 Horizon theme + plugin custom을 다룰 일이 생길 수 있으므로, “플러그인 레이어가 별도로 있다”는 점을 기억해두면 도움이 된다. + +--- + +## **7. Horizon의 역할 정리** + +지금 네 상태: + +- Epoxy 기준으로 + + Keystone / Glance / Placement / Nova / Neutron / (OVS or Linuxbridge)까지 전부 수동 설치 진행 중 + +- **CLI로는 이미 인스턴스 / 네트워크 생성이 가능할 정도**로 갈 거고, +- 그 다음 단계가 바로: + - Horizon 설치 → http://controller/horizon 접속 + - admin / demo 계정으로 로그인해서 + - 같은 작업(VM 생성 / 네트워크 설정)을 **UI로도 할 수 있게 만드는 것** + +그래서 Horizon은: + +- 클러스터 입문 단계에서 **“서비스 연결 상태를 시각적으로 확인하는 대시보드”** 역할 +- 이후엔: + - 팀원에게 “AoldaCloud 콘솔” 형태로 제공할 수 있는 + - **실제 사용자 포털 레이어**로 이해하면 된다. + +--- + +원하면 다음 턴에서: + +- 네 Epoxy 랩 기준으로 + - openstack-dashboard 설치 + - local_settings.py를 “controller / 10.100.100.11 / Asia/Seoul / Keystone v3” 에 맞게 + + 구체적인 입력 방식은 **실제 예시 파일** 형태로 바로 제시한다. + + +[Horizon 오류 해결](ch2_4_29_lec.qmd) diff --git a/lectures/ch2/ch2_4_23_lec.qmd b/lectures/ch2/ch2_4_23_lec.qmd new file mode 100644 index 0000000..16c10d4 --- /dev/null +++ b/lectures/ch2/ch2_4_23_lec.qmd @@ -0,0 +1,517 @@ +--- +title: "2-4장. Horizon 대시보드에서 인스턴스 생성" +--- + +# Horizon 대시보드에서 인스턴스 생성 + +인스턴스 시작 버튼 누르기 + +대시보드가 정상적으로 표시되었다. + +이제 인스턴스 생성 가능 상태다. + +다만 “정상 생성 여부 / 외부 ping·ssh 가능 여부”는 Nova/Neutron 설정 완료 상태에 따라 달라진다. + +아래 순서로 확인한다. + +--- + +## **1. 지금 바로 체크하면 좋은 것들 (컨트롤러에서)** + +터미널에서: + +``` +source /root/admin-openrc.sh + +# Nova / compute 상태 +openstack compute service list +openstack hypervisor list + +# Neutron 에이전트 상태 +openstack network agent list + +# 이미지 / flavor / 네트워크가 있는지 +openstack image list +openstack flavor list +openstack network list +``` + +아래와 같은 형태로 구성되면 된다: + +- compute 서비스가 up +- hypervisor list 에 compute1 보임 +- network agent 들(OVS agent, L3, DHCP, metadata)이 :-) / UP +- cirros 이미지 하나, m1.small 같은 flavor 하나, selfservice/provider 네트워크 있음 + +이 정도면 Horizon에서 인스턴스 띄워 볼 수 있어. + +--- + +## **2. Horizon에서 인스턴스 만드는 방법** + +지금 화면이 “프로젝트 → Compute → 인스턴스”니까 거기서: + +1. **오른쪽 위 인스턴스 시작 버튼 클릭** +2. **탭별로 설정** + + ### **(1) 세부 정보(Details)** + + - 인스턴스 이름: 예) test-cirros-1 + - 가용 영역: nova + - 수량: 1 + + ### **(2) 소스(Source)** + + - 부팅 소스: **이미지로 부팅** (Boot from image) + - 선택 가능한 이미지 목록에서 **cirros** 골라서 오른쪽으로 추가 + + ### **(3) Flavor** + + - 선택 가능한 flavor 있으면 아무거나 (예: m1.small). + - 만약 flavor가 하나도 없으면 컨트롤러에서: + +``` +openstack flavor create --id 1 --ram 512 --disk 5 --vcpus 1 m1.small +``` + +1. + - 만들고 새로고침해서 선택 + + ### **(4) 네트워크(Network)** + + - self-service 네트워크(SDN)까지 만들었으면 + + → **selfservice 같은 네트워크를 선택해서 오른쪽으로 추가** + + - 아직 self-service 안 만들고 provider만 있다면 + + → provider 네트워크 선택 + + + ### **(5) 보안 그룹(Security Groups)** + + - 기본 default 하나만 있으면: + - 미리 CLI에서 ICMP/SSH Allow 룰 추가해 두는 게 좋아: + +``` +openstack security group rule create --proto icmp default +openstack security group rule create --proto tcp --dst-port 22 default +``` + +1. + + ### **(6) 키 페어(Key Pair)** + + - 이미 키 페어 있으면 선택 + - 없으면 상단 메뉴의 **키 페어** 메뉴에서 하나 생성하고, + + PEM 파일 다운 받아두고, 그걸 선택 + +2. **인스턴스 시작 버튼 클릭** + +잠시 후 리스트에 test-cirros-1 이 올라오고 + +Status가 Active 로 바뀌면 성공 ✨ + +--- + +## **3. 그 다음에 할 수 있는 것들** + +- **콘솔로 접속 테스트** + - 인스턴스 이름 오른쪽 작업 → “콘솔 보기” + - noVNC 콘솔 열리면 로그인: + - cirros / gocubsgo (기본 계정) +- **네트워크 테스트** + - self-service 네트워크까지 만들었다면: + - 같은 네트워크에 인스턴스 두 개 띄워서 서로 ping + - 외부에서 ping/ssh 까지 하고 싶으면: + - provider 네트워크 + router + floating IP 구성까지 끝내야 함 + + (이건 우리가 Neutron Option 2 파트에서 하려던 거) + + +--- + +## **4. 요약** + +- 지금 대시보드 뜨는 시점까지 왔으면 **Nova/Keystone/Horizon 라인은 정상**이고, +- Neutron 쪽 agent/status만 괜찮다면 **인스턴스 만들기 가능**. +- 인스턴스 시작 마법사에서: + - 이미지 = cirros + - flavor = m1.small + - 네트워크 = selfservice(or provider) + - 보안 그룹/키페어 세팅 + - → Launch! + +해보고 Status가 Error로 떨어지거나 부팅 안 되면, + +- Horizon에서 에러 메시지, +- openstack server show <인스턴스ID> 결과, +- nova-compute / neutron-openvswitch-agent 로그 + +점검 중 확인한 사항은 다음과 같다. + +![image.png](images/ch2_4_img_15.png) + +네트워크 목록이 비어 있다. + +대시보드 접속이 정상적으로 확인되었다 + +지금 화면에서 **네트워크 탭이 비어있는 이유**는 다음 한 가지다: + +> 아직 Neutron 네트워크를 하나도 안 만들어서 그래. +> + +> (NIC를 붙일 네트워크가 없으니 “사용 가능 0개”) +> + +그래서 인스턴스 만들기 전에 **provider / self-service 네트워크 + 서브넷 + 라우터**를 먼저 만들어줘야 돼. + +아래는 **컨트롤러에서 CLI로 일괄 생성하는 방법**이다. + +(예시 값이므로, 필요 시 IP 대역은 실습 환경에 맞게 변경한다.) + +--- + +## **1. 준비: admin 자격 로딩 + 상태 확인** + +컨트롤러에서: + +``` +sudo -i +source /root/admin-openrc.sh + +# Neutron 에이전트 상태 체크 +openstack network agent list + +# 이미 만들어진 네트워크 있는지 +openstack network list +``` + +- agent 들이 전부 Alive :-), State UP 이면 OK +- network 리스트가 비어 있으면 아래대로 새로 만드는 거. + +--- + +## **2. provider(외부) 네트워크 만들기** + +예시로 **172.32.200.0/24** 를 외부 풀로 쓰겠다고 가정할게. + +(중요: 실제로는 네 로컬 네트워크 대역에 맞춰서 subnet-range, gateway, allocation-pool 을 바꿔야 함.) + +[외부에서 접근 가능한 IP 대역이란?](ch2_4_30_lec.qmd) + +``` +# 1) provider 네트워크 생성 (external, flat) +openstack network create provider \ + --share \ + --external \ + --provider-network-type flat \ + --provider-physical-network provider + +# 2) provider 서브넷 생성 +openstack subnet create provider-subnet \ + --network provider \ + --subnet-range 172.32.200.0/24 \ + --allocation-pool start=172.32.200.100,end=172.32.200.200 \ + --gateway 172.32.200.1 \ + --dns-nameserver 8.8.8.8 +``` + +> 주의: 여기 나오는 `172.32.200.0/24`, `172.32.200.1`, `200.100~200.200` 은 예시 값이다. +> + +> 실제로는 +> +> +> **외부에서 접근 가능한 IP 대역** +> + +--- + +## **3. self-service(테넌트) 네트워크 + 라우터 만들기** + +`admin` 프로젝트에서 생성해도 되며, 이후 `demo` 프로젝트를 분리하면 해당 프로젝트에서 다시 생성하면 된다. + +``` +# self-service 네트워크 생성 +openstack network create selfservice + +# self-service 서브넷 생성 (10.0.0.0/24 예시) +openstack subnet create selfservice-subnet \ + --network selfservice \ + --subnet-range 10.0.0.0/24 \ + --dns-nameserver 8.8.8.8 \ + --gateway 10.0.0.1 +``` + +라우터 생성 + 연결: + +``` +# 라우터 생성 +openstack router create router1 + +# 라우터에 selfservice-subnet 연결 +openstack router add subnet router1 selfservice-subnet + +# 라우터 외부 게이트웨이를 provider 네트워크로 설정 +openstack router set router1 --external-gateway provider +``` + +마지막으로 한 번 확인: + +``` +openstack network list +openstack subnet list +openstack router list +``` + +여기서 provider, selfservice, router1 이 보이면 준비 완료 + +--- + +## **4. Horizon에서 다시 인스턴스 시작 시도** + +이제 Horizon으로 돌아가서: + +1. **인스턴스 시작 → 네트워크 탭** 다시 열어보면 + - 사용 가능 목록에 selfservice 네트워크가 떠야 함 +2. selfservice 선택해서 **할당됨** 쪽으로 옮긴 뒤 인스턴스 시작 + +나머지 탭은: + +- 소스: cirros 이미지 +- Flavor: m1.small (없으면 CLI로 하나 생성) +- 보안 그룹: default (ICMP/22 허용 룰 추가해두면 좋음) +- 키페어: 만들어서 선택 + +위 항목까지 맞춘 뒤 시작하면 인스턴스 리스트에 VM이 표시된다. + +--- + +## **5. 인스턴스가 보이지 않을 때 점검 항목** + +- 인스턴스가 ERROR 로 떨어지면: + +``` +openstack server show <인스턴스ID> +openstack console log show <인스턴스ID> | tail +``` + +- +- 네트워크 문제 같으면: + +``` +openstack port list --server <인스턴스ID> +openstack network agent list +``` + +로그/출력을 확인하면 해당 지점에서 추가 디버깅이 가능하다. + +일단 먼저 위 명령으로 provider + self-service 네트워크부터 하나 만들어 보고, + +인스턴스 시작 화면의 네트워크 탭에 `selfservice`가 표시되는지 확인한다. + +이제 실제 워크로드 검증 단계로 진행한다 + +- **1단계:** provider / self-service 네트워크 + 라우터 만들어주기 +- **2단계:** 네트워크 포트 / 키페어가 Horizon에 보이게 하기 + +전부 **controller 노드**에서 root + admin-openrc 로 한다고 가정할게. + +``` +sudo -i +source /root/admin-openrc.sh +``` + +--- + +## **1. provider(외부) 네트워크 만들기** + +네 Proxmox 환경 기준으로: + +- 물리망 서브넷: 10.100.100.0/24 +- 게이트웨이(공유기/라우터): 10.100.100.1 +- OpenStack 전용 Floating IP 풀: `172.32.200.100 ~ 172.32.200.200` (기존 사용 구간과 충돌하지 않는 범위로 선택) + +### **1-1. provider 네트워크 생성** + +``` +openstack network create provider \ + --share \ + --external \ + --provider-network-type flat \ + --provider-physical-network provider +``` + +### **1-2. provider 서브넷 생성** + +``` +openstack subnet create provider-subnet \ + --network provider \ + --subnet-range 10.100.100.0/24 \ + --allocation-pool start=172.32.200.100,end=172.32.200.200 \ + --gateway 10.100.100.1 \ + --dns-nameserver 8.8.8.8 +``` + +> 주의: 나중에 `172.32.200.*` 대역에 다른 장비를 올릴 계획이라면, +> + +> allocation-pool 범위만 조정해도 된다. (미사용 구간으로 설정) +> + +--- + +## **2. self-service 네트워크 + 라우터 만들기** + +### **2-1. selfservice 네트워크/서브넷** + +``` +openstack network create selfservice + +openstack subnet create selfservice-subnet \ + --network selfservice \ + --subnet-range 10.0.0.0/24 \ + --gateway 10.0.0.1 \ + --dns-nameserver 8.8.8.8 +``` + +### **2-2. 라우터 생성 + 연결** + +``` +# 라우터 만들기 +openstack router create router1 + +# selfservice 서브넷을 라우터 내부 인터페이스로 추가 +openstack router add subnet router1 selfservice-subnet + +# 라우터 외부 게이트웨이를 provider 네트워크로 지정 +openstack router set router1 --external-gateway provider +``` + +### **2-3. 확인** + +``` +openstack network list +openstack subnet list +openstack router list +``` + +여기서 provider, selfservice, router1 나오면 OK. + +Horizon에서도 프로젝트 → 네트워크 → 네트워크 / 라우터 메뉴에 두 리소스가 모두 표시된다. + +--- + +## **3. 네트워크 포트(“네트워크 포트” 탭이 비어있는 이유)** + +지금 인스턴스 마법사에서 **네트워크 포트** 탭이 비는 건 정상임: + +- 이 탭은 “미리 만들어 둔 포트가 있으면 그 포트를 붙일 때” 쓰는 거고 +- 아직 포트를 따로 만든 적이 없으니 0개가 맞아. + +일반적인 흐름은: + +> 네트워크 탭에서 네트워크(selfservice)를 선택하면 → Nova가 자동으로 port 만들어줌 +> + +> → 별도 요구가 없다면 포트를 수동으로 생성할 필요는 없다. +> + +그래도 테스트용으로 포트를 하나 만들어보고 싶으면: + +``` +# selfservice 네트워크에 포트 하나 생성 +openstack port create demo-port1 --network selfservice + +# 확인 +openstack port list --network selfservice +``` + +그러면 Horizon의 네트워크 포트 탭에도 demo-port1 이 뜨고, + +인스턴스 시작할 때 “네트워크 포트” 탭에서 이 포트를 직접 선택해서 붙일 수도 있어. + +--- + +## **4. 키 페어가 안 뜨는 이유 + 만드는 방법** + +키페어도 똑같이: + +> “아직 한 번도 키페어를 생성한 적이 없어서 리스트가 빈 상태” +> + +### **4-1. CLI로 키페어 만들기** + +컨트롤러에서: + +``` +# admin 프로젝트 기준 예시 +openstack keypair create admin-key > /root/admin-key.pem +chmod 600 /root/admin-key.pem + +openstack keypair list +``` + +- admin-key 가 리스트에 보이면 성공 +- 이 키는 이후 `ssh -i admin-key.pem` 접속 시 사용하는 비밀키이다. + +### **4-2. Horizon에서 키페어 만드는 방법** + +Horizon UI에서도 만들 수 있어: + +1. 왼쪽 메뉴 → **프로젝트 → Compute → 키 페어** +2. 오른쪽 위 **키 페어 생성** +3. 이름: admin-key 같은 거 +4. “키 페어 생성” 누르면 브라우저에서 .pem 파일 다운됨 → 안전한 데 저장 + +그 뒤에 인스턴스 마법사의 키 페어 탭에서도 선택 가능해져. + +--- + +## **5. 인스턴스 다시 시작해보기 (요약)** + +이제 필요한 것들: + +- 네트워크: selfservice (필수), provider (external) +- 라우터: router1 +- 키페어: admin-key +- 보안 그룹: default (ICMP/SSH 룰 추가 권장) + +보안 그룹 룰도 CLI로 미리 넣어두자: + +``` +openstack security group rule create --proto icmp default +openstack security group rule create --proto tcp --dst-port 22 default +``` + +그 다음 Horizon에서: + +1. **인스턴스 시작** +2. **소스**: cirros 이미지 선택 +3. **Flavor**: m1.small (없으면 CLI로 생성했을 거고) +4. **네트워크**: selfservice 를 “할당됨” 쪽으로 이동 +5. **키 페어**: admin-key 선택 +6. **보안 그룹**: default 선택 +7. 시작! + +Status가 Active 되면, + +나중에 openstack floating ip create provider → server add floating ip 해서 + +외부 ping/ssh 검증까지 완료되면 전체 경로 검증이 끝난다 + +해보다가 인스턴스가 Error 로 떨어지거나, 네트워크가 이상하면 + +- openstack server show … 결과 +- openstack port list --server … + + 같이 던져주면 거기서부터 같이 디버깅하자. + + +인스턴스가 생성되지 않는 경우 점검 항목 + +[오류를 고쳐봐요!](ch2_4_31_lec.qmd) + +![image.png](images/ch2_4_img_16.png) diff --git a/lectures/ch2/ch2_4_24_lec.qmd b/lectures/ch2/ch2_4_24_lec.qmd new file mode 100644 index 0000000..03a63fc --- /dev/null +++ b/lectures/ch2/ch2_4_24_lec.qmd @@ -0,0 +1,15 @@ +--- +title: "2-4장. 트러블슈팅 모음" +--- + +# 2-4장. 트러블슈팅 모음 + +설치 과정에서 자주 마주치는 오류 해결 문서를 모아둔다. + +- [2.4.25 Keystone 트러블슈팅](ch2_4_25_lec.qmd) +- [2.4.26 Glance 트러블슈팅 (DB 권한)](ch2_4_26_lec.qmd) +- [2.4.27 Glance 트러블슈팅 (Quota)](ch2_4_27_lec.qmd) +- [2.4.28 Neutron OVS 스위치 설정](ch2_4_28_lec.qmd) +- [2.4.29 Horizon 트러블슈팅 (접속/설정)](ch2_4_29_lec.qmd) +- [2.4.30 외부 접근 IP 대역 설정 가이드](ch2_4_30_lec.qmd) +- [2.4.31 Horizon 인스턴스 생성 오류 해결](ch2_4_31_lec.qmd) diff --git a/lectures/ch2/ch2_4_25_lec.qmd b/lectures/ch2/ch2_4_25_lec.qmd new file mode 100644 index 0000000..3c191b9 --- /dev/null +++ b/lectures/ch2/ch2_4_25_lec.qmd @@ -0,0 +1,206 @@ +--- +title: "2-4장. Keystone 트러블슈팅" +--- + +# 에러 처리 + + admin-openrc.sh 내용은 **형식 자체는 완전 맞아** + +``` +export OS_USERNAME=admin +export OS_PASSWORD=1234 +export OS_PROJECT_NAME=admin +export OS_USER_DOMAIN_NAME=Default +export OS_PROJECT_DOMAIN_NAME=Default +export OS_AUTH_URL=http://controller:5000/v3 +export OS_IDENTITY_API_VERSION=3 +``` + +현재 401 오류의 주요 원인은 다음 한 가지다: + +> Keystone 안에 저장된 +> +> +> **admin 계정 비밀번호 ≠ 1234** +> + +> (우리가 DB keystone 유저 비번은 1234로 맞췄지만, +> + +> 관리자(admin) 유저 비번은 따로 있고 그걸 안 바꾼 상태 +> + +따라서 `OS_PASSWORD=1234` 로 로그인 시도하면 401 오류가 발생한다. + +--- + +## **해결 전략** + +### **선택지 1. bootstrap 시 사용한 비밀번호를 정확히 확인한다** + +그럼 제일 쉬워: + +1. admin-openrc.sh 에서 OS_PASSWORD 를 **그때 썼던 비번으로 교체** +2. 다시: + +``` +source /root/admin-openrc.sh +openstack token issue +``` + +여기서 1차 점검이 끝난다. + +--- + +### **선택지 2. (비밀번호를 기억하지 못하는 경우) Keystone 초기화 후 재설정 (권장)** + +지금은 다른 서비스(Glance/Nova/Neutron)도 안 깔았으니까 + +**Keystone만 밀고 다시 bootstrap 하는 게 제일 깔끔해.** + +아래 순서대로 controller에서 root로 실행하면 된다. + +--- + +### **1) Apache 잠깐 멈추기** + +``` +systemctl stop apache2 +``` + +--- + +### **2) keystone DB 초기화 + keystone 유저 비번 1234로 확정** + +``` +mysql -u root -p +``` + +안에서: + +``` +DROP DATABASE IF EXISTS keystone; +CREATE DATABASE keystone; + +DROP USER IF EXISTS 'keystone'@'controller'; +DROP USER IF EXISTS 'keystone'@'localhost'; +DROP USER IF EXISTS 'keystone'@'%'; + +CREATE USER 'keystone'@'controller' IDENTIFIED BY '1234'; +CREATE USER 'keystone'@'localhost' IDENTIFIED BY '1234'; +CREATE USER 'keystone'@'%' IDENTIFIED BY '1234'; + +GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'controller'; +GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost'; +GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%'; + +FLUSH PRIVILEGES; +EXIT; +``` + +--- + +### **3) Keystone 키 리포지토리 비우기** + +``` +rm -rf /etc/keystone/fernet-keys/* +rm -rf /etc/keystone/credential-keys/* +``` + +--- + +### **4)** + +### **/etc/keystone/keystone.conf** + +### **확인** + +[database] / [token] 부분만 맞춰져 있으면 됨: + +``` +[database] +connection = mysql+pymysql://keystone:1234@controller/keystone + +[token] +provider = fernet +``` + +(이미 이렇게 돼 있으면 수정 필요 없음) + +--- + +### **5) DB 마이그레이션 + 키 재생성** + +``` +su -s /bin/sh -c "keystone-manage db_sync" keystone + +keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone +keystone-manage credential_setup --keystone-user keystone --keystone-group keystone +``` + +--- + +### **6) bootstrap (여기서** + +### **admin 비번을 1234로 확정** + +### **수행 절차** + +``` +keystone-manage bootstrap --bootstrap-password 1234 \ + --bootstrap-admin-url http://controller:5000/v3/ \ + --bootstrap-internal-url http://controller:5000/v3/ \ + --bootstrap-public-url http://controller:5000/v3/ \ + --bootstrap-region-id RegionOne +``` + +이제 **admin 계정 비밀번호 = 1234** 가 된다. + +--- + +### **7) Apache 다시 시작** + +``` +systemctl start apache2 +``` + +버전 체크: + +``` +curl -v http://controller:5000/v3/ +``` + +200 OK 나오면 성공. + +--- + +### **8)** + +### **admin-openrc.sh** + +### **그대로 쓰고 토큰 다시 요청** + +지금 admin-openrc.sh 내용이 이미: + +``` +export OS_PASSWORD=1234 +``` + +로 되어 있으니까: + +``` +source /root/admin-openrc.sh +openstack token issue +``` + +이제 **토큰 정보 표가 정상 출력되면 성공**이다. + +--- + +요약하면: + +- 파일 위치/형식은 맞음. +- **문제는 Keystone 안에 저장된 admin 비밀번호랑 openrc의 OS_PASSWORD 가 달랐던 것.** +- 위 단계대로 Keystone 한 번 리셋하고 bootstrap을 1234로 다시 잡으면, + + 이제 `admin-openrc.sh` 를 그대로 사용할 수 있으며, 401 오류도 해소된다. diff --git a/lectures/ch2/ch2_4_26_lec.qmd b/lectures/ch2/ch2_4_26_lec.qmd new file mode 100644 index 0000000..a159d73 --- /dev/null +++ b/lectures/ch2/ch2_4_26_lec.qmd @@ -0,0 +1,177 @@ +--- +title: "2-4장. Glance 트러블슈팅 (DB 권한)" +--- + +# 에러 해결 + + + +```yaml +root@controller:~# su -s /bin/sh -c "glance-manage db_sync" glance +2025-11-22 15:36:38.457 32139 CRITICAL glance [-] Unhandled error: sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user 'glance'@'controller' (using password: YES)") +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-11-22 15:36:38.457 32139 ERROR glance Traceback (most recent call last): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 146, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self._dbapi_connection = engine.raw_connection() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 3298, in raw_connection +2025-11-22 15:36:38.457 32139 ERROR glance return self.pool.connect() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 449, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return _ConnectionFairy._checkout(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 1263, in _checkout +2025-11-22 15:36:38.457 32139 ERROR glance fairy = _ConnectionRecord.checkout(pool) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 712, in checkout +2025-11-22 15:36:38.457 32139 ERROR glance rec = pool._do_get() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/impl.py", line 179, in _do_get +2025-11-22 15:36:38.457 32139 ERROR glance with util.safe_reraise(): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__ +2025-11-22 15:36:38.457 32139 ERROR glance raise exc_value.with_traceback(exc_tb) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/impl.py", line 177, in _do_get +2025-11-22 15:36:38.457 32139 ERROR glance return self._create_connection() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 390, in _create_connection +2025-11-22 15:36:38.457 32139 ERROR glance return _ConnectionRecord(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 674, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self.__connect() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 900, in __connect +2025-11-22 15:36:38.457 32139 ERROR glance with util.safe_reraise(): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__ +2025-11-22 15:36:38.457 32139 ERROR glance raise exc_value.with_traceback(exc_tb) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 896, in __connect +2025-11-22 15:36:38.457 32139 ERROR glance self.dbapi_connection = connection = pool._invoke_creator(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/create.py", line 646, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return dialect.connect(*cargs, **cparams) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/default.py", line 622, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return self.loaded_dbapi.connect(*cargs, **cparams) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 353, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self.connect() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 633, in connect +2025-11-22 15:36:38.457 32139 ERROR glance self._request_authentication() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 907, in _request_authentication +2025-11-22 15:36:38.457 32139 ERROR glance auth_packet = self._read_packet() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 725, in _read_packet +2025-11-22 15:36:38.457 32139 ERROR glance packet.raise_for_error() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/protocol.py", line 221, in raise_for_error +2025-11-22 15:36:38.457 32139 ERROR glance err.raise_mysql_exception(self._data) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/err.py", line 143, in raise_mysql_exception +2025-11-22 15:36:38.457 32139 ERROR glance raise errorclass(errno, errval) +2025-11-22 15:36:38.457 32139 ERROR glance pymysql.err.OperationalError: (1045, "Access denied for user 'glance'@'controller' (using password: YES)") +2025-11-22 15:36:38.457 32139 ERROR glance +2025-11-22 15:36:38.457 32139 ERROR glance The above exception was the direct cause of the following exception: +2025-11-22 15:36:38.457 32139 ERROR glance +2025-11-22 15:36:38.457 32139 ERROR glance Traceback (most recent call last): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/bin/glance-manage", line 10, in +2025-11-22 15:36:38.457 32139 ERROR glance sys.exit(main()) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/cmd/manage.py", line 558, in main +2025-11-22 15:36:38.457 32139 ERROR glance return CONF.command.action_fn() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/cmd/manage.py", line 392, in sync +2025-11-22 15:36:38.457 32139 ERROR glance self.command_object.sync(CONF.command.version) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/cmd/manage.py", line 151, in sync +2025-11-22 15:36:38.457 32139 ERROR glance curr_heads = alembic_migrations.get_current_alembic_heads() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/db/sqlalchemy/alembic_migrations/__init__.py", line 50, in get_current_alembic_heads +2025-11-22 15:36:38.457 32139 ERROR glance engine = db_api.get_engine() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/db/sqlalchemy/api.py", line 98, in get_engine +2025-11-22 15:36:38.457 32139 ERROR glance facade = _create_facade_lazily() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/glance/db/sqlalchemy/api.py", line 85, in _create_facade_lazily +2025-11-22 15:36:38.457 32139 ERROR glance _FACADE = oslo_db_session.EngineFacade.from_config( +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/enginefacade.py", line 1518, in from_config +2025-11-22 15:36:38.457 32139 ERROR glance return cls( +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/enginefacade.py", line 1443, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self._factory._start( +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/enginefacade.py", line 530, in _start +2025-11-22 15:36:38.457 32139 ERROR glance self._setup_for_connection( +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/enginefacade.py", line 647, in _setup_for_connection +2025-11-22 15:36:38.457 32139 ERROR glance engine = engines.create_engine( +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/debtcollector/renames.py", line 41, in decorator +2025-11-22 15:36:38.457 32139 ERROR glance return wrapped(*args, **kwargs) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/engines.py", line 271, in create_engine +2025-11-22 15:36:38.457 32139 ERROR glance _test_connection(engine_event_target, max_retries, retry_interval) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/oslo_db/sqlalchemy/engines.py", line 169, in _test_connection +2025-11-22 15:36:38.457 32139 ERROR glance conn = engine.connect() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 3274, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return self._connection_cls(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 148, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance Connection._handle_dbapi_exception_noconnection( +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 2436, in _handle_dbapi_exception_noconnection +2025-11-22 15:36:38.457 32139 ERROR glance raise newraise.with_traceback(exc_info[2]) from e +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 146, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self._dbapi_connection = engine.raw_connection() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 3298, in raw_connection +2025-11-22 15:36:38.457 32139 ERROR glance return self.pool.connect() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 449, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return _ConnectionFairy._checkout(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 1263, in _checkout +2025-11-22 15:36:38.457 32139 ERROR glance fairy = _ConnectionRecord.checkout(pool) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 712, in checkout +2025-11-22 15:36:38.457 32139 ERROR glance rec = pool._do_get() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/impl.py", line 179, in _do_get +2025-11-22 15:36:38.457 32139 ERROR glance with util.safe_reraise(): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__ +2025-11-22 15:36:38.457 32139 ERROR glance raise exc_value.with_traceback(exc_tb) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/impl.py", line 177, in _do_get +2025-11-22 15:36:38.457 32139 ERROR glance return self._create_connection() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 390, in _create_connection +2025-11-22 15:36:38.457 32139 ERROR glance return _ConnectionRecord(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 674, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self.__connect() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 900, in __connect +2025-11-22 15:36:38.457 32139 ERROR glance with util.safe_reraise(): +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__ +2025-11-22 15:36:38.457 32139 ERROR glance raise exc_value.with_traceback(exc_tb) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/pool/base.py", line 896, in __connect +2025-11-22 15:36:38.457 32139 ERROR glance self.dbapi_connection = connection = pool._invoke_creator(self) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/create.py", line 646, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return dialect.connect(*cargs, **cparams) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/sqlalchemy/engine/default.py", line 622, in connect +2025-11-22 15:36:38.457 32139 ERROR glance return self.loaded_dbapi.connect(*cargs, **cparams) +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 353, in __init__ +2025-11-22 15:36:38.457 32139 ERROR glance self.connect() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 633, in connect +2025-11-22 15:36:38.457 32139 ERROR glance self._request_authentication() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 907, in _request_authentication +2025-11-22 15:36:38.457 32139 ERROR glance auth_packet = self._read_packet() +2025-11-22 15:36:38.457 32139 ERROR glance ^^^^^^^^^^^^^^^^^^^ +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/connections.py", line 725, in _read_packet +2025-11-22 15:36:38.457 32139 ERROR glance packet.raise_for_error() +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/protocol.py", line 221, in raise_for_error +2025-11-22 15:36:38.457 32139 ERROR glance err.raise_mysql_exception(self._data) +2025-11-22 15:36:38.457 32139 ERROR glance File "/usr/lib/python3/dist-packages/pymysql/err.py", line 143, in raise_mysql_exception +2025-11-22 15:36:38.457 32139 ERROR glance raise errorclass(errno, errval) +2025-11-22 15:36:38.457 32139 ERROR glance sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user 'glance'@'controller' (using password: YES)") +2025-11-22 15:36:38.457 32139 ERROR glance (Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-11-22 15:36:38.457 32139 ERROR glance +``` + +또 비밀번호 오류였다고 한다… + +비밀번호가 각 섹션에 정확히 입력되었는지 다시 확인한다. diff --git a/lectures/ch2/ch2_4_27_lec.qmd b/lectures/ch2/ch2_4_27_lec.qmd new file mode 100644 index 0000000..587cf6e --- /dev/null +++ b/lectures/ch2/ch2_4_27_lec.qmd @@ -0,0 +1,195 @@ +--- +title: "2-4장. Glance 트러블슈팅 (Quota)" +--- + +# 오류 해결 + +```yaml +glance image-create --name "cirros" \ + --file cirros-0.4.0-x86_64-disk.img \ + --disk-format qcow2 --container-format bare \ + --visibility=public +HTTP 413 Request Entity Too Large: The request returned a 413 Request Entity Too Large. This generally means that rate limiting or a quota threshold was breached.: The response body:: Project 1ba21d4331d84362bc9ed3d073b3cb23 is over a limit for [Resource image_count_total is over limit of 0 due to current usage 0 and delta 1] +``` + +이 현상은 실습에서 자주 발생하는 대표 사례다. + +에러 메시지 핵심만 보면: + +> Project ... is over a limit for [Resource image_count_total is over limit of 0 ...] +> + += **“이 프로젝트의 이미지 개수 최대 허용치가 0으로 설정되어, 1개만 생성해도 quota 초과로 차단되는 상황”**이다. + +이는 앞서 **[oslo_limit] + use_keystone_limits** 옵션을 조정하는 과정에서 발생한 부작용이다. + +--- + +## **왜 0이 됐냐?** + +[개념 정리] + +- use_keystone_limits = True 켜면 + + Glance가 **Keystone의 registered limits**를 믿고 quota를 계산함. + +- 그런데 registered limit를 제대로 만들어주지 않으면 + + → **기본이 0으로 간주**되어서 + + → 이미지 개수(image_count_total) limit=0, 현재=0, 새로 1개 만들려니까 → 초과… 해서 413. + + +즉: + +> “쿼터 기능만 켜고, 실제 쿼터 값은 안 넣어서 전체 프로젝트가 이미지 0개만 허용되는 상태” +> + +라고 볼 수 있다. + +--- + +## **해결 방법 두 가지 (중에서 하나 골라)** + +### **방법 1: 스터디 환경에서 quota 기능 비활성화 (권장)** + +지금은 **기능 이해/설치가 목적**이니까, + +Unified Limits는 과감히 꺼버리는 게 제일 깔끔해. + +1. Glance 설정 파일 열기 + +``` +vi /etc/glance/glance-api.conf +``` + +1. +2. [DEFAULT] 섹션에서 아래 줄 찾아서: + +``` +[DEFAULT] +... +use_keystone_limits = True +``` + +1. 이걸 **주석 처리하거나 False로 바꾸기**: + +``` +[DEFAULT] +... +# use_keystone_limits = True +use_keystone_limits = False +``` + +1. `[oslo_limit]` 섹션은 유지해도 되고, 실습 목적이라면 제거해도 된다. + + 핵심은 **use_keystone_limits를 비활성화하면 Glance가 quota를 조회하지 않게 된다는 점**이다. + +2. Glance API 재시작 + +``` +systemctl restart glance-api +``` + +1. +2. 다시 이미지 업로드 시도 + +``` +source /root/admin-openrc.sh + +glance image-create --name "cirros" \ + --file cirros-0.4.0-x86_64-disk.img \ + --disk-format qcow2 --container-format bare \ + --visibility=public + +glance image-list +``` + +1. → 이번엔 image_count_total over limit 0 이런 말 없이 **정상으로 올라가야 함.** + +--- + +### **방법 2: 그래도 quota 기능 써보고 싶다 → registered limit 제대로 세팅하기** + +이 방법은 **난이도가 조금 높으며**, 핵심 개념은 다음과 같다: + +1. Keystone에 “이미지 관련 리소스의 기본 상한값” 정의 +2. Glance [oslo_limit] 가 그 값을 보고 enforcement + +아래에는 흐름만 간단히 제시한다. (quota 학습 시 재활용 가능) + +1. admin 권한 로드 + +``` +source /root/admin-openrc.sh +``` + +1. +2. Glance 서비스의 registered limits 확인 + +``` +openstack registered limit list --service glance +``` + +1. +2. 없으면 예시처럼 생성 (RegionOne 기준): + +``` +# 총 이미지 용량 (MiB 단위, 문서 예시는 1000 등) +openstack registered limit create \ + --service glance --region RegionOne \ + --default-limit 1000 image_size_total + +openstack registered limit create \ + --service glance --region RegionOne \ + --default-limit 1000 image_stage_total + +openstack registered limit create \ + --service glance --region RegionOne \ + --default-limit 100 image_count_total + +openstack registered limit create \ + --service glance --region RegionOne \ + --default-limit 100 image_count_uploading +``` + +1. [oslo_limit] 섹션에서 endpoint_id / GLANCE_PASS 맞추고 + +``` +[oslo_limit] +auth_url = http://controller:5000 +auth_type = password +user_domain_id = default +username = glance +system_scope = all +password = GLANCE_PASS +endpoint_id = +region_name = RegionOne +``` + +1. [DEFAULT] 에서 use_keystone_limits = True 다시 켜고 +2. systemctl restart glance-api 후 다시 업로드 테스트 + +근데 이건 **system-scope / registered limits 개념까지 같이 물고 들어가야 해서**, + +현재 단계에서는 “이런 기능이 있다”는 수준으로 이해하고 넘어가면 된다. + +실습/설치 중에는 **방법 1 (끄기)** 로 가는 게 운영 부담이 적다 + +--- + +## **결론** + +- 에러 메시지는 **우리가 enable한 quota 기능 때문에 이미지 개수 limit이 0으로 잡힌 것**. +- 스터디용이면: + - /etc/glance/glance-api.conf에서 + + use_keystone_limits = False 로 바꾸고 + + systemctl restart glance-api + + - 그 다음 `glance image-create ...` 를 다시 실행하면 절차가 마무리된다. + +이대로 한 번 적용해보고, + +`glance image-list`에서 `cirros`가 `active` 상태인지 확인한다. diff --git a/lectures/ch2/ch2_4_28_lec.qmd b/lectures/ch2/ch2_4_28_lec.qmd new file mode 100644 index 0000000..48fe16e --- /dev/null +++ b/lectures/ch2/ch2_4_28_lec.qmd @@ -0,0 +1,235 @@ +--- +title: "2-4장. Neutron OVS 스위치 설정" +--- + +# OVS 스위치 설정 + +정리하면, 현재 구성은 OVS 기반으로 진행하는 것이 적절하다. + +핵심부터 말하면: + +> bridge_mappings = provider:ens19 이렇게 쓰면 안 돼. +> + +> bridge_mappings에는 +> +> +> **OVS 브리지 이름** +> + +> 그 브리지에 ens19를 포트로 붙여줘야 해. +> + +> +> + +> local_ip = 10.100.100.11 이건 +> +> +> **컨트롤러에서는 맞고** +> + +> 컴퓨트에서는 10.100.100.31 로 써야 해. +> + +--- + +## **1. bridge_mappings 에 뭘 넣어야 하는지** + +지금 공식 문서가 제시하는 구조는 다음과 같다: + +``` +[ovs] +bridge_mappings = provider:PROVIDER_BRIDGE_NAME +local_ip = OVERLAY_INTERFACE_IP_ADDRESS +``` + +여기서 + +- PROVIDER_BRIDGE_NAME = **OVS 브리지 이름** +- OVERLAY_INTERFACE_IP_ADDRESS = **VXLAN 터널에 쓸 IP (보통 management IP)** + +**중요 포인트** + +OVS ML2 드라이버는 “물리 네트워크 → **OVS 브리지**”를 매핑하는 거지, + +**NIC 이름(ens19)** 을 직접 매핑하는 게 아냐. + +그래서 해야 할 건: + +1. OVS 브리지 하나 만들고 (예: br-provider) +2. 거기에 ens19를 포트로 붙이고 +3. bridge_mappings에 provider:br-provider 를 넣는 것 + +### **컨트롤러/컴퓨트 공통으로 할 작업** + +각 노드에서: + +``` +apt install -y openvswitch-switch + +# OVS 브리지 생성 +ovs-vsctl add-br br-provider + +# ens19 를 그 브리지에 붙이기 +ovs-vsctl add-port br-provider ens19 +``` + +그 다음 openvswitch_agent.ini 의 [ovs] 섹션은 이렇게: + +### **컨트롤러** + +### **openvswitch_agent.ini** + +``` +[ovs] +bridge_mappings = provider:br-provider +local_ip = 10.100.100.11 +``` + +### **컴퓨트** + +### **openvswitch_agent.ini** + +``` +[ovs] +bridge_mappings = provider:br-provider +local_ip = 10.100.100.31 +``` + +이 구성이 **권장 표준 방식**이다. + +bridge_mappings = provider:ens19 로 하면 Neutron은 “provider 네트워크 → ens19 라는 *브리지*”라고 이해하려다가 + +그 이름의 OVS 브리지 자체가 없어서 꼬일 수 있음. + +--- + +## **2. local_ip 는 이렇게 쓰면 됨** + +이 부분 해석은 거의 정확하다: + +- VXLAN 터널 트래픽은 **노드 간 management 네트워크**로 흘릴 거라, +- 예제 아키텍처에서도 “관리 인터페이스 IP를 local_ip로 써라”고 되어 있음. + +그래서: + +- 컨트롤러 → local_ip = 10.100.100.11 +- 컴퓨트 → local_ip = 10.100.100.31 + +아래 두 항목은 이 값으로 맞춘다. + +--- + +## **3. OVS로 갈 때 꼭 같이 바꿔야 하는 것들 (중요 체크리스트)** + +이미 Linuxbridge 설정 일부를 적용한 상태라면, OVS로 전환할 때는 아래 항목을 함께 정리해야 한다: + +1. **패키지** + - 컨트롤러/컴퓨트 둘 다: + +``` +apt install -y neutron-openvswitch-agent +``` + +1. + - + - neutron-linuxbridge-agent는 **비활성화**: + +``` +systemctl disable --now neutron-linuxbridge-agent +``` + +1. +2. **ML2 설정 (ml2_conf.ini)** + - 이렇게 바꿔야 함: + +``` +[ml2] +type_drivers = flat,vlan,vxlan +tenant_network_types = vxlan +mechanism_drivers = openvswitch,l2population +extension_drivers = port_security + +[ml2_type_flat] +flat_networks = provider + +[ml2_type_vxlan] +vni_ranges = 1:1000 +``` + +1. + - + - 여기서 mechanism_drivers는 이제 **openvswitch** 기준. +2. **OVS agent 설정 (openvswitch_agent.ini)** + - 아까 말한 대로: + +``` +[ovs] +bridge_mappings = provider:br-provider +local_ip = 10.100.100.11 # or 31 on compute + +[agent] +tunnel_types = vxlan +l2_population = true + +[securitygroup] +enable_security_group = true +firewall_driver = openvswitch +# 또는 firewall_driver = iptables_hybrid (원하면 하이브리드) +``` + +1. +2. **서비스 재시작** + - 컨트롤러: + +``` +systemctl restart neutron-server neutron-openvswitch-agent neutron-dhcp-agent neutron-metadata-agent neutron-l3-agent nova-api +``` + +1. + - + - 컴퓨트: + +``` +systemctl restart neutron-openvswitch-agent nova-compute +``` + +1. +2. **agent 상태 확인** + +``` +source /root/admin-openrc.sh +openstack network agent list +``` + +1. 여기서 이제 Open vSwitch agent 가 controller/compute 에서 UP 으로 떠야 정상. + +--- + +## **4. 그래서 정리하면** + +> 핵심 질문: +> + +> 핵심 답변: +> +- local_ip = 10.100.100.11 (컨트롤러 기준, 컴퓨트는 10.100.100.31) +- bridge_mappings = provider:ens19 ❌ + + > → + > + > + > **반드시 OVS 브리지 이름으로 바꿔야 해서** + > + + > → bridge_mappings = provider:br-provider + ovs-vsctl add-br br-provider && ovs-vsctl add-port br-provider ens19 조합이 정답. + > + +일단 OVS로 가기로 했으니까 + +지금 노드별로 br-provider 만들고 저 두 줄만 반영해놓고, + +그 다음에 `ml2_conf.ini` / `openvswitch_agent.ini` 전체를 다시 점검한다. + +필요하면 현재 파일 내용을 기준으로 “수정해야 할 줄”만 추려서 바로 제시할 수 있다. diff --git a/lectures/ch2/ch2_4_29_lec.qmd b/lectures/ch2/ch2_4_29_lec.qmd new file mode 100644 index 0000000..f4dd67e --- /dev/null +++ b/lectures/ch2/ch2_4_29_lec.qmd @@ -0,0 +1,197 @@ +--- +title: "2-4장. Horizon 트러블슈팅 (접속/설정)" +--- + +# 에러 해결 + +![image.png](images/ch2_4_img_17.png) + +처음에 [http://controller/horizon이렇게](http://controller/horizon이렇게) 접속하라길래 아무생각 없이 접속했는데 이렇게 아무것도 안뜸 .. → DNS 문제 + +- 컨트롤러 VM에는 hosts 매핑이 있으나 로컬 PC에는 매핑이 없어 NXDOMAIN이 발생한 상황이다. + +정리하면 `10.100.100.11/horizon` 으로 접속한다. + +![image.png](images/ch2_4_img_18.png) + +접속이 되지 않는다면 설정값을 추가로 확인한다. + +상황을 정리하면 다음과 같다. + +아까 local_settings.py 아래 쪽에 + +```yaml +ALLOWED_HOSTS = ['10.100.100.11', 'controller', 'localhost'] +또는 +ALLOWED_HOSTS = ['*'] +``` + +이 설정이 잘못된 도메인으로 연결되어 있었다. 위와 같이 수정한다. (공식 문서 예시도 현재 환경에 맞춰 검증이 필요하다.) + +흐음 ,, 그랬더니 .. 이렇게 openstack 글자는 볼 수 있었다 .. + +![image.png](images/ch2_4_img_19.png) + +Horizon 대시보드가 표시되지 않았으며, 초기에는 단순 설정 이슈로 판단하였다. + + + +```yaml +tail -n 50 /var/log/apache2/error.log +[Mon Nov 24 23:40:58.469734 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:40:58.469738 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 347, in get +[Mon Nov 24 23:40:58.469741 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] return self._run_cmd("get", key, default, default=default, **kwargs) +[Mon Nov 24 23:40:58.469745 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:40:58.469749 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 322, in _run_cmd +[Mon Nov 24 23:40:58.469752 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] return self._safely_run_func(client, func, default_val, *args, **kwargs) +[Mon Nov 24 23:40:58.469756 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:40:58.469782 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 211, in _safely_run_func +[Mon Nov 24 23:40:58.469786 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] result = func(*args, **kwargs) +[Mon Nov 24 23:40:58.469788 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:40:58.469791 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 687, in get +[Mon Nov 24 23:40:58.469794 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] return self._fetch_cmd(b"get", [key], False, key_prefix=self.key_prefix).get( +[Mon Nov 24 23:40:58.469796 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:40:58.469799 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 1133, in _fetch_cmd +[Mon Nov 24 23:40:58.469802 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] self._connect() +[Mon Nov 24 23:40:58.469804 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 424, in _connect +[Mon Nov 24 23:40:58.469807 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] sock.connect(sockaddr) +[Mon Nov 24 23:40:58.469809 2025] [wsgi:error] [pid 167778:tid 137418327971520] [remote 172.33.0.10:59898] ConnectionRefusedError: [Errno 111] Connection refused +[Mon Nov 24 23:41:18.642630 2025] [wsgi:error] [pid 167777:tid 137418302793408] /usr/lib/python3/dist-packages/debreach/__init__.py:6: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead. +[Mon Nov 24 23:41:18.642744 2025] [wsgi:error] [pid 167777:tid 137418302793408] version_info = version.StrictVersion(__version__).version +[Mon Nov 24 23:41:20.923381 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ERROR django.request Internal Server Error: /horizon/ +[Mon Nov 24 23:41:20.923511 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] Traceback (most recent call last): +[Mon Nov 24 23:41:20.923518 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/django/core/handlers/exception.py", line 55, in inner +[Mon Nov 24 23:41:20.923523 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] response = get_response(request) +[Mon Nov 24 23:41:20.923526 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923533 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/horizon/middleware/simultaneous_sessions.py", line 30, in __call__ +[Mon Nov 24 23:41:20.923539 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] self._process_request(request) +[Mon Nov 24 23:41:20.923542 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/horizon/middleware/simultaneous_sessions.py", line 37, in _process_request +[Mon Nov 24 23:41:20.923548 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] cache_value = cache.get(cache_key) +[Mon Nov 24 23:41:20.923551 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923557 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/django/core/cache/backends/memcached.py", line 75, in get +[Mon Nov 24 23:41:20.923668 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] return self._cache.get(key, default) +[Mon Nov 24 23:41:20.923677 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923683 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 347, in get +[Mon Nov 24 23:41:20.923688 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] return self._run_cmd("get", key, default, default=default, **kwargs) +[Mon Nov 24 23:41:20.923691 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923693 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 322, in _run_cmd +[Mon Nov 24 23:41:20.923696 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] return self._safely_run_func(client, func, default_val, *args, **kwargs) +[Mon Nov 24 23:41:20.923699 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923701 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/hash.py", line 211, in _safely_run_func +[Mon Nov 24 23:41:20.923704 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] result = func(*args, **kwargs) +[Mon Nov 24 23:41:20.923707 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923709 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 687, in get +[Mon Nov 24 23:41:20.923712 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] return self._fetch_cmd(b"get", [key], False, key_prefix=self.key_prefix).get( +[Mon Nov 24 23:41:20.923715 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[Mon Nov 24 23:41:20.923717 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 1133, in _fetch_cmd +[Mon Nov 24 23:41:20.923722 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] self._connect() +[Mon Nov 24 23:41:20.923724 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] File "/usr/lib/python3/dist-packages/pymemcache/client/base.py", line 424, in _connect +[Mon Nov 24 23:41:20.923727 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] sock.connect(sockaddr) +[Mon Nov 24 23:41:20.923730 2025] [wsgi:error] [pid 167777:tid 137418302793408] [remote 172.33.0.10:60011] ConnectionRefusedError: [Errno 111] Connection refused +root@controller:~# ls /var/log/horizon +tail -n 50 /var/log/horizon/horizon.log +ls: cannot access '/var/log/horizon': No such file or directory +tail: cannot open '/var/log/horizon/horizon.log' for reading: No such file or directory +``` + +![image.png](images/ch2_4_img_20.png) + +![image.png](images/ch2_4_img_21.png) + +설정 차이를 비교해 원인을 확인한다. + + .. 알고보니 memcached에 연결해놓은 ip주소와 내가 Cache Location에 연결해놓은 주소가 달랐다 .. + +보이시나 … + +127… 172… + +증상이 즉시 드러나지 않아 원인 파악에 시간이 소요되었다. + +정리하면 다음과 같은 조치가 필요하다. + +같은 결과를 받았다 … + +로그를 확인해도 HTML 출력에 가려져 원인 식별이 어려웠다. + +결국 + +loca_settings.py에서 + +```yaml +DEBUG=True +``` + +로 디버깅 코드를 작성했다. + +![image.png](images/ch2_4_img_22.png) + +이렇게 뜬다 … + +“GPT SAYS” + +스크린샷에 다 나와있어: + +- **Exception Type:** OfflineGenerationError +- **Exception Value:** + + > You have offline compression enabled but key “…” is missing from offline manifest. + > + + > You may need to run “python manage.py compress”. + > + +정리하면 현재 상황은 다음과 같다: + +> Horizon이 +> +> +> **오프라인 압축 모드** +> + +> 정작 압축 결과(manifest)가 안 맞아서 템플릿 렌더링이 터진 것. +> + +패키지 설치 후 기본 설정이랑 우리가 DEBUG 켜고/끄면서 약간 꼬인 것 같아서 그래. + +랩/스터디 환경에서는 **압축 기능을 비활성화하는 방식이 가장 단순하고 안전한 해결책**이다. + +라고 한다 + +```yaml +vi /etc/openstack-dashboard/local_settings.py + +COMPRESS_OFFLINE = True 이 부분을 +COMPRESS_OFFLINE = False +이렇게 고치자! + +켜뒀던 디버깅도 끄자! +DEBUG = False +``` + +후에 여느때와 같이 Apache 재시작을 한다. + +```yaml +systemctl restart apache2 +``` + +![image.png](images/ch2_4_img_23.png) + +정리하면, 여기까지 점검이 완료되었다. + +근데 도메인에 뭐써야하지..? + +Default였다.. + +모르겠으면 죄다 admin, Default일것이다.. + +만약 기본 설정을 바꾸지 않았다면 + +![image.png](images/ch2_4_img_24.png) + +휴 성공했다 diff --git a/lectures/ch2/ch2_4_30_lec.qmd b/lectures/ch2/ch2_4_30_lec.qmd new file mode 100644 index 0000000..fb10d47 --- /dev/null +++ b/lectures/ch2/ch2_4_30_lec.qmd @@ -0,0 +1,131 @@ +--- +title: "2-4장. 외부 접근 IP 대역 설정 가이드" +--- + +# 외부에서 접근 가능한 IP 대역이란? + +이제 네트워크 대역을 결정하는 단계다 + +질문 요약하면: + +> “--subnet-range, --allocation-pool 이런 데 넣을 +> +> +> **외부에서 접근 가능한 IP 대역** +> + +핵심만 간단히 정리한다. + +--- + +## **1. “외부에서 접근 가능”이란 게 정확히 뭐냐** + +Neutron에서 --external 로 만드는 **provider 네트워크**는 + +> “ +> +> +> **OpenStack 바깥 물리망** +> + +이다. + +집/랩 환경이면 보통: + +- 공유기/L3 스위치 ↔ Proxmox ↔ OpenStack VM + +구조일 테니까, + +> 공유기 ↔ Proxmox vmbr0 +> + +라고 볼 수 있다. + +--- + +## **2. 현재 환경의 대역 확인 (제공된 스크린샷 기준)** + +제공된 Proxmox 네트워크 스크린샷 기준으로 `vmbr0`는 다음과 같다: + +- vmbr0: 172.32.0.2/16 +- Gateway: 10.100.100.1 + +즉: + +- **물리망 서브넷**: 10.100.100.0/24 +- **게이트웨이(라우터/Proxmox)**: 10.100.100.1 + +그러면 OpenStack 입장에서는: + +> “외부에서 접근 가능한 IP 대역” = +> +> +> **10.100.100.0/24 중에서 아직 안 쓰고 있는 IP 구간** +> + +이 되는 거지. + +--- + +## **3. 실제로 쓸 CIDR / 풀 고르는 방법** + +1. **서브넷/게이트웨이 확인** + - Proxmox 호스트나 컨트롤러에서: + +``` +ip a show vmbr0 # 또는 ens18/ens19 붙어있는 브리지 +ip route | head +``` + +1. + - + - 여기서 나오는 inet 172.32.0.2/16 / default via 10.100.100.1 → 우리가 아까 본 대로. +2. **DHCP 범위/이미 쓰고 있는 IP 체크** + - 공유기나 Proxmox DHCP 범위 확인 (예: 10.100.100.10–172.32.0.200 등) + - `arp -a` 또는 Proxmox UI에서 현재 사용 중 IP를 확인하면 도움이 된다. +3. **그 범위를 피해 “Floating IP용 풀”을 하나 뽑기** + - 예를 들면: + - 전체 서브넷: 10.100.100.0/24 + - 게이트웨이: 10.100.100.1 + - 예를 들어 이미 사용 중인 구간이 `172.32.0.2~10.100.100.100` 이라면 + - **OpenStack 전용 풀**: 172.32.200.100–172.32.200.200 이런 식으로 뒤쪽 대역 잡기 +4. **Neutron provider 네트워크 만들 때 이렇게 넣기** + +``` +openstack network create provider \ + --share \ + --external \ + --provider-network-type flat \ + --provider-physical-network provider + +openstack subnet create provider-subnet \ + --network provider \ + --subnet-range 10.100.100.0/24 \ + --allocation-pool start=172.32.200.100,end=172.32.200.200 \ + --gateway 10.100.100.1 \ + --dns-nameserver 8.8.8.8 +``` + +- -subnet-range → **물리망 전체 CIDR** (여기선 10.100.100.0/24) +- -gateway → 현재 사용 중인 게이트웨이(10.100.100.1) +- -allocation-pool → 그 중에서 **아직 안 쓰고 있는 일부 범위** + +이렇게만 맞춰주면, 나중에 openstack floating ip create provider 하면 + +`172.32.200.100~172.32.200.200` 사이에서 IP를 하나 할당하고, + +해당 IP를 통해 로컬 노트북에서 바로 ping/ssh 접속이 가능해진다. + +--- + +## **4. 한 줄 정리** + +- “외부에서 접근 가능한 IP 대역” = **Proxmox vmbr0 + 게이트웨이가 붙어 있는 물리 LAN 서브넷** +- 현재 실습 기준: + - 서브넷: 10.100.100.0/24 + - 게이트웨이: 10.100.100.1 + - 풀: 해당 대역 내 미사용 구간 (예: `172.32.200.100-200`) + +이대로 한 번 provider 네트워크 만들고, + +`openstack network list` / Horizon 네트워크 탭에 `provider`가 표시되는지 확인한다. diff --git a/lectures/ch2/ch2_4_31_lec.qmd b/lectures/ch2/ch2_4_31_lec.qmd new file mode 100644 index 0000000..0f2f85d --- /dev/null +++ b/lectures/ch2/ch2_4_31_lec.qmd @@ -0,0 +1,454 @@ +--- +title: "2-4장. Horizon 인스턴스 생성 오류 해결" +--- + +# 오류 해결 + +아래 순서로 원인 분석을 진행한다. + +Horizon 에서 **“서버를 생성하지 못했습니다”** 뜨고 인스턴스 목록이 비어 있다는 건: + +> Horizon → Nova 로 보낸 “VM 만들어줘” API 호출이 실패했고, +> + +> Nova 쪽에서 +> +> +> **instance row 자체를 만들기 전에 에러** +> + +> (따라서 ERROR 상태 인스턴스도 목록에 나타나지 않음) +> + +즉, 이유는 대부분 **Nova/Neutron 쪽 설정 문제**고, + +Horizon은 실패 메시지만 표시하므로, 실제 원인은 Nova/Neutron 설정과 로그에서 확인해야 한다. + +아래 순서대로 **컨트롤러에서 점검하면 병목 지점을 빠르게 확인할 수 있다.** + +--- + +### **1. Nova 서비스/하이퍼바이저가 살아있는지** + +컨트롤러에서: + +```bash +sudo -i +source /root/admin-openrc.sh + +openstack compute service list +openstack hypervisor list +``` + +위 출력 기준으로 서비스 상태는 정상이다. + +### 2. CLI로 서버 생성 재현 + +컨트롤러에서: + +```bash +source /root/admin-openrc.sh + +IMG_ID=$(openstack image show cirros -f value -c id) +NET_ID=$(openstack network show selfservice -f value -c id) + +openstack server create \ + --flavor m1.small \ + --image "$IMG_ID" \ + --nic net-id="$NET_ID" \ + --security-group default \ + --key-name admin-key \ + cli-test-1 +``` + +주의: `admin-key`에는 사전에 생성한 키페어 이름을 사용하거나, 새 키페어를 생성한 뒤 해당 이름을 사용한다. + +![image.png](images/ch2_4_img_25.png) + +### **3. Neutron 네트워크/포트 상태 간단 체크** + +계속 CLI에서: + +```bash +openstack network list +openstack subnet list +openstack router list +openstack port list --network selfservice +``` + +![image.png](images/ch2_4_img_26.png) + +- `provider`, `selfservice`, `router1`이 보이는지 확인한다. +- `selfservice` 네트워크에서 `port list` 결과가 비어 있으면, 인스턴스 생성 전 단계에서는 정상이다. +- 인스턴스 생성 시 “사용 가능한 네트워크가 없습니다” 오류가 발생하면, 네트워크/포트 구성 쪽을 우선 점검한다. + + +또 한 번: + +```bash +openstack network agent list +``` + +- Open vSwitch agent (또는 linuxbridge agent)가 controller/compute 둘 다 Alive :-), State UP 인지 +- DHCP agent, L3 agent, Metadata agent 가 UP 인지 + +위 항목이 모두 `UP`이면 에이전트 상태는 정상이다. + +그런데 CLI에서 다음 오류가 발생했다. + +HttpException: 500: Server Error for url: [http://controller:8774/v2.1/servers](http://controller:8774/v2.1/servers), Unexpected API Error. + + +의미는 다음과 같다. + +> `nova-api`가 Keystone(인증 서비스)와 통신하는 과정에서 `DiscoveryFailure`가 발생해 500을 반환했다. +> + +즉, + +- Nova의 keystone_authtoken / service_user / placement 같은 설정에서 + + **Keystone endpoint(주소) 또는 인증 정보가 잘못되었음을 의미한다.** + + +Compute/네트워크 상태 자체는 정상이다: + +- openstack compute service list → nova-compute up +- openstack hypervisor list → `compute1` 확인 +- openstack network list / subnet list / router list / port list → `provider/selfservice/router1` 확인 + +따라서 **스케줄링/자원 문제가 아니라, Nova의 Keystone URL 설정 불일치** 가능성이 높다. + +1. **Keystone 엔드포인트 주소 확인부터** + +먼저 Keystone의 **정식 URL**을 확인한다: + +```bash +source /root/admin-openrc.sh + +openstack endpoint list --service identity +``` + +`http://controller:5000/v3` + +**이 URL을 그대로 `nova.conf`에 적용해야** `keystoneauth`의 discovery가 정상 동작한다. + +점검 결과, `auth_url` 값이 설정 파일별로 서로 다르게 기록되어 있었다. + +1. `vi /etc/nova/nova.conf`에서 아래 섹션의 `auth_url`을 확인하고 수정한다. + +[keystone_authtoken] +**[service_user] +[placement]** + +> 요약: nova.conf 안의 **모든 auth_url / www_authenticate_uri 를** +openstack endpoint list --service identity 에 나오는 identity URL로 맞춰준다. +> + +![image.png](images/ch2_4_img_27.png) + +1. Nova 서비스 재시작 + +```bash +systemctl restart nova-api nova-scheduler nova-conductor +systemctl status nova-api +``` + +![image.png](images/ch2_4_img_28.png) + +재시작 후 오류 유형이 다음과 같이 변경되었다. + +```bash +HttpException: 500: Server Error for url: http://controller:8774/v2.1/servers, Unexpected API Error. + +``` + +즉: + +> nova-api → Keystone 으로 인증하러 가다가 SSLError 나서 500 +> + +이 경우는 대부분 **“Nova 설정에서 Keystone URL을 HTTPS로 잘못 인식하는 상태”**를 의미한다. + +- 실제 Keystone 엔드포인트: http://controller:5000/v3/ (HTTP) +- Nova 쪽 keystoneauth는 **어딘가에서 HTTPS(https://…)로 접속을 시도** → TLS 핸드셰이크 실패 → SSLError + +따라서 수행할 작업은 다음과 같다: + +> /etc/nova/nova.conf 안에서 Keystone/Placement 관련 URL 을 +> + +> 전부 `http://controller:5000/v3` 로 통일하고, `https` 흔적을 모두 제거한다. +> + +문서 예시는 그대로 복사하기보다 현재 환경에 맞게 검증하며 반영하는 것이 안전하다. + +1. `vi /etc/nova/nova.conf`에서 + +**[keystone_authtoken], +[service_user], +[placement]** +  +`grep -n "https://" /etc/nova/nova.conf` +로 `https://` 흔적을 모두 제거한다. +2. Nova 서비스 재시작 + + ```bash + systemctl restart nova-api nova-scheduler nova-conductor + systemctl status nova-api + ``` + + +```bash +HttpException: 500: Server Error for url: http://controller:8774/v2.1/servers, Unexpected API Error. Please report this at http://bugs.launchpad.net/nova/ and attach the Nova API log if possible. + +``` + +추가 점검에서 다음 오류가 확인되었다. + +요약하면 다음과 같다: + +> `nova-api`가 Keystone에 서비스 유저(`nova`)로 인증 요청을 보내는 과정에서 `401(Unauthorized)`를 받아, 결과적으로 500 오류가 발생한 상황 +> + +즉, Nova 쪽에서 쓰는 **Keystone 계정/비밀번호가 Keystone에 등록된 값이랑 안 맞는다**는 뜻. + +1. 추가 확인 결과 `www_authenticate_uri` 일부 값이 이전 설정으로 남아 있었다. 해당 값을 함께 수정한다. + +점검 명령: + +```bash +grep -n "auth_url" /etc/nova/nova.conf +grep -n "www_authenticate_uri" /etc/nova/nova.conf +grep -n "password = " /etc/nova/nova.conf +grep -n "https://" /etc/nova/nova.conf +``` + +다음 명령으로 관련 설정을 일괄 확인할 수 있다. + +설정을 반영하고 서비스를 재시작한 뒤, VM 생성 CLI 명령을 다시 실행했다. + +![image.png](images/ch2_4_img_29.png) + +오류가 반복되어 추가 원인 분석이 필요했다. + +- 참고: 아래는 전체 에러 메시지 원문이다. + + ```bash + {'code': 500, 'created': '2025-11-24T16:41:37Z', 'message': 'Build of instance 09c24c88-0f65-42c7-a51d-6939218abc61 aborted: 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: HTTPSCo', 'details': 'Traceback (most recent call last):\n File | + | | "/usr/lib/python3/dist-packages/urllib3/connection.py", line 203, in _new_conn\n sock = connection.create_connection(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/urllib3/util/connection.py", line 85, in create_connection\n raise err\n File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 73, in create_connection\n | + | | sock.connect(sa)\n File "/usr/lib/python3/dist-packages/eventlet/greenio/base.py", line 251, in connect\n socket_checkerr(fd)\n File "/usr/lib/python3/dist- | + | | packages/eventlet/greenio/base.py", line 50, in socket_checkerr\n raise OSError(err, errno.errorcode[err])\nConnectionRefusedError: [Errno 111] ECONNREFUSED\n\nThe above exception was the | + | | direct cause of the following exception:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 791, in urlopen\n response = | + | | self._make_request(\n ^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 492, in _make_request\n raise new_e\n File | + | | "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 468, in _make_request\n self._validate_conn(conn)\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line | + | | 1097, in _validate_conn\n conn.connect()\n File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 611, in connect\n self.sock = sock = self._new_conn()\n | + | | ^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 218, in _new_conn\n raise NewConnectionError(\nurllib3.exceptions.NewConnectionError: | + | | : Failed to establish a new connection: [Errno 111] ECONNREFUSED\n\nThe above exception was the direct cause of the following | + | | exception:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/requests/adapters.py", line 667, in send\n resp = conn.urlopen(\n ^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 845, in urlopen\n retries = retries.increment(\n ^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/urllib3/util/retry.py", line 517, in increment\n raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nurllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host=\'controller\', port=443): Max retries exceeded with url: /identity (Caused by | + | | NewConnectionError(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, | + | | another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1169, in _send_request\n resp = | + | | self.session.request(method, url, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/sessions.py", line 589, in request\n resp | + | | = self.send(prep, **send_kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/sessions.py", line 703, in send\n r = adapter.send(request, | + | | **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/adapters.py", line 700, in send\n raise ConnectionError(e, | + | | request=request)\nrequests.exceptions.ConnectionError: HTTPSConnectionPool(host=\'controller\', port=443): Max retries exceeded with url: /identity (Caused by | + | | NewConnectionError(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, | + | | another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 136, in _do_create_plugin\n disc = | + | | self.get_discovery(\n ^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 703, in get_discovery\n return discover.get_discovery(\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 1742, in get_discovery\n disc = Discover(session, url, authenticated=authenticated)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 585, in __init__\n self._data = get_version_data(\n | + | | ^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 114, in get_version_data\n resp = session.get(url, headers=headers, authenticated=authenticated)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1320, in get\n return self.request(url, \'GET\', | + | | **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1057, in request\n resp = send(**kwargs)\n | + | | ^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1184, in _send_request\n raise | + | | exceptions.ConnectFailure(msg)\nkeystoneauth1.exceptions.connection.ConnectFailure: Unable to establish connection to https://controller/identity: HTTPSConnectionPool(host=\'controller\', | + | | port=443): Max retries exceeded with url: /identity (Caused by NewConnectionError(\': Failed to establish a new connection: [Errno | + | | 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist- | + | | packages/nova/compute/manager.py", line 2901, in _build_resources\n yield resources\n File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 2648, in _build_and_run_instance\n | + | | self.driver.spawn(context, instance, image_meta,\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/driver.py", line 4840, in spawn\n created_instance_dir, created_disks = | + | | self._create_image(\n ^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/driver.py", line 5251, in _create_image\n | + | | created_disks = self._create_and_inject_local_root(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/driver.py", line 5380, in | + | | _create_and_inject_local_root\n self._try_fetch_image_cache(backend, fetch_func, context,\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/driver.py", line 11652, in | + | | _try_fetch_image_cache\n image.cache(fetch_func=fetch_func,\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/imagebackend.py", line 304, in cache\n self.create_image(\n File | + | | "/usr/lib/python3/dist-packages/nova/virt/libvirt/imagebackend.py", line 682, in create_image\n prepare_template(target=base, *args, **kwargs)\n File "/usr/lib/python3/dist- | + | | packages/oslo_concurrency/lockutils.py", line 412, in inner\n return f(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/nova/virt/libvirt/imagebackend.py", line 301, in fetch_func_sync\n fetch_func(target=target, *args, **kwargs)\n File "/usr/lib/python3/dist-packages/nova/virt/libvirt/utils.py", | + | | line 495, in fetch_image\n images.fetch_to_raw(context, image_id, target, trusted_certs)\n File "/usr/lib/python3/dist-packages/nova/virt/images.py", line 228, in fetch_to_raw\n | + | | fetch(context, image_href, path_tmp, trusted_certs)\n File "/usr/lib/python3/dist-packages/nova/virt/images.py", line 109, in fetch\n IMAGE_API.download(context, image_href, | + | | dest_path=path,\n File "/usr/lib/python3/dist-packages/nova/image/glance.py", line 1311, in download\n return session.download(context, image_id, data=data,\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/nova/image/glance.py", line 394, in download\n _reraise_translated_image_exception(image_id)\n File | + | | "/usr/lib/python3/dist-packages/nova/image/glance.py", line 1044, in _reraise_translated_image_exception\n raise new_exc.with_traceback(exc_trace)\n File "/usr/lib/python3/dist- | + | | packages/nova/image/glance.py", line 391, in download\n image_chunks = self._client.call(\n ^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/nova/image/glance.py", | + | | line 191, in call\n result = getattr(controller, method)(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/glanceclient/common/utils.py", line 652, in inner\n return RequestIdProxy(wrapped(*args, **kwargs))\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/glanceclient/v2/images.py", line 249, in data\n resp, image_meta = self.http_client.get(url)\n ^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/adapter.py", line 599, in get\n return self.request(url, \'GET\', **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/glanceclient/common/http.py", line 357, in request\n resp = super(SessionClient,\n ^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/keystoneauth1/adapter.py", line 592, in request\n return self._request(url, method, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/keystoneauth1/adapter.py", line 294, in _request\n return self.session.request(url, method, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 901, in request\n auth_headers = self.get_auth_headers(auth)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1387, in get_auth_headers\n return auth.get_headers(self)\n ^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/keystoneauth1/service_token.py", line 39, in get_headers\n token = self.service_auth.get_token(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 91, in get_token\n return self.get_access(session).auth_token\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 139, in get_access\n self.auth_ref = self.get_auth_ref(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 221, in get_auth_ref\n plugin = self._do_create_plugin(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | + | | File "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 163, in _do_create_plugin\n raise | + | | exceptions.DiscoveryFailure(\nkeystoneauth1.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(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, | + | | another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 203, in _new_conn\n sock = | + | | connection.create_connection(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 85, in create_connection\n raise err\n File | + | | "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 73, in create_connection\n sock.connect(sa)\n File "/usr/lib/python3/dist-packages/eventlet/greenio/base.py", line 251, in | + | | connect\n socket_checkerr(fd)\n File "/usr/lib/python3/dist-packages/eventlet/greenio/base.py", line 50, in socket_checkerr\n raise OSError(err, | + | | errno.errorcode[err])\nConnectionRefusedError: [Errno 111] ECONNREFUSED\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n File | + | | "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 791, in urlopen\n response = self._make_request(\n ^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/urllib3/connectionpool.py", line 492, in _make_request\n raise new_e\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 468, in _make_request\n | + | | self._validate_conn(conn)\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 1097, in _validate_conn\n conn.connect()\n File "/usr/lib/python3/dist- | + | | packages/urllib3/connection.py", line 611, in connect\n self.sock = sock = self._new_conn()\n ^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/urllib3/connection.py", line 218, in _new_conn\n raise NewConnectionError(\nurllib3.exceptions.NewConnectionError: : | + | | Failed to establish a new connection: [Errno 111] ECONNREFUSED\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n File | + | | "/usr/lib/python3/dist-packages/requests/adapters.py", line 667, in send\n resp = conn.urlopen(\n ^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", | + | | line 845, in urlopen\n retries = retries.increment(\n ^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 517, in increment\n raise | + | | MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nurllib3.exceptions.MaxRetryError: | + | | HTTPSConnectionPool(host=\'controller\', port=443): Max retries exceeded with url: /identity (Caused by NewConnectionError(\': Failed | + | | to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1169, in _send_request\n resp = self.session.request(method, url, **kwargs)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/sessions.py", line 589, in request\n resp = self.send(prep, **send_kwargs)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/sessions.py", line 703, in send\n r = adapter.send(request, **kwargs)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/requests/adapters.py", line 700, in send\n raise ConnectionError(e, | + | | request=request)\nrequests.exceptions.ConnectionError: HTTPSConnectionPool(host=\'controller\', port=443): Max retries exceeded with url: /identity (Caused by | + | | NewConnectionError(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, | + | | another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 136, in _do_create_plugin\n disc = | + | | self.get_discovery(\n ^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 703, in get_discovery\n return discover.get_discovery(\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 1742, in get_discovery\n disc = Discover(session, url, authenticated=authenticated)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 585, in __init__\n self._data = get_version_data(\n | + | | ^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/discover.py", line 114, in get_version_data\n resp = session.get(url, headers=headers, authenticated=authenticated)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1320, in get\n return self.request(url, \'GET\', | + | | **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1057, in request\n resp = send(**kwargs)\n | + | | ^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1184, in _send_request\n raise | + | | exceptions.ConnectFailure(msg)\nkeystoneauth1.exceptions.connection.ConnectFailure: Unable to establish connection to https://controller/identity: HTTPSConnectionPool(host=\'controller\', | + | | port=443): Max retries exceeded with url: /identity (Caused by NewConnectionError(\': Failed to establish a new connection: [Errno | + | | 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist- | + | | packages/nova/compute/manager.py", line 2918, in _build_resources\n self._shutdown_instance(context, instance,\n File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 3153, in | + | | _shutdown_instance\n network_info = self.network_api.get_instance_nw_info(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/nova/network/neutron.py", line 2049, in get_instance_nw_info\n result = self._get_instance_nw_info(context, instance, **kwargs)\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/nova/network/neutron.py", line 2075, in _get_instance_nw_info\n nw_info = | + | | self._build_network_info_model(context, instance, networks,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/nova/network/neutron.py", line 3498, in _build_network_info_model\n data = client.list_ports(**search_opts)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/nova/network/neutron.py", line 198, in wrapper\n ret = obj(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/neutronclient/v2_0/client.py", line 815, in list_ports\n return self.list(\'ports\', self.ports_path, retrieve_all,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | + | | File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 372, in list\n for r in self._pagination(collection, path, **params):\n File "/usr/lib/python3/dist- | + | | packages/neutronclient/v2_0/client.py", line 387, in _pagination\n res = self.get(path, params=params)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/neutronclient/v2_0/client.py", line 356, in get\n return self.retry_request("GET", action, body=body,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 333, in retry_request\n return self.do_request(method, action, body=body,\n | + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 284, in do_request\n resp, replybody = | + | | self.httpclient.do_request(action, method, body=body,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/neutronclient/client.py", line 342, in do_request\n return self.request(url, method, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/neutronclient/client.py", line 330, in request\n resp = super(SessionClient, self).request(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/adapter.py", line 592, in request\n return self._request(url, method, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/adapter.py", line 294, in _request\n return self.session.request(url, method, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | + | | File "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 901, in request\n auth_headers = self.get_auth_headers(auth)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/session.py", line 1387, in get_auth_headers\n return auth.get_headers(self)\n ^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist- | + | | packages/keystoneauth1/service_token.py", line 39, in get_headers\n token = self.service_auth.get_token(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 91, in get_token\n return self.get_access(session).auth_token\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/base.py", line 139, in get_access\n self.auth_ref = self.get_auth_ref(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^\n File | + | | "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 221, in get_auth_ref\n plugin = self._do_create_plugin(session)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | + | | File "/usr/lib/python3/dist-packages/keystoneauth1/identity/generic/base.py", line 163, in _do_create_plugin\n raise | + | | exceptions.DiscoveryFailure(\nkeystoneauth1.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(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n\nDuring handling of the above exception, | + | | another exception occurred:\n\nTraceback (most recent call last):\n File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 2463, in _do_build_and_run_instance\n | + | | self._build_and_run_instance(context, instance, image,\n File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 2676, in _build_and_run_instance\n with | + | | excutils.save_and_reraise_exception():\n File "/usr/lib/python3/dist-packages/oslo_utils/excutils.py", line 227, in __exit__\n self.force_reraise()\n File "/usr/lib/python3/dist- | + | | packages/oslo_utils/excutils.py", line 200, in force_reraise\n raise self.value\n File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 2631, in _build_and_run_instance\n | + | | with self._build_resources(context, instance,\n File "/usr/lib/python3.12/contextlib.py", line 158, in __exit__\n self.gen.throw(value)\n File "/usr/lib/python3/dist- | + | | packages/nova/compute/manager.py", line 2926, in _build_resources\n raise exception.BuildAbortException(\nnova.exception.BuildAbortException: Build of instance | + | | 09c24c88-0f65-42c7-a51d-6939218abc61 aborted: 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(\': Failed to establish a new connection: [Errno 111] ECONNREFUSED\'))\n'} | + | flavor | description=, disk='5', ephemeral='0', , id='m1.small', is_disabled=, is_public='True', location=, name='m1.small', original_name='m1.small', ram='512', rxtx_factor=, swap='0', vcpus='1' + ``` + + +에러 메시지 핵심은 다음과 같다: + +> Build of instance ... aborted: 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): ... ECONNREFUSED +> + +의미: + +> 시스템 내부의 일부 설정이 Keystone을 `https://controller/identity`로 해석하여, 443 포트 접속이 계속 거절되는 상황 +> + +> (현재 환경의 Keystone 엔드포인트는 `http://controller:5000/v3` 이므로 실패) +> + +스택 트레이스 경로를 보면: + +- nova/virt/libvirt/imagebackend.py → images.fetch_to_raw → nova.image.glance → glanceclient → keystoneauth1.service_token ... +- 이는 **compute 노드(`nova-compute`)**가 Glance에서 이미지를 가져오는 과정에서 Keystone을 호출하는 경로다. + +> 즉, +> +> +> **컨트롤러 쪽 nova.conf는 어느 정도 맞았지만,** +> + +> compute 노드의 nova.conf 안에는 아직 https://controller/identity 같은 옛날 설정이 남아있다 +> + +컨트롤러뿐 아니라 `nova-compute`가 실행되는 compute 노드 설정도 동일하게 정렬해야 한다. + +1. 정리하면 원인은 compute 측 설정이었다. `compute` 노드의 `nova.conf` 점검 결과 `[neutron]` 섹션 자격 정보 불일치가 확인되었다. + +```bash +grep -n "https://" /etc/nova/nova.conf +grep -n "identity" /etc/nova/nova.conf +``` + +[keystone_authtoken] +[service_user] +[placement] + +**포인트** + +- **어떤 섹션에도 `https://controller/identity` 같은 문자열이 남지 않도록 모두 제거해야 한다.** + - auth_url, www_authenticate_uri 등 +- 도메인/프로젝트 이름도 컨트롤러와 동일하게 맞춘다: + - project_name = service + - user_domain_name = Default + - project_domain_name = Default + +1. 수정 후 `nova-compute`를 재시작한다. + +```bash +systemctl restart nova-compute +systemctl status nova-compute +``` + +컨트롤러에서 다시 확인한다: + +```bash +source /root/admin-openrc.sh +openstack compute service list --service nova-compute +``` + +1. 수정 후 컨트롤러에서 인스턴스 생성을 다시 수행한다. + +![image.png](images/ch2_4_img_30.png) + +인스턴스 생성이 정상 완료되었다. + +![image.png](images/ch2_4_img_31.png) + +Horizon에서도 동일하게 생성이 확인된다. diff --git a/lectures/ch2/ch2_4_8_lec.qmd b/lectures/ch2/ch2_4_8_lec.qmd new file mode 100644 index 0000000..867eee4 --- /dev/null +++ b/lectures/ch2/ch2_4_8_lec.qmd @@ -0,0 +1,75 @@ +--- +title: "2-4장. NTP(Chrony) 동기화" +--- + +# 3. NTP + +### **공식 문서** + +- 환경 > NTP: https://docs.openstack.org/install-guide/environment-ntp.html + +[개념 정리] + +- 노드들 시계가 어긋나면 토큰 만료, 메시지 큐 에러 등 **문제가 발생함**. +- 그래서 **controller는 외부 NTP 서버를 보고**, + + 다른 노드들은 **controller를 보고** 시간 맞추게 함. + + +### **3-1. controller 노드** + +https://docs.openstack.org/install-guide/environment-ntp-controller.html + +```yaml +# controller +apt update +apt install -y chrony +``` + +/etc/chrony/chrony.conf 편집: + +```yaml +# 예시 (맨 아래쪽에 추가) +server 0.asia.pool.ntp.org iburst +server 1.asia.pool.ntp.org iburst + +# 우리 management 네트워크 허용 (10.100.100.0/24 대역) +allow 10.100.100.0/24 +``` + +서비스 재시작/확인: + +```yaml +service chrony restart +chronyc sources +``` + +**3-2. compute1 노드** + +```yaml +# compute1 +apt update +apt install -y chrony +``` + +/etc/chrony/chrony.conf 에 controller를 NTP 서버로 지정: + +```yaml +server controller iburst # 또는 10.100.100.11 +``` + +서비스 재시작/확인: + +```yaml +service chrony restart +chronyc sources +``` + +> 핵심: +> +> +> **모든 노드가 같은 시계를 본다** +> + +> controller는 기준 NTP 서버로 두고, 나머지 노드는 controller를 참조하도록 설정한다. +> diff --git a/lectures/ch2/ch2_4_9_lec.qmd b/lectures/ch2/ch2_4_9_lec.qmd new file mode 100644 index 0000000..f324d21 --- /dev/null +++ b/lectures/ch2/ch2_4_9_lec.qmd @@ -0,0 +1,44 @@ +--- +title: "2-4장. OpenStack 패키지 저장소 활성화" +--- + +# 4. **OpenStack 패키지 (Ubuntu Cloud Archive 활성화)** + +### **공식 문서** + +- Ubuntu용 패키지: https://docs.openstack.org/ko_KR/install-guide/environment-packages-ubuntu.html + +[개념 정리] + +- Ubuntu 24.04 기본 저장소에는 Caracal까지 들어있고, + + **Epoxy 같은 최신 릴리즈는 Cloud Archive에서 가져와야 함.** + +- “Epoxy 릴리즈 쓰겠다”라는 선언을 각 노드에서 하는 단계. +- 유의사항: root 에서만 깔리니 처음에 root로 진입해야함 + + ```yaml + sudo -i + ``` + + +### **4-1. 모든 OpenStack 노드 (controller, compute)** + +```yaml +# 공통 +apt install -y software-properties-common + +# Epoxy용 Cloud Archive 활성화 (Ubuntu 24.04 기준) +add-apt-repository cloud-archive:epoxy + +apt update +``` + +OpenStack CLI도 미리 깔아두면 편함: + +```yaml +apt install -y python3-openstackclient +``` + +이걸 해야 나중에 apt install keystone 같은 게 **Epoxy 버전**으로 깔림. + diff --git a/lectures/ch2/ch2_4_lec.qmd b/lectures/ch2/ch2_4_lec.qmd new file mode 100644 index 0000000..2ff4bb3 --- /dev/null +++ b/lectures/ch2/ch2_4_lec.qmd @@ -0,0 +1,256 @@ +--- +title: "2-4장. 오픈스택 필수 컴포넌트 수동 설치해보기" +--- + +# 2-4장. 오픈스택 필수 컴포넌트 수동 설치해보기 + +## 학습 목표 +- 3노드(`controller`, `compute1`, `storage1`) 기반 실습 환경을 구성한다. +- NIC 3개(MGMT/Tenant/Provider) 네트워크를 분리해 고정 IP로 설계한다. +- OpenStack 본설치 전에 필수 컴포넌트(NTP, DB, MQ, 캐시, Etcd)를 수동으로 준비한다. + +## 2.4.1 실습 범위 + +이 장은 OpenStack 서비스(Keystone/Glance/Nova/Neutron/Cinder)를 설치하기 전에, +"환경 챕터"를 완성하는 것을 목표로 한다. + +핵심은 다음 2가지다. + +1. 노드 간 이름 해석과 시간 동기화를 안정화한다. +2. controller 중심의 공통 인프라(DB/MQ/Cache/Etcd)를 미리 준비한다. + +## 2.4.2 노드 사양 및 역할 + +실습 VM은 총 3개다. + +| 노드 | 호스트명 | 주요 역할 | 권장 리소스 | +|---|---|---|---| +| Control | `controller` | API/스케줄러/DB/MQ/캐시/Etcd | vCPU 4, RAM 8GB, Disk 32GB+ | +| Compute | `compute1` | Nova Compute, 하이퍼바이저 | vCPU 4, RAM 4GB+, Disk 32GB+ | +| Storage | `storage1` | Cinder Volume(블록 스토리지) | vCPU 4, RAM 4GB+, Disk 32GB+ | + +## 2.4.3 네트워크 요구사항 (사용자 기준) + +- 모든 노드는 NIC 3개를 사용한다. + +| 역할 | 대역 | 인터넷 | DHCP | 비고 | +|---|---|---|---|---| +| MGMT | `10.100.100.0/24` | O | X | 관리/API/SSH 네트워크 | +| Tenant (VXLAN) | `10.100.200.0/24` | X | X | 오버레이 내부 통신 네트워크 | +| Provider | `10.200.100.0/24` | O | X | 노드 NIC는 무IP(인터페이스만 유지) 가능 | + +권장 IP 계획: + +| 노드 | MGMT | Tenant (VXLAN) | Provider | +|---|---|---|---| +| controller | `10.100.100.11/24` | `10.100.200.11/24` | 무IP(권장) | +| compute1 | `10.100.100.31/24` | `10.100.200.31/24` | 무IP(권장) | +| storage1 | `10.100.100.41/24` | `10.100.200.41/24` | 무IP(권장) | + +참고: + +- 기본 게이트웨이는 MGMT NIC에만 둔다. +- Provider는 노드 자체 IP 없이 브리지/물리 경로 용도로만 사용할 수 있다. + +## 2.4.4 Security 단계 + +공식 문서의 Security 단계는 "필수는 아니지만" 실제 운영에서는 매우 중요하다. + +- 서비스 비밀번호 정책을 미리 정한다. +- 노드별 공통 환경변수 파일(예: `admin-openrc.sh`)에 민감정보를 정리한다. + +공식 참고: + +- + +## 2.4.5 호스트명 설정 + +먼저 3개 노드의 호스트명을 고정한다. + +```bash +# controller +sudo hostnamectl set-hostname controller + +# compute +sudo hostnamectl set-hostname compute1 + +# storage +sudo hostnamectl set-hostname storage1 +``` + +적용 확인: + +```bash +hostname +``` + +## 2.4.6 `/etc/hosts` 통일 + +OpenStack는 서비스 간 통신이 많기 때문에, +DNS가 불안정한 실습 환경에서는 `/etc/hosts` 고정 매핑이 안정적이다. + +모든 노드에 동일하게 아래 내용을 반영한다. + +```text +127.0.0.1 localhost + +10.100.100.11 controller +10.100.100.31 compute1 +10.100.100.41 storage1 + +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +``` + +주의: + +- `127.0.1.1 <옛 호스트명>` 항목은 제거 또는 주석 처리한다. +- 세 노드에서 `ping controller`, `ping compute1`, `ping storage1` 결과가 동일해야 한다. + +## 2.4.7 Netplan 고정 IP 설정 (NIC 3개) + +실제 NIC 이름은 환경마다 다르므로 먼저 확인한다. + +```bash +ip -br a +``` + +이 문서에서는 다음으로 가정한다. + +- `ens18`: MGMT +- `ens19`: Tenant +- `ens20`: Provider + +### 2.4.7.1 controller + +```yaml +network: + version: 2 + renderer: networkd + ethernets: + ens18: # MGMT + addresses: + - 10.100.100.11/24 + routes: + - to: 0.0.0.0/0 + via: 10.100.100.1 + nameservers: + addresses: [8.8.8.8, 1.1.1.1] + + ens19: # Tenant (VXLAN) + addresses: + - 10.100.200.11/24 + dhcp4: false + + ens20: # Provider + dhcp4: false + dhcp6: false + optional: true +``` + +### 2.4.7.2 compute1 + +```yaml +network: + version: 2 + renderer: networkd + ethernets: + ens18: # MGMT + addresses: + - 10.100.100.31/24 + routes: + - to: 0.0.0.0/0 + via: 10.100.100.1 + nameservers: + addresses: [8.8.8.8, 1.1.1.1] + + ens19: # Tenant (VXLAN) + addresses: + - 10.100.200.31/24 + dhcp4: false + + ens20: # Provider + dhcp4: false + dhcp6: false + optional: true +``` + +### 2.4.7.3 storage1 + +```yaml +network: + version: 2 + renderer: networkd + ethernets: + ens18: # MGMT + addresses: + - 10.100.100.41/24 + routes: + - to: 0.0.0.0/0 + via: 10.100.100.1 + nameservers: + addresses: [8.8.8.8, 1.1.1.1] + + ens19: # Tenant (VXLAN) + addresses: + - 10.100.200.41/24 + dhcp4: false + + ens20: # Provider + dhcp4: false + dhcp6: false + optional: true +``` + +### 2.4.7.4 적용 및 검증 + +```bash +sudo netplan apply +ip -br a + +ping -c 2 controller +ping -c 2 compute1 +ping -c 2 storage1 +ping -c 2 8.8.8.8 +``` + + +## 2.4.8 이후 학습 페이지 + +초기 세팅(2.4.7.4 적용 및 검증) 이후 내용은 하위 페이지로 분리했다. + +### 환경 컴포넌트 준비 + +- [2.4.8 NTP(Chrony) 동기화](ch2_4_8_lec.qmd) +- [2.4.9 OpenStack 패키지 저장소 활성화](ch2_4_9_lec.qmd) +- [2.4.10 SQL Database (MariaDB)](ch2_4_10_lec.qmd) +- [2.4.11 Message Queue (RabbitMQ)](ch2_4_11_lec.qmd) +- [2.4.12 Memcached](ch2_4_12_lec.qmd) +- [2.4.13 Etcd](ch2_4_13_lec.qmd) +- [2.4.14 사전 점검 체크리스트](ch2_4_14_lec.qmd) +- [2.4.15 OpenStack 서비스 설치 순서](ch2_4_15_lec.qmd) +- [2.4.16 공통 비밀번호 변수 예시](ch2_4_16_lec.qmd) + +### OpenStack 서비스 설치 + +- [2.4.17 Keystone 설치](ch2_4_17_lec.qmd) +- [2.4.18 Glance 설치](ch2_4_18_lec.qmd) +- [2.4.19 Placement 설치](ch2_4_19_lec.qmd) +- [2.4.20 Nova 설치](ch2_4_20_lec.qmd) +- [2.4.21 Neutron 설치](ch2_4_21_lec.qmd) +- [2.4.22 Horizon 설치](ch2_4_22_lec.qmd) +- [2.4.23 Horizon 대시보드에서 인스턴스 생성](ch2_4_23_lec.qmd) +- [2.4.24 트러블슈팅 모음](ch2_4_24_lec.qmd) + +### 트러블슈팅 상세 + +- [2.4.25 Keystone 트러블슈팅](ch2_4_25_lec.qmd) +- [2.4.26 Glance 트러블슈팅 (DB 권한)](ch2_4_26_lec.qmd) +- [2.4.27 Glance 트러블슈팅 (Quota)](ch2_4_27_lec.qmd) +- [2.4.28 Neutron OVS 스위치 설정](ch2_4_28_lec.qmd) +- [2.4.29 Horizon 트러블슈팅 (접속/설정)](ch2_4_29_lec.qmd) +- [2.4.30 외부 접근 IP 대역 설정 가이드](ch2_4_30_lec.qmd) +- [2.4.31 Horizon 인스턴스 생성 오류 해결](ch2_4_31_lec.qmd) diff --git a/lectures/ch2/images/ch2_4_img_01.png b/lectures/ch2/images/ch2_4_img_01.png new file mode 100644 index 0000000..853b68e Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_01.png differ diff --git a/lectures/ch2/images/ch2_4_img_02.png b/lectures/ch2/images/ch2_4_img_02.png new file mode 100644 index 0000000..3ab767d Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_02.png differ diff --git a/lectures/ch2/images/ch2_4_img_03.png b/lectures/ch2/images/ch2_4_img_03.png new file mode 100644 index 0000000..92dcb62 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_03.png differ diff --git a/lectures/ch2/images/ch2_4_img_04.png b/lectures/ch2/images/ch2_4_img_04.png new file mode 100644 index 0000000..a54334d Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_04.png differ diff --git a/lectures/ch2/images/ch2_4_img_05.png b/lectures/ch2/images/ch2_4_img_05.png new file mode 100644 index 0000000..6ad214c Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_05.png differ diff --git a/lectures/ch2/images/ch2_4_img_06.png b/lectures/ch2/images/ch2_4_img_06.png new file mode 100644 index 0000000..8172013 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_06.png differ diff --git a/lectures/ch2/images/ch2_4_img_07.png b/lectures/ch2/images/ch2_4_img_07.png new file mode 100644 index 0000000..93bb768 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_07.png differ diff --git a/lectures/ch2/images/ch2_4_img_08.png b/lectures/ch2/images/ch2_4_img_08.png new file mode 100644 index 0000000..302a1a8 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_08.png differ diff --git a/lectures/ch2/images/ch2_4_img_09.png b/lectures/ch2/images/ch2_4_img_09.png new file mode 100644 index 0000000..5cd998b Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_09.png differ diff --git a/lectures/ch2/images/ch2_4_img_10.png b/lectures/ch2/images/ch2_4_img_10.png new file mode 100644 index 0000000..4b9ad53 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_10.png differ diff --git a/lectures/ch2/images/ch2_4_img_11.png b/lectures/ch2/images/ch2_4_img_11.png new file mode 100644 index 0000000..5125d73 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_11.png differ diff --git a/lectures/ch2/images/ch2_4_img_12.png b/lectures/ch2/images/ch2_4_img_12.png new file mode 100644 index 0000000..bce008e Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_12.png differ diff --git a/lectures/ch2/images/ch2_4_img_13.png b/lectures/ch2/images/ch2_4_img_13.png new file mode 100644 index 0000000..41a02b9 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_13.png differ diff --git a/lectures/ch2/images/ch2_4_img_14.png b/lectures/ch2/images/ch2_4_img_14.png new file mode 100644 index 0000000..a5c3f1e Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_14.png differ diff --git a/lectures/ch2/images/ch2_4_img_15.png b/lectures/ch2/images/ch2_4_img_15.png new file mode 100644 index 0000000..656325b Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_15.png differ diff --git a/lectures/ch2/images/ch2_4_img_16.png b/lectures/ch2/images/ch2_4_img_16.png new file mode 100644 index 0000000..a6a8d6d Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_16.png differ diff --git a/lectures/ch2/images/ch2_4_img_17.png b/lectures/ch2/images/ch2_4_img_17.png new file mode 100644 index 0000000..958a934 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_17.png differ diff --git a/lectures/ch2/images/ch2_4_img_18.png b/lectures/ch2/images/ch2_4_img_18.png new file mode 100644 index 0000000..d091fa2 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_18.png differ diff --git a/lectures/ch2/images/ch2_4_img_19.png b/lectures/ch2/images/ch2_4_img_19.png new file mode 100644 index 0000000..2086b97 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_19.png differ diff --git a/lectures/ch2/images/ch2_4_img_20.png b/lectures/ch2/images/ch2_4_img_20.png new file mode 100644 index 0000000..98b0a7c Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_20.png differ diff --git a/lectures/ch2/images/ch2_4_img_21.png b/lectures/ch2/images/ch2_4_img_21.png new file mode 100644 index 0000000..0882483 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_21.png differ diff --git a/lectures/ch2/images/ch2_4_img_22.png b/lectures/ch2/images/ch2_4_img_22.png new file mode 100644 index 0000000..3543424 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_22.png differ diff --git a/lectures/ch2/images/ch2_4_img_23.png b/lectures/ch2/images/ch2_4_img_23.png new file mode 100644 index 0000000..2b0cb59 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_23.png differ diff --git a/lectures/ch2/images/ch2_4_img_24.png b/lectures/ch2/images/ch2_4_img_24.png new file mode 100644 index 0000000..7e9197e Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_24.png differ diff --git a/lectures/ch2/images/ch2_4_img_25.png b/lectures/ch2/images/ch2_4_img_25.png new file mode 100644 index 0000000..8e1aa9d Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_25.png differ diff --git a/lectures/ch2/images/ch2_4_img_26.png b/lectures/ch2/images/ch2_4_img_26.png new file mode 100644 index 0000000..b0b7499 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_26.png differ diff --git a/lectures/ch2/images/ch2_4_img_27.png b/lectures/ch2/images/ch2_4_img_27.png new file mode 100644 index 0000000..105fe8a Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_27.png differ diff --git a/lectures/ch2/images/ch2_4_img_28.png b/lectures/ch2/images/ch2_4_img_28.png new file mode 100644 index 0000000..751476c Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_28.png differ diff --git a/lectures/ch2/images/ch2_4_img_29.png b/lectures/ch2/images/ch2_4_img_29.png new file mode 100644 index 0000000..98e7894 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_29.png differ diff --git a/lectures/ch2/images/ch2_4_img_30.png b/lectures/ch2/images/ch2_4_img_30.png new file mode 100644 index 0000000..8d08a48 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_30.png differ diff --git a/lectures/ch2/images/ch2_4_img_31.png b/lectures/ch2/images/ch2_4_img_31.png new file mode 100644 index 0000000..48d9dc7 Binary files /dev/null and b/lectures/ch2/images/ch2_4_img_31.png differ diff --git a/lectures/ch2_lec.qmd b/lectures/ch2_lec.qmd index 2cbb0a9..17d7f31 100644 --- a/lectures/ch2_lec.qmd +++ b/lectures/ch2_lec.qmd @@ -2,7 +2,7 @@ title: "2장. 오픈스택 설치 가이드" --- -이 강의에서는 오픈스택을 설치하는 두 가지 주요 방법을 다룹니다: +이 강의에서는 오픈스택 실습 환경 구축과 설치 방식별 접근을 다룹니다: ## 강의 구성 1. [2-1장. DevStack으로 구성하기](ch2/ch2_1_lec.qmd) @@ -15,6 +15,11 @@ title: "2장. 오픈스택 설치 가이드" - 멀티 노드 구성 - 컨테이너 기반 배포 +3. [2-4장. 오픈스택 필수 컴포넌트 수동 설치해보기](ch2/ch2_4_lec.qmd) + - 3노드(controller/compute/storage) 기준 사전 환경 구성 + - MGMT/Tenant/Provider 네트워크 분리 + - OpenStack 설치 전 필수 컴포넌트 수동 준비 + ## 준비사항 - Linux 기본 명령어 이해 - 가상화 개념 이해 @@ -22,4 +27,4 @@ title: "2장. 오픈스택 설치 가이드" ## 참고 자료 - [환경 설정 가이드](/guides/basic.html) -- [문제 해결 가이드](/guides/advanced.html) \ No newline at end of file +- [문제 해결 가이드](/guides/advanced.html) diff --git a/lectures/index.qmd b/lectures/index.qmd index ab4d52c..49deb0d 100644 --- a/lectures/index.qmd +++ b/lectures/index.qmd @@ -9,7 +9,7 @@ title: "오픈스택 강의 자료" - [2-1장. VirtualBox으로 기본 환경 구성하기](ch2/ch2_1_lec.qmd) - [2-2장. VMWare Fusion으로 기본 환경 구성하기](ch2/ch2_2_lec.qmd) - [2-3장. Proxmox로 기본 환경 구성하기](ch2/ch2_2_lec.qmd) - - [2-4장. 오픈스택 필수 컴포넌트 수동 설치해보기](ch2/ch2_2_lec.qmd) + - [2-4장. 오픈스택 필수 컴포넌트 수동 설치해보기](ch2/ch2_4_lec.qmd) - [3장. Keystone]() - [3-1장. Keystone 토큰]() - [4장. Nova]()