TECH PLAY

SCSKクラウドソリューション

SCSKクラウドソリューション の技術ブログ

1141

本記事は TechHarmony Advent Calendar 12/9付の記事です。 どうも、CLI推進派の寺内です。 かねてよりAWSより周知 のあった、AWS CloudShell の Amazon Linux 2023 への変更が、ついに東京リージョンに来ました。12/4 から順次リリースということで、毎日チェックしていたのですが私が確認できたのは12/6の日本時間朝になります。 AWS CloudShell migrating from Amazon Linux 2 to Amazon Linux 2023 - AWS CloudShell Learn about the CloudShell migration from Amazon Linux 2 to Amazon Linux 2023. docs.aws.amazon.com CloudShellを起動すると、以下のようなメッセージが現れます。 ということで、中身を見ていきましょう。 OSの確認 Amazon Linux 2 の頃から変わらずインテルですね。そろそろArmになるかと思ってました。 $ cat /etc/system-release Amazon Linux release 2023 (Amazon Linux) $ uname -a Linux ip-10-132-91-243.ap-northeast-1.compute.internal 6.1.61-85.141.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Nov 8 00:39:18 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux 認証情報 $ export declare -x AWS_CONTAINER_AUTHORIZATION_TOKEN="ABCDEFGHIJKLMNOPQRSTUVWXYZ12345=" declare -x AWS_CONTAINER_CREDENTIALS_FULL_URI="<http://localhost:1338/latest/meta-data/container/security-credentials>" declare -x AWS_DEFAULT_REGION="ap-northeast-1" declare -x AWS_EXECUTION_ENV="CloudShell" declare -x AWS_PAGER="less -K" declare -x AWS_REGION="ap-northeast-1" declare -x AWS_TOOLING_USER_AGENT="AWS-CloudShell/2023.10.01" declare -x EDITOR="/usr/bin/nano" (以下略) こちらも変わりはないですね。 以下のようにこの環境変数を使いトークン情報も取得できます。 $ curl -sH "X-aws-ec2-metadata-token: $AWS_CONTAINER_AUTHORIZATION_TOKEN" $AWS_CONTAINER_CREDENTIALS_FULL_URI { "Type": "", "AccessKeyId": "ASIABCDEFGHIJKLM", "SecretAccessKey": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "Token": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "Expiration": "2023-12-05T23:14:16Z", "Code": "Success" } AWS CloudShellのインフラ CloudShellが実行しているコンテナについて見ていきましょう。以下のドキュメントに仕様があります。 AWS CloudShell compute environment: specifications and software - AWS CloudShell Provides details about the virtual machine and pre-installed tools that support your AWS CloudShell environment. docs.aws.amazon.com CPU ドキュメント にvCPU1つとあるとおり、vCPUは1つで2コアのようです。 $ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 85 model name : Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz stepping : 7 microcode : 0x5003604 cpu MHz : 2499.998 cache size : 36608 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 1 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves ida arat pku ospke bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit mmio_stale_data retbleed gds bogomips : 4999.99 clflush size : 64 cache_alignment : 64 address sizes : 46 bits physical, 48 bits virtual power management: processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 85 model name : Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz stepping : 7 microcode : 0x5003604 cpu MHz : 2499.998 cache size : 36608 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 1 apicid : 1 initial apicid : 1 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves ida arat pku ospke bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit mmio_stale_data retbleed gds bogomips : 4999.99 clflush size : 64 cache_alignment : 64 address sizes : 46 bits physical, 48 bits virtual power management: メモリ ドキュメント の記載は2GBですが、4GBあります。 $ cat /proc/meminfo MemTotal: 3919564 kB MemFree: 212156 kB MemAvailable: 3155248 kB Buffers: 82420 kB Cached: 3004044 kB SwapCached: 0 kB Active: 564328 kB Inactive: 2840924 kB Active(anon): 684 kB Inactive(anon): 318944 kB Active(file): 563644 kB Inactive(file): 2521980 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 0 kB SwapFree: 0 kB Zswap: 0 kB Zswapped: 0 kB Dirty: 14640 kB Writeback: 0 kB AnonPages: 309560 kB Mapped: 454664 kB Shmem: 840 kB KReclaimable: 123512 kB Slab: 194032 kB SReclaimable: 123512 kB SUnreclaim: 70520 kB KernelStack: 5664 kB PageTables: 7648 kB SecPageTables: 0 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 1959780 kB Committed_AS: 3479776 kB VmallocTotal: 34359738367 kB VmallocUsed: 17648 kB VmallocChunk: 0 kB Percpu: 984 kB HardwareCorrupted: 0 kB AnonHugePages: 34816 kB ShmemHugePages: 0 kB ShmemPmdMapped: 0 kB FileHugePages: 0 kB FilePmdMapped: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB Hugetlb: 0 kB DirectMap4k: 115120 kB DirectMap2M: 3997696 kB DirectMap1G: 0 kB ネットワーク ensというネットワークインターフェースがありますね。何かしらのセキュリティのインターフェースでしょうか。 $ cat /proc/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 246936 1988 0 0 0 0 0 0 246936 1988 0 0 0 0 0 0 ens5: 737030808 378013 0 0 0 0 0 0 14131513 31527 0 0 0 0 0 0 docker0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 グローバルIPも今まで通り取得できます。 $ curl <http://checkip.amazonaws.com/> XX.XX.XX.XX ストレージ /home 領域、 ドキュメント の記載は1GBですが、なんだかんだ1.5GBありますね。 またAmazon Linux 2の頃のCloudShellのファイルは、ちゃんと引き継がれています。 この /home 領域は120日アクセスがないと自動的に削除されますので注意してください。数日前に削除する旨の警告メールが届きます。 $ df -m Filesystem 1M-blocks Used Available Use% Mounted on overlay 15955 3806 11314 26% / tmpfs 64 0 64 0% /dev shm 64 0 64 0% /dev/shm /dev/nvme1n1 15955 3806 11314 26% /home /dev/loop0 974 10 897 2% /home/cloudshell-user /dev/nvme0n1p1 30644 4280 26365 14% /aws/mde/mde AWS CloudShellのシェル環境 AWS CloudShellでは、 ドキュメント にあるように以下のシェルが使えます。 Bash PowerShell zsh cshは入っていません。 バージョンは、2023/12/06時点のものです。 $ bash --version GNU bash, version 5.2.15(1)-release (x86_64-amazon-linux-gnu) Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. $ zsh --version zsh 5.8.1 (x86_64-amazon-linux-gnu) $ pwsh --version PowerShell 7.3.8 AWS関連のコマンド AWS CLIをはじめとして、各種AWSが提供するサービス操作コマンドが入っています。 AWS CLI AWS Elastic Beanstalk CLI Amazon ECS CLI AWS SAM バージョンは、2023/12/06時点のものです。 $ aws --version aws-cli/2.14.5 Python/3.11.6 Linux/6.1.61-85.141.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023 prompt/off $ eb --version EB CLI 3.20.10 (Python 3.9.16 (main, Sep 8 2023, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)]) $ ecs-cli --version ecs-cli version 1.21.0 (bb0b8f0) $ sam --version SAM CLI, version 1.98.0 ランタイム プログラム実行環境は、Node.js とPython3が入っています。Amazon Linux 2023 に従い、Python2は削除されていますので気をつけてください。 ruby、gccは入っていないようです。 バージョンは、2023/12/06 時点のものです。 $ node --version v18.18.2 $ npm --version 9.8.1 $ npm -g ls --depth 0 2>/dev/null | grep aws-sdk `-- aws-sdk@2.1472.0 $ python --version Python 3.9.16 $ python3 --version Python 3.9.16 $ pip --version pip 21.3.1 from /usr/lib/python3.9/site-packages/pip (python 3.9) $ pip3 --version pip 21.3.1 from /usr/lib/python3.9/site-packages/pip (python 3.9) $ pip3 list | grep boto3 boto3 1.28.62 boto3-stubs 1.28.55 mypy-boto3-apigateway 1.28.36 mypy-boto3-cloudformation 1.28.48 mypy-boto3-ecr 1.28.45 mypy-boto3-iam 1.28.37 mypy-boto3-lambda 1.28.36 mypy-boto3-s3 1.28.55 mypy-boto3-schemas 1.28.36 mypy-boto3-secretsmanager 1.28.36 mypy-boto3-signer 1.28.36 mypy-boto3-stepfunctions 1.28.36 mypy-boto3-sts 1.28.58 mypy-boto3-xray 1.28.47 その他 エディターは、nano と vim が使えます。 $ nano --version GNU nano, version 5.8 (C) 1999-2011, 2013-2021 Free Software Foundation, Inc. (C) 2014-2021 the contributors to nano Compiled options: --enable-utf8 $ vim --version VIM - Vi IMproved 9.0 (2022 Jun 28, compiled Nov 03 2023 00:00:00) Included patches: 1-2081 Modified by <amazon-linux-engine@amazon.com> Compiled by <amazon-linux-engine@amazon.com> (以下略) また make コマンドが入っています。Amazon Linux 2023 には入っていなかったと思います。 $ make --version GNU Make 4.3Built for x86_64-amazon-linux-gnu Copyright (C) 1988-2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. ということで、あまり大きくは変わっていませんが、OSが新しくなったのは気持ちがいいですね。 無料で使えるAWS CloudShell を活用していきましょう。 Amazon Linux 2 の深掘りは以下にあります。 AWS CloudShellを深掘ってみる ブラウザで使うことのできるシェル環境、AWS CloudShell の仕組みを紐解きます。 制約事項を整理しましたので、皆さんも的確に使いこなせるようになりましょう。 blog.usize-tech.com 2022.02.15
アバター
本記事は TechHarmony Advent Calendar 12/8付の記事です。 こんにちは、SCSK 浦野です。 気がついたら12月だね、と会話したと思ったら、その12月も1/3が過ぎてしまいました。 雪国で育ったためか、雪を見ないとなかなか冬という気分になれていない筆者です。 さて、検証などでプライベートサブネットに Amazon EC2 インスタンスを用意して AWS Systems Manager セッションマネージャ経由でアクセスしたいと考えることがあるかと思います。似た状況で複数の AWS 初学者の方が「接続ができない!」とハマった所を見ましたので、環境を作成しながら確認ポイントを解説できればと思います。 前提など 以下図のように、VPC と接続先の Amazon EC2 インスタンスを作成し、AWS マネジメントコンソールからセッションマネージャ経由でアクセスしようとしたところアクセスできない、となった状況からスタートとします。 私の環境で、マネジメントコンソールのVPC作成から、「VPCなど」を選んで、設定を変更せずに作成したところ、このVPCの構成となりました。そこに、Amazon Linux の EC2 インスタンスを該当のプライベートサブネットに置く設定以外はデフォルトにしてデプロイしています。 つながらない理由と対応方法を考える ネットワーク的につながらない 現時点の構成では、EC2 インスタンスはプライベートサブネットで孤立しており、外部との通信ができません。 対応方法としては2つ考えられます。 1つ目は NAT ゲートウェイを設置する方法。もう一つは、エンドポイントを置く方法。 今回は、NAT ゲートウェイを設置する方法で進めたいと思います。   IAMロールの設定が適切に行われていない。 マネジメントコンソールでデフォルトのままインスタンスを立ち上げるとIAMロールを付与されないインスタンスが起動します。 セッションマネージャ経由でアクセスする為には、インスタンスに「AmazonSSMManagedInstanceCore」が許可されているIAMロールが付与されている必要があります。付与までの手順を確認しましょう。 対応する NAT ゲートウェイを設置する  パブリックサブネットに NAT ゲートウェイを設置します。設置しただけでは、プライベートサブネットにあるインスタンスは依然として外部への通信先を知らないので、プライベートサブネット用のルートテーブルに 0.0.0.0/0を NAT ゲートウェイに向かわせる設定も行っていきましょう。 ※この手順はVPCを作成する際に NAT ゲートウェイを追加して作成していれば不要です。もし、VPCの作成前であればそちらのほうが簡単に設定が可能です。       マネジメントコンソールで「VPC」を検索して開きます。   画面左の「NAT ゲートウェイ」をクリック後に画面右に表示される「NAT ゲートウェイを作成」をクリックします。    表示された画面で、NAT ゲートウェイに付与する名前、設置先のサブネット、EIP の割り当てを行い「NAT ゲートウェイを作成」をクリックします。   NAT ゲートウェイは作成されますが、この時点では EC2 インスタンスからはインターネットへの経路が不明のままの為、ap-northeast-1aのプライベートサブネットのルートテーブルに情報を追記します。「VPC」→左ペインから「ルートテーブル」を選択し、ap-northeast-1aのプライベートサブネットに関連づけられているルートテーブルを選択します。   表示された画面の「ルート」タブをクリックすると登録されているルートの情報が確認できます。ここで、まだ NAT ゲートウェイが登録されていないことが確認できるかと思います。そこで「ルートを編集」をクリックします。   「ルートを追加」をクリックし、追加された送信先に「0.0.0.0/0」、ターゲットに「NAT ゲートウェイ」、その詳細に「先ほど作成した NAT ゲートウェイ」を選択し、「変更を保存」をクリックします。保存後登録されたことを確認してください。     IAMロールの作成と付与 マネジメントコンソールでデフォルトのままインスタンスを立ち上げるとIAMロールを付与されないインスタンスが起動します。 セッションマネージャ経由でアクセスする為には、インスタンスに「AmazonSSMManagedInstanceCore」が許可されているIAMロールが付与されている必要があります。付与まで行って行きましょう。     マネジメントコンソールで「IAM」を検索して開きます。    画面左の「ロール」をクリック後に画面右に表示される「ロールを作成」をクリックします。    信頼されたエンティティタイプで「AWSのサービス」を選び、ユースケースで「EC2」を選択して、「次へ」をクリックします。     許可を追加で、今回必要となる「AmazonSSMManagedInstanceCore」を検索、「次へ」をクリックします。     適当なロール名を設定し「ロールを作成」をクリックし、作成完了です。    次に、ここで作成したロールをインスタンスへ付与する為に、マネジメントコンソールでEC2を開き、付与先のインスタンスを選択して、「アクション」→ 「セキュリティ」→ 「IAMロールを変更」を選択します。    IAMロールの欄に手順5で登録したIAMロールの名前を入れ検索し、設定し「IAMロールの更新」をクリックします。これで、必要なロールの付与は完了です。    接続を試してみる 2点の対応が完了すると、通信できるはずですので通信確認を行います。EC2のインスタンス一覧から、該当の EC2 インスタンスを選択して「接続」をクリックすると、以下が表示されます。ここでさらに「接続」をクリックすると、SSHで接続した画面が表示されます。     作業後は以下の図のようになります。 それでも接続できないときは 接続ができない状況が続いた後に接続が可能になった場合、すぐに接続が開始できないことがあります。すべての問題を解決したのに繋がらないと思ったときは、EC2 インスタンスを再起動してみてください。 また、今回は取り上げていませんが個別にセキュリティグループやネットワークACL等を設定している場合それらが通信をブロックする可能性もありますので設定されている内容が通信をブロックしていないか確認してみてください。 まとめ プライベートサブネットに配置した EC2 インスタンスにセッションマネージャ経由でアクセスできない際の確認ポイントと、その対応手順を確認してきました。自分が作成した EC2 インスタンスに接続ができないとなると焦ることもあるかもしれませんが、落ち着いて原因を探して対応していきましょう!
アバター
はじめに こんにちは。SCSKのふくちーぬです。 今回は、CI/CD配下でネストされた AWS CloudFormation スタックの子スタックに対して、変更状況が分かるような高度なテクニックをご紹介します。 実はCI/CD配下でネストされたスタックを採用した場合、AWS CodePipelineが子スタックの変更セットの状況まで面倒を見てくれません。つまり、親スタックの変更セットの状況しか分からないということになります。親スタックの大まかな差分しか分からないとなると、開発者はデプロイ時に自身の変更分がどこまで反映されるのか知る由もなく、少々不便ですよね。 そこで今回は、AWS CodeBuildにて変更セットを作成することでこの問題に対処しようと思います。 CodePipelineがネストされたスタックの変更セットをサポートしていない件 CodePipeineではCloudFormationのアクションを利用することで、変更セットの作成・更新・実行が可能になります。このアクションを利用して、CloudFormationテンプレートをデプロイすることができます。 AWS CloudFormation - AWS CodePipeline AWS CloudFormation スタックでオペレーションを実行します。スタックは、単一のユニットとして管理できる AWS リソースのコレクションです。スタック内のすべてのリソースは、スタックの AWS CloudFormation テンプレートで定義されます。変更セットにより、元のスタックを変更せずに表示できる比... docs.aws.amazon.com しかしCodePipelineでは、ネストされたスタックに対しての記述が書かれていませんね。AWSサポートに確認したところ、CodePipelineではサポートされていないようでした。 ネストされたスタックの子スタックに対して変更セットを有効にするためには CloudFormation APIについて CloudFormation APIでは、サポートされています。 IncludeNestedStacks Creates a change set for the all nested stacks specified in the template. The default behavior of this action is set to  False . To include nested sets in a change set, specify  True . CreateChangeSet - AWS CloudFormation Creates a list of changes that will be applied to a stack so that you can review the changes before executing them. You can create a change set for a stack that... docs.aws.amazon.com   create-change-set — AWS CLI 2.14.5 Command Reference awscli.amazonaws.com つまり、CloudFormationコンソール上からまたはAWS CLI等を通してならば子スタックの変更分も判別できるようにすることが可能です。 CodeBuildの使用 CI/CDを利用している場合は、CodeBuildも大概利用しているかと思います。今回の解決方法としては、CodeBuild内にて変更セットを作成することで対応することができます。 CodePipelineで任せている部分をCodeBuildにてカスタムで処理をするイメージとなります。 構成図 今回の構成図となります。 CodeCommit+CodeBuild+CodePipelineにてCI/CDを構成しています。 サンプルのインフラリソースも用意しています。 CI/CDパイプラインの準備 Cloud9の作成 以下に AWS Cloud9 の作成方法を記載しているので参考にしてください。 AMI から AWS Cloud9 を復元する Cloud9を削除してしまった際の適切な復元方法についてお話します。 blog.usize-tech.com 2023.11.24   Cloud9とCodeCommitの連携 Cloud9からCodeCommitにプッシュできるように準備してください。 AWS Cloud9 と AWS CodeCommit を統合する - AWS CodeCommit AWS Cloud9 を AWS CodeCommit と統合する方法について説明します。 docs.aws.amazon.com Cloud9及びCodeCommitのディレクトリ構成について 以下のようなディレクトリ構成としておきます。 param.json 以下の3つのCloudFormationテンプレートで使用するためのパラメータファイルです。 [ { "ParameterKey": "Environment", "ParameterValue": "dev" }, { "ParameterKey": "VPCCidrBlock", "ParameterValue": "192.168.0.0/16" } ]  -cfn.yaml -network.yaml -securitygroup.yaml network.yaml VPC・パブリックサブネット・ルートテーブル・インターネットゲートウェイを作成するCloudFormationテンプレートです。 cfn.yamlの子スタックとなります。 AWSTemplateFormatVersion: 2010-09-09 Description: Network Stack Parameters: Environment: Type: String VPCCidrBlock: Type: String Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCidrBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub ${Environment}-vpc # ------------------------------------------------------------# # InternetGateway # ------------------------------------------------------------# InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${Environment}-igw AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PublicRouteTable: Type: AWS::EC2::RouteTable DependsOn: AttachGateway Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${Environment}-rtb PublicRoute: Type: AWS::EC2::Route DependsOn: AttachGateway Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PublicSubnet: Type: AWS::EC2::Subnet DependsOn: AttachGateway Properties: VpcId: !Ref VPC AvailabilityZone: !Select - 0 - Fn::GetAZs: !Ref AWS::Region CidrBlock: !Join [ "", [ !Select [ 0, !Split [ "/", !Ref VPCCidrBlock ] ], /24 ] ] Tags: - Key: Name Value: !Sub ${Environment}-subnet PublicSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet RouteTableId: !Ref PublicRouteTable Outputs: VpcId: Value: !Ref VPC SubnetId: Value: !Ref PublicSubnet  securitygroup.yaml セキュリティグループを作成するCloudFormationテンプレートです。 cfn.yamlの子スタックとなります。 AWSTemplateFormatVersion: 2010-09-09 Description: Security Group Stack Parameters: Environment: Type: String VpcId: Type: AWS::EC2::VPC::Id Resources: # ------------------------------------------------------------# # SecurityGroup # ------------------------------------------------------------# SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable ssh and web access VpcId: !Ref VpcId SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 192.168.0.0/16 #192.168.0.0/24に変更 - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 192.168.0.0/16 #192.168.0.0/24に変更 Tags: - Key: Name Value: !Sub ${Environment}-sg Outputs: SGId: Value: !Ref SG cfn.yaml 上記2ファイルを子とする親スタックとなります。 AWSTemplateFormatVersion: 2010-09-09 Description: Parent Stack Parameters: Environment: Type: String VPCCidrBlock: Type: String Resources: # ------------------------------------------------------------# # Network Stack (VPC Subnet RouteTable InternetGateway) # ------------------------------------------------------------# NW: Type: AWS::CloudFormation::Stack Properties: TemplateURL: src/network.yaml Parameters: Environment: !Ref Environment VPCCidrBlock: !Ref VPCCidrBlock # ------------------------------------------------------------# # Security Stack (SecurityGroup) # ------------------------------------------------------------# SECURITY: Type: AWS::CloudFormation::Stack Properties: TemplateURL: src/securitygroup.yaml Parameters: Environment: !Ref Environment VpcId: !GetAtt NW.Outputs.VpcId  buildspec.yaml CodeBuildで使用するビルド仕様ファイルです。 CloudFormationテンプレートの構文チェックを実施しています。 version: 0.2 phases: pre_build: commands: - | [ -d .cfn ] || mkdir .cfn aws configure set default.region $AWS_REGION for template in src/*.yaml cfn.yaml; do echo "$template" | xargs -I% -t aws cloudformation validate-template --template-body file://% done  changeset-buildspec.yaml CodeBuildで使用するビルド仕様ファイルです。 ネストされたスタックに対して変更セットを作成しています。 version: 0.2 phases: install: commands: build: commands: - | [ -d .cfn ] || mkdir .cfn aws cloudformation package \ --template-file cfn.yaml \ --s3-bucket $S3_BUCKET \ --output-template-file .cfn/packaged.yaml post_build: commands: - | #変数の設定 stack_name=$STACK_NAME change_set_name="changeset" template_body="file://.cfn/packaged.yaml" parameters="file://params/param.json" capabilities="CAPABILITY_NAMED_IAM" role_arn=$CFNROLE_ARN #スタックが存在するか確認する関数 function stack_exists() { aws cloudformation describe-stacks --stack-name "$1" 2>&1 1>/dev/null | grep -e "ValidationError" > /dev/null } #スタックがレビュー中か確認する関数 function stack_reviewin() { aws cloudformation describe-stacks --stack-name "$1" | grep -e "REVIEW_IN_PROGRESS" } #変更セットが存在するか確認する関数 function changeset_exists() { local stack_name="$1" local change_set_name="$2" aws cloudformation describe-change-set --stack-name "$stack_name" --change-set-name "$change_set_name" 2>&1 1>/dev/null | grep -e "ChangeSetNotFound" > /dev/null } #変更セットを削除する関数 function delete_changeset() { local stack_name="$1" local change_set_name="$2" aws cloudformation delete-change-set --stack-name "$stack_name" --change-set-name "$change_set_name" sleep 3 #既存の変更セットが削除されるまで待つ } #変更セットを作成する関数 function create_changeset() { local stack_name="$1" local change_set_name="$2" local template_body="$3" local parameters="$4" local capabilities="$5" local change_set_type="$6" local role_arn="$7" aws cloudformation create-change-set \ --stack-name "$stack_name" \ --change-set-name "$change_set_name" \ --template-body "$template_body" \ --parameters "$parameters" \ --capabilities "$capabilities" \ --change-set-type "$change_set_type" \ --role-arn "$role_arn" \ --include-nested-stacks > output.json } #メイン if stack_exists "$stack_name"; then echo "Stack doesn't exist. Creating new changeset." create_changeset "$stack_name" "$change_set_name" "$template_body" "$parameters" "$capabilities" "CREATE" "$role_arn" else echo "Stack exists." if changeset_exists "$stack_name" "$change_set_name"; then echo "Changeset doesn't exist. Creating new changeset." else echo "Changeset exists. Creating new changeset." delete_changeset "$stack_name" "$change_set_name" fi if stack_reviewin "$stack_name"; then echo "Stack review_in_progress." create_changeset "$stack_name" "$change_set_name" "$template_body" "$parameters" "$capabilities" "CREATE" "$role_arn" else create_changeset "$stack_name" "$change_set_name" "$template_body" "$parameters" "$capabilities" "UPDATE" "$role_arn" fi fi artifacts: files: - .cfn/* - params/* discard-paths: yes cicd.yaml パイプライン構築用のCloudFormationファイルです。 AWSTemplateFormatVersion: 2010-09-09 Description: cfn CI/CD Pipeline Parameters: ResourceName: Type: String REPOSITORYNAME: Type: String Description: aws codecommit repository name STACKNAME: Type: String Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# ArtifactStoreBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub s3bucket-${AWS::AccountId}-artifactbucket CodeBuildBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub s3bucket-${AWS::AccountId}-codebuildtbucket # ------------------------------------------------------------# # EventBridge Rule for Starting CodePipeline # ------------------------------------------------------------# PipelineEventsRule: Type: AWS::Events::Rule Properties: Name: !Sub ${ResourceName}-rule-pipeline EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default" EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${REPOSITORYNAME} detail-type: - "CodeCommit Repository State Change" detail: event: - referenceCreated - referenceUpdated referenceName: - main State: ENABLED Targets: - Arn: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":codepipeline:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":" - Ref: Pipeline Id: Target RoleArn: !GetAtt PipelineEventsRole.Arn DependsOn: - PipelineEventsRole - Pipeline # ------------------------------------------------------------# # CodePipeline Events Role (IAM) # ------------------------------------------------------------# PipelineEventsRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: events.amazonaws.com Path: / ManagedPolicyArns: - !Ref PipelineEventsPolicy RoleName: !Sub "IRL-EVENTBRIDGE-CodePipelineAccess" # ------------------------------------------------------------# # CodePipeline Events Role Policy (IAM) # ------------------------------------------------------------# PipelineEventsPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: CodePipelineAccessForEvents PolicyDocument: Statement: - Action: codepipeline:StartPipelineExecution Effect: Allow Resource: - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline} Version: "2012-10-17" # ------------------------------------------------------------# # CodeBuild Role (IAM) # ------------------------------------------------------------# CodeBuildRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: codebuild.amazonaws.com Path: / ManagedPolicyArns: - !Ref CodeBuildPolicy - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess RoleName: !Sub "IRL-CODEBUILD-S3CloudWatchlogsAccess" # ------------------------------------------------------------# # CodeBuild Role Policy (IAM) # ------------------------------------------------------------# CodeBuildPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: CodeBuildAccess PolicyDocument: Version: '2012-10-17' Statement: - Sid: CloudWatchLogsAccess Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/* - Sid: S3Access Effect: Allow Action: - s3:PutObject - s3:GetObject - s3:GetObjectVersion Resource: - !Sub arn:aws:s3:::${ArtifactStoreBucket} - !Sub arn:aws:s3:::${ArtifactStoreBucket}/* - !Sub arn:aws:s3:::${CodeBuildBucket} - !Sub arn:aws:s3:::${CodeBuildBucket}/* - Sid: IAMPass Effect: Allow Action: - iam:PassRole Resource: "*" - Sid: CloudFormationAccess Effect: Allow Action: cloudformation:ValidateTemplate Resource: "*" # ------------------------------------------------------------# # CodeBuild linter Project # ------------------------------------------------------------# CodeBuildProjectLint: Type: AWS::CodeBuild::Project Properties: Name: !Sub ${ResourceName}-project-lint ServiceRole: !GetAtt CodeBuildRole.Arn Artifacts: Type: CODEPIPELINE Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 EnvironmentVariables: - Name: AWS_REGION Value: !Ref AWS::Region - Name: S3_BUCKET Value: !Ref CodeBuildBucket Source: Type: CODEPIPELINE # ------------------------------------------------------------# # CodeBuild changeset Project # ------------------------------------------------------------# CodeBuildProjectChangeset: Type: AWS::CodeBuild::Project Properties: Name: !Sub ${ResourceName}-project-changeset ServiceRole: !GetAtt CodeBuildRole.Arn Artifacts: Type: CODEPIPELINE Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 EnvironmentVariables: - Name: AWS_REGION Value: !Ref AWS::Region - Name: S3_BUCKET Value: !Ref CodeBuildBucket - Name: STACK_NAME Value: !Ref STACKNAME - Name: CFNROLE_ARN Value: !GetAtt CloudformationRole.Arn Source: Type: CODEPIPELINE BuildSpec: changeset-buildspec.yaml # ------------------------------------------------------------# # CloudFormation Role (IAM) # ------------------------------------------------------------# CloudformationRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: cloudformation.amazonaws.com Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess RoleName: "IRL-CLOUDFORMATION-ServiceFullAccess" # ------------------------------------------------------------# # CodePipeline Role (IAM) # ------------------------------------------------------------# PipelineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: codepipeline.amazonaws.com Path: / ManagedPolicyArns: - !Ref PipelinePolicy RoleName: "IRL-CODEPIPELINE-Access" # ------------------------------------------------------------# # CodePipeline Role Policy (IAM) # ------------------------------------------------------------# PipelinePolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: CodePipelineAccess PolicyDocument: Version: '2012-10-17' Statement: - Sid: S3FullAccess Effect: Allow Action: s3:* Resource: - !Sub arn:aws:s3:::${ArtifactStoreBucket} - !Sub arn:aws:s3:::${ArtifactStoreBucket}/* - Sid: FullAccess Effect: Allow Action: - cloudformation:* - iam:PassRole - codecommit:GetRepository - codecommit:ListBranches - codecommit:GetUploadArchiveStatus - codecommit:UploadArchive - codecommit:CancelUploadArchive - codecommit:GetBranch - codecommit:GetCommit Resource: "*" - Sid: CodeBuildAccess Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild Resource: !GetAtt CodeBuildProjectLint.Arn - Sid: CodeBuildChangesetAccess Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild Resource: !GetAtt CodeBuildProjectChangeset.Arn # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# Pipeline: Type: AWS::CodePipeline::Pipeline Properties: Name: !Sub ${ResourceName}-pipeline RoleArn: !GetAtt PipelineRole.Arn ArtifactStore: Type: S3 Location: !Ref ArtifactStoreBucket Stages: - Name: Source Actions: - Name: download-source ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit Configuration: RepositoryName: !Ref REPOSITORYNAME BranchName: main PollForSourceChanges: false OutputArtifacts: - Name: SourceOutput - Name: Test Actions: - InputArtifacts: - Name: SourceOutput Name: testing ActionTypeId: Category: Test Owner: AWS Version: 1 Provider: CodeBuild Configuration: ProjectName: !Ref CodeBuildProjectLint - Name: Build Actions: - InputArtifacts: - Name: SourceOutput Name: changeset ActionTypeId: Category: Build Owner: AWS Version: 1 Provider: CodeBuild OutputArtifacts: - Name: BuildOutput Configuration: ProjectName: !Ref CodeBuildProjectChangeset Namespace: BuildVariables - Name: Deploy Actions: - Name: execute-changeset ActionTypeId: Category: Deploy Owner: AWS Version: 1 Provider: CloudFormation Configuration: StackName: !Join [ '-', [ !Ref ResourceName, 'infra-stack' ] ] ActionMode: CHANGE_SET_EXECUTE ChangeSetName: changeset RoleArn: !GetAtt CloudformationRole.Arn CI/CDパイプラインの作成 パイプラインを構築するための”cicd.yaml”ファイルを使用して、デプロイしておきます。 その後各種ファイルをCodeCommitにプッシュすることで、パイプラインが起動します。 パイプラインの起動・スタックの作成 以下のようにパイプラインが自動で動いて、スタックの作成まで行ってくれます。   念の為親スタックを確認しておくと、2つの子スタックが作成されたことが分かります。リンクから子スタック(NW)に飛んでみます。   VPCやサブネット等のリソースが新規作成されることが分かります。 スタックの更新 セキュリティグループのインバウンドルールのソースアドレスを変更してみます。 以下のように更新したファイルをCodeCommitにプッシュします。 AWSTemplateFormatVersion: 2010-09-09 Description: Security Group Stack Parameters: Environment: Type: String VpcId: Type: AWS::EC2::VPC::Id Resources: # ------------------------------------------------------------# # SecurityGroup # ------------------------------------------------------------# SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable ssh and web access VpcId: !Ref VpcId SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 192.168.0.0/24 - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 192.168.0.0/24 Tags: - Key: Name Value: !Sub ${Environment}-sg Outputs: SGId: Value: !Ref SG    親スタックの変更セットで子スタック(SECURITY)に変更分があることを示してくれました。セキュリティグループのみを変更したので意図した動作になりましたね。 リンクから子スタック(SECURITY)に飛んでください。   きちんとネストされたスタックの子スタックに対しても、変更セットが作成されて差分(AWS::EC2::SecurityGroup)が理解できるようになりました。 まとめ CodePipelineでは、ネストされたスタックに対しての 変更セットをサポートしていません。 CloudFormation APIではサポートしてい るため、CodeBuild内から呼び出すことで実現可能です。 最後に いかがだったでしょうか。 ネストされたスタックの子スタックに対して変更セットを有効にできるようなテクニックを紹介しました。今回はCodeBuildを利用してカスタマイズすることで実現できました。 ネストされたスタックをご利用する際には、一度検討していただければと思います。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
アバター
こんにちは、SCSKの前田です。 前回は池田さんから、AWSでは仮想IPアドレスが機能として提供されないことから、それに代わる2つの接続方式についてお伝えしました。 5回目の今回は、どんな構成でどちらの接続方式を選択すれば良いかについて前田がお伝えします。 2つの接続方式について、おさらいしたい方は以下のリンクからどうぞ! 【LifeKeeper】AWSでは仮想IPアドレスが使えない!?をこうして解決する!! オンプレや仮想環境でHAクラスタを構築する際は仮想IPアドレスを使えましたが、AWSでは仮想IPアドレスの機能が提供されません。この課題をLifeKeeperがどのように解決しているかを解説します。 blog.usize-tech.com 2023.11.29   接続元によって接続方式を使い分けよう! 第4回「 【LifeKeeper】AWSでは仮想IPアドレスが使えない!?をこうして解決する!! 」では、ルートテーブルとRoute53を用いた2つの接続方式をお伝えしましたが、どんな構成の時に、どちらの接続方式を選択するか迷ってしまいますよね。 そんな時はこのマトリクスを思い出してください。どちらの接続方式を選ぶかは、主に「 どこから接続するか 」や「 接続経路 」によって決定することができます。 いろんなところからの接続に対応しているのね 本来AWSの仕様上、ルートテーブル接続方式では、クライアントはHAクラスタサーバと同一VPC内にある必要がありますが、 AWS Transit Gatewayを組み合わせることで、VPC外(オンプレミスや別VPC)からも接続できるようになりました。 最近では、オンプレミスとの相互接続や、JP1やZabbixに代表される統合運用管理ツール、HULFTに代表されるファイル転送ソフトでは、接続元がVPC外にある構成が多い為、ルートテーブルとTransit Gatewayを組み合わせた接続方式が主流になりつつあります。 まとめ 今回は、AWSにおいて2つの接続方式のどちらを選択するかについてご説明しましたがいかがでしたでしょうか?どこから接続するか、と接続構成によっておのずと一意に決まってくるので意外と簡単ですよね。 まとめますと ・AWSにおいて、接続方式の選択基準は2つ (1)接続元がどこか?(同一VPC、別VPC、オンプレミス) (2)AWS Transit Gatewayが使えるか? 次回は、HAクラスター構成で、より可用性を高めることのできる機能「Quorum/Witness」についてお伝えします。お楽しみに!!
アバター
本記事は TechHarmony Advent Calendar 12/7付の記事です。 こんにちは。SCSK石原です。 AWS re:Invent2023にて、 Amazon BedrockのKnowledge baseとAgentsがGA されたと発表がありました。今回はこのうちKnowledge baseを利用して、RAG(Retrieval Augment Generation)を試してみたいと思います。 RAGにより、データストアから情報を取得して大規模言語モデル (LLM) によって生成された応答を拡張することができます。これにより、社内ドキュメントや一般に公開されていない情報を回答してくれる機能を実現することができます。 Knowledge Bases for Amazon Bedrock is now generally available aws.amazon.com このビックウェーブに乗りたい一心で、 生成AI初心者がサービスに触れてみた結果を記事にします。 概要 RetrieveAndGenerate APIを使って内部ドキュメントからいい感じに検索してもらって、その結果をLLMをつかっていい感じにテキスト生成して出力してもらうというのが、今回のゴールになります。AWSのサービスでいうと下記の手順になります。 Amazon S3に内部ドキュメントを保存する。 Knowlegde baseを利用して、検索させたい情報をエンベディングしてベクトルDB(OpenSearch)に保存する。 RetrieveAndGenerate APIが叩ける何かしらを実行する。(今回はAWS CLIから実行予定) Knowledge base for Amazon Bedrock - Amazon Bedrock Learn how to use knowledge bases in Bedrock to augment your agents' generative capabilities with your own data. docs.aws.amazon.com 記事執筆時点(2023/12/04)で利用可能なバージニア北部(us-east-1)で実行しています。 設定 Amazon S3に内部ドキュメントを保存する バージニア北部(us-east-1)でAmazon Bedrockを構成するため、Amazon S3も同じリージョンに作成します。 バケットの作成 - Amazon Simple Storage Service Amazon S3 にデータをアップロードするときは、いずれかの AWS リージョン に S3 バケットを作成しておく必要があります。バケットを作成するときは、バケット名とリージョンを選択します。必要に応じて、このバケットに他のストレージ管理オプションを選択できます。一度バケットを作成したら、そのバケット名またはリージ... docs.aws.amazon.com 今回はお試しなので、普段業務で利用しているインフォマティカ製品のドキュメントを内部ドキュメントとして保存します。PDFエクスポートできるので、エクスポートしてS3バケットに保存します。 今回保存した情報は、こちらのサイトから取得しています。 Home docs.informatica.com Knowlegde baseでS3に保存したドキュメントをエンベディング 下記の手順で作成できます。 Create a knowledge base - Amazon Bedrock If you are using a custom role, set up security configurations for your newly created knowledge base. Follow the steps in the tab corresponding to the database ... docs.aws.amazon.com 今回はこちらのパラメータで設定しました。 Knowlegde base name knowledge-base-techharmony-01 IAM permissions Create and use a new service role Data source name knowledge-base-techharmony-01-data-source S3 URI s3://ishihara-bedrock-knowledge Embeddings model Titan Embeddings G1 Text v1.2 Vector database Quick create a new vector store 数分で構築が完了します。 構築が完了したら、Data Sourceタブで先ほど指定したバケットを「sync」をクリックして同期させます。 今回は7つのPDFファイルで17.5MB程度でしたが、1分程度で同期処理が完了しました。 同期処理が完了すると、テストができますので適当に検索してみました。何かしら検索出来ていることがわかります。   RetrieveAndGenerate APIを実行(AWS CLI) とりあえずお試しがてら、AWSコンソールからCloudShellを起動して、AWS CLIでコマンドをしらべてみようと思いおもむろにHELPを実行。 [cloudshell-user@ip-10-132-39-170 ~]$ aws bedrock-agent-runtime help usage: aws [options] [ ...] [parameters] To see help text, you can run: aws help aws help aws help aws: error: argument command: Invalid choice, valid choices are: ・・・中略 Invalid choice: 'bedrock-agent-runtime', maybe you meant: * bedrock-runtime [cloudshell-user@ip-10-132-39-170 ~]$ コマンドがないだと!!! さすが最新アップデートの機能です。 焦らずにAWS CLIのアップデート をしましょう。 [cloudshell-user@ip-10-132-39-170 ~]$ aws --version aws-cli/2.13.34 Python/3.11.6 Linux/6.1.59-84.139.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2 prompt/off [cloudshell-user@ip-10-132-39-170 tmp]$ curl -s "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" [cloudshell-user@ip-10-132-39-170 tmp]$ ls awscliv2.zip tmux-1000 v8-compile-cache-0 [cloudshell-user@ip-10-132-39-170 tmp]$ unzip -q awscliv2.zip [cloudshell-user@ip-10-132-39-170 tmp]$ sudo ./aws/install --update You can now run: /usr/local/bin/aws --version [cloudshell-user@ip-10-132-39-170 tmp]$ aws --version aws-cli/2.14.5 Python/3.11.6 Linux/6.1.59-84.139.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2 prompt/off [cloudshell-user@ip-10-132-39-170 tmp]$ AWS CLIが2.13.34から2.14.5にアップデートされました。 まずはKnowledge baseの画面上でテストした検索のみを「bedrock-agent-runtime retrieve コマンド」を実行しました。 retrieve — AWS CLI 2.14.5 Command Reference awscli.amazonaws.com 先ほどと同様、S3バケットに保存したPDFからそれっぽい結果が出力されています。 [cloudshell-user@ip-10-132-39-170 tmp]$ aws bedrock-agent-runtime retrieve \ > --knowledge-base-id 5TSXYNE1QK \ > --retrieval-query text="What can you do with CDMP?" { "retrievalResults": [ { "content": { "text": ". (2023 October) CDMP-24657 When you try to remove an already existing value of a date type custom field of a data collection, Data Marketplace displays an error even if you haven't configured the field as a mandatory field in Metadata Command Center. (2023 October) CDMP-24605 When you try to create a data collection, Data Marketplace displays an error even if your user profile is assigned the Data Owner and Category Owner roles. (2023 September) CDMP-24462 When you select an asset in the Data Assets grid on the Create Linked Data Assets wizard, the grid reverts to its default state. (2023 September) Known issues The following table describes the known issues in Data Marketplace: CR Description CDMP-24363 In Metadata Command Center, if you modify the acceptable values of a custom attribute of a Data Marketplace order, the response of the Data Marketplace API that you use to retrieve the order details displays the value for the custom attribute that was previously present in the same position in the sequence of acceptable values. For example, for a custom field the acceptable values are A, B and C. In Metadata Command Center, you replace the acceptable values with new values 1, 2 and 3." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_ReleaseNotes_en.pdf" } }, "score": 0.6106315 }, { "content": { "text": "Sort the search result by various parameters. To sort the search results, you can enter one of the following values: - NAME - STATUS - ID - CREATED_BY - CREATED_ON - MODIFIED_BY - MODIFIED_ON Default value is MODIFIED_ON. Delivery formats 31 Parameter Description Additional Information sort Optional. Set the sorting order of the search results. Enter one of the following values: - To sort the search results by ascending order, enter ASC. - To sort the search results by descending order, enter DESC. Default value is DESC. offset Optional. The starting index for the paginated results. Default value is 0. limit Optional. The maximum number of results. Default value is 50. Note: The API has no payload. Example request The following example shows how you can use an API to retrieve the details of delivery format: https://{{CDMP_URL}}/api/v1/integration/provisioning/deliveryFormats Response When you pass the API query parameters in the REST client, the client displays a response for the parameter values that you have entered." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } }, "score": 0.61188567 }, { "content": { "text": "Ensure that you don't use the prefix value that is configured in Metadata Command Center. status Optional. The status of the consumer access that you want to create. Enter one of the following values: - For an active consumer access, enter AVAILABLE. - For a consumer access that is awaiting withdrawal, enter PENDING_WITHDRAW. - For a consumer access that is withdrawn, enter WITHDRAWN. Default value is AVAILABLE. dataCollectionId Required. The system generated unique identifier of the data collection to which the Data User was granted access. For more information about how you can use an API to get the unique identifier of a data collection, see “Retrieve data collections” on page 125. To get the unique identifier of a data collection from the Data Marketplace interface, open the data collection. The data collection page's URL contains the unique identifier. For example, in the URL https:// {{CDMP_URL}}/datacollection/ 25158afc-3dfb-44ef-8f3e- cec1e171d0f1?dtn=&tab=summary, the unique identifier is 25158afc-3dfb-44ef-8f3e- cec1e171d0f1. deliveryTargetId Required. The system generated unique identifier of the delivery target that was used to deliver the data." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } }, "score": 0.6162587 }, { "content": { "text": "The value of the sessionId parameter in the response body is the value that you must use for the userSessionId parameter. For more information about how you can retrieve the sessionId value, see the Login topic in the REST API Reference help in Administrator. The Bearer is a JSON Web Token. For more information about how you can retrieve the JSON Web Token, see the Generating and getting JWT tokens for managed APIs topic in the API Portal Guide help in API Portal. 10 Chapter 1: Introduction https://knowledge.informatica.com/s/article/CDMP-Rest-Accelerator-Pack Start properties In Application Integration, define the binding type and access details for the system service action that you want to create. The following table describes how to configure the Start properties for a system service action to call a Data Marketplace: Property Description Binding If you want to run the process by using a service URL, select REST/SOAP as the binding type Allowed Groups Specify the user groups that should have access to the process service URL at run time. Allowed Users Specify the users that should have access to the process service URL at run time. Allow anonymous access Ensure that you do not select the Allow anonymous access property. If you select Allow anonymous access, you cannot call Data Marketplace APIs." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } }, "score": 0.61763495 }, { "content": { "text": "status=ACTIVE https://{{CDMP_URL}}/ is the base URL and /api/v1/integration/provisioning/deliveryTemplates is the API endpoint. How to call a Data Marketplace API 9 https://network.informatica.com/docs/DOC-19140 The base URL varies based on your region. The following table shows the regions and their corresponding base URLs: Region Base URL United States of America https://cdgc-api.dm-us.informaticacloud.com/cdmp-marketplace/ United Kingdom https://cdgc-api.dm-uk.informaticacloud.com/cdmp-marketplace/ Canada https://cdgc-api.dm-na.informaticacloud.com/cdmp-marketplace/ Europe, Middle East, Africa (EMEA) https://cdgc-api.dm-em.informaticacloud.com/cdmp-marketplace/ Asia, Pacific https://cdgc-api.dm-ap.informaticacloud.com/cdmp-marketplace/ Japan https://cdgc-api.dm-apne.informaticacloud.com/cdmp-marketplace/ You can call Data Marketplace APIs only via Application Integration. You can call only 100 APIs per minute." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } }, "score": 0.64015627 } ] }   今回はいい感じにテキスト生成もしてほしいので、「bedrock-agent-runtime retrieve-and-generate コマンド」を実行します。 モデルはclaude-v2を利用しました。何かしら回答してくれていますね。 [cloudshell-user@ip-10-132-39-170 tmp]$ aws bedrock-agent-runtime retrieve-and-generate \ > --input text="What can you do with CDMP?" \ > --retrieve-and-generate-configuration type=KNOWLEDGE_BASE,knowledgeBaseConfiguration="{knowledgeBaseId=5TSXYNE1QK,modelArn=arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2}" { "sessionId": "f4132659-dbef-4411-8b44-3b2dd49e3f1c", "output": { "text": "The Informatica Cloud Data Marketplace Platform (CDMP) allows you to access, publish and subscribe to data services. You can use CDMP APIs to programmatically manage data services, users, subscriptions, etc. Some key things you can do with CDMP APIs include:" }, "citations": [ { "generatedResponsePart": { "textResponsePart": { "text": "The Informatica Cloud Data Marketplace Platform (CDMP) allows you to access, publish and subscribe to data services. You can use CDMP APIs to programmatically manage data services, users, subscriptions, etc. Some key things you can do with CDMP APIs include:", "span": { "start": 0, "end": 257 } } }, "retrievedReferences": [ { "content": { "text": "sort Optional. Set the sorting order of the search results. Enter one of the following values: - To sort the search results by ascending order, enter ASC. - To sort the search results by descending order, enter DESC. Default value is DESC. offset Optional. The starting index for the paginated results. Default value is 0. limit Optional. The maximum number of results. Default value is 50. Note: The API has no payload. Example request The following example shows how you can use an API to retrieve the details of delivery method: https://{{CDMP_URL}}/api/v1/integration/provisioning/deliveryMethods? search=SK&ids=909668e3-f91d-4cde-a7d5- e3dca316ce97&status=ACTIVE&createdDateFrom=2022-01-12&createdDateTo=2022-12-12&modifiedDa teFrom=2022-01-12&modifiedDateTo=2022-12-12&sortByField=NAME&sort=DESC&offset=0&limit=2 Response When you pass the API query parameters in the REST client, the client displays a response for the parameter values that you have entered." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } }, { "content": { "text": "sort Optional. Set the sorting order of the search results. Enter one of the following values: - To sort the search results by ascending order, enter ASC. - To sort the search results by descending order, enter DESC. Default value is DESC. offset Optional. The starting index for the paginated results. Default value is 0. limit Optional. The maximum number of results. Default value is 50. Note: The API has no payload. Example request The following example shows how you can use an API to retrieve the details of a delivery target: https://{{CDMP_URL}}/api/v1/integration/provisioning/deliveryTargets? search=AWS&status=ACTIVE&sortByField=MODIFIED_BY&sort=ASC Response When you pass the API query parameters in the REST client, the client displays a response for the parameter values that you have entered. The following example shows the response of an API call to retrieve the details of a delivery target: { \"processingTime\": 4190, \"offset\": 0, \"limit\": 50, \"totalCount\": 1, Delivery targets 61 \"objects\": [ { \"id\": \"16e32a13-6fba-4f1a-a337-e0aeecf9fab4" }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } }, { "content": { "text": "sort Optional. Set the sorting order of the search results. Enter one of the following values: - To sort the search results by ascending order, enter ASC. - To sort the search results by descending order, enter DESC. Default value is DESC. offset Optional. The starting index for the paginated results. Default value is 0. limit Optional. The maximum number of results. Default value is 50. Note: The API has no payload. Example request The following example shows how you can use an API to retrieve cost centers: https://{{CDMP_URL}}/api/v1/integration/costCenters?" }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } }, { "content": { "text": "Sort the search result by various parameters. To sort the search results, you can enter one of the following values: - NAME - STATUS - ID - CREATED_BY - CREATED_ON - MODIFIED_BY - MODIFIED_ON Default value is MODIFIED_ON. Delivery formats 31 Parameter Description Additional Information sort Optional. Set the sorting order of the search results. Enter one of the following values: - To sort the search results by ascending order, enter ASC. - To sort the search results by descending order, enter DESC. Default value is DESC. offset Optional. The starting index for the paginated results. Default value is 0. limit Optional. The maximum number of results. Default value is 50. Note: The API has no payload. Example request The following example shows how you can use an API to retrieve the details of delivery format: https://{{CDMP_URL}}/api/v1/integration/provisioning/deliveryFormats Response When you pass the API query parameters in the REST client, the client displays a response for the parameter values that you have entered." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } }, { "content": { "text": "status=ACTIVE https://{{CDMP_URL}}/ is the base URL and /api/v1/integration/provisioning/deliveryTemplates is the API endpoint. How to call a Data Marketplace API 9 https://network.informatica.com/docs/DOC-19140 The base URL varies based on your region. The following table shows the regions and their corresponding base URLs: Region Base URL United States of America https://cdgc-api.dm-us.informaticacloud.com/cdmp-marketplace/ United Kingdom https://cdgc-api.dm-uk.informaticacloud.com/cdmp-marketplace/ Canada https://cdgc-api.dm-na.informaticacloud.com/cdmp-marketplace/ Europe, Middle East, Africa (EMEA) https://cdgc-api.dm-em.informaticacloud.com/cdmp-marketplace/ Asia, Pacific https://cdgc-api.dm-ap.informaticacloud.com/cdmp-marketplace/ Japan https://cdgc-api.dm-apne.informaticacloud.com/cdmp-marketplace/ You can call Data Marketplace APIs only via Application Integration. You can call only 100 APIs per minute." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } } ] } ] }   日本語でも質問を投げてみました。 「CDMPとは何ですか。特徴を3つ回答してください。」という問いかけに対して、下記の回答が返ってきました。内容はそれっぽいことを言ってくれています。※少し間違っている気もしますが 「CDMPはInformaticaのData Marketplaceの略称です。 特徴としては、\n1. データソースを検索・取得できるマーケットプレイス\n2. データを統合・変換できるインテグレーション機能\n3. セキュリティ機能が充実\nが挙げられます。」 出力の「retrievedReferences」を確認すると、Amazon S3に格納したPDFファイルを参照していることも確認できました。 [cloudshell-user@ip-10-132-39-170 tmp]$ aws bedrock-agent-runtime retrieve-and-generate \ > --input text="CDMPとは何ですか。特徴を3つ回答してください。" \ > --retrieve-and-generate-configuration type=KNOWLEDGE_BASE,knowledgeBaseConfiguration="{knowledgeBaseId=5TSXYNE1QK,modelArn=arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2}" { "sessionId": "03a426cf-b8bc-4b44-9cb3-a0be05b200fb", "output": { "text": "CDMPはInformaticaのData Marketplaceの略称です。 特徴としては、\n1. データソースを検索・取得できるマーケットプレイス\n2. データを統合・変換できるインテグレーション機能\n3. セキュリティ機能が充実\nが挙げられます。" }, "citations": [ { "generatedResponsePart": { "textResponsePart": { "text": "CDMPはInformaticaのData Marketplaceの略称です。", "span": { "start": 0, "end": 38 } } }, "retrievedReferences": [ { "content": { "text": "status=ACTIVE https://{{CDMP_URL}}/ is the base URL and /api/v1/integration/provisioning/deliveryTemplates is the API endpoint. How to call a Data Marketplace API 9 https://network.informatica.com/docs/DOC-19140 The base URL varies based on your region. The following table shows the regions and their corresponding base URLs: Region Base URL United States of America https://cdgc-api.dm-us.informaticacloud.com/cdmp-marketplace/ United Kingdom https://cdgc-api.dm-uk.informaticacloud.com/cdmp-marketplace/ Canada https://cdgc-api.dm-na.informaticacloud.com/cdmp-marketplace/ Europe, Middle East, Africa (EMEA) https://cdgc-api.dm-em.informaticacloud.com/cdmp-marketplace/ Asia, Pacific https://cdgc-api.dm-ap.informaticacloud.com/cdmp-marketplace/ Japan https://cdgc-api.dm-apne.informaticacloud.com/cdmp-marketplace/ You can call Data Marketplace APIs only via Application Integration. You can call only 100 APIs per minute." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } } ] }, { "generatedResponsePart": { "textResponsePart": { "text": "特徴としては、\n1. データソースを検索・取得できるマーケットプレイス\n2. データを統合・変換できるインテグレーション機能\n3. セキュリティ機能が充実\nが挙げられます。", "span": { "start": 40, "end": 125 } } }, "retrievedReferences": [ { "content": { "text": "status=ACTIVE https://{{CDMP_URL}}/ is the base URL and /api/v1/integration/provisioning/deliveryTemplates is the API endpoint. How to call a Data Marketplace API 9 https://network.informatica.com/docs/DOC-19140 The base URL varies based on your region. The following table shows the regions and their corresponding base URLs: Region Base URL United States of America https://cdgc-api.dm-us.informaticacloud.com/cdmp-marketplace/ United Kingdom https://cdgc-api.dm-uk.informaticacloud.com/cdmp-marketplace/ Canada https://cdgc-api.dm-na.informaticacloud.com/cdmp-marketplace/ Europe, Middle East, Africa (EMEA) https://cdgc-api.dm-em.informaticacloud.com/cdmp-marketplace/ Asia, Pacific https://cdgc-api.dm-ap.informaticacloud.com/cdmp-marketplace/ Japan https://cdgc-api.dm-apne.informaticacloud.com/cdmp-marketplace/ You can call Data Marketplace APIs only via Application Integration. You can call only 100 APIs per minute." }, "location": { "type": "S3", "s3Location": { "uri": "s3://ishihara-bedrock-knowledge/DMP_2023November_(API)Reference_en.pdf" } } } ] } ] } 「anthropic.claude-v2」を有効化しておく必要があります。また、はじめは「amazon.titan-text-express-v1」で試していたのですが、非対応だとエラーが出力されました。 おわりに Amazon Bedrockは初めてでしたが、数時間程度でお試し利用ができました。 API叩くだけで、ぱっとわかりやすい結果が返ってくるので非常に楽しかったです。また、PDFファイルも少ししか投入していないにもかかわらず、いい感じにそれっぽい回答をしてくれたことは驚きでした。 最後に、リソース削除はお忘れなく!
アバター
本記事は TechHarmony Advent Calendar 12/6付の記事です。 こんにちは、SCSK 西山です。 前回に引き続き、AWS User Notifications を使ってみたシリーズとなります。   AWS IAM ルートユーザー宛の通知を AWS User Notifications を使用して複数メールアドレスへ送信してみた AWS User Notifications を使用して、AWS から IAM ルートユーザー宛に配信される通知を複数メールアドレスに送信する機能を実装しましたので紹介します。 blog.usize-tech.com 2023.10.06   やりたいこと Amazon EC2 インスタンス停止を、いち早く通知メールで受信したい!! 簡単な処理の流れ: EC2が停止→User Notificationsで受信→登録したメールアドレスへ送信 AWS User Notifications AWSサービスからの通知を、一元的に設定して表示できるサービスです。 マネジメントコンソール上でAWSサービスからの通知を確認でき、User Notifications自体は 無料 で利用できます。 新着 – AWS の通知を一元的に設定する | Amazon Web Services 5月3日、AWS User Notifications をリリースしました。これは、AWS コンソール内で、複 aws.amazon.com 設定手順 通知ハブ 通知ハブより、通知メールを保存するリージョンを選択後、更新します。 配信チャ ネル ①今回はメールで受信したいので、配信チャネルより「Eメールの追加」を選択します。   ②”受信者” に送信先メールアドレスを入力し、名前を設定後、「Eメールの追加」で追加完了です。 通知設定 ①「通知設定を作成」を選択します。 ②名前を設定します。 ③イベントルールを設定します。 AWSのサービスの名前: EC2 を選択します。 イベントタイプ: EC2 Instance State-change Notificaions を選択します。 特定の状態: stopped を設定します。 特定のインスタンスID:監視対象の インスタンスID を入力します。 リージョン:監視対象の リージョン を選択します。 ④今回はいち早く通知メールを受け取りたいので、集約設定を 集約しない に設定します。 ⑤Eメールより、配信チャネルで設定した受信者を選択後、「通知設定を作成」で作成完了です。 EC2を停止して検証してみた 監視対象のEC2を停止したところ、即時通知メールが届きました!   まとめ 簡単な設定手順のみでEC2停止を即時確認できるので、是非お試しください!
アバター
  本記事は TechHarmony Advent Calendar 12/5付の記事です。 はじめに こんにちは。SCSKのふくちーぬです。 今回は、自動で Amazon API Gateway REST API 定義ファイルのバックアップを取得してみました。REST API には、API エンドポイントの情報を出力するエクスポート機能が備わっています。またエクスポート機能は、マネジメントコンソール・AWS CLIやSDKでサポートされています。 API Gateway から REST API をエクスポートする - Amazon API Gateway API Gateway から既存の REST API を OpenAPI およびその他の API 定義ファイルにエクスポートします。 docs.aws.amazon.com このAPI定義ファイルは、クライアントサイドからAPI呼び出しを利用した開発をするために、配布・提供することが一般的です。そのため最新のAPI定義ファイルを保管しておくことは重要なのです。しかし、みなさんAPIのデプロイ毎にポチポチと面倒なエクスポート処理をしていないでしょうか? SDKを使用したLambdaを活用することで、自動でAPI定義ファイルのバックアップを取得してみましょう。 環境準備編 ここでは、サンプルであるAPI Gateway + Lambdaをデプロイしておきます。既にAWSアカウント上で、デプロイ済みのAPI Gatewayが存在する場合は本作業は飛ばしていただいても結構です。 以前紹介した記事に、API Gateway + LambdaのCloudFormationテンプレートを用意しているのでご利用ください。 REST API を自動デプロイするための AWS CloudFormation テンプレートの記述テクニック Amazon API Gateway REST API を自動デプロイするためのちょっとした工夫についてお話しします。 blog.usize-tech.com 2023.11.27 CloudFormationテンプレートをデプロイできたら環境準備完了です。 REST APIのエクスポート機能について REST APIでステージへデプロイが完了すると、以下のようにAPI定義ファイルをエクスポートできる状態となります。 オプション機能として、API Gateway固有の設定(Lambda等)も含めてエクスポートすることも可能です。 またこのREST APIの定義ファイルは、標準規格であるOpen API仕様に基づいて作成されています。 OpenAPI Specification v3.1.0 | Introduction, Definitions, & More The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for HTTP APIs. spec.openapis.org 今回は、以下の設定でエクスポートしてみます。 API仕様タイプ 形式 拡張機能 Open API 3 YAML API Gateway拡張機能ありでエクスポート   検証①日時でバックアップを取得してみる 構成図 サンプルAPIとしてAPI Gateway + Lambdaがデプロイ済みです。 EventBridgeにて日時起動します。 トリガーが動いたことで、Lambdaが起動します。対象は、サンプルAPIを対象にエクスポート処理を実施します。 Lambdaにて取得したAPI定義ファイルをS3に保存します。 ソリューションのデプロイ 以下のCloudFormationテンプレートをデプロイしてください。 AWSTemplateFormatVersion: 2010-09-09 Parameters: ResourceName: Type: String APIId: Type: String APIStage: Type: String Resources: # ------------------------------------------------------------# # IAM Policy IAM Role # ------------------------------------------------------------# LambdaPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ${ResourceName}-lambda-policy Description: IAM Managed Policy with S3 PUT and APIGateway GET Access PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:PutObject' - 'apigateway:GET' Resource: - '*' LambdaRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${ResourceName}-lambda-role AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: - lambda.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - !GetAtt LambdaPolicy.PolicyArn # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# MyS3Bucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub ${ResourceName}-${AWS::AccountId}-bucket # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# APIExportFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub ${ResourceName}-lambda-function Role: !GetAtt LambdaRole.Arn Runtime: python3.11 Handler: index.lambda_handler Environment: Variables: RESTAPI_ID: !Ref APIId RESTAPI_STAGE: !Ref APIStage S3_BUCKET_NAME: !Sub ${ResourceName}-${AWS::AccountId}-bucket Code: ZipFile: !Sub | import boto3 import tempfile import os from datetime import datetime, timedelta import shutil # APIGatewayとS3の指定 client_apigateway = boto3.client('apigateway') client_s3 = boto3.client('s3') def lambda_handler(event, context): try: response = client_apigateway.get_export( restApiId= os.environ['RESTAPI_ID'], stageName= os.environ['RESTAPI_STAGE'], exportType= 'oas30', parameters= { "extensions": "apigateway" }, accepts= 'application/yaml' ) # ファイルを保存する一時ディレクトリのパスを作成 temp_dir = '/tmp/swagger' os.makedirs(temp_dir, exist_ok=True) # 変数の代入 # 現在のUTC時間を取得 utc_now = datetime.utcnow() # UTCから日本時間に変換(9時間を加算) jst_now = utc_now + timedelta(hours=9) # 日付と時刻 jst_time = jst_now.strftime("%Y-%m-%d_%H%M%S") # 日付のみ jst_date = jst_now.strftime("%Y-%m-%d") # S3バケット名 s3_bucket_name = os.environ['S3_BUCKET_NAME'] s3_object_key = 'apigateway/' + jst_date + '/' + jst_time + '_' + 'apigateway' + '_' + os.environ['RESTAPI_ID'] + '_' + os.environ['RESTAPI_STAGE'] + '.yaml' # YAMLファイルを一時ディレクトリに書き込み yaml_file_path = os.path.join(temp_dir, 'api_gateway.yaml') with open(yaml_file_path, 'w') as file: file.write(response['body'].read().decode('utf-8')) # S3にファイルをアップロード client_s3.upload_file(yaml_file_path, s3_bucket_name , s3_object_key) print('API Gateway YAML file uploaded to S3 successfully.') except Exception as e: # 例外が発生した場合の処理 print(f"An error occurred: {str(e)}") return { 'statusCode': 500, 'body': f'Error: {str(e)}' } finally: # 一時ディレクトリを削除 shutil.rmtree(temp_dir) return { 'statusCode': 200, 'body': 'API Gateway YAML file uploaded to S3 successfully.' } # ------------------------------------------------------------# # EventBridge # ------------------------------------------------------------# EventBridgeRule: Type: AWS::Events::Rule Properties: EventBusName: default Name: !Sub ${ResourceName}-eventbridge-rule ScheduleExpression: cron(0 15 * * ? *) State: ENABLED Targets: - Arn: !GetAtt APIExportFunction.Arn Id: Lambda PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Sub ${ResourceName}-lambda-function Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt 'EventBridgeRule.Arn'   IAM Policy IAM Role 簡単にCloudFormationテンプレートの説明をします。 Lambdaに付与するIAM権限として、以下の2つを指定しています。 ‘s3:PutObject’(S3にファイルをアップロードするための権限) ‘apigateway:GET’(API定義ファイルをエクスポートするための権限) S3 スタック削除後にAPI定義ファイルが格納されたS3が削除されないように、削除ポリシーにて”Retain”を指定しています。 Lambda 大きく2つの操作を行っています。 指定したREST APIのID及びデプロイされたステージに対して、API定義ファイルのエクスポート処理をする。 指定したS3バケットにAPI定義ファイルを格納する EventBridge 日本時間で、00時00分に起動するよう設定しています。 またEventBridgeがLambdaをターゲットとして起動できるようリソースポリシーを設定しています。 バックアップの確認 以下のように00時00分に指定のS3に、バックアップが取得できていることがわかります。 API定義ファイルを確認してみると、1つのGETメソッドの情報が記述されています。 openapi: "3.0.1" info: title: "sampleapi-20231130-apigateway" version: "2023-11-30T14:41:36Z" servers: - url: "https://jy67uff70h.execute-api.ap-northeast-1.amazonaws.com/{basePath}" variables: basePath: default: "dev" paths: /utcnow: get: x-amazon-apigateway-integration: type: "aws_proxy" httpMethod: "POST" uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:<awsアカウントid>:function:sampleapi-20231130-lambda-function-utcnow/invocations" passthroughBehavior: "when_no_match" components: {} 検証②デプロイ毎にバックアップを取得してみる 次は、ステージへのデプロイ(更新)ごとにバックアップを取得するパターンをみていきます。REST APIのエンドポイントの更新が行われる度に、API定義ファイルも最新版に保持しましょう。 構成図 基本的には、①と同様となります。イベント実行をしている箇所のみ異なります。 EventBridgeにて対象APIのデプロイ毎に起動します。 トリガーが動いたことで、Lambdaが起動します。対象は、サンプルAPIを対象にエクスポート処理を実施します。 Lambdaにて取得したAPI定義ファイルをS3に保存します。 ソリューションのデプロイ 以下のCloudFormationテンプレートをデプロイしてください。 基本的には、①のテンプレートと同様です。EventBridgeにてイベント実行している箇所のみ異なります。 AWSTemplateFormatVersion: 2010-09-09 Parameters: ResourceName: Type: String APIId: Type: String APIStage: Type: String Resources: # ------------------------------------------------------------# # IAM Policy IAM Role # ------------------------------------------------------------# LambdaPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ${ResourceName}-lambda-policy Description: IAM Managed Policy with S3 PUT and APIGateway GET Access PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:PutObject' - 'apigateway:GET' Resource: - '*' LambdaRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${ResourceName}-lambda-role AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: - lambda.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - !GetAtt LambdaPolicy.PolicyArn # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# MyS3Bucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub ${ResourceName}-${AWS::AccountId}-bucket # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# APIExportFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub ${ResourceName}-lambda-function Role: !GetAtt LambdaRole.Arn Runtime: python3.11 Handler: index.lambda_handler Environment: Variables: RESTAPI_ID: !Ref APIId RESTAPI_STAGE: !Ref APIStage S3_BUCKET_NAME: !Sub ${ResourceName}-${AWS::AccountId}-bucket Code: ZipFile: !Sub | import boto3 import tempfile import os from datetime import datetime, timedelta import shutil # APIGatewayとS3の指定 client_apigateway = boto3.client('apigateway') client_s3 = boto3.client('s3') def lambda_handler(event, context): try: response = client_apigateway.get_export( restApiId= os.environ['RESTAPI_ID'], stageName= os.environ['RESTAPI_STAGE'], exportType= 'oas30', parameters= { "extensions": "apigateway" }, accepts= 'application/yaml' ) # ファイルを保存する一時ディレクトリのパスを作成 temp_dir = '/tmp/swagger' os.makedirs(temp_dir, exist_ok=True) # 変数の代入 # 現在のUTC時間を取得 utc_now = datetime.utcnow() # UTCから日本時間に変換(9時間を加算) jst_now = utc_now + timedelta(hours=9) # 日付と時刻 jst_time = jst_now.strftime("%Y-%m-%d_%H%M%S") # 日付のみ jst_date = jst_now.strftime("%Y-%m-%d") # S3バケット名 s3_bucket_name = os.environ['S3_BUCKET_NAME'] s3_object_key = 'apigateway/' + jst_date + '/' + jst_time + '_' + 'apigateway' + '_' + os.environ['RESTAPI_ID'] + '_' + os.environ['RESTAPI_STAGE'] + '.yaml' # YAMLファイルを一時ディレクトリに書き込み yaml_file_path = os.path.join(temp_dir, 'api_gateway.yaml') with open(yaml_file_path, 'w') as file: file.write(response['body'].read().decode('utf-8')) # S3にファイルをアップロード client_s3.upload_file(yaml_file_path, s3_bucket_name , s3_object_key) print('API Gateway YAML file uploaded to S3 successfully.') except Exception as e: # 例外が発生した場合の処理 print(f"An error occurred: {str(e)}") return { 'statusCode': 500, 'body': f'Error: {str(e)}' } finally: # 一時ディレクトリを削除 shutil.rmtree(temp_dir) return { 'statusCode': 200, 'body': 'API Gateway YAML file uploaded to S3 successfully.' } # ------------------------------------------------------------# # EventBridge # ------------------------------------------------------------# EventBridgeRule: Type: AWS::Events::Rule Properties: EventBusName: default Name: !Sub ${ResourceName}-eventbridge-rule EventPattern: source: - "aws.apigateway" detail: eventSource: - "apigateway.amazonaws.com" eventName: - "CreateDeployment" requestParameters: restApiId: - !Ref APIId State: ENABLED Targets: - Arn: !GetAtt APIExportFunction.Arn Id: Lambda PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Sub ${ResourceName}-lambda-function Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt 'EventBridgeRule.Arn' Event Bridge 対象APIにてデプロイが行われるとEventBridgeが起動するようイベントパターンを設定しています。 ステージへの再デプロイ 今回は、自動デプロイ機能を利用してREST APIを自動更新します。もしくは、ご自身でREST APIに何らかの変更を加えた上で、ステージへデプロイしていただいても構いません。 以下の記事で紹介しているので、是非こちらのCloudFormationテンプレートを利用してデプロイしてください。 REST API を自動デプロイするための AWS CloudFormation テンプレートの記述テクニック Amazon API Gateway REST API を自動デプロイするためのちょっとした工夫についてお話しします。 blog.usize-tech.com 2023.11.27 今回は、以下構成のようにサンプルAPIのメソッドを1つ追加しています。  バックアップの確認 デプロイが行われると指定のS3に、バックアップが取得できていることがわかります。 API定義ファイルにも、2つのメソッド情報がきちんと記述されていますね。 openapi: "3.0.1" info: title: "sampleapi-20231130-apigateway" version: "2023-12-01T15:53:53Z" servers: - url: "https://jy67uff70h.execute-api.ap-northeast-1.amazonaws.com/{basePath}" variables: basePath: default: "dev" paths: /jstnow: get: x-amazon-apigateway-integration: httpMethod: "POST" uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:<awsアカウントid>:function:sampleapi-20231130-lambda-function-jstnow/invocations" passthroughBehavior: "when_no_match" type: "aws_proxy" /utcnow: get: x-amazon-apigateway-integration: httpMethod: "POST" uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:<awsアカウントid>:function:sampleapi-20231130-lambda-function-utcnow/invocations" passthroughBehavior: "when_no_match" type: "aws_proxy" components: {} まとめ いかがだったでしょうか。自動でREST APIのAPI定義ファイルのバックアップを取得してみました。 API Gatewayを構築したら終わりではなく、必ずAPIの利用者は存在しますのでAPI定義ファイルの面倒まで見てあげることが大切ですね。 ご利用の際は、要件に応じてカスタマイズ等していただければと思います。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
