0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redshift ServerlessをCDKでデプロイするまでの手順を整理した

0
Last updated at Posted at 2026-04-05

背景・目的

以前、Redshift Serverlessをマネコンから試してみましたが、今回はCDKで作る機会があったので、その時のメモになります。

実践

下記の手順で作成しました。

前提

1.CDKをインストールします

npm install -g aws-cdk

2.インストールされたかバージョンを確認します

% cdk --version
2.1117.0 (build 1fe7075)
% 

CDKプロジェクトの初期化

1.CDKプロジェクトを初期化します

cdk init app --language typescript
・・・
✅ All done!

2.以下のファイルが生成されます。

infra/
  ├── bin/
  │     └── infra.ts              ← エントリポイント
  ├── lib/
  │     ├── infra-stack.ts        ← Stack定義
  ├── test/
  │     └── infra.test.ts         ← テスト雛形
  ├── cdk.json                    ← CDK設定
  ├── tsconfig.json               ← TypeScript設定
  ├── package.json                ← 依存パッケージ
  ├── package-lock.json
  ├── jest.config.js
  ├── .gitignore
  ├── .npmignore
  └── README.md

lib下のStack定義を修正

  1. ファイルをリネームします(任意)
  2. 下記のように書き換えます
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless';

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

    const env = 'dev';

    // VPC with 3 private subnets (3 AZs)
    const vpc = new ec2.Vpc(this, 'Vpc', {
      ipAddresses: ec2.IpAddresses.cidr('XX.XX.0.0/16'),
      maxAzs: 3,
      natGateways: 0,
      subnetConfiguration: [
        {
          name: 'Private',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 24,
        },
      ],
    });

    // VPC Endpoints
    vpc.addInterfaceEndpoint('SecretsManagerEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
    });
    vpc.addGatewayEndpoint('S3Endpoint', {
      service: ec2.GatewayVpcEndpointAwsService.S3,
    });

    // Security Group for Redshift
    const sg = new ec2.SecurityGroup(this, 'RedshiftSg', {
      vpc,
      description: 'SG for Redshift Serverless',
      allowAllOutbound: true,
    });
    sg.addIngressRule(sg, ec2.Port.tcp(5439), 'Self reference');

    // Redshift Serverless Namespace
    const namespace = new redshiftserverless.CfnNamespace(this, 'Namespace', {
      namespaceName: `${env}-redshift-namespace`,
      dbName: `${env}-redshift-db`,
      adminUsername: 'xxxxx',
      manageAdminPassword: true,
      logExports: ['userlog', 'connectionlog', 'useractivitylog'],
    });

    // Redshift Serverless Workgroup
    const workgroup = new redshiftserverless.CfnWorkgroup(this, 'Workgroup', {
      workgroupName: `${env}-redshift-workgroup`,
      namespaceName: namespace.namespaceName,
      baseCapacity: 4,
      maxCapacity: 1024,
      port: 5439,
      publiclyAccessible: false,
      enhancedVpcRouting: true,
      securityGroupIds: [sg.securityGroupId],
      subnetIds: vpc.isolatedSubnets.map(s => s.subnetId),
    });
    workgroup.addDependency(namespace);
  }
}

下記を作成しています。

  • VPC

    • CIDR: XX.XX.0.0/16
    • AZは3つ
    • PrivateサブネットのCIDR:/24で自動生成される
    • NAT Gateway:なし
    • IGW:なし
    • ルートはローカルのみ。SubnetTypeのパラメータは下記の通り
      SubnetType ルートテーブルに追加されるルート
      PUBLIC IGWへのルート
      PRIVATE_WITH_EGRESS NAT GWへのルート
      PRIVATE_ISOLATED ローカルのみ(何も追加しない)
  • VPCe

    • SecretsManagerにパスワードを保存するため
    • EVR有効時にS3へVPC経由でアクセスするため
  • セキュリティグループ

    • Outbound: 制御無し
    • Inbound: 5439のみ許可
  • Redshift Serverless Namespace

    • manageAdminPassword: True。SecretsManagerでパスワードを自動で生成
    • logExports: 監査ログをCloudWatch Logsにエクスポートする
    ログ種別 内容
    userlog ユーザーの作成・削除・権限変更などの操作記録
    connectionlog 接続・切断の記録(誰がいつ接続したか)
    useractivitylog 実行されたSQL文の記録
  • Redshift Serverless Workgroup

    • baseCapacity: 4(クエリ時の最小RPU、アイドル時は0)、maxCapacity: 1024(スケール上限)
    • ポート:5439
    • セキュリティグループ:作成済みのSG
    • subnetIds:作成したサブネットID
    • enhancedVpcRouting: true
      • S3等へのデータアクセスをVPC経由に強制する
      • セキュリティグループやVPC Flow Logsで通信を制御・監視可能
      • 有効時はS3用VPC Endpoint等が必要

