ENTERPRISE AI CODING AGENT
BizCoder 온프렘 설치·사용 가이드
사내 Kubernetes · Helm 위에 BizCoder 를 올리고, 사내 onpod · 사내 GitLab · Azure OpenAI 와 연결해 직원이 쓰기까지
- 번들 버전
- 번들 안
bundle.env참고 - 설치·운영 대상
- 시스템 아키텍처팀 · 플랫폼 운영자
- 사용 대상
- 직원 · 조직 관리자
- 작성일
- 2026-10-27 · 주식회사 AI3
- 지원 문의
- support@onpod.ai
스크린샷을 누르면 원본 크기로 열려요. 표·코드·구성도는 좌우로 밀어서 볼 수 있어요.
목차 보기
독자별 안내
| 누가 | 어디를 읽나요 |
|---|---|
| 플랫폼 운영자 (설치) | 2장 준비물 → 3장 설치 → 4장 첫 로그인·SSO → 7장 업그레이드 → 8장 보안 → 9장 문제 해결 |
| 조직 관리자 | 4장 초기 설정 → 6장 관리자 운영(멤버·좌석·배포 승인·감사 로그) |
| 직원 | 5장 직원 사용법 (GitLab 연결 → 새 작업 → 결과 확인 → 배포) |
이 가이드는 번들과 별도로 제공돼요. 번들 안의 MANUAL.ko.md 는 같은 설치 절차의 텍스트 판이에요. 화면 캡처는 예시 회사 「예시 주식회사」, 도메인 corp.example 로 구성한 실제 구동 화면이에요.
1. 개요와 구성도
1.1 BizCoder 온프렘이 하는 일
사내 Kubernetes 에 BizCoder 를 올리면, 직원이 사내 브라우저로 접속해 AI 코딩 에이전트에게 일을 시킬 수 있어요. 코딩 작업은 사내 onpod 가 띄우는 샌드박스에서 실행되고, 결과물은 사내 GitLab 에 커밋·푸시·MR 로 쌓여요. 배포하면 사내 onpod 가 https://<앱 이름>.<onpod 도메인> 주소를 만들어 줘요. LLM 은 Azure OpenAI(또는 OpenAI 호환 사내 게이트웨이)를 쓰고, 인터넷으로 나가는 호출은 없어요.
- 직원이 사내 브라우저로 BizCoder 웹 화면에 접속해 저장소 주소와 하고 싶은 일을 적어요.
- BizCoder 가 사내 onpod 에 샌드박스 Pod 를 만들어요. 코딩 에이전트는 이 Pod 안에서 일하고, 샌드박스는 소스 파일만 받아요.
- LLM 호출은 BizCoder 의 LLM 프록시를 거쳐 Azure OpenAI 로 가요. LLM 키와 Git 토큰은 BizCoder 서버에만 있어요.
- 결과는 사내 GitLab 에 브랜치로 커밋·푸시되고 Merge Request 가 자동으로 만들어져요. 클론·푸시·MR 은 BizCoder 가 대신해요.
- 「배포까지 한 번에」를 켜면 사내 onpod 에 배포돼
https://<이름>.<onpod 도메인>주소가 나와요. 기본 공개 범위는 「우리 회사 사람만」(로그인 필요)이에요.
1.2 구성도
실선은 BizCoder 가 거는 호출, 점선은 샌드박스 Pod 가 BizCoder 로 거는 호출이에요. GitLab 과는 양방향이에요(웹훅은 GitLab → BizCoder).
| 구성 요소 | 하는 일 | 위치 |
|---|---|---|
| BizCoder | 웹 화면·API, LLM 프록시, git 중계(클론·푸시·MR), onpod 호출. LLM 키·Git 토큰은 여기에만 있어요. | 네임스페이스 bizcoder 의 Deployment bizcoder (포트 8077) |
| 1 PostgreSQL | BizCoder 가 쓰는 데이터베이스. 번들 PostgreSQL 은 소규모·PoC 용이고, 운영은 사내 DB 를 권장해요. | 번들 StatefulSet 또는 사내 DB |
| 2 Azure OpenAI | LLM. BizCoder 의 LLM 프록시만 호출해요. 엔드포인트·키·모델 매핑은 values 로 지정해요. | 사내 또는 Azure (사내망에서 도달 가능해야 해요) |
| 3 사내 onpod | 샌드박스 Pod 실행, 서버 빌드(샌드박스 이미지·앱 빌드), 앱 배포. BizCoder 는 회사 조직의 서비스용 API 키 1개로 호출해요. | Kubernetes 앱 서버가 등록된 사내 onpod |
| 4 사내 GitLab | 저장소. 푸시 자동 배포를 쓰면 push 때 GitLab 이 BizCoder 로 웹훅을 보내요. | 사내 GitLab |
| 샌드박스 Pod | 코딩 에이전트가 실행되는 곳. 비밀키가 없고 클러스터 안에서 BizCoder :8077 만 불러요. | onpod 가 띄우는 Pod |
2. 준비물과 용량 산정
2.1 준비물
| 항목 | 내용 |
|---|---|
| Kubernetes | 1.25 이상. Ingress 컨트롤러(예: AWS Load Balancer Controller) 또는 LoadBalancer. 번들 DB 를 쓰면 기본 StorageClass 가 있어야 해요. |
| 사내 onpod (온프렘) | Kubernetes 앱 서버가 등록돼 있고, 컨트롤플레인에 ONPOD_SANDBOX_API_ENABLED=1 과 서버 빌드 API(ONPOD_BUILD_API_ENABLED=1)가 켜져 있어야 해요. 꺼져 있으면 3장의 provision.sh 가 알려 주고 켜는 방법을 출력해요. |
| 회사 조직 | onpod 에 회사 조직(org)이 있고 관리자 토큰이 있어야 해요. BizCoder 는 이 조직의 서비스용 API 키 1개로 onpod 를 호출해요. |
| 이미지 저장소 | 번들의 앱 이미지·DB 이미지를 올릴 사내 레지스트리 (또는 각 노드에 미리 로드). |
| LLM | Azure OpenAI 리소스 + 모델 배포 이름 + 키. 또는 /v1/chat/completions 호환 사내 게이트웨이. |
| 사내 CA | ALB·GitLab·onpod 의 인증서를 발급한 사내 루트 CA(PEM). 공인 인증서만 쓰면 필요 없어요. |
| 라이선스 | 도입 시 받은 .lic 파일(서명된 JSON). |
| 관리자 PC 도구 | helm 3.12+, kubectl, curl, python3(라이선스 서명 검증을 쓰려면 python cryptography 도). 이미지를 올릴 때만 docker. |
| 직원 쪽 | 사내 브라우저, 사내 SSO 계정(OIDC), 사내 GitLab 개인 액세스 토큰(5장). |
2.2 설치 전에 꼭 확인할 세 가지
- onpod 의 샌드박스 API·서버 빌드 API 가 켜져 있나요? onpod 차트 values 의
controlplane.extraEnv에ONPOD_SANDBOX_API_ENABLED: "1"을 넣어요. 꺼져 있으면provision.sh가 501 로 알리고 켜는 법을 출력한 뒤 멈춰요(3.3절). - 코딩 에이전트(CLI)는 x86-64 에서 AVX2 를 지원하는 CPU 가 필요해요. 가상머신이면 호스트 CPU 기능 전달 설정을 확인하세요. 3.6절의
preflight.sh --smoke-sandbox에서 「샌드박스 CLI 실행」 항목이 미리 알려 줘요. - onpod 회사 조직과 관리자 토큰이 있어야 해요. 조직이 없으면
provision.sh --create-org "회사 이름"으로 만들 수 있어요. 토큰을 따로 주지 않으면provision.sh가kubectl로 onpod 컨트롤플레인에서 읽어요.
2.3 용량 산정 — 동시에 몇 건까지 코딩할 수 있나요
코딩 작업 1건은 onpod 샌드박스 1개를 써요. 그래서 동시 코딩 작업 수는 onpod 가 가진 해당 크기 슬롯의 수예요. 샌드박스 크기는 values 의 onpod.sandbox.class(nano | micro | small | medium)로 정하고, 기본값 small 은 2 vCPU · 4 GiB 한도로 onpod 의 cpu_medium 슬롯 1개를 써요.
온프렘 onpod 는 호스트(클러스터)의 RAM 에서 슬롯을 잘라 내요.
크기별 슬롯 수 = 사용 가능 RAM × 크기별 비율 ÷ 슬라이스 크기 (RAM 이 작아도 각 크기 최소 1개)
| 슬라이스 크기 | micro | small | medium | large |
|---|---|---|---|---|
| 한 슬롯의 RAM | 512 MiB | 2 GiB | 4 GiB | 8 GiB |
기본 비율 (ONPOD_ONPREM_SLOT_MIX) | 20% | 40% | 25% | 15% |
예) RAM 16 GiB 서버 — 사용 가능 12.8 GiB 중 medium 몫은 3.2 GiB 이고 슬롯 하나가 4 GiB 라서 계산상 1개에 못 미치지만 최소 1개가 보장돼요. 그래서 medium 슬롯 1개, 곧 동시 코딩 1건이에요.
- 슬롯이 없으면 작업이 잠시 대기(최대 약 4분)하다 실패할 수 있어요. 직원이 몰리는 시간대 동시 작업 수를 기준으로 슬롯을 계산하세요.
- 배포된 앱도 같은 슬롯을 써요. 배포 앱의 기본 크기는
cpu_2c_4g(= medium)이고, values 의deploy.appShape로cpu_small등으로 낮출 수 있어요. 정적 사이트가 대부분이면 낮추는 것을 권장해요.
BizCoder 자체 요구량(차트 기본값)
| 대상 | requests | limits | 비고 |
|---|---|---|---|
| BizCoder | CPU 500m · 메모리 1Gi | 메모리 4Gi | /tmp 최대 10Gi (저장소 클론·빌드 컨텍스트 임시 공간), 레플리카 1 |
| 번들 PostgreSQL | CPU 250m · 메모리 512Mi | 메모리 2Gi | PVC 20Gi (postgres.persistence.size) |
3. 설치 (5단계)
설치는 번들 폴더 안에서 실행해요. 예시의 bizcoder 는 네임스페이스이자 릴리스 이름이에요. 아래 5단계를 순서대로 하면 끝나요.
키 · Secret · 샌드박스 이미지
my-values.yaml
helm upgrade --install
첫 로그인
3.1 번들 구성
bizcoder-onprem-<태그>-amd64/
├─ images/
│ ├─ bizcoder-app.tar 앱 이미지
│ ├─ bizcoder-sandbox-rootfs.tar.gz 샌드박스 이미지의 rootfs
│ └─ postgres.tar 번들 DB 이미지
├─ Dockerfile.sandbox 샌드박스 이미지 정의 (FROM scratch + ADD rootfs)
├─ helm/bizcoder/ Helm 차트
├─ scripts/ provision.sh · preflight.sh · seed-sandbox-image.sh · lib.sh
├─ argocd/ Application · ExternalSecret · SealedSecret 안내
├─ values.example.yaml OpenAI 호환 게이트웨이용 예시 values
├─ values.azure.example.yaml Azure OpenAI 용 예시 values
└─ bundle.env · SHA256SUMS · MANUAL.ko.md
3.2 1단계 — 번들 확인 · 이미지 올리기
sha256sum -c bizcoder-onprem-<태그>-amd64.tar.gz.sha256 # macOS: shasum -a 256 -c …
tar xzf bizcoder-onprem-<태그>-amd64.tar.gz && cd bizcoder-onprem-<태그>-amd64
sha256sum -c SHA256SUMS # 모든 파일이 OK 여야 해요
docker load -i images/bizcoder-app.tar # 앱 이미지
docker load -i images/postgres.tar # 번들 DB 를 쓸 때만
# 사내 레지스트리로 재태그해서 push (노드에 직접 로드하는 환경이면 건너뛰어도 돼요)
docker tag $(sed -n 's/^APP_IMAGE=//p' bundle.env) registry.corp.example/bizcoder/wrks-code:$(sed -n 's/^APP_TAG=//p' bundle.env)
docker push registry.corp.example/bizcoder/wrks-code:$(sed -n 's/^APP_TAG=//p' bundle.env)
- 번들 버전과 태그는 번들 안의
bundle.env에 있어요. 3단계 values 의image.tag에는bundle.env의APP_TAG값을 넣어요. - 번들 DB(
postgres.tar)도 같은 방식으로 사내 레지스트리에 올리고, 올린 주소를 values 의postgres.image.repository에 적어요. 사내 DB 를 쓰면 이 이미지는 필요 없어요. bizcoder-sandbox-rootfs.tar.gz는 docker 로 올리지 않아요. 2단계provision.sh가 사내 onpod 서버 빌드로 샌드박스 이미지를 만들 때 써요.
3.3 2단계 — 서비스 키 · Secret · 샌드박스 이미지 만들기 (provision.sh)
scripts/provision.sh \
--onpod-url https://api.onpod.corp.example --cacert ca.crt \
--license bizcoder.lic --azure-key-from-stdin \
--public-base-url https://bizcoder.corp.example
이 한 번으로 아래 8단계가 끝나요. 여러 번 실행해도 이미 만든 비밀은 건드리지 않아요. Azure 키는 표준입력(없으면 프롬프트)에서 읽어요.
![scripts/provision.sh 실행 화면 — [1/8]~[8/8] 단계가 차례로 끝나요](img/term-provision.jpg)
scripts/provision.sh 실행 화면 — [1/8]~[8/8] 단계가 차례로 끝나요| 단계 | 하는 일 |
|---|---|
| [1/8] onpod 확인 | onpod 에 연결해 에디션(onprem)·앱 주소 규칙·라이선스 상태를 확인해요. |
| [2/8] 샌드박스 API / 서버 빌드 API | 둘 다 켜져 있는지 확인해요. 꺼져 있으면(501) 켜는 방법을 정확히 출력하고 멈춰요. |
| [3/8] 회사 조직 확인 | 이 토큰이 속한 조직과 역할을 확인해요. 조직이 없으면 안내만 하고 만들지 않아요(--create-org 를 줄 때만 만들어요). |
| [4/8] 서비스용 org API 키 | 서비스 키를 발급해 Secret bizcoder-onpod 에 저장하고(값은 화면에 나오지 않아요) 스코프별로 점검해요. |
| [5/8] 앱 비밀 | WRKS_SECRET_KEY·DB 비밀번호·로그인 접근 코드를 만들어 Secret bizcoder-app·bizcoder-db·bizcoder-dev-login 에 저장해요. |
| [6/8] 라이선스 · 사내 CA · LLM 키 | Secret bizcoder-license·bizcoder-ca·bizcoder-azure 를 만들어요. |
| [7/8] 샌드박스 이미지 | 번들의 rootfs 로 사내 onpod 서버 빌드를 실행해 샌드박스 이미지를 만들어요. 외부 레지스트리·인터넷이 필요 없어요. 결과 별칭은 onpod://bizcoder-sandbox:<버전>. |
| [8/8] values 조각 | 위 값들을 담은 values.generated.yaml 을 출력해요(비밀값 없음 — Secret 이름만 참조). |
만들어지는 Secret
| Secret | 키 | 내용 |
|---|---|---|
bizcoder-onpod | token | 서비스용 org API 키 (BizCoder → onpod 호출용) |
bizcoder-app | WRKS_SECRET_KEY | 저장값 암호화 키(Fernet). 한 번 정하면 바꾸지 마세요 — 저장된 암호화 값이 읽히지 않게 돼요. |
bizcoder-db | password | 번들 PostgreSQL 비밀번호 |
bizcoder-dev-login | code | 첫 관리자 로그인 접근 코드 |
bizcoder-license | license.json | 서명된 라이선스 |
bizcoder-ca | ca.crt | 사내 CA(PEM) — 공개 인증서 |
bizcoder-azure | api-key | Azure OpenAI 키 |
서비스 키 스코프
sandboxes:* builds:* pods:* static:* 에 더해 org:read org:write 예요. 배포물을 「사내 전용」으로 걸고, 승인제에서 승인 전까지 잠그는 데 필요해요. onpod 는 키 발급·초대·멤버 변경·조직 삭제를 스코프 키로는 못 하게 막아 두었어요(계정 관리급).
--without-site-access 로 이 둘을 뺄 수 있지만, 배포 정책이 「자유」이고 공개 범위를 BizCoder 가 걸 필요가 없는 환경에서만 쓰세요. 승인제에서는 배포가 되돌려져요(9장).
자주 쓰는 옵션
| 옵션 | 설명 |
|---|---|
--onpod-url URL | (필수) 사내 onpod API 주소 |
--admin-token-file · --admin-token-env · --admin-token-stdin | 관리자 토큰 전달(하나만). 안 주면 kubectl 로 onpod 컨트롤플레인(기본 네임스페이스 onpod)에서 읽어요. |
--cacert FILE | 사내 CA PEM. BizCoder 파드도 이 CA 를 신뢰하도록 Secret 으로 저장해요. |
--create-org NAME | 회사 조직이 아직 없을 때만 이 이름으로 만들어요. |
--org-key-name NAME · --rotate-key | 같은 이름의 서비스 키(기본 bizcoder-service)가 이미 있을 때 — 다른 이름을 주거나, 기존 키를 회수하고 새로 발급해요. |
--azure-key-from-stdin · --azure-key-file FILE | Azure OpenAI 키를 읽어 Secret bizcoder-azure 에 저장해요. OpenAI 호환 게이트웨이면 --openai-key-from-stdin, APIM 등 추가 헤더는 --llm-extra-headers-file. |
--namespace NS · --kube-context CTX | 대상 네임스페이스(기본 bizcoder)와 kubectl 컨텍스트 |
--sandbox-class C · --skip-sandbox-image | values 에 넣을 샌드박스 크기(기본 small) / 샌드박스 이미지 빌드 건너뛰기 |
--dry-run | 읽기 조회만 하고 조직·키·Secret·빌드는 바꾸지 않아요. 계획만 확인할 때 써요. |
--emit-yaml DIR | 클러스터에 적용하지 않고 Secret 매니페스트를 파일(권한 600)로 저장해요. SealedSecret·ExternalSecret 을 쓰는 GitOps 용이에요(7.3절). |
--ignore-sandbox-api | 샌드박스·빌드 API 가 꺼져 있어도 계속 진행해요(나중에 켤 계획일 때). 켜기 전에는 샌드박스 이미지를 만들 수 없어요. |
provision.sh 가 「샌드박스 API 가 꺼져 있어요 (GET /v1/sandboxes → 501)」를 출력하고 멈춰요. onpod 컨트롤플레인에 환경변수를 켜세요. 서버 빌드가 꺼져 있으면 ONPOD_BUILD_API_ENABLED=1 도 같은 방법으로 켜요.
# onpod 차트 values — Helm/Argo 로 운영하면 이 방법을 권장해요 (재배포해도 유지돼요)
controlplane:
extraEnv:
ONPOD_SANDBOX_API_ENABLED: "1"
그 뒤 helm upgrade 또는 Argo CD 동기화를 하고, 컨트롤플레인이 다시 뜨면 provision.sh 를 다시 실행하세요. 임시로 바로 켜려면 kubectl -n onpod set env deploy/onpod-controlplane ONPOD_SANDBOX_API_ENABLED=1 뒤 rollout restart 를 하면 되지만 Argo CD 가 되돌릴 수 있어요.
출력: values.generated.yaml
3단계에서 my-values.yaml 과 함께 쓰는 값 조각이에요. 비밀값은 없고 Secret 이름만 들어 있어요.
publicBaseUrl: https://bizcoder.corp.example
onpod:
apiUrl: https://api.onpod.corp.example
tokenSecret: {name: bizcoder-onpod, key: token}
caSecret: {name: bizcoder-ca, key: ca.crt}
sandbox: {image: "onpod://bizcoder-sandbox:<버전>", class: small} # 샌드박스 이미지 별칭
secretKey: {existingSecret: bizcoder-app, key: WRKS_SECRET_KEY}
postgres:
auth: {existingSecret: bizcoder-db, passwordKey: password}
license: {existingSecret: bizcoder-license, key: license.json}
llm:
azure: {existingSecret: bizcoder-azure, keyKey: api-key}
auth:
devLogin:
secretRef: {name: bizcoder-dev-login, key: code}
3.4 3단계 — values 작성
번들의 values.azure.example.yaml(OpenAI 호환 게이트웨이는 values.example.yaml)을 복사해 my-values.yaml 로 고쳐요. values.generated.yaml 이 채워 준 값은 다시 적지 않아도 돼요.
| 꼭 채울 곳 | 값 |
|---|---|
image.repository · image.tag | 1단계에서 이미지를 올린 사내 레지스트리 주소 / bundle.env 의 APP_TAG |
publicBaseUrl | 직원이 접속하는 HTTPS 주소(ALB). 끝에 / 를 붙이지 않아요. 쿠키·초대 링크·Git 웹훅이 이 주소를 써요. |
llm.* | LLM 종류(azure 또는 openai)·엔드포인트·모델·단가 |
ingress.* | className, ALB 어노테이션(annotations), TLS |
helm install 이 BizCoder 차트 값 검증 실패 와 함께 어느 값이 비었는지 알려 주며 실패해요. 알 수 없는 키나 잘못된 값도 values.schema.json 이 거절해요.
Azure OpenAI 예시 (my-values.yaml 의 핵심)
image:
repository: registry.corp.example/bizcoder/wrks-code # 1단계에서 올린 사내 레지스트리 주소
tag: <bundle.env 의 APP_TAG>
publicBaseUrl: https://bizcoder.corp.example # 직원이 접속하는 HTTPS 주소(ALB)
llm:
kind: azure
azure:
endpoint: https://<리소스 이름>.openai.azure.com # Azure OpenAI 리소스 주소
apiVersion: v1 # GA v1 API. 날짜 버전이면 클래식(배포 이름) 주소
auth: api-key # api-key | bearer | both
existingSecret: bizcoder-azure # provision.sh 가 만든 Secret
keyKey: api-key
models:
main: gpt-4.1 # 사용자 화면의 모델 id (= Azure 배포 이름)
fallback: gpt-4.1-mini
curated: [gpt-4.1, gpt-4.1-mini]
modelMap: {} # 배포 이름이 다르면 매핑. 예: {gpt-4.1: my-gpt41-deployment}
prices: # KRW / 1M tokens — 예산·사용액 집계 기준
gpt-4.1: {input_krw_per_1m: <입력 단가>, output_krw_per_1m: <출력 단가>}
gpt-4.1-mini: {input_krw_per_1m: <입력 단가>, output_krw_per_1m: <출력 단가>}
sidecallModel: gpt-4.1-mini # 제목·요약 같은 보조 호출용
auth:
devLogin:
enabled: true
allowlist: [admin@corp.example] # 첫 관리자 한 명만
secretRef: {name: bizcoder-dev-login, key: code}
superadminEmails: [admin@corp.example]
ingress:
enabled: true
className: alb
annotations: # AWS Load Balancer Controller 예 — 환경에 맞게 조정
alb.ingress.kubernetes.io/scheme: internal
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: <ACM 인증서 ARN>
alb.ingress.kubernetes.io/healthcheck-path: /api/auth/mode
alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=300 # 스트리밍 응답이 끊기지 않게
- 모델 단가표(
llm.prices)는 예산·사용액 집계의 기준이에요. 표에 없는 모델의 작업은 시작할 때 「단가를 찾지 못했어요」로 막혀요. 모든 모델을 적지 않으려면llm.priceOptional: true(단가표에 없는 모델을 0원으로 집계해 허용, 토큰은 기록)를 쓸 수 있어요. auth.devLogin.allowlist에는 첫 관리자 한 명만 넣어요. 이메일마다 별도 조직이 만들어지기 때문이에요. 다른 관리자는 4장 이후 초대로 추가해요.- ALB 가 TLS 를 끝내면(권장)
ingress.tls.secretName은 비워 둬요. Ingress 컨트롤러가 TLS 를 끝내는 환경이면 인증서 Secret 이름을 넣어요. ALB 대상 유형이instance면service.type을NodePort로 맞춰요.
환경에 따라 더 채우는 값
| 상황 | 값 |
|---|---|
| 사내 PostgreSQL 사용(운영 권장) | postgres.enabled: false + database.urlSecret.{name,key}(DATABASE_URL 이 든 Secret) |
| OpenAI 호환 사내 게이트웨이 | llm.kind: openai + llm.openai.baseUrl·existingSecret (values.example.yaml 참고) |
| 모델 id 와 Azure 배포 이름이 다름 | llm.modelMap (예: {gpt-4.1: my-gpt41-deployment}) |
| APIM 등이 추가 헤더를 요구 | llm.extraHeaders.existingSecret (JSON, Secret 으로만 받아요) |
| 사내 CA 로 서명된 GitLab·onpod | onpod.caSecret (provision.sh 가 만든 bizcoder-ca) |
| 생성된 앱의 빌드가 npm/PyPI 를 씀 | build.npmRegistry · build.pipIndexUrl 에 사내 미러 주소 |
| 정적 사이트가 많아 배포 앱을 작게 | deploy.appShape: cpu_small (2.3절) |
| 샌드박스 크기 변경 | onpod.sandbox.class (nano | micro | small | medium) |
3.5 4단계 — helm 으로 설치
helm upgrade --install bizcoder ./helm/bizcoder -n bizcoder --create-namespace \
-f values.generated.yaml -f my-values.yaml
kubectl -n bizcoder rollout status deploy/bizcoder
앱은 시작할 때 DB 마이그레이션을 실행해서 첫 기동에 시간이 걸릴 수 있어요(시작 프로브는 최대 약 6분을 기다려요). 설치가 끝나면 NOTES 로 접속 주소와 확인 명령, 첫 관리자 로그인 방법이 나와요.

