emahiro/b.log

Drastically Repeat Yourself !!!!

Setting Explorer in VSCode

Overview

VSCodeExplorer をカスタマイズしたお話です。

Explorer 以外を消す

デフォでは

  • Open Editor
  • Outline(ファイル内で定義されてる関数一覧とか)
  • Explorer (プロジェクトの一覧)

がありますが、一番したの Explorer 以外消したいなーと思ったので消す方法を探しました。それが以下。

github.com

github.com

OutLine は setting.json から消せるものかと思って色々調べてましたが、普通にUI操作でした。

その他

Timeline view

ファイルごとにコミットログとどの変更でその差分が入ったのか調べたい時があります。
git blame すればいいんですが、具体的な差分まで調べようと思うと git 力が問われます。

そんな時に VSCode の Timeline view の機能を知りました。
Cmd + Shift + P で Timeline... と検索すると出てきますが、Explorer の画面にコミット履歴とそのコミットにおける差分が pane に描画されます。

地味に便利だなーと思ったので書きました。

defer の順序

よく忘れるので備忘録。

https://play.golang.org/p/n5aHnATxXMr

上記の例だと cb() が実行された時点で呼ばれ b が出力される。
defer は関数を抜けるときに呼ばれるので cb()() を宣言すると func cb() func() の return で指定した関数が呼ばれる。

つまり defer cb()() では最初と最後にそれぞれ cb() で実装した内容が呼ばれることになる。

追記: メソッドを繋いだときの挙動

logger や trace を使うときに多いですが、defer で関数から抜けるときに Finish したり、Flush してメモリを解放する処理を行いたいケースがあると思います。
そう言った場合でも defer で一括りにしちゃうことが可能です。
以下のケースでが New が実行されるのは関数が呼び出された直後ですが、Finish が呼ばれるのは関数を抜けるときになります。

ref: https://play.golang.org/p/3U6WsyEJzLx

エウレカに入社して1ヶ月が経ちました

まぁなんてことないタイトルの通りの話なんですが、転職して1ヶ月(少しアルバイトもしていたので出社開始からは1ヶ月半くらい)経ち、いろいろ環境が変わったのでその記録を書きます。

何してるの?

Gopher

変わらず Gopher です。Go を書いてます。

詳しくは会社の Wantedly でエントリを書いたので読んでみてください。

www.wantedly.com

何か変わった?

GCP -> AWS

ずっと GCP の世界で過ごしてきましたが、インフラは AWS になりました。ただ、ほぼ何もわかりません。 AWS の概要をつかめる Udemy を教えてもらったので GW を使って概要をさらってみるつもりです。
出てくる単語すら何もわからないレベルなので。

マイクロサービス -> モノリス

前職はずっとマイクロサービスの世界で生きてきましたが、今はモノリスな設計の中で開発しています。
マイクロサービスを運用してた頃の辛みも知った上でモノリスを触ると、結構色々な発見があって面白いです。
流行りの技術を使っていても、数年運用された歴史を持つコードはやはり古く、またコードベースが大きいのでCIも遅かったり、まだまだ改善の余地は多そうかなと感じてます。

副業いったん辞める

あと、副業で関わっていた会社を一旦離れました。
タイミングの問題なので特にコメントすることもないのですが、ちゃんとスタートアップで働いてみたのは初めての経験だったので、学ぶことも多かったです。

そんなにやりたいっていう温度感でもないですが、現在少し時間が空いてるので副業のお話があれば検討できます。 こんなご時世ですが、事業に直結しない緊急度は低いが重要なタスク(CI整備とかソフトウェアのバージョンアップとか)周りを進めることは得意です。
※ 技術スタックは Go に限ります。

リモートワーク

多分これが一番大きな変化かなと思います。WHOのパンデミック宣言があった3月末以降原則リモートワークに移行して、在宅で勤務をしています。
僕は場所で仕事モードに切り替わる人間だったので、これはめちゃくちゃ影響が大きかったです。
ただ、もう1ヶ月くらいフルリモートで作業してるので流石になれてきました。

リモートに合わせて、自宅の設備をバージョンアップしたのでなんとか作業をできています。
(この設備投資については会社で補助が出たのでとても感謝してます。)

補助の内容は以下のエントリに詳しく載ってます(僕の買ったものも載ってます)

medium.com

総じて

時流もあり、働く環境は想像以上に大きく変わりましたが、やることは至って変わってないです。
引き続き頑張るぞい。

エウレカに入社しました

本日、3/16より株式会社エウレカに入社しました。

引き続き Gopher です。
ベイスターズとブレイブサンダースも変わらず応援してます。

働く場所は渋谷から赤羽橋の近くになります。
麻布十番にオフィスから歩いていけますが、渋谷と空気感が違ってまだ馴染めません。

前職より出社時刻が早くなるので「朝起きられるのか?」というのが最大の懸念事項でしたが、今のところ大丈夫そうです。

