フツーって言うなぁ!

フツーなサラリーマンのフツーな嘆き.

チームでコードレビューをしているときに考えていること

この記事はなに?

最近、自分の経験や考えていることを言語化してみる実験をしています。 自分で言語化したことのある概念については自信を持って他の人に話せるし、一般化して横展開していくことも可能になるので、メリットが大きいのかなと。

第一弾として、私がテックリードとしてチーム内でコードレビューをしている際に考えていることをまとめてみました。

注意点

  • 思いついたら随時更新します
  • 今回の話のターゲットは主にWebフロントエンド/バックエンドのアプリですが、その他の開発にも使えるノウハウはあると思います
  • ここで書いているのは、仕事でチームを組んだ際にピアレビューをする際の話です。OSS等のコードレビューとは違いがあるかもしれないです

TL; DR

  • 十分な事前準備をすることでレビューの手間を減らそう
  • 目的にフォーカスすることで本質的で建設的なレビューをしよう
  • コードレビューを通してレビュワー、レビュイーともにもっと議論しよう

プルリクエストを出す前にやっておきたいこと(コードレビューで見たくないこと)

以下については、PRを出す前にやっておくべき。

コードレビューの目的と期待値についての認識合わせ

これが一番大事。

これはすべての「作業指示」に共通することだが、いちばん大事なのは指示者(この場合はレビュワー)と作業者(この場合はレビュイー)で事前に作業の目的とその期待値を揃えておくこと。 これが十分でないとあとで揉める原因になるが、意外とできていない場合が多い。

少なくとも、

  • レビューの対象になる機能/バグ対応
  • 何が完了した時点で、どの点をレビューするか/レビュー依頼するか?
  • どれぐらいの変更が入り、それにどれぐらい工数がかかりそうか

あたりについては事前に握っておく。 時間があればissueチケットにまとめておけばいいし、なければチャット等に残しておくとよい(口約束だけだと忘れられやすい)。

ここで、PRの規模が大きくなりすぎないように注意する。

一般に、レビューするコードの行数/範囲が大きくなるとレビュワーもレビュイーもしんどい(イメージとしてはコードの行数に対して指数関数的につらくなる)。 基本的にPRの単位はひとつのフィーチャーを対象とし、それでもフィーチャーが大きすぎる場合は、小さなフィーチャーに分割できないかを考える。 一概には言えないが、変更されるコードが本番コード/テストコード含め800行、ファイル数で30を超えると分割の目安になる(リファクタリングとかでない限りこんなに大きくなることはほぼない)。

例:
X機能の実装(UIデザインのみ)のレビュー。ビジネスロジックと単体テストは範囲に含まず、正常系のUIデザインのみをレビューのターゲットとする。
変更するクラスはXComponentとYService。
1日あたり3.5時間(約50%)の稼働を想定して、初回レビューまで2人日程度を見込む。

また、可能なら、コードレビューで見るべき/見るべきでない点についてのガイドラインを作って先に共有しておくとよい。 このブログ記事も半分ぐらいはそれを意図している。

設計/修正方針に関する打ち合わせ

上とは別に、時間があればおおまかに設計/修正の方針について認識を合わせておくとよい。

  • UIデザイン
  • APIインタフェースデザイン
  • クラス設計
  • ディレクトリ構成

あたりを握っておく。

CIを通す

コードレビューを持続可能な仕組みにするために、レビュワーの負担はできるだけ減らしたい。 人間が見る必要のないもの(見るべきではないもの)については、自動化された仕組みで拾えるようにしておく。

  • コードフォーマットが正しいか?

    Linter/フォーマッタにかける。フォーマットに違反している場合、PRを出した時かビルド時にCIで落とす

  • コードの複雑度と臭い

    静的コード解析にかける。ある程度の基準を設けて開発者間で合意し、それを下回るものはCIで落とす。もちろん、静的解析だけでは全部拾いきれないので、漏れた部分はレビューの対象に回す

  • 単体テスト

    これも、どのクラスを対象としてどの程度のカバレッジ単体テストを行うかの基準を設けて合意し、それを下回るものはCIで落とす。カバレッジ基準については、むやみに高くしすぎるとコードの質に寄与しないテストが増えてしまうことがあるため、うまい落とし所を探るようにする

ルフレビュー

コードレビューをレビュワーに依頼する(= PRを出す)前に、レビュイー自身で自分が書いたコードをレビューするようにする。 これは意外と重要で、実際にやってみると、これだけでもけっこう見落としや間違いが見つかる。

観点は以下のような感じ。

  • PRの形式が正しいか?

    Issueチケットと紐付けられているか、PRを出すターゲットブランチが正しいか、ブランチ名やコミットがあらかじめ合意した形式に従っているか、diffは想定通りか、コンフリクトが起こっていないかなど、形式的にチェックできることについてはチェックし、問題がある場合は修正しておく。これらもCIに載せられそうならCIで落とすようにする。

  • コードが期待通り動くかどうか?

    機能要件を満たさない、つまり「設計書通りに動かない」コードをレビューに出さない/出させない。当たり前のことのように思えるが、特に経験の少ないエンジニアがレビュイーになっている場合は、ケアレスミスや単純な見落としが多いので、基本的な挙動については必ずPR提出前に自分で確認させるようにする。 前述した「レビューの目的と期待値」にもよるが、多くの場合、PRを出す時点では自動化された単体テストまで終了していると思う。ここでの「確認」は正常系のさっくりした挙動確認にとどめ、細かい部分はコードレビュー、スプリントレビュー、結合テスト、QA等で多角的に拾うとよい。

  • PRにおける議論の論点をまとめる

    事前に、「コードを書いていて疑問に思ったこと」、「意見がほしいこと」、「やってもよさそうな気がするけどやらなかったこと」、「やりたかったけどやれなかったこと」について、理由も含めてまとめておくと、PRにおいて議論しやすい。レビュワーに直接伝えてもいいし、PRのコメントとして記載してもよい。 レビューは双方向の議論の場であることを意識し、積極的に意見を出していくと、理解が深まってレビューの価値が高まると思う。

その他、もちろん下記のレビュー観点についても、レビュイーの責任でセルフレビューし、必要があれば修正するかPRコメントに記載しておく。

コードレビューで見たいこと

自分がレビューをする時に気にしている観点をざっとまとめておく。 まだ足りない点やまとめきれていない点があるので、見つけたら随時追加する。

PRの形式

  • Issueチケットとの紐付け
    • あとでコミット履歴を見たときに変更理由が明確なようにしておく
  • ブランチ戦略との整合性
  • Descriptionの妥当性(事前に合意したことをやろうとしているか?)
  • コミットが適切か?
    • コミットメッセージ、粒度は適切か?
  • コミットのコンフリクトがないか?

PR内の事前質問についての回答

レビュイーから質問や意見を求めるコメントがあれば、それに回答する。再レビューの場合は、以前に行った指摘が意図通りに解決されているかについても確認する。

機能要件を満たしているか

前述の通り、ここはレビュイーによるセルフレビューにより解決できていることを期待したいが、設計についての認識違いや実装の瑕疵など、拾えそうなものについては拾う。 一行一行見ていくと時間がかかるため、実際に開発環境等で動作させてチェックするとよい。

非機能要件を満たしているか

上述の通り、コードレビューでは、主に非機能要件についてのレビューを中心にしたい。

以下、思いつく限りの観点を箇条書きで書いていく。

非機能要件については、IPAの非機能要求グレードを参考にしている。

www.ipa.go.jp

可用性について

  • エッジケースを適切にハンドリングできているか?
    • null値/空文字/空集合/デフォルト値
    • 境界値(0、負数、intの最大値とそれより大きい数など)
    • 型変換(intの変数に文字列を入れたときどうなるか?)
    • ネットワークエラー/IOエラー/DBエラー
    • 非同期処理によるエラー(排他制御のミス、デッドロックなど)

パフォーマンス(性能・拡張性)について

  • (動作はするが)現実的でない実行時間(≒ループ回数)になっていないか?
    • 例えば、通常のプログラミング言語 10^{9}回以上ループが走ると、実行に10秒以上かかる可能性が高く、Web APIであればほとんどの場合非機能要件を満たせない
  • 適切なアルゴリズムとデータ構造が使われているか?
    • 例えば、HashMapを使えばO(1)で検索できるところをArrayListで検索していないか?
    • もちろん拡張性とのトレードオフはあるので、場合によっては遅い方を選ぶこともある
  • 現実的でない量のメモリを確保する実装になっていないか?
  • 現実的でない量のスレッドを作成する実装になっていないか?
  • スレッドセーフか?
  • 利用したメモリをアプリのライフサイクルの中ですべて回収しているか?メモリリークしていないか?
  • アプリが吐き出すログやその他のファイルの分量は適切か?ログ溢れが起こらないか?
  • DBへのコミットの単位は適切か?
    • 特にバッチ/ワーカーアプリにおける逐次コミットは遅さの原因になることが多い。バルクアップデートの手段がないか検討すること

