~fvknk/bk

技術関連の備忘録

応用情報技術者試験を受験してきた

はじめに

2024/04/21(日)に開催された応用情報技術者の春期試験を受験しました。 合否については現時点では不明ですが、自分が当日までやったことを備忘として残します。

※注意※

比較的準備を余裕を持って行っています。短期間で準備する方法を紹介する記事ではありませんのでご注意ください。

受験時点のステータス

受験時点で以下のステータスでした。

  • 大学:情報系
  • エンジニア4年目
  • 基本情報技術者試験合格済み

スケジュール

時期 内容
試験6か月前 午前試験対策開始
試験3か前 参考書購入
試験3か月前 午後試験対策開始
試験1週間前 試験会場までの経路確認
試験1日前 試験日の荷物準備
試験当日 受験

スケジュール詳細

午前試験対策開始(試験6か月前)

この時点では2024(令和6)年度版の参考書は発売されていませんでした。 そのため、お馴染みの過去問道場で午前問題の過去問をひたすら解きながら現状の確認と、間違えた問題については関連事項を調べながら理解を進めていました。

www.ap-siken.com

学習時間は通勤で30分強電車に乗るので、その時間をメインに使って1日1時間程度実施していました。

また解答の際には、根拠を持って答えられないものはわざと誤答を選ぶ、解答しないなどして、偶然の正答で正答率が上がることはないようにしていました。

試験直前の成績レポートは以下のようになっていました。

参考書購入(試験3か月前)

12月ごろになると次年度向けの参考書が出てくるようです。

1月半ばごろに参考書の出版状況を確認したところ、目をつけていた以下が発売済みだったため購入しました。

gihyo.jp

最初から最後まで読むことは一度もなく、あくまで過去問を解いている際に解説を読んでもピンと来ない内容を調べる、辞書の1つとして使いました。

また、テキストにも過去問サービスが提供されていましたが、過去問道場と比較して使用感が自分に合わないと感じたため利用はしませんでした。

午後試験対策開始(試験3か月前)

大まかには、午後問題も午前問題と同様、過去問道場で午後問題の過去問をひたすら解いていました。

www.ap-siken.com

ただ、選択式問題があるためどの問題を選択するか目処をつけるため、直近2回の試験で、すべての分野について解答し、解答する分野の候補(=学習範囲)を以下のように決めていきました。

  • プログラミング
  • システムアーキテクチャ
  • ネットワーク
  • データベース
  • 組み込みシステム開発
  • 情報システム開発
  • システム監査

少し多めですが、比較的準備期間に余裕があったため多めにしています。

こちらも大問を解くのは通勤電車の片道30分程度で大問を1~2問程度 + 時間があれば別途解答する感じで進めていました。

問題への取り組みはスマホにインストールしているメモアプリ(Goodnotes)にPDFを貼り付ける機能があるため、そちらに書き込みながら解答していました。

www.goodnotes.com

解答履歴はNotionに記録し、間違えた問題、理解が浅い問題は別途調べてまとめていました。

以下の記事もその流れで書いたものです。

fvknk97034.hatenablog.com

fvknk97034.hatenablog.com

この段階の調べ物の際には、先に紹介したテキスト以外に以前に購入していた以下もかなり使っていました。

www.ohmsha.co.jp

www.c-r.com

試験直前の成績レポートは以下のようになっていました(問題を開いたまま放置していることもあったため、右上の時間はあまり参考にはなっていないです)。 受けなかったけどプロジェクトマネジメントとサービスマネジメントがひどい。

試験会場までの経路確認(試験1週間前)

受験票がすでに届いていて受験会場がわかっているため、試しに受験会場入口まで行ってみました。

あらかじめ到着までにかかる時間と経路を実際に見ておくことで、試験日に不安を抱きながら向かう、ということがないようにできたかと思います。

試験日の荷物準備(試験1日前)

忘れ物がないように以下の持ち物を当日持っていくカバンに入れておきました。

  • 受験票裏の机上に置けるもの(自分が持って行ったもののみ)
    • 受験票(写真添付済み)
    • シャーペン2本(HB、芯を3本ずつ予備で入れておく)
    • 鉛筆2本
    • 鉛筆削り
    • 消しゴム3個(落としたときにテンパらないように多めに)
    • ハンカチ
    • ポケットティッシュ
    • 時計(デジタル式・スマートウォッチではない)
  • シャーペンの芯
  • (念の為)受験票訂正用の筆記用具
    • 予備の写真
    • 黒・赤ペン
    • スティックのり
  • 昼食
    • ランチパック
    • チョコチップメロンパン
    • パックの野菜ジュース
  • 飲み物
    • 水500ml

机上に置けるものは2024年度春季試験で許可されているもののため、他の回では置けるものが変わっている可能性があります。

