Terraform で AWS環境を実運用する上で困ったことと、その対処

忘備録的なもの。
2017年2月時点、Terraformは0.8.6.

操作用AWSアカウントの認証情報の扱い

困ったこと

ネットの参考情報だと、awsの認証情報(credentials)を直接書くサンプルが非常に多かった。
しかし、tfファイルを書き換える運用だと、いつか間違えてcommit & pushしてインシデントになりそう。

Terraformを最初に動かすためのユーザーの認証情報は、その性質上大きな権限を持っていることが多いと思うので、慎重になりたい。

解決策

AWS-CLIのNamed Profile使おう。

$ aws configure --profile my-profile-name
AWS Access Key ID [None]: xxxxxxxxxx
AWS Secret Access Key [None]: xxxxxxxxxx
Default region name [None]: ap-northeast-1
Default output format [None]: 

事前にこうして設定しておけば、認証情報は$home/.aws/credentialsに名前付きで入る。

スクリプトからはこう参照する。これならばcommitしてしまっても問題ない。

example1.tf
provider "aws" {
  profile = "my-profile-name"
}

あとは、上記の設定手順をREADME.md なんかに書いて、KEYIDとSECRET自体はいつも通り正しく管理してあげればいい。

.tfstateファイルの扱い

困ったこと

.tfstateファイルを紛失してしまうと、作成したインスタンスを、Terraformから管理できなくなってしまうので、最重要ファイルだ。
かといって、gitにcommitするルールだと、commit忘れが怖いし、その際pullし忘れるとつらい。
一方、手動であちこち引き回しても同じことで、別の開発者が古いstateに基づいて、重複したインスタンスを立ててしまうかもしれない…

解決策

Backendの、remote state機能使おう。

ちゃんと公式ドキュメントある。 https://www.terraform.io/docs/state/remote/s3.html

こんな感じ。profileも指定できるので、そちらにregionを書いておけば省略できる。

$ terraform remote config 
    -backend=s3 
    -backend-config="bucket=my-tfstate-store-name-at-s3" 
    -backend-config="key=hogehoge/terraform.tfstate" 
    -backend-config="profile=my-profile-name"

これを実行した時点で、存在していたterraform.tfstateは、./.terraform/terraform.tfstate に移動されるようだ。

あとは自動でtfstateファイルをアップロード、ダウンロードしてくれる。
指定するバケットはacl=privateにしておくこと!!
あと、上記リンクでは、S3のversioningとかもつけておくことを勧めている。(私はやらなかった)

S3以外にも、いろいろ手段は用意されているようだ。

環境ごとのvariableの扱い

困ったこと

ステージングと本番、みたいに分けようと思って、環境ごとにvariableで設定値を与えられるようにしたけど、
-var オプションで引数に全部指定するの辛い

解決策

共通の設定ならば、terraform.tfvars という名前のファイルに書いておけば、指定しないでも勝手に読み込んでくれるとのこと。
https://www.terraform.io/intro/getting-started/variables.html#from-a-file

環境ごとに違う変数は、-var-fileオプションを使ってスイッチするのがよさそうだ。

$ terraform plan -var-file staging.tfvars
$ terraform plan -var-file production.tfvars

的な。

varは後ろに指定したものが有効(上書きされる)とのことなので、上手に使えば強いはず。

Packerで作ったAMIでEC2インスタンスを生成したい

困ったこと

auto-scalingを考えたときに、元となるAMIをちゃんと運用したい。
(注意、私はAWSのAutoScaling をよくわかっていないかも。)

そこで、Packerでイミュータブルなイメージ作ったらすごーいはず。
イミュータブルということは、イメージにDB接続情報がないといけない気がする。
よって、Terraformで先にRDS立てないといけないけど、そのTerraform側でAMIを使いたいからこういう話になっているわけで…
循環参照してしまう。

そもそも、AutoScaling配下のインスタンスを入れ替える際に全インスタンスを落とさないようにする、というのがなかなか厳しいようだ。
AutoScalingとの相性は、改善まち、という感じか。

