Microservice Architecture

์žฅ์—ฌ์ง„ยท2022๋…„ 4์›” 27์ผ
0
post-thumbnail

Microservice Architecture๋ž€?๐Ÿค”

  • ํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์—ฌ๋Ÿฌ ์ž‘์€ ์„œ๋น„์Šค๋กœ ๋ถ„ํ•ดํ•˜์—ฌ ๊ฐœ๋ฐœ,์šด์˜,๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒ€๋ณ„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœ ๊ฐ€๋Šฅ

- ๐Ÿ’ก ์žฅ์ 

  1. ์†Œ์Šค์ฝ”๋“œ ์ „์ฒด๋ฅผ ๋นŒ๋“œ/๋ฐฐํฌํ•˜๋ ค๋ฉด ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆผ
    -> ๊ฒŒ์‹œํŒAPI ๋ฐ”๋€Œ๋ฉด ๊ฒŒ์‹œํŒ ํด๋”๋งŒ ๋‹ค์‹œ ๋ฐฐํฌ
  2. ์—๋Ÿฌ๋‚˜์„œ ์„œ๋ฒ„๊ฐ€ ์ฃฝ์œผ๋ฉด ๋ชจ๋“  API ์‚ฌ์šฉ๋ถˆ๊ฐ€๋Šฅ
    -> ๊ฒŒ์‹œํŒ์ด ์ฃฝ์–ด๋„ ์ƒํ’ˆ, ๋กœ๊ทธ์ธ ๋“ฑ ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ ์‚ฌ์šฉ๊ฐ€๋Šฅ
  3. NestJS๊ฐœ๋ฐœ์ž๋งŒ ๋ฝ‘์•„์•ผ๋จ
    -> ๊ฒŒ์‹œํŒ์€ Nestjs ์ƒํ’ˆ์€ Django ๋กœ๊ทธ์ธ์€ Spring ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ์ž ์ฑ„์šฉ ๊ฐ€๋Šฅ

- ๐Ÿ’ก ๋‹จ์ 

์ „์ฒด์ ์ธ ๊ธฐ์ˆ  ๋ณต์žก๋„ ์ฆ๊ฐ€ => ์ž‘์€ ์„œ๋น„์Šค๋ณด๋‹ค ํฐ ์„œ๋น„์Šค์—์„œ ๋งŽ์ด ์‚ฌ์šฉ!

[์˜ˆ์‹œ ํŒŒ์ดํ”„๋ผ์ธ]

[๊ณตํ†ต ์„ค์ •]

  • ๋ชจ๋“  ํด๋”์— Dockerfile์ƒ์„ฑ
  • docker-compose.yamlํŒŒ์ผ์€ ๊ฐ€์žฅ ์ƒ์œ„ ํด๋”์— ์ƒ์„ฑ
version: "3.3"

services:
  api-gateway:
    build:
      context: ./api-gateway
      dockerfile: Dockerfile
    volumes:
      - ./api-gateway/src:/api-gateway/src
    ports:
      - 3000:3000

  auth-service:
    build:
      context: ./services/auth
      dockerfile: Dockerfile
    volumes:
      - ./services/auth/src:/auth-service/src
    ports:
      - 3001:3001

  resource-service:
    build:
      context: ./services/resource
      dockerfile: Dockerfile
    volumes:
      - ./services/resource/src:/resource-service/src
    ports:
      - 3002:3002

[Rest]

1. API-Gatewayํด๋”

  • Module์—์„œ ClientModule์ƒ์„ฑ => ๊ฐ๊ฐ์˜ service๋“ค์— ๋Œ€ํ•œ ์ •๋ณด ์ž…๋ ฅ
  • Controller์—์„œ ์„œ๋น„์Šค ์ฃผ์ž…ํ•˜๊ณ  ์—”๋“œํฌ์ธํŠธ ์ž‘์„ฑ,cmd๋Š” MessagePattern์„ ๊ธฐ์ž…
//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ClientsModule, Transport } from '@nestjs/microservices';

@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'AUTH_SERVICE', // ip์ฃผ์†Œ
        transport: Transport.TCP, // TCP๊ฐ€ ๊ฐ€์žฅ ๊ธฐ๋ณธ ์—ฐ๊ฒฐ
        options: { host: 'auth-service', port: 3001 }, // ip์ฃผ์†Œ
      },
      {
        name: 'RESOURCE_SERVICE', // ip์ฃผ์†Œ
        transport: Transport.TCP,
        options: { host: 'resource-service', port: 3002 },
      },
    ]),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';