そういえば、オフィスの交差点を挟んだ向かい側に TAILORED CAFE があったので、今度利用してみようと思います。

Go1.14 をサポートしました

Go の Release Policy に合わせて、以下のリポジトリを Go.1.14対応しました。
合わせて Go1.12 のサポートを切りました。

Each major Go release is supported until there are two newer major releases.

ref: Release History - The Go Programming Language

github.com

github.com

github.com

TSLint -> ESLint に乗り換える

Overview

ESLint から TSLint に乗り換える手順について記載します。
サンプルとして自分で作成してる Firebase のプロジェクトで乗り換えた手順を記載しました。
対象は TypeScript で記載されている Functions のコードになります。

乗り換え手順

tslint-to-eslint-config を使用します。

$ npx tslint-to-eslint-config
npx: 54個のパッケージを4.37秒でインストールしました。
✨ 31 rules replaced with their ESLint equivalents. ✨ 
❗ 3 ESLint rules behave differently from their TSLint counterparts ❗
  * no-redeclare:
    - ESLint does not support check-parameters.
  * no-invalid-this:
    - Functions in methods will no longer be ignored.
  * no-void:
    - ESLint does not support ignoring arrow function shorthand. 
⚡ 2 packages are required for new ESLint rules. ⚡
  import
  eslint-plugin-import 

✅ All is well!

TSLint で設定したルールで ESLint に乗り換えることができなかったものについては警告として出力されるので、ESLint のルールで代用できなかを検討すると良さそうです。

Firebase で Functions を使用してるケースでは、functions ディレクトリのデフォルトの .gitignore ファイルに **/*.js が記載されていて、このままだと新しく生成された .eslintrc.js が ignore されてしまうので、 !.eslintrc.js を追記します。

TSLint の削除

  1. eslint のプラグインを入れます。
$ npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint
  1. package.jsonスクリプトから tslint を外します。
- tslint --project tsconfig.json
+ eslint --fix ./src/**/*.ts // ./XXX/**/*.ts で XXX 配下の全ての ts ファイルに eslint をかけます。
  1. tslint の依存を切ります。
- "tslint": "^5.12.0",

ESLint をかける

$ npm run lint

> functions@ lint /Users/hiromichi.ema/emahiro/github.com/emahiro.dev/functions
 > eslint --fix ./src/**/*.ts  
 /Users/hiromichi.ema/emahiro/github.com/emahiro.dev/functions/src/index.ts
   1:1  error  Definition for rule '@typescript-eslint/no-param-reassign' was not found  @typescript-eslint/no-param-reassign
   1:1  error  Definition for rule 'import/no-deprecated' was not found                  import/no-deprecated
   1:1  error  Definition for rule 'import/no-extraneous-dependencies' was not found     import/no-extraneous-dependencies
   1:1  error  Definition for rule 'import/no-unassigned-import' was not found           import/no-unassigned-import

# 略

幾つのかのルールが ESlint に定義がないと言われます。
ルールがないものは適宜書き換える必要があります。
このタイミングで定義が ESLint にないと言われたものについては以下です。

- "@typescript-eslint/no-param-reassign": "error",
+ "no-param-reassign": "error",

ref: no-param-reassign

1:1  error  Definition for rule 'import/no-deprecated' was not found               import/no-deprecated
1:1  error  Definition for rule 'import/no-extraneous-dependencies' was not found  import/no-extraneous-dependencies
1:1  error  Definition for rule 'import/no-unassigned-import' was not found        import/no-unassigned-import

import 周りについては eslint-plugin-import を参考に plugin を追加します。

$ npm i --save-dev eslint-plugin-import

.eslintrc.js の plugin に import を追加します。

- "@typescript-eslint"
+ "@typescript-eslint",
+ "import"

これで今回当たったESLint の未定義ルールについては置き換えが完了しました。

TSLint -> ESLint の過程で error Definition for rule 'XXXXX' was not found のエラーが ESLint で発生したら、ルールを置き換えられないか?or 足りない plugin がないかということを調査してみると良さそうです。

VSCode で TypeScript に ESLint を使う

Setting.json に以下を追記します。

// eslint
"eslint.workingDirectories": [{"mode": "auto"}]

ref: https://github.com/microsoft/vscode-eslint/issues/881

設定後に Cmd + Shift + P で ESLint - Fix all auto-fixable problems. が使えるようになります。
var i = 0 などを適当に書いてみて、no-used-vars の警告が出ていれば動作してるとみて良さそうです。

iTerm2 が Dock にアプリが表示されなくなる問題

以下の設定を ON にするとDock で iTerm が表示されなくなり、タブ移動もできなくなる。不便。
新しく iTerm2 を入れたらデフォでONになってる気がした。Offると良い。

f:id:ema_hiro:20200303015752p:plain

DeNA を退職しました

※ 個人の記録です。

本日 2/28 が現職 DeNA の最終出社日でした。

単体で3年弱、iemo の時代を含めると3年半お世話になりました。

