본문으로 건너뛰기

설치

PlantPulse Studio 를 고객사 서버 한 대에 설치하는 절차입니다. 도커 컨테이너 4종(웹 · 서버 · 빌더 사이드카 · 도커 프록시)으로 구성되며, 설치 스크립트가 도커 설치부터 기동까지 자동으로 처리합니다.

PostgreSQL 은 스택에 없습니다

외부 PG 를 씁니다 — 플랫폼이 쓰는 것과 같은 PG 를 나눠 쓰면 됩니다. 예전에는 번들 컨테이너를 띄우는 모드가 있었지만 없앴습니다(2026-08-28) — 한 서버에서 5432 를 두고 다투는 사고가 실제로 있었습니다.

인터넷이 되지 않는 폐쇄망이라면 이 문서 대신 폐쇄망 설치를 보세요.


1. 사전 요구사항​

서버 사양​

항목최소권장비고
CPU4 코어8 코어 이상앱 세션마다 빌드가 돌아갑니다
메모리8 GB16 GB 이상아래 표 참조
디스크50 GB 여유100 GB 이상컨테이너 이미지만 약 6 GB
OSLinux (x86_64)RHEL 9 / Rocky 9 / Ubuntu 22.04 이상systemd + 도커가 돌면 됩니다

메모리 산정 근거 — 스택이 고정으로 잡는 상한값입니다.

컨테이너메모리 상한
studio-server (오케스트레이터)2 GB
agent-server (빌더 사이드카)2 GB (AGENT_SERVER_MEMORY 로 조정)
studio-web (nginx)256 MB
docker-proxy (도커 창구)128 MB
앱 세션 컨테이너 1개당2 GB

앱 세션 컨테이너는 사용자가 프로젝트를 열 때마다 하나씩 뜨고, 30분간 쓰지 않으면 자동 회수됩니다. 동시 작업자 수만큼 2 GB 를 더해 잡으세요.

동시 사용자 3~5명이 목표라면

16 GB 메모리 · 8 코어 · 100 GB 디스크를 기준으로 잡으면 여유롭습니다.

소프트웨어​

  • Docker Engine + Docker Compose v2 (v2.22 이상) 설치되어 있지 않으면 원라인 설치 스크립트가 자동으로 설치합니다.
  • curl, tar (대부분의 배포판에 기본 포함)

포트와 방화벽​

스택은 host 네트워크로 동작합니다. 즉 컨테이너 포트가 호스트에 그대로 열리므로, 접근 통제는 오직 서버 방화벽으로만 이뤄집니다.

포트용도방화벽
80스튜디오 UI (nginx)개방
5171앱 리스너 — 프리뷰 · 배포앱 · QR 접속개방(필수)
5170스튜디오 API웹이 내부 프록시 — 개방 불필요(진단용)
8000빌더 사이드카(agent-server)루프백 전용 — 절대 개방 금지
5172MCP 도구 브리지루프백 전용 — 절대 개방 금지
2375도커 데몬 창구(docker-proxy)127.0.0.1 에만 바인드 — 스택에서 유일하게 host 네트워크가 아닌 컨테이너
5171 을 막으면 프리뷰와 배포앱이 안 열립니다

프리뷰(/preview/…)와 배포앱(/apps/…)은 보안 경계 때문에 스튜디오와 다른 오리진 (기본 5171 포트)에서 서빙됩니다. 채팅으로 만든 앱의 자바스크립트가 스튜디오 로그인 토큰에 접근하지 못하게 하는 장치입니다. 5171 을 열지 않으면 화면이 비어 보입니다.

8000 · 5172 는 인증이 없습니다

두 포트는 내부 부품 간 통신 전용이며 인증 절차가 없습니다. 외부에 노출되면 전체 사용자 워크스페이스를 읽고 쓸 수 있게 됩니다. 스택은 기본적으로 루프백에만 바인드하지만, 방화벽에서도 절대 열지 마세요.

레지스트리 접근​

이미지는 KOPENS 레지스트리(docker.kopens.io)에서 받습니다. 설치 전에 한 번 로그인하세요. 계정은 KOPENS 운영팀이 발급합니다.

docker login docker.kopens.io

2. 데이터베이스 준비​

