나 한테 알려줘! (3부: CDK를 사용한 Transit Gateway)

개요



Transit Gateway (TGW) is a relatively new thing on AWS 그러나 특히 더 복잡한 토폴로지(예: 서로 다른 AWS 지역 및 계정에 걸쳐 있는 수십 또는 수백 개의 VPC)의 경우 네트워킹이 크게 단순화되었습니다.

간단히 말해서 acts as a highly scalable cloud router . A single TGW can support up to 5,000 attachments , 여기서 연결은 VPC, Direct Connect Gateway (DXGW) , VPN 연결 또는 다른 TGW에 대한 피어링 연결일 수 있습니다.

TGW와 VPC 간의 트래픽은 물론 리전 간 트래픽도 계속 유지됩니다the AWS backbone network.

TGW(메시 네트워크, 허브 앤 스포크 네트워크, 공유 서비스가 포함된 격리된 VPC 등)를 사용하기 위한 다양한 시나리오가 있으며 TGW를 사용하지 않고 구축하는 것은 사실상 불가능합니다enterprise-grade infrastructure on AWS.

비용 측면에서 두 가지 비용을 지불합니다: the number of attachments to a TGW (there's an hourly rate) and data transfer .

구현





이 시리즈의 공통 주제인 것처럼 두 개의 VPC를 함께 연결하여 두 VPC에 배치된 EC2 인스턴스 간에 성공적인 ping을 수행할 것입니다. 이번에는 TGW(Transit Gateway)가 접착제가 될 것입니다.

NET에서 생성한 VpcStackInstanceStack 클래스를 다시 한 번 재사용합니다. 또한 두 개의 클래스를 생성합니다. 둘 다 하나의 파일에 존재합니다. 하나는 TGW용이고 다른 하나는 TGW로 연결되는 경로용입니다.

➜  ping-me-cdk-example$ touch lib/tgw.ts


  • ping-me-cdk-example/lib/tgw.ts

  • import * as ec2 from '@aws-cdk/aws-ec2';
    import * as cdk from '@aws-cdk/core';
    
    interface TransitGatewayProps extends cdk.StackProps {
        vpcs: [ec2.Vpc, ec2.Vpc, ...ec2.Vpc[]]; // <--- a list of VPC objects (at least two are required) to be attached to the Transit Gateway; NB only routes between the first two VPCs will be created
    }
    
    export class TransitGatewayStack extends cdk.Stack {
    
        constructor(scope: cdk.Construct, id: string, props: TransitGatewayProps) {
            super(scope, id, props);
    
            // create a Transit Gateway
            const tgw = new ec2.CfnTransitGateway(this, 'Tgw');
    
            // For each supplied VPC, create a Transit Gateway attachment
            props.vpcs.forEach((vpc, index) => {
                new ec2.CfnTransitGatewayAttachment(this, `TgwVpcAttachment${index}`, {
                    subnetIds: vpc.privateSubnets.map(privateSubnet => privateSubnet.subnetId),
                    transitGatewayId: tgw.ref,
                    vpcId: vpc.vpcId,
                });
            });
    
            // Output the Transit Gateway's ID
            new cdk.CfnOutput(this, 'TransitGatewayId', {
                value: tgw.ref,
                exportName: 'TransitGatewayId',
            });
        }
    }
    
    export class RoutesToTransitGatewayStack extends cdk.Stack {
    
        constructor(scope: cdk.Construct, id: string, props: TransitGatewayProps) {
            super(scope, id, props);
    
            // Add route from the private subnet of the first VPC to the second VPC over the Transit Gateway
            // NB the below was taken from: https://stackoverflow.com/questions/62525195/adding-entry-to-route-table-with-cdk-typescript-when-its-private-subnet-alread
            props.vpcs[0].privateSubnets.forEach(({ routeTable: { routeTableId } }, index) => {
                new ec2.CfnRoute(this, 'RouteFromPrivateSubnetOfVpc1ToVpc2' + index, {
                    destinationCidrBlock: props.vpcs[1].vpcCidrBlock,
                    routeTableId,
                    transitGatewayId: cdk.Fn.importValue('TransitGatewayId'), // Transit Gateway must already exist
                });
            });
    
            // Add route from the private subnet of the second VPC to the first VPC over the Transit Gateway
            props.vpcs[1].privateSubnets.forEach(({ routeTable: { routeTableId } }, index) => {
                new ec2.CfnRoute(this, 'RouteFromPrivateSubnetOfVpc2ToVpc1' + index, {
                    destinationCidrBlock: props.vpcs[0].vpcCidrBlock,
                    routeTableId,
                    transitGatewayId: cdk.Fn.importValue('TransitGatewayId'), // Transit Gateway must already exist
                });
            });
        }
    }
    


    이 네 가지 클래스를 마음대로 사용할 수 있으므로 필요한 스택을 초기화할 수 있습니다.
  • ping-me-cdk-example/bin/ping-me-cdk-example.ts

  • import * as cdk from '@aws-cdk/core';
    import { VpcStack } from '../lib/vpc';
    import { InstanceStack } from '../lib/instance';
    import { PeeringStack } from '../lib/peering';
    import { CustomerGatewayDeviceStack } from '../lib/cgd';
    import { TransitGatewayStack, RoutesToTransitGatewayStack } from '../lib/tgw';
    
    const app = new cdk.App(); // <--- you can read more about the App construct here: https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.App.html
    
    /**
     * CODE FROM "Ping Me! (Part 1: VPC Peering Using CDK)" AND "Ping Me! (Part 2: Site-to-Site VPN Using CDK)" WAS REMOVED FOR VISIBILITY
     */
    
     // Create two VPCs
    const vpcsMetInTransit = new VpcStack(app, 'VpcsMetInTransitStack', {
      vpcSetup: {
        cidrs: ['10.0.4.0/24', '10.0.5.0/24'], // <--- two non-overlapping CIDR ranges for our two VPCs
        maxAzs: 1, // <--- to keep the costs down, we'll stick to 1 availability zone per VPC (obviously, not something you'd want to do in production)
      },
    });
    
    // Create two EC2 instances, one in each VPC
    new InstanceStack(app, 'InstanceTransitStack', {
      vpcs: vpcsMetInTransit.createdVpcs,
    });
    
    // Create a Transit Gateway and attach both VPCs to it
    new TransitGatewayStack(app, 'TransitGatewayStack', {
      vpcs: [vpcsMetInTransit.createdVpcs[0], vpcsMetInTransit.createdVpcs[1]],
    });
    
    // Create routes between both VPCs over the Transit Gateway
    new RoutesToTransitGatewayStack(app, 'RoutesToTransitGatewayStack', {
      vpcs: [vpcsMetInTransit.createdVpcs[0], vpcsMetInTransit.createdVpcs[1]],
    });
    


    배포는 다음 세 단계로 수행됩니다.
  • 먼저 InstanceTransitStack (암시적으로 VpcsMetInTransitStack 와 함께).

  • (이 단계에서 원본 EC2 인스턴스의 ID와 대상 EC2 인스턴스의 프라이빗 IP를 가져올 수 있습니다.
    둘 다 서로 ping을 시도할 때 조금 있으면 유용합니다.)

    ➜  ping-me-cdk-example$ cdk deploy InstanceTransitStack --require-approval never
    Including dependency stacks: VpcsMetInTransitStack
    VpcsMetInTransitStack
    VpcsMetInTransitStack: deploying...
    VpcsMetInTransitStack: creating CloudFormation changeset...
    [██████████████████████████████████████████████████████████] (30/30)
    
     ✅  VpcsMetInTransitStack
    
    Outputs:
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30CidrBlockB8164F9E = 10.0.4.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30DefaultSecurityGroup52C351BF = sg-0b97deeeacfd5627d
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BCidrBlock933A5AA8 = 10.0.5.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BDefaultSecurityGroup87C47BC2 = sg-0dfe3c25a0cd42e84
    VpcsMetInTransitStack.ExportsOutputRefVpc07C831B304FE08623 = vpc-063e0aaa8aaf32b32
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1RouteTableB5C6777D52F53FE8 = rtb-053b342d2f9950c58
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1SubnetD6383522ACB05B9B = subnet-002286581738a15da
    VpcsMetInTransitStack.ExportsOutputRefVpc1C211860B64169B74 = vpc-0020c0197873df61c
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1RouteTable339A93B3DFC75FCA = rtb-0b3cac3abd02c16d6
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1Subnet41967AFDFF883DAB = subnet-0c10da57ee874b680
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/VpcsMetInTransitStack/a54b7350-3560-11eb-ae91-0643678755c5
    InstanceTransitStack
    InstanceTransitStack: deploying...
    InstanceTransitStack: creating CloudFormation changeset...
    [██████████████████████████████████████████████████████████] (10/10)
    
     ✅  InstanceTransitStack
    
    Outputs:
    InstanceTransitStack.Instance0BastionHostId1959CA92 = i-03d7c391c35302d4a # <--- COPY THE ID OF YOUR SOURCE EC2 INSTANCE!
    InstanceTransitStack.Instance0PrivateIp = 10.0.4.58
    InstanceTransitStack.Instance1BastionHostIdEF2AA144 = i-0d315dbb89ed80f82
    InstanceTransitStack.Instance1PrivateIp = 10.0.5.54 # <--- COPY THE PRIVATE IP OF YOUR DESTINATION EC2 INSTANCE!
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/InstanceTransitStack/11f0b6a0-3561-11eb-842c-0aa13688a741
    


  • 다음 TransitGatewayStack

  • ➜  ping-me-cdk-example$ cdk deploy TransitGatewayStack --require-approval never
    Including dependency stacks: VpcsMetInTransitStack
    VpcsMetInTransitStack
    VpcsMetInTransitStack: deploying...
    
     ✅  VpcsMetInTransitStack (no changes)
    
    Outputs:
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30CidrBlockB8164F9E = 10.0.4.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30DefaultSecurityGroup52C351BF = sg-0b97deeeacfd5627d
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BCidrBlock933A5AA8 = 10.0.5.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BDefaultSecurityGroup87C47BC2 = sg-0dfe3c25a0cd42e84
    VpcsMetInTransitStack.ExportsOutputRefVpc07C831B304FE08623 = vpc-063e0aaa8aaf32b32
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1RouteTableB5C6777D52F53FE8 = rtb-053b342d2f9950c58
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1SubnetD6383522ACB05B9B = subnet-002286581738a15da
    VpcsMetInTransitStack.ExportsOutputRefVpc1C211860B64169B74 = vpc-0020c0197873df61c
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1RouteTable339A93B3DFC75FCA = rtb-0b3cac3abd02c16d6
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1Subnet41967AFDFF883DAB = subnet-0c10da57ee874b680
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/VpcsMetInTransitStack/a54b7350-3560-11eb-ae91-0643678755c5
    TransitGatewayStack
    TransitGatewayStack: deploying...
    TransitGatewayStack: creating CloudFormation changeset...
    [██████████████████████████████████████████████████████████] (5/5)
    
     ✅  TransitGatewayStack
    
    Outputs:
    TransitGatewayStack.TransitGatewayId = tgw-057de86d7c789626e
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/TransitGatewayStack/e86b7b70-3561-11eb-b82a-0ad12ebbcfd9
    


  • 그리고 마지막으로 RoutesToTransitGatewayStack

  • ➜  ping-me-cdk-example$ cdk deploy RoutesToTransitGatewayStack --require-approval never
    Including dependency stacks: VpcsMetInTransitStack
    VpcsMetInTransitStack
    VpcsMetInTransitStack: deploying...
    
     ✅  VpcsMetInTransitStack (no changes)
    
    Outputs:
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30CidrBlockB8164F9E = 10.0.4.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc07C831B30DefaultSecurityGroup52C351BF = sg-0b97deeeacfd5627d
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BCidrBlock933A5AA8 = 10.0.5.0/24
    VpcsMetInTransitStack.ExportsOutputFnGetAttVpc1C211860BDefaultSecurityGroup87C47BC2 = sg-0dfe3c25a0cd42e84
    VpcsMetInTransitStack.ExportsOutputRefVpc07C831B304FE08623 = vpc-063e0aaa8aaf32b32
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1RouteTableB5C6777D52F53FE8 = rtb-053b342d2f9950c58
    VpcsMetInTransitStack.ExportsOutputRefVpc0privateSubnet1SubnetD6383522ACB05B9B = subnet-002286581738a15da
    VpcsMetInTransitStack.ExportsOutputRefVpc1C211860B64169B74 = vpc-0020c0197873df61c
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1RouteTable339A93B3DFC75FCA = rtb-0b3cac3abd02c16d6
    VpcsMetInTransitStack.ExportsOutputRefVpc1privateSubnet1Subnet41967AFDFF883DAB = subnet-0c10da57ee874b680
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/VpcsMetInTransitStack/a54b7350-3560-11eb-ae91-0643678755c5
    RoutesToTransitGatewayStack
    RoutesToTransitGatewayStack: deploying...
    RoutesToTransitGatewayStack: creating CloudFormation changeset...
    [██████████████████████████████████████████████████████████] (4/4)
    
     ✅  RoutesToTransitGatewayStack
    
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:REDACTED:stack/RoutesToTransitGatewayStack/d803d8d0-3562-11eb-aaeb-02e586bc56f0
    


    확인



    이제 핑을 풀어볼 시간입니다!

    따라하는 경우 아래를 실행하기 전에 원본 EC2 인스턴스( i-03d7c391c35302d4a )의 ID와 대상 EC2 인스턴스(10.0.5.54 )의 프라이빗 IP를 적절한 값으로 교체해야 합니다.

    aws ssm send-command \
    --document-name "AWS-RunShellScript" \
    --document-version "1" \
    --targets '[{"Key":"InstanceIds","Values":["i-03d7c391c35302d4a"]}]' \
    --parameters '{"workingDirectory":[""],"executionTimeout":["3600"],"commands":["ping 10.0.5.54 -c 3"]}' \
    --timeout-seconds 600 \
    --max-concurrency "50" \
    --max-errors "0"
    {
        "Command": {
            "CommandId": "f7ed8e0e-a313-405a-a811-7885b4d532e7",
            "DocumentName": "AWS-RunShellScript",
            "DocumentVersion": "1",
            "Comment": "",
            "ExpiresAfter": "2020-12-03T15:06:43.691000+01:00",
            "Parameters": {
                "commands": [
                    "ping 10.0.5.54 -c 3"
                ],
                "executionTimeout": [
                    "3600"
                ],
                "workingDirectory": [
                    ""
                ]
            },
            "InstanceIds": [],
            "Targets": [
                {
                    "Key": "InstanceIds",
                    "Values": [
                        "i-03d7c391c35302d4a"
                    ]
                }
            ],
            "RequestedDateTime": "2020-12-03T13:56:43.691000+01:00",
            "Status": "Pending",
            "StatusDetails": "Pending",
            "OutputS3BucketName": "",
            "OutputS3KeyPrefix": "",
            "MaxConcurrency": "50",
            "MaxErrors": "0",
            "TargetCount": 0,
            "CompletedCount": 0,
            "ErrorCount": 0,
            "DeliveryTimedOutCount": 0,
            "ServiceRole": "",
            "NotificationConfig": {
                "NotificationArn": "",
                "NotificationEvents": [],
                "NotificationType": ""
            },
            "CloudWatchOutputConfig": {
                "CloudWatchLogGroupName": "",
                "CloudWatchOutputEnabled": false
            },
            "TimeoutSeconds": 600
        }
    }
    


    이제 AWS CLI aws ssm get-command-invocation command를 사용하여 성공했는지 확인하겠습니다.

    다시 말하지만, 따라하는 경우 아래를 실행하기 전에 명령 ID( f7ed8e0e-a313-405a-a811-7885b4d532e7 )와 소스 EC2 인스턴스의 ID( i-03d7c391c35302d4a )를 적절한 값으로 바꿔야 합니다.

    ➜  ping-me-cdk-example$ aws ssm get-command-invocation --command-id f7ed8e0e-a313-405a-a811-7885b4d532e7 --instance-id i-03d7c391c35302d4a
    {
        "CommandId": "f7ed8e0e-a313-405a-a811-7885b4d532e7",
        "InstanceId": "i-03d7c391c35302d4a",
        "Comment": "",
        "DocumentName": "AWS-RunShellScript",
        "DocumentVersion": "1",
        "PluginName": "aws:runShellScript",
        "ResponseCode": 0,
        "ExecutionStartDateTime": "2020-12-03T12:56:44.343Z",
        "ExecutionElapsedTime": "PT2.044S",
        "ExecutionEndDateTime": "2020-12-03T12:56:46.343Z",
        "Status": "Success",
        "StatusDetails": "Success",
        "StandardOutputContent": "PING 10.0.5.54 (10.0.5.54) 56(84) bytes of data.\n64 bytes from 10.0.5.54: icmp_seq=1 ttl=254 time=0.489 ms\n64 bytes from 10.0.5.54: icmp_seq=2 ttl=254 time=0.311 ms\n64 bytes from 10.0.5.54: icmp_seq=3 ttl=254 time=0.306 ms\n\n--- 10.0.5.54 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2027ms\nrtt min/avg/max/mdev = 0.306/0.368/0.489/0.087 ms\n",
        "StandardOutputUrl": "",
        "StandardErrorContent": "",
        "StandardErrorUrl": "",
        "CloudWatchOutputConfig": {
            "CloudWatchLogGroupName": "",
            "CloudWatchOutputEnabled": false
        }
    }
    

    3 packets transmitted, 3 received, 0% packet loss . 그것은 놀라운 성공입니다!

    대청소



    지갑을 위해 모든 것을 마무리하기 전에 현재 인프라를 즉시 파괴합시다.

    구축 과정의 경우와 마찬가지로 파괴 부분도 단계적으로 수행되어야 합니다.
  • 먼저 Transit Gateway에 대한 경로를 제거해야 합니다. 메시지가 나타나면 y를 입력합니다(예).

  • ➜  ping-me-cdk-example$ cdk destroy RoutesToTransitGatewayStack
    Are you sure you want to delete: RoutesToTransitGatewayStack (y/n)? y
    RoutesToTransitGatewayStack: destroying...
    14:08:49 | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack | RoutesToTransitGatewayStack
    14:08:51 | DELETE_IN_PROGRESS   | AWS::EC2::Route    | RouteFromPrivateSubnetOfVpc2ToVpc10
     ✅  RoutesToTransitGatewayStack: destroyed
    


  • 경로가 제거되면 나머지 스택을 안전하게 삭제할 수 있습니다. 메시지가 나타나면 y를 입력합니다(예).

  • ➜  ping-me-cdk-example$ cdk destroy --all
    Are you sure you want to delete: InstanceVpnDestinationStack, VpcVpnDestinationStack, TransitGatewayStack, RoutesToTransitGatewayStack, PeeringStack, InstanceTransitStack, InstancePeersStack, CustomerGatewayDeviceStack, VpcsMetInTransitStack, VpcVpnSourceStack, VpcPeersStack (y/n)? y
    InstanceVpnDestinationStack: destroying...
     ✅  InstanceVpnDestinationStack: destroyed
    VpcVpnDestinationStack: destroying...
     ✅  VpcVpnDestinationStack: destroyed
    TransitGatewayStack: destroying...
    14:12:23 | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack         | TransitGatewayStack
     ✅  TransitGatewayStack: destroyed
    RoutesToTransitGatewayStack: destroying...
     ✅  RoutesToTransitGatewayStack: destroyed
    PeeringStack: destroying...
     ✅  PeeringStack: destroyed
    InstanceTransitStack: destroying...
    14:15:30 | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack | InstanceTransitStack
     ✅  InstanceTransitStack: destroyed
    InstancePeersStack: destroying...
     ✅  InstancePeersStack: destroyed
    CustomerGatewayDeviceStack: destroying...
     ✅  CustomerGatewayDeviceStack: destroyed
    VpcsMetInTransitStack: destroying...
    14:16:49 | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack            | VpcsMetInTransitStack
    14:18:56 | DELETE_IN_PROGRESS   | AWS::EC2::InternetGateway             | Vpc0/IGW
    14:18:56 | DELETE_IN_PROGRESS   | AWS::EC2::VPC                         | Vpc0
     ✅  VpcsMetInTransitStack: destroyed
    VpcVpnSourceStack: destroying...
     ✅  VpcVpnSourceStack: destroyed
    VpcPeersStack: destroying...
     ✅  VpcPeersStack: destroyed
    


    결론



    이 기사 시리즈에서는 어떻게 비교적 쉽게 사용Cloud Development Kit (CDK)하여 다양한 AWS 리소스를 생성, 업데이트 및 삭제하고 요구 사항에 가장 적합한 구성으로 모두 결합할 수 있는지 살펴보았습니다.

    IaC(Infrastructure as Code)와 같은 것들에 신경을 쓰는 이유에 대해 논의했습니다.

    에서 우리는 첫 번째 클래스를 작성한 다음 스택으로 초기화했습니다.

    AWS CLI를 통한 추가 구성이 필요한 보다 복잡한 구조에 중점을 두었습니다.

    마지막 부분은 가장 멋진 AWS 네트워크 리소스 중 하나인 TGW(Transit Gateway)를 중심으로 이루어졌습니다.

    all of the code is available on GitHub 을 기억하십시오.

    "그게 다야!"즐겁게 읽으셨기를 바라며 다음 시간까지!

    좋은 웹페이지 즐겨찾기