『ドメイン駆動設計入門 - ボトムアップでわかる!ドメイン駆動設計の基本』を読んだ

どんな本か

ドメイン駆動設計についてかなり噛み砕いて説明された本です。

難しく抽象的な説明はあまりなく、具体的なパターンを説明しながらドメイン駆動設計とはどういうものか語られています。

一つの章で一つのパターンが説明されているので読み進めやすく、サンプルコードも豊富に書かれているので具体的なサンプルからなにかヒントを掴みたい人におすすめです。

目次は以下の通り。

  • 1章 ドメイン駆動設計とは
  • 2章 システム固有の値を表現する「値オブジェクト」
  • 3章 ライフサイクルのあるオブジェクト「エンティティ」
  • 4章 不自然さを解決する「ドメインサービス」
  • 5章 データにまつわる処理を分離する「リポジトリ」
  • 6章 ユースケースを実現する「アプリケーションサービス」
  • 7章 柔軟性をもたらす依存関係のコントロール
  • 8章 ソフトウェア・システムを組み立てる
  • 9章 複雑な生成処理を行う「ファクトリ」
  • 10章 データの整合性を保つ
  • 11章 アプリケーションを1から組み立てる
  • 12章 ドメインのルールを守る「集約」
  • 13章 複雑な条件を表現する「仕様」
  • 14章 アーキテクチャ
  • 15章 ドメイン駆動設計のとびらを開こう
  • 付録 ソリューション構成

目的

システムやアプリケーションの設計力をつけて、より継続的に開発しやすく変更にも強いシステムを開発できるようになりたいと思い読み始めました。

ドメイン駆動設計にこだわらないですが、よりシステム設計に関する文書をたくさん読み込むことで、自分が持つパターンを増やして表現しやすくしたいという思いもあります。

感想

ドメイン駆動設計がドメインの知識に焦点をあてて設計する手法で、そのためにドメインやユースケースをどのようにコードで表現するのかが具体的で分かりやすかったです。

一方で、どのようにパッケージを分けるのか、どういうアーキテクチャを採用するとすると良いかに関してはページがあまり割かれておらず、他の本で補完する必要があるなと思いました。

また、エリック・エヴァンスのドメイン駆動設計と実践ドメイン駆動設計も読んで、自分の中で腹落ちするまで時間かける必要がありそうです。

読書ノート

  • ドメイン駆動設計
    • ドメインの知識に焦点を当てた設計手法
  • ドメイン
    • ソフトウェア開発におけるドメインは、「プログラムを適用する対象となる領域」を指す
  • モデル
    • 現実の事象あるいは概念を抽象化した概念
  • ドメインモデル
    • ドメインの概念をモデリングして得られたモデル
  • 値オブジェクト
    • 不変である
    • 交換可能である
    • 等価性によって比較される
    • 独自のふるまいを定義できる
  • エンティティ
    • 可変である
    • 同じ属性であっても区別される
    • 同一性によって区別される
    • ライフサイクルが存在し、そこに連続性が存在するかが判断基準
  • ドメインサービス
    • 値オブジェクトやエンティティに記述すると不自然になるふるまいが存在するが、それを解決するためのオブジェクト
    • 記述するのは「不自然なふるまい」に限定すること
  • ドメイン貧血症
    • ドメインオブジェクトに本来記述されるべき知識や振る舞いがドメインサービスやアプリケーションサービスに記述され、語るべきことを何も語っていないドメインオブジェクトの状態のこと
  • リポジトリ
    • 責務はドメインオブジェクトの永続化や再構築を行うこと
    • 重要なのはインスタンスを何らかの手段によって保存するということ
  • アプリケーションサービス
    • ユースケースを実現するオブジェクト
    • ドメインオブジェクトのふるまいを呼び出す役目を持つ
    • ドメインオブジェクトのタスク調整に徹するべき
    • ドメインのルールは記述されるべきではない
  • 凝集度
    • モジュールの責任範囲がどれだけ集中しているかを測る尺度
    • 凝集度を高めると、モジュールがひとつの事柄に集中することになり、堅牢性・信頼性・再利用性・可読性の観点から好ましい
    • 凝集度を高めるにはクラス分割が簡単な対処法
  • サービス
    • クライアントのために何かを行うもの
    • サービスはものごとではなく、活動や行動であることが多い
    • サービスは自身のふるまいを変化させる目的で状態を保持しない
    • 状態を一切もっていないことを意味しない
      • 自身のふるまいを変化させる目的の状態ではない
  • 依存関係逆転の原則(Dependency Inversion Principle)
    • A: 上位レベルのモジュールは下位レベルのモジュールに依存してはならない、どちらもモジュールも抽象に依存すべきである
    • B: 抽象は、実装の詳細に依存してはならない。実装の称しあは抽象に依存すべきである。
    • 依存の方向を制御し、すべてのモジュールが抽象へ依存するように制御する
  • ファクトリ
    • 求められることは複雑なオブジェクトの生成処理をオブジェクトとして定義すること
    • オブジェクトの生成に関わる地s樹がまとめられたオブジェクト
  • ユニットオブワーク
    • あるオブジェクトの変更を記録するオブジェクト
    • オブジェクトの読み取り動作を行う際にインスタンスの状態を記録する。読み取られたオブジェクトの変更や削除はユニットオブワークにつ打ちしない限りデータストアへ反映されることがない
    • コミット処理が呼び出されると、そこまでの変更処理をデータストアに対して適用する
    • このパターンを適用すると、永続化の対象となるオブジェクトの作成・変更・削除といった動作はすべてユニットオブワークを通じて行うようになる
  • 集約
    • 関連するオブジェクト同士を線で囲う境界として定義される
    • 集約の外部から境界の内部のオブジェクトを操作してはならない
    • 集約を操作するための直接のインターフェースとなるオブジェクトは集約ルート(AR: Aggregate Root)とよばれるオブジェクトに限定される
    • 外部から内部のオブジェクトに対して直接操作するのではなく、そのオブジェクトを保持するオブジェクトに依頼する形を取る
    • 「変更の単位」で集約を区切る
  • 仕様
    • 仕様はあるお部会j区とがある評価基準に達しているかを判定するオブジェクト
    • 値オブジェクトやエンティティにリポジトリを操作させないために取られる手段は仕様と呼ばれるオブジェクトを利用した解決
    • 仕様はれっきとしたドメインオブジェクト