스튜디오는 외부 PostgreSQL 을 씁니다. 플랫폼이 쓰는 것과 같은 PG 를 나눠 쓰면 됩니다.

DATABASE_URL 은 필수입니다

비어 있으면 설치가 그 자리에서 멈추고 무엇이 비었는지 알려 줍니다 — 그럴듯한 기본값을 지어내지 않습니다. 그럴듯한 기본값이 엉뚱한 곳을 가리켜 사고가 난 적이 있어서입니다.

DATABASE_URL=postgres://`<사용자>`:`<비밀번호>`@`<DB호스트>`:5432/`<DB명>`

데이터베이스와 계정을 먼저 만듭니다​

🔴 서버는 «있는» 데이터베이스에 마이그레이션만 합니다. DB 와 역할을 만들지는 않습니다. 만들어 주는 스크립트가 따로 있습니다.

# .env 의 DATABASE_URL 이 가리키는 DB · 역할을 만든다
bin/provision-platform-db.sh

# 관리자 계정을 따로 줘야 하는 경우
ADMIN_URL=postgres://`<관리자>`:`<비밀번호>`@`<호스트>`:5432/postgres bin/provision-platform-db.sh

# 무엇을 할지 보기만 한다
DRYRUN=1 bin/provision-platform-db.sh

AI 쪽과 같은 규칙입니다.


3. 설치 (원라인 · 권장)​

서버에 root 로 접속해 한 줄을 실행합니다. 스크립트가 도커 확인·설치 → 패키지 내려받기 → .env 생성 → 스택 기동까지 수행합니다.

curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo bash

설치 위치는 /opt/kopens/plantpulse-studio-docker 이고, 이후 모든 운영 명령은 이 디렉터리에서 실행합니다.

값을 미리 정해 무인 설치​

환경변수는 bash 앞에 붙여야 파이프를 타고 전달됩니다.

curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh \
| sudo PLATFORM_API_TARGET=https://192.168.0.41 \
DATA_ROOT=/data1/pp-studio \
PS_ANTHROPIC_API_KEY=sk-ant-... \
bash

값을 물어보게 하기​

curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo INTERACTIVE=1 bash
⌨️ 새 박스에 설치 — 처음부터 «Ready» 까지 2분 6초. 실제 실행을 녹화해 지연까지 그대로입니다(이미지 내려받는 정적 구간은 접혀 있습니다)
이 화면은 «새» 박스에서만 나옵니다

이미 설치된 서버에서 다시 실행하면 사이드카에 값이 있어 프롬프트를 통째로 건너뜁니다. 위 화면은 깨끗한 박스의 첫 설치입니다.

묻는 것은 다섯뿐입니다(2026.09 부터). 나머지 일곱은 기본값으로 정하고, 정해진 값들을 설치 끝에 목록으로 찍습니다 — 숨기는 것이 아니라 보여주고 넘어갑니다.