参考: http://qiita.com/minamijoyo/items/e32eaeebc906b7e77ef8

準解決策1 null_resource

最終的にはうまくいかなかったが、最初に試した方法

null_resourceというものがある。
https://www.terraform.io/docs/provisioners/null_resource.html

何もしない代わりに、自身の状態(=変更が起きるトリガー)を定義するtriggers属性と、provisionerを設定できるリソースだ。
これを使って、

example2.tf
variable "ami_name_prefix" {}

resource "null_resource" "packer" {
  triggers {
    db_host = "${aws_db_instance.hogehoge.address}"
  }

  provisioner "local-exec" {
    command = <<EOS
packer build 
  -var 'DB_HOST=${aws_db_instance.hogehoge.address}' 
  -var 'AMI_NAME_PREFIX=${ami_name_prefix}' 
  packer.json"
EOS
  }
}

data "aws_ami" "packer_ami" {
  most_recent = true
  name_regex = "^${var.ami_name_prefix}.*"
  owners = ["self"]
  depends_on = ["null_resource.packer"]
}

resource "aws_instance" "hoge" {
  ami_id = "${data.aws_ami.packer_ami.id}"
  ...
}

とこんな感じ。

しかし、Terraform 0.8.6だと、triggersの中にinterpolationを混ぜると、問答無用でcomputed扱いになってしまうようで、
この記述では毎回AMIが作成されてしまって、差分のみ実行というTerraformの観点からは、使えなかった。

準解決策2 イミュータブルを諦める

オートスケーリング自体はこのパターンでは不可能か。AMI化を再度行う必要があると思う。

ほぼ完成品のAMIを組み立てておいて、aws_instanceのprovisionerで最後の仕上げをする。
Dockerなんかは、環境変数で外部情報を読み込ませる、なんてことをするらしいので、この手法に踏み切った。

// .envが欠けた状態でAMIをつくる
$ packer packer.json
example3.tf
data "aws_ami" "packer_ami" {
  most_recent = true
  name_regex = "^packer-ami-.*$"
  owners = ["self"]
  depends_on = ["null_resource.packer"]
}

data "template_file" "envfile" {
  # template = "${file(./env.tpl)}" などとした方が望ましいが例示のため。
  template = <<EOS
export DB_HOST="${db_host}"
export DB_PORT="${db_port}"
....
EOS

  vars {
    db_host = "${aws_db_instance.main.address}"
    db_port = "${aws_db_instance.main.port}"
  }
}

resource "aws_instance" "ec2" {
  ami_id = "${data.aws_ami.packer_ami.id}"
  ...

  # envファイルをuploadする。envファイルはdirenvとかdotenvとかで読み込まれるようにしておく。
  provisioner "file" {
    content = "${data.template_file.envfile.rendered}"
    destination = "/home/ec2-user/.env"
  }

  # 上で入れた環境変数を使ってサービスを立ち上げる。
  provisioner "remote-exec" {
    inline = ["sudo service unicorn start"]
  }

  # provisionerは、実行した環境からssh(のgolang実装。sshコマンドではない)で接続することになる。
  # 当然security_groupとvpc_internet_gatewayが適切に設定されている必要がある。
  connection {
    type = "ssh"
    ...
  }
}

tfファイルを構造化したい

困ったこと

コピペや反復は悪だし、再利用性も下がる。

COUNT

COUNT = nで配列のように同じタイプのリソースを複数作れる。
それぞれ微妙に違う値を書きたいなら、

COUNT = 2
ATTR = "${element(list("a", "b", "c", "d"), count.index)}"

などとできる。
listは変数化するとなおよい。

module

複数のリソースにまたがっての反復パターンなら、module化してしまうとよい。
module自体の書き方はここでは説明しないが、

./modules/my_module/variables.tf
./modules/my_module/main.tf
./modules/my_module/output.tf

を作り、

example4.tf
module "use_of_my_module" {
  source = "./my_module"
  var1 = ...
  var2 = ...
}