2021年8月2日 ~ 9日 振り返り

久しぶりの振り返り。 毎週やっていたけど、やらなくなると途端に面倒になるので継続するには習慣化が大切だなと思いました。

仕事

外部APIから情報取得していた処理を保存しているキャッシュから読むように修正

PoCを行うため、とある外部サーバーに登録してある情報をリクエストして返すような簡単なAPIが既に実装されていました。 ただ問題があって、この外部サーバーの秒間リクエスト数の制限がかなり厳しいので、それらの情報はキャッシュして使う必要がありました。

外部サーバーに登録済みの情報をRedisにキャッシュとして保存するLambdaは先々週のスプリントで実装したので、先週のスプリントでは既存のコードを書き換えてキャッシュ(Redis)からデータを読むように処理を修正するというタスクを行いました。

キャッシュを読む処理自体はもともとすぐに既存のコードに置き換えやすいように実装していたのでサクッとタスクを完了にはできました。 また、今のプロジェクトはマルチプロジェクト構成になっており、依存関係が複雑になりつつあったのでそこの整理もできたのでなかなか楽しかったです。

Datadogエージェントのアップデート

現在開発しているアプリケーションはEC2上で動いているのですが、ミドルウェア等のアップデートの際はゴールデンAMIを作成して検証環境にデプロイし、アプリケーションの動作に問題ないか確認する必要があります。

Datadogのエージェントは結構早くバージョンが上がるので2,3ヶ月に一回バージョンを上げては動作確認しています。

動作確認等は地味に時間食うのでここらへんは自動化したいところです。

他の会社さんとかは、ミドルウェア等をアップデートした際にアプリケーションに影響ないかの確認はどのようにしているんだろうか。。。

参考にできそうなものがあればどんどん取り入れてプロジェクトを良くしていきたい。

プライベート

やったこと

AWS MLSの試験を受けた

本当は7月30日に受ける予定だったんですが、試験勉強が全く足りないなという判断で8月6日に受けてきました。

合格はできたんですが、継続的な勉強は必要そうです。

blog.ryskit.com

AtCoder Boot camp for Beginners

最近、毎日1問ずつ解くようにしています。

アルゴリズムを今までちゃんと勉強してこなかったので、もっと学び必要があるなと感じてやっています。

少し前までC++で書いていたんですが、Scalaで書きたいなと思い最近はずっとScalaで書いています。

https://kenkoooo.com/atcoder/#/training/Boot%20camp%20for%20Beginners

「Akka Essentials with Scala | Rock the JVM」 コースを修了した

仕事でPlayframeworkやScalaを使用して仕事をしていますが、Akkaをちゃんと学んだことがなかったのでもっと知りたいなと思って始めました。

解説されているAkkaのバージョンが2.5と最新ではないのですが、実際にコードを書きながらAkkaについて学べるのは大変おもしろかったです。

www.udemy.com

次は、「Akka Persistence with Scala」というコースをやってみようと思います。

www.udemy.com

AWS Certified Machine Learning - Specialtyに合格した

本日、2021年8月6日(金) に AWS Certified Machine Learning - Specialty(MLS) に合格しました🎉🎉

スコアとセクションごとのパフォーマンス

今回受けたMLSのスコアは 835 で何とか8割を超えることができました!

f:id:ryskit:20210806213414p:plain

