-
Notifications
You must be signed in to change notification settings - Fork 4
refactor: VM 할당 개선 #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
7a338bc
d59ac22
1097519
c2f311c
996fab5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,22 +26,26 @@ func CreateVM(input CreateVMInput, contextStruct *vms.ControlContext, rdb *redis | |||||
|
|
||||||
| log.Info("func CreateVM() memory=%d GiB, cpu=%d, disk=%d GiB", hwReq.Memory, hwReq.CPU, hwReq.Disk, true) | ||||||
|
|
||||||
| // 1) 코어 선택 | ||||||
| selectedCore, selectedCoreIndex, err := selectCoreOrFail(contextStruct, hwReq) | ||||||
| // 1) 코어 선택 + 자원 예약 (원자적: 선택과 Free* 차감이 한 락 안에서 → TOCTOU 제거) | ||||||
| selectedCore, selectedCoreIndex, err := reserveCoreOrFail(contextStruct, hwReq) | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
|
|
||||||
| // 예약 직후 롤백 등록. 이후 모든 실패 경로는 cleanup.run()으로 예약을 되돌려야 함 | ||||||
| cleanup := &cleanupChain{} | ||||||
| cleanup.push(func() { | ||||||
| contextStruct.Resources.DeallocateResources(selectedCore, uuid, hwReq) | ||||||
| }) | ||||||
|
|
||||||
| // 2) SSH 키 생성 | ||||||
| privateKeyPEM, publicKeyOpenSSH, err := internalssh.GenerateSSHKey() | ||||||
| if err != nil { | ||||||
| cleanup.run() | ||||||
| log.Error("GenerateSshKey() failed: %v", err, true) | ||||||
| return fmt.Errorf("CreateVM: failed to generate SSH key: %w", err) | ||||||
| } | ||||||
|
|
||||||
| // 단계별 롤백 등록을 위한 chain | ||||||
| cleanup := &cleanupChain{} | ||||||
|
|
||||||
| //사용자 수 확인 | ||||||
| if len(input.Users) == 0 { | ||||||
| cleanup.run() | ||||||
|
|
@@ -53,6 +57,7 @@ func CreateVM(input CreateVMInput, contextStruct *vms.ControlContext, rdb *redis | |||||
| cmsResp, isNewSubnet, err := allocateCmsSubnet(contextStruct, input.SubnetType, uuid) | ||||||
| if err != nil { | ||||||
| //TODO AllocateCmsSubnet cleanup logic implement | ||||||
| cleanup.run() | ||||||
| log.Error("CreateVM: failed to allocate CMS subnet: %v", err, true) | ||||||
| return fmt.Errorf("CreateVM: failed to allocate CMS subnet: %w", err) | ||||||
| } | ||||||
|
|
@@ -83,11 +88,8 @@ func CreateVM(input CreateVMInput, contextStruct *vms.ControlContext, rdb *redis | |||||
| IP_VM: cmsResp.IP, | ||||||
| } | ||||||
|
|
||||||
| // 5) 코어 자원 할당 (VMInfoIdx + Free* 원자적 갱신) | ||||||
| contextStruct.Resources.AllocateResources(selectedCore, uuid, newVM, hwReq) | ||||||
| cleanup.push(func() { | ||||||
| contextStruct.Resources.DeallocateResources(selectedCore, uuid, hwReq) | ||||||
| }) | ||||||
| // 5) 코어에 VM 메타데이터 부착 (Free* 예약은 ReserveCore에서 완료, 롤백은 위 cleanup이 담당) | ||||||
| contextStruct.Resources.AttachVMInfo(selectedCore, uuid, newVM) | ||||||
| log.DebugInfo("core %s updated: FreeMemory=%d, FreeCPU=%d, FreeDisk=%d", | ||||||
| selectedCore.IP, selectedCore.FreeMemory, selectedCore.FreeCPU, selectedCore.FreeDisk) | ||||||
|
|
||||||
|
|
@@ -113,6 +115,24 @@ func CreateVM(input CreateVMInput, contextStruct *vms.ControlContext, rdb *redis | |||||
| return fmt.Errorf("CreateVM: failed to create VM on core %s: %w", selectedCore.IP, err) | ||||||
| } | ||||||
|
|
||||||
| // 코어 생성 성공 이후의 실패 발생 시 처리 | ||||||
| cleanup.push(func() { | ||||||
| log.Info("clean up: requesting VM deletion on core %s for %s", selectedCore.IP, uuid, true) | ||||||
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||||||
| defer cancel() | ||||||
| if _, err := coreClient.DeleteVM(ctx, model.DeleteVMRequest{ | ||||||
| UUID: uuid, | ||||||
| Type: model.HardDelete, | ||||||
| }); err != nil { | ||||||
| // 삭제 실패 자체가 롤백을 중단시키지 않도록. | ||||||
| log.Error("rollback: failed to delete VM %s on core %s (manual cleanup or core GC required): %v", | ||||||
| uuid, selectedCore.IP, err, true) | ||||||
| } | ||||||
| if err := RemoveVMInfoFromRedis(ctx, rdb, uuid); err != nil { | ||||||
| log.Warn("rollback: failed to remove vm info from redis: %v", err, true) | ||||||
| } | ||||||
| }) | ||||||
|
|
||||||
| // 8) DB에 인스턴스 정보 영속화 | ||||||
| if err := contextStruct.VMRepo.AddInstance(newVM, selectedCoreIndex); err != nil { | ||||||
| log.Error("Error database instance insertion failed: %v", err, true) | ||||||
|
|
@@ -164,12 +184,13 @@ func buildCoreCreateVMRequest(input CreateVMInput, cmsResp *client.CmsNewInstanc | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| // selectCoreOrFail은 코어 선택 + 실패 시 진단 로그 출력 캡슐화를 진행 | ||||||
| func selectCoreOrFail(contextStruct *vms.ControlContext, req vms.HardwareRequirement) (*vms.Core, int, error) { | ||||||
| // reserveCoreOrFail은 코어 선택+예약 + 실패 시 진단 로그 출력 캡슐화를 진행. | ||||||
| // 성공 반환 시 해당 코어의 Free* 자원은 이미 예약(차감)된 상태 - 호출자는 실패 경로에서 롤백해야 함. | ||||||
| func reserveCoreOrFail(contextStruct *vms.ControlContext, req vms.HardwareRequirement) (*vms.Core, int, error) { | ||||||
| log := util.GetLogger() | ||||||
|
|
||||||
| log.DebugInfo("core selection process. req: memory=%d GiB, cpu=%d, disk=%d", req.Memory, req.CPU, req.Disk) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Fix the diagnostic units.
Suggested fix- log.DebugInfo("core selection process. req: memory=%d GiB, cpu=%d, disk=%d", req.Memory, req.CPU, req.Disk)
+ log.DebugInfo("core selection process. req: memory=%d MiB, cpu=%d, disk=%d MiB", req.Memory, req.CPU, req.Disk)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| result := contextStruct.Resources.SelectCore(req) | ||||||
| result := contextStruct.Resources.ReserveCore(req) | ||||||
|
|
||||||
| if result.Core != nil { | ||||||
| log.DebugInfo("core found: %s (idx=%d)", result.Core.IP, result.Index) | ||||||
|
|
@@ -241,6 +262,12 @@ func DeleteVM(uuid vms.UUID, contextStruct *vms.ControlContext, rdb *redis.Clien | |||||
| return fmt.Errorf("DeleteVM: failed to delete VM %s on core %s: %w", uuid, core.IP, err) | ||||||
| } | ||||||
|
|
||||||
| // Core 삭제 성공 -> 인메모리 자원 회수 (Free* 복구 + VMInfoIdx/VMLocation/AliveVM 정리) | ||||||
| if contextStruct.Resources.ReleaseVM(core, uuid) { | ||||||
| log.DebugInfo("released in-memory resources for VM %s on core %s: FreeMemory=%d, FreeCPU=%d, FreeDisk=%d", | ||||||
| uuid, core.IP, core.FreeMemory, core.FreeCPU, core.FreeDisk) | ||||||
| } | ||||||
|
|
||||||
|
Comment on lines
+265
to
+270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Don’t remove the in-memory VM location before later fatal cleanup can fail.
🤖 Prompt for AI Agents |
||||||
| cmsClient := client.NewCmsClient() | ||||||
| if err := DeleteCmsSubnet(cmsClient, contextStruct, uuid); err != nil { | ||||||
| log.Error("DeleteVM: failed to delete CMS subnet for VM %s: %v", uuid, err) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -214,27 +214,31 @@ func InitializeCoreData(configPath string) (structure.ControlContext, error) { | |
| currentCore.IsAlive = false | ||
| return fmt.Errorf("failed to get Disk info for core %s:%d: %w", currentCore.IP, currentCore.Port, err) | ||
| } | ||
| cpuResp, err := coreClient.GetCoreMachineCpuInfo(ctx) | ||
| if err != nil { | ||
| currentCore.IsAlive = false | ||
| return fmt.Errorf("failed to get CPU info for core %s:%d: %w", currentCore.IP, currentCore.Port, err) | ||
| } | ||
| if cpuResp == nil || cpuResp.VcpuStatus == nil { | ||
| currentCore.IsAlive = false | ||
| return fmt.Errorf("core %s:%d returned no vcpu_status in CPU info", currentCore.IP, currentCore.Port) | ||
| } | ||
|
Comment on lines
+217
to
+225
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== startup/init.go around InitializeCoreData ==\n'
nl -ba startup/init.go | sed -n '140,280p'
printf '\n== client/model/vm.go around VcpuStatus ==\n'
nl -ba client/model/vm.go | sed -n '70,120p'
printf '\n== client/vm.go around GetCoreMachineCpuInfo ==\n'
nl -ba client/vm.go | sed -n '80,140p'
printf '\n== search for errgroup usage in startup/init.go ==\n'
rg -n "errgroup|Wait\(\)|Go\(" startup/init.goRepository: easy-cloud-Knet/KWS_Control Length of output: 254 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== startup/init.go around InitializeCoreData ==\n'
python3 - <<'PY'
from pathlib import Path
p = Path("startup/init.go")
for i, line in enumerate(p.read_text().splitlines(), 1):
if 140 <= i <= 280:
print(f"{i:4d}\t{line}")
PY
printf '\n== client/model/vm.go around VcpuStatus ==\n'
python3 - <<'PY'
from pathlib import Path
p = Path("client/model/vm.go")
for i, line in enumerate(p.read_text().splitlines(), 1):
if 70 <= i <= 120:
print(f"{i:4d}\t{line}")
PY
printf '\n== client/vm.go around GetCoreMachineCpuInfo ==\n'
python3 - <<'PY'
from pathlib import Path
p = Path("client/vm.go")
for i, line in enumerate(p.read_text().splitlines(), 1):
if 80 <= i <= 140:
print(f"{i:4d}\t{line}")
PY
printf '\n== search for errgroup usage in startup/init.go ==\n'
python3 - <<'PY'
from pathlib import Path
for i, line in enumerate(Path("startup/init.go").read_text().splitlines(), 1):
if "errgroup" in line or "Wait()" in line or ".Go(" in line:
print(f"{i:4d}\t{line}")
PYRepository: easy-cloud-Knet/KWS_Control Length of output: 9872 Single core CPU lookup still aborts startup for all cores. 🤖 Prompt for AI Agents |
||
|
|
||
| totalMemoryMiB := uint32(memResp.Total * 1024) | ||
| totalDiskMiB := uint32(diskResp.Total * 1024) | ||
| freeMemoryMiB := uint32(memResp.Available * 1024) | ||
| freeDiskMiB := uint32(diskResp.Free * 1024) | ||
|
|
||
| var totalCpuCores uint32 | ||
| if currentCore.CoreInfoIdx.Cpu > 0 { | ||
| totalCpuCores = currentCore.CoreInfoIdx.Cpu | ||
| } else { | ||
| log.DebugInfo("currentCore.CoreInfoIdx.Cpu: %d", currentCore.CoreInfoIdx.Cpu) | ||
| totalCpuCores = 9999 // 음 코어를 현재 반환받지 못하는- | ||
| } | ||
| totalCpuCores := cpuResp.VcpuStatus.Total | ||
| freeCpuCores := cpuResp.VcpuStatus.Idle | ||
|
|
||
| currentCore.CoreInfoIdx.Cpu = totalCpuCores | ||
| currentCore.CoreInfoIdx.Memory = totalMemoryMiB | ||
| currentCore.CoreInfoIdx.Disk = totalDiskMiB | ||
|
|
||
| currentCore.FreeDisk = freeDiskMiB | ||
| currentCore.FreeMemory = freeMemoryMiB | ||
| currentCore.FreeCPU = totalCpuCores | ||
| currentCore.FreeCPU = freeCpuCores | ||
|
|
||
| return nil | ||
| }) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.