午前はマーク式なので鉛筆と鉛筆削りも持っておきました。 定規も許可されていたのですが、流石に使わないと思って定規は持っていきませんでした。

受験(試験当日)

出発まで

出発の2時間半前くらいに起床し、朝ごはんを食べてパラパラとテキストを見ながら出発時間まで時間を潰し、出発時にはテキストをカバンに入れていきました。

結構早く起きているのは前日にお腹を壊していたのも1つです(家を出る少し前までは調子が微妙に怪しかったですが、幸い受験中は問題ありませんでした)。

受験会場到着

遅くとも集合時間20分前くらい前には受験会場前に到着するくらいで出発して、到着しました。

受験会場が大学だったのですが、構内に入ってからは途中途中に試験関係の職員と思われる人が随所にいることと、同じく試験を受けると思われる人が流れているのが見えたため流れに乗ってついていきました。

最終的には受験番号単位で受験する建物・部屋が張り出されているため、その部屋に移動して指定されている席に座って準備等を進めました。

主に行った準備は以下です。

  • 机上に置けるものを置く
  • 水分補給
  • お手洗い
  • スマートウォッチを外して、試験用の時計をつける
  • 時間までテキストをパラパラ見る

午前試験受験

試験問題は以下に公開されています。

www.ipa.go.jp

問題1週目は計算がそこまで要求されない、かつ自信を持って解答できる問題を優先して解答しました。

2週目は1週目の確認と計算問題の解答、自身がない問題の解答を進めました。

3週目は2週目の解答の検算や2週目でもわからなかった問題について何らかの解答をしました。また、1問のみは計算を捨てて適当にそれらしい解答を選択しました(それ1問で落ちるほど酷い結果ではなさそうという感触があったというのもあります)。

最終的に1問に対して1つの解答をしているか、受験番号が合っているかを確認し、30分ほど前に退出しました。

昼食

試験会場の建物のエントランスにソファがあったので、そちらで持ち込んだ昼食を食べました。

また、試験終了前に昼食を終えていたので、気分転換がてら近くのコンビニまで行ってチョコとガムを買って糖分補給とリフレッシュを行なっていました。

午後試験集合時刻10分くらい前をめどに部屋へ戻り、午前と同じように各種準備を行なって待機していました。

昼休み中は気分転換を優先して、テキストは見ずにいました。

午後試験受験

午前試験と同じく試験問題は以下に公開されています。

www.ipa.go.jp

最終的に解答したのは以下でした。

  • 問1情報セキュリティ(必須)
  • 問3プログラミング(選択)
  • 問4システムアーキテクチャ(選択)
  • 問5ネットワーク(選択)
  • 問6データベース(選択)

最初に必須解答の情報セキュリティを回答しました。 たまに問題文をそこまで読まずに解答可能な問題があるため、それらを先に解答しました(ゼロトラストとか)。

その後序文と節のタイトルを先に読んで何となく話の流れを想像しながら、本文を読みながら問題に解答をしていきました。

次にざっと他の大問の本文と問題文を見ながら、比較的正答できそうな大問を選択して、同じように解答していくということを進めていきました。

最後に解答の確認と選択している大問があっているか、受験番号が合っているかを確認し、同じく30分ほど前に退出しました。

自己採点

午前

過去問道場で午前問題の自己採点用のサイトが用意されていたため、そちらで午前問題の採点を行いました。

www.ap-siken.com

結果は66/80で82.5%正答のため、午前試験は問題なく突破できていそうです。

午後

TACで模範解答と配点予想が出ていたので、こちらを参考に以下の基準で採点してみたところ66点だったので、多分、おそらく、大丈夫でしょう……!

  • 部分点なし
  • 記述式は表現としてもほぼ同じ文を書いている場合のみ正答

www.tac-school.co.jp

まとめ

応用情報技術者試験を受けてきました。

とくに午後問題については、アプリケーションを構築する上で最終的に考える可能性がある話題について、実際にありそうな事例を見ることができ、良い勉強になったかと思います。

合格しているといいなあ……。

メール送信の仕組みが覚えられないので手を動かしながら調べてみた

はじめに

SMTPが覚えられないため、実際に手を動かしながらまとめてみました。

SMTPとはなにか

まず、送信側のプロトコルであるSMTPの特徴をざっと書き出します。

  • Simple Mail Transfer Protocolの略
  • メールの送信・転送を行うためのプロトコル
  • TCPポートは25番

以上の情報はざっくりと頭に入ってはいるのですが、実際に何が行われているのかよくわかっていないため、詳細手順を追っていきます。

SMTPの詳細な手順

SMTPはクライアントとメールサーバ間でTCPコネクションを確立して、以下の手順でやり取りをするもののようです。

上記のクライアントからメールサーバーへのコマンドの送信を手元の端末で試してみました。

SMTPを使ってメールを送信してみる

1. Dockerコンテナを立てる