import { ClientProxy } from '@nestjs/microservices';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,

    @Inject('AUTH_SERVICE')
    private readonly clientAuthService: ClientProxy,
    @Inject('RESOURCE_SERVICE')
    private readonly clientResourceService: ClientProxy,
  ) {}

  @Get('/auth/login')
  login() {
    return this.clientAuthService.send({ cmd: 'aaa' }, { name: '์ฒ ์ˆ˜' 	});
  }
  
  @Get('/boards')
  fetchboards() {
    return this.clientResourceService.send({ cmd: 'bbb' }, { age: 13 });
  }
}

2. Service ํด๋” ๋‚ด ๊ฐ๊ฐ์˜ ๊ธฐ๋Šฅ ํด๋”

  • yarn add @nestjs/microservices ์„ค์น˜
  • ์•ฑ๋ชจ๋“ˆ์€ create๊ฐ€ ์•„๋‹Œ createMicroservice์‚ฌ์šฉ
  • transport๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ TCP๋ ˆ์ด์–ด ์‚ฌ์šฉ
  • Controller์—์„œ @Get์ด๋‚˜ @Post ๊ฐ™์€ Rest-API์˜ Method๋ฅผ ์ ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ @MessagePattern์‚ฌ์šฉ
// main.ts
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.TCP, /
      options: { host: 'auth-service', port: 3001 },
    },
  );
  await app.listen();
}
bootstrap();

//app.controller.ts
import { Controller } from '@nestjs/common';
import { AppService } from './app.service';
import { MessagePattern } from '@nestjs/microservices';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @MessagePattern({ cmd: 'login' })
  login(): string {
    return 'login์„ ์š”์ฒญํ•˜์…จ์Šต๋‹ˆ๋‹ค!';
  }
}

[Graphql]

๋ชจ๋“  ํŒŒ์ผ์— yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express ์„ค์น˜

1. API-Gatewayํด๋”

  • ๊ฐ๊ฐ์˜ subgraph๋ฅผ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์—ญํ• 
    yarn add @apollo/gateway ์„ค์น˜

  • app.module.ts์— subgraph ์ •๋ณด ์ž…๋ ฅ

//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo';
import { IntrospectAndCompose } from '@apollo/gateway';

@Module({
  imports: [
      driver: ApolloGatewayDriver,
      gateway: {
        //์ž…๊ตฌ
        supergraphSdl: new IntrospectAndCompose({
          // ์—ฐ๊ฒฐ ์„ค์ •
          subgraphs: [
            // subgraph ์ •๋ณด ์ž…๋ ฅ
            { name: 'auth', url: 'http://auth-service:3001/graphql' },
            { name: 'resource', url: 'http://resource-service:3002/graphql' },
          ],
        }),
      },
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

๐Ÿ“Œlocalhost:3000์œผ๋กœ ์ ‘์†ํ•˜๋ฉด app.module.tsํŒŒ์ผ์˜ ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด ๊ฐ๊ฐ์˜ port๋กœ ์—ฐ๊ฒฐ๋จ

2. Service ํด๋” ๋‚ด ๊ฐ๊ฐ์˜ ๊ธฐ๋Šฅ ํด๋”

  • subgraph์—ญํ• 
    yarn add @apollo/federation์„ค์น˜
    yarn add @apollo/subgraph์„ค์น˜
  • main.ts์—์„œ ํฌํŠธ๋ฒˆํ˜ธ ์„ค์ •
  • Module์— GraphQLModule์ถ”๊ฐ€ - Federation์‚ฌ์šฉ!
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3001);
}
bootstrap();

// app.resolver.ts
import { Resolver, Mutation } from '@nestjs/graphql';
import { AppService } from './app.service';

@Resolver()
export class AppResolver {
  constructor(private readonly appService: AppService) {}

  @Mutation(() => String)
  login() {
    return '๋กœ๊ทธ์ธ ์„ฑ๊ณต!';
  }
}

// app.module.ts
import { Module } from '@nestjs/common';
import { AppResolver } from './app.resolver';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import {
  ApolloFederationDriver,
  ApolloFederationDriverConfig,
} from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloFederationDriverConfig>({
      driver: ApolloFederationDriver,
      autoSchemaFile: 'scr/common/grapql/schema.gql',
    }),
  ],
  providers: [AppService, AppResolver],
})
export class AppModule {}