可読性(運用・保守性)について

  • クラス/メソッド設計
    • 単一責任の原則(SRP)を満たしているか?神クラスになっていないか?
    • DRYを満たしているか?まったく同じことを実現しようとしているのにクラス、メソッド等を分けていないか?
    • クラス/メソッド/変数のスコープは妥当か?
      • 例えば、ローカル変数で十分なことをインスタンス変数で行っていないか?
    • 1メソッドで実行しようとしていることが大きくなりすぎていないか?
      • 特に、メソッドの行数が多すぎたり(1メソッド50行以上とか)、メソッド内の条件分岐が多くなりすぎたり(ネストが3階層以上とか)していないか?
  • コーディング規約
    • クラス/メソッド/変数の命名は妥当か?
      • 命名はコーディング規約に沿ったものか?
      • 名前が体を表しているか?
    • コードコメントは適切な内容か?
      • コードを読むだけでは意図のわかりづらい箇所にコードコメントを入れてあるか?
        • 特に、暫定対応をした箇所には対応するチケット番号を書いておくとよい
      • コードコメントの量は必要十分か?また、内容が陳腐化していないか?
        • "How"(どのように実現するか?)ではなく"Why"(なぜこの実装になっているか?)や"Why not"(なぜ他の実装になっていないか?)をソースコードにコメントするようにする。Howはソースコードを読めばすぐにわかり、かつ頻繁に陳腐化するのに対し、他はコードに残らない重要な情報になることが多いため。
        • 不要なコードコメントは減らし、できるだけクラス/メソッド/変数名に伝えたい情報を詰め込むようにする
        • コードに残す必要のない、議論の過程などについてはコードコメントではなくPRコメントに残すようにする
  • ログ
    • ログレベルは適切か?
    • ログ文言は事象を表すわかりやすいものになっているか?エラーログの場合、エラーが発生した場所と時間、原因がログから追跡できるようになっているか?
  • テスタビリティ
    • メソッドが不要な副作用を持っていないか?
    • イミュータブルな変数/メソッドを利用しているか?
    • ユーザからの入力値や現在時刻、ランダム値など、自動テストの実施において障害になる値を外部化できているか?
  • 自動テスト
    • 単体テストが十分にロジックを網羅しているか?
    • タイミングによってテストが通ったり落ちたりするようなつくりになっていないか?
      • 特に、非同期処理や時刻(現在時刻、月末、うるう年、タイムゾーンなど)の扱いが正確にできているか?
    • 単体テストの量は必要十分か?
      • テストケースとして重複しているテストや、コードの質に寄与しないテストがないか?
  • 設定、環境構築
    • 容易にアプリのビルド/動作確認ができるようになっているか?複雑な手順が必要になってしまっていないか?
    • 設定値の必要十分な外部化ができているか?
      • ステージング環境がある場合、容易に設定をステージング環境向けに切り替えられるか?

移行性について

  • 捨てやすいコードになっているか?
    • 例えば、Web APIにおけるコントローラ、Webフロントシステムにおけるコンポーネントなどが、今後の運用の中で通常の使用に即した分割になっており、不要になったり、システム分割したりする際に容易になっているか?
  • リリース後に問題が発生した場合、ロールバックが可能な実装になっているか?
  • 後方互換性が必要な場合、それを担保できているか?
    • 旧バージョンを廃棄することが決まった場合、容易に可能か?

セキュリティについて

  • 最新版のプログラミング言語/フレームワーク/ライブラリを使っているか?
    • 改修の場合は、影響範囲を見ながらバージョンアップするか都度判断する
  • ミドルウェアのユーザ名/パスワードにデフォルト値や安易な値を使っていないか?
  • 入力値のバリデーションが十分であるか?
  • (特に通信がインターネットを介す場合、)安全な通信を利用しているか?
  • クレデンシャル(パスワードや秘密鍵等)が外部に露出していないか?
    • クレデンシャルについては、可能なら設計時に別リポジトリで管理し、アプリ実行時に値を注入するようにする
  • 技術詳細など、ユーザにとって不必要な情報を外部に露出していないか?
  • 典型的な攻撃(DoS/XSS/CSRF/SQLインジェクション/コマンドインジェクション/ディレクトリトラバーサルなど)に対処できる形になっているか?

設計指針に合致した変更か?

プロジェクトとして文書化された設計指針があるのであれば、それに従っているかもここで見る。 なければ、テックリードの責任で変更がコードベース内での一貫性を保っているかを確かめる。

必要十分な変更か?

「PRの目的を達成するために必要な変更は余さず入れる」、「PRの目的を達成するために不要な変更は何も入れない」という観点も重要。

例えば、

  • 動作させるのに必要な設定変更がコミットから漏れていないか?
    • 例1:NPMのpackage-lock.json
    • 例2:バージョンを上げるために必要なファイル(AndroidManifest.xmlのversionCode)
  • 余計なファイル/クラス/メソッド/変数/ログ等が含まれていないか?
  • ファイルの実行権限等が必要十分か?

など。

特に、既存のコードを変更/削除するPRの場合は、他に変更漏れ/消し忘れがないか気をつける。レビューツールには変更のあるファイルのdiffしか出てこないので、これはコードレビューの観点として忘れずやった方がよい。

また、PRの目的に合致しないが有用と思われるリファクタについては、面倒でも新しくPRを作って入れてもらうようにする。

プルリクエストを承認する前にやっておきたいこと

テックリードとしてPRを承認し、マージする際には、以下を確認しておくとよいと思う。

  • 指摘対応がすべて終了しているかを確認する
  • レビューの結果見つかった残作業やさらなる改善点がある場合は、レビュイーと話した上で、必要であればチケット化しておく
  • 議論の記録がたどれるようにしておく
    • 議論の結果決まったことがあれば、issueチケットかPRのどちらかに記載しておく

その他気をつけたいこと

その他、レビューをしていく中で培った私見をざっとまとめておく。

目的に沿ったレビューをする

自分自身、意外とやってしまうのは、PRの目的にそぐわない指摘をしてしまうこと。 あまり関係ない指摘を続けてしまうと、それがある種の正論であったとしても、手戻りによる工数が膨らんだり、レビュイーを必要以上に混乱させることになる。 事前に握った目的外の指摘は最小限に留めるようにする。

明らかにスケジュール内で実装不可能でありリリースのブロッカーにならないものについては、それを明記した上で「今後の課題」とする、あるいは別のチケットに切り出すことを考えるべき。 実装のボリュームにもよるが、レビューの手戻りのピンポンが恒常的に3往復を超えていると、プロセスに何らかの問題がある可能性が高い。

レビューは双方向の議論の場

上でも述べたが、レビューは双方向の対等な議論の場であるという意識が必要だと思う。

ありがちなのは、コードレビューは開発プロセスに含まれているが、レビューが承認を取る/渡すための作業になってしまい、レビュワー、レビュイー双方の学びの機会が失われてしまうパターン。 特にレビュワーとレビュイーにプログラマとしての実力差が大きい場合、レビュワーが一方的に指摘し、レビュイーは言われた点をひたすら修正するだけの作業になりやすい。

レビュイーは自分が書いたコードの意図や設計についての疑問、その他気になったことは何でも積極的にレビュワーに話すようにするとよいし、レビュワーの指摘が正しくないと感じたときは反論してよい。 レビュワーは、コードレビューを自分が担当しているコードや周辺技術についての学習の機会、また、コードの質とチーム全体のスキルを上げていくための重要な工程だと認識し、レビューしていて気になったことについては調べ、思ったことを積極的に指摘するようにする。

指摘は論理的、客観的、具体的にする

コードレビューにおいて指摘をする際は、「論理が明快で、誰もが納得でき、何をどうすればよいかが明確になっている説明」をするようにする。 言うのは非常に簡単だが、実行するのは簡単なことではないし、自分自身、完璧なアイデアがあるわけではない。

以下に自分がレビュワーとして試していることを記す。

  • 「この方がキレイ」など、本人の主観によるような表現は避け、論理的な説明になるようにする。必ず指摘の理由を説明する
  • レビュイーの背景知識を考慮し、言葉遣いに反映する
    • 特に、馴染みのない概念を説明する際には、具体的な例と、それをすることで何がいい/悪いのかを明示する
  • 指摘の際、持論を補強するための参考文献を貼る
  • 自分が少しでも理解できていない点があるのであれば、「指摘」ではなく「質問」の形式を取る。相手の意図を確かめた上で、自分の意見に反映する
  • 修正方法が説明しづらいと感じた場合、指摘コメントの中に具体的なコードスニペットを書く
  • 具体的な修正箇所を明示しておく
    • レビューツールで該当箇所へのURLが取得できるなら、コメント内でリンクを張る
    • 同一の指摘が複数箇所に当てはまる場合、「ここも同様です」を記載しておく
  • 修正の必要がないものについては、「修正不要です」と明記しておく

お互いに理解し、理解されるような説明を心がける。

テキストコミュニケーションに頼りすぎない

レビューツールやチャットツールなど、テキストでのコミュニケーションは、非同期的に行うことができ、かつ記録が残るので非常に便利であるが、一方で難しさもあると感じている。

