20190708のAWSに関する記事は27件です。

基本的なシステム構成図を理解するためのAWS基礎を初心者がまとめてみた

はじめに

4月からの業務でAWSのシステム構成図を見ることが多くなり、AWS上で動いているシステムのシステム構成図を理解できるようになるために個人的に知っておきたいと思ったAWSの用語・サービスをまとめてみました。
2018年10月に新しくなったAWSアーキテクチャアイコンを使用しています。

サーバ・クライアントなどの説明については以下がとてもわかりやすいです!
超絶初心者のためのサーバとクライアントの話

知っておきたい用語

リージョン

  • AWSがサービスを提供している拠点(国と地域)
  • リージョン同士はそれぞれ地理的に離れている
  • 日本だとap-northeast-1(リージョン名はアジアパシフィック(東京))

アベイラビリティゾーン

  • リージョン内のさらに細かい拠点
  • 1つのアベイラビリティゾーンは複数のデータセンターから構成されている
  • ap-northeast-1には3つのアベイラビリティゾーンがある(a,c,d なぜかbが無い)
  • ごく稀にアベイラビリティゾーン全体で障害が発生することがあり、その時にリソースが1つのAZに固まっているとサービスが停止してしまうため、アベイラビリティゾーンをまたがってインスタンスを配置する設計にする必要がある

リージョンとアベイラビリティゾーンの関係性

リージョンとアベイラビリティゾーン.jpeg

主なサービス

VPC

  • Virtual Private Cloudの略
  • VPCを使うことで、AWS内にプライベートなネットワークを作成できる
  • 自由にIPアドレス(CIDRブロック)を割り当てることができる VPC.jpeg

サブネット

  • VPCのさらに内側につくる、EC2インスタンス(※後述)などを起動するための領域
  • VPCに設定したCIDRブロックに収まるCIDRブロックを割り当てる
  • サブネット作成時にアベイラビリティゾーンを指定する
  • パブリックサブネットとプライベートサブネットがあり、それらの違いはインターネットゲートウェイ(※後述)から直接アクセスできるかできないかである(アクセスできるのがパブリックで、アクセスできないのがプライベート) subnet.jpeg

ルートテーブル

  • サブネット内のインスタンスの通信先のルールを定めたもの(表)
  • 個々のサブネットに1つずつ設定する
  • 各サブネットをルートテーブルに関連付ける必要がある(指定しない場合はデフォルトのルートテーブルになる)
  • ルートテーブルで、サブネットの通信先にインターネットゲートウェイを指定することで、サブネットがインターネットに接続できる

ゲートウェイ

  • VPCの内部と外部との通信をやり取りする出入り口

インターネットゲートウェイ

  • VPCとインターネットを接続するためのゲートウェイ
  • 各VPCに1つだけ取り付けられる

NATゲートウェイ

  • プライベートIPしか持っていないEC2インスタンスがインターネットと通信できるようにするためのもの
  • プライベートIPをNATゲートウェイが持つグローバルIPに変換し、外部と通信する ゲートウェイ.jpeg

ゲートウェイエンドポイント

  • S3やDynamoDBといったVPCの中に入れられないサービスに、VPC内から接続する際に利用する

EC2

  • Amazon Elastic Compute Cloudの略でAWSの基幹サービス
  • 仮装サーバを提供する
  • 仮装サーバの1つ1つの実体は「インスタンス」と呼ばれる
  • ボタンワンクリックでインスタンスをつくることができるので便利
  • クライアントからのリクエスト数が増えたらインスタンスの数を増やしたり(スケールアウト)、インスタンスの性能を上げたり(スケールアップ)も簡単にできるため、サーバ調達のための時間やコストを削減できる EC2.jpeg

ELB

  • Elastic Load Balancingの略
  • その名の通りロードバランサーのマネージドサービス(管理とかを一括して請け負ってくれるサービス)
  • EC2インスタンスがスケールアウトされて複数ある場合、ELBがクライアントからリクエストを受け取り、それを各インスタンスに分散させる
  • ELB自体もスケーリングができ、負荷に応じてスケールする設計になっている

ELBの種類

  • Classic Load Balancer(CLB)
    • L4/L7レイヤーで負荷分散を行う。現在はあまり使わない。
  • Application Load Balancer(ALB)
    • L7レイヤーでの負荷分散を行う。CLBより後に登場。
  • Network Load Balancer(NLB)
    • L4レイヤーでの負荷分散を行う。HTTP以外のプロトコル通信の負荷分散をしたい時に利用する。 ALB.jpeg

RDS

  • RDB(リレーショナルデータベース、関係データベース)と呼ばれる、SQL文でデータを操作するデータベースのマネージドサービス
  • Amazon Aurora、MySQL、MariaDB、PostgresSQL、Oracleなどのデータベースエンジンから好きなものを選べる
  • データ保存用ストレージはEBS(Elastic Block Store)を使用している
  • インターネットからの接続はデフォルトでオフになっているため、EC2などの他のAWSサービスから通信することが多い RDS.jpeg

マルチAZ構成

  • 1つのリージョン内の2つのアベイラビリティゾーンにDBインスタンス(データベースの実体)をそれぞれ配置する構成
  • 1つのアベイラビリティゾーンで障害が発生しても、もう一つのアベイラビリティゾーンが生きていればシステムのダウンタイムを短くすることができる
  • DBインスタンス作成時にマルチAZ構成を選択したら、あとはAWSが勝手に冗長化してくれて便利

S3

  • Simple Storage Serviceの略
  • 容量無制限のストレージ(補助記憶)サービス
  • 保存するファイルにメタデータを追加し、「オブジェクト」として管理する
  • S3に保存されている各オブジェクトには、RESTやSOAPといったWeb APIでアクセスする
  • データのバックアップや、静的コンテンツ(HTMLファイルなど)のホスティングとしても利用される
  • VPCの中には入れられないため、ゲートウェイエンドポイントを通じてVPC内のEC2インスタンスと通信する S3.jpeg

バケット

  • オブジェクトを保存するための領域
  • バケット名はAWS内(全世界)で一意にしなければならない

オブジェクト

  • S3に格納されるデータそのもの
  • 各オブジェクトには必ず一意になるURLが作成される

Route53

  • ドメイン管理機能と権威DNS機能を持つ
    • 権威DNSとは、ドメイン名とIPアドレスの変更情報を保持しているDNS
  • DNSのポート番号が53であることからこの名前が付いている

CloudFront

  • 静的コンテンツをキャッシュして配信するサービス
  • ロードバランサーの前段に配置し、元のサーバにあるデータをキャッシュしておくことで、次回に同じリクエストが来たらキャッシュしたものを返す
    • サーバの負荷を軽減できる
    • 利用者から最も近いロケーションからコンテンツを高速に配信することができる
  • キャッシュの期間は拡張子やURLごとに設定することができる
    • 頻繁にアップデートされるコンテンツはキャッシュを短くし、あまり更新されないコンテンツは長くするなど CDN.jpeg CloudFront.jpeg

オーソドックスなアーキテクチャ

  • 簡易的なブログのシステム構成図を想定
  • S3にはエラーページなどの静的コンテンツを配置 システム構成図.jpeg

参考資料

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

基本的なシステム構成図を理解するためのAWS基礎をまとめてみた

はじめに

最近、AWSのシステム構成図を見ることが多くなり、AWS上で動いているシステムのシステム構成図を理解できるようになるために個人的に知っておきたいと思ったAWSの用語・サービスをまとめてみました。
私自身も勉強がてら作成したので、わかりづらい部分も多くあると思いますが、AWSのサービス全くわからん→なんとなく雰囲気把握した、となっていただけたらと思います。
2018年10月に新しくなったAWSアーキテクチャアイコンを使用しています。

サーバ・クライアントなどの説明についてはここではしていませんが、以下の記事でとてもわかりやすくまとめてくださっています!
超絶初心者のためのサーバとクライアントの話

知っておきたい用語

リージョン

  • AWSがサービスを提供している拠点(国と地域)
  • リージョン同士はそれぞれ地理的に離れている(例えば日本とオレゴンとか)
  • 日本はap-northeast-1(リージョン名はアジアパシフィック(東京))

アベイラビリティゾーン

  • リージョン内のさらに細かい拠点
  • 1つのアベイラビリティゾーンは複数のデータセンターから構成されている
  • ap-northeast-1には3つのアベイラビリティゾーンがある(a,c,d なぜかbが無い)
  • ごく稀にアベイラビリティゾーン全体で障害が発生することがあり、その時にリソースが1つのアベイラビリティゾーンに固まっているとサービスが停止してしまうため、アベイラビリティゾーンをまたがってインスタンスを配置する設計にする必要がある

リージョンとアベイラビリティゾーンの関係性

リージョンとアベイラビリティゾーン.jpeg

主なサービス

VPC

  • Virtual Private Cloudの略
  • AWSの中のプライベートゾーンのようなもの
  • VPCを使うことで、AWS内にプライベートなネットワークを作成できる
  • 自由にIPアドレス(CIDRブロック)を割り当てることができる VPC.jpeg

サブネット

  • VPCのさらに内側につくる、EC2インスタンス(※後述)などを起動するための領域
  • VPCに設定したCIDRブロックに収まるCIDRブロックを割り当てる
  • サブネット作成時にアベイラビリティゾーンを指定する
  • パブリックサブネットとプライベートサブネットがあり、それらの違いはインターネットゲートウェイ(※後述)から直接アクセスできるかできないかである(アクセスできるのがパブリックで、アクセスできないのがプライベート) subnet.jpeg

ルートテーブル

  • サブネット内のインスタンスの通信先のルール(サブネットがどこと通信するか)を定めた表のようなもの
  • 個々のサブネットに1つずつ設定する
  • 各サブネットをルートテーブルに関連付ける必要がある(指定しない場合はデフォルトのルートテーブルになる)
  • ルートテーブルで、サブネットの通信先にインターネットゲートウェイを指定することで、サブネットがインターネットに接続できる ルートテーブル.jpeg

ゲートウェイ

  • VPCの内部と外部との通信をやり取りする出入り口

インターネットゲートウェイ

  • VPCとインターネットを接続するためのゲートウェイ
  • 各VPCに1つだけ取り付けられる

NATゲートウェイ

  • プライベートIPしか持っていないEC2インスタンスがインターネットと通信できるようにするためのもの
  • プライベートIPをNATゲートウェイが持つグローバルIPに変換し、外部と通信する

ゲートウェイエンドポイント

  • S3やDynamoDBといったVPCの中に入れられないサービスに、VPC内から接続する際に利用する

ゲートウェイ.jpeg

EC2

  • Amazon Elastic Compute Cloudの略でAWSの基幹サービス
  • 仮想サーバを提供する
  • 仮想サーバの1つ1つの実体は「インスタンス」と呼ばれる
  • EC2インスタンス≒サーバ という感覚
  • ボタンワンクリックでインスタンスをつくることができるので便利
  • クライアントからのリクエスト数が増えたらインスタンスの数を増やしたり(スケールアウト)、インスタンスの性能を上げたり(スケールアップ)も簡単にできるため、サーバ調達のための時間やコストを削減できる EC2.jpeg スケールアップとスケールアウト.jpeg

ELB

  • Elastic Load Balancingの略
  • その名の通りロードバランサーのマネージドサービス(管理とかを一括して請け負ってくれるサービス)
  • ロードバランサー→サーバへの負荷を分散させるやつ
  • EC2インスタンスがスケールアウトされて複数ある場合、ELBがクライアントからリクエストを受け取り、それを各インスタンスに分散させる
  • ELB自体もスケーリングができ、負荷に応じてスケールする設計になっている

ELBの種類

  • Classic Load Balancer(CLB)
    • L4/L7レイヤーで負荷分散を行う。現在はあまり使わない。
  • Application Load Balancer(ALB)
    • L7レイヤーでの負荷分散を行う。CLBより後に登場。
  • Network Load Balancer(NLB)
    • L4レイヤーでの負荷分散を行う。HTTP以外のプロトコル通信の負荷分散をしたい時に利用する。 ALB.jpeg

RDS

  • RDB(リレーショナルデータベース、関係データベース)と呼ばれる、SQL文でデータを操作するデータベースのマネージドサービス
  • Amazon Aurora、MySQL、MariaDB、PostgresSQL、Oracleなどのデータベースエンジンから好きなものを選べる
  • データ保存用ストレージはEBS(Elastic Block Store)を使用している
  • インターネットからの接続はデフォルトでオフになっている(多分セキュリティを保つため)ため、EC2などの他のAWSサービスから通信することが多い RDS.jpeg

マルチAZ構成

  • 1つのリージョン内の2つのアベイラビリティゾーンにDBインスタンス(データベースの実体)をそれぞれ配置する構成
  • 1つのアベイラビリティゾーンで障害が発生しても、もう一つのアベイラビリティゾーンが生きていればシステムのダウンタイムを短くすることができる
  • DBインスタンス作成時にマルチAZ構成を選択したら、あとはAWSが勝手に冗長化してくれて便利

S3

  • Simple Storage Serviceの略
  • 容量無制限のストレージ(補助記憶)サービス
  • なんかめっちゃ色々なものが大量に入る箱のようなもの
  • 保存するファイルにメタデータを追加し、「オブジェクト」として管理する
  • S3に保存されている各オブジェクトには、RESTやSOAPといったWeb APIでアクセスする
  • データのバックアップや、静的コンテンツ(HTMLファイルなど)のホスティング(置き場)としても利用される
  • VPCの中には入れられないため、ゲートウェイエンドポイントを通じてVPC内のEC2インスタンスと通信する S3.jpeg

バケット

  • オブジェクトを保存するための領域
  • バケット名はAWS内(全世界)で一意にしなければならない

オブジェクト

  • S3に格納されるデータそのもの
  • 各オブジェクトには必ず一意になるURLが作成される

Route53

  • DNSのサービス
  • ドメイン管理機能と権威DNS機能を持つ
    • 権威DNSとは、ドメイン名とIPアドレスの変更情報を保持しているDNS
  • DNSのポート番号が53であることからこの名前が付いている

CloudFront

  • 静的コンテンツをキャッシュして配信するサービス
  • ロードバランサーの前段に配置し、元のサーバにあるデータをキャッシュしておく(記憶しておく)ことで、次回に同じリクエストが来たらキャッシュしたものを返す
    • サーバの負荷を軽減できる
    • 利用者から最も近いロケーションからコンテンツを高速に配信することができる
  • キャッシュの期間は拡張子やURLごとに設定することができる
    • 頻繁にアップデートされるコンテンツはキャッシュを短くし、あまり更新されないコンテンツは長くするなど CDN.jpeg CloudFront.jpeg

オーソドックスなアーキテクチャ

  • 簡易的なブログのシステム構成図を想定
  • S3にはエラーページなどの静的コンテンツを配置 システム構成図.jpeg

参考資料

以下を参考にさせていただきました。ありがとうございました。

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

ElasticBeanstalk(MultiContainer)の利用方法

概要

Dockerを利用した本番運用をしたくなり、ElasticBeanstalkについて調べたのでまとめる。

ElasticBeanstalkとは

定義ファイルを準備する事で、AWS上のリソース(EC2,ECS,ELB...etc)を利用した環境を構築してくれるサービスであり、MultiContainerパターンではCloudFormation + ECSと同等の環境を設定ファイルのみで構築できる。

マルチコンテナパターン概要

  • Dockerrun.aws.jsonにコンテナの関係を定義して、ElasticBeanstalkのコマンドを叩けばデプロイされる。
  • 冗長化構成やスケール設定をしたい場合は.ebextensions内に追加設定を書いていくことで実現できる。

サンプルの構成図

サンプルのリソース関係図・構成図は下記のようになる

AWSリソース関係図

ElasticBeanstalk01.png

EC2内のContainer構成図

ElasticBeanstalk2.png

Dockerrun.aws.jsonのサンプル

ECRを利用したサンプルを下記に示す(書き方はdocker-composeのjson版?)

Dockerrun.aws.json
{
  "AWSEBDockerrunVersion": 2,
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "xxxxx.amazonaws.com/xxxxx/nginx:latest",
      "essential": true,
      "memory": 128,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80
        }
      ],
      "links": [
        "rails-app"
      ],
      // Log設定(今回はCloudWatchLogsを利用する)
      "logConfiguration" : {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "ap-northeast-1",
          "awslogs-group": "/xxxxx/develop/nginx"
        }
      }
    },
    {
      "name": "rails-app",
      "image": "xxxxx.amazonaws.com/xxxxx/rails-app:latest",
      "essential": true,
      "memory": 256,
      "portMappings": [
        {
          "containerPort": 3000
        }
      ],
      "logConfiguration" : {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "ap-northeast-1",
          "awslogs-group": "/xxxxx/develop/rails-app"
        }
      }
    }
  ]
}

ログの運用について

デフォルトでは各コンテナの標準出力がS3に保存されるが、それだと見づらいためCloudWatchLogsを利用する。
設定方法はこちらを参照
※ ElasticBeanstalkを利用する場合はLoggingDriverとしてFluentdを指定できない(カスタムAMIでなんとかいけるがオススメできない)

Deployまでの流れ

  1. AWS-CLIとEB-CLIの準備
  2. DockerImageの作成(説明省略)
  3. ImageをECRにPush(こちらを参照)
  4. EB-CLIでcreate or deploy

AWS-CLIとEB-CLIの準備

AWS-CLIのインストール