そして、セクションごとのパフォーマンスは画像のとおりです。

f:id:ryskit:20210806213423p:plain

実際に試験を受けてみた感想

今回MLSに何とか合格することができましたが、正直これを合格できただけでAWSの機械学習関連のサービスを使いこなしてプロダクション環境に構築できるかと言われればノーです。

試験勉強中も正直意味がよく分からんなと思いつつやっていましたし、試験問題解いてるときもやっぱり分からんなと思ってたので、合格できるなんて思ってもなかったです。

機械学習で使われるアルゴリズムはいろいろあって、どういうことを期待するのか、データの特徴によっても最適なアルゴリズムも違うので、試験のためにこれらを理解するのもなかなか大変でした。

あと、そもそもモデルの学習などを一度も試さなかったし、実際にコードを書いてアプリケーションを作ったわけでもないので理解できているとは到底言える状態ではないなというのが本音です。

MLS合格したけどどうするよ?

業務で機械学習を使うわけでもなければ、MLエンジニアでもないのでがっつりのめり込んでやろうとは思ってないのですが、やったことのない分野は勉強したりするのは新鮮で面白みがあります。

せっかくMLSも合格したので、勉強も継続して続けて知識を広げたいなと思っています。

あと、合格してすぐ近くの紀伊国屋に行って、こんな本を買ってみました。

この本を読んで実際にアプリケーションを構築したり、簡単なレコメンドサービスなども作って試してみたいなとも思っています。

最後に

ひとまずAWS 9冠になりました。

残るは、Advanced Networking - Specialtyと Cloud Practitioner だけです。

9月茉までには11 冠まで行けるように頑張ろうと思います!

AWS Certified Data Analytics - Specialtyに合格した

本日、2021年7月9日(金)に、AWS Certified Data Analytics - Specialtyに合格しました🎉🎉

スコア・セクションごとのパフォーマンス

今回受験したData Analyticsのスコアは 821 でひとまず8割を超えたので良かったです。

f:id:ryskit:20210709205023p:plain

そして、セクションごとのパフォーマンスは画像のとおりです。

f:id:ryskit:20210709205615p:plain

前回受けたDBSのように 改善が必要 と言われることがなくてこちらもひとまず安心しました。

本当に改善は必要ない?

そんなことはありません。

Data Analyticsで出題された分野というのは、正直なところ仕事で関わりを持つことが少ないです。

そして、AWS GlueやAmazon EMRなどのETLサービスやビッグデータフレームワークを実際に構築して運用した経験がありません。

そのため、今の能力としては試験勉強のためにとりあえず知識を頭に詰め込んだだけで、実務に耐えうる知識や経験は不足している状態です。

なので、今後の課題・改善ポイントとしては、

  • 実際に出題された範囲のAWSサービスを構築して動かしてみる
  • AWS GlueやAmazon Kinesis、Elasticsearchであれば今の仕事でも使いどころはあると思うので、そのためにもっと知識を深める・使用用途を考える
  • Athenaはログの分析用途で使っているので、もっと踏み込んだ使い方をする

などなど、やっていこうかなと思います。

今回どうやって勉強した?

今回のDAS取得にあたり勉強のために読んだ書籍や教材を紹介します。

読んだ書籍

そもそもデータレイクやデータウェアハウスの違いもよく分かっていなかったので、AWSの中の人が書いた以下の本をサクッと読みました。

前半はデータレイクの概念や特徴、機能などを基本から解説してくれているので、大まかに理解するにはすごく良かったです。

後半は実際にAWSサービスを立ち上げて動かすようなチュートリアルがありましたが時間がなかったのでスキップしました。時間作ってやろうと思います。

教材

Udemyのtutorial dojoからリリースされているData Analyticsの模擬試験です。

こちらを3周ほどやりました。

とにかく問題を解いて、間違った箇所や理解が浅い問題に関しては、参考リンクとしてリストアップされているAWSの公式ドキュメントを読み漁りました。

とりあえず、AWSサービスの公式ドキュメントを読めば答えは書いているので、頑張って理解して覚えるのが早いです。

www.udemy.com

最後に

やっとAWS 8冠まで来ました!

残るは機械学習と高度なネットワーキング、クラウドプラクティショナーの3つです。

次は機械学習を7/30 (金)に受験予定なのですが、機械学習の知識はゼロの状態からスタートです。

なかなか理解するのが大変そうな分野ですが、準備期間としては3週間ほどあるので地道に頑張っていこうと思います!

AWS Certified Database - Specialtyに合格した

本日、2021年6月25日(金)にAWS Certified Database - Specialtyに合格しました!

f:id:ryskit:20210625224807p:plain

ちょっと待て

今日の午前中に試験を受けている最中、データベース固有の問題に関して記憶が曖昧で合ってるかな?と思いながら回答した問題が多々ありました。