入社当時、何の武器も持ってなかった自分にエンジニアとしての武器を授けてくれた本当にいい会社でした。

次の職場は東京タワーの近くです。
引き続き Gopher をやっていく予定です。
GCP(App Engine)とはしばらくお別れです。

また働き始めたタイミングで何か書きます。

障害対応時のTODOリストをまとめてみる

Overview

先日久しぶりの障害対応を行ったときに、色々鈍っていたなと痛感したのと、いくつか改めて気づいたこともあるのでその振り返りもかねての記録です。
なおTODOリストとエントリ名はしてますが、実態はかなりポエムに近いし、勢いで書いたところがほとんどなので拙い文章であることや、ところどころ話が逸れてること自体はご容赦ください。

動機

この記録を書こうと思った動機は、障害対応(インシデント対応)に関しては、危機管理側に立った情報はいくつか出揃ってきてるなという印象を持っている一方で、実際に現場で障害対応するときの手順だったり、自分自身まず何からやってるっけ?どういう順序で情報を出してるっけ?と言った実業務に即した内容はあまりないので、いい機会なのでいくつかまとめて、記録として残しておこうと思ったことです。
以下でリストとして出して、それぞれどんなことしてるっけ?って言うのをまとめてますが、もしかしたら今後増えていくかもしれません。

障害が起きたときにやることリスト

  • 障害を報告する。
    • エスカレーション。
    • 切り戻し。
      • 直近のリリース影響などが疑われる場合。
  • 一次対応
    • ケースバイケース。
    • だいたい原因調査と並行して行う。
  • 原因調査。
    • 事象の把握。
      • 期待する動作の確認。
      • 期待する動作になっていないときに、どこまでが正常に動作していて、どこからが異常なのかを把握する。
  • 原因の切り分け。
    • どこに原因があったのか?
    • コードを追って原因を特定する。
  • 障害報告書を書く。
    • 事実を集めること。
      • 書くことリスト
        • 何が起きたか?
        • どれくらい継続したか?
        • どれくらいの影響があったか?
        • 何が原因か?
          • 対応はどうするのか?(暫定・恒久)
          • 人に目を向けない。仕組みに目を向ける。
    • エンジニア以外の人が読む文章であることを意識する。
      • 専門用語は正しく伝える最低限でなるべくツールの名前などは一般化する(もちろん限界はある)。
  • 修正対応
    • 原因が特定して、修正できるのであれば修正してしまう。
    • 時間がかかるようであれば別のタスクとして切り出す。

障害を報告する

まずやるべきはこれだと思っていますが、この初動が一番難しいと思います。
というのもエンジニアなら、何かしら意図しない挙動があったときに、この振る舞いは自分が混入したものなのかどうかが気になってしまいますし、何より、障害を発生させてしまった場合に、そう報告するのって実際かなりストレスがあるなと感じてます。

ただし、そんな自分の気持ちなんかユーザーには一ミリも関係ないので、とりあえず「ヤバみ」を感じたら「障害です」と上に投げちゃう方がいいのかなと考えてます。
もちろん、ちゃんと調査したら違った、みたいな話は出てくると思いますし、そうなったらそうなったらで「拙速だったすまん」と謝ればいいかと思います。その辺の見極めって本当に難しいと思うので。自分もできてないし、「障害か?」の裏をとってる間にも状況は悪化してるかもしれない、と言うことを考えると迷ったら安全に倒す、くらいのメンタリティが大事なのではないかと思っています。
そしてこの辺の初動のスピード感については、すぐにエスカレーションが上がってくるようなチームになってることと密接に関わっていそうなので、よく言われる「組織における心理的安全性」や「人ではなく仕組みに目を向ける」といったことが体現してる組織やチームは障害対応の側面から見ても強いと思います。

話が逸れましたが、この障害かどうかの報告については、とりあえず15分程度ガッと調べて裏が取れれば、そう報告するし、迷ってもとりあえず「障害かどうかの判断に迷ってる」と言う前提と一緒に上に投げちゃうのがいいのかなと思います。
調べる時には自分は大体開発環境でも同様の事象が発生するのか?(再現手順踏まえて ※)と言うところを調べてます。
これは商用環境と開発環境で同じコードが動いてることが前提だったりするので、日頃から同じコードになるようにしておくことはこう言う時にきっと活きます。
実際のところ開発環境の検証で見つかると儲けもんなのですが、開発環境では再現しないみたいなこともしばしばあるので、その場合は再現できていない前提で、上述したように報告します。

次に直帰何かリリースがあったかを確認します。
リリースがある場合は、そのリリースによる変更が原因であることが疑われるので、まずはユーザー影響を最小限に抑えるために切り戻します。トラフィックの向き先を1つ前のバージョンに戻すか、そう言う仕組みになっていない場合は、リリースのコミットごと revert して再デプロイするのか。とはいえ、リリースによってデータに変更が生じてる場合などは...どうするんですしょうね...そのオペレーションは経験がないのでよくわからないです。
生じている変更が切り戻してもサービスに影響があるかないかをまずは確認しますし、それで影響が出る、となったらそれこそ、切り戻せないので、原因を早急に突き止めて爆速でパッチ当てる...とかになるんですかね...。 これもやったことはないので自信ないです。

