TECH PLAY

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

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

1268

はじめに こんにちは。SCSKのふくちーぬです。 皆さんは、イベントベースのCodePipelineのパイプラインを利用していますでしょうか。それともポーリングベースのパイプラインを利用していますでしょうか。 まだポーリングベースを利用している方は、本記事を読んでいただきイベントベースへの移行をしていただけると幸いです。(ふくちーぬもその内の1人でした。。) 本記事では、以下のAWS公式ドキュメントに従いCodeCommitをソースとしたCloudFormationで構築済みのポーリングベースのCodepipelineからイベントベースへの移行を実施します。 ポーリングパイプラインを移行してイベントベースの変更検出を使用する - AWS CodePipeline パイプラインをイベントベースの変更検出用に更新する方法をソースタイプ別に説明します。 docs.aws.amazon.com CodePipelineの検出オプションの種類 CodePipelineには、ソースの検出オプションとしてポーリングベース・イベントベースの2種類があります。 ポーリングベースでは、CodePipelineがCodeCommit内の対象ブランチに対して定期的に監視することで、変更を検出しています。 イベントベースでは、対象ブランチに変更があった際にEventBridgeにイベントを送信するだけです。すなわちCodePipelineは、どんと構えてEventBridgeからのメッセージを待っていればよいだけです。 ポーリングベースのパイプラインの移行を推奨している件 2023年4月下旬に、AWS サポートから以下のようなメールが届きました。 本メッセージは、1つ以上ポーリングパイプラインをお持ちのお客様にお送りしています。ポーリングパイプラインは、変更をポーリングするように設定されたソースアクションが少なくとも1つあるパイプラインとして定義されます。AWS CodePipelineチームは、2023年5月25日より、非アクティブなパイプラインでのポーリングを無効にします。非アクティブなパイプラインとは、過去30日間にパイプラインの実行が開始されていないパイプラインと定義されます。Amazon EventBridgeルール、AWS CodeStar接続、またはウェブフックのみを使用してパイプラインをトリガーする非アクティブなパイプラインは影響を受けません。また、アクティブなパイプラインも影響を受けません。 ソースのトリガーメカニズムとして、Amazon EventBridge、AWS CodeStar接続、またはウェブフックを使用するように更新することをお勧めします。 つまり、「30日以上利用されていないポーリングベースのパイプラインを無効にする」、「ポーリングベースのパイプラインを移行することを推奨する」のアナウンスをしています。 現時点でも、ポーリングベースのパイプラインを新規に作成することはできます。また無効になったパイプラインも手動で実行することで、パイプラインを起動することは可能です。 推測ですが、AWS的にCodePipelineがポーリングするコストが割りに合っていないことも要因の1つかもしれませんね。 イベントベースのパイプラインを推奨する理由 ソースリポジトリのブランチの変更を迅速に検出できるようになり、パイプライン実行パフォーマンスが向上する 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 CodeCommitへのソースコード配置 今回の検証ではCodeCommitに配置するものは任意のもので問題ありませんが、以下を利用することにします。 CI/CD配下でネストされたスタックの子スタックに対して、変更セットを有効にするテクニック[AWS CodePipeline+AWS CloudFormation] CI/CD 配下のネストされた AWS CloudFormation スタックに対して変更セットを有効化する方法を紹介します。 blog.usize-tech.com 2023.12.07 CI/CDパイプラインの作成 ご使用のAWSアカウント内にポーリングベースのパイプラインがCloudFormationにて作成済みの方は、それをご利用ください。 今回は、新規にポーリングベースのパイプラインを作成した後に、イベントベースへ移行します。 ポーリングベースのパイプラインの作成 ポイントはパイプラインの定義にて、”PollForSourceChanges”を”true”に設定することでポーリングベースのパイプラインを作成できます。 以下の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 MailAddress: Type: String Resources: 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 # ------------------------------------------------------------# # 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 - Sid: SNSAccess Effect: Allow Action: - sns:Publish Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${ResourceName}-topic # ------------------------------------------------------------# # 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: true #ポーリングベースのパイプラインでは、"true"に設定 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: Approval Actions: - Name: approve-changeset ActionTypeId: Category: Approval Owner: AWS Version: 1 Provider: Manual Configuration: ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId} NotificationArn: !GetAtt SNSTopic.TopicArn - 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 # ------------------------------------------------------------# # SNS # ------------------------------------------------------------# SNSTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Ref MailAddress Protocol: email TopicName: !Sub ${ResourceName}-topic ポーリングベースのパイプラインの一覧の取得 今回はboto3(Python)を使用して、ポーリングベースのパイプラインの一覧を取得してみます。 Cloud9へのboto3のインストール 以下のコマンドを実行して、boto3ライブラリのインストールをします。 pip install boto3 以下のコマンドを実行して、バージョンが返却されればインストール済みとなります。今回は、”boto3 1.33.11″が返ってきています。 pip list | grep boto3 Pythonスクリプトの作成 Cloud9におけるGUI経由でのファイル作成方法は、以下を参照ください。 AMI から AWS Cloud9 を復元する Cloud9を削除してしまった際の適切な復元方法についてお話します。 blog.usize-tech.com 2023.11.24 以下のPythonファイルを任意のディレクトリに作成してください。ファイル名は、” PollingPipelinesExtractor . py”とします。 import boto3 import sys import time import math hasNextToken = True nextToken = "" pollablePipelines = [] lastExecutedTimes = [] if len(sys.argv) == 1: raise Exception("Please provide region name.") session = boto3.Session(profile_name='default', region_name=sys.argv[1]) codepipeline = session.client('codepipeline') def is_pollable_action(action): actionTypeId = action['actionTypeId'] configuration = action['configuration'] return actionTypeId['owner'] in {"AWS", "ThirdParty"} and actionTypeId['provider'] in {"GitHub", "CodeCommit", "S3"} and ('PollForSourceChanges' not in configuration or configuration['PollForSourceChanges'] == 'true') def has_pollable_actions(pipeline): hasPollableAction = False pipelineDefinition = codepipeline.get_pipeline(name=pipeline['name'])['pipeline'] for action in pipelineDefinition['stages'][0]['actions']: hasPollableAction = is_pollable_action(action) if hasPollableAction: break return hasPollableAction def get_last_executed_time(pipelineName): pipelineExecutions=codepipeline.list_pipeline_executions(pipelineName=pipelineName)['pipelineExecutionSummaries'] if pipelineExecutions: return pipelineExecutions[0]['startTime'].strftime("%A %m/%d/%Y, %H:%M:%S") else: return "Not executed in last year" while hasNextToken: if nextToken=="": list_pipelines_response = codepipeline.list_pipelines() else: list_pipelines_response = codepipeline.list_pipelines(nextToken=nextToken) if 'nextToken' in list_pipelines_response: nextToken = list_pipelines_response['nextToken'] else: hasNextToken= False for pipeline in list_pipelines_response['pipelines']: if has_pollable_actions(pipeline): pollablePipelines.append(pipeline['name']) lastExecutedTimes.append(get_last_executed_time(pipeline['name'])) fileName="{region}-{timeNow}.csv".format(region=sys.argv[1],timeNow=math.trunc(time.time())) file = open(fileName, 'w') print ("{:<30} {:<30} {:<30}".format('Polling Pipeline Name', '|','Last Executed Time')) print ("{:<30} {:<30} {:<30}".format('_____________________', '|','__________________')) for i in range(len(pollablePipelines)): print("{:<30} {:<30} {:<30}".format(pollablePipelines[i], '|', lastExecutedTimes[i])) file.write("{pipeline},".format(pipeline=pollablePipelines[i])) file.close() print("\nSaving Polling Pipeline Names to file {fileName}".format(fileName=fileName))  Pythonスクリプトの実行 以下のコマンドを実行して、pythonスクリプトを実行します。 引数として、指定のリージョンを入力します。ここでは、東京リージョンの”ap-northeast-1″を指定します。 python3 PollingPipelinesExtractor.py ap-northeast-1    指定のリージョン内に存在しているポーリングベースのパイプラインの一覧を取得することができます。これで、どのパイプラインがポーリングベースを採用しているのか一目瞭然ですね。またCSVファイルとしても出力されています。 今回は、”cicd-20231210-pipeline”を新規作成したので表示されています。ご自身で作成したものと読み替えてください。 イベントベースのパイプラインへ移行 ポーリングベースからイベントベースへ移行するために、CloudFormationテンプレートの修正をしていきます。 PollForSourceChangesの変更 以下のように、ポーリングベースを採用しないようにCodePipelineの”PollForSourceChanges”プロパティを”false”に変更します。 Stages: - Name: Source Actions: - Name: download-source ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit Configuration: RepositoryName: !Ref REPOSITORYNAME BranchName: main PollForSourceChanges: false #イベントベースを採用するためfalseに変更 OutputArtifacts: - Name: SourceOutput  EventBridgeが利用するIAMポリシー・ロールの追加 以下のように、EventBridgeが利用するためのIAMポリシー・ロールを作成するための記述を追加します。 EventBridgeがCodePipelineを起動できる権限を付与しています。 # ------------------------------------------------------------# # 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"  EventBridgeの追加 以下のように、EventBridge本体の記述を追加します。対象のリポジトリや付与するロール等の指定もしています。 これによりCodeCommitのブランチの更新をトリガーにCodePipelineを起動できるようになります。 # ------------------------------------------------------------# # 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  完成したテンプレート 完成したテンプレートは、以下の通りです。 AWSTemplateFormatVersion: 2010-09-09 Description: cfn CI/CD Pipeline Parameters: ResourceName: Type: String REPOSITORYNAME: Type: String Description: aws codecommit repository name STACKNAME: Type: String MailAddress: Type: String Resources: 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 # ------------------------------------------------------------# # 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 - Sid: SNSAccess Effect: Allow Action: - sns:Publish Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${ResourceName}-topic # ------------------------------------------------------------# # 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 #イベントベースを採用するため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: Approval Actions: - Name: approve-changeset ActionTypeId: Category: Approval Owner: AWS Version: 1 Provider: Manual Configuration: ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId} NotificationArn: !GetAtt SNSTopic.TopicArn - 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 # ------------------------------------------------------------# # SNS # ------------------------------------------------------------# SNSTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Ref MailAddress Protocol: email TopicName: !Sub ${ResourceName}-topic  CI/CDパイプラインの更新 上記テンプレートを使用して、CI/CDパイプラインのスタックを更新します。 CloudFormationコンソールから該当のスタック選択して、”更新”を押下してください。   修正済みのテンプレートファイルをアップデートして、”次へ”を押下してください。   既存のパラメータ等は変更することなく、”次へ”を押下してください。 “次へ”を押下してください。   最後にレビュー画面に遷移します。内容に間違いがないか確認します。 変更セットのレビューにて、IAMポリシー・IAMロール・EventBridgeルールが追加されることを確認します。またCodePipelineでは、変更があることも確認します。問題なければ”送信”を押下してください。   数分経過すると、スタックの更新が完了します。 移行の確認 移行が完了したかどうか、 こちらの手順 で再度スクリプトを実行することで確かめます。 先ほどまで存在した”cicd-20231210-pipeline”が表示されていないため、イベントベースのパイプラインとして正常に認識されています。 これにて移行完了となります。お疲れ様でした。 パイプライン起動確認 念の為、イベントベースのパイプラインが起動するか確認しておきます。 CodeCommitにプッシュすると、パイプラインが起動していることが分かります。(承認ステージを組み込んでいるため、レビュー中で一時停止しています。) また、イベントベースへの移行を勧める警告メッセージも表示されなくなっています。 ちなみに、ポーリングベースのパイプラインを利用していると、CodePipelineの対象パイプラインの画面を開くたびに以下のように警告メッセージが表示されます。 パイプラインがアクティブの場合: このパイプラインには、ポーリング用に設定されたソース(download-source)があります。変更検出用に推奨されるイベントベースのメカニズムにパイプラインを移行(更新)します。詳細については、「 移行ガイド 」を参照してください。   30日以上パイプラインを起動していない(アクティブでない)場合: ○か月前時点で、CodePipelineはこのパイプラインのポーリングを無効にしています。これは、以前30日間に実行されていないためです。推奨されるイベントベースのメカニズムを使用して、新しいパイプライン実行を開始します。 最後に いかがでしたでしょうか。 CodeCommitをソースとしたCloudFormationで構築済みのポーリングベースのCodePipelineをイベントベースへと移行しました。 既存のCodePipelineでポーリングベースを利用している方は、是非この手順に沿って移行の検討をしていただければと思います。また新規でパイプラインを構築する際は、イベントベースでパイプラインを構築するように意識してください。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
本記事は TechHarmony Advent Calendar 12/13付の記事です。 どうも、Catoクラウドを担当している佐々木です。 今回は、先日機能追加が発表された Catoクライアントの常時起動 「Always-On」の新機能 についてご紹介します。   ※画⾯は2023年12⽉時点のものです。機能アップデート等で変わる場合がありますので、あらかじめご了承ください。   Always-Onとは SDP ユーザーのインターネットセキュリティを強化するための機能です。 「Always-On」を使用すると、CatoクライアントのON/OFFをユーザー側で制御することができなくなり、 SDPユーザーからのトラフィックが常に「Catoクラウド」を通過するようにできます。   詳細は、以下のブログ記事を参照ください。 CatoクラウドのAlways-Onを試してみた Catoクラウドで「Always-On」を試してみました。設定方法~動作検証までやってます。 blog.usize-tech.com 2023.08.28   Always-Onの新機能 2023/11/20のアップデート情報で以下3つの新機能が発表されました。 Remote Internet Security with One Time Authentication New Bypass Mode for Always-On Always-On Recovery Mode   それぞれ異なる機能になりますので、個別に説明していきます。 なお、このアップデート情報は以下で確認できます。 Catoクラウドアップデート情報(2023年11月20日) | よくあるご質問 | Cato Cloud ケイトクラウド - SCSK □新機能と機能強化・ソケット(Socket)接続ステータスの可視性を強化 ソケット・ポートとリンクのステータスを表示するために以下の拡張を行いました。 ソケットの物理ポートと接続ステー... cato-scsk.dga.jp   2023/12/13時点で、この機能はまだ利用できません。                              Remote Internet Security with One Time Authentication ⇒ EA(Early Availability)               New Bypass Mode for Always-On / Always-On Recovery Mode ⇒ 2023/12/4から数週間後にアップデート予定                                     ※EAの申し込みは当社担当者またはサービス窓口までご連絡ください。   「Remote Internet Security with One Time Authentication」について 直訳すると、「ワンタイム認証によるリモートインターネットセキュリティ」となります。   この機能は、Catoのナレッジやアップデート情報を見ても何ができるのか非常にわかりにくいのですが、 端的に言えば、 Catoとの認証の有効期限が切れた時の動作を指定できるようになります。   機能・用途 これまでの動作 Always-Onが有効な場合、これまでは以下の動作をします。 認証成功後、認証の有効期限が切れると再認証するまでインターネットアクセスやWAN通信ができません この動作は変更できません   アップデート後の動作 有効期限切れ後の動作を以下の3つから指定できるようになります。 認証の有効期限が切れると再認証するまでインターネットアクセスやWAN通信ができません(これまでと同じ) 認証の有効期限が切れてもインターネットアクセスだけ継続利用することが可能です 認証の有効期限が切れてもインターネットアクセス、WAN通信ともに利用可能です つまり、一度の認証(ワンタイム認証)で、インターネットアクセスやWAN通信が継続的に利用することができる。 といったことを実現できます。   どういう時に使うのか? 個人的には、以下のようなシチュエーションで利用できるかと思っています。   長期休暇対策                                                       長期休暇などでCatoの再認証ができなかったとしても、この機能を利用して、認証切れ後もインターネットアクセスは許可します。 これまでは再認証するまでは業務できませんでしたが、再認証前でもインターネット経由の業務は継続して可能です。          例:メール送受信、WEB会議、TeamsやSlackなどのチャットツールなど ※お客様業務システムに依存しますが。。 ※Catoのナレッジにも利用用途が複数記載されていましたが、商習慣の違いなのかどれも正直ピンときませんでした。。   設定方法 設定箇所は大きく2つです。 Client Connectivity Policyの設定 ユーザー単位で認証中・もしくは認証切れの時にどのような動作をするか定義します。 Client Access(Authentication)の設定 再認証の間隔や有効期限切れの通知有無などを定義します。   Client Connectivity Policy の設定 CMAにログインし、 「Access」 > 「Client Connectivity Policy」 を選択します。 右端の 「New」 をクリックすると、設定ウィンドウが表示されます。 以下の画面にて各設定項目を入力ください。 ※ルール名や対象ユーザーなどはこれまでの設定方法と同じため説明を割愛します。適宜任意の値を設定ください。   「Confidence Level(信頼レベル)」について ユーザーが認証している時の状態を「 Confidence Level(信頼レベル) 」として定義します。 High :ユーザーは認証に成功しており、かつCatoトークン(認証)の有効期限は切れていない状態(通常のCato利用時)。 Low :ユーザーは認証に成功しているが、Catoトークン(認証)の有効期限は切れている状態。 Any :あらゆる状態(認証の状態に応じて制限をかけない場合に使用)。   「Action」について どのような動作をさせるのか定義します。   Allow WAN and Internet :WANとインターネットアクセスを許可する。 Allow Internet :インターネットアクセスのみを許可する。 Block :ブロックする(Catoへの接続不可)。   Client Connectivity Policy の設定例 例:すべてのユーザーが、認証切れ後もインターネットアクセスのみ可能とする設定   注意事項 Client Connectivity Policyは、ホワイトリスト形式です。 ルールにマッチしない通信は、すべてブロックされてしまいます。 そのため、すべてのユーザーで信頼度がHigh/Lowの時の動作を設定する必要があります。 ※Low(認証切れ)の時にブロックしたい場合、Lowの設定はなくても構いません。   以下のように、すべてのユーザーで信頼度がHigh場合、インターネットアクセスもWANアクセスも許可する といったルールを最後に設定することを推奨します。   Client Access(Authentication) の設定 CMAにログインし、 「Access」 > 「Client Access」 を選択します。 「Authentication」 をクリックすると、設定ウィンドウが開きます。 以下の画面にて各設定項目を入力ください。 Default Method :認証方式を定義(MFA/SSO/User & Password) Token validity :トークンの有効期間を定義します。1分~999日まで設定可能です。 Before token expires: Notify User :ユーザーに有効期限の通知をするかどうか。48時間/24時間/2時間前に通知します。 After token expires: Prompt User re-authentication :有効期限切れ後に再認証を求めるプロンプトを表示するかどうか。   設定は以上です。   Catoクライアントイメージ 本機能利用時は、Catoクライアントは以下のような表示になります。   「New Bypass Mode for Always-On」について ユーザーが理由を記載するだけでCatoをBypassすることが可能なモードが追加になりました。   機能・用途 これまでの動作 Always-Onが有効な場合、CatoクラウドをBypassするためにはワンタイムパスワード(Bypassコード)の入力が必要でした。 また、ワンタイムパスワードはCMA上でしか確認できないため、Bypassしたい場合、管理者がワンタイムパスワードを確認⇒ユーザーに共有 という手間がありました。   アップデート後の動作 Bypassの実施方法を以下から指定できるようになります。 ワンタイムパスワード方式(これまでと同じ) ユーザー自身が任意の理由をCatoクライアント上に入力するだけで、いつでもBypass可能な方式 ユーザー単位でどちらの方式を利用するか選択可能で、(一回の操作で)Bypassできる時間も設定可能です(1分~100時間)。   なお、新方式を全ユーザーに適用してしまうと、全ユーザーが常時CatoクラウドをBypass可能となります。 Catoのメリットがなくなりますので、新方式の適用は特定のユーザーに限定することを推奨します。   どういう時に使うのか? 上述の通り、この機能を多用するとセキュリティホールになりかねないので、結構ピンポイントな使い方になると考えます。   運用管理者向け設定                                                       Always-Onが必須な組織にて、万が一運用管理者がAlways-Onの機能でインターネットアクセスできなくなると業務に影響が出るため、運用管理者のみ任意にBypass可能な新方式を採用する。他のユーザーはこれまで通りワンタイムパスワード方式とする。 ※何とかひねり出してます。。運用管理者だけAlways-On無効にすれば、、、というツッコミはお控えください。   設定方法 設定箇所は1つです。 Always-On Policyの設定 BypassモードとBypass可能な時間を定義します。   Always-On Policyの設定 CMAにログインし、 「Access」 > 「Always-On Policy」 を選択します。 右端の 「New」 をクリックすると、設定ウィンドウが表示されます。 以下の画面にて各設定項目を入力ください。 ※ルール名や対象ユーザーなどはこれまでの設定方法と同じため説明を割愛します。適宜任意の値を設定ください。   「Bypass Mode」について Bypass Modeで以下のいずれかを選択します。 Admin passcode required to disconnect :ワンタイムパスワード方式(これまでと同じ) User can choose to disconnect :ユーザーが自身でBypassを選択可能な方式。 また、 Disconnect Duration でBypass可能な時間を設定できます(1分~100時間)。   設定は以上です。   クライアントイメージ 新方式でユーザーがBypassを選択すると、以下の画面が表示されます。 「緑の枠」に何でもいいので理由を記載し、EnterをクリックするとBypass可能です。   また、ユーザーがBypassしていること、記入した理由は、以下のように「event」で確認可能です。   「Always-On Recovery Mode」について 何らかの理由でCato クラウドへの接続が利用できない場合に、これまで通りインターネットアクセスを制限するか、許可するか、 選択ができるようになります。   機能・用途 これまでの動作 何らかの理由でCatoとのトンネルが構築できない場合、Always-Onの制限でインターネットアクセスができなくなります。   アップデート後の動作 上記のような場合の動作を以下から指定できるようになります。 インターネットアクセス不可 インターネットアクセス可   Always-On Recovery Modeの発動条件 クライアントがどの PoP へのトンネルも確⽴できない場合に発動します。 例) ネットワーク接続なし (ホストが接続されていない / ルータのダウン) インターネット接続無し (ISP障害) Captive Portalが検出されない ネットワークルールが必要な通信をブロックしてしまう   どういう時に使うのか? 先に紹介したBypassモードと二者択一になるかと思っています。   運用管理者向け設定                                                       Always-Onが必須な組織にて、万が一運用管理者がAlways-Onの機能でインターネットアクセスできなくなると業務に影響が出るため、運用管理者のみRecovery Modeでインターネットアクセス可にする。   設定方法 設定箇所は1つです。 Always-On Policyの設定 Recovery Modeの動作を定義します。   Always-On Policyの設定 CMAにログインし、 「Access」 > 「Always-On Policy」 を選択します。 右端の 「New」 をクリックすると、設定ウィンドウが表示されます。 以下の画面にて各設定項目を入力ください。 ※ルール名や対象ユーザーなどはこれまでの設定方法と同じため説明を割愛します。適宜任意の値を設定ください。   「Recovery Mode」について Recovery Modeで以下のいずれかを選択します。 Restrict Internet access :インターネットアクセス不可(これまでと同じ) Allow internet :インターネットアクセス可。   設定は以上です。   クライアントイメージ Recovery Modeが動作していると以下のような表示がでます。     アップデートに伴う注意事項 機能アップデートにあたり、既存の設定に影響を及ぼさないか気になる方もいらっしゃると思います。   本アップデートで新しい設定項目が追加されるため、既存設定にも自動的に新しいパラメータが適用されますが、 今の動作と同じ設定が適用されるため、通信に影響は出ません。   ただし、「Remote Internet Security with One Time Authentication」の注意事項でも記載したように、 「Client Connectivity Policy」設定が現在有効になっている方は、 一番最後に「すべてのアクセスを許可する」ルールの設定をお忘れなきようご注意ください。   まとめ ここまでAlways-On機能アップデート情報の紹介をしてきましたが、個人的に今回のアップデートのポイントは以下だと思っています。   今まで仕様だった動作が、ユーザーのやりたいようにカスタマイズできるようになったこと   これまで通りの動作を希望される方は、特に設定を変えず今まで通りの利用で問題ありませんし、 「こうしたい!」がある方は、このアップデート機能を活用してぜひチューニングをご検討いただければと思います。   上記以外のアップデート情報についても弊社の 「Catoに関するFAQサイト」 に多数情報ございますのでご参考にください。 よくあるご質問 | Cato Cloud ケイトクラウド - SCSK Cato SASE Cloud Platform. powered by SCSK cato-scsk.dga.jp   最後に、SCSKではPoCから導入、運用まで幅広くCatoに関する支援を行っております。 本番構成への移行を見据えたPoC構成や、PoCでつまづきやすい点のサポートなど、豊富な導入実績を基にご支援いたします。 ぜひお声がけください!
はじめに こんにちは。SCSKのふくちーぬです。 前回・前々回の記事では、CI/CD配下でネストされた AWS CloudFormation スタックに対してパイプラインを構築してきました。こちらの記事を読んでいない方は、是非ご一読していただけると幸いです。 CI/CD配下でネストされたスタックの子スタックに対して、変更セットを有効にするテクニック[AWS CodePipeline+AWS CloudFormation] CI/CD 配下のネストされた AWS CloudFormation スタックに対して変更セットを有効化する方法を紹介します。 blog.usize-tech.com 2023.12.07 CI/CD配下のネストされたスタックを利用しているパイプラインに承認プロセスを組み込む[AWS CodePipeline+AWS CloudFormation] CI/CD配下のネストされたスタックに対して、変更セットが妥当なものか判断するための承認フローをパイプラインに追加します。 blog.usize-tech.com 2023.12.11 今回は、ネストされたスタックのスタック間の依存関係起因による変更セットの挙動を検証したので整理します。 構成図 前回から、サンプルスタック内にEC2が追加されています。   CI/CDパイプラインの構成 前回作成したスタックを更新します。スタックを作成していない方は、新規にスタックを作成してください。 Cloud9及びCodeCommitのディレクトリ構成について ディレクトリ構成は前回から、”ec2.yaml”が追加されました。ファイルの中身は、子スタックとしてEC2を記述したものとなります。 以下のファイルに対して更新があります。 -param.json -cfn.yaml -ec2.yaml param.json インスタンススタックで使用する、AMIIdを追加しています。 [ { "ParameterKey": "Environment", "ParameterValue": "dev" }, { "ParameterKey": "VPCCidrBlock", "ParameterValue": "192.168.0.0/16" }, { "ParameterKey": "AMIId", "ParameterValue": "ami-012261b9035f8f938" } ]  cfn.yaml 親スタックでは、インスタンススタックの内容を追記しています。ポイントとして、他の子スタックからサブネットID及びセキュリティグループIDを参照しています。 AWSTemplateFormatVersion: 2010-09-09 Description: Parent Stack Parameters: Environment: Type: String VPCCidrBlock: Type: String AMIId: 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 # ------------------------------------------------------------# # Instance Stack (EC2) # ------------------------------------------------------------# INSTANCE: Type: AWS::CloudFormation::Stack Properties: TemplateURL: src/ec2.yaml Parameters: Environment: !Ref Environment AMIId: !Ref AMIId SubnetId: !GetAtt NW.Outputs.SubnetId SGId: !GetAtt SECURITY.Outputs.SGId  ec2.yaml ここでは、EC2を1台作成する記述がされています。 AWSTemplateFormatVersion: 2010-09-09 Description: EC2 Stack Parameters: Environment: Type: String AMIId: Type: String SubnetId: Type: AWS::EC2::Subnet::Id SGId: Type: AWS::EC2::SecurityGroup::Id # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# Resources: EC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !Ref AMIId InstanceType: t2.micro SubnetId: !Ref SubnetId SecurityGroupIds: - !Ref SGId  CodeCommitへのプッシュ 更新済みの以下のファイルをCodeCommitにプッシュします。 -param.json -cfn.yaml -ec2.yaml EC2の作成 承認ステージにて、変更セットの内容を確認します。インスタンススタックが追加されて、EC2が新規作成されることが分かります。 問題なければ、”承認します”を押下してデプロイしてください。 セキュリティグループの更新 ここから本題です。依存関係による変更セットの挙動を検証するために、セキュリティグループを更新してみます。 今回は、以下のようにインバウンドルールを更新します。 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/26 #変更 - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 192.168.0.0/26 #変更 Tags: - Key: Name Value: !Sub ${Environment}-sg Outputs: SGId: Value: !Ref SG 更新済みの”securitygroup.yaml”をCodeCommitにプッシュしてください。 変更セットの確認 数分待つと承認ステージまで進んでいるので、通知されたメールから変更セットを確認してください。 親スタックの変更セットの確認 親スタックの変更セットを確認してみると、”INSTANCE”,”SECURITY”に更新があります。セキュリティグループのみ変更しているはずが、セキュリティスタックに加えてインスタンススタックも更新されるように判断してしまいますね。 子スタックの変更セットの確認(INSTANCE) 子スタックの変更セットの中身を確認します。やはり、EC2の更新(置き換えまで)が発生しそうです。 子スタックの変更セットの確認(SECURITY) こちらは意図した挙動になっていますね。セキュリティグループが更新されることが分かります。 承認とデプロイ 上記の変更セットの中身を確認したら、”承認します”を押下してデプロイしてください。 デプロイが完了しました。 スタックの挙動の確認(INSTANCE) 子スタックのイベントをみてみます。ここでリソースの作成・更新・削除の順序を確認することができます。 どうやら子スタック(INSTANCE)全体の更新はされているようですが、論理IDであるEC2Instanceの更新はされていないですね ➡EC2に対しての更新はされていないことが明らかになりました。 EC2の置き換え等発生していないことが分かって、安心しましたね! スタックの挙動の確認(SECURITY) 上記と同様に確認します。論理IDであるSGの更新がされていて、子スタック(SECURITY)全体の更新もされたことが分かります。 ➡セキュリティグループに対して更新されたことが明らかになりました。 まとめ 今回の検証で、子スタック間で依存関係がある際に、変更されていない子スタックの変更セットにおいて誤検知が発生していることが判明しました。 以下の注意点を理解した上で、ネストされたスタックを利用する必要があります。下記の理由としては、!GetAtt等で取得する値については変更セットの作成時点では、変更の有無を判断できない仕様によるものとなります。すなわち変更の有無は、変更セットの実行(デプロイ)時に決定されるということですね。 現状CloudFormationでは、 ネストされたスタックにおいて、Outputs等にて依存関係がある場合、参照先の子スタックにて差分が表示される可能性があります。   Nested Stack Change Set Evaluation · Issue #142 · aws-cloudformation/cloudformation-coverage-roadmap Duplicate of posted Mar 30, 2016. The current behavior of evaluating a change set on the "root" stack is that all nested ... github.com   Nested ChangeSets w/ !Ref or !GetAtt · Issue #759 · aws-cloudformation/cloudformation-coverage-roadmap Nested ChangeSets w/ !Ref or !GetAtt Originally posted by @herbertmuraro in #142 (comment) While using --include-nested-stacks option, when parameters of a nest... github.com 最後に いかがだったでしょうか。 CI/CD配下のネストされたスタックに対して、変更セットの挙動を検証してみました。ネストされたスタックを利用する際に依存関係が複雑な場合、適切な差分を把握することができず、デプロイに少々不安が残る結果になりました。 特にネストされたスタックを利用する場合は、子スタックを分割しすぎないことで子スタック間の依存関係を減らすことが可能なことを念頭においていただければと思います。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
本記事は TechHarmony Advent Calendar 12/12付の記事です。 昨日に引き続き、Catoクラウドについてです。どうぞお付き合いください。 現在稼働中のWANをCatoクラウドに移行しようとしたとき、どのような点に考慮が必要でしょうか。またどういった順序で進めるべきでしょうか。 今年もいくつかの移行をご支援した振り返りとして、移行の進め方の例をご紹介します。 一般的な閉域網WAN構成と移行後イメージ 現状のWAN構成として、以下のような構成で運用されている例は多いかと存じます。 拠点を閉域WANサービスで接続 データセンタに社内システムを集約 インターネット向けの通信は、データセンタに設定したセキュリティアプライアンスを通って、データセンタのインターネット回線から出ていく データセンタにリモートアクセスシステムを設置し、社外から社内のシステムへアクセスする データセンタまたは閉域WANから、SaaSやパブリッククラウドへのアクセス経路がある このWAN構成のイメージが下図の左側、これをCatoクラウドに移行した場合の例が右側です。 データセンタでのボトルネックを解消し、セキュリティアプライアンスをCatoクラウドのセキュリティ機能に置き換え、シンプル化するイメージです。 移行フェーズの例 では、Catoクラウドの移行はどのように取り組んでいけばよいのでしょうか。おおよその流れは以下です。 現状構成の調査 移行要件の整理 移行設計 移行の実施 それぞれのフェーズでの実施内容をご紹介します。 1. 現状構成の調査 ネットワークを切り替えるにあたり、まずは現状の構成がどうなっているかを整理する必要があります。 具体的には、以下のような情報を確認し、最新化します。 ネットワーク全体のIPアドレス設計 通信フロー ルーティング設計 (使用しているルーティングプロトコル、拠点間通信の制限有無、例外ルールの有無等) データセンター内の物理・論理構成、回線情報 各拠点の物理・論理構成、回線情報 リモートアクセスシステムの利用者数、アクセス制限 これらの情報に 不明瞭な箇所があると、移行時に考慮漏れが起こってしまう ことがあるため、情報が最新でない場合には、稼働中の機器のConfig確認や現地の調査を行います。 2. 移行要件の整理 Catoクラウドへの移行にあたり、支障となる点や、特別な考慮が必要な点がないかを検討します。 多くの場合は、これらを確認するためにPoCを実施します。 また、あわせて移行後の構成を検討します。 移行要件の確認ポイント フェーズ1で調査した現状の構成情報を用いて、以下のような点を確認していきます。 IPアドレス体系はそのまま移行できるか。 拠点間での重複や、Catoの管理アドレス(10.254.254.0/24)の利用がないか。 ※あった場合移行設計時に考慮が必要になるため リモートアクセスをCatoに移行できるか。現状のリモートアクセス装置でないと実現できない、固有の通信要件がないか。 拠点間通信・Internet向け通信において特殊な経路・要件がないか。 外部向けの公開サーバなど、Internetから内部向けの通信があるか。ある場合、その通信の宛先となるグローバルIPアドレスは変更が可能か。 ※Catoに移行するとグローバルIPアドレスが変更されるため 現行のセキュリティアプライアンスとCatoセキュリティ機能を比較し、対応できない機能がないか。あった場合にはその機能の必要性や代替手段を検討する。 移行後構成の検討ポイント 同じく、現状の構成情報をもとに、移行後の構成を検討していきます。 各拠点のCatoクラウドへの接続を冗長化するか、シングル構成とするか。 各拠点の接続帯域の検討。 各拠点で利用する回線の選定、手配。 ※申込みから開通まで2~3ヶ月かかるため早めの手配が必要です モバイル接続ユーザ数の検討。 モバイル接続ユーザの登録・認証方法・接続制限の検討。 パブリッククラウド(AWS/Azure/GCP等)がある場合、Catoクラウドへの接続方法の検討。 拠点間通信の見直しについて なお、閉域ネットワークからCatoクラウドへの移行は、境界型防御からゼロトラストモデルへの移行でもあり、セキュリティ環境を見直す絶好の機会です。 境界型防御は社内ネットワーク=安全とするモデルのため、拠点間の通信に制限をかけていない場合が多いですが、ゼロトラストにおいてはすべての通信を信頼せず、許可すべき通信のみを許可することが推奨されます。 Catoクラウドでは「WAN Firewall」機能にて、拠点間やユーザ間、またユーザと拠点間の通信の制御を簡単に実現できます。また、ルール内で、アプリケーションやサービスの指定や、OS等デバイス条件の指定も可能ですので、許可すべき通信だけを細かに指定して許可し、ネットワーク内のセキュリティを高めることが可能です。 このフェーズにて、ネットワーク内通信の厳格化を検討することをおすすめしております。 WAN Firewallの機能については、以下の記事もご参照ください。 CatoクラウドのFirewall機能について CatoクラウドのFirewall機能について解説いたします。 blog.usize-tech.com 2023.09.28 3. 移行設計 移行後の構成が描けましたら、続いて実際の移行方法を考えていきます。 移行Stepの検討 多くの場合、全拠点の一斉移行は難しく、各拠点の都合にあわせて順次移行していくことになります。 順次移行は、データセンター等、主要なシステムがある拠点にまずCato Socketを設置し、その拠点を中継してCatoクラウドと既存網の間を相互通信させることにより実現します。 以下は移行Stepの一例です。 Step0 主要なシステムのある拠点(データセンタ等)にCato Socketを設置し、限定したユーザで導入テストを行う。 ※このStep0をPoCとして実施する場合が多いです Step1 リモートユーザの接続をCatoクラウドに切り替える。  Step2 パイロット拠点をCatoクラウドに切り替える。 Step3 各拠点を順次Catoクラウドに切り替えていく。 Step4 パブリッククラウド(AWS/Azure等)をCatoクラウド経由の接続に変更する。 ※Catoへ切り替え済みの拠点/ユーザとの通信効率を重視する場合には、より早い段階で移行します Step5 不要となった回線・機器を廃止する。 既存網とCatoクラウドのルーティング方法 前述のとおり、拠点の順次移行を行うには、中継拠点を設け、既存網とCatoクラウドを相互ルーティングさせる必要があります。 移行中のルーティングをどのように制御するかは移行前の構成によりますが、随時手動でルートを設定するか、ダイナミックルーティングで制御するかのどちらかとなります。 CatoクラウドはルーティングプロトコルとしてBGPのみに対応しております。このため中継拠点のルータ・L3SW等がBGPに対応していれば、既存網とCatoクラウド間のルーティングを動的に制御することが可能です。 以下は移行時のルーティングの例です。BGPが利用できる場合には、移行時の手間を減らすために、この方法がおすすめです。 前提として、既存網のネットワークがダイナミックルーティング(図の例はOSPF)で制御されている データセンター内のL3SWにてOSPFとBGP間のルート再配布を行い、既存網とCatoクラウド間で経路交換する 拠点が既存網を外れ、Catoクラウドへ接続されると、自動でCatoクラウド側から経路広報される なお、この方法がとれない場合(BGP対応機器がない、元々ダイナミックルーティングをしていない等)には、拠点を移行するたびに手動でルートを変更する必要があります。 移行手順の作成 移行Stepを決めた後、続いて各Stepの移行作業の詳細を詰めていきます。特にStep3の拠点移行については、現状構成の調査や移行要件の整理であきらかになった情報に注意し、移行手順を作成します。 切り替え時の作業項目、障害試験内容、正常性確認項目等を作成し、実施日程を調整します。 4. 移行の実施 Catoクラウドの機能設定 移行を開始する前に、Catoクラウドの各種機能を移行要件に沿って設定し、ユーザが本番利用して問題ない状況にしておきます。 また、忘れがちな事前作業として、端末へのルート認証局証明書の導入があります。拠点に据え置きのデスクトップ端末やサーバなど、Catoクライアントを導入しない端末には、Catoのルート認証局証明書の導入が必要です。詳細は下記の記事もご参照ください。 CatoクラウドのTLS Inspection機能で躓く証明書の仕組み Cato クラウドでHTTPS通信のセキュリティ対策を行うためのTLS Inspection機能で躓くことの多いTLS証明書関連の仕組みや課題について解説します。 blog.usize-tech.com 2023.10.06 移行の実施 準備ができましたら、計画に沿って、実際にStepごとの移行を実施していきます。 最後に Catoクラウドへの移行について、駆け足でご説明しました。あらためて流れを振り返りますと、以下のようなフェーズです。記事ではご説明しきれない内容も多いのですが、概要として参考にしていただけますと幸いです。 現状構成の調査 移行要件の整理 移行設計 移行の実施 SCSKでは、各フェーズにてお客様のご要望に合わせた移行ご支援を行っております。1~4すべてをお任せいただくことも可能ですし、1~3のみご支援し、4の移行作業はお客様にて実施される例もございます。 移行にかかる期間は、拠点数や構成の複雑さに左右されますが、 フェーズ1~3を3ヶ月間 で実施させていただくことが多いです。フェーズ4の期間は、拠点数やスケジュールによりまちまちです。 Catoクラウドはもちろん、ネットワーク・インフラ全般に精通したエンジニアが、お客様ネットワーク構成にあわせた最適な移行をご支援します。ぜひご相談ください。
はじめに こんにちは。SCSKのふくちーぬです。 前回の記事では、CI/CD配下でネストされた AWS CloudFormation スタックの子スタックに対して、変更セットを有効にするテクニックを紹介しました。こちらの記事を読んでいない方は、まずご一読いただけますとより内容理解が進むと思います。 CI/CD配下でネストされたスタックの子スタックに対して、変更セットを有効にするテクニック[AWS CodePipeline+AWS CloudFormation] CI/CD 配下のネストされた AWS CloudFormation スタックに対して変更セットを有効化する方法を紹介します。 blog.usize-tech.com 2023.12.07 今回は、変更セットが妥当なものか判断するための承認フローをパイプラインに追加してみます。 構成図 前回から、パイプライン内の承認ステージの追加とメール送信用のSNSが追加されています。 CI/CDパイプラインの構成 前回作成したスタックを更新します。スタックを作成していない方は、新規にスタックを作成してください。 Cloud9及びCodeCommitのディレクトリ構成について ディレクトリ構成は前回と同様です。”cicd.yaml”と”changeset-buildspec.yaml”のみファイルの更新があります。 changeset-buildspec.yaml 環境変数として”expoted-variables”を追加しています。これらの環境変数をエクスポートすることで、承認ステージで利用できます。 CodeBuild のビルド仕様に関するリファレンス - AWS CodeBuild AWS CodeBuild でのビルドの仕様 (buildspec) ファイルに関するリファレンス情報を提供します。 docs.aws.amazon.com   ここでは、スタックIDと変更セットIDを参照できるようエクスポートしています。 version: 0.2 env: exported-variables: #変数のエクスポート - SackId - ChangeSetId 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: - pwd - | #変数の設定 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 #|| aws cloudformation describe-stacks --stack-name "$1" | grep -e "REVIEW_IN_PROGRESS" > /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 #0の場合 echo "Stack doesn't exist. Creating new changeset." # if changeset_exists "$stack_name" "$change_set_name"; then # echo "Changeset exists. Deleting the changeset." # fi create_changeset "$stack_name" "$change_set_name" "$template_body" "$parameters" "$capabilities" "CREATE" "$role_arn" else #1の場合 echo "Stack exists." if changeset_exists "$stack_name" "$change_set_name"; then #0の場合 echo "Changeset doesn't exist. Creating new changeset." else #1の場合 echo "Changeset exists. Creating new changeset." delete_changeset "$stack_name" "$change_set_name" fi if stack_reviewin "$stack_name"; then #0の場合 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 - SackId=`cat output.json | jq .StackId | sed 's/"//g'` #変数の代入 - ChangeSetId=`cat output.json | jq .Id | sed 's/"//g'` #変数の代入 artifacts: files: - .cfn/* - params/* discard-paths: yes cicd.yaml 以下3つを追加しています。 メール送信用のSNSトピックの追加 CodePipelineのIAMロールにSNSトピック発行の権限を追加 CodePipelineのパイプラインに承認ステージを追加 CodePipeline で承認アクションを管理する - AWS CodePipeline CodePipelineステージでマニュアルの承認リクエストおよび Amazon SNS 通知を作成し、管理する方法について説明します。 docs.aws.amazon.com 変数の受け渡し 重要な点を少し嚙み砕いて説明します。名前空間である”Namespace”を指定することで、ビルドステージにてエクスポートした変数(ここでは”SackId”・”ChangeSetId”を指す。)を後続のステージへ渡すことが可能になります。 可変 - AWS CodePipeline パイプラインアクションにおける変数と名前空間のリファレンス。 docs.aws.amazon.com - 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  レビュー用URLの作成 承認ステージの”ExternalEntityLink”にて、任意のレビュー用URLを指定することができます。 CodePipeline でパイプラインに手動の承認アクションを追加する - AWS CodePipeline 認証アクションまたは承認リクエストを CodePipeline パイプラインに追加する方法について説明します。 docs.aws.amazon.com 変更セットのURLには規則性があるので、それを変数と組み合わせることで動的にリンクを作成することができます。  https://【リージョン】.console.aws.amazon.com/cloudformation/home?region=【リージョン】#/stacks/changesets/changes?stackId=【スタックID】&changeSetId=【変更セットID】 ここでは以下を設定することで、変更セットを確認できるURLを動的に作成します。 - Name: Approval #承認ステージの追加 Actions: - Name: approve-changeset ActionTypeId: Category: Approval Owner: AWS Version: 1 Provider: Manual Configuration: ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId} NotificationArn: !GetAtt SNSTopic.TopicArn  完成したcicd.yaml AWSTemplateFormatVersion: 2010-09-09 Description: cfn CI/CD Pipeline Parameters: ResourceName: Type: String REPOSITORYNAME: Type: String Description: aws codecommit repository name STACKNAME: Type: String MailAddress: Type: String Resources: 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 - Sid: SNSAccess #SNSトピック発行の権限を追加 Effect: Allow Action: - sns:Publish Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${ResourceName}-topic # ------------------------------------------------------------# # 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: Approval #承認ステージの追加 Actions: - Name: approve-changeset ActionTypeId: Category: Approval Owner: AWS Version: 1 Provider: Manual Configuration: ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId} NotificationArn: !GetAtt SNSTopic.TopicArn - 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 # ------------------------------------------------------------# # SNS # ------------------------------------------------------------# SNSTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Ref MailAddress Protocol: email TopicName: !Sub ${ResourceName}-topic   ポイント CodeBuild内で環境変数をエクスポートして、次のステージに渡すよう設定する 承認ステージのレビュー用URLにて、スタックID及び変更セットIDを使用するようURLを動的に設定する CI/CDパイプラインの更新 更新した”cicd.yaml”ファイルを利用して、パイプラインを構成したスタックを更新してください。 サブスクリプションの確認 その後、指定したメールアドレスに届くSNSのサブスクリプションを許可してください。 E メール通知 - Amazon Simple Notification Service Amazon SNS で E メール通知を送信します。 docs.aws.amazon.com サブスクリプションの確認が完了しました。 CodeCommitへのプッシュ ここでは、更新済みの”changeset-buildspec.yaml”と”securitygroup.yaml”をCodeCommitにプッシュします。”securitygroup.yaml”では、前回同様にソースアドレスの変更等実施してください。 パイプラインが起動して、承認ステージまで進んでいます。 通知の確認 承認プロセスを挟んでいるため、指定のメールアドレスに以下のようなメッセージが届いています。 “Content to review”を押下すると、変更セットの画面に飛びます。 “Approve or reject”を押下すると、パイプラインの承認画面に飛びます。 “Approve or reject”を押下してみてください。 変更セットの確認とレビュー 承認ステージ内の”レビュー”を押下してください。以下のような画面になります。 “レビュー用URL”を押下すると、変更セットの内容を確認することができます。今回は、セキュリティグループが変更されることが明らかですね。 先ほどの画面に戻ってください。 変更セットの内容に問題ないため、コメントを記載の上”承認します”を押下して、システムをリリースします。   デプロイできましたね!ちょー気持ちいい まとめ いかがだったでしょうか。 CI/CD配下のネストされたスタックに対して、承認用のレビュー用URLを組み込んでみました。 CodePipelineに承認プロセスを取り入れることで、品質を担保したデプロイが可能になり思わぬ事故を防ぐことができます。 本記事が皆様のお役にたてば幸いです。 ではサウナラ~🔥
本記事は TechHarmony Advent Calendar 12/11付の記事です。 TechHarmony Advent Calendar 2023 の 11日目を担当します。 また、本日12月11日(月)から17日(日)まで 7日間連続 でCatoクラウドの記事を投稿しますので、ご期待ください。 それでは、Catoクラウドを検討される場合、ほぼ100%のお客様が実施されるPoCについて解説します。   PoC PoC(ピーオーシー、ポック) は「 Proof of Concept 」の略称で「 概念実証 」を意味しますが、少し前までは「事前(効果)検証」「技術検証」「実証実験」と言われていた新しい製品やソリューションを検討する際に、お客様の既存環境において、その製品・ソリューションが適切な効果を実現(発揮)することが可能か、期待通りの効果を見込めるかどうかを試すことです。 特に、Catoクラウドは、SASE(サッシー、サーシ)と言う新しいネットワークとセキュリティが統合されたクラウドサービスとなるため、ほぼ100%のお客様がPoCを実施されます。 ※ SASE ( Secure Access Service Edge )とは、アメリカ調査会社ガートナーが2019年8月発行したレポート「ネットワークセキュリティの未来はクラウドにある(The Future of Network Security Is in the Cloud)」において提唱した、新たなネットワークセキュリティフレームワークです。ネットワークとセキュリティを統合し、ユーザが場所を問わず企業のシステム、アプリケーション、クラウドサービス、データにアクセスできるよう、安全で信頼性が高く、最適化されたネットワーク接続を提供することがコンセプトになっています。 また、SASEと最近よく比較されるSSEとの違いについては以下の記事をご覧ください。 SSEとSASEどちらを選べばよいのか? SSE(Security Service Edge)とSASE(Secure Access Service Edge)どちらを選択すれば良いのかについて解説を行っています。 blog.usize-tech.com 2023.11.27 Catoクラウドは、PoC向けに30日間無償の評価アカウントを発行することが可能 です。 ただし、1企業につき、評価アカウント発行は原則1回のみで、期間(30日間)の延長はできません。 アカウントは30日間を経過するとロック(Lock)状態となり、さらに30日間が経過すると無効(Disable)状態となります。 アカウント設定情報は一定期間は保持されますが、その後アカウント削除が行われると設定情報は削除されます(設定情報をExport/Importする機能はございません) そのためPoCの30日間を如何に有効に活用できるかが重要になります。   PoC構成パターン PoCを実施される構成は、大きく以下の3パターンになります。 1. モバイルユーザのみ 2. モバイルユーザ+DC(本社) 3. モバイルユーザ+DC(本社)+拠点 「1.モバイルユーザのみ」のPoCは、お申込みからおよそ3日間程度で評価アカウント発行が可能のため、すぐに開始することが可能です。 ご利用になられるデバイス(PC、スマートデバイス)にCato Clientをインストールするだけでご利用いただけます。 セキュリティ機能としては、SWG(Secure Web Gateway)とクライアント制御が中心の検証になります。 「1.モバイルユーザのみ」での検証内容 ・管理ポータル Cato Management Application(以下、CMA)全般 ・モバイルユーザ管理(LDAP/SCIM連携、SSO連携) ・URLフィルタリング(Internet Firewall) ・モバイルユーザ間の通信制御(WAN Firewall) ・クラウドサービス利用および制御(EgressIP設定など) ・クライアント制御(常時起動、デバイスポスチャ、デバイス認証、スプリットトンネル等) ・その他のセキュリティ機能(CASB/DLP/RBI/SaaS Security API)   「2.モバイルユーザのみ+DC」「3.モバイルユーザのみ+DC+拠点」のPoCは、物理エッジデバイス(Socket)が必要な場合と、仮想エッジデバイス(vSocket)が必要な場合で準備期間が異なります。 vSocketは、AWS、Azure、VMware ESXiをサポートしています。GCPは今後サポート予定です。 Socketのお貸出しは原則はX1500となりますが、必要であれば(在庫があれば)お申込みから2-3日でお届けすることが可能です。 Socketの設置は、以下のような構成(設定例)が多いですが、お打ち合わせも可能です。 Socketを設置する拠点には、インターネット回線(またはインターネット接続環境)が必須となります。 Socketは、DHCP(Default)、固定IP、PPPoEのいずれかでの方法での接続します。 SocketをFirewall配下に設置する場合には、経路設定(ルーティング)と特定ポート(udp/53、tcp/443、udp/443)の許可が必要です。 2.3.は、拠点内での利用検証をいただくことが可能となります。3.では拠点間での利用検証をいただくことが可能です。 「2.モバイルユーザのみ+DC」「3.モバイルユーザのみ+DC+拠点」で実施できる追加検証内容 ・Officeモード(クライアント) ・モバイルユーザと拠点間の通信制御(WAN Firewall) ・拠点内の通信制御(LAN Firewall) ・Port Forwarding ・QoS制御(Quality of Service) ・バイパス   「3.モバイルユーザのみ+DC+拠点」でのみ実施できる追加検証内容 ・拠点間ルーティング ・拠点と拠点間の通信制御(WAN Firewall) ・Off-Cloud(Socket間のVPN通信) 機能が多岐に渡るため、検証期間中にすべてを実施することはできないことを前提に、検証項目の優先順位付けが必要となります。   PoC支援 SCSKでは以前はPoCの支援を無償で実施していましたが、現在は原則有償で実施させていただいております。 PoC支援の課題 無償で実施していた際は、お客様へ30日間の無償アカウント発行し(営業ベースの)メールでの問い合わせ対応を行っていました。 しかしながら、Catoクラウドは、Knowledge Base(ナレッジベース)、各種説明動画、コミュニティ等が非常に充実しているのですが、残念ながら、現時点(2023年12月時点)でも、 英語のみ での提供となっております。 Webブラウザ・翻訳サイトでの日本語翻訳は可能ですが、やはり英語ドキュメントは敷居が高いです。 また、管理ポータル(CMA)で、まっさらな何も無い状態からイチから設定を行うのは、何をどこからどの順番で始めて良いのかも分からず、こちらもかなり敷居が高い状態でした。 ※ちなみに、 管理ポータル(CMA)については日本語化対応が完了 しています。 そのため、お客様から、PoC開始時点は質問をいただくのですが、その後は質問が減り、30日間が経過した後に、検証状況や結果をご確認すると殆ど検証を進めることができず、そのまま検証終了となるケースが発生しました。 皆さんお忙しいので、ゼロからいちいち調べてPoCを実施するのは、かなり無理があることが分かりました。 また、問い合わせをいただいても、当社がいち早く回答を行うこともできていませんでしたので、不明な点が発生し、問い合わせのメールをすると、その日は検証を進めることができなかったのではないかと推測して思います。 PoC支援の課題解決に向けて これらの課題を解決するために、よくある質問と回答をFAQとして資料(Excel)にとりまとめて提供する、主要な機能の操作手順書を作成し提供することにしましたが、残念ながら、これもあまりうまくは行きませんでした。 FAQは、どんどん増えて数十行を超え、一覧性が無くなった時点でExcelのFAQはお客様にあまり活用されなくなりました。 手順書についても、個々の機能の手順書は揃えたのですが、お客様毎にPoCで優先して実施したいこと(実施する必要がないこと)、優先順位が異なっていたり、何よりこれはCatoクラウドだけではなく、他のクラウドやSaaSでも同じ課題を抱えていますが、作成した手順書の画面イメージ(CMAのスクリーンショット)が頻繁に変更となるため、手順書がすぐに陳腐化する問題が発生しました。手順書がどんどん増えていくため、この課題も大きくなってきました。 FAQについては、Catoクラウドのご契約いただいた既存のお客様からの問い合わせも増加していたこともあり、PoCのお客様も対象にしたFAQシステムを立ち上げることにしました。 CatoクラウドのFAQについては以下の記事で紹介しています。 Catoクラウド FAQサイトについて SCSKが2022年より運営するCatoクラウドのFAQサイトに関してご紹介を行っています。 blog.usize-tech.com 2023.10.11 そして手順書の問題については、すぐにCMA(画面)が変更になることから、必要最低限の資料のみを作成し、お客様へは、都度CMAの操作レクチャーを実施する方針としました。 また、基本的な操作方法については、この技術ブログ(TechHarmony)の記事として無料公開にすることにしました。 例えば、以下のような記事です。 Catoクラウド はじめの一歩 Catoクラウドのテナント開設後、テストユーザ・テスト拠点をCatoクラウドへ接続するまでの流れを紹介します。 blog.usize-tech.com 2023.08.18 問い合わせは、FAQを充実し自己解決率を増やすことに注力していますが、やはり一番の課題は、限られた30日という期間で、限られた時間の中でPoCを実施されている中で、 問い合わせに対する回答を可能な限り短くすること でした。 そのため、SCSKではPoC支援時に、 Catoクラウドエンジニアを担当者としてアサインし、問い合わせに対する回答を可能な限り当日中に回答する体制を取る ことにしました。 PoC支援の有償化 担当者アサインも含め、結果として、 PoCを成功裏に導くために 、PoCの支援を有償で実施させていただくことにしました 。 当社支援が不要のアカウント発行のみのPoCについては、引き続き無償で実施させていただいております。 PoCのコストは、もしCatoクラウドを採用しなかった場合に「お金をドブに捨てるようなものだ」というお客様もいらっしゃいますが、その場合は、当社でなく無償PoCを実施されているパートナー(ディストリビュータ)様へご相談ください。 ただし「ただより高いものはない」ので、お気をつけてください。 当社の PoC支援(有償)の主なサービス内容 は以下となります。 PoC支援サービス内容 ・ヒアリングシートをもとにしたPoC要件のヒアリング ・Catoクラウド PoCアカウント作成 ・Socketのお貸し出し(Socketが必要な場合のみ) ・ヒアリングシート、およびSCSK推奨設定をもとにした初期設定作業 ・CMAのご利用方法、操作方法のレクチャー ・ 担当エンジニアをアサインしたPoC期間中の問い合わせ対応 ・ FAQサイトの契約者IDの付与(PoC期間中) PoC支援の期間は、原則PoCアカウントと同じ30日間となります。 打ち合わせは、すべてオンライン会議としております。 問い合わせの受付および返信は、基本メールとなりますが、問い合わせ内容に応じてはオンライン会議を開催させていただきます。 また、PoC支援期間は、FAQサイトの契約者IDを付与しますので、契約者サイトで当社の作成した各種手順書をご覧いただくことも可能です。 PoC支援の有償化にて、殆どのお客様が、Catoクラウドが適切な効果を実現するかどうかの確認、期待通りの効果を見込めるかどうかの確認を期間中に実施いただけることとなりました。 このPoC支援は、PoCを実施しないお客様向けに「簡易初期構築サービス」としてメニュー化をしております。 ちなみに、CatoクラウドのPoCを実施したお客様の半数以上は、Catoクラウドをそのまま契約(継続利用)されるケースが多いです。 前述しましたが、PoCの期間切れで、アカウント(設定)削除される前に最小構成で購入されるケースとなります。   スターターパック 「Go!Go!SASEキャンペーン!」 PoC支援を有償で実施させていただいた場合も、30日間で検証が完了しない例が少なからず発生しています。 クライアント関連の検証を幅広く実施される場合に、期限切れになるお客様が多いです。 例えば、最初にWindowsで検証を開始され、Always-On(常時接続)、Device Posture(デバイス状態確認)、Device Authentication(デバイス証明書)などを検証された後に、macOSを実施し、さらにiOS(iPhone/iPad)を検証される場合や、CASB/DLP等を検証される場合に時間切れになる場合が多いです。 そのため、Catoクラウドを30日間と言う限られた時間でのPoCではなく、 Catoクラウドの最低契約期間1年間 で、 最小構成である1拠点PoP接続(25Mbps) と モバイル(SDP)ユーザ10名 、さらに、Catoクラウドの特徴の1つである Socket X1500 1台 をセットとして、今回ご紹介した PoC支援 と、1年間の Catoクラウドサポート窓口 をつけた「 Catoクラウドスターターパック 」の発売を開始することにしました。 また発売記念に 「Go!Go!SASEキャンペーン!」として、55万円(税抜)の超特別価格で期間限定発売 します。 ※2024年2月にCatoクラウドの価格改訂があるため、価格は変更になる可能性があります。 Catoクラウドライセンスが61.2万円(定価)と、当社サービスを加えると約150万円相当となります。 Catoクラウド スターターパック内容 ・PoP接続(25Mbps)1拠点      定価40,000円/月(年額480,000円) ・モバイル(SDP)ユーザ10名      定価 8,000円/月(年額96,000円) ・Socket X1500 1台          定価 3,000円/月(年額36,000円) ・PoC支援・簡易初期構築サービス(1ヵ月間) ・Catoクラウドサポート窓口(1年間) モバイルユーザは、予備の5ユーザを含めると 最大15名までご利用が可能 です。 Catoクラウドサポート窓口は、メール/電話による24時間365日受付、技術問い合わせ回答は当社営業日の09:00-17:00となります。もちろん FAQサイトの契約者IDの付与(1年間) も含まれます。 特別価格のため、2024年3月末までの期間限定となります。また、販売状況によっては数量限定とさせていただく可能性もありますので予めご了承ください。また、同業企業様やリセラー様への販売はお断りさせていただきます。 もしSASE、Catoクラウドをご検討のお客様、この際1年間実際にSASEを使用してみたいお客様は是非この機会に「Catoクラウドスターターパック」を検討されてはいかがでしょうか? お問い合わせは以下へお願いします。 お問い合わせ 製品・サービスについて 入力 | SCSK株式会社 SCSK株式会社 製品・サービスについてご意見・ご質問をお受けしております。 www.scsk.jp
本記事は TechHarmony Advent Calendar 12/10付の記事です。 皆さんこんにちは。SCSKで飼育されているひつじです。 突然ですがエンドポイントちゃんと理解して使っていますか!? エンドポイントと聞いて私が思い出すのはヨーロッパに赴任していたころです。 ヨーロッパにはシェンゲン協定というものがあり、協定内の国家間の移動は入国審査などが免除されます。 日本の場合は下図のように出向審査、入国審査を経て外国に行きますよね。 しかしシェンゲン協定内たとえばスペイン、ポルトガル間は審査不要で入国できるのです。 このような外国への移動が、まるで国内のように移動できる体験をVPCエンドポイント体験と名付けております。 さて、本題ですがそんなエンドポイントですがなんとなく使っていたりはしませんでしょうか。 今日はそんなエンドポイントの役割について説明したいと思います。 AWSのVPC外にあるサービスにつなぐ場合に使う 以下の図をご覧ください。よく聞くサービスですが実はVPCの外に配置されているサービスがいくつかあるのがわかりますね。 このようなVPCの外にいるサービスにはどのようにアクセスすればよいでしょうか。 例えばVPC内のEC2から別のSubnetにあるEC2につなぐことを考えた場合、同一VPC内にインスタンスを立てることでIPベースでの接続が可能になりますね。これは非常に単純でわかりやすいと思います。 しかし、VPC内に構築できないサービスと接続したい場合はどうでしょうか。 このような場合に使われるのがVPCエンドポイントです。 たとえばS3にアクセスしたいとします。S3はインターネットにも公開できるためインターネット経由でアクセスすることも可能です。 しかしインターネット公開はセキュリティ的に安全な手法とは言えません。 そこでインターネットを経由しないでAWSの中を通ってアクセスする必要があります。 エンドポイントの役割のひとつはインターナルに接続する手段となります。 別のVPC環境につなぐ場合に使う 次に別のVPC環境につなぐ場合のケースを考えてみましょう。 別のVPCということNW環境としては分離されています。 そのため、IPベースでの通信を行うことができません。 ではVPC間を接続(Peering)しNWを拡張することを考えてみましょう。 この場合はVPCに接続することができますが、以下の点に注意です。 NWアドレス帯が重複している場合はコンフリクトを起こして正常な通信ができない。 VPC間の通信を制御する仕組みが必要(接続元IPをフィルタする、通信プロトコルを制限するなど) より管理する手間を省き安全な接続を実現するためにエンドポイントを利用します。 このようなエンドポイントの使い方はPrivateLinkと呼ばれます。 通信をHTTPSに限定することができる。 送信元と送信先を明確に定義することできる。 これはクロスアカウントの場合も同様です。別のアカウントや他社が提供するAWSリソースにアクセスする要件があった場合に、PrivateLinkを使うことで安全に接続することが可能です。 VPCエンドポイントの種別について このように便利なエンドポイントですが大きく3つの種類があります。 ゲートウェイ型 DynamoDBとS3の2種類のみで利用することが可能です。 無償で使えるため お手軽に利用することができます。 このゲートウェイ型で払い出されるエンドポイントの実態は パブリックIP です。 そのためインターナル接続だと思い通信をローカルに閉じてしまった場合は接続ができなくなります。 ※本仕様について、パブリックIPへのアクセスだとインターネットを経由しているのでは?みたいな記事も散見されましたが、正確な情報はつかめませんでした。 インターフェース型 200種類近い種類が用意されています。そのためECRやCodeシリーズへのアクセスにも利用することが可能です。 インターフェース型のエンドポイントの IPはプライベート となります。 ただし1エンドポイントあたりに金額がかかります。ただNATGatewayと比較してもリーズナブルな価格となっています。 1か月で10GB通信した場合の試算(2023年12月時点/アジアパシフィック(東京)) エンドポイント インターフェース型 NAT Gateway 約10USD VPC エンドポイント 1 つあたりの料金 (USD/時間):0.014USD 処理データ 1 GB あたりの料金 (USD)0.01USD 約45USD NAT Gateway 1 つあたりの料金 (USD/時間):0.062USD 処理データ 1 GB あたりの料金 (USD)0.062USD Gateway Loadbalancer 型 こちらは今までの文脈とは少し異なる使い方になります。 通信経路にエンドポイントを介在させて、別VPC(例えばセキュリティ用VPC)に用意した仮想アプライアンスで通信を検査する場合などに有効なエンドポイントとなります。 引用:AWSドキュメント( https://docs.aws.amazon.com/ja_jp/vpc/latest/privatelink/vpce-gateway-load-balancer.html ) まとめ いかがでしたでしょうか。 普段なんとなく使っているエンドポイントにもさまざまな種別がありましたね。 AWSを触り始めた当初、「VPC外にあるサービスに接続する」という概念がわかりにくかったのですが図にすることでイメージできました。 また冒頭でも記載した入国審査の例に関しても、クロスアカウントで接続するためのエンドポイントがまさしく言い得て妙なのかと思います。 AWSリソース間=シェンゲン協定というイメージで覚えていこうかなと思います! もし海外旅行などで入国審査しないで入国する場合「VPCエンドポイント体験だ~」と感じて頂ければと思います。 それではご査収ください。
本記事は 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 は変更できませんので、効率化の余地もほとんど無いように思います。