まぁでも概ね自信を持って回答したつもりだったのですが、スコアは 774 と受かったとはいえさすがに低い。

試験結果の内訳を見ても、記憶が曖昧だなと思った問題はやはり間違っている傾向があります。

f:id:ryskit:20210625225247p:plain

「分野1のワークロード固有のデータベース設計」ここは明らかな改善ポイントです。合格を喜びつつ、ダメなところは反省して再度勉強し直します。

ちなみにどうやって学習した?

まずは、最近出版された『AWS認定データベース - 専門知識』の対策本を読み込みました。

また、最後の章にある練習問題を解いて、解説もしっかり読んで理解しようとしました。

次に、もっと問題を解きたかったので以下を購入。

FullTestの問題が一つとDBSの出題範囲のセクションごとの練習問題を解きました。

Tutorial Dojoにも解説が記載されていて、AWSの公式ドキュメントのリンクも添えられているので、とにかく記載されているものにはすべて目を通すというやり方をしていました。

www.udemy.com

学習方法に関しては参考になれば幸いです。

最後に

今週は仕事でシステム障害対応であまり勉強時間を取れませんでしたが、なんとか合格できて良かったです。

www.credly.com

あと、Specialtyは DAS、MLS、ANS、最後にCloud Practitionerを受ければAWS資格11冠を取得できるので、そこまでは突っ走ろうと思います。

ちなみに、DBS合格してオフィスに到着してすぐに、次のDASの試験の申し込みをしましたw

ダラダラ試験勉強しても忘れていく方が多そうなので、短期集中で頑張るぞ!

AWS CDK + Step Functionsで入門してみた

AWS資格取得のためにドキュメントを読んだりしてStep Functionsは知っていましたが、仕事で使う機会もなかったのでとりあえず触っておくかということで 、AWS builders.flashに公開されている「Step FunctionsでFizzBuzzを実現する」というのをやってみました。

aws.amazon.com

AWS Serverless Application Model (SAM) を使ってデプロイできるように完成済みのプログラムが提供されていましたが、コンソールでポチポチ試したかったので、jsonベースの構造化言語である Amazon States Language(ASL) を記事通りに写経しました。

やってみて分かりましたが正直ASLを手で書くのはツラいw

「これをどうにか楽に書きたい、できればLambdaを組み合わせても試してみたい」と社内Slackでつぶやいたところ、以下のブロク記事を紹介してもらいました。

紹介

前置きが長くなりましたが、紹介したかったのはこちら!

dev.classmethod.jp

AWS CDK + Step Functions + AWS Lambdaの構成で、様々なステートを用いたステートマシンの書き方が紹介されています。

ステート

以下に関しては、実際にAWS CDKで定義してステートマシンを作成するところまでできるので、真似して書くだけで試せるのがありがたいところです。

  • Task
  • Wait
  • Parallel
  • Pass
  • Choice
  • Success *こちらは説明のみ
  • Fail *こちらは説明のみ

docs.aws.amazon.com

実践

ブログ記事を参考にステートを組み合わせてステートマシンを作ってみました。

リポジトリにあるREADMEにはセットアップ方法・ビルド・デプロイ方法を記載しています。

github.com

最後に

AWS CDKでStep Functionsを書くのは書き心地が最高に良いので、引き続きいろいろ試してみたいと思います。

Amazon ECSのrun-taskでタスク実行が失敗する

Go言語で書いたプッシュ通知システムをAmazon ECS上で構築・運用し始めてから安定稼働していました。

ある日、朝会でチームメンバーとシステムのメトリクスを見ていたら、SQSのメッセージが前日から滞留している不穏なメトリクスを確認しました。

その障害内容と調査内容、原因、対応内容について備忘録として書いていきます。

プッシュ通知システムの前提知識・その他

  • Amazon ECS on Fargate上で構築・運用(Fargate Platform Versionは1.4.0)
  • Go言語で実装
  • プッシュ通知システムはいくつかの種類のECSタスク(以下、タスクと呼ぶ)で構成されている
    • その中でいくつかのタスクは他のタスクから Amazon ECSの run-task APIをコールして実行されている
  • 障害が発生した当日に、別チームがVPCのデフォルトセキュリティグループを削除する対応を行っていた

障害内容

Amazon ECSの run-task APIをコールして実行されるタスクが起動に失敗しており、ステータスが PENDINGSTOPPED となっており、メッセージの処理ができず、プッシュ通知を送信できていませんでした。

調査内容

エラー内容を確認する

タスクのステータスが PENDINGSTOPPED の状態ではアプリケーションはログを出力しませんが、AWSの管理コンソールからタスクの停止理由を確認することができます。

確認すると以下のように表示されていました。

ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve secrets from ssm: service call has been retried 5 time(s): RequestCanceled: request context canceled caused by: context deadli...

ドキュメントでエラーコードの内容を確認すると、以下のように記載がありました。