※ 再現手順ってまじで大事なので、もしおかしな挙動を上げる時は再現手順とセットにして欲しい、と言うのはエンジニアとしては密かな願いでもあります。何でもかんでもバグ!障害だ!として上げられたら心折れます。

一次対応

ユーザー影響をひとまず止めるために最初にどう言う対応をするのかエスカレーションしたタイミングで何かしら決定があるはずなので、その作業を先に行います。 エンジニアで現場で実際に作業をする場合を考えると、上述した切り戻し作業を行うことになると思います。
このタイミングではビジネス側で何かしら決まって、すぐに次の原因調査に移行できることもあるでしょうし、ここは原因調査と並行で進むものですし、ケースバイケースだとは思います。

原因調査

何かしら期待しない動作がある、そしてユーザーに不利益がある、というような事象に遭遇したときに、どうしてその状況になってるのかを調査を行います。
調査を行うにあたっては手順は主に「事象の把握」と「原因の切り分け」の2つの工程を経て、原因を突き止めるまでに至ると考えています。
そしてこの原因調査のタイミングで最初にやってはいけないと思ってるのは コードを最初から追いかけること だと思います。
もちろん、障害の発生したポイントがすでにドメイン知識を持っている範囲で、かつ障害の発生に関連した変更に関わっていた場合などはすぐに原因を特定してパッチを当てたり、迅速な報告ができる、などもあるとは思います。
しかし、ここでは、自分が深く知らないドメインの領域や、触ったこともないマイクロサービスで発生した障害を想定します。
自分の知らないドメインであれ、サービスに関わっている以上は当事者として関わっていかないといけない、しそう言うところの障害が結局多い。と思っているからです。

話を戻します。
まず最初のプロセスとして、事象の把握で何をしてるかですが、ここではまずどう言う症状が起きてるのか?から確認していきます。そもそもどういうことを期待していて、今そうなっていないのか、がわからないと調べようも対応のしようもありません。
そのため、まずはプロダクトの振る舞いとして「何を期待するのか?」を確認するのが大事です。
ときには、この段階で「そんな風には作ってない」と言う想定外の使われ方、みたいなケースがサッと出てきて、次のステップに進めることもあります。これができればラッキーです。あとはその裏を取るためにコードを追う作業にいけるので。
次に期待結果を得るためにはシステムがどう言う風に動く必要があるのかの見通しを立てます。ここまでできてから実際に現状のシステムに置いて「どこまでが正常に動作していて、どこからが期待した動作をしていないのか?」の切り分け作業を進めながら原因の切り分けと特定を行なっています。
実際に原因を調べるときは データの状態やり、リクエストログなりを手がかりにして、1つ1つ切り分けを行なっていくと思います。
そしてこの切り分けを行うときの注意点としては、意図した振る舞いになっていないからといって、すぐにUI側の実装を疑ったり、みたいなことをしないことです。(僕はこの間やりました。完全にミスでした。)
UIはAPIから返したデータを描画するように作ってあるかもしれないし、そうした場合にはAPIのレスポンスが意図した振る舞いをするためのレスポンスになっていない、と言うことになります。

ここで初めてコードを追いかけることになると思います。
結局ここで伝えたかったのは、すぐにコードを追うな。コードを追っかけ始めるのは一番最後、と言うことをルールとして決めるだけでも障害対応の進め方は変わってくるのではないかな?ということでした。

障害報告書を書く

原因調査と並行して障害報告を書きます。 ここでは、原因調査でわかっている事実を書きます。逆を言えば、原因が見つかっていない場合は 調査中。判明次第更新します と書いて「...に原因がありそうです」みたいな憶測は書きません。 この文章はエンジニア以外の人向けに書かないといけないので、専門用語やツール名は正確な情報を記述するための最低限に留めます。

書く内容としては以下かなと思います。あくまで僕の考えるところが大きいので、チームごとに内容は変わってしかるべきかと思います。

  • 障害内容
    • 何が起きたか?
  • 影響範囲
    • どれくらいのユーザーに影響があったか?
  • 障害発生時刻
    • どれくらいの時間続いたのか?
    • すでに収束してるのか?現在も継続中なのか?
  • 対応について -1
  • 原因
    • なぜ障害が発生したのか?
  • 対応について - 2
    • 恒久対応
      • 原因となった事象に対してどう言う対応を取るのか?
        • 多くはコードを修正することになると思います。
        • または既存の何かしらの仕組みを変更することになると思います。

繰り返しですが、ここでは 事実のみ を記載することがポイントです。そして、原因は Blemeless である必要があります。原因は人に求めず、仕組みに求めます。

