チカクのまごちゃんねるというサービスでMySQLをAuroraに移行したお話です 中間のpendingタイムを除けば、仕様把握含め2ヶ月くらいですね。 一度prodのaurora移行にチャレンジしたとき、AWS DMS
を使ってデータを移行しようとしたら、インデックスが正常に移行できずに失敗したという背景がありました。 また同じことする不安を持つより、今回は下記のような判断にしました
github issueをベースに進めました。会議で話した内容、手順など全てがこのissueに載ってます。 メンテナンスモードがないため、移行中にリクエストがきてデータ欠損する懸念がありました。 またサービスダウンが発生するので、その時の表示がユーザに不親切な状態になります。 対策としては、s3hosting / ALBを介したapi の2つメンテナンスモードを用意して、slackからON/OFF出来るようにしました。
旧VPCと新VPCが存在する環境なので、そこのpeering状況の確認が必要です。 動作チェックとして、どちらのVPCからもAuroraリードレプリカに対してアクセスできるかを確認しました。 これをすることで、RDSを切り替える際のdeployを省き、 またTTL=0(接続するたびにドメイン解決)にすることで、インフラ側で接続先を切り替えることができます。 その分速度は若干遅くなりますが、運用メリットのほうが大きいです。 terraformで書くとこんな感じですが、terraformだとTTL=0にセットできないので、コンソールで設定してterraformに反映します。 コマンド自体は簡単で、気をつけるのは 大きなDBに対し事前にdumpがどれくらいかかるか検証したほうが良いでしょう。 DB名を変更するためには、移行先のDBを用意し、RENAMEコマンドで対応しました。 RENAMEはcopyが走らないのですぐ終わります。 こちらは然るべき方からよしなにやりました。 実際の手順書は下記となりました。(具体的な内容と、題目も若干伏せます) 5.7から対応してるので、一旦5.6 -> 5.7にmysqlを上げる必要がありました。 stgだけスナップショットに1時間かかりました。 これはインスタンスサイズが低すぎて時間がかかった可能性がありますね。 2時間経っても終わらなかったです。 こちらはawsに問い合わせたところ、aws側のトラブルだと判明し、そこから迅速に対応していただけました。 一時的にアップデートした5.7のときにdefaultパラメータを利用したのですが、 max_connections値が少なく2日目に行くまでに若干エラーが発生しました。 defaultなので変更できず、上限値と通常のリクエストを見て1日は許容という判断になりました。 状況把握をするのにかなり時間を費やしたと思います。 terraformに残ってなかったので、awsのコンソールとgithubを見ながら現状を把握するのはとても大変でした。 そこからissueを立て、流れや手順を書いた後、関係者に的確にレビューしていただいたので、スムーズに終わることが出来たのがとても感謝です!はじめに
TL;DR
Why
Aurora?
その他の改善項目
過去に1回チャレンジして失敗している
How
進め方
移行中のデータ欠損対策
peeringの確認
RDS接続用のprivateドメインを作ってインフラ側で制御する
/* private ドメイン作成 */
resource "aws_route53_zone" "private" {
name = "private"
vpc {
vpc_id = data.aws_vpc.xxxx.id
}
}
/* DBのwriterの例 */
resource "aws_route53_record" "rds-xxx-writer" {
zone_id = aws_route53_zone.private.zone_id
name = "rds-xxx-writer"
type = "CNAME"
ttl = "0" # providerが対応してないため、1に設定 -> 手動で0に設定し、terraformも0にする対応をしてる
records = [data.aws_rds_cluster.xxx.endpoint]
}
dumpの手順
--single-transaction
でロックかけないようにdumpしましょう。-- DB全体の容量確認
SELECT
table_schema, sum(data_length) /1024/1024 AS mb
FROM
information_schema.tables
GROUP BY
table_schema
ORDER BY
sum(data_length+index_length) DESC;
-- 今回の移行対象 + 一番大きなDBでdumpして時間を測る
-- UserName / HostName / xxx は適宜変えてください
mysqldump -uUserName -hHostName -p --quote-names --opt --single-transaction --skip-column-statistics --hex-blob -R --order-by-primary xxx > xxx.sql
-- ちなみに流し込む時のコマンド
-- UserName / HostName / Password / DBName / xxx は適宜変えてください
mysql -uUserName -hHostName -p Password DBName < xxx.sql
DB名を変更する手順
-- NewDBName / OldDBName は適宜変えてください
create NewDBName
RENAME TABLE OldDBName.TableName TO NewDBName.TableName
ユーザ通知や社外調整
手順書
# 事前作業
- ユーザ作成
- auroraリードレプリカを作成(2日目)
# 当日作業1日目
- 1. s3 hostingをメンテにする
- 2. 各batch止める
- 3. Lamdaのトリガーを無効化する
- 4. ALBをメンテにする
- 5. 動画のキューが0なことを確認
- 6. スナップショットを取る
- 7. 5.6 -> 5.7にmysqlをあげる
- 8. 確認作業
- 9. Lamdaのトリガーを有効化する
- 10. メンテ解除
# 当日作業2日目
- 1. s3 hostingをメンテにする
- 2. 各batch止める
- 3. Lamdaのトリガーを無効化する
- 4. ALBをメンテにする
- 5. 動画のキューが0なことを確認
- 6. mysqldump
- 7. auroraをmasterに昇格
- 8. dumpファイルをRDSにDBを流し込む
- 9. DB名変更
- 10. Route53で一気にドメイン切り替える
- 11. DB名、ユーザ、パスワード、ホスト名変更デプロイ
- 12. 確認作業
- 13. Lamdaのトリガーを有効化する
- 14. メンテ解除
想定外のトラブル集
5.6だとAuroraリードレプリカが作成できない
スナップショットに時間がかかった
prodが5.7になるまで時間がかかった
default parameterだと設定値が変更できない
最後に
株式会社チカクに入社しました
2021/3月に株式会社チカクに転職しました。
あまり入社エントリーは書かないのですが、1ヶ月経った節目として書いてみます。
自己紹介
元プロゲーマーでアジアチャンピオンです。
会社歴としては、下記の通りです。
会社名 | 職務 |
---|---|
ビットアイル | ハードウェア周り、ネットワークエンジニア |
リブセンス | インフラ / サーバサイド |
エウレカ | SRE |
fluct(VOYAGE GROUP) | SRE |
FOLIO | SRE |
タイミー | SRE/DRE/コーポレートエンジニア |
チカク | SRE |
チカクってどんな会社?
まごちゃんねる というサービスを提供してます。
"撮れたてのまご動画を スマホから実家のテレビへ"
Wi-Fi機能付きのテレビで見れるフォトフレーム を(今は)提供してる感じです。
なぜチカク?
@yoshihiro_1992と前職のタイミーで知り合い、副業でチカクを手伝ってた時期がありました。
去年の12月からtwitterで転職先探してたところに改めて声をかけてもらいました。
そこから色々な社員の方と話す機会をいただいて「とても素敵だな〜」と思う反面、「今後の戦略どうなってるの?」といったところが正直不安でした。
が、 @yoshihiro_1992がいるということは、どんどん良いサービスに発展していく道筋があるんだろうなと思って、海外事例とか色々と調べて働きたいなと思いました。
調べたお話を少しだけ
ref: https://coralcap.co/2019/11/agetech-startups/
高齢者向けのスタートアップはこんな感じ。
高齢者が多い日本ではあまりテック化されてないので、まだブルーオーシャンかと思います。
また、まごチャンネルの技術は高齢者以外にも適用できるので、戦略次第でさらに幅が広くなるのが強みだと思います。
その他の決め手の要因
知人が似たようにデバイスを提供する会社で働いてて、ユーザ(Customer)の声がストレートに聞ける環境はとても楽しそうだと思ったのが1つ。
デバイスを持ってる会社は強いし、技術的に面白そうだと思ったのも1つ。
あと自分自身、親とは色々あってほぼ無縁だった人間なのですが、この年齢(33歳)になるにつれ、親も老いていき。
数年振りに会った時、寿命というワードが思い浮かぶくらい老けたのを見て、そこから親孝行を徐々にし始めた経緯もあります。
私ですら親孝行をし始めるなら、世の中はもっといる。から手助けになりたいなぁと思うことが増えた次第です。
入社後の話
環境
今はフルリモートで働いてます。
自粛明けたらある程度出社しようかなと思いますが、自宅のほうがスピード出そうなので悩み中です。
朝は10:00開始で今まで裁量労働だった身としてはキツいイメージでしたが、この歳(33)になると起きれてしまうので私は苦じゃないですね。
やってること
大きなポイントだと
費用としてはEC2(オーバースペックに運用)が大きくかかっており、オートスケーリングやCI/CD周りなどを考慮すると、なるべくコンテナ化したい気持ちで、Fargateに移行するための検証やmodule作成を行ってます。
EKSは一旦Fargate挟んでから考えます。
deployも、slackからdeployする仕組みを用意しました。
コンテナ移行するための材料は揃ったので、今は既存と新規アーキテクチャの全体設計図を書いてます。
terraformをapplyしても差分が出てたり、そもそもterraform化されてない部分も多いので、落とし込みたいですね。
コストもめちゃくちゃかかってるな〜と思って、S3の料金を削減する案もreviewしてもらい、いつやるかまでのissueを切ったりしました。
datadogもコストがかかっており、また契約更新のタイミングなので、精査して手を打っているところです。
楽しいこと
既存アーキテクチャはよく出来てるので勉強になることが多いです。
あとユーザの指摘がわかりみが深いので、頑張らないと!という気持ちになります。
大変なこと
あまりドキュメントが無かったり、terraformでコード化されてなかったりリバースエンジニアリングが大変です。
awsアカウントとある程度のterraformをポンと渡されて「どうぞ!」みたいな状態だと私は思ってます笑
slackやgithubやnotionをかなり漁って理解することができてますが、それでも背景が見えづらい箇所はかなりきついですね。
あとはservice、cron、batch、job queue周りを安全に移行するため、中で何がどう動いてるか、railsやネイティブ、端末側のコードを読んで把握していく作業をしてます。
複雑ではありますが、今行ってる全体アーキテクチャ図に落とし込んでしまえば、後から入る人でもすんなり参画できると思います。
私自身、1ヶ月経った今はほぼ把握できてる(と思います。)
今後やっていきたいこと
コンテナ化はもちろんのこと、既存システムを全てterraformに落とし込んだり、SLOをさらによく定義したり。
スピード改善にも着手していきたい気持ちです。
データマネジメントも改善の余地が多くあるので、、DRE業務も合わせて行っていきたいですね。
AWS S3 Glacierの取り出しをRubyで検証してみる
What
- S3のGlacier, Glacier Deep Archiveがあるが、実際に取り出したことがあまりない
- 取り出すにはリクエストが必要
- 今働いてる chikaku がrailsをメインにしてるので、rubyで検証しました
Glacier周りの料金や仕様
- S3にあるオブジェクトは保存するのにお金がかかる
- Glacierは保存するのがめちゃくちゃ安いが、取り出すにもリクエストにもお金がかかるし、時間もかかる
ref: https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/restoring-objects.html
やったこと
- s3にオブジェクト(動画)を保存
- 動画は https://www.home-movie.biz/free_movie.html から拝借しております
- 下記のように3種類ストレージクラスを用意し、ダウンロードしてみる
- STANDARD
- Glacier...今回は迅速取り出し(Expedited)をやります
- Deep Archive
今回使ったコード
Gemfile
source 'https://rubygems.org' gem 'aws-sdk-s3'
main.rb
### 秘匿情報はxxxxxで隠してます ### awsにはsaml認証経由でアクセスしてます require 'aws-sdk-s3' ENV['AWS_REGION'] = "ap-northeast-1" def main(object_key) bucket_name = 'xxxxx' local_path = "./#{object_key}" role_arn = "arn:aws:iam::xxxxx:role/xxxxx" role_session_name = "xxxxx" sts_client = Aws::STS::Client.new(profile: 'saml') role_credentials = Aws::AssumeRoleCredentials.new( client: sts_client, role_arn: role_arn, role_session_name: role_session_name ) client = Aws::S3::Client.new(credentials: role_credentials) s3_download(client, bucket_name, object_key, local_path) end # glacierはrestoreしてからダウンロードする def s3_download(s3_client, bucket_name, object_key, local_path) # storageクラスによってはrestoreする必要があるので、取得する resp = s3_client.list_objects_v2( { bucket: bucket_name, prefix: object_key, } ) case resp.contents[0].storage_class when "STANDARD" when "GLACIER" restore(s3_client, bucket_name, object_key, local_path, "Expedited") when "DEEP_ARCHIVE" # こいつだけrestoreが終わるまで下記エラーが出て実行できない # Error getting object: Object restore is already in progress restore(s3_client, bucket_name, object_key, local_path, "Standard") end download(s3_client, bucket_name, object_key, local_path) rescue StandardError => e puts "Error getting object: #{e.message}" end # glacierはrestoreする def restore(s3_client, bucket_name, object_key, local_path, tier) s3_client.restore_object( { bucket: bucket_name, key: object_key, restore_request: { days: 1, glacier_job_parameters: { # https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/restoring-objects.html # Standard...標準取り出し # Bulk...大容量取り出し # Expedited...迅速取り出し。deep archiveは対応してない tier: tier }, }, } ) end # download def download(s3_client, bucket_name, object_key, local_path) s3_client.get_object( response_target: local_path, bucket: bucket_name, key: object_key ) end # 動作確認用 def list_bukets(s3_client) puts s3_client.list_buckets end # 動作確認用 def list_objects(s3_client, bucket_name) s3_client.list_objects(bucket: bucket_name).contents.each do |object| p object.key end end # STANDARD main(mov_hts-samp001.mp4) # Glaicer main(mov_hts-samp002.mp4) # Deep Archive main(mov_hts-samp003.mp4)
実行
$ bundle install --path vendor/bundle # 最初はglacier両方ともエラー $ bundle exec ruby main.rb Error getting object: Object restore is already in progress Error getting object: Object restore is already in progress # 1分後くらい $ bundle exec ruby main.rb Error getting object: Object restore is already in progress # Glacierはダウンロードできたけど、Deep Archiveはだめだった # 30分すぎても取り出せないので諦めた
結論
- standard...すぐ取り出しができる
- glacier...Expeditedはコストがかかるが、1分以内には取り出しができた。
- deep archive...Expeditedは対応してないので、Standardで実行。restoreにとても時間がかかった(というか諦めた)
- ちなみにリクエストしたら24時間は気軽に取り出せるみたい
terraform 0.13でローカルproviderを利用する方法
はじめに
terraform 0.13からproviderが https://registry.terraform.io/ から配布され、下記のように記述すると自動取得するようになりました。
# ref: https://www.terraform.io/upgrade-guides/0-13.html#explicit-provider-source-locations terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 2.12" } newrelic = { source = "newrelic/newrelic" version = "~> 2.1.1" } } }
0.13でprovider開発などでローカルで利用する場合や、まだregistryに上がってないcommunity providerはどうするのか?
0.12では ~/.terraform.d/plugins に置いてました。
しかし、これをやっても読み込めずエラーが出てしまいます。それを私が実際にどうやって解決したかを書きます。
他に良い方法を知ってる方がいらっしゃれば別途記事を書いてもらえると有難いです。
今回は https://github.com/anasinnyk/terraform-provider-1password を例として紹介します。
1. providerのバイナリを配置します
### https://github.com/anasinnyk/terraform-provider-1password/releases/tag/1.1.0 ### をダウンロードして下記のように配置します。このファイル名形式じゃないとダメです /Users/sion_cojp/.terraform.d/plugins/registry.terraform.io/hashicorp/onepassword/1.1.0/darwin_amd64/terraform-provider-onepassword_v1.1.0 ### hashicorp じゃないと下記エラーが出ます # Error: Failed to query available provider packages # # Could not retrieve the list of available versions for provider # anasinnyk/onepassword: provider registry.terraform.io/anasinnyk/onepassword # was not found in any of the search locations # # - /Users/sion_cojp/.terraform.d/plugins
2. required_providerを書きます
$ vim backend.tf terraform { backend "s3" { 必要であれば書く } required_providers { onepassword = { source = "hashicorp/onepassword" email = "sion_cojp@xxxxx" password = "xxxxxxx" secret_key = "xxxxxx" subdomain = "organization_name" } } }
3. 1passwordにcliからsign inしてセッションキーをexportします
### signinしたらOP_SESSIONが出力されます $ op signin organization_name.1password.com sion_cojp@xxxxx export OP_SESSION_organization_name="xxxxxxxx" ### OP_SESSION_myに変えてexportする必要があります。理由はわかりませんが、やらないと下記エラーのどれかがでます # 1. Error: email, password or secret_key is empty and environment variable OP_SESSION_my is not set # 2. Output: [ERROR] 2020/10/13 17:07:29 You are not currently signed in. Please run `op signin --help` for instructions export OP_SESSION_my="xxxxxxxx"
4. applyやshowをします
$ vim main.tf data onepassword_user this { email = "sion_cojp@xxx" } ### initでpluginの場所を指定するのがポイント。絶対パスじゃないとダメです $ terraform init -plugin-dir=/Users/sion_cojp/.terraform.d/plugins $ terraform show # module.users.data.onepassword_user.this: data "onepassword_user" "this" { email = "sion_cojp@xxxxxx" firstname = "Shohei Koyama" id = "xxxxx" state = "A" }
Reference
タイミーのEMとしてどのようにマネジメントしているか
はじめに
16歳からゲーマーとしてずっとチームマネジメントをやっており、
自身の強みなので本腰入れてやりたいという思いで1月にタイミーに入社しました。
結果、今はSREマネージャー(2人) + BI基盤チームマネージャー(3人) + コーポレートエンジニア(3人)として働いてます。
そんなにメンバーはいませんがマネージャーはやれてると思ってるので、色々と書いてみたいと思います。
そんなに掛け持ちして大丈夫なの?
スタートアップは仕方ないです。ぜひ強い方募集してます。
自分の脳内の長期計画は全てアウトプットしてチームと精査してるので出来る人がやればいい状態です。
私自身マネジメントしながら、業務でコードも書いてレビューもしてるので、自身の時間を作るために自走できるチームを作り上げるためのマネジメントでもあります。
マネージャーとしてどういう動きしてるの?
- ローテーション可能のものとする - マネージャーを無くす意思って良いチームを - 1on1でフィードバックをもらいましょう - チーム内で最適な人がやりましょう
基本方針はこれです。(私がマネジメントしてるチームのオンボーディングに書いてます)
ローテーションは、それが出来れば理想的だなぁと思ってます。
なので、チームの権限は同じで、by nameで呼ばれた会議やメッセージはチームメンションに促してます。
もちろんベンダーとのメールのやりとりはslackに飛ばしてます。
あとは拾うべき役割の人がやってくれるでしょう。
私はマネージャーというのは偉い立場ではなく、あくまで役割だと思ってるのでチームメンバーと同じ扱いでいいと思ってます。
1on1
チームメンバーと1on1をまずは週1。徐々に週2くらいの間隔で30分行なってます。
質問は、その人のslackでの動きや発言の仕草、仕事ぶりを見て「その人が今どう悩んでるか」を分析し、それを踏まえて聞きたいことを考えてます。
もちろん1on1をやる前に項目を毎回伝えてます。またテンプレートで聞く部分もあります。
最近は
「もし10万円与えられたら、より効率的なリモートワークをするには何に使いますか?」
「変えたいと思ってる会社全体の文化はありますか?」
とか聞いたりしました。
ちなみに最初の1on1で送るメッセージはテンプレートでこれです。
### 最初なので1週間に1日/30分ほど作っていきたい気持ちです。 ### そこから徐々に頻度を下げていきます - キャリア - 今後の休暇や働き方 - 目標とアクション決定 - 目標を阻害する問題に対して助けられること - 時間があればその他
なぜ1on1をやるの?
# メンバー向け - チームの意思を統一するためのコーチング - チームワークのためのコーチング - キャリア開発 - 可能性を広げていく # 自身向け - メンバーを知り、チームの可能性が広がるピースを考える(採用) - メンバーがやりたいことを知り、その役割に当てはめる - 自身のフィードバックをもらう - 会社やチームがより成長するアドバイスをもらう
などを意識してます。
「良いチームを作るためにメンバーの変化に柔軟に対応して常にベストな状態にする」ために、個性やステータス(特に体調不良やモチベーション)を理解して、チームにどう適用するか戦略を立てていく。
ゲームもそうですが、他スポーツもそうですね。
意思の統一?
意思の統一 = ベクトルが同じかどうかです。
無駄にだらだらコミュニケーション(意思疎通)をやるより、まず先に意思を統一すれば相互理解も早まってシンプルに解決しやすい思ってます。
そうすることで、必要なときに助けを求めやすくなるし、ダイバーシティーなチームでも回答も一致しやすくなります。そのためには相手が自身や相手がどんな人かを理解する必要があります。
- 自分がどういうポジションで動きたいか、動いてるか理解して働きましょう - 相手がどういう役割なのか、相手がどういう思考をする人なのかを意識しましょう - 仕事には感情入れずシンプルに会話しましょう
シンプルな会話はリモートワークにおいて非常に効果的なテクニックであり難しいと思ってます。
「会話の途中途中に絵文字はなるべく使わない」「できる限り短い文章にする」など色々あります。(ただし雑談は別
これを意識し、ベクトルが同じであれば意見はスピーディーに収束すると思ってます。
また「多部署に対して、同じ意見を言える」「役割を理解して連携し合う」ところも重視したいですね。
どんなチームを目指してるの?
# メンバー全員が - 責任持って自走する - 他部署に対して、同じ意見を言える - お互いに役割を理解して連携し合う
みたいな感じですね。
SREチームとしてどう適用してるの?
- 未来予測しながら、仕事する - 建設的でロジカルな議論をする - 健康的である - 成果させ出してれば自由。色々試していけばよい - 意思疎通より意思の統一(ベクトルが同じかどうか)。意思疎通はPR。(深い、長くなりそうならchat or meets) - HRT - 謙虚さを持って相手より勉学に励む(「おれについてこい」くらいの度量で) - 信頼を持ってあまり口を出さない。任せる - 尊敬して讃えあう(thumbsup)
というのを掲げてます。
HRTを意識することで、シンプルな会話になりやすくなります。
例えば会話の中で「人格否定してるわけではないですが」「非難してるわけではないですが」のような無駄な枕詞をなくした会話になりやすいです。
目指すは 誠実に言い合えるチーム です。
「自由を与えながら成果を出させる」というのは非常に環境が影響しやすいと思ってます。
なので我々は「失敗しても良い。何やってもいい。その代わり責任は持ちましょう。」という環境にしてます。
「迷ったり不安になったら、相談やレビューが絶対くる」とメンバーを信頼しましょう。来なかったらフィードバックし成長しあいましょう。
また働く時間も休みも自由にしてます。要所で会議や相談をセットすると思いますし、例えば新入社員が入ったばかりなら、相手が働いてる時間帯で働くメンバーだと信頼してます。
やりたい仕事をさせる
バックログにはいっぱいタスクが積んであります。
メンバーが自由にとってやらせれば良い仕組みにしており、なるべくslackや1on1で様子を伺いながら、成長したいorしてほしい分野をやらせます。
被ったら一緒にやらせれば良いのですが、問題はやりたくない仕事が余ることです。
そこはマネージャーである私がやってもよいし、成長のためにやらせてもよいし、そういうのが得意なメンバーを雇った良いチームを作り上げたいと思ってます。
会議はどれくらいやってるの?
- 1週間何やったかを共有 + datadog dashboardを見る(15分。ドキュメントさえ見てれば参加自由)
- 1on1(2週間に1回、30分)
各チーム会議はこれだけです。
これ以上必要ないと思いますし、全員時間を有意義に使うためです。
相手が何やってるか把握出来ないのでは?
まずマネージャーだから全部把握する必要はないかなぁと思ってます。
今何をやってるかに関しては、チーム全体で「相手に情報を出す」より「必要そうだったら出して、それを各々pull型で必要だったら拾う」を意識してます。
今後何やっていくかに関しては、基本github issueのissueやPRの通知や動きで把握出来ます。
その上でslackでは議論、issueやPRに5W1Hで決まったこととslackのURLだけ貼って簡潔にしてます。
なので、よくプレイヤーが疲弊しがちな、マネージャーのためにバックログにリアルタイム経緯を書いたりはしません。
物事の成果が出るタイミングにレビュー会を開いて、そこで「コメントに経緯を書いておくと、後からjoinした人がわかりやすいよね」というツッコミをすることが多いです。
最後に
多様な人材をうまく組み合わせることで、良いチームが出来上がります。
今でも良いチームですが、これからもっと高みを目指すために日々勤勉を積んで、爆速に成果を出せるチームにしたいと思います。
Fargateでfirelensを利用し、datadog logsとs3にログを保存する
firelens is 何?
fluentd/fluent-bitに連携することが可能なログドライバーです。
なぜfirelensか
firelensが出るまでFargateはawslogsドライバくらいしか選択肢がありませんでした。
s3に生ログの保存、解析+アラート用にdatadog logsに送るためには
fargate -> cloudwatch -> firehose -> lambda -> datadog logs, s3
といった構成を取る必要があり、そこには2つ問題がありました。
- cloudwatch使わないのに無駄な費用が発生
- lambdaがたまにこけていた(これは我々の実装の問題だと思います)
fargate(firelens) -> firehose -> datadog logs, s3
とfirelensを使うとこの構成になり、上記解決 + cloudwatch, lambda費用もかからなくなります。
fluent-bitのイメージについて
今回サイドカーで立てるfluent-bitのイメージは
https://hub.docker.com/r/sioncojp/fluent-bit-datadog-firehose を使います。
なぜこのイメージを使うかというと、Fargateはs3にあるconfigファイルを利用できないので、firehose, datadogに送るconfigを設定したカスタムイメージを作成する必要がありました。
ここで注意するのは、 /fluent-bit/etc/fluent-bit.conf
と /fluentd/etc/fluent.conf
のファイルパスは予約されてるので、/fluent-bit/etc/extra.conf
などの名前で保管しましょう。
ref: s3にあるconfigが使えない + ファイルパスの予約
ECS task definition
タイミーではecs-deployというGoの自社ツールでecsのtask設定 + service updateをしてますが、そちらで自動的にfluent-bitのContainerDefinition設定がされるようになってます。
fluent-bitの管理はSREなので、開発者には動かしたいコンテナの設定だけしてもらうよう意識してます。
設定されるjsonについて、firelens部分だけ紹介するとこのような感じになります
# サービス側 "logConfiguration": { "logDriver": "awsfirelens", "secretOptions": null, "options": null },
# fluent-bit側 "environment": [ { "name": "AWS_REGION", "value": "ap-northeast-1" }, { "name": "DD_SERVICE", "value": "koyama" }, { "name": "DD_SOURCE", "value": "fargate" }, { "name": "DD_TAGS", "value": "env:sandbox" }, { "name": "DELIVERY_STREAM", "value": "koyama_koyama" } ], "secrets": [ { "valueFrom": "SSMのDD_API_KEYパス", "name": "DD_API_KEY" } ], "firelensConfiguration": { "type": "fluentbit", "options": { "config-file-type": "file", "config-file-value": "/fluent-bit/etc/extra.conf" } },
全task definitionはこちら。
datadogに飛ばす
firelens -> サイドカーのfluentbit -> datadog
### タグ付け
- source: fargate/cron/runtask
- service: コンテナのサービス名
- env: prod/stg/sandbox...
s3に飛ばす
firelens -> fluentbit-> firehose -> s3
https://github.com/cosmo0920/fluent-bit-go-s3 も検証しましたが、1リクエスト1ファイルだったので管理が辛く断念しました。
またfirehoseは各サービスで1つ作るのがベストです。そうしないと同じs3ディレクトリに全ファイルが集約されてしまいます。
### 共有firehose s3://生ログ保存バケット/共通ディレクトリ/YYYY/MM/DD/ファイル名 ### サービス毎にfirehose s3://生ログ保存バケット/クラスタ名/サービス名/YYYY/MM/DD/ファイル名
1つずつ作り クラスタ名/サービス名
にfirehose prefixを設定することでディレクティブに保管できます。データ分析の際に使いやすくなるよう意識しました。
fluent-bit自体のロギング
awslogsドライバーでcloudwatchに吐くことができますが、特に必要な情報が今の所なさそうだったので出してません。
もし必要になった場合、cloudwatch logsで全fluent-bitログを保管するlog groupを作り、そこのグループにlogstreamにサービス名を定義して保管するのがベストだと思います。
fluent-bit自体のモニタリング
今の所はしてません。問題が起きたらそれに合わせてすると思います。
またessential:trueなので、落ちた場合、taskにある全コンテナ落ちますので、datadogでサービス単位のコンテナ変動をモニタリングはしております。
Fargate + cloudwatch eventでcronシステム モニタリング編
前回のお話
Fargate + cloudwatch eventでcronシステム構築 - Sionの技術ブログ
non exit0かどうか
こちらはlambdaで監視してます。
Fargate + cloudwatch eventでcronシステム構築 - Sionの技術ブログ
ロジックとしては、タスク終了時のイベントをトリガーに、exitCodeの値をとって、0じゃない場合slackに通知してます。
タスクが正常に動いてるかどうか
こちらはdatadog logsの量が0以上なのをチェックしてます。
例えば1分間毎のcronの場合、「1分間でログが0以上あるよね」というチェックをしてます。
クラスタのタスク数と、タスクが並列で動いてないかどうか
クラスタのタスク数
1Clusterにおける、Fargateのタスク上限数は50です
Amazon ECS サービス制限 - Amazon Elastic Container Service
datadogで監視出来るのはFargateのserviceのみ。今回はcloudwatch eventsを通してタスクを動かしてるので取得できませんでした。
これが監視できないと、上限値に達した場合タスクが起動できなくなってしまいます。
その前に検知して上限緩和をしたいですね。
タスクが並列で動いてないか
最初は「datadog logsで決めた量の2倍以上になったら2つ動いてる」というロジックで監視してましたが、
2倍以上にならない場合もあるので、悩みました。
双方の監視はGoで実装し、Fargate service上で動かして監視してます。
上記でやってることは
- stg, prodのcronクラスター内のタスクの数を監視し、slackに通知。 - タスクがn個以上実行されてるかを監視します。15分ごとにまとめをslackに通知。(nは設定できます)
となってます。
大変だったところ
メトリクスが取れない?
datadogではfargate service + clusterの場合メトリクスが取れます。
が、今回はfargate + cloudwatch eventなのでクラスタからメトリクスが取れません。
なのでもし中々taskが終わらない...などの調査用に取りたい場合は、サイドカーでdatadog/agentをいつでも立ち上げれるよう、準備するだけでとどめてます。
datadog/agentが立ち上がる前にメインコンテナが起動終了する?
https://hub.docker.com/r/sioncojp/docker-slack
curlでslackにメッセージPOSTするイメージで検証してました。
あまりにも起動、終了が早いため、datadog/agentより先に立ち上がって終了します。
dependsOnでdatadogに向ければ、datadogが立ち上がった後にアプリケーションが起動するので、メトリクスが取れるようになります。
datadog/agentが永遠に立ち上がる?
dependsOnサイドカーのdatadogを向けた場合、永遠に立ち上がってしまいます。
https://github.com/sioncojp/fargate-sidecar-datadog-agent
のようにタスクが全部終了したら、検知してdatadogをshutdownするソリューションを考えましたが、
app = essential: true + dependsOn: datadog/agent datadog/agent = essential: false
の設定を施せばこれは解決しました
タスクが永久にpending?
例えば、
app:latest = essential: true + dependsOn: datadog/agent datadog/agent:hogeeee = essential: false
でdatadogをサイドカーで立ててる場合、datadog/agent:hogeeee のような存在しないイメージを指定すると永久にタスクがpendingになります。
細かい原因はよくわかりませんが、もしこのようなパターンを使う場合は、app側のwrapperでサイドカーの起動を失敗検知する設定が必要です