bin下のエントリポイントを修正

  1. ファイルをリネームします(任意)
  2. 下記のように書き換えます
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib/core';
import { RedshiftDevStack } from '../lib/redshift-dev-stack';

const app = new cdk.App();
new RedshiftDevStack(app, 'RedshiftDevStack', {
  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION || 'ap-northeast-1',
  },
});

testコードの修正

  1. ファイルをリネームします(任意)
  2. 下記のように書き換えます
import * as cdk from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { RedshiftDevStack } from '../lib/redshift-dev-stack';

const app = new cdk.App();
const stack = new RedshiftDevStack(app, 'TestStack');
const template = Template.fromStack(stack);

test('VPC created with correct CIDR', () => {
  template.hasResourceProperties('AWS::EC2::VPC', {
    CidrBlock: 'XX.XX.0.0/16',
  });
});

test('3 private subnets created', () => {
  template.resourceCountIs('AWS::EC2::Subnet', 3);
});

test('Redshift Namespace created', () => {
  template.hasResourceProperties('AWS::RedshiftServerless::Namespace', {
    NamespaceName: 'dev-redshift-namespace',
    DbName: 'dev-redshift-db',
    AdminUsername: 'XXXX',
    ManageAdminPassword: true,
  });
});

test('Redshift Workgroup created', () => {
  template.hasResourceProperties('AWS::RedshiftServerless::Workgroup', {
    WorkgroupName: 'dev-redshift-workgroup',
    BaseCapacity: 4,
    MaxCapacity: 1024,
    Port: 5439,
    PubliclyAccessible: false,
  });
});

test('Security Group has self-reference ingress on 5439', () => {
  template.hasResourceProperties('AWS::EC2::SecurityGroup', {
    GroupDescription: 'SG for Redshift Serverless',
    SecurityGroupName: 'dev-sg-redshift',
  });
});

test('Secrets Manager VPC Endpoint created', () => {
  template.hasResourceProperties('AWS::EC2::VPCEndpoint', {
    ServiceName: { 'Fn::Join': ['', ['com.amazonaws.', { Ref: 'AWS::Region' }, '.secretsmanager']] },
  });
});

test('No NAT Gateway', () => {
  template.resourceCountIs('AWS::EC2::NatGateway', 0);
});

3.テストを実行します

npm test

4.いくつかエラーになりました。Private Subnet、Security Groupが失敗してます

・・・・

FAIL test/redshift-dev-stack.test.ts (6.083 s)
  ✓ VPC created with correct CIDR (7 ms)
  ✕ 3 private subnets created (3 ms)
  ✓ Redshift Namespace created
  ✓ Redshift Workgroup created
  ✕ Security Group has self-reference ingress on 5439 (2 ms)
  ✓ Secrets Manager VPC Endpoint created
  ✓ No NAT Gateway

・・・・

Test Suites: 1 failed, 1 total
Tests:       2 failed, 5 passed, 7 total
Snapshots:   0 total
Time:        6.248 s
Ran all test suites.

5.元のコードを修正(サブネット数3→2、SecurityGroupNameの削除等)して再テストします

npm test

> infra@0.1.0 test
> jest

・・・・

PASS test/redshift-dev-stack.test.ts (5.859 s)
  ✓ VPC created with correct CIDR (7 ms)
  ✓ 3 private subnets created
  ✓ Redshift Namespace created
  ✓ Redshift Workgroup created
  ✓ Security Group has self-reference ingress on 5439 (1 ms)
  ✓ Secrets Manager VPC Endpoint created (1 ms)
  ✓ No NAT Gateway

・・・・

Test Suites: 1 passed, 1 total
Tests:       7 passed, 7 total
Snapshots:   0 total
Time:        6.013 s, estimated 7 s
Ran all test suites.

Cfnテンプレートを生成

1.cdk synthを実行します
2.cdk.outの下にファイルが生成されました

cdk.out/
  ├── RedshiftDevStack.template.json   ← CFnテンプレート本体
  ├── RedshiftDevStack.assets.json     ← アセットのS3アップロード先
  ├── RedshiftDevStack.metadata.json   ← リソースとコードの対応
  ├── manifest.json                    ← スタック一覧・デプロイ順序
  ├── tree.json                        ← コンストラクトツリー
  ├── cdk.out                          ← バージョン情報
  └── asset.7fa1e.../                  ← Lambda関数コード
        ├── index.js                   ← デフォルトSG制限ロジック
        └── __entrypoint__.js          ← Lambda起動ハンドラ