修正対応

原因を特定できた、修正できるのであれば修正しましょう。この修正をどのタイミングでリリースするかは、すぐの時もあるでしょうし、翌営業日、と言うようなこともあると思います。障害が発生したのがピークタイムの時間の場合には、安全を期して翌営業日リリースと言うパターンが多いと思います。
すぐには改善できない原因もあると思いますし、その場合は別途イシュー化して対応の優先順位を意思決定層と相談することになると思います。
まぁ優先度周りについては現場に居る人間としては、あまり意識するところではないかもしれません。

振り返り(と言うかポエム)

大なり小なり、障害って日々発生してるものだと思っていて、ユーザーに対して期待する価値を提供できていない、もしくはサービスが壊れていると言う状態がままある、サービスに関わるエンジニアとしては常に向かい合わないといけないことだと思います。そんなしょっちゅう起きてても、オイオイ...って思いますが、成長して、常に何かしらの変更を加えているサービスでは、障害は日々発生する可能性があります。

正直このエントリで書いたことが自分で「ちゃんと」できてるかと言われると、できてないことの方が多いです。
障害対応ってまじでストレスのかかる仕事だし、起こした当人が一番頭混乱してるので、二次災害をどう起こさないか、みたいなところで最低限の冷静さを保ちながらチームをうまくまとめるのは実はめちゃくちゃハイスキルな対応です。
こればっかりは完全に場数勝負、経験が物を言う世界なのかなと。。。
昔ある人にエンジニアの戦闘力をどこで測ってます?と言う質問をしたときに「障害対応を行ってきた経験があること」と言うニュアンスのことを言っていて、改めてなるほどと思わされます。

そして、いくつかエントリの中で触れたリストの中に「...と並行して」と書きましたが、この言葉の指す通り障害対応はチームで行う作業です。
誰かが原因調査をしてるときに並行して、エスカレーションする内容をまとめたり、それを障害報告の文章に起こしたり、と言うことは協同して行います。

最後に、原因の切り分けプロセスにおいて、技術に詳しいことはそれ自体がアドバンテージになります。
例えばバックエンドのエンジニアが、サービスで使われてるフロントエンドの技術を少しでも触ったり、理屈をわかっていれば原因の切り分けの速度は上がります。
土日に勉強しろ!とは思いませんし、土日に勉強してる人が偉いとも、評価されるべきとも考えてはいませんが、日頃の技術的な研鑽はいざというときに間違いなく活きる、と言うことは事実として存在すると思います。

例えば Vue のコンポーネントにおいて、props の受け渡しがどう行われるのかを知ってると知らないとでは、APIから正しいデータを返してるかもしれないときに UI 側の原因だと突き止めることはできません。
それを作った人に聞ける、と言う恵まれた環境であればそれはそれでラッキーですが、現実はすでに作った人がいない、なんてことはザラにあります。
ドメインの知識はないけど、技術を理解していれば作った人ほど速くとは行かないまでも、切り分けを行いながら原因にたどり着ける可能性は増します。

正しく技術を理解していること、そしてそれを使えるように日頃から素振りをしてるとそれが思っていないときに日の目を見たりするので、自分ももう少し素振りの時間は取りたいなと思いました。本当は業務でそれができるとベストではありますが。

余談: カオスエンジニアリング

カオスエンジニアリングとは、雑に言うと障害対応訓練(雑すぎて刺されそうですが)で、マイクロサービスアーキテクチャのような小さなサービスが複数で協調して1つのシステムを作り上げてるケースの障害対応だったり、サービスの安定運用のために、それこそ日頃の素振りとして行われる類のものなのですが、例えば、仕様の漏れとか想定外の使われた方を原因とした障害って実際訓練しようがなくないですかね?なんてことを考えました。

そもそも想定してない使われ方をできてしまうことが問題ではありますが、訓練項目に入らないようなことが発生したら、訓練でやったこと全ては活きることなく、その時々で最善を尽くす他ないのかなと。 もちろん、訓練の中でログの見方だったり、APIの仕様だったりをシュッと調べられて原因特定のスピードは改善していくと思うので訓練や素振りが全て無駄にはなりませんが、訓練項目書を作る、と言うか仕様を把握するって難しいなと....。

と言う単なるぼやきでした。

まとめ

まとまってないけど、障害対応はエンジニアとして成長するいい機会だと思います。

ドメイン名からIPアドレスを調べる

Linux を触っていた頃に覚えてたはずの dig コマンドをすっかり忘れてました。

$ dig www.google.com

; <<>> DiG 9.10.6 <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54506
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;www.google.com.          IN  A

;; ANSWER SECTION:
www.google.com.       66  IN  A   216.58.197.164

;; Query time: 32 msec
;; SERVER: 2001:268:fd07:4::1#53(2001:268:fd07:4::1)
;; WHEN: Tue Feb 04 02:01:03 JST 2020
;; MSG SIZE  rcvd: 59