と書くことで使用。

$ terraform get

で、モジュールをterraformに読み込ませる準備をさせる。(./.terraform/modules/ にsym-linkが作成されるようだ)

様々に公開されているmoduleもあるようなので、むしろ自分でresourceを書かないほうが良いのかもしれない。

その他

また何かあれば書く。

続きを読む

Amazon Elastic BeanstalkでサクッとElixir製サーバーをAWSにデプロイする

はじめに

Amazon Elastic Beanstalk では、 Dockerを使ってよしなにアプリケーションをAWSにデプロイできる。
Docker ImageがS3に上がり、サーバーはEC2にデプロイされる。
これを使ってElixir製のping叩いたらpong返してくるだけのアプリをデプロイしてみる。

mix で新規プロジェクトを作成

今回はelixir:1.4.1を用いる。

$ mix new testex

ライブラリの追加

今回は cowboy(HTTPサーバ), poison (JSON ライブラリ), distillery(release 用ライブラリ)を使う。

mix.exs に以下のように追加して

defmodule Testex.Mixfile do
  use Mix.Project

  def project do
    [app: :testex,
     version: "0.1.0",
     elixir: "~> 1.4",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  def application do
    # Specify extra applications you'll use from Erlang/Elixir
    [applications: [:logger, :cowboy, :poison],
      mod: {Testex,[]}]
  end

  defp deps do
   [{:cowboy, github: "ninenines/cowboy"},
    {:poison, "~> 3.0.0"},
    {:distillery, "~> 1.1.0", runtime: false}]
  end
end
$ mix deps.get
$ mix release

$ _build/dev/rel/testex/bin/testex console

これで elixir が走るようになるはず。

cowboy で http サーバを走らせる

ポートやルーティングの設定

lib/testex.ex
defmodule Testex do
  def start(_type, _args) do
  import Supervisor.Spec, warn: false
  children = []
  dispatch = :cowboy_router.compile([
      {:_, [
           {"/ping", Testex.Handlers.Ping, []},
      ]}
    ])
    {:ok, _} = :cowboy.start_clear(:http,
                                   100,
                                   [{:port, 4000}],
                                   %{env: %{dispatch: dispatch}})
  opts = [strategy: :one_for_one, name: Testex.Supervisor]
  Supervisor.start_link(children, opts)
  end
end

ホントに pong を返すだけ

lib/testex/handlers/testex.ex

defmodule Testex.Handlers.Ping do
    def init(req, opts) do
    body = %{"type" => "pong"} |> Poison.encode!()
    req2 = :cowboy_req.reply 200, %{"content-type" => "application/json"}, body, req
    {:ok, req2, opts}
    end
end

AWS用のDocker 設定ファイルを作成

Dockerrun.aws.json
{
  "AWSEBDockerrunVersion": 1,
  "volumes": [
    {
      "name": "testex",
      "host": {
        "sourcePath": "/app"
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "testex",
      "essential": true,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 4000
        }
      ]
    }
  ]
}

WORKDIR として /app を明記するのがポイント

Dockerfileの作成

Dockerfile

# elixir の Docker Image を引っ張ってくる。今回は Phoenix を使わないので node 等は不要
FROM trenpixster/elixir:1.4.1

# WORKDIR の作成
RUN mkdir /app
WORKDIR /app

# mix deps.get
ADD mix.* ./
RUN MIX_ENV=prod mix local.rebar
RUN MIX_ENV=prod mix local.hex --force
RUN MIX_ENV=prod mix deps.get

# Install app
ADD . .
RUN MIX_ENV=prod mix release

# Exposes this port from the docker container to the host machine
EXPOSE 4000

# distillery の run command を実行する。今回はフォアグラウンドで
CMD MIX_ENV=prod _build/prod/rel/testex/bin/testex foreground

eb コマンドの実行

あらかじめ eb をインストールする。 pip で入る。
http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html

これで、