docs.aws.amazon.com

このエラーは、コンテナの起動に必要なリソースやタスクの所属先のリソースを作成またはブートストラップすることに Fargate エージェントが失敗した場合に発生します。 このエラーは、プラットフォームバージョン 1.4 以降を使用している場合にのみ発生します。

エラー内容やドキュメントを見て、イメージの取得やネットワーク周りが原因だろうと考えました。

アプリケーションコードを確認する

run-task を実行する場合に以下のようなインプットを渡して実行しています。

func (c ECSClient) NewRunTaskInput(clusterName string, arn string, subnets []string, securityGroupId *string, count int64) ecs.RunTaskInput {
    input := ecs.RunTaskInput{
        Cluster:        aws.String(clusterName),
        PlatformVersion: aws.String("1.4.0"),
        Count:          aws.Int64(count),
        TaskDefinition: aws.String(arn),
        NetworkConfiguration: &ecs.NetworkConfiguration{
            AwsvpcConfiguration: &ecs.AwsVpcConfiguration{
                Subnets: aws.StringSlice(subnets),
            },
        },
        LaunchType: aws.String("FARGATE"),
    }
    if securityGroupId != nil {
        input.NetworkConfiguration.AwsvpcConfiguration.SecurityGroups = []*string{securityGroupId}
    }
    return input
}

今回障害が発生してしまったタスクに関しては、セキュリティグループのIDを引数として渡していないのを確認しました。(それでも今までは動作していた)

おそらくこのSecurityGroupを指定していないことが原因だろうと思い、アウトバウンドを許可したセキュリティグループのIDを渡すように書き換えたところ、検証環境で正常に動作することが確認できました。

原因

いくつかの要因が重なり、障害が発生しました。

  • 障害発生当日に別チームがセキュリティリスクを減らすため、VPCのデフォルトのセキュリティグループを削除する対応を行っていた
  • 障害が発生したプッシュ通知システムのタスクでは特定のセキュリティグループを指定せずに実行していた

特定のセキュリティグループIDを指定せずに run-task を実行すると、どうなるのか?

aws-sdk-goのコメントに記載がありました。

aws-sdk-go > service > ecs > api.go

// The security groups associated with the task or service. If you do not specify
// a security group, the default security group for the VPC is used. There is
// a limit of 5 security groups that can be specified per AwsVpcConfiguration.
//
// All specified security groups must be from the same VPC.
SecurityGroups []*string `locationName:"securityGroups" type:"list"`

特定のセキュリティグループIDがある場合はそれを使用し、指定がない場合はVPCのデフォルトのセキュリティグループを利用して実行されると明記されています。

つまり、今までVPCのデフォルトセキュリティグループで実行されていたタスクは、デフォルトのセキュリティグループが削除されたことでアタッチできず、ECRと通信ができなくなりイメージを取得できなくなった。

その結果、タスクの実行に失敗していました。

対応内容

run-task 実行時のインプットにセキュリティグループIDを指定するよう修正しました。

まとめ

  • ちゃんとライブラリのコードを読もう
  • run-task 実行時のインプットには必ず適切なセキュリティグループIDを指定しよう
    • VPCのデフォルトのセキュリティグループを使っちゃダメ

Serverless Framework内部で使用しているwsライブラリの脆弱性対応を行う

とあるシステムでServerless Framework(以下、serverlessと呼ぶ)を使っているのですが、Githubのリポジトリ上で Dependabot alert が出ていたので、それの調査と対応の内容を書き留めておきます。

前提

  • serverless@2.41.0 を使用

脆弱性の内容を確認する

npm audit コマンドを実行して脆弱性の内容を確認します。

$ npm audit

                       === npm audit security report ===

# Run  npm update ws --depth 7  to resolve 4 vulnerabilities
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate      │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ ws                                                           │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ serverless [dev]                                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ serverless > @serverless/components >                        │
│               │ @serverless/platform-client > ws                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1748                            │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate      │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ ws                                                           │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ serverless [dev]                                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ serverless > @serverless/enterprise-plugin >                 │
│               │ @serverless/platform-client > ws                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1748                            │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate      │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ ws                                                           │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ serverless [dev]                                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ serverless > @serverless/components >                        │
│               │ @serverless/platform-client-china > @serverless/utils-china  │
│               │ > socket.io-client > engine.io-client > ws                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1748                            │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate      │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ ws                                                           │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ serverless [dev]                                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ serverless > @serverless/components >                        │
│               │ @serverless/platform-client-china > ws                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1748                            │
└───────────────┴──────────────────────────────────────────────────────────────┘


found 4 moderate severity vulnerabilities in 668 scanned packages
  run `npm audit fix` to fix 4 of them.

脆弱性の詳細

脆弱性の詳細情報を見ると、以下のように記載がありました。

Overview

In ws before versions 6.2.2 and 7.4.6 there is a ReDOS vulnerability.