SMTPサーバーをたてることができるDockerイメージを探したところ、MailHogというものがあったためそちらを利用しました。

今回はMailHogの以下の機能を使って、SMTPを使ったメール送信の理解を進めていきます。

  • SMTP用のアプリケーションの構成
  • Web上でのメッセージの閲覧

MailHogでは、SMTP通信用のポートとして1025、HTTP用のポートとして8025を使うようなので、コンテナ起動のコマンドでポートの指定をしています。

$ docker run -p 1025:1025 -p 8025:8025 --name smtp_test --rm -d mailhog/mailhog
$ docker ps                       
CONTAINER ID   IMAGE                            COMMAND            CREATED             STATUS             PORTS                                            NAMES
2841e62b4a98   mailhog/mailhog                  "MailHog"          7 seconds ago       Up 6 seconds       0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp   smtp_test

registry.hub.docker.com

ブラウザで http://localhost:8025 にアクセスしたところ、以下のような画面が表示されました。

2. コマンドを実行する

シーケンス図に沿ってコマンドを送信します。

ドメイン等はデフォルトのままなので、以下を見ながら指定しました。

github.com

$ telnet localhost 1025
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 mailhog.example ESMTP MailHog
EHLO mailhog.example
250-Hello mailhog.example
250-PIPELINING
250 AUTH PLAIN
MAIL FROM:<soshinsya@sender.com>
250 Sender soshinsya@sender.com ok
RCPT TO:<jushinsya@receiver.com>               
250 Recipient jushinsya@receiver.com ok
DATA
354 End data with <CR><LF>.<CR><LF>
Hello, World.
Goodbye, World.
.
250 Ok: queued as hrX_V8PWHuT8Vw9pEbb289G36P0caoNsLkPIQzKYsII=@mailhog.example
QUIT
221 Bye
Connection closed by foreign host.

http://localhost:8025 にアクセスすると、メッセージが1件増えていました。

開いてみると入力したテキストが表示されていました。送信成功のようです。Return-Pathには指定したMAIL FROMコマンドで指定したメールアドレスが表示されていますが、Receivedには、mailhog.exampleのドメインから送信されていることがわかります。

また、DATAコマンド送信後のメッセージはMIMEの仕様に沿っているようです。

www.rfc-editor.org

試しに本文のFromヘッダーでメールアドレスを指定して、かつ MAIL FROMコマンドで指定するメールアドレスを異なるものにしてメールを送信してみたところ、無事に送信できました。

$ telnet localhost 1025
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 mailhog.example ESMTP MailHog
EHLO mailhog.example
250-Hello mailhog.example
250-PIPELINING
250 AUTH PLAIN
MAIL FROM:<soshinsha@dummy.com>     
250 Sender soshinsha@dummy.com ok
RCPT TO:<jushinsya@receiver.com> 
250 Recipient jushinsya@receiver.com ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: soshinsha@sender.com
To: jushinsha@receiver.com

test message.
.
250 Ok: queued as h9b3OSBwY-FFGNFY_AxNwiMTdgmfKagffjAnfm8lO_w=@mailhog.example
QUIT
221 Bye
Connection closed by foreign host.

受信したメールを http://localhost:8025 で確認したところヘッダーを表示していない状態では、Fromには DATA コマンド実行後のメールヘッダに指定したメールアドレスが表示されました。

ヘッダーを開いたところ、Receivedには先ほどと同様 mailhog.exampleReturn-Pathには MAIL FROM コマンドで指定したメールアドレスが指定されていることを確認できました。

何の対策もしていない場合であれば、FromReturn-Pathの改ざんは簡単そうです。 Receivedは信頼できるのかは不明だったので調べてみたところ、 Receivedヘッダーは送信者ではなく中継するサービスで付与されるもののようでしたので、FromReturn-Path よりは信頼性の高い情報になるのかと思いました。

www.rfc-editor.org

まとめ

SMTPの仕組みについて詳細な手順に踏み込んで手を動かしながらまとめてみました。 実はPOP3の方もdovecotを使いながら手を動かしてみていたのですが力尽きたので、気が向いたら続きをやろうと思います。

DHCPとは何か調べてみた

概要

応用情報の勉強をしている中でDHCPについて調べる機会があったので、まとめてみました。

DHCP(Dynamic Host Configuration Protocol)とは

DHCPとはネットワーク上のホストに対し、自動でIPアドレスを割り当てたり、割り当てるIPアドレスを一括管理するプロトコルです。 DHCPによって、クライアントをDHCPサーバーがあるネットワークに接続しただけで、TCP/IPを用いた通信ができるようになります(このような性質をプラグ&プレイとも言うようです)。 また、接続したネットワークのルータがDHCPサーバーとして振る舞うことも多いようです。

クライアントにIPアドレスが割り振られるまでのやり取りは以下のようになっています。