$ eb init # awsアカウント情報を入力
$ eb create # サーバ名等を入力。Dockerfileがあるとeb側でよしなに判断してくれる。
$ eb deploy testex

すると本番にデプロイされる。

おわり

これはお試しですが、実際に運用するなら、本番・開発環境を切り分けたり、CORS対策としてAPI Gatewayなどを別途で使う必要あり。

サンプルコード

https://github.com/GigantechHQ/ex-server-sample

続きを読む

「フロントエンドエンジニアに伝えたいインフラの話」参加メモ 関西フロントエンドUG 2017/02/18

関西フロントエンドUG勉強会
「フロントエンドエンジニアに伝えたいインフラの話」
https://kfug.connpass.com/event/49305/
の参加メモです。

全般的にAWSいいね!という話ばかりでした。
サービスの内容については、書き取った内容を記載していますが、
最新の正確な情報は各サービス公式サイトの確認をお願いいたします。


フロントエンドで捗るAWS ー WordPress の事例と共に

岡本 秀高 さん

AMIMOTOでは、AWSクラウド上で動くWordPressホスティングサービスを提供しています。なぜクラウド上でWordPressを利用するようになったのか。ケーススタディと共にご紹介します。(CMSユーザー・管理者向け)

  • クラウドは環境の規模を柔軟に変えられる
  • 料金は使った分だけ
  • マネージドサービスならインフラレイヤのメンテを丸投げできる

AWSの豊富なサービス群を活用できる

  • Amason Elasticsearch
  • Amazon lightsail
  • Amazon Cognito User Pools (ユーザ認証)
  • Amazon API Gateway
  • AWS Certificate Manager (SSL)

Frontend Meets a Service

後藤 知宏 さん

Serverless を使用すれば Javascript を使用して簡単に 公開API を実装することが出来ます。
バックエンドのJSにつきものな面倒なExpress の学習も抜きにさっと始められる Serverless のAPI 実装を、 Serverless と Webpack の活用を通じて紹介していきます。

便利なサービスを使うことで、バックエンドを組まなくても、作れるものがある。

  • PaaS
  • Baas
  • FaaS

PaaS – Platform as a Service –

サービスの構築をしなくても自動でサーバーを立ち上げてくれる。
構築まではやってくれるが、保守・スケール・監視は自分で。
バックエンドアプリケーションの構築必要。
簡単なAPIサーバの構築や、APIモックなどをサッと上げてしまうには楽。

BaaS – Backend as a Service –

データの管理、検索、Push通知、メール配信、認証など、機能ごとに様々なモノがある。
サービスから提供されれるAPIなどを通じて機能を利用できる。
一方で認証周り等でフロントエンドでの活用は難しく、
バックエンド内での実装負担削減に用いられる。

FaaS – Function as a Service –

Javascriptのfunctionが書ければ、バックエンドを構築することができる。
サーバーサイドに関する知識は殆ど不要。

AWS Lambda

関数をデプロイしたら、API Gateway やスケジューラなどと連動させて様々な処理を行わせることが可能。
構成管理は Serverless framework を通じて簡単に行うことができる。
Serverless は Lambda の面倒なデプロイなどのタスクを自動で処理してくれるツール。
機能単位では無く関数単位でデプロイできる。


常時SSLとCDNの話

岩本貴久さん

常時SSL対応していますか?

  • 通常(非SSL)のインターネット通信は傍受可能
  • 個人情報、クレジットカード情報等をそのまま通信することは危険
  • 通信の暗号化の仕組み
  • HTTP/2は常時SSLが必須

  • メリット 暗号化される

  • デメリット サーバー負荷 SSL証明書の手配・更新

SSL証明書

  • 通信を暗号化するための、鍵と鍵穴
  • 証明書の取得が面倒くさい
  • WEBサーバーへの割り当て作業
  • 証明書の更新

CDN

検索やログイン画面などがキャッシュされないよう注意
キャッシュ時間を考慮

AWSなら快適

AWS Certificate Manager – 無料でSSL
Amazon CloudFront のみで利用可能、自動更新