$ brew install awscli

EB-CLIのインストール

$ brew install awsebcli

EB-CLIでDeploy

初期設定

$ eb init
9) ap-northeast-1 : Asia Pacific (Tokyo) 
2) [ Create new Application ]
8) Multi-container Docker

残りはお好きなように設定

新規作成

$ eb create rails-app-develop

デプロイ

$ eb deploy rails-app-develop

オートスケールなどの細かい設定について

  • Docker.run.jsonと同階層に.ebextensionsを作成し、そこに追加設定ファイルを置く
  • 「ファイル名.config」としないと認識しないため注意
  • 拡張設定値はこちらを参照

階層サンプル

.
├── .ebextensions
│   ├── 00-vpc.config
│   └── 01-autoscaling.config
├── .elasticbeanstalk 
│   └── config.yml
└── Dockerrun.aws.json
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ALBのOIDC認証をSpring Securityで使用してみた。

認証が辛い!! もっと楽に安全に認証したい。したいですよね?

そういえば、ALBがOIDC認証に対応していたな、Spring Securityに食わせれば手間いらずで認証できるんじゃ・・・?

事前準備

ALBの仕様

検証が成功してターゲットグループ(Spring Securityが載ってるアプリ)を呼ぶときにはヘッダーのx-amzn-oidc-dataにJWTを乗せて送ってくる。

  • 2回目以降もセッションが有効であれば毎回ちゃんと乗せてくれる。
  • 有効期限(exp)は3分ぐらいとかなり短い(厳密には計ってない)
  • 署名はECDSA + P-256 + SHA256

Spring Securityの設定

まず、主となるのが以下のフィルターこのフィルターが今回すべての起点となる。
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter

見ての通り本来はbearerトークン用ではあるもののトークンはJWTが入ってくることが決め打ちになってるので、今回はこれを活用する。
その際、JWTの検証や詰め替えは次のアーティファクトが担っている。
org.springframework.security:spring-security-oauth2-jose

bearerトークンではなくx-amzn-oidc-dataをトークンとして認識させる

以下を継承したクラスを作成
org.springframework.security.oauth2.server.resource.web.BearerTokenResolver

ALBTokenResolver
public class ALBTokenResolver implements BearerTokenResolver {
    @Override
    public String resolve(HttpServletRequest request) {
        return request.getHeader("x-amzn-oidc-data");
    }
}

このような形で任意のヘッダーやリクエストボディもトークンとして認識させることができる。

JWTの検証

org.springframework.security.oauth2.jwt.JwtDecoderがJWTのパースから検証までを行っている。
実体はorg.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupportである。

公開鍵の取得

まず、JWTを署名した公開鍵は所定のURLからPEMでEC公開鍵が得られるので、それに合わせてcom.nimbusds.jose.proc.JWSVerificationKeySelectorを継承したクラスを作成する。
実装はcom.nimbusds.jose.util.X509CertUtilsを参考に作成する。
長いので以下のリンクを参照
https://github.com/tac-yacht/Sample_SpringSecurityForAWSALB/blob/master/src/main/java/com/example/auth/ALBPublicKeySelector.java
※JwtDecoderから来るjwkSetUrlを流用しているため、application.ymlに以下を設定すること(決め打ちとする場合はこの限りでは無い)

application.yml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://public-keys.auth.elb.us-west-2.amazonaws.com/

JwtDecoderへの取得処理の設定

実体は前述したとおりNimbusJwtDecoderJwkSupportなのだが、任意の署名アルゴリズムと鍵取得が設定できない。
そのためまるごとコピーして以下の部分に先ほどのKeySelectorで初期化する。

private_constractor(String,String)
        JWSKeySelector<SecurityContext> jwsKeySelector =
            new ALBPublicKeySelector<SecurityContext>(this.jwsAlgorithm, jwkSource); //ここ

アルゴリズムは以下の部分を書き換える

public_constractor
    public ALBJwtDecoder(@Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") String jwkSetUrl) {
        this(jwkSetUrl, JwsAlgorithms.ES256);
}

処理利用の設定

あとはほかのSpring Security同様ConfigJavaを記述

OAuth2ResourceServerSecurityConfiguration.java
@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private ALBTokenResolver resolver;
    @Autowired
    private ALBJwtDecoder decoder;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .oauth2ResourceServer()
                .bearerTokenResolver(resolver)
                .jwt()
                    .decoder(decoder)
        ;
    }
}

ちなみにdecoderからメソッドチェインできるjwtAuthenticationConverterを実装することでコントローラーの@AuthenticationPrincipalから得られるオブジェクトを任意のものに変更できる。ユーザーの詳細情報をDBやRedisから引いたものを設定したり

まとめ

残念ながら設定だけとは行かなかったが、ALBが行った認証を受け取って利用できることがわかった。
Issueを見ると、今後はNimbusJwtDecoderJwkSupportは非推奨になり、OidcIdTokenDecoderFactoryに置き換わることで先ほどの署名アルゴリズムといったところが設定可能になるようだ。
https://github.com/spring-projects/spring-security/issues/6883

ソースコード

https://github.com/tac-yacht/Sample_SpringSecurityForAWSALB

参考文献

余談

以下を読むとLet's EncryptはEC2はダメとのこと。ALBならドメイン違うし(elb.amazonaws.com)、リスナールールで適当に静的応答すれば行けるんじゃね?!→ダメでした。
https://hacknote.jp/archives/37697/
短期間でいいからAWS Certificate ManagerからXXX.elb.amazonaws.com向けの電子署名払い出してくれないかな……

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

Heroku CLIをAWSで使えるようにする

https://devcenter.heroku.com/articles/heroku-cli

:point_up:からダウンロードします。

Other installation methodsのリンクからページに飛び。

$ cd ~/environment/で移動してからやるとよい)

TarballsのLinux(x64)のリンクアドレスをコピー

$ curl -OL "(URL)を貼る。"

ダウンロードが始まる。
.
.
.
.
.
.

Heroku CLIのインストールしていく

$tar zxf heroku-linux-x64.tar.gz
$ sudo mv heroku /usr/local/

$ ls /usr/local/heroku/ (確認)

$echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.bash_profile
$source $HOME/.bash_profile > /dev/null

最後に確認する

$ heroku -v

バージョンが表示されたらOK!
.
.
.
.
.
.

終わったら消そう!

$ rm -f heroku-linux-x64.tar.gz

これで完了です。

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

Amazon DocumentDBにRobo3T(Robomongo)で繋ぎたいンゴ

ローカルからRobo3TでAmazon DocumentDBに繋ぐために四苦八苦した非エンジニアの備忘録メモ。

前置き

Amazon DocumentDBとは?

AWSが提供しているMongoDB互換のドキュメント指向データベースサービス。
※MongoDBのフルマネージドサービスという訳ではない。

Robo3Tとは?

旧名: Robomongo。
オープンソースのMongoDB用GUIツール。
※非エンジニアはCLIなんて操作できないので必須アイテム。

①DocumentDBのクラスターを作成

  • サブネットグループやパラメーターグループは事前に作っておく。作成後に変更するのはしんどいので作成段階でセットする。
  • VPCは後ほど作成するEC2と同じVPCをセットする。

docdb.png

②EC2を作成

トンネル用のEC2を作成する。
VPSとセキュリティグループさえ気をつければ、他は適当で良い。

③EC2に接続

下記を参考に裏で接続しておく。

ssh ec2-user@12.34.567.89 -p22 -L27018:172.20.0.1:27017 -i /.ssh/sample.pem

④Robo3Tを設定

下記を参考に新規プロファイルを作成する。
DocumentDB用のpemはhttps://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pemからダウンロードしておく。
スクリーンショット 2019-07-08 20.18.42.png

スクリーンショット 2019-07-08 20.19.01.png

スクリーンショット 2019-07-08 20.20.45.png

スクリーンショット 2019-07-08 20.21.11.png

⑤Robo3Tで接続

無事繋がったンゴ
robo.png

参考

amazon web services - AWS DocumentDB with Robo 3T (Robomongo) - Stack Overflow
https://stackoverflow.com/questions/54384253/aws-documentdb-with-robo-3t-robomongo

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

S3のオブジェクトの更新をトリガーとして発火する LambdaFunctionを作成する。

はじめに

以前、CloudFrontの配信元のファイル(S3)が更新されたら自動的にキャッシュをクリアする仕組みを作るという記事を作成しました。

その対応の中で「S3のオブジェクトの更新をトリガーとして発火する LambdaFunction」が必要となります。

当記事では具体的な設定方法を紹介します。

具体的な設定方法

  1. イベント発火元のS3バケットを選択
  2. 「プロパティ」-「Events」-「詳細はこちら」をクリック
  3. 「通知の追加」をクリックして、[送信先]にLambdaFunctionを選択、[Lambda]に自分の実行したいLambdaFunction名を選択する image.png

以上で完了です。

終わりに

設定自体は見つけてしまえば簡単です。
設定が反映されるまでは3分くらいかかる為、留意して下さい。
※実はLambdaFunction側からでも簡単に設定できます。

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

AWS S3のオブジェクトの更新をトリガーとして発火する LambdaFunctionを作成する。

はじめに

以前、CloudFrontの配信元のファイル(S3)が更新されたら自動的にキャッシュをクリアする仕組みを作るという記事を作成しました。

その対応の中で「S3のオブジェクトの更新をトリガーとして発火する LambdaFunction」が必要となります。

当記事では具体的な設定方法を紹介します。

具体的な設定方法

  1. イベント発火元のS3バケットを選択
  2. 「プロパティ」-「Events」-「詳細はこちら」をクリック
  3. 「通知の追加」をクリックして、[送信先]にLambdaFunctionを選択、[Lambda]に自分の実行したいLambdaFunction名を選択する

image.png

以上で完了です。

終わりに

設定自体は見つけてしまえば簡単です。
設定が反映されるまでは3分くらいかかる為、留意して下さい。
※実はLambdaFunction側からでも簡単に設定できます。

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

AWS S3のオブジェクトの更新をトリガーとして発火する LambdaFunctionを作成する

はじめに

以前、CloudFrontの配信元のファイル(S3)が更新されたら自動的にキャッシュをクリアする仕組みを作るという記事を作成しました。

その対応の中で「S3のオブジェクトの更新をトリガーとして発火する LambdaFunction」が必要となります。

当記事では具体的な設定方法を紹介します。

LambdaFunctionの中身の件についてはこちらの記事にて記載しております。
AWS S3バケット名からCloufFrontのキャッシュをクリア(CreateInvalidation)するAPIをコールする

具体的な設定方法

  1. イベント発火元のS3バケットを選択
  2. 「プロパティ」-「Events」-「詳細はこちら」をクリック
  3. 「通知の追加」をクリックして、[送信先]にLambdaFunctionを選択、[Lambda]に自分の実行したいLambdaFunction名を選択する

image.png

以上で完了です。

終わりに

設定自体は見つけてしまえば簡単です。
設定が反映されるまでは3分くらいかかる為、留意して下さい。
※実はLambdaFunction側からでも簡単に設定できます。

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

【RDS for Oracle】Performance Insights(パフォーマンスインサイト)メモ

メモ

  • チューニングの手前までができる。チューニング自体はしないRDSで提供しているすべてのエンジン(Oracleなど)でつかえる。

  • データベースロード:アクティブなセッション数(CPU使用率、アイドルの2つのステータス)。1秒おきにアクティブなセッションの詳細な情報をサンプリング

  • カウンターメトリクス:OSよりのメトリクス(CPU使用率)

  • Top N ディメンション:その時間帯のパフォーマンスに影響していたTop NのSQL。データ保持基幹デフォルト7日(2年間の長期保持も)

  • パフォーマンス問題の有無はデータベースロードがインスタンスの最大vCPUを超えているかどうかが基準

  • 分析

    • どんな待機イベントが多かったのか。
    • 高負荷SQL
    • 接続元ホスト(システム全体の中で問題の比率の多いアプリケーションは?)
    • 接続ユーザ
  • クエリのパフォーマンス管理
    SQLヒント
    SQL Plan Stability
    SQL Plan管理
    →OracleだとSPM(SQL Plan Manager)

  • 重要なとこ

    負荷グラフでの重要な視覚的手がかりは「最大CPU」ラインです。このラインはホスト上のvCPUの数を表します。vCPUよりも多くのアクティブセッションがCPU待機している場合、CPUの許容量を超えてインスタンスが実行されていることを意味します。負荷全体が「最大CPU」のラインを超えると、ボトルネックが発生する可能性があります。ボトルネックはCPUの飽和が原因かもしれませんし、データベース内でセッションが待機するその他の多くの理由によって引き起こされているかもしれません。

performance.png

参考

Amazon RDSパフォーマンスインサイトの使用
Performance Insights を使用した Amazon RDS データベースの負荷分析

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

AWSのS3をマウントしたい人に。(Goofys)

はじめに

私はS3をマウントすることになりました。ググると主にStorageGateway、goofys、s3fsの3つのうちのどれかを使用するようです。試行錯誤の末、goofysを使用することにしました。
ちなみに私の場合最初、StorageGatewayを使おうとしましたが、ファイルゲートウェイ設定の際VMwareEsxiをインストールする必要があり、なぜか詰まりました。VMwareの環境構築の苦労についてはまた別の機会で、、
環境はLinuxです。

go,fuseのインストール

goofysの使用にあたってgo,fuseが必要になるため、先にインストールします。

sudo apt-get update
sudo apt-get install golang

PATHを通す

export GOPATH =$HOME/go

この時色々ググって試した結果、いつのまにかGOPATHとGOROOTが同じパスになってエラーになったため注意。

echo $GOPATH
echo $GOROOT
echo $PATH

上記のようにパスを確認しながら進めるとよかったです。

goofysのインストール

go get github.com/kahing/goofys
go install github.com/kahing/goofys

1行目を実行しても反応がなく、2行目を先に試したりすると、”permissiondenied”や”許可がありません”というエラーが出た。rootユーザーで試したり、awsconfigureの設定をし直したり、PATHの確認したり試行錯誤した。さらによく調べると、この部分の処理には時間がかかることがあると分かりました。そこで、しばらく待ってみると正常に実行されました。2行目の実行に関しては私の場合、2,3秒で終わりました。

(goofysのインストールver2)

wget https://github.com/kahing/goofys/releases/download/v0.0.5/goofys -P /usr/local/bin/

上記の方法も試してみたりしました。ですが、これは必要なかったと思います。

マウントの確認

$aws s3 mb s3://shangben-goofys  //S3バケット作成
$mkdir ~/mount-goofys  //ローカルディレクトリ作成

上のようにS3バケットとマウント先のローカルディレクトリを作ります。

ちょっとファイルを作ってみます。

$touch ~/mount-goofys/test

S3やボリュームを確認してみます。

$aws s3 ls s3://マウントしたバケット名
$ps auxf|grep goofys //プロセス確認
$df -h //ボリュームの確認

おわり

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

GoofysでAWSのS3をマウントする。

はじめに

私はS3をマウントすることになりました。ググると主にStorageGateway、goofys、s3fsの3つのうちのどれかを使用するようです。試行錯誤の末、goofysを使用することにしました。
ちなみに私の場合最初、StorageGatewayを使おうとしましたが、ファイルゲートウェイ設定の際VMwareEsxiをインストールする必要があり、なぜか詰まりました。VMwareの環境構築の苦労についてはまた別の機会で、、
環境はLinuxです。

go,fuseのインストール

goofysの使用にあたってgo,fuseが必要になるため、先にインストールします。

sudo apt-get update
sudo apt-get install golang

PATHを通す

export GOPATH =$HOME/go

この時色々ググって試した結果、いつのまにかGOPATHとGOROOTが同じパスになってエラーになったため注意。

echo $GOPATH
echo $GOROOT
echo $PATH
go env GOPATH //これでもできる。

上記のようにパスを確認しながら進めるとよかったです。

goofysのインストール

go get github.com/kahing/goofys
go install github.com/kahing/goofys

1行目を実行しても反応がなく、2行目を先に試したりすると、”permissiondenied”や”許可がありません”というエラーが出た。rootユーザーで試したり、awsconfigureの設定をし直したり、PATHの確認したり試行錯誤した。さらによく調べると、この部分の処理には時間がかかることがあると分かりました。そこで、しばらく待ってみると正常に実行されました。2行目の実行に関しては私の場合、2,3秒で終わりました。

(goofysのインストールver2)

wget https://github.com/kahing/goofys/releases/download/v0.0.5/goofys -P /usr/local/bin/

上記の方法も試してみたりしました。ですが、これは必要なかったと思います。

マウントの確認

$aws s3 mb s3://shangben-goofys  //S3バケット作成
$mkdir ~/mount-goofys  //ローカルディレクトリ作成

上のようにS3バケットとマウント先のローカルディレクトリを作ります。

ちょっとファイルを作ってみます。

$touch ~/mount-goofys/test

S3やボリュームを確認してみます。

$aws s3 ls s3://マウントしたバケット名
$ps auxf|grep goofys //プロセス確認
$df -h //ボリュームの確認

おわり

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

AWS DeepRacer Virtual Circuit 6月の優勝者Fumiakiに聞いてみた

はじめに

AWS DeepRacerリーグの6月の仮想サーキットで登場した新たなコースは、社内でも勉強会をはじめとして多くの話題に挙がりました。特に

  • トレーニング用トラック(Kumo Torakku Training)が、なかなか完走できない問題
  • レース用トラック(KumoTorakku)は形が異なる問題

