20200809のAWSに関する記事は14件です。

ECSなコンテナにSSM Parameter Storeの値を渡して保持させる

はじめに

ECSの環境変数機能のちょっとニッチな使い方。
コンテナに固め込んで起動することから、動的に変更することができなくなるので注意。環境情報はコンテナから切り離すというプラクティスから離れるアンチパターンであることを意識して、コンテナ化するプロダクトがクラウドにリフトするだけのケース等やむを得ない場合だけ利用することを推奨。

SSM Parameter Storeの参照方法

コンテナのアプリケーションからSDKを使って参照するという方法もあるが、もっとお手軽に実装するのが、ECSの(正確にはタスク定義の)環境変数を使うのが良い。

以前の記事で書いた「コンテナの編集」⇒「環境変数」から設定可能だ。プルダウンで「ValueFrom」を選択して、Parameter StoreのARNを指定する。

TerraformでIaCで記述するのであれば、aws_ssm_parameterリソースを

resource "aws_ssm_parameter" "test" {
  name        = "/${var.environment}/teststring"
  description = "Test for SSM Parameter Store"
  type        = "SecureString"
  value       = "this-is-test-string"

  tags = {
    environment = var.environment
  }
}

な感じで定義し、これをaws_ecs_task_definitionリソースの中で

resource "aws_ecs_task_definition" "ecsfargate" {
  :
(中略)
  :
  container_definitions = <<EOF
  [
    {
      "name" : "${local.container_name}",
      "image": "${data.aws_ecr_repository.my_image.repository_url}:latest",
      "cpu": 0,
      "memoryReservation": 256,
      "secrets": [
        {
          "name": "TESTSTRING",
          "valueFrom": "${aws_ssm_parameter.test.arn}"
        }
      ],
  :
(以下略)

として参照する。

あとは、アプリケーションの中で環境変数で参照するなり、Dockerの起動コマンドの中で参照するなりしてあげれば期待通りのことができるようになる。

ただし、上記の通り、aws_ssm_parametertypeプロパティがSecureStringであるにもかかわらず、.tfファイル中の文字列は平文になってしまっている。チームで作業する場合なんかは、非常によろしくないのではなかろうか……。
ちなみに、Terraformの公式ドキュメントでは、「Terraform Cloud使うかリモートステートでちゃんと暗号化してね!」と書いてある。terraform.stateファイルはともかくとして、ローカル情報を暗号化したまま渡すことはできないのだろうか……。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2 Linuxにオプションモジュールをインストール

症状

WordPressの管理者画面のサイトヘルスに「1件の致命的な問題」が出てる><
内容は、以下の通り。

1つ以上の必須モジュールが存在しません
PHP モジュールはサイトの稼働に必要なほとんどのタスクをサーバー上で実行します。変更はサーバー管理者が実施する必要があります。
- オプションのモジュール dom がインストールされていないか、無効化されています。
- オプションのモジュール mbstring がインストールされていないか、無効化されています。
- オプションのモジュール imagick がインストールされていないか、無効化されています。
- 必須モジュール gd がインストールされていないか、無効化されています。

環境

  • AWS Amazon EC2 Linux 2
  • php 7.4.7
  • WordPress

対処法

上で「インストールされていないか、無効化されています」と言われているものをインストールして、httpd.serviceを再起動する。

$ sudo yum install -y php php-dom
$ sudo yum install -y php php-mbstring
$ sudo yum install -y php php-imagick
$ sudo yum install -y php php-gd
$ sudo systemctl restart httpd.service

wordpressのダッシュボードに行くと、致命的な問題が消えた。よかったよかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テクトロジーによる実践的組織構造学

今回の記事では、ソ連の革命家、医師、哲学者、小説作家であったアレクサンダーボグダノフが提唱したテクトロジーと呼ばれる実践的組織構造学について紹介する。
テクトロジーでは組織が安定、成長、破綻する環境、条件について詳細に解説し、安定した組織を生成する手法について解説している。
テクトロジーの概念を拝借し、創造的な組織創造の手法を紹介したいと思う。

テクトロジーにおける組織の定義

テクトロジーでは、組織をオープンクローズ型の成長する有機体システムと定義している。
組織とは以下の要素で構成されている。
ビジョン・・・組織の目指すべき方向性。
経済・・・組織のボディ。巨大であるほど収容できる人の人数が増加する
金融・・・組織を循環する血液。
生産・・・もの、サービスを生産し、組織の経済を巨大化する手段。

これらの有機的な要素が相互作用し、成長することで組織という有機体が構成されていると考える。

テクトロジーにおける成長する有機体システムとは

テクトロジーでは、組織をオープンクローズ型の成長する有機体システムと定義している。
テクトロジーにおける組織の定義を中国の陰陽論によって説明することができる。
陰陽論とは、原初は混沌(カオス)の状態であると考え、この混沌の中から光に満ちた明るい澄んだ気、すなわち陽の気が上昇して天となり、重く濁った暗黒の気、すなわち陰の気が下降して地となった。この二気の働きによって万物の事象を理解し、また将来までも予測しようというのが陰陽思想である。
組織が外部からエネルギーを取り入れ、出力するインプット、アウトプットの運動を陰陽論における陽の気と捉えることができる。
逆に組織内部に沈殿し、成長し、ヒエラルキーを形成する秩序生成を担う運動を陰陽論における陰の気と捉えることができる。

テクトロジーにおける生産の定義

テクトロジーでは、組織における生産活動は以下の3つに分類されている。
人の生産・・・人に教育を施し、組織活動に従事する生産者を作成する
モノ、サービスの生産・・・外部から取得した資材を用いて、モノ、サービスの生産を行う。
アイデア・・モノ、サービスを生成するための知識、アイディアを作成する。
組織における生産活動を高めることで、組織の経済を成長させることができる。

テクトロジーにおける組織が不安定化する条件

テクトロジーにおける組織が不安定化する条件として以下の2点が挙げられる。
・外部からのエネルギー取得の減少・・外部から人、モノ、金の循環が減少することで組織のサイズ、経済を維持することできなくなる。
・ヒエラルキーシステムの固定化・・・ヒエラルキーシステムが巨大化し、組織が硬直化してしまう。
組織不安定化を回避する手法として以下の手段が有効とされている。
生産手段を研究、開発、更新を行い、組織の経済成長のスピードを増加させる。
組織が硬直化の原因になっているヒエラルキーシステムを解体し、適切なサイズに組み替える。

まとめ

アレクサンダーボグダノフテクトロジーに関するアイディアを発表した時期は1920年代である。
独学で組織が破綻する条件、環境を発見し、持続可能な成長のコンセプトを提唱したアレクサンダーボグダノフの先見性は恐るべきものである。
ソ連は軍事、IT、経済においてアメリカと張り合うことができた超大国だった。
ソ連時代に考えられたアイディア、思想などは現代においても見直されるべきものだと思われる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS-SAA試験勉強メモ3

CIDR表記

X.Y.Z.W/24
なら、X.Y.Z.Wと255.255.255.0をそれぞれ2進数にあらわしたものの積を取る

☆「/24」の部分が、2進数表記のサブネットマスクで頭からの1の個数

Auto ScalingグループへのEC2インスタンスへのアタッチ可能になる条件

  • インスタンスがrunnning
  • インスタンス起動時のAMIが存在する
  • インスタンスが他のauto scalingグループに属していないこと
  • インスタンスがAuto Scalingと同じAZにあること

補足:Auto Scalingグループは複数のリージョンにまたがることは出来ない

CloudTrailログファイル

アカウントのAPIアクティビティログファイルをS3に送信

S3Glacier無料取り出し枠

無料取り出し枠=10GB/月

S3のオブジェクト削除

  • DELETE API
    →単一のオブジェクト削除

  • Multi-Object Delete API
    →複数のオブジェクト(1つのHTTPリクエストで最大1000のオブジェクト削除)

CloudWatchのメトリクスの保管期間

基本メトリクス、カスタムメトリクス問わず15か月

DynamoDB

  • NoSQLDB
  • キャパシティユニットでスループットを指定(書き込み&読み込み)
  • データ容量制限なし
  • スケールアウトが良い鵜
  • 高速パフォーマンス、シームレスな拡張性

フロントエンドリスナーにHTTPSまたはSSLを使用する場合

ELBにSSL証明書のインストールが必要

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Redshiftでテーブルとそのカラム、データ型の一覧を抽出するメモ

この記事について

Redshift上に格納している複数のテーブルについて、まとめてドキュメント化する必要があり、こういうの↓がCSVで欲しかったのでそのためのクエリをまとめた。
ほぼ参考記事のクエリを改変しただけです(@foursue 様ありがとうございます)がメモまで

table_id schema_name table_name column_number column_name column_datatype
1 user demographic 1 uuid VARCHAR
1 user demographic 2 age INT
1 user demographic 3 gender VARCHAR
1 user demographic 4 country VARCHAR
2 order order_detail 1 order_id BIGINT
2 order order_detail 2 timestamp TIMESTAMP
2 order order_detail 3 uuid VARCHAR
2 order order_detail 4 type VARCAHR
2 order order_detail 5 price NUMERIC

実際の方法

対象のredshiftクラスタにadminでログインして下記を実行しましょう。

dump_table_column_datatype.sql
SELECT
  table_id,
  schema_name,
  table_name,
  column_number,
  column_name,
  column_datatype
FROM
  (
    SELECT
      c.oid::BIGINT AS table_id,
      n.nspname AS schema_name,
      c.relname AS table_name,
      a.attnum AS column_number,
      QUOTE_IDENT(a.attname) AS column_name,
CASE
        WHEN STRPOS(
          UPPER(format_type(a.atttypid, a.atttypmod)),
          'CHARACTER VARYING'
        ) > 0 THEN REPLACE(
          UPPER(format_type(a.atttypid, a.atttypmod)),
          'CHARACTER VARYING',
          'VARCHAR'
        )
        WHEN STRPOS(
          UPPER(format_type(a.atttypid, a.atttypmod)),
          'CHARACTER'
        ) > 0 THEN REPLACE(
          UPPER(format_type(a.atttypid, a.atttypmod)),
          'CHARACTER',
          'CHAR'
        )
        ELSE UPPER(format_type(a.atttypid, a.atttypmod))
      END AS column_datatype
    FROM
      pg_namespace AS n
      INNER JOIN pg_class AS c ON n.oid = c.relnamespace
      INNER JOIN pg_attribute AS a ON c.oid = a.attrelid
      LEFT OUTER JOIN pg_attrdef AS adef ON a.attrelid = adef.adrelid
      AND a.attnum = adef.adnum
    WHERE
      c.relkind = 'r'
      AND a.attnum > 0
    ORDER BY
      a.attnum
  )
ORDER BY
  schema_name,
  table_name,
  column_number

参考記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロスアカウント・クロスリージョンなCodePipelineを実行する

はじめに

以前の記事で、クロスアカウントなパイプラインを実行するコツをまとめてみた。

今回は、さらにこの2つのイベント連携部分をクロスリージョンにするにはどうしたら良いかを考えた。

最初に結論を書いておくと、CodePipelineのクロスリージョンアクションを使うのが良い、ということだ。

【AWS公式】CodePipeline でクロスリージョンアクションを追加する

検討経緯

以下の記事を参考にして、CodePipelineの完了時にS3にファイルを突っ込んでトリガしたり、サービス側アカウントにSNSトピックを置いて、CodePipelineの完了時にビルドアカウント側からPublishしてリージョン跨ぎのLambdaを起動したりした。

【参考】【Developers.IO】S3 Event Notificationを別アカウントのリージョンの異なるAmazon SNSに送信し、Lambdaを発火させる

多少強引ではあるが、これなら別リージョンのCodePipelineに着火することはできそうだった。

だがしかし、結局はCodePipelineのソースステージがクロスリージョン対応していなくて、別リージョンのCodeCommitを参照できないので、どうにもならなかった。S3でソース連携とか、あまりに泥臭すぎる。

素直にCodePipelineとソースステージは同一リージョンに置いて、ビルド・デプロイステージをクロスリージョンアクションを使うのが良いだろう。

クロスリージョンアクションのためのIaC(Terraform)

マネージメントコンソールでの実施方法はAWS公式にもあるので、ここではTerraformの書き方を残しておく。

と言っても、そんなに難しいことではない。
以下のように、2つのリージョンのアーティファクトストアを用意して、★の箇所にregionのプロパティを設定すれば良い。このケースでは、以前の記事のビルド側アカウントをap-northeast-1、サービス側アカウントのクロスリージョン対応をus-east-2で行っている例だ。S3はグローバルサービスのはずなのに、クロスリージョンで動かす先のリージョンに作っておかなければいけないという制約は謎……。

resource "aws_codepipeline" "service_account" {
  
(中略)
  
  artifact_store {
    region   = "ap-northeast-1"    
    type     = "S3"
    location = aws_s3_bucket.service_artifact1.bucket

    encryption_key {
      id   = aws_kms_key.cross_account.arn
      type = "KMS"
    }
  }

  artifact_store {
    region   = "us-east-2"     
    type     = "S3"
    location = aws_s3_bucket.service_artifact2.bucket
  }
  
(中略)
  
  stage {
    name = "Build"

    action {
      run_order        = 2
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      version          = "1"
      input_artifacts  = ["SourceArtifact"]
      output_artifacts = ["BuildArtifact"]
      region           = "us-east-2"         

      configuration = {
        ProjectName = aws_codebuild_project.service_account.name
      }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

俺のAWSセキュリティ管理

個人的にAWSのセキュリティ管理のためにつかっているものをザっと並べてみました。
セキュリティ管理に終わりはないので、随時アップデートや新サービスが登場したら利用していこうとおもいます。

使用サービス一覧

アカウント管理
・IAM(ユーザー、ロール、グループ、ポリシー)
履歴の記録
・Cloud Trails
・AWS Config
ログ(履歴)の保存
・S3
検知
・Guard Duty
・IAM Access Analyzer
・Security Hub
通知
・SNS
・CloudFormation
請求系
・Cost Explorer
・AWS Budgets
AWS CLI
・AWS Vault
外部アプリ
・spark
・1password
・Authy

各詳細

アカウント管理
・IAMはrootユーザーを使わないでIAMユーザーを使用。権限は最小限に。
履歴の保存
・Cloud Trailsですべての証跡ログを記録する。
・AWS Configで特定のリソースの変更履歴を記録する。
ログ(履歴)の保存
・S3にてログの履歴を保存して、ライフサイクルルールで管理。
検知
・Guard Dutyで不審な操作や悪意のある操作を検知。
・IAM Access Analyzerで自アカウント以外からアクセスできるリソースがないかを検知。
・Security Hubで複数のセキュリティサービスを統合して見やすく。
通知
・SNSで異常な操作、変更などをEメールにて知らせる。
・CloudFormationで通知の型を参考。
請求系
・Cost Explorerで月額の利用料金を予想。
・AWS Budgetsで月額の上限を設定。
AWS CLI
・AWS Vaultでアクセスキーをローカル環境で安全に保管。暗号化。
外部アプリ
・sparkでメール管理を見やすく。
・1passwordでパスワード管理、自動入力。
・Authyで仮想MFA、二段階認証。

個人的なオススメ

・起きたらMY請求ダッシュボード→Guard Duty→Security Hub→IAM Access Analyzerと必ず目を通す習慣をつけておく。
・AWSのMY請求ダッシュボードはドルを円に変換して、最初の方は必ず毎日頻繁にチェックしておくこと。とにかくここは見慣れておくことが精神的にも大切。
・メールの通知を見逃さないためにもsparkアプリを入れて、AWSの通知専用のメールアドレスを作っておく。そうすることで他のメールと混在しなくて見逃しにくい。
・SNSで飛んでくるメールの説明には日本語で説明を補足しておくと、通知が来た時に見やすく対処しやすい(基本英語)。
・100%の安全は100%ありえないと自覚すること。
・セキュリティ関係が面倒なら、AWSは利用しないほうがいい。

必読

AWSが不正利用され300万円の請求が届いてから免除までの一部始終
初心者がAWSでミスって不正利用されて$6,000請求、泣きそうになったお話。
AWS初学者が「うっかり課金」されがちなポイントとその対策まとめ
AWSで不正利用され80000ドルの請求が来た話

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IAMユーザーとIAMロールは似ていることのメモ

なぜIAMユーザーとIAMロールを比べるか

IAMの勉強をしていると、概念が多いことやAWS独自の用語、位置関係と複雑なことが多くいつも混乱しがちです。特にIAMのユーザー、ポリシー、ロールの概念は非常に初心者にとっては難しいと思います。そしていつもポリシーに注目がいきがち。ただ、IAMユーザーとIAMロールが非常に似ているということを理解してからだいぶIAMあたりの概念の理がしやすくなったのでメモとして。

IAMユーザーとIAMロールは似ている

自分なりにIAMユーザーとロールについて表現してみました。

IAMユーザーとは、IAMアイデンティティでありアクセス許可ポリシーをもつAWS IDである。

(以下細かい説明)
アクセス許可ポリシー = ポリシーによって付与されるアクセス許可(IAMユーザーよりIAMグループに付与することが推奨)
AWS ID = 「そのユーザーはだれか」

IAMロールとは、IAMユーザーと同様にIAMアイデンティティであり特定のアクセス権限(アクセス許可ポリシー)をもつAWS IDであり、
AWSサービスやアプリケーションに対して、AWS操作の権限を与える仕組みである。

(以下細かい説明)
AWSサービスやアプリケーションに対して = ロールを使用できるもの

・ロールと同じ AWS アカウントの IAM ユーザー
・ロールとは異なる AWS アカウントの IAM ユーザー
・AWS が提供するウェブサービス (Amazon EC2)
・その他→IAMロールの概念

仕組みである = IAMロールはIAMユーザーと違って関連付けが任意であるため(だからヘルメットという特定のものでなく色んなものにつけることのできるイメージ)、あらゆるもの(ロールを使用できる色んなもの)に付与できる。

IAMロールはリソースに付与するもののイメージが強く、IAMユーザーの付与したりするイメージが湧きにくかったりします。AssumeRoleとかをやるとユーザーに付与したりするイメージが湧きやすくなると思います。

IAMユーザーとIAMロールの違い

IAMユーザー IAMロール
関連付け 一意 任意
長期認証情報 passwordやアクセスーなど 一時的なセキュリティ認証情報

参照

IAMロールの概念(公式)
IAMロールの細かいところ(Developers.IO)
IAMユーザーの概念(公式)
IMAロールとポリシーの違い(Qiita)


IAM難しいので誤解を生むような理解であれば、是非ご指摘してくださると嬉しいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CopilotでSpringBootアプリケーション(Kotlin)をECSにデプロイする

概要

AWS Copilotを使い、AWS ECS x Fargateへアプリケーションをデプロイしてみます。

AWS Copilotとは?

  • 「Copilot」を和訳すると「副操縦士」
  • AWSが提供するECS CLIの後継
  • 従来のECS CLIより簡単にFargateへのコンテナデプロイを実現できる
  • 詳しくはAWSのブログを参照
  • AWS Copilotのソースコードはgithubに公開されている

簡単に言うと、「ちゃんと動くDockerfile」があれば、$ copilot init とか $ copilot deploy といった簡単なコマンドだけでECSにアプリケーションをデプロイできるよ!というツールです。

モチベーション

  • 従来であれば、こうしたECS環境を使った公開アプリケーションを開発しようとしたとき、ある程度のネットワーク構成なども同時に構築する必要があり、手間がかかるもの (ポート設定、ロードバランサー、サービスディスカバリ etc...)
  • Copilotがイケてると思ったのはそのあたりの低レイヤの構築をいい感じにやってくれる、という点 (その恩恵としてアプリケーションエンジニアはより開発に集中できる)
  • PaaSに近い感覚で、さっと小さなコンテナベースのアプリケーションを公開するのに使えそう、と思い基本的な使い方を学習しておこうと

環境概要

  • Mac OS Catalina
  • Dockerインストール済
  • AWS CLI v2インストール済

なので、ここではCopilotのインストールから実行していきます。

AWS Copilotのインストール

インストール方法はAWS Copilotのドキュメントに記載されています。
https://github.com/aws/copilot-cli/wiki

$ brew install aws/tap/copilot-cli

サンプルアプリケーションの作成

Dockerfileで構築可能なアプリケーションであればなんでも良いのですが、
ここではタイトルの通り、Kotlinで単純なSpringBootアプリケーションを作成し、それをECS上に構築してみます。

利用したJavaとGradleのバージョンは以下。
(ちなみに、jenvとsdkmanで入れたもの)

$ java --version
openjdk 13.0.1 2019-10-15
OpenJDK Runtime Environment (build 13.0.1+9)
OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)

$ gradle -v

------------------------------------------------------------
Gradle 6.5
------------------------------------------------------------
...

ディレクトリ構造

最終的にこんな感じになります。

$ tree aws-copilot-app-example/
aws-copilot-app-example/
├── Dockerfile
├── README.md
├── build.gradle.kts
├── copilot
│   └── aws-copilot-app-example-service
│       └── manifest.yml
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
    ├── main
    │   ├── kotlin
    │   │   └── com
    │   │       └── example
    │   │           ├── ExampleApplication.kt
    │   │           └── ExampleController.kt
    │   └── resources
    │       └── application.yml
    └── test
        ├── kotlin
        │   └── com
        │       └── example
        │           └── ExampleControllerTest.kt
        └── resources

15 directories, 14 files

SpringBootプロジェクト作成

Spring Initializrを使ってもいいですが、自分の場合$ gradle initで作りました。

$ gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 4

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Project name (default: foo): aws-copilot-app-example
Source package (default: aws.copilot.app.example): com.example

BUILD SUCCESSFUL in 33s
2 actionable tasks: 2 executed

その後、各種ファイルを作成・修正していきます。

build.gradle.kts

ビルド設定です。
こんな感じ。

build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
  id("org.springframework.boot") version "2.3.2.RELEASE"
  id("io.spring.dependency-management") version "1.0.8.RELEASE"
  kotlin("jvm") version "1.3.72"
  kotlin("plugin.spring") version "1.3.72"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"

repositories {
  mavenCentral()
}

dependencies {
  implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
  implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

  // SpringBoot
  implementation("org.springframework.boot:spring-boot-starter-web")

  implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.2")

  // Testing
  testImplementation("org.springframework.boot:spring-boot-starter-test") {
    exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
  }
  testImplementation("io.mockk:mockk:1.10.0")
}

tasks.withType<Test> {
  useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
  kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
    jvmTarget = "1.8"
  }
}

ExampleApplication.kt

SpringBootアプリケーションの起動部です。

ExampleApplication.kt
package com.example

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class ExampleApplication

fun main(args: Array<String>) {
  runApplication<ExampleApplication>(*args)
}

ExampleController.kt

Hello AWS Copilot!!! というメッセージを返す GETのAPIを作成します。

ExampleController.kt
package com.example

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class ExampleController {
  @GetMapping("/")
  fun get(): ResponseEntity<Result> =
      ResponseEntity(Result(message = "Hello AWS Copilot!!!"), HttpStatus.OK)

  data class Result(
      val message: String
  )
}

ExampleControllerTest.kt

こちらはコントローラのテストコードです。

ExampleControllerTest.kt
package com.example

import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@SpringBootTest
@AutoConfigureMockMvc
class ExampleControllerTest {

  @Autowired
  private lateinit var mockMvc: MockMvc

  @Autowired
  private val mapper = jacksonObjectMapper()

  @Test
  fun `response data contains welcome message`() {

    val expected = ExampleController.Result(
        message = "Hello AWS Copilot!!!"
    )

    mockMvc.perform(MockMvcRequestBuilders.get("/"))
        .andExpect(status().isOk)
        .andExpect(content().json(mapper.writeValueAsString(expected)))
  }
}

Dockerfileの作成

上記で作成したSpringBootアプリケーションをコンテナ化するために、以下のようなDockerfileを作成します。

FROM openjdk:jdk-alpine
VOLUME /tmp
RUN mkdir /aws-copilot-app-example
WORKDIR /aws-copilot-app-example

ENV JAVA_OPTS=""
ENV APP_VERSION=0.0.1-SNAPSHOT

COPY ./build/libs/aws-copilot-app-example-$APP_VERSION.jar /aws-copilot-app-example

EXPOSE 8080

ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./build/libs/aws-copilot-app-example-$APP_VERSION.jar" ]

試しにローカルで起動してみます。

# アプリケーションのビルド(jarを生成)
$ gradle clean build

# Dockerイメージのビルド
$ docker build -t aws-copilot-app-example:latest ./

# Dockerイメージを起動
$ docker run --rm -it -p 80:8080 --name aws-copilot-app-example aws-copilot-app-example:latest

動作確認

$ curl http://localhost
{"message":"Hello AWS Copilot!!!"}

成功すると上記のようにメッセージが返ってきます。

AWS Copilotを使ってECSに上記のSpringBootアプリケーションをデプロイ

Dockerfileがあるディレクトリで以下のコマンドを実行します。

$ copilot init

すると、作成したいアプリケーションについて順に聞かれるので、対話式に入力していきます。
ここでは以下のように入力しました。

項目 入力内容
Application name aws-copilot-app-example
Service type Load Balanced Web Service
Service name aws-copilot-app-example-service
Dockerfile ./Dockerfile

動作確認を簡単化するため、公開サービスを想定したLoad Balanced Web Serviceとして立ち上げていますが、Backend Serviceも試してみたところ、Cloud Map上にネームスペースもきちんと作成されていました(サービスディスカバリもバッチリ。しゅごい)

その後、以下のようにtest環境にデプロイしてよいか聞かれるので「y」と入力しEnter

Would you like to deploy a test environment? [? for help] (y/N) y

諸々回答していくと、出力は以下のようになります。

$  copilot init
Note: It's best to run this command in the root of your Git repository.
Welcome to the Copilot CLI! We're going to walk you through some questions
to help you get set up with an application on ECS. An application is a collection of
containerized services that operate together.

Application name: aws-copilot-app-example
Service name: aws-copilot-app-example-service
Dockerfile: ./Dockerfile
Ok great, we'll set up a Load Balanced Web Service named aws-copilot-app-example-service in application aws-copilot-app-example listening on port 8080.

✔ Created the infrastructure to manage services under application aws-copilot-app-example.

✔ Manifest file for service aws-copilot-app-example-service already exists at copilot/aws-copilot-app-example-service/manifest.yml, skipping writing it.
Your manifest contains configurations like your container size and port (:8080).

✔ Created ECR repositories for service aws-copilot-app-example-service.

All right, you're all set for local development.
Deploy: Yes

⠹ Proposing infrastructure changes for the test environment.

さすがに環境をまるっと一式作るのでけっこう時間がかかる。しばらく待つ。
ちゃんと処理が進んでいるのか気になるようであれば、AWSコンソールよりCloudFormationのイベントなどを参照すると進捗状況を確認できる。

✔ Deployed aws-copilot-app-example-service, you can access it at http://aws-c-Publi-xxx-xxx.ap-northeast-1.elb.amazonaws.com.

AWSにデプロイしたアプリケーションの動作確認

$ curl http://aws-c-Publi-xxx-xxx.ap-northeast-1.elb.amazonaws.com
{"message":"Hello AWS Copilot!!!"}

すげぇ、マジでできてしまった(当たり前ですが)
実質、$ copilot init しか打ってないんだがw (神なの)

特に面白いと思ったのは、Dockerfile上でEXPOSE 8080と指定し、コンテナの8080ポートを公開しているのですが、
これを解釈してELBの80ポートからコンテナの8080ポートまでのルートをマッピングしてくれているところです。

これは本当に気が利いているw

作成したサービスの削除

放っておくとお金もかかるので、作成したサービスを削除します。

$ copilot app ls
aws-copilot-app-example

$ copilot app delete aws-copilot-app-example

AWSのprofileが複数ある場合、「この環境ではどのプロファイルを使って消します?」と聞かれるので、自身の環境に合わせて削除用のprofileを選ぶ。(ここではdefaultを選択)

Which named profile should we use to delete test? default

今回作成したサンプルコード

※執筆時点から変更される可能性もあります
https://github.com/otajisan/aws-copilot-app-example

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Terraform で AWS VPC の環境づくり[入門]

やりたいこと

terraform の大まかな流れが知りたい。
とりあえず AWS VPC の環境を作成して、確認後廃棄。

各種ファイルの用意

VPC 本体

vpc.tf

variable "aws_region" {}

provider "aws" {
  version = "~> 3.1"
  region  = var.aws_region
}

variable "project_prefix" {}
variable "vpc_cidr" {}

resource "aws_vpc" "vpc" {
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.project_prefix}-vpc"
  }
}

変数の設定

test.tfvars

project_prefix = "tftest"
vpc_cidr       = "10.0.0.0/16"
aws_region     = "ap-northeast-1"

Git ignore

ref: https://github.com/github/gitignore/blob/master/Terraform.gitignore

.gitignore

# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log

# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
*.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

実行

init

$ terraform init

差分の確認

$ terraform plan -var-file=test.tfvars

適用

$ terraform apply -var-file=test.tfvars

AWS Console 上からでもリソースが確認できる。

実行結果の表示

$ terraform show

破棄

$ terraform destroy -var-file=test.tfvars

おまけ

Makefile

今回は env という変数で呼び出すファイルを制御したが、workspace を使った方が良さそう(?)

env=test

clean:
    rm -rf ./.terraform

init:
    terraform init

plan:
    terraform plan -var-file=$(env).tfvars

apply:
    terraform apply -var-file=$(env).tfvars

show:
    terraform show

deploy: init plan apply show

destroy:
    terraform destroy -var-file=$(env).tfvars

次やりたいこと

  • Docker 化
  • リソース間連携
    • VPC に IGW 生やして Attach みたいな
  • module 分割
  • workspace の活用
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS VPC の設定を terraform init

terraform init に成功するまでに少し詰まったので、構文理解のためにも成功前後のファイル差分と解決までの道程を残しておく。

やること

terraform init まずはこれだけ。

init 成功ファイル

vpc.tf

provider "aws" {
  version = "~> 3.1"
}

variable "project_prefix" {}
variable "vpc_cidr" {}

resource "aws_vpc" "vpc" {
  name             = "${var.project_prefix}-vpc"
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = var.project_prefix
  }
}

エラー解決の道程

元ファイル

provider "aws" {}

variable "project_prefix" {}
variable "vpc_cidr" {}

resource "aws_vpc" "${var.project_prefix}-vpc" {
  cidr_block       = "${var.vpc_cidr}"
  instance_tenancy = "default"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.project_prefix}"
  }
}