Elastic Load Balancing – ロードバランサ
複数台のマシンで負荷分散

CloudFront ー CDN
都度の利用 従量課金 最低利用期間無し、安い

ーーー

VPSからクラウドサーバーに移ったらこんなに快適に!

榊原昌彦 さん

某VPSからAWSに移ったメリットを中心にご紹介します。EC2, RDS, S3, SNSあたりの話をします。初心者向け。

1. 自宅サーバー

  • 構築が大変
  • 電源抜いたら死ぬ
  • 自由度は高い

2. レンタルサーバー

  • とにかく安い
  • 自由はない
  • 最近はgitやssh等使えて便利

3. VPS

  • 融通が利くけれど、融通が利かない
  • 構築の煩雑さ
  • セキュリティの心配
  • 他の同居VPSにパフォーマンスを阻害される
  • プラン変更時の移転が大変

4. AWS

  • メモリの増強も簡単数クリック
  • スナップショットがとれる
  • 便利だがお高い

サービス群

RDS データベースサービス
VPS上にDBを入れるのでは無く、データベース専用サービスがある。
DBだけだから、バックアップも高速。かつバックアップの復元も楽。
DB単位でアクセス制限できるから、データ管理を寄り強固にできる。

S3 静的ファイルの保管サービス

Elastic Transcoder メディア変換サービス

Cloud Front 高速コンテンツ配信

プッシュ通知、ユーザー制御サービスなど…


LT

5分でブログ公開する話

しまきょうすけ さん

Netlify

スタティックサイトジェネレーター選んで、cloneして、ドメイン設定するだけ。

開発環境を構築する話

小渕周 さん

AppleMusicを使って100GBあいた話

技術書は Amazon Unlimited を使うと捗る

環境 VartualBox Vagrant bento

フロントエンドとインフラ層をつなぐ知識

pastelInc さん

インフラ層の学習すべきか?

学ぶ機会はある

  • トラブルに遭遇
  • パフォーマンスの話
  • 層の違いを跨ぐ技術 Vagrant Docker 文字エンコード
  • 所属する組織が提供するサービスであれば構成の知識を吸収
  • フロントエンドも層の違う知識を、隠蔽化されているだけで使っている

知らないはずは無い

  • 層の違うエンジニアとコミュニケーションする
  • 層の違いを埋める知識には人や組織によって差がある
  • 完全な分業は無理なので繋ぎの知識を知っている

どう学ぼう

  • 全部を学ぶ方法は無いとき付いている
  • 学ぶ題材は今までの歴史の積み重ね
  • テーマを決めて時間をかけてゆっくり学ぶ
  • そしきが押さえておくべき最低限の繋ぎの知識は最優先で学ぶ

所感

  • CPU、メモリのブレイクスルーが起こるかもしれない
  • ここまで変化に富んだ業界は珍しい
  • 変化を柔軟に楽しむ事が大切

運用していくことについて考えてみた

モリカワタカユキ さん

速さは大事

  • 速く実装
  • 速く動かせればテストに工数を割ける
  • 想定外を減らせる

速さはやっつけ仕事では無い

  • デプロイまでが作業期間ではない。

何を意識していくのか

  • そのループ本当に必要か
  • そのデータ必要か
  • 他の人がコードの意味が分かるか

現実甘くないよね

  • 納期が最優先
  • 本当にこのやり方で大丈夫なのかと 考える余地を持つ,,

続きを読む

ECSで困ったときに読むための俺的Q&A

みなさんこんにちは

ここ一年でdockerを使った開発を進めた結果、ついには「デプロイもコンテナイメージでやったらええやん!」みたいに思い、そうするとAWSでECSというサービスがあるじゃあないですかとなって、実際に環境構築を試みたわけです。
謳い文句には「一瞬でできる!」とか「容易に」とか書かれていますが、やっぱり実際にはなれるまで時間がかかるわけです。
web上に転がっている情報は、どういうわけかaws cliの情報ばかりなので、超初心者であり普通のコンソール使おうとしている私にとっては、まあ、解読が難しいわけです。
そうしていると、変なノウハウが溜まっていくわけで、せっかくなのでこっちの方に書きなぐってしまおうと考えたわけです。