特に、

  • 上で述べた通り、自分の指摘意図を完全に相手に伝えるのは概して難しい。特に非同期コミュニケーションの場合、相手が指摘を理解しているかどうかを確認する術がない。理解を誤っていた場合、その分はすべて手戻りになる可能性がある
  • 意見が対立した際に加熱しやすい

などの問題がある。

結果的に5分話したら解決した、というケースも結構あったので、「うーん、伝わってないなー」と感じたら、面倒がらず相手と直接話した方がよい。 ペアプログラミング、モブプログラミングのように、時間を取って一緒に画面を見ながら話すのも有効だと思う。

参考文献

開発現場に学ぶ、円滑なコードレビューに必要な8つの手法 ~手段から準備、実施時期まで徹底解説~ - エンジニアHub|Webエンジニアのキャリアを考える!

www.jpcert.or.jp

CKAD / CKAを受けてきた

例によって放置しまくってますが,あまり気にせず書きたいことを書くことにします.

タイトルの通り,今年の2月から9月にかけて,Certififed Kubernetes Application Developer(認定Kubernetesアプリケーション開発者.以下,「CKAD」),Certified Kubernetes Administrator(認定Kubernetes管理者.以下,「CKA」)を受けてきたので,何をやっていたかとちょっとした感想を残しておきます.

試験の概要

CKAD:

www.cncf.io

CKA:

www.cncf.io

どちらもCloud Native Computing Foundation(CNCF)が運営する,Kubernetesについてのスキルを認定する資格試験. 実際にKubernetesクラスタを操作することで問題に回答する,ハンズオン形式であることが特徴.

CKADとCKAの違い

注意点として,2020年9月からCKAの出題形式(試験範囲,試験時間,問題数など)が大きく変わっていることに注意.古い記事に当たるときは気をつけること.

training.linuxfoundation.org

CKAD CKA
試験の目的 Kubernetesを利用するアプリケーション開発者(クラスタ利用者)」としてのスキルを認定 Kubernetesクラスタ管理者」としてのスキルを認定
受験料 300USD*1 300USD
有効期限 受験日から3年間 受験日から3年間
試験時間 2時間 2時間
問題数 15-20問 15-20問
合格点 66% 66%
出題範囲
  • Core Concepts(13%)
  • Configuration(18%)
  • Multi-Container Pods(10%)
  • Observability(18%)
  • Pod Design(20%)
  • Services & Networking(13%)
  • State Persistence(8%)
  • Cluster Architecture, Installation & Configuration (25%)
  • Workloads & Scheduling(15%)
  • Services & Networking(20%)
  • Storage(10%)
  • Troubleshooting (30%)

最も大きな違いとして,CKADは,ほぼすべての問題が与えられたKubernetes上のリソースを閲覧/作成/変更することで解答できるものであるのに対し,CKAの試験範囲には,Kubernetesアーキテクチャについての理解と,クラスタのインストール,アップグレード,トラブルシューティングなどのオペレーションも含まれる点が挙げられる.

個人的な所感として,CKADは一問一問の実装量がそれなりに多く,ちゃんと対策していかないととうてい時間が足りない. 対して,CKAはカバーする範囲が広い分,ひとつひとつの問題は易しめ,といった感じ.

どちらから始めればよいか

「マネージドサービスのKubernetesを雰囲気で使ってきたけどそろそろ覚えたい」とか,「Kubernetesそんなに知らないけどこれからガッツリやっていきたい」のであれば,CKADから始めるのがよいかと思う. Kubernetesを利用する機会と運用する機会であれば,たぶん利用する機会の方が多いし,試験内容が「Kubernetesを利用してアプリを作る」ことにフォーカスしていることが理由だ.

逆に,「SREとしてKubernetesを運用できるようになりたい」とか,「Kubernetesアーキテクチャについても知りたい」のであれば,CKAから始めることをおすすめする. Kubernetesはもともと複雑な分散システムなので,この試験だけでKubernetesのすべてがわかるわけではないが,これからどういう勉強をしていけばいいかの指針にはなると思う.

試験範囲はわりとかぶっているので,ある程度理解に自信があるのであれば,両方一気に受けてしまうのもよいと思う.

受験記的ななにか

受験前のスペック

以前からクラスタ利用者としてはKubernetesを使っていたが,当時はせいぜい,DeploymentとServiceを作るのと,デバッグが必要になったときkubectl execでコンテナにログインしてログを読むぐらいだった. デバッグに必要なコマンド(kubectl get / describeとか)は知っていたが,それ以外は全然.

2019年秋

8月からアサインされたプロジェクトでKubernetesやIstioに触れる機会ができ,そろそろ体系的に理解したいなぁと思っていた.

社内プライベートクラウドKubernetesを管理している人たちとお酒を飲む機会があり,そこで,

「自分たちの部署に入るなら最低限CKAは取ってて欲しいよねー」

という話になった.

ちょうど今後何をしていくか悩んでいた時期だったこともあり,Kubernetesという技術自体に興味はあったので,「受けてみてもいいかなー」と思うようになった.

2020年1月

正月特有の謎のやる気のままCKADに申し込んだ.

たぶんこの時にはCKAも受けようと思っていたと思う. 最初がCKAではなかったのは,両方チラッと比較した上で,CKADの方がすでに知っていることが多かったし,とりあえずちゃんとManifestが書けるようになりたかったから,だったかな.

ちなみに申し込んだのは日本版のCKAD-JP. 試験としての違いはないが,日本語で試験監督が受けられる.

2020年2月

そのまま1ヶ月放置してしまったが,重い腰を上げて試験日を決めた(CKAD/CKAは申込日から1年間の間,試験日を自由に選べる.受験場所さえ自分で確保すればわりといつでも予約できるので,かえってモチベーションが上がらなかった).

とりあえずいろいろググってみて,

Kubernetes Slack

の#ckad-exam-prepチャンネルでおすすめされていたUdemyの講座*2をやった.

www.udemy.com

この講座は,

  • 動画の講義
  • 実際にクラスタを使った理解度テスト
  • 模擬試験

に分かれており,講義パートで覚えたことを理解度テストですぐに試せる構成になっているのが良い点. 講師はインド人のようだが,英語の訛りもさほどきつくなく,非常に聞き取りやすかった(英語字幕もある).

最終的に1週間ほどでこの講座を2週したが,試験対策としてはこれで十分だったと思っている.

その他には,

公式ドキュメントのブックマーク(後述)をしたり,

Kubernetes完全ガイド(当時は第1版)

で知識を確認したり,

github.com

www.youtube.com

このあたりで試験範囲とTipsを確認したり,

界隈では有名なKubernetes the hard way(自動化ツールに頼らずVM上にKubernetesクラスタを構築するハンズオン)をやってみたりしていた.*3

github.com

試験対策はトータルで2週間ちょいだったと思う.

CKAD当日

CKAD/CKAはリモートで行う試験のため,受験者の責任で「片付いていて」「人の入ってこない」部屋を選んで,受験会場とする必要がある. 試験のために自室を片付けるのは面倒だったので,休日の午前中を選んで会社の会議室を会場にした.

持ち物は,

  • パスポート(身分証明用)
  • MacBook ProWebカメラが付属しているのでわざわざ用意する必要がないのが便利)
  • キーボード(いつも使っているHHKB)
  • ディスプレイ(会社に転がっていたもの)

予定した時間の15分前ぐらいに試験用のポータルサイトが開くので,そこで待っていると監督者からチャットで指示が来る. パスポートをWebカメラで見せた後,不正防止のために会議室全体や机のまわりをWebカメラで写すように言われる(MacBookだとここがしんどい). その後試験に関しての注意事項が伝えられたあとで,試験開始となる.

普通に時間が足りなくて,3問ぐらい一瞥もできなかった問題があったと思う.

あと,最初は試験問題の言語を日本語にしていたが,翻訳の質があまり良くなく,出題意図がわからなかったので,すぐに英語に変更した. ここは改善してもらえると嬉しい…

翌日,合格通知があった. 得点は86%.

f:id:lethe2211:20201003195619p:plain

2020年4月

受かった直後はすぐにCKAも受けようと思っていたが,気づいたら1ヶ月経っていた(ちょうどこの頃仕事が忙しかったのもある).

申し込む.

2020年8月

長過ぎた五月病(笑)から復帰したので受験日を決める.

ちょうど前述のカリキュラム変更があったこともあり,変更後の9月に受けることを決めた.

2020年9月

すでにCKADを取っていたこともあり,試験対策としては,

前述のKubernetes完全ガイドを副読本にしながら,

上記のUdemyの講義のCKA版

www.udemy.com

を2周したのと,

kubernetes.io

の内容を読んだことぐらいしかない.

特に,クラスタをインストール/アップグレード/トラブルシューティングするタイプの問題については,アーキテクチャを念頭に置きながら,動きを理解するために手順含め何回も試して覚えた.

トータルで1週間程度.

CKA当日

こちらも休日に会社の会議室で受けた.

試験のポータルサイトがアップグレードされていてUXが良くなっていた. 試験全体の流れはCKADと同様.

試験慣れしていたこともあってか,終了20分前には完答できた.

こちらも,翌日に合格通知が来たが,得点は81%だった.