helm upgrade --install 뒤에 나오는 NOTES — 접속 주소와 첫 관리자 로그인 안내kubectl -n bizcoder logs deploy/bizcoder --tail=50
curl -sS https://bizcoder.corp.example/api/auth/mode
3.6 5단계 — 점검(preflight.sh)과 첫 로그인
scripts/preflight.sh --from-release bizcoder/bizcoder --cacert ca.crt \
--gitlab-url https://gitlab.corp.example --license bizcoder.lic --smoke-sandbox
--from-release bizcoder/bizcoder는 helm 릴리스 값(helm get values)과 Secret 에서 주소·모델·키를 읽어 채워 줘요. 직접 준 옵션이 우선이에요.--smoke-sandbox는 샌드박스를 실제로 하나 띄웠다가 지워요(가장 작은nano). 샌드박스 이미지·도구·코딩 에이전트 실행·BizCoder 로의 도달을 한 번에 확인해요.- FAIL 이 하나라도 있으면 종료 코드가 1 이에요. 모두 PASS 면 다음 장으로 가요.

scripts/preflight.sh --smoke-sandbox 점검 표 — 모두 PASS 면 준비 끝이에요| 점검 항목 | 확인하는 것 |
|---|---|
| onpod 도달·TLS 신뢰 / 에디션 | onpod API 에 닿고 사내 CA 로 인증서를 검증할 수 있는지, 온프렘 에디션과 앱 주소 규칙·라이선스 상태 |
| 샌드박스 API / 서버 빌드 API | 두 API 가 켜져 있는지(GET /v1/sandboxes·/v1/builds 가 200) |
| 샌드박스 이미지 · 서비스 키 스코프 | onpod://bizcoder-sandbox:<버전> 의 빌드 성공 기록, 서비스 키에 필요한 스코프 |
| Azure 엔드포인트 도달 / 최소 호출 | LLM 엔드포인트에 닿고, 모델(배포) 1토큰 호출이 200 으로 돌아오는지 |
| GitLab 도달·TLS | 사내 GitLab 에 닿고 인증서를 신뢰하는지 |
| BizCoder 주소 DNS · ALB/Ingress → 앱 | 접속 주소가 풀리고, /api/auth/mode 가 JSON 으로 응답하는지 |
| 라이선스 | 서명·만료·좌석을 오프라인으로 검증 |
| 샌드박스 생성 / 도구 / CLI 실행 | 샌드박스가 뜨는지, 안에 git·Python·코딩 에이전트(CLI)가 있는지, CLI 가 실제로 실행되는지(x86-64 는 AVX2 필요) |
| 샌드박스 → BizCoder | 샌드박스에서 BizCoder 내부 주소(:8077)에 닿는지 — LLM 프록시·파일 전송 경로 |
4. 첫 로그인과 초기 설정
4.1 첫 관리자 로그인
- 접근 코드를 확인해요. 코드는 화면에 직접 출력되지 않아요.
kubectl -n bizcoder get secret bizcoder-dev-login -o jsonpath='{.data.code}' | base64 --decode; echo - 브라우저로
https://<BizCoder 주소>/?dev=1에 접속해요(주소 뒤에?dev=1). 일반 주소(/)에는 「사내 SSO로 로그인」 단추만 보여요. - 「이메일로 시작」 칸에
auth.devLogin.allowlist에 적은 이메일을 넣고 단추를 눌러요. 접근 코드를 물으면 1번의 값을 입력해요. - 첫 관리자는 조직 소유자가 돼요.
/?dev=1 주소·allowlist 이메일·접근 코드를 확인하고, publicBaseUrl 이 https 인지도 확인하세요(9장).
4.2 회사 이름 입력 (처음 한 번)
첫 관리자가 처음 들어오면 「회사 이름을 알려주세요」 창이 떠요. 직원을 초대할 때, 만든 사이트를 공유할 때, 사용 내역서에 이 이름이 보여요.
처음에는 임시 이름으로 되어 있고, 나중에 조직 관리에서 바꿀 수 있어요. 이름을 적고 「저장하고 시작하기」를 누르면 콘솔로 들어가요.

