LoginSignup
1
0

More than 1 year has passed since last update.

CDK - Infrastructureの新たなBoilerplateの時代

Last updated at Posted at 2021-12-04

実はCDKにを始めてまだ2ヶ月程度なのですが、CDKの世界感がとても気持ちがよく、のめり込んでしまいました。
インフラ界隈では以下のような変遷がありますが、

  • Scriptによるインフラ構築
  • IaC (Infrastructure as Code)
  • CaD (Configuration as Data)

私はTemplateによるインフラ構築が最終形だとばかり思ってました。
しかし、CloudFormationやk8s manifestなどTemplateの管理やメンテにも以下のような課題があり、限界を感じてきています。

  • システムが肥大化するにつれTemplate管理が難しくなってきた
  • Templateの引き継ぎで設計意図が伝わらない

CDKとは (個人見解)

[AWS Black Belt Online Seminer]AWS Cloud Development Kit (CDK)によると「あるべき状態の定義がコードで可能+抽象化」とあります。

今までCloudFormationやk8sのManifestなどの宣言的TemplateによりReconciliation loopを実現してきましたが、CodeによりReconciliation loopを実現するというのがCDKのコンセプトだという認識です。
CDKはIaCやCaDとは一線を画すもので、IaCやCaDの上位概念になるものかと理解してます。

ということは、k8s manifestをCaDで書くにはどうしたらいいのかという疑問が湧いてきましたが、ちゃんとありましたね。CDK for Kubernetes (CDK8s)が。このAdvent Calenderでどなたか記事を書いてくれるのを期待してます。

Infrastructure as Boilerplate

「あるべき状態の定義がコードで可能」
CDKがIaCやCaDの上位概念だとすると、TemplateとCodeの違いはなにかが気になります。

Boilerplate と Template は何が違うのでしょうか?

ここを参考に考えると、CDK-constructはCodeというよりBoilplateなのではないかと。
また、CDKではCustom Construct LibraryをnpmやPyPIに公開し共有可能ということからもBoilerplateのほうがしっくり来ます。

つらつらと書いてしまいましたが私の考えをまとめると・・・
CDKは以下のようになります。

  • IaC、CaDの上位概念である。
  • Reconciliation loopを実現するBoilPlateである。

こうなるとIaCやCaDのような新しい用語が生まれてほしいなあと。

  • Infrastructure as Boilerplate
  • Configuration as Boilerplate

いやあ、私の乏しい発想ではいいものが考えられません。
CDKを作った方にこれを表現する用語を作ってもらいたいものですね。
そうすれば、CDKの世界も一気に広がるのではないでしょうか。

CDK construct library - Python

CDK constructはBoilerplateであると言いましたが、私も流れにのってconstructを公開していきたいと思ってます。(いいものができたらPyPIに公開していきたいと思います。)

また、私はpythonianなのでCDKをPythonで書きたいのすが、巷にあるCDKのサンプルはTypeScriptが主流でPythonは少なく寂しい限りです。なのでいくつかPythonのサンプルをGitHubにUpしてます。

苦情や質問、Issueなどありましたら是非お願いいたします。

1. SSM Parameter Store

CDKでSSM Parameter StoreのParameterのサンプルを見つけられなかったのですが、Documentを調べるとちゃんとありました。
aws_cdk.aws_ssm.StringParameter
aws_cdk.aws_ssm.StringListParameter
こちらを参考に作ったものがGitHub: cdk-ssm-parameter-storeです。
SsmStringParameterクラスは単純なものなのですがconstructのレイヤーで作成しました。
この程度のものでしたらあえてconstructを作る必要はないかもしれませんが、将来のためにもStackにベダ書きしたくないのでconstructのレイヤーを作成しています。

Parameterの作成

String parameter

ssm_parameter_construct.py

# Stringパラメータの作成
class SsmStringParameter(Construct):
    def __init__(self,
                 scope: Construct,
                 id: str,
                 parameter_name: str,
                 string_value: str,
                 ) -> None:
        super().__init__(scope, id)

        aws_ssm.StringParameter(
            scope=self,
            id=f'{id}-construct',
            parameter_name=parameter_name,
            string_value=string_value,
            type=aws_ssm.ParameterType.STRING,
            tier=aws_ssm.ParameterTier.STANDARD,
        )

Secure String parameter

aws_ssmには安全のためにSecureStringParameterのconstructがありません(パスワードをCodeに書かないよう配慮されています)。そのため、SecureStringParameterは、CLIなどを使って作成する必要があります。

