AWS CDK を使用して VPN 接続環境をコード化してみた

Posted on 2019-09-16 in zakki

AWS CDK for python です。

動機

環境をコード化するというところに興味があり、GA される前から Typescirpt で勉強がてら少し触っていたりしていました。さて、何か具体的なものにフォーカスして書いてみたいな、と思い以前作った VPN 接続環境 をコード化してみようと思い立ち書いてみました。言語はそのまま Typescript でもよかったのですが、まずは (Typescript に比べると ) 慣れている Python で書いてみようと。

環境

$ cdk --version
1.8.0 (build 5244f97)
$ python3 --version
Python 3.7.4

構成図

1AZ にパブリックとプライベートサブネットを 1 つずつ作成。パブリックに立てた EC2 インスタンスと自宅の PC を softetherVPN で接続します。今回 softetherVPN 用のインスタンスは事前に作成している AMI を使用しています。プライベートサブネット側のインスタンスは当初の目的では VPN 経由で接続したいインスタンスなどを立てて使用しますが、今回は立てられることを確認するのみなので一般的な AMI を使用します。セキュリティグループはパブリックサブネット側のみに設定します。

書いたもの

https://github.com/paraselene92/aws-cdk-py

結果

$ cdk deploy --profile [profile]
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

Security Group Changes
┌───┬─────────────────┬─────┬────────────┬────────────────────┐
│   │ Group           │ Dir │ Protocol   │ Peer               │
├───┼─────────────────┼─────┼────────────┼────────────────────┤
│ + │ ${mysg.GroupId} │ In  │ TCP 22     │ 192.168.0.0/24     │

( 略 )

cdk deploy でデプロイ。--profile [profile] で認証情報に設定しているプロファイル情報を指定できます。

Do you wish to deploy these changes (y/n)? y
awscdk-py: deploying...
awscdk-py: creating CloudFormation changeset...
  0/15 | 21:18:10 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack            | awscdk-py User Initiated
  0/15 | 21:18:18 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack            | awscdk-py User Initiated
  0/15 | 21:18:21 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | testVpc/IGW (testVpcIGW8765943D) 
  0/15 | 21:18:21 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | testVpc (testVpcCB3A84F3) 

( 略 )

y で進めるとリソースが作成されていきます。良い感じ。

 13/15 | 21:19:32 | CREATE_COMPLETE      | AWS::EC2::Instance                    | softetherInstance 
 14/15 | 21:19:34 | CREATE_COMPLETE      | AWS::EC2::Instance                    | IsolatedInstance 

   awscdk-py

Outputs:
awscdk-py.myPublicInstanceattrprivateip = XXX.XXX.XXX.XXX
awscdk-py.myIsolatedInstanceattrprivateip = YYY.YYY.YYY.YYY
awscdk-py.myPublicInstanceattrpublicip = ZZZ.ZZZ.ZZZ.ZZZ

Stack ARN:
arn:aws:cloudformation:us-east-1:--------------:stack/awscdk-py/---------------------------

できました。Outputs の一部と Stack ARN は消しています。

softether のインスタンスと、プライベートサブネットに作成したインスタンスの作成成功ログがあります。Output も問題なく動作しています。

AWS コンソール側の表示

( インスタンスの名前をつけておけばよかった。。)

こちらもよい感じ。1 つのインスタンスはパブリック IP アドレスが振られていることに対し、もう 1 つはパブリック IP アドレスは振られていない。

感想

aws-ec2.VPC() がよしなにしてくれる所について

aws-ec2.VPC() は最低限の引数定義をして生成すると、cdk がよしなに考えてくれて AWS が想定する高可用性ネットワークを意識した構成を作ります。パブリックサブネットには IGW が割り当てられるのはもちろんですが、プライベートサブネットには NATGW を割り当ててくれていて低レイヤに意識を向けさせない作りという感じ。

では今回そうしたのかと言うとそのまま使用してはおらず少し手を加えています。

  • サブネットの数はプライベートとパブリックともに1つでよい
  • プライベートサブネットにインターネット接続はいらない

前者は subnet_configuration のパラメータを作成して、後者は private_subnet の代わりに isolated_subnet を使用しました。 特に後者は NATGW の数を 0 にして aws-ec2.VPC() に渡しているのに何で NATGW が割り当てられるんだろうとずっと不思議がっていました。よく見ればドキュメントに記載されているのにね …。

高可用性ネットワークがそのまま当てはまることは稀だと思うので、是非一度試してみて想定する環境との差異を確認するのが吉。

分離

vpccidrName など最初変数をクラス内にずらずら書いていたのですが記載が多くなり把握しにくくなったので vars.py を作成してそこに分離しました。

  • awscdk_py/vars.py
from aws_cdk import aws_ec2 as ec2

class var:

    # vars
    def __init__(self):

        # vpc
        self.vpcName = "testVpc"
        self.vpcCidr = "192.168.96.0/20"
()

のような。少し考えてこのような形にしたのですが、ちゃんとしたやり方はなんだろうと今ももやもや考えています。

CloudFormation にはある程度親しんでおいたほうが良い

CloudFormation ( というか yaml) のフォーマットをもっとプログラマブルに記載できないか?の方針だと思うのですが、結局は CloudFormation をもってリソースを作成することになるし、Cfn 系の低レイヤクラスを使用する場合は特にですが、CloudFormation フォーマットの外観が分かっていないと辛い場面は出てくるのではないかなあと思いました。これはまあ Terraform や pulumi を使用していても同じ話が出てくると思いますが。

ちなみに Terraform も触ってみたいなあと思うのですが、独自言語という気持ちの重みに負け続けています。

今後

書いたものは書いたもので使用とするとして、何か他にも書いてみたい気持ちもありますね。あとはちゃんとした書き方を身に着けたい。

AWSCDK