については、闊達な議論が飛び交いました。

この癖のある6月のバーチャルサーキットで1位を獲得することができたFumiakiの走行記録とヒアリングした学習方針を公開します。

今回も分析にはAWS DeepRacerワークショップのGitHubで公開されているlog-analysisノートブックを使いました。
https://github.com/aws-samples/aws-deepracer-workshops/
ログ分析ツールの導入方法や使い方は
AWS DeepRacerのログ分析ツールを使ってみた
を参考にしてください。

注意

  • 以下の内容は 2019 年 7 月 4 日時点のサンプルノートブックを対象にしています。その他のバージョンのノートブックでは利用できない可能性があります。
  • ログの分析は、6月の仮想サーキットで1位を獲得したデータ、モデルを対象にしています。
  • 本記事は、あくまで個人の見解に基づいて記載しております。

トレーニングモデルの分析結果

Plot rewards per Iteration

plotreward.png
1stepあたりの最大報酬は1です。

Reward distribution for all actions

rewarddistribution.png

Probability distribution on decisions (actions)

Probability distribution.png

仮想サーキットの走行記録

仮想サーキットで送信した記録はCloudWatchLogsの
/aws/deepracer/leaderboard/SimulationJobs/以下に走行後にログとして残されています。

Evaluation Run Analysis

eval_run.png

全エピソード中、完走したのは4回目の1回だけでした。
結果中のLaptimeは10.11秒とありますが分析では最初のステップがタイムとして含まれていないため、公式記録は10.312秒です。

また、分析用にトレーニングトラック用を利用しているのでコースアウトしているように見えますが、実際のレース用トラックは以下の図(右)のように急なヘアピン、シケインはありません。

eval_truck.png

完走したエピソードのstep数は149です。
completelap.png

感想と考察

以下はあくまで個人的な見解となります。

Reward distribution for all actionsを見ると、トラック上のカーブやシケインに沿うのではなくショートカットコースに沿っていることがわかります。(Fumiakiは5月のLondonLoopでも同じ手法をとっていました)
トレーニングトラックで左曲がりになる以下のポイントは、レーストラックでは右曲がりになっています。
kansou1.png

トレーニング中においてシミュレーションでは、左に曲がるコース映像のハズです。
このポイントだけあえてコースラインとは逆の方向、右に曲がれる学習をし、且つ他のポイントではコース通りに曲がれるような学習をしていることがわかりました。
これらは仮想サーキットの完走率(1/5)にも現れており、コースアウトしやすいポイントにもなっています。
このことから、仮想サーキットへはこの可能性を信じておそらく何度も挑戦したのだと思います。

学習方針

ログやデータから読み取れないところは、直接Fumiakiに聞いてみました。

1. トレーニング用のKumoTorakkuを完走するには?

まずは完走させることを目的に学習した後で、速いスロットルが選ばれやすくなるように追加学習をしました。
具体的にはKumo Torakkuをメインに他のトラックを少し組み入れてトレーニングしました。
正直、これ以外にもいろいろと試行錯誤を繰り返しているので、何が効いているのか分からない部分はありますが。。

2. トレーニングとレース(仮想サーキット)トラックの違いについて

トレーニングトラックよりレーストラックの方が走りやすいので、トレーニングトラックが殆ど走れなくてもレース用のトラックで完走出来たりするケースもありました。
リーダーズボードのEvaluationを見るとレースは全5回走行で、Kumo Torakkuはそのうち1回完走すれば良いので、奇跡の1周に頼りやすいと思いました。
(※5月のLondonLoopでは3回以上の完走が記録として必須でした。)

また、トレーニングのトラックではもっと完走率の高く、速いモデルができていたのですが、それは仮想サーキットコースでは一度も完走できませんでした。
トレーニングトラックに特化し過ぎるのも注意が必要かもしれません。

最後に

今月のバーチャルレース当初はそれぞれの課題を個人で抱えていましたが、今回のトラックが比較的難しかったことが共通の課題となり、勉強会以外でも、職場・部署の壁を越えてより積極的にコミュニケーションをとり、議論を深めあう機会になりました。
エンジニア同士お互いに敬意を持って、わからないことを聞いてみる、知っていることを教える、ということも良かったと思います。

Congratulations! Fumiaki!

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

AmazonLinuxのEC2上でHIDSのAIDEを構築する

IDSとは?

IDSは、侵入検知システム(Intrusion Detection System)の略称。
似たような概念として、IPSがありますが、こちらは侵入防止システム(Intrusion Prevention System)の略称で、侵入されたことを検知するか、それとも侵入自体を防止するか分けられています。

IDSにはNIDSとHIDSの二種類があり、一般的にIDSと呼ぶと前者のことをさすことが多いです。
NIDSは、Network-based Intrusion Detection Systemの頭文字を取ったもので、ネットワーク上で侵入検知を行うシステムです。
HIDSは、その頭にHost-basedが付いたもので、ホスト型の侵入検知システムのことをさします。
本ポストでは、オープンソースで提供されるHIDSのひとつである、AIDEのインストール方法について述べます。

AIDEとは?

HIDSの一つで、ファイルの改ざん検知を実施するツールです。
Advanced Intrusion Detection Environmentの頭文字を取っているそうです。
https://aide.github.io/

似たようなツールとして、OSSECというのがあります。
https://ossec.github.io/
そっちの方はAWSさんの公式ドキュメントに構築手順が載っているので、そちらを参照。
https://aws.amazon.com/jp/blogs/news/how-to-monitor-host-based-intrusion-detection-system-alerts-on-amazon-ec2-instances/

インストール&初期設定

まずは、yumを使ってインストールをしていきましょう。
今回は、OSとしてAmazonLinuxを利用しました。

yum install -y aide

/etcの下に、どこのフォルダを検知対象とするかを書くためのコンフィグファイルが
出来ているはずです。

/etc/aide.conf
@@define DBDIR /var/lib/aide
@@define LOGDIR /var/log/aide
database=file:@@{DBDIR}/aide.db.gz
database_out=file:@@{DBDIR}/aide.db.new.gz
gzip_dbout=yes
verbose=5
report_url=file:@@{LOGDIR}/aide.log
report_url=stdout
FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
ALLXTRAHASHES = sha1+rmd160+sha256+sha512+tiger
EVERYTHING = R+ALLXTRAHASHES
NORMAL = FIPSR+sha512
DIR = p+i+n+u+g+acl+selinux+xattrs
PERMS = p+i+u+g+acl+selinux
LOG = >
LSPP = FIPSR+sha512
DATAONLY =  p+n+u+g+s+acl+selinux+xattrs+sha256
/boot   NORMAL
/bin    NORMAL
/sbin   NORMAL
/lib    NORMAL
/lib64  NORMAL
…

上の方はいじらなくてよいですが、/boot NORMAL等ディレクトリがずらっと記述されているはずです。
これは、検知対象のディレクトリを表しています。検知対象としたいディレクトリはそのままにして、検知したくないディレクトリの行の先頭には!を付けてあげましょう。
(今回はテストなので、/usr以外すべてのディレクトリを検知対象外としました。)

/etc/aide.conf
/boot   NORMAL
↓
! /boot   NORMAL

次に、現状のファイル状態を保存したデータベースファイルを作成します。

aide --init
console
### AIDE database at /var/lib/aide/aide.db.new.gz initialized.

結構時間がかかります。現状のファイル構造が複雑であれば複雑であるほど時間がかかるんだと思います。
終わったら、早速ファイルを変更してみて検査…をしようとすると、エラーが出ます。
↑でやった初期化だけでは、検知に利用するためのデータベースファイルの元しかできないので、自分で名前を変更してあげる必要があります。

mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

ようやく、検知する準備ができました。
さっそく適当に一つファイルを作ってみて、検知ができるか見てみましょう。

touch /usr/testfile.txt

/usrの直下にtestfile.txtという名前のテキストファイルを置きました。
(あくまで例ですので、ご自身の都合のいいディレクトリで試してみてください)

検査してみます。

aide --check
console
AIDE found differences between database and filesystem!!
Start timestamp: 2019-07-08 06:21:37

Summary:
  Total number of files:        36082
  Added files:                  1
  Removed files:                0
  Changed files:                1


---------------------------------------------------
Added files:
---------------------------------------------------

added: /usr/testfile.txt

---------------------------------------------------
Changed files:
---------------------------------------------------

changed: /usr

--------------------------------------------------
Detailed information about changes:
---------------------------------------------------


Directory: /usr
  Mtime    : 2019-05-13 17:54:02              , 2019-07-08 06:18:33
  Ctime    : 2019-05-13 17:54:02              , 2019-07-08 06:18:33

やはり少し時間はかかりますが、検知できました!
実際に利用する場合は、cronに検査とデータベースファイルの更新処理を書いたシェルを登録することになるかと思います。

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

【RDS for Oracle】Enterprise Managerを使う

前提

  • RDS for Oracle で使用できるEnterprise Manager(EM)は以下です。
Oracleバージョン EMの種類 デフォルト・ポート
12c EM Express 5500
11g EM Database Control 1158
  • 検証環境(ORCL1)が12cなのEMはEM Expressになります。
  • RDSでEMを使うにはオプショングループを設定します。以前の記事でオプショングループを作っているので、今回はこちらにオプションを追加していきます。

  • 注意点

    • 次のDB インスタンスクラスではEMはサポートされない
      • db.t2.micro
      • db.t2.smal
      • db.m1.small

事前準備

  • RDSのセキュリティグループのインバウンドにEMのポート(5500 or 1158)を追加する
  • オプショングループの設定

オプショングループの設定

RDSのコンソールからオプショングループオプションに追加
1.png

オプションにOEMを選択

RDS_·_AWS_Console.png

RDS_·_AWS_Console2.png

すでにオプショングループがインスタンスに紐付いているので、インスタンスに反映されます(インスタンスの再起動はありません)。

RDS_·_AWS_Console3.png

EMにアクセス

ブラウザ経由でEMにアクセスします。

今回、パブリックサブネット上に踏み台サーバ(Amazon Linux)、プライベートサブネット上にRDS(Oracle)があるためポートフォワードして続します。

  • ローカルPC(今回はmac)でポートフォワード
$ ssh -L 13389:orcl1.xxxx.us-west-2.rds.amazonaws.com:5500 -i <踏み台のキーペア> ec2-user@<踏み台のパブリックIPアドレス>
Last login: Mon Jul  8 08:14:36 2019 from 210.227.234.114

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
7 package(s) needed for security, out of 13 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-0-223 ~]$
  • この状態で下記URLをブラウザに入れます
https://localhost:13389/em
  • ユーザ名とパスワードにマスターユーザのユーザ名とパスワードを入れログインします
    EM_Express_Login_と_RDS_と_1__ec2-user_ip-10-0-0-223____ssh_.png

  • 無事ログインできました
    EM_Express_-_データベース・ホーム.png

参考

Oracle Enterprise Manager Database Express

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

【RDS for Oracle】初期化パラメータの変更

はじめに

RDSではalter system文での初期化パラメータの変更ができない。
RDSの場合パラメータグループを作成し初期化パラメータを変更する。

変更できる初期化パラメータは変更可能列がtrueの初期化パラメータのみとなる。
また、パラメータグループの適用タイプにより、変更時の動作が変わる。

適用タイプ 初期化パラメータ変更時の動作
dynamic 即時変更が有効
static インスタンス再起動後変更が有効

パラメータグループの作成

パラメータグループからパラメータグループの作成

RDS_·_AWS_Console1.png

パラメータグループファミリーを選択し、グループ名説明を記入し作成
RDS_·_AWS_Console2.png

パラメータの編集から初期化パラメータを変更できる。
RDS_·_AWS_Console3.png

作成したパラメータグループをインスタンスに付け替える。

参考

RDSパラメーターグループはいつ変更が有効になるのか?
DB パラメータグループを使用する

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

【RDS for Oracle】リストアのやりかた

はじめに

AWSのユーザガイド「DB スナップショットの復元」によると。。。

復元すると新しい DB インスタンスが作成

されるらしいです。なのでエンドポイントも変わるみたい。

マネージメントコンソールからのリカバリ方法

  • RDS コンソールからSnapshots を選択
  • 復元の元にする DB スナップショットを選択

RDS_·_AWS_Console1.png

  • スナップショットのアクションスナップショットの復元 の順に選択

RDS_·_AWS_Console2.png

  • DB インスタンスの復元 ページで、DB インスタンス識別子 に、復元された DB インスタンスの名前を入力
  • DBインスタンスの復元 を選択

RDS_·_AWS_Console3.png

  • スナップショットが作成された DB インスタンスの DB インスタンス機能を復元する場合は、セキュリティグループを使用するために DB インスタンスを変更します。次のステップは、DB インスタンスが VPC 内にあることを前提としています。DB インスタンスが VPC 内にない場合は、EC2 マネジメントコンソールを使用して、DB インスタンスに必要な DB セキュリティグループを見つけます。

AWS CLIでのリカバリ方法

  • Linux、OS X、Unix の場合:
aws rds restore-db-instance-from-db-snapshot \
    --db-instance-identifier <Newインスタンス名> \
    --db-snapshot-identifier <mydbsnapshot>
  • Windows の場合
aws rds restore-db-instance-from-db-snapshot ^
    --db-instance-identifier <Newインスタンス名> ^
    --db-snapshot-identifier <mydbsnapshot>

https://www.cloud-koubou.jp/archives/1175

補足

AWSのDBサービスのバックアップ取得時間などは「AWS データベースサービスの自動バックアップ設定まとめ」にまとまってます。

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

一番意識の低いAWS CloudWatch Insights入門

昨年末にAWSに追加されたAmazon CloudWatch Logs Insightsが結構便利です。
せっかくなので、最低限のやる気でなんとなく使えるようになっておきましょう。5分くらいで。

インサイトを開いてみる

ひとまずマネコンのCloudWatchの左のメニューからインサイトを開いてみるとこんな感じの画面になります。
image.png

ログを表示してみる

まずは何も考えずにログを表示してみましょう。
AWSのマネコンにしては珍しく、部分一致のインクリメンタルサーチをしてくれるので快適に目的のロググループにたどり着けます。
search.gif

エラーを探してみる

ログからエラーを抽出してみます。
何も考えずにデフォルトのfilterを追加すると、親切にエラーっぽい文字列の正規表現が追加されます。
error.gif

⬆の手順では5\d5\dはノイズが混じることが多いので除却しています。

ヒットしたログの詳細を確認する

ヒットしたログの前後を見たい場合も多いでしょう。
そんな場合は何も考えずにfieldsを追加しましょう。クエリでいろいろ重複しててもOKぽいです。
fields.gif

@logStreamのフィールドがリンクとなり、おなじみログストリームでの確認ができました。

時間を絞ってログを確認する

数分前に起こったエラーが確認したい、という場合は時間を絞ると効率が良いです。
term.gif

1時間以内にはエラーが起こっていないことが確認できました。

所感

  • CloudWatchを利用するなら使うべき
    • 手軽にログ調査の時間が短縮できます
  • AWSにしては珍しく親切な仕様
    • 部分一致のインクリメンタルサーチとかコマンドからクエリを追加した時にいい感じのデフォルト値が入ってることとか、使い勝手がかなり良いです
    • おかげでなんとなく使うだけでも効用が得られます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RDS for Oracle】ノーアーカイブログモードへ変更

はじめに

AWSのユーザガイドを確認すると。。。

大量のデータをロードしている間など、特定の状況では、自動バックアップを一時的に無効にすることをお勧めします。

とあります。
デフォルトはアーカイブログモードのため、DataPumpなどでインポートしたとき、アーカイブログが大量に生成されディスクを圧迫する恐れがあるということですね。

オンプレのOracleでもインポート前にSQL> alter database noarchivelog;で、ノーアーカイブログモードにしてました。

しかし、RDSではSYSやSYSTEMユーザでログインできず、alter database文も実行できません。

どうやら、RDS for Oracleのばあい、自動バックアップを無効化することで、アーカイブログモード(デフォルト)からノーアーカイブログモードに変更できるようです。

自動バックアップを無効化した場合、以下の注意点があります。
移行などの作業時に一時的に無効するのがよさそうです。

重要
自動バックアップは、無効にするとポイントインタイムリカバリも無効になるため、無効にしないことを強くお勧めします。DB インスタンスの自動バックアップを無効にすると、そのインスタンスの既存の自動バックアップがすべて削除されます。自動バックアップを無効にしてから再度有効にした場合、自動バックアップを再度有効にした時点からしか復元できません。

また、自動バックアップの無効化(ノーアーカイブログモードへの変更)時にはDBインスタンスが再起動するようです。

自動バックアップの無効方法(ノーアーカイブログモード)

  • RDSコンソールで対象インスタンスを選択
  • 変更を選択すると、DBインスタンスの変更ページが表示される
    RDS_·_AWS_Console.png

  • バックアップの保存期間0 days (0 日) を選択

  • 次へ を選択

RDS_·_AWS_Console2.png

  • すぐに適用を選択
  • 確認ページで、DBインスタンスの変更を選択して変更を保存し、自動バックアップを無効にします

RDS_·_AWS_Console3.png

  • 対象インスタンスのステータスが変更中になります
    RDS_·_AWS_Console4.png

  • 利用可能となったので変更が反映されます
    RDS_·_AWS_Console6.png

  • メンテナンスとバックアップからスナップショットを確認すると削除されています
    RDS_·_AWS_Console5.png

  • インスタンスに接続しノーアーカイブログモードになったか確認します。