4.3 라이선스 확인
콘솔에서 조직 관리 → 라이선스 로 가서 사용 중 좌석 / 전체, 만료일, 상태를 확인해요. 좌석은 활성 멤버 + 아직 합류하지 않은 초대를 합한 수예요. 좌석이 다 차면 새 멤버를 초대하거나 회사 SSO 로 새로 들어올 수 없어요(6.1절).

캡처의 좌석 수·만료일은 예시예요.
4.4 회사 SSO(OIDC) 설정
SSO 를 설정하면 직원은 별도 가입 없이 회사 계정으로 들어와요. 퇴사자는 IdP 에서 계정을 끊는 즉시 차단돼요. OIDC 가 기준이고, SAML 전용 IdP 는 도입 문의가 필요해요(설정 화면 안내).
- 사내 IdP(Okta·Entra 등)에 OIDC 앱(클라이언트)을 만들고, 리디렉션 URI 로
https://<BizCoder 주소>/api/auth/oidc/callback을 등록해요. SSO 설정 화면의 「리디렉션 URI (IdP 앱에 등록)」에 표시되는 값이에요. - BizCoder 에서 조직 관리 → 정책 → 「회사 SSO」 로 가서 발급자(issuer, https), 클라이언트 ID / 시크릿, 회사 이메일 도메인을 입력해요.
- 저장하면 「연결됨 · 도메인」으로 바뀌고 직원 로그인 주소(
…/api/auth/oidc/start?domain=<이메일 도메인>)가 표시돼요. 「변경」·「해제」 단추로 바꾸거나 끌 수 있어요.

