Skip to content

About

프로비저닝·배포 스크립트와 런북 샘플

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

234 Commits

Folders and files

Repository files navigation

pickle-infra-example

부산대학교 클라우드 플랫폼(Pickle)의 인프라 계층이 어떻게 구성돼 있는지 보여주는 공개 예시본입니다. 실제 인프라 레포지토리는 비공개이며, 여기에는 프로비저닝·배포·검증 스크립트와 호스트 설정을 옮겨 담고 민감한 값을 치환해 두었습니다.

여기 적힌 IP 주소와 관리 포트, 볼트 경로는 실제 값이 아닙니다. 아래 무엇을 바꿨나에 치환 목록이 있습니다. 합성 로그인 시험의 도메인도 예시 이름으로 치환했습니다. 다른 기존 서비스 도메인은 그대로 표시합니다.

플랫폼 전체는 PNUops 프로필에서, 애플리케이션 코드는 pickle-api와 pickle-console 같은 공개 레포지토리에서 볼 수 있습니다.

운영 대상

캠퍼스 방화벽이 인바운드를 막기 때문에 오프캠퍼스 SSH는 외부 릴레이가 받아, 캠퍼스가 아웃바운드로 개통한 WireGuard 터널로 넘깁니다.

pickle.example.ac.kr ──┐                   외부 릴레이 (오프캠퍼스 SSH)
<name>.example.dev   ──┤ :80/:443           HAProxy(send-proxy-v2)
                       ▼                      │ 캠퍼스발 아웃바운드 WireGuard
pve-node (Proxmox VE) ────────────────────────────┘
 ├─ vmbr1 (인프라)
 │   ├─ LXC 100 reverse-proxy  nginx(HTTP vhost·SNI 스트림) + proxy-agent + certbot
 │   ├─ LXC 101 pickle-app     PostgreSQL + pickle-api + nginx(콘솔 정적·/api 프록시)
 │   └─ LXC 102 pickle-sshgw   proxyfront + sshpiperd + 터미널 브리지 + WireGuard 종단
 └─ vmbr2 (사용자, 격리)       사용자 VM (Ubuntu cloud-init 템플릿에서 클론)

비Proxmox 노드:
 ├─ gpu-node   aarch64 GPU 노드        캠퍼스망 192.0.2.20 — vLLM 서빙 :8000 (pickle-vllm)
 └─ dept-node  x86 서버(Ubuntu)        dept-node.example.ac.kr:22 — 학과 공유 서버

Proxmox 운영 후보 (클러스터 구성, 플랫폼 미등록):
 ├─ pve-node-2  x86 서버               캠퍼스망 192.0.2.30, pve-node와 같은 L2
 └─ pve-node-3  x86 서버, GPU 1장      캠퍼스망 192.0.2.31, GPU는 vfio-pci 부팅 시 바인딩, 패스스루 실측 완료 2026-09-08, 플랫폼 미등록

vmbr1은 인프라 전용, vmbr2는 사용자 전용이고 둘 사이에 직접 경로가 없습니다. 산출물은 전부 셸과 마크다운입니다. 주 대상은 Proxmox 호스트이고, 플랫폼에 편입된 비Proxmox 노드의 편입 절차도 함께 다룹니다. dept-node의 기존 Ubuntu와 Docker를 유지하는 PBS VM, qnetd 설치와 복구 절차를 제공합니다. pve-node-2와 pve-node-3은 NetBird에 등록된 빈 example-prod 클러스터입니다. 순차 재부팅 후 관리 SSH와 HTTPS, 정족수 복귀를 확인했습니다. GPU 노드는 호스트 GPU 시험의 NVIDIA와 CUDA 실행 패키지를 제거하고 VFIO 바인딩과 nouveau/nova 차단을 유지합니다. 두 Proxmox 노드는 플랫폼 배치 대상으로 등록하지 않았습니다.

주요 기능

플랫폼은 VM 신청·승인·생성, SSH와 웹 터미널 접속, 도메인 공개, 만료와 삭제까지를 다룹니다. 이 레포지토리가 맡는 부분은 아래와 같습니다.

  • 프로비저닝: 호스트 위의 컨테이너와 사용자 VM 템플릿을 스크립트로 만듭니다.
  • 배포: 각 서비스를 올리고, 헬스 체크를 통과하지 못하면 직전 상태로 되돌립니다.
  • 정책 적용: TLS 암호군, 로그 보존, 인그레스 설정 같은 호스트 정책을 한 번에 맞춥니다.
  • 점검: 살아 있는 시스템에 실제 요청을 보내는 스모크와 읽기 전용 헬스 스냅샷으로 상태를 확인합니다.
  • 예약 작업: 백업과 헬스체크를 타이머로 돌리고, 실패가 조용히 묻히지 않게 기록과 알림을 붙입니다.