SQL> select name,log_mode from v$database;

NAME      LOG_MODE
--------- ------------
ORCL1     NOARCHIVELOG  ARCHIVELOGからNOARCHIVELOGに変わった

自動バックアップの有効方法(アーカイブログモード)

  • RDSコンソールで対象インスタンスを選択
  • 変更 を選択するとDBインスタンスの変更 ページが表示されます。
  • バックアップの保存期間で、ゼロ以外の正の値 (7 日など) を選択
    RDS_·_AWS_Console7.png

  • 次へを選択

  • すぐに適用を選択

  • 確認ページで、DBインスタンスの変更 を選択して変更を保存し、自動バックアップを有効化
    RDS_·_AWS_Console8.png

    • 対象インスタンスのステータス変更中から利用可能になったら変更が反映される
    • インスタンスに接続しアーカイブログモードになったか確認します。
SQL> select name,log_mode from v$database;

NAME      LOG_MODE
--------- ------------
ORCL1     ARCHIVELOG
  • 自動バックアップを有効化した場合、スナップショットも自動で取得してくれるようです。 RDS_·_AWS_Console11.png

 手動でスナップショットを取る場合

  • RDSコンソールで対象インスタンスを選択
  • アクション で、スナップショットの取得 を選択
    RDS_·_AWS_Console9.png

  • DB スナップショットの取得ウィンドウが表示されます

  • スナップショット名 ボックスにスナップショットの名前を入力します。

  • スナップショットの取得を選択

RDS_·_AWS_Console10.png

  • メンテナンスとバックアップからスナップショットが取得されたことが確認できます RDS_·_AWS_Console12.png

AWS CLIでの自動バックアップの無効/有効方法

AWS CLIでの自動バックアップの無効

modify-db-instance コマンドを使用して、バックアップ保持期間を0 に設定し --apply-immediately を指定します。

  • Linux、OS X、Unix の場合
aws rds modify-db-instance \
    --db-instance-identifier <インスタンス名> \
    --backup-retention-period 0 \
    --apply-immediately
  • Windows の場合
aws rds modify-db-instance ^
    --db-instance-identifier <インスタンス名> ^
    --backup-retention-period 0 ^
    --apply-immediately

変更が有効になるタイミングを確認するには、バックアップ保持期間の値が 0 になり mydbinstance のステータスが available になるまで、DB インスタンスに対して describe-db-instances を呼び出します。

aws rds describe-db-instances --db-instance-identifier <インスタンス名>

AWS CLIでの自動バックアップの有効

自動バックアップをすぐに有効にするには、modify-db-instance コマンドを使用します。

(例)バックアップ保持期間を 3 日に設定することで、自動バックアップを有効にします。

  • Linux、OS X、Unix の場合
aws rds modify-db-instance \
    --db-instance-identifier <インスタンス名>  \
    --backup-retention-period 3 \
    --apply-immediately
  • Windows の場合:
aws rds modify-db-instance ^
    --db-instance-identifier <インスタンス名>  ^
    --backup-retention-period 3 ^
    --apply-immediately
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS] Amazon ECS(EC2タイプ)のちょっと長めのチュートリアル(第3回)

※本記事は Amazon ECS(EC2タイプ)のちょっと長めのチュートリアル(第1回) および (第2回) の続きです。

前回までで、ECSクラスターが作成できました。今回はサービスとタスクを作成して、Dockerコンテナが起動されることを確認します。

注:このチュートリアルでは前回同様に、ELB 1台 と、t3.microのEC2 2台 のAWS利用料が発生します。

サービス、タスクとは

毎回使っている簡単な方の図:
image.png

これから残りのサービスとタスクを作成していくわけですが、正確には"タスク定義"というものを先に作成するため、

  • タスク定義(Task Definition):1つ以上のコンテナ設定が記述されたJSONファイル、具体的には以下のような設定を記述します。
    • 実行するDocker(ECR)イメージ
    • Docker起動オプション(環境変数/マウントポイント/ログ設定/ヘルスチェックなど)
    • IAMロール
    • ネットワークモード
    • CPU/メモリの制限
  • サービス(Service):定義されたタスクを、ECSクラスターで動作させるための設定
    • クラスターと、起動させるタスクの数
    • デプロイ設定
    • インスタンス配置設定
    • VPC、ロードバランサ設定
  • タスク(Task):サービスによって、実際に実行されたコンテナ(群)。今回の例では1タスク=1 Dockerイメージで起動されます。

の順で作成していきます。ざっくり言うと、タスクの方がコンテナの起動設定(多くがDockerのパラメタで指定可能なもの)で、サービスの方がAWS環境の設定となります。

タスク定義は図で表現されることはほとんどありませんが、実際の設定の多くはここにあります。(慣れると気にならなくなりますが、サービスの方にもサービス定義があった方が分かりやすいんじゃないかと最初は思いました。)

image.png

難しい方の図。ようやく 第1回 で作成したコンテナの動作確認ができるようになります。

インスタンスを起動

まず、前回 EC2の課金が発生しないようインスタンス数を0にしておいたのを、戻します。
ECSクラスターの設定から「ECSインスタンス」タブ→「ECSインスタンスのスケール」で台数を2に設定しておきます。
100.png

4) タスク定義の作成

ECSのメニューから、「タスク定義」→「新しいタスク定義の作成」を選択します。

image.png

起動タイプでEC2を選択して次のステップに進みます。

image.png

「タスクとコンテナ定義の設定」で必要な情報を入力します。
- タスク定義名 : 任意の名前
- タスクロール : 前回作成した タスク用ロール を指定
- ネットワークモード : " <default> " を選択

image.png

次に、EC2インスタンスのストレージをコンテナにマウントする設定を追加します(テスト用コンテナの動作に必要ありませんが説明のため)。
画面の下(縦長の画面なので一番下までスクロールします)にある「ボリュームの追加」を選択します。

image.png

「ソースパス」がEC2インスタンスのパスになります。ここでは /tmp/ecsdemo を指定しています。
a001.png

「追加」を押すと元の設定画面に戻り、設定が追加されたことが確認できます。

image.png

次に、画面を少し上に戻って、コンテナ定義の項目で「コンテナの追加」ボタンを押します。

a002.png

必要な情報を入力します。
- コンテナ名 : 任意の名前
- イメージ : 第1回で作成したECRイメージのパス(AWSアカウント名からダグまで含むフルパスを設定します)
- メモリ制限 : 128
- ポートマッピング:ホストポート8080、コンテナポート80

image.png

入力画面を下に進み、環境変数を設定します(これもテスト用コンテナの動作に必要ありませんが説明のため)。ここでは TEST_ENV01 という変数を設定しています。ここの画面が少し分かりづらいですが、キーと値の間にある"Value"というプルダウンも明示的に選択する必要があるので注意して下さい。

image.png

最後に「ストレージとログ」で、マウントポイントを設定します。
こちらはコンテナ内のパスで、例として /mnt/host_dir というディレクトリを指定しています(事前に存在しなくても構いません)。

image.png

「追加」ボタンを押してタスク定義の作成画面に戻り、そこで「作成」を押してタスク定義の作成は完了となります。

image.png

5) サービスの作成

サービスはECSクラスターの「サービス」タブから「作成」を選択します。

image.png

サービスの設定画面で必要な情報を入力します。
- 起動タイプ : EC2
- タスク定義 : 4)で作成したタスク定義
- クラスター : 前回作成したクラスター
- サービス名 : 任意の名前
- タスクの数 : 2
その他の設定はデフォルトでOKです。

image.png

次に進み、ALBの設定を行います。
- ELBタイプ : "Application Load Balancer"を選択
- サービス用のIAMロールの選択 : "AWSServiceRoleForECS"(デフォルト)
- ELB名 : 前回作成したELB を選択
をそれぞれ選択します。続いて下にある「負荷分散コンテナ」で、先ほど作成したコンテナが表示されていることを確認して「ELBへの追加」を押します。

image.png

