# Headscale 데이터베이스 외래키 제약조건 문제 해결 기록 ## 🔍 문제 상황 ### 발생한 오류 ``` backend error: handling register with auth key: registering node: failed register(save) node in the database: SQL logic error: foreign key mismatch - "pharmacy_info" referencing "users" (1) ``` ### 증상 - Windows Tailscale 클라이언트 연결 시 Google SSO 로그인만 표시됨 - `tailscale up --login-server` 명령어 실행 시 위 오류 발생 - Headscale 컨테이너 로그에서 foreign key mismatch 오류 지속 발생 ### 원인 분석 1. **Flask Admin 앱 개발 과정에서 추가된 커스텀 테이블들이 Headscale 스키마와 충돌** - `pharmacy_info` 테이블이 `users` 테이블을 참조하는 외래키 제약조건 설정 - `machine_specs`, `monitoring_data` 테이블도 유사한 외래키 제약조건 존재 2. **Headscale이 자체 사용자 관리 스키마를 가지고 있어 외부 테이블의 외래키 참조 거부** - Headscale은 내부적으로 `users`, `machines`, `nodes` 등의 테이블을 관리 - 외부에서 추가된 테이블이 이들을 참조할 때 스키마 불일치 발생 ## 🛠️ 해결 과정 ### 1단계: 문제 테이블 식별 ```python # 문제가 되는 테이블들 확인 cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%pharmacy%'") problem_tables = cursor.fetchall() # 결과: ['pharmacy_info', 'machine_specs', 'monitoring_data'] ``` ### 2단계: 데이터베이스 정리 스크립트 실행 ```bash python3 clean-database.py ``` #### 스크립트 수행 작업: 1. **백업 생성**: `db.sqlite.clean_backup.20250909_170759` 2. **외래키 제약조건 비활성화**: `PRAGMA foreign_keys = OFF` 3. **문제 테이블 제거**: - `DROP TABLE IF EXISTS pharmacy_info` - `DROP TABLE IF EXISTS machine_specs` - `DROP TABLE IF EXISTS monitoring_data` 4. **변경사항 커밋 및 무결성 검사** ### 3단계: Headscale 서비스 재시작 ```bash cd /srv/headscale-setup && docker-compose restart headscale ``` ## ✅ 해결 결과 ### Before (문제 상황): ``` 2025-09-09T17:07:02+09:00 FTL Migration failed: SQL logic error: foreign key mismatch - "pharmacy_info" referencing "users" (1) ``` ### After (해결 후): ``` 2025-09-09T17:08:42+09:00 INF Using policy manager version: 2 2025-09-09T17:08:42+09:00 INF Starting Headscale commit=474ea236d0c6d393dbcf7baa98da240ad20c1b66 version=0.26.1 2025-09-09T17:08:46+09:00 INF node has connected, mapSession: 0xc000172600, chan: 0xc000286d90 node=DESKTOP-EMJD1DC node.id=2 2025-09-09T17:08:48+09:00 INF node has connected, mapSession: 0xc00021b680, chan: 0xc000251180 node=0bin-Ubuntu-VM node.id=1 ``` ### 성공 지표: - ✅ Headscale 정상 시작 - ✅ Windows 클라이언트 (DESKTOP-EMJD1DC) 연결 성공 - ✅ Ubuntu VM 클라이언트 (0bin-Ubuntu-VM) 연결 유지 - ✅ Health check 통과: `{"status":"pass"}` ## 📚 교훈 및 베스트 프랙티스 ### 1. Headscale과 커스텀 애플리케이션 분리 원칙 ``` ❌ 잘못된 접근: Headscale DB에 직접 외래키 제약조건으로 연결 ✅ 올바른 접근: 별도 데이터베이스 또는 느슨한 결합 방식 사용 ``` ### 2. 외래키 제약조건 설계 시 고려사항 - **Headscale 스키마 독립성 유지** - **별도 데이터베이스 사용** 또는 **ID 참조만 사용** (외래키 제약조건 없이) - **마이그레이션 전 스키마 호환성 검증** ### 3. 향후 개발 가이드라인 ```python # 권장하지 않음 pharmacy_info = Table('pharmacy_info', Column('user_id', Integer, ForeignKey('users.id')) # ❌ ) # 권장 방법 pharmacy_info = Table('pharmacy_info', Column('headscale_user_id', Integer) # ✅ 단순 참조, 제약조건 없음 ) ``` ### 4. 데이터베이스 백업 중요성 - 모든 스키마 변경 전 백업 생성 필수 - 백업 파일명에 타임스탬프 포함으로 버전 관리 - 롤백 절차 사전 준비 ## 🔧 복구 방법 (필요시) 만약 문제가 재발하거나 백업에서 복원해야 할 경우: ```bash # 백업에서 복원 cp /srv/headscale-setup/data/db.sqlite.clean_backup.20250909_170759 /srv/headscale-setup/data/db.sqlite # 컨테이너 재시작 cd /srv/headscale-setup && docker-compose restart headscale ``` ## 📝 관련 파일들 - **해결 스크립트**: `/srv/headscale-setup/clean-database.py` - **백업 파일**: `/srv/headscale-setup/data/db.sqlite.clean_backup.20250909_170759` - **Docker Compose**: `/srv/headscale-setup/docker-compose.yml` - **로그 위치**: `docker logs headscale` --- *문제 해결일: 2025-09-09* *해결 소요시간: ~30분* *영향 범위: Windows/Ubuntu 클라이언트 연결 복구*