というわけでほぼ自分用のECSのQ&Aを作りました。
ただのバッドノウハウになっている可能性もありますが。

概念編

AWSの日本語って、単純に訳したかのような、ちょっと見慣れない or 聞きなれない表現があったりして、こちらの混乱を誘います。
そういう場合は一生懸命英語を読むわけですが、メモっておいたほうがいいかなと思いましたので、ここに記載しておこうと思います。

ECS (EC2 Container Service)

dockerコンテナを通して処理をしたりサービスを立ち上げたりと行った挙動をEC2上で容易に行うためのAWSのサービスです。
EC2上での操作は全部AWSでやってくれるので、こっちはコンテナイメージの作成に注力できるし、動作する際のパラメータも任意に決められます。
ちゃんと設定できればコンテナイメージを作ってそれをアップするだけで、簡単にアプリケーションがデプロイできる、みたいなシステムを構築することもできます。

ECR (EC2 Container Registry)

ECSはコンテナを操作するサービスだったのに対し、こちらはコンテナイメージをprivateな環境に格納しておけるサービスです。
また、当然ではありますが、ECSと連携することができて、ECSで使うコンテナイメージをECRから引っ張ってくるという使い方ができます。
私の場合、ECSでプライベートリポジトリを使う場合、ECRから取得する方法しか使ったことがないです。

ただ、ここにプッシュするためにはaws-cli の使用が必要になるようで、使ったことない人には敷居が高いです。

クラスター

ECSでコンテナを動かすためのホストマシンの集合体です。
クラスターを作るときに実際にホストマシンのサイズなどが指定でき、クラスターを作成するとAWSが提供するコンテナを動かすのに適したAMIでEC2インスタンスが起動します。

ちなみにこの時クラスターが持っているEC2のインスタンスのことをコンテナインスタンスって読んでいるようです。

タスク定義 (Task Definition)

タスク定義と言うのはアプリケーションの動作を一つ以上のコンテナで表現したものです。
ナンノコッチャというと、例えばWEBサーバの挙動を定義したければ、nginx+php-fpmの入ったコンテナを持ってきて、起動させてしまえばいいという感じです。

このとき、nginxとphp-fpmは別のコンテナで定義させておき、nginx -> php-fpmのリンクを設定することでも同じようなタスクを表現できます。
コンテナはアプリケーションの最小構成単位にまで落とし込んだほうがいいらしいので、分けられるのなら分けちゃったほうがいいかもですね。

リビジョン

タスク定義にはリビジョンというものが存在しています。タスク定義の変更は、新しいリビジョンを作成することによって行うことができます。
こいつの利点は、任意のリビジョンのタスクを実行できる点にあり、例えば間違った設定をして突如タスクが動かなくなってしまった場合は、一つ前のリビジョンを動かすことで、タスクの停止時間を抑えることができます。

サービス

クラスターの中の適当なインスタンスでタスクを起動させてもいいのですが、スケーリングとかELBの設定とかをいちいちやっているのは面倒くさいですよね。
そんなとき、各コンテナインスタンスにいい感じにタスクを配置させたり、ELBとの接続の設定を管理してくれたり、負荷が上がったときにスケーリングしてくれたりする機構がサービスとなります。
サービスにはタスクを一つ設定できますが、この時設定するタスクはリビジョン付きで設定します。

サービスは、タスクを更新 (=設定されているタスクのリビジョンを変更) した場合、突然いま動作している古いタスクを落とすわけではなく、新しいリビジョンのタスクを起動しつつ、必要タスク数を下回らないように注意しながら古いリビジョンのタスクを停止させていきます。
いわゆるブルーグリーン方式で、一時的に新旧のタスクを共存させることで、アプリケーションが一瞬でも完全停止しないように気を使っています。