負荷分散コンテナの設定を入力します。
- リスナーポート : 8080:HTTP
- ターゲットグループ名 : 新規作成を選択し、任意の名前を入力
- ターゲットグループのプロトコル : HTTP
- パスパターン : /* (スラッシュとアスタリスク)を入力
- 評価値 : 1
- ヘルスチェックパス : /index.html

image.png

画面下に進み、サービスの検出オプションは外して次のステップに進みます。

a003.png

Auto Schaingオプションはデフォルトのまま
次のステップに進みます。

a004.png

最後に確認画面で「サービスの作成」を押します。

a005.png

サービスが作成されたら「サービスの表示」ボタンを押します。

a006.png

クラスターの画面に遷移し、(サービスではなく)「タスク」タブが表示されます。2つのインスタンスで、それぞれタスクが起動していれば作成は完了となります。

101.png

動作確認:ブラウザでの確認

ようやくこの構成ができたので、ブラウザでnginxのテストページにアクセスしてみます。

image.png

EC2 → ロードバランサから ELBのDNS名 を確認し、 http://<ELBのDNS名>:8080/index.html にアクセスします。

image.png

次に、テスト用に追加したhtmlが表示されることを確認します。 http://<ELBのDNS名>:8080/demo.html にアクセスします。

image.png

うまく接続できない場合は、第2回で作成した ELB用セキュリティグループ の設定をまず確認して下さい。マイIPに限定して作成してあったので、お使いのネットワーク環境によってはアクセス元をもう少し広く設定する必要があるかもしれません。

動作確認:EC2にログインして確認してみる

EC2タイプなので、ログインして確認することが可能です。前回作成した鍵を使って、ECSインスタンスにsshでログインして確認します。

※Dockerプロセスの確認※
# docker ps
CONTAINER ID    IMAGE                                                                       COMMAND                  CREATED             STATUS              PORTS                  NAMES
5aa208dd78ff    <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-demo01:latest   "nginx -g 'daemon of…"   40 minutes ago      Up 40 minutes       0.0.0.0:8080->80/tcp   ecs-va-ecs-task-definition01-1-nginx-demo01-faa6d1db8188b2ae4900
1aa15ba1a11f    amazon/amazon-ecs-agent:latest                                              "/agent"                 2 hours ago         Up 2 hours                                 ecs-agent

※テスト用のコンテナにログイン※
# docker exec -it 5aa208dd78ff /bin/bash

※環境変数が設定されていることの確認※
root@5aa208dd78ff:/# export | grep TEST_ENV01
declare -x TEST_ENV01="This_is_test"

※マウントポイントを確認するため、ファイルを作成※
root@5aa208dd78ff:/# ls -l /mnt/host_dir/
total 0

root@5aa208dd78ff:/# export > /mnt/host_dir/container_export.txt

root@5aa208dd78ff:/# ls -l /mnt/host_dir/container_export.txt
-rw-r--r-- 1 root root 599 Jul  2 07:51 /mnt/host_dir/container_export.txt

root@5aa208dd78ff:/# cat /mnt/host_dir/container_export.txt
declare -x AWS_CONTAINER_CREDENTIALS_RELATIVE_URI="/v2/credentials/221a631f-ecee-4ec0-8539-921f512c0efa"
declare -x AWS_EXECUTION_ENV="AWS_ECS_EC2"
declare -x ECS_CONTAINER_METADATA_URI="http://169.254.170.2/v3/5c8ca5e4-8043-4aad-ba1f-aca76518f101"
declare -x HOME="/root"
declare -x HOSTNAME="5aa208dd78ff"
declare -x NGINX_VERSION="1.13.1-1~stretch"
declare -x NJS_VERSION="1.13.1.0.1.10-1~stretch"
declare -x OLDPWD
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/"
declare -x SHLVL="1"
declare -x TERM="xterm"
declare -x TEST_ENV01="This_is_test"

※ホストOSに戻る※
root@5aa208dd78ff:/# exit
exit

※マウントポイントにコンテナで作成したファイルがあることを確認※
# ls -l /tmp/ecsdemo/
total 4
-rw-r--r-- 1 root root 599 Jul  2 07:51 container_export.txt

# cat /tmp/ecsdemo/container_export.txt
declare -x AWS_CONTAINER_CREDENTIALS_RELATIVE_URI="/v2/credentials/221a631f-ecee-4ec0-8539-921f512c0efa"
declare -x AWS_EXECUTION_ENV="AWS_ECS_EC2"
declare -x ECS_CONTAINER_METADATA_URI="http://169.254.170.2/v3/5c8ca5e4-8043-4aad-ba1f-aca76518f101"
declare -x HOME="/root"
declare -x HOSTNAME="5aa208dd78ff"
declare -x NGINX_VERSION="1.13.1-1~stretch"
declare -x NJS_VERSION="1.13.1.0.1.10-1~stretch"
declare -x OLDPWD
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/"
declare -x SHLVL="1"
declare -x TERM="xterm"
declare -x TEST_ENV01="This_is_test"

※イメージの確認※
# docker images
REPOSITORY                                                           TAG      IMAGE ID       CREATED       SIZE
<AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-demo01   latest   de6344632e81   2 weeks ago   109MB
amazon/amazon-ecs-agent                                              latest   267bac512a39   3 weeks ago   57.1MB
amazon/amazon-ecs-pause                                              0.1.0    54d8403124ce   3 weeks ago   954kB

スケールインしておく

以上で、一通りの構築作業は完了しました。次回以降コンテナの更新処理や異常系を試す予定ですが、それまではインスタンス費用がからないよう台数をまた0にしておきます。

これまでのようにインスタンス台数を減らすだけで課金は発生しませんが、それではタスクの再起動が繰り返されることになるので、まずタスクの数を0に設定します。サービスの設定で「更新」を選択します。

image.png

タスクの数を0に変更して更新します。

a008.png

その後、クラスターの設定からインスタンスを0に変更します。
102.png

Link

参考資料

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

【RDS for Oracle】自動統計情報収集(自動オプティマイザ統計収集)変更方法

  • Oracleでは3つの自動メンテナンス・タスクがあります

    • 自動統計情報収集(自動オプティマイザ統計収集)
    • 自動セグメント・アドバイザ
    • 自動SQLチューニング・アドバイザ
  • これらのタスクはメンテナンス・ウィンドウ(メンテナンス可能として設定した時間帯)で実行されます。

  • 自動化メンテナンス・タスクの有効/無効の確認

SQL> col CLIENT_NAME for a40
SQL> select client_name, status from dba_autotask_client;

CLIENT_NAME              STATUS
---------------------------------------- --------
auto optimizer stats collection      ENABLED
auto space advisor           ENABLED
sql tuning advisor           ENABLED

※注意
自動SQLチューニング・アドバイザを使用するにはEnterprise EditionのオプションのOracle Tuning Packオプションが必要です。このオプションがない場合は自動SQLチューニング・アドバイザを無効化する必要があります。

自動メンテナンス・タスク CLIENT_NAME
自動統計情報収集 auto optimizer stats collection
自動セグメント・アドバイザ auto space advisor
自動SQLチューニング・アドバイザ sql tuning advisor
  • 自動メンテナンス・タスクの無効化の方法

 - 例:自動SQLチューニング・アドバイザの無効

BEGIN
 dbms_auto_task_admin.disable(
 client_name => 'sql tuning advisor',
 operation => NULL,
 window_name => NULL);
END;
/
  • 自動SQLチューニング・アドバイザの確認
SQL> select client_name, status from dba_autotask_client;

CLIENT_NAME              STATUS
---------------------------------------- --------
auto optimizer stats collection      ENABLED
auto space advisor           ENABLED
sql tuning advisor           DISABLED  ★無効化されてる
  • 自動メンテナンス・タスクの有効化の方法

 - 例:自動SQLチューニング・アドバイザの有効

BEGIN
 dbms_auto_task_admin.enable(
 client_name => 'sql tuning advisor',
 operation => NULL,
 window_name => NULL);
END;
/
  • 自動SQLチューニング・アドバイザの確認
SQL> select client_name, status from dba_autotask_client;

CLIENT_NAME              STATUS
---------------------------------------- --------
auto optimizer stats collection      ENABLED
auto space advisor           ENABLED
sql tuning advisor           ENABLEDD  ★有効化に戻った
  • メンテナンス・ウィンドウのスケジュールを確認

  • デフォルトの実行時間

曜日 実行時間
月〜金 22:00〜翌日2:00(4時間)
土〜日 06:00〜翌日2:00(20時間)
SQL> col REPEAT_INTERVAL for a70
SQL> col DURATION for a30
SQL> select WINDOW_NAME ,REPEAT_INTERVAL ,DURATION, ENABLED from DBA_SCHEDULER_WINDOWS;

WINDOW_NAME      REPEAT_INTERVAL                                DURATION               ENABL
-------------------- ---------------------------------------------------------------------- ------------------------------ -----
WEEKEND_WINDOW       freq=daily;byday=SAT;byhour=0;byminute=0;bysecond=0            +002 00:00:00          FALSE
WEEKNIGHT_WINDOW     freq=daily;byday=MON,TUE,WED,THU,FRI;byhour=22;byminute=0; bysecond=0  +000 08:00:00          FALSE
SUNDAY_WINDOW        freq=daily;byday=SUN;byhour=6;byminute=0; bysecond=0           +000 20:00:00          TRUE
SATURDAY_WINDOW      freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0           +000 20:00:00          TRUE
FRIDAY_WINDOW        freq=daily;byday=FRI;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
THURSDAY_WINDOW      freq=daily;byday=THU;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
WEDNESDAY_WINDOW     freq=daily;byday=WED;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
TUESDAY_WINDOW       freq=daily;byday=TUE;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
MONDAY_WINDOW        freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE

9 rows selected.
  • メンテナンス・ウィンドウの実行予定時間を確認
SQL> set pages 100 line 200
SQL> col WINDOW_NAME for a20
SQL> col WINDOW_NEXT_TIME for a40
SQL> select WINDOW_NAME,to_char(WINDOW_NEXT_TIME,'yyyy/mm/dd hh24:mm;ss'),WINDOW_ACTIVE,AUTOTASK_STATUS,OPTIMIZER_STATS,SEGMENT_ADVISOR,SQL_TUNE_ADVISOR,HEALTH_MONITOR
 from DBA_AUTOTASK_WINDOW_CLIENTS order by 1;                                             

WINDOW_NAME      TO_CHAR(WINDOW_NEXT WINDO AUTOTASK OPTIMIZE SEGMENT_ SQL_TUNE HEALTH_M
-------------------- ------------------- ----- -------- -------- -------- -------- --------
FRIDAY_WINDOW        2019/07/12 22:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
MONDAY_WINDOW        2019/07/08 22:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
SATURDAY_WINDOW      2019/07/13 06:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
SUNDAY_WINDOW        2019/07/14 06:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
THURSDAY_WINDOW      2019/07/11 22:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
TUESDAY_WINDOW       2019/07/09 22:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED
WEDNESDAY_WINDOW     2019/07/10 22:07;00 FALSE ENABLED  ENABLED  ENABLED  ENABLED  DISABLED

7 rows selected.
  • メンテナンス・ウィンドウの開始時刻を変更

 - (例)日曜のメンテナンス・ウィンドウの開始時間を2:00に変更

BEGIN
    DBMS_SCHEDULER.SET_ATTRIBUTE(
         NAME      =>'SYS.SUNDAY_WINDOW'
        ,ATTRIBUTE =>'REPEAT_INTERVAL'
        ,VALUE     =>'freq=daily;byday=SUN;byhour=2;byminute=0; bysecond=0'
    );
END;
/
  • 実行時間を変更
    • (例)日曜のメンテナンス・ウィンドウの実行時間を6時間に変更
BEGIN
    DBMS_SCHEDULER.SET_ATTRIBUTE(
         NAME      =>'SYS.SUNDAY_WINDOW'
        ,ATTRIBUTE =>'DURATION'
        ,VALUE     => NUMTODSINTERVAL(6, 'HOUR')
    );
END;
/
  • 確認
SQL> select WINDOW_NAME ,REPEAT_INTERVAL ,DURATION, ENABLED from DBA_SCHEDULER_WINDOWS;

WINDOW_NAME      REPEAT_INTERVAL                                DURATION               ENABL
-------------------- ---------------------------------------------------------------------- ------------------------------ -----
WEEKEND_WINDOW       freq=daily;byday=SAT;byhour=0;byminute=0;bysecond=0            +002 00:00:00          FALSE
WEEKNIGHT_WINDOW     freq=daily;byday=MON,TUE,WED,THU,FRI;byhour=22;byminute=0; bysecond=0  +000 08:00:00          FALSE
SUNDAY_WINDOW        freq=daily;byday=SUN;byhour=2;byminute=0; bysecond=0           +000 06:00:00          TRUE  2:008:006時間)に変更
SATURDAY_WINDOW      freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0           +000 20:00:00          TRUE
FRIDAY_WINDOW        freq=daily;byday=FRI;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
THURSDAY_WINDOW      freq=daily;byday=THU;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
WEDNESDAY_WINDOW     freq=daily;byday=WED;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
TUESDAY_WINDOW       freq=daily;byday=TUE;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE
MONDAY_WINDOW        freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0          +000 04:00:00          TRUE

9 rows selected.

参考

DBMS_SCHEDULER ジョブの変更

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

Amazon SageMaker の推論パイプラインで、独自コンテナを組み合わせる方法

概要

Amazon SageMaker で、独自コンテナを含む 推論パイプラインエンドポイントを構築する 方法について書きます

SageMaker で提供されるコンテナで所望の前処理が実現できず、独自コンテナによる前処理をしたい場合、カスタム前処理コンテナはどのようにつくるか、どのように組み込むか 調べた際のメモです

組み込みアルゴリズムと推論パイプライン

Amazon SageMaker では組み込みアルゴリズムが提供されており、解きたい問題にフィットするものがあれば、MLアルゴリズムに関するコードを書かずに手軽にMLモデルを作れるようになっています

組み込みアルゴリズムに引き渡す入力形式はアルゴリズムごとに決まっており、基本的には、保有するデータを整形する前処理が必要です
こうした 前処理は学習/推論で同じ処理なため、共通化されていることが望ましい ということで、SageMaker では前処理アルゴリズムをコンテナ化して学習/推論時に実行させる仕組みが提供されています

前処理コンテナでは SageMaker でのコンテナの挙動を踏襲し、fit でインスタンス上に前処理コンテナを展開してデータ整形/S3バケットへ出力したり、deploy でインスタンス上に前処理コンテナを展開してリクエストデータ整形/次処理へ渡す、といった挙動をさせます
とくに推論処理では、エンドポイントへ届いたリクエストを前処理して後続の推論アルゴリズムへ渡すために、「前処理+推論処理(+後処理等)」をセットにした 推論パイプラインエンドポイント が構築できるようになっています

前処理で独自コンテナが必要になるケース

推論パイプラインのサンプル には、SageMaker で MLフレームワークを使うときと同様 sagemaker.sklearn.estimator.SKLearn など Estimator を利用して、任意のスクリプトを所定のコンテナ環境下で実行させる例が載っています
提供されているコンテナで所望の処理が実現できれば、カスタムコンテナは不要です

from sagemaker.sklearn.estimator import SKLearn

script_path = 'sklearn_abalone_featurizer.py'
sklearn_preprocessor = SKLearn(
    entry_point=script_path,
    role=role,
    train_instance_type="ml.c4.xlarge",
    sagemaker_session=sagemaker_session)

しかし提供されるコンテナにないライブラリや、インストールされていないミドルウェアを使いたい場合には、独自の環境をコンテナ化して実行させる必要があります

たとえば自然言語を扱う課題で MeCab で形態素解析したいといった場合には、MeCab が動作してかつ Amazon SageMaker の仕様に従ったコンテナが必要になります

前処理コンテナのつくりかた

Amazon SageMaker で提供されているコンテナ群は GitHub で公開されているので、ライブラリを追加する程度であれば、既存コンテナを拡張するのが楽かもしれません
用意されているコンテナを FROM 文で呼び出した上に、追加のインストールやセットアップを施します

そうした提供済みコンテナで都合が悪い(ミドルウェアのバージョンが合わないとか、既存の資産を使いたいとか)場合には独自コンテナを作ります

前処理コンテナの要件

基本的な挙動は SageMaker の 独自のトレーニングイメージ の仕様にあわせる必要があります
fitdeploy といった命令をうけて所定の処理が RUN される必要があるため、 scikit_bring_your_own などをベースに、枠組みそのままで中身の処理を書き換えるのがよさそうです
また、パイプラインエンドポイントのための独自の設定がいくつか必要です

  • 構成要素
    • train:トレーニング(fit)時に呼び出される処理
    • serve:エンドポイント構築(deploy)時に呼び出される処理
    • predictor.py:推論エンドポイントで呼び出される処理
  • その他の要件
    • Dockerfile に次のコードが必要
      • LABEL com.amazonaws.sagemaker.capabilities.accept-bind-to-port=true
    • エンドポイントのリクエスト受け付けポートが次の環境変数に依存する
      • SAGEMAKER_BIND_TO_PORT
    • Dockerコンテナリポジトリに、次のポリシー付与が必要

前処理コンテナに実装する内容

scikit_bring_your_own 等をベースに、下記の機能を実装します

train

生データを受け取って整形加工し、MLモデルトレーニングに使用する訓練データをつくり、S3へ配置します
訓練データ生成の際に作成される「辞書データ」や「ベクトライザ」は推論時にも同じものを使うため、推論エンドポイントへ引き継ぐ必要がありますが、SageMaker のアルゴリズムで /opt/ml/model 以下を共有する仕組みにのせて、推論エンドポイントの同一ディレクトリへ展開させるようにします

  • Estimator.fit で呼び出される
  • 指定されたタイプのインスタンスに、inputs={'raw':'s3://'} で引き渡した S3 パスのデータが /opt/ml/input/data/{channel} 以下へ 自動的に 展開される
  • /opt/ml/input/data/{channel} からデータを参照して一括整形する
  • MLトレーニングで使用する、加工整形した訓練データを S3 上の任意のパスへアップロードする
  • 推論エンドポイントでデータの整形処理に必要になるモデルデータ(辞書やベクトライザなど)を /opt/ml/model へ出力する
    • joblib.dump など
  • /opt/ml/model 以下のファイル群が tar.gz 形式でアーカイブされて output_path で指定された S3 パスへ 自動的に アップロードされる

serve

train 時に生成したモデルファイルをインスタンス上に配置し、 /invocations のリクエストを受け付ける web サーバを起動します

  • Estimator.deploy で呼び出される
  • output_path に指定された S3 パスから、Training 時に生成済みのモデル群をインスタンスの /opt/ml/model 以下へ 自動的に 展開する
  • /invocations/ping に応答するウェブサーバー を起動する
    • パイプラインモデルのエンドポイントは、リクエストを受け付けるポートを環境変数 SAGEMAKER_BIND_TO_PORT で指定されるため、web サーバの設定でポートを環境変数を参照するようにする(環境変数がなければ 8080 を使う)

predict

serve で起動したエンドポイントで、推論を行いたい生データを所定の書式で受け取り、後続の推論アルゴリズムが要求する書式に整形して返します

  • serve の際に配置したモデルファイル群を /opt/ml/model 以下から取得し、コード上へ読み出す
    • joblib.load など
  • 推論リクエストに含まれる生データを取得し、文字列の正規化を行う
  • 正規化したデータについて、先に取得したベクトライザ等を使用して推論アルゴリズムが要求する形式のデータへ変換する
  • 変換した形式のデータをレスポンスとして返す

コンテナのビルド、ECRへのPUSH

上記実装したコード群をコンテナ化し、ECR のリポジトリへ push します

push した ECR のリポジトリには、SageMaker のパイプラインモデルからの呼び出しを許可するポリシーを付与する必要があります(推論パイプラインの Amazon ECR アクセス許可のトラブルシューティング

ECR コンソールから Repositories > 該当リポジトリリンク > Permissions と進んで、アクセス許可ポリシーを下記 JSON の通り追加します


{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "allowSageMakerToPull",
            "Effect": "Allow",
            "Principal": {
                "Service": "sagemaker.amazonaws.com"
            },
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability"
            ]
        }
    ]
}

ここまででパイプライン用の前処理コンテナができました

MLプロセスへの組み込み

組み込みMLアルゴリズムと、独自コンテナによる前処理を組み合わせ、MLフローを実行します

前処理(訓練データの準備)

生データを用意してS3へアップロードし、以下のようなコードによって、訓練データへ変換します
&前処理で生成した ベクトライザ 等の生成物を S3 へ格納します

import sagemaker
sess = sagemaker.Session()
role = sagemaker.get_execution_role()

preprocess_image_name = 'preprocess_ecr_container_image'
preprocess_job_name = '{preprocess_id}'
rawdata_input_path = 's3_rawdata_path'
train_dataset_path = 's3_preprocessed_data_path'

preprocessor = sagemaker.estimator.Estimator(
            image_name           = preprocess_image_name,
            role                 = role,
            train_instance_count = preprocess_instance_count, 
            train_instance_type  = preprocess_instance_type,
            train_volume_size    = preprocess_volume_size,
            output_path          = train_dataset_path,
            sagemaker_session    = sess )
preprocessor.fit(
    inputs={'rawdata': rawdata_input_path},
    job_name = preprocess_job_name )

学習

前処理で訓練に必要なデータはすべて S3 上の所定の場所へアップロードされている状態です
下記のコードによって、組み込みアルゴリズムによるトレーニングを実行します

import sagemaker

algorithm_name = 'ml_hoge'
ml_container_name = get_image_uri(boto3.Session().region_name, algorithm_name)
ml_model_path = 's3_ml_model_path'
ml_training_job_name = '{algorithm_name}-{preprocess_id}-{timestamp}'

ml = sagemaker.estimator.Estimator(
    image_name           = ml_container_name,
    role                 = role, 
    sagemaker_session    = sess,
    train_instance_count = train_instance_count, 
    train_instance_type  = train_instance_type,
    output_path          = ml_model_path )
ml.fit(
    inputs={
        'train':      train_dataset_path_train, 
        'validation': train_dataset_path_valid, 
        'auxiliary':  train_dataset_path_aux, 
        'test':       train_dataset_path_test },
    job_name = ml_training_job_name )

エンドポイント構築

前処理で生成した ベクトライザ 等を利用するデータ前処理コンテナと、組み込みアルゴリズムの学習済モデルを参照する推論コンテナをセットにして、推論パイプラインエンドポイントを構築します
sagemaker.pipeline.PipelineModelmodels 変数へ渡した配列のモデル順に、リクエストに応答するエンドポイントが構築されます)

import sagemaker

preprocessor     = sagemaker.estimator.Estimator.attach(preprocess_job_name)
preprocess_model = preprocessor.create_model()
ml               = sagemaker.estimator.Estimator.attach(ml_training_job_name)
ml_model         = ml.create_model()

pipeline_endpoint_name = '{algorithm_name}-pipeline-endpoint-{version}'
pipeline_model = sagemaker.pipeline.PipelineModel(
    name   = pipeline_model_name,
    role   = role,
    models = [ preprocess_model, ml_model ] )
pipeline_model.deploy(
    initial_instance_count = pipeline_instance_count, 
    instance_type          = pipeline_instance_type, 
    endpoint_name          = pipeline_endpoint_name )

ここまでで、独自コンテナによる前処理を組み込んだ推論エンドポイントが起動します

推論リクエストへの応答

生データを所定の形式で引き渡せば、前処理コンテナで整形 -> 組み込みアルゴリズムの推論コンテナで推論 した結果が返ります

import sagemaker 
from sagemaker.content_types import CONTENT_TYPE_JSON

ml_predictor = sagemaker.predictor.RealTimePredictor(
            endpoint    = pipeline_endpoint_name,
            serializer  = sagemaker.predictor.json_serializer,
            deserializer= sagemaker.predictor.json_deserializer,
            content_type= CONTENT_TYPE_JSON,
            accept      = CONTENT_TYPE_JSON )

payload = {'target':'日本語文字列'}
pprint(ml_predictor.predict(payload))

トラブルシューティング

エンドポイント構築時に ping health check に失敗する

エンドポイント構築時に ping に失敗して構築できないというエラーが発生する場合

ValueError: Error hosting endpoint pipeline-endpoint-vXX: Failed Reason:  The container-1 for production variant AllTraffic did not pass the ping health check. Please check CloudWatch logs for this endpoint.

独自コンテナで起動させたWebサーバが、環境変数 SAGEMAKER_BIND_TO_PORT ポートでリクエストを受け付けていない可能性があります(CloudWatchLogs にも何の情報も出ず途方に暮れるのですが、落ち着いて)
Webサーバ( nginx など)の設定を見直し、SAGEMAKER_BIND_TO_PORT 環境変数が存在する場合には、環境変数の指定するポートで受け付けるようにします

ECR コンテナリポジトリに必要な権限が付与されていない

パイプラインエンドポイントを構築しようとする際、権限が足りないといったエラーが出る場合

ValueError: Error hosting endpoint pipeline-endpoint-vXX: Failed Reason:  The repository of your image 111111111.dkr.ecr.xxxxx.amazonaws.com/XXXXXXXXXXXXXXXXXXXX does not grant ecr:GetDownloadUrlForLayer, ecr:BatchGetImage, ecr:BatchCheckLayerAvailability permission to sagemaker.amazonaws.com service principal.

推論パイプラインエンドポイントを構築する際には、パイプラインに組み込む独自コンテナのリポジトリに SageMaker からの読み出しを許可するポリシー付与が必要です
推論パイプラインの Amazon ECR アクセス許可 を参考に、ECR コンソールから必要なポリシーを付与します

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "allowSageMakerToPull",
            "Effect": "Allow",
            "Principal": {
                "Service": "sagemaker.amazonaws.com"
            },
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability"
            ]
        }
    ]
}

その他

推論パイプラインのトラブルシューティング 参照

まとめ

独自コンテナを含む推論パイプラインエンドポイントを構築する際、次の点に注意してください

日本語処理など独自コンテナを必要とするケースで、ぜひ参考にしていただければと思います

参考資料

パイプラインのコンテナは、(8080 ではなく) SAGEMAKER_BIND_TO_PORT 環境変数で指定されたポートでリッスンします。

コンテナがこの要件に準拠していることを示すには、次のコマンドを使用して Dockerfile にラベルを追加します
LABEL com.amazonaws.sagemaker.capabilities.accept-bind-to-port=true

Amazon SageMaker 組み込みアルゴリズムを含むパイプラインでカスタム Docker イメージを使用する場合は、Amazon ECR ポリシーが必要です。ポリシーは、イメージをプルするために Amazon SageMaker のアクセス許可を Amazon ECR レポジトリに与えます。

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

Packer, Ansible, Terraformで作るBlue-Greenデプロイメント (AWS)

概要

AWS環境で、デプロイメントのフローを自動化する仕事を任されたので、成果物をここにメモしておきます。

デプロイをするシステムは、以下のように構成されています。
122905.png

Blue/Greenの2環境を用意し、普段は片方のみを使用しています。使用していないほうの環境にデプロイをして、ELBの振り分け先を変更することで、ダウンタイムのないデプロイメントを目指します。また、packerやAnsible, Terraformなどのツールを使うことで、このフローを自動化していきます。

尚、こちらの記事こちらの記事などを参考にして作成しました。

作業手順

  1. アプリケーションや必要なミドルウェアなどをインストールした、ゴールデンAMIを作成する
  2. ゴールデンAMIを参照する起動設定を新規作成し、デプロイ先のAutoScaling Groupにそれを読み込ませる
  3. Elastic Load BalancerのListenerが転送するTargetGroupを変更し、本番環境をBlueからGreenに(もしくはGreenからBlueに)切り替える

AMIの作成

PackerとAnsibleを使って、ゴールデンAMIを作成します。AMIの基本的な設定はPackerで、ツールのインストール等の構成管理はAnsibleで、それぞれ行います。

まずは、Packerの設定です。

packer.json
{
    "variables":{
        "version":"" //デプロイのたびに変わる値は、変数としておきます
    },                                  
    "builders":      // AMIを作成するために一時的に立てられるEC2インスタンスについて定義                               
    [                                       
        {                                       
            "type":"amazon-ebs",                                        
            "ami_name": "test-AMI-v{{user `version`}}",
            "region": "ap-northeast-1",                                     
            "source_ami": "ami-xxxxxxxxxxx",  // 基とするAMI
            "instance_type": "t2.micro",
            "ssh_username": "centos",
            "tags":{
                "version":"{{user `version`}}" // 起動設定を作成するときに、見つけやすいようにタグをつけておく
            },
            "force_deregister": true,
            "force_delete_snapshot": true,
            "security_group_ids":["sg-xxxxxxx","sg-xxxxxxx"],
            "iam_instance_profile": "xxxxxxx",
            "subnet_id":"subnet-xxxxxxxx"
        }
    ],
    "provisioners":   // AMI作成用のインスタンスの中で行いたい処理を定義
    [
        {
            "type":"shell",
            "inline": [  // Ansibleを動かすためのツールをインストール
                "sudo -E yum -y update",
                "sudo -E yum -y install epel-release",
                "sudo -E yum -y install python-pip",
                "sudo -E yum -y install ansible"
            ]
        },
        {
            "type":"ansible-local",   // Ansibleの起動
            "playbook_file":"setup.yml"
        }
    ]
}

上のファイルの中から呼ばれている、Ansibleの設定ファイル(playbook)の設定は、以下の通りです。
尚、以下ではS3上のmavenリポジトリから成果物をとってくるようにしてあります。

setup.yml
- hosts: all
  become: yes
  vars:
    proxy_env: # すべてに共通するプロキシ設定は、変数化しておく
      https_proxy: xxxxxxxxx
      http_PROXY: xxxxxxxxx
      HTTPS_PROXY: xxxxxxxxx
      HTTP_PROXY: xxxxxxxxx
      no_proxy: xxxxxxxxx
  tasks:
    - environment: "{{proxy_env}}"
      name: "install boto3 (required by aws-s3)" # boto3のインストール
      pip:
        name: boto3
    - pip:
        name: lxml
      name: "install lxml" # Ansibleのs3モジュールを動かすのに必要
      environment: "{{proxy_env}}"
    - maven_artifact: # S3上のmavenリポジトリから成果物を取得
        repository_url: 's3://xxxxxxxx'
        group_id: jp.co.xx.xx
        artifact_id: xxxxxx
        version: 1.0.0
        extension: tgz
        dest: /tmp/xxxxxx.tgz

packerコマンドで上記のpackerおよびAnsibleを実行すると、AMIが作成されるはずです

ASGの設定変更

Terraformを用いて、まず上で作成したAMIを読み込む起動設定を作成し、そしてデプロイ先のASG(使用中でない環境のASG)にその起動設定を読み込ませるようにします。

deploy.tf
provider "aws" {
  region = "ap-northeast-1"
}

variable "target_version" {} // デプロイのたびに値が変わるものは、変数化しておく

data "aws_ami" "centos" { // packerとAnsibleで作成したAMIを取得
  most_recent = true
  owners      = ["xxx"]
  filter {  // タグで検索
    name = "version"
    values = ["${var.target_version}"]
  }
}

resource "aws_launch_configuration" "test" { // 起動設定を新規作成
  name                 = "config-test-v${var.target_version}"
  image_id             = "${data.aws_ami.centos.id}" // 作成したAMIを指定
  instance_type        = "t2.micro"
  enable_monitoring    = false
  security_groups      = ["sg-xxxxxxx", "sg-xxxxxxxxx","sg-xxxxxxx"]
  key_name             = "xxxxxxxxx"
  iam_instance_profile = "xxxxxxxxx"
  root_block_device {
    delete_on_termination = true
    iops = 100
    volume_size = 30
    volume_type = "gp2"
  }
}

resource "aws_autoscaling_group" "test_resource" {  // デプロイ先のASGが上の起動設定を読みこむよう、設定変更
  name = "asg-test"
  launch_configuration = "${aws_launch_configuration.test.name}"  // 上で作成した起動設定を指定
  min_size = 2
  max_size = 2
  desired_capacity = 2
  target_group_arns = ["xxxxx","xxxxxx"]
  vpc_zone_identifier = ["xxxxxxx","xxxxxxxx"]
  lifecycle {
    create_before_destroy = true
  }
}

terraformコマンドで上の設定を反映させると、使用中でない環境のASGに設定が反映されます。

Blue/Greenの切り替え

最後に、ELBの振り分けの設定を変えます。適当なスクリプト言語でシェルを書くのですが、今回はCentOSにデフォルトで入っているPythonの2系で書きます(今時Python2かよ、って感じですが)。

処理の手順は、以下の通りです
1. ASGに紐づくEC2のインスタンスのリストを取得
2. EC2がすべて立ち上がって、テストをパスしたか確認する
3. EC2がすべて立ち上がっていれば、ELBのListenerのリクエストの転送先のTargetGroupを変更し、Blue/Greenを切り替える

以下がコードです。

switch_elb.sh
#! /bin/python
# -*- coding: utf-8 -*-

from time import sleep
import json
import subprocess

# ASGに紐づくEC2インスタンスのリストの取得
GET_INSTANCE_LIST = """aws ec2 describe-instances --filters "Name=tag:version,Values=%s"""
# EC2インスタンスがヘルシーな状態であるか確認
GET_INSTANCE_STATUS = "aws ec2 describe-instance-status --instance-id %s"
# ELBの切り替え
SWITCH_ELB_LISTENER = "aws elbv2 modify-rule --rule-arn %s --actions Type=forward,TargetGroupArn=%s"

# EC2のリストを取得し、ヘルスチェック
def get_ec2_health(version):
    instance_ids = []
    # インスタンスのリスト取得
    instance_list = subprocess.check_output(GET_INSTANCE_LIST % version,shell=True)
    instance_dict = json.loads(instance_list)
    for instance in instance_dict["Reservations"]:
        # 停止中のインスタンスは除く
        if instance["Instances"][0]["State"]["Code"] != 48:
            instance_ids.append(instance["Instances"][0]["InstanceId"]) 
    for i in range(30):
        # 一つずつインスタンスをヘルスチェックし、一つでもダメであれば、15秒後に再チェック
        result = check_ec2_status(instance_ids)
        if result == True:
            return True
        else:
            sleep(15)
    return False

# EC2のヘルスチェック
def check_ec2_status(instance_ids):
    for instance_id in instance_ids:
        result = commands.getoutput(GET_INSTANCE_STATUS % instance_id)
        status_dict = json.loads(result) 
        # 一つでもヘルスチェックに失敗すれば、Falseを返す
        if status_dict["InstanceStatuses"][0]["SystemStatus"]["Details"][0]["Status"] != "passed" or status_dict["InstanceStatuses"][0]["InstanceStatus"]["Details"][0]["Status"] != "passed":
            return False
    return True

# ELBの切り替え
def switch_elb():
    ELB_rules = {}
    try:
        # ELBのListenerのRuleのarnをjson形式で羅列しておくファイル
        file = open("../../ELB_rules_arns.json","r")
        ELB_rules = json.load(file)
    except:
        print("ELBのルールの情報が読み込めませんでした")
        return
    target_group_arns = {}
    try:
        # デプロイ先の環境の、TargetGroupのarnをjson形式で羅列しておくファイル
        file = open("./target_group_arns.json","r")
        target_group_arns = json.load(file)
    except:
        print("ターゲットグループの情報が読み込めませんでした")
        return
    for key in ELB_rules:
        result = subprocess.call(SWITCH_ELB_LISTENER % (ELB_rules[key],target_group_arns[key]),shell=True)
        if result == 0:
            print("切り替え成功")
    return 0

# メインの関数
def main():
    version = input("デプロイのバージョンを指定してください")
    ec2_health = get_ec2_health(version)
    if ec2_health == True:
        switch_elb_status = switch_elb()
        if switch_elb_status != 0:
            print("ELBの切り替えがうまくいきませんでした")
        else:
            print("ELBの切り替えに成功しました")
    else:
        print("EC2のヘルスチェックが失敗したため、切り替えを行いません")

if __name__ == "__main__":
    main()

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

AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法

AWS CloudFormationのLambda-backedカスタムリソースを利用するとAWS CloudFormationで管理できないリソースも管理することができますが、Lambda-backedで作成したリソースの更新・削除するのにリソースのIDをどうやって取り回そうか悩みました。

下記は解決策の1案となりますが、他に良い方法があれば教えてほしいです!

前提

  • AWSアカウントがある
  • AWS CLIが利用できる
  • AWS Lambda、CloudFormationの作成権限がある

CloudFormationのテンプレート作成

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> touch cfn-template.yaml

Lambda-backedカスタムリソースを利用して何かしらのリソースを作成・更新・削除するテンプレートとなります。
ポイントとしてはCreateResource のひとつで完結できたら良かったのですが、CreateResource で作成したリソースのIDを自前で取り回せなかったので、更新と削除を別リソースUpdateResource で行うようにしました。うーん、めんどうです

cfn-template.yaml
Resources:
  CreateResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt CreateResourceFunction.Arn

  UpdateResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt UpdateResourceFunction.Arn
      ResourceId: !GetAtt CreateResource.Id

  CreateResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            if event['RequestType'] == 'Create':
              # なんかリソース作成
              response = {'Id': 'hoge'}
              print('create resources ' + response['Id'])
              cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
              return

            # 他のRequestTypeは無視
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7

  UpdateResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            Id = event['ResourceProperties']['ResourceId']
            if event['RequestType'] == 'Update':
              # なんかリソース更新
              print('update resources ' + Id)
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              return

            if event['RequestType'] == 'Delete':
              # なんかリソース削除
              print('delete resources ' + Id)
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              return

            # 他のRequestTypeは無視
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7

  FunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: root
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: "arn:aws:logs:*:*:*"

動作確認

スタック作成

> aws cloudformation create-stack \
  --stack-name cfn-lambda-backed-test \
  --template-body file://cfn-template.yaml \
  --capabilities CAPABILITY_IAM

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268"
}

スタック作成して各リソースが作成できたらLambda関数のログを確認します。
各リソースと関数のPhysicalResourceId をパラメータにaws logs get-log-events コマンドで取得します。

> aws cloudformation list-stack-resources \
  --stack-name cfn-lambda-backed-test

{
    "StackResourceSummaries": [
        {
            "LogicalResourceId": "CreateResource",
            "PhysicalResourceId": "2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-25T01:33:55.358Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "CreateResourceFunction",
            "PhysicalResourceId": "cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-25T01:33:49.819Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "FunctionExecutionRole",
            "PhysicalResourceId": "cfn-lambda-backed-test-FunctionExecutionRole-17PMYTJ3NS41Z",
            "ResourceType": "AWS::IAM::Role",
            "LastUpdatedTimestamp": "2019-06-25T01:33:46.370Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UpdateResource",
            "PhysicalResourceId": "2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-25T01:34:01.068Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UpdateResourceFunction",
            "PhysicalResourceId": "cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-25T01:33:49.516Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --log-stream-name '2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592' \
  --output=text \
  --query "events[*].message"

START RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7 Version: $LATEST
        create resources hoge
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592", "PhysicalResourceId": "2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "1cb162f1-979f-4e6f-b277-dd7e729d23cc", "LogicalResourceId": "CreateResource", "NoEcho": false, "Data": {"Id": "hoge"}}
        Status code: OK
        END RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7
        REPORT RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7  Duration: 292.74 ms     Billed Duration: 300 ms         Memory Size: 128 MB   Max Memory Used: 29 MB


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --log-stream-name '2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268' \
  --output=text \
  --query "events[*].message"

START RequestId: d593f33a-9231-4284-8157-97b5b799f70e Version: $LATEST
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268", "PhysicalResourceId": "2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "fe1b2ba8-0aff-44a6-ac1f-f46863594b0f", "LogicalResourceId": "UpdateResource", "NoEcho": false, "Data": {}}
        Status code: OK
        END RequestId: d593f33a-9231-4284-8157-97b5b799f70e
        REPORT RequestId: d593f33a-9231-4284-8157-97b5b799f70e  Duration: 242.26 ms     Billed Duration: 300 ms         Memory Size: 128 MB   Max Memory Used: 29 MB

スタック作成時にはCreateResource でリソースの作成、UpdateResource は呼び出しのみとなることが確認できました。

スタック削除

スタックを削除して動作を確認します。

> aws cloudformation delete-stack \
  --stack-name cfn-lambda-backed-test

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268"
}

スタック削除すると当然のことながらリソースが取得できなくなるので、ログストリーム名を取得してからログを確認します。

> aws logs describe-log-streams \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --output=text \
  --query "logStreams[*].logStreamName"

2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592    2019/06/25/[$LATEST]8d6fac4288674ecbb7b6f7a577b8b932


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --log-stream-name '2019/06/25/[$LATEST]8d6fac4288674ecbb7b6f7a577b8b932' \
  --output=text \
  --query "events[*].message"

START RequestId: 8a0c34a7-8c3a-47b5-9128-3a49748aca52 Version: $LATEST



> aws logs describe-log-streams \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --output=text \
  --query "logStreams[*].logStreamName"

2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511    2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --log-stream-name '2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511' \
  --output=text \
  --query "events[*].message"

START RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2 Version: $LATEST
        delete resources hoge
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511", "PhysicalResourceId": "2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "5d390a57-55b6-4ddd-8820-0104c94ab705", "LogicalResourceId": "UpdateResource", "NoEcho": false, "Data": {}}
        Status code: OK
        END RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2
        REPORT RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2  Duration: 644.07 ms     Billed Duration: 700 ms         Memory Size: 128 MB   Max Memory Used: 56 MB

スタック削除時にはCreateResource は呼び出しのみ、UpdateResource でリソースの削除がされることが確認できました。

まとめ

若干定義が面倒になりますがAWS CloudFormationのLambda-backedカスタムリソースを利用してリソースを更新・削除できることが確認できました。

参考

Blue21: lambdaのログを aws-cli で見る
https://blue21neo.blogspot.com/2018/02/lambda-aws-cli.html

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

ゼロから始めるAWS Elastic Beanstalk #2~ 独自ドメイン対応、HTTPS対応、HTTP→HTTPSへのリダイレクト対応、Auto Scaling設定 ~

概要

image.png

本編

ALB(Application Load Balancer)をHTTPSに対応する

Elastic BeanstalkでつくったWebアプリをHTTPSに対応させるため、拡張設定用のYAMLファイルを作る。
プロジェクトのルートに .ebextensionsというディレクトリをつくり、そこにalb-secure-listener.configというファイルを作成する。

elastic-beantalk-java-app
├── .ebextensions
│   └── alb-secure-listener.config
・
・ その他のファイル達
・

alb-secure-listener.configは以下のようにする

alb-secure-listener.config
option_settings:
  aws:elbv2:listener:default:
    ListenerEnabled: 'false' 
  aws:elbv2:listener:443:
    DefaultProcess: https
    ListenerEnabled: 'true'
    Protocol: HTTPS
    SSLCertificateArns: arn:aws:acm:ap-northeast-1:123456789:certificate/abcdef-01234-6123-caca-123456789b
  aws:elasticbeanstalk:environment:process:https:
    Port: '80'
    Protocol: HTTP  



以下、設定内容の説明をしていく。

設定その1:「HTTPは受け付けません」

  aws:elbv2:listener:default:
    ListenerEnabled: 'false' 

aws:elbv2:listener:defaultはデフォルトのリスナー(ポート80)の設定を意味する。
ここでListenerEnabled: 'false'としていったんポート80、つまり、HTTPは利用不可とした。つまりHTTPでのアクセスを受け付けない設定にした。

(「こういう設定もアリだよね」という例のためで、後で変更してHTTP(ポート80)でアクセス来たらHTTPS(ポート443)にリダイレクトするように構成する。)

設定その2:「HTTPSを受け付けます、証明書はこれです。」

  aws:elbv2:listener:443:
    DefaultProcess: https
    ListenerEnabled: 'true'
    Protocol: HTTPS
    SSLCertificateArns: arn:aws:acm:ap-northeast-1:123456789:certificate/abcdef-01234-6123-caca-123456789b

aws:elbv2:listener:443はポート443の設定。
ここでは、HTTPSでつかうポート443についての設定で、HTTPSプロトコルで使うことを指定している。
SSLCertificateArnsにはACM(Certificate Mangaer)で取得した証明書のARNを指定する

image.png

設定その3:「ロードバランサーにHTTPSでアクセス来たら、ロードバランサーはEC2のポート80番にHTTPでつなぎます」

aws:elbv2:listener:443:以下にあったDefaultProcess: httpsの部分だが、
このhttpsというのは、以下の設定にあるaws:elasticbeanstalk:environment:process:https:httpsに対応している。つまり単なる名前。httpsじゃなくてもOK。

  aws:elasticbeanstalk:environment:process:https:
    Port: '80'
    Protocol: HTTP  

これで、ロードバランサーにHTTPSでアクセスがきたら、その先にあるEC2の80番ポートにつなぎに行くという設定ができた。
つまり、以下の赤マルの部分の設定ができた。

image.png

Auto Scalingを構成する

  aws:autoscaling:asg:
    Availability Zones: Any
    MaxSize: '1'
    MinSize: '1'    

っこではaws:autoscaling:asgでAuto Scalingグループで使用するインスタンスの最小数と最大数を、それぞれ1に設定した。つまりこれはスケールしない設定。

最大数を2以上に設定した場合Availability Zones: Anyになってるときは、使用可能なAvailability Zone間で均等にインスタンスを起動するようになる。

HTTPSのみ有効で、AutoScaleしないalb-secure-listener.configの内容

ここまでの内容まとめると
alb-secure-listener.configは以下のようにする

ファイルの置き場所
elastic-beantalk-java-app
├── .ebextensions
│   └── alb-secure-listener.config
・
・ その他のファイル達
・

HTTPSのみ有効で、AutoScaleしないalb-secure-listener.configの内容

alb-secure-listener.config
option_settings:
  aws:elbv2:listener:default:
    ListenerEnabled: 'false' 
  aws:elbv2:listener:443:
    DefaultProcess: https
    ListenerEnabled: 'true'
    Protocol: HTTPS
    SSLCertificateArns: arn:aws:acm:ap-northeast-1:456509554684:certificate/fa982b4b-01f3-4efe-9db6-782e1144a88b
  aws:elasticbeanstalk:environment:process:https:
    Port: '80'
    Protocol: HTTP
  aws:autoscaling:asg:
    Availability Zones: Any
    MaxSize: '1'
    MinSize: '1'     

アプリをデプロイする

さて、ここまで設定したところでアプリをデプロイする

eb create my-jetty-app-test2 --instance_type t2.small --elb-type application --vpc.id vpc-xxxxxxxxxxxxxxxxx --vpc.elbpublic --vpc.elbsubnets subnet-xxxxxxxxxxxxxxxxx,subnet-yyyyyyyyyyyyyyyyy --vpc.publicip --vpc.ec2subnets subnet-xxxxxxxxxxxxxxxxx,subnet-yyyyyyyyyyyyyyyyy

独自ドメイン対応

アプリのデプロイが終わったら、Elastic Beanstalkを独自ドメインに対応する

Webコンソールから Route 53 に行き、対象ドメインを選択してレコードセットの作成をクリック

image.png

エイリアス を選択し、エイリアス先から Elastic Beanstalk環境グループのなかから、このドメインを割り当てたい環境を選択し作成をクリックすればOK

image.png

これで、ドメインがElastic Beanstalkに作った環境にひもづいた。

ドメイン名は仮にexample.comとすると

https://example.com

で無事アプリが公開できた。
ここまでで独自ドメイン+HTTPS化が終了。

HTTPアクセスのHTTPSへのリダイレクト対応

ALB(Application Loadbalancer)の機能アップにより、ALBの設定だけでHTTP→HTTPSへのダイレクトができるようになっている。

さきほどは、alb-secure-listener.configの設定で以下のようにHTTPを無効にしてしまったが、これを有効にする

alb-secure-listener.config(HTTP無効)
option_settings:
  aws:elbv2:listener:default:
    ListenerEnabled: 'false' 

alb-secure-listener.config(HTTP有効)
option_settings:
  aws:elbv2:listener:default:
    ListenerEnabled: 'true' 

既にデプロイしているなら、Webコンソールから有効にしてもOK

Webコンソールから有効にするには、Elastic Beanstalkにアクセスし、

https://ap-northeast-1.console.aws.amazon.com/elasticbeanstalk

ElasticBeanstalk>変更したい環境>設定>ロードバランサーとメニューを選択して、変更をクリック

image.png

ロードバランサーの変更画面で、無効になっているポート80を有効にして、適用をクリックすればOK

image.png

5分程度まつと、構成の更新が終了する。

ポート80のリスナーが有効になったらロードバランサーのリダイレクト設定をする

https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LoadBalancers:

Elastic Beanstalkが自動生成したロードバランサーを選択し、リスナータブを選択する。

HTTP:80のリスナーのルールを表示をクリックする

image.png

ルール設定画面でimage.pngボタンをクリックする

image.png

次に、image.pngをクリック

image.png

IFTHENの条件式を設定できるので、
- IFにはパスが・・・を選択し、値として *(アスタリスク=ワイルドカード) を入力
- THENには、アクションの追加で リダイレクト先 を選択する

image.png

リダイレクト先として HTTPSを選択し、ポート番号として443と入力する

image.png

できたら、保存をクリック。

これで、HTTPにアクセスが来たらHTTPSにリダイレクトする設定は完了。

実際に、

http://example.com にアクセスしてみると、ちゃんと https://example.com にリダイレクトされる。

まとめ

説明した構成
image.png

本稿で使用したソースコード
https://github.com/riversun/java-jetty-app-on-elasticbeanstalk/tree/https_conf_with_ebextensions_dir

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

ゼロから始めるJavaでAWS Elastic Beanstalk #1~ EB CLIを使ったJava Webアプリ環境構築 ~

概要

  • AWS Elastic Beanstalkを使えば、EC2でのOSやミドルウェアのセットアップ不要でコマンド1つでWebアプリ実行環境を構築することができます。セキュリティパッチ等も自動適用できるため運用が軽くなるメリットもある上、ベースはEC2等AWSのコンポーネントの組み合わせなので、その気になれば色々カスタマイズもできるという、とても使い勝手の良いサービスです。
  • 本稿ではEB CLIというツールをつかってコマンド1つでJava Web アプリの実行環境を構築します。
  • Java Webアプリは、Tomcatで動作するWARではなく、Java SEでつくったスッピンのWebアプリ(JAR)が対象です。
    (つまり概念さえ理解できればWebアプリFrameworkはSpring BootでもPlay frameworkでもかまいませんし、アプリサーバーもTomcatでもJettyでもGlassfishでもOKです)
  • また、続編で独自ドメイン対応、HTTPS対応、HTTP→HTTPSへのリダイレクト対応、Auto Scaling設定をします。

環境・構成

以下のような構成をコマンドラインから作ります
(コマンドラインから作るので環境の量産、再構築もカンタン)

image.png

  • アプリ・プラットフォーム
    • AWS Elastic Beanstalkの Java 8 プラットフォーム
    • 本稿ではJSP/ServletのWebアプリをJetty9で実行
  • クラウド環境
    • サービス:AWS Elastic Beanstalk (EC2 + Application Loadbalancer on VPC)
    • リージョン:東京(ap-northeast-1) →東京じゃなくてもOK

ソースコード

本稿で紹介する全ソースコードはこちらにあります
https://github.com/riversun/java-jetty-app-on-elasticbeanstalk

本編

Elastic Beanstalk コマンドラインインターフェイス(EB CLI)のインストール

Elastic BeanstalkアプリはWeb GUIをつかっても構築できるが、環境構築の自動化など本番運用を考えるとコマンドラインを使うと同じような操作を何度もせずにすみ手間もへるし、アプリ・環境の構築・変更がコマンド一発で済のでCIに組み込むなどすれば安定した運用が可能となる。

1.EB CLIをインストールする

EB CLIのインストールにはPythonが必要となる。
もし、PCにPythonがインストールされていなければ、
https://www.anaconda.com/distribution/
などからインストールしておく。最新版をインストールしておけばOK

Pythonがインストールが完了していれば、以下のコマンドで EB CLI をインストールできる

pip install awsebcli --upgrade --user

2.以下のコマンドでインストール終了チェック

EB CLIがインストールされ、コマンドラインで利用可能になっているかどうか、以下のコマンドで確認する

eb --version

EB CLI 3.15.2 (Python 3.7.1)

ちゃんとインストールできた模様

TIPS
Windows環境の場合、eb コマンドがみつからない場合がある。
そのときは、

C:\Users\[ユーザー名]\AppData\Roaming\Python\Python37\Scripts

をパスに追加する

JavaでWebアプリを作る

Elastic Beanstalkで作るJavaアプリは FAT JAR形式で作る

Elastic BeanstalkでJava Webアプリを実行するにはいくつかの方法がある。

  • warファイルをアップロード(Tomcatで実行する)
  • JARファイルをアップロード(Java SE環境で実行する)

本稿では JARファイルをアップロード する方式を採用する。

JARファイルとは、Javaで作ったアプリ(Webアプリ)のソースコード+依存ファイルを1つのJARにまとめたモノの事を言う。全部1つにしてサイズの大きなJARを作るので「FAT JAR」とも言う。

JARを使うメリットは、作ったWebアプリをJARファイルにさえできれば、何でもOKということ。

FrameworkはPlay Frameworkでも、Spring Bootでも、Strutsでも、JSFでも良いし(もちろん素のJSP/ServletでもOK)、APサーバーもTomcatでもJettyでもGlassfishでもUndertow(Wildfly)でもOK。

ただしJARをつくってElastic Beanstalkで動かすには、「Elastic Beanstalkのお作法」があるので、そちらをみていく。

といっても、難しくはない。

Elastic Beanstalk用のJavaソースコード構成

Web APサーバーとしてJettyを使ったJava Webアプリを考える。

まず、全体像は以下のとおり。「★」印がついているところが Elastic Beanstalkのための構成
その他はJavaのMavenプロジェクトの標準的なソースコード構成となる

ElasticBeanstalk用Javaソースコード構成
elastic-beantalk-java-app
├── src/main/java ・・・Javaのソースコードのルートディレクトリ
│   └── myserver  
│       └── StartServer.java
├── src/main/resources
│   └── webroot  ・・・静的WebコンテンツやJSPのルートディレクトリ
│       ├── index.html
│       └── show.jsp 
├── src/main/assembly
│   └── zip.xml ・・・★elastic beanstalkにアップロードするZIPファイルの生成ルールを記述
├── .elasticbeanstalk ・・・★Elastic Beanstalkへのデプロイ情報を格納するディレクトリ
│   └── config.yml  ・・・★Elastic Beanstalkへのデプロイ情報のYAMLファイル
├── target ・・・ビルドした結果(jarなど)が生成されるディレクトリ
├── Procfile ・・・★Elastic BeanstalkのEC2上でJARファイルを実行するためのスクリプトを記述する
└── pom.xml ・・・Maven用設定ファイル

ソースコードや設定ファイル等の中身については後で説明するとして、
先にこのソースコードがどのようにビルドされて Elastic Beanstalkにアップロードされるかをみておく。

Elastic BeanstalkにアップロードするZIPファイルの構造

本稿では、手元のPCでソースコードをビルドして、それを1つのJARファイルにパッケージし、さらにJARファイル以外にもElastic Beanstalkに必要なファイル含めてZIPファイルを生成する。

ここで、JavaのソースコードをビルドしてできたFAT JARのファイル名をeb-app-jar-with-dependencies.jarとする。(maven-assembly-pluginで生成する、後で説明)
ElasticBeanstalkにアップロードするためのZIPファイル名をmy-jetty-app-eb.zipとすると、そのZIPファイルの中身構造は以下となる。

my-jetty-app-eb.zipの中身
my-jetty-app-eb.zip
├── my-jetty-app-jar-with-dependencies.jar ・・・Javaのソースコードを1つのJARにパッケージしたもの
└── Procfile ・・・Elastic BeanstalkのEC2内でJARを実行するためのスクリプト

ZIPファイルの中には、ソースをまとめたJARファイルと、実行スクリプトの書いてあるファイルであるProcfileの2つとなる。
ということで、この形式のZIPファイルをEB CLI経由でElastic Beanstalkにアップロードすればデプロイできる。
これが「Elastic Beanstalkのお作法」の1つということになる。

次は、Javaアプリのソースをみていく。
とくに「Elastic Beanstalkのお作法」に関連する部分を中心に説明する

Javaアプリのソースコードの作り方

これからJavaアプリを動かそうとしているElastic Beanstalk環境は以下のようなもので、Java Webアプリとしてケアしておくべき点はポート番号 5000をListenするところ

image.png

つまり、Javaでポート番号 5000をListenするサーバープログラムを作ってあげればひとまずOK

サンプルコード
ということでサンプルコードを以下のリポジトリに置いた
https://github.com/riversun/java-jetty-app-on-elasticbeanstalk

このサンプルはServlet/JSP/プッシュ通知機能をJetty上動作させている

このコードをクローンすると

clone https://github.com/riversun/java-jetty-app-on-elasticbeanstalk.git

前述したとおり以下のようなソースコード構成となっている。
(一部ファイルは省略)

ElasticBeanstalk用Javaソースコード構成
elastic-beantalk-java-app
├── src/main/java ・・・Javaのソースコードのルートディレクトリ
│   └── myserver  
│       └── StartServer.java
├── src/main/resources
│   └── webroot  ・・・静的WebコンテンツやJSPのルートディレクトリ
│       ├── index.html
│       └── show.jsp 
├── src/main/assembly
│   └── zip.xml ・・・★elastic beanstalkにアップロードするZIPファイルの生成ルールを記述
├── .elasticbeanstalk ・・・★Elastic Beanstalkへのデプロイ情報を格納するディレクトリ
│   └── config.yml  ・・・★Elastic Beanstalkへのデプロイ情報のYAMLファイル
├── target ・・・ビルドした結果(jarなど)が生成されるディレクトリ
├── Procfile ・・・★Elastic BeanstalkのEC2上でJARファイルを実行するためのスクリプトを記述する
└── pom.xml ・・・Maven用設定ファイル

※「★」印がついているところが Elastic Beanstalkのための構成

Javaアプリとしてのメインクラス(エントリーポイント)は StartServer.javaとなる。
これをローカル実行して http://localhost:5000/ にアクセスすれば、以下のようになり
JSPやServletなどのシンプルな実装を試すことができるサンプルとなっている。

image.png

ここからは、ソースコードで重要なところを以下に説明する

src/main/java myserver.StartServer.java

メインクラス。
Jettyを起動してServlet/JSPをホストしポート5000で待ち受ける

/pom.xml

pom.xml(抜粋)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.riversun</groupId>
    <artifactId>my-jetty-app</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <name>my-jetty-app</name>
    <description>jetty app on elastic beanstalk</description>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.riversun</groupId>
    <artifactId>my-jetty-app</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <name>my-jetty-app</name>
    <description>jetty app on elastic beanstalk</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jetty-version>9.4.19.v20190610</jetty-version>
    </properties>

    <dependencies>
        <!-- for server -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-annotations</artifactId>
            <version>${jetty-version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>${jetty-version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>apache-jsp</artifactId>
            <version>${jetty-version}</version>
            <type>jar</type>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>apache-jstl</artifactId>
            <version>${jetty-version}</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <excludes>
                        <exclude>examples/**/*</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <author>true</author>
                    <source>1.7</source>
                    <show>protected</show>
                    <encoding>UTF-8</encoding>
                    <charset>UTF-8</charset>
                    <docencoding>UTF-8</docencoding>
                    <doclint>none</doclint>
                    <additionalJOption>-J-Duser.language=en</additionalJOption>
                </configuration>
            </plugin>
            <!-- add source folders -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <!-- ソースコードをfat-jar化する -->
                    <execution>
                        <id>package-jar</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <appendAssemblyId>true</appendAssemblyId>
                            <archive>
                                <manifest>
                                    <mainClass>myserver.StartServer</mainClass>
                                </manifest>
                            </archive>
                            <finalName>${project.artifactId}</finalName>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                    <!-- Elastic Beanstalkにアップロードするzip(fat.jarや関連ファイルを含む)を作成する -->
                    <execution>
                        <id>package-zip</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <appendAssemblyId>true</appendAssemblyId>
                            <finalName>${project.artifactId}</finalName>
                            <descriptors>
                                <descriptor>src/main/assembly/zip.xml</descriptor>
                            </descriptors>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>