f:id:lethe2211:20201003195652p:plain

自分の感触的にはほぼ完答だと思っていたので,なんか問題文の誤読が多かったのかな… すこしもやもや.

Tips

以下,試験対策時,試験時に使えそうなTipsをまとめておく. 基本的にCKAD/CKAに共通して使えると思われる.

1. aliasを登録する

試験の性質上,コマンドのタイプ数はどうしても多くなるので,(指を壊さないためにも)できるだけ減らしたい. 毎回入力するコマンドについてはalias登録しておく癖をつけておくとよい.

$ alias k=kubectl

2. リソースのshort nameを覚えておく

Kubernetesの一部のリソースには,"short name"というものが定義されている. 例えば,PersistentVolumeClaimのresource nameは"persistentvolumeclaims",short nameは"pvc"であり,kubectlを使って取得する際には,

$ k get persistentvolumeclaims

$ k get pvc
  # こちらでも可

のようにできる. 必須ではないが,タイプ数が減らせるなら減らした方がよい.

ちなみに,

$ k api-resource

で,各リソースの有効なshort nameを確認できる.

3. YAMLのテンプレートを作る

Kubernetesの魅力の1つは,YAMLファイルのManifestを使うことでInfrastructure as Codeを実現できることだが,この試験に関して言うと,毎回エディタでYAMLファイルを書いてkubectl apply -fしていると率直に時間が足りない. うまい時短の方法を考える必要がある.

ひとつの方法として,YAMLのテンプレートを作るコマンドを使うことができる. おそらくほとんどの受験者がこの方法を使っているとみられ,この試験の必勝法のようになっている.

例えば,testという名前の,nginxコンテナを持つPodを作る際には,

$ k run test --image=nginx --restart=Never --dry-run=client -o yaml > 01-pod.yaml
$ cat 01-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: test
  name: test
spec:
  containers:
  - image: nginx
    name: test
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}
$ k apply -f 01-pod.yaml

のようにすればよい. コマンドを覚える必要はあるが,これでかなり時短できる.

コマンドだけで望みのManifestが作れない場合は,テンプレートを作った後にエディタで編集するようにするとよい.

この方法は,DeploymentやServiceなど,主要なリソースについてはだいたい使える. リソースによって使うコマンドが微妙に違うので,kubectl runkubectl createあたりのヘルプを確認するとよい.

4. kubectlの命令的コマンドを使う

3.に関連して,さらに時短したい場合は,kubectlの命令的(Imparative)コマンドを使うという手がある.

よく使っていたのは,

$ k edit svc mysvc
  # "mysvc"という名前のServiceを直接編集する

$ k set image deploy mydeploy nginx=nginx:1.9.1 --record
  # "mydeploy"という名前のDeploymentの,"nginx"という名前のコンテナイメージを"nginx:1.9.1"に差し替える.それを履歴に残す

$ k expose deploy mydeploy2 nginx --name=mysvc2 --type=ClusterIP --port=80 --target-port=8080
  # タイプClusterIPのService"mysvc2"を作成し,"mydeploy2"という名前のDeploymentが内包するPodのポート8080番を,"mysvc2"のポート番号80番で露出する

$ k label po mypod foo=bar
  # "mypod"という名前のPodにfoo=barというLabelを付与する

$ k taint no mynode foo=bar:NoSchedule
  # "mynode"という名前のNodeにfoo=bar:NoScheduleのTaintを付与する

こんな感じ(もっとあったかも). 全部覚えることは不要だが,覚えてちゃんと使えるようにしておけば有利にはなる.

5. kubernetes.ioをブックマークする

試験中には,試験用のWebアプリ以外にもう一つブラウザのタブを開くことができ,

と,これらの子ページの参照が可能.

特によく使うのは https://kubernetes.io/docs/Kubernetes公式ドキュメント)だが,毎回必要なページをサイト内検索しているとけっこう時間がかかる. ブラウザのブックマーク機能を使うことは許されているので,よく使いそうなものについては登録しておくとよい.

以下,自分が使ったものを雑に晒しておく.

kubectl Cheat Sheet | Kubernetes

Ingress | Kubernetes

Persistent Volumes | Kubernetes

Volumes | Kubernetes

Secrets | Kubernetes

Jobs | Kubernetes

CronJob | Kubernetes

Deployments | Kubernetes

ReplicaSet | Kubernetes

Configure a Pod to Use a ConfigMap | Kubernetes

Labels and Selectors | Kubernetes

Taints and Tolerations | Kubernetes

Assigning Pods to Nodes | Kubernetes

Managing Resources for Containers | Kubernetes

Configure a Security Context for a Pod or Container | Kubernetes

Network Policies | Kubernetes

Configure Liveness, Readiness and Startup Probes | Kubernetes

DaemonSet | Kubernetes

JSONPath Support | Kubernetes

Manage TLS Certificates in a Cluster | Kubernetes

Define Environment Variables for a Container | Kubernetes

Init Containers | Kubernetes

Installing kubeadm | Kubernetes

Upgrading kubeadm clusters | Kubernetes

Operating etcd clusters for Kubernetes | Kubernetes

Kubectl Reference Docs

リソースを作る際,kubectl create等で作れるリソースについてはコマンドで作り,PersistentVolume,PersistentVolumeClaim,Ingress,NetworkPolicy,CertificateSigningRequestなど,コマンドで作れないリソースについては,ドキュメントからのコピペで作れるようにしておくとよい.

6. kubectl explainを使う

Manifestを編集する必要がある問題が出された際に,どこにどのkey/valueを追加すべきかわからなくなることがよくある. こういう場合,ドキュメントを調べるより,kubectl explainを使う方が早く見つけられる場合が多い.

$ k explain po.spec.securityContext
  # Pod("po")のspec.securityContextについてのヘルプドキュメントを参照する

これにより,YAMLのどこにどういった型の値を入れれば望みの動作になるかがわかる.

これは普段の運用でも使えるコマンドだと思う.

7. エディタに慣れておく

kubectl edit等で起動するエディタはデフォルトでVimに設定されている. Vimに慣れておくか,

$ export KUBE_EDITOR="nano"

のようにしてエディタを変えておく.

8. 試験時は解いた問題のメモを取る

試験の各問題には傾斜された配点が付いており,難しい問題には多めの配点が付与されている.

試験中,試験用のWebアプリ内のエディタを使ってメモを取ることが許されているので,試験の最初に全問題の配点をメモし,配点の高い問題から取り組むようにした. 加えて,すでに解いた問題をメモにマークしておいた.

結果的に,無駄な時間を使わずに済んだと思う.

9. Chromeの設定をしておく

試験時に使えるブラウザは,所定のChrome拡張をインストールしたChromeChromiumのみ. 他のブラウザを使っている人は動きに慣れておくとよい.

あと,意外とやってしまうのが,誤クリックやショートカットキーでブラウザを閉じてしまうこと. できれば不要なショートカットキーは無効化しておくとよい.

試験の流れや試験環境については,

docs.linuxfoundation.org

docs.linuxfoundation.org

docs.linuxfoundation.org

このあたりに詳しい記載があるので一読しておくとよい.

まとめ

CKAD/CKAともに,Kubernetesの基礎を体系的に理解したい人には非常に良い試験だと思う. 特に,実際にkubectlや他のコマンドを使ってクラスタを操作する体験は,実際のプロジェクトでもかなり役立った.

反面,資格試験としてみると,2/3の得点が取れれば合格してしまうので,「技術力の証明」という観点からだとちょっとよくわからないなとは思った. 逆に考えれば,もっと軽い気持ちで受けてみてもけっこう受かるのかもしれない(CKAD/CKAともに,不合格でも追加費用無しで1回再受験ができる).

これにてKubernetesチュートリアル終了!と思っていたが,今年の11月にCertified Kubernetes Security Specialist(認定Kubernetesセキュリティスペシャリスト.CKS)という新しいKubernetesの試験ができるらしい.

training.linuxfoundation.org

気分が乗ればこっちも受けてみたい,ような気もしないでもない.

*1:"CKAD coupons"とかでググると値引きのためのクーポンコードが見つかる(かも)

*2:いつもは\24,000とけっこうするが,Udemyはちょいちょい95%セールをやってるので,そのタイミングを狙ってみるとよい

*3:Kubernetes the hard way,やってみればわかるが,ちゃんと理解するためにはインフラの知識がかなり必要になる.いちおう手順を最初から最後まで流しはしたが,正直あんまり理解できていなかったと思う

Web APIのログ設計についての私見

かなり放置してしまいましたが,久しぶりに気分が乗ったのでブログを書いてみています.

最近は仕事でSpring Frameworkを使って何度かWeb APIを開発しているのですが,その度に毎回同じようなことを悩んでいる気がするので,次に開発する自分に向けて,ちょこちょここちらにまとめておこうと思います.

結構私見が混ざりそうなのでこちらのブログで.

今回はログについてです.

注意事項

  • 対象は,DBや他のAPIと通信して結果を返すタイプの一般的なRESTful APIを想定しています.
  • 業務領域はちょっとカッチリ目のWeb系です.
  • 特定のWebフレームワークには依存しないように書いたつもりですが,見返してみると若干Spring Frameworkに寄ってるような気もします...