CDKのデプロイ準備(初回のみ)

CDKがAWSにデプロイするために必要なS3バケットやIAMロールを作成します。
アカウント×リージョンごとに初回1回だけ実行が必要です。

1.cdk bootstrapを実行します

cdk bootstrap

Bootstrapping environment aws://XXXXXXXXXX/ap-northeast-1...
・・・
CDKToolkit | 12/12 | 8:54:00 PM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CDKToolkit 
 ✅  Environment aws://XXXXXXXXXX/ap-northeast-1 bootstrapped.

2.CloudFormationで、CDKToolkitスタックがCREATE_COMPLETEになっていました
3.S3の下に、ファイルが出来ていました
image.png

CDKのデプロイ

1.cdk deployを実行します

cdk deploy

Synthesis time: 4.52s

Stack RedshiftDevStack
IAM Statement Changes

・・・・

"--require-approval" is enabled and stack includes security-sensitive updates: 'Do you wish to deploy these changes' (y/n) 
RedshiftDevStack: deploying... [1/1]
RedshiftDevStack: creating CloudFormation changeset...
[██████████████████████▌···································] (7/18)

21:04:07 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack            | RedshiftDevStack
21:04:25 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | Vpc/PrivateSubnet2/RouteTable


✨  Deployment time: 119.1s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXX:stack/RedshiftDevStack/XXXXX

✨  Total time: 123.62s

% 

2.CloudFormationで、RedshiftDevStack スタックがCREATE_COMPLETEになっていました

リソースの確認

デプロイされたか確認します

Redshift

1.下記のコマンドで、namespaceを確認します

aws redshift-serverless list-namespaces --region ap-northeast-1

{
    "namespaces": [
        {
            "adminPasswordSecretArn": "arn:aws:secretsmanager:ap-northeast-1:XXXXXXXXXX:secret:redshift!dev-redshift-namespaceXXXXX",
            "adminUsername": "XXXXXXXXXX",
            "creationDate": "2026-04-05T12:04:11.556000+00:00",
            "dbName": "dev-redshift-db",
            "iamRoles": [],
            "kmsKeyId": "AWS_OWNED_KMS_KEY",
            "logExports": [
                "connectionlog",
                "useractivitylog",
                "userlog"
            ],
            "namespaceArn": "arn:aws:redshift-serverless:ap-northeast-1:XXXXXXXXXX:namespace/XXXXXXXXXX",
            "namespaceId": "XXXXXXXXXX",
            "namespaceName": "dev-redshift-namespace",
            "status": "AVAILABLE"
        }
    ]
}

2.下記のコマンドでworkgroupを確認します

aws redshift-serverless list-workgroups --region ap-northeast-1

{
    "workgroups": [
        {
            "baseCapacity": 4,
            "configParameters": [
                {
・・・・

            "status": "AVAILABLE",
            "subnetIds": [
                "subnet-XXXXX",
                "subnet-XXXXX",
                "subnet-XXXXX"
            ],
        }
    ]
}

クエリを実行する

  1. AWSマネコンからRedshift>サーバレスダッシュボードをクリックします
  2. Redshift Serverlessでクエリエディタ>クエリエディタv2でクエリをクリックします
  3. そのまま「Configure account」をクリックします(AWS管理キーでクエリ等を保存、ローカルからアップする際に使用するS3バケットの指定。)
    image.png
  4. クエリエディタが別タブで開かれます。左側に作成したWorkgroupが表示されるので、クリックします
  5. AWS Secrets Managerを選択し、「Create connection」をクリックします
    image.png

6.下記のクエリを実行します。current_user、current_database、バージョンが表示されました

-- 接続確認
SELECT current_user, current_database(), version();

7.下記のクエリを実行します

-- テスト用テーブル作成
CREATE TABLE public.test_table (
  id INT IDENTITY(1,1),
  name VARCHAR(100),
  created_at TIMESTAMP DEFAULT GETDATE()
);

-- データ挿入
INSERT INTO public.test_table (name) VALUES ('テスト太郎'), ('テスト花子');

-- 確認
SELECT * FROM public.test_table;

8.テーブルができました
image.png

9.Result2を確認します。成功したようです
image.png

10.Result 3を確認します。データとして登録されていました
image.png

考察

下記のようなことがわかりました。

  • CDKのStackに account を指定しないと、maxAzs: 3 と書いてもサブネットが2AZしか作られない。process.env.CDK_DEFAULT_ACCOUNT で動的に取得するのが良い
  • EVRを有効にするとS3等へのアクセスがVPC経由になり、セキュリティグループやFlow Logsで制御・監視できる。

参考

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?