직원이 로그인하는 모습 — 직원은 일반 주소의 「사내 SSO로 로그인」 → 회사 이메일 입력 → 사내 IdP 로그인 순서로 들어오고, 자동으로 조직 멤버로 합류해요.


4.5 초기 설정 체크리스트
- 라이선스의 상태·만료일·좌석을 확인했어요 (4.3절)
- 회사 이름을 입력했어요 (4.2절)
- 회사 SSO 를 설정하고 직원 계정으로 로그인해 봤어요 (4.4절)
- 배포 정책을 확인했어요 — 기본은 승인제예요 (6.3절)
- 직원에게 GitLab 개인 액세스 토큰 발급·연결 방법을 안내했어요 (5.2절)
- 다른 관리자는 멤버 초대로 추가했어요 (6.1절)
5. 직원 사용법
직원이 BizCoder 로 코딩을 맡기고 결과를 받는 순서예요. 필요한 것은 사내 SSO 계정과 사내 GitLab 개인 액세스 토큰이에요.
5.1 콘솔 둘러보기
로그인하면 콘솔이 열려요. 왼쪽 위의 「새 작업」으로 작업을 시작하고, 아래로 내 작업 목록이 쌓여요. 「내 배포 사이트」에서 내가 올린 사이트를 볼 수 있고, 왼쪽 아래에는 연결된 저장소 계정이 표시돼요.

5.2 GitLab 연결 (처음 한 번)
작업 결과를 사내 GitLab 에 푸시하고 MR 을 만들려면 직원마다 개인 액세스 토큰을 한 번 등록해요.
- 사내 GitLab 에서 개인 액세스 토큰(Personal Access Token)을 만들어요. 권장 범위는
api·read_repository·write_repository예요. - BizCoder 에서 내 계정 → 연결 계정 으로 가서 GitLab 줄의 「연결」을 눌러요.
- GitLab 호스트(예:
gitlab.corp.example)와 Personal Access Token 을 입력하고 「연결」을 눌러요. - GitLab 줄에 초록 점이 켜지고 서버 주소가 보이면 끝이에요. 끊으려면 「해제」를 눌러요.



