---
title: "OpenStack 환경에서 Designate를 활용한 내부 DNS 서버 구축 가이드"
description: "OpenStack 환경에 Designate를 활용해 내부 DNS 서비스를 구축하는 전체 과정을 안내한다. Keystone에 사용자·서비스를 등록하고 API 엔드포인트를 추가한 뒤, Designate와 BIND9 패키지를 설치하고 데이터베이스·RabbitMQ·Keystone 연동 설정을 진행한다. BIND9에 rndc 키와 AppArmor 권한을 설정하고, "
date: "2025-06-09"
last_modified: "2026-08-06T00:35:00.000Z"
type: "Post"
tags:
  - "openstack"
  - "service"
  - "troubleshooting"
categories:
  - "📗 Docs"
series:
  - "오픈스택 운영"
canonical_url: "https://blog.pieroot.xyz/designate-dns"
markdown_url: "https://blog.pieroot.xyz/designate-dns.md"
---

# OpenStack 환경에서 Designate를 활용한 내부 DNS 서버 구축 가이드

OpenStack 환경에 Designate를 활용해 내부 DNS 서비스를 구축하는 전체 과정을 안내한다. Keystone에 사용자·서비스를 등록하고 API 엔드포인트를 추가한 뒤, Designate와 BIND9 패키지를 설치하고 데이터베이스·RabbitMQ·Keystone 연동 설정을 진행한다. BIND9에 rndc 키와 AppArmor 권한을 설정하고, 

OpenStack 내부에 GitLab 서버와 GitLab Runner가 함께 돌아가고 있는 상황이다. 문제는 Runner가 이미지를 빌드할 때 **public network를 거쳐서 데이터를 받아오면**, 가뜩이나 느린 네트워크에서 더 말도 안 되게 느린 속도를 자랑한다는 것이다.

그래서 내부에 자체 DNS 서버를 구성하려고 했는데, SSL 인증이 시원찮은 관계로 다른 방법을 찾던 중 — OpenStack 서비스들 중에 DNS를 제공하는 놈이 있다는 소식을 듣게 되었다. 바로 **Designate**다.

이번 글에서는 OpenStack 환경에 Designate(DNSaaS)를 설치하고, Horizon 대시보드까지 연동하는 전체 과정을 다룬다.

> **이 글에서 다루는 내용**
> 
> - Designate란 무엇인가, 왜 필요한가
> 
> - Keystone에 서비스 등록 및 엔드포인트 생성
> 
> - BIND9 설치 및 연동 설정
> 
> - designate.conf 주요 설정 항목 해설
> 
> - pools.yaml 구성과 DNS 풀 업데이트
> 
> - Horizon 대시보드 플러그인 설치
> 
> - 트러블슈팅 (rndc.key 권한 문제 등)

---

### 🤔 Designate란?

> **주의**
> 
> Designate는 Keystone, RabbitMQ, MySQL(또는 MariaDB)에 의존한다. 이 서비스들이 불안정한 상태에서 Designate를 설치하면 디버깅 지옥에 빠질 수 있다. ~~경험담이다~~