IPアドレスをクライアントで使用し始めるまでには、最初に使用して良いIPアドレスの付与を依頼して受け取る、次に実際にIPアドレスを使用することの許可を受け取る2つのフェーズに分かれています。

まず、使用して良いIPアドレスの付与を依頼して受け取る段階の説明をします。最初にDHCPサーバーのいるネットワークに接続したとき、ブロードキャストアドレス宛にIPアドレスの付与をリクエストを送信することで、DHCPサーバーからレスポンスとして使用可能なIPアドレスの通知を受け取ります。

次の段階の、実際にIPアドレスを使用することの許可を受け取る段階では、再度ブロードキャスト宛にIPアドレス使用の許可を求めるリクエストを送信することで、DHCPサーバーからレスポンスとしてIPアドレスの使用許可の通知を受け取ります。 このとき、付与されたIPアドレスの使用許可を求めるリクエストを送信しているのは、同一ネットワークに複数DHCPサーバーがあったときに適切にIPアドレスの割り振りができるようにするためのようです。

DHCPリレーエージェントとは

オフィスでDHCPサーバーを運用する場合、DHCPサーバーを1つのネットワークごとに対して1つずつ設置すると、DHCPサーバーの管理が煩雑になります。 そのため、1つのネットワークにのみDHCPサーバーを設置して一元管理したいですが、先ほど説明した方法でのIPアドレスの割り当て方法では、DHCPクライアントからDHCPサーバーへの要求がブロードキャストで行われるため、DHCPサーバーとクライアントが同一ネットワークにある必要があります。 これを解決するための手段として用いられる機能が、DHCPリレーエージェントのようです。

DHCPリレーエージェントを挟んだ場合のIPアドレス決定までのやり取りは以下のようになります。

リレーエージェントがない場合もDHCPクライアントのリクエストの送信先は、ブロードキャストアドレス宛てであることは変わりありません。 一方、リクエストを受けて処理するのはDHCPサーバー自身ではなく、DHCPサーバーのありかを知るDHCPリレーエージェントになっています。 DHCPリレーエージェントがDHCPクライアントに変わってIPアドレス付与・使用許可のリクエストを送信し、レスポンスをDHCPクライアントに転送することで、DHCPクライアントはリレーエージェントがない場合と同じようにIPアドレスを割り振りを依頼・許可申請ができます。

まとめ

ネットワークに繋いだクライアントへIPアドレスを割り振る方法について調べてまとめてみました。リレーエージェントがある場合も、ない場合もクライアントはブロードキャストアドレス宛に使用可能なIPの付与依頼と、実際にIPを使う許可依頼をしてIPアドレスを割り振ってもらっているようです。 問題を解いて解説を読むだけではいまいちピンと来なかった流れについて、きちんと理解できた気がしています。

参考書籍

www.ohmsha.co.jp gihyo.jp

gRPCとRESTは何が違うのか?

はじめに

以前、gRPCについて自分なりに調べてまとめた記事を書きました。

fvknk97034.hatenablog.com

一方で、異なるコンピュータにメソッドの実行を依頼し、その結果を返してもらう、というだけであればRESTで十分ではないかという疑問も生まれました。 そのため、REST APIとの違いに着目して追加で調査を行いました。

REST APIとは

gRPCとの比較をする前に、REST APIがどのようなものかざっくりと確認します。

まず、RESTはAPIの動作に一定の条件を求めるソフトウェアアーキテクチャです。

aws.amazon.com

RESTful Webサービスを読む限り、RESTとは「(一般的にURIの形式で表現される)リソースと(GET、POST、PUT、DELETE等の)メソッドの組み合わせでリクエストを表現することができるという特徴を持つアーキテクチャ」で、このアーキテクチャを適用して作ったAPIがREST APIと理解できそうです。

また、Web を支える技術曰く、通信方式はリクエスト/レスポンス型のプロトコルで、クライアントはリクエスト後レスポンスが返却されるまで待機する、という流れのようです。

REST APIとの比較

ちょうど以下に比較の表があったので、かいつまんで取り上げてみようと思います。

aws.amazon.com apidog.com

項目 REST gRPC
流通度合い 古くから利用される形式 比較的新しいが注目を集めている
通信方式 リクエスト・レスポンス型の単方向通信 バイナリ方式での双方向通信
結合度合い リソースとメソッド(とAPIごとの制約)が分かれば利用できる クライアント・サーバーの両方にgRPCのソフトウェアが必要、かつクライアントとサーバーの両方にデータ形式を定義する同一のファイルが必要
パフォーマンス gRPCと比較すると低速 高速
コード生成機能 サードパーティ製ツールでの提供あり ビルトイン機能として備わっている

ざっくりみた感じ、RESTはgRPCよりクライアントとサーバーが比較的独立した形で構築できる比較的一般に広まっているシンプルな方式、gRPCはクライアント・サーバー間で連携が必要である一方、RESTと比べると高速かつできるだけ早く構築を進められるようにコード自動生成機能もビルトインで持っている、といった印象です。