Impact

A specially crafted value of the Sec-Websocket-Protocol header can be used to significantly slow down a ws server.

Patches

The vulnerability was fixed in ws@7.4.6

  • ライブラリ ws@7.4.6 より前のバージョンで ReDOS の脆弱性がある
  • Sec-Websocket-Protocolヘッダに特別な細工を施した値を使用すると、wsサーバの速度を大幅に低下させることができる
  • ws@7.4.6 で脆弱性は修正された

対応内容

serverless@2.44.0 にアップデートしました。 serverless@2.44.0package.json の内容を確認すると、脆弱性が修正された ws@7.4.6 を指定しているのを確認できます。

https://github.com/serverless/serverless/blob/v2.44.0/package.json

2.44.0 より前のバージョンでは ws@7.4.5 を使用しているため、2.44.0 までアップデートするのが良さそうでした。 脆弱性の内容を見る限り、AWS Lambdaのデプロイにserverlessを使っているだけだったのでそこまで影響がない内容でしたが、アラートを放置するのも良くないのでサクッと対応しました。

最後に

ライブラリはこまめにアップデートしていきたいですね!

『イラストでわかるDockerとKubernetes』を読んだ

どんな本か

この本はタイトルどおりイラストを交えてDocker・Kubernetesについて分かりやすく説明された本です。

構成は以下の通りで、ページ数も130ページ程度なのでサクッと読めるようになっています。

  • 第1章 コンテナ技術の概要
  • 第2章 Dockerの概要
  • 第3章 Kubernetesの概要
  • 第4章 コンテナランタイムとコンテナの標準仕様の概要

目的

普段はローカル環境でDockerを使ったり、本番環境にAWSのAmazon ECSを導入してみたりはしましたが、Kubernetesは全くさわってきませんでした。

最近ふとKubernetesを実際にさわってみて、仕事に活かしたりできないかと思い、この本を手に取りました。

「まずはKubernetesやその周辺技術の概要をざっくりで良いので掴みたい」というのが目的です。

感想

Kubernetesの仕組みやより踏み込んだコンテナランタイムや標準仕様などの説明が分かりやすく、個人的にすごく面白かったです。 特にkubelet、CRIランタイム、CNIプラグイン、OCIランタイムらへんはもっと深堀りしてみたい。

要所要所にイラストや実際に実行したコマンドの結果を載せてくれているので、手を動かさずに読み進めても雰囲気で理解できますが、一度Kubuernetesの環境構築をしたほうが良さそうです。 あと、実際に使う場合、Amazon EKSやGoogleのGKEを利用すると思うので、そこらへんの構築も試して手順として残しておきたいところ。

以下の本は初めてKubernetesさわる人が読むと良いらしいとTwitterで見たので、次はこれを読みつつ手を動かしていこう。

あと、以前聞いたfukabori.fmのdocker/kubernetes周りのネットワーキングの話も改めて聴く。

fukabori.fm

eh-career.com

読書ノート

第1章 コンテナ技術の概要

  • コンテナもその実態はプロセスで、異なるのはOSカーネルの機能を用いて通常のプロセスよりもより強く環境が隔離されている点

第2章 Dockerの概要

  • コンテナイメージの作成
    • Dockerfile: コンテナの作成手順書
    • コンテキスト: コンテナに格納するプログラムなどのファイル群
  • コンテナイメージのレイヤ構造
    • レイヤ(変更差分)
    • コンテナは変更差分を集めたもの
  • コンテナのレイヤ構造は、その実行時にも保たれている
  • コンテナのレイヤ構造は、コンテナ同士で可能な限りデータの重複を作らないようにしつつ、お互いの環境が影響し合わないようにする
  • あるイメージからコンテナを複数実行する場合でも、それらコンテナ同士で共通のレイヤ群はコピーされることなく、共有される
  • Storage Driverは、コンテナを構成する各レイヤをホスト上で保持しており、それらを重ね合わせてコンテナのルートファイルシステムとして利用できるようにするなど、レイヤ群の管理を担うコンポーネント
    • 実装は、aufs, btrfs, overlayfsなど、さまざまなファイルシステムなどの技術が用いられている
  • overlayファイルシステムはLinuxカーネル3.18から導入され、あるディレクトリを別のディレクトリへ重ね合わせ、その重ね合わせた結果をマウントすることができるファイルシステム
  • マシン上ではDockerデーモン(dockerd)が起動しており、dockerコマンドからDocker APIと呼ばれるHTTP API経由で支持を受ける
  • Dockerデーモンは、コンテナの実行だけでなく、そのイメージやネットワーク、ストレージなどコンテナのライフサイクル全体にわたる管理を担っている
  • ホストから隔離された実行環境をコンテナとして作り出したり、それを直接操作するのはDockerデーモンではなく、OCIランタイム(低レベルランタイム)と呼ばれるソフトウェアが担当する