</project>

以下、ポイントだけみていく。

mavenのartifactId

以下はartifactIdとしてmy-jetty-appとした。
この後のビルドで生成されるファイル名などもこのartifactIdの値が使われる

    <groupId>org.riversun</groupId>
    <artifactId>my-jetty-app</artifactId>

maven-assembly-pluginの設定

以下はmaven-assembly-pluginの設定を行っている。
maven-assembly-pluginとは、配布用のjarファイルやzipファイルを作るためのmavenプラグイン。
このmaven-assembly-pluginには2つのタスクをさせている。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <!-- ソースコードをfat-jar化する -->
    <execution>
      <id>package-jar</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <appendAssemblyId>true</appendAssemblyId>
        <archive>
          <manifest>
            <mainClass>myserver.StartServer</mainClass>
          </manifest>
        </archive>
        <finalName>${project.artifactId}</finalName>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
    </execution>
    <!-- Elastic Beanstalkにアップロードするzip(fat.jarや関連ファイルを含む)を作成する -->
    <execution>
      <id>package-zip</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <appendAssemblyId>true</appendAssemblyId>
        <finalName>${project.artifactId}</finalName>
        <descriptors>
          <descriptor>src/main/assembly/zip.xml</descriptor>
        </descriptors>
      </configuration>
    </execution>
  </executions>