どのようなケースでgRPCを使うべきか

gRPCではクライアントとサーバーの連携が必要になる以上、広く使用されることを想定するAPIには向いていなさそうな印象です。

一方で、緊密な連携が比較的しやすい社内APIで、パフォーマンスが重視されるケースでは、選択肢として挙がってきそうです。

まとめ

gRPCについて概要を理解した上で、RESTとの比較を行うことでgRPCの使いどころをなんとなく把握できました。

自分しか使わないAPIといったケースでもgRPCは問題なく使えそうなので、自分用のREST APIを作る機会があれば、合わせてgRPCでも作ってみて比較等もしてみたいですね。

docker init を試してみる

はじめに

Dockerfilecompose.yamlを自動生成してくれるdocker initコマンドが使えるようになったようなので、早速試しに使ってみました。

www.docker.com

実行環境

  • macOS:Sonoma 14.3
  • Docker:4.27.1

実際にやってみる

まずは基本的なケースを試してみる

適当な作業用ディレクトリを作成し、早速docker initコマンドを実行したところ、以下のようなメニューが表示されました。

どうやら、.dockerignoreDockerfilecompose.yamlREADME.Docker.mdを自動生成してくれるようです。

$ docker init

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

? What application platform does your project use?  [Use arrows to move, type to filter]
  Go - suitable for a Go server application
  Python - suitable for a Python server application
  Node - suitable for a Node server application
  Rust - suitable for a Rust server application
  ASP.NET Core - suitable for an ASP.NET Core application
  PHP with Apache - suitable for a PHP web application
  Java - suitable for a Java application that uses Maven and packages as an uber jar
> Other - general purpose starting point for containerizing your application
  Don't see something you need? Let us know!
  Quit

今回はひとまず Otherを選択して、汎用的なコンテナを作成してもらいました。

$ docker init

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

? What application platform does your project use? Other

CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md

✔ Your Docker files are ready!

Take a moment to review them and tailor them to your application.

When you're ready, start your application by running: docker compose up --build

Consult README.Docker.md for more information about using the generated files.

プロジェクトを選択したところ、すぐに処理が完了して、実行コマンドが表示されました。

念の為ls -aコマンドを実行したところ、本当にファイルが生えていました。

$ ls -a 
.                .dockerignore    README.Docker.md
..               Dockerfile       compose.yaml

compose.yamlの中身は以下のようになっているようです。

コメントアウトを外せばDBもすぐに使用できるようになっているようでありがたいです。

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/

# Here the instructions define your application as a service called "app".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  app:
    build:
      context: .
      target: final
    # If your application exposes a port, uncomment the following lines and change
    # the port numbers as needed. The first number is the host port and the second
    # is the port inside the container.
    # ports:
    #   - 8080:8080

    # The commented out section below is an example of how to define a PostgreSQL
    # database that your application can use. `depends_on` tells Docker Compose to
    # start the database before your application. The `db-data` volume persists the
    # database data between container restarts. The `db-password` secret is used
    # to set the database password. You must create `db/password.txt` and add
    # a password of your choosing to it before running `docker compose up`.
    #     depends_on:
    #       db:
    #         condition: service_healthy
    #   db:
    #     image: postgres
    #     restart: always
    #     user: postgres
    #     secrets:
    #       - db-password
    #     volumes:
    #       - db-data:/var/lib/postgresql/data
    #     environment:
    #       - POSTGRES_DB=example
    #       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    #     expose:
    #       - 5432
    #     healthcheck:
    #       test: [ "CMD", "pg_isready" ]
    #       interval: 10s
    #       timeout: 5s
    #       retries: 5
    # volumes:
    #   db-data:
    # secrets:
    #   db-password:
    #     file: db/password.txt

Dockerfileは以下のようになっているようです。

「Hello world from appuser! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.」と表示されれば成功していると見て良さそうです。

# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7

################################################################################
# Pick a base image to serve as the foundation for the other build stages in
# this file.
#
# For illustrative purposes, the following FROM command
# is using the alpine image (see https://hub.docker.com/_/alpine).
# By specifying the "latest" tag, it will also use whatever happens to be the
# most recent version of that image when you build your Dockerfile.
# If reproducability is important, consider using a versioned tag
# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff).
FROM alpine:latest as base

################################################################################
# Create a stage for building/compiling the application.
#
# The following commands will leverage the "base" stage above to generate
# a "hello world" script and make it executable, but for a real application, you
# would issue a RUN command for your application's build process to generate the
# executable. For language-specific examples, take a look at the Dockerfiles in
# the Awesome Compose repository: https://github.com/docker/awesome-compose
FROM base as build
RUN echo -e '#!/bin/sh\n\
echo Hello world from $(whoami)! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.'\
> /bin/hello.sh
RUN chmod +x /bin/hello.sh