第3章 Kubernetesの概要

  • Kubernetesに対して「アプリケーションやそれを構成するコンテナ群はこういう状態であるべき」というような理想状態をYAMLやJSON形式の「マニフェスト」と呼ばれる設定ファイルの形で宣言すると、それを実現・維持するための具体的な作業をKubernetesがよしなに行ってくれるというもの。
  • Kubernetesは管理情報をHTTP APIで公開しており、ユーザはそのAPIの操作を通じて前節で述べたような理想状態の宣言や、アプリケーションに関する状態の確認などを行う
    • このAPIを参照・操作しながら、ユーザーが宣言した理想状態を維持するために具体的な管理作業を行うコンポーネント群は「コントローラ」と呼ばれる
  • コンテナ群を実行するマシンの集合を「クラスタ」と呼ぶ
  • 各コンテナが実行されるマシンは「ノード」と呼ぶ
  • ノード上では複数の「ノードコンポーネント」が稼働し、そのノード上のコンテナ群の実行管理やイメージの管理、通信の管理などを行う
  • Kubernetesクラスタ全体の管理を担うコンポーネントは「コントロールプレーン」と呼ぶ
    • コントローラーやコンテナのデプロイ時にそのスケジューリングを行うコンポーネントなどが含まれる
  • Kubernetesにおいて最も基本的なデプロイ単位は関連する複数のコンテナ群を1つにまとめた「Pod」と呼ばれているもの
  • 1つのPodに含まれるコンテナ群は同一のノード上にデプロイされ、ネットワーク・インターフェースやストレージの割当などを共有する
  • KubernetesはIPアドレスをPodごとに払い出すため、Pod同士はそれぞれのIPアドレスを使って通信できる。またPod内のコンテナはlocalhostで通信できる。
  • Pod群のデプロイにまつわるリソース
    • Deployment
      • Pod群を一定数を維持しながらクラスタ上に展開するのに有用なリソース
      • セルフヒーリング
        • 障害の発生などによりクラスタ全体で設定された数のPodが正常に稼働していない場合に、自動敵に新たなPodを実行し復旧を試みる機能
      • スケーリング
    • StatefullSet
      • ステートフルなコンテナの実行をサポート
      • Podにはインデックス0を含むPod名と固有のボリューム)PersistentVolume)が付与される
    • DeamonSet
      • 各ノード上にPodが1つずつ実行されている状態を維持するリソース
    • Job
      • 単発に実行するユースケースに有効
    • CronJob
      • Conフォーマットで実行開始時間や定期実行など設定可能
  • Podやコンテナは、長期的な状態を持たないステートレス、またはエフェメラルな(揮発性の)実行単位と言われる
  • 設定項目・秘匿項目を独立して管理できるように、ConfigMapやSecretというリソースがある
    • コンテナ内の環境変数としてみせる
    • コンテナ内のファイルシステムに読み取り専用でマウントし、ファイルとして見せる
  • Service
    • あるサービスを提供する複数のPodに共通のIPアドレスを付与し、1つの「サービス」のようにアクセスできるようになる
    • Pod自体にもIPアドレスが付与されているが、Serviceが必要なのはなぜか?
      • Kubernetes上ではPodのIPアドレスは頻繁に変わり得るという点が挙げられる
    • リソースの種類
      • NodePort
        • 各ノード上のポートをクラスタ外に公開し、そのポートを通じた通信を、そのServiceを構成するPodのいずれかにロードバランスする。
      • Load Balancer Service
      • Ingress
        • L7ロード・バランシングの機能を提供する
        • 1つのアプリケーションを複数のServiceを用いて構成し、URLのホスト名やパスのルールベースで、実際にアクセス先として用いる
  • kubelet
    • ノード上のPod群の実行管理を行う
  • CRIランタイム
    • イメージの取得やPod・コンテナ群の管理を行う
  • CNIプラグイン
    • Podの作成はCRIランタイムが担当するが、そのPodにIPアドレスを払い出し、仮想的なNICをPodに付与するのはCNIプラグインが行う
      • flannel, Calico
  • OCIランタイム
    • CRIランタイムなど高位のランタイムから指示を受け、ホストから隔離された実行環境をコンテナとして作り出しその直接操作の手段を与える

