LoginSignup
2
0

AWS CDK で Infrastructure as Code する: ECS編

Last updated at Posted at 2023-09-03

こんにちは。AWS CDKでAWSをいろいろ操作する練習中です。
前回はVPC上にEC2インスタンスを立てましたが、今回はECS編です。

ECSとは「Amazon Elastic Container Service」のことで、いわゆるAWS上にDockerコンテナを実行する環境を提供してくれます。
さらに実行するサーバ環境は自分でEC2インスタンスを用意するタイプと、その環境すらもAWSに用意してもらうFargateタイプがあります。Fargateタイプは実行するDocker イメージだけを用意すればよいので、ホント手軽に環境を構築できます。

今回はそのFargateタイプを使います。

前提

やってみる

作る環境はこんな感じ。

FGTiTKi

  • ECSサービスでnginx上にサンプルのアプリ(Hello的なヤツ) を立ち上げます。
  • Public Subnetに Application Load Balancerを設置(ALB)
  • ALBのセキュリティグループは、WEB用のポート8080に外部のネットワークからアクセスOKとする
    • なかで待ち受けるnginxはポート80番で動くのですが、区別するためにあえて外(ALBのリスナのポート)はポート8080にしてみました
  • Private Subnet にECS環境を構築
  • ECSのセキュリティグループは、ALBのセキュリティグループからと、VPC内のネットからのアクセスのみOKとする
  • Dockerのイメージは、シンプルにDocker Hub上のnginxを用います。その定義を書いたECSのタスク定義とECSのサービスを作成
  • ECSが必要とするロールを作成

CDKのソースコード

コードです。作成済みのGitのリンクです。適宜ダウンロードするかGitのコマンドで取得してください。

さて、コードの主要部分だけ、のせておきます。

main部分

メイン部分で様々なスタックを作っていきます。前回の記事までに作成したVPCのうえに

  • ECSSecurityGroupStack: ELBとECSが使用するセキュリティグループ
  • ECSRoleStack: ECSが使用するロール
  • ELBStack: Public Subnet に配置するALB
  • ECSServiceELBStack: そのELB上にListenerと、そのリスナの先のTargetGroup
  • AppTaskdefinitionStack: ECSサービスのコンテナを指定するタスク定義
  • AppServiceStack: ECSサービス自体

を作っていきます。

bin/cdk-samples.ts
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from 'aws-cdk-lib'
import { VPCStack } from '../lib/VPCStack'
import { ELBStack } from '../lib/ELBStack'
import { ClusterStack } from '../lib/ClusterStack'
import { ECSRoleStack } from '../lib/ECSRoleStack'
import { ECSServiceStack } from '../lib/ECSServiceStack'
import { BastionStack } from '../lib/BastionStack'
import { ECSSecurityGroupStack } from '../lib/ECSSecurityGroupStack'
import { ECSServiceELBStack } from '../lib/ECSServiceELBStack'
import { AppTaskdefinitionStack } from '../lib/AppTaskdefinitionStack'
import { ContainerInfo, ServiceInfo } from '../lib/Utils'

const main = () => {
  const app = new cdk.App()

  const vpcStack = new VPCStack(app, 'VPCStack')
  // new BastionStack(app, 'BastionStack', vpcStack.vpc, vpcStack.publicSubnets[0])

  const sgStack = new ECSSecurityGroupStack(app, 'ECSSecurityGroupStack', { vpc: vpcStack.vpc })
  const clusterStack = new ClusterStack(app, 'ClusterStack')
  const ecsRoleStack = new ECSRoleStack(app, 'ECSRoleStack')

  const elbStack = new ELBStack(app, 'ELBStack', {
    subnets: vpcStack.publicSubnets,
    elbSecuriyGroup: sgStack.ELBSecurityGroup,
  })

  const serviceInfo: ServiceInfo = {
    serviceName: 'app-service',
    listenerPort: 8080,
    testListenerPort: 9080,
  }

  const containerInfo: ContainerInfo = {
    name: 'app',
    port: 80,
    healthCheckPath: '/',
  }

  const serviceStackELB = new ECSServiceELBStack(app, 'ECSServiceELBStack', {
    loadbalancer: elbStack.loadbalancer,
    vpc: vpcStack.vpc,
    containerInfo,
    serviceInfo,
  })

  const appTaskdefinition = new AppTaskdefinitionStack(app, 'AppTaskdefinitionStack', {
    ecsTaskRole: ecsRoleStack.ecsTaskRole,
    ecsTaskExecutionRole: ecsRoleStack.ecsTaskExecutionRole,
    containerInfo,
  })

  const serviceStack = new ECSServiceStack(app, 'AppServiceStack', {
    cluster: clusterStack.cluster,
    subnets: vpcStack.privateSubnets, // ECSを配置するネットはPrivate Subnet
    taskDef: appTaskdefinition.taskDef,
    ecsSecurityGroup: sgStack.ECSSecurityGroup,
    targetGroup: serviceStackELB.targetGroup,
    containerInfo,
    serviceInfo,
  })
}

