以下の設定を ON にするとDock で iTerm が表示されなくなり、タブ移動もできなくなる。不便。
新しく iTerm2 を入れたらデフォでONになってる気がした。Offると良い。
以下の設定を ON にするとDock で iTerm が表示されなくなり、タブ移動もできなくなる。不便。
新しく iTerm2 を入れたらデフォでONになってる気がした。Offると良い。
※ 個人の記録です。
本日 2/28 が現職 DeNA の最終出社日でした。
単体で3年弱、iemo の時代を含めると3年半お世話になりました。
入社当時、何の武器も持ってなかった自分にエンジニアとしての武器を授けてくれた本当にいい会社でした。
次の職場は東京タワーの近くです。
引き続き Gopher をやっていく予定です。
GCP(App Engine)とはしばらくお別れです。
また働き始めたタイミングで何か書きます。
最終出社日でした!
— ema (@ema_hiro) 2020年2月28日
お世話になりました! pic.twitter.com/0mUB2Sk2II
先日久しぶりの障害対応を行ったときに、色々鈍っていたなと痛感したのと、いくつか改めて気づいたこともあるのでその振り返りもかねての記録です。
なおTODOリストとエントリ名はしてますが、実態はかなりポエムに近いし、勢いで書いたところがほとんどなので拙い文章であることや、ところどころ話が逸れてること自体はご容赦ください。
この記録を書こうと思った動機は、障害対応(インシデント対応)に関しては、危機管理側に立った情報はいくつか出揃ってきてるなという印象を持っている一方で、実際に現場で障害対応するときの手順だったり、自分自身まず何からやってるっけ?どういう順序で情報を出してるっけ?と言った実業務に即した内容はあまりないので、いい機会なのでいくつかまとめて、記録として残しておこうと思ったことです。
以下でリストとして出して、それぞれどんなことしてるっけ?って言うのをまとめてますが、もしかしたら今後増えていくかもしれません。
まずやるべきはこれだと思っていますが、この初動が一番難しいと思います。
というのもエンジニアなら、何かしら意図しない挙動があったときに、この振る舞いは自分が混入したものなのかどうかが気になってしまいますし、何より、障害を発生させてしまった場合に、そう報告するのって実際かなりストレスがあるなと感じてます。
ただし、そんな自分の気持ちなんかユーザーには一ミリも関係ないので、とりあえず「ヤバみ」を感じたら「障害です」と上に投げちゃう方がいいのかなと考えてます。
もちろん、ちゃんと調査したら違った、みたいな話は出てくると思いますし、そうなったらそうなったらで「拙速だったすまん」と謝ればいいかと思います。その辺の見極めって本当に難しいと思うので。自分もできてないし、「障害か?」の裏をとってる間にも状況は悪化してるかもしれない、と言うことを考えると迷ったら安全に倒す、くらいのメンタリティが大事なのではないかと思っています。
そしてこの辺の初動のスピード感については、すぐにエスカレーションが上がってくるようなチームになってることと密接に関わっていそうなので、よく言われる「組織における心理的安全性」や「人ではなく仕組みに目を向ける」といったことが体現してる組織やチームは障害対応の側面から見ても強いと思います。
話が逸れましたが、この障害かどうかの報告については、とりあえず15分程度ガッと調べて裏が取れれば、そう報告するし、迷ってもとりあえず「障害かどうかの判断に迷ってる」と言う前提と一緒に上に投げちゃうのがいいのかなと思います。
調べる時には自分は大体開発環境でも同様の事象が発生するのか?(再現手順踏まえて ※)と言うところを調べてます。
これは商用環境と開発環境で同じコードが動いてることが前提だったりするので、日頃から同じコードになるようにしておくことはこう言う時にきっと活きます。
実際のところ開発環境の検証で見つかると儲けもんなのですが、開発環境では再現しないみたいなこともしばしばあるので、その場合は再現できていない前提で、上述したように報告します。
次に直帰何かリリースがあったかを確認します。
リリースがある場合は、そのリリースによる変更が原因であることが疑われるので、まずはユーザー影響を最小限に抑えるために切り戻します。トラフィックの向き先を1つ前のバージョンに戻すか、そう言う仕組みになっていない場合は、リリースのコミットごと revert して再デプロイするのか。とはいえ、リリースによってデータに変更が生じてる場合などは...どうするんですしょうね...そのオペレーションは経験がないのでよくわからないです。
生じている変更が切り戻してもサービスに影響があるかないかをまずは確認しますし、それで影響が出る、となったらそれこそ、切り戻せないので、原因を早急に突き止めて爆速でパッチ当てる...とかになるんですかね...。
これもやったことはないので自信ないです。
※ 再現手順ってまじで大事なので、もしおかしな挙動を上げる時は再現手順とセットにして欲しい、と言うのはエンジニアとしては密かな願いでもあります。何でもかんでもバグ!障害だ!として上げられたら心折れます。
ユーザー影響をひとまず止めるために最初にどう言う対応をするのかエスカレーションしたタイミングで何かしら決定があるはずなので、その作業を先に行います。
エンジニアで現場で実際に作業をする場合を考えると、上述した切り戻し作業を行うことになると思います。
このタイミングではビジネス側で何かしら決まって、すぐに次の原因調査に移行できることもあるでしょうし、ここは原因調査と並行で進むものですし、ケースバイケースだとは思います。
何かしら期待しない動作がある、そしてユーザーに不利益がある、というような事象に遭遇したときに、どうしてその状況になってるのかを調査を行います。
調査を行うにあたっては手順は主に「事象の把握」と「原因の切り分け」の2つの工程を経て、原因を突き止めるまでに至ると考えています。
そしてこの原因調査のタイミングで最初にやってはいけないと思ってるのは コードを最初から追いかけること だと思います。
もちろん、障害の発生したポイントがすでにドメイン知識を持っている範囲で、かつ障害の発生に関連した変更に関わっていた場合などはすぐに原因を特定してパッチを当てたり、迅速な報告ができる、などもあるとは思います。
しかし、ここでは、自分が深く知らないドメインの領域や、触ったこともないマイクロサービスで発生した障害を想定します。
自分の知らないドメインであれ、サービスに関わっている以上は当事者として関わっていかないといけない、しそう言うところの障害が結局多い。と思っているからです。
話を戻します。
まず最初のプロセスとして、事象の把握で何をしてるかですが、ここではまずどう言う症状が起きてるのか?から確認していきます。そもそもどういうことを期待していて、今そうなっていないのか、がわからないと調べようも対応のしようもありません。
そのため、まずはプロダクトの振る舞いとして「何を期待するのか?」を確認するのが大事です。
ときには、この段階で「そんな風には作ってない」と言う想定外の使われ方、みたいなケースがサッと出てきて、次のステップに進めることもあります。これができればラッキーです。あとはその裏を取るためにコードを追う作業にいけるので。
次に期待結果を得るためにはシステムがどう言う風に動く必要があるのかの見通しを立てます。ここまでできてから実際に現状のシステムに置いて「どこまでが正常に動作していて、どこからが期待した動作をしていないのか?」の切り分け作業を進めながら原因の切り分けと特定を行なっています。
実際に原因を調べるときは データの状態やり、リクエストログなりを手がかりにして、1つ1つ切り分けを行なっていくと思います。
そしてこの切り分けを行うときの注意点としては、意図した振る舞いになっていないからといって、すぐにUI側の実装を疑ったり、みたいなことをしないことです。(僕はこの間やりました。完全にミスでした。)
UIはAPIから返したデータを描画するように作ってあるかもしれないし、そうした場合にはAPIのレスポンスが意図した振る舞いをするためのレスポンスになっていない、と言うことになります。
ここで初めてコードを追いかけることになると思います。
結局ここで伝えたかったのは、すぐにコードを追うな。コードを追っかけ始めるのは一番最後、と言うことをルールとして決めるだけでも障害対応の進め方は変わってくるのではないかな?ということでした。
原因調査と並行して障害報告を書きます。 ここでは、原因調査でわかっている事実を書きます。逆を言えば、原因が見つかっていない場合は 調査中。判明次第更新します と書いて「...に原因がありそうです」みたいな憶測は書きません。 この文章はエンジニア以外の人向けに書かないといけないので、専門用語やツール名は正確な情報を記述するための最低限に留めます。
書く内容としては以下かなと思います。あくまで僕の考えるところが大きいので、チームごとに内容は変わってしかるべきかと思います。
繰り返しですが、ここでは 事実のみ を記載することがポイントです。そして、原因は Blemeless である必要があります。原因は人に求めず、仕組みに求めます。
原因を特定できた、修正できるのであれば修正しましょう。この修正をどのタイミングでリリースするかは、すぐの時もあるでしょうし、翌営業日、と言うようなこともあると思います。障害が発生したのがピークタイムの時間の場合には、安全を期して翌営業日リリースと言うパターンが多いと思います。
すぐには改善できない原因もあると思いますし、その場合は別途イシュー化して対応の優先順位を意思決定層と相談することになると思います。
まぁ優先度周りについては現場に居る人間としては、あまり意識するところではないかもしれません。
大なり小なり、障害って日々発生してるものだと思っていて、ユーザーに対して期待する価値を提供できていない、もしくはサービスが壊れていると言う状態がままある、サービスに関わるエンジニアとしては常に向かい合わないといけないことだと思います。そんなしょっちゅう起きてても、オイオイ...って思いますが、成長して、常に何かしらの変更を加えているサービスでは、障害は日々発生する可能性があります。
正直このエントリで書いたことが自分で「ちゃんと」できてるかと言われると、できてないことの方が多いです。
障害対応ってまじでストレスのかかる仕事だし、起こした当人が一番頭混乱してるので、二次災害をどう起こさないか、みたいなところで最低限の冷静さを保ちながらチームをうまくまとめるのは実はめちゃくちゃハイスキルな対応です。
こればっかりは完全に場数勝負、経験が物を言う世界なのかなと。。。
昔ある人にエンジニアの戦闘力をどこで測ってます?と言う質問をしたときに「障害対応を行ってきた経験があること」と言うニュアンスのことを言っていて、改めてなるほどと思わされます。
そして、いくつかエントリの中で触れたリストの中に「...と並行して」と書きましたが、この言葉の指す通り障害対応はチームで行う作業です。
誰かが原因調査をしてるときに並行して、エスカレーションする内容をまとめたり、それを障害報告の文章に起こしたり、と言うことは協同して行います。
最後に、原因の切り分けプロセスにおいて、技術に詳しいことはそれ自体がアドバンテージになります。
例えばバックエンドのエンジニアが、サービスで使われてるフロントエンドの技術を少しでも触ったり、理屈をわかっていれば原因の切り分けの速度は上がります。
土日に勉強しろ!とは思いませんし、土日に勉強してる人が偉いとも、評価されるべきとも考えてはいませんが、日頃の技術的な研鑽はいざというときに間違いなく活きる、と言うことは事実として存在すると思います。
例えば Vue のコンポーネントにおいて、props の受け渡しがどう行われるのかを知ってると知らないとでは、APIから正しいデータを返してるかもしれないときに UI 側の原因だと突き止めることはできません。
それを作った人に聞ける、と言う恵まれた環境であればそれはそれでラッキーですが、現実はすでに作った人がいない、なんてことはザラにあります。
ドメインの知識はないけど、技術を理解していれば作った人ほど速くとは行かないまでも、切り分けを行いながら原因にたどり着ける可能性は増します。
正しく技術を理解していること、そしてそれを使えるように日頃から素振りをしてるとそれが思っていないときに日の目を見たりするので、自分ももう少し素振りの時間は取りたいなと思いました。本当は業務でそれができるとベストではありますが。
カオスエンジニアリングとは、雑に言うと障害対応訓練(雑すぎて刺されそうですが)で、マイクロサービスアーキテクチャのような小さなサービスが複数で協調して1つのシステムを作り上げてるケースの障害対応だったり、サービスの安定運用のために、それこそ日頃の素振りとして行われる類のものなのですが、例えば、仕様の漏れとか想定外の使われた方を原因とした障害って実際訓練しようがなくないですかね?なんてことを考えました。
そもそも想定してない使われ方をできてしまうことが問題ではありますが、訓練項目に入らないようなことが発生したら、訓練でやったこと全ては活きることなく、その時々で最善を尽くす他ないのかなと。 もちろん、訓練の中でログの見方だったり、APIの仕様だったりをシュッと調べられて原因特定のスピードは改善していくと思うので訓練や素振りが全て無駄にはなりませんが、訓練項目書を作る、と言うか仕様を把握するって難しいなと....。
と言う単なるぼやきでした。
まとまってないけど、障害対応はエンジニアとして成長するいい機会だと思います。
これはあくまで主観ですが、障害対応こそが成長のきっかけになると思ってるたりします。
— ema (@ema_hiro) 2020年2月7日
情報を正しく整理して、確実にエスカレーションする機会というのは、中々ないですが、障害対応はある種このスキルをつけるいい機会だなと考えてますし、これを何度も潜り抜けてる人の戦闘力は実際半端なかったりするのである程度確度の高い事実だと思ってます。
— ema (@ema_hiro) 2020年2月7日
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
こういうことができるやつね。 インフラ触れなくなって久しいので遠い過去の記憶になっていました。
Update to Pyhon3 にて Goole Cloud SDKを同梱した Docker Image 本体にて Python2 のサポートが切られたために、Python2 系でのみ動作する App Engine を起動するコマンドの dev_appserver.py
が動作せず、本家の Google Cloud SDK - Dockerfile を使用して aetest を走らせてる場合(CI上などで)は、App Engine のインスタンスの起動に失敗して CI が落ちる、という挙動を確認しました。
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 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
系をコンテナ内で参照するようにする必要がある、ということがわかった。
GCPUG での議論はこの辺
alias で指定して無理やり pyhon2 系を使う方法もあるっぽいけど、個人的には好きではない...
SRE NEXT のセッション、および懇親会でお話いただいた内容を受けて自分なりに SLI/SLO についてまとめてみる。
※ あくまで自分の解釈ですのでご了承ください。
SRE本 によると以下のように定義される。
SLI ... Service Level Indicator
SLO ... Service Level Object
原則として 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 に参加した背景ですが、自分は専業の SRE ではなく、さらにはインフラ経験も特にない、普段の業務でもアプリケーションを開発するエンジニアです。
ただ、普段の業務で運用がメインになる中で SRE っぽい?業務にも若干関わるようになりつつある一方、手探りで色々やってるので、何かしら指針となるプラクティスや現場のノウハウを知りたいと思ったのがきっかけです。
※ 参加した各セッションのスライドはすでに Twitter 上に上がってるものもありますのでこのエントリでは特にリンク等は記載してません。
早速スライド一覧をまとめてくれてる方がいらっしゃって感謝です。
~ ご飯休憩(何も食べずにきたので...) ~
まとめてみて気づきましたが、ほぼ Room A にしかいませんでした笑
いいカンファレンスでした。
参加したセッションはどれもよかったですが、特にメルペイの SRE のオンボーディング事例の話は同じ悩み(オンボーディングされる側として)を持っていたので、何度もうなづきながらセッションを聞いていました。
SRE に限らず、新しい環境に置いて「ドメイン知識」と「コミュニケーション」のキャッチアップは実は個人差がある、という課題感は自分も持っていたので、それをチームとしてサポートしていく改善をしていくことはとても良い事例でした。
これ良さそう。
— ema (@ema_hiro) 2020年1月25日
welcome系のチャンネル以外に、チームのタスクに特化したオンボーディングチャンネルあると業務の質問のハードルが下がると思う。新しく来てくれた方のコミュニケーションの心理的なハードルを下げるのはチームの役割だよねぇ。#srenext pic.twitter.com/If7uL2YuoG
特にこの業務のオンボーディングチャンネルなどは、すぐにでも実践できるようコンテンツだなと思ったりしました。
懇親会でスピーカーの tkuchiki san と少しお話しさせてもらいましたが、この辺の施策の効果はこれから計測していくそうです。
ちなみにワークエンゲージメントのお話しもしていただき、少し興味が出たので、ざっと勉強してみようと思いました。
懇親会では他にもパネルディスカッションのパネラーだった tsekine san に Google のときの SRE のあれこれを質問させていただく時間を偶然にも手に入れて、SRE 本でも語られている Google の中の実態はどうなのか?っということを色々お話しさせてもらいました。
SRE 本はやはりプラクティスとしては体系的にまとまっていますが、Google でもあの本の内容に載ってることを全て実践できているわけでもないそうです。
とはいえ、実際の Google の現場でのオンコール当番制の運用方法や、チケットの管理方法、プライオリティーの付け方やチケットのアサインの仕方、そしてトリアージの仕方、Blemeless post mortem のお話などいくつか実践に活かせそうなエッセンスがありました。
特に SLI/SLO を決めないと何も始まらないな、ということはお話をさせていただく中で何度も痛感し、やはり議論の中心にすえるべき指標を先に定義するこのは大事だと思いました。どう決めていいか悩んでる場合でもなさそうです。
ちなみに余談ですが、SRE 本は Google の書いた同人誌みたいな感じで、担当者が各々別々に寄稿してるとのことで、中にいても知らない内容があったそうです。そのため、パネルディスカッションでも話されていた「SRE 本で書かれてるプラクティスのつまみ食い」は本ができた背景から考えてもアリっぽいということを思いました。
僕もこの本は読み進めながら、どうも話が繋がらないなーみたいなことは感じてはいました。
この他にも懇親会で色々な方とお話しさせていただき、楽しい会でした。
運営の皆さん、スピーカーの皆さん、会場でお話しさせていただいた皆さん、本当にありがとうございました。
こういうことができる。便利。
$ 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
Go の net/httpパッケージ には HTTP に関連する定数が予め定義されており、HTTP クライアントを実装するときなどに自前で定義したり、GET
や 200
といった定数を実装しなくてもいいように、標準の net/http
がデザインされています。
こういう実装側で毎回用意してしまいそうな定数すら、Go は標準ライブラリで用意してくれているので、基本的にはこの定数を実装でも使っていきたいところですが、実際どういう時に使うといいのか、逆に使わないべきなのか、という点について自分なりの考えをまとめてみました。
net/http
で予め定義されてる定数を使おう。net/http
の定数定数一覧は net/http の Godoc を見ればわかります。
net/http
の定数の使いどころ以下のように使います。
req, err := http.NewRequest(http.MethodGet, "https://emahiro.dev", nil)
これは
req, err := http.NewRequest("GET", "https://emahiro.dev", nil)
と書いても同じですが、毎回 "GET"
書いてるとそのうち定数にしたくなる時がくると思います。
その時に定数にするくらいなら最初から net/http
で定義されてる定数を使っておきましょう。
以下のような正常系チェックの時は、http.StatusOK
を書いた方が正常系と異常系のチェックを示すという点で、直感的にはわかりやすいかと思います。
resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { // 異常系のハンドリング }
※ 一方で異常系で 400系, 500系の場合分けを行うケースでは、数字の方が比較を直感的に理解しやすいと考えたので後述の「使うべきでないケース」に追記しました。
これは言わずもがなかと思います。ステータスコードを指定して返すときは net/http に定数を使いましょう。
func(w http.ResponseWriter, r *http.Request){ w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(`hello`)) }
どのステータスコードを返すのか直感的にわかります。
これはすべき、というレベルではないのですが、HTTP のステータスコードに紐づくメッセージを返すときに Go の net/http package には StatusText というメソッドが用意されており、ステータスコードを渡すとそれにひもづくステータスコードのメッセージを返してくれるので便利です。
http.StatusText(http.StausOK) // Output: OK http.StatusText(http.StatusInternalServerError) // Outout: Internal Server Error
以下のようなケースでは数字のまま比較した方が直感的かもしれません。
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 関連のステータスがあらかじめ定義されてることを知った時はとても便利で多用しがちでしたが、数字の比較など逆に直感的でないケースもあるので、どういう指針でコードを書いてるのかを備忘録として記載しました。
この記事がちょうど 200 記事目です。
100 記事に到達してからも、ゆるゆると続けていたらいつの間にか 200 記事をインターネット上に放流してました。
ちなみに、100 記事目当時の投稿は以下
全エントリのなかで、この振り返りを書いてる時点での流入(アクセス)の多いエントリ TOP5 は以下になります。
一番アクセスが多いのは多いのは webpack × html のエントリなのが意外です。世の中の需要がなんとなく推し量れる気がします。
書いた本人がいうのもアレですが、すでに古くなっている内容があるのであまり参考にしないほうがいいと思います。
検索に引っかかってしまうから仕方ないのかもしれませんが...
書いた本人としてイチオシなのは Go を描き始めた当時に書いた Go の template/html の値評価のチートシートのエントリです。
自分向けに書いたつもりが、マークアップエンジニアの方からも「参考にしてる」とフィードバックいただきましたし、これはまだまだ使えるエントリだと思っています。
KPT 方式で振り返ってみます。
100 記事継続した当初の目標の1つであった「無理しない」ということをテーマにゆるゆるとアウトプットを続け、結果として習慣化していることはよかったです。
また、業務の中でも「これブログに書けそうだな」という観点で手順なり、ハマったところなりをメモるようになり、それなりに再現性のあるエントリを書けるようになってきました。
あと継続してるとトンマナのズレとかに気づけるようになってきました。
(まだ、「ですます。」調と「である。」調の混同とか、たまにやりがちですが。)
社内の人にも補足されつつあり、無理せず、雑な内容でも書くことを大前提としつつも、変なことを書けないという妙なプレッシャーに悩まされています。
それでも見てもらえるのは嬉しいので、継続するモチベーションになっています。
これはまぁその通りですね。
上記とも被りますが「ブログ拝見しました」と言ってもらえるのは素直に嬉しいし、そういうところの話は聞いてみたくなります。
Problem というほどのものもないですが、300 エントリ目指して改善したいポイントをあげてみます。
業務に関連する技術の備忘録という側面があるのでどうしても Go, GCP に寄りがちでした。
この2つを軸にしておくことは変わりませんが、ちょうどこの三連休で Firebase にも入門してみたので、もう少し題材とする技術要素の幅は広げてみてもいいかもしれないかなと思っています。
いい感じの章立てがまだできないので、文章の構造化力が足りていないのかなと思います。
全然いいし、こういうのをどんどん書いていきたい所存なんですけど、いやそれは README 読めばわかるやん、みたいなこともあるので、単なる調べた系のエントリに閉じないオリジナリティーを出していきたいと思います。
ないものはないのでしょうがない。
300 記事に向け「これから」チャレンジしてみたいことを書いてみます。
2017.4 頃に、このはてなブログを本格的に運用し始めたので、3年弱くらいで 200 投稿した計算になります。
始めた当初は 200 個もの拙い文章ををインターネット上に放流するなんて考えてもいませんでした。
質にもそれなりに目を向けつつ、引き続き「無理をしない」「続けることが目標」という意識低めのスタンスは変えずに 300 記事目指します。
Nuxt + Firebase でポートフォリオサイトを作って公開するまでの過程で行なったことをまとめます。
(まだまだやらないといけないことはありますが、とりあえず最初の一歩でやったことをまとめるところまで)
なお作成したポートフォリオサイトはこちら -> emahiro.dev
自分へのコンタクト方法に SNS アイコンを使うケースが多いと思います。
流石にインターネットからアイコンを拾ってきて、画像として表示するみたいな過去の自分がやっていたような道は通りなくなかったので、アイコンを無償で使える何かがないかを探して、今回は Font Awesome というサービスの無償プランを使いました。
ブランドのアイコン一覧 を軽く眺めたところ、欲しいサービスのアイコンは網羅されてそうだったので、今の所無償プランで問題なさそうです。
今回は無償プランで使える Solid
と Brands
をインストールしました。
Nuxt(Vue) への導入方法については こちら を参照してください。Font Awesome 専用のツールのインストールが必要です。
インストール手順は こちら
npm i --save @fortawesome/fontawesome-svg-core @fortawesome/vue-fontawesome @fortawesome/free-solid-svg-icons @fortawesome/free-brands-svg-icons @fortawesome/free-regular-svg-icons
Nuxt での使用手順は こちら
Font Awesome を使えるようにするには plugins
配下に fontawesome.js
というファイルを作成し、Nuxt での導入方法に記載してある Component の設定を追記する必要がありました。
fontawesome.js
で Font Awesome のコンポーネントをグローバルに設定したので、使いたいコンポーネントで FontAwesomeIcon
を指定することで <font-awesome-icon :icon="['fab', 'twitter-square']" />
で Font Awesome のアイコンが使えるようになります。
Nuxt のアイコンがそのまま使われてなのでとりあえず適当な顔文字に変更しました。
そのうちちゃんとしたやつ作りたいです。
Figma とか使って。
はてなブログの RSS を表示させようと考えましたので、Cloud Functions でそのエンドポイントを実装してみます。
公式ドキュメント にローカルのエミュレーターの起動方法について記載してあります。
firebase emulators:start emulators: Starting emulators: functions, firestore, database, hosting, pubsub ⚠ Your requested "node" version "8" doesn't match your global version "10" ✔ functions: Emulator started at http://localhost:5001 i firestore: Serving ALL traffic (including WebChannel) on http://localhost:8080 ...
起動した状態で local で curl を叩くと結果が返ってきます。
curl -i localhost:5001/emahiro-dev/us-central1/helloWorld HTTP/1.1 200 OK x-powered-by: Express content-type: text/html; charset=utf-8 content-length: 20 etag: W/"14-z3iZXchEt5DVWZKsMncy8Wl4KSQ" date: Sun, 12 Jan 2020 14:15:14 GMT connection: close Hello from Firebase!
ランタイムを Node 10 に変更し、標準で us-central が指定されてしまう Functions のリージョンを日本リージョンを使用するように設定を変更します。
diff --git a/functions/package.json b/functions/package.json index 02a2f48..61f7b66 100644 --- a/functions/package.json +++ b/functions/package.json @@ -10,7 +10,7 @@ "logs": "firebase functions:log" }, "engines": { - "node": "8" + "node": "10" }, "main": "lib/index.js", "dependencies": {
diff --git a/functions/src/index.ts b/functions/src/index.ts index e6e0bb8..fa15f33 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -3,6 +3,10 @@ import * as functions from "firebase-functions"; // // Start writing Firebase Functions // // https://firebase.google.com/docs/functions/typescript -export const helloWorld = functions.https.onRequest((request, response) => { - response.send("Hello from Firebase!"); -}); +const jp = "asia-northeast1"; + +export const helloWorld = functions + .region(jp) // 追加 + .https.onRequest((request, response) => { + response.send("Hello from Firebase!"); + });
functions/index.ts
に新しい関数を定義したら npm run build
をします。
( functions/package.json
内に定義されてる build スクリプトの中身は tsc ですが )
Functions の動作確認には上述した Firebase Functions のエミュレーターを使用します。
一旦自分が Nuxt + Firebase でポートフォリオサイトを作成するところまでやったことをまとめました。
今後、機能やコンテンツを増やしたときにはその内容を公開していきたいと思います。
Nuxt のアプリケーションにおいて HTML の head タグを編集する方法を記載します。
nuxt.config.js
の head
property を更新します。
デフォルトで生成される設定ファイルは以下です。
{ head: { title: process.env.npm_package_name || '', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: process.env.npm_package_description || '' } ], }, }
レスポンシブ設定をするために Bootstrap-Vue の Responsive meta tag
に記載されてる内容に更新します。
diff --git a/nuxt.config.js b/nuxt.config.js index 53fe780..fe3be60 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -8,7 +8,7 @@ module.exports = { title: process.env.npm_package_name || '', meta: [ { charset: 'utf-8' }, - { name: 'viewport', content: 'width=device-width, initial-scale=1' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no' }, { hid: 'description', name: 'description', content: process.env.npm_package_description || '' } ], link: [
Nuxt と Firebase Hosting を使ってサイトを公開したのでその記録について記載します。
公式のインストール手順 に則って進めます。
npx create-nuxt-app emahiro.dev
今回自分は以下のような設定にしました。
create-nuxt-app v2.12.0 ✨ Generating Nuxt.js project in emahiro.dev ? Project name emahiro.dev ? Project description emahiro's portfolio site ? Author name emahiro ? Choose the package manager Npm ? Choose UI framework Bootstrap Vue ? Choose custom server framework Micro ? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Choose test framework Jest ? Choose rendering mode Single Page App
ただ、裏側に Firebase を使っているので必要なかったものもありました。
npm run build npm run start # start server at localhost:3000
これで .nuxt 配下にビルドされたアプリケーションが作成され、 localhost:3000
で接続できるようになります。
Firebase の通常設定の流れのまま firebase deploy
を行なった場合、デプロイが完了しても Firebase Hosting のアプリのテンプレートが反映されて Nuxt のアプリが反映されませんでした。
原因は単純で、Firebase の通常の設定フローでぽちぽち進めていった場合に、Firebase Hosting で見る先は $FB_PROJECT_ROOT/pulic/index.html
になり、 Nuxt の成果物とはディレクトリ( dist
)が異なります。
そのため、Nuxt のビルドした成果物を Firebase Hosting で見れるようにする必要があります。
Nuxt の成果物を Firebase Hosting で参照するには Firebase Hosting の参照先を設定時の public
から Nuxt の成果物が配置される dist
に変更します。
diff --git a/firebase.json b/firebase.json index 7b0091d..5a0db41 100644 --- a/firebase.json +++ b/firebase.json @@ -13,7 +13,7 @@ ] }, "hosting": { - "public": "public", + "public": "dist", "ignore": [ "firebase.json", "**/.*",
これで再度 firebase deploy
を実行すると Nuxt のページが Firebase Hosting で参照されるようになりました。
Firebase を始めるに当たって、Firebase のセットアップからカスタムドメインの設定を行い、簡単な hello world を表示したところまでやったことを記載します。
Firebase にて「コンソールに移動」を選択します。
コンソールに移動するだけでアカウントは無料プラン( Sparkプラン )に登録されてました。
Firebase つよつよな令和のシステムアーキテクトより無料でも使えるけど、機能制限を受けるのでさっさと Blaze (従量課金プラン) にしなさいとアドバイスをもらったので従量課金プランに変更しました。
とはいえ従量課金制で運用するのは怖いので課金アラートを設定します。
プロジェクト名の右の「歯車」マークから「使用量と制限」を選択します。
使用量と制限 > 詳細と設定 > 予算とアラート > 「最初の予算を作成」を選択。
予算の範囲を指定できるので
僕は予算制限をとりあえず ¥1000 に設定して以下のような閾値にしました。
とりあえず実値で閾値を設定しましたが、予測値を指定することも可能です。
最終的には以下のように課金アラート一覧に表示されます。
あらかじめ取得しておいたドメインと紐付けます。
僕は Google Domain で emahiro.dev
というドメインを取得していたのでこちらを使います。
ドメインと紐づけるには Firebase Hosting を利用します。
Firebase Hosting にアクセスすると、まず最初に設定を始める必要があるので ドキュメント に則って Firebase の設定を進めます。
npm install -g firebase-tools firebase login # firebase に登録してる gmail を選択する。 cd $MYPROJECT_ROOT firebase init
プロジェクトにおいて Firebase のどのリソースを使うのかを設定する時に「全て」を選択したために、Cloud FireStore のロケーションを設定されておらずセットアップがコケることがありました。
firebase init # 略 === Firestore Setup Error: Cloud resource location is not set for this project but the operation you are attempting to perform in Cloud Firestore requires it. Please see this documentation for more details: https://firebase.google.com/docs/projects/locations
これを解消するために Cloud Firestore のスタートページでロケーションを設定します。
これで再度 setup を実行し、最終的に以下のようなファイル群が生成されます。
.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
functions/
public/
storage.rules
Firebase のセットアップが完了したので Firebase Hosting へのデプロイを行います。
firebase deploy
Firebase のセットアップという横道に逸れましたが、設定が完了して、deploy -> firebase のデフォルト URL でアプリケーションが表示できるところまで確認したので、emahiro.dev
の紐付け作業を行います。
Firebase Hosting のドメインでカスタムドメインを追加します。
ドメインを追加すると IP アドレスが割り振られます。「表示」から確認できます。
Google Domains の DNS > カスタムリソースレコードの項目で、上記の Firebase にカスタムドメインを追加した時に割り振られた IP アドレスを追加します。
カスタムリソースに追加すると Firebase のドメインの欄に追加したドメインの反映が始まり「保留中」表示に変更になります。
正常に反映されると「接続されています」表示になります。
これで紐付けは完了で、emahiro.dev でアクセスが可能になりました。
デプロイして喜んでたそばから Firebase つよつよおじさんが /__/firebase/init.js
というファイルにアクセスしてきて API Key 丸々外部に公開されてることを教えてくれました( いじめられました )
Nuxtのロゴしかないのに早速いじめられててウケる。
— ema (@ema_hiro) 2020年1月11日
ただ、注意しないといけないところがあるんやで、ということは理解した。#mokumoku_onsen
なるほどこの辺が公開されちゃってるわけなので、Firestore のセキュリティルール等を完全に理解した上で Firebase を使わないと 簡単におもちゃにされる 脆弱なアプリケーションになってしまうわけですね。
ちなみのこのエンドポイントはアプリケーション側でアクセスしないように制御できるようなものでもないとのこと。
ここで公開されてる環境変数を使うことも普通にあるらしい。ふむふむ。
Firebase こと始めができました。
サクサク設定を進めて、簡単に公開までできてしまったので、ほんとすごいツールが出てきたな (小並) といった感じでした。
(前々から聞いてはいましたけど)
関数を引数に指定してすることで、呼び出し先の関数内にて、特定の条件で指定した関数を実行する (Callback のように振る舞わせる) 実装方法をやってみたのでその記録を記載する。
R/W の競合を発生させないために sync.Mutex
をフィールドに持っている struct に対してロック中に別の関数を実行させたいケースなどを考えたい。
例えば、上記のお試し実装にあるような 一度の呼び出しでオンメモリキャッシュが存在すればキャッシュを返し、なければ API の呼び出しを待ってキャッシュに書き込む、みたいなことをしたいときに、引数に関数を当てておいて呼び出し元ではロックをかけず、呼び出し先でかけてるロックを利用する、みたいなケースが考えられると思う。
この他にどんなときに使えるのか、ということはこのエントリ執筆時点では考えていないが、こういういった関数を渡して、呼び出し先でゴニョるみたいな実装方法は覚えていて損はないなと思った。