第4章 コンテナランタイムとコンテナの標準仕様の概要

  • containerd
    • Dockerはコンテナ実行のためにcontainerdを内部で使用している
    • containerdはCRIを実装しているため、Kubernetes環境ではcontainerdを単体のCRIランタイムとして用いることができる
    • containerdが低レベルランタイムを呼び出すときは、shimと呼ばれるバイナリコンポーネントを介する。低レベルランタイムは、それぞれの持つアーキテクチャに合ったshimを実装し、それをcontainerdにプラグインすることで、containerdを通じてそれら低レベルランタイムを操作できるようになる。
  • 低レベルランタイムはOCIで定義されたインターフェースを通じて高レベルランタイムから指示を受け、ホストから隔離された実行環境を作成したり、その操作手段を提供する
    • コンテナ(ホストから隔離された実行環境)の作り方は1つではなく、低レベルランタイムによってさまざなバリエーションがある。
    • runc, gVisor, Kata Containersas
  • OCI Runtime SpecificationはOCIによって策定されている低レベルランタイムの仕様のこと
  • Filesystem bundle
    • ファイル群が格納されたディレクトリ
      • コンテナのルートファイルシステム
      • コンテナ実行環境の設定ファイル
  • コンテナのライフサイクル
    1. Filesystem bundleをOCI準拠のランタイムに指定し、コンテナを作成
    2. コンテナの実行を開始
    3. コンテナ内のアプリケーションが終了する
    4. コンテナを削除する
  • コンテナに対して可能な操作
    • create
    • start
    • kill
    • delete
    • state
  • OCI Image Specification
    • マニフェスト
    • レイヤ
    • コンフィギュレーション
    • インデックス(optional)
  • namespace
    • あるプロセスから操作可能なリソースを、その他のプロセスから隔離できる機能
    • PIC namespace プロセス群の管理
    • Mount namespace マウントポイントリストの隔離
    • Network namespace ネットワーク関連のリソースの隔離
  • cgroup
    • プロセスが使用可能なリソースについて、たとえば以下を含むさまざまな設定を施せる機能
    • デバイスファイルへのアクセス権限
    • プロセスから利用可能なCPUの制限
    • プロセスが利用可能なメモリ使用量の制限

2021年5月10日 ~ 17日 振り返り

GWと先週の振り返り。

仕事

CircleCIの設定ファイルの修正

概要

CircleCI上でサブプロジェクトをsbt assemblyするよう設定ファイルを修正しました。

詳細

今進めているプロジェクトのコードは一つのディレクトリに複数のサブプロジェクトが入っており、それらをマルチプロジェクトビルドしています。

その中にはEC2上で実行されるアプリケーションやAWS Lambdaとして起動されるものも含まれています。

CI上ではこれらサブプロジェクトのビルドやテストを実行していたのですが、AWS Lambdaとして起動されるサブプロジェクトのコードはCI上ではビルドする設定が記述されていませんでした。

そのため、あるライブラリをバージョンアップしてdevelopブランチでは動いていたため、masterブランチにマージしてリリースしようとした際に、sbt assemblyコマンドでjarを生成する段階でエラーが発生しました。

エラー内容としては、sbt-assemblyを使用してjarを生成する際に同名ファイルがあったためエラーになっていました。

気づかなかった原因としては、違うタイミングでsbtのMergeStrategyが修正されており、masterブランチにそれらが含まれていなかったということです。

ただ、CIでsbt assemblyをするよう設定をしておけばもっと早い段階で気づけたため、今回の修正対応を行いました。

解決策

.circleci/config.yml に以下のような設定を追加

      - run:
          name: run lambdaXXXX/assembly
          command: |
            if [ "$CIRCLE_NODE_INDEX" == "5" ]; then cat /dev/null | sbt 'set lambdaXXXX/test in lambdaXXXX/assembly := {}' lambdaXXXX/clean lambdaXXXX/assembly; else true; fi
          no_output_timeout: 30m

CIRCLE_NODE_INDEX はCircleCIが定義している環境変数で、並列実行しているジョブのIndexを指します。

circleci.com

なぜ、以下のような書き方をしているかというと、他のジョブでテストを実行するよう設定しているため、ここではsbt assembly時はテストの実行を無視したかったからです。

sbt 'set lambdaXXXX/test in lambdaXXXX/assembly := {}' lambdaXXXX/clean lambdaXXXX/assembly

プロジェクトに新しいAPIを追加

概要

PoCプロジェクトで利用するAPIを新規で追加しました。

詳細

あまり詳細に書けないのであれですが、以下のライブラリとかプロジェクトで使ってて便利だしバリバリ使ってるので、コードリーディングしないとなーと感じてます。

github.com

github.com

github.com

プライベート

読書

『雰囲気でOAuth2.0を~』等

Auth屋さんのOAuth2.0・OIDC関連の本を読みました。

OAuth2.0とOIDCがいまいち分からない人やOAuthを利用したり実装したりする場合に気をつけるポイントを押さえたいと思っている方はぜひ読んでみてください!

かなりおすすめです!

blog.ryskit.com

blog.ryskit.com

blog.ryskit.com

やったこと

GW中や休日は FP in Scalaをやり直してました。

tatsu-zine.com

前回やったときはPart1 までやったのですが、今回はすべてやり抜こうと思い少しずつ進めています。

今はやっと第8章のプロパティベースのテストに入りました。

Part2 から難しく感じて、進みが遅いですがやり抜きたい!

github.com