Help us understand the problem. What is going on with this article?

AWS CDKでEC2 Auto Scalingをデプロイしてみる

はじめに

Auto Scalingのテスト環境の構築をAWS CDKの勉強も兼ねて行ってみました。
EC2にCPUの負荷を与えて、想定通りにEC2がスケールアウトすることを確認したかったため、作業内容を備忘録のつもりでまとめます。

環境

  • OS: macOS Cataline 10.15.5
  • VSCode: 1.46.0
  • Node.js: v14.4.0
  • npm: 6.14.5
  • TypeScript: 3.9.5
  • CDK: 1.45.0

AWS CDKのインストール

公式の情報を参考にインストールします。

$ npm install -g aws-cdk

インストールできているか、念のため確認します。

$ cdk --version
1.45.0 (build 0cfab15)

プロジェクトの作成

CDKのプロジェクトを公式に沿って作成します。

$ mkdir test-asg
$ cd test-asg
$ cdk init app --language=typescript

コードの実装

必要なCDKのモジュールをインストールします。

$ npm install @aws-cdk/aws-autoscaling @aws-cdk/aws-ec2 @aws-cdk/aws-elasticloadbalancingv2

なお、自分が検証した際にはこのエラーに遭遇したため、package.jsonを編集して@aws-cdk/core@aws-cdk/assertのバージョンを合わせます。

package.json
{

  : (省略)

  "devDependencies": {
    // 1.45.0 -> 1.46.0
    "@aws-cdk/assert": "^1.46.0",
    "@types/jest": "^25.2.1",
    "@types/node": "10.17.5",
    "jest": "^25.5.0",
    "ts-jest": "^25.3.1",
    "aws-cdk": "1.45.0",
    "ts-node": "^8.1.0",
    "typescript": "~3.7.2"
  },
  "dependencies": {
    "@aws-cdk/aws-autoscaling": "^1.46.0",
    "@aws-cdk/aws-ec2": "^1.46.0",
    "@aws-cdk/aws-elasticloadbalancingv2": "^1.46.0",
    // 1.45.0 -> 1.46.0
    "@aws-cdk/core": "^1.46.0",
    "source-map-support": "^0.5.16"
  }
}

node_modulesディレクトリを削除し、再度インストールします。これでエラーが解消され、ビルドも可能になりました。(CDKのバージョンを最初から上げていれば、このようなことはしなくても良かったかもしれません…)

$ rm -rf node_modules
$ npm install

lib/test-asg-stack.tsを編集します。以下の点に留意して作成しました。

  • 予めAWSのコンソールでTestAsgEnvという名前のキーペアを作成しておき、SSHでログインできるようにする。
  • EC2起動時にsudo yum update -yが実行されるようにする。
  • CPU使用率が50%でスケールアウトするようにする。
  • スタック作成完了時にALBのDNS名が表示されるようにする。
lib/test-asg-stack.ts
import * as cdk from '@aws-cdk/core';
import * as autoscaling from '@aws-cdk/aws-autoscaling';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';

export class TestAsgStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        const cidr = '10.0.0.0/16';

        const vpc = new ec2.Vpc(
            this,
            'test-asg-vpc', {
            cidr,
            natGateways: 1,
            subnetConfiguration: [
                {
                    cidrMask: 18,
                    name: 'public',
                    subnetType: ec2.SubnetType.PUBLIC,
                },
                {
                    cidrMask: 18,
                    name: 'private',
                    subnetType: ec2.SubnetType.PRIVATE,
                },
            ],
        }
        );

        const securityGroup = new ec2.SecurityGroup(
            this,
            'test-asg-security-group',
            {
                vpc,
                securityGroupName: 'test-asg-security-group',
                allowAllOutbound: true
            }
        );

        securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'SSH from anywhere');
        securityGroup.addEgressRule(ec2.Peer.anyIpv4(), ec2.Port.allTraffic());

        const userData = ec2.UserData.forLinux();
        userData.addCommands('sudo yum update -y');

        const alb = new elbv2.ApplicationLoadBalancer(this, 'TestAsgALB', {
            vpc,
            vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
            internetFacing: true,
            loadBalancerName: 'test-asg-alb'
        });

        const asg = new autoscaling.AutoScalingGroup(this, 'TestAutoScalingGroup', {
            vpc,
            instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.MICRO),
            machineImage: new ec2.AmazonLinuxImage(),
            desiredCapacity: 1,
            maxCapacity: 3,
            keyName: 'TestAsgEnv',
            associatePublicIpAddress: true,
            vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
            userData,
        });

        asg.addSecurityGroup(securityGroup);

        asg.scaleOnCpuUtilization('CPU50Percent', {
            targetUtilizationPercent: 50
        });

        const listener = alb.addListener('Listener', {
            port: 80
        });

        listener.addTargets('Target', {
            targets: [asg],
            port: 80
        });

        new cdk.CfnOutput(this, 'Application LoadBalancer DNS', { value: alb.loadBalancerDnsName });
    }
}

デプロイ

アプリケーションをビルドします。

$ npm run build

AWS CloudFormationのスタックをデプロイします。デプロイが完了するまで待ちます。

$ cdk deploy

Auto Scalingのテスト

CloudWatch Alarmに2つ登録されていました。もっと細かく設定すれば、条件をカスタマイズできると思います。
スクリーンショット 2020-06-21 11.10.51.png

Chaos EngineeringのツールであるGremlinでEC2にCPUの負荷(CPU 51%の攻撃を300秒間実行)を与えてみたところ、無事にスケールアウトできました。
(結果のスクリーンショットを取るのを忘れていました… 既に環境は削除済みで、GremlinのFreeアカウントの制約でターゲット数が限られているようなので事象の再現は控えます…)

おわりに

CloudFormationのyamlファイルよりも、コードで記述できるため作りやすかったですね。
ただ、思いの外「これだ!」といえる情報を見つけられなかったので、公式の仕様のドキュメントを眺めてパラメータを推測しながら、そしてIDEの補完を駆使してゴリ押ししながら作成しました。
EKSの環境もCDKで作れるので、こういうものにもチャレンジしていきたいです。

参考

AWS CDKでAutoScaling環境を作成(Single Stack / Nested Stack)
Deploy your Auto-Scaling Stack with AWS-CDK
Gremlinを使ってCPU負荷を注入する

21emon
Software/Cloud Developerです。自動車業界のTier1で働いてます。いろいろ勉強しています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした