Skip to content

Commit

Permalink
[EO2-WAVY#120] ML 서버 시작 Interceptor 개발 및 GET /auth/token에 적용
Browse files Browse the repository at this point in the history
GET /auth/token에 적용한 이유는 해당 API가 호출되었다면 회원가입 또는 로그인이 이루어졌으므로, 조금 후 분석이 일어날 수 있기 때문이다.
차후 Google Analytics와의 연동으로 접속자 수에 따라 ML 서버를 시작 / 종료하게 만들 계획이다.
  • Loading branch information
Yeonwu committed Nov 3, 2021
1 parent 6dbb5bd commit 254c636
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { JwtMiddleware } from './auth/auth-jwt.middleware';
import { BookmarksModule } from './bookmarks/bookmarks.module';
import { AwsModule } from './aws/aws.module';
import { AwsSdkModule } from 'nest-aws-sdk';
import { Lambda, S3, SharedIniFileCredentials } from 'aws-sdk';
import { EC2, Lambda, S3, SharedIniFileCredentials } from 'aws-sdk';

const getEnvFilePath = (node_env: string): string => {
if (node_env === 'dev') {
Expand Down Expand Up @@ -115,7 +115,7 @@ const getEnvFilePath = (node_env: string): string => {
timeout: 300000,
},
},
services: [S3, Lambda],
services: [S3, Lambda, EC2],
}),
MembersModule,
PracticesModule,
Expand Down
10 changes: 9 additions & 1 deletion src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
Query,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
ApiBadRequestResponse,
Expand All @@ -9,6 +15,7 @@ import {
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { StartMLInstanceInterceptor } from 'src/aws/aws-ml-instance.interceptor';
import { AuthJwt } from './auth-jwt.decorator';
import { AccessTokenGuard } from './auth.guard';
import { AuthService } from './auth.service';
Expand Down Expand Up @@ -72,6 +79,7 @@ export class AuthController {
type: GetJwtOutput,
})
@Get('token')
@UseInterceptors(StartMLInstanceInterceptor)
getJwtToken(@Query() getJwtInput: GetJwtInput): Promise<GetJwtOutput> {
return this.authService.getJwt(getJwtInput);
}
Expand Down
82 changes: 82 additions & 0 deletions src/aws/aws-ml-instance.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { EC2 } from 'aws-sdk';
import { InjectAwsService } from 'nest-aws-sdk';
import { Observable } from 'rxjs';

@Injectable()
export class StartMLInstanceInterceptor implements NestInterceptor {
static instanceId: string;

constructor(
@InjectAwsService(EC2)
private readonly ec2Service: EC2,
private readonly config: ConfigService,
) {
StartMLInstanceInterceptor.instanceId =
this.config.get('AWS_ML_INSTANCE_ID');
}
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
this.startInstance();
return next.handle();
}

async startInstance() {
const instanceStatusResult = await this.ec2Service
.describeInstances({
DryRun: false,
InstanceIds: [StartMLInstanceInterceptor.instanceId],
})
.promise();

if (instanceStatusResult.$response.error) {
const error = instanceStatusResult.$response.error;
const errString = `${error.name}(${error.code}): ${error.message}`;
throw Error(errString);
}

const mlInstance = instanceStatusResult.Reservations[0].Instances[0];
const instanceState = mlInstance.State.Name;

if (instanceState == 'stopping') {
this.startWhileStopping();
} else if (instanceState == 'stopped') {
this.start();
} else if (
instanceState == 'terminated' ||
instanceState == 'shutting-down'
) {
throw new Error(`!!! MLInstance is ${instanceState}. !!!`);
}
}

private async start() {
const result = await this.ec2Service
.startInstances({
DryRun: false,
InstanceIds: [StartMLInstanceInterceptor.instanceId],
})
.promise();
return result.StartingInstances[0].InstanceId;
}
private async startWhileStopping() {
await this.ec2Service
.waitFor('instanceStopped', {
DryRun: false,
InstanceIds: [StartMLInstanceInterceptor.instanceId],
$waiter: {
delay: 3,
},
})
.promise();
return await this.start();
}
}
15 changes: 13 additions & 2 deletions src/aws/aws.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { Analysis } from 'src/analyses/entities/analyses.entity';
import { UserImageS3Service } from './aws-user-image.service';
import { UserVideoS3Service } from './aws-user-video.service';
import { StartMLInstanceInterceptor } from './aws-ml-instance.interceptor';

@Module({
imports: [TypeOrmModule.forFeature([Analysis])],
providers: [AwsService, UserVideoS3Service, UserImageS3Service],
providers: [
AwsService,
UserVideoS3Service,
UserImageS3Service,
StartMLInstanceInterceptor,
],
controllers: [AwsController],
exports: [AwsService, UserVideoS3Service, UserImageS3Service],
exports: [
AwsService,
UserVideoS3Service,
UserImageS3Service,
StartMLInstanceInterceptor,
],
})
export class AwsModule {}

0 comments on commit 254c636

Please sign in to comment.