突発的な作業のためには裸のタスク定義を使い、WEBサーバなど継続して動作すべきタスクに対してはサービスを作ってあげるといいと思います。

トラブルシューティング編

私が実際に動かしていて発生したトラブルに、どのように対応していったかを書き並べていきます。
事細かな詳細を書くこともできないので、ちょっとふわっとしています。
あと、大事なことですが、AWS マネジメントコンソール上での話です。

あれが削除できない

間違って作っちゃったものを削除してなかったことにしたいのは人間の心理ですが、わりとハードルが高いので困りものです。
え?そもそも削除しちゃだめ?ははは

タスク定義が削除できない

タスク定義が削除できない場合があります。
タスク定義を削除するためには、

  1. 有効なリビジョンが存在しない
  2. 全部のタスクが停止している

状態でなければなりません。
というわけで、クラスター上でまだ残っているタスク定義がないか調べた上で、動いているものがあったら停止していきましょう。
タスクが勝手に起動して停止できない!という場合はもう少し後ろの項を参照してください

サービスが削除できない

サービスは自身が管理しているタスクが起動していると、削除できません。
起動しているタスクを停止させてから削除しましょう。

やっぱり タスク or サービスが削除できない

タスク全部停止したのに、やっぱり削除できないやんけ!ってときは、もしかしたらタスクが自動的に起動している可能性があります。
サービスは自身が管理するタスクが停止し、必要タスク数を下回ってしまうと、自動的にタスクを起動して必要タスク数を満たすように努力します。
よって、一旦サービスの必要タスク数を0にしてから、起動中のタスクを落としていくようにすれば問題なしです。

クラスターが削除できない

VPCの設定でミスったり、よくわからないけど動かなかったりして、とりあえず今作ったクラスターを削除したいんだけど、どういうわけだか削除できない場合があります。

私の場合、クラウドフォーメーションの削除処理のときにセキュリティグループがほかから参照されていて削除できないという状況になっていました。

クラスターのコンテナインスタンスもセキュリティグループを持っており、同じVPC上のDBとかキャッシュを参照したいときにセキュリティグループのアクセス許可設定をするわけですが、そうしているとコンテナインスタンスに設定されているセキュリティグループが削除できず、結果としてクラスターも削除できないという状況になります。
他のセキュリティグループからコンテナインスタンスのセキュリティグループを手動で削除してからクラスターを削除してみましょう。

タスク定義が原因のトラブル

タスク定義って単純にdocker run するようなものだから、簡単ですよね?…というわけにも行かず、やっぱりローカルとは違うようねって思う今日このごろ
タスク定義で発生し得るミスなどを追ってみましょう。

タスクが一つしか起動できない

タスクなんてコンテナの集まりなのだから、リソースがある限りいくつも起動できるようにして欲しいところですが、実際に動かしてみると、コンテナが一つしか動いてくれないということがママあります。
私の場合は、コンテナ上のポートマッピングの際に、ホストに固定のポートを指定していることが原因でした。
同じタスクを起動させようとしても、すでにホストのポートが使われているので、起動できないというやつですね。
docker runでmysqlを別に2台立ち上げたときに、私も遭遇したことがあります。

こんなとき便利なのはポートの設定にダイナミックポートを設定してしまうことです。こうすることで、ホストの開いているポートを使ってポートマッピングしてくれます。
ダイナミックポートの設定方法は、コンテナのポートマップ設定のときに、ホストポートに0もしくは空欄にしておくことで、設定可能です。

タスクが動かない原因がわからない

タスクが起動できたかもしれないけど、いつの間にか落ちているっていうときは、ログを調べるべきなのですが、どんなふうにログを出したらよいかわからない方もいると思います。
コンテナログを簡単に出すならば、CloudWatchのログ機能を使うと、追跡がしやすいです。