The following providers do not have any version

該当箇所

provider "aws" {}

warning

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 3.1"

修正後

バージョンが上がった時に再現性がないから、指定しとけよってことらしい。

provider "aws" {
  version = "~> 3.1"
}

Interpolation-only expressions are deprecated

該当箇所

  cidr_block       = "${var.vpc_cidr}"

warning

Warning: Interpolation-only expressions are deprecated

  on vpc.tf line 9, in resource "aws_vpc" "${ ... }-vpc":
   9:   cidr_block       = "${var.vpc_cidr}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.

Template interpolation syntax is still used to construct strings from
expressions when the template includes multiple interpolation sequences or a
mixture of literal strings and interpolations. This deprecation applies only
to templates that consist entirely of a single interpolation sequence.

修正後

普通に変数使うときは、わざわざ "" で囲わんでもええで、ってことらしい。

  cidr_block       = var.vpc_cidr

Invalid resource name

該当箇所

resource "aws_vpc" "${var.project_prefix}-vpc" {

Error

Error: Invalid resource name

  on vpc.tf line 8, in resource "aws_vpc" "${ ... }-vpc":
   8: resource "aws_vpc" "${var.project_prefix}-vpc" {

A name must start with a letter or underscore and may contain only letters,
digits, underscores, and dashes.

Error: Invalid string literal

  on vpc.tf line 8, in resource "aws_vpc" "${ ... }-vpc":
   8: resource "aws_vpc" "${var.project_prefix}-vpc" {

Template sequences are not allowed in this string. To include a literal "$",
double it (as "$$") to escape it.

修正後

ここは AWS 上の名前ではなく、 Terraform 都合の名前を置く場所らしい。
名前は tags のプロパティを用いて指定する。

resource "aws_vpc" "vpc" {

  tags = {
    Name = "${var.project_prefix}-vpc"
  }

successed!

$ terraform init
Initializing the backend...

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

次やりたいこと

  • plan, apply
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS VPC の設定を terraform init [入門]

terraform init に成功するまでに少し詰まったので、構文理解のためにも成功前後のファイル差分と解決までの道程を残しておく。

やること

terraform init まずはこれだけ。

init 成功ファイル

vpc.tf

provider "aws" {
  version = "~> 3.1"
}

variable "project_prefix" {}
variable "vpc_cidr" {}

resource "aws_vpc" "vpc" {
  name             = "${var.project_prefix}-vpc"
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = var.project_prefix
  }
}

エラー解決の道程

元ファイル

provider "aws" {}

variable "project_prefix" {}
variable "vpc_cidr" {}

resource "aws_vpc" "${var.project_prefix}-vpc" {
  cidr_block       = "${var.vpc_cidr}"
  instance_tenancy = "default"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.project_prefix}"
  }
}

The following providers do not have any version

該当箇所

provider "aws" {}

warning

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 3.1"

修正後

バージョンが上がった時に再現性がないから、指定しとけよってことらしい。

provider "aws" {
  version = "~> 3.1"
}

Interpolation-only expressions are deprecated

該当箇所

  cidr_block       = "${var.vpc_cidr}"

warning

Warning: Interpolation-only expressions are deprecated

  on vpc.tf line 9, in resource "aws_vpc" "${ ... }-vpc":
   9:   cidr_block       = "${var.vpc_cidr}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.

Template interpolation syntax is still used to construct strings from
expressions when the template includes multiple interpolation sequences or a
mixture of literal strings and interpolations. This deprecation applies only
to templates that consist entirely of a single interpolation sequence.

修正後

普通に変数使うときは、わざわざ "" で囲わんでもええで、ってことらしい。

  cidr_block       = var.vpc_cidr

Invalid resource name

該当箇所

resource "aws_vpc" "${var.project_prefix}-vpc" {

Error

Error: Invalid resource name

  on vpc.tf line 8, in resource "aws_vpc" "${ ... }-vpc":
   8: resource "aws_vpc" "${var.project_prefix}-vpc" {

A name must start with a letter or underscore and may contain only letters,
digits, underscores, and dashes.

Error: Invalid string literal

  on vpc.tf line 8, in resource "aws_vpc" "${ ... }-vpc":
   8: resource "aws_vpc" "${var.project_prefix}-vpc" {

Template sequences are not allowed in this string. To include a literal "$",
double it (as "$$") to escape it.

修正後

ここは AWS 上の名前ではなく、 Terraform 都合の名前を置く場所らしい。
名前は tags のプロパティを用いて指定する。

resource "aws_vpc" "vpc" {

  tags = {
    Name = "${var.project_prefix}-vpc"
  }

successed!

$ terraform init
Initializing the backend...

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

次やりたいこと

  • plan, apply
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Personalize では Role だけではなく S3 バケットにもポリシーを書く必要がある

概要

Amazon Personalize にデータをインポートするときに S3 のバケットポリシーも記載する必要があり、少しはまったので共有します。

やり方

Role のポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::バケット名"
            ]
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

バケットポリシー

{
    "Version": "2012-10-17",
    "Id": "PersonalizeS3BucketAccessPolicy",
    "Statement": [
        {
            "Sid": "PersonalizeS3BucketAccessPolicy",
            "Effect": "Allow",
            "Principal": {
                "Service": "personalize.amazonaws.com"
            },
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

参考
https://docs.aws.amazon.com/personalize/latest/dg/data-prep-upload-s3.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのt2.2xlargeをhibernationしながら常用する

AWSは、2018年末辺りから、インスタンスの休止(ハイバネーション)ができるようになりました。
しばらくは、Amazon Linuxでしか使えなかったのですが、2019年にUbuntuでも使えるようになり、必要な時だけ立ち上げ、終わったら休止というのができるようになりました。

たとえば、時間のかかるビルドをするときに、ビルドが終わったら休止しておけば、コストを抑えられますし、シャットダウンと違って、エラーの確認も再開後すぐにできます。

しかし、休止をAWS Consoleからするのでは面倒です。そのため、実行中のインスタンスから休止できるコマンドを作成します。

hibernate.sh
#!/bin/sh
aws ec2 stop-instances --instance-ids `curl -s 169.254.169.254/latest/meta-data/instance-id/` --hibernate

上記を実行中のインスタンスに置いて、パスを通してください。そして、hibernate.shを実行すると休止します。

これで、例えば、

$ make; hibernate.sh

などとすれば、ビルド後、休止します。make && hibernate.shとすると成功時にしか休止しないので、make; hibernate.shとすべきです。

ただ、上記だと、makeが一瞬で失敗してしまったときも、休止します。そのため、sleepを入れましょう。単にsleepを入れると分かりにくいのでカウントダウンのコマンドを作ります。

countdown.sh
secs=$1
while [ $secs -gt 0 ]
do
        sleep 1 &
        printf "\r%02d:%02d:%02d" $((secs/3600)) $(( (secs/60)%60)) $((secs%60))
        secs=$(( $secs - 1 ))
        wait
done
echo

そして、hibernate.sh でcountdown.shを呼びですようにします。以下では10分待っています。

hibernate.sh
#!/bin/sh
countdown.sh 600 && aws ec2 stop-instances --instance-ids `curl -s 169.254.169.254/latest/meta-data/instance-id/` --hibernate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む