こういうことができるやつね。 インフラ触れなくなって久しいので遠い過去の記憶になっていました。

CLOUD SDK で python2 系が切られて aetest が落ちるようになった問題

Overview

Update to Pyhon3 にて Goole Cloud SDKを同梱した Docker Image 本体にて Python2 のサポートが切られたために、Python2 系でのみ動作する App Engine を起動するコマンドの dev_appserver.py が動作せず、本家の Google Cloud SDK - Dockerfile を使用して aetest を走らせてる場合(CI上などで)は、App Engine のインスタンスの起動に失敗して CI が落ちる、という挙動を確認しました。

対応方法

  1. 上記の変更が入る前の dockerfile image を使って自前でビルドしたイメージを使う。
  2. dev_appserver.py を起動するときに CLOUDSDK_PYTHON=pyhon2.7 を指定する。

調査方法

調査した方法をログでまとめました。

エラーを観測する

Traceback (most recent call last):
     File "/usr/lib/google-cloud-sdk/platform/google_appengine/dev_appserver.py", line 95, in <module>
       assert sys.version_info[0] == 2
   ... unable to find admin server URL

どうやら dev_appserver.py の実行に失敗し、App Engine のインスタンスが起動せず、App Engine の内部で実行するべきテストが落ちている。

なぜ起動に失敗するようになったのか調べる

本家の golang/appengine を見に行くが、何かめぼしい変更が入った様子はない。
そもそも dev_appserver.py の実行に失敗してるので dev_appserver.py を確認する。
sys.version_info[0] == 2 を見る限りどうやら python2 系が docker コンテナの中で使われなくなった模様?という仮説を立てる。

本当か?と思って本家の Docker Image で動作検証する

$ docker pull gcr.io/google.com/cloudsdktool/cloud-sdk:latest
# 古いバージョンを入れるには以下
$ docker run gcr.io/google.com/cloudsdktool/cloud-sdk:260.0.0

# docker のコンテナ内に入る
$ docker run --rm -it -v $PWD gcr.io/google.com/cloudsdktool/cloud-sdk:latest

$  python --version
Python 2.7.16    # 2.7 じゃん...
    
$ which python2.7
/usr/bin/python2.7 # 2.7 も入ってるじゃん....
   
$ which dev_appserver.py
/usr/bin/dev_appserver.py
   
$ python /usr/bin/dev_appserver.py
Traceback (most recent call last):
     File "/usr/lib/google-cloud-sdk/platform/google_appengine/dev_appserver.py", line 95, in <module>
       assert sys.version_info[0] == 2

# python 2系で dev_appserver.py が起動できなくなっている。
# エラーが発生している箇所でバージョンを出力してみる
   
$ vi /usr/lib/google-cloud-sdk/platform/google_appengine/dev_appserver.py 
# sys.version_info[0] を print(sys.version_info[0]) に書き換える
   
$ dev_appserver.py 
3 
# 3 ?! 3?!....さん?????

どうやらコンテナ内部には python2 入っているが、dev_appserver.py を起動するときには python3 系が使われるようである。(なぜ....?)