main()

SecurityGroup

セキュリティグループ(SG)です。ELB向けのSGは外からの8080-8088への通信を受け付けるようにしました。ECSはサービスを増やしていくとリスナを増やしていかないといけないので、ポートを幅であけておくことにします。

ECS向けのSGは、ELBのSGからとVPCのネットワークからの通信を受け付けるようにしました。9080系とかもいつか使うんですが、今回は説明は割愛します。

lib/ECSSecurityGroupStack.ts
import { App, Stack, StackProps } from 'aws-cdk-lib'
import { CfnSecurityGroup, CfnSecurityGroupIngress, CfnSubnet, CfnVPC } from 'aws-cdk-lib/aws-ec2'
import { Profile, getProfile } from './Utils'

type ECSSecurityGroupStackProps = StackProps & {
  vpc: CfnVPC
}

export class ECSSecurityGroupStack extends Stack {
  public readonly ECSSecurityGroup: CfnSecurityGroup
  public readonly ELBSecurityGroup: CfnSecurityGroup

  constructor(scope: App, id: string, /*vpc: CfnVPC,*/ props: ECSSecurityGroupStackProps) {
    super(scope, id, props)
    const p = getProfile(this)

    // 8080-8088,9080-9088 がOKなセキュリティグループをELB用に作成
    this.ELBSecurityGroup = createELBSecurityGroup(this, `ELBSecurityGroup${p.name}`, props.vpc, p)

    // ELBのSGと、VPCのネットからのアクセスを許可
    this.ECSSecurityGroup = createECSSecurityGroup(
      this,
      `ECSSecurityGroup${p.name}`,
      props.vpc,
      this.ELBSecurityGroup,
      p,
    )
  }
}

const createELBSecurityGroup = (stack: Stack, id: string, vpc: CfnVPC, p: Profile): CfnSecurityGroup => {
  const group = new CfnSecurityGroup(stack, id, {
    groupName: `elb-sg${p.name}`,
    groupDescription: 'elb-sg',
    vpcId: vpc.attrVpcId,
    tags: [{ key: 'Name', value: `ELB-SecurityGroup${p.name}` }],
  })

  new CfnSecurityGroupIngress(stack, 'ELBSecurityGroupIngress000', {
    ipProtocol: '-1',
    groupId: group.ref,
    sourceSecurityGroupId: group.ref,
  })

  new CfnSecurityGroupIngress(stack, 'ELBSecurityGroupIngress001', {
    ipProtocol: 'tcp',
    fromPort: 8080,
    toPort: 8088,
    groupId: group.ref,
    cidrIp: '0.0.0.0/0',
  })

  new CfnSecurityGroupIngress(stack, 'ELBSecurityGroupIngress002', {
    ipProtocol: 'tcp',
    fromPort: 9080,
    toPort: 9088,
    groupId: group.ref,
    cidrIp: '0.0.0.0/0',
  })

  return group
}

const createECSSecurityGroup = (
  stack: Stack,
  id: string,
  vpc: CfnVPC,
  elbsg: CfnSecurityGroup,
  p: Profile,
): CfnSecurityGroup => {
  const group = new CfnSecurityGroup(stack, id, {
    groupName: `ecs-sg${p.name}`,
    groupDescription: 'ecs-sg',
    vpcId: vpc.attrVpcId,
    tags: [{ key: 'Name', value: `ECS-SecurityGroup${p.name}` }],
  })

  new CfnSecurityGroupIngress(stack, 'ECSSecurityGroupIngress000', {
    ipProtocol: '-1',
    groupId: group.ref,
    sourceSecurityGroupId: group.ref,
  })
  new CfnSecurityGroupIngress(stack, 'ECSSecurityGroupIngress001', {
    ipProtocol: '-1',
    groupId: group.ref,
    sourceSecurityGroupId: elbsg.ref,
  })
  new CfnSecurityGroupIngress(stack, 'ECSSecurityGroupIngress002', {
    ipProtocol: '-1',
    groupId: group.ref,
    cidrIp: vpc.cidrBlock,
  })

  return group
}