アバター
本記事は TechHarmony Advent Calendar 12/4付の記事です 。 こんにちは、SCSK北村です。 Amazon EC2 インスタンスのリソースデータを取得するために、Datadog API を使ってみました。 結論として、期間を指定してCPUの使用率データをCSVファイルで出力することができました。 今回は、備忘も兼ねてファイル出力までの流れをまとめたいと思います。 事前準備 実行環境は以下の通りです。 OS:Windows プログラミング言語:Python 3.12.0 Datadog API を使用するために必要なライブラリをインストールします。 対象:datadog-api-client ※Python3.7以上で利用可能 pip install datadog-api-client   GitHub - DataDog/datadog-api-client-python: Python client for the Datadog API Python client for the Datadog API. Contribute to DataDog/datadog-api-client-python development by creating an account on GitHub. github.com 実際にやったこと 基本的には下記リンク[※1]を見ながらスクリプトを作成していきました。 メトリクス Datadogが大規模なクラウドのモニタリングサービスをリードします。 docs.datadoghq.com 手順を追って紹介しますが、全体のコードをまず見たい方は読み飛ばしてください。 APIキーとAPPキーの準備 Datadog APIを利用する際、APIキーとAPPキーによる認証が必要になります。 今回、YAML形式で別ファイルに認証情報を記載し、プログラムから読み取っています。 datadog: api_key: <ここにAPI KEYを入力> app_key: <ここにAPP KEYを入力> リクエストで送信するクエリを作成 リソースデータの取得には、TimeseriesFormulaQueryRequestメソッドを使用して、まずリクエストボディを作成します。 公式リファレンス[※1]にサンプルコードが掲載されているので、要件に合わせてボディをカスタマイズしていきます。 今回はCPU使用率データの取得ということで、以下のようにカスタマイズしました。 クエリを任意のものに書き換えれば、メモリ使用率やディスク使用率などのDatadogで収集しているデータは取得可能です! Datadog のメトリクスエクスプローラーを使用すると、クエリを色々と試せるので便利です。 メトリクスエクスプローラー すべてのメトリクスを調査し分析する docs.datadoghq.com # リクエストで送信するクエリを定義 QUERY1 = 'avg:system.cpu.idle{name:'+target+'} by {name}' #EC2のNameタグ(target)でフィルタリング # リクエストボディ body = TimeseriesFormulaQueryRequest( data=TimeseriesFormulaRequest( attributes=TimeseriesFormulaRequestAttributes( formulas=[ QueryFormula( formula='100 - q1', limit=FormulaLimit( count=10, order=QuerySortOrder.DESC, ), ), ], _from=time_from*1000, #データ取得の始点(UNIX時間) interval=300000, #データ取得間隔(ミリ秒) queries=TimeseriesFormulaRequestQueries( [ MetricsTimeseriesQuery( data_source=MetricsDataSource.METRICS, query=QUERY1, name='q1', ), ] ), to=time_to*1000, #データ取得の終点(UNIX時間) ), type=TimeseriesFormulaRequestType.TIMESERIES_REQUEST, ), ) Datadog APIへリクエスト送信 作成したリクエストボディをセットして、Datadog APIにリクエストを送信します。 その際に、前述の認証情報が必要なので、あわせてセットします。 問題なければ、response 変数にDatadog APIからのレスポンスデータが格納されます。 configuration = Configuration() configuration.unstable_operations['query_timeseries_data'] = True # Datadog API にリクエスト送信 with ApiClient(configuration) as api_client: api_client.default_headers['DD-API-KEY'] = api_key api_client.default_headers['DD-APPLICATION-KEY'] = app_key api_instance = MetricsApi(api_client) response = api_instance.query_timeseries_data(body=body) レスポンスデータを整形してCSVに書き出す まず、ファイルに書き込みたい情報をレスポンスデータから取得します。 データは、”<レスポンスデータのキー>.value”で取り出すことができました。 # ホスト名を取得 res_series = response['data']['attributes']['series'].value group_tag = res_series[0]['group_tags'].value[0].split(':') hostname = group_tag[-1] # リソースデータのリストを取得 res_values = response['data']['attributes']['values'].value # 時間データのリストを取得 res_times = response['data']['attributes']['times'].value レスポンスデータに含まれる時間はUNIX時間なので、日本時間へ変換します。 # UNIX時間を日本時間に変換した結果を格納するリスト timeseries = [] # UNIX時間を秒に変換 (ミリ秒から秒に変換) for epoch_time in res_times: epoch_time_sec = epoch_time / 1000 # UNIX時間を日本時間に変換 jst = datetime.datetime.fromtimestamp( epoch_time_sec, datetime.timezone(datetime.timedelta(hours=9)) ) # 指定された形式で日本時間をフォーマット jst_formatted = jst.strftime('%Y/%m/%d %H:%M') timeseries.append(jst_formatted) 複数のEC2インスタンスのデータを取得するので、インスタンス毎にファイルを分けて出力するようにしています。 CSVファイルの中身は、1列目が「時間」、2列目が「CPU使用率の値」としています。 # hostname ごとにデータをまとめる grouped_data = {} grouped_data[hostname] = {'values': res_values[0]} # ファイル保存先 SAVE_DIR = f'result/{dt}/{TARGET_RESOURCE}/' # SAVE_DIR が存在しなかったら作成 if not os.path.exists(SAVE_DIR): os.makedirs(SAVE_DIR) # データをCSVファイルに書き込む for group_data in grouped_data.values(): values = group_data['values'] filepath = SAVE_DIR + f'{hostname}_CPU.csv' with open(filepath, mode='w', newline='') as csvfile: writer = csv.writer(csvfile) for i in range(len(timeseries)): row = [timeseries[i], values.value[i]] writer.writerow(row) 作成したコード cpu_util.py 今回作成したコードの全体です。 import os import csv import datetime import logging.config import yaml from datadog_api_client import ApiClient, Configuration from datadog_api_client.v2.api.metrics_api import MetricsApi from datadog_api_client.v2.model.formula_limit import FormulaLimit from datadog_api_client.v2.model.metrics_data_source import MetricsDataSource from datadog_api_client.v2.model.metrics_timeseries_query import MetricsTimeseriesQuery from datadog_api_client.v2.model.query_formula import QueryFormula from datadog_api_client.v2.model.query_sort_order import QuerySortOrder from datadog_api_client.v2.model.timeseries_formula_query_request import TimeseriesFormulaQueryRequest from datadog_api_client.v2.model.timeseries_formula_request import TimeseriesFormulaRequest from datadog_api_client.v2.model.timeseries_formula_request_attributes import TimeseriesFormulaRequestAttributes from datadog_api_client.v2.model.timeseries_formula_request_queries import TimeseriesFormulaRequestQueries from datadog_api_client.v2.model.timeseries_formula_request_type import TimeseriesFormulaRequestType # 環境設定 CONFIG = 'config/credentials.yaml' EC2_TARGETS = 'config/target_ec2_list.yaml' TARGET_RESOURCE = 'cpu' LOG_DIR = 'log/' # LOG_DIR が存在しなかったら作成 if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) with open('config/log_config.yaml', 'r') as config_file: config = yaml.safe_load(config_file) logging.config.dictConfig(config) logger = logging.getLogger('cpu') def get_cpu_utilization(api_key, app_key, time_from, time_to, target, dt): try: # リクエストで送信するクエリを定義 QUERY1 = 'avg:system.cpu.idle{name:'+target+'} by {name}' # リクエストボディ body = TimeseriesFormulaQueryRequest( data=TimeseriesFormulaRequest( attributes=TimeseriesFormulaRequestAttributes( formulas=[ QueryFormula( formula='100 - q1', limit=FormulaLimit( count=10, order=QuerySortOrder.DESC, ), ), ], _from=time_from*1000, interval=300000, queries=TimeseriesFormulaRequestQueries( [ MetricsTimeseriesQuery( data_source=MetricsDataSource.METRICS, query=QUERY1, name='q1', ), ] ), to=time_to*1000, ), type=TimeseriesFormulaRequestType.TIMESERIES_REQUEST, ), ) configuration = Configuration() configuration.unstable_operations['query_timeseries_data'] = True # Datadog API にリクエスト送信 with ApiClient(configuration) as api_client: api_client.default_headers['DD-API-KEY'] = api_key api_client.default_headers['DD-APPLICATION-KEY'] = app_key api_instance = MetricsApi(api_client) response = api_instance.query_timeseries_data(body=body) # ホスト名を取得 res_series = response['data']['attributes']['series'].value group_tag = res_series[0]['group_tags'].value[0].split(':') hostname = group_tag[-1] # リソースデータのリストを取得 res_values = response['data']['attributes']['values'].value # 時間データのリストを取得 res_times = response['data']['attributes']['times'].value # UNIX時間を日本時間に変換した結果を格納するリスト timeseries = [] # UNIX時間を秒に変換 (ミリ秒から秒に変換) for epoch_time in res_times: epoch_time_sec = epoch_time / 1000 # UNIX時間を日本時間に変換 jst = datetime.datetime.fromtimestamp( epoch_time_sec, datetime.timezone(datetime.timedelta(hours=9)) ) # 指定された形式で日本時間をフォーマット jst_formatted = jst.strftime('%Y/%m/%d %H:%M') timeseries.append(jst_formatted) # hostname ごとにデータをまとめる grouped_data = {} grouped_data[hostname] = {'values': res_values[0]} # ファイル保存先 SAVE_DIR = f'result/{dt}/{TARGET_RESOURCE}/' # SAVE_DIR が存在しなかったら作成 if not os.path.exists(SAVE_DIR): os.makedirs(SAVE_DIR) # データをCSVファイルに書き込む for group_data in grouped_data.values(): values = group_data['values'] # ファイル保存先パス・ファイル名 filepath = SAVE_DIR + f'{hostname}_CPU.csv' with open(filepath, mode='w', newline='') as csvfile: writer = csv.writer(csvfile) for i in range(len(timeseries)): row = [timeseries[i], values.value[i]] writer.writerow(row) logger.info(f"データが書き込まれました: '{filepath}'") except Exception as e: # 例外処理 logger.error(f'予期せぬエラーが発生しました: {str(e)}') else: # 処理成功 logger.info(f'[{target}] の処理が完了しました') def main(): try: logger.info(f'リソースデータ[{TARGET_RESOURCE}] の取得を開始します') # 設定ファイルを読み込む with open(CONFIG, 'r') as config_file: config = yaml.safe_load(config_file) # APIキーとAPPキーを取得 api_key = config['datadog']['api_key'] app_key = config['datadog']['app_key'] # 設定ファイルを読み込む with open(EC2_TARGETS, 'r') as ec2_file: ec2 = yaml.safe_load(ec2_file) # データ取得する対象を読み込む target_list = ec2['hostname'] # 現在の日付を取得 today = datetime.date.today() # 当月の初日を計算 first_day_of_current_month = datetime.datetime(today.year, today.month, 1) # 前月の初日を計算 if today.month == 1: # 1月の場合、前年の12月の初日になる first_day_of_last_month = datetime.datetime(today.year - 1, 12, 1) dt = str(today.year - 1) + str(12) # for 'SAVE_DIR' path else: # それ以外の場合、前月の初日になる first_day_of_last_month = datetime.datetime(today.year, today.month - 1, 1) dt = str(today.year) + str(today.month - 1) # for 'SAVE_DIR' path # UNIX時間に変換 time_from = int(first_day_of_last_month.timestamp()) # 前月初日 time_to = int(first_day_of_current_month.timestamp()) # 当月初日 # 対象毎にループ処理 for target in target_list: logger.info(f'[{target}] の処理を開始します') get_cpu_utilization(api_key, app_key, time_from, time_to, target, dt) except FileNotFoundError as e: # ファイルが存在しない場合の例外処理 logger.error(f'設定ファイルが見つかりません: {str(e)}') except IOError as e: # その他の入出力関連のエラーに対する例外処理 logger.error(f'入出力エラーが発生しました: {str(e)}') except Exception as e: # その他の例外に対する例外処理 logger.error(f'予期せぬエラーが発生しました: {str(e)}') else: # 処理成功 logger.info(f'リソースデータ[{TARGET_RESOURCE}] の取得が完了しました') if __name__ == '__main__': logger.info('START SCRIPT') main() logger.info('END SCRIPT') credentials.yaml Datadog APIにリクエストを送信する際の認証情報をここに記載します。 datadog: api_key: <ここにAPI KEYを入力> app_key: <ここにAPP KEYを入力> log_config.yaml loggingモジュールでログを出力させるための設定ファイルです。 ターミナルとファイルのそれぞれに出力できるように記載しています。 version: 1 formatters: simple_fmt: format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" datefmt: '%Y/%m/%d %H:%M:%S' handlers: console: class: logging.StreamHandler level: DEBUG formatter: simple_fmt stream: ext://sys.stdout file: class: logging.handlers.TimedRotatingFileHandler level: DEBUG formatter: simple_fmt filename: log/app.log encoding: utf-8 when: midnight interval: 30 backupCount: 3 loggers: cpu: level: INFO handlers: [console, file] propagate: no root: level: DEBUG handlers: [console, file] target_ec2_list.yaml データ取得対象のEC2インスタンスをこのファイルに記載し、プログラムから取得しています。 # EC2インスタンスのNameタグの値を以下に記載 hostname: - server01 - server02 - server03 出力ファイルのサンプル 今回作成したプログラムでは以下のように出力されます。 プログラムを実行した日付から、前月1か月分のCPU使用率が5分間隔で取得することができました。 2023/10/01 00:00,9.209855 2023/10/01 00:05,8.721778 2023/10/01 00:10,8.922941 2023/10/01 00:15,8.763545 2023/10/01 00:20,8.784494 2023/10/01 00:25,8.795538 2023/10/01 00:30,8.714313 2023/10/01 00:35,8.817114 2023/10/01 00:40,8.730093 2023/10/01 00:45,8.940528 2023/10/01 00:50,8.863131 2023/10/01 00:55,8.967478 ・・・ あとがき 運用業務の中でサーバリソースの使用状況を確認する場面が出てくると思います。 取得するサーバ台数やメトリクスが多くなると、コンソールのGUI操作でデータ取得作業は結構手間がかかりますよね。 今回、Datadog APIを使用して自動化ができたことで、この運用がかなり楽になりました。 今後はAWS Lambdaへの移行も検討していきたいと考えています。 本記事が皆様のお役に立てれば幸いです。
アバター
本記事は TechHarmony Advent Calendar 12/3付の記事です 。 こんにちは。SCSKの川原です。 皆さんは、ウェブサイトを訪れた際、期待したページが表示されずフラストレーションを感じたことがありませんか? 今回は、そんなネガティブな体験を改善するカスタムエラーページをAmazon CloudFrontのカスタムレスポンス機能を用いて設定する方法についてまとめてみました! Amazon CloudFrontのカスタムレスポンスについて Amazon CloudFrontのカスタムエラーレスポンスは、特定のHTTPステータスコードを返すときに表示されるエラーページをカスタマイズする機能です。CloudFront がカスタムエラーページを返すことのできる HTTP ステータスコードは、以下のとおりです。 400、403、404、405、414、416 500、501、502、503、504 それぞれのステータスコードごとにカスタムエラーページを設定できます。これにより、ユーザーがウェブサイトでエラーに遭遇したときに、デフォルトのエラーメッセージではなく、独自のメッセージやデザインを表示することができます。 今回の構成 以下、構成図になります。今回は、サーバーが停止した場合を想定し、その際のステータスコード504が発生した際に、AmazonS3から504エラー用のエラーページを返すよう設定します。 カスタムエラーレスポンスの設定 AmazonS3をAmazon CloudFrontのオリジンに追加 まずは、エラーページをホストするAmazonS3をCloudFrontのオリジンに追加します。 AWS管理コンソールからCloudFrontに移動し、編集したいディストリビューションを選択します。 カスタムエラーページをホストするオリジン(AmazonS3)をCloudFrontに追加します。これにより、エラーページのファイルをディストリビューションのキャッシュ動作に組み込みます。 ビヘイビアとして、カスタムエラーページのリクエストがルーティングされる場所を指定します。今回設定するS3のパスパターンは/contents/*です。 カスタムエラーページの作成 次に、HTMLファイルなどでカスタムエラーページを作成し、ホスティングするためのオブジェクトとしてAmazon S3バケットの「contents」フォルダにアップロードします。 ここで、注意点があります。HTMLファイルでCSSファイルや画像ファイルを読み込む際は、 絶対パス で設定してください。以下は、HTMLファイルの一部になりますがCSSファイルとして絶対パスを設定しています。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" type="text/css" href="https://(WebサイトのFQDN)/contents/504error.css"> <title>504 GatewayTimeout</title> <meta name="viewport" contest="width=device-width, initial-scale=1"> </head> 相対パスで設定してしまうと以下のようにCSSや画像ファイルの参照できません。 私は、ここで相対パスを指定しまい、躓きました。。。 CloudFrontでエラーページ設定 それでは、AmazonS3にファイルのアップロードができたところで、CloudFrontでエラーページの設定をしましょう。 CloudFrontに移動し、エラーページを設定するディストリビューションを選択します。 「エラーページ」タブをクリックし、「カスタムエラーレスポンスの作成」をクリックします。 「HTTPエラーコード」にカスタマイズしたいHTTPステータスコードを入力します。 今回は「504」を選択します。 「エラーコードのTTL」にエラーページがキャッシュされる時間(秒)を入力します。 「カスタムエラーレスポンス」を「Yes」に設定します。 「応答ページパス」に、S3バケット内のカスタムエラーページへのパスを入力します。 今回は「/contents/504error.html」とします。 「HTTPレスポンスコード」に、カスタムエラーページを返す際のHTTPステータスコードを入力します。 「作成」ボタンをクリックして設定を保存します。 サーバー(EC2)を停止してみる それでは、エラーページ設定をしたところで、実際にサーバー(EC2)を停止してみます! すると、以下のように504エラーで設定したエラーページが表示されました。きちんとCSSファイルと画像ファイルも参照されました。 まとめ CloudFrontカスタムエラレスポンスを使用して任意のエラーページを設定することで、予期せぬエラーがユーザーに混乱をもたらすことを防ぎ、適切なガイダンスを提供することで、ユーザーエクスペリエンスの向上、信頼性の確保に繋がります。 また、標準のエラーページは、ウェブサイトのデザインとは一致しない可能性があります。カスタムエラーページを使用すると、ブランドイメージの一貫性を保つことができます。 CloudFrontのカスタムエラーレスポンスを使用することで簡単にメンテナンスページなどさまざまなパターンのエラーページが作成できるのでぜひご活用ください!ありがとうございました!
アバター
本記事は TechHarmony Advent Calendar 12/2付の記事です。 こんにちは。SCSKの江木です。 Goolge Cloud Next Tokyo ’23に参加してきたので、イベントの内容と感想を投稿します。 Google Cloud Next Tokyo ’23とは? Google Cloud Next Tokyo ’23は2023年11月15日・16日に東京ビッグサイトで開催されたGoogle Cloudのカンファレンスイベントです。日本では4年ぶりの現地開催となりました。 本イベントは主に以下の内容で構成されています。 基調講演 セッション ブレイクアウトセッション ハンズオンセッション Expo Innovators Hive 基調講演と各セッションではGoogle Cloudの最新情報や技術動向、国内外の企業での活用事例紹が紹介されます。ExpoではGoogle Cloudの最新サービス・ソリューション、パートナーのデモを体験できます。Innovators Hiveではデベロッパーやエンジニア向けに、Google Cloudのデモ体験や認定資格の最新情報が提供されます。 Google Cloudの最新情報を知れるだけでなく、パートナーや資格取得者同士の交流を深められるイベントになっております。 参加した基調講演・セッション 多くの基調講演・セッションに参加したので、表にまとめてみました。 日時 基調講演・セッションタイトル 11/15 10:00~11:30 DAY1 基調講演 11/15 12:00~12:40 生成AIを活用したコンタクトセンターの顧客体験の変革 11/15 13:00~13:40 中外製薬がGoogle Cloudで進めるスケーラブルなMLOpsの構築と創薬への活用 11/15 15:00~15:40 Google AIと電通デジタル∞AIの融合:PaLM2とVertex AIを活用したAI実装の新境地 11/15 16:00~16:40 データ駆動型ヘルスケア:医療イノベーションの新時代 11/15 17:00~17:40 生成AI時代のMLOps実現方法とは? 11/16 10:00~11:30 DAY2 基調講演 11/16 13:00~13:40 GKEを使い倒して機械学習/HPCに最適化されたKubernetes基盤を作る方法 11/16 14:00~14:40 Street Fighter 6 アーキテクチャー大解剖 それぞれの基調講演・セッションについて記載していきます。 DAY1 基調講演 概要 生成AI、AIに関する新しいサービス、Google Cloudの生成AIソリューションの導入事例の発表 内容・感想 生成AIは今年最も話題になったテクノロジーであると話されていました。しかし、生成AIにはハルシネーション(嘘をつくこと)という課題があるので、大胆かつ責任あるAIが求められているそうです。 AIに関する新しいサービスが発表されていました!様々なサービスが発表されていましたが、その中でもDuet AI in Google Workspaceが気になります!すでに英語版はリリースされているのですが、ついに日本語対応するそうです。Duet AI in Google Workspaceではブログ作成、スライド作成、議事録作成を自動でしてくれるそうです!議事録作成を自動でやってくれたら会議に集中できるので、どれだけありがたいことか… また、中外製薬株式会社様、株式会社ZOZO様の事例を紹介されていました。中外製薬様ではAlphaFold2とMed-PaLM2を使用して、創薬プロセスの効率化を図ったとのことでした。ZOZO様では、コーディネートの説明文をvertexAIとPaLM2を使用して生成したとのことでした。 生成AIを活用したコンタクトセンターの顧客体験の変革 概要 コンタクトセンターAI(CCAI)の主なサービス紹介と新機能発表 内容・感想 コンタクトセンターには、研修、業務効率と顧客、コスト効率などの観点で課題があるそうです。AIがこれらの課題解決になるのはわかっているが、解決に導くのが難しいとのことでした。 CCAIのサービスとして、Dialogflow CX、Agent Assist、Insights、CCAI Platformが紹介されていました。また、Dialogflow CXと連携しているvertex AI conversationというサービスを用いて、社内・社外データにグラウンディングされた生成AIを利用したカスタムチャットボットを作成できるとのことでした。このサービスでハルシネーションを防げそうですね! 本セッションを通して、CCAIの全体像を把握することができました。 中外製薬がGoogle Cloudで進めるスケーラブルなMLOpsの構築と創薬への応用 概要 中外製薬様で行われた創薬プロセスにおけるAI活用 内容・感想 1つの薬を生み出すには、10~15年の年月と3000億以上のコストがかかっていたそうです。また、これだけの時間とお金をかけても成功率は0.004%だそうです… これだけコストがかかる創薬プロセスにおいて、タンパク質構造予測の自動化と臨床試験計画の手助けに生成AIのサービスを使用したとのことでした。 タンパク質構造予測にはAlphaFold2を使用したそうです。AlphaFold2はアミノ酸配列を入力すると、タンパク質立体構造を出力します。「未知のタンパク質の構造を知りたい」、「構造データが欲しい」、「立体評価を行いたい」というときに使用するそうです。 臨床試験計画にはMed-PaLM2を使用したそうです。Med-PaLM2はPaLM2を医学的な分野に特化させたモデルで、医学的な質問に高品質な回答をするそうです。プロンプトエンジニアリングがいらないほど、精度が良いみたいです。 このセッションを聞くまで、創薬プロセスにおいてどのようにAIを活用するのか検討がつかなかったので、非常に勉強になりました。 GoogleAIと電通デジタル∞AIの融合:PaLM2とVertexAIを活用したAI実装の新境地 概要 ビジネス・マーケティングの次世代化と∞AIのサービスの紹介 内容・感想 ビジネス・マーケティングでは「新たな価値/事業の創出・顧客対応の自動化→リッチなユーザニース・AIの成長→社内業務効率化・生産性向上」のAI利活用サイクルを回すことが大切であるとお話しされておりました。また、顧客ニーズは対話から引き出されるとのことでした。自分の経験でも身に覚えがあるので、かなり納得してしまいました。 ∞AIのサービスとして、キャッチコピー案自動生成を行う「∞AI ads」と企業と顧客の対話を行う「∞AI Chat/Content」というサービスが紹介されていました。∞AI Contentにはチャットボットのコンシェルジュを自分で作ることができる機能があるそうで、かなり面白いと感じました。 PaLM2とVertex AIの話をあまり聞くことができませんでしたが、顧客のニーズや情報を引き出すためには対話が大事であるということを学ぶことができました。 データ駆動型ヘルスケア:医療イノベーションの新時代 概要 株式会社エムネス様の画像診断支援AIサービスとPSP株式会社様医療データ活用のサービスの紹介 内容・感想 医療業界はデジタル化が最も遅れている業界であることをお話しされてしました。 画像診断AIサービスは放射線科医の診断を手助けするためのサービスだそうです。医療データ活用サービスとしては、医用画像の保管を行うクラウドPACS、個人の健康を自分で管理するPHRアプリ、画像診断レポートから病名を抽出するツールを紹介していました。 医療業界ではデータドリブンが非常に重要であるということがわかりました。 生成AI時代のMLOps実現方法とは? 概要 生成AIのMLOpsについての説明とMLOpsに必要なサービスの紹介(vertex AI) 内容・感想 MLOpsはMLシステムを迅速かつ確実に構築、導入、運用するための標準化されたプロセスと機能のセットのことで、事前学習モデルの選定とプロンプト、アーティファクト、生成AIのアウトプットの評価、エンタープライズデータとの接続の話をされていました。 また、生成AI評価サービスとして、Automatic Metrics、AutoSxS、Safety Biasを発表していました。 実稼働するシステムを創るためには生成AIのアウトプットの評価とエンタープライズデータとの接続が重要であると感じました。 DAY2 基調講演 概要 IaaSの新サービスの発表、Google CloudのIaaS導入事例の紹介、Google Workspaceの活用事例、Google for Startupsの紹介 内容・感想 Google Cloudのインフラストラクチャーサービスの歴史の紹介に始まり、新サービスの発表がされました。Google Cloudのインフラストラクチャーサービスの導入事例として、株式会社カプコン様、北國銀行FHD様が紹介されていました! Google Workspaceの導入事例として、TBS様が紹介されていました。Google Workspaceを展開したことでファイル共有が楽になったそうです。今後の展望としてはAppSheet、Duet AIの活用を掲げていました。 Google for Startupsは資金面、技術面、ビジネス面でスタートアップ企業をサポートするプログラムで、事例として、LUUPが紹介されていました。電動キックボードシェアで有名なLUUPがGoogle Cloudを利用していたことに驚きました。 GKEを使い倒して機械学習/HPCに最適化されたKubernetes基盤を作る方法 概要 GKE上で機械学習/HPCを行うためのリソースの選択、考慮するべきポイントを紹介 内容・感想 機械学習/HPCにGKEを使う理由として、「コンテナによる高い再現性とOSSエコシステム・フレームワークの活用」と「細かなパフォーマンスチューニングや柔軟なインフラ構成を実現」を挙げられていた。 「コンピュートリソースのコスト効率を高めるために、GPUを複数ワークロードで共有する機能やSpotVMsの活用する」、「CA(Cluster Autoscaler)やNAP(Node Auto Provisioning)を活用し、動的にNodeを増減できるようにする」など技術的な紹介が非常に多い印象でした。 Street Fighter 6 アーキテクチャー大解剖 概要 格闘ゲームであるStreet Fighter 6の概要とアーキテクチャーを紹介 内容・感想 近日話題かつ、私が普段お世話になっているStreet fighter 6が、どのようなアーキテクチャーなのかを紹介していました。 クロスプレイ、グローバル展開ということでCloud SpannerやGKEなどでアーキテクチャが実装されていました。このアーキテクチャーはランキングやユーザ管理の機能を実装するためのアーキテクチャーで、対戦中の通信はラグを考慮して、P2P接続だそうです。また、Spannerの良いところや悪いところ、Spannerでの実装テクニックを紹介していました。 普段プレイしているゲームの中身を見ることができて、とても楽しかったです。 見学したブース セッションの隙間時間にExpoとInnovators Hiveの2つのエリアでブース見学をしてきました! エリアごとにブースの内容を紹介していきます! Expo 生成AI対応の新しい情報検索基盤 Vertex AI Search  Vertex AI Searchについて紹介していました。 Vertex AI Searchは企業内のドキュメントやデータベースから情報収集し、AIによる「意味検索」とキーワード検索を実現する検索エンジンです。生成AIによる要約、RAG(検索結果を参考にして、生成を補強する手法)、マルチモーダル検索に対応しています。 ブースではRAGのコーディングによる実装を見せてもらいました。 PaLM活用│次世代チャットアプリ LLMであるPaLM2を用いたチャットアプリを紹介していました。 PaLM2 APIとCloud Runで実装しているそうで、Bardとは違い、エンタープライズ用のチャットアプリだそうです。 Let’s Talk with Romi Romiという会話ロボットを紹介していました。 自動音声認識と音声を処理するコンピューティングでGoogle Cloudを使用しているそうです。 Google Cloudは使用していないそうなのですが、Romiは複数人の会話に対応しているということにとても驚きました。 Duet AI in Google Workspace ~ AIとの新たなコラボレーション ~ DAY1 基調講演でも紹介されていましたが、Duet AI in Google WorkspaceはGoogle Workspaceに秘書機能を追加したものです。 Duet AI in Google Workspaceを使うと、会議の議事録作成、文書作成、スライド作成、ブログ作成などをすべて自動でやってくれるそうです。英語はすでにリリースされており、ついに日本語対応するとのことでした。 ブースでは文書作成のデモを見せてもらいました!誤字脱字なく文書が作成されていたので、驚きました。 F1レースで活用。AIプラットフォームによる大規模なリアルタイム分析 Cloud SpannerのData Boostという機能を使って、F1レースのリアルタイムデータ分析を行っていました。 車のテレメトリデータ、気象データ、集計されたレースデータを統合するだけでなく、適切なタイヤタイプの予測も行っていました。 Fitbit と Google Cloud で実現するヘルスデータの可視化 & パフォーマンス解析 Fitbitアプリからヘルスデータを取得し、分析を行った後にLooker Studioで出力するということを行っていました。 複数人のヘルスデータを比較・分析できるので、会社の社員の健康管理に活用できるとのことでした。 THIS IS “Street Fighter 6” 話題の格闘ゲームであるStreet Fighter 6の構成図を紹介していました。 私はプレイしていませんが、写真のように試遊台が用意されており、Street Fighter 6を体験できるようになっていました。 Innovators Hive Tech Base Google Cloudのサービスを使ったアプリケーションが紹介されていたり、認定資格に似たクイズの挑戦ができるようになっていたりしました。 認定資格試験が近かったので、私もチャレンジしてみました! Architect分野が50点、Data分野が60点、App Developer分野が60点と何とも言えない点数でした。 もっと努力しないといけませんね… クイズの景品として、写真のポーチを3つもらいました! 認定資格者ラウンジ Google Cloudの認定資格を持っている人だけが入れるラウンジで、資格保有者同士の交流を深める場所でした。 飲み物が無料でもらえたり、席にコンセントがついていたりしたので、ゆっくりくつろげました。 感想 2023年11月15日・16日の両日ともに、Google Cloud Nextに参加してみて、Google Cloudの最新情報や技術動向を学ぶことができ、非常に有意義な時間となりました。 特に、Google Cloudの最新サービスや機能の発表は、今後のクラウド活用を考える上で非常に参考になりました。また、各業界のリーダーによる講演では、Google Cloudを活用した先進的な取り組みを知ることができ、新たな視点を得ることができました。 また、普段から資格勉強をしているので、Google Cloudのサービスをある程度知っているつもりでしたが、いざイベントに参加してみると、まだまだ知らないサービスがたくさんありました。特に、機械学習やAI関連のサービスは、今後ますます重要になると感じました。 今回のイベント参加をきっかけに、Google Cloudの最新情報を常にキャッチアップし、より深く学んでいきたいと考えています。 最後まで読んでいただき、ありがとうございました。
アバター
本記事は、 Japan AWS Ambassador Advent Calendar 2023 の 2023/12/2 付記事となります。 こんにちは、SCSK の広野です。 私は Amazon API Gateway と AWS Lambda 関数の CI/CD を学習する社内研修を開催しておりまして、研修用 CI/CD パイプラインを AWS Code シリーズのサービスと AWS SAM (Serverless Application Model)、AWS CloudFormation で作成しています。 本記事では、その環境を抜粋して AWS CloudFormation でデプロイできるようにしたものを紹介します。 アーキテクチャ 以下のように、AWS サービスを組み合わせて作成しています。 開発環境は AWS Cloud9 を使用。 ソースコード管理に AWS CodeCommit を使用。Lambda 関数コードだけでなく、ビルド設定ファイル buildspec.yml と AWS SAM テンプレートファイル template.yml もセットで管理する。 コードの変更検知に Amazon EventBridge を使用。AWS CodeCommit のソースコードが変更されると CI/CD パイプラインを開始させる。 CI/CD パイプラインに AWS CodePipeline を使用。ビルドステージで AWS CodeBuild、AWS SAM を、デプロイステージで AWS CloudFormation を使用。AWS SAM で API Gateway と Lambda 関数をデプロイする。 パイプラインの各ステージで作成される成果物 (アーティファクト) 置き場に Amazon S3 を使用。 ※図は簡略化していますが 2つのバケットがあります。SAM テンプレート (API Gateway と Lambda 関数リソース作成) と Lambda 関数コードの ZIP で。 これにより、コードを更新かければ API Gateway と Lambda 関数が作成/更新されるようになります。 CI/CD パイプライン準備 CI/CD パイプラインは、以下の AWS CloudFormation テンプレートを流すことでデプロイできます。※ Cloud9 や VPC は除く AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a S3 bucket, a CI/CD environment with Code service series, and relevant IAM roles. Parameters: # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# YourID: Type: String Description: Please fill your full name and today's date, or another unique name. (e.g. yujihirono20231202) You can not use any upper cases and special characters. MaxLength: 100 MinLength: 5 Resources: # ------------------------------------------------------------# # CodeCommit Repository # ------------------------------------------------------------# SampleProjectRep: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Sub SampleProject-${YourID} Tags: - Key: Cost Value: !Ref YourID # ------------------------------------------------------------# # EventBridge Rule for Starting CodePipeline # ------------------------------------------------------------# SampleProjectRepPipelineEventsRule: Type: AWS::Events::Rule Properties: Name: !Sub SampleProject-StartPipelineRule-${YourID} EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default" EventPattern: source: - aws.codecommit resources: - !GetAtt SampleProjectRep.Arn detail-type: - "CodeCommit Repository State Change" detail: event: - referenceCreated - referenceUpdated referenceName: - master State: ENABLED Targets: - Arn: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":codepipeline:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":" - Ref: Pipeline Id: Target0 RoleArn: !GetAtt PipelineEventsRole.Arn DependsOn: - PipelineEventsRole - Pipeline # ------------------------------------------------------------# # CodeBuild Project Role (IAM) # ------------------------------------------------------------# BuildProjectRole: Type: AWS::IAM::Role Properties: RoleName: !Sub BldPrjRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: codebuild.amazonaws.com Version: "2012-10-17" # ------------------------------------------------------------# # CodeBuild Project Role Policy (IAM) # ------------------------------------------------------------# BuildProjectRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub BldPrjRolePolicy-${YourID} PolicyDocument: Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: - Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":logs:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - :log-group:/aws/codebuild/ - Ref: BuildProject - :* - Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":logs:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - :log-group:/aws/codebuild/ - Ref: BuildProject - Action: - codebuild:BatchPutCodeCoverages - codebuild:BatchPutTestCases - codebuild:CreateReport - codebuild:CreateReportGroup - codebuild:UpdateReport Effect: Allow Resource: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":codebuild:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - :report-group/ - Ref: BuildProject - -* - Action: s3:PutObject Effect: Allow Resource: Fn::Join: - "" - - !GetAtt PipelinePackageBucket.Arn - /* - Action: - s3:Abort* - s3:DeleteObject* - s3:GetBucket* - s3:GetObject* - s3:List* - s3:PutObject - s3:PutObjectLegalHold - s3:PutObjectRetention - s3:PutObjectTagging - s3:PutObjectVersionTagging Effect: Allow Resource: - !GetAtt PipelineArtifactBucket.Arn - Fn::Join: - "" - - !GetAtt PipelineArtifactBucket.Arn - /* Version: "2012-10-17" Roles: - Ref: BuildProjectRole DependsOn: - BuildProjectRole - PipelineArtifactBucket - PipelinePackageBucket # ------------------------------------------------------------# # CodeBuild Project # ------------------------------------------------------------# BuildProject: Type: AWS::CodeBuild::Project Properties: Name: !Sub SampleProjectBuild-${YourID} Artifacts: Type: CODEPIPELINE Environment: ComputeType: BUILD_GENERAL1_SMALL EnvironmentVariables: - Name: S3_BUCKET Type: PLAINTEXT Value: Ref: PipelinePackageBucket Image: "aws/codebuild/standard:7.0" ImagePullCredentialsType: CODEBUILD PrivilegedMode: false Type: LINUX_CONTAINER ServiceRole: !GetAtt BuildProjectRole.Arn Source: Type: CODEPIPELINE Cache: Type: NO_CACHE Tags: - Key: Cost Value: !Ref YourID DependsOn: - BuildProjectRole # ------------------------------------------------------------# # CodePipeline Build Package Bucket (S3) # ------------------------------------------------------------# PipelinePackageBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub codepipeline-package-${YourID} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Ref YourID # ------------------------------------------------------------# # CodePipeline Artifact Bucket (S3) # ------------------------------------------------------------# PipelineArtifactBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub codepipeline-artifact-${YourID} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Ref YourID # ------------------------------------------------------------# # CodePipeline Role (IAM) # ------------------------------------------------------------# PipelineRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: codepipeline.amazonaws.com Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Role Policy (IAM) # ------------------------------------------------------------# PipelineRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlRolePolicy-${YourID} PolicyDocument: Statement: - Action: - s3:Abort* - s3:DeleteObject* - s3:GetBucket* - s3:GetObject* - s3:List* - s3:PutObject - s3:PutObjectLegalHold - s3:PutObjectRetention - s3:PutObjectTagging - s3:PutObjectVersionTagging Effect: Allow Resource: - !GetAtt PipelineArtifactBucket.Arn - Fn::Join: - "" - - !GetAtt PipelineArtifactBucket.Arn - /* - Action: sts:AssumeRole Effect: Allow Resource: - !GetAtt PipelineBuildCodeBuildActionRole.Arn - !GetAtt PipelineDeployCreateChangeSetActionRole.Arn - !GetAtt PipelineDeployExecuteChangeSetActionRole.Arn - !GetAtt PipelineSourceCodeCommitActionRole.Arn Version: "2012-10-17" Roles: - Ref: PipelineRole DependsOn: - PipelineRole - PipelineArtifactBucket - PipelineBuildCodeBuildActionRole - PipelineDeployCreateChangeSetActionRole - PipelineDeployExecuteChangeSetActionRole - PipelineSourceCodeCommitActionRole # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# Pipeline: Type: AWS::CodePipeline::Pipeline Properties: Name: !Sub SampleProjectPipeline-${YourID} RoleArn: !GetAtt PipelineRole.Arn Stages: - Actions: - ActionTypeId: Category: Source Owner: AWS Provider: CodeCommit Version: "1" Configuration: RepositoryName: !GetAtt SampleProjectRep.Name BranchName: master PollForSourceChanges: false Name: CodeCommit OutputArtifacts: - Name: Artifact_Source_CodeCommit RoleArn: !GetAtt PipelineSourceCodeCommitActionRole.Arn RunOrder: 1 Name: Source - Actions: - ActionTypeId: Category: Build Owner: AWS Provider: CodeBuild Version: "1" Configuration: ProjectName: Ref: BuildProject InputArtifacts: - Name: Artifact_Source_CodeCommit Name: CodeBuild OutputArtifacts: - Name: Artifact_Build_CodeBuild RoleArn: !GetAtt PipelineBuildCodeBuildActionRole.Arn RunOrder: 1 Name: Build - Actions: - ActionTypeId: Category: Deploy Owner: AWS Provider: CloudFormation Version: "1" Configuration: StackName: !Sub SampleProject-Stack-${YourID} Capabilities: CAPABILITY_NAMED_IAM RoleArn: !GetAtt PipelineDeployCreateChangeSetRole.Arn ActionMode: CHANGE_SET_REPLACE ChangeSetName: !Sub SampleProject-Deploy-${YourID} TemplatePath: Artifact_Build_CodeBuild::packaged.yaml ParameterOverrides: !Sub '{"YourID": "${YourID}"}' InputArtifacts: - Name: Artifact_Build_CodeBuild Name: CreateChangeSet RoleArn: !GetAtt PipelineDeployCreateChangeSetActionRole.Arn RunOrder: 1 - ActionTypeId: Category: Deploy Owner: AWS Provider: CloudFormation Version: "1" Configuration: StackName: !Sub SampleProject-Stack-${YourID} ActionMode: CHANGE_SET_EXECUTE ChangeSetName: !Sub SampleProject-Deploy-${YourID} Name: ExecuteChangeSet RoleArn: !GetAtt PipelineDeployExecuteChangeSetActionRole.Arn RunOrder: 2 Name: Deploy ArtifactStore: Location: Ref: PipelineArtifactBucket Type: S3 Tags: - Key: Cost Value: !Ref YourID DependsOn: - SampleProjectRep - PipelineRoleDefaultPolicy - PipelineRole - PipelineSourceCodeCommitActionRole - PipelineBuildCodeBuildActionRole - PipelineDeployCreateChangeSetActionRole - PipelineDeployExecuteChangeSetActionRole - PipelineArtifactBucket # ------------------------------------------------------------# # CodePipeline Source Code Commit Action Role (IAM) # ------------------------------------------------------------# PipelineSourceCodeCommitActionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlSrcCcActionRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":iam::" - Ref: AWS::AccountId - :root Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Source CodeCommit Action Role Policy (IAM) # ------------------------------------------------------------# PipelineSourceCodeCommitActionRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlSrcCcActionRolePolicy-${YourID} PolicyDocument: Statement: - Action: - s3:Abort* - s3:DeleteObject* - s3:GetBucket* - s3:GetObject* - s3:List* - s3:PutObject - s3:PutObjectLegalHold - s3:PutObjectRetention - s3:PutObjectTagging - s3:PutObjectVersionTagging Effect: Allow Resource: - !GetAtt PipelineArtifactBucket.Arn - Fn::Join: - "" - - !GetAtt PipelineArtifactBucket.Arn - /* - Action: - codecommit:CancelUploadArchive - codecommit:GetBranch - codecommit:GetCommit - codecommit:GetUploadArchiveStatus - codecommit:UploadArchive Effect: Allow Resource: !GetAtt SampleProjectRep.Arn Version: "2012-10-17" Roles: - Ref: PipelineSourceCodeCommitActionRole DependsOn: - PipelineArtifactBucket - SampleProjectRep - PipelineSourceCodeCommitActionRole # ------------------------------------------------------------# # CodePipeline Events Role (IAM) # ------------------------------------------------------------# PipelineEventsRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlEventsRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: events.amazonaws.com Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Events Role Policy (IAM) # ------------------------------------------------------------# PipelineEventsRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlEventsRolePolicy-${YourID} PolicyDocument: Statement: - Action: codepipeline:StartPipelineExecution Effect: Allow Resource: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":codepipeline:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":" - Ref: Pipeline Version: "2012-10-17" Roles: - Ref: PipelineEventsRole DependsOn: - PipelineEventsRole # ------------------------------------------------------------# # CodePipeline Build CodeBuild Action Role (IAM) # ------------------------------------------------------------# PipelineBuildCodeBuildActionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlBldCbActionRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":iam::" - Ref: AWS::AccountId - :root Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Build CodeBuild Action Role Policy (IAM) # ------------------------------------------------------------# PipelineBuildCodeBuildActionRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlBldCbActionRolePolicy-${YourID} PolicyDocument: Statement: - Action: - codebuild:BatchGetBuilds - codebuild:StartBuild - codebuild:StopBuild Effect: Allow Resource: !GetAtt BuildProject.Arn Version: "2012-10-17" Roles: - Ref: PipelineBuildCodeBuildActionRole DependsOn: - BuildProject - PipelineBuildCodeBuildActionRole # ------------------------------------------------------------# # CodePipeline Deploy Create ChangeSet Action Role (IAM) # ------------------------------------------------------------# PipelineDeployCreateChangeSetActionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlDepCrChsetActionRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":iam::" - Ref: AWS::AccountId - :root Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Deploy Create ChangeSet Action Role Policy (IAM) # ------------------------------------------------------------# PipelineDeployCreateChangeSetActionRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlDepCrChsetActionRolePolicy-${YourID} PolicyDocument: Statement: - Action: iam:PassRole Effect: Allow Resource: !GetAtt PipelineDeployCreateChangeSetRole.Arn - Action: - s3:GetBucket* - s3:GetObject* - s3:List* Effect: Allow Resource: - !GetAtt PipelineArtifactBucket.Arn - Fn::Join: - "" - - !GetAtt PipelineArtifactBucket.Arn - /* - Action: - cloudformation:CreateChangeSet - cloudformation:DeleteChangeSet - cloudformation:DescribeChangeSet - cloudformation:DescribeStacks Condition: StringEqualsIfExists: cloudformation:ChangeSetName: !Sub SampleProject-Deploy-${YourID} Effect: Allow Resource: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":cloudformation:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - !Sub :stack/SampleProject-Stack-${YourID}/* Version: "2012-10-17" Roles: - Ref: PipelineDeployCreateChangeSetActionRole DependsOn: - PipelineDeployCreateChangeSetRole - PipelineArtifactBucket - PipelineDeployCreateChangeSetActionRole # ------------------------------------------------------------# # CodePipeline Deploy Create ChangeSet Role (IAM) # ------------------------------------------------------------# PipelineDeployCreateChangeSetRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlDepCrChsetRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: cloudformation.amazonaws.com Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Deploy Create ChangeSet Role Policy (IAM) # ------------------------------------------------------------# PipelineDeployCreateChangeSetRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlDepCrChsetRolePolicy-${YourID} PolicyDocument: Statement: - Action: - s3:GetBucket* - s3:GetObject* - s3:List* Effect: Allow Resource: - !GetAtt PipelineArtifactBucket.Arn - Fn::Join: - "" - - !GetAtt PipelineArtifactBucket.Arn - /* - Action: "*" Effect: Allow Resource: "*" Version: "2012-10-17" Roles: - Ref: PipelineDeployCreateChangeSetRole DependsOn: - PipelineArtifactBucket - PipelineDeployCreateChangeSetRole # ------------------------------------------------------------# # CodePipeline Deploy Execute ChangeSet Action Role (IAM) # ------------------------------------------------------------# PipelineDeployExecuteChangeSetActionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub PlDepExecChsetActionRole-${YourID} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":iam::" - Ref: AWS::AccountId - :root Version: "2012-10-17" # ------------------------------------------------------------# # CodePipeline Deploy Execute ChangeSet Action Role Policy (IAM) # ------------------------------------------------------------# PipelineDeployExecuteChangeSetActionRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub PlDepExecChsetActionRolePolicy-${YourID} PolicyDocument: Statement: - Action: - cloudformation:DescribeChangeSet - cloudformation:DescribeStacks - cloudformation:ExecuteChangeSet Condition: StringEqualsIfExists: cloudformation:ChangeSetName: !Sub SampleProject-Deploy-${YourID} Effect: Allow Resource: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":cloudformation:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - !Sub :stack/SampleProject-Stack-${YourID}/* Version: "2012-10-17" Roles: - Ref: PipelineDeployExecuteChangeSetActionRole DependsOn: - PipelineDeployExecuteChangeSetActionRole CI/CD パイプライン使用方法 AWS Cloud9 準備 今回は、API Gateway と Lambda 関数をコードで管理します。コードは AWS CodeCommit で管理し、コードの編集は AWS Cloud9 を使用します。あらかじめ CodeCommit の該当するリポジトリ と Cloud9 を連携する必要があります。ここでは、この準備の説明は割愛します。以下の AWS 公式ドキュメントを参考に準備頂けると幸いです。 AWS Cloud9 と AWS CodeCommit を統合する - AWS CodeCommit AWS Cloud9 を AWS CodeCommit と統合する方法について説明します。 docs.aws.amazon.com コードの準備 API Gateway と Lambda 関数をデプロイするために、以下 3 種類のコードファイルを準備します。ファイル名はこの例のまま使用頂いた方が良いです。(CI/CD パイプライン内で指定している都合でw) lambda_function.py buildspec.yml template.yml 以下、それぞれについて解説します。 lambda_function.py Lambda 関数の実体です。ここでは、サンプルなので現在時刻を返すだけの Python コードになっています。 from datetime import datetime def lambda_handler(event, context): # Get the current date and time now = datetime.now() # Format the date and time in a readable format (e.g., YYYY-MM-DD HH:MM:SS) current_time = now.strftime("%Y-%m-%d %H:%M:%S") # Return the current date and time return { "statusCode": 200, "body": current_time } buildspec.yml ビルドステージで何をするか、を定義したファイルです。 実際のところ、Lambda 関数コードは上述の lambda_function.py をそのまま使用するのでコンパイルすることはありません。API Gateway や Lambda 関数リソースをこの CI/CD パイプラインでデプロイするため、sam build や sam package というコマンドを使用して AWS SAM テンプレートを AWS 側で処理しやすいフォーマットに?若干変更するようです。(変更後ファイルは packaged.yaml)  version: 0.2 phases: install: runtime-versions: python: 3.11 commands: pre_build: commands: - sam build build: commands: - sam package --s3-bucket $S3_BUCKET --output-template-file packaged.yaml artifacts: files: - packaged.yaml template.yml AWS SAM テンプレートファイルです。この中で、デプロイしたい API Gateway と Lambda 関数のリソースを定義しています。 AWS CloudFormation と比べるとかなり少ない行数です。標準的な設定を使用して動くリソースをデプロイしてくれます。 AWSTemplateFormatVersion: 2010-09-09 Description: >- SampleProject Transform: - AWS::Serverless-2016-10-31 Parameters: YourID: Type: String Default: dummy Resources: SampleFunction: Type: AWS::Serverless::Function Properties: Handler: lambda_function.lambda_handler Runtime: python3.11 Policies: - DynamoDBReadPolicy: TableName: YourTableName Environment: Variables: YourID: !Ref YourID Events: Api: Type: Api Properties: Path: /test Method: POST Outputs: ApiEndpoint: Description: "APIG Endpoint" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" SAM (Serverless Application Model) は AWS CloudFormation よりも簡易にリソースを定義できる分、カスタマイズ性に欠けるので要件によって CloudFormation との使い分けを考える必要があります。 ここでは、全く使用しないのですが Lambda 関数に YourTableName という Amazon DynamoDB テーブルの Read Only 権限を与えています。このように AWS があらかじめ用意した権限パターンセットを使うと簡単な権限定義ができます。 AWS SAM ポリシーテンプレート - AWS Serverless Application Model このセクションには、サポートされるポリシーテンプレートの完全なリストが記載されています。 docs.aws.amazon.com 最終的に、前述した通り SAM テンプレートはビルドステージで内部的に CloudFormation テンプレートに変換され、デプロイに使用されます。 実際にデプロイしてみる CodeCommit と連携した状態の Cloud9 で、以下のようにプロジェクトのルートディレクトリに 3 つのコードを配置します。 配置後、CodeCommit にコードをプッシュします。(以下、コマンド例) git commit -A git commit -m "initial commit" git push origin master プッシュすると、自動的に CI/CD パイプライン (AWS CodePipeline) が動き出します。 Deploy ステージまで完了。 全てのステージが完了すると、API Gateway と Lambda 関数が連携された状態でデプロイされています。 Lambda 関数の IAM ロールも、デフォルトのポリシー + DynamoDB Read only 系の権限が自動的に作成されてますね。 AWS CloudFormation のコンソールを見ると、CI/CD パイプラインから実行された CloudFormation スタックがあることが確認できます。 説明は省略しますが、API Gateway や Lambda 関数を更新するときは Cloud9 のコードを更新して上記手順を繰り返します。 注意点ですが、一連の環境を削除するときには先にこの CI/CD パイプラインから呼び出された CloudFormation スタックを削除する必要があります。その後、CI/CD パイプラインをデプロイしたときのスタック削除になります。 まとめ いかがでしたでしょうか? API Gateway と Lambda 関数をコードでバージョン管理するときにはこのような方法を採るのが今時点のソリューションかと思います。あくまでも簡易な一例ですので、ディテールは書き換えて下さい。 本記事が皆様のお役に立てれば幸いです。
アバター
本記事は TechHarmony Advent Calendar 12/1付の記事です。 TechHarmony Advent Calendar 2023の1日目担当、MasedatiことMasedaです。よろしくお願いします。 さて、2023年11月16日、「PartyRock」なる楽しそうなサービスが発表されたのはご存じでしょうか。 Announcing PartyRock, an Amazon Bedrock Playground aws.amazon.com PartyRockとは、Amazon Bedrock Playgroundの一つで、生成AIアプリをノーコードで簡単に作成することができるサービスです。 現在、期間限定で無料トライアル中とのことで、今回はPartyRockを使って「食材からレシピを提案してくれるアプリ」を作ってみました。 (一人暮らしをしている身としては、食材が余りがちなので…) 準備 PartyRock にアクセスしてみましょう。必要なものは、「Google・Apple・Amazonアカウント」のいずれかだけです。 Sign inしてさっそく作ります。 つくってみる 「Build your own app」ボタンを押して、作りたいアプリの説明を書きます。 日本語でも入力できますが、テキストは内部で英訳されてから構築するため、英語入力がより正確かもしれません。 今回は「入力した食材から作れるレシピとその料理完成写真を一つ提示するアプリ」としました。 「Generate app」ボタンを押します。 ↓すぐにいい感じに作ってくれました。 デフォルトのUIのままだと収まりが悪いので、Widgetの大きさ、配置を調整します。上記画像右上の「Edit」ボタンを押して編集モードに切り替えることができます。調整したいWidgetの右下にカーソルを合わせてドラッグすることで大きさを変えます。   きれいなUIになりました。左上に材料を入力後、左下に料理写真、右にレシピが表示される仕様です。 タイトルも変更してみました。 ためしてみる 冷蔵庫を見ると「トマト2個、じゃがいも1個、豚肉100g、大根1/8本、ヨーグルト」がありました。 そのまま入力してみます。 「野菜と肉の umami 」が引き出されておいしそうです。 ちなみに… 食べられないものを入力すると、どうなるのでしょう? 上記食材に加えて「ボールペン、フグ、スズラン」を加えてみます。 「フグ、ボールペン」は除外されましたが、毒物である「スズラン」が食材として使われてしまいました。 一方、同じ食材を英語で入力してみます。 “フグもスズランも毒がある。ボールペンも食品ではない。”のように除外してくれます。 やはり、日本語より英語の方が精度が高そうです。これは生成AI(日本語)の課題の一つでしょう。より精度のよいモデルの登場を待つか、専門的な知識は調整(fine-tuning、pre-trainingなど)を行う必要がありそうです。 欲しい機能を追加でつくる アプリとして完成しましたが、「料理のカロリーを知りたい」等の追加機能が欲しい場合があります。 そのような場合、「Edit」>「+Add Widget」で追加Widgetを作ることができます。(合計50個まで) レシピの下に栄養成分を表示したいと思います。デフォルトでは、俳句(?)が生成されているので、右枠のWidget Title・Model・Promptを編集します。 Modelは現在5種類選ぶことができますが、モデルごとに消費される無料枠クレジット・精度が異なります。今回は、安価で日本語の精度が良いと噂の「Claude Instant」を選びました。また、Promptでは、「@Recipe」のように@を付けることで他のWidgetの出力を指定することができます。 以下のように、入力しました。 Widget Title:Nutrition facts Model:Claude Instant Prompt: 以下の形式で@Recipeの栄養成分を書いてください。 ・熱量:kcal ・たんぱく質:g ・脂質:g ・炭水化物:g ・食塩相当量:g Promptは、ある程度のルール付けを行ったほうが良いでしょう。期待通りの出力がされるまで苦労しますが、手早く調整できるのはPartyRockのメリットだと思います。また、ベースとなるPromptが固定かつWidget間で情報が連携できるのもPartyRockのいいところですね。 完成! 私が欲しかった「食材からレシピを提案してくれるアプリ」が完成しました。作ったアプリは「Make public and Share」で公開することができます。 ↓私も公開してみました。ぜひ、遊んでみてください。 フードロス削減れしぴ フードロス削減れしぴ-ChatBotつき- フードロス削減れしぴ-マルチ- 感想 短時間で簡単に生成AIアプリを作ることができました。 生成AIのWidgetとして今回使用したテキスト生成・画像生成のほかにChatBotもあるので、組み合わせは多種多様です。 みなさんも試してみてはいかがでしょうか。 余談:実際につくってみた   だいぶ完成写真と違う気がしますが、おいしかったです。
アバター
MTU とは MTU (Maximum Transmission Unit) は、ネットワーク上で一度に送信できるデータの最大サイズのことを指します。一般的なイーサネットの MTU は1500バイトであり、イーサネットでIP通信を行う場合、IPパケットの最大サイズは1500バイトということになります。これより大きなデータを送信したい場合は、複数のパケットに分割する必要があります。 MTU の値はネットワーク接続の種類によって変わってきます。例えば PPPoE (PPP over Ethernet) を用いて通信を行っている場合、PPPoE でカプセル化されたデータがイーサネット上で送信されます。PPPoE のカプセル化に必要なオーバーヘッドは合計8バイトですので、PPPoE 接続の MTU は1492 (1500 – 8) バイトとなります。また、NTT東西のフレッツ光回線で PPPoE 接続を行う場合の MTU は1454バイトであり、v6プラス (IPoE 接続) 上で MAP-E または DS-Lite 方式による IPv4 通信を行う場合の MTU は1460バイトであるように、ネットワーク接続の種類によって MTU の値は異なります。 利用するネットワークの MTU の値を把握しておくことは、効率的な通信を行うために不可欠です。MTU よりも大きなサイズのデータを送信すると、ネットワーク上のどこかでパケットがフラグメント化または破棄されてしまいます。また、MTU よりも小さなサイズのデータを送信すると、パケットの数が増えることになるため通信処理のオーバーヘッドやパケット損失が増えることにもなります。 Cato クラウドを利用する場合においても、Socket や Cato Client と PoP 間の暗号化通信や、Cato クラウドのバックボーンネットワーク内の通信方式により、MTU は1500バイトよりも小さな値となっています。しかし、Cato クラウドにおける MTU の値はドキュメント上では公表されていませんでしたので、今回調査・検証してみました。 調査・検証環境 今回は AWS の Amazon EC2 上で主として Linux マシンを利用し、Cato Client を用いてインターネット経由で PoP に接続して調査・検証しました。実施のしやすさを優先して Linux マシンを用いていますが、後述の通り Windows マシンであっても同様の結果となります。 AWS リージョン : 東京 (ap-northeast-1) OS : Ubuntu 22.04 LTS Cato Client : 5.1.0.21 (deb 版) 接続先 PoP : Tokyo_DC2 マシン起動直後は、インターネットにも繋がる ens5 というネットワークインターフェースと、ローカルループバックインターフェースの2つのがある状態です。 $ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000 link/ether 0a:b6:b1:e1:3d:c3 brd ff:ff:ff:ff:ff:ff inet 172.31.3.80/20 metric 100 brd 172.31.15.255 scope global dynamic ens5 valid_lft 3479sec preferred_lft 3479sec inet6 fe80::8b6:b1ff:fee1:3dc3/64 scope link valid_lft forever preferred_lft forever Amazon EC2 ではジャンボフレームが利用可能なのでインターフェースの MTU は9001バイトとなっていますが、Amazon EC2 とインターネットとの通信時の MTU は1500バイトです。 Cato Client 接続直後の状態 それでは、Cato Client を用いて PoP に接続してみます。  $ cato-sdp start --user SDPユーザアカウント --account テナント名 --reset-cred PoP に接続すると、tun0 というインターフェースが新たに作られ、デフォルトルートとしても設定されていました。150.195.209.225 は PoP のIPアドレスです。 $ ip addr show dev tun0 4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1370 qdisc fq_codel state UNKNOWN group default qlen 500 link/none inet 100.67.0.168/32 scope global tun0 valid_lft forever preferred_lft forever inet6 fe80::84e8:ac2b:6e3b:4f84/64 scope link stable-privacy valid_lft forever preferred_lft forever $ ip route default dev tun0 default via 172.31.0.1 dev ens5 proto dhcp src 172.31.3.80 metric 100 8.8.8.8 dev tun0 10.254.254.1 dev tun0 150.195.209.225 via 172.31.0.1 dev ens5 172.31.0.0/20 dev ens5 proto kernel scope link src 172.31.3.80 metric 100 172.31.0.1 dev ens5 proto dhcp scope link src 172.31.3.80 metric 100 172.31.0.2 dev ens5 proto dhcp scope link src 172.31.3.80 metric 100 $ ethtool -i tun0 | grep driver driver: tun tun0 は Linux の TUN デバイス機能を用いて実現された仮想的なインターフェースであり、tun0 に書き込まれたIPパケットは Cato Client のプロセスがいったん受け取り、DTLS でカプセル化されて ens5 を通して PoP に送信されていきます。 tun0 の MTU が1370と設定されていますので、マシン上で動作するアプリケーションが PoP 経由で行う通信では1370バイト以下のIPパケットしか送信できないよう制限されています。 PoP 接続におけるオーバヘッドの確認 マシンから送信されるIPパケットは、DTLS プロトコルを用いた暗号化通信によってカプセル化されて PoP に送信されます。 まず、DTLS のカプセル化によるオーバーヘッドを確認してみます。インターネット側にあるターゲットマシンに ping で1000バイトのデータを送信し、tcpdump でパケットを確認しました。 $ sudo tcpdump -i any -nn not port 22 05:45:27.868003 tun0 Out IP 100.67.0.168 > 35.78.XXX.XXX: ICMP echo request, id 2, seq 1, length 1008 05:45:27.868066 ens5 Out IP 172.31.3.80.52292 > 150.195.209.225.443: UDP, length 1073 05:45:27.872783 ens5 In IP 150.195.209.225.443 > 172.31.3.80.52292: UDP, length 1073 05:45:27.872860 tun0 In IP 35.78.XXX.XXX > 100.67.0.168: ICMP echo reply, id 2, seq 1, length 1008 結果の1行目 (tun0 Out) は ping コマンドによって送信されたパケットを表しており、そのパケット長は1028バイト (IPヘッダ20バイト、ICMPヘッダ8バイト、データ1000バイト) です。 2行目 (ens5 Out) は Cato Client のプロセスから PoP に送信された DTLS のパケットであり、そのパケット長は1101バイト (IPヘッダ20バイト、UDPヘッダ8バイト、データ1073バイト) です。 3行目と4行目は上記の逆向きのパケットです。 アプリケーションが送信したパケットに対し、PoP に送信されたパケットは73バイト大きくなっており、これが PoP 接続におけるオーバーヘッドであると言えます。 なお、Cato Client と PoP との間では暗号スイート “TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ” を用いて DTLS v1.2 接続が確立しており、DTLS によるカプセル化単体のオーバーヘッドは57バイトですので、Cato Client が16バイトの何らかのデータを付加していることがわかります。   PoP 経由のインターネット通信における MTU の調査 さて、PoP 接続におけるオーバーヘッドが73バイトであるのに対し、MTU が1370バイトに制限されているのは小さすぎるように感じます。インターネットとの通信部分の MTU が1500バイトですので、tun0 インターフェースの MTU は1427であることを期待したいです。 そこで、tun0 の MTU を手動で変更し、実際に何バイトのIPパケットを送受信できるか確認してみました。 $ sudo ip link set dev tun0 mtu 1427 $ ip addr show dev tun0 4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1427 qdisc fq_codel state UNKNOWN group default qlen 500 link/none inet 100.67.0.168/32 scope global tun0 valid_lft forever preferred_lft forever inet6 fe80::84e8:ac2b:6e3b:4f84/64 scope link stable-privacy valid_lft forever preferred_lft forever ping コマンドで1427バイトのIPパケット (ICMPデータ長 1399) を送信したところ、PoP に対してフラグメンテーションを起こさずに送信はできるものの、同じサイズの受信したパケットではフラグメンテーションが発生していました。インターネット側にあるターゲットマシン上では受信・送信ともにフラグメンテーションは発生していませんでした。 $ sudo tcpdump -i any -nn not port 22 05:49:32.968442 tun0 Out IP 100.67.0.168 > 35.78.XXX.XXX: ICMP echo request, id 6, seq 1, length 1407 05:49:32.968501 ens5 Out IP 172.31.3.80.52292 > 150.195.209.225.443: UDP, length 1472 05:49:32.973275 ens5 In IP 150.195.209.225.443 > 172.31.3.80.52292: UDP, length 1425 05:49:32.973275 ens5 In IP 150.195.209.225.443 > 172.31.3.80.52292: UDP, length 112 05:49:32.973362 tun0 In IP 35.78.XXX.XXX > 100.67.0.168: ICMP echo reply, id 6, seq 1, length 1360 05:49:32.973381 tun0 In IP 35.78.XXX.XXX > 100.67.0.168: ip-proto-1 データ長を少しずつ小さくしていったところ、最終的に1383バイト以下のIPパケットであればフラグメンテーションは発生しないという結果が得られました。 また、TCP 接続を試したところ、クライアントから提示した MSS (Maximum Segment Size, 通常は MTU – 40) は1387バイトであるのに対し、サーバから提示された MSS は1343バイトとなっていました。 このことから、PoP 経由でインターネットと通信する際の実質的な MTU は1383バイトであり、tun0 インターフェースの MTU を1383に増やせば、より効率的に通信できるようになる可能性があると言えます。また、1383バイトを超えるとクライアントまたは PoP 内部でフラグメンテーションが発生するものと推測できます。 PoP の違いによる MTU の差異の確認 ここまでは Tokyo_DC2 PoP に接続して試していましたが、PoP によって違いがあるか確認しました。 AWSの大阪リージョンでマシンを用意して Osaka_DC2 PoP に接続してみると、PoP 経由のインターネット通信の実質的な MTU は1383であり、Tokyo_DC2 PoP と同じ結果となりました。 一方、AWSのバージニア北部リージョンでマシンを用意して Ashburn_DC2 PoP に接続して同様に試したところ、この PoP 経由のインターネット通信の実質的な MTU は1367であることがわかりました。Cato PoP は世界各地に存在し、PoP ごとに物理ネットワークの特性がおそらく異なるものと考えられます。 Ashburn_DC2 PoP に接続した場合も tun0 インターフェースの MTU は1370として作られていましたので、PoP に合わせて MTU を1367に変更すれば、TCP 以外の通信において僅かですがフラグメンテーションが発生しにくくなり、効率的に通信できるようになる可能性があります。 WAN 通信における MTU の調査 Tokyo_DC2 PoP に接続したマシンを2台用意し、PoP 経由で行う Cato Client 間の WAN 通信についても確認してみました。 ping コマンドで確認した MTU は想定通り1383となっていましたが、TCP 接続の MSS は1250で確立していました。 (100.67.0.74 がクライアント、100.67.0.214 がサーバです。) $ ## TCP クライアント側 $ sudo tcpdump -i any -nn not port 22 01:44:42.218685 tun0 Out IP 100.67.0.74.32958 > 100.67.0.214.30000: Flags [S], seq 487546713, win 65189, options [mss 1387,sackOK,TS val 2662317462 ecr 0,nop,wscale 6], length 0 01:44:42.218844 ens5 Out IP 172.31.3.80.42447 > 103.203.222.69.443: UDP, length 105 01:44:42.223744 ens5 In IP 103.203.222.69.443 > 172.31.3.80.42447: UDP, length 105 01:44:42.223837 tun0 In IP 100.67.0.214.30000 > 100.67.0.74.32958: Flags [S.], seq 1022880092, ack 487546714, win 64625, options [mss 1250,sackOK,TS val 3972244012 ecr 2662317462,nop,wscale 6], length 0 01:44:42.223863 tun0 Out IP 100.67.0.74.32958 > 100.67.0.214.30000: Flags [.], ack 1, win 1019, options [nop,nop,TS val 2662317467 ecr 3972244012], length 0 01:44:42.223899 ens5 Out IP 172.31.3.80.42447 > 103.203.222.69.443: UDP, length 97 $ ## TCP サーバ側 $ sudo tcpdump -i any -nn not port 22 01:44:42.221845 ens5 In IP 103.203.222.130.443 > 172.31.26.144.56542: UDP, length 105 01:44:42.221930 tun0 In IP 100.67.0.74.32958 > 100.67.0.214.30000: Flags [S], seq 487546713, win 65189, options [mss 1250,sackOK,TS val 2662317462 ecr 0,nop,wscale 6], length 0 01:44:42.221961 tun0 Out IP 100.67.0.214.30000 > 100.67.0.74.32958: Flags [S.], seq 1022880092, ack 487546714, win 64625, options [mss 1387,sackOK,TS val 3972244012 ecr 2662317462,nop,wscale 6], length 0 01:44:42.221995 ens5 Out IP 172.31.26.144.56542 > 103.203.222.130.443: UDP, length 105 01:44:42.226638 ens5 In IP 103.203.222.130.443 > 172.31.26.144.56542: UDP, length 97 01:44:42.226682 tun0 In IP 100.67.0.74.32958 > 100.67.0.214.30000: Flags [.], ack 1, win 1019, options [nop,nop,TS val 2662317467 ecr 3972244012], length 0 WAN 通信ではどうやら MSS が MTU よりもかなり小さくなるよう調整されているようです。クライアント側とサーバ側で見える TCP 通信の送信元・宛先IPアドレスやポート番号が同一であることから、PoP 側で TCP の終端は行われていないように見えますし、Network Rules 設定で TCP Acceleration を有効・無効のどちらにしても同じ結果でした。 また、Tokyo_DC2 PoP および Ashburn_DC2 PoP に接続したマシンを1台ずつ用意し、Cato クラウドのバックボーンネットワークを経由した WAN 通信についても確認したところ、MTU 1383、TCP MSS 1250 で通信でき、同一 PoP に接続した場合と同じ結果となりました。 WAN 通信であれば、どの PoP に接続していたとしても MTU 1383、TCP MSS 1250 で通信できると言えると思います。 回線の MTU が小さい場合の検証 ここまでインターネット回線の MTU が 1500 である環境で Cato Client を起動して試していましたが、実際の MTU はそれよりも小さい場合が多いです。そこで、試しに今回の検証マシンのインターネットと繋がるインターフェースの MTU を1400に変更し、Cato Client の振る舞いを検証しました。 $ sudo ip link set dev ens5 mtu 1400 Cato Client で改めて Tokyo_DC2 PoP に接続してみると、相変わらず MTU 1370 の tun0 インターフェースが作られましたので、インターネット回線側の MTU を考慮しない動作となっていることがわかります。 また、インターネット上のマシンに ping コマンドで1370バイトのIPパケットを送信すると、DTLS パケットがフラグメント化されて送信されていました。IPパケットの DF (Don’t Fragment) ビットを立てても結果は同じです。 $ sudo tcpdump -i any -nn not port 22 06:30:50.768469 tun0 Out IP 100.67.0.195 > 35.78.XXX.XXX: ICMP echo request, id 12, seq 1, length 1350 06:30:50.768526 ens5 Out IP 172.31.3.80.45201 > 150.195.209.226.443: UDP, length 1415 06:30:50.768529 ens5 Out IP 172.31.3.80 > 150.195.209.226: ip-proto-17 06:30:50.773356 ens5 In IP 150.195.209.226.443 > 172.31.3.80.45201: UDP, length 1415 06:30:50.773416 tun0 In IP 35.78.XXX.XXX > 100.67.0.195: ICMP echo reply, id 12, seq 1, length 1350 tun0 インターフェースの MTU が1370の場合、PoP に送信される DTLS パケットは最大で1443バイトとなり、インターネット回線の MTU を超えるとフラグメンテーションが発生します。そのため、MTU が 1443 バイト未満のインターネット回線を利用している場合は tun0 インターフェースの MTU を小さくしたほうが良く、その値は “インターネット回線の MTU – 73” とするのが良いと言えます。 なお、日本のお客様のインターネット回線であれば通常は問題となりませんし、仮に MTU が小さなインターネット回線を利用していたとしても、フラグメンテーションが発生して非効率的ではあるものの通信できないわけではありませんので、気にしすぎる必要はありません。 TLS で PoP に接続した場合 Cato Client から PoP に対して DTLS (over UDP) ではなく TLS (over TCP) で接続することもできます。 $ cato-sdp start --user SDPユーザアカウント --account テナント名 --reset-cred --tcp 2 TLS で接続した場合も MTU が1370バイトの tun0 インターフェースが作られました。 Cato Client と PoP との間の TCP 接続の MSS は1460バイトで確立しており、これはインターネット回線の MTU が1500バイトであることから期待通りの結果です。 また、インターネット側にあるターゲットマシンに ping で1000バイトのデータを送信したときの tcpdump の結果は次の通りです。 $ sudo tcpdump -i any -nn not port 22 05:14:50.812285 tun0 Out IP 100.67.0.252 > 35.77.117.11: ICMP echo request, id 18, seq 1, length 1008 05:14:50.812530 ens5 Out IP 172.31.3.80.53346 > 150.195.209.236.443: Flags [P.], seq 828:1886, ack 607, win 17546, options [nop,nop,TS val 3527218244 ecr 1214912535], length 1058 05:14:50.815134 ens5 In IP 150.195.209.236.443 > 172.31.3.80.53346: Flags [.], ack 1886, win 65535, options [nop,nop,TS val 1214913289 ecr 3527218244], length 0 05:14:50.817393 ens5 In IP 150.195.209.236.443 > 172.31.3.80.53346: Flags [.], seq 607:1687, ack 1886, win 65535, options [nop,nop,TS val 1214913291 ecr 3527218244], length 1080 05:14:50.817454 tun0 In IP 35.77.117.11 > 100.67.0.252: ICMP echo reply, id 18, seq 1, length 1008 05:14:50.859427 ens5 Out IP 172.31.3.80.53346 > 150.195.209.236.443: Flags [.], ack 1687, win 17546, options [nop,nop,TS val 3527218291 ecr 1214913291], length 0 1028バイトのIPパケットが TLS でカプセル化されて1110バイトのIPパケットとして送信されていますので、オーバーヘッドは82バイトと言えます。 さらに、ターゲットマシンに TCP 接続を行うと MSS は1036バイトで確立し、ping による確認でも1076バイトを超えるIPパケットでは受信時にフラグメンテーションが発生しましたので、PoP 内部で MTU が1076バイトとなっているものと推測できます。そのため、tun0 インターフェースの MTU を1076に変更すると TCP 以外の通信において効率的に通信できるようになる可能性があります。 なお、PoP との間では暗号スイート “TLS_AES_256_GCM_SHA384” を用いた TLS v1.2 接続が確立しており、TLS によるカプセル化単体のオーバーヘッドは81バイト (IPヘッダ20バイト、TCPヘッダ20バイト、TCPオプション12バイト、TLSヘッダ5バイト、ノンス8バイト、認証タグ16バイト) ですので、Cato Client が1バイトの何らかのデータを付加しているようです。 Windows 版 Cato Client で接続した場合 多くの方が利用している Windows 版の Cato Client でも同様に確認しました。利用したバージョン 5.8.15 です。 まず、Cato Client をインストールした時点で、2つのネットワークインターフェース (下記例の36番と31番) が新たに作成されました。 c:\>netsh interface ipv4 show interfaces Idx Met MTU 状態 名前 --- ---------- ---------- ------------ --------------------------- 1 75 4294967295 connected Loopback Pseudo-Interface 1 36 5 1380 disconnected CatoNetworks 6 15 1500 connected Ethernet 3 31 35 1380 disconnected Ethernet 4 どちらも MTU は1380バイトとなっており、PoP に接続すると36番のインターフェースが connected に変化して通信で利用されました。 PoP との接続は Linux 版と同じ暗号スイートで DTLS v1.2 で行われ、73バイトのオーバーヘッドがありました。また、MTU を手動で変更して試したところ、PoP 経由のインターネット通信における MTU も同様に1383バイトでした。 このことから、Linux 版で調査・検証した結果は Windows 版でも同じように適用できるものと考えられます。 まとめ Cato Client を利用して PoP に接続した場合の通信の MTU について調査・検証したところ、推測を含むものの、次の結果が得られました。 DTLS で PoP に接続して通信する場合の MTU は1383である。ただし、PoP によってインターネット通信の MTU は異なる場合がある。 WAN 通信では TCP の MSS は1250にまで小さく調整される。 TLS で PoP に接続して通信する場合の MTU は1076である。 Cato Client の仮想的なネットワークインターフェースの MTU のデフォルト値は1380 (Windows) または1370 (Linux) である。 Cato Client はインターネット回線の MTU を考慮しない。 これらの結果から、PoP に合わせて MTU の値を大きくしたり、あるいはインターネット回線に合わせて小さくしたりすれば、より効率的に通信できる可能性があり、スループットの増加やレイテンシの減少が見込まれます。ただし、その実測値までは評価できておらず、効果は微々たるものかもしれません Socket や vSocket で接続した場合については調査・検証していませんが、Cato Client と同様に PoP との間で DTLS 接続を行っていますので、MTU はおそらく同じなのではないかと思います。Socket や vSocket の LAN 側 MTU は変更できませんので、効率化の余地もほとんど無いように思います。
アバター
こんにちは、SCSK株式会社の池田です。 前回は、AWSで可用性を上げるために「 LifeKeeper 」が有効であることをお伝えしました。4回目の今回は、AWS環境でLifeKeeperを使ったHAクラスタを構成する際の接続方式についてお伝えします。 なぜAWS(パブリッククラウド)でLifeKeeperが有効なのか! パブリッククラウドでシステムを構築すれば耐障害は万全という間違った思い込み。AWSを初めとするパブリッククラウドでなぜLifeKeeperが有効なのかを解説します。 blog.usize-tech.com 2023.11.29   AWSでは仮想IPアドレスが設定できない!? 第1回「 HAクラスタウェア「LifeKeeper」で可用性を高めよう! 」でもお伝えした通り、オンプレミス環境や仮想環境(vSphere)では、仮想IPアドレス(Virtual IPアドレスとも言います)に接続することで、利用者はどちらのサーバに接続しているかを意識する必要がありませんでした。 しかしながら、AWSでは仮想IPアドレスの機能が提供されていません。 ではLifeKeeperでは、どのようにして冗長化されたサーバへの接続を実現させているのでしょうか? 今回は、LifeKeeperで用意されている接続方式にどのようなものがあるかについて説明します。 AWSでは仮想IPアドレスの機能がないのね 仮想IPアドレスに代わる2つの接続方式 LifeKeeperには、仮想IPアドレスに代わる接続方式が2つ用意されています。 今回お伝えする2つ以外の方式もありますが、それらの接続方式が使用されるケースは非常に稀な為、割愛します。 1.ルートテーブルを使用した接続方式 まず一つ目はルートテーブルを使用した接続方式です。 先述した通り、AWSでは仮想IPアドレスの機能が提供されていません。 その為、LifeKeeperを構成するサーバが存在するVPCのCIDR外に「ダミーの仮想IPアドレス」を用意し、その「ダミーの仮想IPアドレス」と「稼働系サーバのENI」をルートテーブルで紐づけることによって、稼働系サーバにアクセスできるようにする接続方式です。 この方式については、LifeKeeperに同梱されている「 Recovery Kit for EC2 」というオプション製品で実現できます。 イメージ図は以下の様になります。   (1)クライアントは「ダミーの仮想IPアドレス」(図では10.1.0.10)にアクセスします。 (2)ルートテーブルには予め「ダミーの仮想IPアドレス」のTargetとして稼働系サーバのENI(図では10.0.2.4)を 指定しておくことによって、クライアントからの通信が稼働系サーバに送信されます。 (3)障害が発生しフェイルオーバ処理が必要となった際には、LifeKeeperの機能(AWS CLIが自動実行)により、 ルートテーブルの「ダミーの仮想IPアドレス」のTargetが待機系サーバのENI(図では10.0.4.4)に書き換えられます。 このイメージ図では、クライアントはLifeKeeperを構成するサーバが存在するVPC内に存在していますが、AWS Transit Gatewayを組み合わせることによって、VPC外(オンプレミスや別のVPC)のクライアントからの通信にも対応できます。 この構成については、また別の機会にご説明します。 2.Route53を使用した接続方式 もう一つの接続方式はAmazon Route53(以下、Route53)を用いるものです。 Route53はご存じのかたも多いと思いますが、AWSが提供するグローバルに分散されたDNSの機能です。 Route53のAレコードには、稼働系ノードのIPアドレスを指定しておくことで、稼働系サーバにアクセスできるようにする接続方式です。 この方式については、LifeKeeperに同梱されている「 Recovery Kit for Route53 」というオプション製品で実現できます。 イメージ図は以下の様になります。 (1)クライアントはホスト名(図ではscsk.jp)でクラスターにアクセスし、Amazon Route53で名前解決を行い、 名前解決したIPアドレスで稼働系サーバにアクセスを試みます。 (2)Amazon Route53のAレコードには、稼働系サーバのIPアドレス(図では10.0.2.4)を指定しておくことで、 クライアントからの通信が稼働系へ送信されます。 (3)障害が発生しフェイルオーバ処理が必要となった際には、LifeKeeperの機能(AWS CLIが自動実行)により、 Amazon Route53のAレコードを待機系サーバのIPアドレス図では10.0.4.4)へ書き換えることで、 以降のクライアントからの通信は10.0.4.4へ到達します。 まとめ 今回は、AWSにおける2つの接続方式についてご説明しましたがいかがでしたでしょうか?いずれの方式もサイオステクノロジーからARKが提供されているので、比較的容易に構成することができますね。 まとめますと ・AWSで仮想IPアドレスの機能が提供されていない ・仮想IPアドレスに代わる接続方法としては、主にルートテーブルとRoute53を使用した方法がある ・2つの接続方式を実現する為のARKがサイオステクノロジーから無償で提供されている。 次回は、今回お伝えした2つの接続方式について、どのような構成の場合にどちらの方式を選択するかについてお伝えします。お楽しみに!!
アバター
こんにちは、SCSK株式会社の池田です。 前回は、「 LifeKeeper 」のオプション製品であるARKに焦点を当てて解説しました。3回目の今回は、LifeKeeperがAWSを代表とするパブリッククラウドで有効な理由についてお伝えします。 「LifeKeeper」で可用性を高められるミドルウェアはこれだ! HAクラスタウェアのLifeKeeperについて、どんなミドルウェアの可用性を高めることができるのか?またオプション製品であるARKの仕様の概要についてお伝えします! blog.usize-tech.com 2023.11.20   パブリッククラウドの責任共有モデルって知ってますか? たまに「AWSに移行したら、全部AWSにお任せで良いんでしょ?」って声を聞くことがありますが、実はAWSを代表とするパブリッククラウド事業者と利用者とで、機能要件・非機能要件における機密性、完全性、可用性について明確に責任が別けられているってご存じでしたでしょうか? 以下がAWSが公開している「責任共有モデル」を図示したものになりますが、「お客様」と「AWS」とでキレイに責任範囲が別けられています。 一例でご説明すると、サーバやストレージといったハードウェアの障害については、全てAWSが責任を持って可用性を担保してくれますが、上記の図のように、「お客様」担当範囲のレイヤについては、利用者側で責任をもって可用性を考慮する必要があるんです! 例えば、アプリケーションで障害が起きても、AWSの責任範囲ではないので、利用者側でアプリケーションの可用性を担保しておく必要があります。 パブリッククラウドは万能だと思っていたけど、意外と盲点だったわ ここでLifeKeeperの登場! そこで登場する強い味方が LifeKeeper なのです! LifeKeeperを導入することでアプリケーションを保護し、またEC2インスタンスを別AZに配置することにより、万が一のAZ障害にも耐えうる構成とすることができます。 AWSの公式アイコンで表現するとこんな感じになります。 まとめ 今回は、AWSの責任共有モデルを参考に、アプリケーションのレイヤについては、利用者の責任で可用性を担保する必要があることをご説明しました。 ・パブリッククラウドには責任共有モデルがある ・アプリケーションレイヤは利用者の責任範囲となっている ・パブリッククラウドはアプリケーションレイヤの可用性は担保しない ・LifeKeeperはアプリケーションレイヤの可用性を高めることができる 次回は、AWSにおいて、どのような接続構成を取ることができるのかについてお伝えします。お楽しみに!!
アバター
前回記事は、パブリッククラウド環境をCatoクラウドへ接続する方法として、vSocketとCross Connectを比較してみました。   vSocket × Cross Connect 徹底比較 パブリッククラウドをCatoクラウドへ接続する方法「vSocket」と「Cross Connect」を性能面・コスト面等から比較します。 blog.usize-tech.com 2023.11.29   続いて、本記事では実際にAWSをCross ConnectでCatoクラウドに接続する際の流れをご紹介します。 各パブリッククラウドの接続手順は、以下のCato Knowledge Baseもご参照ください。   Cato Knowledge Base | Cato Cross Connect Sites   まずは構成を決める 接続ロケーションを決める AWSのDirect Connectロケーションと、CatoのCross Connectロケーションを照らし合わせ、利用するロケーションを2つ選択します。 AWS Direct Connect Locations Cato Knowledge Base | Cato Cross Connect Availability 2023年11月現在、日本国内のCross Connectロケーションは以下2つのみですので、AWSの東京または大阪リージョンを利用する場合には、この2つを選択します。 Equinix TY2, Tokyo, Japan Equinix OS1, Osaka, Japan ネットワーク構成を決める 続いて、ネットワーク構成を検討します。構成イメージとしては、Transit Gatewayを使うパターンと使わないパターンが考えられます。選択のポイントは、 接続したいVPCが(将来の拡張も含めて)いくつあるか です。 構成例A : Transit Gatewayを利用しないパターン Direct Connect Gatewayに接続できる VPCの上限が20 です。 VPC同士のDirect Connect Gatewayでの折返し通信は、基本的には対応していません。※詳細はAWS側の仕様をご確認ください 構成例B : Transit Gatewayを利用するパターン Transit Gatewayに接続できるアタッチメントの上限が5,000です。 Direct Connect Gatewayに接続できるTransit Gateway の上限が6です。 上記2点より、 たくさんのVPCを接続する構成が可能 です。 Transit GatewayでVPC同士の折返し通信ができます。※通信させない構成も可能です 構成例Aと比較し、 Transit Gatewayおよびアタッチメントの利用料金が発生 します。 なお、構成例Aで構築した後にBに変更することも可能ですが、その際は、AWS側の設計変更や構成変更時の通信断が発生します。 ASNを割り振る 各仮想プライベートゲートウェイ(VGW)、Direct Connectゲートウェイ(DXGW)、トランジットゲートウェイ(TGW)と、Cato側の設備はBGPで経路交換を行うため、重複しないAS番号(ASN)を割り当てる必要があります。 なお、Cato側のASNは2byte(64512~65535)のみ対応しておりますが、4byte ASNとのピアリングに対応しているため、AWS側のASNは2byte、4byte(4200000000~4294967294)のどちらでも可能です。 Direct Connect接続部分のIPアドレスを決める Direct Connect の両端に、接続セグメントを作成します。 具体的には、社内ネットワークで利用されていないIPアドレスレンジから、/30のIPアドレスレンジを2つ割当し、Cato側・AWS側のIPアドレスを決めます。 例として、検証用に構成を作成し、ASNとIPアドレスを割り当てた図が以下です。 以上で、構成が確定です。 Cross Connect用Siteを作成する 続いて、Cato Management Consoleから、Cross Connectで接続するSiteを作成します。 作成方法は通常のSiteと同じですが、 Connection Type は 「Cross Connect」を選択 します。 この時点ではSiteを作成するのみでOKです。他の設定はDirect Connectの開通後に行います。 必要情報をCatoパートナーへ連絡する ここまで準備ができたら、実際にDirect ConnectをCato側から接続してもらうよう、以下の必要情報をCatoサービスの提供元へ連絡します。   項目 説明 記載例 1 対象のCato Site名 手順2で作成したSite名 CrossConnectAWS 2 接続するパブリッククラウド AWS/Azure/GCP/ Oracle Cloud AWS 3 パブリッククラウドのアカウントID 上記のアカウント AWSアカウント(数字12桁) 4 パブリッククラウドの利用リージョン メインで利用されるリージョン ap-northeast-1(Tokyo) 5 Primary Linkのロケーション 手順1で選択したもの Equinix TY2, Tokyo 6 Secondary Linkのロケーション 手順1で選択したもの Equinix OS1, Osaka 7 接続帯域 回線帯域 1Gbps 申請後、接続が準備されるまでに必要時間は、ロケーションによって異なります。1.のCross Connectロケーション一覧に納期の目安が書いてありますので、ご参照ください。2023年11月現在は日本ですと2週間程度となっています。 接続が準備され次第、パートナーから完了の連絡があります。 AWS側の設定を行う パートナーから準備完了の連絡を受けたら、AWS側・Cato側それぞれに設定を行っていきます。 なお、以後の手順はすべて、前述の 「構成例A Transit Gatewayを利用しない」パターンでの例 となります。 Direct Connect接続をAcceptする AWSコンソールの「Direct Connect」を確認すると、以下のように接続が準備されています。 状態がorderingとなっており、承諾待ちの状態です。IDをクリックすると「承諾する」ボタンがありますので、これをクリックして接続を承諾します。 2回線それぞれ承諾します。 しばらくすると状態が「available」になります。 Direct Connect Gatewayを作成する 設定項目は名称とASNのみです。ASNは手順1で決めたものを指定します。 この手順は、新規の環境を想定した例となっています。すでに各VPCで仮想プライベートゲートウェイ(VGW)を利用し通信を行っている場合、これ以降の作業により既存の通信が切断される可能性がありますので、十分にご注意ください。 Direct Connect Gatewayと仮想プライベートゲートウェイを紐付けする 作成したDirect Connect Gateway(以下DXGW)をクリックし、「ゲートウェイを関連付ける」に進みます。 紐付ける仮想プライベートゲートウェイ(以下VGW)をすべて選択します。 また、「許可されたプレフィックス」は、Cato側にBGPで広報するルートとなります。空欄にした場合には、各VPCのアドレスレンジが自動で広報されますので、特に要件がなければ空欄で問題ありません。 仮想インターフェイスを作成する Primary、Secondaryそれぞれの接続に対し、仮想インターフェイス(以下VIF)を作成します。 以後、AWS側・Cato側でそれぞれ設定を行っていきますが、お互いがお互いを「Peer(ピア)」と呼ぶので、IPアドレスやASNの設定項目がCato側を指しているのかAWS側を指しているのかを間違いやすいです。作成した構成図を見ながら作業されることをおすすめします。 以下に設定項目の例を掲載します。 タイプ VIFをDXGWへ接続する場合は「プライベート」を選択 仮想インターフェイス名 任意の名前を入力 接続 VIFを作成するDirect Connect接続をプルダウンで選択 仮想インターフェイスの所有者 通常は「自分のAWSアカウント」 ゲートウェイタイプ DXGWへ接続する場合は「Direct Connect ゲートウェイ」を選択 Direct Connect ゲートウェイ 先ほど作成したものをプルダウンで選択 Virtual Local Area Network 元となるDirect Connect接続のVLANと同じ番号を指定 BGP ASN 手順1で決めた Cato側の ASNを指定 上記まで入力したら 「▶追加設定」をクリック して下に進みます。 ここを忘れがちです。 アドレスファミリー IPv4を選択 ルーターのピアIP Cato側の IPアドレス AmazonルーターのピアIP AWS側の IPアドレス BGP認証キー 任意の文字列 ※設定必須 ジャンボMTU チェックを入れない (Catoは非対応) SiteLinkの有効化 チェックを入れない (Catoは非対応) すべて設定したら作成ボタンを押します。Primary/SecondaryそれぞれのVIFを作成したら、AWS側の設定は完了です。 CatoのSite側の設定を行う 続いて、Cato側の設定を行います。 Cross Connectを設定する Network > 対象Site  > Cross Connect の設定項目にて、Primary/Secondary の接続情報を設定します。手順1での設計どおりIPアドレスを設定します。また契約帯域に合わせたBandwidthを指定します。 設定後、Saveします。CatoとAWS間の疎通にしばらく時間がかかるため、次へ進みます。 BGPを設定する 続いて、Cato側のBGP設定を行います。左メニューの「BGP」から「New」をクリックして設定を作成します。PrimaryとSecondaryの2つの設定が必要です。    Name 任意の名前を指定 ASN Settings Peer 手順1で割り当てた AWS側の ASN (この構成だとDXGWのASN) ASN Settings Cato 手順1で割り当てた Cato側の ASN Peer AWS側 のIPアドレス 続いてBGP Policyの設定です。こちらはCatoおよびAWS内のルーティングに応じて設定する必要があります。 Advertise AWS側に対してどのようにルートをアドバタイズするかの設定です。 Default routeにすると、0.0.0.0をアドバタイズします。 もしAWS側で0.0.0.0を他に向ける必要がある場合には、All routesまたはSummary routesのアドバタイズをご検討ください。(※) Accept AWS側からのRouteを受け入れるかどうかです。 通常はチェックを入れます。 チェックを入れ忘れると、AWS側のルートをCatoで受け取らなくなってしまうので注意してください。 NAT CatoからPeer宛の通信において、送信元IPアドレスをNATするかどうかです。 チェックを入れた場合には、送信元IPがCato側のPeer IPアドレスに変換されます。 ※AWSの場合受け取れるルート数の上限があります。All routesをアドバタイズすると上限を超えがちなため、ご注意ください。(2023年11月現在は上限100ルート) MD5 Auth AWS側で設定したのと同じBGP認証キーの文字列を入力。入力必須です。 Metric 低い値の経路が優先されますので、値をPrimary<Secondaryとするのが一般的ですが、Cato側で自動でPrimary側が優先されるため、Metricを同じ値にしても正常に動作します。(優先度の詳細は後述します) Hold time デフォルト値で問題ありません。もし接続先のクラウド側で指定があればそれに従ってください。 Keepalive interval Email Notification BGPピアがDown/Upした際にEメールで通知する機能です。接続監視のため設定されることをおすすめします。 PrimaryとSecondary両方のBGP設定を作成したら、Saveします。 以上で設定は完了です。 接続テストを行う 続いて接続が正常にできるかのテストを行います。 Connectivityのテスト 先ほど設定したCross Connect のページで「Test Connectivity」ボタンをクリックします。Cato側からAWSに対し、ICMPでの疎通テストが実行されます。 以下のように「 Success! 」と表示されたら接続成功です。もし「Failed!」やその他エラーとなる場合は、アドレス設定がAWS側の設定と相違ないかを確認します。 BGPのステータス確認 BGPの設定画面で、「Show BGP Status」をクリックすると、BGPの状態が確認できます。 BGP Status BGP の接続状態です。「Established via ~」となっていれば正常に接続できています。 「Not Established」やその他表示の場合、BGP Peerが張れていません。IPアドレスやASNが正しく設定されているか、BGP認証キーがAWS側と一致しているかを確認します。 Routes from neighbor AWS側から受け取っているルートです。 ここが空欄の場合、BGP設定でAcceptのチェックが外れている可能性があります。 Routes to neighbor AWS側へ渡しているルートです。BGP設定のAdvertiseで設定したルートが渡されます。 Catoルートテーブルの確認 Monitoring > Routing Table で、AWS内のルートを受け取れていることを確認します。 VPCのIPアドレスレンジが「DYNAMIC」で受け取れていれば正常です。 図のように、通常時は同じルートが2つあり、Secondary Linkから受け取っているルートはグレーアウトしています。Primary Linkが障害等でDownした場合には、自動でPrimaryのルートが消え、Secondaryのルートが有効になります。 AWS側のルートテーブルを設定・確認する 続いて、AWS側のルートテーブルを確認します。この検証構成ですと、サーバがいるサブネットのルートテーブルを確認します。 AWSのルートテーブルでは、BGPで受け取ったルートをルートテーブルに自動反映するか、しないかを設定することができます。上記の「ルート伝播の編集」の箇所です。 デフォルトは、伝播が「いいえ」になっており、自動反映はされません。どちらにするかは、サブネット内のルーティング要件に合わせて検討します。 今回は伝播を確認するために「はい」に設定しました。すると、ルートが以下のようになります。 「伝播済み」が「はい」となっているルートが、Cato側から自動反映されたルートです。今回はCatoからAWSへDefault routeとAll routesを広報しており、それがAWSのルートテーブルに反映されていることがわかります。 以上ですべての設定が完了です。セキュリティグループ等を確認の上、Cato網からAWS上のホストへ疎通が通ることを確認します。 その他補足情報 障害テストの実施 Direct Connectでは、仮想インターフェイスのメニューから、BGPの障害テストを行うことができます。 この方法で、Primary Linkに擬似的に障害を発生させ、Secondary Linkへの切り替わりを試験することができます。 今回構築した検証環境で試したところ、以下のような結果になりました。通信先(172.16.0.29)はAWSのEC2、通信元は別SiteのSocket配下にあるWindows PCです。 切り替え前 : 172.16.255.1(Primary LinkのAWS側アドレス)を経由していることがわかります。 疑似障害発生 : Pingを打ち続けた状態で、前述のAWSの障害テストを行いました。Pingが止まった後、数秒で再開しました。 切り替わり後 : もう一度Tracerouteしたところ、172.16.255.5(Secondary LinkのAWS側アドレス)を経由して到達しています。経路が切り替わっていることがわかります。 このように、構築後には障害テストを実施することをおすすめします。 Primary/Secondaryの優先度について Cross Connectにおいては、Primary/Secondary Linkの優先度は、常にPrimary側が優先されるようCato側で制御されています。優先度はRouting TableのMetric Infomationから確認可能です。 Tunnel Metric(値が低いほうが優先)が、Primaryが5、Secondaryが10に自動設定されており、この値は変更することができません。 その下のWeightは、BGP設定の「Metric」で指定した値が反映されますが、経路選択においてはWeightよりもTunnel Metricが優先されるため、影響を与えることができません。 このため、 両方のLinkをActive/Activeで利用することはできません。 まとめ 以上、長くなりましたが、AWSでのCross Connect接続方法のご紹介でした。 他のパブリッククラウドでも、おおよその流れは同じになりますので、参考にしていただけますと幸いです。
アバター
今回は、パブリッククラウドをCatoクラウドへ接続する際の方法についてのご紹介です。 現在、Catoクラウドをご利用中・ご検討中のお客様のほとんどが、社内システムの一部または大半をパブリッククラウドに置かれており、クラウド環境をどのようにCatoと接続するかご相談をいただきます。 AWS・Azure・GCP等のパブリッククラウドをCatoへ接続する方法は、2023年11月現在、以下の3つがあります。   接続方法 対象環境 主な利用シーン 1 vSocket AWS/Azure/VMware ESXi 通常の場合 2 Cross Connect AWS/Azure/GCP/Oracle Cloud 高スループット・大容量通信を必要とする場合 3 IPSec すべての環境 1および2が利用できない場合 (機能制約が多いため) 通常は、最も手軽な方法として、vSocketでの接続方法をおすすめしておりますが、パブリッククラウド上のシステム数が多い場合や、高スループット・大容量通信を必要とする場合には、2番のCross Connectも合わせてご提案しております。 本記事では、このvSocketとCross Connectを、性能・コスト・その他の面で比較していきます。 なお、Cross Connectがどういったサービスなのかは、以下の記事にて詳細に解説しております。 Catoクラウド の Cross Connect について Catoクラウドの Socket/vSocket、IPsec以外の拠点接続方法である”Cross Connect”について紹介します。 blog.usize-tech.com 2023.08.25 また、別記事にて、Cross Connectの実際の設定方法をご紹介しておりますので、あわせてご参照ください。 Cross ConnectでAWSをCatoへ繋いでみよう AWS上のVPCをCatoクラウドのCross Connectで接続してみます。CatoとAWS Direct Connect、両方の設定の流れをご紹介します。 blog.usize-tech.com 2023.11.29 Cross Connectとは? CatoクラウドのCross Connectとは、 パブリッククラウドとCatoを仮想の専用回線で接続するサービス です。 具体的には、AWSのDirect Connectや、AzureのExpress Routeを利用し、クラウドエクスチェンジ経由でお客様のクラウド環境をCatoへ接続します。 vSocketがInternetを経由した暗号化通信を行うのに対し、 Cross Connectは仮想専用回線を利用するため暗号化が不要となり、より低遅延・高スループットの通信が可能 です。 このため、パブリッククラウド上に 高速な応答が求められるシステムを設置されている場合や、大容量の通信が想定される場合には、Cross Connectをおすすめ しています。 なお、Cross Connectは原則、最小契約帯域が501Mbps以上、2回線での冗長利用となります。詳細はご相談ください。 vSocket vs Cross Connect レスポンス比較 では、実際にどのくらいレスポンスが違うのでしょうか。vSocketとCross Connectで環境を構築し、応答時間を検証してみました。 条件 AWS東京リージョンに接続 vSocket・Cross Connectともに、Catoの帯域は1Gbpsで接続 Cato Clientで接続したSDPユーザから、VPC内の仮想サーバに対し32byteのPingを1000回実施 結果 接続方法 測定したラウンドトリップタイム vSocket 最小 = 29ms、最大 = 41ms、 平均 = 33ms Cross Connect 最小 = 17ms、最大 = 25ms、 平均 = 20ms 確かにCross Connectのほうがラウンドトリップタイムが短い、つまりレスポンスが早いという結果が得られました。 実際の通信においては、体感速度は様々な条件に左右されますが、少なくともIPレベルではCross Connectが有意に早いと言えそうです。 vSocket vs Cross Connect 費用比較 続いて費用面ではどうでしょうか。 専用線接続は一般的に基本利用費用が高額ですが、データ通信量が従量課金となっているパブリッククラウドの場合には、通信量が多いと専用線のほうが低コストになる場合があります。 では単純にパブリッククラウドの利用費用だけを比較した場合、vSocketとCross Connectでどのくらいの差があるのか、またどのくらいのデータ通信量があればコストが逆転しそうか、AWSの場合で計算してみました。 前提条件 AWSの東京リージョンを利用し、vSocketはHA構成、Cross Connect(AWSの場合Direct Connect)は1Gbpsの冗長構成とします。1ヶ月=730時間の計算です。 ※費用はすべて2023年11月現在のものです。あくまで机上の計算であることをご了承ください。 vSocketの月額概算費用 用途 サービス 単価(USD) 1ヶ月費用(USD) 備考 vSocket EC2 c5.xlarge ×2台 0.214/1時間 312.44 On-Demand料金 vSocket EBS gp2 16GiB×2台 0.12/GB・1ヶ月 3.84   データ通信量 アウトバウンド通信 0.114/GB ???   合計     316.28 + データ通信量   ※Saving Plansやリザーブドインスタンスを利用することにより費用削減可能です。 ※2024年2月より、IPv4 Global IPアドレスに対する費用が発生します。vSocket1台につき2つのGlobal IPアドレスを使用するため、その分の費用が追加発生します(4IPで約14.6USD/月)。 Direct Connect(専用線利用)の月額概算費用 用途 サービス 単価(USD) 1ヶ月費用(USD) 備考 Direct Connect ホスト型 1Gbps×2回線 0.314/1時間 458.44   データ通信量 アウトバウンド通信 0.041/GB ???   合計     458.44 + データ通信量   ※複数VPCがありTransit Gatewayを必要とする場合には、その利用料金が別途必要です。 比較結果 上記のとおり、データ通信量を除くとDirect Connectのほうが142.16USD分高コストとなります。 しかし、データ通信量の単価はDirect Connectのほうが安いので、一定量以上の通信量が発生するとコストが逆転します。この逆転の目安を計算すると、おおよそ2TB/月となりました。 つまり この条件においては、AWS⇒Catoのデータ通信量が月間2TBを超える場合にはDirect Connectのほうが安価 、2TBを超えない場合にはvSocketのほうが安価です。費用比較のひとつの目安にできるかと思います。 ※費用体系は各パブリッククラウドサービス(AWS/Azure/etc…)ごとに異なりますのでご注意ください しかしながら、 Direct Connectには前述のとおりスループット等性能面でのメリットがあるため、実際にはこれらも踏まえて比較検討 することになるかと存じます。 その他比較 また、vSocketとCross Connectでは他にも以下のような差異があります。 Cross ConnectではvSocketの運用が不要 vSocketは仮想アプライアンスですので、ユーザ側での構成管理やセキュリティグループ等のセキュリティ管理、またvSocketのファームアップがあったりと、仮想マシンとしての運用が発生します。 対してCross Connectは仮想マシンを持たないため、これらの運用が不要です。 Cross Connectでは利用できない機能もある Cross ConnectはSocketを持たず、かつインターネットを通らない接続のため、以下の機能は利用できません。 Local Routing / LAN Firewall Backhauling Bypass Local Port Forwarding 上記機能はいずれも、パブリッククラウドで利用するシーンはあまりないため、利用上の影響は少ないかと思います。 まとめ CatoクラウドのCross Connectは、vSocketとは異なり、仮想的な専用回線を使用しInternetを経由せずにCatoへ接続するサービスで、 利用をおすすめする場面は、主に下記2つです。 パブリッククラウド上に、高速な応答が求められるシステムを設置されている場合 パブリッククラウドから拠点/ユーザに対する通信量が多い場合 (AWSの場合はおよそ2TB/月目安) SCSKには、Catoクラウドとパブリッククラウド、両方に精通したエンジニアが多数おりますので、どちらにするかお悩みの際はぜひご相談ください。
アバター
はじめに こんにちは。SCSKのふくちーぬです。 今回は、AWS Resource Groupsのライフサイクルイベントを使用して、リソースに一意に付与している共通タグの変更検知をしてみます。 AWS Resource Groupsは、AWSクラウド上のリソースをカスタムグループにまとめるためのサービスです。AWS上で散らばったリソースの整理整頓をすることができます。 例として、EC2・セキュリティグループ等の複数リソースがある場合、それらを一つのグループにまとめておくことができます。これによって、関連するAWSリソースを見つけたり、一括で管理したりするのが容易になります。 環境準備 今回は、セキュリティグループとS3を作成します。作成時に、グループとしてまとめるために一意の共通タグも付与しておきます。 タグをつけ忘れた際には、リソース作成後付与することもできますし、Tag Editorを使用して一括で付与することもできます。 タグキー タグ値 RgName test-system これでリソースの作成は完了です。   Resource Groupの作成 グループの作成 今回は共通タグでリソースをグループ化します。”グループリソースをプレビュー”を押下します。 先ほど作成したセキュリティグループとS3が選択されています。 グループ名を入力します。今回は、”test-group”としています。 “グループを作成”を押下します。 グループが作成されました。 グループリソース内のリンクを押下すると、それぞれのマネジメントコンソールに飛ぶことができて便利ですね。 ライフサイクルイベントの有効 この機能を使用すると、グループ内のアクティビティをモニタリングすることができ、Event Bridgeで検知することが可能です。   AWS Resource Groups がライフサイクルイベントの発行を開始 aws.amazon.com Resource Groups グループライフサイクルイベントを有効にする - AWS Resource Groups リソースグループのライフサイクル変更に関する通知を受け取るには、グループライフサイクルイベントをオンにできます。次に、リソースグループは、グループの変更に関する情報を Amazon に提供します。 EventBridgeでは EventBridge、 サービスで定義したルールを使用して変更を評価し、それに基づいて対処で... docs.aws.amazon.com   ライフサイクルイベント有効するために、”オン”に変更します。   10分ほど待つと、”オン”になります。 これでEvent Bridgeとイベントのやり取りができるようになりました。   通知の設定 Event Bridgeを作成する前に、ターゲットとなる(=通知先となる)SNSトピックの作成をしておきます。 SNSトピックの作成 タイプは”スタンダード”としており、他はデフォルト設定としています。 Event Bridgeの設定 Resource Groupsのライフサイクルイベントについても、CloudTrail等のイベントの構文と記述方法は同様となります。 イベントルール内のイベントパターンの構文に迷ったら、AWSドキュメントのサンプルを参考にしてください。 Resource Groups ライフサイクルイベントの構造と構文 - AWS Resource Groups のライフサイクルイベントは、以下の一般的な形式の JSONAWS Resource Groups オブジェクト文字列の形式をとります。 docs.aws.amazon.com イベントルールの作成 それでは、リソースグループ内のリソースに付与された共通タグに変更があった際にイベントを送信するルールを作成します。   下記のイベントパターンを入力して、イベントルールを作成してください。  arn内の<AWSアカウントID>には、ご自身のAWSアカウントIDを入力してください。  対象とするリソースグループは、”test-group”とします。名前が異なる場合は、そちらを入力してください。 { "source": ["aws.resource-groups"], "detail": { "resources": { "membership-change": ["remove"] }, "group": { "arn": ["arn:aws:resource-groups:ap-northeast-1:<AWSアカウントID>:group/test-group"] } } }   “membership-change”を”remove”に設定することで、リソースグループから外れた場合に通知ができるようになります。 検証 今回は、以下の4種類のパターンで検証を行います。 ①共通タグを削除した場合 セキュリティグループの共通タグを削除してみます。 登録したメールアドレスに以下の通知がきます。 意訳すると、「EC2のセキュリティグループがリソースグループのメンバーから削除されました」という意味になります。 またResource Groupsのマネジメントコンソール上でもリソースグループ内から外されていることが分かります。もちろん共通タグを削除しただけなので、セキュリティグループ自体は存在しているので安心ください。 ②共通タグを変更した場合 セキュリティグループの共通タグを付与し直してください。 次に共通タグを変更してみます。ここでは、”test-system-change”に変更しました。 登録したメールアドレスに以下の通知がきます。 内容は先ほどと同様となります。 つまり、共通タグの名前を変更した場合にも”リソースグループから外れた”と判断されることになります。 ③リソースを削除した場合 セキュリティグループの共通タグを修正してください。 では、このセキュリティグループ自体を削除してください。   登録したメールアドレスに以下の通知がきます。 ここまでくると分かっている方も多いと思いますが、上記2パターンと同様の内容ですね。 つまり、リソース自体を削除した場合にも”リソースグループから外れた”と判断されることになります。 ④リソースを追加した場合 最後にリソースを追加してみます。 同様の方法でセキュリティグループを作成してください。 今度は、メール通知が来ないですね。 現在は”membership-change”を”remove”としているためですね。もしリソースグループに新しいリソースが追加された際にも検知したい場合は、以下のように”membership-change”に”add”を追加すれば問題ありません。 { "source" : [ "aws.resource-groups" ], "detail" : { "resources" : { "membership-change" : ["add", "remove" ] }, "group" : { "arn" : [ "arn:aws:resource-groups:ap-northeast-1:<AWSアカウントID>:group/test-group" ] } } } まとめ 検証の内容をまとめます。 特に③において、リソースの削除検知としても利用できないことはないです。 しかし、あくまでResource Groupsの判断基準は「共通タグに変更があったかどうか」で「実際にリソースが存在しているかどうか」まで面倒は見てくれません。 CloudTrailの証跡にて該当リソースの削除APIが実行されたかどうかも併せて判断するのがよいと思われます。 検証 検知の挙動 備考 ①共通タグを変更した場合 リソースグループから外されたと検知される 実際のリソースは存在する ②共通タグを削除した場合 実際のリソースは存在する ③リソースを削除した場合 実際のリソースは存在しない ※リソースの削除をしても検知されるが、削除の確証として使用したい場合にはマネコンでの存在確認/CloudTrailの証跡も併用するのがよい ④リソースを追加した場合 検知されない “membershup-change”に”add”を追加することで検知可能となる 最後に いかがだったでしょうか。Resource Groupsのライフサイクルイベントを使用して一通りの挙動を確認しました。 プロジェクトで各種AWSリソースを指定の共通タグで付与するといったケースが一般的にあります。初期構築時には一意の共通タグを付与したものの、共通タグの変更/リソースの削除・追加オペレーションが発生することは大いに発生します。そんな時に、共通タグの管理方法の一つして導入の検討をしていただければと思います。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
アバター
最近、 SSE と SASE どちらを選択すればよいのか?という質問をいただくことが多いので、改めてSSEとSASEについて説明をします。 まず、結論から先に言いますと、SSE、SASEどちらでも良いと思っています。 ただし、一点注意が必要なのは、 SSEを選択する場合には、SASEをカバーしているSSEを選択される方が賢明 です。 将来的にSASEが必要となった際に、採用したSSEがSASEをカバーしていなければ、導入のやり直しをする必要があるためです。   SSEとSASEについて まず、SSE(エス・エス・イー)は「 Security Service Edge 」、SASE(サッシー、サーシ)は「 Secure Service Access Edge 」の略称です。 ともにアメリカ調査会社であるガートナーが定義(提唱)しており、SASEは2019年に、SSEは2年後の2021年に提唱されました。 2023年6月に当社が独自調査した「国内企業における SASE に関する実態調査結果」を8月に公開しています。 SASEの認知度、実際の導入企業はどれくらいなのか? SASE に関する実態調査結果とCatoクラウドの認知度について blog.usize-tech.com 2023.09.01 SSE、SASEの認知度(知名度)を調査していますが、SSEの認知度(よく知っている、ある程度知っている)は17.2%で、SASEの認知度は31.6%と、SASEの2年後に登場したSSEより認知度低い状況です。 いずれにしても、SSE、SASE共に7割から8割の方は、よく知らない状況ですので、この記事をご覧の方も、よくご存じない方が多いことを前提に、まずは2019年に登場したSASEについて説明します。 SASEは、ガートナーが2019年8月発行したレポート「 ネットワークセキュリティの未来はクラウドにある (The Future of Network Security Is in the Cloud)」において提唱した、新たなネットワークセキュリティフレームワーク(概念)です。 IaaSやPaaS、SaaSのように言葉があまり浸透していませんでしたが、 NaaS (ナース)「Network as a Service」と、 NSaaS (エヌ・サース)「Network Security as a Service」が統合・融合したものが、SASEとされています。 NaaS + NSaaS = SASE NaaS機能 NSaaS機能 Routing QoS Caching/Content Delivery Traffic Shaping Latency Optimization SaaS Acceleration Geo Restriction etc. Cloud SWG ZTNA/SDP FWaaS Anti-Malware/Next Generation Anti-Malware IPS/IDS CASB/DLP DNS RBI etc. つまり、 SASEとは、ネットワークとセキュリティと統合し、ユーザが場所を問わず企業のシステム、アプリケーション、クラウドサービス、データにアクセスできるよう、安全で信頼性が高く、最適化されたアクセス(接続)を提供すること がコンセプトとなっています。 SASEのアーキテクチャとしては、 SD-WAN (Software-Defined Wide Area Network)、 NGFW (Next Generation Firewall)/ FWaaS (Firewall as a Service)、 SWG (Secure Web Gateway)、 ZTNA (Zero Trust Network Access)/ SDP (Software Defined Perimeter)などから構成されています。 SASEが流行した背景は、業種・業態を問わず、企業の従来のネットワークとセキュリティのインフラの変革期であったためだと言われています。 ハイブリッドワークとクラウドサービス利用の両方のニーズを効率的、かつセキュアにサポートする必要があるためです。 企業のネットワークは、そもそも通信キャリアが提供する専用線、閉域網・MPLS網(Multiprotocol Label Switching)をベースに構成されており、WAN、つまり企業の拠点間を接続することを目的(アーキテクト)としたネットワークインフラに、その後インターネットへの接続するためにネットワークやセキュリティ機器を1つの拠点であるデータセンター(または本社)を出口として集約し、さらにリモートワークをサポートするためのリモートアクセス機器もデータセンター(本社)へ集約することになりました。 その結果、WANのアーキテクチャとは異なる増改築を繰り返さざるを得なくなり、さらに、M365やオンラインストレージ(Dropbox/Box)などのクラウドサービスの利用をサポートするにあたっては、データセンター(本社)内でインターネットの出口分散(M365用のインターネット、Dropbox/Box用のインターネット、、)または、WANが狭帯域(低速)のため拠点からクラウドサービスに直接アクセスさせるLBO(Local Break Out:拠点脱出)をせざるを得ない状況になりました。 インターネットの出口分散やLBOは、きちんとしたセキュリティ検査が行われておらず、アクセス証跡(ログ)保管もきちんと行われていない場合があり、各拠点機器の設定(LBOを含む)は、拠点の担当者に任せるなど、一元管理が行えず、セキュリティインシデントが発生した際に、調査が非常に困難になるなどの課題を抱えることになりました。 つまり、このようなレガシーネットワークや、セキュリティ上の様々な課題、2020年に世界的にコロナウィルスのパンデミックが拡大しリモートワークの機会が増大したことが、SASEのコンセプトに非常にマッチしたため、SASEが流行したと言われています。 2年後の2021年に登場したSSEは、SASEからネットワーク除き、セキュリティ機能のみにフォーカスしたものです。 つまり、 SSEはあくまでもSASEのサブセット(一部分)であり、一般的には、SWG、ZTNA/SDP、CASB/DLP機能のみを指す 場合が多いようです。 ちなみに、ガートナーのSSE定義は、SWG、ZTNA、CASBの3機能になり、DLPやFWaaSは含まれません。 そのため、企業のネットワークインフラであるWANの課題はSSEでは解決ができません。 SWG、ZTNA/SDP、CASB/DLP機能は、あくまでもインターネット接続とリモートアクセスにフォーカスしています。 WANの課題については、通信キャリアが提供するSD-WANや、LBOで解決していることから、まずはSSEの導入を検討される場合があります。 逆に、SSEではなく、最初からSASEを検討されるお客様は、通信キャリア提供するSD-WANを利用されていても、高額であったり、納期が長かったり、ルーティング・NAT・セキュリティポリシーに制限があり、使い勝手が悪いなどの課題を持たれている場合が殆どです。 SSEで実現できること、できないこと SSEとSASEを検討する時、同時にゼロトラストを目指されている方が殆どだと思います。 ゼロトラスト(Zero Trust) は言葉の通り、「何も信用しない」というコンセプトに基づいた概念で、すべてのユーザやデバイス、接続先のロケーションを信用しないものとして捉えます。 通信の暗号化はもちろん、システム、アプリケーション、クラウドサービス、データにアクセスする際は、正当性や安全性を都度検証することをコンセプトとしています。 従来型のセキュリティである 境界型防御 は、「社内は安全」と「社外は危険」とし、その境界にFirewall(UTM)やProxyを設置することで、社外(外部)からの攻撃を防ぐアプローチとなります。 一方で、最近のランサムウェア被害の多くは、この境界であるFirewall(UTM)の脆弱性を狙ったものが殆どであることは説明するまでもないと思います。 インターネット上には脆弱性のある機器がリストアップされており、ある程度の知識があれば誰でも容易に侵入することが可能です。 SSE、SASEを検討されている方は、これまでの境界型防御をやめて、ゼロトラストモデル(ゼロトラストネットワーク・ゼロトラストセキュリティ)を目指されていると思いますが、 残念ながら、SSEではゼロトラストは極一部に限られ、境界型防御をやめることにはなりません 。 SSEで「ゼロトラストネットワークやゼロトラストセキュリティを実現」または「境界型防御からゼロトラストを実現」等は、過剰なマーケティングで、あくまでもデータセンター集約型のセキュリティ境界を、データセンターのFirewall(UTM)からクラウド(SSE)へ移すだけで、境界型防御のままです。 境界型防御のままですが、社外からのリモートアクセスについては、一部ゼロトラストの概念(ZTNA)がサポートされるため、”ゼロトラストアクセス”は実現可能ですが、社内からのアクセスについては何も変わっていませんので、ゼロトラストネットワーク、ゼロトラストセキュリティの実現はできません。 つまり、 SSEは境界型防御をオンプレからクラウドへ移行するソリューション、セキュリティ境界のクラウドリフト セキュリティ上の重要ポイントは、WAN、社内アクセスがそのままなので、拠点間の境界が存在しないことです。 いわゆるランサムウェアを含むマルウェアや攻撃者の水平方向への移動(ラテラルムーブメント)への防御ができません。 SSEで外部からの攻撃の防御力は上がっていますが、企業内ネットワークや、あるいは企業間ネットワークが通信キャリアが提供するレガシーネットワークインフラ(閉域網)のままでは、水平方向の攻撃を防ぐことはできません。 特に、最近の標的型攻撃は、防御力の高い企業を狙うのではなく、サプライチェーン企業(子会社・工場・関連グループ企業等)を狙う サプライチェーン攻撃 が増えてますので、その点でもSSEは不十分です。 それではSASEではどうか?と言うと以下の図のように、拠点間を含め、すべてに境界を設置、つまりゼロトラストモデルを実現していると言えます。 SSEは、SASEの移行期 SSEは多くの場合、SASEの移行期(移行段階、移行途中)である と言えます。 SCSKでは、世界初のSASEプラットフォーム提供ベンダーであるCato Networks社のCatoクラウド(Cato Cloud/Cato SASE Cloud)を、すでに30社以上のお客様の導入/運用をご支援させていただいておりますが、公開しているお客様導入事例にもありますが、SASE、Catoクラウドの導入にあたっては、いきなりWANの全面移行される例は殆どありません。 多くのお客様においては、リモートアクセスまたはProxyのリプレースでの採用からスタートし、その後、WAN移行(拠点移行)を実施される場合が殆どです。 つまり、PoCも含めると以下のようなプロセスとなります。 1. PoC→リモートアクセス移行→WAN移行→インターネット移行 2. PoC→インターネット移行→リモートアクセス移行→WAN移行 SSEのデータセンターを拠点接続(既存WANとSASE)のハブ拠点として、WANに接続されている拠点を徐々に移行することで、最終的にSASEへ移行することが可能となります。 シングルベンダーSASEについて SCSKでは、2021年から主要なSASEソリューションを一同に紹介を行うオンラインセミナー「SCSK SASE Solution Summit(通称 S4)」を定期的に開催しています。 2021年の開始当初から、Catoクラウド、Prisma Access、Netskope、Zscalerの4ソリューションの引き合いが多かったことから、これらの紹介を行っておりましたが、2022年にガートナーが、SASEの過剰なマーケティングを嫌厭した結果があってか、SASEは1社から購入すべきという方針のもと シングルベンダーSASE(Single-Vendor SASE) つまり単一の1社でSASEを提供するベンダーを再定義しました。 (SASEは、様々なベンダーのソリューションを組み合わせて初めて実現できるものである、と言う誤ったマーケティングも未だに存在しますが) シングルベンダーSASEに選出され、また、2023年8月に発表された「 シングルベンダーSASE マジック・クアドラント(Magic Quadrant for Single-Vendor SASE) 」に選出されたのは、以下の8社となります。 Cato Networks(Cato Cloud/Cato SASE Cloud) Palo Alto Networks(Prisma Access) Versa Networks(Versa SASE) Fortinet(FortiSASE) Cisco Forcepoint(Forcepoint ONE) VMware(VMware SASE) Juniper Networks Zscalerについては、シングルベンダーSASEでは無いため、SASEセミナーであるS4での取り扱いは辞めざるを得なくなりました。 ※Zscalerは、SSEとして定義されています。 あくまでも私見になりますが、ガートナーが、2019年にSASEを提唱し、その後、2021年にSSE、2022年にシングルベンダーSASEを発表したのは、SASEの発表をした後、ありとあらゆるベンダーが、我こそはSASEとしてマーケティングをスタートしましたが、実際には、殆どのソリューションがSASEの定義に沿っておらず、他のパートナーと協業した パートナーシップモデル のサービスが殆どでした。 そのため、2年後にSASEのサブセット(一部分)としてSSEを改めて定義し、その直後に「 SSE マジック・クアドラント(Magic Quadrant for Security Service Edge) 」を発表し、明確にSSEソリューションを定義しました(ちなみに、SSE MQには、Cato Networks社は入っていません) その後も、パートナーシップモデルのSASEが横行したので、シングルベンダーSASEを定義したのではないかと推測しています。 「2023年度版 ゼロトラストネットワーキングのハイプサイクル(Hype Cycle for Zero Trust Networking, 2023)」においても、SASEは多くのテクノロジーベンダーによる誇張されたマーケティングにより幻滅の谷にあるとあります。 2023年には「 シングルベンダーSASE マジック・クアドラント(Magic Quadrant for Single-Vendor SASE) 」も発表されていますので、SSEのみのベンダー、あるいはSASE(シングルベンダーSASE)ベンダーについては、2つのマジック・クアドラントをご覧いただければ良いと思います。 まとめ SSEとSASEについて説明しました。 SSEは、ゼロトラストを目指す場合には、SASEの移行期であると考えています。 もちろんすべての企業がSASEに移行するとは考えていませんが、実際にSSEソリューションからSASE、Catoクラウドへの切り替えのご相談も多く、実際に切り替えをされた事例も少なからずあります。 SSEからSASEへのニーズは、コスト(SSEが契約更新都度に価格アップ)、または通信キャリアのSD-WAN課題(コスト・納期・利便性)となります。 現状のWAN・SD-WANには特に課題はなく、SSE導入のみを検討されているお客様においても、将来的にゼロトラストを目指されているのであれば、SASE(特にシングルベンダーSASE)を前提に検討されるのが良いのではないかと思います。 前述のS4は、次回は2024年2月に開催する予定です。 シングルベンダーSASEの主要ソリューションであるCatoクラウド、Prisma Access、Netskope、Ciscoの4ソリューションを一同に紹介するセミナーですので、SSE、SASEをご検討の方は、是非ご参加ください。 また、SSEとSASEについて迷われている方、ゼロトラストについて相談されたい方は、お気軽に当社までお問い合わせください。
アバター
はじめに こんにちは。SCSKのふくちーぬです。 今回は、Amazon API Gateway REST API を自動デプロイするためのちょっとした工夫についてお話しします。 REST APIの場合、2回目のAPIのデプロイ以降、APIの内容に変更があった際に手動で再デプロイする必要があります。 API を更新するたびに、API を既存のステージまたは新しいステージに再デプロイする必要があります。API の更新には、ルート、メソッド、統合、オーソライザー、ステージ設定以外の変更が含まれます。 Amazon API Gateway での REST API のデプロイ - Amazon API Gateway Amazon API Gateway で REST API をデプロイする方法について説明します。 docs.aws.amazon.com   弊社広野の記事にも、その旨が説明されています。 Amazon API Gateway は設定を変更した後に必ずデプロイをする必要があります。そうしないと変更が反映されません。 HTTP API であれば自動デプロイの設定があり、設定変更後に自動でデプロイしてくれます。特別な事情がなければ、安全のために自動デプロイを活用した方が良いと思います。 REST API には自動デプロイの設定がありません。無理やり自動デプロイさせる仕組みも作り込みできなくはないのですが大変です。ここは今後の改善が待たれるところです。 Amazon API Gateway と AWS Lambda の連携でもうハマりたくない [AWS CloudFormation テンプレート付き] Amazon API Gateway と AWS Lambda の連携がうまくできず時間を浪費してしまう方は結構いらっしゃるのではないかと思います。Amazon API Gateway のタイプや AWS Lambda との統合の仕方によって AWS Lambda 関数のコードも変わるので厄介です。私はそこでハマるのが嫌で、Amazon API Gateway と AWS Lambda 関数の標準設定パターンを決めて使い回ししています。 blog.usize-tech.com 2022.04.11   では、この忘れがちな手動での再デプロイの手間をなくすためのを方法を紹介します。 構成とシナリオの確認 構成図 今回作成する構成は、以下の通りです。 一般的な Amazon API Gateway + AWS Lambda の構成ですね。APIに対して、2つのGETメソッドが存在します。 1つ目のGETメソッドを叩くと、UTC(イギリス時間)にて現在時刻が返却されます。 2つ目のGETメソッドを叩くと、JST(日本時間)にて現在時刻が返却されます。 シナリオ シナリオは、以下の通りです。 UTCを取得できるAPIを開発するよう依頼されました。しかし仕様変更が発生してJSTでも取得できるよう依頼され、即座にAPIの更新・デプロイをする必要がでてきました。 これを AWS CloudFormation (以降、CFN) テンプレートで表現していきます。 ①まずは、UTCを取得するためのメソッドを備えたAPIを作成します。 ②UTCが取得できることを確認します。 ③CFNテンプレートを加筆修正して、JSTを取得するためのメソッドも備えたAPIへ自動デプロイ(更新)します。 ④UTC・JSTが取得できることを確認します。 環境準備編(UTC APIの構築) REST APIやLambdaを準備するために、以下のCFNテンプレートにて構築します。 CloudFormationコンソールやCLIにてデプロイをします。 AWSTemplateFormatVersion: '2010-09-09' Parameters: ResourceName:   Type: String APIStage:   Type: String Resources: # ------------------------------------------------------------# #  IAM Role # ------------------------------------------------------------# LambdaRole:   Type: AWS::IAM::Role   Properties:     RoleName: !Sub ${ResourceName}-lambda-role     AssumeRolePolicyDocument:       Version: '2012-10-17'       Statement:         - Effect: Allow           Action: sts:AssumeRole           Principal:             Service:               - lambda.amazonaws.com     ManagedPolicyArns:       - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # ------------------------------------------------------------# # APIGateway UtcnowLambda # ------------------------------------------------------------# UtcnowFunction:   Type: AWS::Lambda::Function   Properties:     FunctionName: !Sub ${ResourceName}-lambda-function-utcnow     Role: !GetAtt LambdaRole.Arn     Runtime: python3.11     Handler: index.lambda_handler     Code:       ZipFile: !Sub |         import json         from datetime import datetime, timedelta         def lambda_handler(event, context):             utc_now = datetime.utcnow()             format_time = utc_now.strftime("%Y-%m-%d %H:%M:%S")             print(utc_now)             return {                 'statusCode': 200,                 'body': json.dumps(format_time)             } RestApi:   Type: AWS::ApiGateway::RestApi   Properties:     Name: !Sub ${ResourceName}-apigateway UtcnowResource:   Type: AWS::ApiGateway::Resource   Properties:     RestApiId: !Ref RestApi     ParentId: !GetAtt RestApi.RootResourceId     PathPart: utcnow UtcnowLambdaPermission:   Type: AWS::Lambda::Permission   Properties:     FunctionName: !Ref UtcnowFunction     Action: lambda:InvokeFunction     Principal: apigateway.amazonaws.com   DependsOn: UtcnowResource UtcnowMethod:   Type: AWS::ApiGateway::Method   Properties:     RestApiId: !Ref RestApi     ResourceId: !Ref UtcnowResource     AuthorizationType: None     HttpMethod: GET     Integration:       Type: AWS_PROXY       IntegrationHttpMethod: POST       Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ResourceName}-lambda-function-utcnow/invocations   DependsOn: UtcnowLambdaPermission DeploymentVer1: ##(必須)デプロイメントIDを変更するため、バージョンが分かるよう記載しておく。   Type: AWS::ApiGateway::Deployment   Properties:     RestApiId: !Ref RestApi     Description: ver1 ##(任意)Descriptionを変更するため、バージョンが分かるよう記載しておく。   DependsOn:     - UtcnowMethod     #- JstnowMethod Stage:   Type: AWS::ApiGateway::Stage   Properties:     StageName: !Ref APIStage     Description: dev stage     RestApiId: !Ref RestApi     DeploymentId: !Ref DeploymentVer1 ##(必須)デプロイメントIDを変更するため、バージョンが分かるよう記載しておく。 #------------------------------------------------------------# # APIGateway JstnowLambda #------------------------------------------------------------# # JstnowFunction: #   Type: AWS::Lambda::Function #   Properties: #     FunctionName: !Sub ${ResourceName}-lambda-function-jstnow #     Role: !GetAtt LambdaRole.Arn #     Runtime: python3.11 #     Handler: index.lambda_handler #     Code: #       ZipFile: !Sub | #         import json #         from datetime import datetime, timedelta           #         def lambda_handler(event, context): #             utc_now = datetime.utcnow() #             jst_now = utc_now + timedelta(hours=9) #             format_time = jst_now.strftime("%Y-%m-%d %H:%M:%S")               #             print(jst_now) #             return { #                 'statusCode': 200, #                 'body': json.dumps(format_time) #             }             # JstnowResource: #   Type: AWS::ApiGateway::Resource #   Properties: #     RestApiId: !Ref RestApi #     ParentId: !GetAtt RestApi.RootResourceId #     PathPart: jstnow #   DependsOn: #     - RestApi # JstnowLambdaPermission: #   Type: AWS::Lambda::Permission #   Properties: #     FunctionName: !Ref JstnowFunction #     Action: lambda:InvokeFunction #     Principal: apigateway.amazonaws.com #   DependsOn: JstnowResource # JstnowMethod: #   Type: AWS::ApiGateway::Method #   Properties: #     RestApiId: !Ref RestApi #     ResourceId: !Ref JstnowResource #     AuthorizationType: None #     HttpMethod: GET #     Integration: #       Type: AWS_PROXY #       IntegrationHttpMethod: POST #       Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ResourceName}-lambda-function-jstnow/invocations   #   DependsOn: JstnowLambdaPermission デプロイ後のURLを叩きます。 今回の場合は、”https://lefldh5ct1.execute-api.ap-northeast-1.amazonaws.com/dev/utcnow”となります。 restapiidは、各々異なるため自身のものをご確認ください。 現在時刻のUTCが取得できましたね! API更新編(JST APIの追加) 上記テンプレートを修正して、現在時刻がJSTでも取得でき、自動デプロイされるようにします。 CFNテンプレートの記述テクニック Deploymentの論理IDを強制的に変更することで、デプロイメントが再作成されるようにする。 これだけで、自動デプロイが可能になります。加筆・修正箇所をそれぞれ見ていきましょう。 JST APIの追加 1番簡単な箇所から説明します。UTC APIの時と同様に、JSTにて現在時刻が取得できるようリソースを追加します。 コメントアウトを外すだけです。 #------------------------------------------------------------# # APIGateway JstnowLambda #------------------------------------------------------------# JstnowFunction:   Type: AWS::Lambda::Function   Properties:     FunctionName: !Sub ${ResourceName}-lambda-function-jstnow     Role: !GetAtt LambdaRole.Arn     Runtime: python3.11     Handler: index.lambda_handler     Code:       ZipFile: !Sub |         import json         from datetime import datetime, timedelta                 def lambda_handler(event, context):             utc_now = datetime.utcnow()             jst_now = utc_now + timedelta(hours=9)             format_time = jst_now.strftime("%Y-%m-%d %H:%M:%S")                         print(jst_now)             return {                 'statusCode': 200,                 'body': json.dumps(format_time)             }             JstnowResource:   Type: AWS::ApiGateway::Resource   Properties:     RestApiId: !Ref RestApi     ParentId: !GetAtt RestApi.RootResourceId     PathPart: jstnow   DependsOn:     - RestApi JstnowLambdaPermission:   Type: AWS::Lambda::Permission   Properties:     FunctionName: !Ref JstnowFunction     Action: lambda:InvokeFunction     Principal: apigateway.amazonaws.com   DependsOn: JstnowResource JstnowMethod:   Type: AWS::ApiGateway::Method   Properties:     RestApiId: !Ref RestApi     ResourceId: !Ref JstnowResource     AuthorizationType: None     HttpMethod: GET     Integration:       Type: AWS_PROXY       IntegrationHttpMethod: POST       Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ResourceName}-lambda-function-jstnow/invocations   DependsOn: JstnowLambdaPermission デプロイメントの修正 デプロイメントの記述部分を修正します。 デプロイメントのリソースIDについて、”DeploymentVer1″から”DeploymentVer2″に修正してください。これにより、devステージへ自動で再デプロイされるようにします。 “Description”についても、後々分かりやすいように”ver2″と修正しておきましょう。 最後に”DependsOnについて、”JstnowMethod”を追加しましょう。JST APIのリソース・メソッドが作成された後に、デプロイされるようコントロールしています。 DeploymentVer2: ##(必須) DeploymentVer2に変更。   Type: AWS::ApiGateway::Deployment   Properties:     RestApiId: !Ref RestApi     Description: ver2 ##(任意)ver2に変更。   DependsOn:     - UtcnowMethod   - Jst nowMethod   完成したテンプレート 完成した加筆修正後のテンプレートは、以下の通りです。 AWSTemplateFormatVersion: '2010-09-09' Parameters: ResourceName:   Type: String APIStage:   Type: String Resources: # ------------------------------------------------------------# #  IAM Role # ------------------------------------------------------------# LambdaRole:   Type: AWS::IAM::Role   Properties:     RoleName: !Sub ${ResourceName}-lambda-role     AssumeRolePolicyDocument:       Version: '2012-10-17'       Statement:         - Effect: Allow           Action: sts:AssumeRole           Principal:             Service:               - lambda.amazonaws.com     ManagedPolicyArns:       - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # ------------------------------------------------------------# # APIGateway UtcnowLambda # ------------------------------------------------------------# UtcnowFunction:   Type: AWS::Lambda::Function   Properties:     FunctionName: !Sub ${ResourceName}-lambda-function-utcnow     Role: !GetAtt LambdaRole.Arn     Runtime: python3.11     Handler: index.lambda_handler     Code:       ZipFile: !Sub |         import json         from datetime import datetime, timedelta         def lambda_handler(event, context):             utc_now = datetime.utcnow()             format_time = utc_now.strftime("%Y-%m-%d %H:%M:%S")             print(utc_now)             return {                 'statusCode': 200,                 'body': json.dumps(format_time)             } RestApi:   Type: AWS::ApiGateway::RestApi   Properties:     Name: !Sub ${ResourceName}-apigateway UtcnowResource:   Type: AWS::ApiGateway::Resource   Properties:     RestApiId: !Ref RestApi     ParentId: !GetAtt RestApi.RootResourceId     PathPart: utcnow UtcnowLambdaPermission:   Type: AWS::Lambda::Permission   Properties:     FunctionName: !Ref UtcnowFunction     Action: lambda:InvokeFunction     Principal: apigateway.amazonaws.com   DependsOn: UtcnowResource UtcnowMethod:   Type: AWS::ApiGateway::Method   Properties:     RestApiId: !Ref RestApi     ResourceId: !Ref UtcnowResource     AuthorizationType: None     HttpMethod: GET     Integration:       Type: AWS_PROXY       IntegrationHttpMethod: POST       Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ResourceName}-lambda-function-utcnow/invocations   DependsOn: UtcnowLambdaPermission DeploymentVer2: ##(必須) DeploymentVer2に変更。   Type: AWS::ApiGateway::Deployment   Properties:     RestApiId: !Ref RestApi     Description: ver2 ##(任意)ver2に変更。   DependsOn:     - UtcnowMethod       - Jst nowMethod       Stage:   Type: AWS::ApiGateway::Stage   Properties:     StageName: !Ref APIStage     Description: dev stage     RestApiId: !Ref RestApi     DeploymentId: !Ref DeploymentVer2 ## (必須) DeploymentVer2に変更 #------------------------------------------------------------# # APIGateway JstnowLambda #------------------------------------------------------------# JstnowFunction:   Type: AWS::Lambda::Function   Properties:     FunctionName: !Sub ${ResourceName}-lambda-function-jstnow     Role: !GetAtt LambdaRole.Arn     Runtime: python3.11     Handler: index.lambda_handler     Code:       ZipFile: !Sub |         import json         from datetime import datetime, timedelta                 def lambda_handler(event, context):             utc_now = datetime.utcnow()             jst_now = utc_now + timedelta(hours=9)             format_time = jst_now.strftime("%Y-%m-%d %H:%M:%S")                         print(jst_now)             return {                 'statusCode': 200,                 'body': json.dumps(format_time)             }             JstnowResource:   Type: AWS::ApiGateway::Resource   Properties:     RestApiId: !Ref RestApi     ParentId: !GetAtt RestApi.RootResourceId     PathPart: jstnow   DependsOn:     - RestApi JstnowLambdaPermission:   Type: AWS::Lambda::Permission   Properties:     FunctionName: !Ref JstnowFunction     Action: lambda:InvokeFunction     Principal: apigateway.amazonaws.com   DependsOn: JstnowResource JstnowMethod:   Type: AWS::ApiGateway::Method   Properties:     RestApiId: !Ref RestApi     ResourceId: !Ref JstnowResource     AuthorizationType: None     HttpMethod: GET     Integration:       Type: AWS_PROXY       IntegrationHttpMethod: POST       Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ResourceName}-lambda-function-jstnow/invocations   DependsOn: JstnowLambdaPermission スタックの更新 上記テンプレートを使用して、スタックの更新を行ってください。 スタックの更新が完了したら、リソースの作成順序を確認してみましょう。 JST Resource作成 ⇒ JST Lambda作成 ⇒ JST Method作成 ⇒ Deployment Ver2作成 ⇒ Dev Stage更新 ⇒ Deployment Ver1削除 つまりデプロイメントが再作成されていることを示しています。 マネジメントコンソールから手動で再デプロイせずに、自動デプロイが完了しました! API呼び出し では、早速APIリクエストをしてみましょう。 UTC APIリクエスト 先ほどど同様のリクエストURLとなります。   JST APIリクエスト 今回の場合は、”https://lefldh5ct1.execute-api.ap-northeast-1.amazonaws.com/dev/utcnow”となります。 リソース部分が異なるだけですね、JSTでのAPIリクエストも成功しました! あれ、日本時間の深夜1時にリクエストしていますね。。いえいえただの夜更かしです(笑) 注意点 自動デプロイ実施時は、ステージに関連付いているデプロイ履歴が1つしか保有できません。 つまり、以下のような状態になります。 ステージのデプロイ履歴から、以前のバージョン(ver1)に戻すことができないのでご注意ください。この事象は、デプロイメントを再作成しているのが起因です。もし以前のバージョンに戻す際は、以前のCFNテンプレートを利用しデプロイメントを再作成する流れとなります。 自動デプロイを利用する際には、CFNテンプレートを別途Gitでバージョン管理・保管しておくこと まとめ いかがだったでしょうか。REST APIを自動デプロイするためのテクニックをご紹介しました。メリット・デメリットも感じましたが、要件に応じて導入も検討して頂ければと思います。そしてREST APIの自動デプロイがサポートされることを心待ちにしています。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
アバター