From a80e6eab20374e5919d23f757bbb6908dadc4140 Mon Sep 17 00:00:00 2001 From: chyoo Date: Wed, 15 Jul 2026 14:28:36 +0900 Subject: [PATCH] fix: apply deployment resource settings to Kubernetes workloads --- src/apps/apps.service.ts | 67 +++++++++++++++++++++++++++++++++++++++- src/apps/dto.ts | 31 ++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/apps/apps.service.ts b/src/apps/apps.service.ts index 7c7b1d4..03efad8 100644 --- a/src/apps/apps.service.ts +++ b/src/apps/apps.service.ts @@ -975,6 +975,7 @@ export class AppsService { const imageTags = parseReleaseImageTags(release.imageTags); const images = imageTags.images; + const requestedResources = body.resources; const runtimeProfile = await this.prisma.runtimeEnvProfile.findFirst({ where: { appId: release.appId, @@ -1053,14 +1054,25 @@ export class AppsService { deploymentName, componentName, image, - replicas: component?.replicas ?? 1, + replicas: requestedResources?.replicas ?? component?.replicas ?? 1, ports: getComponentPorts(component), + resources: requestedResources, }) ), ); } const workloadRef = `${workloadType.toLowerCase()}/${deploymentName}`; + if (requestedResources?.replicas) { + logs.push(`[scale] ${workloadRef} replicas=${requestedResources.replicas}`); + appendOutput(logs, await this.kubectl([ + 'scale', + workloadRef, + `--replicas=${requestedResources.replicas}`, + '-n', + namespace, + ])); + } logs.push(`[set-image] ${workloadRef} ${deploymentName}=${image}`); const setImageOutput = await this.kubectl([ 'set', @@ -1072,6 +1084,31 @@ export class AppsService { ]); appendOutput(logs, setImageOutput); + const resourceArgs = [ + 'set', + 'resources', + workloadRef, + `--containers=${deploymentName}`, + '-n', + namespace, + ...(requestedResources?.cpuRequest || requestedResources?.memoryRequest + ? [`--requests=${[ + requestedResources.cpuRequest ? `cpu=${requestedResources.cpuRequest}` : null, + requestedResources.memoryRequest ? `memory=${requestedResources.memoryRequest}` : null, + ].filter(Boolean).join(',')}`] + : []), + ...(requestedResources?.cpuLimit || requestedResources?.memoryLimit + ? [`--limits=${[ + requestedResources.cpuLimit ? `cpu=${requestedResources.cpuLimit}` : null, + requestedResources.memoryLimit ? `memory=${requestedResources.memoryLimit}` : null, + ].filter(Boolean).join(',')}`] + : []), + ]; + if (resourceArgs.length > 6) { + logs.push(`[resources] ${workloadRef}`); + appendOutput(logs, await this.kubectl(resourceArgs)); + } + logs.push(`[rollout] ${workloadRef}`); const rolloutOutput = await this.kubectl([ 'rollout', @@ -2814,6 +2851,12 @@ function createDeploymentManifest(input: { image: string; replicas?: number | null; ports?: ComponentPort[]; + resources?: { + cpuRequest?: string; + cpuLimit?: string; + memoryRequest?: string; + memoryLimit?: string; + }; }) { const labels = { 'app.kubernetes.io/name': input.deploymentName, @@ -2846,6 +2889,28 @@ function createDeploymentManifest(input: { name: input.deploymentName, image: input.image, imagePullPolicy: 'IfNotPresent', + ...(input.resources?.cpuRequest || input.resources?.memoryRequest || input.resources?.cpuLimit || input.resources?.memoryLimit + ? { + resources: { + ...(input.resources.cpuRequest || input.resources.memoryRequest + ? { + requests: { + ...(input.resources.cpuRequest ? { cpu: input.resources.cpuRequest } : {}), + ...(input.resources.memoryRequest ? { memory: input.resources.memoryRequest } : {}), + }, + } + : {}), + ...(input.resources.cpuLimit || input.resources.memoryLimit + ? { + limits: { + ...(input.resources.cpuLimit ? { cpu: input.resources.cpuLimit } : {}), + ...(input.resources.memoryLimit ? { memory: input.resources.memoryLimit } : {}), + }, + } + : {}), + }, + } + : {}), ...(input.ports?.length ? { ports: input.ports.map((port) => ({ diff --git a/src/apps/dto.ts b/src/apps/dto.ts index cb5822b..20ab684 100644 --- a/src/apps/dto.ts +++ b/src/apps/dto.ts @@ -1,4 +1,5 @@ -import { ArrayNotEmpty, IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, IsUUID, Min } from 'class-validator'; +import { Type } from 'class-transformer'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, IsUUID, Min, ValidateNested } from 'class-validator'; import { AppStatus, ComponentType } from './app-enums'; export class CreateAppDto { @@ -195,6 +196,29 @@ export class CreateAppReleaseDto { buildJobIds!: string[]; } +export class DeploymentResourceLimitsDto { + @IsOptional() + @Type(() => Number) + @IsInt() + replicas?: number; + + @IsOptional() + @IsString() + cpuRequest?: string; + + @IsOptional() + @IsString() + cpuLimit?: string; + + @IsOptional() + @IsString() + memoryRequest?: string; + + @IsOptional() + @IsString() + memoryLimit?: string; +} + export class CreateDeploymentJobDto { @IsUUID() releaseId!: string; @@ -208,6 +232,11 @@ export class CreateDeploymentJobDto { @IsOptional() @IsBoolean() updateLauncherAppVersion?: boolean; + + @IsOptional() + @ValidateNested() + @Type(() => DeploymentResourceLimitsDto) + resources?: DeploymentResourceLimitsDto; } export class CreateRuntimeEnvProfileDto {