순서묻는 것변수
1플랫폼 API 서버 (https://…)PP_API_URL
2플랫폼 API 토큰 (X-API-Key)PP_API_TOKEN
3AI 게이트웨이 주소 — /v1 까지 포함PI_LLM_URL
4AI 게이트웨이 키 (LiteLLM 마스터 키)PI_LLM_API_KEY
5AI 모델 이름 — 게이트웨이에 등록된 이름AI_MODEL
이 값들을 준비해 두십시오 — 없으면 설치가 중단됩니다

필수 값이 비면 설치가 멈춥니다. 「설치는 끝난 것처럼 보이는데 스택이 안 뜨는」 상태를 막으려는 의도된 동작입니다. DATABASE_URL 은 묻지 않습니다 — PostgreSQL 이 스택에 함께 오고 설치가 스스로 채웁니다.

묻지 않고 기본값을 쓰는 것 일곱 — DATA_ROOT(/var/lib/pp-studio) · AI_PROVIDER(openai-compatible) · PP_LANG(en) · PP_TZ(Asia/Seoul) · APPS_PORT(5171) · REGISTRY(docker.kopens.io/ps) · PP_IMAGE_TAG(현재 캘린더 버전).

모델 이름을 묻는 이유

게이트웨이가 서빙하지 않는 모델 이름을 적으면 틀린 키와 똑같이 실패합니다. 그래서 기본값에서 빼고 묻는 값으로 올렸습니다.

바꾸려면 환경변수로 주거나 PP_ADVANCED=1 로 실행해 전부 묻게 하면 됩니다.

PS_ANTHROPIC_API_KEY 는 이제 묻지 않습니다

클라우드 Claude 를 쓸 때만 필요한 값인데 이 제품은 게이트웨이가 전제라, 사실상 모두가 엔터로 넘기던 질문이었습니다. 기능은 그대로이고 넣는 길이 셋입니다 — 설치 때 환경변수로, 나중에 bin/passwd.sh PS_ANTHROPIC_API_KEY(값을 생략하면 프롬프트로 받아 명령행에 남지 않습니다), 또는 화면의 설정 > AI 입니다. PP_ADVANCED=1 로도 묻지 않습니다.

다시 실행해도 안전합니다

같은 서버에서 설치 스크립트를 재실행하면 코드만 갱신되고 .env 와 로컬 백업(dist/)은 보존됩니다. 업그레이드 용도로 그대로 써도 됩니다.

설치 스크립트가 404 나 옛 버전을 준다면

CDN 캐시가 남아 있을 수 있습니다. URL 뒤에 ?v=$(date +%s) 를 붙여 다시 받으세요.

값을 틀리게 주면 설치 전에 멈춥니다​

기계를 건드리기 전에 — 도커를 깔기도 전에 — 받은 자격증명으로 실제 호출을 한 번씩 해 봅니다. 플랫폼은 GET /api/v5/site?limit=1, 게이트웨이는 GET /v1/models 입니다.

⌨️ 틀린 게이트웨이 키로 실행 — 플랫폼 토큰이 401 로 거부돼 설치가 서지 않고 멈춥니다. 뒤의 «503» 은 경고로만 남습니다

거부만 멈춥니다. 「못 물어봤다」는 멈추지 않습니다.

응답판정설치
2xx받아들여짐계속
401 · 403자격증명이 틀림중단
400설치 스크립트의 요청이 잘못됨 — 제품 결함이니 알려 주십시오중단
5xx · 도달 실패판정 불가경고만, 계속

5xx 를 401 과 같은 칸에 넣지 않은 것은 의도입니다 — 부하가 걸린 플랫폼이 503 을 내고 3분 뒤 같은 토큰이 401 을 낸 사례가 있었습니다. 점검 중이거나 스튜디오보다 늦게 뜨는 게이트웨이를 「틀린 키」로 몰면 멀쩡한 설치가 거부됩니다.

모델 이름도 같이 봅니다. 게이트웨이의 /v1/models 응답을 읽어 AI_MODEL 이 그 목록에 있는지 확인하고, 없으면 서빙하는 목록을 찍고 멈춥니다. 목록을 읽지 못했을 때는 판정하지 않습니다 — 답이 비었는데 「없다」고 말하는 것은 실패할 수 없는 검사와 같은 거짓이기 때문입니다.

검사를 건너뛰려면 — PP_SKIP_CRED_CHECK=1

자리표시자 자격증명으로 설치 자체만 시험할 때 쓰는 문입니다(제품의 릴리즈 게이트가 이 경로를 씁니다). 운영 설치에서 이 값을 켜는 것은, 첫 사용에서 날 실패를 설치 시점에서 뒤로 미루는 것뿐입니다.


4. 설치 (수동)​

패키지를 이미 받아 놓았거나 .env 를 직접 작성하려는 경우입니다.

cd /opt/kopens/plantpulse-studio-docker
sudo cp .env.example /etc/kopens/plantpulse-studio.env
sudo vi /etc/kopens/plantpulse-studio.env # 아래 표를 보고 값 채우기
sudo chmod 600 /etc/kopens/plantpulse-studio.env
bash bin/up.sh
설정 정본은 /etc/kopens/plantpulse-studio.env 하나다

환경변수 파일은 설치 디렉터리 안이 아니라 repo 트리 밖인 /etc/kopens/plantpulse-studio.env 에 둡니다. 권한은 0600 입니다. platform · ai · studio 세 제품이 같은 규약으로 /etc/kopens/plantpulse-<제품>.env 를 씁니다.

설치 디렉터리 루트의 .env 는 옛 경로입니다. compose 도 나머지 스크립트도 이제 그 파일을 읽지 않으므로, 거기를 고쳐도 스택은 바뀌지 않습니다. 기존 설치의 .env 는 bin/install.sh 가 새 경로로 옮겨 줍니다(새로 만들지 않습니다 — 새로 만들면 DB 는 옛 비밀번호인데 env 는 새 기본값이 되어 다음 재기동에서 죽습니다).

환경변수 주요 항목​

항목설명기본값
REGISTRY이미지 레지스트리. 비우면 pull 없이 로컬 이미지만 사용(에어갭)docker.kopens.io/ps
PP_IMAGE_TAG이미지 태그. 비우면 VERSION 의 칼버를 따라갑니다. 못 박으려면 스냅샷 태그(2026.09-20260911) 권장(빈 값)
DATA_ROOT모든 데이터(워크스페이스 · 빌드 · 상태 · DB)가 저장될 경로/var/lib/pp-studio
PLATFORM_API_TARGET실데이터·인증을 위임할 PlantPulse 플랫폼 주소https://192.168.0.41
APPS_PORT앱 리스너 포트(프리뷰·배포앱)5171
PP_LANG웹 기본 언어 ko / enen
PP_TZ타임존(IANA ID). "오늘 · 어제 · 지난주" 해석 기준Asia/Seoul
STUDIO_CORS_ORIGINS다른 도메인에서 API 를 부를 때만 지정. 기본은 차단비움
DATABASE_URL외부 PostgreSQL 접속 주소 — 위 2절 참조비움 (필수)
PI_LLM_API_KEY · PP_API_TOKEN 등AI · 플랫폼 키 — 비밀 관리 참조비움 (필수)
계정과 역할은 환경변수로 나르지 않습니다

계정은 환경변수로 나르지 않습니다. 서버가 계정이 하나도 없을 때 부트스트랩 관리자를 DB 에 심고, 그 뒤로는 앱의 환경설정 → 사용자 관리에서 계정을 추가·삭제하고 역할(관리자 / 빌더 / 뷰어)을 줍니다. 아래 7. 첫 로그인 참조.

한국어 현장은 PP_LANG=ko 를 직접 넣으세요

원라인 설치가 만드는 .env 에는 PP_LANG · PP_TZ 줄이 없어 기본값(en / Asia/Seoul)이 적용됩니다. 한국어로 띄우려면 .env 에 PP_LANG=ko 를 추가하고 bash bin/restart.sh.

.env 를 고친 뒤에는 반드시 bin/restart.sh

docker restart 는 .env 를 다시 읽지 않습니다. 값이 반영되지 않은 채로 "왜 안 바뀌지"를 한참 찾게 되는 대표적인 함정입니다. 자세한 내용은 비밀 관리에 있습니다.


5. 스택 구성 확인​

설치가 끝나면 컨테이너 넷이 떠 있어야 합니다. PostgreSQL 은 여기 없습니다 — 외부 PG 를 씁니다.

컨테이너역할
plantpulse-studio-web스튜디오 UI(nginx :80) — 정적 화면 + API 프록시
plantpulse-studio-server오케스트레이터(:5170) — 에이전트 · 세션 · 배포 · 앱 리스너(:5171)
plantpulse-studio-agent-server빌더 사이드카 — 앱 코드를 작성하는 코딩 엔진
plantpulse-studio-docker-proxy도커 데몬 창구. 127.0.0.1 에만 열립니다
오케스트레이터는 도커 소켓을 직접 잡지 않습니다

docker.sock 을 서버에 물리지 않고 도커 프록시를 거칩니다. 세션 컨테이너를 만들고 지우려면 데몬이 필요한데, 소켓을 그대로 주면 그것은 호스트 전체 권한과 같습니다.

프록시만 host 네트워크를 쓰지 않고 127.0.0.1:2375 에 게시합니다. host 네트워크였다면 2375 는 방화벽 말고는 아무것도 지키지 않는 열린 데몬이 됐을 것입니다.

도커 프록시는 «탈출을 막는 경계» 가 아닙니다

세션 컨테이너를 만들려면 POST /containers/create 가 필요하고, 그것을 부를 수 있는 쪽은 / 를 마운트한 컨테이너도 만들 수 있습니다. 프록시의 통제 단위는 엔드포인트이지 파라미터가 아닙니다.

이 프록시가 실제로 하는 일은 둘입니다 — 쓰지 않는 API 표면(볼륨 · 시크릿 · 설정 · 스웜 · 노드 · 플러그인 · 시스템 이벤트)을 잘라 내는 것, 그리고 데몬 접근을 한 지점으로 모아 감사 가능하게 만드는 것. 허용 범위는 studio-server 가 실제로 쓰는 것만으로 좁혀져 있습니다(컨테이너 · 이미지 · 빌드 · exec · 네트워크 · 레지스트리 로그인).

그러니 이 스택을 신뢰 경계 안쪽에 두세요. 앱을 만드는 사람이 곧 호스트에 대한 권한을 갖는 것으로 보고 운영해야 합니다.

여기에 더해, 사용자가 프로젝트를 열면 앱 세션 컨테이너가 프로젝트마다 하나씩 뜹니다. 스택 컨테이너가 아니라 서버가 필요할 때 만들고 유휴 30분 후 회수합니다.


6. 기동 확인​

cd /opt/kopens/plantpulse-studio-docker

bash bin/status.sh # 컨테이너 상태 + 헬스 + 세션 컨테이너 수
curl -s localhost:5170/health

설치 직후에는 종합 점검 스크립트를 한 번 돌리는 것을 권장합니다. 헬스 · 웹 응답 · 실제 로그인 · 세션 런타임 이미지 · 컨테이너 상태를 모두 검사하고, 하나라도 실패하면 오류로 끝납니다.

bash bin/smoke-install.sh
✓ health
✓ client-config → {"lang":"ko",...}
✓ web 200
✓ 로그인(부트스트랩 계정)
✓ 세션 런타임 이미지
✓ 컨테이너 running(...)
✅ 설치 스모크 통과
세션 런타임 이미지

앱 세션 컨테이너는 plantpulse-studio-runtime 이미지를 씁니다. bin/up.sh 가 로컬에 없을 때 내려받아 태그하지만, 실패하면 ⚠️ 세션 런타임 이미지 없음 경고가 뜨고 프로젝트 열기가 실패합니다. 이때는 레지스트리 로그인 상태를 확인하고 bash bin/up.sh 를 다시 실행하세요.

이미 로컬에 있으면 up.sh 는 건드리지 않습니다 — 새 버전으로 바꾸려면 bin/update.sh 입니다.


7. 첫 로그인​

브라우저에서 http://<서버IP>/ 로 접속합니다.

계정이 하나도 없는 상태에서 서버가 처음 뜨면 부트스트랩 관리자를 DB 에 심습니다.

항목값
아이디admin
비밀번호admin123!

부팅 로그에도 남습니다 — 부트스트랩 관리자 생성: admin — 로그인 후 비밀번호를 바꿀 것.

가장 먼저 이 비밀번호를 바꾸세요

모든 설치에서 같은 값입니다. 로그인하자마자 환경설정 → 사용자 관리에서 바꾸세요 (비밀번호 재설정, 6자 이상). 환경변수 파일을 고치는 것으로는 바뀌지 않습니다 — 서버는 로그인할 때 ①DB → ②환경변수 순으로 보고 DB 에서 잡히면 거기서 끝냅니다.

로그인 후 할 일:

  1. 환경설정 → 사용자 관리 — 부트스트랩 관리자 비밀번호 변경, 현장 사용자 계정 생성, 역할(관리자 / 빌더 / 뷰어) 지정
  2. 비밀번호 · API 키 변경 — DB 계정과 AI 키를 bin/passwd.sh 로 한 번에 회전. 커미셔닝 때 한 번만 하면 됩니다
  3. 환경설정 → AI — AI 프로바이더와 키 연결 상태 확인
  4. 환경설정 → 플랫폼 — 플랫폼 주소·키 연결 확인(실데이터 조회에 필요)

자세한 화면 설명은 시작하기와 환경설정(관리자)에 있습니다.​

8. 일상 운영 명령​

모든 명령은 설치 디렉터리(/opt/kopens/plantpulse-studio-docker)에서 실행합니다.

bash bin/up.sh # 시작(레지스트리에서 최신 이미지 pull 포함). 종료 코드 0 = «준비됨»
bash bin/down.sh # 중지 — 데이터는 그대로 보존, 세션 컨테이너도 정리
bash bin/restart.sh # 재기동(.env 변경 반영)
bash bin/status.sh # 상태 요약
bash bin/logs.sh # 서버 로그 팔로우(기본 studio-server)
bash bin/logs.sh studio-web # 특정 서비스 로그
bash bin/update.sh # 이미지 갱신 + 재기동 → [업데이트](#9-업데이트)
bash bin/remove.sh # 컨테이너·네트워크 제거. **데이터는 보존이 기본**
start.sh · stop.sh 라는 옛 이름도 그대로 동작합니다

세 제품(Platform · AI · Studio)이 같은 이름·같은 뜻의 공통 동사를 쓰도록 정리됐습니다 — up · down · restart · status · logs · update · remove.

옛 이름은 지우지 않았습니다. bin/start.sh 와 bin/stop.sh 는 계속 같은 일을 합니다 (CI 가 원격 호스트에서 그 이름을 직접 부릅니다). 기존 런북을 한꺼번에 고치지 않아도 됩니다.

down.sh 가 컴포즈만 부르지 않는 이유

Studio 서버가 앱을 열 때 만드는 세션 컨테이너(plantpulse-studio=1 라벨)는 컴포즈가 관리하지 않습니다. docker compose down 만 부르면 그것들이 그대로 돌아갑니다 — 「내렸는데 컨테이너가 살아 있다」가 되는 자리입니다. down.sh 는 그것까지 정리합니다.

remove.sh 는 데이터를 지우지 않습니다 — 지우려면 명시해야 합니다
bash bin/remove.sh # 컨테이너·네트워크만. 데이터 그대로
bash bin/remove.sh --volumes # 데이터까지 (확인을 묻습니다)
bash bin/remove.sh --volumes --yes # 자동화용 — 묻지 않습니다

이 스택의 진짜 데이터는 named volume 이 아니라 DATA_ROOT 바인드 마운트(기본 /var/lib/pp-studio)에 있습니다 — PG 데이터 · 워크스페이스 · 빌드 산출물 · 상태. docker compose down -v 로는 지워지지 않지만, 운영자가 「제거」라고 할 때는 그것까지 뜻하는 경우가 많습니다. 되돌릴 수 없으므로 명시적으로 요청할 때만 건드립니다.

세션 컨테이너만 따로 보려면,

docker ps --filter label=plantpulse-studio=1

9. 업데이트​

bin/update.sh 가 이미지를 다시 받고 재기동까지 합니다. 설치 스크립트를 다시 실행하면 운영 스크립트까지 함께 갱신됩니다(.env · 백업은 보존).

=== "이미지만 갱신"

cd /opt/kopens/plantpulse-studio-docker
bash bin/update.sh
up.sh(옛 start.sh)만으로는 «세션 런타임» 이 옛 이미지로 남습니다

up.sh 는 컴포즈 서비스의 이미지를 받아 옵니다. 그런데 사용자 앱이 실제로 도는 세션 런타임 이미지는 컴포즈 서비스가 아니라서 compose pull 에 걸리지 않고, up.sh 는 그것이 로컬에 없을 때만 받아 옵니다 — 이미 있으면 그대로 둡니다.

그래서 up.sh 만 돌리면 나머지는 새 이미지인데 세션 런타임만 조용히 옛 버전으로 남습니다. update.sh 는 그 하나를 강제로 다시 받는 것이 up.sh 와의 차이입니다.

=== "패키지까지 갱신"

curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo bash

버전 고정​

버전 표기는 YYYY.MM 형식입니다(예: 2026.08). 자동 갱신을 원치 않는 운영 환경에서는 .env 에 태그를 고정하세요.

TAG=2026.08
업데이트 전에는 백업

bash bin/backup.sh 를 먼저 돌려 두세요. 절차는 백업과 복구에 있습니다.

이미지 서명 검증(선택)​

배포된 이미지가 KOPENS 가 서명한 것인지 확인할 수 있습니다. cosign 이 설치돼 있어야 합니다.

cd /opt/kopens/plantpulse-studio-docker
bash bin/verify-images.sh
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-server:2026.08
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-web:2026.08
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-runtime:2026.08

다음 단계​