まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

terraform0.9.1からplanレベルでsecurity_groupのcidr_blocksのvalidateが入るようになった

0.9.1 changlog :tada:

https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md#091-march-17-2017

provider/aws: Validate CIDR Blocks in SG and SG rule resources (#12765)

planで未然に防げる

つい間違ってcidr_blocksの方にsecurity_groupのidを指定しまったことがあったけどそれが防げるようになる💯

mpon.hatenablog.com

terraform consoleっていうREPLみたいなやつがあるの知らなかった

v0.8から導入された模様。俗に言うREPL。今のtfstateをもとにsyntaxを試すことができる。planやapplyなどを発行することはできないので安全。単にこういう時はterraformではどう書くんだろう??みたいなのを試したいときに便利。

例1: moduleのoutputを呼び出して要素を取得する

> "${module.base_network.private_subnet_001_ids}"
[
  subnet-xxxxxxxxx,
  subnet-yyyyyyyyy
]
> "${module.base_network.private_subnet_001_ids[0]}"
subnet-xxxxxxxxx
> "${module.base_network.private_subnet_001_ids[1]}"
subnet-yyyyyyyyy

例2: 組み込み関数が何を返すか調べる

> cidrsubnet("172.24.0.0/16", 10, 1)
172.24.0.64/26
> cidrsubnet("172.24.0.0/16", 10, 0)
172.24.0.0/26
> cidrsubnet("172.24.0.0/16", 26, 0)
cidrsubnet: insufficient address space to extend prefix of 16 by 26 in:
> cidrsubnet("172.24.0.0/16", 10, 4)
172.24.1.0/26
> cidrsubnet("172.24.0.0/16", 10, 5)
172.24.1.64/26
> cidrsubnet("172.24.0.0/16", 10, 6)
172.24.1.128/26
> cidrsubnet("172.24.0.0/16", 10, 8)
172.24.2.0/26

自分で立てたdeisにアプリをdeployするまで

quickstartのConfigure DNSregister an admin user and deploy your first app.をやっただけ。

ロードバランサーの確認

$ kubectl --namespace=deis describe svc deis-router | grep LoadBalancer
Type:           LoadBalancer
LoadBalancer Ingress:   104.198.90.182

これがロードバランサーのIP。

でも、これがいつできたのがわからない :scream:

On Google Container Engine, Deis Workflow will automatically provision and attach a Google Cloud Loadbalancer to the router copmonent.

Deis Workflowは自動的にGCPロードバランサーを作ってアタッチするっていってるけど、一体これはいつ行われたんだ・・ :thinking: helm install したときか?該当のコードをdeisのorganization配下でgcpとかload balancerとかgclbとかいろいろ検索してみたけど、わからず・・

kubernetesのserviceでtype:LoadBalancerとしてるから

そもそも、k8sにserviceというcomponentがあって、serviceは色んなタイプがあるけれどLoadBalancerを選ぶと、k8sがよしなにバックエンドのcloud providerを見てロードバランサーを作ってくれるってことですね。GKEの場合は、それは、Google cloud load balancerを使って実現すると。 なので、多分k8sのコードにそういうことが書いてあるはず(まだ見てないけど)。

deis/workflowがやるのはpodを作成するってことだけだと思いこんでしまっていて、podはcontainerを包含した単位なのに、なぜ上位のGCPの操作の仕方まで知っているんだろう?というところがつまづいてしまった原因でした:scream: そもそもk8sは何ができるのかを分かったようで分かってなかったってことですね。

ロードバランサーのIPをもとにnip.ioというサービスを利用したときのhostを確認する

$ host 104.198.90.182.nip.io
104.198.90.182.nip.io has address 104.198.90.182

これで deis.<GCPロードバランサーIPアドレス>.nip.io で手元でアクセスできるようになる。

$ curl http://deis.104.198.90.182.nip.io/v2/ && echo
{"detail":"Authentication credentials were not provided."}

手元のdeisコマンドでユーザー登録する

$ deis register http://deis.104.198.90.182.nip.io
username: admin
password:
password (confirm):
email: xx@bb
Registered admin
Logged in as admin
Configuration file written to /Users/mpon/.deis/client.json

既存のやつが上書きされちゃうっぽい。ってことは複数のdeis workflowに対して何かやりたいときは、deis registerをやり直さないといけないのか??ちょっとめんどいかもしれない :rolling_eyes:

$ deis register --help
Registers a new user with a Deis controller.

Usage: deis auth:register <controller> [options]

Arguments:
  <controller>
    fully-qualified controller URI, e.g. 'http://deis.local3.deisapp.com/'

Options:
  --username=<username>
    provide a username for the new account.
  --password=<password>
    provide a password for the new account.
  --email=<email>
    provide an email address.
  --login=true
    logs into the new account after registering.
  --ssl-verify=true
    enables/disables SSL certificate verification for API requests

deis registerのhelpを見てみても複数に切り替える的なやつはなさげだ。まぁいっか :dancer:

アプリケーションの器をdeis上に作る

$ deis create --no-remote
Creating Application... done, created jangly-sailfish
If you want to add a git remote for this app later, use `deis git:remote -a jangly-sailfish`

作ったとこにサンプルアプリケーションをpullする

$ deis pull deis/example-go -a jangly-sailfish
Creating build... done

アクセスする

$ curl http://jangly-sailfish.104.198.90.182.nip.io
Powered by Deis

コンフィグを上書きする

この間、勝手にローリングデプロイが行われるのでdown timeは0 💯

$ deis config:set POWERED_BY="Docker Images + Kubernetes" -a jangly-sailfish
Creating config... done

=== jangly-sailfish Config
POWERED_BY      Docker Images + Kubernetes
$ curl http://jangly-sailfish.104.198.90.182.nip.io
Powered by Docker Images + Kubernetes

スケーリング

コマンドでさくっとできる。

$ deis scale cmd=2 -a jangly-sailfish
Scaling processes... but first, coffee!
done in 10s
=== jangly-sailfish Processes
--- cmd:
jangly-sailfish-cmd-2108971595-1vxqg up (v3)
jangly-sailfish-cmd-2108971595-b7xhf up (v3)

Google Cloud Platformの環境をterraformで作る

そもそもGoogle Cloud Platformとは

Googleが出してるAWS的なやつ。GCPと略す。だいたいの比較表。

dev.classmethod.jp

なんでAWS使わないの?

コンテナでぽんぽんアプリケーションをdeployしたいんだけどそれをAWSでやろうとすると、ECSというサービスを使うことになる。でも、このECSというのがdeployまわりの取り扱いがめんどくさくてちょっといけてない。 で、同じようなコンテナのスケジューリングとかやってくれるソフトウェアでkubernetesというのがあるんだけど、それをAWS上にたてようとするとちょいめんどい。GCPにはGKE(Google Container Engine)というkubernetesのマネージドサービスが最初から用意されてるのでこれを使いたいから。

GCPにおけるprojectという概念

GCPは一つのアカウントに対して、projectという概念を持ってる。たくさんprojectは作ることができる。すごくざっくりだけど、AWSでいうVPCぐらいの単位かも。

GCPにおける地域とゾーン

GCPにもAWSでいうregion的なやつがある。

  • AWSでいうregionはGCPだと地域で、アジアだと日本(asia-northeast1)と台湾(asia-east1)がある
  • AWSでいうazはGCPだとゾーンと言って、日本には1a〜1cまである。

GCPのprovider

ここからterraformの話。とにもかくにもproviderを定義する。 GOOGLE CLOUD PROVIDER | terraform

provider "google" {
  credentials = "${file("../account.json")}"
  project = "elated-bebop-161001"
  region = "asia-northeast1"
}
  • account.jsonというのは、GCPのコンソールから認証情報(秘密鍵が書いてある)ファイルをjson形式で落としてきたもの。
  • projectは上で言及してるもので全GCP上で一意になるもの。Project名ではなくIDが振られるのでそれを書く。
  • regionは地域。ここでは日本を指定する。

terraformの実行🚀

terraformコマンドで直接実行してもいいけど、いろんなprojectを掛け持ちしてるとローカルのversionを揃えるのが大変なので、docker imagesで提供されているものを使う。 参考にしたサイト 実行するときにいろんなことをしたり、長いコマンドうつの面倒なのでRakefileを作ってそれで実行する。

Rakefileの中身とはまったポイント

  • 🙅 terraformのdocを見るとGOOGLE_CREDENTIALSに認証情報のjsonの中身をつめて渡せばそれを使ってくれるとかいてあったが、これをdockerのコマンドに渡すようにするとstdoutに表示されてしまう
  • 😓 出力されないように環境変数jsonの中身をいれようと試行錯誤したが、jsonの末尾の改行を取り除かないといけないのと、private keyが記述されてるのでそこに文字列として\nが入っててそれが環境変数として渡すと評価されてしまって改行扱いになってしまう

docker実行時に、GOOGLE_APPLICATION_CREDENTIALSで認証情報のファイルの場所を渡してあげるようにする💯

@terraform_version = "0.8.8"

@docker = %Q(docker run --rm -v ${PWD}:/work -w /work/terraform -e GOOGLE_APPLICATION_CREDENTIALS=/work/account.json hashicorp/terraform:#{@terraform_version})

namespace :tf do
  task :init do
    sh %Q(#{@docker} remote config \
            -backend=gcs \
            -backend-config="bucket=my-first-project-terraform-state-20170309" \
            -backend-config="path=terraform.tfstate" \
            -backend-config="project=elated-bebop-161001")
  end

  task :plan do
    sh %Q(#{@docker} plan)
  end
end

まとめ

俺はなぜあんな無駄な時間を・・ by 三井 寿

teeコマンドの使いどころ

標準入力を標準出力とファイルに出力する

というコマンドです。

なんか使いどころが分からないなーって思ってたけど、コマンドの結果をファイルに書き出してそれを別のコマンドに渡すってのをcircle ci上でやってて、あー、画面上でもこのファイルの中身見たいなーって思って、あっ!こういうときにteeを使いたくなるのか!!!ってなった。

terraformでif文的なものを使ったユースケース例

usecase

EC2インスタンス、ELB、security_groupあたりがまとまったmoduleがあるんだけど、特定の役割のインスタンスにだけiam_roleにSNSFullAccessをつけたい。

設定内容

  • allow_sns_full_accessっていう変数宣言しとく。
variable "allow_sns_full_access" {
  default = false
}
  • 対象のリソースでtrueならcount = 1、falseならcount = 0にする。
resource "aws_iam_role_policy_attachment" "app_sns_full_access" {
  count = "${var.allow_sns_full_access ? 1 : 0}"
  role = "${aws_iam_role.app.name}"
  policy_arn = "arn:aws:iam::aws:policy/AmazonSNSFullAccess"
}
  • 利用したいやつだけ、trueにする
module "app" {
  source = "../modules/app"

  allow_sns_full_access = true
}

結果

+ module.app.aws_iam_role_policy_attachment.app_sns_full_access
    policy_arn: "arn:aws:iam::aws:policy/AmazonSNSFullAccess"
    role:       "app"

AWSのロードバランサーあたりのセキュリティーグループ制限がさくっと緩和できなかった

http://docs.aws.amazon.com/ja_jp/general/latest/gr/aws_service_limits.html#limits_elastic_load_balancer

ロードバランサーあたりのセキュリティグループ 5

ご担当者様

平素よりお世話になっております。
AWSカスタマーサービスの藁品でございます。

折角ご申請いただきましたのに誠に申し訳ございませんが、「Classic Load Balancer」の
「ロードバランサーあたりのセキュリティグループの上限」を緩和することは致しかねます。
理由といたしましては、「ロードバランサーあたりのセキュリティグループ」は
「ネットワークインターフェースあたりのセキュリティグループ」に基づいている為でございます。

なお、下記サービスの上限値の変更を行うことで「ロードバランサーあたりのセキュリティグループの上限」の
緩和をすることも可能でございますが、この変更を行うことによりネットワークインターフェースを使用している
全リソースに影響を及ぼしますのでご留意くださいませ。

1) セキュリティグループあたりのインバウンド/アウトバウンドルール(デフォルト50)を減らす。
2) ネットワークインターフェースあたりのセキュリティグループを(デフォルト5)増やす。

引き続き緩和をご希望ということであれば、ケースにご返信いただくかたちでお知らせいただければ幸いです。
その他ご不明点、ご要望等ございましたら、ご遠慮なくお問い合わせくださいませ。

何卒よろしくお願いいたします。

terraformでsecurity groupをmodule化するときに気をつけること

IPアドレスを定義した共通のsecurity groupを作りたい

あるプロジェクトで外部のIPアドレスなどをまとめたものをsecurity groupとしてmodule化している。このアイディア自体はいいんだけど、環境ごとにこれを利用しようとした場合に失敗する。

例えば、こんな感じでmodulesに外部IPを書いたようなsecurity_groupをつくる。

terraform
├── development
│   └── main.tf
├── staging
│   └── main.tf
├── production
│   └── main.tf
└── modules
    └── external_ips_sg
        ├── security_group.tf
        └── outputs.tf

で、これらを各環境のmain.tfで利用すると、同一のsgを作ろうとするので、security groupの名前を変える必要がある。

module "external_ips_sg" {
  source = "../modules/external_ips_sg"
}

ここで、security groupのnameを変数化しておかないと同じ名前のsecurity groupを作ろうとして失敗する。

解決策

当たり前の話だけど、変数でnameを変えられるようにしておく。

└── modules
    └── external_ips_sg
        ├── variables.tf
        ├── security_group.tf
        └── outputs.tf
variable "env" {}
variable "vpc_id" {}
module "external_ips_sg" {
  source = "../modules/external_ips_sg"

  env = "dev"
  vpc_id = "vpc-xxxxxxxx"
}
resource "aws_security_group" "a" {
  name = "external-ips-${var.env}"
  vpc_id = "${var.vpc_id}"

ちなみに、Security GroupのDescriptionはForce new Resourceなのであとから変更できないので、何も設定しない方がいいかも。ちょっと文言変えたいなーってときにもう変更できない。何も設定しなければ、 Mangaed By Terraformのデフォルト文言が設定される。何か名前付けをしたい場合は、TagのNameを使おう。 あと、vpc_idを入れるのを忘れがち。vpc_idはなければ自動的にdefautl vpcが設定されるのでうまくいってしまう。

awsのsecurity groupにはルールの上限がある

IPアドレスをたくさん追加しようとしたら:scream:

各拠点のIPアドレスを追加してほしいという依頼で、terraform planで問題なかったので、applyしたらエラーが出た。

* aws_security_group.elb_app: Error authorizing security group ingress rules: 
RulesPerSecurityGroupLimitExceeded: 
The maximum number of rules per security group has been reached.
    status code: 400, request id: 88277351-1fc1-4e48-a005-3fe046a8145b

制限があるのを知らなかった。ドキュメントによると制限は50だった。

http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Appendix_Limits.html#vpc-limits-security-groups

多分手動で追加してれば51個目でエラーになるから気づけるけど、terraformだとapplyしてはじめてエラーになる。なので、エラーになった瞬間、該当のsecurity groupの設定は空っぽになってしまう。

気をつけよう :cry:

とくにsecruty groupの変更は要注意

ingressとか書くときに、cidr_blocksと、security_groupsを指定できるけど、つい間違ってcidr_blocksの方にsecurity_groupのidを指定しまったことがあった。これも、planのときは問題ないけど、applyで指定方法が間違ってるってことでエラーになる。

ingress {
...
  cidr_blocks = [
     "${aws_security_group.hoge.id}"
  ]
}

まとめ

validateしたい。terraformにPRチャンスかもしれない。

aws_security_groupのdescriptionはupdateできない

descriptionを気軽な名前で作ってしまって、あとでちょこっと変更したいなーと思って terraform planするとforce new resourceになってしまう。

ドキュメントにも書いてあった。

AWS: aws_security_group - Terraform by HashiCorp

description - (Optional, Forces new resource) The security group description. Defaults to "Managed by Terraform". Cannot be "". NOTE: This field maps to the AWS GroupDescription attribute, for which there is no Update API. If you'd like to classify your security groups in a way that can be updated, use tags.

aws_security_groupは気軽にdescriptionを設定しないようにしよう!識別したいだけなら、tags.Nameを使えばok

今日覚えたやつメモ 2017/1/27

rubyとかrailsとかRSpecのこと

fluentdのこと

dockerとmacでunix domain socketを共有する

ECSでの動的なネットワークポートマッピングとfluentd

固定ポートだとインスタンスの数以上にコンテナが増やせない

ECS上でfluentdを複数コンテナで動かして集約サーバーとして動かそうとしている。 最初だから試しにautoscaling groupで1つのインスタンスだけ立ち上げて、コンテナを2つ(desired count = 2)なサービスで立ち上げようとした。

でも、2台目のコンテナはportが埋まってますよというエラーで立ち上がってくれずにstoppedになってしまう。 そう言われてみればそうだなと。

ALBなら動的なネットワークポートマッピングが可能だが・・

Amazon ECSのELB構成パターンまとめ(ALB対応) | Developers.IOとか、Amazon ECS と AWS Application Load Balancer の組み合わせを試しているメモ - ようへいの日々精進XPとかを見ると動的にポートマッピングしてるからALBを使えばいけそう。しかし、healthcheckはHTTPかHTTPSでやらないといけない。

fluentdの集約サーバーはforwardプロトコルで24224で待ち受けていて、ELBならTCPでのhealthcheckでOKなので問題なかったが、ALBにするならばHTTPでhealthcheckできるようにしないといけない。 healthcheckに関してはfluentdはin httpも受け付けているのでそれでhealthcheckできるようにすればいける。

ただ、healthcheckはいけたとしても、結局forward Output Pluginはforward protocolでhttpじゃないのでダメだ。。

まとめ

集約サーバー側でforwardを使う限りALBは使えない。

fluent-plugin-s3でなかなかs3にアップロードされない

github.com

time_slice_formatの値をexampleに従って、%Y%m%d-%Hにしたんだけど、なかなかs3にアップされない。 buffered_pathには吐かれてるんだけどなかなかS3には上がってこない。

instanceのpoliciyかなーとかs3のbucketのpoliciyかなーとか色々迷っててうーんってハマってたらいつのまにかアップされてた。 どうやら1時間ぐらいたってた。

で、これはなぜかっていうと、time_slice_formatが時間ごとの値になってるからファイル名が変更になるまではs3にアップされない。s3にアップされて欲しいintervalを設定するのが、flush_intervalだった。 flush_intervalはplugin-s3のものではなく、そもそもbuffered-outputのもの。 flush_interval 60sと書けば1分ごとにs3にアップされた。