diff --git a/CLAUDE.md b/CLAUDE.md index 9a1172d..d7ee440 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,6 @@ This is a Docker base image for Coder workspaces, built on top of `codercom/ente - Graphite CLI for stacked PRs - GitHub CLI extensions: `gh stack` (github/gh-stack) and `gh actions-lock` (github/gh-actions-lock) - AWS CLI, Google Cloud CLI, and Qodana CLI -- Orca headless server (`orca-server` wrapper; launch via Coder template startup script) - Custom Nerd Fonts for terminal display ## Repository Structure diff --git a/docs/orca-server.md b/docs/orca-server.md deleted file mode 100644 index 43cb0aa..0000000 --- a/docs/orca-server.md +++ /dev/null @@ -1,150 +0,0 @@ -# Orca Headless Server in Coder Workspaces - -베이스 이미지에는 Orca headless server가 설치되어 있습니다 (`orca-server` wrapper). -실행과 클라이언트 접속 주소(`--pairing-address`)는 Coder 템플릿이 담당합니다. - -- 설치 내용: [images/CLAUDE.md](../images/CLAUDE.md)의 `install-orca.sh` 섹션 참고 -- 원본 가이드: [headless-linux-server.md](https://github.com/amondnet/orca/blob/main/docs/reference/headless-linux-server.md) - -## 동작 방식 - -- `orca-server serve`는 포그라운드 프로세스로 실행되며 pairing URL을 출력합니다. -- `--pairing-address`는 **Orca 클라이언트(노트북/모바일)가 도달할 수 있는 주소**여야 합니다. -- 접속 방식 선택: - 1. **Docker provider + VPN** — 호스트 포트 publish. 가장 깔끔 (아래 권장 섹션) - 2. **서브도메인 앱** — wildcard access URL 필요. 모바일/팀 공유용이나 `share` 설정 유의 - 3. **coder port-forward / SSH 포워딩** — 개인 데스크톱 용도의 최소 구성 - -## Docker provider + VPN (권장) - -워크스페이스가 Docker provider로 사내 호스트에 뜨고, 클라이언트가 VPN으로 그 호스트에 -접근 가능한 경우 — 컨테이너 포트를 호스트에 publish하면 Coder 프록시를 거치지 않고 -VPN 안에서 직접 접속됩니다. `share` 옵션 고민이 사라지는 가장 깔끔한 구성입니다. - -컨테이너 IP(예: `172.18.0.2`)는 호스트 밖으로 라우팅되지 않으므로 반드시 포트 publish가 -필요합니다. 워크스페이스마다 포트가 겹치지 않도록 workspace ID에서 결정적으로 유도합니다 -(재시작해도 동일 포트 유지): - -```hcl -variable "docker_host_ip" { - description = "VPN에서 접근 가능한 Docker 호스트 IP" - type = string -} - -locals { - # workspace ID 기반 결정적 포트 (30000–34095, 워크스페이스가 많으면 충돌 가능성 유의) - orca_port = 30000 + parseint(substr(md5(data.coder_workspace.me.id), 0, 3), 16) -} - -resource "coder_agent" "main" { - # ... 기존 설정 ... - - startup_script = <<-EOT - nohup orca-server serve \ - --port ${local.orca_port} \ - --pairing-address "${var.docker_host_ip}" \ - > /tmp/orca-server.log 2>&1 & - EOT -} - -resource "docker_container" "workspace" { - # ... 기존 설정 ... - - # 내부/외부 포트를 동일하게 맞춰 pairing URL의 포트가 그대로 유효하도록 함 - ports { - internal = local.orca_port - external = local.orca_port - } -} -``` - -클라이언트에서는 `/tmp/orca-server.log`에 출력된 pairing URL로 접속하면 됩니다 -(주소는 `docker_host_ip:orca_port`). - -## 대안: 서브도메인 앱 (wildcard access URL) - -```hcl -variable "wildcard_domain" { - description = "Coder wildcard access domain (예: coder.example.com — '*.' 제외)" - type = string -} - -data "coder_workspace" "me" {} -data "coder_workspace_owner" "me" {} - -locals { - # coder_app(slug=orca, subdomain=true)의 외부 호스트명 - orca_host = "orca--main--${lower(data.coder_workspace.me.name)}--${lower(data.coder_workspace_owner.me.name)}.${var.wildcard_domain}" -} - -resource "coder_agent" "main" { - # ... 기존 설정 ... - - startup_script = <<-EOT - # Orca headless server (Xvfb는 orca가 자동 기동) - nohup orca-server serve \ - --port 6768 \ - --pairing-address "wss://${local.orca_host}" \ - > /tmp/orca-server.log 2>&1 & - EOT -} - -resource "coder_app" "orca" { - agent_id = coder_agent.main.id - slug = "orca" - display_name = "Orca Server" - url = "http://localhost:6768" - subdomain = true - share = "authenticated" -} -``` - -- `local.orca_host`의 `main`은 agent 이름입니다. 템플릿의 agent 이름에 맞춰 수정하세요. -- pairing URL은 `/tmp/orca-server.log`에서 확인합니다: - ```bash - grep -m1 "pairing" /tmp/orca-server.log - ``` - -### share 옵션 선택 - -| 값 | 접속 가능 대상 | 비고 | -|---|---|---| -| `authenticated` | Coder 로그인 세션이 있는 브라우저 | 외부 Orca 클라이언트(데스크톱/모바일 앱)는 Coder 세션이 없어 차단될 수 있음 | -| `public` | 누구나 (URL 노출 시) | Orca pairing 토큰이 자체 인증 역할을 하지만, 포트가 인터넷에 공개됨 | - -Orca 데스크톱/모바일 앱에서 직접 페어링하려면 `public`이 필요할 수 있습니다. -`authenticated`로 먼저 시도하고, 클라이언트가 연결하지 못하면 `public`으로 전환하되 -워크스페이스를 신뢰 경계 밖에 두지 않도록 주의하세요. - -## 대안: coder port-forward / SSH 포워딩 - -서브도메인 앱이나 포트 publish 없이 개인 용도로만 쓸 경우. -이때 startup script는 `--pairing-address 127.0.0.1 --port 6768`로 실행합니다. - -```bash -# 클라이언트 머신에서 -coder port-forward --tcp 6768:6768 -``` - -`coder config-ssh`를 설정해 두었다면 표준 SSH 포트 포워딩도 동일하게 동작합니다 -(둘 다 Coder의 터널 위로 흐르므로 기능적으로 같습니다): - -```bash -ssh -L 6768:localhost:6768 coder. -``` - -매번 명령을 유지하기 번거로우면 `~/.ssh/config`에 등록해 SSH 접속 시 자동 포워딩: - -``` -Host coder. - LocalForward 6768 localhost:6768 -``` - -## 트러블슈팅 - -- **sandbox 크래시 (`Failed to move to new namespace`)**: 컨테이너 seccomp 제약. - `orca-server`가 기본으로 `ELECTRON_DISABLE_SANDBOX=1`을 설정하므로 wrapper를 우회하지 말 것. - privileged 환경에서 sandbox를 켜려면 `ELECTRON_DISABLE_SANDBOX= orca-server serve ...`. -- **dbus 에러 로그**: headless 컨테이너에서 무해한 노이즈. -- **클라이언트 연결 실패**: `--pairing-address`가 클라이언트에서 도달 가능한지, - 포트 publish 또는 `coder_app`의 `share` 설정이 클라이언트 인증 방식과 맞는지 확인. diff --git a/images/CLAUDE.md b/images/CLAUDE.md index 1e21a38..dc865ad 100644 --- a/images/CLAUDE.md +++ b/images/CLAUDE.md @@ -16,7 +16,6 @@ images/ ├── install-root-sdk.sh # Root 레벨 도구 (Qodana, AWS CLI) ├── install-mise.sh # mise 버전 매니저 설치 (Root, apt) ├── install-docker.sh # Docker Engine 설치 (Root) - ├── install-orca.sh # Orca headless server (Root) ├── install-sdk.sh # 사용자 개발 도구 (mise 글로벌 런타임, Claude Code) ├── install-prompt.sh # Zimfw 모듈 + Powerlevel10k 프롬프트 ├── install-brew.sh # Homebrew 패키지 매니저 @@ -84,11 +83,6 @@ images/ - Docker Engine 설치 - coder 사용자를 docker 그룹에 추가 -6. **Orca headless server 설치** (`install-orca.sh`) - - Xvfb 및 Electron 런타임 라이브러리 - - Orca AppImage를 `/opt/orca/squashfs-root`에 추출 설치 (컨테이너에 FUSE 없음) - - `orca-server` wrapper (`/usr/local/bin`) - #### Phase 2: Coder 사용자 설정 1. **개발 SDK 설치** (`install-sdk.sh`) @@ -187,18 +181,6 @@ Docker Engine 설치: - Docker CE, CLI, containerd 설치 - coder 사용자는 Dockerfile에서 별도로 docker 그룹 추가 -#### install-orca.sh (Root) - -Orca headless server 설치 ([가이드](https://github.com/amondnet/orca/blob/main/docs/reference/headless-linux-server.md)): -- Xvfb + Electron 런타임 라이브러리 (libnss3, libgtk-3-0, libgbm1 등) -- AppImage를 빌드 시점에 `--appimage-extract`로 `/opt/orca/squashfs-root`에 추출 - (컨테이너에는 FUSE가 없어 AppImage 직접 실행 불가) -- `/usr/local/bin/orca-server` wrapper 제공 - - `APPDIR`, `LIBGL_ALWAYS_SOFTWARE=1` 설정 - - `ELECTRON_DISABLE_SANDBOX=1` 기본값 (컨테이너 seccomp이 sandbox namespace 차단) -- **실행은 Coder 템플릿 담당**: `orca-server serve --port 6768 --pairing-address <주소>`를 - startup script에서 기동 (systemd 없음, `--pairing-address`는 워크스페이스별 상이) - #### install-sdk.sh (Coder User) 사용자 개발 도구 설치: @@ -264,7 +246,6 @@ GitHub CLI 확장 설치: 4. 폰트 (`install-fonts.sh`) 5. Root SDK (`install-root-sdk.sh`) 6. Docker (`install-docker.sh`) -7. Orca (`install-orca.sh`) **Root Phase (mise):** Docker 직전에 mise 바이너리를 apt로 시스템 설치 (`install-mise.sh`) diff --git a/images/base/Dockerfile b/images/base/Dockerfile index 810fbbf..03cfbcd 100644 --- a/images/base/Dockerfile +++ b/images/base/Dockerfile @@ -105,12 +105,6 @@ RUN chmod +x /tmp/install-docker.sh && \ # coder 사용자를 docker 그룹에 추가 RUN usermod -aG docker coder -# Orca headless server 설치 -COPY --chown=root:root scripts/install-orca.sh /tmp/install-orca.sh -RUN chmod +x /tmp/install-orca.sh && \ - /tmp/install-orca.sh && \ - rm /tmp/install-orca.sh - # coder 사용자로 전환 USER coder diff --git a/images/base/scripts/install-orca.sh b/images/base/scripts/install-orca.sh deleted file mode 100644 index 4de8010..0000000 --- a/images/base/scripts/install-orca.sh +++ /dev/null @@ -1,43 +0,0 @@ -# Orca headless server 설치 -# 참고: https://github.com/amondnet/orca/blob/main/docs/reference/headless-linux-server.md -# 실행(xvfb 기동, --pairing-address 등)은 Coder 템플릿의 startup script에서 담당 - -echo "Installing Orca headless server dependencies..." -apt-get update -apt-get install -y xvfb libnss3 libatk-bridge2.0-0 libgtk-3-0 libgbm1 libxss1 -apt-get install -y libasound2t64 || apt-get install -y libasound2 - -echo "Downloading Orca AppImage..." -mkdir -p /opt/orca -curl -fsSL https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage \ - -o /opt/orca/orca-linux.AppImage -chmod +x /opt/orca/orca-linux.AppImage - -# 컨테이너에는 FUSE(/dev/fuse)가 없으므로 빌드 시점에 AppImage를 미리 추출 -echo "Extracting Orca AppImage..." -cd /opt/orca -./orca-linux.AppImage --appimage-extract > /dev/null -rm orca-linux.AppImage -chmod -R a+rX /opt/orca/squashfs-root - -# Chromium setuid sandbox 활성화 (비 root 사용자 실행용) -chown root:root /opt/orca/squashfs-root/chrome-sandbox -chmod 4755 /opt/orca/squashfs-root/chrome-sandbox - -# coder 사용자용 실행 wrapper -# (데스크톱 CLI `orca`와 이름 충돌을 피하기 위해 orca-server 사용) -cat > /usr/local/bin/orca-server <<'EOF' -#!/bin/bash -export LIBGL_ALWAYS_SOFTWARE=1 -# 추출된 AppImage를 직접 실행하므로 APPDIR을 명시 (AppRun 자동 감지는 인자가 있으면 실패) -export APPDIR=/opt/orca/squashfs-root -# 컨테이너 기본 seccomp 프로필이 sandbox의 namespace 생성을 차단함 -# sandbox가 필요하면 ELECTRON_DISABLE_SANDBOX를 비우고 실행 (privileged 환경) -export ELECTRON_DISABLE_SANDBOX="${ELECTRON_DISABLE_SANDBOX-1}" -exec /opt/orca/squashfs-root/AppRun "$@" -EOF -chmod +x /usr/local/bin/orca-server - -apt-get clean -rm -rf /var/lib/apt/lists/* -echo "Orca headless server installed successfully."