api 권한이 없으면(read_api·write_repository 만 고르면) 커밋 화면의 배포 결과(✓·×) 표시가 실패해요(연결 창 안내). 사내 CA 로 서명된 GitLab 이면 관리자가 onpod.caSecret 에 그 CA 를 포함해야 해요(3.4절).
5.3 새 작업 시작
- 왼쪽 위 「새 작업」(단축키 N)을 눌러요.
- 입력창에 하고 싶은 일을 평소 말투로 적어요. 예: 「팀 회식 장소를 투표로 정하는 간단한 한 페이지 웹앱을 index.html 하나로 만들어 줘. 장소를 추가하고 항목마다 투표 수를 올릴 수 있게, 결과는 막대로. 외부 CDN 없이.」
- 아래 줄에 저장소 URL을 적어요. 예:
https://gitlab.corp.example/dev/hello.git. 저장소 없이도 작업할 수 있고, 이때는 결과를 내려받아요. - 결과를 바로 사내 서버에 올리려면 「배포까지 한 번에」 스위치를 켜요. 「완성되면 사내 서버에 자동 배포돼요. 결과가 웹앱일 때만요」라고 안내돼요.
- 「코딩 시작」을 눌러요. 모델 선택 칸에는 관리자가 values 의
llm.models로 정한 모델이 나와요.

5.4 진행 보기
작업이 시작되면 가운데에 샌드박스 준비됨 → 툴체인 설치됨 → 리포 클론됨 → 에이전트 시작 → 에이전트가 작업 중 순서로 단계가 쌓이고, 도구 실행과 테스트가 실시간으로 보여요. 이 창을 닫거나 다른 일을 해도 작업은 서버에서 계속되고, 나중에 다시 들어오면 이어서 볼 수 있어요. 중간에 말을 걸려면 아래 입력창에 후속 메시지를 보내고(Enter 전송 · Shift+Enter 줄바꿈), 멈추려면 위쪽 「작업 중단」을 눌러요.

오른쪽 탭은 파일(만든·고친 파일), 바뀐 곳(줄 단위 변경), 작업 기록, GitLab(MR) 이에요.
5.5 결과 확인 — MR 과 바뀐 곳
작업이 끝나면 위쪽에 「완료」와 함께 「PR」·「사이트 열기」·「다시 배포」·「결과 다운로드」 단추가 생겨요. 가운데에는 MR 링크와 라이브 사이트 주소가 나와요. 카드에는 「Merge Request 생성됨」과 주소(…/-/merge_requests/<번호>), 「GitLab에서 열기」 단추가 나와요.

「바뀐 곳」 탭에서는 무엇이 달라졌는지 파일별·줄 단위로 볼 수 있어요. 새로 추가된 줄은 초록, 지워진 줄은 빨강이에요. 파일 이름 옆의 +264 −0 같은 숫자는 추가·삭제된 줄 수예요.
사내 GitLab 에서도 같은 MR 을 볼 수 있어요. 작업마다 MR 이 하나씩 생기고, 제목에는 요청한 내용이 들어가요. 검토와 머지는 평소 쓰는 GitLab 방식으로 하면 돼요.


5.6 배포된 사이트 열기
「배포까지 한 번에」를 켰다면 결과 화면의 「사이트 열기」로 https://<이름>.<onpod 도메인> 주소를 열어요. 기본 공개 범위는 「우리 회사 사람만」이라 사내 로그인이 필요해요.
이미 올린 사이트는 콘솔 왼쪽 위 「내 배포 사이트」에서 다시 열고 관리해요. 사이트마다 주소·상태(「실행 중」)·작업 이름·공개 범위가 보이고, 줄 끝에 「수정하기」 등의 단추가 있어요.


조직 배포 정책이 승인제(기본)이면 사이트는 올라가지만 관리자가 승인하기 전에는 다른 직원에게 열리지 않아요. 결과 화면에 「관리자 승인 대기 — 아직 아무도 볼 수 없어요」가 표시되고, 승인 요청은 관리자에게 자동으로 가요. 승인되면 다시 올릴 필요 없이 곧바로 열려요(6.3절).

5.7 Git 자동 배포 (push 하면 사이트가 갱신)
저장소의 한 브랜치에 push 할 때마다 사이트를 다시 올리고 싶을 때 써요.
- 「내 배포 사이트」에서 사이트의 Git 아이콘을 눌러 「Git 자동 배포」 창을 열어요.
- 「저장소 연결」 탭에서 연결된 계정(GitLab)을 고르고, 저장소 목록에서 저장소·브랜치를 골라 「연결하기」를 눌러요.
- 저장소에 웹훅이 자동으로 등록돼요. 연결한 다음 push 부터 자동으로 올라가요.

| 배포 정책 | 그 브랜치에 push 하면 |
|---|---|
| 자유 (「모두 허용」) | 약 5초 안에 사이트가 갱신돼요. |
| 승인제 (「관리자 승인」) | 검토용 주소에 먼저 올라가고, 관리자가 승인(커밋 확인)하면 실제 사이트에 반영돼요. 그동안 기존 사이트는 그대로예요. |
- 그 브랜치에 push 할 수 있는 사람은 누구나 이 사이트를 바꿀 수 있어요. 보호 브랜치를 권해요(연결 창 안내).
- 웹훅이 동작하려면 GitLab 이 BizCoder 주소의 인증서를 신뢰해야 해요(ALB 인증서의 발급 CA 를 GitLab 에 등록). 웹훅이 사내 IP 로 가는 경우에는 GitLab 관리자 설정의 「로컬 네트워크 요청 허용」도 켜야 해요.
- 파이프라인에서 테스트를 통과한 뒤에만 올리고 싶다면 「Git 자동 배포」 창의 「CI 배포 토큰」 탭을 써요.
6. 관리자 운영
소유자·관리자 역할은 콘솔에서 조직 관리 화면을 쓸 수 있어요. 왼쪽 메뉴는 아래처럼 구성돼 있고, 이 장은 굵게 표시한 항목을 다뤄요.
| 그룹 | 메뉴 |
|---|---|
| 워크스페이스 | 개요 · 사용량·비용 · 성과 리포트 · 프로젝트 |
| 조직 | 멤버 · 부서 |
| 거버넌스 | 정책(회사 SSO·배포 통제 등) · 배포 사이트 · 예산 관리 · 공용 하네스 · 감사 로그 |
| 결제 | 라이선스 · 조직 설정 |
6.1 멤버와 좌석
조직 관리 → 멤버 에서 멤버별 작업 수·누적 비용·최근 로그인을 보고 역할을 바꿔요. 역할은 세 가지예요.
| 역할 | 권한 |
|---|---|
| 소유자 | 조직 전체 통제 · 예산 한도 · 소유권 이전 (조직당 보통 1명) |
| 관리자 | 멤버·정책·예산·하네스·감사 관리 |
| 멤버 | 코딩 작업 수행 (조직 관리 화면은 안 보여요) |

- 회사 SSO 를 설정했다면 직원은 SSO 로 처음 로그인할 때 자동으로 멤버가 돼요. 별도 초대가 필요 없어요.
- 다른 관리자는 「메일로 초대」 또는 「초대 링크 만들기」로 초대한 뒤 역할을 관리자로 바꿔요. 초대 링크는 하나를 슬랙·메일에 올려 두면 받은 사람이 눌러서 스스로 들어와요. 유효기간·인원·메일 도메인은 링크마다 정하고, 언제든 사용을 중지할 수 있어요.
- 좌석 = 활성 멤버 + 아직 합류하지 않은 초대 수예요. 가득 차면 새 직원의 SSO 로그인이 막혀요.
좌석이 가득 찬 상태에서 새 직원이 SSO 로 로그인하면 로그인 화면에 「라이선스 좌석이 모두 사용 중이거나 라이선스가 유효하지 않아 새로 들어올 수 없어요. 관리자에게 문의해 주세요.」가 뜨고, 주소에 ?sso_error=license_seats 가 붙어요.
좌석을 비우거나 라이선스를 늘려야 해요.