################################################################################
# Create a final stage for running your application.
#
# The following commands copy the output from the "build" stage above and tell
# the container runtime to execute it when the image is run. Ideally this stage
# contains the minimal runtime dependencies for the application as to produce
# the smallest image possible. This often means using a different and smaller
# image than the one used for building the application, but for illustrative
# purposes the "base" image is used here.
FROM base AS final

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser
USER appuser

# Copy the executable from the "build" stage.
COPY --from=build /bin/hello.sh /bin/

# What the container should run when it is started.
ENTRYPOINT [ "/bin/hello.sh" ]

docker compose up --buildを実行します。

$ docker compose up --build
[+] Building 11.3s (11/11) FINISHED                                                                                                                                                                                      docker:desktop-linux
 => [app internal] load build definition from Dockerfile                                                                                                                                                                                 0.1s
 => => transferring dockerfile: 2.88kB                                                                                                                                                                                                   0.0s
 => [app] resolve image config for docker.io/docker/dockerfile:1                                                                                                                                                                         3.6s
 => [app] docker-image://docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021                                                                                                           2.0s
 => => resolve docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021                                                                                                                     0.0s
 => => sha256:9d9c93f4b00be908ab694a4df732570bced3b8a96b7515d70ff93402179ad232 11.80MB / 11.80MB                                                                                                                                         1.3s
 => => sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021 8.40kB / 8.40kB                                                                                                                                           0.0s
 => => sha256:657fcc512c7369f4cb3d94ea329150f8daf626bc838b1a1e81f1834c73ecc77e 482B / 482B                                                                                                                                               0.0s
 => => sha256:a17ee7fff8f5e97b974f5b48f51647d2cf28d543f2aa6c11aaa0ea431b44bb89 1.27kB / 1.27kB                                                                                                                                           0.0s
 => => extracting sha256:9d9c93f4b00be908ab694a4df732570bced3b8a96b7515d70ff93402179ad232                                                                                                                                                0.6s
 => [app internal] load metadata for docker.io/library/alpine:latest                                                                                                                                                                     2.3s
 => [app internal] load .dockerignore                                                                                                                                                                                                    0.0s
 => => transferring context: 667B                                                                                                                                                                                                        0.0s
 => [app base 1/1] FROM docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b                                                                                                          1.2s
 => => resolve docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b                                                                                                                   0.0s
 => => sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b 1.64kB / 1.64kB                                                                                                                                           0.0s
 => => sha256:6457d53fb065d6f250e1504b9bc42d5b6c65941d57532c072d929dd0628977d0 528B / 528B                                                                                                                                               0.0s
 => => sha256:05455a08881ea9cf0e752bc48e61bbd71a34c029bb13df01e40e3e70e0d007bd 1.47kB / 1.47kB                                                                                                                                           0.0s
 => => sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8 3.41MB / 3.41MB                                                                                                                                           0.6s
 => => extracting sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8                                                                                                                                                0.4s
 => [app build 1/2] RUN echo -e '#!/bin/sh\necho Hello world from $(whoami)! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.'> /bin/hello.sh                      0.7s
 => [app final 1/2] RUN adduser     --disabled-password     --gecos ""     --home "/nonexistent"     --shell "/sbin/nologin"     --no-create-home     --uid "10001"     appuser                                                          0.7s
 => [app build 2/2] RUN chmod +x /bin/hello.sh                                                                                                                                                                                           0.6s
 => [app final 2/2] COPY --from=build /bin/hello.sh /bin/                                                                                                                                                                                0.1s
 => [app] exporting to image                                                                                                                                                                                                             0.1s
 => => exporting layers                                                                                                                                                                                                                  0.1s
 => => writing image sha256:53c892d7cb61f29514101b9605d5d820ad7384c5aa93afa7d724dc8969702798                                                                                                                                             0.0s
 => => naming to docker.io/library/docker-init-app                                                                                                                                                                                       0.0s
[+] Running 2/2
 ✔ Network docker-init_default  Created                                                                                                                                                                                                  0.1s 
 ✔ Container docker-init-app-1  Created                                                                                                                                                                                                  0.2s 
Attaching to app-1
app-1  | Hello world from appuser! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.
app-1 exited with code 0

事前の想定通りのメッセージが表示されました。

事前に準備されたプラットフォームを選択する

再度docker initを実行して、Nodeを選択してみました。

最終的には以下のガイドに沿って行うのがスマートそうです。 (無視して先にdocker initを実行したところ、クローンしたサンプルファイルを移動するか、compose.yamlを編集するかが必要になりました……)

docs.docker.com