Overview で記載した差分 (https://github.com/GoogleCloudPlatform/cloud-sdk-docker/pull/188/files) において CLOUDSDK_PYTHON=python3 という環境変数が指定されている。

そのため、 dev_appserver.py では書かれていないが、コマンド実行時には環境変数で指定された python のバージョンを使うのでは?という仮説を立て、環境変数を指定してみて上記の書き換えた dev_appserver.py を再度実行する。

$ CLOUDSDK_PYTHON=python2.7 dev_appserver.py
2  
# なるほど....

うまくいった。
つまり、公式の Docker Image を使って aetest を起動するときには CLOUDSDK_PYTHON=python2.7 を指定するか、Docker のコンテナで環境変数を指定する、もしくは独自イメージを作成するなどして python2 系をコンテナ内で参照するようにする必要がある、ということがわかった。

refs

GCPUG での議論はこの辺

alias で指定して無理やり pyhon2 系を使う方法もあるっぽいけど、個人的には好きではない...

SLI/SLO について

Overview

SRE NEXT のセッション、および懇親会でお話いただいた内容を受けて自分なりに SLI/SLO についてまとめてみる。
※ あくまで自分の解釈ですのでご了承ください。

そもそも SLI / SLO とは?

SRE本 によると以下のように定義される。

  • SLI ... Service Level Indicator

    • サービスを維持するための指標。指標なので具体的な数値ではない
  • SLO ... Service Level Object

    • SLI で計測される具体的なターゲット値やターゲット値の範囲。SLO が具体的な数値。
      • SLI <= ターゲット値(SLO)
      • 下限(SLO)<= SLI <= 上限 (SLO)

SLO と Error Budget

原則として SLO は 100% にしてはいけない
100% を目指すのは好ましくない という表現も見かけたことがあるが、SRE NEXT の懇親会で聞いた話の中には、100% にしてはいけない、という表現を使っていたと記憶している。

これはなぜなのかということを考えてみた。
まず サービスの信頼性 100% - SLO = Error Budget という式が成り立つことをまず抑える必要がある。
そして、この Error Budget について「予算」という表現をしていることがポイントなのではないかなと個人的には考えるようになった。

予算は予算だし、これは使いすぎたらもちろんアウトだけど、余らせることも好ましいことではない、というのが予算の考え方における一般的な解釈だと思う。

SLO と Error Budget についても同様のアプローチで考えることができ、SLO を下回らない範囲で色々なチャレンジをすることをSRE(ここはSREに止まらずプロダクトの開発に関わるステークホルダーに対して)に課しているという捉え方ができる。予算なんだから十分に使い切らなければならない。

そう考えると SLO はこれを下回らなければ何をしてもいい、という「余裕」を持つこと と捉えることができる。
(ymotongpoo san のセッションでも「余裕」を持つこと、という話がされていたと思う。)

SLI/SLO は「どう失敗すべきか、そしてそこから何を学ぶか」という考え方によって生み出された指標とも言えそう。

Google の SRE は予算を使い切らない場合は「本当に仕事をしてるの?」ということを問われるという話もあった。
予算を余らせている = フィーチャーや改善のチャレンジをしていないと見なされるし、そのように評価される。もし予算を使い切っていなければ、そのプロダクトから SRE の工数を削られてしまうこともあるらしい。
SLI/SLO という指標と数字が実際の業務のあり方を定義している具体的なお話でこれを聞いた時には、月並みな感想だけど「Googleってやっぱりすごい...」と感じてしまった...。

まとめ

SLI/SLO、言葉だけは知っていたし、その定義や指し示すところも理解していたつもりだったけど、突き詰めて考えると、その指標が自身の行動や業務を規定するある意味強烈な指標であり、策定するにも運用するにも覚悟が求められる指標だな、ということを考えたので備忘録で残しておく。

SRE NEXT 2020 に参加してきました

概要

SRE NEXT 2020に参加してきたのでそのまとめです。

今回 SRE NEXT に参加した背景ですが、自分は専業の SRE ではなく、さらにはインフラ経験も特にない、普段の業務でもアプリケーションを開発するエンジニアです。
ただ、普段の業務で運用がメインになる中で SRE っぽい?業務にも若干関わるようになりつつある一方、手探りで色々やってるので、何かしら指針となるプラクティスや現場のノウハウを知りたいと思ったのがきっかけです。

参加したセッション

※ 参加した各セッションのスライドはすでに Twitter 上に上がってるものもありますのでこのエントリでは特にリンク等は記載してません。

早速スライド一覧をまとめてくれてる方がいらっしゃって感謝です。

qiita.com

  • [A1] 40000 コンテナを動かす SRE チームに至るまでの道
  • [A2] パフォーマンスを最大化するための SRE のオンボーディング事例
  • [A3] freee のエンジニアは障害から何を学び、どう改善しているのか?
  • [C4] SLO Review

~ ご飯休憩(何も食べずにきたので...) ~

  • [A7] サイト信頼性エンジニアリングの原則
  • [A8] Webサービスを1日10回デプロイするための取り組み
  • [A9] パネルディスカッション

まとめてみて気づきましたが、ほぼ Room A にしかいませんでした笑

感想

いいカンファレンスでした。
参加したセッションはどれもよかったですが、特にメルペイの SRE のオンボーディング事例の話は同じ悩み(オンボーディングされる側として)を持っていたので、何度もうなづきながらセッションを聞いていました。
SRE に限らず、新しい環境に置いて「ドメイン知識」と「コミュニケーション」のキャッチアップは実は個人差がある、という課題感は自分も持っていたので、それをチームとしてサポートしていく改善をしていくことはとても良い事例でした。

特にこの業務のオンボーディングチャンネルなどは、すぐにでも実践できるようコンテンツだなと思ったりしました。
懇親会でスピーカーの tkuchiki san と少しお話しさせてもらいましたが、この辺の施策の効果はこれから計測していくそうです。
ちなみにワークエンゲージメントのお話しもしていただき、少し興味が出たので、ざっと勉強してみようと思いました。

懇親会では他にもパネルディスカッションのパネラーだった tsekine san に Google のときの SRE のあれこれを質問させていただく時間を偶然にも手に入れて、SRE 本でも語られている Google の中の実態はどうなのか?っということを色々お話しさせてもらいました。
SRE 本はやはりプラクティスとしては体系的にまとまっていますが、Google でもあの本の内容に載ってることを全て実践できているわけでもないそうです。
とはいえ、実際の Google の現場でのオンコール当番制の運用方法や、チケットの管理方法、プライオリティーの付け方やチケットのアサインの仕方、そしてトリアージの仕方、Blemeless post mortem のお話などいくつか実践に活かせそうなエッセンスがありました。
特に SLI/SLO を決めないと何も始まらないな、ということはお話をさせていただく中で何度も痛感し、やはり議論の中心にすえるべき指標を先に定義するこのは大事だと思いました。どう決めていいか悩んでる場合でもなさそうです。

ちなみに余談ですが、SRE 本は Google の書いた同人誌みたいな感じで、担当者が各々別々に寄稿してるとのことで、中にいても知らない内容があったそうです。そのため、パネルディスカッションでも話されていた「SRE 本で書かれてるプラクティスのつまみ食い」は本ができた背景から考えてもアリっぽいということを思いました。
僕もこの本は読み進めながら、どうも話が繋がらないなーみたいなことは感じてはいました。

この他にも懇親会で色々な方とお話しさせていただき、楽しい会でした。

運営の皆さん、スピーカーの皆さん、会場でお話しさせていただいた皆さん、本当にありがとうございました。

Go をバージョンごとにコマンドとして入れる

こういうことができる。便利。

$ go get golang.org/dl/go1.X.X
$ go1.X.X download
$ go1.X.X version
go version go1.X.X linux/amd64

公式のインストールページにも記載してある。
https://golang.org/doc/install#extra_versions

僕の考える net/http の定数の使いどころ

Overview

Go の net/httpパッケージ には HTTP に関連する定数が予め定義されており、HTTP クライアントを実装するときなどに自前で定義したり、GET200 といった定数を実装しなくてもいいように、標準の net/http がデザインされています。
こういう実装側で毎回用意してしまいそうな定数すら、Go は標準ライブラリで用意してくれているので、基本的にはこの定数を実装でも使っていきたいところですが、実際どういう時に使うといいのか、逆に使わないべきなのか、という点について自分なりの考えをまとめてみました。

TL;DR

  • HTTP に関係する定数は自前で定義せずに net/http で予め定義されてる定数を使おう。
  • レスポンスコードを比較するケースでは、HTTP レスポンスコードの定数よりも数字の方が可読性は高いかもね。

net/http の定数

定数一覧は net/http の Godoc を見ればわかります。

検討 - net/http の定数の使いどころ

使うべきケース

1. HTTP リクエストを作成するとき

以下のように使います。

req, err := http.NewRequest(http.MethodGet, "https://emahiro.dev", nil)

これは

req, err := http.NewRequest("GET", "https://emahiro.dev", nil)

と書いても同じですが、毎回 "GET" 書いてるとそのうち定数にしたくなる時がくると思います。
その時に定数にするくらいなら最初から net/http で定義されてる定数を使っておきましょう。

2. 正常系をチェックするとき

以下のような正常系チェックの時は、http.StatusOK を書いた方が正常系と異常系のチェックを示すという点で、直感的にはわかりやすいかと思います。

resp, err := client.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
   // 異常系のハンドリング
}