[nginx]

Nginx Reverse Proxy
1) ์ˆœ๋ฐฉํ–ฅ Proxy(Forward Proxy)

  • ์„œ๋ฒ„๋“ค์ด ์ธํ„ฐ๋„ท์— ์ ‘์†ํ• ๋•Œ ๋จผ์ € Proxy ์„œ๋ฒ„๋ฅผ ๊ฑฐ์ณ์„œ ์ธํ„ฐ๋„ท์— ์—ฐ๊ฒฐ๋˜๋Š” ๋ฐฉ์‹
  • ๋ณด์•ˆ: Proxy Server์—์„œ In/Out bound ํŒจํ‚ท์— ๋Œ€ํ•œ ๋ณด์•ˆ ์ •์ฑ…(Content filtering ๋“ฑ)์„ ์ ์šฉ
  • ์„ฑ๋Šฅย : Proxy Server๋Š” ๋‚ด๋ถ€์— Cache๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ ์ด๋ฏธ ํ•œ๋ฒˆ ํ†ต์‹ ํ•œ ์™ธ๋ถ€ ์„œ๋ฒ„์˜ ์ด๋ฏธ์ง€, ํŒŒ์ผ, ๊ทธ ์™ธ์ •๋ณด๋“ค์„ ์ €์žฅ ๊ฐ€๋Šฅ => Proxy ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๋น ๋ฅธ ํ†ต์‹ ์„ ์ง€์›

2) ์—ญ๋ฐฉํ–ฅ Proxy (Reverse Proxy)

  • ์™ธ๋ถ€์—์„œ ๋‚ด๋ถ€ ์„œ๋ฒ„๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค ์ ‘๊ทผย ์‹œ, Proxy ์„œ๋ฒ„๋ฅผ ๋จผ์ € ๊ฑฐ์ณ์„œ ๋‚ด๋ถ€ ์„œ๋ฒ„๋กœ ๋“ค์–ด์˜ค๋Š” ๋ฐฉ์‹
  • ๋ณด์•ˆย : ์™ธ๋ถ€ ์‚ฌ์šฉ์ž๋Š” ์‹ค์ œ ๋‚ด๋ถ€๋ง์— ์žˆ๋Š” ์„œ๋ฒ„์˜ ์กด์žฌ๋ฅผ ๋ชจ๋ฆ„
  • ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑย : Proxy ์„œ๋ฒ„๊ฐ€ ๋‚ด๋ถ€ ์„œ๋ฒ„์˜ ์ •๋ณด๋ฅผ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์„ ํ†ตํ•ด ๋ถ€ํ•˜ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์š”์ฒญ์„ ๋ถ„๋ฐฐ ๊ฐ€๋Šฅ

  • docker-compose.yaml์— api-gateway์—์„œ nginx์ถ”๊ฐ€!!
version: "3.3"

services:
  api-gateway:
    image: nginx:latest
    volumes:
      - ./api-gateway/default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - 80:80

  auth-service:
    build:
      context: ./services/auth
      dockerfile: Dockerfile
    volumes:
      - ./services/auth/src:/auth-service/src
    ports:
      - 3001:3001

  stock-service:
    build:
      context: ./services/stock
      dockerfile: Dockerfile
    volumes:
      - ./services/stock/src:/stock-service/src
    ports:
      - 3002:3002

1. API-Gatewayํด๋”

  • nginx ์„ค์ • ํŒŒ์ผ (default.conf) => nginx๊ฐ€ ์—ญ๋ฐฉํ–ฅ Proxy๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •!
server{
    listen 80; # 80์ด ๊ธฐ๋ณธ ํฌํŠธ
    
    # /graphql๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๋„˜๊ฒจ์ค„ ๋‚ด์šฉ(url)
    location /graphql{ 
        proxy_pass http://auth-service:3001;

    }

	# /stocks๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๋„˜๊ฒจ์ค„ ๋‚ด์šฉ(url)
    location /stocks { 
        proxy_pass http://stock-service:3002;
    }

}

2. Service ํด๋” ๋‚ด ๊ฐ๊ฐ์˜ ๊ธฐ๋Šฅ ํด๋”

  • ๊ฐ๊ฐ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์— ๋งž๊ฒŒ ์„ค์ •!

0๊ฐœ์˜ ๋Œ“๊ธ€