ELB

ELBです。ELBにはALB(Application Load Balancer)とNLB(Network Load Balancer) があるのですが今回はALBで。なのでTypeはapplication で、インターネット向け(internet-facing) なELBを作成しています。サブネットはPublicなサブネットを指定しています。
インターネットに公開しない内部向けのELBを構築する際は、schemeをinternet-facingではなくinternalに、サブネットはPrivateなサブネットを指定します。

セキュリティグループは先に作成したELB用のSGを指定しています。

lib/ELBStack.ts
import { App, Stack, StackProps } from 'aws-cdk-lib'
import { CfnSecurityGroup, CfnSecurityGroupIngress, CfnSubnet, CfnVPC } from 'aws-cdk-lib/aws-ec2'
import { Profile, getProfile, toRefs } from './Utils'
import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancingv2'
// import * as sqs from 'aws-cdk-lib/aws-sqs';

type ELBStackProps = StackProps & {
  subnets: CfnSubnet[]
  elbSecuriyGroup: CfnSecurityGroup
  albFlag?: boolean
  internetFlag?: boolean
}

export class ELBStack extends Stack {
  public readonly loadbalancer: CfnLoadBalancer

  constructor(scope: App, id: string, props: ELBStackProps) {
    props.albFlag = props.albFlag ?? true // 指定しないときはtrue(ALB)
    props.internetFlag = props.internetFlag ?? true // 指定しないときはtrue(internet-facing)

    super(scope, id, props)
    const p = getProfile(this)

    const type = props.albFlag ? 'application' : 'network'
    const scheme = props.internetFlag ? 'internet-facing' : 'internal'

    this.loadbalancer = createELB(this, 'app-ELB', type, scheme, props.subnets, [props.elbSecuriyGroup], p)
  }
}
const createELB = (
  stack: Stack,
  name: string,
  type: string,
  scheme: string,
  subnets: CfnSubnet[],
  elbsgs: CfnSecurityGroup[],
  p: Profile,
): CfnLoadBalancer => {
  return new CfnLoadBalancer(stack, `${name}${p.name}`, {
    type,
    name: `${name}${p.name}`,
    subnets: toRefs(subnets),
    securityGroups: toRefs(elbsgs),
    scheme,
  })
}

ECSタスク定義

ECSのタスク定義です。まずは全体。

lib/AppTaskdefinitionStack.ts
import { App, ScopedAws, Stack, StackProps } from 'aws-cdk-lib'
import { ContainerInfo, getProfile } from './Utils'
import { CfnTaskDefinition } from 'aws-cdk-lib/aws-ecs'
import { CfnRole } from 'aws-cdk-lib/aws-iam'

type AppTaskdefinitionStackProps = StackProps & {
  ecsTaskRole: CfnRole
  ecsTaskExecutionRole: CfnRole
  containerInfo: ContainerInfo
}

export class AppTaskdefinitionStack extends Stack {
  public readonly taskDef: CfnTaskDefinition
  constructor(scope: App, id: string, props: AppTaskdefinitionStackProps) {
    super(scope, id, props)
    const p = getProfile(this)
    const { accountId, region } = new ScopedAws(this)

    this.taskDef = new CfnTaskDefinition(this, 'ECSTaskDefinition', {
      family: `${props.containerInfo.name}-taskdefinition${p.name}`,
      containerDefinitions: [
        {
          essential: true,
          image: 'nginx',
          name: props.containerInfo.name,
          logConfiguration: {
            logDriver: 'awslogs',
            options: {
              'awslogs-create-group': 'true',
              'awslogs-group': `/ecs/app-taskdefinition${p.name}`,
              'awslogs-region': `${region}`,
              'awslogs-stream-prefix': 'ecs',
            },
          },
          memoryReservation: 100,
          portMappings: [
            {
              containerPort: props.containerInfo.port,
              hostPort: props.containerInfo.port,
              protocol: 'tcp',
            },
          ],
        },
      ],
      taskRoleArn: props.ecsTaskRole.attrArn,
      executionRoleArn: props.ecsTaskExecutionRole.attrArn,
      networkMode: 'awsvpc',
      requiresCompatibilities: ['FARGATE'],
      cpu: '256',
      memory: '512',
    })
  }
}