ひとまずサンプルのリポジトリをクローンしてきて、ディレクトリを移動後、docker initを実行しました。 Nodeの場合、バージョン、パッケージマネージャー、アプリケーションの実行コマンド、ポート番号が指定できるようです。また、アプリケーションの実行コマンドは、すでにファイルが存在する場合はある程度推測してくれるようです。が、今回はsrcディレクトリ配下にファイルが入っており、デフォルト値とは異なっていたため明示的に指定しています。

$ git clone https://github.com/docker/docker-nodejs-sample            
Cloning into 'docker-nodejs-sample'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 61 (delta 2), reused 2 (delta 2), pack-reused 55
Receiving objects: 100% (61/61), 1.71 MiB | 5.65 MiB/s, done.
Resolving deltas: 100% (6/6), done.
$ cd docker-nodejs-sample/
$ docker init              

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

? Do you want to overwrite them? Yes
? What application platform does your project use? Node
? What version of Node do you want to use? 19.4.0
? Which package manager do you want to use? npm
? What command do you want to use to start the app? node src/index.js
? What port does your server listen on? 3000

CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md

✔ Your Docker files are ready!

Take a moment to review them and tailor them to your application.

When you're ready, start your application by running: docker compose up --build

Your application will be available at http://localhost:3000

先ほどと同様、実行コマンドが表示されているのでdocker compose up —buildを実行したところ、正常に起動しました。

$ docker compose up --build
[+] Building 2.7s (11/11) FINISHED                                                            docker:desktop-linux
 => [server internal] load build definition from Dockerfile                                                   0.0s
 => => transferring dockerfile: 1.21kB                                                                        0.0s
 => [server] resolve image config for docker.io/docker/dockerfile:1                                           1.3s
 => CACHED [server] docker-image://docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752  0.0s
 => [server internal] load metadata for docker.io/library/node:19.4.0-alpine                                  0.9s
 => [server internal] load .dockerignore                                                                      0.0s
 => => transferring context: 659B                                                                             0.0s
 => [server stage-0 1/4] FROM docker.io/library/node:19.4.0-alpine@sha256:ab3603cb7934b21f1ffb522b1a1d538809  0.0s
 => [server internal] load build context                                                                      0.0s
 => => transferring context: 2.51kB                                                                           0.0s
 => CACHED [server stage-0 2/4] WORKDIR /usr/src/app                                                          0.0s
 => CACHED [server stage-0 3/4] RUN --mount=type=bind,source=package.json,target=package.json     --mount=ty  0.0s
 => CACHED [server stage-0 4/4] COPY . .                                                                      0.0s
 => [server] exporting to image                                                                               0.0s
 => => exporting layers                                                                                       0.0s
 => => writing image sha256:de5217186e7035038bec6263f52c28f67d67a0d608e25bc34cf18556093557cc                  0.0s
 => => naming to docker.io/library/docker-nodejs-sample-server                                                0.0s
[+] Running 2/2
 ✔ Network docker-nodejs-sample_default     Created                                                           0.1s 
 ✔ Container docker-nodejs-sample-server-1  Created                                                           0.1s 
Attaching to server-1
server-1  | Using sqlite database at /tmp/todo.db
server-1  | Listening on port 3000

ブラウザでhttp://localhost:3000へアクセスしたところ、以下のようなサイトが表示されていました。

まとめ

今回のような自動生成のコマンドができることで、いちいちDocker関連のファイルの書き方を調べることなく、試しにDockerで環境を作って手を動かしてみるといった作業がしやすくなって、非常にありがたいと感じました。 上記の使い方の場合、docker initでプラットフォームを選択したらよしなにサンプルのファイルを作成してくれるところまでやってくれるとありがたいのですが、そこまでをdocker initのコマンドにさせるのは責任過多な気がしているので、今回のNodeの例ようにファイル群は別途用意して、あくまでDocker関連のファイル群の生成に使うのがベターかなと思いました。

gRPCの概要を知る

はじめに

gRPCに触れる機会があったので、どのような技術か調べてみました。 ※あまり英語は得意でないので認識違いを含む可能性があります。正確な理解が必要な場合、リンク先を参照することを強く勧めます。

gRPCとは

まず、gRPCの公式サイトを訪れたところ、以下のようなフレーズが最初に表示されました。

A high performance, open source universal RPC framework

grpc.io

直訳すると、「高パフォーマンスでオープンソースの汎用的なRPCフレームワーク」といった感じでしょうか。

RPCとは

gRPCを理解するために、まずRPCを理解する必要がありそうです。 RPCとは「Remote Procedure Call」の略で、ネットワーク上の他のコンピュータプログラムを呼び出す技術やプロトコルを指す用語です。

www.ntt-west.co.jp

RPCの基本的な実行手順は以下のようになっています。

www.hpcs.cs.tsukuba.ac.jp http://www-higashi.ist.osaka-u.ac.jp/~nakata/mobile-cp/www-higashi.ist.osaka-u.ac.jp learn.microsoft.com