</plugin>

その1 JARファイル生成
まず前半の設定に注目。

  <execution>
      <id>package-jar</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <appendAssemblyId>true</appendAssemblyId>
        <archive>
          <manifest>
            <mainClass>myserver.StartServer</mainClass>
          </manifest>
        </archive>
        <finalName>${project.artifactId}</finalName>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
    </execution>

ここで記述しているのはJavaソースコードを1つのJARファイルにまとめること。
つまり、FAT JARを作るためのタスク設定となる。

<mainClass>myserver.StartServer</mainClass>はJARファイルを実行するときの起動クラス。

<finalName>${project.artifactId}</finalName>は、最終的にできあがるJARファイルのファイル名がartifactIdで指定された値=my-jetty-app-[AssemblyId].jarとなる。

<descriptorRef>jar-with-dependencies</descriptorRef>はmavenのdependenciesに記載した依存ライブラリも一緒にJARファイルとしてパッケージする。

<appendAssemblyId>true</appendAssemblyId>は、生成されるJARファイルのファイル名にAssemblyIdをつけるか否かをセットする。もしこれをtrueにすると、JARファイル名はmy-jetty-app-jar-with-dependencies.jarとなる。

ここで設定した条件でJARファイルを生成するには

mvn package

とすればよい。するとtargetディレクトリにmy-jetty-app-jar-with-dependencies.jarが生成される