동작 방식

  • 파괴적 변경 전에 백업합니다. 설정 파일을 덮어쓰는 스크립트는 손대는 파일을 타임스탬프 폴더에 먼저 복사합니다.
  • 배포는 readiness 게이트를 통과해야 끝납니다. deploy-api.sh는 새 jar로 서비스를 올린 뒤 readiness가 통과하지 못하면 직전 아티팩트로 되돌립니다. 되돌리기 전에 api 저널 마지막 150줄을 찍습니다. 기동 실패는 유닛이 failed로 눕지 않고 재시작을 반복하므로, 그 출력이 원인을 볼 수 있는 자리입니다. SMTP 같은 외부 dependency는 별도 전체 health가 감시합니다. deploy-sshgw.sh는 바이너리 세 개를 한 세트로 원자 교체해, 절반만 새 버전인 상태를 만들지 않습니다.
  • 소스에서 빌드하는 배포 스크립트는 체크아웃을 갱신하지 않습니다. 빌드는 PICKLE_ROOT 아래에 지금 있는 트리로 돕니다. 배포 전에 그 레포의 HEAD를 올리려는 커밋으로 맞추지 않으면 옛 트리가 빌드되고 배포는 그대로 성공하므로, readiness 게이트도 롤백도 이 상황을 잡지 못합니다. 둘 다 올라간 것이 건강한지만 보지 무엇이 올라갔는지는 보지 않기 때문입니다. 유닛과 설정을 반영하는 스크립트도 빌드만 안 할 뿐 같은 트리에서 읽습니다.
  • 정제 검사에 셀프테스트가 붙어 있습니다. sanitization-check.sh는 매 실행마다 합성 위반 케이스로 자기 검사 로직이 살아 있는지 먼저 확인한 뒤 본 검사를 수행합니다.
  • 주기 작업의 실패가 남습니다. cron-wrap.sh가 성공과 실패 마커를 기록하고, systemd OnFailure가 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 노드)를 따릅니다.

scripts/

분류 스크립트
프로비저닝 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-api.sh, deploy-console.sh(기본 설정 검증 후 요청한 Vite 기능 플래그로 최종 빌드), 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, 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
검증 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-console.sh는 기본적으로 hostname이 pickle-app인 LXC만 받습니다. 다른 후보 LXC에는 CTID와 EXPECTED_CT_HOSTNAME을 함께 지정합니다. 실제 hostname과 다르면 설치 전에 중단합니다. 후보의 백업·격리 복원 절차는 runbooks/isolated-core-bootstrap.md에 있습니다. 배포 스크립트는 정적 산출물 dist/를 nginx가 읽을 수 있게 조정합니다. 배포 로그는 호출자가 별도 보호 경로에 둡니다.

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으로 검증합니다. 대상 노드와 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 전후와 완료 시 상태를 확인합니다. 설정한 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 정기 백업은 이 도구의 대상이 아닙니다.

candidate-core-vzdump-hook.sh는 예시 후보 노드 pve-node-2의 CT 1200/1201/1202/1204를 PBS로 백업하기 전 노드·스토리지·정족수·설정·실행 상태를 확인합니다. 등록 절차는 후보 코어 백업 런북에 있습니다. core-pbs-monitor.py는 pve-node-3의 읽기 전용 PBS storage에서 네 CT의 일일 복구점과 보호된 수동 복구점을 확인하고 장애·복구 상태 변화를 알립니다. 설치 절차는 독립 감시 런북에 있습니다. CTID, 설정 해시, 암호화 fingerprint와 날짜는 예시 값이며 실제 환경에 맞게 검증해 채워야 합니다.

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-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)으로 정합니다. 직접 소유하고 그 메일이 스모크 메일함으로 라우팅되는 도메인이어야 합니다. 이 스모크는 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가 그 역할을 맡습니다.

runbooks/

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), 붙이기·떼기·인계, 카드 상태 확인과 손실 대응, 시험 게스트 정리)를 담았습니다. 나머지 재구축과 복구 절차는 비공개 레포지토리에 둡니다.

플랫폼 DNS 점검

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로 실행합니다. 이 시험은 네트워크가 차단된 임시 컨테이너를 만들고 종료 후 정리합니다.

설정 초기화 테스트는 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 접속과 서비스 복구는 별도 시험입니다.

무엇을 바꿨나

원본에서 이 레포지토리로 옮기며 치환한 값입니다. 아래는 전부 실제 값이 아닙니다. 합성 로그인 시험 도메인은 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, 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에서 따로 설명합니다.

전체 아키텍처

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
Loading
레포지토리 역할
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 운용 절차

About

프로비저닝·배포 스크립트와 런북 샘플

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages