ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

docker build 時の apt install が遅い問題 (docker build の --add-host オプションを使って APT のパッケージリポジトリを国内ミラーに設定できる)

こんにちは、Docker ビルドおじさんです。

ubuntu:18.04 ベースで Dockerfile を書いてビルドしまくってるのですが、apt install がとにかく遅い!!! なぜなら日本に住んでいるから!! archive.ubuntu.com が遠い!!!


解決方法

調べてみたらいろいろ解決策がありそうだった。

解決策 その 1

日本を出る。 archive.ubuntu.com の近くに行きましょう。

解決策 その 2

Dockerfile をいじって (一時的にでも) APT のパッケージリポジトリの URL を国内ミラーに変更する。 「apt docker 遅い」 みたいなキーワードで検索するとこの方法がひっかかる。

解決策 その 3

apt-cacher みたいなやつでキャッシュしたり、Squid による透過型プロキシでキャッシュしたりする。 わりとこれが素直な方法な気もするが、ちょっと大変そうな気もする。

解決策 その 4

この記事で共有したいのはこの方法。 docker build コマンドの --add-host オプションを使って APT のリポジトリのホスト名に対応する IP アドレスを書き換えちゃう方法。

例えば ubuntu:18.04 をベースとするイメージの場合、リポジトリのホスト名として archive.ubuntu.com などが使われていて、国内ミラーは jp.archive.ubuntu.com となっているので、nslookup jp.archive.ubuntu.com で国内ミラーの IP アドレスを調べて、docker build --add-host="archive.ubuntu.com:xxx.xxx.xx.xx" . という感じで国内ミラーの IP アドレスを指定してやると良さそう。

セキュリティ的なところはちょっと気になる。

Ubuntu では ca-certificates パッケージで CA 証明書をインストールできるぞ

1 行まとめ

Ubuntu ベースの Docker コンテナSSL 接続をするために ca-certificates パッケージをインストールしましょう。

エラー内容

Ubuntu 18.04 (ubuntu:18.04) ベースの Docker コンテナで HTTPS 接続ができなくて npm install に失敗した。

root@068c8e545fca:/# npm install -g carto
npm ERR! Linux 4.9.125-linuxkit
npm ERR! argv "/usr/bin/node" "/usr/bin/npm" "install" "-g" "carto"
npm ERR! node v8.10.0
npm ERR! npm  v3.5.2
npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY

npm ERR! unable to get local issuer certificate
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR!     <https://github.com/npm/npm/issues>

npm ERR! Please include the following file with any support request:
npm ERR!     /npm-debug.log

見るからに CA 証明書のエラーっぽい。 /npm-debug.log の中身を見ると以下のような感じ。

0 info it worked if it ends with ok
1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'install', '-g', 'carto' ]
(略)
15 info attempt registry request try #1 at 10:34:44 AM
16 verbose request id 454780186dc942d1
17 http request GET https://registry.npmjs.org/carto
18 info retry will retry, error on last attempt: Error: unable to get local issuer certificate
19 info attempt registry request try #2 at 10:34:54 AM
20 http request GET https://registry.npmjs.org/carto
21 info retry will retry, error on last attempt: Error: unable to get local issuer certificate
22 info attempt registry request try #3 at 10:35:54 AM
23 http request GET https://registry.npmjs.org/carto
24 silly fetchPackageMetaData Error: unable to get local issuer certificate
24 silly fetchPackageMetaData at TLSSocket. (_tls_wrap.js:1105:38)
24 silly fetchPackageMetaData at emitNone (events.js:106:13)
24 silly fetchPackageMetaData at TLSSocket.emit (events.js:208:7)
24 silly fetchPackageMetaData at TLSSocket._finishInit (_tls_wrap.js:639:8)
24 silly fetchPackageMetaData at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38)
24 silly fetchPackageMetaData error for carto { Error: unable to get local issuer certificate
24 silly fetchPackageMetaData at TLSSocket. (_tls_wrap.js:1105:38)
24 silly fetchPackageMetaData at emitNone (events.js:106:13)
24 silly fetchPackageMetaData at TLSSocket.emit (events.js:208:7)
24 silly fetchPackageMetaData at TLSSocket._finishInit (_tls_wrap.js:639:8)
24 silly fetchPackageMetaData at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38) code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' }
(略)
30 verbose stack Error: unable to get local issuer certificate
30 verbose stack at TLSSocket. (_tls_wrap.js:1105:38)
30 verbose stack at emitNone (events.js:106:13)
30 verbose stack at TLSSocket.emit (events.js:208:7)
30 verbose stack at TLSSocket._finishInit (_tls_wrap.js:639:8)
30 verbose stack at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38)
31 verbose cwd /
32 error Linux 4.9.125-linuxkit
33 error argv "/usr/bin/node" "/usr/bin/npm" "install" "-g" "carto"
34 error node v8.10.0
35 error npm v3.5.2
36 error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY
37 error unable to get local issuer certificate
38 error If you need help, you may report this error at:
38 error <https://github.com/npm/npm/issues>
39 verbose exit [ 1, true ]

curl コマンドを試してみるも、当然ながらこちらも CA 証明書のエラー。

root@068c8e545fca:/# curl https://google.com/
curl: (77) error setting certificate verify locations:
  CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs

原因と対応

Ubuntu では基本的な CA 証明書が ca-certificates パッケージで提供されている。 これがインストールされていない状態で npm install コマンドや curl コマンドで HTTPS 通信しようとすると CA 証明書のエラーが発生する。 curl パッケージなどをインストールする際に、デフォルトでは推奨パッケージとして ca-certificates パッケージもインストールされるのでこのエラーには遭遇しづらいが、Docker イメージをビルドする際に apt install -y --no-install-recommends curl という感じで --no-install-recommends オプションを付けていると ca-certificates パッケージがインストールされずに、上記の状態になる。

対策としては、単純に ca-certificates パッケージをインストールすれば良い。

Java EE 技術から EE4J (Jakarta EE) 技術に移行する

2017 年に発表された Java EE の Eclipse Foundation への移管。 移管されたプロジェクトをまとめるルートプロジェクトの名前は Eclipse Enterprise for Java (EE4J) で、Java EE に相当するプラットフォームの名前は Jakarta EE となっている *1

EE4J には JAX-RSJSON Processing (JSON-P) などの Java EE の一部として使われていたプロジェクトも移管されている。 これらを単体で使う場合も今後は EE4J のものを使っていくべきだろう。 (古いリポジトリの方は更新されないだろうので。)

EE4J 傘下になってからの最初のリリースがぼちぼちなされていたりして、そろそろ EE4J の方に移行できるようになってきているので、移行について調べたことをまとめておく。

EE4J の各プロジェクト

EE4J 傘下のプロジェクトについては EE4J プロジェクトページ からリンクがあるので、それを辿ると見つけられる。 例えば JAX-RS のページや JSON-P のページは下記である。

EE4J 各プロジェクトの Maven リポジトリ

EE4J への移管に伴って、多くのプロジェクトの API や参照実装 (Reference Implementation; RI) の Maven リポジトリのグループ ID やアーティファクト ID が変更されている。 API については下記ページにまとめられている。

見た感じでは、javax という部分が jakarta に変更されている模様 *2

参照実装についてはプロジェクトごとに異なるようである。

例えば、JSON-P の参照実装については下記のように書かれており、org.glassfish:javax.json から org.glassfish:jakarta.json に変更されていることがわかる。

The main API jar file is now located at jakarta.json:jakarta.json-api and the main RI jar file is now located at org.glassfish:jakarta.json.

JSON Processing (JSON-P)

一方で、JAX-RS の参照実装である Jersery については、バージョン 2.28 が最初の Jakarta EE 実装としてリリースされているが、特にグループ ID やアーティファクト ID の変更はないようである。

ソースコードの変更は必要か?

Maven アーティファクトの名前が変わってはいつつも、API 自体に変更はない。 Java EE 時代の最後の API を使っているソースコードであれば、そのまま EE4J の最初のリリースに移行できるはず。

This is common for every Jakarta EE project this release: it was required not to provide any changes in API and functionality to ensure the compatibility between last Java EE and initial Jakarta EE releases.

Jersey 2.28 has been released | Jan's Blog

*1:EE4J と Jakarta EE の名前の使い方などは Jakarta EE の FAQ に書かれている。 が、これを読んでも正直なところ EE4J と Jakarta EE の使い分けがいまいちわからない……。

*2:正式な名前変更のルールについてはどこかで言及されているのかもわからないが、見つけられていない。

読んだ : What Is An Engineering Manager?

最近エンジニアリングマネージャという言葉をよく聞くようになった気がするのだけど、自分の思っているエンジニアリングマネージャ像 *1 と全然違うエンジニアリングマネージャ像を語っている人もいる *2 ので、何かしら文献があったりしないのかなーと思って調べたところ、1997 年の paper を見つけた。

peer.asee.org

修士課程のエンジニアリングマネジメントのカリキュラムで使えることを目的とした paper らしく、なかなか興味深かった。 ので、面白かった点などを書き残しておく。

面白かった点とか気になった点とか共感した点とか

エンジニアリングマネージャは優秀なエンジニアでなければならない

There are two kinds of engineers in industry: individual contributors and managers. The managers must be competent engineers first on the ground that you can't manage what you don't understand. Ideally these people should be educated in two professions: engineering and management.

自分が理解してないものをマネジメントできるわけがない。 ド正論。

プロジェクト軸と機能軸

In this paper we will be discussing engineers and managers in the engineering department. These people may be employed on three kinds of engineering work (as viewed by engineering managers):

  • Projects
  • Programs (groups of related projects)
  • Functions (work that supports the projects, e.g., materials engineering, testing, failure analysis, etc.)

大きく分けて、プロジェクト軸と機能軸があるという話。 これに関することを最近よく考えていて、高いレベルの専門性の維持のためには機能組織がないと難しいのではないか、と思っている。 「Definition of a Function」 にて、マネージャの責務として以下のことが書かれている。

A manager responsible for

  • Maintaining a high level of personnel competence in the specialty
  • Equipment relevant to the specialty
  • Investigating new technology

今のうちの開発組織には開発支援 G という機能組織がある *3 のだけど、逆に言うとエンジニアリングの機能組織はそれだけしかなくて、開発支援 G が受け持つ分野以外について高いレベルで専門性を維持するための責任を持つエンジニアリングマネージャが (明確な役割としては) 存在しない *4。 プロダクト開発組織としてより強くなっていくためには、より高いレベルで専門能力の維持向上を図っていく必要があると思っていて、そのためには機能組織が大きな役割を果たすのではないか、と思っている。 (考え中。)

(こういう話に興味がある方が居たらぜひお話したいです! お気軽にお声がけください!!)

エンジニアとエンジニアリングマネージャのキャリアパス

f:id:nobuoka:20190221020749p:plain
Career Progressions

ざっくりとは 「まあそうだよね」 って感じの図ではあるけど、engineering management の先に general management があることや、マネジメントの梯子の最初が program management と function management である点が、なるほどー、という感じ。

マネジメントの梯子の方に project management がないのは、多分 (本 paper における) project management は、通常の (マネージャでない) エンジニアにも求められることだからなのかなー、と思った。

マネージャのおしごと

仕事の計画から体制づくり、プロセスの構築と組織への装着、結果の評価、それをもとにした再計画など。

f:id:nobuoka:20190221023419p:plain
General Tasks of Managers

この中で、エンジニアリングマネージャについての議論で話題にあがりやすいのは体制づくり (人の育成や組織マネジメント) についてだと感じている。 おそらく、仕事の計画などの点は他のマネージャとも共通の要素であるが、人の採用や育成、評価などはエンジニア固有の事情が多いからなのかな、と思う。 また、チームビルディングが難しい、ということも理由の一つなのかもしれない。

本 paper でも、エンジニアリングマネージャが扱わなければならない組織内部の複雑な点として、tiger team *5 の形成について多くの点 (採用や育成など) が挙げられている。

  • Building tiger teams
    • Recruiting the best people
    • Training them
      • Planning and providing engineering experience
      • Continuing their formal education
    • Providing facilities and tools
    • Demanding performance

Time-related thinking

11 の思考のモードが挙げられており、その中で特に Time-related thinking とそれが Innovation のプロセスで求められたときのエンジニアリングマネージャの義務についてなども書かれている。

最近社内で新規プロジェクトの進め方などを議論するときに、よく 「時間」 について話をしていたりするし、本 paper の内容もわかりがあるものだった。

おわり

エンジニアリングマネージャと一口に言っても、対象となるエンジニアリングの領域はいろいろあって、組織や人によってどういう役割を担うのかはだいぶ違ってくるんだろうなー、と思った。 また、エンジニアリングマネージャが担う役割のうち、仕事の計画と推進、結果の評価などの部分はプロジェクトマネージャ、人員配置などはラインマネージャ、という感じで役割を分割したりもできるのだろう。 GREE のブログにもそういうことが書かれていた。

*1:エンジニアリングマネージャとはエンジニアリングのマネジメントをする人である、という認識。

*2:単なるエンジニア組織のラインマネージャをエンジニアリングマネージャって呼んでるような人もいる。 「エンジニアリングマネージャーはエンジニアリングがわからなくてもよいのか」 というブログ記事とか。

*3:エンジニアをサポートするエンジニア~RMP内製開発の強さ・魅力~【前編】」 参照。

*4:同じ技術分野のエンジニア同士の勉強会などは行われてはいるが。

*5:特定のゴールに向かう専門家集団のことらしい。

読んだ : ユースケース駆動開発実践ガイド

ユースケース駆動開発実践ガイド (OOP Foundations)

ユースケース駆動開発実践ガイド (OOP Foundations)

ユースケースを出発点とする開発プロセスを学びたかったので読んだ。 ちなみにこの書籍が言っている 「ユースケース」 というのは Ivar Jacobson (イヴァー・ヤコブソン) によるものであり、Uncle Bob の Clean Architecture が言う 「ユースケース」 と (基本的には) 同じものである *1

結論から言うと、本書は個人的に学びの多いものであった。 私は 「DDD を実際の開発プロセスにどう取り込んでいくと良いか?」 「Clean Architecture でいうところの UseCases 層のメソッドって具体的にどういう粒度で書くことになるのか?」 といったところを知りたかったのだが、非常に参考になった。 また、ちゃんとした要求定義・要件定義などの経験がなく *2 て、要求定義を含んだ開発プロセスについても学びたかったので、その点を学ぶことができたのも良かった。

古い本ではあるが、開発プロセスを概念的に理解するという目的であれば現代でも十分に通用するものだと感じる。 不要な内容もそれなりにある *3 が、そこはいい感じに飛ばして読むと良いだろう。

内容 : 開発プロセス概要

本書で述べられている開発プロセスICONIX プロセスと呼ばれるものである。 大まかな流れは以下のようになる。

f:id:nobuoka:20190217132851p:plain
ICONIX プロセス

要求定義から始まり、実装・テストまで静的モデルと動的モデルをブラッシュアップしながら進めていく。 全体の流れを説明すると下記のようになる。

  1. 要求定義
    • 機能要求 : システムができることの定義。 プロジェクトの組織化によって開発チームが機能要求の抽出に加わるのか、顧客やビジネス分析者が機能要求を開発チームに伝えるのかが決まる。
    • ドメインモデリング : 問題領域からユビキタス言語を定義し、単語間の関連を表現する。
    • 振る舞い要求 : 紙芝居から始めて、アクター (ユーザー) とシステムがどう対話するかをユースケースとして記述する。
    • 要求レビュー : ユースケース記述が顧客の要望に応えているかレビューする。 ユースケースごとに行っても良い。
  2. 分析・予備設計レビュー : 個別のイテレーションごとに (= ユースケースの小さな単位で) 下記を行う。
  3. 詳細設計
    • シーケンス図の作成
    • ドメインオブジェクトに振る舞いを追加して、クラスモデルに昇華
    • 詳細設計レビュー
  4. 実装
    • コーディングと単体テスト
    • 統合テストとシナリオテスト

ICONIX プロセスの特徴はロバストネス分析にあるらしく、ロバストネス分析により 「ユースケースとシーケンス図の間のギャップが大きく、ユースケースとシーケンス図がうまく対応付けられていない」 という状況が起こることを避けられるらしい。

ユースケースについて

Jacobson の 「ユースケース」 と言っても、いろいろ流儀があるらしい。 本書では、ユースケースの記述として下記を重要と言っている。

  • イベントとその応答として、アクター (ユーザー) とシステムの対話を記述すること。 すなわち、ユーザーがどういう操作をするのかだけでなく、システムの挙動も記述する。
  • UI についても考慮する。 GUI プロトタイプや画面モックアップを使用する。
  • ドメインモデル中の言葉や、画面のようなバウンダリクラスの名前を使って記述する。
  • 代替コース (「雨の日」 のシナリオ) (バリデーションエラー時のシステムの応答など) を忘れないようにする。
  • ユースケース記述は叙述的にする。 要求自体をユースケース記述に含めたりはしない。 (要求は別に管理し、要求を満たすようにユースケースを記述すれば良い。)

思ったことなど

アジャイルなプロセスに適用できるのか?

本書ではできると書かれている。 私自身の実感としても可能だと感じた。

上流工程でのモデリングを、コーディングの前に少しだけ (1 回にひとつのユースケースに対してだけ) 行うのか、すべてのユースケースをコーディング前にモデリングするのかの判断は、あなたに任されてい ます。 ICONIX プロセスを利用する場合、プロジェクトに応じてアジャイルに行う (短いイテレーションによって、短期間で連続的にリリースする) ことも、ウォーターフォール的に行う (まずすべての要求を記述し、設計を行い、それからコードを書く) ことも可能です。

ユースケース駆動開発とドメイン駆動設計 (DDD)

ユースケース駆動開発」 と聞くと DDD とは全然違う概念のように思うが、実際のところはドメインモデリングをするし、動的モデルをユースケース記述からブラッシュアップしていくのにあわせて静的モデル (ドメインモデル) をブラッシュアップするという開発プロセスなので、ある意味 DDD (の一部?) を包含しているように思う。 DDD で難しいのは 「ドメインオブジェクトにどういう振る舞いを割り当てるのか」 というところ *4 だと私は思っているのだが、ICONIX プロセスではユースケース記述中に出てくる動詞がドメインオブジェクトの振る舞いの候補となるので、DDD をやりやすいと感じた。

つまり、ドメインモデルを使う側 (ユースケース側) の記述をもとにドメインモデルに振る舞いを割り当てられるわけである。 さらに言うと、そうすることでアプリケーション上のユースケースのステップに相当する処理がユースケース記述に近いものになり、実装を理解しやすくなるという利点もある。

ドキュメントを書くべきなのか?

ICONIX プロセスではドキュメント (ユースケース記述やドメインモデル、ロバストネス図やシーケンス図など) をしっかりと作り、コードの変更に合わせてドキュメントを更新していくという教えになっている。 個人的には、ユースケース記述とユビキタス言語についてはドキュメント化はした方が良くて (= 非開発者とも共有するため)、それ以外の設計用のドキュメントについては開発チームの練度によるかなーという気はする。 ユースケース記述とユビキタス言語から直接設計・実装に入れる場合も多々ありそうなので、必要に応じてで良さそう。 ロバストネス図やシーケンス図をしっかりと書くことでユースケースドメインモデルのブラッシュアップに効くというのはわかるので、それを明示されたモデル上でやるのか、頭の中でやるのか、実際のコード上でやるのか、みたいな話になりそう。

Clean Architecture でいう UseCases との関係

冒頭でも述べた通り、Clean Architecture におけるユースケースも Jacobson のユースケースがもとになっている。 『Clean Architecture 達人に学ぶソフトウェアの構造と設計』 には、ユースケースについて 『ユーザーとエンティティのインタラクションを支配するアプリケーション固有のルールを記述したものである』 と書かれており、基本的には本書のユースケースと同じものを表している思って良いだろう。 ただ、ユースケースの例を見るとシステムの挙動しか表していなかったり、『ユースケースは、ユーザーインターフェイスについては記述していない』 と書かれていたりして、やや差異はあるようだ。 ここら辺はユースケースの書き方の流儀の違いなどによるものだと思われる。

UseCases 層の実装として、『ユースケースはオブジェクトである。アプリケーション固有のビジネスルールを実装した関数を1つ以上持っている。』 と書かれている。 これをそのまま受け取ると、1 ユースケースに対して 1 つの UseCase クラスを用意し、ユースケース記述におけるシステムの応答のステップのひとまとまりを 1 メソッドとして実装する (ユースケースに複数の応答がある場合などは複数メソッドを定義する) という感じになりそう?

とはいえ Android だと非同期処理とか画面遷移とかで 1 ユースケースを 1 クラスで実装するのは設計的にやりにくい気もするので、「UseCases 層のメソッドはユースケース記述の粒度で実装する」 ぐらいの感じで設計すれば良さそう。

ユースケース記述に技術的な内容を含めて良いか

例えば 「キャッシュ」 の扱いなど。 個人的にはユーザーから検知できるものであればユースケースに含めるべきなきはしている。

例えばブラウザでスーパーリロードするとキャッシュがあっても再読み込みする、みたいな場合、ユーザーからキャッシュの存在が見えるわけなので、ユースケース記述に含めるべきな気がする。 キャッシュの有無がユーザーから見える挙動に全く影響を与えない、本当にパフォーマンスのためだけのキャッシュだったらユースケースに含めない方が良さそう。

関連

目線合わせシートとユースケース記述

うちのチームでは、新機能開発などの施策を行う際にデザイナの人が 「目線合わせシート」 というものを作ってくれている。

UI のプロトタイプやユーザーの行動などがまとめられている。 うちのチームでユースケースを使うとしたら、目線合わせシートをもとにユースケースを抜き出してユースケース記述をしていく、という感じにするのが良さそう。

*1:書籍 『Clean Architecture 達人に学ぶソフトウェアの構造と設計』 において、「ユースケース」 が Ivar Jacobson によるものであることが述べられている。

*2:Web 業界だと明確な要求定義がないことが多いように思う。

*3:UML についてだったり、モデリングツールの使い方だったりの説明などがある。

*4:戦略的には境界付けられたコンテキストをどう分けるかというところが一番難しいと思っているけど、ここでは戦術的な話をしている。