![image](https://blog.pieroot.xyz/api/image-proxy?id=1ca169f8-e4d8-4eef-a175-34bf8e398229&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-8067-a53c-c44005a20138)

[Installing OpenStack DNS as a Service &#8212; designate 22.1.0.dev65 documentation](https://docs.openstack.org/designate/latest/install/index.html)

Designate는 OpenStack의 **DNSaaS(DNS as a Service)** 프로젝트다. 위 링크의 제목처럼 DNS를 위한 서비스인데, 요즘 뭐든 *...as a Service*를 붙이면 좀 더 그럴듯해 보이는지 여기저기 다 붙는다. ~~LBaaS, DNSaaS, 다음은 뭐가 나올까~~

쉽게 말하면, REST API로 DNS 레코드(zone, record)를 관리할 수 있고, 뒤에서는 **BIND9**이나 **PowerDNS** 같은 실제 DNS 서버를 조종하는 구조다.

> **Designate의 핵심 구성 요소**
> 
> - **API**: 외부 요청을 받는 REST API 서버 (포트 9001)
> 
> - **Central**: 비즈니스 로직을 처리하고 DB에 데이터를 저장하는 핵심 서비스
> 
> - **Worker**: 실제 DNS 서버(BIND9 등)에 zone 변경 사항을 전파하는 역할
> 
> - **Producer**: 주기적인 작업(zone 만료, 갱신 등)을 스케줄링
> 
> - **Mini DNS (mdns)**: zone transfer(AXFR)를 담당하는 경량 DNS 서버
> 
> - **Message Queue**: 컴포넌트 간 통신을 위한 RabbitMQ

위 그림처럼 여러 API와 컴포넌트들이 유기적으로 연결되어 동작한다. Designate API로 요청이 들어오면 Central이 처리하고, Worker가 BIND9에 반영하는 흐름이다.

> **Neutron/Nova 연동도 가능하다**
> 
> Designate는 Neutron과 Nova의 notification을 받아서 **floating IP가 할당되거나 인스턴스가 생성될 때 자동으로 DNS 레코드를 생성**할 수 있다. 다만 이번 글에서는 기본 설치에 집중하고, Neutron 연동은 다루지 않는다.

---

### 🔧 사전 준비

우선 OpenStack에서 기본 패키지(Nova, Neutron, Keystone, Glance, Placement)는 모두 설치가 되어있다는 가정 하에 진행된다. 모든 기능이 정상 동작하고 **안정화된 상태**에서 진행하기를 권장한다.

우리가 해야 할 건 크게 두 가지다:

1. **Designate 서비스 설치** (Keystone 등록 → BIND9 설정 → Designate 설정 → 패키지 기동)

1. **Horizon에서 사용 가능하도록 대시보드 플러그인 설치**

---

### ⚙️ 서비스 생성

기존에 만들어두었던 openrc 파일을 통해 환경변수를 설정한다.

```bash
source admin-openrc
```

#### Keystone에 사용자 및 서비스 등록

Designate 전용 사용자를 생성하고, 서비스 프로젝트에 admin 역할을 부여한다.

```bash
# designate 사용자 생성
openstack user create --domain default --password-prompt designate

# service 프로젝트에 admin 역할 할당
openstack role add --project service --user designate admin

# DNS 서비스 등록
openstack service create --name designate --description "DNS" dns
```

> **각 명령어 해설**
> 
> - `openstack user create`: Keystone에 designate 사용자를 생성한다. `--password-prompt`로 비밀번호를 직접 입력
> 
> - `openstack role add`: designate 사용자에게 service 프로젝트의 admin 권한을 부여한다. 이게 있어야 다른 OpenStack 서비스들과 통신할 수 있다
> 
> - `openstack service create`: Keystone 서비스 카탈로그에 DNS 타입으로 등록한다

#### API 엔드포인트 추가

DNS 서비스를 API 엔드포인트에 추가한다.

```bash
openstack endpoint create --region RegionOne \
  dns public http://dms-controller:9001

openstack endpoint create --region RegionOne \
  dns internal http://dms-controller:9001

openstack endpoint create --region RegionOne \
  dns admin http://dms-controller:9001
```

레퍼런스에는 public만 추가하면 된다고 나와있지만 **그냥 다 추가하면 된다**. 그리고 이런 엔드포인트는 가급적이면 VIP나, VIP가 연결되어있는 hostname으로 설정하는 게 좋다.

> **왜 9001 포트인가?**
> 
> Designate API의 기본 리슨 포트가 9001이다. 이 포트는 나중에 `designate.conf`의 `[service:api]` 섹션에서 변경할 수 있지만, 공식 문서 기준으로 9001을 사용한다.

---

### 📦 패키지 설치

#### Designate 패키지

```bash
apt-get install designate
```

#### 데이터베이스 생성

designate 전용 데이터베이스와 사용자를 생성한다.

```sql
-- MySQL에 접속하여 실행
CREATE DATABASE designate CHARACTER SET utf8 COLLATE utf8_general_ci;

GRANT ALL PRIVILEGES ON designate.* TO 'designate'@'localhost' \
IDENTIFIED BY 'DESIGNATE_DBPASS';

GRANT ALL PRIVILEGES ON designate.* TO 'designate'@'%' \
IDENTIFIED BY 'DESIGNATE_DBPASS';
```

> **비밀번호 보안**
> 
> `DESIGNATE_DBPASS`는 반드시 강력한 비밀번호로 변경해야 한다. 복잡한 것보다 **길이가 긴 게** 훨씬 중요하다. 최소 20자 이상을 권장한다.

#### BIND9 설치

Linux에서 DNS 설정을 하기 위해 주로 사용되는 BIND9 패키지를 설치한다.

```bash
apt-get install bind9 bind9utils bind9-doc
```

BIND9이 무엇을 하는지 깊게 서술하지는 않겠지만, 간단하게 말하면 **실제 DNS 레코드를 관리하고 질의에 응답하는 서비스**를 이 BIND9이 수행한다. Designate는 BIND9의 "관리자" 역할이고, BIND9이 실제 "일꾼"인 셈이다.

> **BIND9 말고 다른 선택지는?**
> 
> Designate는 플러그인 방식으로 다양한 DNS 백엔드를 지원한다:
> 
> - **BIND9**: 가장 널리 사용되는 오픈소스 DNS 서버. 안정적이고 문서가 풍부하다
> 
> - **PowerDNS**: 고성능 DNS 서버. SQL 기반 레코드 관리가 가능하여 대규모 환경에 적합하다
> 
> - **Akamai, Infoblox**: 상용 DNS 서비스와의 연동도 가능
> 
> 이 글에서는 가장 범용적인 **BIND9**을 사용한다.

---

### 🔑 BIND9 설정

#### rndc 키 생성

BIND9에 명령을 전달하기 위한 rndc(Remote Name Daemon Control) 인증 키를 생성한다.

```bash
rndc-confgen -a -k designate -c /etc/designate/rndc.key
```

> **rndc란?**
> 
> `rndc`는 BIND9을 원격으로 제어하기 위한 도구다. Designate의 Worker가 zone을 추가하거나 삭제할 때 이 rndc를 통해 BIND9에 명령을 전달한다. 인증 키가 없으면 아무나 DNS 서버를 조작할 수 있으니, 반드시 키 기반 인증을 사용해야 한다.

#### AppArmor 설정

Ubuntu에서는 AppArmor가 BIND9의 파일 접근을 제한하고 있다. rndc.key 파일에 접근할 수 있도록 설정을 추가해야 한다.

**`/etc/apparmor.d/usr.sbin.named`** 파일에 아래 한 줄을 추가한다:

```bash
/etc/designate/rndc.key r,
```

이 설정은 BIND9(named) 프로세스가 designate의 rndc.key 파일을 **읽을 수 있도록** 허용하는 것이다. 추가한 뒤 AppArmor를 재시작한다:

```bash
sudo systemctl reload apparmor.service
```

#### named.conf.options 수정

BIND9의 핵심 설정 파일인 **`/etc/bind/named.conf.options`**를 수정한다. `...`으로 표시된 부분은 기존 내용을 유지하라는 의미다.

```bash
...
include "/etc/designate/rndc.key";

options {
    ...
    allow-new-zones yes;
    request-ixfr no;
    listen-on port 53 { 127.0.0.1; };
    recursion no;
    allow-query { 127.0.0.1; };
};

controls {
  inet 127.0.0.1 port 953
    allow { 127.0.0.1; } keys { "designate"; };
};
```

> **설정값 해설**
> 
> - `include "/etc/designate/rndc.key"`: 아까 생성한 rndc 키 파일을 로드한다
> 
> - `allow-new-zones yes`: Designate가 동적으로 새 zone을 추가할 수 있도록 허용
> 
> - `request-ixfr no`: 증분 zone transfer(IXFR) 대신 전체 transfer(AXFR)를 사용. Designate의 mdns와의 호환성을 위해 끄는 게 안전하다
> 
> - `listen-on port 53 { 127.0.0.1; }`: DNS 서비스를 로컬에서만 리슨. 외부 직접 질의를 차단한다
> 
> - `recursion no`: 재귀 질의를 비활성화. 이 서버는 authoritative DNS 전용이다
> 
> - `allow-query { 127.0.0.1; }`: 로컬에서만 질의를 허용
> 
> - `controls`: rndc 명령을 127.0.0.1:953에서만 수신하고, designate 키로 인증한다

![image](https://blog.pieroot.xyz/api/image-proxy?id=8c062a59-4ea8-45a0-b5b4-2d073c6fe118&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-8022-afc4-ca0e0e687b48)

설정값들을 넣으면 위 사진처럼 될 것이다.

DNS 서비스를 재시작한다:

```bash
systemctl restart bind9.service
```

---

### ⚙️ Designate 설정

이제 본격적으로 Designate의 설정 파일인 **`/etc/designate/designate.conf`**를 수정한다.

> **주의!**
> 
> 이 파일을 열어본 사람이라면 당황스러울 수 있다. 아래에서 설명하는 섹션들 중 상당수가 **기본 파일에는 존재하지 않는다**. 없는 섹션은 파일 맨 아래에 그대로 추가해주면 된다.

#### API 서비스 설정

```toml
[service:api]
listen = 0.0.0.0:9001
auth_strategy = keystone
enable_api_v2 = True
enable_api_admin = True
enable_host_header = True
enabled_extensions_admin = quotas, reports
```

> **설정 해설**
> 
> - `listen = 0.0.0.0:9001`: 모든 인터페이스의 9001 포트에서 API 요청을 수신한다
> 
> - `auth_strategy = keystone`: Keystone을 통해 인증을 수행한다
> 
> - `enable_api_v2 = True`: Designate API v2를 활성화. 현재 표준 API 버전이다
> 
> - `enable_api_admin = True`: 관리자용 API를 활성화. quota 관리 등에 필요하다
> 
> - `enable_host_header = True`: HTTP Host 헤더를 사용한 라우팅을 허용한다
> 
> - `enabled_extensions_admin`: 관리자 API에서 사용할 확장 기능(quota, report)을 지정

#### Keystone 인증 설정

```toml
[keystone_authtoken]
auth_type = password
username = designate
password = DESIGNATE_PASS
project_name = service
project_domain_name = Default
user_domain_name = Default
www_authenticate_uri = http://dms-controller:5000/
auth_url = http://dms-controller:5000/
memcached_servers = dms-controller1:11211,dms-controller2:11211
```

여느 OpenStack 서비스와 마찬가지로 Keystone 인증 토큰 설정이다. `DESIGNATE_PASS`는 아까 `openstack user create`에서 입력한 비밀번호와 동일해야 한다.

#### RabbitMQ 설정

```toml
[DEFAULT]
# ...
transport_url = rabbit://openstack:RABBIT_PASS@dms-controller1:5672,openstack:RABBIT_PASS@dms-controller2:5672
```

> **RabbitMQ 클러스터 팁**
> 
> RabbitMQ 서버가 클러스터로 여러 개 있다면, memcached 서버처럼 **여러 개의 URL을 나열**하는 게 좋다. 하나가 죽어도 나머지에서 메시지를 처리할 수 있어 가용성이 올라간다. 물론 단일 서버라면 하나만 적어도 된다.

#### 데이터베이스 연결 설정

```toml
[storage:sqlalchemy]
connection = mysql+pymysql://designate:DESIGNATE_DBPASS@dms-controller/designate
```

아까 MySQL에서 생성한 designate 데이터베이스에 연결하는 설정이다. 이 섹션도 기본 파일에는 없으니 그대로 파일 맨 아래에 추가하면 된다.

#### 데이터베이스 동기화

설정 파일이 완료되었다면 데이터베이스 스키마를 동기화한다:

```bash
su -s /bin/sh -c "designate-manage database sync" designate
```

> **`su -s /bin/sh -c "..." designate`**가 뭔지 궁금하다면?
> 
> `designate` 사용자 권한으로 명령어를 실행하는 것이다. OpenStack 서비스들은 보안을 위해 각자 전용 시스템 사용자를 가지고 있고, DB 마이그레이션도 해당 사용자 권한으로 수행하는 것이 원칙이다.

#### 핵심 서비스 시작

```bash
# API와 Central 서비스 시작
systemctl start designate-central designate-api

# 부팅 시 자동 시작 설정
systemctl enable designate-central designate-api
```

---

### 🏊 Pools 설정

Designate에서 **Pool**은 DNS 서버들의 그룹이다. 어떤 네임서버를 사용할지, zone transfer는 어디서 받을지 등을 정의한다.

**`/etc/designate/pools.yaml`** 파일을 생성하고 아래 내용을 넣는다:

```yaml
- name: default
  # Pool 이름은 한번 생성하면 변경 불가. 삭제 후 재생성만 가능하다.
  description: Default Pool

  attributes: {}

  # 이 Pool에서 호스팅하는 zone의 NS 레코드 목록
  # Designate 외부에서 생성된 레코드여야 하며,
  # 컨트롤러 노드의 public IP를 가리켜야 한다.
  ns_records:
    - hostname: ns1.dmslab.xyz.
      priority: 1

  # 실제 BIND 서버 목록. zone 변경이 전파되었는지 확인하는 데 사용한다.
  nameservers:
    - host: 127.0.0.1
      port: 53

  # BIND 서버 타겟 목록. 각 BIND 서버마다 하나의 엔트리가 필요하다.
  targets:
    - type: bind9
      description: BIND9 Server 1

      # zone transfer(AXFR)를 요청할 designate-mdns 서버 목록
      # 컨트롤러 노드의 IP를 지정한다.
      masters:
        - host: 127.0.0.1
          port: 5354

      # BIND9 연결 옵션
      options:
        host: 127.0.0.1
        port: 53
        rndc_host: 127.0.0.1
        rndc_port: 953
        rndc_key_file: /etc/bind/designate-rndc.key
```

> **pools.yaml 동작 원리**
> 
> 간단히 정리하면 이런 흐름이다:
> 
> 1. Designate API로 zone 생성 요청이 들어온다
> 
> 1. Central이 요청을 처리하고 DB에 저장한다
> 
> 1. Worker가 pool 설정을 참고하여 **targets**에 정의된 BIND9 서버에 zone을 생성한다
> 
> 1. Worker는 **rndc**(953 포트)를 통해 BIND9에 명령을 전달한다
> 
> 1. **mdns**(5354 포트)가 BIND9의 zone transfer 요청에 응답한다
> 
> 1. **nameservers**에 정의된 서버에서 zone이 정상 전파되었는지 확인한다
> 
> 즉, Designate가 "이 zone 추가해줘"라고 rndc로 BIND9에 알려주고, BIND9이 mdns한테 "zone 데이터 줘"라고 AXFR 요청을 보내는 구조다.

> **ns\_records의 hostname에 주의!**
> 
> `ns1.dmslab.xyz.` 끝에 점(.)이 붙어있는 것에 주목하자. DNS에서 FQDN(Fully Qualified Domain Name)은 **반드시 마침표로 끝나야** 한다. 이걸 빠뜨리면 상대 도메인으로 해석되어 이상한 결과가 나올 수 있다.

Pool 정보를 갱신한다:

```bash
su -s /bin/sh -c "designate-manage pool update" designate
```

#### 나머지 패키지 설치 및 시작

```bash
# Worker, Producer, Mini DNS 설치
apt install designate-worker designate-producer designate-mdns

# 서비스 시작
systemctl start designate-worker designate-producer designate-mdns

# 부팅 시 자동 시작 설정
systemctl enable designate-worker designate-producer designate-mdns
```

#### 설치 확인

아래 명령어로 DNS 서비스가 정상적으로 올라왔는지 확인한다:

```bash
openstack dns service list
```

![image](https://blog.pieroot.xyz/api/image-proxy?id=12365ca2-0b70-4e4e-a2e0-4f2918583c45&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-80dc-a11f-f6b575198319)

위와 같이 모든 서비스가 `UP` 상태로 표시되면 Designate 설치는 마무리다. 🎉

---

### 🖥️ Horizon 대시보드 연동

[https://opendev.org/openstack/designate-dashboard/](https://opendev.org/openstack/designate-dashboard/)

Horizon은 가장 기본이 되는 패키지들만 기본 지원하기 때문에, 추가 설치되는 서비스들은 **별도로 플러그인을 설치**해서 넣어주어야 한다. 위 링크는 designate-dashboard 플러그인의 Git 저장소다.

#### 소스 받기

```bash
git clone https://opendev.org/openstack/designate-dashboard.git
cd designate-dashboard/
```

#### 패키지 설치

공식 가이드에서는 `python3 setup.py sdist`로 tarball을 만든 뒤 설치하라고 하지만, 나는 그냥 pip로 설치했다:

```bash
pip install designate_dashboard
```

~~삽질을 줄이는 가장 좋은 방법은 복잡한 걸 단순화하는 거다~~

#### 패널 플러그인 및 설정 파일 복사

```bash
# 패널 파일 복사 (Horizon이 Designate 메뉴를 표시할 수 있도록)
cp designatedashboard/enabled/*.py \
  /usr/share/openstack-dashboard/openstack_dashboard/local/enable/

# 기본 구성 파일 복사
mkdir -p /etc/openstack-dashboard/local_settings.d/
cp designatedashboard/local_settings.d/* \
  /etc/openstack-dashboard/local_settings.d/
```

> **파일 복사 위치 해설**
> 
> - **`enabled/`**: Horizon의 패널 등록 파일이 들어가는 곳. 여기에 파일을 넣으면 Horizon이 자동으로 Designate 관련 메뉴를 로드한다
> 
> - **`local_settings.d/`**: Horizon의 설정 파일 디렉토리. 기존 `local_settings.py`를 **덮어쓰면 안 되기 때문에** 별도 디렉토리에 넣어준다. Horizon은 이 디렉토리의 설정 파일들을 자동으로 읽어들인다

> **`local_settings.py`****를 절대 덮어쓰지 말 것!**
> 
> Horizon의 핵심 설정 파일인 `local_settings.py`를 designate 설정으로 덮어쓰면 Horizon 전체가 먹통이 될 수 있다. 반드시 `local_settings.d/` 디렉토리를 사용하자.

#### 정적 파일 갱신 및 재시작

```bash
cd /usr/share/openstack-dashboard/

# 정적 파일(CSS, JS 등) 압축 및 갱신
python3 manage.py compress

# Horizon 재시작
systemctl restart apache2.service
```

플러그인을 설치한 뒤에는 **정적 파일을 갱신**해야 새로운 CSS와 JavaScript가 반영된다. `manage.py compress`가 이 작업을 수행하고, `apache2`를 재시작하면 Horizon이 새로고침된다.

![image](https://blog.pieroot.xyz/api/image-proxy?id=a801044b-fb2c-42cf-bfb3-2b689007d0f6&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-8048-8589-ca25b7747356)

![image](https://blog.pieroot.xyz/api/image-proxy?id=8975c78f-8717-4797-b85d-67689a96054b&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-8028-87e4-e8f53bdf6478)

위와 같이 Horizon에 **DNS** 메뉴가 나타나고, Zone 관리 화면이 정상적으로 표시된다면 모두 끝이다. ✅

---

### 🔧 트러블슈팅

#### rndc.key 파일 접근 오류

만약 BIND9 설정 단계에서 정상적으로 동작하지 않고, rndc.key 파일을 연결할 수 없다는 오류가 발생한다면 아래를 따라해보자.

![image](https://blog.pieroot.xyz/api/image-proxy?id=84cae66e-9cb8-4dcc-97e4-096bc759b4d7&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-8028-be2a-da11e96412b8)

위와 같은 오류는 `named.conf.options`에서 `rndc.key` 파일을 **읽지 못하는** 문제다. 파일이 존재하더라도 권한 문제로 접근이 안 될 수 있다.

#### 1. 디렉토리 및 파일 권한 확인

Ubuntu 기준으로 BIND9 관련 패키지는 **`bind`** 계정과 그룹을 사용한다. 하지만 `/etc/designate/` 디렉토리는 `root`와 `designate` 사용자/그룹이 소유하고 있다.

![image](https://blog.pieroot.xyz/api/image-proxy?id=330af70f-b39f-4209-ba67-f2e6dcaa85c8&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-80fa-87e8-f3e6f54e3c93)

rndc.key 파일뿐만 아니라 **디렉토리 자체가** 외부 사용자의 접근을 차단하고 있기 때문에, 파일의 경로를 알더라도 읽을 수 없다.

![image](https://blog.pieroot.xyz/api/image-proxy?id=a071a1a4-e53d-4cb3-b1fa-8539e148faee&kind=s3&pageId=30d067c0-15d0-8088-bff9-e3248b3d1901&source=block&blockId=30d067c0-15d0-804c-b3d7-dbbea11c92bf)

#### 2. 읽기 권한 추가

디렉토리와 파일에 other 사용자의 읽기 권한을 추가해야 한다:

```bash
# 디렉토리에 읽기+실행 권한 추가 (디렉토리 진입을 위해 실행 권한도 필요)
sudo chmod o+rx /etc/designate/

# rndc.key 파일에 읽기 권한 추가
sudo chmod o+r /etc/designate/rndc.key
```

> **더 안전한 방법**
> 
> `chmod o+r`은 모든 사용자에게 읽기 권한을 주는 것이라 보안상 최선은 아니다. 좀 더 안전하게 하려면 **bind 사용자를 designate 그룹에 추가**하는 방법도 있다:
> 
> ```bash
> sudo usermod -aG designate bind
> sudo chmod g+r /etc/designate/rndc.key
> ```
> 
> 이렇게 하면 bind 사용자만 designate 그룹 권한으로 파일을 읽을 수 있다.

권한을 변경한 뒤 BIND9을 재시작하면 정상적으로 rndc.key를 로드할 수 있다:

```bash
sudo systemctl restart bind9.service
```

#### 그래도 안 된다면?

rndc.key 파일의 경로 자체를 BIND9이 접근하기 쉬운 곳으로 변경하는 방법도 있다:

```bash
# rndc.key를 BIND9 디렉토리로 복사
sudo cp /etc/designate/rndc.key /etc/bind/designate-rndc.key
sudo chown bind:bind /etc/bind/designate-rndc.key

# named.conf.options의 include 경로도 변경
# include "/etc/designate/rndc.key" → include "/etc/bind/designate-rndc.key"
```

이 경우 `pools.yaml`의 `rndc_key_file` 경로도 함께 변경해야 한다는 점을 잊지 말자.

---

### ✅ 동작 확인

모든 설치가 완료되었으면 간단한 zone을 생성하여 테스트해보자.

```bash
# zone 생성
openstack zone create --email admin@dmslab.xyz dmslab.xyz.

# zone 목록 확인
openstack zone list

# zone에 A 레코드 추가
openstack recordset create --record '192.168.1.100' --type A dmslab.xyz. test

# 레코드 확인
openstack recordset list dmslab.xyz.
```

> **zone 생성 시 주의사항**
> 
> - zone 이름 끝에 **점(.)**을 반드시 붙여야 한다. `dmslab.xyz`가 아니라 `dmslab.xyz.`이다
> 
> - `--email` 옵션은 SOA 레코드의 관리자 이메일로 사용된다. 실제 메일이 전송되지는 않지만, 유효한 형식이어야 한다
> 
> - zone 생성 후 상태가 `ACTIVE`가 되어야 정상이다. `ERROR`라면 Worker 로그를 확인하자

```bash
# 로컬 DNS에 직접 질의하여 확인
dig @127.0.0.1 test.dmslab.xyz A
```

응답이 정상적으로 돌아온다면 Designate가 완벽하게 동작하는 것이다. 🚀

---

### 핵심 정리

✅ **Designate**: OpenStack의 DNSaaS 서비스. REST API로 DNS 레코드를 관리하고, 뒤에서 BIND9이 실제 DNS를 서빙한다

✅ **구성 요소**: API(9001) → Central → Worker → BIND9(53), 그리고 mdns(5354)가 zone transfer를 담당

✅ **BIND9 연동**: rndc(953)를 통해 Designate가 BIND9에 명령을 전달하는 구조

✅ **pools.yaml**: DNS 서버 풀을 정의. nameservers, targets, masters 설정이 핵심

✅ **Horizon 플러그인**: designate-dashboard를 설치하고 패널 파일을 복사한 뒤 정적 파일 갱신

✅ **AppArmor/권한**: Ubuntu에서는 AppArmor와 파일 권한 설정을 반드시 확인해야 한다

### 주의사항

⚠️ **designate.conf의 빈 섹션**: `[service:api]`, `[storage:sqlalchemy]` 등은 기본 파일에 없다. 직접 추가해야 한다

⚠️ **rndc.key 권한**: BIND9(bind 사용자)이 `/etc/designate/rndc.key`를 읽을 수 있어야 한다. 권한 문제가 가장 흔한 트러블 원인

⚠️ **FQDN 마침표**: zone 이름과 NS 레코드의 hostname은 반드시 마침표(.)로 끝나야 한다

⚠️ **Pool 업데이트**: `pools.yaml`을 수정한 후에는 반드시 `designate-manage pool update`를 실행해야 한다

⚠️ **Horizon local\_**[**settings.py**](http://settings.py/): 기존 설정 파일을 덮어쓰지 말고, `local_settings.d/` 디렉토리를 사용하자

### 참고 자료

- [Designate 공식 문서](https://docs.openstack.org/designate/latest/)

- [Designate Ubuntu 설치 가이드](https://docs.openstack.org/designate/latest/install/install-ubuntu.html)

- [Designate 아키텍처](https://docs.openstack.org/designate/latest/contributor/architecture.html)

- [BIND9 Backend 설정 가이드](https://docs.openstack.org/designate/latest/admin/backends/bind9.html)

- [DNS Server Pools 설정](https://docs.openstack.org/designate/latest/admin/pools.html)

- [designate-dashboard GitHub](https://opendev.org/openstack/designate-dashboard)
