AWS CDKでリソース作成 第5回: Route 53編

こんにちは。 エキサイト株式会社で内定者アルバイトをしています。

今回も、実際のリソースの作成を行いたいと思います。第5回は、Route 53です。

前の記事 tech.excite.co.jp

Route 53とは?

AWSの公式ドキュメントによると、可用性と拡張性に優れたクラウドドメインネームシステム(DNSWebサービスであり、www.example.comのような名前を、IPアドレスに変換するシステムです。デベロッパや企業がエンドユーザをインターネットアプリケーションにルーティングする、信頼性が高く、効率の良い方法になるように設計されているようです。
そして、Amazon EC2インスタンス、Elastic Balancing ロードバランサーAmazon S3バケットなどのAWSで実行するインフラストラクチャにユーザリクエストを効率的に接続してくれるようです。

Route 53にはDNSのエントリをレコードとして追加していきます。これらのレコードを保持するコンテナをホストゾーンと呼び、インターネットでどのようにルーティングするかを指定するレコードを保持するホストゾーンをパブリックホストゾーン、VPC内でどのようにルーティングするかを指定するレコードを保持するホストゾーンをプライベートホストゾーンと呼びます。

レコードの種類として多くのものがありますが、よく使われるのは以下の5つかと思います。

  • A record type
    • Webサーバのようなリソースに対するルーティングのためのレコードで、ドット付き10進記法でIPv4アドレスを指定します。
  • AAAA record type
    • Webサーバのようなリソースに対するルーティングのためのレコードで、colon-separated hexadecimal format(コロン区切り16進記法)でIPv6アドレスを指定します。
  • CNAME record type
  • NS record type
    • ホストゾーンのためのネームサーバを特定するために使用します。(自動で作成されます)
  • SOA record type
    • ドメインに関する情報と対応するAmazon Route 53ホストゾーンに関する情報を提供します。(自動で作成されます)

実際に作成してみる

ここでは、プライベートホストゾーンを作成し、そこに2つのEC2インスタンスに対するA recordを作成します。

前回までのコード

前回は、RDSのデータベースクラスタとデータベースインスタンスを作成しました。

ただし、今回はRDS関連のリソースは使用しませんので、第3回までのコードを示します。

package com.myorg;

import software.amazon.awscdk.CfnTag;
import software.amazon.awscdk.Duration;
import software.amazon.awscdk.services.ec2.CfnInternetGateway;
import software.amazon.awscdk.services.ec2.CfnVPCGatewayAttachment;
import software.amazon.awscdk.services.ec2.IMachineImage;
import software.amazon.awscdk.services.ec2.Instance;
import software.amazon.awscdk.services.ec2.InstanceClass;
import software.amazon.awscdk.services.ec2.InstanceSize;
import software.amazon.awscdk.services.ec2.InstanceType;
import software.amazon.awscdk.services.ec2.MachineImage;
import software.amazon.awscdk.services.ec2.Peer;
import software.amazon.awscdk.services.ec2.Port;
import software.amazon.awscdk.services.ec2.PrivateSubnet;
import software.amazon.awscdk.services.ec2.PublicSubnet;
import software.amazon.awscdk.services.ec2.SecurityGroup;
import software.amazon.awscdk.services.ec2.SubnetSelection;
import software.amazon.awscdk.services.ec2.Vpc;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;

import java.util.List;

public class MyProjectStack extends Stack {
    public MyProjectStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyProjectStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Vpc vpc = Vpc.Builder.create(this, "SampleVPC")
                .vpcName("sample-vpc")
                .cidr("cidr")
                .subnetConfiguration(List.of())
                .build();

        // Availability zone: ap-northeast-1a
        PrivateSubnet privateSubnetA = PrivateSubnet.Builder.create(this, "PrivateSubnetA")
                .availabilityZone("ap-northeast-1a")
                .vpcId(vpc.getVpcId())
                .cidrBlock("cidr")
                .build();

        PublicSubnet publicSubnetA = PublicSubnet.Builder.create(this, "PublicSubnetA")
                .availabilityZone("ap-northeast-1a")
                .vpcId(vpc.getVpcId())
                .cidrBlock("cidr")
                .build();

        // Availability zone: ap-northeast-1c
        PrivateSubnet privateSubnetB = PrivateSubnet.Builder.create(this, "PrivateSubnetB")
                .availabilityZone("ap-northeast-1c")
                .vpcId(vpc.getVpcId())
                .cidrBlock("cidr")
                .build();

        PublicSubnet publicSubnetB = PublicSubnet.Builder.create(this, "PublicSubnetB")
                .availabilityZone("ap-northeast-1c")
                .vpcId(vpc.getVpcId())
                .cidrBlock("cidr")
                .build();

        // NAT gatewayの追加
        publicSubnetA.addNatGateway();
        publicSubnetB.addNatGateway();

        // Internet Gateway
        CfnInternetGateway internetGateway = CfnInternetGateway.Builder.create(this, "InternetGateway")
                .tags(List.of(CfnTag.builder()
                        .key("Name")
                        .value("sample-internet-gateway")
                        .build()))
                .build();

        // Internet gatewayをVPCにattachする
        CfnVPCGatewayAttachment.Builder.create(this, "VpcGateAwayAttachment")
                .vpcId(vpc.getVpcId())
                .internetGatewayId(internetGateway.getAttrInternetGatewayId())
                .build();

        SecurityGroup securityGroup = SecurityGroup.Builder.create(this, "SampleSecurityGroup")
                .securityGroupName("sample-security-group")
                .vpc(vpc)
                .build();

        IMachineImage machineImage = MachineImage.latestAmazonLinux();

        Instance instance01 = Instance.Builder.create(this, "ec2-instance-01")
                .instanceName("ec2-instance-01")
                .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetA)).build())
                .machineImage(machineImage)
                .vpc(vpc)
                .availabilityZone("ap-northeast-1a")
                .instanceType(InstanceType.of(InstanceClass.T2, InstanceSize.SMALL))
                .securityGroup(securityGroup)
                .build();

        Instance instance02 = Instance.Builder.create(this, "ec2-instance-02")
                .instanceName("ec2-instance-02")
                .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetB)).build())
                .machineImage(machineImage)
                .vpc(vpc)
                .availabilityZone("ap-northeast-1c")
                .instanceType(InstanceType.of(InstanceClass.T2, InstanceSize.SMALL))
                .securityGroup(securityGroup)
                .build();

        // Load Balancer
        // Load Balancer用のSecurity Group
        SecurityGroup securityGroupEC2LB = SecurityGroup.Builder.create(this, "SecurityGroupEC2LoadBalancer")
                .securityGroupName("ec2-lb-security-group")
                .vpc(vpc)
                .description("security-group-of-ec2-load-balancer")
                .allowAllOutbound(true)
                .build();

        securityGroupEC2LB.addIngressRule(Peer.ipv4("0.0.0.0/0"), Port.tcp(80));

        ApplicationLoadBalancer applicationLoadBalancer = ApplicationLoadBalancer.Builder.create(this, "SampleEC2ApplicationLoadBalancer")
                .loadBalancerName("sample-ec2-load-balancer")
                .deletionProtection(false)
                .vpc(vpc)
                .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetA, privateSubnetB)).build())
                .securityGroup(securityGroupEC2LB)
                .ipAddressType(IpAddressType.IPV4)
                .idleTimeout(Duration.seconds(120))
                .http2Enabled(true)
                .build();

        ApplicationTargetGroup ec2TargetGroup = ApplicationTargetGroup.Builder.create(this, "EC2TargetGroup")
                .targetGroupName("ec2-target-group")
                .targetType(TargetType.INSTANCE)
                .port(80)
                .vpc(vpc)
                .targets(List.of(new InstanceTarget(instance01, 80), new InstanceTarget(instance02, 80)))
                .protocol(ApplicationProtocol.HTTP)
                .protocolVersion(ApplicationProtocolVersion.HTTP1)
                .healthCheck(HealthCheck.builder()
                        .enabled(true)
                        .path("/")
                        .healthyThresholdCount(3)
                        .unhealthyThresholdCount(2)
                        .interval(Duration.seconds(60))
                        .timeout(Duration.seconds(5))
                        .protocol(Protocol.HTTP)
                        .port("80")
                        .build())
                .build();

        ApplicationListener applicationListener = ApplicationListener.Builder.create(this, "ApplicationListener")
                .loadBalancer(applicationLoadBalancer)
                .protocol(ApplicationProtocol.HTTP)
                .port(80)
                .defaultAction(ListenerAction.forward(List.of(ec2TargetGroup)))
                .build();
    }
}