※ 一方で異常系で 400系, 500系の場合分けを行うケースでは、数字の方が比較を直感的に理解しやすいと考えたので後述の「使うべきでないケース」に追記しました。

3. HTTP のステータスコードを返すとき

これは言わずもがなかと思います。ステータスコードを指定して返すときは net/http に定数を使いましょう。

func(w http.ResponseWriter, r *http.Request){
    w.WriteHeader(http.StatusInternalServerError)
    w.Write([]byte(`hello`))
}

どのステータスコードを返すのか直感的にわかります。

4. HTTPのステータスメッセージを返すとき

これはすべき、というレベルではないのですが、HTTP のステータスコードに紐づくメッセージを返すときに Go の net/http package には StatusText というメソッドが用意されており、ステータスコードを渡すとそれにひもづくステータスコードのメッセージを返してくれるので便利です。

http.StatusText(http.StausOK) 
// Output: OK

http.StatusText(http.StatusInternalServerError) 
// Outout: Internal Server Error 

使うべきでないケース

1. レスポンスコードを比較して正常/異常系をハンドリングするとき

以下のようなケースでは数字のまま比較した方が直感的かもしれません。

resp, err := client.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if 400 <= resp.StatusCode && resp.StatusCode < 500 {
   // 400系のハンドリング
}

if 500 <= resp.StatusCode {
   // 500系のハンドリング
}

以下のように書いても同じですが、

resp, err := client.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if http.StatusBadRequest <= resp.StatusCode && resp.StatusCode < http.StatusIntenalServerError {
   // 400系のハンドリング
}

if http.StatusInternalServerError <= resp.StatusCode {
   // 500系のハンドリング
}

http.StatusBadRequest = 400 だなと頭の中で一度変換が入る、かつこのケースでは範囲で比較をしたいのであって、ステータスコード単体のチェックを行いたいわけではないので、数字のまま比較した方がより直感的な表現になると考えています。

まとめ

Go のコードを書いてて net/http 内に HTTP 関連のステータスがあらかじめ定義されてることを知った時はとても便利で多用しがちでしたが、数字の比較など逆に直感的でないケースもあるので、どういう指針でコードを書いてるのかを備忘録として記載しました。