6.2 라이선스 만료와 갱신
- 만료되면 유예 14일이 지난 뒤부터 새 작업만 막혀요(「라이선스 유효기간이 지나서 새 작업을 시작할 수 없어요…」). 기존 작업과 기록은 계속 볼 수 있어요.
- 현재 상태는 조직 관리 → 라이선스(4.3절), 파일 자체는
scripts/preflight.sh --license <파일>로 서명·만료·좌석을 오프라인으로 확인해요.
- 새
.lic파일을 받아 위 명령으로 먼저 점검해요. - Secret
bizcoder-license(키license.json)를 새 파일로 교체해요.kubectl -n bizcoder create secret generic bizcoder-license \ --from-file=license.json=<새 라이선스 파일> --dry-run=client -o yaml | kubectl apply -f - - 반영해요. values 에
license.hotReload: true를 줬다면 재시작 없이 약 1분 안에 반영돼요. 아니면 파드를 다시 띄워요.kubectl -n bizcoder rollout restart deploy/bizcoder - 조직 관리 → 라이선스에서 만료일과 좌석이 바뀌었는지 확인해요.
6.3 배포 정책과 승인
직원이 올린 사이트는 사내 주소로 열려요. 누가 올릴 수 있고 누가 볼 수 있는지는 조직 관리 → 정책 → 배포 통제 의 「배포 허용 범위」에서 정해요.
| 배포 허용 범위 | 동작 |
|---|---|
| 관리자 승인 (승인제, 기본) | 직원이 올린 사이트는 올라가되, 승인 전에는 아무도 못 보게 잠겨요. 관리자가 승인하면 그때 열려요. |
| 모두 허용 (자유) | 직원이 스스로 올리고, 승인 없이 바로 열려요. |
| 금지 | 배포를 허용하지 않아요. |

부서 단위 공개 범위는 온프렘에서 지원하지 않고 「우리 회사」로 처리돼요. 정책과 별개로 관리자는 배포 사이트 탭에서 사이트별로 「범위 변경」을 할 수 있어요.
승인 처리 순서
- 조직 관리 → 배포 사이트 로 가요. 승인 대기가 있으면 「승인 대기 중」 칸이 주황색으로 바뀌고 왼쪽 메뉴에 건수 배지가 떠요. 이 화면에서 우리 회사 직원들이 올린 사이트와 공개 범위, Git 자동 배포(연결된 사이트·일시정지·마지막 배포 실패)도 한눈에 볼 수 있어요.

- 대기 중인 사이트 줄에 「관리자 승인 대기 — 아직 아무도 볼 수 없어요」가 붙고 「공개 승인」·「거절」 단추가 있어요. 주소를 열어 내용을 확인한 뒤 「공개 승인」을 눌러요.

- 공개 범위를 골라 「이 범위로 승인」을 눌러요. 선택지는 「요청대로 — 우리 회사 사람만」과 「주소를 아는 누구나」예요. 직원이 요청한 범위보다 넓히거나 좁혀도 돼요. 승인하면 그 범위로 바로 열려요.

- 승인이 끝나면 사이트에 「우리 회사 사람만」 표시가 붙고 「승인 대기 중」이 0 으로 돌아와요. 직원은 다시 올릴 필요 없이 곧바로 사이트를 열 수 있어요.

Git 자동 배포와 승인제 — 직원이 연결한 브랜치에 push 하면 사이트가 바로 바뀌지 않고 검토용 주소에 먼저 올라가요. 관리자가 승인(커밋 확인)하면 실제 사이트에 반영되고, 그동안 기존 사이트는 그대로예요(5.7절).
6.4 감사 로그
조직 관리 → 감사 로그 에는 작업·정책 변경·보안 플래그가 시간순으로 남아요. 위쪽 탭은 활동 · LLM 호출 로그 · 불변 감사 원장 · 전문 기록 이고, 활동 탭에서는 전체 · 작업 · 크레딧 · 변경 · 플래그로 걸러 볼 수 있어요. 작업 시작·종료, 사이트 배포, MR 생성(외부 전송), 정책 변경이 한 줄씩 기록돼요. 정책 화면 아래의 「정책 변경 이력」에서도 최근 변경을 볼 수 있어요.