Route 53リソースとそれにレコードを追加するコード

        PrivateHostedZone privateHostedZone = PrivateHostedZone.Builder.create(this, "SamplePrivateHostedZone")
                .zoneName("your.zone.name")
                .vpc(vpc)
                .comment("private-sample")
                .build();
        
        ARecord.Builder.create(this, "EC2ARecord01")
                .zone(privateHostedZone)
                .recordName("ec2-instance-01.your.zone.name")
                .target(RecordTarget.fromIpAddresses(instance01.getInstancePrivateIp()))
                .ttl(Duration.seconds(3600))
                .build();

        ARecord.Builder.create(this, "EC2ARecord02")
                .zone(privateHostedZone)
                .recordName("ec2-instance-02.your.zone.name")
                .target(RecordTarget.fromIpAddresses(instance02.getInstancePrivateIp()))
                .ttl(Duration.seconds(3600))
                .build();

your.zone.nameには、ご自身が使用するドメイン名を指定してください。

今回はかなりシンプルですね。

参考

同様にCNAME recordも以下のように登録できます。

        CnameRecord.Builder.create(this, "CnameRecord")
                .zone(privateHostedZone)
                .recordName("sample-cname.your.zone.name")
                .domainName("other-domain.co.jp")
                .ttl(Duration.seconds(600))
                .build();

デプロイ

ここまで、記述できればあとはデプロイするだけです。

cdk deploy

エラーなくデプロイできれば成功です。

終わりに

今回は、AWS CDKを用いてRoute 53のホストゾーンとレコードを作成しました。

では、また次回。

次の記事 tech.excite.co.jp

参考文献