その2 ZIPファイルの生成

次は後半の設定

   <execution>
      <id>package-zip</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <appendAssemblyId>true</appendAssemblyId>
        <finalName>${project.artifactId}</finalName>
        <descriptors>
          <descriptor>src/main/assembly/zip.xml</descriptor>
        </descriptors>
      </configuration>
    </execution>

ここで記述しているのは、上でつくったJARファイルとElastic Beanstalk関連ファイル(Procfileなど)をZIPファイルにまとめるタスクとなる。

<finalName>${project.artifactId}</finalName>としているところはJARのときと同じく生成されるファイル名を指定している。

ZIPの生成ルールは外だしされた設定ファイルである<descriptor>src/main/assembly/zip.xml</descriptor>で設定する。

src/main/assembly/zip.xml

さて、その外だしされた zip.xmlをみてみる。

これは最終的にZIPファイルを生成するときの生成ルールとなる。

<include>でJARファイルとProcfileなどを指定して、Elastic Beanstalkにアップロードする形式のZIPファイルの生成方法を指示している。

zip.xml
<?xml version="1.0" encoding="UTF-8"?>
<assembly
    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <id>eb</id>
    <baseDirectory>/</baseDirectory>
    <formats>
        <format>zip</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>Procfile</include>
                <include>Buildfile</include>
                <include>.ebextensions/*</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>my-jetty-app-jar-with-dependencies.jar</include>
                <!-- <include>*.jar</include> -->
            </includes>
        </fileSet>
    </fileSets>
</assembly>

最終的に生成されるZIPファイルのファイル名はmy-jetty-app-[id].zipとなる。
zip.xmlでは、<id>eb</id>と指定しているので、生成されるZIPファイル名はmy-jetty-app-eb.zipとなる。

まとめると必要パッケージを生成するためのmavenコマンド mvn package を実行するとmy-jetty-app-jar-with-dependencies.jarmy-jetty-app-eb.zipの両方がtargetディレクトリ以下に生成されることになる。

ここは、あとで実際にdeployファイルを生成するところでもう一度確認する。

/Procfile

ProcfileはElastic BeanstalkのEC2内でJARを実行するためのファイル。

Elastic BeanstalkのEC2上でアプリを起動するためのコマンドをweb:以下に記載する。

Procfile
web: java -jar my-jetty-app-jar-with-dependencies.jar

↓のように起動オプションを記述してもOK

web: java -jar my-jetty-app-jar-with-dependencies.jar -Xms256m

詳細は公式参照

.elasticbeanstalk/config.yml

config.ymlにはデプロイ情報を記述する
ここにはElastic Beansalkにデプロイするファイルtarget/my-jetty-app-eb.zipを指定している。

config.yml
deploy:
  artifact: target/my-jetty-app-eb.zip

Elastic Beanstalkにデプロイする際に、このファイルが参照される。

いまはdeploy:artifact:しか記述していないが、これからEB CLIをつかってElastic Beanstalkのデプロイ設定をしていく過程でこのconfig.ymlに必要な値が追加されていく。

EB CLIを使ってアプリをデプロイする

ファイルの意味をざっくり理解できたところで、さっそくElastic Beanstalkにアプリをデプロイする。

EB CLIを使ってElastic BeanstalkアプリケーションをAWS上に作る

1.アプリのディレクトリに移動する

cd java-jetty-app-on-elasticbeanstalk

2.Elastic BeanstalkにJava用の箱*を作る
(* 箱=アプリケーション)

そのためのEB CLIのコマンドは以下の形式となる。

eb init アプリ名 --region リージョン名 --platform プラットフォーム

ここではアプリ名をmy-eb-app、リージョンは東京リージョン(ap-northeast-1)、プラットフォームをjava-8とする

コマンドは以下のようになる。

eb init my-eb-app --region ap-northeast-1 --platform java-8

すると

Application my-eb-app has been created.

というメッセージがでて、
AWS Elasticbeanstalk上にアプリケーションの箱ができる

さっそくWebコンソールで確認すると箱(アプリケーション)ができている

https://ap-northeast-1.console.aws.amazon.com/elasticbeanstalk/home

image.png

さて、ソースコードにある/.elasticbeanstalk/config.ymlを見てみる。

上記コマンドで、config.ymlも更新され、以下のようになっている。

config.yml
branch-defaults:
  master:
    environment: null
deploy:
  artifact: target/my-jetty-app-eb.zip
global:
  application_name: my-eb-app
  branch: null
  default_ec2_keyname: null
  default_platform: java-8
  default_region: ap-northeast-1
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  profile: eb-cli
  repository: null
  sc: git
  workspace_type: Application

TIPS
------------

もし、Credential情報が登録されていなければ、以下のようにaws-access-idaws-secret-keyを聞かれるので入力する。

eb init my-eb-app --region ap-northeast-1 --platform java-8
You have not yet set up your credentials or your credentials are incorrect
You must provide your credentials.
(aws-access-id): xxxxx
(aws-secret-key): xxxxx
Application my-eb-app has been created.

1回入力すれば、[user]/.awsディレクトリ以下にconfigファイルができるので、次からは聞かれない。

------------

デプロイ用のパッケージを作る

さきほどみてきたように、Elastic BeanstalkにデプロイするためのZIPファイルを生成する。

ZIP生成はMavenでやるのでmaven packageコマンドをたたく

(cleanもついでにやっておくと、コマンドは以下のとおり)

mvn clean package

以下のようになり、無事 target/ディレクトリにmy-jetty-app-eb.zipが生成できた。

[INFO] --- maven-assembly-plugin:2.2-beta-5:single (package-zip) @ my-jetty-app ---
[INFO] Reading assembly descriptor: src/main/assembly/zip.xml
[INFO] Building zip: jetty-app-on-eb\target\my-jetty-app-eb.zip
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 32.984 s
[INFO] Finished at: 2019-07-01T15:55:14+09:00
[INFO] Final Memory: 37M/449M
[INFO] ------------------------------------------------------------------------

Elastic Beanstalkにデプロイし環境を構築する

いまから、つくったZIPファイルをEB CLIをつかってElastic Beanstalkにデプロイする

Elastic Beanstalkにデプロイするには、eb createコマンドを使う

eb createコマンドの使い方

eb createコマンドは以下のように指定する。

eb create 環境の名前 [オプション][オプション]・・・[オプション]
オプションは以下のとおり
オプション 説明
--cname URLのCNAMEを指定。
例 CNAMEにmy-jetty-app-test を指定すると
http://my-jetty-app-test.ap-northeast-1.elasticbeanstalk.com/
としてアクセスできる
--instance_type インスタンスタイプ。
t2シリーズを指定する場合はVPC必須
--elb-type ロードバランサーのタイプ
「application」を指定すると
アプリケーションロードバランサーになる。
あとで、独自ドメインやHTTPS対応するときにも
ロードバランサーあると楽
--vpc.id VPCのID
--vpc.elbpublic ロードバランサーを
パブリックサブネットに置く
--vpc.elbsubnets ロードバランサーのサブネットIDを指定。
複数指定するときはカンマ区切り
--vpc.publicip Elastic BeanstalkのEC2を
パブリックサブネットに置く
--vpc.ec2subnets Elastic Beanstalkの
EC2のサブネットIDを指定。
複数指定するときはカンマ区切り

詳しくは公式参照

それでは、
コマンドラインから以下を実行する。

eb create my-jetty-app-test --cname my-jetty-app-test --instance_type t2.small --elb-type application --vpc.id vpc-xxxxxxxxxxxxxxxxx --vpc.elbpublic --vpc.elbsubnets subnet-xxxxxxxxxxxxxxxxx,subnet-yyyyyyyyyyyyyyyyy --vpc.publicip --vpc.ec2subnets subnet-xxxxxxxxxxxxxxxxx,subnet-yyyyyyyyyyyyyyyyy

上記は実際のID等はダミーだが、環境名my-jetty-app-test、cnameがmy-jetty-app-testでEC2のインスタンスタイプがt2.small、ELB(ロードバランサー)がapplicationロードバランサー、VPCのIDがvpc-xxxxxxxxxxxxxxxxxとしてさきほどのZIPファイルをデプロイするコマンドとなる。

(どのZIPファイルがアップロードされるかは、.elasticbeanstalk/config.ymlで指定されているのでそれが参照される)

また、--vpc.elbpublic--vpc.publicipはELB(ロードバランサー)とElastic BeanstalkのEC2が指定したVPC内のパブリックサブネットで実行されることを示している。--vpc.elbsubnets--vpc.ec2subnetsそれぞれおなじパブリック・サブネット(2つ)を指定してある。ELBはパブリックにアクセスできないとアプリにアクセスできないのでパブリックサブネットに置く。EC2側はパブリックサブネットに置く方法とプライベートサブネットにおく方法がある。本稿の例では、パブリックサブネットにおいているが、自動生成されたセキュリティグループによってELBからしかアクセスできないようになっている。ただし、パブリックIPは割り当てられる。
(よりセキュアにするにはEC2はプライベートサブネットに置くなど工夫ができるが、これはElastic BeanstalkというよりEC2,VPCまわりのセキュリティイシューなのでここでは割愛とする。)

さて、上記コマンドを実行すると、以下のようにアップロードから環境構築までが自動的に実行される。

Uploading: [##################################################] 100% Done...

Environment details for: my-jetty-app-test
  Application name: my-eb-app
  Region: ap-northeast-1
  Deployed Version: app-1d5f-190705_00000
  Environment ID: e-2abc
  Platform: arn:aws:elasticbeanstalk:ap-northeast-1::platform/Java 8 running on 64bit Amazon Linux/2.8.6
  Tier: WebServer-Standard-1.0
  CNAME: my-jetty-app-test.ap-northeast-1.elasticbeanstalk.com
  Updated: 2019-07-01 07:08:01.989000+00:00

Printing Status:
2019-07-01 07:08:00    INFO    createEnvironment is starting.
2019-07-01 07:08:02    INFO    Using elasticbeanstalk-ap-northeast-1-000000000000 as Amazon S3 storage bucket for environment data.
2019-07-01 07:08:23    INFO    Created target group named: arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:targetgroup/awseb-AWSEB-LMAAAA/000000000
2019-07-01 07:08:23    INFO    Created security group named: sg-11111111111111111
2019-07-01 07:08:39    INFO    Created security group named: sg-22222222222222222
2019-07-01 07:08:39    INFO    Created Auto Scaling launch configuration named: awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingLaunchConfiguration-3V
2019-07-01 07:09:41    INFO    Created Auto Scaling group named: awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXX
2019-07-01 07:09:41    INFO    Waiting for EC2 instances to launch. This may take a few minutes.
2019-07-01 07:09:41    INFO    Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:000000000000:scalingPolicy:4e:autoScalingGroupName/awseb-e-
xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXX:policyName/awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingScaleUpPolicy-FA
2019-07-01 07:09:42    INFO    Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:000000000000:scalingPolicy:8a:autoScalingGroupName/awseb-e-
xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXX:policyName/awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingScaleDownPolicy-SSZW
2019-07-01 07:09:57    INFO    Created CloudWatch alarm named: awseb-e-xxxxxxxxxx-stack-AWSEBCloudwatchAlarmHigh-H0N
2019-07-01 07:09:57    INFO    Created CloudWatch alarm named: awseb-e-xxxxxxxxxx-stack-AWSEBCloudwatchAlarmLow-BX
2019-07-01 07:10:34    INFO    Created load balancer named: arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:loadbalancer/app/awseb-AWSEB-1BQ
2019-07-01 07:10:34    INFO    Created Load Balancer listener named: arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:listener/app/awseb-AWSEB-1BQ
2019-07-01 07:11:05    INFO    Application available at my-jetty-app-test.ap-northeast-1.elasticbeanstalk.com.
2019-07-01 07:11:05    INFO    Successfully launched environment: my-jetty-app-test

環境構築が開始されると、Webコンソールでも状況を把握することができる。

デプロイできた模様

image.png

image.png

無事アプリが http://my-jetty-app-test.ap-northeast-1.elasticbeanstalk.com/ にデプロイされた模様

アクセスすると、

image.png

ちゃんとJSPやServletも動いている模様

image.png

まとめ

  • Java SEをつかったWebアプリをEB CLIをつかって、AWS Elastic Beanstalkにデプロイするまでの手順をハンズオン方式で紹介しました

  • 結果、以下のような構成をコマンドから構築することができました

image.png

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

ServerlessFrameworkでLambda+ALBを使う

はじめに

API Gatewayを利用するのであればfunctionsに記載するだけであったが
ALBをLambdaのトリガーとする場合別途ALBを作成する必要がありそうだったのでやってみた

サービス構成

ALB -> Lambda

Lambdaは引数をそのままレスポンスとして返すだけのものを作成。
ALBは2つのパスでのルーティングをそれぞれ同じLambdaへ、それ以外を404とするものを作成。

※事前に用意したもの
- LambdaにつけるIAMロール
- ALBにつけるVPC(のサブネットID2つとセキュリティグループID)

フォルダ構成

.
├── handler.js
└── serverless.yml

Lambdaソースコード

コードは内容には特に関係ないので別になんでもよい

handler.js
module.exports.main = async (event) => {
    return response = {
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(event),
        statusCode: 200
    }
}

serverless.yml

API Gatewayと若干書き方は変わるが
単純にALBをLambdaのAPIとしての口としたいだけなら
ALBとリスナーをresourcesで作成して
functionsでそれに紐づけるだけでよさそう。

serverless.yml
service: alb-lambda-example

provider:
  name: aws
  runtime: nodejs10.x
  region: ap-northeast-1

functions:
  echo:
    handler: handler.main
    role: arn:aws:iam::00000000000:role/iam_role # IAMロールのARN
    events:
      - alb:
          listenerArn: 
            Ref: exampleLoadBalancerListener
          priority: 2
          conditions:
            path: /hello
      - alb:
          listenerArn: 
            Ref: exampleLoadBalancerListener
          priority: 1
          conditions:
            path: /world

resources:
  Resources:
    exampleLoadBalancer:
      Type: AWS::ElasticLoadBalancingV2::LoadBalancer
      Properties:
        Name: alb-lambda-example-alb
        Scheme: internet-facing
        Subnets:
          - subnet-XXXXXXXXXXXXX # サブネットID
          - subnet-YYYYYYYYYYYYY # サブネットID
        SecurityGroups:
          - sg-ZZZZZZZZZZZZZZ # セキュリティグループID
    exampleLoadBalancerListener:
      Type: "AWS::ElasticLoadBalancingV2::Listener"
      Properties:
        DefaultActions:
          - Type: fixed-response
            FixedResponseConfig: 
              ContentType: text/html
              MessageBody: <h1>Not Found</h1>
              StatusCode: 404
        LoadBalancerArn: 
          Ref: exampleLoadBalancer
        Port: 80
        Protocol: HTTP

結果

AWSコンソール上のALBの振り分けルール

image.png

作成されたALBのDNSにブラウザでアクセスすると/hello/worldではJSONがレスポンスされそれ以外ではNot Foundページが表示された。

トラブルシュート

sls deployに成功するがLambdaがALBと関連付けられてない

serverless をアップデートすると治るかも

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