ログ設計の観点

そもそもですが,Web APIのログってどんなものがあればいいのでしょうか?

仕様面の観点としては,

  1. 障害発生時にできるだけ早く運用者に通知されるようになっているか?また,ログから障害の原因を追跡しやすいか?
  2. APIクライアントからの問い合わせなど,日常運用に際して使いやすいか?もう少し具体的に言うと,そもそもログの中から問い合わせの対象となるリクエストを見つけやすいか?また,ログの内容から問い合わせを解決するための手がかりをつかみやすいか?
  3. 業務量(QPS)やリクエストの実行時間を把握しやすいか?
  4. 開発時に起こった問題の原因を追いやすいか?(要するにデバッグログ)
  5. その他,開発者/運用者/ログ分析者が知っているとよい情報を適切なタイミングで表示しているか?

があるかなぁと思っています.

要するに,使いやすいログを設計しましょう,ということです.

また,性能面の観点としては,

  1. ログの出力がAPIの性能に大きく影響しないか?
  2. ログによってシステムの他の構成要素に影響がないか?

といったところも重要です.

何も考えずたくさん出してしまうとログ追跡も難しくなりますし,ログ出力そのものがAPIの負荷の原因になったり,ログを吐き出しすぎてディスク溢れを起こしてしまったりするケースもあります.

これらの観点を鑑みて,私は以下のようにログを設計しています.

ログ設計

ログ分割と各ログの役割

以下の4つのログをファイルの形でAPIサーバに配置しています. その後,fluentd等のログ集約ツールで適宜集約サーバに送信するようにしています.

(以下,ログレベルはSLF4Jのものを基準としています)

SLF4J

request.log

APIが受けたリクエストを記録するログ.

HTTPのリクエストパス,クエリ文字列,リクエストボディ,リクエストヘッダ(,クッキー),リクエスト元のIPアドレスを記録します.

特に,何らかのバグによって途中でレスポンスが返らないまま処理が終了してしまう場合も考えて,できるだけ早いタイミングでログを記録することが重要です.

ログレベルはINFO.

response.log

APIが返したレスポンスの内容と,リクエストの処理にかかった時間(経過時間)を記録するログ.

HTTPのステータスコード,レスポンスボディ,レスポンスヘッダ,経過時間を記録します. アクセス量の調査については,基本的にこのログを利用します.

加えて,

  • APIのID(呼ばれたAPIを一意に識別するID)
  • ユーザのID(あれば)
  • ユーザが認証済みかどうか(認証済みユーザにのみコンテンツを開示する場合など)
  • APIクライアントのID(あれば)

あたりを別に記録しておくと,ログ検索の際の利便性が上がると思います.

ログレベルはINFO.

application.log

APIの内部ロジックにて,開発/運用に必要な情報を記録するログ.

いわゆるアプリログ.

ログレベル 観点
TRACE 基本的に使用しない.開発時に本当に必要になったときのみ.
DEBUG デバッグの際に必要な情報を記録する.DEBUGレベルのログは本番環境で出力することを想定しない.「開発/デバッグ時にはいつも必要になるが本番には出したくないログ」のみを残し,開発中にだけ必要だったものなどは本番投入前に消しておくようにするとよい.例えば,DBに接続する際の接続情報や,APIロジック内での処理件数,経過時間など.
INFO 本番運用に際して有用な情報を記録する.INFOレベル以上のログはすべて,本番環境で出力することを想定する.不要な情報はできるだけ削ぎ落とし,本当に必要な情報のみを出力するようにする.特に,毎リクエストごとに出力されるログについては吟味し,request.log/response.logに入れられないかを検討する.例えば,バックグラウンド処理が起動/停止したことを示す情報や,APIの設定項目が更新された際の通知など.
WARN 「即時の対応が必要ではないが,運用者にすぐに通知されるべき警告」を記録する.自動電話の対象とはしないが,メールやSlackなどのメッセージングツールなどで運用者がすぐに把握できるようにはしておく.例えば,APIが想定しない入力が受け付けた場合や,(マッシュアップAPI等で)一部の外部APIの呼び出しがエラーを返した(がAPI全体には大きな影響がない)場合など.
ERROR 「運用の継続を阻害しており,即時の対応が必要なエラー」を記録する*1.エラーの詳細(例外のスタックトレースなど)がわかるようにしておく.アラートの対象であり,自動電話等で夜間帯でも即時対応できるようにしておく.例えば,APIの業務ロジックが想定しない例外を送出した場合や,メモリやディスクの枯渇によってAPI自体の状態が不安定になった場合など.

stdout.log

フレームワークが吐き出すログの内容をリダイレクトして記録するログ.

多くのWebフレームワークでは,フレームワーク側で実行した内容のログを標準出力に出力する設定になっていると思うので,それを記録しておきます.

ログレベルは出力したログに依存します.

気をつけるべきところ

ファイルフォーマット

プレーンテキストで記録してもいいのですが,一度集約サーバに送ってしまうと目視で確認する機会がほぼなくなってしまうので,プログラムで処理しやすい形式が良いかと思います. 個人的にはJSON形式がおすすめです.

ちなみに,ロギングにLogbackを利用しているのであれば,logstash-logback-encoderを利用することで,いい感じ(Logstash形式)にフォーマットしてくれるので,ELKで扱いやすいです.

GitHub - logstash/logstash-logback-encoder: Logback encoder which creates JSON for use with Logstash

JSONを使う場合でも,例えば開発時には目視しやすい方が楽かと思うので,必要に応じてフォーマットを変更しやすいようにしておくとよいかと.

リクエストID

ログごとにファイルを分割すると問題になるのが,どのログがどのリクエストに紐付いているのかわからなくなる点です. 例えば,request.logから同じリクエストに紐づくresponse.logを探すことができなくなります. このため,各ログには,リクエストごとに(ほぼ)*2一意になるIDを付与するようにしています.

ちなみに,ZipkinやJaegerなど,OpenTracing APIに対応するトレーサーを利用しているのであれば,IDとしてTrace IDの値を使うとよいかと思います.

時刻

ログの中でいちばん大事なのは,そのログを取った時刻の情報だと思います. もちろん要件次第だと思いますが,普通のAPIであれば,精度はmsecぐらいあれば十分かと思います.

時刻のフォーマットについては,ISO 8601の基本形式か拡張形式のどちらかを使っておけばいいと思います.

ISO 8601 - Wikipedia

私はよくuuuu-MM-dd'T'HH:mm:ssSSSXXX (E.g., 2018-10-27T22:17:35.213+09:00)のフォーマットを使っています.

ログローテーション

サーバのディスク容量には限りがあるので,ログがディスクを圧迫しないようにすることは重要です. 多くのロギングライブラリには,日別や日時別でログファイルを分割して古いファイルを圧縮,削除するログローテーション機能があるので,それを使うとよいと思います.

忘れがちですが,ログが溢れないかどうか,開発時に負荷試験で調べておくことも重要です.

ある程度信頼性を犠牲にしていいなら,ファイルを介さず直接TCP等で集約サーバに送ってしまう,という手もあるかと思います*3

リクエスト元のIPアドレス

リクエスト元のIPアドレスについては,(他にAPIクライアントを識別するための情報があれば必須ではないものの,)どのAPIクライアントのどのサーバがAPIを呼び出しているかを把握していると障害対応が楽になるため,できれば取得しておきたい情報です.