大事なとこは containerDefinitionsの配列でDockerのコンテナイメージを指定しています。メイン部からパラメタでもらっている値を埋めてみると、コンテナ定義の部分はこんな感じ。

 AppTaskdefinitionStack.tsの一部
containerDefinitions: [
{
  image: 'nginx',
  name: 'app',
  logConfiguration: {
    logDriver: 'awslogs',
    options: {
      'awslogs-create-group': 'true',
      'awslogs-group': `/ecs/app-taskdefinition`,
      'awslogs-region': `${region}`,
      'awslogs-stream-prefix': 'ecs',
    },
  },
  portMappings: [
    {
      containerPort: 80,
      hostPort: 80,
      protocol: 'tcp',
    },
  ],
},...

イメージはnginxのイメージ(実際はコンテナイメージのURLを入れたりします)、コンテナはポートは80番を使用、ログはCloudWatchへ転送してね1、みたいな指定をしています。

ECSサービスが使うリスナやターゲットグループ

ELB関連のリスナやターゲットグループです。まずは全体。

lib/ECSServiceELBStack.ts
import { App, ScopedAws, Stack, StackProps } from 'aws-cdk-lib'
import { CfnVPC } from 'aws-cdk-lib/aws-ec2'
import { ContainerInfo, ServiceInfo, getProfile } from './Utils'
import { CfnListener, CfnLoadBalancer, CfnTargetGroup } from 'aws-cdk-lib/aws-elasticloadbalancingv2'

const createTargetGroup = (
  stack: Stack,
  id: string,
  groupName: string,
  containerInfo: ContainerInfo,
  loadbalancer: CfnLoadBalancer,
  vpcId: string,
): CfnTargetGroup => {
  const targetProtocol = loadbalancer.type === 'network' ? 'TCP' : 'HTTP'
  const param = {
    healthCheckPath: containerInfo.healthCheckPath,
    name: groupName,
    port: containerInfo.port,
    protocol: targetProtocol,
    targetType: 'ip',
    healthCheckProtocol: 'HTTP',
    healthCheckTimeoutSeconds: 20,
    unhealthyThresholdCount: 5,
    vpcId: vpcId,
  }

  if (loadbalancer.type === 'network') {
    return new CfnTargetGroup(stack, id, param)
  } else {
    return new CfnTargetGroup(stack, id, {
      ...param,
      // ALBがStickySessionしたい場合。
      targetGroupAttributes: [
        {
          key: 'stickiness.enabled',
          value: 'true',
        },
        {
          key: 'stickiness.type',
          value: 'lb_cookie',
        },
        {
          key: 'stickiness.lb_cookie.duration_seconds',
          value: '86400',
        },
      ],
    })
  }
}

const createListener = (
  stack: Stack,
  id: string,
  targetGroup: CfnTargetGroup,
  listenerPort: number,
  loadbalancer: CfnLoadBalancer,
): CfnListener => {
  return new CfnListener(stack, id, {
    defaultActions: [
      {
        type: 'forward',
        targetGroupArn: targetGroup.ref,
      },
    ],
    loadBalancerArn: loadbalancer.ref,
    port: listenerPort,
    protocol: loadbalancer.type === 'network' ? 'TCP' : 'HTTP',
  })
}

type ECSServiceELBStackProps = StackProps & {
  loadbalancer: CfnLoadBalancer
  vpc: CfnVPC
  containerInfo: ContainerInfo
  serviceInfo: ServiceInfo
}

export class ECSServiceELBStack extends Stack {
  public readonly targetGroup: CfnTargetGroup
  public readonly targetGroupSub: CfnTargetGroup
  public readonly listener: CfnListener
  public readonly testListener: CfnListener
  constructor(scope: App, id: string, props: ECSServiceELBStackProps) {
    super(scope, id, props)
    const p = getProfile(this)
    const { accountId, region } = new ScopedAws(this)

    const serviceName = props.serviceInfo.serviceName

    const targetGroup = createTargetGroup(
      this,
      'TargetGroup',
      `${serviceName}-group`,
      props.containerInfo,
      props.loadbalancer,
      props.vpc.ref,
    )
    const targetGroupSub = createTargetGroup(
      this,
      'TargetGroupSub',
      `${serviceName}-groupsub`,
      props.containerInfo,
      props.loadbalancer,
      props.vpc.ref,
    )
    this.targetGroup = targetGroup
    this.targetGroupSub = targetGroupSub

    const listener = createListener(this, 'Listener', targetGroup, props.serviceInfo.listenerPort, props.loadbalancer)
    const testListener = createListener(
      this,
      'ListenerTest',
      targetGroup,
      props.serviceInfo.testListenerPort!,
      props.loadbalancer,
    )
    this.listener = listener
    this.testListener = testListener
  }
}

ELBに8080ポートでリクエストを受け付けるリスナーを作成し、デフォルトの挙動として作成するターゲットグループに転送するよう設定しています。でターゲットグループは、コンテナのポート80を設定し、 targetType: 'ip', なグループとしています。またターゲットグループは通常「そのグループに属するリソース2」も指定するんですが、ECSサービス側で動的に制御するので、ココでは指定しなくてOKです。

その他もろもろオプション指定もあるんですが、説明は割愛します。

ECSサービス

最後にECSのサービスです。

lib/ECSServiceStack.ts
import { App, ScopedAws, Stack, StackProps } from 'aws-cdk-lib'
import { CfnSecurityGroup, CfnSubnet } from 'aws-cdk-lib/aws-ec2'
import { ContainerInfo, ServiceInfo, getProfile, toRefs } from './Utils'
import { CfnCluster, CfnService, CfnTaskDefinition } from 'aws-cdk-lib/aws-ecs'
import { CfnTargetGroup } from 'aws-cdk-lib/aws-elasticloadbalancingv2'

type ECSServiceStackProps = StackProps & {
  subnets: CfnSubnet[]
  cluster: CfnCluster
  taskDef: CfnTaskDefinition
  ecsSecurityGroup: CfnSecurityGroup
  containerInfo: ContainerInfo
  serviceInfo: ServiceInfo
  targetGroup: CfnTargetGroup
}

export class ECSServiceStack extends Stack {
  public readonly ecsService: CfnService
  constructor(scope: App, id: string, props: ECSServiceStackProps) {
    super(scope, id, props)
    const p = getProfile(this)
    const { accountId, region } = new ScopedAws(this)

    const DesiredCount = props.subnets.length

    const ecsService = new CfnService(this, 'ECSService', {
      cluster: props.cluster.ref,
      // deploymentController: ECS
      capacityProviderStrategy: [
        {
          capacityProvider: 'FARGATE',
          base: 0,
          weight: 1,
        },
      ],
      deploymentController: {
        type: 'ECS',
      },
      deploymentConfiguration: {
        maximumPercent: 200,
        minimumHealthyPercent: 100,
        deploymentCircuitBreaker: {
          enable: true,
          rollback: true,
        },
      },
      // deploymentController: ECS
      // // deploymentController: CODE_DEPLOY
      // launchType: 'FARGATE',
      // deploymentController: {
      //   type: 'CODE_DEPLOY',
      // },
      // // deploymentController: CODE_DEPLOY

      taskDefinition: props.taskDef.ref,
      serviceName: props.serviceInfo.serviceName,
      schedulingStrategy: 'REPLICA',
      desiredCount: DesiredCount,
      loadBalancers: [
        {
          containerName: props.containerInfo.name,
          containerPort: props.containerInfo.port,
          // LoadBalancerName:
          //   Ref: 'AWS::NoValue'
          targetGroupArn: props.targetGroup.ref,
        },
      ],
      networkConfiguration: {
        awsvpcConfiguration: {
          assignPublicIp: 'DISABLED',
          securityGroups: toRefs([props.ecsSecurityGroup]),
          subnets: toRefs(props.subnets),
        },
      },
      platformVersion: 'LATEST',
      serviceConnectConfiguration: { enabled: false },
      tags: [{ key: 'Name', value: `ECS${p.name}` }],
      enableEcsManagedTags: true,
    })
    this.ecsService = ecsService
  }
}

タスク定義は先ほど作成したもの(taskDefinition: props.taskDef.ref)をつかうとか、セキュリティグループは先ほど作成したSGを指定するとか、下記のようなロードバランサの設定をするとか、

loadBalancers: [
  {
    containerName: 'app',
    containerPort: 80,
    targetGroupArn: props.targetGroup.ref, // このターゲットグループを使ってね、という指定
  },
],

ECSを配置するサブネットはPrivateサブネットにする、などなどの設定をしています。

これで ELB:8080 → リスナの定義で ポート80のターゲットグループに転送 → ターゲットグループに設定されたコンテナたちに転送 という構成ができましたね。

cdk.json

cdk.json(の主要な部分)
{
  "app": "npx ts-node --prefer-ts-exts bin/cdk-samples.ts",
  ...
  "context": {
    ...
    "dev": {
      "name": "dev-20230903"
    }
  }
}

実行してみる

長かったですね。。CDKを実行して、ECSサービスでWEBサーバを立ててみましょう。

$ yarn cdk deploy --all
... しばらくかかります
$

終わったようです。

ちゃんとできているか、WEBサーバにアクセスしてみます。

% aws elbv2 describe-load-balancers \
--query "LoadBalancers[*].[LoadBalancerName,DNSName]" \
--output table
---------------------------------------------------------------------------------------------------------------------
|                                               DescribeLoadBalancers                                               |
+---------------------------------+---------------------------------------------------------------------------------+
|  .......                        |  xx.elb.ap-northeast-1.amazonaws.com                                            |
|  app-ELB-dev-20230903           |  app-ELB-dev-20230903-1280708179.ap-northeast-1.elb.amazonaws.com               |
+---------------------------------+---------------------------------------------------------------------------------+
% 
$ curl http://app-ELB-dev-20230903-1280708179.ap-northeast-1.elb.amazonaws.com:8080 -i

HTTP/1.1 200 OK
Date: Sun, 27 Aug 2023 16:08:03 GMT
Content-Type: text/html
Content-Length: 615
Connection: keep-alive
Set-Cookie: AWSALB=oCDHz/dYXw1e0q8vCAQyHHxxxxQG71ZGv64ybMzI; Expires=Sun, 03 Sep 2023 16:08:03 GMT; Path=/
Set-Cookie: AWSALBCORS=oCDHz/dYXw1e0xxxxxx71ZGv64ybMzI; Expires=Sun, 03 Sep 2023 16:08:03 GMT; Path=/; SameSite=None
Server: nginx/1.25.2
Last-Modified: Tue, 15 Aug 2023 17:03:04 GMT
ETag: "64dbafc8-267"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
$

うまくいったようですね!AWSのコンソール画面も見てみましょう。

Elastic Container Service > クラスター > app-server-dev-20230903-cluster > サービス > app-service > ネットワーキング

ECSService

できていそうです。

以下はELBの画面。ターゲットグループやリスナーもできてるっぽいです。9080に関しては今回は説明を割愛します。

ELB

ターゲットグループの中身。Private Subnet上にたっているポート80のコンテナ達が登録されているのがわかりますね!

target

などなど、ECSサービスでnginxを起動してみました!

作った環境を消す

最後に、作った環境を消す際には

$ yarn cdk destroy --all

で。VPCからなにまで全て一度に消してくれます。。

TIPS

今回はココまでですが、今回作成したCDKのコードを書き換えて、今後も以下のことをやっていきたいと思います。

  • アプリをnginxではなく、SpringBootなどの自前のアプリに差し替え
  • LogをデフォルトのCloudWatchに流す設定にしているところを、Logを制御するサイドカー(別のコンテナ)を設定し、それに制御させる
    • まずはAWSが提供する FluentBitのイメージを使って制御する
    • 自前で用意したFluentBitのイメージを使って、設定ファイルなども制御する
    • SpringBootが出力するJSON形式のログを整形してCloudWatchに出力するとか、エラーのみのロググループを作成するとか自前のイメージでいろいろやる
  • ALBをPrivate Subnetに配置する
    • PrivateなSubnetに配置しつつも、おなじVPC上のEC2インスタンスにPortForwardingを仕掛けて、外のネットからもアクセスできるようにする練習
  • ALBじゃなくてNLBにしてみる
    • API GatewayのVPC Linkなどを用いる場合に状況によってはNLBを使う必要もあるため、やってみる
    • セッション共有のための API Gateway → NLB → ALBなどの多段ELB構成をやってみる
  • ECSコンテナへのリリース方式(デプロイタイプ)をいろいろ試してみる
    • 今回はデフォの方式(ローリングアップデート?)を用いた
    • ELBとCodeDeployの機構を用いることで、blue green deployment 3してみる

お疲れさまでした!

関連リンク

  1. ココをFirelensとかに設定することで、エラーログはCloudWatchだけど残りのログはS3ね、みたいなログ制御を行うことができます。

  2. 今回の構成だと、ELBから転送されるECSコンテナ達のIPアドレスのことです

  3. 新機能をリリースして新旧同時に動かせる(ポート9080に先にリリースします)ようにして、あるタイミングで新機能にトラフィックを変更するリリース方法

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0