まず、マネジメントコンソール上でCloudWatchのログ->アクション->ロググループの作成で適当なロググループを作っておきます。
次にタスク定義のコンテナのログ設定で、ドライバーを「awslog」にします。
すると、ロググループとリージョンを設定する項目が追加されるので、自分のリージョンと先程作成したロググループを入力して、リビジョンを作成します。
これで、新しいタスクを起動すると、ログが出力されるので、これを元にトラブルを解決しましょう。

サービスが更新されたのに、古いリビジョンのタスクが走り続けている

これはいくつか原因が考えられるので、コンソールのECSのサービスから、直近のイベントを眺めてみましょう。
サービスに設定されているリビジョンはすでに新しいリビジョンに更新されているにも関わらず、古いリビジョンが走り続けている場合は、新しいリビジョンのタスクが何らかの原因で起動していない可能性があります。
先に述べたとおり、サービスは私の場合、タスク( コンテナ )自体がエラーを出すときと、古いタスクがポートを専有しているため、新しいタスクが起動できないという2つの原因がありました。

ポートの問題に関しては上述したダイナミックポートを使い、コンテナ自身のエラーについては

サービスが原因のトラブル

サービスが原因になることってそんなにないのですが、ないわけではありません。

タスク定義が更新されたけど、古いタスクが走り続けている

タスク定義を更新してリビジョンが新しくなっても、サービスに設定されているタスクのリビジョンを変えなければ、サービスが管理しているタスクは更新されません。
サービスに設定されているタスクも更新しましょう。

古いタスクと新しいタスクが同時に走っている

サービスは新しいタスクに切り替えるときに、外見上アプリが完全停止しないように、新しいタスクが立ち上がってロードバランサに登録されるまでは、古いタスクを走らせ続けます。
これにより、アプリのダウンタイムを0にするだけでなく、新しいタスクが何らかの異常で停止した場合は、古いタスクをそのまま走らせ続けることで、長時間のアプリ停止を防ぐことができます。

まとめ

せっかくdockerで開発環境構築しているのなら、やはりデプロイもコンテナでやりたいですよね。
とりあえず身近で使いやすいコンテナ運用環境としてECSがあるので、どんどん使っていきたいです。

ちょっとストレージの部分がまだわからなかったりするので、そのへんは勉強しなきゃなぁと思っています。

今回はこんなところです。

参考

ECS
ECSのダイナミックポートについて

続きを読む

Redash での Amazon Athena 連携に関して

概要

Redash が Amazon Athena を正式サポートしたとのことで試そうと思ったら上手くいかなかったので備忘録的な

使う物

redash-amazon-athena-proxy

公式で作っている Redash で Athena に接続するためのプロキシです

使い方

redash-amazon-athena-proxy は Dockerfile が用意されてるので今回は Docker のコンテナ上で起動することにしました

Ubuntu に Docker をインストール

[Docker] ubuntu 14.04/16.04にDockerをインストール を参考にインストールしました

ソースを取ってきて Docker で起動

$ cd /path/redash-amazon-athena-proxy
$ sudo docker build .
$ sudo docker run -d --name redash-amazon-athena-proxy -p 4567:4567 イメージID

Docker が上手く動いているか試す

$ curl -H "Accept: application/json" 
       -H "Content-type: application/json" 
       -X POST -d '{"athenaUrl":"jdbc:awsathena://athena.[us-east-1|us-west-2].amazonaws.com:443/","awsAccessKey":"アクセスキー","awsSecretKey":"シークレットキー","s3StagingDir":"s3://[Athena用バケット]","query":"SELECT 1"}' 
        http://localhost:4567/query

Redash の環境変数に追加

デフォルトだと「/etc/redash/.env」

export ATHENA_PROXY_URL=http://localhost:4567/query

上記内容を追記してRedashを再起動します

$ sudo supervisorctl restart all

雑感

今回苦労した点だと「ATHENA_PROXY_URL」の指定の仕方がわからなかったことです

ソースをみたところ「/query」で受け取るように書かれていたので気付けました…

また環境変数の設定が「.env」に書く必要があるというのも途中から Redash の入ってる環境を見た為、気付くのに時間がかかりました…

続きを読む