様々な取得の方法がありますが,APIクライアントと取り決められるなら取り決めた方法で,特にそういうものがないなら,

  1. X-Forwarded-Forヘッダが存在する際は,その値を,(カンマとスペース1つ)で分割した際の最初の要素(ロードバランサやリバースプロキシを経由するなどしてIPアドレスの付け替えがあっても対応できるので*4) 2.X-Forwarded-Forヘッダが存在しない場合は,REMOTE_ADDR(前段の呼び出し元IPアドレス

を取得するとよいかと思います.

X-Forwarded-For - Wikipedia

参考記事

qiita.com

7.1. ロギング — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.4.1.RELEASE documentation

www31.atwiki.jp

*1:RESTful APIであれば,APIクライアントにはステータスコード5XXを返すことになる.その際,レスポンスにエラーの詳細を記載するとセキュリティ面で問題があるため,問題が発生している旨のみ記述して残りはマスクしておくとよい.

*2:もし衝突してもちょっと探すのが面倒になるぐらいなので,あまり一意であることにこだわりすぎなくてもよいかと.

*3:最近はこちらの方が主流なんでしょうか...

*4:厳密に言うと,ヘッダの値は改ざん可能なので信頼できる値ではないけれど.

昨年やったことと今年やりたいこと 2018

↓2017年版 lethe2211.hatenablog.com

去年やったこと

Rustハッカソン

あんまりここで書いたことはなかったけれど,就職してから1年に1度,GWあたりに有志で温泉ハッカソンをしている.
お題を決めて1晩でやる形式で,以前やったのはGoLangとかSwiftとか.

今年は湯河原のおんやど恵で行い,お題はRustだった.

www.onyadomegumi.co.jp

コンパイルが通せる程度のプログラマにはなりたい,という気持ちだった…

「達人プログラマー」でも言ってたし,1年に1度ぐらい新しい言語に触れるのもいいと思うけれど,役に立つモノが作れた記憶が無いので,今年もあるなら頑張りたい.

新装版 達人プログラマー 職人から名匠への道

新装版 達人プログラマー 職人から名匠への道

富士登山

とうとう登った.

登頂には吉田ルートを使った. もともと登山道がそこまで広くないのと,シーズンだったためか,登山道が非常に混雑していて,もはやアトラクションの待機列に並んでいるようだった.

8合目の山小屋で1泊した. 事前に「山小屋は寝るためだけの場所」と聞いていたので,あまり期待していなかったが,ご飯もちゃんと出たし,特に困ることはなかった. 夜中に起きて出発したが,星がめちゃくちゃキレイだった*1

9合目を過ぎたあたりから急に列の人が減って(高山病で脱落した?),日の出の時間が近づいてきたこともあり,誘導のおじさん達に急かされながら最後の数百メートルを登った. ココが一番キツかった.

山頂に着いたが,残念ながら天気が大荒れで,あまり日の出は見えなかった. 最初は山頂付近を1周することも考えていたが,キツそうなのですぐに下山した.

次登るのであれば,吉田ルート以外にしたい*2

上海&台湾旅行

11月,12月と,立て続けに上海,台湾に行った.

上海は,厳密に言うと旅行ではなく出張だったが,そんなに仕事をした記憶が無いので実質旅行だったと思う. 摩天楼がキレイだった. あと,上海ガニを初めて食べた.

台湾には家族旅行で行った. 台北市内や,アニメ映画の舞台になった九份など,北部を中心に回った. 家族にここ数年海外旅行に行った人が自分しかおらず,フォローに苦労した. 今度は一人で行きたい.

とりあえずNISA枠現物取引から始めてみた. 一応今のところプラスだけど,そもそも元金が多くないからそんなにでもない.

今年はもう少しつぎ込んで,少しづつ結果を積み重ねていきたい.

最近は仮想通貨(取引 & マイニング)が流行ってるけどどうなんだろう.

Data Science Online Course

gci.t.u-tokyo.ac.jp

去年の10月から今年の3月までの期間,社会人向けデータサイエンスのオンライン講義を受けることになっている.

最後まで終わった後にヒマができたら,こちらについても記事を書きたいと思ってはいる.

統計検定(2級)

lethe2211.hatenablog.com

機会があれば,準1級にも挑戦してみたい.

システムアーキテクト試験

lethe2211.hatenablog.com

今年やりたいこと

なんか全体的にふわっとしているので,もう少し具体的な目標にしたい気もする.

趣味プロダクト開発

学生時代以来,完全な趣味で作ったプロダクトがないので,転職面接用のデモ作品を兼ねて何か1つ作りたい.

あまりまとまった時間が取れなかったことと,作りたいものが明確にイメージできなかったことから,見せられるプロダクトを開発する機会がなかなかなかった.

今だとスマホアプリとかになるんだろうか,ノウハウないけれど…

技術のアウトプット

そろそろ,ここ数年学んだ技術をどこかにアウトプットしていきたいという気持ちがある.

できれば一度どこかの勉強会に登壇してプレゼンしたいけれど,そこまででなくても,ブログで流すようにしたい.

アウトプットを目標として学んでいけば,場当たり的な知識にとどまらず,もう少し体系的に勉強する癖がつくと思うので頑張りたい.

料理

「やっぱり,料理できる人はカッコイイよなー」ということを最近ひしひしと感じている.

今までも自炊はしてきたけど,そんなに頻度も多くないし,そもそも料理と呼べるか怪しいものが多かった*3

ハウツー本でも買って,週末のヒマな時間にでも少しずつ始めてみたい,煮物とかちょっと時間のかかりそうなやつも含めて.

お絵かき

最近iPad ProとApple Pencilを買ったので,ペンタブ代わりにしてちょっとお絵描きしてみたい.

年末にちょっとやってみて思ったが,最低限の絵が書けることはそれなりに必要だし,絵を書く過程で対象のモノの観察をすることは結構アタマを使うので面白いと思う.

自分に画才がないのは重々承知しているし,めちゃくちゃ上手くなろうと思っているわけでもないが,少しずつやっていきたいと思う.

Apple iPad Pro Appleペンシル/MK0C2J/A

Apple iPad Pro Appleペンシル/MK0C2J/A

抱負

「地に足を着けて実行する」を今年の目標にしたい.

自分ではいろいろと基本的なことを学んではきたつもりだが,まだまだ分野の一流の人達には到底及んでいない. スペシャリストになる人の特徴として,同じ分野を飽きずに突き詰められる能力というのはあると思う.

その時その時にやりたいことをやるだけでなく,少しまとまった時間(1ヶ月とか)を作って,大きな作業を着実に進める癖をつけたい. また,個々の作業をがむしゃらにやることも必要ではあるが,時にはまわりを見渡して方向性を確認するようにしたい.


去年感じましたが,こういうところで宣言しておくとモチベーション保ちやすいのでいいですね.

*1:写真に撮っておけばよかった

*2:山は他にも塔ノ岳とか赤城山とか登った

*3:「冷蔵庫にあるものを炒めただけ」を料理と呼べるか?

システムアーキテクト試験を受けてきた

前に情報処理技術者試験はもう飽きたとか言ってたような気もしますが,懲りずに受けてきましたので,毎度のように何やってたか簡単にまとめておきます.

概要

システムアーキテクト試験(SA試験)は,主にシステム開発の上流工程を担当するエンジニアに向けた試験で,簡単に言えば,要件定義,外部仕様設計あたりを主な出題範囲としている. 対象となるシステムは,情報システムと組込みシステムの2つがあり,その中から自分が得意な分野の設問を選択して解答できる作りになっている.

www.jitec.ipa.go.jp

出題傾向

試験は,

  • 午前1(全問必答.多肢選択式.50分.問題は全高度試験で共通)
  • 午前2(全問必答.多肢選択式.40分)
  • 午後1(4問中2問選択.記述式.90分)
  • 午後2(3問中1問選択.論述式.120分)

の4つ.
スペシャリスト試験とは違い,午後2試験が論述問題になっている.

出題形式はわりと明確で,かつ年度によるブレは大きくない印象.

午前2で基本的な知識を確認.

午後1では,与えられた現状仕様/システムを理解し,そこからシステムを設計したり改修したりする問題が多く出題される. 4問中3問が情報システム,残りの1問が組込みシステムについての設問.

午後2では,リード文を読んだ上で,自身の経験をもとにした論文を書く. 試験の最初に,関わった業務やシステムについての基本情報の欄を埋めてから,各設問について論述していく. 「設問ア」,「設問イ」,「設問ウ」の3つの設問があり,それぞれ,「800字以内」,「800字以上1,600字以内」,「600字以上1,200字以内」という字数制限がある. 3問中2問が情報システム,残りの1問が組込みシステムについての設問.

受験動機

正直に言って,スペシャリストを3つ取ってしまって情処に対するモチベーションはかなり下がっていたし,やろうと思えばいくらでもネタのでっち上げができそうな論述試験なんてやる意味あるのかという疑問は拭えなかったが,

  • 業務で上位設計者としての仕事を任されることが増え,一般的なアーキテクトができる(しなければならない)ことが知りたくなった
  • モチベーションが高かった頃になんとなく買った参考書が残っていた
  • 午前1免除がもったいなかった
  • 「エンジニアにも説明力が必要」というこちらのブログ記事の内容に大きく頷くことができた

dimeiza.hatenablog.com

*1

これらから,受験を決めた.

やったこと

2017年8月中旬

上記のように思い悩みながらも受験申し込み.

8月下旬〜9月上旬

少し時間が取れたので,午後試験を中心に勉強を始めた. 自身の業務領域と問題数を考えて,組込みシステムの問題を取ることは100%ないと思ったので,最初の段階で捨て,情報システムの問題にヤマを張ることにした.

使ったのは,先ほど挙げた参考書.

情報処理教科書 システムアーキテクト 2017年版

情報処理教科書 システムアーキテクト 2017年版

午後1については,1問やってみて多少の手応えを感じられたので,労力少なめで進めることに決めた. やはりスペシャリスト試験に比べると技術まわりの出題の難易度は低く,業務でもアーキテクトとして理不尽&&意味不明な仕様と闘っていることもあり,問題文の勘違いさえしなければ,さほど厳しくはならないだろうと感じた. 解く際には,問題文の重要そうな点には線を引くようにし,また,データの流れが複雑な場合についてはできるだけ図を書くように工夫した.

やはりキモになってくるのは午後2で,一度何も考えず問題を解いてみて,これは慣れが必要だと感じた.

まず,とにかく手書きがダルい. 普段はほとんどシャーペンを持たないので,腱鞘炎になりそうで文字を書くのがただただつらかった. また,手書きはPC入力と違ってBackSpaceやコピペが使えず,かつ,解答用紙はマス目のついたタイプのものなので,文章の構成ミスや誤字脱字に後から気づくと修正が事実上不可能になってしまうのもいただけない.

加えて,時間は120分と,字数を考慮すると非常に短く*2,時間配分もキッチリやらないと到底書き切ることができない.

結果,1問目は途中で解くのを諦め,おとなしく上述の参考書やWeb記事にしたがうことにした.

どうやら,この試験にはある種の必勝法があるらしく,合格体験記を見ると,大方の人がそれを実践していた.

0.設問アについては,毎年ほぼ変わらず「関わった業務とシステムについての概要」を問われるので,表現も含めて暗記しておく(ついでにある程度字数調整できるようにしておく).

1.試験開始後の30~35分程度で,その後の設問で書きたいことを,(字数制限を考慮した上で)箇条書きにしてまとめる.ここで,論旨展開(設問アで設問イへの伏線を張っておく,設問ウで書きたいことは設問イでは書かない,など)についても考慮しておく.

2.残りの時間でひたすら書く.ここで,箇条書きの部分はそのままそれをタイトルとして書き,その後に詳細を書いていくスタイルで進める(ここで筆が止まる場合は,1での論旨のまとめ方が不十分ということなので反省する).

といっても,やはり最初から手書きは厳しいので,まずはPCのテキストエディタに要点をまとめることとした.

現在はWeb企業で業務APIプラットフォームの内製開発,運用をしているが,現状運用している業務とシステムについて,その概要を書くというのは,自分にとって意外と新鮮な体験だった. ふわっとした理解だったものも,論理的な文章にしようと思うと,間を埋める努力をしなければならず,その度に運用ドキュメントを当たるなどした. これは非常にいい経験になったと思う.

ある程度まとまってきた段階で,同じ問題を時間を測って解いてみて,「この方法で行けそう」と確信した.

字数が数えられるタイプの紙が家になかったので,罫線の上にドットがついているタイプ*3のレポート用紙を買った. ホントは原稿用紙みたいなマス目になってるやつが欲しかったけど,これはこれで結構便利だった↓

9月中旬〜下旬

少し仕事が忙しくなってきたが,ペースを落としつつ午後2の対策を続けた.

難しいのは,30分程度で十分な字数を確保できる内容を考えるところと,箇条書きの部分をちゃんとスジの通った文章にするところ.

前者については,結局はプロジェクトを経験した数(≒引き出しの数)による部分が大きく,特に苦しんだが,それでも内容のでっち上げはできるだけ避けたかったので,関わったプロジェクトの内容を逐一思い出して書くようにした. それでもダメな部分については,実際には運用しかしていないシステムを開発した体で書くようにするなど,できるだけ具体性を損なわないようにした*4

後者については,ある程度問題演習をすることで慣れてきたと思う.

また,問題で問われていることを勘違いして2時間をムダにすることもあったので,問題文だけでなく,リード文もよく読むように心がけた. 特に,リード文における例示については,注意を払って読むようにした*5

加えて,問題を解いた後に,参考書の合格論文をざっと読んでみて,解答の方向性にズレがないかを常に確かめるようにした.

10月上旬〜中旬

このあたりから少しだけ午前対策を始めた. といっても,2年分過去問をやって,イケそうなのですぐにやめた.

基本的には午後試験の対策をしていた. 結局試験前までに解いたのは,午後1が3年分(6問),午後2が5年分(5問). これは今までの情処の試験対策の中では最も少ない.

当日

実際に解いた問題↓

IPA 独立行政法人 情報処理推進機構:問題冊子・配点割合・解答例・採点講評(2017、平成29年)

午前1

免除.

午前2

いつもの起床ゲー+暗記ゲー. 受けた記憶すら曖昧.

午後1

「ああマイナンバー出たかー.これ問題難しくしたらいろんなとこから怒られるんだろうなー」と思って問1,雰囲気で問3を選択.

問1はデータの流れが追えれば比較的簡単だったが,問3の人事関連の業務フローを理解するのは少し難しかった. パッケージソフトウェアのFit & Gap分析の問題はちょいちょい出るらしいので,来年以降受けるなら調べておいてもいいかもしれない.

午後2

「非機能要件」の定義に少し自信がなくなって問2を取ろうかと思ったが,問2の方がふわっとしてて出題意図を踏み外しそうなので問1を選択.

自分が問題用紙にメモした内容(をちょっとキレイにして出しちゃうと身バレしそうなところを改変したもの)を晒しておく.

設問ア:
アー1:要件定義に関わった対象業務の概要と情報システムの概要
 * 自身はWeb企業のシステムアーキテクト
 * XXXサービスに従事
 * XXXサービスとはうんぬん(を書く)
アー2:情報システムの概要
 * 自身が担当したのはYYYバッチシステム
 * YYYバッチシステムは,XXXサービスを運用するためのYYYシステムの一部
 * 自社他部署及び他社がクライアント
 * クライアントから連携されるデータ(1000万レコード/日程度)をもとに,システムのDBを更新
 * コスト面から,運用できるサーバ数には限りあり

設問イ:
イー1:検討した非機能要件
 * 効率性と運用性のトレードオフについて検討
イー2:検討した際の視点とプロセス
 (1)業務観点での検討における視点とそのプロセス
  * 利用部署へのヒアリングを行う
  * 業務要件:データの更新は更新データの連携日の翌日午前中まで(できればもっと早く)に行いたい.データ量は1000万レコード+バッファ
 (2)情報システム観点での検討における視点とそのプロセス
  * 現状のサーバを調査する
  * 簡単なストレステストを行う
  * バッチプログラムはシングルスレッドを想定(簡単な実装であり,運用性が高いから)
イー3:検討した結果
 * バッファ込み3000万レコードでストレステストしてみた -> 9hかかる!
 * 業務要件を考えると時間がかかりすぎ
 * バッチのマルチスレッド化を検討

設問ウ:
 * 意思決定者=YYYシステムの開発責任者
 * 現状のサーバ構成は変えられないことを意思決定者に確認
 * シングルスレッド版,マルチスレッド版のそれぞれについて,バッチのプロトタイプを作り,3000万レコードを読み込ませた時の速度を見てもらう
 * マルチスレッド化により運用性が下がることを示す

基本的に,上記のメモベースで箇条書きを行い,それぞれに内容を補足する形で実際の論文を書いた.

少しでも大量データを触る機会のあるエンジニアなら,最初はシンプルなコードでとりあえずプログラムを書いてみて,負荷試験でダメなら仕方なく手を入れる*6という開発経験をされた方は多いのではないだろうか. わりと自身の経験に基づいたものになったかなとは感じる.

設問イまではわりとうまく書けたが,設問ウでネタ切れを起こしてしまい,字数を必死に稼いで600字超ギリギリまで持っていった. やはり,文章構成を考える段階で字数制限より少し多めにネタを考えておいた方がいいと思う.

結果

f:id:lethe2211:20171223015720p:plain 合格でした.

感想

全体的に暗記する項目が少なかったので,比較的コンパクトな勉強で合格点まで持っていくことができた. どちらかと言うと,特定の分野について勉強したことを問うというよりは,地頭の良さと日頃の文章力を見る試験という印象.

個人的には,ただただシステムの設計について記述するだけでなく,数多くいるステークホルダへそれをどのように説明するかという観点が問題に組み込まれているのが面白いと思った. 実際に上位設計者としてやっていく以上,ただシステムを作ればいいのではなく,システムの対象領域となる業務を理解し,かつ,作ったものの仕様や設計意図を(相手の背景知識に合わせて)説明できるようになることが重要なのだと感じた*7

でもまあやっぱり,「こんな妄想たっぷりの論述で本当にシステムアーキテクトととしての能力がわかるんだろうか」という疑問は最後まで解決されなかったのと,イマドキの試験で数千字の文章を手書きで書かせるのはどうなのという感じはした.


たまには資格試験以外の技術記事も書きたいのでちょっとがんばります…

*1:こちらの記事,試験対策記事としても非常にお世話になった.感謝.

*2:2,500字って大学の期末レポートより少し短いぐらいだし.

*3:伝われ.

*4:たぶんシステムへの理解を深めることも試験の重要な目的だと思うので許して.

*5:基本的に例示に沿ってそのまま書けば得点的には問題ない気もするが,あまりにコピペをしてしまうとそれはそれでなんかダメな気がする.試験講評でも毎年怒られてるし.

*6:だいたい後からいじった部分がぐちゃぐちゃになって保守できない原因になってくるのだが.

*7:流暢な文章にできることと,同じ内容を口頭で伝えられることは(特にコミュ障にとっては)一致しないが,相手に説明すべき観点が理解できるようになるだけでも,まあ一歩前進という感じはする.

情報処理安全確保支援士の集合研修を受けてきた

久しぶりの更新です.

情報処理安全確保支援士の資格を取って初めての集合研修を受講してきたので,ちょこっとメモしておきます.

lethe2211.hatenablog.com

情報処理安全確保支援士では,資格維持に年1回のオンライン講習と3年に1回の集合講習が義務付けられています.
今回は,この中で集合講習を受講してきました.

時間は9:45〜17:30の約8時間(休憩込み)で,内容としては,大きく,

  • 理解度確認テスト
  • 30分程度の講義(座学).
  • 40分程度,5人1組のグループに分かれてのグループワークx3.

でした.

詳細については(口止めがあるので)書きませんが,講義では,情報セキュリティインシデント対応と,支援士としての倫理について総論を学び,グループワークでは,それを実践する形で,グループ内で議論を行い発表しました.

特徴的だったのは,講習の大部分がグループワークになっている点.

架空の企業で起こったインシデントについて対応や予防策を検討する,支援士としての倫理について考えるという2つのテーマがあったのですが,どちらも設問の前提がわざと曖昧にしてあり,教科書的な回答だけでなく,メンバーの知識と経験に基づいた意見を求められるので,なかなかアタマを使いました.
また,倫理の問題では,顧客からの職業倫理上問題のある依頼をどう扱うかなど,実際に直面するであろう問題に近いものもありました.

支援士試験自体の合格率と資格の維持費からか,グループの方は皆見た目30代後半〜40代以上で,かつ企業内でもソコソコの役職の方が多かったです.*1
議論をしていても,技術まわりの話よりはマネジメントや社内規定の話になることが多く,自分との視点の違いを感じました.
これは受講してみてよかった点だと思う反面,全体を通してもう少しセキュリティ関連技術の話ができると楽しかったかな,とも感じました.

この記事を書くことで受講料8万円(自腹)を供養…できるといいな.

*1:正直僕は完全に浮いてました

統計検定2級を受けてきた

情報処理技術者試験に若干飽きてきたので(?)受けてきました.

www.toukei-kentei.jp

概要

「大学基礎科目レベルの統計学の知識の習得度と活用のための理解度を問うために実施される検定」だそうです.

試験範囲は,多くの大学で1,2年次に教養科目として開講されている「確率論」と「統計学」の内容におおかた沿ったものとなっています*1

また,2級と3級については,PBT(マークセンス方式の筆答試験.6月と11月の年2回開催)に加え,CBT(コンピュータに解答を入力するタイプの試験.随時開催)も用意されているので,わりと気軽な気持ちで受けられるかと思います.

出題傾向

上で述べたように,大学教養レベルの統計が範囲です.

特に,

  • データの分析,可視化(度数分布表,ヒストグラム,箱ひげ図等)
  • 高校レベルの確率計算,ベイズの定理
  • 標本調査の方法,実験計画
  • 確率変数,確率分布の性質と平均,分散等の計算
  • 統計的推定(点推定,区間推定)
  • 統計的検定(z検定,t検定,x2検定等)
  • 回帰分析

あたりは押さえておくと良いでしょう.

経緯

そもそも大学の専門はコンピュータサイエンスだったので,統計学は教養でちらっと学んだ程度でした*2

数ヶ月ほど前に統計学に興味を持ち始めたのですが,Web上に落ちている記事をいくつか読んだだけでは体系的に知識を得るのが難しいと考えていたところ,ある勉強会で統計検定を知りました.

巷には,「なんとなく」統計・機械学習の技術が使えるようになるライブラリやフレームワーク等がありますが,やはり理論的な背景もある程度知っておきたいと思い,そのためにはとりあえず大学教養レベルからちゃんと学び直す必要があると感じていたので,いい機会だと思い,受験を決めました.

やったこと

2017年3月

この時点では,2-3週間ほど勉強した上でCBT試験を受けようと思っていた.

とりあえずテキストと過去問を買う.

改訂版 日本統計学会公式認定 統計検定2級対応 統計学基礎

改訂版 日本統計学会公式認定 統計検定2級対応 統計学基礎

以前の記事でも言及したが,このテキスト,解説に不十分な箇所が多く,あまり評判がよくない.
結果的にこのテキストはあまり使わず,試験日前日に試験範囲の勉強に漏れがないかチェックするためだけに使った.

日本統計学会公式認定 統計検定 2級 公式問題集[2014〜2016年]

日本統計学会公式認定 統計検定 2級 公式問題集[2014〜2016年]

日本統計学会公式認定 統計検定 2級 公式問題集[2013〜2015年]

日本統計学会公式認定 統計検定 2級 公式問題集[2013〜2015年]

逆に,こちらの過去問*3は,実際の検定の問題とその簡単な解説を並べている,非常にシンプルで読みやすい本だった.
これを数回回して,理解が十分でない点を他の参考書等で補うのが基本的な学習の流れになるかと思う.
また,一読すれば,ここ数年は回を経るごとに問題が難化する傾向にあったことと,推定・検定からの出題が減って,確率分布についての出題が増えていることも見て取れるか.

2017年4月

若干仕事が忙しくなり,ズルズルと受験を引き伸ばしていた.
こういう時,「いつでも受けられる」試験は申し込みボタンを押すまでのモチベーションが保てないので厳しい.

そうこうしている間にいつの間にか6月のPBT試験の受付が始まっていたので,えいやで申し込む.

2017年5月

GWで英気を養ったので,勉強を再開.

せっかくやるからには,ただ問題を解くだけでなく数学的な背景も知りたいと思ったので,統計入門の名著である「赤本」を買った.

統計学入門 (基礎統計学?)

統計学入門 (基礎統計学?)

評判通り,基礎教養としての統計学として学ぶべきことが過不足なく体系的にまとまっており,昔受けた講義の内容を思い出すのに役立った.
決してカンタンに読み進められる本ではないが,出て来る数式についてはわりと補足があり,必要に応じて例示によって理解を促す構成になっているので,自分のような数式が苦手な人間にもありがたい.
統計検定2級と範囲がかぶっているので,これを一読した上で過去問を解いていくだけで合格点を取れる実力は十分につくかと思う.
一点いちゃもんをつけると,回帰分析についての内容が十分でなく,分散分析は内容に含まれていないので,そこはWebページや他の本で補う必要がある.

また,計算問題を解くに当たって,家に眠っていた

スバラシク実力がつくと評判の確率統計キャンパス・ゼミ (大学数学「キャンパス・ゼミ」シリーズ)

スバラシク実力がつくと評判の確率統計キャンパス・ゼミ (大学数学「キャンパス・ゼミ」シリーズ)

の演習問題を使った.

検定の問題では,計算問題がそれなりに出るが,これが結構面倒で,どうしても計算ミスが多くなってしまう.
特に,分散・共分散の計算では,計算式をうまくいじくることで計算過程を大幅に省略できることがあるので,実際に練習問題にあたって式を追っておくことは重要だと思った.

Webページとしては,

bellcurve.jp

が,統計検定2級の範囲を網羅しており,解説も具体例をベースとした非常に理解しやすいものであり,非常にお世話になった.
自分は気づくのが遅かったので実行しなかったが,このページベースで学習を進めるのは大いにアリだと思う.

2017年6月

試験場には「四則演算(+-×÷)や百分率(%)、平方根(√)の計算ができる一般電卓又は事務用電卓」の持ち込みが認められているが,電卓が家にないことに気づいたので買った.

電卓使うのすごく難しかったのでたぶん自分はデジタルネイティブ世代なんだと思う…

試験当日

某大学での受験だったが,入口の門がことごとく閉まっており,入れる入口を探し回ったことからわりとギリギリの到着になってしまった.

全体的には,2016年の試験より簡単に感じられた.
重箱の隅をつつくような出題が減り,基本的な知識を問う問題に回帰しているような気がした.

一問,例年の傾向から外れて分散分析の範囲から問題が出題されたが,ちょうど直前に対策をしていたので解くことができた.ラッキー.

結果

数日後に正解が発表されるので,自己採点してみたところ32/35.
6割が合格ラインとされているので,マークミス等なければ合格はしたかな,と思っていました.

およそ3週間後にWebでの合格発表がありました.
統計検定では,最優秀成績者に評価S,優秀成績者に評価Aの評価と表彰状が与えられますが*4,評価Sをいただくことができました.

f:id:lethe2211:20170722000604j:plain


こんな感じでした.

全体的な感想として,表面的に教科書を読んでいるだけでは理解できない,しかしよく本質をついた問題が出題されていたように思います.
特に,データから特徴を見抜いたり,世の中によくある試行からその確率分布を導く問題などは,数学の世界と実世界を結びつける,良い問題だと感じました.

受験後,ちらっと準1級の問題を見てみましたが,やはり結構難しそうなので,2級から受けてよかったと思っています*5

少しづつ統計の知識も身につけていきたいです.

*1:なので,学生であれば,講義内容の予習,復習がてら受けてみる,という使い方もできるかもしれない.逆に,この辺の講義を受けたことがない人がそのまま2級を受けるのは少し厳しいので,自習するか,3級からはじめることを勧める.

*2:学士・修士の研究でちょっとだけデータまとめをした程度.

*3:過去問については,http://www.toukei-kentei.jp/past/#pastpaperに過去1回分がUPされる.世の中にはInternet Archiveというモノがあっておっと誰か来t(ry

*4:こういうの,他の検定試験ではあまり見かけない気がする.

*5:準1級から上はPBTのみで,かつ年1回であることに注意.