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を指定しまったことがあったけどそれが防げるようになる💯
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
H/A とは
H/Aとかよく出てくるけどうまくググれなかったので。 多分 High Availability(高可用性)の略
自分で立てたdeisにアプリをdeployするまで
quickstartのConfigure DNSとregister 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と略す。だいたいの比較表。
なんでAWS使わないの?
コンテナでぽんぽんアプリケーションをdeployしたいんだけどそれをAWSでやろうとすると、ECSというサービスを使うことになる。でも、このECSというのがdeployまわりの取り扱いがめんどくさくてちょっといけてない。 で、同じようなコンテナのスケジューリングとかやってくれるソフトウェアでkubernetesというのがあるんだけど、それをAWS上にたてようとするとちょいめんどい。GCPにはGKE(Google Container Engine)というkubernetesのマネージドサービスが最初から用意されてるのでこれを使いたいから。
GCPにおけるprojectという概念
GCPは一つのアカウントに対して、projectという概念を持ってる。たくさんprojectは作ることができる。すごくざっくりだけど、AWSでいうVPCぐらいの単位かも。
GCPにおける地域とゾーン
- 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
- GCPの方の https://developers.google.com/identity/protocols/application-default-credentials に
GOOGLE_APPLICATION_CREDENTIALS
を使えって書いてあった。 - これをすれば、providerの方に
credentials = "${file("account.json")}"
って書かなくてOK
まとめ
俺はなぜあんな無駄な時間を・・ by 三井 寿
teeコマンドの使いどころ
標準入力を標準出力とファイルに出力する
というコマンドです。
なんか使いどころが分からないなーって思ってたけど、コマンドの結果をファイルに書き出してそれを別のコマンドに渡すってのをcircle ci上でやってて、あー、画面上でもこのファイルの中身見たいなーって思って、あっ!こういうときにteeを使いたくなるのか!!!ってなった。
terraformでif文的なものを使ったユースケース例
usecase
EC2インスタンス、ELB、security_groupあたりがまとまったmoduleがあるんだけど、特定の役割のインスタンスにだけiam_roleにSNSFullAccessをつけたい。
設定内容
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のロードバランサーあたりのセキュリティーグループ制限がさくっと緩和できなかった
ロードバランサーあたりのセキュリティグループ 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だった。
多分手動で追加してれば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のこと
class << self
Rubyist Magazine - Ruby 初級者のための class << self の話 (または特異クラスとメタクラス)class << self
で定義したクラスメソッドがmoduleがincludeしたときに呼べない。- moduleなんかscalaのtraitとかswiftのprotocol的なものを思い浮かべてしまってはまってしまった。
- Rails の module ClassMethods がやっている事 - Qiita includeするときは色々必要。
- includeせずに、moduleは名前空間的に使える。
Hoge.Fuga.foo
みたいにいけちゃう。scalaのobjectっぽい感じ。 - クラス・モジュールの概念 Ruby - Qiita
- Railsの正規表現でよく使われる \A \z って何?? - Qiita
- ActiveSupport::Concernの使い方とテスト(RSpec) - Qiita
- Rails のルーティング | Rails ガイド
- routingのことほとんど忘れてた
- Railsのモデルの作成、検索、更新、削除のよく使うメソッドのまとめ - Rails Webook
- activerecordのこともほとんど忘れてた
- Rails Assetの管理についてまとめる - Qiita
- 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 - Qiita 分かりやすかった
- 使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」 - Qiita
- mockするのが超簡単。
allow
とかallow_any_instance
とか - メモ化 Ruby で メモ化カッコカリ( #rubytokai 発表メモ) - 名古屋で数学するプログラマ(仮)
fluentdのこと
- fluent-logger-rubyがunix socket対応したのがつい最近だった。
- fluent-logger-ruby/ChangeLog at master · fluent/fluent-logger-ruby · GitHub
- Macでdmgでtd-agent入れるとunix socketの権限がrootになって通常ユーザーだとconnectできない
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にアップロードされない
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にアップされた。