参考文献にはさらに詳しい話があるのですが、ここでは「RPCはクライアントからリモートサーバー上のプログラムを呼び出すために使われるプロトコルの1つ」といった理解に留めておきます。

改めてgRPCとは

ここで改めてgRPCについて理解をしていこうと思います。 gRPCのIntroductionをひととおり読んでみると以下のような技術のようです。

  • コンピュータ間のプログラムの呼び出しを行うRPCベースの技術
  • インターフェイスの定義とメッセージの交換形式にProtocol Buffersを使用する
  • 多様な言語をサポートしており、異なる言語で書かれたプログラム間でのメソッド呼び出しができる

https://grpc.io/docs/what-is-grpc/introduction/
つまりgRPCは、RPC上の各登場人物間のやり取り(≒処理の呼び出し、メッセージの転送)の仕方にProtocol Buffersと呼ばれる技術を使っており、異なる言語間でのメソッド呼び出しをサポートするRPCベースのフレームワーク、といった理解ができそうです。

Protocol Buffers とは

gRPCではProtocol Buffersを使用しているとのことでしたが、Protocol Buffersについても知らないので調べました。 Protocol Buffersドキュメントを確認すると、以下のような文がありました。

Protocol Buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.

protobuf.dev

Protocol BuffersとはGoogleが作った、言語・プラットフォームに依存しない、構造化データをシリアライズするための、拡張性のあるメカニズムといった感じでしょうか。
さらに読み進めると以下のような特徴を有する技術のようです。

  • 定義言語がある
  • 定義言語を使って.protoファイルを作り、コンパイラを通すことでデータとインターフェイスを生成する
    • コンパイルをしてさまざまな言語で扱えるコードを生成する
  • コンパイル先の言語に特化したランタイムのライブラリがある
  • データのシリアライズのフォーマットを定義している

protobuf.dev protobuf.dev

つまり、Protocol Buffersとは、言語、言語のコンパイラ、各言語向けのランタイム、シリアライズのフォーマット」という感じのようです。
ここまで読んだ感じ、gRPCの異なる言語間でのメソッド呼び出しという特徴は、Protocol Buffersによって成立している部分が大きそうな印象でした。

まとめ

gRPCは「コンピュータ間でのメソッド呼び出しを行う技術の1つであるRPCをベースに作られた、言語に依存しないフレームワーク」ということがわかりました。

参考資料

2024年の目標

概要

正月の非日常感も薄れてきていますが、良い機会なので今年の目標を記録しておこうと思います。

そもそも私は普段何をしている人か

去年の中頃から小さなWebアプリケーションの開発チームの責任者のようなことをしています。具体的には以下のようなことをメインにしています。

  • メンバーの書いたコードをレビューする
  • 機能の仕様・設計を検討して、実装タスクをメンバーにタスクを割り当てる
  • アプリケーションをリリースする

現状の課題

まず、現状自分に感じている課題は以下のようになっています。

  • 設計やプログラミングに関する知識・技術に自信がない
    • コードレビューのコメントが根拠の薄い内容になってしまっている
    • コード構成(モジュールの分割方法や持つべき責任)につっこんだ指摘ができず、メソッド名・コメント内容など比較的表面的な指摘にとどまることが多い
  • チームメンバーとタスクの認識の面で齟齬が多い
    • 実装タスクで何をすべきか、何をしなくて良いかをうまく伝えられてない
    • 実装チケットの内容に抜け漏れがある
  • チームマネジメントがよくわかっていない
    • アジャイルとかスクラムとか心理的安全性とかの用語はぼんやりと理解しているが、具体的な目的やメリットデメリット、何が求められるかを理解した上で運用できるわけではない

1年後にどうなっていたいか

課題に対して、どう改善したいかは以下のようになっています。

  • コードレビューのコメントを根拠を持ってつけられるようになりたい
  • コードを書く上での各種原則やパターンを深く理解したい
  • 適切なアウトプットが出てくるようなタスクの依頼の仕方ができるようになりたい
  • 運用ができるかはともかく、チームマネジメントの有名な理論を押さえておきたい

今年の抱負

1年後どうなっていたいかに対して、やることを以下のように定めました。

  • SOLID原則・デザインパターンの本を1冊ずつ読んで、まとめる
  • エンジニアに関わる文章の書き方を学び、基本的な依頼文などをテンプレート化する
    • 仕様書・設計書の書き方と項目の必要性を理解する
    • チームで必要な項目を理解して加工し、テンプレート化する
  • 文章を適切に書く練習のため、ブログを1月に1本以上投稿する
  • チームマネジメント系の情報のインプットを増やす
    • 本を最低1冊は読む
    • いくつかポッドキャストがあるようなので聴く

上記以外にも家計簿アプリ完成させるとか、応用情報取るとか、リングフィットアドベンチャーの3周目クリア&称号コンプとかできれば良いなと思っています。