# Plan-B-Base — NAS Redis 컨테이너 + Windows WSL2/Docker/Tailscale/SMB Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 분산 아키텍처 base 인프라 셋업 — NAS에 24/7 Redis 컨테이너 신설 + Windows AI 머신에 WSL2 + Docker Engine + Tailscale + NAS SMB 마운트 구성. 후속 Plan-B-Insta/Music/Video/Infra 트랙의 prerequisite. **Architecture:** SP-1 (NAS Redis) = docker-compose service 추가 + deployer auto-rebuild. SP-2 (Windows) = 박재오 머신 192.168.45.59에서 직접 셋업 (WSL2 Ubuntu 22.04 + Docker Engine + Tailscale + cifs-utils로 NAS SMB 마운트). 두 SP가 모두 끝나야 후속 트랙의 worker가 NAS ↔ Windows 양방향 통신 가능. **Tech Stack:** Redis 7-alpine, WSL2, Ubuntu 22.04, Docker Engine 24+, Tailscale, cifs-utils (SMB 3.0). PowerShell (관리자) + bash (WSL2 내부). **Spec:** `web-backend/docs/superpowers/specs/2026-05-18-nas-windows-distributed-architecture-design.md` §4 SP-1·SP-2, §10 SP-1·SP-2 상세 --- ## 사전 확인 사항 - **박재오 자격증명 필요**: NAS SMB 마운트용 user/password (Synology DSM 사용자, SMB 권한 보유) - **Windows AI 머신 직접 접근 필요**: WSL2 설치는 관리자 PowerShell + 재부팅 1회. Claude는 별도 머신이라 명령 직접 실행 불가 — **Task 4~7은 박재오가 콘솔에서 직접 수행**. 명령어와 검증 방법 명시. - **NAS deployer 사용자**: Gitea webhook으로 docker compose up -d 자동 실행. 새 redis 서비스도 추가 시 자동 startup. ## File Structure ### SP-1 — NAS 측 (Modify) | 파일 | 변경 | 책임 | |------|------|------| | `web-backend/docker-compose.yml` | `redis:` 서비스 블록 추가 | 컨테이너 정의 (image, volume, healthcheck) | ### SP-2 — Windows 측 (Create, 박재오 머신 로컬) | 파일/위치 | 변경 | 책임 | |----------|------|------| | (Windows AI) WSL2 Ubuntu-22.04 | install | Linux 런타임 | | WSL2 `/etc/apt/keyrings/docker.gpg` | install | Docker Engine apt key | | WSL2 `/etc/apt/sources.list.d/docker.list` | install | Docker Engine apt source | | (Windows AI) Tailscale | install + auth | 사설망 100.x.x.x | | WSL2 `/etc/nas-smb-credentials` (신규) | NAS user/password | SMB 자격증명 (chmod 600) | | WSL2 `/etc/fstab` (수정) | SMB 마운트 항목 추가 | 부팅 시 자동 마운트 | | WSL2 `/mnt/nas` | mkdir | 마운트 포인트 | --- ## Task 1: NAS docker-compose.yml에 redis 서비스 추가 **Files:** - Modify: `C:/Users/jaeoh/Desktop/workspace/web-backend/docker-compose.yml` - [ ] **Step 1: 현재 docker-compose.yml 끝부분 확인 (deployer 위치)** Run: `tail -20 C:/Users/jaeoh/Desktop/workspace/web-backend/docker-compose.yml` Expected: `deployer` 서비스가 마지막. line ~277-293 영역. - [ ] **Step 2: redis 서비스 블록 추가** `C:/Users/jaeoh/Desktop/workspace/web-backend/docker-compose.yml` 파일 **끝**에 (deployer 서비스 다음, volumes 블록 있다면 그 전에) 다음 블록 추가. 들여쓰기는 다른 서비스(`lotto:`, `stock:` 등)와 동일하게 services 아래 2칸 들여쓰기: ```yaml redis: image: redis:7-alpine container_name: redis restart: unless-stopped ports: - "6379:6379" volumes: - ${RUNTIME_PATH}/redis-data:/data command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 60s timeout: 5s retries: 3 networks: - default ``` **주의:** - 파일 끝에 추가하되, 만약 `networks:` / `volumes:` top-level 블록이 services 다음에 있다면 그 블록들 **앞에** 삽입 - 첫 줄에 빈 줄 1개 두기 (deployer와 분리) - `${RUNTIME_PATH}` 환경변수는 다른 서비스에서도 사용 중. 자동 적용됨 - [ ] **Step 3: yaml 문법 검증** Run: ```bash python -c "import yaml; yaml.safe_load(open('C:/Users/jaeoh/Desktop/workspace/web-backend/docker-compose.yml'))" && echo "yaml OK" ``` Expected: `yaml OK` 만약 실패하면 indent 또는 trailing space 확인. - [ ] **Step 4: redis 서비스가 services dict에 들어갔는지 확인** Run: ```bash python -c "import yaml; d=yaml.safe_load(open('C:/Users/jaeoh/Desktop/workspace/web-backend/docker-compose.yml')); print(sorted(d['services'].keys()))" ``` Expected: 리스트에 `'redis'` 포함. 다른 서비스(`lotto`, `stock`, `music-lab`, `insta-lab`, `realestate-lab`, `agent-office`, `personal`, `packs-lab`, `travel-proxy`, `frontend`, `deployer`)도 모두 그대로. - [ ] **Step 5: 커밋** ```bash cd C:/Users/jaeoh/Desktop/workspace/web-backend git add docker-compose.yml git commit -m "$(cat <<'EOF' feat(infra): add redis container as 24/7 queue + cache base (SP-1) redis:7-alpine, 256MB maxmemory, AOF appendonly ON, allkeys-lru. docker volume ${RUNTIME_PATH}/redis-data로 영속화. Plan-B 후속 트랙(insta-render/music-render/video-render Windows 워커)의 BLPOP 큐 + NAS↔Windows pub/sub의 base. Co-Authored-By: Claude Opus 4.7 (1M context) EOF )" ``` - [ ] **Step 6: push (Gitea webhook → NAS deployer 자동 적용)** ```bash cd C:/Users/jaeoh/Desktop/workspace/web-backend git push origin main ``` 자격증명 prompt 시 입력. 1회 실패 시 1회 재시도 패턴. Expected: push 성공. NAS deployer가 webhook 수신 → `git pull` → `docker compose up -d redis` 자동 실행. --- ## Task 2: NAS Redis 컨테이너 헬스 확인 **Files:** 없음 (NAS 검증) - [ ] **Step 1: deployer 완료까지 대기 (통상 30초~2분)** Run (Windows 로컬에서): ```bash for i in 1 2 3 4 5 6 7 8 9 10; do code=$(curl -s -o /dev/null -w "%{http_code}" https://gahusb.synology.me/api/stock/news -m 5) echo "[try $i] HTTP $code" if [ "$code" = "200" ]; then break; fi sleep 15 done ``` Expected: HTTP 200 응답 — NAS 컨테이너 안정 상태. redis 컨테이너는 별도 endpoint 없으나 deployer가 build 완료했음을 시사. - [ ] **Step 2: NAS에서 redis 컨테이너 확인 (박재오 SSH)** NAS bash: ```bash ssh -p 22 박재오@gahusb.synology.me cd /volume1/docker/webpage docker compose ps redis ``` 또는 한 번에: ```bash ssh -p 22 박재오@gahusb.synology.me "cd /volume1/docker/webpage && docker compose ps redis && docker exec redis redis-cli PING" ``` Expected: - `docker compose ps redis` → `redis ... healthy` 또는 `Up X seconds (health: starting)` 후 곧 healthy - `redis-cli PING` → `PONG` 만약 `docker compose ps`에 redis가 안 보이면: ```bash cd /volume1/docker/webpage && docker compose up -d redis ``` 수동 실행해서 startup 확인. - [ ] **Step 3: redis-data 볼륨 생성 확인 (Z: drive로)** Run (Windows): ```powershell Test-Path "Z:\webpage\redis-data" ``` 또는 NAS bash: ```bash ls -la /volume1/docker/webpage/redis-data/ ``` Expected: 디렉토리 존재. 그 안에 `appendonlydir/` 또는 `dump.rdb` 등의 redis 데이터 파일. - [ ] **Step 4: AOF append-only 작동 확인 (선택, 데이터 영속성 검증)** ```bash ssh -p 22 박재오@gahusb.synology.me 'docker exec redis redis-cli SET test_key "hello"' ssh -p 22 박재오@gahusb.synology.me 'docker exec redis redis-cli RESTART' # 또는 docker restart # 잠시 대기 ssh -p 22 박재오@gahusb.synology.me 'docker exec redis redis-cli GET test_key' ``` Expected: `"hello"` — 재시작 후에도 값 유지 (AOF 영속화 작동). 테스트 후 정리: `docker exec redis redis-cli DEL test_key` --- ## Task 3: Windows AI에 WSL2 + Ubuntu 22.04 설치 **Files:** 없음 (Windows AI 머신 192.168.45.59에서 박재오 직접 실행) **전제:** Windows 10 build 19041+ 또는 Windows 11. 박재오 9800X3D 머신 충족. - [ ] **Step 1: 관리자 PowerShell 실행** 박재오 Windows AI 머신에서 시작 메뉴 → "PowerShell" 우클릭 → "관리자 권한으로 실행". - [ ] **Step 2: WSL2 + Ubuntu 22.04 설치** ```powershell wsl --install -d Ubuntu-22.04 ``` Expected: 다운로드 progress + "Ubuntu-22.04 has been installed". **재부팅 필요할 수 있음.** - [ ] **Step 3: 재부팅 (필요 시)** 설치 완료 메시지에 "재시작이 필요합니다"가 보이면 재부팅. 자동 재부팅 안 됨. - [ ] **Step 4: Ubuntu 초기 설정 (재부팅 후 자동 실행 또는 시작 메뉴에서 "Ubuntu" 클릭)** 새 콘솔이 열리고 다음 입력 요청됨: - 새 UNIX username: `jaeoh` 또는 박재오 선호 username (이후 모든 sudo에 사용) - 비밀번호: 박재오가 정하는 값. 잘 기억할 것. Expected: `jaeoh@:~$` 프롬프트 표시 → WSL2 진입 성공. - [ ] **Step 5: WSL 버전 확인** WSL2 내부에서 PowerShell로 잠시 돌아와서: ```powershell wsl -l -v ``` Expected: ``` NAME STATE VERSION * Ubuntu-22.04 Running 2 ``` VERSION=2 확인. 만약 1이면: ```powershell wsl --set-version Ubuntu-22.04 2 ``` - [ ] **Step 6: WSL2 안 진입 (이후 작업)** ```powershell wsl -d Ubuntu-22.04 ``` 이후 Task 4~7은 모두 WSL2 안 bash에서 실행. --- ## Task 4: WSL2 안 Docker Engine 설치 (Docker Desktop 사용 X) **Files:** (WSL2 내부) `/etc/apt/keyrings/docker.gpg`, `/etc/apt/sources.list.d/docker.list` **위치:** WSL2 Ubuntu-22.04 bash 프롬프트. - [ ] **Step 1: 패키지 인덱스 + 기본 의존성 설치** ```bash sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release ``` Expected: 에러 없이 완료. - [ ] **Step 2: Docker apt key 등록** ```bash sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg ``` Expected: 에러 없이 완료. `/etc/apt/keyrings/docker.gpg` 파일 생성. - [ ] **Step 3: Docker repository 추가** ```bash echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update ``` Expected: `Hit:N https://download.docker.com/linux/ubuntu jammy InRelease` 라인 보임. - [ ] **Step 4: Docker Engine + Compose 설치** ```bash sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ``` Expected: 설치 완료. 용량 ~400MB. - [ ] **Step 5: 현재 사용자를 docker 그룹에 추가** ```bash sudo usermod -aG docker $USER ``` Expected: 출력 없음 (정상). **새 셸 열어야 적용됨.** - [ ] **Step 6: Docker 서비스 시작 + 자동 시작 설정** ```bash sudo systemctl enable docker sudo systemctl start docker sudo systemctl status docker | head -5 ``` Expected: `Active: active (running)`. 만약 `systemctl: command not found` 또는 systemd 미지원 시: ```bash sudo service docker start ``` WSL2 systemd 활성화는 `/etc/wsl.conf`에 `[boot]\nsystemd=true` 추가 후 PowerShell에서 `wsl --shutdown` 후 재진입. (Ubuntu-22.04는 보통 기본 활성) - [ ] **Step 7: docker 명령 동작 확인** 새 셸로 (PowerShell에서 다시 `wsl -d Ubuntu-22.04` 또는 현재 셸 종료 후 재진입): ```bash docker version docker run --rm hello-world ``` Expected: - `docker version`: Client + Server 둘 다 표시 (Server에 Engine version) - `hello-world`: "Hello from Docker!" 출력 --- ## Task 5: WSL2 안 Tailscale 설치 + 가입 **Files:** Tailscale은 systemd service 등록 (별도 path 신경 안 써도 됨) - [ ] **Step 1: Tailscale 설치** WSL2 bash: ```bash curl -fsSL https://tailscale.com/install.sh | sh ``` Expected: 패키지 install 후 "Installation complete!" 출력. - [ ] **Step 2: Tailscale 가입 (브라우저 OAuth)** ```bash sudo tailscale up ``` Expected: `To authenticate, visit: https://login.tailscale.com/a/...` URL 표시. 브라우저에서 그 URL 열기 → Google/Microsoft/GitHub 등으로 로그인 → 박재오 Tailscale 네트워크에 가입 (기존 계정 없으면 생성). - [ ] **Step 3: 가입 완료 확인** ```bash tailscale status ``` Expected: - 첫 줄에 Windows AI 머신의 100.x.x.x IP 표시 - (이미 가입된) NAS도 같은 네트워크에 있다면 NAS의 100.x.x.x IP도 표시 - [ ] **Step 4: NAS와 Tailscale ping (양방향 사설망 확인)** NAS의 Tailscale IP를 `tailscale status` 출력에서 찾아 (예: `100.64.0.10`): ```bash tailscale ping 100.64.0.10 ``` Expected: `pong from ` (직접 LAN 또는 DERP 중계). 만약 NAS가 Tailscale 미가입이면 별도로 NAS DSM Tailscale 패키지 셋업 필요 — 이는 박재오 결정 사항이라 plan 외. > **참고:** Tailscale은 spec §3 sense의 사설망 layer 보조. LAN(192.168.45.0/24) 안에서만 작업한다면 Tailscale 없이도 작동. 외부 출장 등에서 NAS↔Windows 통신을 위해 권장. --- ## Task 6: WSL2 안 NAS SMB 자격증명 파일 + 마운트 포인트 준비 **Files:** `/etc/nas-smb-credentials`, `/mnt/nas` - [ ] **Step 1: cifs-utils 설치 (SMB 마운트 패키지)** ```bash sudo apt install -y cifs-utils ``` Expected: 설치 완료. - [ ] **Step 2: SMB 자격증명 파일 생성** 박재오 NAS 계정의 username과 password를 사용. 파일 위치는 system-wide `/etc/`. ```bash sudo bash -c 'cat > /etc/nas-smb-credentials < **share name 변형:** 박재오 NAS는 메모리(`feedback_nas_deploy_paths.md`)에 따르면 SMB 매핑이 `/volume1/docker/`를 share `docker`로 노출. 만약 다른 share name(예: `webpage`)이라면 그것으로 교체. - [ ] **Step 2: 마운트 결과 확인** ```bash ls /mnt/nas/ ``` Expected: `webpage/` 디렉토리 + 다른 share 내 디렉토리 보임. ```bash ls /mnt/nas/webpage/data/ ``` Expected: `insta/`, `music/` 등 후속 트랙에서 사용할 디렉토리. 없으면 후속 트랙에서 생성됨. - [ ] **Step 3: 마운트 해제 후 fstab으로 자동화** ```bash sudo umount /mnt/nas ``` Expected: 출력 없음. `/etc/fstab` 끝에 다음 라인 추가: ```bash sudo bash -c 'cat >> /etc/fstab <&1 | head -5 mount | grep cifs ``` Expected: - `mount -a` 출력 없음 (성공) - `ls /mnt/nas/webpage/data/` 디렉토리 내용 표시 - `mount | grep cifs` 라인에 마운트 정보 보임 - [ ] **Step 5: WSL2 재시작 시 자동 마운트 확인** PowerShell에서 (관리자 권한 불필요): ```powershell wsl --shutdown wsl -d Ubuntu-22.04 ``` WSL2 다시 진입 후: ```bash ls /mnt/nas/webpage/data/ ``` Expected: 정상 디렉토리 목록. 자동 마운트 성공. 만약 마운트 안 됨: - `dmesg | grep cifs` 확인 - `nofail` 때문에 boot은 통과했으나 마운트 실패 가능. 수동 `sudo mount -a` 후 동작 확인 → fstab syntax 재검토 --- ## Task 8: 통합 검증 — base 인프라 동작 확인 **Files:** 없음 (검증) - [ ] **Step 1: NAS Redis 외부 ping (Windows 로컬에서)** ```powershell # Windows AI 또는 박재오 PC에서 Test-NetConnection -ComputerName 192.168.45.54 -Port 6379 ``` Expected: `TcpTestSucceeded : True` > 외부 6379 노출은 LAN 한정. 가능하면 NAS firewall (DSM Control Panel)에서 6379 LAN-only allowed로 한정 권장. (이번 plan에 포함 안 됨, 별도 사용자 작업) - [ ] **Step 2: WSL2에서 NAS Redis 접속** WSL2 bash: ```bash docker run --rm redis:7-alpine redis-cli -h 192.168.45.54 PING ``` 또는 Tailscale 사용 시: ```bash docker run --rm redis:7-alpine redis-cli -h PING ``` Expected: `PONG` - [ ] **Step 3: NAS volume 쓰기 테스트 (Windows→NAS 양방향)** WSL2 bash: ```bash echo "Plan-B-Base test $(date)" | sudo tee /mnt/nas/webpage/data/.plan-b-test.txt cat /mnt/nas/webpage/data/.plan-b-test.txt sudo rm /mnt/nas/webpage/data/.plan-b-test.txt ``` Expected: - `tee` 출력에 같은 내용 + 파일 생성됨 - `cat` 으로 확인 성공 - 파일 삭제 성공 `sudo` 필요 시 chmod로 uid 1000 쓰기 권한 확인. 또는 mount option `uid=1000,gid=1000` 적용 후 일반 사용자도 쓰기 가능. 만약 안 되면 NAS DSM에서 SMB user의 write 권한 확인. - [ ] **Step 4: WSL2 Docker로 hello-world 한 번 더 (재진입 후 상태 확인)** ```bash docker run --rm hello-world ``` Expected: "Hello from Docker!" - [ ] **Step 5: 모든 검증 완료 후 보고 — 후속 트랙으로 진입 가능 상태** 다음 plan(Plan-B-Insta 등)이 가정하는 상태: - ✅ NAS `redis:6379` PING/PONG 성공 - ✅ Windows WSL2 Ubuntu-22.04 작동 + Docker Engine 실행 - ✅ `/mnt/nas/webpage/data/` 양방향 read·write 성공 - ✅ Tailscale 가입 (선택, 외부 출장 시 필요) --- ## Self-Review ### Spec 커버리지 | Spec 요구사항 | 구현 Task | |---------------|-----------| | §4 SP-1: NAS Redis 컨테이너 | Task 1 (compose 추가) + Task 2 (헬스 검증) | | §10 SP-1: redis:7-alpine + 256MB + AOF + healthcheck | Task 1 Step 2 | | §4 SP-2: Windows WSL2 + Docker Engine | Task 3 (WSL2) + Task 4 (Docker) | | §10 SP-2: Tailscale | Task 5 | | §10 SP-2: NAS SMB mount `/mnt/nas` | Task 6 (자격증명·포인트) + Task 7 (마운트+fstab) | | §10 SP-2: 검증 (docker ps, tailscale status, ls /mnt/nas) | Task 8 | | §6 Redis 키 컨벤션 사용 가능 | Task 2 Step 2 (PING) — 컨벤션 자체는 후속 트랙에서 RPUSH로 시작 | ### Placeholder 스캔 - TBD/TODO 없음 ✓ - 모든 명령어가 그대로 실행 가능한 형태 ✓ - 한 가지 예외: Task 6 Step 2 — `박재오NAS사용자명/박재오NAS비밀번호`는 사용자 자격증명이라 placeholder가 의도된 것. 실행 전 교체 명시 ✓ - Task 5 Step 4 — ``는 `tailscale status` 출력에서 박재오가 보고 입력. 사용자 환경에서만 결정 가능, plan에 명시 ✓ ### Type/이름 consistency - `redis` 서비스명 (Task 1, 2, 8 모두 동일) ✓ - `/mnt/nas` 마운트 포인트 (Task 6, 7, 8 모두 동일) ✓ - `/etc/nas-smb-credentials` 자격증명 파일 (Task 6, 7 동일) ✓ - share name `docker` (Task 7 Step 1, fstab 동일) ✓ - Ubuntu-22.04 (Task 3, 4 동일) ✓ ### 위험·주의 | 위험 | 완화 | |------|------| | Windows 재부팅 시 WSL2 자동 시작 안 함 | 향후 Plan-B-Infra(SP-9)에서 NSSM으로 자동 시작 | | WSL2 systemd 미지원 시 docker service 자동 시작 안 함 | Task 4 Step 6의 fallback `sudo service docker start` 또는 `/etc/wsl.conf` 수정 | | SMB 마운트 자격증명 노출 | `/etc/nas-smb-credentials` chmod 600 + root:root | | NAS firewall에서 6379 외부 노출 | 권장: LAN(192.168.45.0/24) only allow. 본 plan 외 (DSM 수동) | | Tailscale 미가입 시 NAS↔Windows 외부 통신 불가 | LAN 내에선 작동. 외부 출장 시 필요할 때만 가입 | | /mnt/nas 쓰기 권한 부족 | uid=1000 mount option + NAS DSM에서 SMB user의 share write 권한 확인 | --- ## 완료 후 다음 단계 Plan-B-Base 완료 후 spec §14 권장 순서대로: 1. **Plan-B-Insta** — SP-3 (insta-render Windows worker) + SP-4 (NAS insta-lab 분할) 2. **Plan-B-Music** — SP-5 + SP-6 3. **Plan-B-Video** — SP-7 + SP-8 4. **Plan-B-Infra** — SP-9 (NSSM 자동 시작) + SP-10 (task-watcher) 각 후속 plan은 본 plan이 제공한 base 인프라(Redis + WSL2/Docker + /mnt/nas)에 의존.