부산대학교 클라우드 플랫폼(Pickle)의 호스트 설정과 운영 도구를 살펴볼 수 있는 공개 예시예요. 프로비저닝과 배포, 검증 스크립트, 운영 런북을 담고 있어요.
IP 주소와 관리 포트, 내부 호스트명, 볼트 경로는 예시 값이에요. 공개 서비스 도메인은 일부 원래 이름을 표시해요. 실제 실행 전에는 대상 환경에 맞게 검증하고 바꿔 주세요. 치환 범위는 무엇을 바꿨나에서 확인해요.
플랫폼 구성은 PNUops 프로필에서, 애플리케이션 코드는 pickle-api와 pickle-console에서 볼 수 있어요.
개발과 운영의 대상은 root 소유 target 설정에서 선택해요. 이 예시의 주소와 이름은 실제 장비를 가리키지 않아요. 대상별 CTID와 서비스, DB 연결을 확인하는 순서는 개발·운영 배포 예시를 따라요.
캠퍼스 밖 SSH 연결은 외부 릴레이와 캠퍼스에서 연결한 WireGuard 터널을 거쳐요. Proxmox 외의 GPU 노드와 백업 호스트도 각 런북에서 설치와 복구 범위를 확인해요.
플랫폼은 VM 신청과 승인, 생성, SSH와 웹 터미널 접속, 도메인 공개, 만료와 삭제까지 다뤄요. 이 레포지토리는 다음 기능을 맡아요.
- 프로비저닝: 호스트 위의 컨테이너와 사용자 VM 템플릿을 스크립트로 만들어요.
- 배포: 서비스별 실행 파일과 설정을 반영하고 상태를 확인해요.
- 정책 적용: TLS 암호군, 로그 보존, 인그레스 설정 같은 호스트 정책을 한 번에 맞춰요.
- 점검: 실행 중인 시스템에 요청을 보내는 스모크와 읽기 전용 헬스 스냅샷으로 상태를 확인해요.
- 예약 작업: 백업과 헬스체크를 타이머로 돌리고, 실패가 조용히 묻히지 않게 기록과 알림을 붙여요.
- 설정 백업: 파일을 덮어쓰는 스크립트는 변경 전에 타임스탬프 폴더로 복사해요.
- 배포 확인과 복구: API와 콘솔, LLM 게이트웨이는 사전 빌드 artifact를 받아요.
Readiness 실패 시 복구 방식은 대상 설정과 승인된 DB 복구 범위를 따라요.
고정 production unit은
apply를 거부해요. SSH 게이트웨이의 수동 배포는 바이너리 세 개를 한 세트로 교체해요. - 소스에서 빌드하는 배포 스크립트는 체크아웃을 갱신하지 않아요. 빌드는
PICKLE_ROOT아래에 지금 있는 트리로 돌아요. 배포 전에 그 레포의 HEAD를 올리려는 커밋으로 맞추지 않으면 옛 트리가 빌드되고 배포는 그대로 성공하므로, readiness 게이트도 롤백도 이 상황을 잡지 못해요. 둘 다 올라간 것이 건강한지만 보지 무엇이 올라갔는지는 보지 않기 때문이에요. 유닛과 설정을 반영하는 스크립트도 빌드만 안 할 뿐 같은 트리에서 읽어요. - 정제 검사에 셀프테스트가 붙어 있어요.
sanitization-check.sh는 매 실행마다 합성 위반 케이스로 자기 검사 로직이 살아 있는지 먼저 확인한 뒤 본 검사를 수행해요. - 주기 작업의 실패가 남아요.
cron-wrap.sh가 성공과 실패 마커를 기록하고, systemdOnFailure가ops-unit-failed.sh로 알림을 보내요.
hosts/pve-node/ 호스트 네트워크 설정, systemd 유닛과 타이머 // 백업·헬스체크·실패 알림
lightsail/ 외부 릴레이 설정: HAProxy, nftables, WireGuard, sysctl, 커널 모듈
scripts/ 프로비저닝·배포·정책 적용·검증·스모크
runbooks/ 운영 절차 // 이 예시본에는 일부만 포함
호스트별 설정 파일은 hosts/<이름>/ 아래에 둬요. 새 노드의 디렉터리는 첫 설정
산출물이 생길 때 만들고, 편입 자체는 runbooks/node-intake.md(비Proxmox 노드)나
runbooks/proxmox-node-intake.md(기존 플랫폼에 더하는 Proxmox 노드)를 따라요.
| 분류 | 스크립트 |
|---|---|
| 프로비저닝 | create-app-lxc.sh, create-sshgw-lxc.sh, bootstrap-backup-host.sh, create-pbs-vm.sh, install-pbs-guest.sh, bootstrap-isolated-core.sh, bootstrap-isolated-services.sh, bootstrap-candidate-llm.sh |
| 노드 등록 | register-node.py (실측, 기본 dry-run, 신규 MAINTENANCE, 기존 IP pool 연결과 물리 예약과 CPU 공유 용량 기록) |
| CPU 경계 | cpu-isolation.py (whole-SMT 후보와 native 조회, 설치와 적용은 기본 차단과 별도 승인 창) |
| 배포 | deploy-environment.py(환경과 artifact 검사 예시), pickle-api-start.py(열린 JAR descriptor를 유지하는 시작 wrapper), verify-production-backup.py(독립 PBS 복원 검사), deploy-api.sh, deploy-console.sh(명시 환경과 artifact 진입점), deploy-proxy-agent.sh, deploy-relay.sh, deploy-sshgw.sh, sync-systemd-units.sh, apply-gpu-node-vllm.sh |
| 정책 적용 | apply-tls-ciphers.sh, apply-terminal-ingress.sh, apply-log-retention.sh, apply-main-domain-vhost.sh, apply-ops-timers.sh, apply-platform-inventory.sh, apply-settings.sh, apply-terms.sh, apply-os-catalog.sh, register-image.py, apply-relay-token.sh, apply-production-sdn.sh, apply-production-network.sh |
| 운영 | db-backup.sh, db-pbs-backup.sh(host runner의 DB LXC dump와 독립 복구점 감시), candidate-core-vzdump-hook.sh, core-pbs-monitor.py, health-check.sh, cron-wrap.sh, ops-unit-failed.sh, enroll-backup-peer.sh, configure-qnetd.sh, check-backup-host.py, check-backup-storage.py, activate-qdevice.py, pbs-capacity-probe.py, pbs-capacity-monitor.py, discord-ops-alerts.py |
| 검증 | verify.sh, sanitization-check.sh, hook-verify.sh, verify-production-network.py |
| 스모크 | smoke-provisioning.sh, smoke-llm-key-lifecycle.sh, smoke-http-publish.sh, smoke-ssh-gateway.sh, smoke-web-terminal.sh, smoke-account-ops.sh, smoke-dashboards-notify.sh, smoke-prod.sh, smoke-signup.sh |
deploy-api.sh, deploy-console.sh, deploy-llm-gateway.sh는
<check|apply> <dev|prod> <commit> <artifact> <sha256>을 받아 환경 배포 도구에 전달해요.
기본 호출과 CTID와 hostname, 체크아웃 override는 빌드나 대상 조회 전에 거부해요.
Target 설정은 보존 source CT를 제외하며 artifact는 root 보호 staging 경로에서 선택해요.
빌드와 Vite flag 선택은 별도 build job에서 수행해요.
Dev 배포는 root 보호 manifest의 approved_artifacts[target][source_commit] digest pin을 요구해요. CI에서 같은 commit을 다시 빌드해 digest가 달라지면 검증한 실제 artifact의 수동 pin 갱신이 필요하며, 검토된 gateway archive pin이 없으면 배포를 거부해요.
개발 CI는 새 전용 key와 frozen forced-command receiver로 artifact만 전달해요. 운영 key는 별도 production job이 사용해요. Receiver 설치와 secret 등록은 별도 검토 후 준비해요.
새 개발·운영 배포 예시는 root 소유 대상 설정과
40자리 commit, artifact SHA-256을 검사해요. 이 공개 레포지토리의
config/deploy-target-{dev,prod}.json은 예약 주소와 예시 이름을 사용하므로 실제
운영 파일로 설치하지 않아요. 비공개 운영 workflow는 개발 배포와 운영 artifact
stage/check를 분리하며 이 예시 레포지토리에는 실행 workflow를 싣지 않아요.
운영 apply는 artifact와 복구 범위에 결합된 root 보호 승인 창 및 실제 PBS 독립
파일 복원과 TOC, Flyway 대조를 요구해요. 현재 고정 production unit은 apply를 거부해요.
개발과 운영 API의 명시 rollback 정책, 실행 프로세스가 연 artifact의 inode와 SHA,
개발 effective environment와 loopback authority와 Chat-free 정상 키를 포함한 독립
호환 검증 결과를 검사해요. Console archive는 root 소유이며 directory 0755와 file 0644로
정규화해요. 초기 mock과 별도 실연동 개발 manifest, 독립 reader 설정의 예시는
config/deploy-target-dev-real.json과 config/deploy-backup-reader.json에 있어요.
자리표시자는 실제 검증한 배치 값으로 교체하며 기존 운영 helper는 수정하지 않아요.
운영 DB 사본으로 시험하는 개발 환경은 보호 manifest의
PICKLE_DEV_DATA_MODE=production-copy를 사용해요. 같은 개발 CT와 DB 배치에서
API의 isolated profile을 선택하며, 복제한 계정과 비밀번호, MFA, 기관 권한, 이력을
유지해요. 관리자 MFA는 계속 요구해요. JobRunr와 외부 polling, 메일, DNS 쓰기,
VM 제어를 중단하고 guest outbound를 개발 DB로 제한해요. Gateway 배포도 거부해요.
Google 계정 연결 기록은 보존하지만 개발용 Google OAuth는 별도 구성 전까지 사용할
수 없어요. 기존 합성 DB는 별도 이름으로 보존하고 복원 절차에서 다시 선택할 수 있어요.
이 환경 배포 예시는 개발 CT901과 DB902, DB 주소 198.18.0.60과
example-dev-host를 사용해요. Source commit과 artifact SHA는 EXAMPLE_*
자리표시자이며 실제 배치 전에 검증한 값으로 교체해요.
create-pbs-vm.sh는 명시한 UEFI loader와 vars template을 host capability 및 firmware
descriptor와 대조해요. cloud-init seed는 read-only virtio block으로 연결해요.
apply-settings.sh는 누락된 런타임 설정 키를 추가하고 기존 값은 유지해요. GPU 미연결
검토 시간과 저사용 판단 기간, 유지 결정 후 재검토 유예의 초기값은 12시간이에요.
관리자 콘솔의 플랫폼 설정에서 변경하면 이후 판정이 변경값을 읽어요. GPU 임대 기간은
신청과 승인에서 직접 입력해요.
PBS 준비 도구는 Ubuntu 22.04 amd64의 libvirt와 네트워크를 유지하며 Debian 13 전용 VM을
만들어요. 새 boot 64 GiB/data 1 TiB와 4 vCPU/8 GiB를 사용하고, NetBird peer와 host qnetd를
별도로 구성해요. 기본 실행은 사전 검사이며 --apply를 지정해야 변경해요.
검증한 cloud image SHA256과 운영자 SSH 공개키, 실제 hostname과 보호 backup 경로가 필요해요.
설치 기준은 PBS 4.2.5-1, NetBird 0.78.2, qnetd 3.0.1-1이에요.
절차와 복구 범위는 백업 호스트 런북에 있어요.
첫 root 실행은 bootstrap-backup-host.sh --expected-host <hostname> --check예요.
check-backup-storage.py는 hash로 고정한 portable SMART/PERC 도구를 읽기 진단에 사용해요. 패키지나 서비스를 설치하지 않아요.
기존 도구로 disk/RAID 상태만 수집하며, 확인하지 못한 RAID 상태를 정상으로 표시하지 않아요. 인증서 등록을 마친 PVE 두 노드에서는 qdevice 활성화 런북에 따라 설정 잠금과 TLS 및 정족수 확인을 진행해요.
운영 SDN 도구는 hosts/production/network.json을 입력으로 사용해요. 예시 운영망은
100.65.0.0/16과 100.66.0.0/16이며 기존 개발망 198.18.0.0/16과 198.19.0.0/16에
mesh 100.64.0.* 예시를 구분해요. 실제 배포 전에는 이 예약 주소를 해당 환경에 맞게 교체해요.
PVE와 NetBird의 기존 firewall을 유지하며, guest 정책을 우회하는 mark와 VLAN frame을
별도 guard로 처리해요.
선택 설정 interim_ingress는 edge의 확인된 IPv4에서 활성 gateway owner의 campus
TCP 24080/24443으로 들어온 연결만 pinfra proxy의 같은 포트로 전달해요. Standby는
해당 포트를 열지 않으며 proxy guest firewall은 별도로 설정해야 해요.
선택 설정 public_ssh_transit는 확인한 edge 출발지 IPv4에서 활성 gateway owner의
campus TCP 2224로 들어온 연결만 pinfra SSH gateway의 같은 포트로 전달해요.
Standby는 원래 campus 목적지의 TCP 2224를 차단하고, 전달은 활성 owner만
담당해요. 설정하지 않으면 해당 규칙을 만들지 않으며 SSH gateway와 공개
relay 설정은 별도로 검증해야 해요.
render-ssh-transit.py는 Python 3에서 실행하며 private SSH 중계 unit과 선택적인
source 방화벽 파일을 새 디렉터리에 생성해요. 입력과 적용 전 검증, 영속화와 원복
순서는 SSH 중계 런북에 있어요. 파일 설치나 서비스 기동은
별도로 승인된 절차에서 수행해요.
선택 설정 relay_transit는 릴레이의 고정 IPv4에서 활성 gateway owner로 들어온
새 TCP, UDP 연결만 guest 망으로 전달하고, API의 릴레이 동기화 포트 8080을 별도로
허용해요. Guest 포트 22는 차단하며, guest 연결은 RETURN으로 PVE VM 방화벽의
최종 판단을 받아요. Standby에는 이 신규 연결 예외를 만들지 않아요.
기본 실행은 사전 검사예요. 생성된 SDN 파일을 수동으로 편집하지 않는 적용과 부팅, rollback
절차는 운영 네트워크 런북에 있어요.
격리 core 도구는 Debian 13 템플릿으로 PostgreSQL 18과 Java 25 실행 환경을 준비해요.
새 게스트의 eth0 MTU는 소유권 확인 직후 /etc/network/if-pre-up.d/isolated-core-mtu
hook으로 지속 적용하고, APT 전에 ip -j link readback으로 검증해요.
PVE는 net0의 mtu=를 기동 때 만드는 veth에만 걸고 guest의 interfaces 파일에는 쓰지 않아,
hook이 없으면 guest의 networking이 다시 올라올 때 1500으로 돌아가요. 그래서 격리 core,
후보 서비스 core, 후보 LLM 게이트웨이 세 생성 경로가 isolated_core.install_guest_mtu
하나로 hook 설치와 적용, readback을 수행하고, hook 파일 이름만 경로마다 달라요.
대상 노드와 CTID, private 주소, 검증한 패키지 버전 및 새 자격증명 파일을 명시해요.
API는 isolated profile, 시작을 막는 marker와 비활성 job/정책/vendor 설정을 적용한 상태로
남아요. 별도 --bootstrap-admin one-shot은 host/CT/machine/DB identity와 빈 DB를 확인한
뒤 SYS_ADMIN과 PERSONAL workspace만 만들며, postcheck 뒤 marker를 만들고 서비스를
disabled 상태로 둬요. 카탈로그, 기존 데이터 이관과 공개 진입은 후속 작업이에요. 실제 설치와 전체 서비스 복구 검증은
격리 core 런북의 순서와 소유권 확인을 따라요.
후보 LLM 게이트웨이는 examples/candidate-llm.json에 대상 노드와 새 CTID, proxy 주소,
proxy CT 설정 파일, Debian 템플릿 및 바이너리와 유닛 경로와 SHA-256을 명시해요. --config만 주면 계획을
출력하고 --apply를 추가해야 생성해요. 적용 전에 게스트 소유권과 빈 CTID와 volume,
클러스터의 활성 PVE 작업과 HA 리소스 부재, proxy가 실행 중인지와 단일 net0, 네트워크 및 저장소 여유를 확인해요. 첫 부팅
직후 SSH와 Postfix service/path를 중지하고 mask하며, APT 전에 eth0 MTU를
/etc/network/if-pre-up.d/candidate-llm-mtu hook으로 지속 적용해요. APT 전후와 완료 시
unit 상태, hook과 실제 MTU, 설정한 DNS를 확인하고, TCP listener가 없음을 확인해요. 게스트 방화벽은 APT 설치 뒤에 켜지므로 그 사이에는 기존
격리망 경계에 의존해요. 실패하면 소유권을 다시 확인해 제한 시간 안에 CT 정지를 시도하고
결과를 기록해요. 게스트와 volume은 보존해요. 완료해도
onboot=0과 LLM 서비스 disabled/inactive 상태로 남아요. 자격증명 복원, 활성화,
트래픽 전환과 롤백은 별도 검토 단계예요. 입력과 확인 순서는
후보 LLM 게이트웨이 런북에 있어요.
DB/PBS 도구는 Python 3와 proxmox-backup-client, source의 PostgreSQL 18 client를
사용해요. 기본 실행은 계획 출력이며 실제 백업은 --run으로 시작해요. DB dump를
암호화하여 업로드하고 실제 복원한 파일의 hash를 대조한 뒤 별도 검증 receipt를 게시해요.
다른 호스트의 monitor는 현재 PBS 증거로 10분 경고와 15분 실패를 판단해요. timer와
메일은 별도 설치 및 활성화가 필요해요. 키 보관, 보존 정책과 전체 서비스 복원 시험은
DB/PBS 런북에 있어요. 사용자 VM 정기 백업은 이 도구의 대상이 아니에요.
production-health.py는 분리 운영 container의 서비스, DB, 정기 작업과 인증서를
선택 config로 점검해요. 정기 점검과 와일드카드 갱신 소유, expiry row 인계 절차는
운영 점검 런북에 있어요.
discord-ops-alerts.py는 호스트 감시와 generation collector의 확정 사건과 관측 장애를
Discord 전용 대기열로 전달해요. 원본 상태와 요약의 시각을 대조하며 보호 설정,
전송 상태와 설치 절차는 운영 알림 런북에 있어요.
candidate-core-vzdump-hook.sh는 예시 후보 노드 pve-node-2의 CT 1200/1201/1202/1204를
PBS로 백업하기 전 노드와 스토리지, 정족수, 설정, 실행 상태를 확인해요. 등록 절차는
후보 코어 백업 런북에 있어요.
core-pbs-monitor.py는 pve-node-3의 읽기 전용 PBS storage에서 다섯 CT의 일일 복구점과
기존 네 보호 baseline을 확인해요. 최신 관측과 복구점 나이, 최초 사건 기록을 분리하고
기존 장애와 복구 메일을 유지해요. 설치 절차는
독립 감시 런북에 있어요. CTID, 설정 해시, 암호화
fingerprint와 날짜는 예시 값이며 실제 환경에 맞게 검증해 채워야 해요.
backup-health-summary.py는 기존 core 관측과 DB 정상 캐시, native 실행을 읽어 현재 판정과
마지막 정상 증거를 분리해요. 추가 백업이나 알림 없이 로컬 요약을
게시하며 기존 감시와 SMTP state를 변경하지 않아요.
pbs-capacity-probe.py는 인증서 pin을 확인하고 읽기 전용 token으로 datastore 용량만
조회해요. pbs-capacity-monitor.py는 pve-node-3에서 5분마다 상태 변화를 확인하고
알림 receipt를 기록해요. token ACL, 보호 설정, 설치와 장애 대응은
PBS 용량 감시 런북에 있어요.
합성 계정 로그인 공개 시험 예시는 CT1200의 health-only TLS vhost 한 파일을
일시적으로 교체해요. scripts/apply-candidate-synthetic-login.py는 예시 설정
SHA와 nginx 문법, 응답을 검사하고, recover는 health-only 상태를 복원해요.
적용 조건과 허용 경로는 합성 로그인 진입 런북에
있어요. 예시 해시와 주소는 실제 장비에 그대로 사용할 수 없어요.
스모크는 실행 중인 시스템에 실제 요청을 보내요. smoke-provisioning.sh는
DB에 직접 만든 인증된 사용자로 워크스페이스 생성, VM 신청, 관리자 승인, 프로비저닝 완료 대기,
SSH 도달 확인, 전원 왕복, 삭제, DB 정합 검증까지 한 번에 확인해요.
smoke-http-publish.sh, smoke-dashboards-notify.sh, smoke-ssh-gateway.sh,
smoke-account-ops.sh, smoke-web-terminal.sh도 사용자를 DB에 직접 만들어요. 인증 메일이
실제 메일함으로 가고 api는 토큰의 해시만 저장하므로, 가입 경로로 만들면 토큰을 읽을 방법이
없기 때문이에요. 로그인 제한 카운터는 그 사용자와 이 호스트 주소의 행만 지워요.
smoke-account-ops.sh의 workspace 삭제는 해당 대상의 termination-impact를 먼저 읽고
현재 fingerprint와 confirmed=true를 JSON 본문으로 전달해요. 영향 조회 실패나
fingerprint 누락 시 삭제 요청을 보내지 않아요. 이 확인 형식을 지원하는 API와 함께
실행해요.
회원가입과 메일 인증 경로는 smoke-signup.sh 하나가 확인해요. 실행마다 새 주소
signup-<epoch>-<난수>@example.com으로 POST /auth/signup을 보내고, 운영 메일함에 도착한
인증 메일을 IMAP(imap.example.com:993, SSL)으로 읽어 링크의 토큰을 꺼내요. 그 토큰으로
인증한 뒤 같은 토큰의 재사용이 410으로 거절되는지, 로그인한 계정이 ACTIVE이고 약관 동의와
개인 워크스페이스를 갖는지 확인해요. 202 응답은 이미 있는 주소에도 똑같이 오므로 계정은
DB에서 확인해요. 메일함 앱 비밀번호는 볼트 파일 smoke-mailbox/mailbox-imap.txt에서
읽고(VAULT로 볼트 위치, PICKLE_SMOKE_IMAP_PASSWORD_FILE로 파일 경로 변경), 파일이 없거나
비었거나 로그인이 거절되면 계정을 만들기 전에 실패해요. 메일함은 읽기 전용으로 열고
BODY.PEEK로 가져오므로 읽음 표시도 바뀌지 않고, 이 실행의 주소로 가입 요청 뒤에 도착한
메일만 봐요. 실행 중 나가는 메일은 그 주소로 가는 인증 메일 한 통이고, 가입과 인증은
관리자를 포함해 다른 누구에게도 알리지 않아요.
가입 주소의 도메인은 PICKLE_SMOKE_SIGNUP_DOMAIN(기본 example.com)으로 정해요. 직접
소유하고 가입 API가 허용하며, 메일이 스모크 메일함으로 라우팅되는 도메인이어야 해요. 이 스모크는 DB에 계정을
만드는 스모크와 달리 로그인 제한 카운터를 지우지 않으므로, 다른 스모크 직후에 돌리면 1분 창이
지날 때까지 429를 받을 수 있어요.
스모크가 만든 계정은 끝날 때(실패 경로 포함, VM 정리 뒤) 지우거나 비활성화해요. 이 샘플의
주소는 소문자 예시 도메인(@example.com)이고, 실제 환경에서는 메일이 반송되지 않고 운영자가
받는 도메인으로 바꿔 써요. 스모크의 공지는 자기 워크스페이스 구성원에게만 가지만, 알림과 메일은
플랫폼의 시스템 관리자와 시드 테스트 기관(이름 「테스트 기관」으로 찾아요)의 관리자에게도 가요.
실제 사람에게 그 역할을 주면 그 사람도 알림과 메일을 받아요. notifications 행의 SENT는 메일 서버가 발송 요청을 받았다는 뜻이에요.
메일함 도착 여부는 별도로 확인해요. smoke-prod.sh는 읽기 전용이고
인자를 받지 않아요. VM 생성부터 삭제까지는 smoke-provisioning.sh가 확인해요.
smoke-llm-key-lifecycle.sh는 LLM gateway까지 배포한 뒤 평문 키를 출력하지 않고 실제
1-token 호출과 snapshot 기반 정지와 재개, 폐기 반영을 확인해요. 이 일반 lifecycle은
OpenRouter 사업 account가 없어도 실행할 수 있도록 금액 한도를 0으로 둔 TOKEN 축 smoke예요.
호출 모델은 기본 pickle-general이며 다른 TOKEN 모델을 검증할 때만 LLM_SMOKE_MODEL로
바꿔요. 지정한 모델이 gateway /models에 없으면 호출 전에 실패해요.
양수 CREDIT 최초 binding과 cross-org account isolation은 실제 account 준비와 binding ON 뒤 별도
smoke를 구현해 검증해야 하며 현재 이 script의 coverage가 아니에요.
사용자 VM 템플릿을 만드는 빌드 레시피는 이 레포지토리에 없어요. 공개 레포지토리 pickle-image-builder가 그 역할을 맡아요.
node-registration.md는 Proxmox 노드 단독 등록과 core와 복구 용량 예약,
활성화 전 검증을 설명해요.
노드 등록 설정의 cpu_allocation_ratio와 committed_vcpu는 함께 지정해요.
두 필드를 생략하거나 1과 0으로 지정하면 기존 schema 1 예약 계산을 유지해요.
공유 정책은 schema 2의 cpu_policy에 기록하며 CPU 배치 가능량을
(물리 thread - 보호할 물리 예약 thread) × 비율 - 별도 commit된 vCPU로 계산해요.
비율은 정수 1 또는 2이고 이미 플랫폼 DB의 VM으로 계산되는 vCPU를
committed_vcpu에 다시 넣지 않아요. RAM과 디스크 예약은 공유 비율과 무관해요.
examples/node-registration-cpu-sharing.json은 공유 정책의 입력 형상을 보여 줘요.
schema 2를 읽는 API 배치 consumer를 먼저 배포하고 검증한 뒤 노드를 활성화해 주세요.
플랫폼 CPU를 학생 영역과 분리할 때는 host 2물리 core/4thread와 플랫폼 4물리
core/8thread를 합친 reserve_cpu_threads=12를 먼저 차감하고 학생 영역에만 2:1을
적용해요. 물리 24/32thread에서 별도 보존 VM 5/3vCPU를 차감한 예산은 19/37vCPU예요.
플랫폼 CT의 vCPU를 별도 commit에 다시 넣지 않아요. label 계산은 kernel 격리를
대신하지 않아요. scripts/cpu-isolation.py의 whole-SMT 후보, 정상 exclusive partition,
CT mask와 QEMU hook의 영속화와 실패 경계는 CPU 격리 런북에
있어요. 이 도구는 host IP와 부팅 인자, IRQ 정책을 바꾸지 않으며 I/O와 latency 검증은 별도예요.
학생 VM이 모두 중지된 뒤에도 child cpuset controller를 유지하는
examples/pickle-student-cpu-controller.service.template은 같은 학생 mask의 idle process를
qemu.slice에 둬요. 템플릿의 보호 배치와 별도 소유 기록, 부팅 순서와 원복 조건은
CPU 격리 런북에 있어요.
이 예시본에는 proxmox-api-principal.md(Proxmox API 서비스 principal 권한 분리와 token custody), new-environment.md(신규 환경 관통 구축 순서: 환경별로 바꿀 값 표와
사람만 할 수 있는 단계와 절차가 없는 지점 명시), node-intake.md(비Proxmox 노드 편입 절차:
실측 체크리스트, 운영자 접속 키 설치, 대역외 관리 평면 점검), drift-resolution.md(DB와
하이퍼바이저 상태가 어긋났을 때의 판정 절차), db-restore.md(백업 복원),
inventory-readiness.md(IP pool 한 건 등록과 MAINTENANCE 노드의 VM 방화벽 opt-in 준비),
template-replication.md(중지 VM template의 검증된 VMA archive 복제와 실패 정리),
isolated-core-bootstrap.md(새 API/콘솔과 별도 DB LXC의 private TLS 연결 및 기동 제한),
isolated-service-core.md(새 후보 proxy/SSH gateway LXC의 방화벽 우선 준비와 서비스 정지 인계),
candidate-llm-gateway.md(새 후보 LLM 게이트웨이 LXC의 닫힌 상태 준비와 활성화 경계),
pbs-egress.md(DB LXC의 PBS TCP 8007 egress와 state-guarded 재적용),
db-pbs-backup.md(플랫폼 DB의 암호화 PBS 백업, 독립 복구점 감시 및 같은 서비스의 수동 복원),
gpu-node-vllm.md(GPU 노드 vLLM 서빙 운영: 시작과 종료, 모델과 플래그 교체와 롤백, 장애
복구, 재부팅), proxmox-node-intake.md(Proxmox 노드 후보 인수 절차 초안: 초기화 전
실측, 설치 전 결정 항목, standalone 설치, 등록 전에 실행하면 안 되는 스크립트),
gpu-passthrough.md(GPU 패스스루: Resource Mapping 생성, 토큰 최소 권한, VM 모양(rombar=0),
붙이기와 떼기, 인계, 카드 상태 확인과 손실 대응, 시험 게스트 정리)를 담고 있어요. 나머지 재구축과 복구 절차는 비공개 레포지토리에 둬요.
| 문서 | 내용 |
|---|---|
llm-health-monitor.md |
자체 서빙 실제 생성과 host/query 분리 감시, 무통지 초기 배치와 원복 |
llm-health-external.md |
외부 HTTPS/TLS 감시, 전용 진단 키 검증과 제한된 SSH 결과 수집 |
VM 공개 이름은 개별 A 레코드로 ingress를 가리켜요. 와일드카드 인증서는 DNS-01로
별도 갱신해요. health-check.sh는 apex, 등록된 VM 이름, 수동 ingress 이름과 정확한
NS 위임을 확인하고 미등록 이름에는 NXDOMAIN을 요구해요.
/etc/pickle/host.env의 PLATFORM_ROOT_DOMAIN과 PLATFORM_DNS_EXPECTED_NS로
검사를 설정해요. PLATFORM_DNS_MODE 기본값은 explicit이며 전환과 롤백 중에는
wildcard를 사용해요. DB 외부에서 관리하는 이름은 PLATFORM_DNS_MANUAL_FQDNS에
기록해요. 설정과 복구 순서는 플랫폼 DNS 런북에 있어요.
scripts/verify.sh # shellcheck, 설정 초기화, core, DB 백업, SDN, qdevice와 인벤토리 등록 테스트, 정제와 스케줄 유닛 검사verify.sh는 커밋 전 필수예요. shellcheck 위반이 하나라도 있으면 실패하고, 이어서 도는
정제 검사는 이 샘플에 실제 주소나 실제 값이 섞이지 않았는지 확인해요.
기본 검증은 설정 초기화, disk 진단 수집기, 격리 core, DB/PBS 백업, 운영 SDN, qdevice 활성화와 노드 등록의
테스트 묶음을 실행해요. 호스트 설정이나 운영 DB를 변경하지 않아요. 노드 등록의
실제 SQL 검증 두 건은 별도 선택 사항이며, 로컬 Docker에 있는 PostgreSQL image digest를
PICKLE_TEST_POSTGRES_IMAGE에 지정하고 python3 -B scripts/tests/test_node_registration.py로
실행해요. 이 시험은 네트워크가 차단된 임시 컨테이너를 만들고 종료 후 정리해요.
오프라인 추론 metric 파서 scripts/lib/inference_metrics.py는 Prometheus 텍스트와 관측 시각,
source와 boot, process, configuration identity를 입력으로 받아요. 모델과 엔진을 구분한 누적
counter와 histogram을 보존하고 실제 관측 간격으로 차분해요. 미관측과 미지원, 수집 실패는
0과 구분하며 identity 변경이나 긴 공백은 새 baseline으로 처리해요. snapshot 파싱의
입력과 출력은 각각 512 KiB, 모델과 엔진 그룹은 32개, 선택 sample은 2,048개,
histogram 경계는 128개로 제한해요.
이는 파서의 기술 제한이며 장비 용량이나 수집과 보관 정책을 뜻하지 않아요. 합성 입력을
검증하는 순수 모듈로 네트워크 수집, 저장 또는 호스트 설치를 실행하지 않아요.
verify.sh에 포함된 회귀는 아래 명령으로 따로 실행할 수 있어요.
PYTHONDONTWRITEBYTECODE=1 python3 -B scripts/tests/test_inference_metrics.py전용 관측 프로그램 scripts/inference-observation.py는 기존 health/incident 경로와 별도
파일을 사용해요. 모델 호스트에서 정규화한 최신 표본을 고정 SSH 명령으로 읽고, 중계
호스트의 bounded outbox가 같은 HTTP body bytes를 API에 재전송해요. source/epoch/sequence와
body SHA가 맞는 ACK 뒤에만 checkpoint를 먼저 보존하고 원문을 제거해요. 누락과 미지원,
실제 0, CPU 1분 load average와 GPU 사용률, producer 시각과 전달 시각을 구분해요.
코드와 systemd 예시의 설치와 활성화, 자격증명 준비는 별도예요. 설정과 실패/만료 시
원문 보존, 운영 승인 경계는 전용 관측 런북에 있어요.
전용 outbox와 전달 fixture는 운영 호스트에 연결하지 않아요.
python3 -B scripts/tests/test_observation_spool.py
python3 -B scripts/tests/test_inference_observations.py
python3 -B scripts/tests/test_observation_io.py설정 초기화 테스트는 Python 3와 jq를 사용해요. 호스트에 연결하지 않고 로컬 SQL 픽스처로 스크립트를 두 번 실행해 기존 설정값과 수정 시각이 유지되는지 검사해요. PostgreSQL의 타입 검사와 실제 컨테이너 연결은 이 테스트의 범위에 포함하지 않아요. 격리 core 테스트는 Python 3 표준 라이브러리로 입력 검증과 기존 자원 보호, TLS 및 서비스 기동 조건을 확인해요. 후보 서비스 core 테스트는 candidate token 분리, 닫힌 포트, onboot 0, artifact checksum readback, package endpoint 사전 검사와 guest transient unit의 process-group timeout, firewall/network/service 순서를 확인해요. 둘 다 실제 PVE 호스트에 접속하거나 컨테이너를 만들지 않아요. DB 백업 테스트는 PBS 응답을 대신하는 메모리 객체로 snapshot 누락, 복원 대조 실패, 조회 지연과 알림 재시도를 확인해요. 실제 DB/PBS/SMTP 접속과 서비스 복구는 별도 시험이에요.
서비스 레포지토리(api, console, llm-gateway, sshgw, proxy-agent, relay-agent)는 develop
에서 개발을 통합하고 main 을 운영 배포 기준으로 둬요. 인프라 레포지토리는 main 하나만
사용해요. pve-node 의 예약 작업이 체크아웃의 main 을 그대로 실행하고, 격리 코어
부트스트랩은 브랜치 대신 검토한 고정 커밋(INFRA_REVISION)을 받아요.
develop의 변경이 요구하는 인프라 변경은 하위 호환으로 만들어main에 먼저 머지해요. env 키는 스켈레톤에 추가만 하고 릴리스 전까지 스크립트가 그 키를 요구하지 않아요. 새 경로는 있는지 확인한 뒤 사용해요. psql 로 값을 쓰는 스크립트는main과develop두 스키마 모두와 맞아야 해요.- 하위 호환으로 만들 수 없는 변경은 토픽 브랜치에 두었다가 서비스 레포지토리의
릴리스(
develop→main머지)와 같은 배포 창에서 머지해요. - 릴레이는
main에서만 배포해요. 릴레이는 하나뿐이고 운영과 함께 써요. - 템플릿은 image-builder
main의 레시피로 빌드한 것만 다른 노드로 복제해요. 확인 절차는runbooks/template-replication.md의 「시작 조건」에 있어요.
아래 표는 공개 예시에 사용하는 값이에요.
합성 로그인 시험 도메인은 staging.example.com으로 치환해요. 플랫폼 DNS 런북의
도메인과 NS 설정은 아래 표의 예시 이름을 사용해요. 그 밖의 기존 서비스 도메인은
플랫폼이 사용하는 이름을 유지해요.
| 항목 | 이 레포지토리의 값 |
|---|---|
| 호스트 LAN 주소와 게이트웨이 | 192.0.2.10/24, 192.0.2.1 |
| 리버스 프록시 공인 IP | 203.0.113.10 |
| 플랫폼 DNS 런북의 루트와 수동 ingress | example.dev, staging.example.dev |
| 플랫폼 DNS 런북의 NS 집합 | ns1.example.test부터 ns4.example.test까지 |
| 패스스루 대상 IP | 203.0.113.20 |
| 외부 릴레이 공인 IP | 198.51.100.10 |
| 관리 SSH 포트 | 22 |
| 시크릿 볼트 경로 | $VAULT/ |
| 플랫폼 브리지 대역 | 198.18.0.0/16 |
| 게스트 대역 | 198.19.0.0/16 |
개발용 임시 NAT 브리지 대역(시험 게스트, 런북 gpu-passthrough.md; 프록시와 패스스루 공인 IP와 겹치지 않게 선택) |
203.0.113.224/27 |
| 릴레이 터널 대역 | 100.64.0.0/30 |
| 호스트 이름 | pve-node, gpu-node, dept-node, pve-node-2, pve-node-3 |
| 비Proxmox 노드 주소와 접속명 | 192.0.2.20, dept-node.example.ac.kr |
| 합성 로그인 시험 허용 출발지와 앱 주소 | 203.0.113.14, 203.0.113.182, 198.18.1.20 |
| Proxmox 노드 후보 주소 | 192.0.2.30, 192.0.2.31 |
| 코어 PBS 예시 CTID | 1200, 1201, 1202, 1203, 1204 |
| 코어 PBS 예시 storage와 작업 이름 | pbs-example-core-write, pbs-example-core-read, example-core-daily |
| 코어 PBS 예시 설정 해시와 암호화 fingerprint | 동일 숫자 64자리의 합성 SHA-256 문자열과 aa 32쌍 |
| 코어 PBS 예시 복구점과 시행 날짜 | 2025-01-01 수동 복구점, 2025-01-02 첫 일일 백업 시행 |
| 코어 PBS 예시 메일 설정 경로 | /etc/pickle-example/mail.json |
| 비Proxmox 노드 하드웨어 모델 | 아키텍처만 남기고 제조사와 모델명 생략 |
| 기관 이름 | 예시 기관 |
192.0.2.0/24와 198.51.100.0/24, 203.0.113.0/24는 RFC 5737이 문서화 용도로 예약한
대역이라 실제 인터넷에 존재하지 않아요. 관리 SSH 포트도 한눈에 자리표시자로 보이도록
표준값이에요.
내부 대역도 예시 값이에요. 사설 대역은 인터넷에서 도달할 수 없지만, 어떤 컨테이너가 어느
주소를 쓰고 터널이 어디로 이어지는지는 그 자체로 내부 지도예요. 구조는 그대로 두고 숫자만
바꿨으므로(3번째와 4번째 옥텟 유지) 스크립트가 무슨 일을 하는지는 그대로 읽혀요.
198.18.0.0/15는 RFC 2544의 성능 시험용 대역이고, 100.64.0.0/10은 RFC 6598의
공유 주소 공간이에요. 두 대역은 인터넷에서 전역 도달 가능한 주소가 아니며, 여기서는
내부 배치를 표현하는 예시 값으로 사용해요. LXC 번호와 서비스 포트는 그대로예요.
검사기(scripts/sanitization-check.sh)는 허용 목록으로 동작해요. 위 자리표시자
대역만 통과하고 사설 대역을 포함한 나머지는 거부해요. 원본에서 값을 옮겨 오다 실제 주소가
섞이면 그 자리에서 검사에 실패해요.
자격증명 값은 원본에도 없어요. 인프라 레포지토리는 자격증명을 담지 않고, 볼트 구성은 pickle-secrets-example에서 따로 설명해요.
관리 peer는 root 전용 일회용 key 파일로 등록하며 DNS와 route, SSH config를 보존해요. 신규 관리 peer의 선택 버전은 NetBird 0.80.0이며 기존 peer를 일괄 업그레이드하지 않아요. VPN NTP server는 distro chrony deb를 별도 경로에 추출하고 기존 시간 client를 유지해요.
| 도구 | 용도 |
|---|---|
enroll-management-peer.sh |
setup-key-file과 명시적 apply로 새 management peer 등록 |
install-vpn-ntp-server.sh |
exact overlay 주소와 client allowlist로 clock-control 없는 NTP server 구성 |
실행과 보호, 검증, 원복은 NetBird 관리/NTP 런북을 따라요.
배포 workflow 예시는 .github/deploy.example.yml에 있어요. GitHub Actions 실행 폴더 밖의 비활성 예시이며 실제 배포 workflow를 설치했다고 표현하지 않아요. 개발 전용 key와 운영 key 분리 조건을 예시 파일에도 검증해요.
배포 workflow는 NETBIRD_SETUP_KEY가 있으면 배포 job의 일시 peer를 연결해요.
DEPLOY_TRANSPORT=netbird 변수로 VPN 사용을 필수로 고정할 수도 있어요. 키와 모드가
모두 없으면 기존 native SSH 경로를 사용해요. 선택된 VPN 연결이 실패하면 배포를
중단하며 native SSH로 다시 시도하지 않아요. Build job에는 VPN 키를 주입하지 않아요.
EXAMPLE_DEV_VPN_HOST, EXAMPLE_PROD_VPN_HOST 변수는 각각 개발 호스트와 운영 호스트의 VPN IPv4예요.
기존 SSH Secret의 호스트명은 HostKeyAlias에 사용하므로 개인키와 known_hosts pin을
교체하지 않아요. 포트 22는 기존 호스트명, 다른 포트는 [기존 호스트명]:포트를 사용해요.
scripts/ci-netbird.py는 GitHub-hosted Linux X64 runner에서 공식 NetBird 0.81.5 CLI를
고정 SHA256으로 설치해요. DNS와 client/server routes, 내장 SSH, remote jobs를 끄며,
종료 시 자신이 만든 연결만 중단해요. 키는 별도로 승인한 전용 CI 그룹의 ephemeral
Setup Key여야 해요. 전용 키는 reusable, 사용 횟수 무제한, 만료 없음으로 설정해요.
키를 회수하면 새 등록이 차단되며 이미 등록된 peer 제거는 별도로 확인해요.
check-ssh.example.yml은 비활성 인증 점검 예시예요.
prod 환경의 기존 키와 pin으로 SSH 인증만 확인하며 원격 session을 열지 않아요.
job 실행 제한은 3분이에요. 필요한 권한과 전환 절차는 배포 런북에 있어요.
개발 TLS 인증서는 15분 backup timer로 암호화해 보관해요. 복구 때는 독립 snapshot의 파일 hash를 대조하고 인증서 갱신은 dry-run으로 확인해요.
dev-tls-export.py는 지정된 개발 인증서와 개인 키 bundle을 CMS recipient 공개 인증서로
호스트 안에서 암호화해요. dev-tls-backup.py는 크기와 SHA, recipient를 대조한 암호문을
PBS에 보관하고, 독립 복구에서 원래 파일 hash를 확인해요. 평문 키는 플랫폼 호스트와 독립 복구 서버의
보호 경로에만 남겨요. 예제 설정은 hosts/의 dev-tls-backup.example.json, 실행과 복구
절차는 개발 TLS 백업 런북에 있어요. 개발 CI SSH key의
custody와 GitHub secret 등록은 별도 절차이며 이 TLS 백업을 설정했다고 완료되지 않아요.
llm-health-probe.py는 host/query와 정답 생성 결과를 분리하고,
service-health-monitor.py는 로컬 결과의 신선도와 최초 장애, 60분 안정 복구를 기록해요.
원격 결과의 observer_parent를 지정하면 공통 수집 중단으로 누락되거나 만료된 결과의 observer
경보를 묶으며, 신선한 서비스 실패와 TLS 보안 실패, 개별 probe 오류는 독립 판정해요.
초기 통지는 꺼져 있으며 공개 인증 경로와 원격 수집은 별도 배치예요. Python3 표준
라이브러리와 systemd를 쓰며 실행과 검증, credential 설치, 원복은
LLM 감시 런북을 따라요.
스테이징 health-only 경로는 외부의 HTTP 200과 정확한 본문, 7일 TLS 장애,
30일 만료 주의와 내부 ingress의 403 접근통제 검사를 별도 source로 구성해요.
주의는 기존 상태 enum과 별도의 severity: warning을 사용하며 장애와 다른 제목으로
전달해요. 설정과 전환 시 이력 보존은 스테이징 감시 런북을 따라요.
공개 HTTPS/TLS 감시와 고정 SSH reader/importer의 배치 및 전용 진단 키 검증은
외부 감시와 결과 수집 런북에 있어요.
최종 갱신: 2026-11-03
flowchart LR
subgraph ext [외부]
B[콘솔 접속]
V[VM 도메인 접속]
S[VM SSH 접속]
PC[VM 포트 접속]
L[LLM API 호출]
end
subgraph relay [오프캠퍼스 릴레이]
HA[HAProxy :22]
NFT[nftables DNAT]
RA[pickle-relay-agent]
end
subgraph campus [부산대학교 서버팜]
PN[Pickle nginx]
VN[VM nginx]
C[pickle-console]
A[pickle-api]
J[JobRunr]
G[pickle-sshgw]
P[pickle-proxy-agent]
DB[(PostgreSQL)]
PVE[Proxmox VE]
VM[사용자 VM]
IB[pickle-image-builder]
LG[pickle-llm-gateway]
UP[업스트림 모델 서버]
end
B --> PN
V --> VN
S --> HA
PC --> NFT
L --> LG
HA -->|WireGuard| G
NFT -->|WireGuard| VM
NFT -. 규칙 적용 .- RA
RA -->|sync| A
PN -->|/| C
PN -->|/api| A
PN -->|/terminal| G
G -->|인가 질의| A
LG -->|키·모델 동기화| A
LG --> UP
G --> VM
VN --> VM
A --> DB
A -->|작업 등록| J
J -->|Proxmox API| PVE
A -->|도메인 설정| P
P -.->|vhost 적용| VN
PVE -.->|생성/제어| VM
IB -.->|템플릿 빌드| PVE
| 레포지토리 | 역할 |
|---|---|
| pickle-api | REST API와 프로비저닝 워커 (Spring Boot 4, Java 25, PostgreSQL 18, JobRunr) |
| pickle-console | 사용자·관리자 웹 콘솔 (React 19, TypeScript) |
| pickle-sshgw | SSH 게이트웨이와 웹 터미널 브리지 (sshpiperd, Go) |
| pickle-proxy-agent | nginx 리버스 프록시 제어 에이전트 (Go) |
| pickle-relay-agent | 오프캠퍼스 릴레이의 nftables DNAT 에이전트 (Go) |
| pickle-llm-gateway | 교내 LLM API 게이트웨이 (Go) |
| pickle-image-builder | 사용자 VM OS 이미지 빌드 레시피 (shell, virt-customize) |
| pickle-infra (비공개) | 인프라 프로비저닝 스크립트와 운영 런북 (shell) |
| pickle-infra-example | 프로비저닝·배포 스크립트와 런북 샘플 |
| pickle-secrets (비공개) | 호스트 시크릿 볼트 (git-crypt) |
| pickle-secrets-example | 볼트 레이아웃과 git-crypt 운용 절차 |