$ aws ssm put-parameter \
  --name "/my-site/token" \
  --value "xxxxxxxxx" \
  --type "SecureString"

String List parameter

ssm_parameter_construct.py
# StringListパラメータの作成
class StringListParameter(Construct):
    def __init__(self,
                 scope: Construct,
                 id: str,
                 parameter_name: str,
                 string_list_value: list,
                 ) -> None:
        super().__init__(scope, id)

        aws_ssm.StringListParameter(
            scope=self,
            id=f'{id}-construct',
            parameter_name=parameter_name,
            string_list_value=string_list_value,
            tier=aws_ssm.ParameterTier.ADVANCED
        )

Parameter取得

String parameter取得

ssm_parameter_read_stack.py

email = aws_ssm.StringParameter.value_for_string_parameter(
    scope=self,
    parameter_name='/my-site/alerts-email-dev'
)

Secure String parameter取得

ssm_parameter_read_stack.py
token = aws_ssm.StringParameter.value_for_secure_string_parameter(
    scope=self,
    parameter_name='/my-site/token',
    version=1
)

String List parameter取得

String List parameterを取得して、Outputに文字列出力を試みたのですが、単純には行きませんでした。

ssm_parameter_read_stack.py
environments = aws_ssm.StringListParameter.from_string_list_parameter_name(
    scope=self,
    id='GetEnvironmentFromSsmParameter',
    string_list_parameter_name='/my-site/environments',
)

cdk.CfnOutput(
    scope=self,
    id='parameter_environments',
    value=json.dumps(self.resolve(environments.string_list_value))
)

こちらのGitHub issuesにあるようにaws_cdk . resolve()を使い対応しましたが、以下のように表示するのが限界のようです。

{"Fn::Split": [",", "dev,stg,prod"]}

2. Lambda Layer construct

GitHub: cdk_lambda_layers
docker-lambdaをつかってLambda Layerを作成しました。
以前はローカルでDockerコンテナをマウントして作成していましたがaws_core.BundlingOptionsを使えば簡単につくれますね。

lambda_layer_construct.py
from aws_cdk import BundlingOptions
from constructs import Construct
from aws_cdk import aws_lambda


class LambdaLayerConstruct(Construct):
    def __init__(self,
                 scope: Construct,
                 id: str,
                 layer_version_name: str,
                 ) -> None:
        super().__init__(scope, id)

        self._layer_version_name = layer_version_name

    def lambda_layer(
            self,
            language: str,
            layer_code_path: str) -> aws_lambda.LayerVersion:

        lang_path = language
        commands = [
            '/bin/bash',
            '-c',
            f"""
            pip install -r requirements.txt -t /asset-output/{lang_path} &&
            cp -au . /asset-output/{lang_path}
            """
        ]

        bundling = BundlingOptions(
            image=aws_lambda.Runtime.PYTHON_3_8.bundling_image,
            command=commands
        )

        code = aws_lambda.Code.from_asset(
            path=layer_code_path,
            bundling=bundling
        )

        layer = aws_lambda.LayerVersion(
            scope=self,
            id='LambdaConstructLayerVersion',
            layer_version_name=self._layer_version_name,
            code=code
        )
        return layer

3. EKS Container Stack

Github: cdk-eks-vpc
このCDK appはVPC、EKS Cluster、Containerの3つのStackを持ちます。
Container生成する Stackを分離させてるところがポイントかと思います。

以下はコンテナを生成するStackで、ArgoCD、nginxのコンテナ生成constructがあります。
Container Stackを分離することで、非常に見通しの良いものになっているのではないでしょうか。

container_stack.py
class ContainerStack(Stack):

    def __init__(self,
                 scope: Construct,
                 construct_id: str,
                 cluster: aws_eks.Cluster,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # ArgoCD
        self._container_tool_construct = ContainerToolConstruct(
            self, 'ContainerToolConstruct', cluster)
        # eginx
        self._container_construct = ContainerConstruct(
            self, 'ContainerConstruct', cluster)

 おわりに

CDK constructはBoilerplateであることを意識すれば、CDKのコードの見通しが非常に高まると思いました。また、3. EKS Clusterに関しては、現在のコードをベースにPipeline化を進めていきたく、最終的にはGitOpsにしていきたいと考えてます。CDKであればTemplateに比べアジャイルチックに進めていくのもとてもやりやすいので、CDKの「コードで可能」というメリットを教授できます。CDKによりインフラのアジャイル開発が活発化すればいいなと思いました。

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