明日、弊社で SRE Lounge #6 - connpass というイベントがあるのですが、 残念ながら登壇する枠がないのでスライドだけ公開しました。
世間体ではGKEのお話が多い中、Fargateのお話です。
Fargateはクラスタの管理が不要な分、とても楽に運用出来て結構幸せだったりします。
当日興味がある方は是非お声がけください〜。
明日、弊社で SRE Lounge #6 - connpass というイベントがあるのですが、 残念ながら登壇する枠がないのでスライドだけ公開しました。
世間体ではGKEのお話が多い中、Fargateのお話です。
Fargateはクラスタの管理が不要な分、とても楽に運用出来て結構幸せだったりします。
当日興味がある方は是非お声がけください〜。
CoreOSが作ってる、コンテナの脆弱性スキャンツールです。
CVEデータをpostgresに入れて(定期更新される)、それを元にスキャンします。
CoreOSが提供してた、analyze-local-imagesはdeprecatedだったので、
https://github.com/arminc/clair-scanner を使って、ローカルのイメージをスキャンしてみました。
# clair-scannerをインストール $ make clair-scanner/install # clairサーバとpostgresを起動 $ make clair/run # 起動してから10 ~ 20分くらいでCVEデータが更新されます。それまで待機 # postgresに入ってるCVEデータの量を見たいときは $ make check # golang:1.10をスキャン $ docker pull golang:1.10 $ make run IMAGE=golang:1.10 clair-scanner --ip=host.docker.internal golang:1.10 2018/10/04 14:53:39 [INFO] ▶ Start clair-scanner 2018/10/04 14:53:59 [INFO] ▶ Server listening on port 9279 2018/10/04 14:53:59 [INFO] ▶ Analyzing 4f68522e312f5f12340e4b9559e71bba43ef929a10efc9ea6b67b4ff33bfb82e 2018/10/04 14:54:02 [INFO] ▶ Analyzing f56aeae9371b6c04ca4cf57a326b42de8c0d08a5fdebb26ef6b8df324337ea72 2018/10/04 14:54:02 [INFO] ▶ Analyzing 66f389e10325cd0c5f124eba8c5e9b57a9d490a5ddfa02a38f92686d04bda898 2018/10/04 14:54:03 [INFO] ▶ Analyzing 070ca363ac71db895b70015f3c83a1b09a42e74a52b9afdddd15e8aab4dd4a4a 2018/10/04 14:54:06 [INFO] ▶ Analyzing 13d63372391595f4c4086f342f2d0205542ba4530ec95fb8bd069bc7a19dea5e 2018/10/04 14:54:10 [INFO] ▶ Analyzing bf50139cdff0a2429fdf26ed2464b30ec6c88adf24d973ac5a6ed046283ee397 2018/10/04 14:54:20 [INFO] ▶ Analyzing b16a12b1f36856e47d3cea6f2141b970f4b383c6b5302189b3dd452ce9389af9 2018/10/04 14:54:20 [WARN] ▶ Image [golang:1.10] contains 288 total vulnerabilities 2018/10/04 14:54:20 [ERRO] ▶ Image [golang:1.10] contains 288 unapproved vulnerabilities +------------+-----------------------------+-----------------+-----------------------+--------------------------------------------------------------+ | STATUS | CVE SEVERITY | PACKAGE NAME | PACKAGE VERSION | CVE DESCRIPTION | +------------+-----------------------------+-----------------+-----------------------+--------------------------------------------------------------+ | Unapproved | High CVE-2018-6485 | glibc | 2.24-11+deb9u3 | An integer overflow in the implementation of the | | | | | | posix_memalign in memalign functions in the GNU C Library | | | | | | (aka glibc or libc6) 2.26 and earlier could cause these | | | | | | functions to return a pointer to a heap area that is | | | | | | too small, potentially leading to heap corruption. | | | | | | https://security-tracker.debian.org/tracker/CVE-2018-6485 | +------------+-----------------------------+-----------------+-----------------------+--------------------------------------------------------------+ | Unapproved | High CVE-2018-1000001 | glibc | 2.24-11+deb9u3 | In glibc 2.26 and earlier there is confusion in the | | | | | | usage of getcwd() by realpath() which can be used | | | | | | to write before the destination buffer leading to | | | | | | a buffer underflow and potential code execution. | | | | | | https://security-tracker.debian.org/tracker/CVE-2018-1000001 | +------------+-----------------------------+-----------------+-----------------------+--------------------------------------------------------------+ まだまだCVEの詳細な情報が流れてくるが、量が多いため割愛します # clairサーバのログが見たいときは $ make tail # clairサーバとpostgresのコンテナ削除 $ make clair/rm
プロダクトに入れる前には脆弱性スキャンしたほうが良いですね。
High/Medium/Low/Negligible/Unknown
で区別してくれるので、最低でもHighはチェックしましょう。
また、clair-scannerは -w
でwhitelist指定出来るみたいなので、自前でwhitelistを持ってると良いかもですね。
ただこれらもイメージpush先のリポジトリ側でスキャンされるのが理想であり、デファクトスタンダードになるでしょう。
GCRはα版がすでに提供されており、ECRもいずれ自動でスキャンして通知してくれるでしょう。
FOLIO特有のロジックが入ってますが、参考になればと思います。
FOLIOのterraformはこんなディレクトリ構成になってます。(一部抜粋)
── envs ├── mobile-prod ├── provider.tf ├── ecs ├── iam ├── route53 └── vpc ├── backend.tf ├── provider.tf ├── route_table.tf ├── variables.tf └── vpc.tf ├── mobile-stg ├── sandbox ├── web-prod └── web-stg ── global.tfvars ── backend_tf_template
global.tfvarsは全体の変数定義。(CIDRやIPリストなど)
provider.tfは各環境内の設定です。(subnet_idなど)
terraformのオペレーションはMakefileで管理されてます。
下記のコマンドで特定のディレクトリに対してplanが実行できます。
$ make plan CONFIG=envs/mobile-prod/vpc # initializeした後、terraform plan -var-file=global.tfvarsを打ってるだけです
No changes. Infrastructure is up-to-date
というワードがなかったら出力-n
で並列数を選べる-t
でディレクトリ指定も出来る$ ./check-all-plan --help Usage of ./check-all-plan: -n int 並列数 (default 20) -t string ターゲット。ex: -t mobile-stg $ ./check-all-plan check-all-plan: 2018/09/21 13:49:47 [INFO] Check start...... check-all-plan: 2018/09/21 13:52:37 [INFO] Result: you need check below make plan CONFIG=envs/mobile-stg/kinesis make plan CONFIG=envs/web-prod/elasticache make plan CONFIG=envs/web-prod/teleport make plan CONFIG=envs/web-stg/iam make plan CONFIG=envs/web-prod/instances make plan CONFIG=envs/web-stg/network make plan CONFIG=envs/web-stg/kinesis
package main import ( "flag" "fmt" "log" "os" "os/exec" "path/filepath" "strings" "sync" ) const ( APP = "check-all-plan" ) var ( // parallelNum ... 適当に20並列にしてる。 parallelNum = 20 // target ... envs/mobile-stgのように、ターゲット単位で指定出来るようにしてる target string ) // init ... set flag func init() { flag.IntVar(¶llelNum, "n", 20, "並列数") flag.StringVar(&target, "t", "", "ターゲット。ex: -t mobile-stg") flag.Parse() } func main() { log.SetOutput(os.Stderr) log.SetPrefix(APP + ": ") os.Exit(Run()) } // Run ... 実行 func Run() int { // 結果を格納するslice var results []string // skipするディレクトリ。必要だったら修正してね! ignorePlan := []string{ "sandbox", "web-prod/apps", "web-stg/apps", } // planを実行するdirの取得 targetDirs := GetDir("envs", ignorePlan) // default: 20並列 var m sync.Mutex wg := &sync.WaitGroup{} semaphore := make(chan struct{}, parallelNum) log.Println("[INFO] Check start......") for _, targetdir := range targetDirs { wg.Add(1) semaphore <- struct{}{} go func(d string, m *sync.Mutex) { defer func() { <-semaphore defer wg.Done() }() out := RunTerraformPlan(d) if !strings.Contains(out, "No changes. Infrastructure is up-to-date") { m.Lock() defer m.Unlock() results = append(results, d) } }(targetdir, &m) } wg.Wait() log.Println("[INFO] Result: you need check below") for _, result := range results { fmt.Printf("make plan CONFIG=%s\n", result) } return 0 } // RunTerraformPlan ...make plan CONFIG=xxxxを実行する func RunTerraformPlan(targetDir string) string { dir := fmt.Sprintf("CONFIG=%s", targetDir) // make plan打って差分 or 問題が起こったかを検知したいので、 // ここではエラーハンドリングしない out, _ := exec.Command("make", "plan", dir).Output() return string(out) } // GetDir ...envs/配下の実行対象のディレクトリを取得する func GetDir(parentDir string, ignorePlan []string) []string { var dirs []string err := filepath.Walk(parentDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 隠しディレクトリはskip if info.IsDir() && strings.HasPrefix(info.Name(), ".") { return filepath.SkipDir } // ignorePlanに書かれてるディレクトリ以下をskip for _, v := range ignorePlan { if info.IsDir() && path == fmt.Sprintf("envs/%s", v) { return filepath.SkipDir } } // backend.tfとprovider.tf以外のtfを持つディレクトリを抽出 switch info.Name() { case "provider.tf": break case "backend.tf": break default: if strings.HasSuffix(info.Name(), ".tf") { dirs = append(dirs, filepath.Dir(path)) } } return nil }) if err != nil { log.Printf("[WARN] filepath.Walk: %s\n", err) } return getUniqueDirs(dirs) } // getUniqueDirs ...重複したデータを削除。targetが指定されてたら指定されたものだけをチョイス func getUniqueDirs(src []string) []string { dst := make([]string, 0, len(src)) m := make(map[string]bool) for _, s := range src { if _, ok := m[s]; !ok { m[s] = true // targetが指定されたら、指定されたものだけをappendする if target != "" { if strings.HasPrefix(s, fmt.Sprintf("envs/%s", target)) { dst = append(dst, s) } } else { dst = append(dst, s) } } } return dst }
些細ではありますが、出したPRがmergeされました。
開発方法よくわからないなぁ。と思う人が多いと思うので、私が実際に行ったことを書いて見ます。
# go get。今回は本家で修正して試して、その差分をあとでfork先に適用する感じ。 # この二度手間めんどくさいときは、$GOPATH/src/github.com/terraform-providersにforkしたやつをgit cloneしたほうが良い $ go get -u github.com/terraform-providers/terraform-provider-aws # よしなに修正 # buildする $ cd $GOPATH/src/github.com/terraform-providers/terraform-provider-aws $ make build # このときgofmtしろってエラーが出たらgoのバージョンが低い可能性があります # 最新にしてまたやってみましょう # buildしたものを利用する # 既存のproviderを削除して自前のbuildをcopy。copyしたらinitを打つ $ cd terraform-repository/xxxxx.tfファイルがあるところ $ rm -f .terraform/plugins/darwin_amd64/* $ cp $GOPATH/terraform-provider-aws .terraform/plugins/darwin_amd64 $ terraform init # planとapply。global.tfvarsを設定してるバージョンです $ terraform plan $ terraform apply -var-file=global.tfvars # debugログの出力。めっちゃ便利 # TF_LOGでloglevelが設定できます # TF_LOG_PATHでファイルに出力できます(追加保存) $ TF_LOG=1 TF_LOG_PATH='/tmp/terraform.log' terraform apply -var-file=global.tfvars
PR出す際に貼らないといけません。 結構な権限を持ったaccess_key, secret_keyが必要です。
# 移動してtest。TESTARGSに設定するのはmethod名。*で正規表現マッチが使えます $ cd $GOPATH/src/github.com/terraform-providers/terraform-provider-aws $ AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=xxxx make testacc TEST=./aws TESTARGS='-run=TestFetchRootDevice*' # このときgofmtしろってエラーが出たらgoのバージョンが低い可能性があります # 最新にしてまたやってみましょう
SREとして担当してたモバイルアプリもリリース出来て落ち着いたので書いてみようと思います
Fintech
[https://folio-sec.com/:title]
terraform操作周りのリファクタ
terraform xxxx
を打てるようにしたterraform module作った
モバイルのインフラ構築、運用
セキュリティ周り
fargateで色々サービス立てた
入社したとき、terraformを打つまでにいろんな障壁があることを経験したので、即座に自動化タスクに取り組みました。
前職の影響からか、makeで色々と出来るようにし、今ではその障壁がなくなりました。
make helpはこんな感じです。
弊社で動かしてるansibleやpacker周りも自動化したいです。 が、ansibleに関してはそもそも無くす(減らす)動きをしていく気持ちです。
「まだsshしてるの?」「開発者が開発に集中」という気持ちで進めてました。
結果sshはしなくなったので、セキュアにもなりましたし、インフラ運用コストも下がって楽になりました。
chatopsや、エラーログやアラートなどもslackで対応出来るようになったのもgood。
今回初めてやったのですが、SRE本にも書かれてる Launchチェックリストを元に、構成や仕組みを一から全てチェックしました。
これで安全にリリース出来るよねという合意と、サービスにあまり携わらなかった人もシステム構成を把握出来た(非属人化への一歩)のは良かったです。
モバイルアプリ用APIは、EC2で行こうという話だったのですが、
それだと技術的進歩が望めないので、ECSを導入しました。
最初はEC2 typeで導入したのですが、途中でfargateが登場したので載せ換えました。
挙動、特にEC2 typeにはないオプション周りで悩まされた記憶があります。
運用と拡張しやすいようなterraform module構成を意識したので、すぐにfargate構成が出来るようになりました(今は4, 5サービスくらい動いてます)
fargate対応してないので、やむなくEC2 typeでmodule化。
slack interactive componentsを使っており、Goで書かれてます。
auth周りも実装してるので、config.tomlのプロジェクト設定にある指定された人しかdeploy出来なくなってます。
モバイルアプリのメンテナンス画面切り替えもこちらでやってます。
Fintechなのに、スピード感もって新しいことが出来るいい会社だなぁと思いました。
個人としては運用しやすい、拡張しやすいコードを意識して出来たのとも良かったです。
(そのため、コメントを中心にドキュメント(README.md)や構成図(draw.io)を充実させた。)
インフラ周りが落ち着いたら、microserviceアーキテクチャの実装や、認証認可のサービス実装など色々やりたいですね。
GitHub - suzuken/yamlssm と同様にtomlでも出来るようにしました。
やってることは
使い方はREADMEにも書いてますが、下記のように使います
package main import ( "fmt" toml "github.com/sioncojp/tomlssm" ) type Config struct { User string `toml:"username"` Password string `toml:"password"` } func LoadToml(c string) (*Config, error) { var config Config if _, err := toml.Decode(c, &config, "ap-northeast-1"); err != nil { return nil, err } return &config, nil } func main() { conf, err := LoadToml(` username = "test" password = "ssm://password" `) if err != nil { fmt.Println(err) } fmt.Println(conf) }
# config.toml username = "test" password = "ssm://password"
package main import ( "fmt" toml "github.com/sioncojp/tomlssm" ) type Config struct { User string `toml:"username"` Password string `toml:"password"` } func LoadToml(c string) (*Config, error) { var config Config if _, err := toml.DecodeFile(c, &config, "ap-northeast-1"); err != nil { return nil, err } return &config, nil } func main() { conf, err := LoadToml("config.toml") if err != nil { fmt.Println(err) } fmt.Println(conf) }
GKEを触ってみたかったので実際に触ってみた。
ref: クイックスタート | Kubernetes Engine のドキュメント | Google Cloud
ソースはこちら
ただの"Hello World"を返すGoのコード。
kubernetes特有で、 /healthz
がヘルスチェック用のpathなんですね。
READMEの通りにやれば動くと思います。
流れとしては、
- gcloudコマンドインストール、アカウント設定、ログイン(project_idゲット。とcredential(JSON)を作ってゲット) - gcloudでkubectlインストール - gcloudでGCRにイメージを保存するために、docker-credential-gcrインストール - GCRにイメージpush - gcloudでクラスタ作成 - kubectlでクラスタにdeploy - curlでアクセス出来る