- 웹 작업은 진행 기록을 다시 볼 수 있어요. 요청·응답 본문은 「전문 기록」을 켠 뒤부터 쌓이고, 저장된 기록은 암호화돼요. 열람·내려받기도 감사 로그에 남아요(화면 안내).
- 금칙어를 정하면(정책 → 접근·보안) 작업 프롬프트에 포함될 때 감사 로그에 플래그가 남아요.
7. 업그레이드 · 삭제 · Argo CD
7.1 업그레이드
새 번들을 받으면 이미지를 올리고 image.tag 만 바꿔 helm upgrade 해요. 앱은 시작할 때 DB 마이그레이션을 실행하므로 기본값을 replicas: 1, strategy: Recreate 로 두었어요(새·옛 버전이 같은 DB 를 동시에 만지지 않게). 그래서 교체되는 동안 잠시 접속이 안 될 수 있으니 직원이 적은 시간대에 하세요.
- 새 번들의 체크섬을 확인하고 이미지를 올려요(3.2절).
- 샌드박스 이미지가 바뀐 번들이면
scripts/provision.sh를 같은 옵션으로 다시 실행해요. 새 샌드박스 이미지가 만들어지고,onpod.sandbox.image별칭을 새 값으로 갱신해요(기존 Secret 은 유지돼요). my-values.yaml의image.tag를 새 번들bundle.env의APP_TAG로 바꾸고 3.5절의helm upgrade --install을 다시 실행해요.kubectl -n bizcoder rollout status deploy/bizcoder로 끝났는지 보고,preflight.sh(3.6절)를 다시 돌려요.
RollingUpdate 로 바꾸면 새·옛 파드가 잠깐 같이 떠서 마이그레이션이 겹칠 수 있어요.
기존 Secret 의 내용만 바뀌었을 때는 파드가 자동으로 다시 뜨지 않아요. kubectl -n bizcoder rollout restart deploy/bizcoder 를 하거나 values 의 secretsRevision 을 아무 문자열로 바꿔 반영해요.
7.2 삭제
helm uninstall bizcoder -n bizcoder
실수 방지를 위해 번들 DB 의 데이터(PVC data-bizcoder-postgres-0)와 provision.sh 가 만든 Secret 은 남아요. 완전히 지우려면 직접 삭제하세요.
kubectl -n bizcoder delete pvc data-bizcoder-postgres-0
kubectl -n bizcoder delete secret bizcoder-onpod bizcoder-app bizcoder-db bizcoder-azure bizcoder-license bizcoder-ca bizcoder-dev-login
서비스 키(이름 bizcoder-service)는 onpod 콘솔의 API 키 화면에서 회수해요.
7.3 Argo CD 로 배포하기
차트는 Argo CD 로도 배포할 수 있게 설계했어요. 비밀값을 무작위로 만들지 않고, 설치 순서 훅이 없고, lookup 같은 함수를 쓰지 않아서 같은 입력은 항상 같은 결과를 내요(렌더 결과가 같은지는 시험했어요. 다만 실제 Argo CD 로 동기화해 본 시험은 아직 하지 않았으니, 처음 적용할 때는 Argo CD 의 Diff 화면으로 변경 내용을 먼저 확인해 주세요). 번들의 argocd/ 에 application.yaml.example(Application + AppProject), externalsecret.yaml.example, sealedsecret.md 가 있어요.
준비
- 사내 GitLab 에 번들의
helm/bizcoder디렉터리와 환경별 values 를 올려 둬요. 예:https://gitlab.corp.example/platform/bizcoder-deploy.git의helm/bizcoder/(차트)와envs/prod/values.yaml(비밀값은 넣지 않아요). - 비밀은 Git 밖에 둬요.
provision.sh로 한 번 만들거나(Argo 가 관리하지 않는 Secret 이라 prune 대상이 아니에요), ExternalSecret·SealedSecret 으로 선언해요. - Argo CD 가 사내 레지스트리·GitLab 의 사설 인증서를 신뢰해야 해요(저장소 등록 때 CA 지정).
- Application 이름은 릴리스 이름(
bizcoder)과 같게 둬요. 차트가app.kubernetes.io/instance=<릴리스 이름>라벨을 쓰기 때문이에요. 다르게 하려면argocd-cm에application.resourceTrackingMethod: annotation을 설정해요.
Application 예시 (argocd/application.yaml.example 발췌)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: bizcoder
namespace: argocd
spec:
project: bizcoder
source:
repoURL: https://gitlab.corp.example/platform/bizcoder-deploy.git
targetRevision: main
path: helm/bizcoder
helm:
releaseName: bizcoder
valueFiles:
- ../../envs/prod/values.yaml # source.path 기준 상대 경로 (같은 저장소 안)
destination:
server: https://kubernetes.default.svc
namespace: bizcoder
syncPolicy:
automated:
prune: false # 비밀·DB 가 걸린 앱이라 prune 은 끈 채로 시작해요
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
- PruneLast=true
- PrunePropagationPolicy=foreground
# retry 등 나머지는 번들의 application.yaml.example 참고
알아둘 점
- AppProject 는 같은 파일에 함께 있어요. 허용 리소스는 ConfigMap · Secret · Service · ServiceAccount · Deployment · StatefulSet · Ingress · NetworkPolicy · PodDisruptionBudget 이고, 이 차트는 클러스터 범위 리소스를 만들지 않아요. 샌드박스 NetworkPolicy(8.5절)를 쓰면 onpod 샌드박스 네임스페이스에도 리소스가 생기니 AppProject 의
destinations에 그 네임스페이스를 넣어 주세요. - 업그레이드는 Git 의 values 에서
image.tag를 바꿔 커밋하는 것으로 끝나요. 샌드박스 이미지가 바뀐 번들이면 먼저provision.sh를 다시 실행하세요(7.1절). - 기존 Secret 의 내용만 바뀐 경우 파드는 자동으로 다시 뜨지 않아요 →
secretsRevision값을 바꿔 동기화하면 선언적으로 재시작할 수 있어요. - SealedSecret:
provision.sh --emit-yaml ./secrets-plain으로 평문 Secret 매니페스트를 파일로만(권한 600) 만들고,kubeseal로 봉인해*.sealed.yaml만 Git 에 올려요. 평문 폴더는 지워요. 봉인 범위 기본값(strict)은 이름 + 네임스페이스에 묶이니bizcoder-…이름과bizcoder네임스페이스를 바꾸지 마세요. 사내에 Vault 가 있으면 ExternalSecret 이 회전·감사 면에서 나아요.
8. 보안 구성
8.1 컨테이너와 클러스터
- 컨테이너는 비루트(uid 10001)로 실행돼요. 루트 파일시스템은 읽기 전용(
/tmp,/home/app만 쓰기)이고, 모든 capability 를 제거했고, 서비스 어카운트 토큰은 마운트하지 않아요.
8.2 비밀과 신뢰 경계
| 정보 | 어디에 있나요 | 샌드박스에 전달되나요 |
|---|---|---|
| LLM(Azure) 키 | Secret bizcoder-azure → BizCoder 서버 | 아니요 |
| 직원의 GitLab 토큰 | BizCoder 서버에 암호화 저장 | 아니요 |
| onpod 서비스 키 | Secret bizcoder-onpod | 아니요 |
| 소스 파일 | GitLab 저장소 (클론·푸시·MR 은 BizCoder 가 대신해요) | 소스 파일만 전달돼요 |
샌드박스에는 비밀키가 하나도 없고, 클러스터 안에서 BizCoder :8077 만 불러요.
8.3 사내 CA
onpod.caSecret 로 준 CA 는 컨테이너 기본 CA 목록과 합쳐져서(시작 시 initContainer) onpod·GitLab·Azure 호출 모두에 쓰여요. caBundle.mergeSystemRoots: false 로 끄면 사내 CA 만 신뢰해요. 이때는 공인 인증서를 쓰는 엔드포인트(예: Azure OpenAI)에 연결이 안 돼요. GitLab 웹훅(자동 재배포)은 GitLab 이 BizCoder 의 인증서를 신뢰해야 동작해요(ALB 인증서의 발급 CA 를 GitLab 에 등록).
8.4 설치본의 제한과 알아둘 점
- GitHub·구글 로그인, 결제(토스), 이미지·영상 생성처럼 외부 SaaS 에 의존하는 기능은 설치본에서 꺼져 있어요. 로그인은 라이선스와 사내 SSO 로 대신하고, 과금은 계약 방식에 따라요.
- 동적 앱(Node·Python) 배포의 빌드가 npm/PyPI 를 쓰면 사내 미러를
build.npmRegistry·build.pipIndexUrl로 지정해요. 주소에는 사용자 이름·비밀번호를 넣지 마세요(ConfigMap 에 평문으로 남아요). 인증이 필요한 미러는extraEnvFrom의 Secret 으로WRKS_BUILD_NPM_REGISTRY·WRKS_BUILD_PIP_INDEX_URL을 주세요. - 부서 단위 공개 범위는 지원하지 않고 「우리 회사」로 처리돼요.
- 번들 PostgreSQL 은 소규모·PoC 용이에요. 운영에는 사내 DB(
postgres.enabled=false+database.urlSecret)를 권장해요.
8.5 샌드박스 송신 제한 (선택, 기본 꺼짐)
sandbox.networkPolicy.enabled 는 샌드박스 Pod 의 송신을 「BizCoder 8077 + 클러스터 DNS」로만 좁혀요. 다만 onpod 는 앱·빌더·샌드박스 Pod 에 같은 라벨(app.kubernetes.io/managed-by=onpod 등)만 붙여서 이 정책으로 샌드박스만 골라낼 수 없어요. 그래서 샌드박스 전용 네임스페이스(별도 onpod 에이전트)에서만 켜도록 confirmSandboxOnlyNamespace 확인을 요구해요.
sandbox:
namespace: <샌드박스 전용 네임스페이스>
networkPolicy:
enabled: true
confirmSandboxOnlyNamespace: true # 이 네임스페이스에는 샌드박스 Pod 만 뜬다는 걸 확인했어요
dnsCidrs: [] # NodeLocal DNSCache 면 [169.254.20.10/32]
extraEgress: [] # NetworkPolicy egress 규칙 원문 (예: 사내 npm 미러)
preflight.sh --smoke-sandbox --netpol-probe 1.1.1.1:443 으로 실제로 막히는지 확인하세요(CNI 가 NetworkPolicy 를 집행해야 해요).
9. 문제 해결
먼저 scripts/preflight.sh --from-release bizcoder/bizcoder --smoke-sandbox 의 PASS/FAIL 표를 보세요. 대부분의 설치·연동 문제는 FAIL 항목으로 바로 드러나요.
9.1 설치 · 기동
| 증상 | 원인 · 해결 |
|---|---|
helm install 이 BizCoder 차트 값 검증 실패 로 멈춤 | 출력된 항목이 채우지 않은 필수 값이에요. 그대로 채우세요. |
provision.sh: 샌드박스 API 꺼짐(501) | onpod 컨트롤플레인에 ONPOD_SANDBOX_API_ENABLED=1 을 켜세요(onpod 차트 controlplane.extraEnv 후 재시작). 출력된 명령을 따르세요(3.3절). |
provision.sh: 조직에 속해 있지 않음 | 콘솔에서 회사 조직을 만들거나 --create-org "회사 이름" 을 주세요. |
provision.sh: 같은 이름의 서비스 키가 이미 있음 | --org-key-name 으로 다른 이름을 주거나 --rotate-key 로 기존 키를 회수하고 새로 발급하세요. |
파드 CrashLoopBackOff, 로그에 WRKS_SECRET_KEY 오류 | Secret bizcoder-app 의 값이 Fernet 형식이 아니에요. python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())' 로 만든 값이어야 해요. |
파드가 Pending | 번들 DB 의 PVC 가 바인딩되지 않았거나(kubectl get sc) 리소스가 부족해요. |
ImagePullBackOff | image.repository/tag 와 레지스트리 접근(imagePullSecrets)을 확인하세요. |
| ALB 502/504, 대상 그룹이 unhealthy | 헬스체크 경로를 /api/auth/mode 로, 대상 유형(target-type: ip/instance)과 service.type 을 맞추세요. 스트리밍이 끊기면 ALB 유휴 시간 제한을 늘리세요. |
| 파드가 Secret 변경을 모름 | secretsRevision 값을 바꾸거나 kubectl -n bizcoder rollout restart deploy/bizcoder 를 하세요. |
9.2 로그인 · 라이선스
| 증상 | 원인 · 해결 |
|---|---|
| 접속은 되는데 로그인이 안 됨 | /?dev=1 주소로 들어갔는지, auth.devLogin.allowlist 의 이메일과 접근 코드(Secret bizcoder-dev-login)가 맞는지 확인하세요. publicBaseUrl 이 https 인지도 확인하세요. |
SSO 로그인 뒤 ?sso_error=license_seats | 좌석이 가득 찼어요. 좌석을 비우거나 라이선스를 늘리세요(6.1절). |
?sso_error=oidc_domain_mismatch | 로그인한 이메일의 도메인이 SSO 설정의 회사 이메일 도메인과 달라요(4.4절). |
?sso_error=oidc_not_configured | 회사 SSO 가 설정되지 않았어요. 조직 관리 → 정책 → 회사 SSO 를 먼저 설정하세요. |
| 라이선스 오류 | preflight.sh --license 파일 로 서명·만료·좌석을 오프라인으로 확인하세요(6.2절). |
9.3 작업 · GitLab · LLM
| 증상 | 원인 · 해결 |
|---|---|
| 「작업을 마치지 못했어요」가 시작 직후 반복 | preflight.sh --smoke-sandbox 의 「샌드박스 CLI 실행」(CPU 의 AVX2)과 「샌드박스 도구」를 확인하세요. 샌드박스 이미지 별칭, 샌드박스 → BizCoder 도달, LLM 호출을 한 번에 점검해 줘요. |
작업이 오래 대기하거나 no_capacity | onpod 의 샌드박스 슬롯이 부족해요. 용량 산정(2.3절)을 다시 보고, 배포 앱 크기(deploy.appShape)도 확인하세요. |
| 푸시했는데 MR 이 안 열림 | 직원의 GitLab 토큰 권한(api)과 사내 CA 신뢰(onpod.caSecret)를 확인하세요. |
GitLab 푸시 x509: certificate signed by unknown authority | onpod.caSecret 에 GitLab 인증서를 발급한 CA 가 들어 있는지 확인하세요. |
Azure 401 | 키 또는 인증 방식이 맞지 않아요. llm.azure.auth 를 bearer 또는 both 로 바꿔 보세요. |
Azure 404 DeploymentNotFound | 모델 id 와 Azure 배포 이름이 다르면 llm.modelMap 으로 매핑하세요. |
| 「단가를 찾지 못했어요」로 작업이 거부됨 | llm.prices 에 모델 단가를 넣거나 llm.priceOptional: true 를 쓰세요. |
9.4 배포
| 증상 | 원인 · 해결 |
|---|---|
| 배포가 「승인 전에 잠글 수 없어」 로 되돌려짐 | 서비스 키에 org:read·org:write 스코프가 없어요. provision.sh --rotate-key 로 키를 다시 발급하세요(--without-site-access 를 쓰지 않았는지도 확인). |
| 동적 앱(Node·Python) 빌드가 실패함 | 빌드가 npm/PyPI 에 닿지 못하는 경우예요. 사내 미러를 build.npmRegistry·build.pipIndexUrl 로 지정하세요(8.4절). |
| push 해도 자동 배포가 안 됨 | GitLab 이 BizCoder 주소의 인증서를 신뢰하는지, 사내 IP 로의 웹훅이면 GitLab 관리자 설정의 「로컬 네트워크 요청 허용」이 켜져 있는지 확인하세요(5.7절). |
| 사이트가 승인 대기 상태로 잠겨 있음 | 배포 정책이 승인제예요. 관리자가 조직 관리 → 배포 사이트에서 「공개 승인」을 하면 열려요(6.3절). |
부록
A. values 요약표 (주요 항목)
전체 목록과 설명은 helm/bizcoder/values.yaml 의 주석이고, 형식 검사는 values.schema.json 이에요(알 수 없는 키·잘못된 값은 거절돼요).
| 키 | 기본 | 설명 |
|---|---|---|
image.repository / tag / digest | (필수) | 앱 이미지. 사내 레지스트리 주소로 바꾸세요. digest 를 주면 tag 대신 고정해요. |
publicBaseUrl | (필수) | 직원이 접속하는 HTTPS 주소. 끝에 / 없이. |
sandboxBaseUrl | 서비스 DNS | 샌드박스가 BizCoder 를 부르는 클러스터 안 주소 |
replicas / strategy.type | 1 / Recreate | 마이그레이션 때문에 1 권장 |
onpod.apiUrl | (필수) | 사내 onpod API 주소 |
onpod.tokenSecret.{name,key} | – / token | 서비스용 org API 키가 든 Secret |
onpod.caSecret / caConfigMap / caPem | – | 사내 CA (셋 중 하나) |
onpod.sandbox.image / class | (필수) / small | onpod://bizcoder-sandbox:<버전> / nano · micro · small · medium |
onpod.deployDefaultScope | org | 배포물 공개 범위: public · org · dept |
build.npmRegistry / pipIndexUrl | – | 생성된 앱의 빌드가 쓸 사내 미러 |
deploy.enabled / appShape | true / cpu_2c_4g | 배포 기능 스위치 / 배포 앱 기본 크기 |
llm.kind | openai | openai(OpenAI 호환 게이트웨이) 또는 azure |
llm.azure.{endpoint,apiVersion,auth,existingSecret,keyKey} | – / v1 / api-key | Azure OpenAI. apiVersion 이 날짜면 배포 이름 기반 클래식 주소 |
llm.openai.{baseUrl,existingSecret} | – | OpenAI 호환 게이트웨이 |
llm.models.{main,fallback,curated[]} | (main·curated 필수) | 사용자에게 보이는 모델 id |
llm.modelMap | {} | 모델 id → Azure 배포 이름 |
llm.prices / priceOptional / priceMarkup | {} / false / 1.0 | 단가표(KRW/1M 토큰) · 미등록 모델 허용 · 배수 |
llm.sidecallModel | – | 제목·요약 등 보조 호출 모델 |
llm.extraHeaders.{existingSecret,key} | – | APIM 등이 요구하는 추가 헤더(JSON, Secret) |
secretKey.{existingSecret,key} | – / WRKS_SECRET_KEY | Fernet 키 Secret (필수) |
license.{existingSecret,key,hotReload} | – / license.json / false | 라이선스 Secret (필수). hotReload: true 면 재시작 없이 갱신 |
postgres.enabled + postgres.persistence.* | true / 20Gi | 번들 PostgreSQL. 운영은 사내 DB 권장 |
database.urlSecret.{name,key} | – | 외부 DB 접속 문자열(postgres.enabled=false 일 때) |
auth.devLogin.{enabled,allowlist,secretRef} | true | 첫 관리자 로그인(이메일 + 접근 코드) |
auth.superadminEmails | [] | 슈퍼관리자 이메일 |
ingress.{enabled,className,annotations,host,tls} | true | ALB 어노테이션은 annotations 로 |
service.{type,port} | ClusterIP / 8077 | ALB target-type: instance 면 NodePort |
sandbox.namespace / sandbox.networkPolicy.* | onpod-apps / 꺼짐 | 8.5절 참고 |
caBundle.mergeSystemRoots | true | 사내 CA 를 기본 CA 목록과 합칠지 (8.3절) |
pdb.* · resources · nodeSelector · tolerations · affinity · extraEnv · extraEnvFrom · extraVolumes · extraVolumeMounts | 일반 스케줄링 옵션 · 그 밖의 환경변수·볼륨 | |
secretsRevision · secretAnnotations | 기존 Secret 변경 시 롤링 신호 / 차트가 만든 Secret 의 어노테이션 |
B. 자주 쓰는 명령
kubectl -n bizcoder rollout status deploy/bizcoder # 기동·업그레이드 확인
kubectl -n bizcoder logs deploy/bizcoder --tail=50 # 로그
curl -sS https://bizcoder.corp.example/api/auth/mode # 응답 확인 (JSON)
kubectl -n bizcoder get secret bizcoder-dev-login \
-o jsonpath='{.data.code}' | base64 --decode; echo # 첫 관리자 접근 코드
scripts/preflight.sh --from-release bizcoder/bizcoder --smoke-sandbox # 점검 표
scripts/provision.sh --dry-run --onpod-url https://api.onpod.corp.example # 변경 없이 계획만 확인
helm get values bizcoder -n bizcoder # 적용된 values
C. 지원 문의
설치·운영 중 막히면 support@onpod.ai 로 문의해 주세요. 아래를 함께 보내 주시면 빨리 확인할 수 있어요.
scripts/preflight.sh의 PASS/FAIL 표 전체- 증상이 난 시각과 화면(스크린샷), 어떤 단계(설치·로그인·작업·배포)에서 났는지
kubectl -n bizcoder logs deploy/bizcoder --tail=200의 출력과 번들 버전을 확인할 수 있는bundle.env
보내면 안 되는 것 — API 키·GitLab 토큰·로그인 접근 코드·라이선스 파일·Secret 의 값은 메일에 넣지 마세요.

