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

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

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にアップされた。

terraformのecs_registry.repository_urlにhttpsがついてるのを消す

バグだったようだ。mergeされたのでそのうち修正版がリリースされるはず。それまでは"${replace(aws_ecr_repository.test.repository_url, "https://", "")}"でしのぐ。

github.com

追記

terraform v0.8.5 で取り込まれた。

ECS(Amazon EC2 Container Service)がなんなのかやっと理解できてきた

いざ自分で一から作ってみると・・・

今まで人が作ったECS上でサービスの作成とか更新とか、タスクの更新とかやってきてなんとなく使えてたけど、 いざ自分で一から作ってみるとそれぞれの要素がどうなってるのか何も分かってないことに気づいた。 今まではECSでclusterとserviceを作っていって、clusterとかserviceに対して全体的なネットワークとかインスタンスの数とかの設定をしていくものだと思ってたけどそうじゃなかった。

ECSの役割

ECSがやってるのは主にスケジューラ(どのインスタンスにどのコンテナをいい感じに配置するか)がメインで、コンテナーインスタンスamazon-ecs-agentをおいてecsサービスのAPIを叩かせている。

じゃあ、全体的なネットワークの設定はどうするのかっていうと、ただのAuto Scalingという機能を使っていつも通りインスタンスを立ち上げてるだけ。なんならAuto Scalingを使わずに手動でAMIからインスタンスたちあげて、それをECSにコンテナーインスタンスとして登録したってよい。

GUIのウィザードでECSを作ろうとすると至れり尽せりでいい感じに色々なものができる。で、今やろうとしてるのはterraformで一から作ろうとしてるところだから、必要なものがなんなのか、どんな機能の組み合わせでできてるのかが分からないとterraformを書くことができない。進みは遅いし苦しいけど勉強にはなる。

ecs-agentの起動方法

会社の既存プロジェクトとかterraformのexampleを見てると、launch_configurationでuser_dataで結構がっつりシェルが書いてある。でも、AWS Consoleでウィザード経由で作ると、これだけ。

#!/bin/bash
echo ECS_CLUSTER=hoge >> /etc/ecs/ecs.config

一体この違いはなんなんだ・・ってはまったけどlinuxのdistributionが違ってた。見てたexampleとかはcoreOSとかubuntuとか。一方で、ウィザードだとamazon-linuxのecs-optimizedてやつ。だから色々書かずに済んでたんだ。 amazon-ecs-agentのREADMEにその辺のことが書いてあった。

まとめ

ちょっとすっきりした

terraformで作るときに参考にするもの

社内のプロジェクトのを参考にできる恵まれた環境なのでまずはそれを見る。

他にもterraform公式リポジトリにあるexampleも参考になった。 terraform/examples at master · hashicorp/terraform · GitHub

インフラエンジニアになっていきたいと思った理由

今の思い

ここでいうインフラエンジニアはハードディスクがとかケーブルがとかガチなやつじゃなくて、各種スタックを組み合わせて構築するぐらいのやつ。 プログラミング歴=ほぼ社会人歴で、iOS開発がメインで一番長かった。そのあとScalaでサーバーサイド。で、今はインフラできるようになりたいと思ってる。

iOSエンジニアとしてサーバーサイドへの引け目

iOSは動く画面を作るのが楽しかった。ただ、すぐに太ってしまうUIViewControllerやたくさんのManagerクラスなどクソコードもいっぱい生み出してきてしまった。なので、Clean Architectureとかもっといい設計はないかとかを考えるのが楽しかった。 でも、なんとなくサーバーサイドとかWebのことが分からないことに引け目をずっと感じていたが、プロジェクトの状況で幸か不幸かサーバーサイドが足りないとなり、がっつりサーバーサイドの開発に携わることもできた。 はじめはWebの基本的なこととか効率的なSQL、DB設計とかが分かってなかったりScalaの関数型的な書き方が分からず苦しかったけどそこそこできるようになった。

解決できる領域の広さ

でも、サービスを運営してると障害起きたりしてもインフラ周りだと手も足も出せないのが悔しく、あと、何かXXXという問題を解決したいっていう時に、アプリケーション側で色々苦労して解決できたとしてもインフラ側でそれを何十倍の効果で解決しちゃったりする場面があって、それも悔しかった。なので、出来るようになりたい。

あとアプリもサーバーサイドも保守しやすいとか変更しやすいとかの設計を考えるのは楽しいし、実力もまだまだなんだけど、普通ぐらいのレベルにはなったなという気がしてる。

新しいことを覚える楽しさ

インフラは0だからそれを普通にする方がワクワクする。ほんの1ヶ月前は分からなかったことが分かるようになる感覚がたまらない。 ドラクエ3で言えば転職をしてレベルが1に戻るのを繰り返してる。 最初は簡単なことも分からず進捗も悪くてかなり苦しいんだけど、だんだんできるようになっていくのが好き。マゾなのかもしれない。

terraformというかHCLでif文使いたい

基本的にはサポートされてない。 けど、こちらの方のやり方を参考にして、こんな風にすればできるっちゃできる。

If you set count to 1 on a resource, you get one copy of that resource and if you set count to 0, that resource is not created at all.

blog.gruntwork.io

trueが1、falseが0になることを利用して、countを0または1にするっていうちょっとdirtyな感じ。

resource "aws_eip" "example" {
  count = "${var.create_eip}"
  instance = "${aws_instance.example.id}"
}

module "frontend" {
  source = "/modules/microservice"
  service_name = "frontend"
  ami = "ami-abcd1234"
  instance_type = "t2.medium"
  create_eip = true
}
module "backend" {
  source = "/modules/microservice"
  service_name = "backend"
  ami = "ami-efgh5678"
  instance_type = "m4.large"
  
  create_eip = false
}

なるほどねー。でも、公式にのってないようなやり方はあんまりやりたくないタイプ。

追記

terraform 0.8から公式にsupportされました🎉

resource "aws_instance" "web" {
  subnet = "${var.env == "production" ? var.prod_subnet : var.dev_subnet}"
}

CONDITION ? TRUEVAL : FALSEVAL

sts:AssumeRoleとは

AWS STSが分からなかった

AWS Security Token Serviceのこと。

aws stsググる

このドキュメントが出てきてざーっと読んでなんかCLIしたいときに使うのか?みたいなぐらいでしっくりこなかった。

docs.aws.amazon.com

EC2サービスを信頼するという概念

この記事が分かりやすかった。

さて、ここで封印した記憶を呼び起こします。「EC2サービス」を信頼することにより、「EC2がAssumeRoleを行えるように」なります。

dev.classmethod.jp

なるほどねー。