diff --git a/404.html b/404.html index c73dcf20..bfd94559 100644 --- a/404.html +++ b/404.html @@ -8,13 +8,13 @@ - + -

404

That's a Four-Oh-Four.
+ - + diff --git a/about.html b/about.html index b0679bca..f374b658 100644 --- a/about.html +++ b/about.html @@ -8,7 +8,7 @@ - + @@ -18,7 +18,7 @@ GitHub (opens new window)

# IIJ Bootcampとは

# 目的

IIJ BootcampIIJ (opens new window) で開催しているハンズオン勉強会です。 各技術が誕生した経緯・歴史、ほかの技術と比較といった知識を得るためのきっかけとして、さまざまな言語・フレームワーク・ツールに触れて実際に動かすハンズオンを行っています。 -カリキュラムにはハンズオンだけでなく、「overview」として技術ジャンルの全体像や歴史などを紹介する回も設けています。

このサイトではIIJ Bootcamp用に社内で作成したハンズオン資料をCC BY-SA (opens new window)ライセンスで公開しています(GitHubリポジトリはこちら (opens new window))。

カリキュラムの全体像や資料へのリンクはトップページをご覧ください。

# 資料の構成

ハンズオン資料は以下のカテゴリに分かれています。

カテゴリ 概要
開発系 開発に必須となるGitやdockerのハンズオン
CI/CD + 構成管理 CIサーバのハンズオンとansibleやk8sによるアプリケーションデプロイ
データベース MySQL, MongoDB, Redis ハンズオン
Webサーバ構築 Apacheやnginxを使ったWebサーバの構築
サーバサイドアプリケーション DjangoやJava、golangを使ったサーバサイドアプリケーションの構築
フロントエンド 流行りのWebフレームワークを一通り触ってみる
セキュリティ Webにおけるセキュリティについてと、脆弱なサーバを作らないためのハンズオン

# 資料の利用と修正

資料は CC BY-SAライセンス (opens new window) で公開されており、勉強会などでの2次利用が可能です。その際資料の間違いや最新情報への更新などが必要な場合は、issue (opens new window)で気軽にお知らせください。

またPullRequestについても歓迎しています。その際は CONTRIBUTING.md (opens new window) をご一読ください。

- +カリキュラムにはハンズオンだけでなく、「overview」として技術ジャンルの全体像や歴史などを紹介する回も設けています。

このサイトではIIJ Bootcamp用に社内で作成したハンズオン資料をCC BY-SA (opens new window)ライセンスで公開しています(GitHubリポジトリはこちら (opens new window))。

カリキュラムの全体像や資料へのリンクはトップページをご覧ください。

# 資料の構成

ハンズオン資料は以下のカテゴリに分かれています。

カテゴリ 概要
開発系 開発に必須となるGitやdockerのハンズオン
CI/CD + 構成管理 CIサーバのハンズオンとansibleやk8sによるアプリケーションデプロイ
データベース MySQL, MongoDB, Redis ハンズオン
Webサーバ構築 Apacheやnginxを使ったWebサーバの構築
サーバサイドアプリケーション DjangoやJava、golangを使ったサーバサイドアプリケーションの構築
フロントエンド 流行りのWebフレームワークを一通り触ってみる
セキュリティ Webにおけるセキュリティについてと、脆弱なサーバを作らないためのハンズオン

# 資料の利用と修正

資料は CC BY-SAライセンス (opens new window) で公開されており、勉強会などでの2次利用が可能です。その際資料の間違いや最新情報への更新などが必要な場合は、issue (opens new window)で気軽にお知らせください。

またPullRequestについても歓迎しています。その際は CONTRIBUTING.md (opens new window) をご一読ください。

+ diff --git a/assets/js/11.1132ddf4.js b/assets/js/11.8a1b9020.js similarity index 99% rename from assets/js/11.1132ddf4.js rename to assets/js/11.8a1b9020.js index b686707f..b5329c42 100644 --- a/assets/js/11.1132ddf4.js +++ b/assets/js/11.8a1b9020.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{353:function(t,_,v){t.exports=v.p+"assets/img/lock_01.ab763314.svg"},354:function(t,_,v){t.exports=v.p+"assets/img/lock_02.ed79637a.svg"},355:function(t,_,v){t.exports=v.p+"assets/img/lock_03.6c959893.svg"},356:function(t,_,v){t.exports=v.p+"assets/img/lock_04.39275bcb.svg"},357:function(t,_,v){t.exports=v.p+"assets/img/lock_05.a935bdb6.svg"},358:function(t,_,v){t.exports=v.p+"assets/img/lock_06.82acc786.svg"},359:function(t,_,v){t.exports=v.p+"assets/img/lock_07.c47fc1fd.svg"},360:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_01.e5bf7a58.svg"},361:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_02.a899e10d.svg"},362:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_03.b3206b7e.svg"},363:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_04.ac35d38f.svg"},525:function(t,_,v){"use strict";v.r(_);var a=v(10),e=Object(a.a)({},(function(){var t=this,_=t._self._c;return _("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[_("h1",{attrs:{id:"データベース-overview"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベース-overview"}},[t._v("#")]),t._v(" データベース Overview")]),t._v(" "),_("p",[t._v("Webアプリケーション開発において、取得したデータを適切に保存し、必要なデータを適切に取り出して利用しなければならない場面はよくあります。データベースは、そうした場面で非常に有用な技術ですが、データを適切に取り扱うためにはデータベースがどのようなことに配慮して作られているか理解する必要があります。このOverviewでは、データベースに関する基本的な仕組みについて取り扱います。")]),t._v(" "),_("h2",{attrs:{id:"データベースとは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースとは"}},[t._v("#")]),t._v(" データベースとは")]),t._v(" "),_("ol",[_("li",[t._v("データの集合体\n"),_("ul",[_("li",[t._v("事前に決めたルールに従って保存されたデータの集合体のこと")])])]),t._v(" "),_("li",[t._v("DBMS(Database Management System)\n"),_("ul",[_("li",[t._v("DBMSはいわゆるDB製品とほぼ同義と考えても良い")])])])]),t._v(" "),_("h2",{attrs:{id:"dbmsが備える3つの主要機能"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#dbmsが備える3つの主要機能"}},[t._v("#")]),t._v(" DBMSが備える3つの主要機能")]),t._v(" "),_("p",[t._v("DBMS(Database Management System)は、基本的に以下の3つの機能を備えている")]),t._v(" "),_("ol",[_("li",[t._v("データの管理\n"),_("ul",[_("li",[t._v("データの集合体をどのような方法・形式で管理するか")])])]),t._v(" "),_("li",[t._v("アクセス手段\n"),_("ul",[_("li",[t._v("データベースに保存されているデータをどのように取り出すか")])])]),t._v(" "),_("li",[t._v("トランザクション管理\n"),_("ul",[_("li",[t._v("新たなデータを挿入したり、不要なデータを削除したりするなど、データ操作を行う場合に、どのように読み書きを制御するか")])])])]),t._v(" "),_("h2",{attrs:{id:"データベースの種類とデータ管理方法の違い"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースの種類とデータ管理方法の違い"}},[t._v("#")]),t._v(" データベースの種類とデータ管理方法の違い")]),t._v(" "),_("p",[t._v("データベースは製品設計の考え方の違いにより、データの管理方法が複数存在する")]),t._v(" "),_("ol",[_("li",[t._v("リレーショナルデータベース\n"),_("ul",[_("li",[t._v("ある関係をもつ複数のデータを組(タプル)として扱う、表形式で表現できる")]),t._v(" "),_("li",[t._v("主な製品: PostgreSQL, MySQL, MariaDB, Oracle Database, Microsoft SQL Server")])])]),t._v(" "),_("li",[t._v("ドキュメント指向データベース\n"),_("ul",[_("li",[t._v("JSONやXMLなど階層構造をもつデータをドキュメントとして扱う")]),t._v(" "),_("li",[t._v("リレーショナルデータベースは、保存できるデータ定義(スキーマ)が必要になるが、ドキュメント指向データベースはJSONデータなどをそのまま保存できるため、事前にデータ定義(スキーマ)を行う必要性がない点で、スキーマレスと言われる")]),t._v(" "),_("li",[t._v("主な製品: MongoDB, Apache CouchDB")])])]),t._v(" "),_("li",[t._v("Key-Valueデータベース\n"),_("ul",[_("li",[t._v("1つのキーに1つの値(バリュー)をデータとして扱う")]),t._v(" "),_("li",[t._v("主な製品: Redis, Apache Cassandra")])])]),t._v(" "),_("li",[t._v("グラフ指向データベース\n"),_("ul",[_("li",[t._v("データの関係性を扱うデータベース。ノード、エッジ、プロパティの3要素から構成されるデータを扱う")]),t._v(" "),_("li",[t._v("主な製品: neo4j")])])])]),t._v(" "),_("p",[t._v("上記1以外は、リレーショナルデータベースではないことから、"),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/NoSQL",target:"_blank",rel:"noopener noreferrer"}},[t._v("NoSQL"),_("OutboundLink")],1),t._v("と呼ばれることもある")]),t._v(" "),_("h2",{attrs:{id:"データへのアクセス手段"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データへのアクセス手段"}},[t._v("#")]),t._v(" データへのアクセス手段")]),t._v(" "),_("ol",[_("li",[t._v("リレーショナルデータベース\n"),_("ul",[_("li",[t._v("SQL\n"),_("ul",[_("li",[t._v("SQLには、ANSI/ISOで決められた標準SQLと呼ばれるものがある。標準SQLは、決められた年をつけて表記され、現在の最新は「SQL:2016」")]),t._v(" "),_("li",[t._v("各リレーショナルDB製品には、標準SQLに沿いながらも、製品独自のSQLが実装されている。そのため、アプリケーションで利用しているリレーショナルDBを別のリレーショナルDBに変更する場合、アプリケーションが発行している全てのSQLが移行先のリレーショナルDBでも動作するか確認する必要がある。")])])])])]),t._v(" "),_("li",[t._v("非リレーショナルデータベース(NoSQL)\n"),_("ul",[_("li",[t._v("独自クエリ\n"),_("ul",[_("li",[t._v("データの管理方法により、データへのアクセス方法が異なるため、SQLのような共通言語ではなく、製品ごとに規定された独自クエリを使用する")])])])])])]),t._v(" "),_("p",[t._v("SQLを使うリレーショナルデータベースであっても差異があることが多く、非リレーショナルデータベース(NoSQL)は独自クエリになるため、アプリケーションのバックエンドに利用するデータベースをアプリケーション構築後に別のデータベースに移行するのは容易ではない。そのため、バックエンドに利用するデータベース選定は慎重に検討する必要がある。")]),t._v(" "),_("h2",{attrs:{id:"トランザクションとは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#トランザクションとは"}},[t._v("#")]),t._v(" トランザクションとは")]),t._v(" "),_("p",[t._v("アプリケーションが意味のある処理を実行する際に必要な複数のデータベースへの処理単位")]),t._v(" "),_("ul",[_("li",[t._v("例えば、AさんからBさんに10万円を送金する処理は、Aさんの口座から10万円減額し、Bさんの口座を10万円増額する、という全ての処理が完了しないと「送金」という意図した操作が成立しない。")]),t._v(" "),_("li",[t._v("トランザクションは、意図した操作を成立させるために必要な複数の処理を束ねたような概念。")])]),t._v(" "),_("h2",{attrs:{id:"トランザクションとacid特性"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#トランザクションとacid特性"}},[t._v("#")]),t._v(" トランザクションとACID特性")]),t._v(" "),_("p",[t._v("トランザクションの中でも、特にお金の処理などビジネスデータを扱う場合、データ不整合やデータ消失を起こすと重大な事故になってしまう。そうしたことが起きないように、トランザクションでは以下の4つの性質が保証されるべきとされている。")]),t._v(" "),_("ul",[_("li",[_("strong",[t._v("A")]),t._v("tomicity(原子性)\n"),_("ul",[_("li",[t._v("処理を確定する(コミット) or 確定しない(ロールバック)の二者択一になるようにする")])])]),t._v(" "),_("li",[_("strong",[t._v("C")]),t._v("onsistency(一貫性・整合性)\n"),_("ul",[_("li",[t._v("事前に定義されたルールに違反するデータの入力を拒否する")])])]),t._v(" "),_("li",[_("strong",[t._v("I")]),t._v("solation(独立性・分離性)\n"),_("ul",[_("li",[t._v("他のトランザクションの影響を受けないようにする")])])]),t._v(" "),_("li",[_("strong",[t._v("D")]),t._v("urability(永続性)\n"),_("ul",[_("li",[t._v("実行された処理結果は失われない、永続的に保存される")])])])]),t._v(" "),_("p",[t._v("ACID特性は、上記の4つの特性の英単語の頭文字から作られた単語(参考: "),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/ACID_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E7%A7%91%E5%AD%A6)",target:"_blank",rel:"noopener noreferrer"}},[t._v("Wikipedia"),_("OutboundLink")],1),t._v(")")]),t._v(" "),_("p",[t._v("一般的に、リレーショナルデータベースは、ACID特性が保証されているが、非リレーショナルデータベース(NoSQL)ではACID特性が緩められていることがある。")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("リレーショナルデータベースは、一般的にACID特性が保証されているため、データを安全に扱うことができるが、ACID特性を保証するためには様々な処理を実行する必要があるため、NoSQLに比べると相対的に速度が遅いと言われる。")])]),t._v(" "),_("li",[_("p",[t._v("非リレーショナルデータベース(NoSQL)は、リレーショナルデータベースに比べて、大規模データを高速に処理することができると言われるが、NoSQLがACID特性の一部を緩めることで、大規模データの処理性能を効率的に実施している部分がある。アプリケーション開発者は、アプリケーションで扱うデータを守る上で、ACID特性の一部が緩められていても問題がないか検討し、必要であれば保証されていない特性をアプリケーション側で補う必要があるが、考慮する必要性が無ければ、ACID特性を緩めることで、高速性等の恩恵を受けることができる。")])])]),t._v(" "),_("h2",{attrs:{id:"原子性-atomicity-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#原子性-atomicity-とは"}},[t._v("#")]),t._v(" 原子性(Atomicity)とは")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("例: AさんからBさんに10万円送金する場合")]),t._v(" "),_("ol",[_("li",[t._v("Aさんの口座を10万円減額する")]),t._v(" "),_("li",[t._v("Bさんの口座を10万円増額する")])])])]),t._v(" "),_("p",[t._v("仮に、1の処理を実行した後、2の処理が実行できなかった場合、Aさんの口座だけが減額されるトラブルが発生することになる")]),t._v(" "),_("p",[t._v("原子性は、こうしたトラブルが発生しないよう、例えば2の処理が失敗した場合、1の処理も無かったことになることによって、意図した処理が実行されるか(コミット)、処理が全く実行されないか(ロールバック)、どちらかになるように保証する特性")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("コミット(commit)")]),t._v(" "),_("p",[t._v("トランザクション内で実行してきた処理を確定すること")])]),t._v(" "),_("li",[_("p",[t._v("ロールバック(rollback)")]),t._v(" "),_("p",[t._v("トランザクション内で実行してきた処理を全て破棄し、トランザクション開始時点の状態に戻すこと")])])]),t._v(" "),_("h2",{attrs:{id:"一貫性・整合性-consistency-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#一貫性・整合性-consistency-とは"}},[t._v("#")]),t._v(" 一貫性・整合性(Consistency)とは")]),t._v(" "),_("ul",[_("li",[t._v("例: 口座の金額は0円以上である")])]),t._v(" "),_("p",[t._v("仮に、残高が5千円の口座から、1万円の引落を実行した場合、口座残高はマイナス5千円となり、「口座の金額は0円以上である」というルールに違反することになるため、このケースでは1万円の引落の処理は、データベース側でブロックされ実行されない")]),t._v(" "),_("p",[t._v("一貫性・整合性は、データベースに保存される情報が、設計者の想定していない状態にならないよう、あらかじめ設定されたルール(≒業務仕様)に適合するように保証することができる特性")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("制約の例")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("NOT NULL制約")]),t._v(" "),_("p",[t._v("NULLデータを許容しない(データが必ずあることを保証する)")])]),t._v(" "),_("li",[_("p",[t._v("UNIQUE制約")]),t._v(" "),_("p",[t._v("UNIQUE制約を設定した列のデータは、必ず一意になる(その列のデータは列内で重複しないことを保証する)")])])]),t._v(" "),_("p",[t._v("テーブル内のレコードを一意に特定するために使われる主キー(Primary Key、PKと記載される)と言われるカラムは、NOT NULL制約とUNIQUE制約の2つの制約を設定することで実現されている")])])]),t._v(" "),_("h2",{attrs:{id:"独立性・分離性-isolation-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#独立性・分離性-isolation-とは"}},[t._v("#")]),t._v(" 独立性・分離性(Isolation)とは")]),t._v(" "),_("p",[t._v("データベースは、組織内などで情報共有のために使われるため、同時に複数のアクセス(トランザクション)を処理する必要がある。独立性・分離性は、複数トランザクションを同時実行しても、複数トランザクションを1つずつ逐次実行した時と同じ結果になるように保証する特性。")]),t._v(" "),_("h3",{attrs:{id:"ロック-lock-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#ロック-lock-とは"}},[t._v("#")]),t._v(" ロック(lock)とは")]),t._v(" "),_("p",[t._v("複数のトランザクションを同時実行する際に、処理対象データが他のトランザクションの影響を受けないよう、処理に必要になる期間データを占有する機能。独立性・分離性を保証する上で必要な機能の1つ。")]),t._v(" "),_("ul",[_("li",[t._v("例: AさんからBさんに10万円送金する処理(トランザクション1)と、BさんからCさんに5万円送金する処理(トランザクション2)を同時実行する場合\n"),_("ol",[_("li",[t._v("トランザクション1がAさんの口座をロックし10万円減額し20万円にする\n"),_("br"),_("img",{attrs:{src:v(353),alt:"lock_01"}})]),t._v(" "),_("li",[t._v("トランザクション2がBさんの口座をロックし5万円減額し15万円にする\n"),_("br"),_("img",{attrs:{src:v(354),alt:"lock_02"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(355),alt:"lock_03"}})]),t._v(" "),_("li",[t._v("トランザクション2がCさんの口座をロックし5万円増額し10万円にする\n"),_("br"),_("img",{attrs:{src:v(356),alt:"lock_04"}})]),t._v(" "),_("li",[t._v("トランザクション2が処理結果を保存し、BさんとCさんの口座ロックを解除する\n"),_("br"),_("img",{attrs:{src:v(357),alt:"lock_05"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座をロックし10万増額し25万円にする\n"),_("br"),_("img",{attrs:{src:v(358),alt:"lock_06"}})]),t._v(" "),_("li",[t._v("トランザクション1が処理結果を保存し、AさんとBさんの口座ロックを解除する\n"),_("br"),_("img",{attrs:{src:v(359),alt:"lock_07"}})])])])]),t._v(" "),_("p",[t._v("上記例の場合、Bさんの口座は、5万円の減額処理と10万円の増額処理が競合したが、トランザクション2がBさんの口座を2の時点でロックするため、トランザクション2 → トランザクション1の順番で処理が実施される。この結果は、トランザクション1を先に全て実行し、その後トランザクション2を実行した場合と同じになるので、独立性・分離性が担保できている。")]),t._v(" "),_("h3",{attrs:{id:"デッドロック-deadlock-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#デッドロック-deadlock-とは"}},[t._v("#")]),t._v(" デッドロック(deadlock)とは")]),t._v(" "),_("p",[t._v("デッドロックとは、お互いのロック解除を待つことでお互いの処理が永久に進まなくなる状態のこと。例えば、トランザクション1がトランザクション2のロック解除を待ち、トランザクション2がトランザクション1のロック解除を待ってしまう様な状態になると、トランザクション1もトランザクション2も処理が完了せずロック解除に至らないため、ロックが永久に解除されず処理が永久に終了しなくなってしまう。")]),t._v(" "),_("ul",[_("li",[t._v("例: AさんがBさんに10万円送金する処理(トランザクション1)と、BさんがAさんに5万円送金する処理(トランザクション2)を同時実行する場合\n"),_("ol",[_("li",[t._v("トランザクション1がAさんの口座をロックし10万円減額し20万円にする\n"),_("br"),_("img",{attrs:{src:v(360),alt:"dead_lock_01"}})]),t._v(" "),_("li",[t._v("トランザクション2がBさんの口座をロックし5万円減額し15万円にする\n"),_("br"),_("img",{attrs:{src:v(361),alt:"dead_lock_02"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(362),alt:"dead_lock_03"}})]),t._v(" "),_("li",[t._v("トランザクション2がAさんの口座を5万増額しようとするが、トランザクション1が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(363),alt:"dead_lock_04"}})])])])]),t._v(" "),_("p",[t._v("上記例は、トランザクション1はBさんの口座のロック解除を待つ必要があるが、それにはトランザクション2が終了する必要があり、トランザクション2はAさんの口座のロック解除を待つしか無いが、それにはトランザクション1が終了する必要があり、処理が永久に進まなくなる。そのため、データベースには、デッドロック状態を検知し、デッドロックとなったトランザクションの片方を強制中断させることで、デッドロックを解消する機能が備えられている。")]),t._v(" "),_("p",[t._v("デッドロックによって強制中断したトランザクションは、データベースによって再度トランザクションが実行されることはないので、アプリケーション側でデッドロックによる中断を検知したらトランザクションを再実行する必要がある。デッドロックは、複数トランザクションを同時実行するのであれば必ず発生するため、アプリケーション作成時にデッドロックが起きる想定で設計しておく必要がある。")]),t._v(" "),_("h3",{attrs:{id:"分離レベル-isolation-level-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#分離レベル-isolation-level-とは"}},[t._v("#")]),t._v(" 分離レベル(isolation level)とは")]),t._v(" "),_("p",[t._v("トランザクション間の分離・独立性をどのぐらい緩めるか4段階で定義したもの。")]),t._v(" "),_("table",[_("thead",[_("tr",[_("th",{staticStyle:{"text-align":"left"}},[t._v("分離レベルの名称")]),t._v(" "),_("th",{staticStyle:{"text-align":"left"}},[t._v("分離レベルの詳細")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ダーティーリード")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ノンリピータブルリード")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ファンタムリード")])])]),t._v(" "),_("tbody",[_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("READ UNCOMMITTED")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("他のトランザクションの確定してない(コミットされていない)データを読み取れる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("READ COMMITTED")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("他のトランザクションが確定した(コミットした)データを読み取れる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("REPEATABLE READ")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("トランザクション内では同じデータは何度読み取っても同じデータになる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("SERIALIZABLE")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("複数トランザクションを同時実行しても1つずつ逐次実行した時と同じ結果になる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")])])])]),t._v(" "),_("p",[t._v("SERIALIZABLE以外の分離レベルでは、分離性・独立性を緩めることになるため、分離性・独立性に違反する現象が発生することを理解する必要がある(データベースの実装によって上記表とは異なるケースもある。例えば、MySQLはREPEATABLE READでもファンタムリードは発生しない実装になっている)")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("ダーティーリード(dirty read)")]),t._v(" "),_("p",[t._v("トランザクション1がデータAを更新した後、トランザクション2がデータAを読み取った状態で、トランザクション1が実行されなければ(ロールバックされる)、トランザクション2が読み取ったデータは実在しないデータを読み取ることになってしまう現象。")]),t._v(" "),_("p",[t._v("取得データを元に何らかの処理を行う場合は、実在しないデータを元に処理を実施してしまう状況になるため、意図しない結果を出力してしまう恐れがある。")])]),t._v(" "),_("li",[_("p",[t._v("ノンリピーダブルリード(non-repeatable read)")]),t._v(" "),_("p",[t._v("トランザクション1がデータAを読み取ったあと、トランザクション2がデータAを変更して確定(コミット)し、トランザクション1が再度データAを読み取った場合、1回目の読み取りと2回目の読み取り結果が異なってしまう現象。")]),t._v(" "),_("p",[t._v("読み取るタイミングによってデータ異なってしまうため、アプリケーションの実装によっては、意図しない結果を出力してしまう恐れがある。")])]),t._v(" "),_("li",[_("p",[t._v("ファンタムリード(phantom read)")]),t._v(" "),_("p",[t._v("トランザクション1が条件Aで検索してデータを読み取ると、データα・βが取得できるとする。トランザクション2が、条件Aに該当するデータγを追加し確定(コミット)した場合、トランザクション1が再び条件Aで検索してデータを読み取ると、データα・β以外に、1回目の読み取りでは存在してなかった、トランザクション2が追加したデータγも読み取ってしまうような現象。")]),t._v(" "),_("p",[t._v("登録データ数などを根拠に処理を行うような実装などで意図しない結果を出力してしまう恐れがある。")])])]),t._v(" "),_("p",[t._v("高い分離レベルを実現するためには、ロックをかけてデータを保護するため必要があるため、同時実行速度が犠牲になる。一方で低い分離レベルは、ロックをかける必要がなくなるため、同時実行時の速度が向上する。データベース製品ごとにどのように分離レベルが実装されているかを理解しつつ、上記のような現象が発生するリスクを許容できるかアプリケーション設計者がよく検討した上で、データの安全性よりも実行速度を重視したい場合、分離レベルを低くすることも1つの選択肢になる。")]),t._v(" "),_("h2",{attrs:{id:"永続性-durability-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#永続性-durability-とは"}},[t._v("#")]),t._v(" 永続性(Durability)とは")]),t._v(" "),_("p",[t._v("永続性とは、トランザクションが確定(コミット)したら、それ以降にデータが失われないように保証する特性。")]),t._v(" "),_("p",[t._v("トランザクションにおいて、処理が全て完了し確定(コミット)する場合、確定(コミット)が成功したら、それ以降は確定されたデータは永久に消えない状態になっている必要がある。")]),t._v(" "),_("p",[t._v("しかし、一般的に永続ディスク(ハードディスク・SSDなど)への書き込みは低速で時間のかかる処理とされている。そのため、LinuxなどのOSは、書き込み処理を高速化するため、永続ディスクよりも高速に書き込みが可能なキャッシュ領域にデータを書き込むことでアプリケーションに対しては書き込み処理完了とするような実装になっている。そして、キャッシュ領域から永続ディスクへは、一定間隔でまとめて書き込みを行う(フラッシュ処理)実装になっているが、キャッシュ領域で使われるのは、揮発性のメモリー(RAM)であるため、電源断が生じるとデータが消失してしまう。")]),t._v(" "),_("p",[t._v("仮に、確定(コミット)が成功したかの根拠となるデータの書き込み成功が、OSが用意しているキャッシュ(揮発性メモリー)領域への書き込みだった場合、電源断などの障害によってデータが消失する可能性があり、永続性の原則に違反する状況が発生してしまう。データベースでは、こうした状況を防ぐため確定(コミット)する際に、OSのキャッシュ領域ではなく、永続ディスクへの書き込みになるように制御するような機能が備わっている。")]),t._v(" "),_("h2",{attrs:{id:"まとめ"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[t._v("#")]),t._v(" まとめ")]),t._v(" "),_("h3",{attrs:{id:"データを扱うことの難しさを知る"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データを扱うことの難しさを知る"}},[t._v("#")]),t._v(" データを扱うことの難しさを知る")]),t._v(" "),_("p",[t._v("データを確実に保存し、必要な場面で確実に利用するためには、様々なことに注意を払う必要があります。アプリケーション開発者は、これまで見てきたようなデータに関して起きうる全ての状況を想定しながら、アプリケーションの設計・実装していく必要がありますが、データに関して起きうる全ての事象に対応したアプリケーションを1から実装するのは非現実的です(いわゆる"),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/%E8%BB%8A%E8%BC%AA%E3%81%AE%E5%86%8D%E7%99%BA%E6%98%8E",target:"_blank",rel:"noopener noreferrer"}},[t._v("車輪の再発明"),_("OutboundLink")],1),t._v(")。")]),t._v(" "),_("h3",{attrs:{id:"データベースを学び利用する"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースを学び利用する"}},[t._v("#")]),t._v(" データベースを学び利用する")]),t._v(" "),_("p",[t._v("データベースを学ぶことは、データを扱う上で起きうる事象を学ぶことができ、アプリケーション開発の中でデータを扱う際に何に配慮すれば良いか見えて来ると思います。また、データベースを利用することで、データを扱う上で起きうる事象への配慮をデータベースに任せることができれば、アプリケーション開発者はアプリケーションに求められる本質的な機能の実装に集中でき、より良いアプリケーション開発を行うことができるようになると思います。")]),t._v(" "),_("h3",{attrs:{id:"必要な機能を備えたデータベース製品を選定する"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#必要な機能を備えたデータベース製品を選定する"}},[t._v("#")]),t._v(" 必要な機能を備えたデータベース製品を選定する")]),t._v(" "),_("p",[t._v("昨今、様々なデータベース製品がリリースされています。それらの製品ごとにメリット・デメリット、得意・不得意な領域が存在します。データの安全性を重視するのか、それとも速度を重視するかなど、アプリケーションごとにデータに対する要求の方向性は変わってきますが、求める方向性に合致するデータベース製品を選定できるようになるためにも、データベースの知識を持つことが重要だと思います。")])])}),[],!1,null,null,null);_.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{353:function(t,_,v){t.exports=v.p+"assets/img/lock_01.ab763314.svg"},354:function(t,_,v){t.exports=v.p+"assets/img/lock_02.ed79637a.svg"},355:function(t,_,v){t.exports=v.p+"assets/img/lock_03.6c959893.svg"},356:function(t,_,v){t.exports=v.p+"assets/img/lock_04.39275bcb.svg"},357:function(t,_,v){t.exports=v.p+"assets/img/lock_05.a935bdb6.svg"},358:function(t,_,v){t.exports=v.p+"assets/img/lock_06.82acc786.svg"},359:function(t,_,v){t.exports=v.p+"assets/img/lock_07.c47fc1fd.svg"},360:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_01.e5bf7a58.svg"},361:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_02.a899e10d.svg"},362:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_03.b3206b7e.svg"},363:function(t,_,v){t.exports=v.p+"assets/img/dead_lock_04.ac35d38f.svg"},524:function(t,_,v){"use strict";v.r(_);var a=v(10),e=Object(a.a)({},(function(){var t=this,_=t._self._c;return _("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[_("h1",{attrs:{id:"データベース-overview"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベース-overview"}},[t._v("#")]),t._v(" データベース Overview")]),t._v(" "),_("p",[t._v("Webアプリケーション開発において、取得したデータを適切に保存し、必要なデータを適切に取り出して利用しなければならない場面はよくあります。データベースは、そうした場面で非常に有用な技術ですが、データを適切に取り扱うためにはデータベースがどのようなことに配慮して作られているか理解する必要があります。このOverviewでは、データベースに関する基本的な仕組みについて取り扱います。")]),t._v(" "),_("h2",{attrs:{id:"データベースとは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースとは"}},[t._v("#")]),t._v(" データベースとは")]),t._v(" "),_("ol",[_("li",[t._v("データの集合体\n"),_("ul",[_("li",[t._v("事前に決めたルールに従って保存されたデータの集合体のこと")])])]),t._v(" "),_("li",[t._v("DBMS(Database Management System)\n"),_("ul",[_("li",[t._v("DBMSはいわゆるDB製品とほぼ同義と考えても良い")])])])]),t._v(" "),_("h2",{attrs:{id:"dbmsが備える3つの主要機能"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#dbmsが備える3つの主要機能"}},[t._v("#")]),t._v(" DBMSが備える3つの主要機能")]),t._v(" "),_("p",[t._v("DBMS(Database Management System)は、基本的に以下の3つの機能を備えている")]),t._v(" "),_("ol",[_("li",[t._v("データの管理\n"),_("ul",[_("li",[t._v("データの集合体をどのような方法・形式で管理するか")])])]),t._v(" "),_("li",[t._v("アクセス手段\n"),_("ul",[_("li",[t._v("データベースに保存されているデータをどのように取り出すか")])])]),t._v(" "),_("li",[t._v("トランザクション管理\n"),_("ul",[_("li",[t._v("新たなデータを挿入したり、不要なデータを削除したりするなど、データ操作を行う場合に、どのように読み書きを制御するか")])])])]),t._v(" "),_("h2",{attrs:{id:"データベースの種類とデータ管理方法の違い"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースの種類とデータ管理方法の違い"}},[t._v("#")]),t._v(" データベースの種類とデータ管理方法の違い")]),t._v(" "),_("p",[t._v("データベースは製品設計の考え方の違いにより、データの管理方法が複数存在する")]),t._v(" "),_("ol",[_("li",[t._v("リレーショナルデータベース\n"),_("ul",[_("li",[t._v("ある関係をもつ複数のデータを組(タプル)として扱う、表形式で表現できる")]),t._v(" "),_("li",[t._v("主な製品: PostgreSQL, MySQL, MariaDB, Oracle Database, Microsoft SQL Server")])])]),t._v(" "),_("li",[t._v("ドキュメント指向データベース\n"),_("ul",[_("li",[t._v("JSONやXMLなど階層構造をもつデータをドキュメントとして扱う")]),t._v(" "),_("li",[t._v("リレーショナルデータベースは、保存できるデータ定義(スキーマ)が必要になるが、ドキュメント指向データベースはJSONデータなどをそのまま保存できるため、事前にデータ定義(スキーマ)を行う必要性がない点で、スキーマレスと言われる")]),t._v(" "),_("li",[t._v("主な製品: MongoDB, Apache CouchDB")])])]),t._v(" "),_("li",[t._v("Key-Valueデータベース\n"),_("ul",[_("li",[t._v("1つのキーに1つの値(バリュー)をデータとして扱う")]),t._v(" "),_("li",[t._v("主な製品: Redis, Apache Cassandra")])])]),t._v(" "),_("li",[t._v("グラフ指向データベース\n"),_("ul",[_("li",[t._v("データの関係性を扱うデータベース。ノード、エッジ、プロパティの3要素から構成されるデータを扱う")]),t._v(" "),_("li",[t._v("主な製品: neo4j")])])])]),t._v(" "),_("p",[t._v("上記1以外は、リレーショナルデータベースではないことから、"),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/NoSQL",target:"_blank",rel:"noopener noreferrer"}},[t._v("NoSQL"),_("OutboundLink")],1),t._v("と呼ばれることもある")]),t._v(" "),_("h2",{attrs:{id:"データへのアクセス手段"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データへのアクセス手段"}},[t._v("#")]),t._v(" データへのアクセス手段")]),t._v(" "),_("ol",[_("li",[t._v("リレーショナルデータベース\n"),_("ul",[_("li",[t._v("SQL\n"),_("ul",[_("li",[t._v("SQLには、ANSI/ISOで決められた標準SQLと呼ばれるものがある。標準SQLは、決められた年をつけて表記され、現在の最新は「SQL:2016」")]),t._v(" "),_("li",[t._v("各リレーショナルDB製品には、標準SQLに沿いながらも、製品独自のSQLが実装されている。そのため、アプリケーションで利用しているリレーショナルDBを別のリレーショナルDBに変更する場合、アプリケーションが発行している全てのSQLが移行先のリレーショナルDBでも動作するか確認する必要がある。")])])])])]),t._v(" "),_("li",[t._v("非リレーショナルデータベース(NoSQL)\n"),_("ul",[_("li",[t._v("独自クエリ\n"),_("ul",[_("li",[t._v("データの管理方法により、データへのアクセス方法が異なるため、SQLのような共通言語ではなく、製品ごとに規定された独自クエリを使用する")])])])])])]),t._v(" "),_("p",[t._v("SQLを使うリレーショナルデータベースであっても差異があることが多く、非リレーショナルデータベース(NoSQL)は独自クエリになるため、アプリケーションのバックエンドに利用するデータベースをアプリケーション構築後に別のデータベースに移行するのは容易ではない。そのため、バックエンドに利用するデータベース選定は慎重に検討する必要がある。")]),t._v(" "),_("h2",{attrs:{id:"トランザクションとは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#トランザクションとは"}},[t._v("#")]),t._v(" トランザクションとは")]),t._v(" "),_("p",[t._v("アプリケーションが意味のある処理を実行する際に必要な複数のデータベースへの処理単位")]),t._v(" "),_("ul",[_("li",[t._v("例えば、AさんからBさんに10万円を送金する処理は、Aさんの口座から10万円減額し、Bさんの口座を10万円増額する、という全ての処理が完了しないと「送金」という意図した操作が成立しない。")]),t._v(" "),_("li",[t._v("トランザクションは、意図した操作を成立させるために必要な複数の処理を束ねたような概念。")])]),t._v(" "),_("h2",{attrs:{id:"トランザクションとacid特性"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#トランザクションとacid特性"}},[t._v("#")]),t._v(" トランザクションとACID特性")]),t._v(" "),_("p",[t._v("トランザクションの中でも、特にお金の処理などビジネスデータを扱う場合、データ不整合やデータ消失を起こすと重大な事故になってしまう。そうしたことが起きないように、トランザクションでは以下の4つの性質が保証されるべきとされている。")]),t._v(" "),_("ul",[_("li",[_("strong",[t._v("A")]),t._v("tomicity(原子性)\n"),_("ul",[_("li",[t._v("処理を確定する(コミット) or 確定しない(ロールバック)の二者択一になるようにする")])])]),t._v(" "),_("li",[_("strong",[t._v("C")]),t._v("onsistency(一貫性・整合性)\n"),_("ul",[_("li",[t._v("事前に定義されたルールに違反するデータの入力を拒否する")])])]),t._v(" "),_("li",[_("strong",[t._v("I")]),t._v("solation(独立性・分離性)\n"),_("ul",[_("li",[t._v("他のトランザクションの影響を受けないようにする")])])]),t._v(" "),_("li",[_("strong",[t._v("D")]),t._v("urability(永続性)\n"),_("ul",[_("li",[t._v("実行された処理結果は失われない、永続的に保存される")])])])]),t._v(" "),_("p",[t._v("ACID特性は、上記の4つの特性の英単語の頭文字から作られた単語(参考: "),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/ACID_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E7%A7%91%E5%AD%A6)",target:"_blank",rel:"noopener noreferrer"}},[t._v("Wikipedia"),_("OutboundLink")],1),t._v(")")]),t._v(" "),_("p",[t._v("一般的に、リレーショナルデータベースは、ACID特性が保証されているが、非リレーショナルデータベース(NoSQL)ではACID特性が緩められていることがある。")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("リレーショナルデータベースは、一般的にACID特性が保証されているため、データを安全に扱うことができるが、ACID特性を保証するためには様々な処理を実行する必要があるため、NoSQLに比べると相対的に速度が遅いと言われる。")])]),t._v(" "),_("li",[_("p",[t._v("非リレーショナルデータベース(NoSQL)は、リレーショナルデータベースに比べて、大規模データを高速に処理することができると言われるが、NoSQLがACID特性の一部を緩めることで、大規模データの処理性能を効率的に実施している部分がある。アプリケーション開発者は、アプリケーションで扱うデータを守る上で、ACID特性の一部が緩められていても問題がないか検討し、必要であれば保証されていない特性をアプリケーション側で補う必要があるが、考慮する必要性が無ければ、ACID特性を緩めることで、高速性等の恩恵を受けることができる。")])])]),t._v(" "),_("h2",{attrs:{id:"原子性-atomicity-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#原子性-atomicity-とは"}},[t._v("#")]),t._v(" 原子性(Atomicity)とは")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("例: AさんからBさんに10万円送金する場合")]),t._v(" "),_("ol",[_("li",[t._v("Aさんの口座を10万円減額する")]),t._v(" "),_("li",[t._v("Bさんの口座を10万円増額する")])])])]),t._v(" "),_("p",[t._v("仮に、1の処理を実行した後、2の処理が実行できなかった場合、Aさんの口座だけが減額されるトラブルが発生することになる")]),t._v(" "),_("p",[t._v("原子性は、こうしたトラブルが発生しないよう、例えば2の処理が失敗した場合、1の処理も無かったことになることによって、意図した処理が実行されるか(コミット)、処理が全く実行されないか(ロールバック)、どちらかになるように保証する特性")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("コミット(commit)")]),t._v(" "),_("p",[t._v("トランザクション内で実行してきた処理を確定すること")])]),t._v(" "),_("li",[_("p",[t._v("ロールバック(rollback)")]),t._v(" "),_("p",[t._v("トランザクション内で実行してきた処理を全て破棄し、トランザクション開始時点の状態に戻すこと")])])]),t._v(" "),_("h2",{attrs:{id:"一貫性・整合性-consistency-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#一貫性・整合性-consistency-とは"}},[t._v("#")]),t._v(" 一貫性・整合性(Consistency)とは")]),t._v(" "),_("ul",[_("li",[t._v("例: 口座の金額は0円以上である")])]),t._v(" "),_("p",[t._v("仮に、残高が5千円の口座から、1万円の引落を実行した場合、口座残高はマイナス5千円となり、「口座の金額は0円以上である」というルールに違反することになるため、このケースでは1万円の引落の処理は、データベース側でブロックされ実行されない")]),t._v(" "),_("p",[t._v("一貫性・整合性は、データベースに保存される情報が、設計者の想定していない状態にならないよう、あらかじめ設定されたルール(≒業務仕様)に適合するように保証することができる特性")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("制約の例")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("NOT NULL制約")]),t._v(" "),_("p",[t._v("NULLデータを許容しない(データが必ずあることを保証する)")])]),t._v(" "),_("li",[_("p",[t._v("UNIQUE制約")]),t._v(" "),_("p",[t._v("UNIQUE制約を設定した列のデータは、必ず一意になる(その列のデータは列内で重複しないことを保証する)")])])]),t._v(" "),_("p",[t._v("テーブル内のレコードを一意に特定するために使われる主キー(Primary Key、PKと記載される)と言われるカラムは、NOT NULL制約とUNIQUE制約の2つの制約を設定することで実現されている")])])]),t._v(" "),_("h2",{attrs:{id:"独立性・分離性-isolation-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#独立性・分離性-isolation-とは"}},[t._v("#")]),t._v(" 独立性・分離性(Isolation)とは")]),t._v(" "),_("p",[t._v("データベースは、組織内などで情報共有のために使われるため、同時に複数のアクセス(トランザクション)を処理する必要がある。独立性・分離性は、複数トランザクションを同時実行しても、複数トランザクションを1つずつ逐次実行した時と同じ結果になるように保証する特性。")]),t._v(" "),_("h3",{attrs:{id:"ロック-lock-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#ロック-lock-とは"}},[t._v("#")]),t._v(" ロック(lock)とは")]),t._v(" "),_("p",[t._v("複数のトランザクションを同時実行する際に、処理対象データが他のトランザクションの影響を受けないよう、処理に必要になる期間データを占有する機能。独立性・分離性を保証する上で必要な機能の1つ。")]),t._v(" "),_("ul",[_("li",[t._v("例: AさんからBさんに10万円送金する処理(トランザクション1)と、BさんからCさんに5万円送金する処理(トランザクション2)を同時実行する場合\n"),_("ol",[_("li",[t._v("トランザクション1がAさんの口座をロックし10万円減額し20万円にする\n"),_("br"),_("img",{attrs:{src:v(353),alt:"lock_01"}})]),t._v(" "),_("li",[t._v("トランザクション2がBさんの口座をロックし5万円減額し15万円にする\n"),_("br"),_("img",{attrs:{src:v(354),alt:"lock_02"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(355),alt:"lock_03"}})]),t._v(" "),_("li",[t._v("トランザクション2がCさんの口座をロックし5万円増額し10万円にする\n"),_("br"),_("img",{attrs:{src:v(356),alt:"lock_04"}})]),t._v(" "),_("li",[t._v("トランザクション2が処理結果を保存し、BさんとCさんの口座ロックを解除する\n"),_("br"),_("img",{attrs:{src:v(357),alt:"lock_05"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座をロックし10万増額し25万円にする\n"),_("br"),_("img",{attrs:{src:v(358),alt:"lock_06"}})]),t._v(" "),_("li",[t._v("トランザクション1が処理結果を保存し、AさんとBさんの口座ロックを解除する\n"),_("br"),_("img",{attrs:{src:v(359),alt:"lock_07"}})])])])]),t._v(" "),_("p",[t._v("上記例の場合、Bさんの口座は、5万円の減額処理と10万円の増額処理が競合したが、トランザクション2がBさんの口座を2の時点でロックするため、トランザクション2 → トランザクション1の順番で処理が実施される。この結果は、トランザクション1を先に全て実行し、その後トランザクション2を実行した場合と同じになるので、独立性・分離性が担保できている。")]),t._v(" "),_("h3",{attrs:{id:"デッドロック-deadlock-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#デッドロック-deadlock-とは"}},[t._v("#")]),t._v(" デッドロック(deadlock)とは")]),t._v(" "),_("p",[t._v("デッドロックとは、お互いのロック解除を待つことでお互いの処理が永久に進まなくなる状態のこと。例えば、トランザクション1がトランザクション2のロック解除を待ち、トランザクション2がトランザクション1のロック解除を待ってしまう様な状態になると、トランザクション1もトランザクション2も処理が完了せずロック解除に至らないため、ロックが永久に解除されず処理が永久に終了しなくなってしまう。")]),t._v(" "),_("ul",[_("li",[t._v("例: AさんがBさんに10万円送金する処理(トランザクション1)と、BさんがAさんに5万円送金する処理(トランザクション2)を同時実行する場合\n"),_("ol",[_("li",[t._v("トランザクション1がAさんの口座をロックし10万円減額し20万円にする\n"),_("br"),_("img",{attrs:{src:v(360),alt:"dead_lock_01"}})]),t._v(" "),_("li",[t._v("トランザクション2がBさんの口座をロックし5万円減額し15万円にする\n"),_("br"),_("img",{attrs:{src:v(361),alt:"dead_lock_02"}})]),t._v(" "),_("li",[t._v("トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(362),alt:"dead_lock_03"}})]),t._v(" "),_("li",[t._v("トランザクション2がAさんの口座を5万増額しようとするが、トランザクション1が口座をロック中のため待機\n"),_("br"),_("img",{attrs:{src:v(363),alt:"dead_lock_04"}})])])])]),t._v(" "),_("p",[t._v("上記例は、トランザクション1はBさんの口座のロック解除を待つ必要があるが、それにはトランザクション2が終了する必要があり、トランザクション2はAさんの口座のロック解除を待つしか無いが、それにはトランザクション1が終了する必要があり、処理が永久に進まなくなる。そのため、データベースには、デッドロック状態を検知し、デッドロックとなったトランザクションの片方を強制中断させることで、デッドロックを解消する機能が備えられている。")]),t._v(" "),_("p",[t._v("デッドロックによって強制中断したトランザクションは、データベースによって再度トランザクションが実行されることはないので、アプリケーション側でデッドロックによる中断を検知したらトランザクションを再実行する必要がある。デッドロックは、複数トランザクションを同時実行するのであれば必ず発生するため、アプリケーション作成時にデッドロックが起きる想定で設計しておく必要がある。")]),t._v(" "),_("h3",{attrs:{id:"分離レベル-isolation-level-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#分離レベル-isolation-level-とは"}},[t._v("#")]),t._v(" 分離レベル(isolation level)とは")]),t._v(" "),_("p",[t._v("トランザクション間の分離・独立性をどのぐらい緩めるか4段階で定義したもの。")]),t._v(" "),_("table",[_("thead",[_("tr",[_("th",{staticStyle:{"text-align":"left"}},[t._v("分離レベルの名称")]),t._v(" "),_("th",{staticStyle:{"text-align":"left"}},[t._v("分離レベルの詳細")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ダーティーリード")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ノンリピータブルリード")]),t._v(" "),_("th",{staticStyle:{"text-align":"center"}},[t._v("ファンタムリード")])])]),t._v(" "),_("tbody",[_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("READ UNCOMMITTED")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("他のトランザクションの確定してない(コミットされていない)データを読み取れる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("READ COMMITTED")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("他のトランザクションが確定した(コミットした)データを読み取れる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("REPEATABLE READ")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("トランザクション内では同じデータは何度読み取っても同じデータになる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("起きる")])]),t._v(" "),_("tr",[_("td",{staticStyle:{"text-align":"left"}},[t._v("SERIALIZABLE")]),t._v(" "),_("td",{staticStyle:{"text-align":"left"}},[t._v("複数トランザクションを同時実行しても1つずつ逐次実行した時と同じ結果になる")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")]),t._v(" "),_("td",{staticStyle:{"text-align":"center"}},[t._v("-")])])])]),t._v(" "),_("p",[t._v("SERIALIZABLE以外の分離レベルでは、分離性・独立性を緩めることになるため、分離性・独立性に違反する現象が発生することを理解する必要がある(データベースの実装によって上記表とは異なるケースもある。例えば、MySQLはREPEATABLE READでもファンタムリードは発生しない実装になっている)")]),t._v(" "),_("ul",[_("li",[_("p",[t._v("ダーティーリード(dirty read)")]),t._v(" "),_("p",[t._v("トランザクション1がデータAを更新した後、トランザクション2がデータAを読み取った状態で、トランザクション1が実行されなければ(ロールバックされる)、トランザクション2が読み取ったデータは実在しないデータを読み取ることになってしまう現象。")]),t._v(" "),_("p",[t._v("取得データを元に何らかの処理を行う場合は、実在しないデータを元に処理を実施してしまう状況になるため、意図しない結果を出力してしまう恐れがある。")])]),t._v(" "),_("li",[_("p",[t._v("ノンリピーダブルリード(non-repeatable read)")]),t._v(" "),_("p",[t._v("トランザクション1がデータAを読み取ったあと、トランザクション2がデータAを変更して確定(コミット)し、トランザクション1が再度データAを読み取った場合、1回目の読み取りと2回目の読み取り結果が異なってしまう現象。")]),t._v(" "),_("p",[t._v("読み取るタイミングによってデータ異なってしまうため、アプリケーションの実装によっては、意図しない結果を出力してしまう恐れがある。")])]),t._v(" "),_("li",[_("p",[t._v("ファンタムリード(phantom read)")]),t._v(" "),_("p",[t._v("トランザクション1が条件Aで検索してデータを読み取ると、データα・βが取得できるとする。トランザクション2が、条件Aに該当するデータγを追加し確定(コミット)した場合、トランザクション1が再び条件Aで検索してデータを読み取ると、データα・β以外に、1回目の読み取りでは存在してなかった、トランザクション2が追加したデータγも読み取ってしまうような現象。")]),t._v(" "),_("p",[t._v("登録データ数などを根拠に処理を行うような実装などで意図しない結果を出力してしまう恐れがある。")])])]),t._v(" "),_("p",[t._v("高い分離レベルを実現するためには、ロックをかけてデータを保護するため必要があるため、同時実行速度が犠牲になる。一方で低い分離レベルは、ロックをかける必要がなくなるため、同時実行時の速度が向上する。データベース製品ごとにどのように分離レベルが実装されているかを理解しつつ、上記のような現象が発生するリスクを許容できるかアプリケーション設計者がよく検討した上で、データの安全性よりも実行速度を重視したい場合、分離レベルを低くすることも1つの選択肢になる。")]),t._v(" "),_("h2",{attrs:{id:"永続性-durability-とは"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#永続性-durability-とは"}},[t._v("#")]),t._v(" 永続性(Durability)とは")]),t._v(" "),_("p",[t._v("永続性とは、トランザクションが確定(コミット)したら、それ以降にデータが失われないように保証する特性。")]),t._v(" "),_("p",[t._v("トランザクションにおいて、処理が全て完了し確定(コミット)する場合、確定(コミット)が成功したら、それ以降は確定されたデータは永久に消えない状態になっている必要がある。")]),t._v(" "),_("p",[t._v("しかし、一般的に永続ディスク(ハードディスク・SSDなど)への書き込みは低速で時間のかかる処理とされている。そのため、LinuxなどのOSは、書き込み処理を高速化するため、永続ディスクよりも高速に書き込みが可能なキャッシュ領域にデータを書き込むことでアプリケーションに対しては書き込み処理完了とするような実装になっている。そして、キャッシュ領域から永続ディスクへは、一定間隔でまとめて書き込みを行う(フラッシュ処理)実装になっているが、キャッシュ領域で使われるのは、揮発性のメモリー(RAM)であるため、電源断が生じるとデータが消失してしまう。")]),t._v(" "),_("p",[t._v("仮に、確定(コミット)が成功したかの根拠となるデータの書き込み成功が、OSが用意しているキャッシュ(揮発性メモリー)領域への書き込みだった場合、電源断などの障害によってデータが消失する可能性があり、永続性の原則に違反する状況が発生してしまう。データベースでは、こうした状況を防ぐため確定(コミット)する際に、OSのキャッシュ領域ではなく、永続ディスクへの書き込みになるように制御するような機能が備わっている。")]),t._v(" "),_("h2",{attrs:{id:"まとめ"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[t._v("#")]),t._v(" まとめ")]),t._v(" "),_("h3",{attrs:{id:"データを扱うことの難しさを知る"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データを扱うことの難しさを知る"}},[t._v("#")]),t._v(" データを扱うことの難しさを知る")]),t._v(" "),_("p",[t._v("データを確実に保存し、必要な場面で確実に利用するためには、様々なことに注意を払う必要があります。アプリケーション開発者は、これまで見てきたようなデータに関して起きうる全ての状況を想定しながら、アプリケーションの設計・実装していく必要がありますが、データに関して起きうる全ての事象に対応したアプリケーションを1から実装するのは非現実的です(いわゆる"),_("a",{attrs:{href:"https://ja.wikipedia.org/wiki/%E8%BB%8A%E8%BC%AA%E3%81%AE%E5%86%8D%E7%99%BA%E6%98%8E",target:"_blank",rel:"noopener noreferrer"}},[t._v("車輪の再発明"),_("OutboundLink")],1),t._v(")。")]),t._v(" "),_("h3",{attrs:{id:"データベースを学び利用する"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#データベースを学び利用する"}},[t._v("#")]),t._v(" データベースを学び利用する")]),t._v(" "),_("p",[t._v("データベースを学ぶことは、データを扱う上で起きうる事象を学ぶことができ、アプリケーション開発の中でデータを扱う際に何に配慮すれば良いか見えて来ると思います。また、データベースを利用することで、データを扱う上で起きうる事象への配慮をデータベースに任せることができれば、アプリケーション開発者はアプリケーションに求められる本質的な機能の実装に集中でき、より良いアプリケーション開発を行うことができるようになると思います。")]),t._v(" "),_("h3",{attrs:{id:"必要な機能を備えたデータベース製品を選定する"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#必要な機能を備えたデータベース製品を選定する"}},[t._v("#")]),t._v(" 必要な機能を備えたデータベース製品を選定する")]),t._v(" "),_("p",[t._v("昨今、様々なデータベース製品がリリースされています。それらの製品ごとにメリット・デメリット、得意・不得意な領域が存在します。データの安全性を重視するのか、それとも速度を重視するかなど、アプリケーションごとにデータに対する要求の方向性は変わってきますが、求める方向性に合致するデータベース製品を選定できるようになるためにも、データベースの知識を持つことが重要だと思います。")])])}),[],!1,null,null,null);_.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/15.077431a9.js b/assets/js/15.e79f30b8.js similarity index 99% rename from assets/js/15.077431a9.js rename to assets/js/15.e79f30b8.js index 0256f0d5..5b10ed74 100644 --- a/assets/js/15.077431a9.js +++ b/assets/js/15.e79f30b8.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{386:function(s,t,e){s.exports=e.p+"assets/img/components-of-kubernetes.51120ad2.svg"},387:function(s,t,e){s.exports=e.p+"assets/img/module_03_pods.ccc5ba54.svg"},388:function(s,t,e){s.exports=e.p+"assets/img/image_clusterip.8223e0b9.svg"},389:function(s,t,e){s.exports=e.p+"assets/img/image_nodeport.5357aa05.svg"},390:function(s,t,e){s.exports=e.p+"assets/img/image_loadbalancer.7df1efb3.svg"},535:function(s,t,e){"use strict";e.r(t);var a=e(10),n=Object(a.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"kubernetes-でアプリケーション開発"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#kubernetes-でアプリケーション開発"}},[s._v("#")]),s._v(" Kubernetes でアプリケーション開発")]),s._v(" "),t("h2",{attrs:{id:"_0-まえがき"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-まえがき"}},[s._v("#")]),s._v(" 0. まえがき")]),s._v(" "),t("h3",{attrs:{id:"_0-1-想定している受講者"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-1-想定している受講者"}},[s._v("#")]),s._v(" 0-1. 想定している受講者")]),s._v(" "),t("p",[s._v("本講義では以下の受講者を対象としています。")]),s._v(" "),t("ul",[t("li",[s._v("Kubernetesという名前は知っているがどんなものなのかは知らない")]),s._v(" "),t("li",[s._v("Kubernetes入門しようにも何から始めたらよいのかわからない")]),s._v(" "),t("li",[s._v("Kubernetesの仕組みがわからない")])]),s._v(" "),t("h3",{attrs:{id:"_0-2-前提知識"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-2-前提知識"}},[s._v("#")]),s._v(" 0-2. 前提知識")]),s._v(" "),t("p",[s._v("以下の点を知らないと講義についていけない可能性があります。")]),s._v(" "),t("ul",[t("li",[s._v("Linuxの基本的なコマンド")]),s._v(" "),t("li",[s._v("dockerの基礎")])]),s._v(" "),t("p",[s._v("加えて以下の点を知っていると講義をスムーズに聞けます。")]),s._v(" "),t("ul",[t("li",[s._v("YAMLファイルの読み方/書き方")]),s._v(" "),t("li",[s._v("コンテナアーキテクチャの基礎")])]),s._v(" "),t("h3",{attrs:{id:"_0-3-事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-3-事前準備"}},[s._v("#")]),s._v(" 0-3. 事前準備")]),s._v(" "),t("ul",[t("li",[s._v("docker / docker-compose のインストール")]),s._v(" "),t("li",[s._v("Kubernetes環境\n"),t("ul",[t("li",[s._v("環境構築に自信が無い人katacodaを使ってください\n"),t("ul",[t("li",[t("a",{attrs:{href:"https://www.katacoda.com/courses/kubernetes/playground",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://www.katacoda.com/courses/kubernetes/playground"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("strong",[t("strong",[s._v("外部リソースなのでコピペする際は気を付けてください")])])])])]),s._v(" "),t("li",[s._v("ローカルでkubernetesを動かしたい人はkindを以下の手順で構築してください")])])])]),s._v(" "),t("blockquote",[t("p",[s._v("kindを使ったkubernetes環境の構築")]),s._v(" "),t("p",[s._v("kindはkubernetes in dockerの略です。その名の通り、dockerを使ってkubernetes環境を構築します。\n("),t("a",{attrs:{href:"https://kind.sigs.k8s.io/docs/user/quick-start/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント参照"),t("OutboundLink")],1),s._v(")")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.14.0/kind-linux-amd64")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# chmod +x ./kind")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("dockerホストからkindに対してコマンドを実行したいのでkubectlをdockerホストに入れます")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("試験的にクラスターを構築して正しくインストールされたか確認する")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind create cluster")]),s._v("\nCreating cluster "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n ✓ Ensuring "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" image "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("kindest/node:v1.24.0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" 🖼 \n ✓ Preparing nodes 📦 \n ✓ Writing configuration 📜 \n ✓ Starting control-plane 🕹️ \n ✓ Installing CNI 🔌 \n ✓ Installing StorageClass 💾 \nSet kubectl context to "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind-kind"')]),s._v("\nYou can now use your cluster with:\n\nkubectl cluster-info --context kind-kind\n\nHave a question, bug, or feature request? Let us know"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v(" https://kind.sigs.k8s.io/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#community 🙂")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# docker ps")]),s._v("\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nd76ca5889d8d kindest/node:v1.24.0 "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/local/bin/entr…"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("59")]),s._v(" seconds ago Up "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("49")]),s._v(" seconds "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:35447-"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("6443")]),s._v("/tcp kind-control-plane\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl cluster-info --context kind-kind")]),s._v("\nKubernetes control plane is running at https://127.0.0.1:35447\nCoreDNS is running at https://127.0.0.1:35447/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n\nTo further debug and diagnose cluster problems, use "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'kubectl cluster-info dump'")]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get node")]),s._v("\nNAME STATUS ROLES AGE VERSION\nkind-control-plane Ready control-plane 2m43s v1.24.0\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])]),t("p",[s._v("確認出来たら削除")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind delete cluster")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("bootcamp用のクラスター環境を構築する")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vim cluster.yml ")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("以下の内容を記載する")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Cluster\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kind.x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("k8s.io/v1alpha4\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nodes")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" control"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("plane\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e \n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("p",[s._v("クラスター構築")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind create cluster --config cluster.yml ")]),s._v("\nCreating cluster "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n ✓ Ensuring "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" image "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("kindest/node:v1.24.0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" 🖼 \n ✓ Preparing nodes 📦 📦 📦 📦 \n ✓ Writing configuration 📜 \n ✓ Starting control-plane 🕹️ \n ✓ Installing CNI 🔌 \n ✓ Installing StorageClass 💾 \n ✓ Joining worker nodes 🚜 \nSet kubectl context to "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind-kind"')]),s._v("\nYou can now use your cluster with:\n\nkubectl cluster-info --context kind-kind\n\nThanks "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" using kind"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v(" 😊\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl cluster-info --context kind-kind")]),s._v("\nKubernetes control plane is running at https://127.0.0.1:46863\nCoreDNS is running at https://127.0.0.1:46863/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n\nTo further debug and diagnose cluster problems, use "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'kubectl cluster-info dump'")]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get node")]),s._v("\nNAME STATUS ROLES AGE VERSION\nkind-control-plane Ready control-plane 72s v1.24.0\nkind-worker Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 33s v1.24.0\nkind-worker2 Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 33s v1.24.0\nkind-worker3 Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 32s v1.24.0\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])])]),s._v(" "),t("h2",{attrs:{id:"_1-kubernetesとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-kubernetesとは"}},[s._v("#")]),s._v(" 1. Kubernetesとは")]),s._v(" "),t("p",[s._v("Kubernetesは複数サーバで構成された基盤上でコンテナ群を一元管理するためツール「コンテナオーケストレーションツール」と呼ばれるものです。最近ではdockerを使ってコンテナ単位でアプリケーションを実装することが多くなりましたが、docker単体では複数台のdockerホスト上でコンテナ群を一元管理することができません。そのため複数ホストで構成される規模のプロダクションに耐えられるシステムをdocker単体で構築することは困難とされてきました。そこで複数のdockerホストに跨ってコンテナアプリケーションをデプロイ、スケーリング、ネットワーク管理機能などをするオーケストレーションツールが登場しました。")]),s._v(" "),t("p",[s._v("Kubernetesは元々Googleがアプリケーションデプロイに利用していたBorgと呼ばれるクラスターマネージャーをOSS化したもので、現在はLinux Foundation傘下にあるCNCF(Cloud Native Computing Foundation)が管理しています。ランクはGraduatedで成熟したCNCFプロジェクトとなっています。")]),s._v(" "),t("blockquote",[t("p",[s._v("【豆知識】")]),s._v(" "),t("p",[s._v("Kubernetesはギリシャ語で「操舵士」や「パイロット」を意味し、ロゴは操舵士にちなんで舵をモチーフにされています。7つのスポークは当初のKubernetesのコードネーム「Project Seven」にちなんでいます。")])]),s._v(" "),t("p",[s._v("KubernetesをベースにカスタマイズしたKubernetesサービスを最近ではKaaS(Kubernetes as a Service)と言い、AWSやGCPなどの各クラウドベンダで提供されています。")]),s._v(" "),t("ul",[t("li",[s._v("Amazon EKS")]),s._v(" "),t("li",[s._v("Google Kubernetes Engine(GKE)")]),s._v(" "),t("li",[s._v("Azure Kubernetes Service(AKS)")])]),s._v(" "),t("p",[s._v("先ほど「カスタマイズ」と言いましたが、Kubernetesはそれ単体で完成するものではなくログ基盤にfluentdとelasticsearchを使ったり、それぞれのクラウドサービスとの繋ぎこみなど提供元によって機能やスペックが異なります。このようにカスタマイズされたKubernetes基盤のことをLinuxのディストリビューションに例えて「Kubernetesディストリビューション」と呼びます。")]),s._v(" "),t("p",[s._v("IIJでも社内向けのKubernetes基盤としてIKE(IIJ Kubernetes Engine)の運用と導入が進んでいます。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://www.iij.ad.jp/dev/report/iir/040/03.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIR"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://eng-blog.iij.ad.jp/kubernetes",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIJエンジニアブログ"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("従来のVMでのシステム構築と比べてKubernetesを利用することでシステム開発・管理が格段に楽になります。例えば従来のシステム構築では、どのVMに何を割り当てるかのリソース計算を人が考えなければならず、システムがスケーリングするたびに多くの労力を使いましたが、Kubernetesではマシンリソースやネットワークをプールとして扱い自動で管理するため、システムのスケーリングに柔軟に対応することができます("),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/scale/scale-intro/",target:"_blank",rel:"noopener noreferrer"}},[s._v("詳細"),t("OutboundLink")],1),s._v(")。また、従来ではシステムのアップデートを行うたびに「サービス停止→アップデート→サービス再開」という手順でアップデートをしていたが、Kubernetesではサービスを停止することなくシステムアップデートを行うことができ、サービス可用性を高めてくれます("),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/update/update-intro/",target:"_blank",rel:"noopener noreferrer"}},[s._v("詳細"),t("OutboundLink")],1),s._v(")。")]),s._v(" "),t("p",[s._v("上記以外にも多くのメリットがあり、Kubernetesでシステム開発を行うとアプリケーションの構築作業が劇的に減る他、APIなどからの自動デプロイなども非常に簡単に行うことができます。")]),s._v(" "),t("h2",{attrs:{id:"_2-kubernetesの基本構造"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-kubernetesの基本構造"}},[s._v("#")]),s._v(" 2. Kubernetesの基本構造")]),s._v(" "),t("h3",{attrs:{id:"_2-1-宣言的な構成管理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-宣言的な構成管理"}},[s._v("#")]),s._v(" 2-1. 宣言的な構成管理")]),s._v(" "),t("p",[s._v("Kubernetes上にアプリケーションをデプロイする際、その構成管理は宣言的に行われます。デプロイしたい人は「アプリケーションやそれを構成するコンテナ群はこのような配置であるべき」という宣言(manifest)を"),t("strong",[s._v("マニフェストファイル")]),s._v("に記載することで、Kubernetesはマニフェストファイルに沿った構成を宣言どおりにデプロイします。このような構成管理方法はIaC(Infrastructure as a Code)と呼ばれており、Ansible同様に冪等性の確保や自動化に貢献しています。このような構成管理方法の主なメリットはGitによるバージョン管理のしやすさが挙げられます。")]),s._v(" "),t("blockquote",[t("p",[s._v("Ansibleで「Playbook」と呼ばれているものがKubernetesでいう「Manifest」です。")])]),s._v(" "),t("h3",{attrs:{id:"_2-2-コントロールプレーンとワーカーノード"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-コントロールプレーンとワーカーノード"}},[s._v("#")]),s._v(" 2-2. コントロールプレーンとワーカーノード")]),s._v(" "),t("p",[s._v("Kubernetesは大きく分けて2つの要素で構成されています。"),t("strong",[s._v("コントロールプレーン")]),s._v("と"),t("strong",[s._v("ワーカーノード")]),s._v("です。")]),s._v(" "),t("blockquote",[t("p",[s._v("文献によってはコントロールプレーンのことを"),t("strong",[s._v("マスターノード")]),s._v("と表記することがありますが、同じ意味なので誤解の無いように注意してください。")])]),s._v(" "),t("p",[t("img",{attrs:{src:e(386),alt:"component"}})]),s._v(" "),t("h4",{attrs:{id:"_2-2-1-コントロールプレーン"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-1-コントロールプレーン"}},[s._v("#")]),s._v(" 2-2-1. コントロールプレーン")]),s._v(" "),t("p",[s._v("コントロールプレーンはKubernetesクラスター全体の状態の管理を行うことが主な仕事です。例えばアプリケーション開発者が宣言したマニフェストファイルどおりに作られたPodがワーカーノードに割り当てられているかを監視し、割り当てられていなかった場合はそのPodを実行するノードを各のノードのリソース状況を考慮して割り当てます。このようなコンポーネントを"),t("strong",[s._v("kube-schduler")]),s._v("と言います。他にもマニフェストファイルによって宣言された構成情報を閲覧/編集するためのAPIサーバの役割を果たす"),t("strong",[s._v("kube-apiserver")]),s._v("などがあります。(他のコンポーネントも知りたい人は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/overview/components/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("へ)")]),s._v(" "),t("blockquote",[t("p",[s._v("【Podとは】")]),s._v(" "),t("p",[s._v("Kubernetesはコンテナを「pod」と呼ばれる単位で管理します。podにはいくつかのコンテナの集まりで、同じpodに所属するコンテナ同士はlocalhostでお互いに通信することができます。\n"),t("img",{attrs:{src:e(387),alt:"Pod"}}),s._v("\nPodにどのようなコンテナを同居させるのかは設計次第ですが、例えばアプリケーションのログを集めるコンテナを同じpodに同居させたり、nginxなどwebのフロントになるアプリケーションを同居させたりします。")])]),s._v(" "),t("h4",{attrs:{id:"_2-2-2-ワーカーノード"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-2-ワーカーノード"}},[s._v("#")]),s._v(" 2-2-2. ワーカーノード")]),s._v(" "),t("p",[s._v("ワーカーノードはPodの管理やPodの実行環境/通信機能を提供することが主な仕事です。例えばコントロールプレーンコンポーネントである"),t("strong",[s._v("kube-apiserver")]),s._v("から受け取った構成情報どおりにPodが稼働するように管理します。このようなコンポーネントを"),t("strong",[s._v("kubelet")]),s._v("と言います。ただしkubeletは実際にPodを作成したり、そのネットワーク環境を構築することはせず、あくまでもノード上のPod状態を維持するように管理することが仕事です。実際にPodを作ったりするコンポーネントを"),t("strong",[s._v("コンテナランタイム")]),s._v("と言います。他には、後程出てきますがService宛の通信を稼働中のPod群へ転送させる"),t("strong",[s._v("kube-proxy")]),s._v("などがあります。")]),s._v(" "),t("h3",{attrs:{id:"_2-3-kubernetesの基本構造まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-3-kubernetesの基本構造まとめ"}},[s._v("#")]),s._v(" 2-3. Kubernetesの基本構造まとめ")]),s._v(" "),t("p",[s._v("Kubernetesは主に"),t("strong",[s._v("コントロールプレーン")]),s._v("と"),t("strong",[s._v("ワーカーノード")]),s._v("に分けられており、コントロールプレーンはクラスター全体の状態管理、ワーカーノードはコントロールプレーンからの指示通りにユーザによって宣言された構成を作り上げることが役割でした。そして、それぞれの役割を果たすために多くのコンポーネントが内蔵されている、という話でした。各コンポーネントについてもっと詳しく知りたい方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/architecture/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参照してください。")]),s._v(" "),t("h2",{attrs:{id:"_3-マニフェストファイルの書き方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-マニフェストファイルの書き方"}},[s._v("#")]),s._v(" 3. マニフェストファイルの書き方")]),s._v(" "),t("h3",{attrs:{id:"_3-1-kubernetesオブジェクト"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-1-kubernetesオブジェクト"}},[s._v("#")]),s._v(" 3-1. Kubernetesオブジェクト")]),s._v(" "),t("p",[s._v("先ほども説明したように、Kubernetes上にPodなどをデプロイする際、ユーザはマニフェストファイルを書く必要があります。マニフェストファイルではいくつかのKubernetesオブジェクトを組み合わせて、ユーザの意図する状態を記載します。マニフェストファイルに記載されたKubernetesオブジェクトはコントロールプレーンによって読み取られ、読み取られたKubernetesオブジェクトが存在し続けるようにクラスター全体を管理します。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Kubernetesオブジェクト】")]),s._v(" "),t("p",[s._v("Kubernetesオブジェクトはクラスターの状態を表現するパーツです。以下にKubernetesオブジェクトの例と簡単な説明を記載します")]),s._v(" "),t("ul",[t("li",[s._v("ReplicaSet: Pod群の稼働状況を管理する")]),s._v(" "),t("li",[s._v("Deployment:バージョンに相当するReplicaSetを管理する")]),s._v(" "),t("li",[s._v("CronJob:定期実行するpodを管理する")]),s._v(" "),t("li",[s._v("Service:特定のラベルを持ち、サービスを提供できる状態のPod群への接続を提供する")]),s._v(" "),t("li",[s._v("Ingress:証明書やドメイン名を通して外部からの通信を制御する")]),s._v(" "),t("li",[s._v("PersistentVolume:ストレージなどの永続化volumeを管理する")])]),s._v(" "),t("p",[s._v("代表的な物を上げましたが、他にも色々あります。興味のある方は"),t("a",{attrs:{href:"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式サイト"),t("OutboundLink")],1),s._v("参照。")])]),s._v(" "),t("h3",{attrs:{id:"_3-2-deployment"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-2-deployment"}},[s._v("#")]),s._v(" 3-2. Deployment")]),s._v(" "),t("p",[s._v("上でも述べた通りDeploymentはPodの稼働状況を管理するオブジェクトです。予め起動するPodの数を指定することで、何かしらの原因でPodが消失しても自動で立ち上げ直してくれたり、逆に多すぎる場合は終了させます。")]),s._v(" "),t("p",[s._v("現在稼働しているPodは、kubectlというcliツールを使って確認できます。\n今は何も稼働してないはずです。")]),s._v(" "),t("blockquote",[t("p",[s._v("【kubectl】")]),s._v(" "),t("p",[s._v("KubernetesのコントロールプレーンにはKubernetesクラスターの構成管理情報にアクセスするためのエンドポイントを提供する"),t("strong",[s._v("kube-apiserver")]),s._v("がありますが、生身の人間がAPIを生で叩いて"),t("strong",[s._v("kube-apiserver")]),s._v("にアクセスするのは少しキツイものがあります。そこでAPIをコマンドで操作できる"),t("strong",[s._v("kubectl")]),s._v("というものがあり、kubectlを使うことによりコマンドベースでAPIを叩くことができます。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl get pods\nNo resources found in default namespace.\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("以下のようなマニフェストファイルを"),t("code",[s._v("app.yml")]),s._v("として作成し、Kubernetesクラスターにデプロイしてみましょう。\n"),t("code",[s._v("image")]),s._v("として指定しているのはサンプル用の簡単なアプリケーションです。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Deployment\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("replicas")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("app\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" registry.k8s.io/echoserver"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1.4")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("restartPolicy")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Always\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("blockquote",[t("p",[s._v("【Deploymentにおける必須フィールド】")]),s._v(" "),t("p",[s._v("Kubernetesオブジェクトをマニフェストファイルに記載する際、必ず以下のフィールドに値をセットする必要があります")]),s._v(" "),t("ul",[t("li",[s._v("apiVersion:オブジェクトのAPIVersionを指定")]),s._v(" "),t("li",[s._v("kind:どのオブジェクトを作るかを指定")]),s._v(" "),t("li",[s._v("metadata:オブジェクトを特定するための情報を指定")]),s._v(" "),t("li",[s._v("spec:オブジェクトの状態を指定")])])]),s._v(" "),t("p",[t("code",[s._v("apiVersion")]),s._v("にはオブジェクトのAPIVersionを書きます。\nオブジェクトAPIがどのAPIGROUPに属しているかでapiVersionの書き方が変わってきます。\n今回はDeploymentのオブジェクトなのでそのAPIグループを調べます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl api-resources\nNAME SHORTNAMES APIGROUP NAMESPACED KIND\nbindings true Binding\ncomponentstatuses cs false ComponentStatus\nconfigmaps cm true ConfigMap\n...\ndeployments deploy apps true Deployment\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("p",[s._v("ここではDeploymentが"),t("code",[s._v("apps")]),s._v("に属しているということが分かりました。\nもしAPIGROUPが空の場合はCore groupに属するため、"),t("code",[s._v("apiVersion: v1")]),s._v("で問題ないです。\n次にAPIGROUPで利用可能なversionを調べます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl api-versions | grep apps\napps/v1\napps/v1beta1\napps/v1beta2\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("p",[s._v("ここでは3つほど出ましたが、この中の最も新しいversionを使ってください。\n今回は"),t("code",[s._v("apps/v1")]),s._v("を使ってマニフェストファイルを作りました。")]),s._v(" "),t("p",[s._v("yamlを作成したら、以下のコマンドでデプロイできます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl apply -f app.yml\ndeployment.apps/bootcamp created\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("別端末で"),t("code",[s._v("get pods")]),s._v("しながらpodが作られる様子を見てみましょう"),t("code",[s._v("-w")]),s._v("をつけると自動で表示を更新してくれます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl get pods -w\nNAME READY STATUS RESTARTS AGE\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 11s\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 11s\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 83s\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 84s\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("p",[t("code",[s._v("Running")]),s._v("となっていれば無事にアプリケーションが起動しました。今回は"),t("code",[s._v("replicas")]),s._v("に"),t("code",[s._v("2")]),s._v("を指定したのでpodが2個起動しています。"),t("code",[s._v("replicas")]),s._v("の値を変えて再度"),t("code",[s._v("kubectl apply")]),s._v("して遊んでみましょう。")]),s._v(" "),t("h3",{attrs:{id:"_3-3-service"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-3-service"}},[s._v("#")]),s._v(" 3-3. Service")]),s._v(" "),t("p",[s._v("Podの起動ができましたので、次はPodへのアクセスを試みます。KubernetesクラスターではPod群へのサービスディスカバリーの方法としてServiceオブジェクトが用いられます。Serviceを利用することでPod群に共通のIPアドレスを割り当て、まるで一つの「サービス」であるかのようにアクセスできるようになります。")]),s._v(" "),t("blockquote",[t("p",[s._v("【PodとServiceの関係】")]),s._v(" "),t("p",[s._v("Podは生成の度にIPアドレスが割り振られます。これは何かしらの理由でPodが落ちて別のPodが再生成されるときにもIPアドレスが割り振られますが、落ちたPodと同じIPアドレスが割り振られるとは限りません。こうなった場合に新しいPodへアクセスしたい別Podは新しいPodのIPアドレスがわからなくなってしまいます。ServiceはこのようなPodを共通のIPアドレスで管理し、Podへのアクセスやロードバランシングを行う役割を持っています。また、Serviceの生成によりKubernetesクラスター内の"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tasks/administer-cluster/coredns/",target:"_blank",rel:"noopener noreferrer"}},[s._v("CoreDNS"),t("OutboundLink")],1),s._v("のA/AAAAレコードやPod内の"),t("code",[s._v("resolve.conf")]),s._v("が自動的に書き換えられるため、Service名を使ってPodへアクセスすることも可能になります。")])]),s._v(" "),t("blockquote",[t("p",[s._v("【ServiceからPodへの通信の受け渡し】")]),s._v(" "),t("p",[s._v("Serviceが作られるとService宛の通信がPodへ転送されますが、その仕組みは"),t("strong",[s._v("ワーカーノード")]),s._v("内のコンポーネントである"),t("strong",[s._v("kube-proxy")]),s._v("によって実現されます。すべてのServiceは基本的にClusterIP(あとで説明します)によるVIPの保持が義務付けられており、またClusterIP配下のPodのIPアドレスはendpointに記載されています。そして、ClusterIPからPodの持つIPへの振り替えを"),t("strong",[s._v("kube-proxy")]),s._v("が行います。"),t("strong",[s._v("kube-proxy")]),s._v("の振り替え方式はいくつか選択肢がありますが、デフォルトの"),t("strong",[s._v("iptableモード")]),s._v("ではiptablesのchainがあり、これによってパケットが転送されます。他のモードについて知りたい方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参照してください。")])]),s._v(" "),t("p",[s._v("先ほどと同じようにServiceのマニフェストファイルを作りましょう。今回は"),t("code",[s._v("service.yml")]),s._v("とします。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("svc\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterIP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("p",[s._v("Serviceオブジェクトの中には様々なサービスタイプ種類があり、今回は"),t("code",[s._v("ClusterIP")]),s._v("というサービスタイプを利用しています。ClusterIPはServiceにおけるデフォルト設定であり、明示的に記載が無ければ"),t("code",[s._v("type: ClusterIP")]),s._v("が設定されることに注意してください。")]),s._v(" "),t("blockquote",[t("p",[s._v("【サービスタイプ】")]),s._v(" "),t("p",[s._v("それぞれのServiceの特徴について簡単に触れます。少し長くなるので講義では"),t("code",[s._v("ClusterIP")]),s._v("のみを説明しますが、興味のある人は他のサービスタイプも読んでみてください。")]),s._v(" "),t("h4",{attrs:{id:"clusterip"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#clusterip"}},[s._v("#")]),s._v(" ClusterIP")]),s._v(" "),t("p",[s._v("ClusterIPによって割り振られるIPアドレスはKubernetesクラスター内でのみ有効です。主にクラスター外からアクセスする必要のない箇所などでクラスター内ロードバランスをする際に利用されます。\n"),t("img",{attrs:{src:e(388),alt:"ClusterIP"}})]),s._v(" "),t("h4",{attrs:{id:"nodeport"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#nodeport"}},[s._v("#")]),s._v(" NodePort")]),s._v(" "),t("p",[s._v("ClusterIPを作った上で、全node各々の"),t("code",[s._v("")]),s._v("で受信したアクセスをServiceへ転送することで、クラスタ外からアクセスできるようにします。Docker Swarmでいうところの"),t("code",[s._v("Expose")]),s._v("です。図では全Kubernetes nodeの"),t("code",[s._v("port:30080")]),s._v("へのアクセスを"),t("code",[s._v("NodePort Service")]),s._v("に転送しています。\n"),t("img",{attrs:{src:e(389),alt:"NodePort"}})]),s._v(" "),t("h4",{attrs:{id:"loadbalancer"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#loadbalancer"}},[s._v("#")]),s._v(" LoadBalancer")]),s._v(" "),t("p",[s._v("Kubernetesクラスター外のロードバランサーより払い出された仮想IPアドレスを利用してクラスター外からのアクセスを可能にします。NodePortでは各nodeに"),t("code",[s._v(":")]),s._v("が割り振られ、ユーザはいずれかのアドレス宛にアクセスするため、アクセスしているnodeで障害が起きた際にそのnodeを利用しているユーザはサービスを利用できなくなります。それに対して"),t("code",[s._v("type: LoadBalancer")]),s._v("は、ユーザがクラスター外のロードバランサーから払い出されたIPアドレスのみを知っておくだけでサービスを利用することができます。また、nodeで障害が起きてもそのnodeの切り離しを行うようにクラスター外のロードバランサーを設定することで、ユーザはサービスを継続して利用することができます(ただし従来のロードバランサー+仮想マシンの組み合わせ同様に、障害検知から除外までの間は通信断が発生します)。ここでいうクラスター外のロードバランサーはプロバイダに依存しており、たとえばGCPの場合はGCLBが使われています。\n"),t("img",{attrs:{src:e(390),alt:"LoadBalancer"}})])]),s._v(" "),t("p",[s._v("それでは同じようにapplyしてから"),t("code",[s._v("Service")]),s._v("の稼働状況を確認します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl apply -f service.yml\nservice/bootcamp-svc created\n$ kubectl get svc\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("S"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" AGE\nbootcamp-svc ClusterIP xxx.xxx.xxx.xxx "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/TCP 1h\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("p",[s._v("次に実際にPodにアクセスしてみましょう。"),t("code",[s._v("kubectl proxy")]),s._v("でコントロールプレーンのAPIサーバにポートフォワーディングします。先ほども説明しましたが"),t("code",[s._v("type: ClusterIP")]),s._v("は外から直接アクセスができません。そのため手元のホストからコントロールプレーンまでをポートフォワードし、コントロールプレーンからPodまでをREST APIを使って通信させます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl proxy\nStarting to serve on 127.0.0.1:8001\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("このプロキシ機能はServiceへのアクセスをRESTとして"),t("code",[s._v("/api/v1/namespaces//services/::/proxy/")]),s._v("と表現しているため、今回は"),t("code",[s._v("http://127.0.0.1:8001/api/v1/namespaces//services/bootcamp-svc/proxy/")]),s._v("へアクセスすることでコンテンツを取得することができます。")]),s._v(" "),t("blockquote",[t("p",[t("code",[s._v("")]),s._v("にはデプロイ先のnamespaceを入力します。\nnamespaceがわからない場合は"),t("code",[s._v("kubectl config get-contexts")]),s._v("から探してください。"),t("code",[s._v("CURRENT")]),s._v("に米印が付いているものがいま作業しているコンテキストになります。もし"),t("code",[s._v("NAMESPACE")]),s._v("の欄に何もなければ"),t("code",[s._v("namespace: default")]),s._v("ということになります。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl config get-contexts\nCURRENT NAME CLUSTER AUTHINFO NAMESPACE\n minikube minikube nirazuka\n* nira nirakube nirazuka nirazuka\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])])]),s._v(" "),t("p",[s._v("katacodeを使っている場合、RESTを辿ることができないため"),t("code",[s._v("kubectl port-foward")]),s._v("を利用します。"),t("code",[s._v("kubectl port-foward")]),s._v("はローカルのポートをPodやServiceに直接フォワーディングすることができます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl port-forward service/bootcamp-svc --address=0.0.0.0 :80\nForwarding from 0.0.0.0:35715 -> 8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("あとはTerminal横の「+」ボタンから「select port to view on Host 1」を選択し、表示されているポートへアクセスすればコンテンツを取得することができます。")]),s._v(" "),t("p",[t("code",[s._v("Hello Kubernetes!")]),s._v(" が表示されたでしょうか。無事にpodにアクセスすることができました。")]),s._v(" "),t("blockquote",[t("p",[s._v("今回はServiceでアプリケーションを公開しましたが、本来はServiceの上にIngressを作って公開することが推奨されています。\nIngressを利用するとSSLの設定やVirtualHostの設定などを行えるようになります。\n興味のある方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/services-networking/ingress/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参考に触ってみて下さい。")])]),s._v(" "),t("h3",{attrs:{id:"_3-4-podを削除してみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-4-podを削除してみる"}},[s._v("#")]),s._v(" 3-4. Podを削除してみる")]),s._v(" "),t("p",[s._v("試しに手動で無理やりpodを削除してみましょう。"),t("code",[s._v("kubectl get pods -w")]),s._v("で確認しながら、以下のコマンドでpodを削除してみます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl delete pods "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("pod-name"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("例によってpod-nameはコピペしてください。"),t("code",[s._v("kubectl get pods -w")]),s._v("しているとPodの数が"),t("code",[s._v("replicas")]),s._v("の設定値に合うように新しく起動される様子が分かります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl get pods -w\nNAME READY STATUS RESTARTS AGE\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Terminating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Pending "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Pending "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 7s\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Terminating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("p",[t("code",[s._v("bootcamp-6bcddb7cf8-jpzg5")]),s._v("が手動で削除したpodです。"),t("code",[s._v("bootcamp-6bcddb7cf8-jpzg5")]),s._v("の削除が始まった途端に新しく"),t("code",[s._v("bootcamp-6bcddb7cf8-ffj7f")]),s._v("というpodを立てようとしているのが分かります。")]),s._v(" "),t("p",[s._v("このようにpodがエラーで停止したり、新しいアプリケーションのデプロイなどでpodを停止してもすぐさま"),t("code",[s._v("ReplicaSet")]),s._v("が状態を修復してくれます。\nそれだけではなく、前段の"),t("code",[s._v("Service")]),s._v("がpodの状態を監視しながら通信を流す先を決めてくれるため、一部のpodが停止している間も自動的に生きているpodに通信を流してくれます。")]),s._v(" "),t("p",[s._v("そのためユーザーに一切影響なくpodの停止と復旧が全て自動で可能になっています。このようなインフラをKubernetesとコンテナなしで構築するのはかなり困難です。")]),s._v(" "),t("h2",{attrs:{id:"_4-応用-kubernetesの監視"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-応用-kubernetesの監視"}},[s._v("#")]),s._v(" 4. 応用(Kubernetesの監視)")]),s._v(" "),t("p",[s._v("ここからは本格的なアプリケーションのデプロイを体験してもらいます。katacodeでやっている方はうまくいかないことがあるため本項目は飛ばしてください。")]),s._v(" "),t("p",[s._v("今回Kubernetes上に構築するアプリケーションは監視ツールのPrometheusで、以下の順序でデプロイします。(マニフェストファイルは"),t("a",{attrs:{href:"https://www.hanmoto.com/bd/isbn/9784910313009",target:"_blank",rel:"noopener noreferrer"}},[s._v("Prometheus実践ガイド"),t("OutboundLink")],1),s._v("の内容を一部改変したものを利用しています)")]),s._v(" "),t("ol",[t("li",[s._v("node exporterのデプロイ")]),s._v(" "),t("li",[s._v("RBAC認可を使ってリソースにアクセスするためのアカウントをデプロイ")]),s._v(" "),t("li",[s._v("Prometheusのデプロイ")])]),s._v(" "),t("h3",{attrs:{id:"_4-1-node-exporterのデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-node-exporterのデプロイ"}},[s._v("#")]),s._v(" 4-1. node exporterのデプロイ")]),s._v(" "),t("p",[s._v("node exporterは各ノードのメトリクス情報を収集するツール(exporter)です。これを各nodeに配置する必要がありますが、"),t("code",[s._v("Deployment")]),s._v("オブジェクトを利用すると配置nodeの指定を都度行う必要があり煩雑です。そのため、ここでは"),t("code",[s._v("DeamonSet")]),s._v("オブジェクトを利用します。"),t("code",[s._v("DeamonSet")]),s._v("オブジェクトは各ノードに等しくPodを配置するオブジェクトです。"),t("code",[s._v("node-exporter.yml")]),s._v("という名前で以下の内容のマニフェストファイルを作成します。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" DaemonSet\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'prom/node-exporter:v1.3.1'")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hostNetwork")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hostPID")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tolerations")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("key")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("role.kubernetes.io/control"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("plane\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("operator")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Exists\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("value")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("''")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("effect")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" NoSchedule\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br")])]),t("p",[s._v("各ノードに対して"),t("code",[s._v("prom/node-exporter:v1.3.1")]),s._v("というコンテナを1つずつデプロイさせています。"),t("code",[s._v("hostNetwork")]),s._v("と"),t("code",[s._v("hostPID")]),s._v("を"),t("code",[s._v("true")]),s._v("にすることでノードとコンテナのネットワーク/プロセスIDを共有させます。これは通常、コンテナはホストの環境とプロセス等が分離された状態になっているため、共有させないとPodからノードの情報を取得することができためです。"),t("code",[s._v("node-exporter")]),s._v("は外部から接続させる必要がないため、"),t("code",[s._v("Service")]),s._v("は"),t("code",[s._v("CluserIP")]),s._v("を指定しています。")]),s._v(" "),t("p",[s._v("準備が出来たら"),t("code",[s._v("kubectl apply -f node-exporter.yml")]),s._v("でデプロイします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f node-exporter.yml")]),s._v("\ndaemonset.apps/node-exporter created\nservice/node-exporter created\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get pods")]),s._v("\nNAME READY STATUS RESTARTS AGE\nnode-exporter-75rpz "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\nnode-exporter-p25gq "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\nnode-exporter-tcbsp "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br")])]),t("h3",{attrs:{id:"_4-2-rbac認可を使ってリソースにアクセスするためのアカウントをデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-2-rbac認可を使ってリソースにアクセスするためのアカウントをデプロイ"}},[s._v("#")]),s._v(" 4-2. RBAC認可を使ってリソースにアクセスするためのアカウントをデプロイ")]),s._v(" "),t("p",[s._v("KubernetesはRole Based Access Control(RBAC)といわれる、各種リソースへのアクセス制御をユーザロールベースで行っています。そのため、監視に必要なリソースへのアクセスに必要な権限をユーザに付与する必要があります。ここでは権限の定義を行う"),t("code",[s._v("ClusterRole")]),s._v("、権限とユーザとの紐づけを行う"),t("code",[s._v("ClusterRoleBind")]),s._v("という二つのオブジェクトを利用します。"),t("code",[s._v("role-based-access-control.yml")]),s._v("という名前でマニフェストファイルを作り、以下の内容を記載します。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRole\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("rules")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroups")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("resources")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" nodes\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" services\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" endpoints\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" pods\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" metrics\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" nodes/metrics\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"list"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"watch"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroups")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" extensions\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("resources")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" ingresses\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"list"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"watch"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nonResourceURLs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" /metrics\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRoleBinding\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("roleRef")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroup")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRole\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("subjects")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ServiceAccount\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ServiceAccount\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br")])]),t("p",[t("code",[s._v("default")]),s._v("namespace上の"),t("code",[s._v("prometheus")]),s._v("というアカウントに対して、各種リソースへの参照権限を付与する内容になります。"),t("code",[s._v("kubectl apply -f role-based-access-control.yml")]),s._v("を実行してデプロイします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f role-based-access-control.yml ")]),s._v("\nclusterrole.rbac.authorization.k8s.io/prometheus created\nclusterrolebinding.rbac.authorization.k8s.io/prometheus created\nserviceaccount/prometheus created\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("p",[s._v("これにより、API Serverなどへのアクセスするための認証情報が払い出されました。")]),s._v(" "),t("h3",{attrs:{id:"_4-3-prometheusのデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-3-prometheusのデプロイ"}},[s._v("#")]),s._v(" 4-3. Prometheusのデプロイ")]),s._v(" "),t("p",[s._v("最後にPrometheusのデプロイを行います。Prometheusのデプロイには"),t("code",[s._v("Deployment")]),s._v("と"),t("code",[s._v("Service")]),s._v("オブジェクトを利用しますが、Prometheus自体の設定ファイルは"),t("code",[s._v("ConfigMap")]),s._v("というオブジェクトを利用して定義します。これを利用することによりコンフィグファイルをマニフェストファイルとして管理することができ、さらにPrometheusに反映させることが出来ます。"),t("code",[s._v("prometheus.yml")]),s._v("というマニフェストファイルを作り以下の内容を記載します。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Deployment\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("replicas")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("serviceAccountName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prom/prometheus"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v2.33.3\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("imagePullPolicy")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" IfNotPresent\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("args")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("config.file=/prometheus/prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("log.level=debug\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("web.enable"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("lifecycle\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("volumeMounts")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mountPath")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /prometheus/prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("subPath")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("volumes")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("configMap")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterIP\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ConfigMap\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("data")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("prometheus.yml")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("|")]),t("span",{pre:!0,attrs:{class:"token scalar string"}},[s._v("\n global:\n scrape_interval: 15s\n scrape_configs:\n - job_name: 'prometheus'\n kubernetes_sd_configs:\n - role: pod\n relabel_configs:\n - source_labels: [__meta_kubernetes_pod_name]\n regex: prometheus-.+\n action: keep\n - job_name: 'apiserver'\n kubernetes_sd_configs:\n - role: service\n scheme: https\n tls_config:\n ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n authorization:\n credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n relabel_configs:\n - source_labels:\n - __meta_kubernetes_namespace\n - __meta_kubernetes_service_name\n - __meta_kubernetes_service_port_name\n action: keep\n regex: default;kubernetes;https\n - job_name: 'node-exporter'\n scheme: http\n kubernetes_sd_configs:\n - role: node\n relabel_configs:\n - source_labels: [__address__]\n action: replace\n regex: (.+):.+\n replacement: ${1}:9100\n target_label: __address__")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br"),t("span",{staticClass:"line-number"},[s._v("49")]),t("br"),t("span",{staticClass:"line-number"},[s._v("50")]),t("br"),t("span",{staticClass:"line-number"},[s._v("51")]),t("br"),t("span",{staticClass:"line-number"},[s._v("52")]),t("br"),t("span",{staticClass:"line-number"},[s._v("53")]),t("br"),t("span",{staticClass:"line-number"},[s._v("54")]),t("br"),t("span",{staticClass:"line-number"},[s._v("55")]),t("br"),t("span",{staticClass:"line-number"},[s._v("56")]),t("br"),t("span",{staticClass:"line-number"},[s._v("57")]),t("br"),t("span",{staticClass:"line-number"},[s._v("58")]),t("br"),t("span",{staticClass:"line-number"},[s._v("59")]),t("br"),t("span",{staticClass:"line-number"},[s._v("60")]),t("br"),t("span",{staticClass:"line-number"},[s._v("61")]),t("br"),t("span",{staticClass:"line-number"},[s._v("62")]),t("br"),t("span",{staticClass:"line-number"},[s._v("63")]),t("br"),t("span",{staticClass:"line-number"},[s._v("64")]),t("br"),t("span",{staticClass:"line-number"},[s._v("65")]),t("br"),t("span",{staticClass:"line-number"},[s._v("66")]),t("br"),t("span",{staticClass:"line-number"},[s._v("67")]),t("br"),t("span",{staticClass:"line-number"},[s._v("68")]),t("br"),t("span",{staticClass:"line-number"},[s._v("69")]),t("br"),t("span",{staticClass:"line-number"},[s._v("70")]),t("br"),t("span",{staticClass:"line-number"},[s._v("71")]),t("br"),t("span",{staticClass:"line-number"},[s._v("72")]),t("br"),t("span",{staticClass:"line-number"},[s._v("73")]),t("br"),t("span",{staticClass:"line-number"},[s._v("74")]),t("br"),t("span",{staticClass:"line-number"},[s._v("75")]),t("br"),t("span",{staticClass:"line-number"},[s._v("76")]),t("br"),t("span",{staticClass:"line-number"},[s._v("77")]),t("br"),t("span",{staticClass:"line-number"},[s._v("78")]),t("br"),t("span",{staticClass:"line-number"},[s._v("79")]),t("br"),t("span",{staticClass:"line-number"},[s._v("80")]),t("br"),t("span",{staticClass:"line-number"},[s._v("81")]),t("br"),t("span",{staticClass:"line-number"},[s._v("82")]),t("br"),t("span",{staticClass:"line-number"},[s._v("83")]),t("br"),t("span",{staticClass:"line-number"},[s._v("84")]),t("br"),t("span",{staticClass:"line-number"},[s._v("85")]),t("br"),t("span",{staticClass:"line-number"},[s._v("86")]),t("br"),t("span",{staticClass:"line-number"},[s._v("87")]),t("br"),t("span",{staticClass:"line-number"},[s._v("88")]),t("br"),t("span",{staticClass:"line-number"},[s._v("89")]),t("br"),t("span",{staticClass:"line-number"},[s._v("90")]),t("br"),t("span",{staticClass:"line-number"},[s._v("91")]),t("br"),t("span",{staticClass:"line-number"},[s._v("92")]),t("br"),t("span",{staticClass:"line-number"},[s._v("93")]),t("br"),t("span",{staticClass:"line-number"},[s._v("94")]),t("br"),t("span",{staticClass:"line-number"},[s._v("95")]),t("br"),t("span",{staticClass:"line-number"},[s._v("96")]),t("br"),t("span",{staticClass:"line-number"},[s._v("97")]),t("br"),t("span",{staticClass:"line-number"},[s._v("98")]),t("br"),t("span",{staticClass:"line-number"},[s._v("99")]),t("br")])]),t("p",[s._v("Prometheusの設定の詳細については割愛しますが、4-2ににて発行した認証情報は"),t("code",[s._v("tls_config")]),s._v("ならびに"),t("code",[s._v("authorization")]),s._v("で指定しています。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Prometheus講義受講者向け】")]),s._v(" "),t("p",[s._v("Prometheusの講義内で「Prometheusの特徴の1つにサービスディスカバリがあり、監視対象を動的に取得することができる」と話しました。\nそのサービスディスカバリは"),t("code",[s._v("kubernetes_sd_configs")]),s._v("の部分で設定しています。"),t("code",[s._v("role")]),s._v("という概念を利用してKubernetes内の各種リソースを動的に取得します。"),t("code",[s._v("role")]),s._v("で取得できるリソースは以下の5つです。")]),s._v(" "),t("ul",[t("li",[s._v("Node")]),s._v(" "),t("li",[s._v("Service")]),s._v(" "),t("li",[s._v("Endpoints")]),s._v(" "),t("li",[s._v("Pod")]),s._v(" "),t("li",[s._v("Ingress")])])]),s._v(" "),t("p",[t("code",[s._v("kubectl apply -f prometheus.yml")]),s._v("でPrometheusをデプロイし、確認を行います。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f prometheus.yml ")]),s._v("\nservice/prometheus created\ndeployment.apps/prometheus created\nconfigmap/prometheus created\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get all")]),s._v("\nNAME READY STATUS RESTARTS AGE\npod/node-exporter-75rpz "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/node-exporter-p25gq "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/node-exporter-tcbsp "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/prometheus-76b579c56c-r7nps "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\n\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("S"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" AGE\nservice/kubernetes ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".0.1 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v("/TCP 29h\nservice/node-exporter ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".16.136 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("/TCP 115m\nservice/prometheus ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".128.27 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("/TCP 115m\n\nNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE\ndaemonset.apps/node-exporter "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 115m\n\nNAME READY UP-TO-DATE AVAILABLE AGE\ndeployment.apps/prometheus "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" 115m\n\nNAME DESIRED CURRENT READY AGE\nreplicaset.apps/prometheus-76b579c56c "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" 115m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br")])]),t("h3",{attrs:{id:"_4-4-prometheusの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-4-prometheusの確認"}},[s._v("#")]),s._v(" 4-4. Prometheusの確認")]),s._v(" "),t("p",[s._v("ひと通りのアプリケーションのデプロイが完了したので、さっそくアクセスします。Prometheusは"),t("code",[s._v("ClusterIP")]),s._v("の配下にあるため、ポートフォワーディングしてあげます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl port-forward svc/prometheus --address 0.0.0.0 8080:9090")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ブラウザからアクセスし、"),t("code",[s._v("Status")]),s._v("タブの"),t("code",[s._v("Targets")]),s._v("を開いて全て問題なく取得できていれば完了です。\n"),t("img",{attrs:{src:"images/prometheus_main-menu.png",alt:"prometheus_main-nemu"}}),s._v("\nこれでKuberentes上の各コンポーネントに対して監視を行うことが出来ました。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Prometheus講義を受講した人向け】")]),s._v(" "),t("p",[s._v("式ブラウザから各種APIオブジェクトのメトリクス情報を取得してみてください。\nまた、Kubernetes上で動くアプリケーションの監視にはkube-state-metricsやcAdvidsorといったエクスポートを利用します。余裕のある人はPodの監視も行ってみてください。")])]),s._v(" "),t("h2",{attrs:{id:"_5-最後に"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-最後に"}},[s._v("#")]),s._v(" 5. 最後に")]),s._v(" "),t("p",[s._v("Kubernetesの紹介と代表的なオブジェクトである"),t("code",[s._v("Deployment")]),s._v("と"),t("code",[s._v("Service")]),s._v("について簡単に触ってみました。\nKubetenetesでは他にも様々なオブジェクトや設定を使います。")]),s._v(" "),t("p",[s._v("例えば")]),s._v(" "),t("ul",[t("li",[s._v("アプリケーションの環境変数を設定するために"),t("code",[s._v("env")]),s._v("を使う")]),s._v(" "),t("li",[s._v("データベースなどステートフルなpodを稼働させるために"),t("code",[s._v("StatefulSet")]),s._v("を使う")]),s._v(" "),t("li",[s._v("データの永続化のため"),t("code",[s._v("PersistentVolume")]),s._v("や"),t("code",[s._v("PersistentVolumeClaim")]),s._v("を使う")]),s._v(" "),t("li",[s._v("アプリケーションのコンフィグファイルを管理するのに"),t("code",[s._v("ConfigMap")]),s._v("を使う")]),s._v(" "),t("li",[s._v("APIキーやパスワードなど秘密情報を扱うために"),t("code",[s._v("Secret")]),s._v("を使う(ただしキー値はbase64でエンコードされた文字列)")])]),s._v(" "),t("p",[s._v("などなどです。"),t("code",[s._v("PersistentVolume")]),s._v("や"),t("code",[s._v("PersistentVolumeClaim")]),s._v("などはディストリビューションごとに扱い方が違ったりしますし、構築したいアプリケーションによってマニフェストファイルの書き方は様々なので今回は割愛しました。")]),s._v(" "),t("p",[s._v("社内でKubetenetesを使ってアプリケーションを構築する場合はIKEが使いやすいかと思いますので、ぜひチュートリアルなどを眺めてみてください。")]),s._v(" "),t("blockquote",[t("p",[s._v("【参考文献】")]),s._v(" "),t("ol",[t("li",[s._v("Kubernetes完全ガイド/青山信也(インプレス)")]),s._v(" "),t("li",[s._v("イラストでわかるDockerとKubernetes/徳永航平(技術評論社)")]),s._v(" "),t("li",[s._v("Docker/Kubernetes実践コンテナ開発入門/山田明憲(技術評論社)")]),s._v(" "),t("li",[t("a",{attrs:{href:"https://kubernetes.io/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Kubernetes公式ドキュメント"),t("OutboundLink")],1),s._v("/CNCF")]),s._v(" "),t("li",[s._v("Prometheus実践ガイド/仲亀拓馬(テッキーメディア)")])])])],1)}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{403:function(s,t,e){s.exports=e.p+"assets/img/components-of-kubernetes.51120ad2.svg"},404:function(s,t,e){s.exports=e.p+"assets/img/module_03_pods.ccc5ba54.svg"},405:function(s,t,e){s.exports=e.p+"assets/img/image_clusterip.8223e0b9.svg"},406:function(s,t,e){s.exports=e.p+"assets/img/image_nodeport.5357aa05.svg"},407:function(s,t,e){s.exports=e.p+"assets/img/image_loadbalancer.7df1efb3.svg"},536:function(s,t,e){"use strict";e.r(t);var a=e(10),n=Object(a.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"kubernetes-でアプリケーション開発"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#kubernetes-でアプリケーション開発"}},[s._v("#")]),s._v(" Kubernetes でアプリケーション開発")]),s._v(" "),t("h2",{attrs:{id:"_0-まえがき"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-まえがき"}},[s._v("#")]),s._v(" 0. まえがき")]),s._v(" "),t("h3",{attrs:{id:"_0-1-想定している受講者"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-1-想定している受講者"}},[s._v("#")]),s._v(" 0-1. 想定している受講者")]),s._v(" "),t("p",[s._v("本講義では以下の受講者を対象としています。")]),s._v(" "),t("ul",[t("li",[s._v("Kubernetesという名前は知っているがどんなものなのかは知らない")]),s._v(" "),t("li",[s._v("Kubernetes入門しようにも何から始めたらよいのかわからない")]),s._v(" "),t("li",[s._v("Kubernetesの仕組みがわからない")])]),s._v(" "),t("h3",{attrs:{id:"_0-2-前提知識"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-2-前提知識"}},[s._v("#")]),s._v(" 0-2. 前提知識")]),s._v(" "),t("p",[s._v("以下の点を知らないと講義についていけない可能性があります。")]),s._v(" "),t("ul",[t("li",[s._v("Linuxの基本的なコマンド")]),s._v(" "),t("li",[s._v("dockerの基礎")])]),s._v(" "),t("p",[s._v("加えて以下の点を知っていると講義をスムーズに聞けます。")]),s._v(" "),t("ul",[t("li",[s._v("YAMLファイルの読み方/書き方")]),s._v(" "),t("li",[s._v("コンテナアーキテクチャの基礎")])]),s._v(" "),t("h3",{attrs:{id:"_0-3-事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-3-事前準備"}},[s._v("#")]),s._v(" 0-3. 事前準備")]),s._v(" "),t("ul",[t("li",[s._v("docker / docker-compose のインストール")]),s._v(" "),t("li",[s._v("Kubernetes環境\n"),t("ul",[t("li",[s._v("環境構築に自信が無い人katacodaを使ってください\n"),t("ul",[t("li",[t("a",{attrs:{href:"https://www.katacoda.com/courses/kubernetes/playground",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://www.katacoda.com/courses/kubernetes/playground"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("strong",[t("strong",[s._v("外部リソースなのでコピペする際は気を付けてください")])])])])]),s._v(" "),t("li",[s._v("ローカルでkubernetesを動かしたい人はkindを以下の手順で構築してください")])])])]),s._v(" "),t("blockquote",[t("p",[s._v("kindを使ったkubernetes環境の構築")]),s._v(" "),t("p",[s._v("kindはkubernetes in dockerの略です。その名の通り、dockerを使ってkubernetes環境を構築します。\n("),t("a",{attrs:{href:"https://kind.sigs.k8s.io/docs/user/quick-start/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント参照"),t("OutboundLink")],1),s._v(")")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.14.0/kind-linux-amd64")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# chmod +x ./kind")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("dockerホストからkindに対してコマンドを実行したいのでkubectlをdockerホストに入れます")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("試験的にクラスターを構築して正しくインストールされたか確認する")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind create cluster")]),s._v("\nCreating cluster "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n ✓ Ensuring "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" image "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("kindest/node:v1.24.0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" 🖼 \n ✓ Preparing nodes 📦 \n ✓ Writing configuration 📜 \n ✓ Starting control-plane 🕹️ \n ✓ Installing CNI 🔌 \n ✓ Installing StorageClass 💾 \nSet kubectl context to "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind-kind"')]),s._v("\nYou can now use your cluster with:\n\nkubectl cluster-info --context kind-kind\n\nHave a question, bug, or feature request? Let us know"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v(" https://kind.sigs.k8s.io/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#community 🙂")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# docker ps")]),s._v("\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nd76ca5889d8d kindest/node:v1.24.0 "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/local/bin/entr…"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("59")]),s._v(" seconds ago Up "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("49")]),s._v(" seconds "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:35447-"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("6443")]),s._v("/tcp kind-control-plane\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl cluster-info --context kind-kind")]),s._v("\nKubernetes control plane is running at https://127.0.0.1:35447\nCoreDNS is running at https://127.0.0.1:35447/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n\nTo further debug and diagnose cluster problems, use "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'kubectl cluster-info dump'")]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get node")]),s._v("\nNAME STATUS ROLES AGE VERSION\nkind-control-plane Ready control-plane 2m43s v1.24.0\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])]),t("p",[s._v("確認出来たら削除")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind delete cluster")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("bootcamp用のクラスター環境を構築する")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vim cluster.yml ")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("以下の内容を記載する")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Cluster\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kind.x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("k8s.io/v1alpha4\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nodes")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" control"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("plane\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e \n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" worker\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" kindest/node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v1.24.0@sha256"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("p",[s._v("クラスター構築")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kind create cluster --config cluster.yml ")]),s._v("\nCreating cluster "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n ✓ Ensuring "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" image "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("kindest/node:v1.24.0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" 🖼 \n ✓ Preparing nodes 📦 📦 📦 📦 \n ✓ Writing configuration 📜 \n ✓ Starting control-plane 🕹️ \n ✓ Installing CNI 🔌 \n ✓ Installing StorageClass 💾 \n ✓ Joining worker nodes 🚜 \nSet kubectl context to "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"kind-kind"')]),s._v("\nYou can now use your cluster with:\n\nkubectl cluster-info --context kind-kind\n\nThanks "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" using kind"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v(" 😊\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl cluster-info --context kind-kind")]),s._v("\nKubernetes control plane is running at https://127.0.0.1:46863\nCoreDNS is running at https://127.0.0.1:46863/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n\nTo further debug and diagnose cluster problems, use "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'kubectl cluster-info dump'")]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get node")]),s._v("\nNAME STATUS ROLES AGE VERSION\nkind-control-plane Ready control-plane 72s v1.24.0\nkind-worker Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 33s v1.24.0\nkind-worker2 Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 33s v1.24.0\nkind-worker3 Ready "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 32s v1.24.0\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])])]),s._v(" "),t("h2",{attrs:{id:"_1-kubernetesとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-kubernetesとは"}},[s._v("#")]),s._v(" 1. Kubernetesとは")]),s._v(" "),t("p",[s._v("Kubernetesは複数サーバで構成された基盤上でコンテナ群を一元管理するためツール「コンテナオーケストレーションツール」と呼ばれるものです。最近ではdockerを使ってコンテナ単位でアプリケーションを実装することが多くなりましたが、docker単体では複数台のdockerホスト上でコンテナ群を一元管理することができません。そのため複数ホストで構成される規模のプロダクションに耐えられるシステムをdocker単体で構築することは困難とされてきました。そこで複数のdockerホストに跨ってコンテナアプリケーションをデプロイ、スケーリング、ネットワーク管理機能などをするオーケストレーションツールが登場しました。")]),s._v(" "),t("p",[s._v("Kubernetesは元々Googleがアプリケーションデプロイに利用していたBorgと呼ばれるクラスターマネージャーをOSS化したもので、現在はLinux Foundation傘下にあるCNCF(Cloud Native Computing Foundation)が管理しています。ランクはGraduatedで成熟したCNCFプロジェクトとなっています。")]),s._v(" "),t("blockquote",[t("p",[s._v("【豆知識】")]),s._v(" "),t("p",[s._v("Kubernetesはギリシャ語で「操舵士」や「パイロット」を意味し、ロゴは操舵士にちなんで舵をモチーフにされています。7つのスポークは当初のKubernetesのコードネーム「Project Seven」にちなんでいます。")])]),s._v(" "),t("p",[s._v("KubernetesをベースにカスタマイズしたKubernetesサービスを最近ではKaaS(Kubernetes as a Service)と言い、AWSやGCPなどの各クラウドベンダで提供されています。")]),s._v(" "),t("ul",[t("li",[s._v("Amazon EKS")]),s._v(" "),t("li",[s._v("Google Kubernetes Engine(GKE)")]),s._v(" "),t("li",[s._v("Azure Kubernetes Service(AKS)")])]),s._v(" "),t("p",[s._v("先ほど「カスタマイズ」と言いましたが、Kubernetesはそれ単体で完成するものではなくログ基盤にfluentdとelasticsearchを使ったり、それぞれのクラウドサービスとの繋ぎこみなど提供元によって機能やスペックが異なります。このようにカスタマイズされたKubernetes基盤のことをLinuxのディストリビューションに例えて「Kubernetesディストリビューション」と呼びます。")]),s._v(" "),t("p",[s._v("IIJでも社内向けのKubernetes基盤としてIKE(IIJ Kubernetes Engine)の運用と導入が進んでいます。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://www.iij.ad.jp/dev/report/iir/040/03.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIR"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://eng-blog.iij.ad.jp/kubernetes",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIJエンジニアブログ"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("従来のVMでのシステム構築と比べてKubernetesを利用することでシステム開発・管理が格段に楽になります。例えば従来のシステム構築では、どのVMに何を割り当てるかのリソース計算を人が考えなければならず、システムがスケーリングするたびに多くの労力を使いましたが、Kubernetesではマシンリソースやネットワークをプールとして扱い自動で管理するため、システムのスケーリングに柔軟に対応することができます("),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/scale/scale-intro/",target:"_blank",rel:"noopener noreferrer"}},[s._v("詳細"),t("OutboundLink")],1),s._v(")。また、従来ではシステムのアップデートを行うたびに「サービス停止→アップデート→サービス再開」という手順でアップデートをしていたが、Kubernetesではサービスを停止することなくシステムアップデートを行うことができ、サービス可用性を高めてくれます("),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/update/update-intro/",target:"_blank",rel:"noopener noreferrer"}},[s._v("詳細"),t("OutboundLink")],1),s._v(")。")]),s._v(" "),t("p",[s._v("上記以外にも多くのメリットがあり、Kubernetesでシステム開発を行うとアプリケーションの構築作業が劇的に減る他、APIなどからの自動デプロイなども非常に簡単に行うことができます。")]),s._v(" "),t("h2",{attrs:{id:"_2-kubernetesの基本構造"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-kubernetesの基本構造"}},[s._v("#")]),s._v(" 2. Kubernetesの基本構造")]),s._v(" "),t("h3",{attrs:{id:"_2-1-宣言的な構成管理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-宣言的な構成管理"}},[s._v("#")]),s._v(" 2-1. 宣言的な構成管理")]),s._v(" "),t("p",[s._v("Kubernetes上にアプリケーションをデプロイする際、その構成管理は宣言的に行われます。デプロイしたい人は「アプリケーションやそれを構成するコンテナ群はこのような配置であるべき」という宣言(manifest)を"),t("strong",[s._v("マニフェストファイル")]),s._v("に記載することで、Kubernetesはマニフェストファイルに沿った構成を宣言どおりにデプロイします。このような構成管理方法はIaC(Infrastructure as a Code)と呼ばれており、Ansible同様に冪等性の確保や自動化に貢献しています。このような構成管理方法の主なメリットはGitによるバージョン管理のしやすさが挙げられます。")]),s._v(" "),t("blockquote",[t("p",[s._v("Ansibleで「Playbook」と呼ばれているものがKubernetesでいう「Manifest」です。")])]),s._v(" "),t("h3",{attrs:{id:"_2-2-コントロールプレーンとワーカーノード"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-コントロールプレーンとワーカーノード"}},[s._v("#")]),s._v(" 2-2. コントロールプレーンとワーカーノード")]),s._v(" "),t("p",[s._v("Kubernetesは大きく分けて2つの要素で構成されています。"),t("strong",[s._v("コントロールプレーン")]),s._v("と"),t("strong",[s._v("ワーカーノード")]),s._v("です。")]),s._v(" "),t("blockquote",[t("p",[s._v("文献によってはコントロールプレーンのことを"),t("strong",[s._v("マスターノード")]),s._v("と表記することがありますが、同じ意味なので誤解の無いように注意してください。")])]),s._v(" "),t("p",[t("img",{attrs:{src:e(403),alt:"component"}})]),s._v(" "),t("h4",{attrs:{id:"_2-2-1-コントロールプレーン"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-1-コントロールプレーン"}},[s._v("#")]),s._v(" 2-2-1. コントロールプレーン")]),s._v(" "),t("p",[s._v("コントロールプレーンはKubernetesクラスター全体の状態の管理を行うことが主な仕事です。例えばアプリケーション開発者が宣言したマニフェストファイルどおりに作られたPodがワーカーノードに割り当てられているかを監視し、割り当てられていなかった場合はそのPodを実行するノードを各のノードのリソース状況を考慮して割り当てます。このようなコンポーネントを"),t("strong",[s._v("kube-schduler")]),s._v("と言います。他にもマニフェストファイルによって宣言された構成情報を閲覧/編集するためのAPIサーバの役割を果たす"),t("strong",[s._v("kube-apiserver")]),s._v("などがあります。(他のコンポーネントも知りたい人は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/overview/components/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("へ)")]),s._v(" "),t("blockquote",[t("p",[s._v("【Podとは】")]),s._v(" "),t("p",[s._v("Kubernetesはコンテナを「pod」と呼ばれる単位で管理します。podにはいくつかのコンテナの集まりで、同じpodに所属するコンテナ同士はlocalhostでお互いに通信することができます。\n"),t("img",{attrs:{src:e(404),alt:"Pod"}}),s._v("\nPodにどのようなコンテナを同居させるのかは設計次第ですが、例えばアプリケーションのログを集めるコンテナを同じpodに同居させたり、nginxなどwebのフロントになるアプリケーションを同居させたりします。")])]),s._v(" "),t("h4",{attrs:{id:"_2-2-2-ワーカーノード"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-2-ワーカーノード"}},[s._v("#")]),s._v(" 2-2-2. ワーカーノード")]),s._v(" "),t("p",[s._v("ワーカーノードはPodの管理やPodの実行環境/通信機能を提供することが主な仕事です。例えばコントロールプレーンコンポーネントである"),t("strong",[s._v("kube-apiserver")]),s._v("から受け取った構成情報どおりにPodが稼働するように管理します。このようなコンポーネントを"),t("strong",[s._v("kubelet")]),s._v("と言います。ただしkubeletは実際にPodを作成したり、そのネットワーク環境を構築することはせず、あくまでもノード上のPod状態を維持するように管理することが仕事です。実際にPodを作ったりするコンポーネントを"),t("strong",[s._v("コンテナランタイム")]),s._v("と言います。他には、後程出てきますがService宛の通信を稼働中のPod群へ転送させる"),t("strong",[s._v("kube-proxy")]),s._v("などがあります。")]),s._v(" "),t("h3",{attrs:{id:"_2-3-kubernetesの基本構造まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-3-kubernetesの基本構造まとめ"}},[s._v("#")]),s._v(" 2-3. Kubernetesの基本構造まとめ")]),s._v(" "),t("p",[s._v("Kubernetesは主に"),t("strong",[s._v("コントロールプレーン")]),s._v("と"),t("strong",[s._v("ワーカーノード")]),s._v("に分けられており、コントロールプレーンはクラスター全体の状態管理、ワーカーノードはコントロールプレーンからの指示通りにユーザによって宣言された構成を作り上げることが役割でした。そして、それぞれの役割を果たすために多くのコンポーネントが内蔵されている、という話でした。各コンポーネントについてもっと詳しく知りたい方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/architecture/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参照してください。")]),s._v(" "),t("h2",{attrs:{id:"_3-マニフェストファイルの書き方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-マニフェストファイルの書き方"}},[s._v("#")]),s._v(" 3. マニフェストファイルの書き方")]),s._v(" "),t("h3",{attrs:{id:"_3-1-kubernetesオブジェクト"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-1-kubernetesオブジェクト"}},[s._v("#")]),s._v(" 3-1. Kubernetesオブジェクト")]),s._v(" "),t("p",[s._v("先ほども説明したように、Kubernetes上にPodなどをデプロイする際、ユーザはマニフェストファイルを書く必要があります。マニフェストファイルではいくつかのKubernetesオブジェクトを組み合わせて、ユーザの意図する状態を記載します。マニフェストファイルに記載されたKubernetesオブジェクトはコントロールプレーンによって読み取られ、読み取られたKubernetesオブジェクトが存在し続けるようにクラスター全体を管理します。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Kubernetesオブジェクト】")]),s._v(" "),t("p",[s._v("Kubernetesオブジェクトはクラスターの状態を表現するパーツです。以下にKubernetesオブジェクトの例と簡単な説明を記載します")]),s._v(" "),t("ul",[t("li",[s._v("ReplicaSet: Pod群の稼働状況を管理する")]),s._v(" "),t("li",[s._v("Deployment:バージョンに相当するReplicaSetを管理する")]),s._v(" "),t("li",[s._v("CronJob:定期実行するpodを管理する")]),s._v(" "),t("li",[s._v("Service:特定のラベルを持ち、サービスを提供できる状態のPod群への接続を提供する")]),s._v(" "),t("li",[s._v("Ingress:証明書やドメイン名を通して外部からの通信を制御する")]),s._v(" "),t("li",[s._v("PersistentVolume:ストレージなどの永続化volumeを管理する")])]),s._v(" "),t("p",[s._v("代表的な物を上げましたが、他にも色々あります。興味のある方は"),t("a",{attrs:{href:"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式サイト"),t("OutboundLink")],1),s._v("参照。")])]),s._v(" "),t("h3",{attrs:{id:"_3-2-deployment"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-2-deployment"}},[s._v("#")]),s._v(" 3-2. Deployment")]),s._v(" "),t("p",[s._v("上でも述べた通りDeploymentはPodの稼働状況を管理するオブジェクトです。予め起動するPodの数を指定することで、何かしらの原因でPodが消失しても自動で立ち上げ直してくれたり、逆に多すぎる場合は終了させます。")]),s._v(" "),t("p",[s._v("現在稼働しているPodは、kubectlというcliツールを使って確認できます。\n今は何も稼働してないはずです。")]),s._v(" "),t("blockquote",[t("p",[s._v("【kubectl】")]),s._v(" "),t("p",[s._v("KubernetesのコントロールプレーンにはKubernetesクラスターの構成管理情報にアクセスするためのエンドポイントを提供する"),t("strong",[s._v("kube-apiserver")]),s._v("がありますが、生身の人間がAPIを生で叩いて"),t("strong",[s._v("kube-apiserver")]),s._v("にアクセスするのは少しキツイものがあります。そこでAPIをコマンドで操作できる"),t("strong",[s._v("kubectl")]),s._v("というものがあり、kubectlを使うことによりコマンドベースでAPIを叩くことができます。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl get pods\nNo resources found in default namespace.\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("以下のようなマニフェストファイルを"),t("code",[s._v("app.yml")]),s._v("として作成し、Kubernetesクラスターにデプロイしてみましょう。\n"),t("code",[s._v("image")]),s._v("として指定しているのはサンプル用の簡単なアプリケーションです。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Deployment\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("replicas")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("app\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" registry.k8s.io/echoserver"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1.4")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("restartPolicy")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Always\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("blockquote",[t("p",[s._v("【Deploymentにおける必須フィールド】")]),s._v(" "),t("p",[s._v("Kubernetesオブジェクトをマニフェストファイルに記載する際、必ず以下のフィールドに値をセットする必要があります")]),s._v(" "),t("ul",[t("li",[s._v("apiVersion:オブジェクトのAPIVersionを指定")]),s._v(" "),t("li",[s._v("kind:どのオブジェクトを作るかを指定")]),s._v(" "),t("li",[s._v("metadata:オブジェクトを特定するための情報を指定")]),s._v(" "),t("li",[s._v("spec:オブジェクトの状態を指定")])])]),s._v(" "),t("p",[t("code",[s._v("apiVersion")]),s._v("にはオブジェクトのAPIVersionを書きます。\nオブジェクトAPIがどのAPIGROUPに属しているかでapiVersionの書き方が変わってきます。\n今回はDeploymentのオブジェクトなのでそのAPIグループを調べます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl api-resources\nNAME SHORTNAMES APIGROUP NAMESPACED KIND\nbindings true Binding\ncomponentstatuses cs false ComponentStatus\nconfigmaps cm true ConfigMap\n...\ndeployments deploy apps true Deployment\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("p",[s._v("ここではDeploymentが"),t("code",[s._v("apps")]),s._v("に属しているということが分かりました。\nもしAPIGROUPが空の場合はCore groupに属するため、"),t("code",[s._v("apiVersion: v1")]),s._v("で問題ないです。\n次にAPIGROUPで利用可能なversionを調べます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl api-versions | grep apps\napps/v1\napps/v1beta1\napps/v1beta2\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("p",[s._v("ここでは3つほど出ましたが、この中の最も新しいversionを使ってください。\n今回は"),t("code",[s._v("apps/v1")]),s._v("を使ってマニフェストファイルを作りました。")]),s._v(" "),t("p",[s._v("yamlを作成したら、以下のコマンドでデプロイできます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl apply -f app.yml\ndeployment.apps/bootcamp created\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("別端末で"),t("code",[s._v("get pods")]),s._v("しながらpodが作られる様子を見てみましょう"),t("code",[s._v("-w")]),s._v("をつけると自動で表示を更新してくれます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl get pods -w\nNAME READY STATUS RESTARTS AGE\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 11s\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 11s\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 83s\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 84s\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("p",[t("code",[s._v("Running")]),s._v("となっていれば無事にアプリケーションが起動しました。今回は"),t("code",[s._v("replicas")]),s._v("に"),t("code",[s._v("2")]),s._v("を指定したのでpodが2個起動しています。"),t("code",[s._v("replicas")]),s._v("の値を変えて再度"),t("code",[s._v("kubectl apply")]),s._v("して遊んでみましょう。")]),s._v(" "),t("h3",{attrs:{id:"_3-3-service"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-3-service"}},[s._v("#")]),s._v(" 3-3. Service")]),s._v(" "),t("p",[s._v("Podの起動ができましたので、次はPodへのアクセスを試みます。KubernetesクラスターではPod群へのサービスディスカバリーの方法としてServiceオブジェクトが用いられます。Serviceを利用することでPod群に共通のIPアドレスを割り当て、まるで一つの「サービス」であるかのようにアクセスできるようになります。")]),s._v(" "),t("blockquote",[t("p",[s._v("【PodとServiceの関係】")]),s._v(" "),t("p",[s._v("Podは生成の度にIPアドレスが割り振られます。これは何かしらの理由でPodが落ちて別のPodが再生成されるときにもIPアドレスが割り振られますが、落ちたPodと同じIPアドレスが割り振られるとは限りません。こうなった場合に新しいPodへアクセスしたい別Podは新しいPodのIPアドレスがわからなくなってしまいます。ServiceはこのようなPodを共通のIPアドレスで管理し、Podへのアクセスやロードバランシングを行う役割を持っています。また、Serviceの生成によりKubernetesクラスター内の"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/tasks/administer-cluster/coredns/",target:"_blank",rel:"noopener noreferrer"}},[s._v("CoreDNS"),t("OutboundLink")],1),s._v("のA/AAAAレコードやPod内の"),t("code",[s._v("resolve.conf")]),s._v("が自動的に書き換えられるため、Service名を使ってPodへアクセスすることも可能になります。")])]),s._v(" "),t("blockquote",[t("p",[s._v("【ServiceからPodへの通信の受け渡し】")]),s._v(" "),t("p",[s._v("Serviceが作られるとService宛の通信がPodへ転送されますが、その仕組みは"),t("strong",[s._v("ワーカーノード")]),s._v("内のコンポーネントである"),t("strong",[s._v("kube-proxy")]),s._v("によって実現されます。すべてのServiceは基本的にClusterIP(あとで説明します)によるVIPの保持が義務付けられており、またClusterIP配下のPodのIPアドレスはendpointに記載されています。そして、ClusterIPからPodの持つIPへの振り替えを"),t("strong",[s._v("kube-proxy")]),s._v("が行います。"),t("strong",[s._v("kube-proxy")]),s._v("の振り替え方式はいくつか選択肢がありますが、デフォルトの"),t("strong",[s._v("iptableモード")]),s._v("ではiptablesのchainがあり、これによってパケットが転送されます。他のモードについて知りたい方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参照してください。")])]),s._v(" "),t("p",[s._v("先ほどと同じようにServiceのマニフェストファイルを作りましょう。今回は"),t("code",[s._v("service.yml")]),s._v("とします。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("svc\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterIP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("p",[s._v("Serviceオブジェクトの中には様々なサービスタイプ種類があり、今回は"),t("code",[s._v("ClusterIP")]),s._v("というサービスタイプを利用しています。ClusterIPはServiceにおけるデフォルト設定であり、明示的に記載が無ければ"),t("code",[s._v("type: ClusterIP")]),s._v("が設定されることに注意してください。")]),s._v(" "),t("blockquote",[t("p",[s._v("【サービスタイプ】")]),s._v(" "),t("p",[s._v("それぞれのServiceの特徴について簡単に触れます。少し長くなるので講義では"),t("code",[s._v("ClusterIP")]),s._v("のみを説明しますが、興味のある人は他のサービスタイプも読んでみてください。")]),s._v(" "),t("h4",{attrs:{id:"clusterip"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#clusterip"}},[s._v("#")]),s._v(" ClusterIP")]),s._v(" "),t("p",[s._v("ClusterIPによって割り振られるIPアドレスはKubernetesクラスター内でのみ有効です。主にクラスター外からアクセスする必要のない箇所などでクラスター内ロードバランスをする際に利用されます。\n"),t("img",{attrs:{src:e(405),alt:"ClusterIP"}})]),s._v(" "),t("h4",{attrs:{id:"nodeport"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#nodeport"}},[s._v("#")]),s._v(" NodePort")]),s._v(" "),t("p",[s._v("ClusterIPを作った上で、全node各々の"),t("code",[s._v("")]),s._v("で受信したアクセスをServiceへ転送することで、クラスタ外からアクセスできるようにします。Docker Swarmでいうところの"),t("code",[s._v("Expose")]),s._v("です。図では全Kubernetes nodeの"),t("code",[s._v("port:30080")]),s._v("へのアクセスを"),t("code",[s._v("NodePort Service")]),s._v("に転送しています。\n"),t("img",{attrs:{src:e(406),alt:"NodePort"}})]),s._v(" "),t("h4",{attrs:{id:"loadbalancer"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#loadbalancer"}},[s._v("#")]),s._v(" LoadBalancer")]),s._v(" "),t("p",[s._v("Kubernetesクラスター外のロードバランサーより払い出された仮想IPアドレスを利用してクラスター外からのアクセスを可能にします。NodePortでは各nodeに"),t("code",[s._v(":")]),s._v("が割り振られ、ユーザはいずれかのアドレス宛にアクセスするため、アクセスしているnodeで障害が起きた際にそのnodeを利用しているユーザはサービスを利用できなくなります。それに対して"),t("code",[s._v("type: LoadBalancer")]),s._v("は、ユーザがクラスター外のロードバランサーから払い出されたIPアドレスのみを知っておくだけでサービスを利用することができます。また、nodeで障害が起きてもそのnodeの切り離しを行うようにクラスター外のロードバランサーを設定することで、ユーザはサービスを継続して利用することができます(ただし従来のロードバランサー+仮想マシンの組み合わせ同様に、障害検知から除外までの間は通信断が発生します)。ここでいうクラスター外のロードバランサーはプロバイダに依存しており、たとえばGCPの場合はGCLBが使われています。\n"),t("img",{attrs:{src:e(407),alt:"LoadBalancer"}})])]),s._v(" "),t("p",[s._v("それでは同じようにapplyしてから"),t("code",[s._v("Service")]),s._v("の稼働状況を確認します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl apply -f service.yml\nservice/bootcamp-svc created\n$ kubectl get svc\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("S"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" AGE\nbootcamp-svc ClusterIP xxx.xxx.xxx.xxx "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/TCP 1h\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("p",[s._v("次に実際にPodにアクセスしてみましょう。"),t("code",[s._v("kubectl proxy")]),s._v("でコントロールプレーンのAPIサーバにポートフォワーディングします。先ほども説明しましたが"),t("code",[s._v("type: ClusterIP")]),s._v("は外から直接アクセスができません。そのため手元のホストからコントロールプレーンまでをポートフォワードし、コントロールプレーンからPodまでをREST APIを使って通信させます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl proxy\nStarting to serve on 127.0.0.1:8001\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("このプロキシ機能はServiceへのアクセスをRESTとして"),t("code",[s._v("/api/v1/namespaces//services/::/proxy/")]),s._v("と表現しているため、今回は"),t("code",[s._v("http://127.0.0.1:8001/api/v1/namespaces//services/bootcamp-svc/proxy/")]),s._v("へアクセスすることでコンテンツを取得することができます。")]),s._v(" "),t("blockquote",[t("p",[t("code",[s._v("")]),s._v("にはデプロイ先のnamespaceを入力します。\nnamespaceがわからない場合は"),t("code",[s._v("kubectl config get-contexts")]),s._v("から探してください。"),t("code",[s._v("CURRENT")]),s._v("に米印が付いているものがいま作業しているコンテキストになります。もし"),t("code",[s._v("NAMESPACE")]),s._v("の欄に何もなければ"),t("code",[s._v("namespace: default")]),s._v("ということになります。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl config get-contexts\nCURRENT NAME CLUSTER AUTHINFO NAMESPACE\n minikube minikube nirazuka\n* nira nirakube nirazuka nirazuka\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])])]),s._v(" "),t("p",[s._v("katacodeを使っている場合、RESTを辿ることができないため"),t("code",[s._v("kubectl port-foward")]),s._v("を利用します。"),t("code",[s._v("kubectl port-foward")]),s._v("はローカルのポートをPodやServiceに直接フォワーディングすることができます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ kubectl port-forward service/bootcamp-svc --address=0.0.0.0 :80\nForwarding from 0.0.0.0:35715 -> 8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("あとはTerminal横の「+」ボタンから「select port to view on Host 1」を選択し、表示されているポートへアクセスすればコンテンツを取得することができます。")]),s._v(" "),t("p",[t("code",[s._v("Hello Kubernetes!")]),s._v(" が表示されたでしょうか。無事にpodにアクセスすることができました。")]),s._v(" "),t("blockquote",[t("p",[s._v("今回はServiceでアプリケーションを公開しましたが、本来はServiceの上にIngressを作って公開することが推奨されています。\nIngressを利用するとSSLの設定やVirtualHostの設定などを行えるようになります。\n興味のある方は"),t("a",{attrs:{href:"https://kubernetes.io/ja/docs/concepts/services-networking/ingress/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ページ"),t("OutboundLink")],1),s._v("を参考に触ってみて下さい。")])]),s._v(" "),t("h3",{attrs:{id:"_3-4-podを削除してみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-4-podを削除してみる"}},[s._v("#")]),s._v(" 3-4. Podを削除してみる")]),s._v(" "),t("p",[s._v("試しに手動で無理やりpodを削除してみましょう。"),t("code",[s._v("kubectl get pods -w")]),s._v("で確認しながら、以下のコマンドでpodを削除してみます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl delete pods "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("pod-name"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("例によってpod-nameはコピペしてください。"),t("code",[s._v("kubectl get pods -w")]),s._v("しているとPodの数が"),t("code",[s._v("replicas")]),s._v("の設定値に合うように新しく起動される様子が分かります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ kubectl get pods -w\nNAME READY STATUS RESTARTS AGE\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-tq2fs "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Terminating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Pending "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Pending "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 ContainerCreating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 0s\nbootcamp-6bcddb7cf8-ffj7f "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 7s\nbootcamp-6bcddb7cf8-jpzg5 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("/1 Terminating "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 23m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("p",[t("code",[s._v("bootcamp-6bcddb7cf8-jpzg5")]),s._v("が手動で削除したpodです。"),t("code",[s._v("bootcamp-6bcddb7cf8-jpzg5")]),s._v("の削除が始まった途端に新しく"),t("code",[s._v("bootcamp-6bcddb7cf8-ffj7f")]),s._v("というpodを立てようとしているのが分かります。")]),s._v(" "),t("p",[s._v("このようにpodがエラーで停止したり、新しいアプリケーションのデプロイなどでpodを停止してもすぐさま"),t("code",[s._v("ReplicaSet")]),s._v("が状態を修復してくれます。\nそれだけではなく、前段の"),t("code",[s._v("Service")]),s._v("がpodの状態を監視しながら通信を流す先を決めてくれるため、一部のpodが停止している間も自動的に生きているpodに通信を流してくれます。")]),s._v(" "),t("p",[s._v("そのためユーザーに一切影響なくpodの停止と復旧が全て自動で可能になっています。このようなインフラをKubernetesとコンテナなしで構築するのはかなり困難です。")]),s._v(" "),t("h2",{attrs:{id:"_4-応用-kubernetesの監視"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-応用-kubernetesの監視"}},[s._v("#")]),s._v(" 4. 応用(Kubernetesの監視)")]),s._v(" "),t("p",[s._v("ここからは本格的なアプリケーションのデプロイを体験してもらいます。katacodeでやっている方はうまくいかないことがあるため本項目は飛ばしてください。")]),s._v(" "),t("p",[s._v("今回Kubernetes上に構築するアプリケーションは監視ツールのPrometheusで、以下の順序でデプロイします。(マニフェストファイルは"),t("a",{attrs:{href:"https://www.hanmoto.com/bd/isbn/9784910313009",target:"_blank",rel:"noopener noreferrer"}},[s._v("Prometheus実践ガイド"),t("OutboundLink")],1),s._v("の内容を一部改変したものを利用しています)")]),s._v(" "),t("ol",[t("li",[s._v("node exporterのデプロイ")]),s._v(" "),t("li",[s._v("RBAC認可を使ってリソースにアクセスするためのアカウントをデプロイ")]),s._v(" "),t("li",[s._v("Prometheusのデプロイ")])]),s._v(" "),t("h3",{attrs:{id:"_4-1-node-exporterのデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-node-exporterのデプロイ"}},[s._v("#")]),s._v(" 4-1. node exporterのデプロイ")]),s._v(" "),t("p",[s._v("node exporterは各ノードのメトリクス情報を収集するツール(exporter)です。これを各nodeに配置する必要がありますが、"),t("code",[s._v("Deployment")]),s._v("オブジェクトを利用すると配置nodeの指定を都度行う必要があり煩雑です。そのため、ここでは"),t("code",[s._v("DeamonSet")]),s._v("オブジェクトを利用します。"),t("code",[s._v("DeamonSet")]),s._v("オブジェクトは各ノードに等しくPodを配置するオブジェクトです。"),t("code",[s._v("node-exporter.yml")]),s._v("という名前で以下の内容のマニフェストファイルを作成します。")]),s._v(" "),t("div",{staticClass:"language-yml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" DaemonSet\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'prom/node-exporter:v1.3.1'")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hostNetwork")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hostPID")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tolerations")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("key")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("role.kubernetes.io/control"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("plane\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("operator")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Exists\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("value")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("''")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("effect")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" NoSchedule\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" node"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("exporter\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br")])]),t("p",[s._v("各ノードに対して"),t("code",[s._v("prom/node-exporter:v1.3.1")]),s._v("というコンテナを1つずつデプロイさせています。"),t("code",[s._v("hostNetwork")]),s._v("と"),t("code",[s._v("hostPID")]),s._v("を"),t("code",[s._v("true")]),s._v("にすることでノードとコンテナのネットワーク/プロセスIDを共有させます。これは通常、コンテナはホストの環境とプロセス等が分離された状態になっているため、共有させないとPodからノードの情報を取得することができためです。"),t("code",[s._v("node-exporter")]),s._v("は外部から接続させる必要がないため、"),t("code",[s._v("Service")]),s._v("は"),t("code",[s._v("CluserIP")]),s._v("を指定しています。")]),s._v(" "),t("p",[s._v("準備が出来たら"),t("code",[s._v("kubectl apply -f node-exporter.yml")]),s._v("でデプロイします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f node-exporter.yml")]),s._v("\ndaemonset.apps/node-exporter created\nservice/node-exporter created\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get pods")]),s._v("\nNAME READY STATUS RESTARTS AGE\nnode-exporter-75rpz "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\nnode-exporter-p25gq "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\nnode-exporter-tcbsp "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 47m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br")])]),t("h3",{attrs:{id:"_4-2-rbac認可を使ってリソースにアクセスするためのアカウントをデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-2-rbac認可を使ってリソースにアクセスするためのアカウントをデプロイ"}},[s._v("#")]),s._v(" 4-2. RBAC認可を使ってリソースにアクセスするためのアカウントをデプロイ")]),s._v(" "),t("p",[s._v("KubernetesはRole Based Access Control(RBAC)といわれる、各種リソースへのアクセス制御をユーザロールベースで行っています。そのため、監視に必要なリソースへのアクセスに必要な権限をユーザに付与する必要があります。ここでは権限の定義を行う"),t("code",[s._v("ClusterRole")]),s._v("、権限とユーザとの紐づけを行う"),t("code",[s._v("ClusterRoleBind")]),s._v("という二つのオブジェクトを利用します。"),t("code",[s._v("role-based-access-control.yml")]),s._v("という名前でマニフェストファイルを作り、以下の内容を記載します。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRole\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("rules")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroups")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("resources")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" nodes\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" services\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" endpoints\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" pods\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" metrics\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" nodes/metrics\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"list"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"watch"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroups")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" extensions\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("resources")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" ingresses\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"list"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"watch"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nonResourceURLs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" /metrics\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("verbs")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"get"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRoleBinding\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("roleRef")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiGroup")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rbac.authorization.k8s.io\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterRole\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("subjects")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ServiceAccount\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ServiceAccount\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br")])]),t("p",[t("code",[s._v("default")]),s._v("namespace上の"),t("code",[s._v("prometheus")]),s._v("というアカウントに対して、各種リソースへの参照権限を付与する内容になります。"),t("code",[s._v("kubectl apply -f role-based-access-control.yml")]),s._v("を実行してデプロイします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f role-based-access-control.yml ")]),s._v("\nclusterrole.rbac.authorization.k8s.io/prometheus created\nclusterrolebinding.rbac.authorization.k8s.io/prometheus created\nserviceaccount/prometheus created\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("p",[s._v("これにより、API Serverなどへのアクセスするための認証情報が払い出されました。")]),s._v(" "),t("h3",{attrs:{id:"_4-3-prometheusのデプロイ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-3-prometheusのデプロイ"}},[s._v("#")]),s._v(" 4-3. Prometheusのデプロイ")]),s._v(" "),t("p",[s._v("最後にPrometheusのデプロイを行います。Prometheusのデプロイには"),t("code",[s._v("Deployment")]),s._v("と"),t("code",[s._v("Service")]),s._v("オブジェクトを利用しますが、Prometheus自体の設定ファイルは"),t("code",[s._v("ConfigMap")]),s._v("というオブジェクトを利用して定義します。これを利用することによりコンフィグファイルをマニフェストファイルとして管理することができ、さらにPrometheusに反映させることが出来ます。"),t("code",[s._v("prometheus.yml")]),s._v("というマニフェストファイルを作り以下の内容を記載します。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" apps/v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Deployment\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("namespace")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" default\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("replicas")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("matchLabels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("serviceAccountName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containers")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prom/prometheus"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("v2.33.3\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("imagePullPolicy")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" IfNotPresent\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("args")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("config.file=/prometheus/prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("log.level=debug\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("web.enable"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("lifecycle\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("containerPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("volumeMounts")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mountPath")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /prometheus/prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("subPath")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus.yml\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("volumes")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("configMap")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" Service\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("spec")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" http\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("port")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("protocol")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" TCP\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("targetPort")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("selector")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ClusterIP\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("apiVersion")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" v1\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("kind")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ConfigMap\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("metadata")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("labels")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("app")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" prometheus\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("data")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("prometheus.yml")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("|")]),t("span",{pre:!0,attrs:{class:"token scalar string"}},[s._v("\n global:\n scrape_interval: 15s\n scrape_configs:\n - job_name: 'prometheus'\n kubernetes_sd_configs:\n - role: pod\n relabel_configs:\n - source_labels: [__meta_kubernetes_pod_name]\n regex: prometheus-.+\n action: keep\n - job_name: 'apiserver'\n kubernetes_sd_configs:\n - role: service\n scheme: https\n tls_config:\n ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n authorization:\n credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n relabel_configs:\n - source_labels:\n - __meta_kubernetes_namespace\n - __meta_kubernetes_service_name\n - __meta_kubernetes_service_port_name\n action: keep\n regex: default;kubernetes;https\n - job_name: 'node-exporter'\n scheme: http\n kubernetes_sd_configs:\n - role: node\n relabel_configs:\n - source_labels: [__address__]\n action: replace\n regex: (.+):.+\n replacement: ${1}:9100\n target_label: __address__")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br"),t("span",{staticClass:"line-number"},[s._v("49")]),t("br"),t("span",{staticClass:"line-number"},[s._v("50")]),t("br"),t("span",{staticClass:"line-number"},[s._v("51")]),t("br"),t("span",{staticClass:"line-number"},[s._v("52")]),t("br"),t("span",{staticClass:"line-number"},[s._v("53")]),t("br"),t("span",{staticClass:"line-number"},[s._v("54")]),t("br"),t("span",{staticClass:"line-number"},[s._v("55")]),t("br"),t("span",{staticClass:"line-number"},[s._v("56")]),t("br"),t("span",{staticClass:"line-number"},[s._v("57")]),t("br"),t("span",{staticClass:"line-number"},[s._v("58")]),t("br"),t("span",{staticClass:"line-number"},[s._v("59")]),t("br"),t("span",{staticClass:"line-number"},[s._v("60")]),t("br"),t("span",{staticClass:"line-number"},[s._v("61")]),t("br"),t("span",{staticClass:"line-number"},[s._v("62")]),t("br"),t("span",{staticClass:"line-number"},[s._v("63")]),t("br"),t("span",{staticClass:"line-number"},[s._v("64")]),t("br"),t("span",{staticClass:"line-number"},[s._v("65")]),t("br"),t("span",{staticClass:"line-number"},[s._v("66")]),t("br"),t("span",{staticClass:"line-number"},[s._v("67")]),t("br"),t("span",{staticClass:"line-number"},[s._v("68")]),t("br"),t("span",{staticClass:"line-number"},[s._v("69")]),t("br"),t("span",{staticClass:"line-number"},[s._v("70")]),t("br"),t("span",{staticClass:"line-number"},[s._v("71")]),t("br"),t("span",{staticClass:"line-number"},[s._v("72")]),t("br"),t("span",{staticClass:"line-number"},[s._v("73")]),t("br"),t("span",{staticClass:"line-number"},[s._v("74")]),t("br"),t("span",{staticClass:"line-number"},[s._v("75")]),t("br"),t("span",{staticClass:"line-number"},[s._v("76")]),t("br"),t("span",{staticClass:"line-number"},[s._v("77")]),t("br"),t("span",{staticClass:"line-number"},[s._v("78")]),t("br"),t("span",{staticClass:"line-number"},[s._v("79")]),t("br"),t("span",{staticClass:"line-number"},[s._v("80")]),t("br"),t("span",{staticClass:"line-number"},[s._v("81")]),t("br"),t("span",{staticClass:"line-number"},[s._v("82")]),t("br"),t("span",{staticClass:"line-number"},[s._v("83")]),t("br"),t("span",{staticClass:"line-number"},[s._v("84")]),t("br"),t("span",{staticClass:"line-number"},[s._v("85")]),t("br"),t("span",{staticClass:"line-number"},[s._v("86")]),t("br"),t("span",{staticClass:"line-number"},[s._v("87")]),t("br"),t("span",{staticClass:"line-number"},[s._v("88")]),t("br"),t("span",{staticClass:"line-number"},[s._v("89")]),t("br"),t("span",{staticClass:"line-number"},[s._v("90")]),t("br"),t("span",{staticClass:"line-number"},[s._v("91")]),t("br"),t("span",{staticClass:"line-number"},[s._v("92")]),t("br"),t("span",{staticClass:"line-number"},[s._v("93")]),t("br"),t("span",{staticClass:"line-number"},[s._v("94")]),t("br"),t("span",{staticClass:"line-number"},[s._v("95")]),t("br"),t("span",{staticClass:"line-number"},[s._v("96")]),t("br"),t("span",{staticClass:"line-number"},[s._v("97")]),t("br"),t("span",{staticClass:"line-number"},[s._v("98")]),t("br"),t("span",{staticClass:"line-number"},[s._v("99")]),t("br")])]),t("p",[s._v("Prometheusの設定の詳細については割愛しますが、4-2ににて発行した認証情報は"),t("code",[s._v("tls_config")]),s._v("ならびに"),t("code",[s._v("authorization")]),s._v("で指定しています。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Prometheus講義受講者向け】")]),s._v(" "),t("p",[s._v("Prometheusの講義内で「Prometheusの特徴の1つにサービスディスカバリがあり、監視対象を動的に取得することができる」と話しました。\nそのサービスディスカバリは"),t("code",[s._v("kubernetes_sd_configs")]),s._v("の部分で設定しています。"),t("code",[s._v("role")]),s._v("という概念を利用してKubernetes内の各種リソースを動的に取得します。"),t("code",[s._v("role")]),s._v("で取得できるリソースは以下の5つです。")]),s._v(" "),t("ul",[t("li",[s._v("Node")]),s._v(" "),t("li",[s._v("Service")]),s._v(" "),t("li",[s._v("Endpoints")]),s._v(" "),t("li",[s._v("Pod")]),s._v(" "),t("li",[s._v("Ingress")])])]),s._v(" "),t("p",[t("code",[s._v("kubectl apply -f prometheus.yml")]),s._v("でPrometheusをデプロイし、確認を行います。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl apply -f prometheus.yml ")]),s._v("\nservice/prometheus created\ndeployment.apps/prometheus created\nconfigmap/prometheus created\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl get all")]),s._v("\nNAME READY STATUS RESTARTS AGE\npod/node-exporter-75rpz "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/node-exporter-p25gq "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/node-exporter-tcbsp "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\npod/prometheus-76b579c56c-r7nps "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 Running "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" 115m\n\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("S"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" AGE\nservice/kubernetes ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".0.1 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v("/TCP 29h\nservice/node-exporter ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".16.136 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9100")]),s._v("/TCP 115m\nservice/prometheus ClusterIP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10.96")]),s._v(".128.27 "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9090")]),s._v("/TCP 115m\n\nNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE\ndaemonset.apps/node-exporter "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("none"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" 115m\n\nNAME READY UP-TO-DATE AVAILABLE AGE\ndeployment.apps/prometheus "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("/1 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" 115m\n\nNAME DESIRED CURRENT READY AGE\nreplicaset.apps/prometheus-76b579c56c "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" 115m\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br")])]),t("h3",{attrs:{id:"_4-4-prometheusの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-4-prometheusの確認"}},[s._v("#")]),s._v(" 4-4. Prometheusの確認")]),s._v(" "),t("p",[s._v("ひと通りのアプリケーションのデプロイが完了したので、さっそくアクセスします。Prometheusは"),t("code",[s._v("ClusterIP")]),s._v("の配下にあるため、ポートフォワーディングしてあげます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# kubectl port-forward svc/prometheus --address 0.0.0.0 8080:9090")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ブラウザからアクセスし、"),t("code",[s._v("Status")]),s._v("タブの"),t("code",[s._v("Targets")]),s._v("を開いて全て問題なく取得できていれば完了です。\n"),t("img",{attrs:{src:"images/prometheus_main-menu.png",alt:"prometheus_main-nemu"}}),s._v("\nこれでKuberentes上の各コンポーネントに対して監視を行うことが出来ました。")]),s._v(" "),t("blockquote",[t("p",[s._v("【Prometheus講義を受講した人向け】")]),s._v(" "),t("p",[s._v("式ブラウザから各種APIオブジェクトのメトリクス情報を取得してみてください。\nまた、Kubernetes上で動くアプリケーションの監視にはkube-state-metricsやcAdvidsorといったエクスポートを利用します。余裕のある人はPodの監視も行ってみてください。")])]),s._v(" "),t("h2",{attrs:{id:"_5-最後に"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-最後に"}},[s._v("#")]),s._v(" 5. 最後に")]),s._v(" "),t("p",[s._v("Kubernetesの紹介と代表的なオブジェクトである"),t("code",[s._v("Deployment")]),s._v("と"),t("code",[s._v("Service")]),s._v("について簡単に触ってみました。\nKubetenetesでは他にも様々なオブジェクトや設定を使います。")]),s._v(" "),t("p",[s._v("例えば")]),s._v(" "),t("ul",[t("li",[s._v("アプリケーションの環境変数を設定するために"),t("code",[s._v("env")]),s._v("を使う")]),s._v(" "),t("li",[s._v("データベースなどステートフルなpodを稼働させるために"),t("code",[s._v("StatefulSet")]),s._v("を使う")]),s._v(" "),t("li",[s._v("データの永続化のため"),t("code",[s._v("PersistentVolume")]),s._v("や"),t("code",[s._v("PersistentVolumeClaim")]),s._v("を使う")]),s._v(" "),t("li",[s._v("アプリケーションのコンフィグファイルを管理するのに"),t("code",[s._v("ConfigMap")]),s._v("を使う")]),s._v(" "),t("li",[s._v("APIキーやパスワードなど秘密情報を扱うために"),t("code",[s._v("Secret")]),s._v("を使う(ただしキー値はbase64でエンコードされた文字列)")])]),s._v(" "),t("p",[s._v("などなどです。"),t("code",[s._v("PersistentVolume")]),s._v("や"),t("code",[s._v("PersistentVolumeClaim")]),s._v("などはディストリビューションごとに扱い方が違ったりしますし、構築したいアプリケーションによってマニフェストファイルの書き方は様々なので今回は割愛しました。")]),s._v(" "),t("p",[s._v("社内でKubetenetesを使ってアプリケーションを構築する場合はIKEが使いやすいかと思いますので、ぜひチュートリアルなどを眺めてみてください。")]),s._v(" "),t("blockquote",[t("p",[s._v("【参考文献】")]),s._v(" "),t("ol",[t("li",[s._v("Kubernetes完全ガイド/青山信也(インプレス)")]),s._v(" "),t("li",[s._v("イラストでわかるDockerとKubernetes/徳永航平(技術評論社)")]),s._v(" "),t("li",[s._v("Docker/Kubernetes実践コンテナ開発入門/山田明憲(技術評論社)")]),s._v(" "),t("li",[t("a",{attrs:{href:"https://kubernetes.io/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Kubernetes公式ドキュメント"),t("OutboundLink")],1),s._v("/CNCF")]),s._v(" "),t("li",[s._v("Prometheus実践ガイド/仲亀拓馬(テッキーメディア)")])])])],1)}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/17.a5ea2536.js b/assets/js/17.a5ea2536.js new file mode 100644 index 00000000..46992426 --- /dev/null +++ b/assets/js/17.a5ea2536.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{238:function(n,t,r){"use strict";r.r(t);var u=r(239),i=r.n(u);for(var e in u)["default"].indexOf(e)<0&&function(n){r.d(t,n,(function(){return u[n]}))}(e);t.default=i.a},239:function(n,t){n.exports={}},499:function(n,t,r){"use strict";r.d(t,"a",(function(){return u})),r.d(t,"b",(function(){return i}));var u=function(){var n=this._self._c;this._self._setupProxy;return n("div",[n("hr"),this._v(" "),n("span",[this._v(this._s(this.$page.frontmatter.footer))])])},i=[]},501:function(n,t,r){"use strict";r.r(t);var u=r(499),i=r(238);for(var e in i)["default"].indexOf(e)<0&&function(n){r.d(t,n,(function(){return i[n]}))}(e);var f=r(10),o=Object(f.a)(i.default,u.a,u.b,!1,null,null,null);t.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/17.eba33b98.js b/assets/js/17.eba33b98.js deleted file mode 100644 index cb2145c8..00000000 --- a/assets/js/17.eba33b98.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{240:function(n,t,r){"use strict";r.r(t);var u=r(241),i=r.n(u);for(var e in u)["default"].indexOf(e)<0&&function(n){r.d(t,n,(function(){return u[n]}))}(e);t.default=i.a},241:function(n,t){n.exports={}},500:function(n,t,r){"use strict";r.d(t,"a",(function(){return u})),r.d(t,"b",(function(){return i}));var u=function(){var n=this._self._c;this._self._setupProxy;return n("div",[n("hr"),this._v(" "),n("span",[this._v(this._s(this.$page.frontmatter.footer))])])},i=[]},502:function(n,t,r){"use strict";r.r(t);var u=r(500),i=r(240);for(var e in i)["default"].indexOf(e)<0&&function(n){r.d(t,n,(function(){return i[n]}))}(e);var f=r(10),o=Object(f.a)(i.default,u.a,u.b,!1,null,null,null);t.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/18.1d589aab.js b/assets/js/18.2a5d0854.js similarity index 62% rename from assets/js/18.1d589aab.js rename to assets/js/18.2a5d0854.js index 9977ae97..6fbccee7 100644 --- a/assets/js/18.1d589aab.js +++ b/assets/js/18.2a5d0854.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{238:function(t,n,r){"use strict";r.r(n);var e=r(239),u=r.n(e);for(var o in e)["default"].indexOf(o)<0&&function(t){r.d(n,t,(function(){return e[t]}))}(o);n.default=u.a},239:function(t,n){t.exports={}},499:function(t,n,r){"use strict";r.d(n,"a",(function(){return e})),r.d(n,"b",(function(){return u}));var e=function(){var t=this,n=t._self._c;t._self._setupProxy;return n("table",[n("tr",[n("td",[t._v("このハンズオンでやること")]),n("td",[t._v(t._s(t.$page.frontmatter.description))])]),t._v(" "),n("tr",[n("td",[t._v("想定時間")]),n("td",[t._v(t._s(t.$page.frontmatter.time))])]),t._v(" "),n("tr",[n("td",[t._v("前提知識・用語")]),n("td",[t._v(t._s(t.$page.frontmatter.prior_knowledge))])])])},u=[]},501:function(t,n,r){"use strict";r.r(n);var e=r(499),u=r(238);for(var o in u)["default"].indexOf(o)<0&&function(t){r.d(n,t,(function(){return u[t]}))}(o);var a=r(10),f=Object(a.a)(u.default,e.a,e.b,!1,null,null,null);n.default=f.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{240:function(t,n,r){"use strict";r.r(n);var e=r(241),u=r.n(e);for(var o in e)["default"].indexOf(o)<0&&function(t){r.d(n,t,(function(){return e[t]}))}(o);n.default=u.a},241:function(t,n){t.exports={}},500:function(t,n,r){"use strict";r.d(n,"a",(function(){return e})),r.d(n,"b",(function(){return u}));var e=function(){var t=this,n=t._self._c;t._self._setupProxy;return n("table",[n("tr",[n("td",[t._v("このハンズオンでやること")]),n("td",[t._v(t._s(t.$page.frontmatter.description))])]),t._v(" "),n("tr",[n("td",[t._v("想定時間")]),n("td",[t._v(t._s(t.$page.frontmatter.time))])]),t._v(" "),n("tr",[n("td",[t._v("前提知識・用語")]),n("td",[t._v(t._s(t.$page.frontmatter.prior_knowledge))])])])},u=[]},502:function(t,n,r){"use strict";r.r(n);var e=r(500),u=r(240);for(var o in u)["default"].indexOf(o)<0&&function(t){r.d(n,t,(function(){return u[t]}))}(o);var a=r(10),f=Object(a.a)(u.default,e.a,e.b,!1,null,null,null);n.default=f.exports}}]); \ No newline at end of file diff --git a/assets/js/20.fb716ec8.js b/assets/js/20.df074b21.js similarity index 99% rename from assets/js/20.fb716ec8.js rename to assets/js/20.df074b21.js index bf5fe532..1560b392 100644 --- a/assets/js/20.fb716ec8.js +++ b/assets/js/20.df074b21.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{485:function(s,a,t){s.exports=t.p+"assets/img/apache-start.82b13324.png"},486:function(s,a,t){s.exports=t.p+"assets/img/site-80.7a41e96d.png"},487:function(s,a,t){s.exports=t.p+"assets/img/site-81.19ee5e97.png"},556:function(s,a,t){"use strict";t.r(a);var e=t(10),n=Object(e.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h1",{attrs:{id:"apache-を触ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-を触ってみよう"}},[s._v("#")]),s._v(" Apache を触ってみよう")]),s._v(" "),a("h2",{attrs:{id:"事前準備"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),a("p",[s._v("以下のように"),a("code",[s._v("docker pull")]),s._v("をしておいてください。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.2-buster\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h2",{attrs:{id:"apacheとは"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheとは"}},[s._v("#")]),s._v(" Apacheとは")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」はnginxと並んで2台勢力を誇っているWebサーバソフトウェアのひとつです。\nCentOSではhttpdという名前になっていたり、単にApacheと呼ばれます。")]),s._v(" "),a("p",[s._v("Webサーバソフトウェアとは、HTTPリクエストを受けて何らかのレスポンスを返すソフトウェアのことで、たとえばHTMLファイルなどをブラウザに返すなどの役割を持っています。\nWebを扱ううえでほぼ必ず必要になる、現代に欠かせないソフトウェアです。")]),s._v(" "),a("h3",{attrs:{id:"webサーバソフトウェアのユースケース"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#webサーバソフトウェアのユースケース"}},[s._v("#")]),s._v(" Webサーバソフトウェアのユースケース")]),s._v(" "),a("p",[s._v("ApacheやnginxといったWebサーバの役割を並べてみると、以下のようになります。")]),s._v(" "),a("ul",[a("li",[s._v("HTMLやテキストファイルの配信")]),s._v(" "),a("li",[s._v("動的アプリケーションのホスティング\n"),a("ul",[a("li",[s._v("jsonを返すようなWeb APIを含む")])])]),s._v(" "),a("li",[s._v("リクエストを中継するプロキシ")]),s._v(" "),a("li",[s._v("Basic認証などによる認証処理")]),s._v(" "),a("li",[s._v("ACLなどのアクセス制御・不正な通信への防御")])]),s._v(" "),a("p",[s._v("一番初めのWebサーバの役割は、HTTPのリクエストを受け取りそれに対応するHTMLファイルを返すことでした。\n今でも大半の役割がこれです。それに付随して認証処理やアクセス制御といったセキュリティ的な役割も持つようになります。")]),s._v(" "),a("p",[s._v("その後CGI(Perl)というしくみが登場し、静的なファイルだけではなく動的にページを生成しそれを返す役割を持つようになりました。\n現在はPerlが使われることはめったにありませんが、PHPやRuby・Pythonなどの言語で書かれたプログラムを使って動的なページを扱うことができます。")]),s._v(" "),a("p",[s._v("最近では動的なページ生成を行うプログラムは別で立て、Webサーバはそこに対してリクエストを中継するだけという使われ方も増えてきました。\nこの時Webサーバは不正なリクエストを弾いたり、後段のアプリケーションの負荷分散や冗長性の確保といった役割を持ちます。\n(ほかにもroutingやlogging、アクセス管理などWebサーバに求められる基本的な機能を提供してくれます。)")]),s._v(" "),a("h3",{attrs:{id:"動的アプリケーションのホスティングとモジュール"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#動的アプリケーションのホスティングとモジュール"}},[s._v("#")]),s._v(" 動的アプリケーションのホスティングとモジュール")]),s._v(" "),a("p",[s._v("Apacheやnginxにはモジュールと呼ばれる、いわゆるプラグインのしくみがあります。\nさまざまなモジュールが存在していますが、動的アプリケーションのホスティングもこのモジュールを使ってツールを読み込むことで実現します。")]),s._v(" "),a("p",[s._v("PythonやJavaといったWebアプリケーションをモジュールから動作させる大きな利点として、プロセス管理をApacheに任せられることがあります。\nPythonなどの言語は基本的にシングルプロセスで動作するため、同時に多くのリクエストをさばく必要のあるWebアプリケーションとして動作させるには、何らかの方法でマルチプロセス化する必要があります。\nApacheには多くのリクエストをさばくためpre-forkなどのマルチプロセスを管理するしくみが備わっています。ApacheのモジュールからWebアプリケーションを実行することで、Apacheによるプロセス管理の恩恵に預かることができます。")]),s._v(" "),a("p",[s._v("別の手段として、"),a("a",{attrs:{href:"https://yhbt.net/unicorn/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Unicorn(ruby)"),a("OutboundLink")],1),s._v("や"),a("a",{attrs:{href:"https://gunicorn.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Gunicorn(Python)"),a("OutboundLink")],1),s._v("といったツールでマルチプロセス対応をする。あるいはGo言語など初めから並列処理を前提にした言語の場合はApacheでプロセス管理を行う必要はありません。")]),s._v(" "),a("p",[s._v("その場合Webサーバはプロキシとしての役割に集中することとなります。")]),s._v(" "),a("h3",{attrs:{id:"apacheの立ち位置"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheの立ち位置"}},[s._v("#")]),s._v(" Apacheの立ち位置")]),s._v(" "),a("p",[s._v("Apacheは「Apacheソフトウェア財団」によって管理されるOSSで、20年以上の歴史を持ちます。\n世界的にもっとも普及したWebサーバで、LAMP(Linux, Apache, MySQL, PHP)環境のひとつにも挙げられます。")]),s._v(" "),a("p",[s._v("以前は大量のリクエストを受けた際にプロセスをforkできず、リクエストを捌き切れなくなる(いわゆるC10K問題)ことが問題視されました。\nその際nginxをはじめとして新しいWebサーバーソフトウェアが登場しましたが、Apache自体もworkerやevent MPMといった新しい仕組みを導入し、動作も安定していることからいまだに高いシェアを占めています。")]),s._v(" "),a("p",[s._v("参考: "),a("a",{attrs:{href:"https://news.netcraft.com/archives/2020/02/20/february-2020-web-server-survey.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("February 2020 Web Server Survey"),a("OutboundLink")],1),s._v(")")]),s._v(" "),a("h2",{attrs:{id:"apacheをインストールして立ち上げる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheをインストールして立ち上げる"}},[s._v("#")]),s._v(" Apacheをインストールして立ち上げる")]),s._v(" "),a("p",[s._v("なにはともあれ動かしてみましょう。今回はdebianをdockerで立ち上げてその中にApacheをインストールしてみます。")]),s._v(" "),a("p",[s._v("まずは以下のようにdockerコンテナの中に入ります。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -itd --name test-debian -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8081")]),s._v(":81 python:3.8.2-buster /bin/bash\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it test-debian /bin/bash\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("入れたら"),a("code",[s._v("apt install")]),s._v("を使ってApacheをインストールしてみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [107 kB]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:2 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [4673 B]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:3 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [60.9 kB]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:4 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [8273 B]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#~~~略~~~")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2 apache2-dev "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Reading package lists... Done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Building dependency tree")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Reading state information... Done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#~~~略~~~")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Updating certificates in /etc/ssl/certs...")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0 added, 0 removed; done.")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Running hooks in /etc/ca-certificates/update.d...")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#done.")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br")])]),a("p",[s._v("途中でtimezoneなどを聞かれたら"),a("code",[s._v("Asia")]),s._v("や"),a("code",[s._v("Tokyo")]),s._v("を選んでください。以下のようにバージョンを表示できれば成功です。")]),s._v(" "),a("div",{staticClass:"language-shell line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[s._v("apache2 -v\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server version: Apache/2.4.38 (Debian)")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server built: 2019-10-15T19:53:42")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("以下のようにApacheを起動してください。")]),s._v(" "),a("div",{staticClass:"language-shell line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 start\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("ブラウザを開いて"),a("code",[s._v("localhost:8080")]),s._v("にアクセスしてみてください。以下のような画面が表示されれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(485),alt:"apache-start"}})]),s._v(" "),a("h2",{attrs:{id:"htmlファイルの配信-check1"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#htmlファイルの配信-check1"}},[s._v("#")]),s._v(" HTMLファイルの配信 (check1)")]),s._v(" "),a("p",[s._v("表示されたページはデフォルトのHTMLファイルです。これを自分で作成したページに置き換えてみます。\nデフォルトではDocument Rootは"),a("code",[s._v("/var/www/html/")]),s._v("に設定されています。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("Document RootはApacheが静的ファイルを配信するためのroot directoryです。")])]),s._v(" "),a("p",[s._v("この下にある"),a("code",[s._v("index.html")]),s._v("ファイルを自分の物に置き換えてみましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" /var/www/html/\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mv")]),s._v(" index.html _index.html\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello Bootcamp!!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" index.html\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("再び"),a("code",[s._v("http://localhost:8080/")]),s._v("を開くと"),a("code",[s._v("Hello Bootcamp!!")]),s._v("が表示されるのを確認してください。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[a("code",[s._v("http://localhost:8080/")]),s._v(" のようにファイル名を指定せずディレクトリ(この場合はルートディレクトリ)を指定した場合、Apacheは"),a("code",[s._v("index.html")]),s._v("を返すようにデフォルトで設定されています。\nこの設定は変更できます。")])]),s._v(" "),a("p",[s._v("Document Root配下にディレクトリを作成するとブラウザからも同様にアクセスできます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/hoge\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello HUGA!!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/hoge/huga.txt\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[a("code",[s._v("http://localhost:8080/hoge/huga.txt")]),s._v(" にアクセスすると追加したファイルが表示されます。")]),s._v(" "),a("h2",{attrs:{id:"virtualhost-の設定"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#virtualhost-の設定"}},[s._v("#")]),s._v(" VirtualHost の設定")]),s._v(" "),a("p",[s._v("1つのApacheで複数のWebサイトを管理したいことがあります。異なるIPアドレスやアドレス、port番号からアクセスされた時にDocument Rootなどを切り替えたいときは"),a("code",[s._v("VirtualHost")]),s._v("を設定することで実現できます。")]),s._v(" "),a("p",[s._v("ここではport番号を"),a("code",[s._v("80")]),s._v("と"),a("code",[s._v("81")]),s._v("に分けて別々のWebサイトを設定してみます。\n(docker起動時にport forwardしているため、手元からは"),a("code",[s._v("8080")]),s._v("と"),a("code",[s._v("8081")]),s._v("からアクセスできます。)")]),s._v(" "),a("p",[s._v("まずは新しくDocument RootになるディレクトリとHTMLファイルを作成します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/site-80\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/site-81\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'This is site 80!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/site-80/index.html\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'This is site 81!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/site-81/index.html\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("次にApacheの設定をして行きます。やることは")]),s._v(" "),a("ul",[a("li",[s._v("listen portに81を追加")]),s._v(" "),a("li",[s._v("virtual host設定の追加")])]),s._v(" "),a("p",[s._v("の2つです。listen portの追加は"),a("code",[s._v("/etc/apache2/ports.conf")]),s._v("に書きましょう。\n以下のように"),a("code",[s._v("Listen 80")]),s._v(" の下に "),a("code",[s._v("Listen 81")]),s._v("の記述を追加します。")]),s._v(" "),a("div",{staticClass:"language-apache line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("# If you just change the port or add more ports here, you will likely also\n# have to change the VirtualHost statement in\n# /etc/apache2/sites-enabled/000-default.conf\n\nListen 80\nListen 81\n\n\n Listen 443\n\n\n\n Listen 443\n\n\n# vim: syntax=apache ts=4 sw=4 sts=4 sr noet\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br")])]),a("p",[s._v("VitrualHostの設定は"),a("code",[s._v("/etc/apache2/sites-available")]),s._v("の下に作成して行きます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/apache2/sites-available/site-80.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("80")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-80\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[a("code",[s._v("/etc/apache2/sites-available/site-81.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("81")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-81\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("設定ファイルを作成したら"),a("code",[s._v("a2dissite")]),s._v("、"),a("code",[s._v("a2ensite")]),s._v("コマンドを使って設定を有効化しましょう。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[a("code",[s._v("a2dissite")]),s._v("や"),a("code",[s._v("a2ensite")]),s._v("といったコマンドは実はapache本体の機能ではありません。"),a("code",[s._v("a2ensite")]),s._v("は"),a("code",[s._v("/etc/apache2/sites-available")]),s._v("以下のファイルのsimlinkを"),a("code",[s._v("/etc/apache2/sites-enable")]),s._v("以下に追加するだけのコマンドです。\n実際のApacheは"),a("code",[s._v("/etc/apache2/sites-enable")]),s._v("以下のコンフィグファイルをloadしているため、コマンドによってサイトが有効化されたように見えるのです。")]),s._v(" "),a("p",[s._v("CentOSなど他のディストリビューションでは、これらのコマンドが存在しないことが多いので注意してください。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2dissite 000-default\na2ensite site-80\na2ensite site-81\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("そしてApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 reload\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[a("code",[s._v("localhost:8080")]),s._v("と"),a("code",[s._v("localhost:8081")]),s._v("にアクセスしてみてください。意図通りの挙動になっているでしょうか。")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(486),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(487),alt:"site-81"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("h2",{attrs:{id:"basic-認証をかけてみる-check2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#basic-認証をかけてみる-check2"}},[s._v("#")]),s._v(" Basic 認証をかけてみる(check2)")]),s._v(" "),a("p",[s._v("特定ディレクトリにアクセスできる人を制限するために、Basic認証をかけてみましょう。\nBasic認証用のmoduleが既にインストールされているはずなので、有効化します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2enmod auth_basic\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v(" -l /etc/apache2/mods-enabled/auth_basic.load "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#確認")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#lrwxrwxrwx 1 root root 33 May 10 23:22 /etc/apache2/mods-enabled/auth_basic.load -> ../mods-available/auth_basic.load")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("まずはパスワードが記載された"),a("code",[s._v(".htpasswd")]),s._v("ファイルを作成します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /etc/apache2/auth/\nhtpasswd -c /etc/apache2/auth/.htpasswd admin\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[a("code",[s._v("admin")]),s._v("がユーザ名になります。パスワードの入力を求められるので、適当に設定してください。")]),s._v(" "),a("p",[s._v("次に"),a("code",[s._v("/etc/apache2/sites-available/site-81.conf")]),s._v("を以下のように編集します。")]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("81")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v('\n DocumentRoot /var/www/html/site-81\n \n AuthUserFile /etc/apache2/auth/.htpasswd\n AuthName "site-81 Authorization"\n AuthType Basic\n Require valid-user\n '),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("ファイルを編集したら"),a("code",[s._v("service apache2 restart")]),s._v("で再起動しましょう。"),a("code",[s._v("localhost:8081")]),s._v("にアクセスしてみてください。パスワードが求められ、先ほど入力した認証情報を入れないとアクセスできなくなっているはずです。")]),s._v(" "),a("h2",{attrs:{id:"pythonアプリを動かしてみよう-check3"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#pythonアプリを動かしてみよう-check3"}},[s._v("#")]),s._v(" Pythonアプリを動かしてみよう(check3)")]),s._v(" "),a("p",[s._v("ここまではVirtualHostでリソースを管理しつつ、静的なファイルを配信する設定を作ってきました。この章では別のユースケースとして、Pythonで書かれたWebアプリをApache経由で動かす設定を作ってみます。")]),s._v(" "),a("p",[s._v("このdocker imageには既にpythonがインストールされています。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("python --version\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Python 3.8.2")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("Pythonで作成したWebアプリをApacheなどから実行する場合、"),a("a",{attrs:{href:"https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface",target:"_blank",rel:"noopener noreferrer"}},[s._v("WSGI"),a("OutboundLink")],1),s._v("というインタフェース定義に従ってWebアプリを作成します。\nこれはPython側のインタフェースを規定することで、他のプログラム(今回の場合Apache)から呼び出しやすくする物です。")]),s._v(" "),a("p",[s._v("あとでやるDjangoなど主要なPythonフレームワークはこのAPIに従っているため、Djangoで作成したアプリは今回と同じ手順でApacheから実行することができます。")]),s._v(" "),a("p",[s._v("以下のようなPythonコードを"),a("code",[s._v("/var/www/html/site-80")]),s._v("以下に置いておきましょう。")]),s._v(" "),a("p",[a("code",[s._v("vim /var/www/html/site-80/app.py")])]),s._v(" "),a("div",{staticClass:"language-python line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("environ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n status "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'200 OK'")]),s._v("\n output "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("b'Hello! This is python application!'")]),s._v("\n\n response_headers "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-type'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Length'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" response_headers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("次にwsgiを動かすためのApache moduleをインストールします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" mod-wsgi\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Collecting mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Using cached mod_wsgi-4.7.1.tar.gz (498 kB)")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Building wheels for collected packages: mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Building wheel for mod-wsgi (setup.py) ... done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Created wheel for mod-wsgi: filename=mod_wsgi-4.7.1-cp38-cp38-linux_x86_64.whl size=809821 sha256=570b19e67813e819f04ee00006b5c556339e37a03dea4af0021837b098588c0d")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Stored in directory: /root/.cache/pip/wheels/e9/82/71/1b42d6274a24af477453cecc993213fc8abd15433d80b01e93")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Successfully built mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Installing collected packages: mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Successfully installed mod-wsgi-4.7.1")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br")])]),a("p",[s._v("インストールすると以下のディレクトリにsoファイルが生成されています。Apacheに読み込ませる必要があるため確認しておきましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v(" /usr/local/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("このファイルを読み込むように、"),a("code",[s._v("vim /etc/apache2/mods-available/wsgi.load")]),s._v("を以下のように作成します。")]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[s._v("LoadModule wsgi_module /usr/local/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("moduleを有効化しておきます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2enmod wsgi\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("準備が整ったのでsite-80に先ほどのPythonアプリケーションを読み込ませましょう。\n"),a("code",[s._v("vim /etc/apache2/sites-available/site-80.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("80")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-80\n WSGIScriptAlias /app /var/www/html/site-80/app.py\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("最後にApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 restart\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[a("code",[s._v("http://localhost:8080/app")]),s._v(" にアクセスしてみてください。"),a("code",[s._v("Hello! This is python application!")]),s._v(" が表示されるでしょうか。")]),s._v(" "),a("p",[s._v("うまくいったら"),a("code",[s._v("app.py")]),s._v("を適当に変更して、Pythonが動的に実行されているのを確認してください。")]),s._v(" "),a("h2",{attrs:{id:"パフォーマンス測定-任意課題"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#パフォーマンス測定-任意課題"}},[s._v("#")]),s._v(" パフォーマンス測定(任意課題)")]),s._v(" "),a("p",[s._v("ApacheにはApache Benchというパフォーマンス測定ツールがついています。これを使ってMPMの違いがどのようにパフォーマンスに影響するか確認してみましょう。")]),s._v(" "),a("p",[s._v("Apache Benchは"),a("code",[s._v("ab")]),s._v("コマンドで使用できます。試しに先ほどのPythonアプリケーションのパフォーマンスを測定してみましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これは"),a("code",[s._v("localhost:80/app")]),s._v("に対して合計10000リクエストを同時に100ずつ実行するコマンドです。\n実行結果には成功したリクエスト数や処理時間など、分析に使える情報が書かれています。")]),s._v(" "),a("p",[s._v("同時に1000リクエストを投げても、この時点では捌けていると思います。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これだけでは面白くないので、pythonアプリにわざとディレイを入れてみましょう。")]),s._v(" "),a("p",[a("code",[s._v("vim /var/www/html/site-80/app.py")])]),s._v(" "),a("div",{staticClass:"language-python line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" time\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("environ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n status "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'200 OK'")]),s._v("\n output "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("b'Hello! Thisa is python application!'")]),s._v("\n\n response_headers "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-type'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Length'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" response_headers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br")])]),a("p",[s._v("保存したらもう一度")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("を試してみましょう。理論上は3秒で全部のリクエストが成功するはずですがどうでしょうか。\nさらにもっと数を増やすとどうでしょうか。")]),s._v(" "),a("p",[s._v("他にも色んなことを試してみてください。")]),s._v(" "),a("ul",[a("li",[s._v("psコマンドでApacheのプロセスを確認して、リクエスト中に何が起こってるのか確認しましょう。\n"),a("ul",[a("li",[s._v("apache の再起動直後とパフォーマンス測定後の変化を見てみましょう")])])]),s._v(" "),a("li",[a("code",[s._v("/var/log/apache2/error.log")]),s._v(" を確認してみましょう")]),s._v(" "),a("li",[s._v("MPM(Multi-Processing-Module)をpreforkやworkerに変えるとどうなるでしょうか")]),s._v(" "),a("li",[s._v("MPMの設定を変えてパフォーマンスチューニングをしてみましょう")])]),s._v(" "),a("h3",{attrs:{id:"補足-mpmの変更"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#補足-mpmの変更"}},[s._v("#")]),s._v(" 補足: MPMの変更")]),s._v(" "),a("p",[s._v("現在のMPMの確認")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("apachectl -V "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("grep")]),s._v(" MPM\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server MPM: event")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("MPMをpreforkに変更する。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2dismod mpm_event\na2enmod mpm_prefork\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 restart\napachectl -V "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("grep")]),s._v(" MPM\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server MPM: prefork")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br")])]),a("credit-footer")],1)}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{484:function(s,a,t){s.exports=t.p+"assets/img/apache-start.82b13324.png"},485:function(s,a,t){s.exports=t.p+"assets/img/site-80.7a41e96d.png"},486:function(s,a,t){s.exports=t.p+"assets/img/site-81.19ee5e97.png"},556:function(s,a,t){"use strict";t.r(a);var e=t(10),n=Object(e.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h1",{attrs:{id:"apache-を触ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-を触ってみよう"}},[s._v("#")]),s._v(" Apache を触ってみよう")]),s._v(" "),a("h2",{attrs:{id:"事前準備"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),a("p",[s._v("以下のように"),a("code",[s._v("docker pull")]),s._v("をしておいてください。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.2-buster\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h2",{attrs:{id:"apacheとは"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheとは"}},[s._v("#")]),s._v(" Apacheとは")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」はnginxと並んで2台勢力を誇っているWebサーバソフトウェアのひとつです。\nCentOSではhttpdという名前になっていたり、単にApacheと呼ばれます。")]),s._v(" "),a("p",[s._v("Webサーバソフトウェアとは、HTTPリクエストを受けて何らかのレスポンスを返すソフトウェアのことで、たとえばHTMLファイルなどをブラウザに返すなどの役割を持っています。\nWebを扱ううえでほぼ必ず必要になる、現代に欠かせないソフトウェアです。")]),s._v(" "),a("h3",{attrs:{id:"webサーバソフトウェアのユースケース"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#webサーバソフトウェアのユースケース"}},[s._v("#")]),s._v(" Webサーバソフトウェアのユースケース")]),s._v(" "),a("p",[s._v("ApacheやnginxといったWebサーバの役割を並べてみると、以下のようになります。")]),s._v(" "),a("ul",[a("li",[s._v("HTMLやテキストファイルの配信")]),s._v(" "),a("li",[s._v("動的アプリケーションのホスティング\n"),a("ul",[a("li",[s._v("jsonを返すようなWeb APIを含む")])])]),s._v(" "),a("li",[s._v("リクエストを中継するプロキシ")]),s._v(" "),a("li",[s._v("Basic認証などによる認証処理")]),s._v(" "),a("li",[s._v("ACLなどのアクセス制御・不正な通信への防御")])]),s._v(" "),a("p",[s._v("一番初めのWebサーバの役割は、HTTPのリクエストを受け取りそれに対応するHTMLファイルを返すことでした。\n今でも大半の役割がこれです。それに付随して認証処理やアクセス制御といったセキュリティ的な役割も持つようになります。")]),s._v(" "),a("p",[s._v("その後CGI(Perl)というしくみが登場し、静的なファイルだけではなく動的にページを生成しそれを返す役割を持つようになりました。\n現在はPerlが使われることはめったにありませんが、PHPやRuby・Pythonなどの言語で書かれたプログラムを使って動的なページを扱うことができます。")]),s._v(" "),a("p",[s._v("最近では動的なページ生成を行うプログラムは別で立て、Webサーバはそこに対してリクエストを中継するだけという使われ方も増えてきました。\nこの時Webサーバは不正なリクエストを弾いたり、後段のアプリケーションの負荷分散や冗長性の確保といった役割を持ちます。\n(ほかにもroutingやlogging、アクセス管理などWebサーバに求められる基本的な機能を提供してくれます。)")]),s._v(" "),a("h3",{attrs:{id:"動的アプリケーションのホスティングとモジュール"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#動的アプリケーションのホスティングとモジュール"}},[s._v("#")]),s._v(" 動的アプリケーションのホスティングとモジュール")]),s._v(" "),a("p",[s._v("Apacheやnginxにはモジュールと呼ばれる、いわゆるプラグインのしくみがあります。\nさまざまなモジュールが存在していますが、動的アプリケーションのホスティングもこのモジュールを使ってツールを読み込むことで実現します。")]),s._v(" "),a("p",[s._v("PythonやJavaといったWebアプリケーションをモジュールから動作させる大きな利点として、プロセス管理をApacheに任せられることがあります。\nPythonなどの言語は基本的にシングルプロセスで動作するため、同時に多くのリクエストをさばく必要のあるWebアプリケーションとして動作させるには、何らかの方法でマルチプロセス化する必要があります。\nApacheには多くのリクエストをさばくためpre-forkなどのマルチプロセスを管理するしくみが備わっています。ApacheのモジュールからWebアプリケーションを実行することで、Apacheによるプロセス管理の恩恵に預かることができます。")]),s._v(" "),a("p",[s._v("別の手段として、"),a("a",{attrs:{href:"https://yhbt.net/unicorn/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Unicorn(ruby)"),a("OutboundLink")],1),s._v("や"),a("a",{attrs:{href:"https://gunicorn.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Gunicorn(Python)"),a("OutboundLink")],1),s._v("といったツールでマルチプロセス対応をする。あるいはGo言語など初めから並列処理を前提にした言語の場合はApacheでプロセス管理を行う必要はありません。")]),s._v(" "),a("p",[s._v("その場合Webサーバはプロキシとしての役割に集中することとなります。")]),s._v(" "),a("h3",{attrs:{id:"apacheの立ち位置"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheの立ち位置"}},[s._v("#")]),s._v(" Apacheの立ち位置")]),s._v(" "),a("p",[s._v("Apacheは「Apacheソフトウェア財団」によって管理されるOSSで、20年以上の歴史を持ちます。\n世界的にもっとも普及したWebサーバで、LAMP(Linux, Apache, MySQL, PHP)環境のひとつにも挙げられます。")]),s._v(" "),a("p",[s._v("以前は大量のリクエストを受けた際にプロセスをforkできず、リクエストを捌き切れなくなる(いわゆるC10K問題)ことが問題視されました。\nその際nginxをはじめとして新しいWebサーバーソフトウェアが登場しましたが、Apache自体もworkerやevent MPMといった新しい仕組みを導入し、動作も安定していることからいまだに高いシェアを占めています。")]),s._v(" "),a("p",[s._v("参考: "),a("a",{attrs:{href:"https://news.netcraft.com/archives/2020/02/20/february-2020-web-server-survey.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("February 2020 Web Server Survey"),a("OutboundLink")],1),s._v(")")]),s._v(" "),a("h2",{attrs:{id:"apacheをインストールして立ち上げる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apacheをインストールして立ち上げる"}},[s._v("#")]),s._v(" Apacheをインストールして立ち上げる")]),s._v(" "),a("p",[s._v("なにはともあれ動かしてみましょう。今回はdebianをdockerで立ち上げてその中にApacheをインストールしてみます。")]),s._v(" "),a("p",[s._v("まずは以下のようにdockerコンテナの中に入ります。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -itd --name test-debian -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8081")]),s._v(":81 python:3.8.2-buster /bin/bash\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it test-debian /bin/bash\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("入れたら"),a("code",[s._v("apt install")]),s._v("を使ってApacheをインストールしてみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [107 kB]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:2 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [4673 B]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:3 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [60.9 kB]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Get:4 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [8273 B]")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#~~~略~~~")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2 apache2-dev "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Reading package lists... Done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Building dependency tree")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Reading state information... Done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#~~~略~~~")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Updating certificates in /etc/ssl/certs...")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0 added, 0 removed; done.")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Running hooks in /etc/ca-certificates/update.d...")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#done.")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br")])]),a("p",[s._v("途中でtimezoneなどを聞かれたら"),a("code",[s._v("Asia")]),s._v("や"),a("code",[s._v("Tokyo")]),s._v("を選んでください。以下のようにバージョンを表示できれば成功です。")]),s._v(" "),a("div",{staticClass:"language-shell line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[s._v("apache2 -v\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server version: Apache/2.4.38 (Debian)")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server built: 2019-10-15T19:53:42")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("以下のようにApacheを起動してください。")]),s._v(" "),a("div",{staticClass:"language-shell line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 start\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("ブラウザを開いて"),a("code",[s._v("localhost:8080")]),s._v("にアクセスしてみてください。以下のような画面が表示されれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(484),alt:"apache-start"}})]),s._v(" "),a("h2",{attrs:{id:"htmlファイルの配信-check1"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#htmlファイルの配信-check1"}},[s._v("#")]),s._v(" HTMLファイルの配信 (check1)")]),s._v(" "),a("p",[s._v("表示されたページはデフォルトのHTMLファイルです。これを自分で作成したページに置き換えてみます。\nデフォルトではDocument Rootは"),a("code",[s._v("/var/www/html/")]),s._v("に設定されています。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("Document RootはApacheが静的ファイルを配信するためのroot directoryです。")])]),s._v(" "),a("p",[s._v("この下にある"),a("code",[s._v("index.html")]),s._v("ファイルを自分の物に置き換えてみましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" /var/www/html/\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mv")]),s._v(" index.html _index.html\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello Bootcamp!!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" index.html\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("再び"),a("code",[s._v("http://localhost:8080/")]),s._v("を開くと"),a("code",[s._v("Hello Bootcamp!!")]),s._v("が表示されるのを確認してください。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[a("code",[s._v("http://localhost:8080/")]),s._v(" のようにファイル名を指定せずディレクトリ(この場合はルートディレクトリ)を指定した場合、Apacheは"),a("code",[s._v("index.html")]),s._v("を返すようにデフォルトで設定されています。\nこの設定は変更できます。")])]),s._v(" "),a("p",[s._v("Document Root配下にディレクトリを作成するとブラウザからも同様にアクセスできます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/hoge\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello HUGA!!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/hoge/huga.txt\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[a("code",[s._v("http://localhost:8080/hoge/huga.txt")]),s._v(" にアクセスすると追加したファイルが表示されます。")]),s._v(" "),a("h2",{attrs:{id:"virtualhost-の設定"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#virtualhost-の設定"}},[s._v("#")]),s._v(" VirtualHost の設定")]),s._v(" "),a("p",[s._v("1つのApacheで複数のWebサイトを管理したいことがあります。異なるIPアドレスやアドレス、port番号からアクセスされた時にDocument Rootなどを切り替えたいときは"),a("code",[s._v("VirtualHost")]),s._v("を設定することで実現できます。")]),s._v(" "),a("p",[s._v("ここではport番号を"),a("code",[s._v("80")]),s._v("と"),a("code",[s._v("81")]),s._v("に分けて別々のWebサイトを設定してみます。\n(docker起動時にport forwardしているため、手元からは"),a("code",[s._v("8080")]),s._v("と"),a("code",[s._v("8081")]),s._v("からアクセスできます。)")]),s._v(" "),a("p",[s._v("まずは新しくDocument RootになるディレクトリとHTMLファイルを作成します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/site-80\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/site-81\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'This is site 80!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/site-80/index.html\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("echo")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'This is site 81!'")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" /var/www/html/site-81/index.html\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("次にApacheの設定をして行きます。やることは")]),s._v(" "),a("ul",[a("li",[s._v("listen portに81を追加")]),s._v(" "),a("li",[s._v("virtual host設定の追加")])]),s._v(" "),a("p",[s._v("の2つです。listen portの追加は"),a("code",[s._v("/etc/apache2/ports.conf")]),s._v("に書きましょう。\n以下のように"),a("code",[s._v("Listen 80")]),s._v(" の下に "),a("code",[s._v("Listen 81")]),s._v("の記述を追加します。")]),s._v(" "),a("div",{staticClass:"language-apache line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("# If you just change the port or add more ports here, you will likely also\n# have to change the VirtualHost statement in\n# /etc/apache2/sites-enabled/000-default.conf\n\nListen 80\nListen 81\n\n\n Listen 443\n\n\n\n Listen 443\n\n\n# vim: syntax=apache ts=4 sw=4 sts=4 sr noet\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br")])]),a("p",[s._v("VitrualHostの設定は"),a("code",[s._v("/etc/apache2/sites-available")]),s._v("の下に作成して行きます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/apache2/sites-available/site-80.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("80")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-80\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[a("code",[s._v("/etc/apache2/sites-available/site-81.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("81")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-81\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("設定ファイルを作成したら"),a("code",[s._v("a2dissite")]),s._v("、"),a("code",[s._v("a2ensite")]),s._v("コマンドを使って設定を有効化しましょう。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[a("code",[s._v("a2dissite")]),s._v("や"),a("code",[s._v("a2ensite")]),s._v("といったコマンドは実はapache本体の機能ではありません。"),a("code",[s._v("a2ensite")]),s._v("は"),a("code",[s._v("/etc/apache2/sites-available")]),s._v("以下のファイルのsimlinkを"),a("code",[s._v("/etc/apache2/sites-enable")]),s._v("以下に追加するだけのコマンドです。\n実際のApacheは"),a("code",[s._v("/etc/apache2/sites-enable")]),s._v("以下のコンフィグファイルをloadしているため、コマンドによってサイトが有効化されたように見えるのです。")]),s._v(" "),a("p",[s._v("CentOSなど他のディストリビューションでは、これらのコマンドが存在しないことが多いので注意してください。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2dissite 000-default\na2ensite site-80\na2ensite site-81\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("そしてApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 reload\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[a("code",[s._v("localhost:8080")]),s._v("と"),a("code",[s._v("localhost:8081")]),s._v("にアクセスしてみてください。意図通りの挙動になっているでしょうか。")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(485),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(486),alt:"site-81"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("h2",{attrs:{id:"basic-認証をかけてみる-check2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#basic-認証をかけてみる-check2"}},[s._v("#")]),s._v(" Basic 認証をかけてみる(check2)")]),s._v(" "),a("p",[s._v("特定ディレクトリにアクセスできる人を制限するために、Basic認証をかけてみましょう。\nBasic認証用のmoduleが既にインストールされているはずなので、有効化します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2enmod auth_basic\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v(" -l /etc/apache2/mods-enabled/auth_basic.load "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#確認")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#lrwxrwxrwx 1 root root 33 May 10 23:22 /etc/apache2/mods-enabled/auth_basic.load -> ../mods-available/auth_basic.load")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("まずはパスワードが記載された"),a("code",[s._v(".htpasswd")]),s._v("ファイルを作成します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /etc/apache2/auth/\nhtpasswd -c /etc/apache2/auth/.htpasswd admin\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[a("code",[s._v("admin")]),s._v("がユーザ名になります。パスワードの入力を求められるので、適当に設定してください。")]),s._v(" "),a("p",[s._v("次に"),a("code",[s._v("/etc/apache2/sites-available/site-81.conf")]),s._v("を以下のように編集します。")]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("81")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v('\n DocumentRoot /var/www/html/site-81\n \n AuthUserFile /etc/apache2/auth/.htpasswd\n AuthName "site-81 Authorization"\n AuthType Basic\n Require valid-user\n '),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("ファイルを編集したら"),a("code",[s._v("service apache2 restart")]),s._v("で再起動しましょう。"),a("code",[s._v("localhost:8081")]),s._v("にアクセスしてみてください。パスワードが求められ、先ほど入力した認証情報を入れないとアクセスできなくなっているはずです。")]),s._v(" "),a("h2",{attrs:{id:"pythonアプリを動かしてみよう-check3"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#pythonアプリを動かしてみよう-check3"}},[s._v("#")]),s._v(" Pythonアプリを動かしてみよう(check3)")]),s._v(" "),a("p",[s._v("ここまではVirtualHostでリソースを管理しつつ、静的なファイルを配信する設定を作ってきました。この章では別のユースケースとして、Pythonで書かれたWebアプリをApache経由で動かす設定を作ってみます。")]),s._v(" "),a("p",[s._v("このdocker imageには既にpythonがインストールされています。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("python --version\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Python 3.8.2")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("Pythonで作成したWebアプリをApacheなどから実行する場合、"),a("a",{attrs:{href:"https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface",target:"_blank",rel:"noopener noreferrer"}},[s._v("WSGI"),a("OutboundLink")],1),s._v("というインタフェース定義に従ってWebアプリを作成します。\nこれはPython側のインタフェースを規定することで、他のプログラム(今回の場合Apache)から呼び出しやすくする物です。")]),s._v(" "),a("p",[s._v("あとでやるDjangoなど主要なPythonフレームワークはこのAPIに従っているため、Djangoで作成したアプリは今回と同じ手順でApacheから実行することができます。")]),s._v(" "),a("p",[s._v("以下のようなPythonコードを"),a("code",[s._v("/var/www/html/site-80")]),s._v("以下に置いておきましょう。")]),s._v(" "),a("p",[a("code",[s._v("vim /var/www/html/site-80/app.py")])]),s._v(" "),a("div",{staticClass:"language-python line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("environ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n status "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'200 OK'")]),s._v("\n output "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("b'Hello! This is python application!'")]),s._v("\n\n response_headers "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-type'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Length'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" response_headers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("次にwsgiを動かすためのApache moduleをインストールします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" mod-wsgi\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Collecting mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Using cached mod_wsgi-4.7.1.tar.gz (498 kB)")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Building wheels for collected packages: mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Building wheel for mod-wsgi (setup.py) ... done")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Created wheel for mod-wsgi: filename=mod_wsgi-4.7.1-cp38-cp38-linux_x86_64.whl size=809821 sha256=570b19e67813e819f04ee00006b5c556339e37a03dea4af0021837b098588c0d")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Stored in directory: /root/.cache/pip/wheels/e9/82/71/1b42d6274a24af477453cecc993213fc8abd15433d80b01e93")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Successfully built mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Installing collected packages: mod-wsgi")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Successfully installed mod-wsgi-4.7.1")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br")])]),a("p",[s._v("インストールすると以下のディレクトリにsoファイルが生成されています。Apacheに読み込ませる必要があるため確認しておきましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v(" /usr/local/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("このファイルを読み込むように、"),a("code",[s._v("vim /etc/apache2/mods-available/wsgi.load")]),s._v("を以下のように作成します。")]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[s._v("LoadModule wsgi_module /usr/local/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("moduleを有効化しておきます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2enmod wsgi\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("準備が整ったのでsite-80に先ほどのPythonアプリケーションを読み込ませましょう。\n"),a("code",[s._v("vim /etc/apache2/sites-available/site-80.conf")])]),s._v(" "),a("div",{staticClass:"language-xml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("VirtualHost")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("*:")]),s._v("80")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-80\n WSGIScriptAlias /app /var/www/html/site-80/app.py\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("最後にApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 restart\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[a("code",[s._v("http://localhost:8080/app")]),s._v(" にアクセスしてみてください。"),a("code",[s._v("Hello! This is python application!")]),s._v(" が表示されるでしょうか。")]),s._v(" "),a("p",[s._v("うまくいったら"),a("code",[s._v("app.py")]),s._v("を適当に変更して、Pythonが動的に実行されているのを確認してください。")]),s._v(" "),a("h2",{attrs:{id:"パフォーマンス測定-任意課題"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#パフォーマンス測定-任意課題"}},[s._v("#")]),s._v(" パフォーマンス測定(任意課題)")]),s._v(" "),a("p",[s._v("ApacheにはApache Benchというパフォーマンス測定ツールがついています。これを使ってMPMの違いがどのようにパフォーマンスに影響するか確認してみましょう。")]),s._v(" "),a("p",[s._v("Apache Benchは"),a("code",[s._v("ab")]),s._v("コマンドで使用できます。試しに先ほどのPythonアプリケーションのパフォーマンスを測定してみましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これは"),a("code",[s._v("localhost:80/app")]),s._v("に対して合計10000リクエストを同時に100ずつ実行するコマンドです。\n実行結果には成功したリクエスト数や処理時間など、分析に使える情報が書かれています。")]),s._v(" "),a("p",[s._v("同時に1000リクエストを投げても、この時点では捌けていると思います。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これだけでは面白くないので、pythonアプリにわざとディレイを入れてみましょう。")]),s._v(" "),a("p",[a("code",[s._v("vim /var/www/html/site-80/app.py")])]),s._v(" "),a("div",{staticClass:"language-python line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" time\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("environ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n status "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'200 OK'")]),s._v("\n output "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("b'Hello! Thisa is python application!'")]),s._v("\n\n response_headers "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-type'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Length'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n start_response"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" response_headers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("output"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br")])]),a("p",[s._v("保存したらもう一度")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ab -n "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" -c "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1000")]),s._v(" localhost:80/app\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("を試してみましょう。理論上は3秒で全部のリクエストが成功するはずですがどうでしょうか。\nさらにもっと数を増やすとどうでしょうか。")]),s._v(" "),a("p",[s._v("他にも色んなことを試してみてください。")]),s._v(" "),a("ul",[a("li",[s._v("psコマンドでApacheのプロセスを確認して、リクエスト中に何が起こってるのか確認しましょう。\n"),a("ul",[a("li",[s._v("apache の再起動直後とパフォーマンス測定後の変化を見てみましょう")])])]),s._v(" "),a("li",[a("code",[s._v("/var/log/apache2/error.log")]),s._v(" を確認してみましょう")]),s._v(" "),a("li",[s._v("MPM(Multi-Processing-Module)をpreforkやworkerに変えるとどうなるでしょうか")]),s._v(" "),a("li",[s._v("MPMの設定を変えてパフォーマンスチューニングをしてみましょう")])]),s._v(" "),a("h3",{attrs:{id:"補足-mpmの変更"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#補足-mpmの変更"}},[s._v("#")]),s._v(" 補足: MPMの変更")]),s._v(" "),a("p",[s._v("現在のMPMの確認")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("apachectl -V "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("grep")]),s._v(" MPM\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server MPM: event")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[s._v("MPMをpreforkに変更する。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("a2dismod mpm_event\na2enmod mpm_prefork\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 restart\napachectl -V "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("grep")]),s._v(" MPM\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#Server MPM: prefork")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br")])]),a("credit-footer")],1)}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/24.6750592e.js b/assets/js/24.886f15ea.js similarity index 99% rename from assets/js/24.6750592e.js rename to assets/js/24.886f15ea.js index 3a7e77d6..dfca4746 100644 --- a/assets/js/24.6750592e.js +++ b/assets/js/24.886f15ea.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{351:function(s,a,n){s.exports=n.p+"assets/img/replica-set-primary-with-two-secondaries.bakedsvg.71ff1fec.svg"},352:function(s,a,n){s.exports=n.p+"assets/img/replica-set-trigger-election.bakedsvg.a65dbacf.svg"},524:function(s,a,n){"use strict";n.r(a);var e=n(10),t=Object(e.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("header-table"),s._v(" "),a("h1",{attrs:{id:"page-frontmatter-title"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),a("h2",{attrs:{id:"今日のサンプル環境"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#今日のサンプル環境"}},[s._v("#")]),s._v(" 今日のサンプル環境")]),s._v(" "),a("p",[s._v("以下のレポジトリを手元にクローンして、"),a("code",[s._v("docker compose up -d")]),s._v("を実行してください。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/iij/bootcamp-mongodb-sample",target:"_blank",rel:"noopener noreferrer"}},[s._v("iij/bootcamp-mongodb-sample"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ git clone https://github.com/iij/bootcamp-mongodb-sample.git\n$ cd bootcamp-mongodb-sample/\n$ docker compose up -d\n[+] Running 3/3\n ⠿ Container bootcamp-mongodb-sample-mongo-secondary-1 Started 7.3s\n ⠿ Container bootcamp-mongodb-sample-mongo-primary-1 Started 7.1s\n ⠿ Container bootcamp-mongodb-sample-mongo-arbiter-1 Started 8.0s\n$ docker compose ps\nNAME COMMAND SERVICE STATUS PORTS\nbootcamp-mongodb-sample-mongo-arbiter-1 "docker-entrypoint.s…" mongo-arbiter running 0.0.0.0:27019->27019/tcp, :::27019->27019/tcp\nbootcamp-mongodb-sample-mongo-primary-1 "docker-entrypoint.s…" mongo-primary running 0.0.0.0:27017->27017/tcp, :::27017->27017/tcp\nbootcamp-mongodb-sample-mongo-secondary-1 "docker-entrypoint.s…" mongo-secondary running 0.0.0.0:27018->27018/tcp, :::27018->27018/tcp ::27018->27018/tcp\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br")])]),a("h2",{attrs:{id:"mongodbの紹介"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#mongodbの紹介"}},[s._v("#")]),s._v(" MongoDBの紹介")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://www.mongodb.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("MongoDB"),a("OutboundLink")],1),s._v("は2009年に初版がリリースされた、MongoDB社が開発しているドキュメント指向のデータベースです。\nMySQLなどのRDBが「行と列」からなるテーブル形式でデータを管理するのに対して、ドキュメント指向であるMongoDBには以下のjsonデータのようなオブジェクトをそのまま保存・検索ができます。")]),s._v(" "),a("div",{staticClass:"language-json line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"username"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"address"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"street"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"123 Main Street"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"city"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Springfield"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"state"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"NY"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("いわゆる「NoSQL」としてRDBMSに比べて大量のデータを柔軟に保存し、複雑な検索クエリで比較的高速に検索・集計することができます。\nさらにレプリケーションやインデックス、ドキュメント単位のロックなどRDBMSと同じような機能を持つため、スキーマレス(テーブル定義を事前に決めなくてもいい)でありながらRDBMSのような使い方ができます。")]),s._v(" "),a("p",[s._v("他の特徴として、「レプリカセット」と呼ばれる仕組みで3台(奇数台)1セットの冗長構成を簡単に作れる他、「シャーディング」による負荷分散構成も簡単に構築することができます。")]),s._v(" "),a("h3",{attrs:{id:"個人的な雑感"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#個人的な雑感"}},[s._v("#")]),s._v(" 個人的な雑感")]),s._v(" "),a("p",[s._v("MongoDBはスキーマレスでありながらRDBMSのような使い方もできることから、サービス立ち上げ時に開発スピードが求められる段階において、データスキーマを含めて試行錯誤を高速に繰り返すような使われ方が話題になりました。\n現在では上記のような開発手法で利用されることは少なく、主に以下のような場面で利用されます。")]),s._v(" "),a("ul",[a("li",[s._v("IoTのセンサーデータなど、insert manyなデータの格納・検索\n"),a("ul",[a("li",[s._v("データ間のリレーションや更新の一貫性があまり求められず、更新よりもデータの追加が頻発するケース")]),s._v(" "),a("li",[s._v("書き込みが多く、write操作の負荷分散が必要になるケース")])])]),s._v(" "),a("li",[s._v("データのaggregation(集計)、地理情報などによる特殊な検索用途\n"),a("ul",[a("li",[s._v("スキーマレスを活かし、データを雑に投入して後からゴリゴリクエリを書いて集計するデータレイク的な使い方")]),s._v(" "),a("li",[s._v("地理情報(geo location)検索など特殊な検索が必要になるケース")])])])]),s._v(" "),a("p",[s._v("もちろん上記のようなケースはRDBでも可能ですし、個々の機能について言えばもっと得意なDB製品は存在します。\n一方でMongoDBは幅広いケースについて80点くらいを取れるような製品と言えます。")]),s._v(" "),a("h2",{attrs:{id:"早速使ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#早速使ってみよう"}},[s._v("#")]),s._v(" 早速使ってみよう")]),s._v(" "),a("p",[s._v("何はともあれ使ってみましょう。"),a("code",[s._v("docker compose up -d")]),s._v("に成功していれば、以下のコマンドでMongoDBのコンソールが使えます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ docker compose exec mongo-arbiter mongosh --port 27017 --host mongo-primary\n\nMongoDB shell version v5.0.1\nconnecting to: mongodb://mongo-primary:27017/?compressors=disabled&gssapiServiceName=mongodb\nImplicit session: session { "id" : UUID("c90c0469-9eb0-4583-bb9d-5af4b7a3f907") }\nMongoDB server version: 5.0.1\n\n~~略~~\n\ntest> \ntest> rs.initiate()\n{\n\t"info2" : "no configuration specified. Using a default configuration for the set",\n\t"me" : "edd2f8708eef:27017",\n\t"ok" : 1\n}\nmongo-set [direct: secondary] test>\nmongo-set [direct: primary] test>\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br")])]),a("p",[s._v("まずはおまじないとして"),a("code",[s._v("rs.initiate()")]),s._v("を実行しておいてください。あとで紹介します。")]),s._v(" "),a("p",[s._v("とりあえず適当なデータを作成してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] local> use bootcamp-db\nswitched to db bootcamp-db\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "tanaka-san", age: 22})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5ccab2c5bc0ff08cb33cd")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "sato-san", age: 25})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5ccb82c5bc0ff08cb33ce")\n}\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db> db.people.find()\n[\n {\n _id: ObjectId("64c5ccab2c5bc0ff08cb33cd"),\n name: \'tanaka-san\',\n age: 22\n },\n {\n _id: ObjectId("64c5ccb82c5bc0ff08cb33ce"),\n name: \'sato-san\',\n age: 25\n }\n]\nmongo-set [direct: primary] bootcamp-db>\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br"),a("span",{staticClass:"line-number"},[s._v("19")]),a("br"),a("span",{staticClass:"line-number"},[s._v("20")]),a("br"),a("span",{staticClass:"line-number"},[s._v("21")]),a("br"),a("span",{staticClass:"line-number"},[s._v("22")]),a("br"),a("span",{staticClass:"line-number"},[s._v("23")]),a("br"),a("span",{staticClass:"line-number"},[s._v("24")]),a("br"),a("span",{staticClass:"line-number"},[s._v("25")]),a("br"),a("span",{staticClass:"line-number"},[s._v("26")]),a("br"),a("span",{staticClass:"line-number"},[s._v("27")]),a("br")])]),a("p",[s._v("RDBMSにおける「テーブル」は、MongoDBでは「collection」と呼ばれます。ここでは"),a("code",[s._v("people")]),s._v("がcollectionです。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: primary] bootcamp-db> show collections;\npeople\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("MySQLなどのように事前に"),a("code",[s._v("CREATE TABLE...")]),s._v("などでテーブルを作成しなくても、勝手にcollectionが作成されています。\nMongoDBはスキーマレスなので、形式を問わずデータを保存できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "watanabe-san", age: 23, address: "tokyo"})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdcc2c5bc0ff08cb33cf")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "fujimoto-san", age: 23, address: {post: "123-4567", city: "tokyo"}})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdd12c5bc0ff08cb33d0")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "kawai-san", age: 30, address: {post: "123-9876", city: "tokyo"}})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdd82c5bc0ff08cb33d1")\n}\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br")])]),a("p",[a("code",[s._v("db.people.find()")]),s._v(" してみてください。"),a("code",[s._v("people")]),s._v(" collectionの中にいろんな形式でデータが保存されています。")]),s._v(" "),a("h2",{attrs:{id:"検索と集計-aggregation"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#検索と集計-aggregation"}},[s._v("#")]),s._v(" 検索と集計(Aggregation)")]),s._v(" "),a("p",[s._v("単純な検索であれば"),a("code",[s._v("find()")]),s._v("で可能です。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('# 名前が`sato-san`なデータを検索\ndb.people.find({name: "sato-san"})\n\n# ageが23以上なデータを検索\ndb.people.find({age: {$gte: 23}})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br")])]),a("p",[a("code",[s._v("$gte")]),s._v("は"),a("code",[s._v("greater than or equal")]),s._v("の略で「以上」のデータを検索します。例えばageが23「未満」なデータを検索する場合は"),a("code",[s._v("$lt")]),s._v("("),a("code",[s._v("lower than")]),s._v(")です。\n詳しくはこちら => "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/query-comparison/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Comparison Query Operators"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("ネストされたデータも検索できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('> db.people.find({"address.city": "tokyo"})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("さらに詳しい検索や集計をする場合、強力な "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/core/aggregation-pipeline/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Aggregation"),a("OutboundLink")],1),s._v(" 機能が使えます。")]),s._v(" "),a("p",[s._v("まずは何も集計せずに検索してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] bootcamp-db> db.people.aggregate([ { $match: {"address.city": "tokyo"} } ])\n{ "_id" : ObjectId("62e5e5265beeb8a010811279"), "name" : "fujimoto-san", "age" : 23, "address" : { "post" : "123-4567", "city" : "tokyo" } }\n{ "_id" : ObjectId("62e5e52e5beeb8a01081127a"), "name" : "kawai-san", "age" : 30, "address" : { "post" : "123-9876", "city" : "tokyo" } }\nm\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("例えばここからMySQLの"),a("code",[s._v("group by")]),s._v("と同じことをするには以下のようにします。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $group: {_id: "$address.city", age_sum: {$sum: "$age"}} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("ここでは"),a("code",[s._v("address.city")]),s._v("が"),a("code",[s._v("tokyo")]),s._v("になってる人のデータを集計し、"),a("code",[s._v("age")]),s._v("を合計して表示しています。\n年齢を合計するのもおかしいので、平均を取ってみましょう。平均を取るコマンドは"),a("code",[s._v("$avg")]),s._v("です。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $group: {_id: "$address.city", age_avg: {$avg: "$age"}} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("aggregationで使える機能はたくさんあるので、色々と試してみてください。=> "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Aggregation Pipeline Stages"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("例えば"),a("code",[s._v("$replaceRoot")]),s._v("というpipelineを使うとどうなるか試してみてください。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $replaceRoot: {newRoot: "$address"} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("他には "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/",target:"_blank",rel:"noopener noreferrer"}},[s._v("$unwind"),a("OutboundLink")],1),s._v(" も面白い機能です。")]),s._v(" "),a("h2",{attrs:{id:"レプリカセット"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#レプリカセット"}},[s._v("#")]),s._v(" レプリカセット")]),s._v(" "),a("h3",{attrs:{id:"解説"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),a("p",[s._v("MongoDBでは "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/replication/",target:"_blank",rel:"noopener noreferrer"}},[s._v("レプリカセット"),a("OutboundLink")],1),s._v(" と呼ばれる構成を奇数台(最小3台)で構成することができます。")]),s._v(" "),a("p",[s._v("(以下画像は "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/replication/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.mongodb.com/manual/replication/"),a("OutboundLink")],1),s._v(" より)")]),s._v(" "),a("p",[a("img",{attrs:{src:n(351),alt:"replica-set-primary-with-two-secondaries"}})]),s._v(" "),a("p",[s._v("通常ではクライアントやアプリケーションは「Primary」になっているMongoDBに対してデータを更新します。すると更新されたデータは「Secondary」にもレプリケーションされます。\nそしてレプリカセットを構成しているMongoDBはお互いに投票処理を行い、その結果によって自動的にPrimary役が決定されます。")]),s._v(" "),a("p",[s._v("もしPrimaryが停止したりネットワーク的に分断された場合、残り2台のMongoDB同士で通信(画像のHeartbeat通信)を行い、2台による投票処理によって自動的に次のPrimaryが決定します。")]),s._v(" "),a("p",[a("img",{attrs:{src:n(352),alt:"replica-set-trigger-election"}})]),s._v(" "),a("p",[s._v("この時元々Primaryだったホストは他2台との通信ができなくなったことで、自動的にSecondaryとなり更新クエリを受け付けなくなります。")]),s._v(" "),a("h3",{attrs:{id:"ハンズオン"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン"}},[s._v("#")]),s._v(" ハンズオン")]),s._v(" "),a("p",[s._v("実際にやってみましょう。今回用意したサンプルの"),a("code",[s._v("docker-compose.yml")]),s._v("では以下のホストを立ち上げています。")]),s._v(" "),a("ul",[a("li",[s._v("mongo-primary")]),s._v(" "),a("li",[s._v("mongo-secondary")]),s._v(" "),a("li",[s._v("mongo-arbiter")])]),s._v(" "),a("p",[s._v("この3台でレプリカセットを構築してみましょう。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("Aribiterとは?")]),s._v(" "),a("p",[s._v("先ほどMongoDBのレプリカセットは最低3台の奇数台で構成されると説明しました。\nこれは投票処理におけるsplit brainを防ぐためですが、一方でreplicationによるデータコピーは1台で十分というケースは多々あります。\nその場合3台目に1~2台目と同じスペックのサーバを用意するのは無駄です。そこで使われるのが「投票処理しか行わない」 "),a("a",{attrs:{href:"https://www.mongodb.com/docs/manual/core/replica-set-arbiter/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Arbiter"),a("OutboundLink")],1),s._v(" というサーバです。")]),s._v(" "),a("p",[s._v("Aribiterにはデータのreplicationが行われず、データの書き込みも読み込みもできません。レプリカセットに参加しPrimaryを選出するための投票処理しか行わないため、低スペックで安いサーバに構築することが可能です。")]),s._v(" "),a("p",[s._v("ちなみにこのハンズオンでは、ArbiterのホストをMongoDB clientを起動するためのホストとしても利用しています。")])]),s._v(" "),a("p",[s._v("先ほどと同様に、mongo-arbiterホストから"),a("code",[s._v("mongo-primary")]),s._v("のコンソールに入り、レプリカセットの設定をします。\n(先程までのコンソールを使い回しても大丈夫です)")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ docker compose exec mongo-arbiter mongosh --port 27017 --host mongo-primary\nrs.reconfig( {\n _id : "mongo-set",\n members: [\n { _id: 0, host: "mongo-primary:27017", priority: 2 },\n { _id: 1, host: "mongo-secondary:27018", priority: 1 },\n { _id: 2, host: "mongo-arbiter:27019", priority: 0 }\n ]\n})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("以下のような結果が返ってくるはずです。")]),s._v(" "),a("div",{staticClass:"language-json line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n ok"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n '$clusterTime'"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n clusterTime"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Timestamp("),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" t"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1690685501")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n signature"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n hash"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Binary(Buffer.from("),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0000000000000000000000000000000000000000"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hex"')]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n keyId"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Long("),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0"')]),s._v(")\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n operationTime"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Timestamp("),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" t"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1690685501")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(")\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br")])]),a("p",[s._v("すると残りの2台にも設定が反映され、レプリカセットが構築されます。\n新しいコンソールで以下のようにmongo-secondaryを開き、"),a("code",[s._v("rs.status()")]),s._v("を実行して設定状況を確認してみてください。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("$ docker compose exec mongo-arbiter mongosh --port 27018 --host mongo-secondary\n\nmongo-set [direct: secondary] test> rs.status() # 設定確認\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[a("code",[s._v('"ok" : 1')]),s._v("などでレプリカセットの正常性を確認できます。")]),s._v(" "),a("p",[s._v("以下のようにsecondaryにprimaryからデータがreplicateされていることを確認します。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: secondary] test> use bootcamp-db\nswitched to db bootcamp-db\nmongo-set [direct: secondary] bootcamp-db> db.getMongo().setReadPref(\"primaryPreferred\")\nmongo-set [direct: secondary] bootcamp-db> db.people.find()\n[\n {\n _id: ObjectId(\"64c5ccab2c5bc0ff08cb33cd\"),\n name: 'tanaka-san',\n age: 22\n },\n {\n _id: ObjectId(\"64c5ccb82c5bc0ff08cb33ce\"),\n name: 'sato-san',\n age: 25\n },\n {\n _id: ObjectId(\"64c5cdcc2c5bc0ff08cb33cf\"),\n name: 'watanabe-san',\n age: 23,\n address: 'tokyo'\n },\n {\n _id: ObjectId(\"64c5cdd12c5bc0ff08cb33d0\"),\n name: 'fujimoto-san',\n age: 23,\n address: { post: '123-4567', city: 'tokyo' }\n },\n {\n _id: ObjectId(\"64c5cdd82c5bc0ff08cb33d1\"),\n name: 'kawai-san',\n age: 30,\n address: { post: '123-9876', city: 'tokyo' }\n }\n]\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br"),a("span",{staticClass:"line-number"},[s._v("19")]),a("br"),a("span",{staticClass:"line-number"},[s._v("20")]),a("br"),a("span",{staticClass:"line-number"},[s._v("21")]),a("br"),a("span",{staticClass:"line-number"},[s._v("22")]),a("br"),a("span",{staticClass:"line-number"},[s._v("23")]),a("br"),a("span",{staticClass:"line-number"},[s._v("24")]),a("br"),a("span",{staticClass:"line-number"},[s._v("25")]),a("br"),a("span",{staticClass:"line-number"},[s._v("26")]),a("br"),a("span",{staticClass:"line-number"},[s._v("27")]),a("br"),a("span",{staticClass:"line-number"},[s._v("28")]),a("br"),a("span",{staticClass:"line-number"},[s._v("29")]),a("br"),a("span",{staticClass:"line-number"},[s._v("30")]),a("br"),a("span",{staticClass:"line-number"},[s._v("31")]),a("br"),a("span",{staticClass:"line-number"},[s._v("32")]),a("br"),a("span",{staticClass:"line-number"},[s._v("33")]),a("br"),a("span",{staticClass:"line-number"},[s._v("34")]),a("br")])]),a("p",[s._v("最初にprimaryに登録したデータが、secondaryにも保存されていました。これはprimaryからsecandaryにデータがコピー(replicate)されているからです。")]),s._v(" "),a("p",[s._v("ここで別のターミナルを開き、PrimaryのMongoDBを落としてみましょう")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ sudo docker compose stop mongo-primary\n[sudo] password for r-fujimoto:\n[+] Stopping 1/1\n ✔ Container bootcamp-mongodb-sample-mongo-primary-1 Stopped 11.1s\n$ sudo docker compose ps\nNAME IMAGE COMMAND SERVICE CREATED STATUS PORTS\nbootcamp-mongodb-sample-mongo-arbiter-1 mongo "docker-entrypoint.s…" mongo-arbiter 37 minutes ago Up 37 minutes 27017/tcp, 0.0.0.0:27019->27019/tcp, :::27019->27019/tcp\nbootcamp-mongodb-sample-mongo-secondary-1 mongo "docker-entrypoint.s…" mongo-secondary 37 minutes ago Up 37 minutes 27017/tcp, 0.0.0.0:27018->27018/tcp, :::27018->27018/tcp :::27018->27018/tcp\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("するとSecondaryのプロンプトが"),a("code",[s._v("mongo-set:PRIMARY>")]),s._v("に変わるのが確認できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: secondary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[a("code",[s._v("rs.status()")]),s._v("をもう一度Secondaryで叩いてみてください。先ほどとどう変わったでしょうか。")]),s._v(" "),a("p",[a("code",[s._v("rs.status()")]),s._v("が確認できたら、Primaryを起動してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("$ sudo docker compose start mongo-primary\n[+] Running 1/1\n ✔ Container bootcamp-mongodb-sample-mongo-primary-1 Started 0.3s\n~/w/b/t/bootcamp-mongodb-sample (main|✔) $\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("するとsecondaryが再度secondaryに戻ります。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: secondary] bootcamp-db>\n\nmongo-set [direct: secondary] bootcamp-db>\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("これは"),a("code",[s._v("rs.reconfig()")]),s._v("で設定した"),a("code",[s._v("priority")]),s._v("というパラメータに従い、より値の大きいホストがPrimaryになるように設定されているためです。")]),s._v(" "),a("p",[s._v("このようにMongoDBのレプリカセットでは、一台が落ちても自動的に他がPrimaryに昇格し、データの保存を継続できる構成を簡単に作ることができます。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=t.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{351:function(s,a,n){s.exports=n.p+"assets/img/replica-set-primary-with-two-secondaries.bakedsvg.71ff1fec.svg"},352:function(s,a,n){s.exports=n.p+"assets/img/replica-set-trigger-election.bakedsvg.a65dbacf.svg"},523:function(s,a,n){"use strict";n.r(a);var e=n(10),t=Object(e.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("header-table"),s._v(" "),a("h1",{attrs:{id:"page-frontmatter-title"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),a("h2",{attrs:{id:"今日のサンプル環境"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#今日のサンプル環境"}},[s._v("#")]),s._v(" 今日のサンプル環境")]),s._v(" "),a("p",[s._v("以下のレポジトリを手元にクローンして、"),a("code",[s._v("docker compose up -d")]),s._v("を実行してください。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/iij/bootcamp-mongodb-sample",target:"_blank",rel:"noopener noreferrer"}},[s._v("iij/bootcamp-mongodb-sample"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ git clone https://github.com/iij/bootcamp-mongodb-sample.git\n$ cd bootcamp-mongodb-sample/\n$ docker compose up -d\n[+] Running 3/3\n ⠿ Container bootcamp-mongodb-sample-mongo-secondary-1 Started 7.3s\n ⠿ Container bootcamp-mongodb-sample-mongo-primary-1 Started 7.1s\n ⠿ Container bootcamp-mongodb-sample-mongo-arbiter-1 Started 8.0s\n$ docker compose ps\nNAME COMMAND SERVICE STATUS PORTS\nbootcamp-mongodb-sample-mongo-arbiter-1 "docker-entrypoint.s…" mongo-arbiter running 0.0.0.0:27019->27019/tcp, :::27019->27019/tcp\nbootcamp-mongodb-sample-mongo-primary-1 "docker-entrypoint.s…" mongo-primary running 0.0.0.0:27017->27017/tcp, :::27017->27017/tcp\nbootcamp-mongodb-sample-mongo-secondary-1 "docker-entrypoint.s…" mongo-secondary running 0.0.0.0:27018->27018/tcp, :::27018->27018/tcp ::27018->27018/tcp\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br")])]),a("h2",{attrs:{id:"mongodbの紹介"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#mongodbの紹介"}},[s._v("#")]),s._v(" MongoDBの紹介")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://www.mongodb.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("MongoDB"),a("OutboundLink")],1),s._v("は2009年に初版がリリースされた、MongoDB社が開発しているドキュメント指向のデータベースです。\nMySQLなどのRDBが「行と列」からなるテーブル形式でデータを管理するのに対して、ドキュメント指向であるMongoDBには以下のjsonデータのようなオブジェクトをそのまま保存・検索ができます。")]),s._v(" "),a("div",{staticClass:"language-json line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"username"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"address"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"street"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"123 Main Street"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"city"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Springfield"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[s._v('"state"')]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"NY"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("いわゆる「NoSQL」としてRDBMSに比べて大量のデータを柔軟に保存し、複雑な検索クエリで比較的高速に検索・集計することができます。\nさらにレプリケーションやインデックス、ドキュメント単位のロックなどRDBMSと同じような機能を持つため、スキーマレス(テーブル定義を事前に決めなくてもいい)でありながらRDBMSのような使い方ができます。")]),s._v(" "),a("p",[s._v("他の特徴として、「レプリカセット」と呼ばれる仕組みで3台(奇数台)1セットの冗長構成を簡単に作れる他、「シャーディング」による負荷分散構成も簡単に構築することができます。")]),s._v(" "),a("h3",{attrs:{id:"個人的な雑感"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#個人的な雑感"}},[s._v("#")]),s._v(" 個人的な雑感")]),s._v(" "),a("p",[s._v("MongoDBはスキーマレスでありながらRDBMSのような使い方もできることから、サービス立ち上げ時に開発スピードが求められる段階において、データスキーマを含めて試行錯誤を高速に繰り返すような使われ方が話題になりました。\n現在では上記のような開発手法で利用されることは少なく、主に以下のような場面で利用されます。")]),s._v(" "),a("ul",[a("li",[s._v("IoTのセンサーデータなど、insert manyなデータの格納・検索\n"),a("ul",[a("li",[s._v("データ間のリレーションや更新の一貫性があまり求められず、更新よりもデータの追加が頻発するケース")]),s._v(" "),a("li",[s._v("書き込みが多く、write操作の負荷分散が必要になるケース")])])]),s._v(" "),a("li",[s._v("データのaggregation(集計)、地理情報などによる特殊な検索用途\n"),a("ul",[a("li",[s._v("スキーマレスを活かし、データを雑に投入して後からゴリゴリクエリを書いて集計するデータレイク的な使い方")]),s._v(" "),a("li",[s._v("地理情報(geo location)検索など特殊な検索が必要になるケース")])])])]),s._v(" "),a("p",[s._v("もちろん上記のようなケースはRDBでも可能ですし、個々の機能について言えばもっと得意なDB製品は存在します。\n一方でMongoDBは幅広いケースについて80点くらいを取れるような製品と言えます。")]),s._v(" "),a("h2",{attrs:{id:"早速使ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#早速使ってみよう"}},[s._v("#")]),s._v(" 早速使ってみよう")]),s._v(" "),a("p",[s._v("何はともあれ使ってみましょう。"),a("code",[s._v("docker compose up -d")]),s._v("に成功していれば、以下のコマンドでMongoDBのコンソールが使えます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ docker compose exec mongo-arbiter mongosh --port 27017 --host mongo-primary\n\nMongoDB shell version v5.0.1\nconnecting to: mongodb://mongo-primary:27017/?compressors=disabled&gssapiServiceName=mongodb\nImplicit session: session { "id" : UUID("c90c0469-9eb0-4583-bb9d-5af4b7a3f907") }\nMongoDB server version: 5.0.1\n\n~~略~~\n\ntest> \ntest> rs.initiate()\n{\n\t"info2" : "no configuration specified. Using a default configuration for the set",\n\t"me" : "edd2f8708eef:27017",\n\t"ok" : 1\n}\nmongo-set [direct: secondary] test>\nmongo-set [direct: primary] test>\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br")])]),a("p",[s._v("まずはおまじないとして"),a("code",[s._v("rs.initiate()")]),s._v("を実行しておいてください。あとで紹介します。")]),s._v(" "),a("p",[s._v("とりあえず適当なデータを作成してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] local> use bootcamp-db\nswitched to db bootcamp-db\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "tanaka-san", age: 22})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5ccab2c5bc0ff08cb33cd")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "sato-san", age: 25})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5ccb82c5bc0ff08cb33ce")\n}\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db> db.people.find()\n[\n {\n _id: ObjectId("64c5ccab2c5bc0ff08cb33cd"),\n name: \'tanaka-san\',\n age: 22\n },\n {\n _id: ObjectId("64c5ccb82c5bc0ff08cb33ce"),\n name: \'sato-san\',\n age: 25\n }\n]\nmongo-set [direct: primary] bootcamp-db>\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br"),a("span",{staticClass:"line-number"},[s._v("19")]),a("br"),a("span",{staticClass:"line-number"},[s._v("20")]),a("br"),a("span",{staticClass:"line-number"},[s._v("21")]),a("br"),a("span",{staticClass:"line-number"},[s._v("22")]),a("br"),a("span",{staticClass:"line-number"},[s._v("23")]),a("br"),a("span",{staticClass:"line-number"},[s._v("24")]),a("br"),a("span",{staticClass:"line-number"},[s._v("25")]),a("br"),a("span",{staticClass:"line-number"},[s._v("26")]),a("br"),a("span",{staticClass:"line-number"},[s._v("27")]),a("br")])]),a("p",[s._v("RDBMSにおける「テーブル」は、MongoDBでは「collection」と呼ばれます。ここでは"),a("code",[s._v("people")]),s._v("がcollectionです。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: primary] bootcamp-db> show collections;\npeople\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])]),a("p",[s._v("MySQLなどのように事前に"),a("code",[s._v("CREATE TABLE...")]),s._v("などでテーブルを作成しなくても、勝手にcollectionが作成されています。\nMongoDBはスキーマレスなので、形式を問わずデータを保存できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "watanabe-san", age: 23, address: "tokyo"})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdcc2c5bc0ff08cb33cf")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "fujimoto-san", age: 23, address: {post: "123-4567", city: "tokyo"}})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdd12c5bc0ff08cb33d0")\n}\nmongo-set [direct: primary] bootcamp-db> db.people.insertOne({name: "kawai-san", age: 30, address: {post: "123-9876", city: "tokyo"}})\n{\n acknowledged: true,\n insertedId: ObjectId("64c5cdd82c5bc0ff08cb33d1")\n}\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br")])]),a("p",[a("code",[s._v("db.people.find()")]),s._v(" してみてください。"),a("code",[s._v("people")]),s._v(" collectionの中にいろんな形式でデータが保存されています。")]),s._v(" "),a("h2",{attrs:{id:"検索と集計-aggregation"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#検索と集計-aggregation"}},[s._v("#")]),s._v(" 検索と集計(Aggregation)")]),s._v(" "),a("p",[s._v("単純な検索であれば"),a("code",[s._v("find()")]),s._v("で可能です。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('# 名前が`sato-san`なデータを検索\ndb.people.find({name: "sato-san"})\n\n# ageが23以上なデータを検索\ndb.people.find({age: {$gte: 23}})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br")])]),a("p",[a("code",[s._v("$gte")]),s._v("は"),a("code",[s._v("greater than or equal")]),s._v("の略で「以上」のデータを検索します。例えばageが23「未満」なデータを検索する場合は"),a("code",[s._v("$lt")]),s._v("("),a("code",[s._v("lower than")]),s._v(")です。\n詳しくはこちら => "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/query-comparison/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Comparison Query Operators"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("ネストされたデータも検索できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('> db.people.find({"address.city": "tokyo"})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("さらに詳しい検索や集計をする場合、強力な "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/core/aggregation-pipeline/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Aggregation"),a("OutboundLink")],1),s._v(" 機能が使えます。")]),s._v(" "),a("p",[s._v("まずは何も集計せずに検索してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('mongo-set [direct: primary] bootcamp-db> db.people.aggregate([ { $match: {"address.city": "tokyo"} } ])\n{ "_id" : ObjectId("62e5e5265beeb8a010811279"), "name" : "fujimoto-san", "age" : 23, "address" : { "post" : "123-4567", "city" : "tokyo" } }\n{ "_id" : ObjectId("62e5e52e5beeb8a01081127a"), "name" : "kawai-san", "age" : 30, "address" : { "post" : "123-9876", "city" : "tokyo" } }\nm\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("例えばここからMySQLの"),a("code",[s._v("group by")]),s._v("と同じことをするには以下のようにします。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $group: {_id: "$address.city", age_sum: {$sum: "$age"}} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("ここでは"),a("code",[s._v("address.city")]),s._v("が"),a("code",[s._v("tokyo")]),s._v("になってる人のデータを集計し、"),a("code",[s._v("age")]),s._v("を合計して表示しています。\n年齢を合計するのもおかしいので、平均を取ってみましょう。平均を取るコマンドは"),a("code",[s._v("$avg")]),s._v("です。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $group: {_id: "$address.city", age_avg: {$avg: "$age"}} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("aggregationで使える機能はたくさんあるので、色々と試してみてください。=> "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Aggregation Pipeline Stages"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("例えば"),a("code",[s._v("$replaceRoot")]),s._v("というpipelineを使うとどうなるか試してみてください。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('db.people.aggregate([\n { $match: {"address.city": "tokyo"} },\n { $replaceRoot: {newRoot: "$address"} }\n])\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("他には "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/",target:"_blank",rel:"noopener noreferrer"}},[s._v("$unwind"),a("OutboundLink")],1),s._v(" も面白い機能です。")]),s._v(" "),a("h2",{attrs:{id:"レプリカセット"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#レプリカセット"}},[s._v("#")]),s._v(" レプリカセット")]),s._v(" "),a("h3",{attrs:{id:"解説"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),a("p",[s._v("MongoDBでは "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/replication/",target:"_blank",rel:"noopener noreferrer"}},[s._v("レプリカセット"),a("OutboundLink")],1),s._v(" と呼ばれる構成を奇数台(最小3台)で構成することができます。")]),s._v(" "),a("p",[s._v("(以下画像は "),a("a",{attrs:{href:"https://docs.mongodb.com/manual/replication/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.mongodb.com/manual/replication/"),a("OutboundLink")],1),s._v(" より)")]),s._v(" "),a("p",[a("img",{attrs:{src:n(351),alt:"replica-set-primary-with-two-secondaries"}})]),s._v(" "),a("p",[s._v("通常ではクライアントやアプリケーションは「Primary」になっているMongoDBに対してデータを更新します。すると更新されたデータは「Secondary」にもレプリケーションされます。\nそしてレプリカセットを構成しているMongoDBはお互いに投票処理を行い、その結果によって自動的にPrimary役が決定されます。")]),s._v(" "),a("p",[s._v("もしPrimaryが停止したりネットワーク的に分断された場合、残り2台のMongoDB同士で通信(画像のHeartbeat通信)を行い、2台による投票処理によって自動的に次のPrimaryが決定します。")]),s._v(" "),a("p",[a("img",{attrs:{src:n(352),alt:"replica-set-trigger-election"}})]),s._v(" "),a("p",[s._v("この時元々Primaryだったホストは他2台との通信ができなくなったことで、自動的にSecondaryとなり更新クエリを受け付けなくなります。")]),s._v(" "),a("h3",{attrs:{id:"ハンズオン"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン"}},[s._v("#")]),s._v(" ハンズオン")]),s._v(" "),a("p",[s._v("実際にやってみましょう。今回用意したサンプルの"),a("code",[s._v("docker-compose.yml")]),s._v("では以下のホストを立ち上げています。")]),s._v(" "),a("ul",[a("li",[s._v("mongo-primary")]),s._v(" "),a("li",[s._v("mongo-secondary")]),s._v(" "),a("li",[s._v("mongo-arbiter")])]),s._v(" "),a("p",[s._v("この3台でレプリカセットを構築してみましょう。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("Aribiterとは?")]),s._v(" "),a("p",[s._v("先ほどMongoDBのレプリカセットは最低3台の奇数台で構成されると説明しました。\nこれは投票処理におけるsplit brainを防ぐためですが、一方でreplicationによるデータコピーは1台で十分というケースは多々あります。\nその場合3台目に1~2台目と同じスペックのサーバを用意するのは無駄です。そこで使われるのが「投票処理しか行わない」 "),a("a",{attrs:{href:"https://www.mongodb.com/docs/manual/core/replica-set-arbiter/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Arbiter"),a("OutboundLink")],1),s._v(" というサーバです。")]),s._v(" "),a("p",[s._v("Aribiterにはデータのreplicationが行われず、データの書き込みも読み込みもできません。レプリカセットに参加しPrimaryを選出するための投票処理しか行わないため、低スペックで安いサーバに構築することが可能です。")]),s._v(" "),a("p",[s._v("ちなみにこのハンズオンでは、ArbiterのホストをMongoDB clientを起動するためのホストとしても利用しています。")])]),s._v(" "),a("p",[s._v("先ほどと同様に、mongo-arbiterホストから"),a("code",[s._v("mongo-primary")]),s._v("のコンソールに入り、レプリカセットの設定をします。\n(先程までのコンソールを使い回しても大丈夫です)")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ docker compose exec mongo-arbiter mongosh --port 27017 --host mongo-primary\nrs.reconfig( {\n _id : "mongo-set",\n members: [\n { _id: 0, host: "mongo-primary:27017", priority: 2 },\n { _id: 1, host: "mongo-secondary:27018", priority: 1 },\n { _id: 2, host: "mongo-arbiter:27019", priority: 0 }\n ]\n})\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("以下のような結果が返ってくるはずです。")]),s._v(" "),a("div",{staticClass:"language-json line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n ok"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n '$clusterTime'"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n clusterTime"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Timestamp("),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" t"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1690685501")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n signature"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n hash"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Binary(Buffer.from("),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0000000000000000000000000000000000000000"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hex"')]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(")"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n keyId"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Long("),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0"')]),s._v(")\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n operationTime"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" Timestamp("),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" t"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1690685501")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(")\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br")])]),a("p",[s._v("すると残りの2台にも設定が反映され、レプリカセットが構築されます。\n新しいコンソールで以下のようにmongo-secondaryを開き、"),a("code",[s._v("rs.status()")]),s._v("を実行して設定状況を確認してみてください。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("$ docker compose exec mongo-arbiter mongosh --port 27018 --host mongo-secondary\n\nmongo-set [direct: secondary] test> rs.status() # 設定確認\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])]),a("p",[a("code",[s._v('"ok" : 1')]),s._v("などでレプリカセットの正常性を確認できます。")]),s._v(" "),a("p",[s._v("以下のようにsecondaryにprimaryからデータがreplicateされていることを確認します。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: secondary] test> use bootcamp-db\nswitched to db bootcamp-db\nmongo-set [direct: secondary] bootcamp-db> db.getMongo().setReadPref(\"primaryPreferred\")\nmongo-set [direct: secondary] bootcamp-db> db.people.find()\n[\n {\n _id: ObjectId(\"64c5ccab2c5bc0ff08cb33cd\"),\n name: 'tanaka-san',\n age: 22\n },\n {\n _id: ObjectId(\"64c5ccb82c5bc0ff08cb33ce\"),\n name: 'sato-san',\n age: 25\n },\n {\n _id: ObjectId(\"64c5cdcc2c5bc0ff08cb33cf\"),\n name: 'watanabe-san',\n age: 23,\n address: 'tokyo'\n },\n {\n _id: ObjectId(\"64c5cdd12c5bc0ff08cb33d0\"),\n name: 'fujimoto-san',\n age: 23,\n address: { post: '123-4567', city: 'tokyo' }\n },\n {\n _id: ObjectId(\"64c5cdd82c5bc0ff08cb33d1\"),\n name: 'kawai-san',\n age: 30,\n address: { post: '123-9876', city: 'tokyo' }\n }\n]\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br"),a("span",{staticClass:"line-number"},[s._v("17")]),a("br"),a("span",{staticClass:"line-number"},[s._v("18")]),a("br"),a("span",{staticClass:"line-number"},[s._v("19")]),a("br"),a("span",{staticClass:"line-number"},[s._v("20")]),a("br"),a("span",{staticClass:"line-number"},[s._v("21")]),a("br"),a("span",{staticClass:"line-number"},[s._v("22")]),a("br"),a("span",{staticClass:"line-number"},[s._v("23")]),a("br"),a("span",{staticClass:"line-number"},[s._v("24")]),a("br"),a("span",{staticClass:"line-number"},[s._v("25")]),a("br"),a("span",{staticClass:"line-number"},[s._v("26")]),a("br"),a("span",{staticClass:"line-number"},[s._v("27")]),a("br"),a("span",{staticClass:"line-number"},[s._v("28")]),a("br"),a("span",{staticClass:"line-number"},[s._v("29")]),a("br"),a("span",{staticClass:"line-number"},[s._v("30")]),a("br"),a("span",{staticClass:"line-number"},[s._v("31")]),a("br"),a("span",{staticClass:"line-number"},[s._v("32")]),a("br"),a("span",{staticClass:"line-number"},[s._v("33")]),a("br"),a("span",{staticClass:"line-number"},[s._v("34")]),a("br")])]),a("p",[s._v("最初にprimaryに登録したデータが、secondaryにも保存されていました。これはprimaryからsecandaryにデータがコピー(replicate)されているからです。")]),s._v(" "),a("p",[s._v("ここで別のターミナルを開き、PrimaryのMongoDBを落としてみましょう")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('$ sudo docker compose stop mongo-primary\n[sudo] password for r-fujimoto:\n[+] Stopping 1/1\n ✔ Container bootcamp-mongodb-sample-mongo-primary-1 Stopped 11.1s\n$ sudo docker compose ps\nNAME IMAGE COMMAND SERVICE CREATED STATUS PORTS\nbootcamp-mongodb-sample-mongo-arbiter-1 mongo "docker-entrypoint.s…" mongo-arbiter 37 minutes ago Up 37 minutes 27017/tcp, 0.0.0.0:27019->27019/tcp, :::27019->27019/tcp\nbootcamp-mongodb-sample-mongo-secondary-1 mongo "docker-entrypoint.s…" mongo-secondary 37 minutes ago Up 37 minutes 27017/tcp, 0.0.0.0:27018->27018/tcp, :::27018->27018/tcp :::27018->27018/tcp\n')])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br")])]),a("p",[s._v("するとSecondaryのプロンプトが"),a("code",[s._v("mongo-set:PRIMARY>")]),s._v("に変わるのが確認できます。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: secondary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\nmongo-set [direct: primary] bootcamp-db>\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[a("code",[s._v("rs.status()")]),s._v("をもう一度Secondaryで叩いてみてください。先ほどとどう変わったでしょうか。")]),s._v(" "),a("p",[a("code",[s._v("rs.status()")]),s._v("が確認できたら、Primaryを起動してみましょう。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("$ sudo docker compose start mongo-primary\n[+] Running 1/1\n ✔ Container bootcamp-mongodb-sample-mongo-primary-1 Started 0.3s\n~/w/b/t/bootcamp-mongodb-sample (main|✔) $\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br")])]),a("p",[s._v("するとsecondaryが再度secondaryに戻ります。")]),s._v(" "),a("div",{staticClass:"language-terminal line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("mongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: primary] bootcamp-db>\n\nmongo-set [direct: secondary] bootcamp-db>\n\nmongo-set [direct: secondary] bootcamp-db>\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("これは"),a("code",[s._v("rs.reconfig()")]),s._v("で設定した"),a("code",[s._v("priority")]),s._v("というパラメータに従い、より値の大きいホストがPrimaryになるように設定されているためです。")]),s._v(" "),a("p",[s._v("このようにMongoDBのレプリカセットでは、一台が落ちても自動的に他がPrimaryに昇格し、データの保存を継続できる構成を簡単に作ることができます。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=t.exports}}]); \ No newline at end of file diff --git a/assets/js/25.1947468b.js b/assets/js/25.19720aa5.js similarity index 99% rename from assets/js/25.1947468b.js rename to assets/js/25.19720aa5.js index 37ba1c5a..b0f1fb15 100644 --- a/assets/js/25.1947468b.js +++ b/assets/js/25.19720aa5.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{467:function(t,s,a){t.exports=a.p+"assets/img/fork.drawio.fb839165.png"},468:function(t,s,a){t.exports=a.p+"assets/img/thread.drawio.25427a90.png"},546:function(t,s,a){"use strict";a.r(s);var n=a(10),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("header-table"),t._v(" "),s("h1",{attrs:{id:"page-frontmatter-title"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[t._v("#")]),t._v(" "+t._s(t.$page.frontmatter.title))]),t._v(" "),s("h2",{attrs:{id:"環境準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#環境準備"}},[t._v("#")]),t._v(" 環境準備")]),t._v(" "),s("p",[t._v("以下のdockerコマンドでコンソールを取得してください。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ docker pull iijrfujimoto/bootcamp_concurrent\n$ mkdir bootcamp_work # 作業用のディレクトリ。名前はなんでもいい\n$ cd bootcamp_work\n$ docker run --name bootcamp_concurrent -p 8000:8000 -v $PWD:/work --rm -it iijrfujimoto/bootcamp_concurrent /bin/bash\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br")])]),s("h2",{attrs:{id:"この資料の約束"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#この資料の約束"}},[t._v("#")]),t._v(" この資料の約束")]),t._v(" "),s("p",[t._v("ターミナルでの実行例を示す際、以下のように"),s("code",[t._v("$")]),t._v("で始まっている場合はdockerを起動しているホスト側で実行するコマンドを示します。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("以下のように"),s("code",[t._v("root@40e566b8e23e")]),t._v("などで示されている場合はdockerコンテナ上で実行するコマンドであることを示します。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("root@0dd4d9fad678:/work# python3 main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("h2",{attrs:{id:"このハンズオンの目的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#このハンズオンの目的"}},[t._v("#")]),t._v(" このハンズオンの目的")]),t._v(" "),s("p",[t._v("このハンズオンでは、Webアプリケーションの実装に欠かせない「並行処理」について取り扱います。")]),t._v(" "),s("p",[t._v("プログラミングにおいて、同時に複数の処理を行う「並行処理」は複雑で実装が難しいものです。\nそれは普段のプログラムが「決定的」なものであるのに対し、並行処理は実行時に何が起こるのか分からない「非決定的」な動作となるからです。")]),t._v(" "),s("p",[t._v("しかしユーザからの多数のリクエストに対応するため、Webアプリケーションに並行処理の実装は必須です。\n昨今ではライブラリやフレームワークが発達し並行処理を意識しなくてもWebアプリケーションを作ることが可能ですが、\n並行処理の勘所を理解せずに使うと思わぬバグや事故を起こす可能性があります。")]),t._v(" "),s("p",[t._v("このハンズオンではWebアプリケーションにおける並行処理実装の初歩的な注意点を紹介し、不具合を起こさないための知識を得てもらう目的としています。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("並行処理と並列処理")]),t._v(" "),s("p",[t._v("並行処理(concurrent processing)と並列処理(parallel processing)は似た言葉ですが異なる動作を指す言葉です。")]),t._v(" "),s("ul",[s("li",[t._v("並行処理: ある時間内に複数のタスクを処理すること")]),t._v(" "),s("li",[t._v("並列処理: 複数のタスクを「同時に」処理すること")])]),t._v(" "),s("p",[t._v("「並行処理」と言う場合はある時間内に複数タスクを実行できればいいので、実行するタスクを小まめに切り替えながら処理する動作も含みます。\n一方で「並列処理」の場合は複数のCPUコアによって全く同時に複数タスクを処理することを指します。")]),t._v(" "),s("p",[t._v("(参考: "),s("a",{attrs:{href:"https://go.dev/blog/waza-talk",target:"_blank",rel:"noopener noreferrer"}},[t._v("Concurrency is not parallelism"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("h2",{attrs:{id:"ハンズオン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン"}},[t._v("#")]),t._v(" ハンズオン")]),t._v(" "),s("h3",{attrs:{id:"簡単な並行処理サンプル"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#簡単な並行処理サンプル"}},[t._v("#")]),t._v(" 簡単な並行処理サンプル")]),t._v(" "),s("p",[t._v("まずはPythonで簡単なWebサーバを書いてみましょう。")]),t._v(" "),s("p",[t._v("vscode等を利用して作業用ディレクトリ("),s("code",[t._v("bootcamp_work")]),t._v(")でPythonコードを書いていきましょう。")]),t._v(" "),s("div",{staticClass:"language-termianl line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ vim main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello simple server!\\n'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br")])]),s("p",[t._v("保存したら、dockerコンテナ内からサーバを起動してみます。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("root@0dd4d9fad678:/work# python3 main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("手元のホストから以下のようにcurlで叩いてみましょう。\n「Hello simple server!」と返ってくれば成功です。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000\nHello simple server!\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br")])]),s("p",[t._v("リクエストした直後、サーバー側のログに"),s("code",[t._v("start processing path = /")]),t._v("と表示されたことを覚えておいてください。")]),t._v(" "),s("h3",{attrs:{id:"メモリ空間の共有とレースコンディション"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#メモリ空間の共有とレースコンディション"}},[t._v("#")]),t._v(" メモリ空間の共有とレースコンディション")]),t._v(" "),s("h4",{attrs:{id:"サンプルコード"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#サンプルコード"}},[t._v("#")]),t._v(" サンプルコード")]),t._v(" "),s("p",[t._v("先ほどのプログラムを少し改造して、今までのアクセス数をカウントできるようにしてみましょう。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br")])]),s("p",[t._v("以下のようにcurlを複数叩いてみます。"),s("code",[t._v("&")]),t._v("はコマンドをバックグラウンドで実行する書き方です。\n先ほどはサーバ側が処理をする10秒間curlコマンドはずっと待っていましたが、"),s("code",[t._v("&")]),t._v("をつけることですぐに次のコマンドを叩けます。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("p",[t._v("さて結果はどうでしょうか。3回コマンドを実行しましたが、サーバのログは以下のようになったのではないでしょうか。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("serving at port 8000\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nend processing path = /, after request count = 1\nend processing path = /, after request count = 1\nend processing path = /, after request count = 1\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br")])]),s("p",[t._v("きちんとリクエスト数をカウントできていない、重大な不具合があるようです。")]),t._v(" "),s("h4",{attrs:{id:"コード解説"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#コード解説"}},[t._v("#")]),t._v(" コード解説")]),t._v(" "),s("h4",{attrs:{id:"forkとthread"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#forkとthread"}},[t._v("#")]),t._v(" forkとthread")]),t._v(" "),s("p",[t._v("このプログラムではユーザからのリクエストを同時に処理するために "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/http.server.html#http.server.ThreadingHTTPServer",target:"_blank",rel:"noopener noreferrer"}},[t._v("ThreadingHTTPServer"),s("OutboundLink")],1),t._v(" を利用しています。\nThreadingHTTPServer の説明を見てみましょう。")]),t._v(" "),s("blockquote",[s("p",[t._v("This class is identical to HTTPServer but uses threads to handle requests by using the ThreadingMixIn.")])]),t._v(" "),s("p",[t._v("どうやらリクエストを処理(handle)するために「スレッド」を利用するようです。\n"),s("a",{attrs:{href:"https://docs.python.org/ja/3.7/library/socketserver.html#socketserver.ThreadingMixIn",target:"_blank",rel:"noopener noreferrer"}},[t._v("ThreadingMixIn"),s("OutboundLink")],1),t._v(" の説明も見てみましょう。")]),t._v(" "),s("blockquote",[s("p",[t._v("Forking and threading versions of each type of server can be created using these mix-in classes.")])]),t._v(" "),s("p",[s("code",[t._v("fork")]),t._v("もしくは"),s("code",[t._v("thread")]),t._v("いずれかの仕組みで渡したServerオブジェクトを並列に実行してくれるようです。"),s("code",[t._v("fork")]),t._v("と"),s("code",[t._v("thread")]),t._v("は両方とも並列処理のための方法ですが、大きな違いがあります。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(467),alt:"fork",title:"fork"}})]),t._v(" "),s("p",[s("img",{attrs:{src:a(468),alt:"thread",title:"thread"}})]),t._v(" "),s("p",[t._v("図の通り"),s("code",[t._v("fork")]),t._v("の場合forkで分かれたプロセス上で"),s("code",[t._v("SimpleHelloHandler")]),t._v("のインスタンスが実行されます。そのためリクエストを処理する各インスタンス間でメモリ空間を共有していません(できないとも言う)。\n一方で"),s("code",[t._v("thread")]),t._v("の場合はメモリ空間を共有した同じプロセス内で"),s("code",[t._v("SimpleHelloHandler")]),t._v("インスタンスが実行されます。")]),t._v(" "),s("p",[t._v("今回"),s("code",[t._v("request_total")]),t._v("はクラス変数として宣言されています。そのため各"),s("code",[t._v("SimpleHelloHandler")]),t._v("インスタンスから共有するメモリ上の変数としてアクセスが可能です。\nWebサーバを実装する時に限りませんが、ライブラリやフレームワークを利用する際にはそれがどういう仕組みで動くのか把握しておく必要があります。")]),t._v(" "),s("h4",{attrs:{id:"クリティカルセッション"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#クリティカルセッション"}},[t._v("#")]),t._v(" クリティカルセッション")]),t._v(" "),s("p",[s("code",[t._v("count_and_do_something")]),t._v("の以下の部分に注目してみましょう(といっても中身全てですが)。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("div",{staticClass:"highlight-lines"},[s("br"),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("br")]),s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n")])]),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br")])]),s("p",[t._v("上記2-10行目は"),s("code",[t._v("SimpleHelloHandler.request_total")]),t._v("という共有資源にアクセスしており、複数のスレッドから同時にアクセスされると不具合が起きます。このような箇所を「"),s("strong",[t._v("クリティカルセクション")]),t._v("」と呼びます。\nまた実際にクリティカルセクションに複数のスレッドが同時にアクセスしてしまい、不具合が起きている状態を「"),s("strong",[t._v("レースコンディション(競合状態)")]),t._v("」と呼びます。")]),t._v(" "),s("p",[t._v("スレッドなどを利用する並行処理プログラミングではこのクリティカルセクションを如何に減らし、そして保護するかが大切になります。")]),t._v(" "),s("h3",{attrs:{id:"排他制御・アトミック処理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#排他制御・アトミック処理"}},[t._v("#")]),t._v(" 排他制御・アトミック処理")]),t._v(" "),s("h4",{attrs:{id:"排他制御"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#排他制御"}},[t._v("#")]),t._v(" 排他制御")]),t._v(" "),s("p",[t._v("クリティカルセクションを保護する手法の一つが、ロックを取得する排他制御と呼ばれるものです。まずは素朴に実装してみましょう。\n(説明用にすごく雑な実装なので間違っても仕事で以下のようなコードを書かないでください)")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# request_total_lock がFalseになるまで無限ループする")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの取得")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 処理が完了したらロックを解放する")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br"),s("span",{staticClass:"line-number"},[t._v("37")]),s("br"),s("span",{staticClass:"line-number"},[t._v("38")]),s("br"),s("span",{staticClass:"line-number"},[t._v("39")]),s("br")])]),s("p",[t._v("サーバを実行してみてください。先ほどと同じようにcurlを実行するとどうでしょうか。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("p",[t._v("ここでは新しくクラス変数として"),s("code",[t._v("request_total_lock")]),t._v("を追加し、"),s("code",[t._v("count_and_do_something")]),t._v("の先頭でこの変数を検査しています。\n"),s("code",[t._v("True")]),t._v("の場合は次のループに遷移し、無限に検査を繰り返します。"),s("code",[t._v("False")]),t._v("だった場合は"),s("code",[t._v("True")]),t._v("を代入しクリティカルセクションに入ります。\nクリティカルセクションの終了後、"),s("code",[t._v("request_total_lock")]),t._v("に"),s("code",[t._v("False")]),t._v("を代入し、別のスレッドが実行できるようにします。")]),t._v(" "),s("p",[t._v("少しややこしいですが、複数のスレッド間の動作を順番に書くと以下のようになります。")]),t._v(" "),s("ol",[s("li",[t._v("最初のリクエスト(thread1)が来て"),s("code",[t._v("count_and_do_something")]),t._v("が実行される")]),t._v(" "),s("li",[s("code",[t._v("request_total_lock")]),t._v("は"),s("code",[t._v("False")]),t._v("なのでthread1が"),s("code",[t._v("True")]),t._v("を代入してロックを取得する")]),t._v(" "),s("li",[t._v("2個目のリクエスト(thread2)が来て"),s("code",[t._v("count_and_do_something")]),t._v("が実行される")]),t._v(" "),s("li",[s("code",[t._v("request_total_lock")]),t._v("は"),s("code",[t._v("True")]),t._v("なのでthread2は無限ループを続ける")]),t._v(" "),s("li",[t._v("thread1でクリティカルセクションが完了し、"),s("code",[t._v("request_total_lock")]),t._v("に"),s("code",[t._v("False")]),t._v("が代入される")]),t._v(" "),s("li",[t._v("thread2がロックを取得し、クリティカルセクションを開始する")])]),t._v(" "),s("p",[t._v("このロック機構によりクリティカルセクションが保護され、"),s("code",[t._v("request_total")]),t._v("が正しくカウントされるようになりました。\nこのようにロックが取得できるまで無限ループで待ち続けるような実装を「スピンロック」と言います。")]),t._v(" "),s("p",[t._v("このコードは一見うまくいっていますが、実はレースコンディションを引き起こすクリティカルセクションが隠れています。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("div",{staticClass:"highlight-lines"},[s("br"),s("br"),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("br"),s("br")]),s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ここから")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ここまでが実はクリティカルセクション")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n")])]),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br")])]),s("p",[t._v("上記の3行目で"),s("code",[t._v("request_total_lock")]),t._v("を見た後"),s("code",[t._v("True")]),t._v("を代入するまでにわずかながらでもラグがあるため、運が悪いと3行目が複数のスレッドで同時に実行される可能性があります。\nすると複数のスレッドが同時にロックを取得してしまい、クリティカルセクションが同時に実行されてしまいます。")]),t._v(" "),s("p",[t._v("後でも記載しますが、これは"),s("code",[t._v("request_total_lock")]),t._v("の検査とロックの獲得が「アトミックでない」処理のためです。")]),t._v(" "),s("p",[t._v("さて、このように並行処理に関わるスピンロックなどのコードを自前で実装するのはやめて、ライブラリを使いましょう。これはスレッドプログラミングにおける大原則です。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" threading "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Lock\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("acquire"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの取得。他のスレッドはアクセスできなくなる")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("release"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの解放")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br")])]),s("p",[t._v("Pythonの "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/threading.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("threadingパッケージ"),s("OutboundLink")],1),t._v(" にはスレッドプログラミングに使えるツールが用意されています。今回は "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/threading.html#lock-objects",target:"_blank",rel:"noopener noreferrer"}},[t._v("Lock"),s("OutboundLink")],1),t._v(" を利用しました。\n"),s("code",[t._v("acquire()")]),t._v("を実行するとロックを取得し、もし他のスレッドで既に取得済みの場合はブロック(コード実行をそこで停止する)します。")]),t._v(" "),s("p",[t._v("この部分は以下のようにも書けます。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br")])]),s("p",[t._v("クリティカルセクションが分かりやすくロックの開放し忘れもないので、基本的には"),s("code",[t._v("with")]),t._v("を利用して書きましょう。")]),t._v(" "),s("h4",{attrs:{id:"スレッドセーフ-アトミック-な処理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#スレッドセーフ-アトミック-な処理"}},[t._v("#")]),t._v(" スレッドセーフ(アトミック)な処理")]),t._v(" "),s("p",[t._v("ロックを取得することでクリティカルセクションを保護できることは分かりました。\nしかし皆さんも薄々お気づきの通り、そもそもこのコードのクリティカルセクションをもっと小さくできるのではないでしょうか。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br")])]),s("p",[t._v("どうでしょう。試しに同じようにcurlで叩いてみましょう。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("serving at port 8000\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nend processing path = /, after request count = 1\nend processing path = /, after request count = 2\nend processing path = /, after request count = 3\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br")])]),s("p",[s("code",[t._v("before request count")]),t._v(" はうまく出力できなくなってしまいましたが、「リクエスト数をカウントする」という要件は満たせているように見えます。\nしかしここにもレースコンディションを引き起こすクリティカルセクションが隠れています。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# クリティカルセクション")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br")])]),s("p",[t._v("スレッドプログラミングにおいて、ある処理が複数スレッドから同時に実行されても問題ない実装であることを「スレッドセーフ(thread safe)である」と言います。\n例えば先ほどの"),s("code",[t._v("acquire()")]),t._v("はスレッドセーフなメソッドであり、複数スレッドから同時にアクセスされてもレースコンディションを起こさないように実装されています。")]),t._v(" "),s("p",[t._v("一方でPythonにおける"),s("code",[t._v("+= 1")]),t._v("とはスレッドセーフな処理なんでしょうか。結論から言うとそうではありません。\nなぜこの短いコードでレースコンディションが発生するのでしょうか。")]),t._v(" "),s("p",[t._v("Pythonで書かれたコードは最終的にバイトコードにコンパイルされて実行されます(CPythonの場合)。\n"),s("code",[t._v("dis")]),t._v("を利用して実際に実行されるバイトコードを見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('root@40e566b8e23e:/work# python3\nPython 3.9.2 (default, Feb 28 2021, 17:03:44)\n[GCC 10.2.1 20210110] on linux\nType "help", "copyright", "credits" or "license" for more information.\n>>> import dis\n>>> a = 0\n>>> dis.dis("a += 1")\n 1 0 LOAD_NAME 0 (a)\n 2 LOAD_CONST 0 (1)\n 4 INPLACE_ADD\n 6 STORE_NAME 0 (a)\n 8 LOAD_CONST 1 (None)\n 10 RETURN_VALUE\n>>>\n')])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br")])]),s("p",[s("code",[t._v("a += 1")]),t._v("というコードは上記の通り6行のバイトコードに変換されます。内容を見てみると以下のように動作することが分かります。")]),t._v(" "),s("ol",[s("li",[t._v("「a」というメモリ領域から値を読み込む("),s("code",[t._v("LOAD_NAME")]),t._v(")")]),t._v(" "),s("li",[t._v("加算する("),s("code",[t._v("INPLACE_ADD")]),t._v(")")]),t._v(" "),s("li",[t._v("「a」というメモリ領域に値を書き込む("),s("code",[t._v("STORE_NAME")]),t._v(")")])]),t._v(" "),s("p",[t._v("つまり"),s("code",[t._v("a += 1")]),t._v("という一見1ステップのコードでも、バイトコードやその先の機械語レベルでは複数のステップに分かれた処理である可能性があります。\n(ここで「可能性」と言っているのは、あくまで言語やコンパイラの仕様や実装次第なためです。少なくともCPythonではアトミックな機械語は生成されないはずです。)")]),t._v(" "),s("p",[t._v("仮に機械語レベルで複数ステップに分かれた処理であった場合、タイミングによってはthread1が1ステップ目を実行した後、\n入れ替わりthread2が1ステップ目を実行してしまう可能性があります。\nそのため"),s("code",[t._v("a += 1")]),t._v("のような一見単純なコードでも、レースコンディションを起こす可能性があります。")]),t._v(" "),s("p",[t._v("スレッドプログラミングにおいては常に「その処理がスレッドセーフなのか」、仕様を確認しつつ進める必要があります。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("アトミックな処理")]),t._v(" "),s("p",[t._v("「アトム(atom)」とは「これ以上分割不可能な単位」という意味を持ち、「原子性」などとも呼ばれます。\n同じように「アトミックな処理」とはその処理が「外部から途中経過が観測できず、失敗した場合は処理前の状態に復元される」ことを言います。\nその意味でCPythonにおける"),s("code",[t._v("a += 1")]),t._v("はメモリからreadしてwriteする過程があるためアトミックな処理ではありません。")]),t._v(" "),s("p",[t._v("例えばMySQLなどのRDBでトランザクションを張り、最後にCOMMITする操作はアトミックな処理です。\nまた最近のCPUには加算処理などをアトミックに行う処理が命令として用意されており、言語によってはそういった命令を利用してアトミックな加算処理などを利用することが可能です。")]),t._v(" "),s("p",[t._v("例: gccなどのCコンパイラに実装されている"),s("code",[t._v("__sync_fetch_and_add")]),t._v("など")])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("コンテキストスイッチ")]),t._v(" "),s("p",[t._v("CPython(c言語で書かれたPython処理系)にはGILという仕組みがあり、Pythonプログラムは1個のCPUコアしか使うことができません。\n1個のCPUコアでは同時に1個の仕事しかできないため、複数のスレッドを動かす際は1個のCPUコアの処理時間を分け合うことになります。\nつまりあるCPUコアでthread1の仕事を進めつつあるタイミングでthread1を待機状態にし、thread2の仕事を始める・・・ということを行っていきます。\nこの仕事の割り振りを決めるのがLinuxカーネルにおける「スケジューラ」と呼ばれる機能であり、thread1->thread2など仕事内容が切り替わるのを「コンテキストスイッチ」と呼びます。")])]),t._v(" "),s("p",[s("code",[t._v("a += 1")]),t._v(" がスレッドセーフでないことが分かったので、きちんとロックを利用してスレッドセーフな実装にしましょう。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" threading "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Lock\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("increment_request_total")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("increment_request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br"),s("span",{staticClass:"line-number"},[t._v("37")]),s("br")])]),s("h2",{attrs:{id:"最後に"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#最後に"}},[t._v("#")]),t._v(" 最後に")]),t._v(" "),s("p",[t._v("この資料で伝えたいのは以下のことです。")]),t._v(" "),s("ul",[s("li",[t._v("ライブラリやフレームワークを利用する際はその仕組みをきちんと理解しましょう\n"),s("ul",[s("li",[t._v("webプログラミングにおいては特に並行・並列処理をどのように実装しているのか確認しましょう")])])]),t._v(" "),s("li",[t._v("スレッドプログラミングを書いているという認識を持ち、書こうとしている処理がスレッドセーフなのか常に確認しましょう\n"),s("ul",[s("li",[t._v("Webフレームワークが推奨する書き方に習うことで、トラブルを防ぐことができます")])])]),t._v(" "),s("li",[t._v("ロックなど排他制御の実装を行う際は自前で実装せず、必ずライブラリを利用しましょう")])]),t._v(" "),s("p",[t._v("並行・並列処理、スレッドプログラミングは非常に複雑になります(これは実行するまで処理がどのような順番で実行されるか分からない非決定性から来ています)。")]),t._v(" "),s("p",[t._v("Webフレームワークはこの並行・並列処理の複雑性を隠蔽するために発達しているという側面があり、推奨される書き方をすることで複雑性を意識しなくても実装が可能なようになっています。\nしかし自分が今どのような仕組みの上で実装しているのかを意識していないと、思わぬ事故を起こす可能性があります。")]),t._v(" "),s("p",[t._v("フレームワークが提供するレールに乗りながらも、自分がスレッドプログラミングをしていることを常に意識することが大切です。")]),t._v(" "),s("credit-footer")],1)}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{467:function(t,s,a){t.exports=a.p+"assets/img/fork.drawio.fb839165.png"},468:function(t,s,a){t.exports=a.p+"assets/img/thread.drawio.25427a90.png"},545:function(t,s,a){"use strict";a.r(s);var n=a(10),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("header-table"),t._v(" "),s("h1",{attrs:{id:"page-frontmatter-title"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[t._v("#")]),t._v(" "+t._s(t.$page.frontmatter.title))]),t._v(" "),s("h2",{attrs:{id:"環境準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#環境準備"}},[t._v("#")]),t._v(" 環境準備")]),t._v(" "),s("p",[t._v("以下のdockerコマンドでコンソールを取得してください。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ docker pull iijrfujimoto/bootcamp_concurrent\n$ mkdir bootcamp_work # 作業用のディレクトリ。名前はなんでもいい\n$ cd bootcamp_work\n$ docker run --name bootcamp_concurrent -p 8000:8000 -v $PWD:/work --rm -it iijrfujimoto/bootcamp_concurrent /bin/bash\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br")])]),s("h2",{attrs:{id:"この資料の約束"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#この資料の約束"}},[t._v("#")]),t._v(" この資料の約束")]),t._v(" "),s("p",[t._v("ターミナルでの実行例を示す際、以下のように"),s("code",[t._v("$")]),t._v("で始まっている場合はdockerを起動しているホスト側で実行するコマンドを示します。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("以下のように"),s("code",[t._v("root@40e566b8e23e")]),t._v("などで示されている場合はdockerコンテナ上で実行するコマンドであることを示します。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("root@0dd4d9fad678:/work# python3 main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("h2",{attrs:{id:"このハンズオンの目的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#このハンズオンの目的"}},[t._v("#")]),t._v(" このハンズオンの目的")]),t._v(" "),s("p",[t._v("このハンズオンでは、Webアプリケーションの実装に欠かせない「並行処理」について取り扱います。")]),t._v(" "),s("p",[t._v("プログラミングにおいて、同時に複数の処理を行う「並行処理」は複雑で実装が難しいものです。\nそれは普段のプログラムが「決定的」なものであるのに対し、並行処理は実行時に何が起こるのか分からない「非決定的」な動作となるからです。")]),t._v(" "),s("p",[t._v("しかしユーザからの多数のリクエストに対応するため、Webアプリケーションに並行処理の実装は必須です。\n昨今ではライブラリやフレームワークが発達し並行処理を意識しなくてもWebアプリケーションを作ることが可能ですが、\n並行処理の勘所を理解せずに使うと思わぬバグや事故を起こす可能性があります。")]),t._v(" "),s("p",[t._v("このハンズオンではWebアプリケーションにおける並行処理実装の初歩的な注意点を紹介し、不具合を起こさないための知識を得てもらう目的としています。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("並行処理と並列処理")]),t._v(" "),s("p",[t._v("並行処理(concurrent processing)と並列処理(parallel processing)は似た言葉ですが異なる動作を指す言葉です。")]),t._v(" "),s("ul",[s("li",[t._v("並行処理: ある時間内に複数のタスクを処理すること")]),t._v(" "),s("li",[t._v("並列処理: 複数のタスクを「同時に」処理すること")])]),t._v(" "),s("p",[t._v("「並行処理」と言う場合はある時間内に複数タスクを実行できればいいので、実行するタスクを小まめに切り替えながら処理する動作も含みます。\n一方で「並列処理」の場合は複数のCPUコアによって全く同時に複数タスクを処理することを指します。")]),t._v(" "),s("p",[t._v("(参考: "),s("a",{attrs:{href:"https://go.dev/blog/waza-talk",target:"_blank",rel:"noopener noreferrer"}},[t._v("Concurrency is not parallelism"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("h2",{attrs:{id:"ハンズオン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン"}},[t._v("#")]),t._v(" ハンズオン")]),t._v(" "),s("h3",{attrs:{id:"簡単な並行処理サンプル"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#簡単な並行処理サンプル"}},[t._v("#")]),t._v(" 簡単な並行処理サンプル")]),t._v(" "),s("p",[t._v("まずはPythonで簡単なWebサーバを書いてみましょう。")]),t._v(" "),s("p",[t._v("vscode等を利用して作業用ディレクトリ("),s("code",[t._v("bootcamp_work")]),t._v(")でPythonコードを書いていきましょう。")]),t._v(" "),s("div",{staticClass:"language-termianl line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ vim main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello simple server!\\n'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br")])]),s("p",[t._v("保存したら、dockerコンテナ内からサーバを起動してみます。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("root@0dd4d9fad678:/work# python3 main.py\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("手元のホストから以下のようにcurlで叩いてみましょう。\n「Hello simple server!」と返ってくれば成功です。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000\nHello simple server!\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br")])]),s("p",[t._v("リクエストした直後、サーバー側のログに"),s("code",[t._v("start processing path = /")]),t._v("と表示されたことを覚えておいてください。")]),t._v(" "),s("h3",{attrs:{id:"メモリ空間の共有とレースコンディション"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#メモリ空間の共有とレースコンディション"}},[t._v("#")]),t._v(" メモリ空間の共有とレースコンディション")]),t._v(" "),s("h4",{attrs:{id:"サンプルコード"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#サンプルコード"}},[t._v("#")]),t._v(" サンプルコード")]),t._v(" "),s("p",[t._v("先ほどのプログラムを少し改造して、今までのアクセス数をカウントできるようにしてみましょう。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br")])]),s("p",[t._v("以下のようにcurlを複数叩いてみます。"),s("code",[t._v("&")]),t._v("はコマンドをバックグラウンドで実行する書き方です。\n先ほどはサーバ側が処理をする10秒間curlコマンドはずっと待っていましたが、"),s("code",[t._v("&")]),t._v("をつけることですぐに次のコマンドを叩けます。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("p",[t._v("さて結果はどうでしょうか。3回コマンドを実行しましたが、サーバのログは以下のようになったのではないでしょうか。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("serving at port 8000\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nend processing path = /, after request count = 1\nend processing path = /, after request count = 1\nend processing path = /, after request count = 1\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br")])]),s("p",[t._v("きちんとリクエスト数をカウントできていない、重大な不具合があるようです。")]),t._v(" "),s("h4",{attrs:{id:"コード解説"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#コード解説"}},[t._v("#")]),t._v(" コード解説")]),t._v(" "),s("h4",{attrs:{id:"forkとthread"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#forkとthread"}},[t._v("#")]),t._v(" forkとthread")]),t._v(" "),s("p",[t._v("このプログラムではユーザからのリクエストを同時に処理するために "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/http.server.html#http.server.ThreadingHTTPServer",target:"_blank",rel:"noopener noreferrer"}},[t._v("ThreadingHTTPServer"),s("OutboundLink")],1),t._v(" を利用しています。\nThreadingHTTPServer の説明を見てみましょう。")]),t._v(" "),s("blockquote",[s("p",[t._v("This class is identical to HTTPServer but uses threads to handle requests by using the ThreadingMixIn.")])]),t._v(" "),s("p",[t._v("どうやらリクエストを処理(handle)するために「スレッド」を利用するようです。\n"),s("a",{attrs:{href:"https://docs.python.org/ja/3.7/library/socketserver.html#socketserver.ThreadingMixIn",target:"_blank",rel:"noopener noreferrer"}},[t._v("ThreadingMixIn"),s("OutboundLink")],1),t._v(" の説明も見てみましょう。")]),t._v(" "),s("blockquote",[s("p",[t._v("Forking and threading versions of each type of server can be created using these mix-in classes.")])]),t._v(" "),s("p",[s("code",[t._v("fork")]),t._v("もしくは"),s("code",[t._v("thread")]),t._v("いずれかの仕組みで渡したServerオブジェクトを並列に実行してくれるようです。"),s("code",[t._v("fork")]),t._v("と"),s("code",[t._v("thread")]),t._v("は両方とも並列処理のための方法ですが、大きな違いがあります。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(467),alt:"fork",title:"fork"}})]),t._v(" "),s("p",[s("img",{attrs:{src:a(468),alt:"thread",title:"thread"}})]),t._v(" "),s("p",[t._v("図の通り"),s("code",[t._v("fork")]),t._v("の場合forkで分かれたプロセス上で"),s("code",[t._v("SimpleHelloHandler")]),t._v("のインスタンスが実行されます。そのためリクエストを処理する各インスタンス間でメモリ空間を共有していません(できないとも言う)。\n一方で"),s("code",[t._v("thread")]),t._v("の場合はメモリ空間を共有した同じプロセス内で"),s("code",[t._v("SimpleHelloHandler")]),t._v("インスタンスが実行されます。")]),t._v(" "),s("p",[t._v("今回"),s("code",[t._v("request_total")]),t._v("はクラス変数として宣言されています。そのため各"),s("code",[t._v("SimpleHelloHandler")]),t._v("インスタンスから共有するメモリ上の変数としてアクセスが可能です。\nWebサーバを実装する時に限りませんが、ライブラリやフレームワークを利用する際にはそれがどういう仕組みで動くのか把握しておく必要があります。")]),t._v(" "),s("h4",{attrs:{id:"クリティカルセッション"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#クリティカルセッション"}},[t._v("#")]),t._v(" クリティカルセッション")]),t._v(" "),s("p",[s("code",[t._v("count_and_do_something")]),t._v("の以下の部分に注目してみましょう(といっても中身全てですが)。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("div",{staticClass:"highlight-lines"},[s("br"),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("br")]),s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n")])]),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br")])]),s("p",[t._v("上記2-10行目は"),s("code",[t._v("SimpleHelloHandler.request_total")]),t._v("という共有資源にアクセスしており、複数のスレッドから同時にアクセスされると不具合が起きます。このような箇所を「"),s("strong",[t._v("クリティカルセクション")]),t._v("」と呼びます。\nまた実際にクリティカルセクションに複数のスレッドが同時にアクセスしてしまい、不具合が起きている状態を「"),s("strong",[t._v("レースコンディション(競合状態)")]),t._v("」と呼びます。")]),t._v(" "),s("p",[t._v("スレッドなどを利用する並行処理プログラミングではこのクリティカルセクションを如何に減らし、そして保護するかが大切になります。")]),t._v(" "),s("h3",{attrs:{id:"排他制御・アトミック処理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#排他制御・アトミック処理"}},[t._v("#")]),t._v(" 排他制御・アトミック処理")]),t._v(" "),s("h4",{attrs:{id:"排他制御"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#排他制御"}},[t._v("#")]),t._v(" 排他制御")]),t._v(" "),s("p",[t._v("クリティカルセクションを保護する手法の一つが、ロックを取得する排他制御と呼ばれるものです。まずは素朴に実装してみましょう。\n(説明用にすごく雑な実装なので間違っても仕事で以下のようなコードを書かないでください)")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# request_total_lock がFalseになるまで無限ループする")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの取得")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 処理が完了したらロックを解放する")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br"),s("span",{staticClass:"line-number"},[t._v("37")]),s("br"),s("span",{staticClass:"line-number"},[t._v("38")]),s("br"),s("span",{staticClass:"line-number"},[t._v("39")]),s("br")])]),s("p",[t._v("サーバを実行してみてください。先ほどと同じようにcurlを実行するとどうでしょうか。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("p",[t._v("ここでは新しくクラス変数として"),s("code",[t._v("request_total_lock")]),t._v("を追加し、"),s("code",[t._v("count_and_do_something")]),t._v("の先頭でこの変数を検査しています。\n"),s("code",[t._v("True")]),t._v("の場合は次のループに遷移し、無限に検査を繰り返します。"),s("code",[t._v("False")]),t._v("だった場合は"),s("code",[t._v("True")]),t._v("を代入しクリティカルセクションに入ります。\nクリティカルセクションの終了後、"),s("code",[t._v("request_total_lock")]),t._v("に"),s("code",[t._v("False")]),t._v("を代入し、別のスレッドが実行できるようにします。")]),t._v(" "),s("p",[t._v("少しややこしいですが、複数のスレッド間の動作を順番に書くと以下のようになります。")]),t._v(" "),s("ol",[s("li",[t._v("最初のリクエスト(thread1)が来て"),s("code",[t._v("count_and_do_something")]),t._v("が実行される")]),t._v(" "),s("li",[s("code",[t._v("request_total_lock")]),t._v("は"),s("code",[t._v("False")]),t._v("なのでthread1が"),s("code",[t._v("True")]),t._v("を代入してロックを取得する")]),t._v(" "),s("li",[t._v("2個目のリクエスト(thread2)が来て"),s("code",[t._v("count_and_do_something")]),t._v("が実行される")]),t._v(" "),s("li",[s("code",[t._v("request_total_lock")]),t._v("は"),s("code",[t._v("True")]),t._v("なのでthread2は無限ループを続ける")]),t._v(" "),s("li",[t._v("thread1でクリティカルセクションが完了し、"),s("code",[t._v("request_total_lock")]),t._v("に"),s("code",[t._v("False")]),t._v("が代入される")]),t._v(" "),s("li",[t._v("thread2がロックを取得し、クリティカルセクションを開始する")])]),t._v(" "),s("p",[t._v("このロック機構によりクリティカルセクションが保護され、"),s("code",[t._v("request_total")]),t._v("が正しくカウントされるようになりました。\nこのようにロックが取得できるまで無限ループで待ち続けるような実装を「スピンロック」と言います。")]),t._v(" "),s("p",[t._v("このコードは一見うまくいっていますが、実はレースコンディションを引き起こすクリティカルセクションが隠れています。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("div",{staticClass:"highlight-lines"},[s("br"),s("br"),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("div",{staticClass:"highlighted"},[t._v(" ")]),s("br"),s("br")]),s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ここから")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ここまでが実はクリティカルセクション")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n")])]),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br")])]),s("p",[t._v("上記の3行目で"),s("code",[t._v("request_total_lock")]),t._v("を見た後"),s("code",[t._v("True")]),t._v("を代入するまでにわずかながらでもラグがあるため、運が悪いと3行目が複数のスレッドで同時に実行される可能性があります。\nすると複数のスレッドが同時にロックを取得してしまい、クリティカルセクションが同時に実行されてしまいます。")]),t._v(" "),s("p",[t._v("後でも記載しますが、これは"),s("code",[t._v("request_total_lock")]),t._v("の検査とロックの獲得が「アトミックでない」処理のためです。")]),t._v(" "),s("p",[t._v("さて、このように並行処理に関わるスピンロックなどのコードを自前で実装するのはやめて、ライブラリを使いましょう。これはスレッドプログラミングにおける大原則です。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" threading "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Lock\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("acquire"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの取得。他のスレッドはアクセスできなくなる")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("release"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ロックの解放")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br")])]),s("p",[t._v("Pythonの "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/threading.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("threadingパッケージ"),s("OutboundLink")],1),t._v(" にはスレッドプログラミングに使えるツールが用意されています。今回は "),s("a",{attrs:{href:"https://docs.python.org/ja/3/library/threading.html#lock-objects",target:"_blank",rel:"noopener noreferrer"}},[t._v("Lock"),s("OutboundLink")],1),t._v(" を利用しました。\n"),s("code",[t._v("acquire()")]),t._v("を実行するとロックを取得し、もし他のスレッドで既に取得済みの場合はブロック(コード実行をそこで停止する)します。")]),t._v(" "),s("p",[t._v("この部分は以下のようにも書けます。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br")])]),s("p",[t._v("クリティカルセクションが分かりやすくロックの開放し忘れもないので、基本的には"),s("code",[t._v("with")]),t._v("を利用して書きましょう。")]),t._v(" "),s("h4",{attrs:{id:"スレッドセーフ-アトミック-な処理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#スレッドセーフ-アトミック-な処理"}},[t._v("#")]),t._v(" スレッドセーフ(アトミック)な処理")]),t._v(" "),s("p",[t._v("ロックを取得することでクリティカルセクションを保護できることは分かりました。\nしかし皆さんも薄々お気づきの通り、そもそもこのコードのクリティカルセクションをもっと小さくできるのではないでしょうか。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br")])]),s("p",[t._v("どうでしょう。試しに同じようにcurlで叩いてみましょう。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ curl localhost:8000 &\n$ curl localhost:8000 &\n$ curl localhost:8000 &\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("serving at port 8000\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nstart processing path = /, before request count = 0\nend processing path = /, after request count = 1\nend processing path = /, after request count = 2\nend processing path = /, after request count = 3\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br")])]),s("p",[s("code",[t._v("before request count")]),t._v(" はうまく出力できなくなってしまいましたが、「リクエスト数をカウントする」という要件は満たせているように見えます。\nしかしここにもレースコンディションを引き起こすクリティカルセクションが隠れています。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# クリティカルセクション")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br")])]),s("p",[t._v("スレッドプログラミングにおいて、ある処理が複数スレッドから同時に実行されても問題ない実装であることを「スレッドセーフ(thread safe)である」と言います。\n例えば先ほどの"),s("code",[t._v("acquire()")]),t._v("はスレッドセーフなメソッドであり、複数スレッドから同時にアクセスされてもレースコンディションを起こさないように実装されています。")]),t._v(" "),s("p",[t._v("一方でPythonにおける"),s("code",[t._v("+= 1")]),t._v("とはスレッドセーフな処理なんでしょうか。結論から言うとそうではありません。\nなぜこの短いコードでレースコンディションが発生するのでしょうか。")]),t._v(" "),s("p",[t._v("Pythonで書かれたコードは最終的にバイトコードにコンパイルされて実行されます(CPythonの場合)。\n"),s("code",[t._v("dis")]),t._v("を利用して実際に実行されるバイトコードを見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('root@40e566b8e23e:/work# python3\nPython 3.9.2 (default, Feb 28 2021, 17:03:44)\n[GCC 10.2.1 20210110] on linux\nType "help", "copyright", "credits" or "license" for more information.\n>>> import dis\n>>> a = 0\n>>> dis.dis("a += 1")\n 1 0 LOAD_NAME 0 (a)\n 2 LOAD_CONST 0 (1)\n 4 INPLACE_ADD\n 6 STORE_NAME 0 (a)\n 8 LOAD_CONST 1 (None)\n 10 RETURN_VALUE\n>>>\n')])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br")])]),s("p",[s("code",[t._v("a += 1")]),t._v("というコードは上記の通り6行のバイトコードに変換されます。内容を見てみると以下のように動作することが分かります。")]),t._v(" "),s("ol",[s("li",[t._v("「a」というメモリ領域から値を読み込む("),s("code",[t._v("LOAD_NAME")]),t._v(")")]),t._v(" "),s("li",[t._v("加算する("),s("code",[t._v("INPLACE_ADD")]),t._v(")")]),t._v(" "),s("li",[t._v("「a」というメモリ領域に値を書き込む("),s("code",[t._v("STORE_NAME")]),t._v(")")])]),t._v(" "),s("p",[t._v("つまり"),s("code",[t._v("a += 1")]),t._v("という一見1ステップのコードでも、バイトコードやその先の機械語レベルでは複数のステップに分かれた処理である可能性があります。\n(ここで「可能性」と言っているのは、あくまで言語やコンパイラの仕様や実装次第なためです。少なくともCPythonではアトミックな機械語は生成されないはずです。)")]),t._v(" "),s("p",[t._v("仮に機械語レベルで複数ステップに分かれた処理であった場合、タイミングによってはthread1が1ステップ目を実行した後、\n入れ替わりthread2が1ステップ目を実行してしまう可能性があります。\nそのため"),s("code",[t._v("a += 1")]),t._v("のような一見単純なコードでも、レースコンディションを起こす可能性があります。")]),t._v(" "),s("p",[t._v("スレッドプログラミングにおいては常に「その処理がスレッドセーフなのか」、仕様を確認しつつ進める必要があります。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("アトミックな処理")]),t._v(" "),s("p",[t._v("「アトム(atom)」とは「これ以上分割不可能な単位」という意味を持ち、「原子性」などとも呼ばれます。\n同じように「アトミックな処理」とはその処理が「外部から途中経過が観測できず、失敗した場合は処理前の状態に復元される」ことを言います。\nその意味でCPythonにおける"),s("code",[t._v("a += 1")]),t._v("はメモリからreadしてwriteする過程があるためアトミックな処理ではありません。")]),t._v(" "),s("p",[t._v("例えばMySQLなどのRDBでトランザクションを張り、最後にCOMMITする操作はアトミックな処理です。\nまた最近のCPUには加算処理などをアトミックに行う処理が命令として用意されており、言語によってはそういった命令を利用してアトミックな加算処理などを利用することが可能です。")]),t._v(" "),s("p",[t._v("例: gccなどのCコンパイラに実装されている"),s("code",[t._v("__sync_fetch_and_add")]),t._v("など")])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("コンテキストスイッチ")]),t._v(" "),s("p",[t._v("CPython(c言語で書かれたPython処理系)にはGILという仕組みがあり、Pythonプログラムは1個のCPUコアしか使うことができません。\n1個のCPUコアでは同時に1個の仕事しかできないため、複数のスレッドを動かす際は1個のCPUコアの処理時間を分け合うことになります。\nつまりあるCPUコアでthread1の仕事を進めつつあるタイミングでthread1を待機状態にし、thread2の仕事を始める・・・ということを行っていきます。\nこの仕事の割り振りを決めるのがLinuxカーネルにおける「スケジューラ」と呼ばれる機能であり、thread1->thread2など仕事内容が切り替わるのを「コンテキストスイッチ」と呼びます。")])]),t._v(" "),s("p",[s("code",[t._v("a += 1")]),t._v(" がスレッドセーフでないことが分かったので、きちんとロックを利用してスレッドセーフな実装にしましょう。")]),t._v(" "),s("div",{staticClass:"language-python line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" http"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("server "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ThreadingHTTPServer\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" threading "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Lock\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" time\n\nPORT "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8000")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SimpleHelloHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("BaseHTTPRequestHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n request_total_lock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("increment_request_total")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total_lock"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("count_and_do_something")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'start processing path = {}, before request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sleep"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 何かの処理")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("increment_request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'end processing path = {}, after request count = {}'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("do_GET")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count_and_do_something"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Content-Type'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("end_headers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wfile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("write"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'Hello! request count=%a\\n'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request_total"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" ThreadingHTTPServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SimpleHelloHandler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"serving at port"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PORT"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n httpd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("serve_forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br"),s("span",{staticClass:"line-number"},[t._v("25")]),s("br"),s("span",{staticClass:"line-number"},[t._v("26")]),s("br"),s("span",{staticClass:"line-number"},[t._v("27")]),s("br"),s("span",{staticClass:"line-number"},[t._v("28")]),s("br"),s("span",{staticClass:"line-number"},[t._v("29")]),s("br"),s("span",{staticClass:"line-number"},[t._v("30")]),s("br"),s("span",{staticClass:"line-number"},[t._v("31")]),s("br"),s("span",{staticClass:"line-number"},[t._v("32")]),s("br"),s("span",{staticClass:"line-number"},[t._v("33")]),s("br"),s("span",{staticClass:"line-number"},[t._v("34")]),s("br"),s("span",{staticClass:"line-number"},[t._v("35")]),s("br"),s("span",{staticClass:"line-number"},[t._v("36")]),s("br"),s("span",{staticClass:"line-number"},[t._v("37")]),s("br")])]),s("h2",{attrs:{id:"最後に"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#最後に"}},[t._v("#")]),t._v(" 最後に")]),t._v(" "),s("p",[t._v("この資料で伝えたいのは以下のことです。")]),t._v(" "),s("ul",[s("li",[t._v("ライブラリやフレームワークを利用する際はその仕組みをきちんと理解しましょう\n"),s("ul",[s("li",[t._v("webプログラミングにおいては特に並行・並列処理をどのように実装しているのか確認しましょう")])])]),t._v(" "),s("li",[t._v("スレッドプログラミングを書いているという認識を持ち、書こうとしている処理がスレッドセーフなのか常に確認しましょう\n"),s("ul",[s("li",[t._v("Webフレームワークが推奨する書き方に習うことで、トラブルを防ぐことができます")])])]),t._v(" "),s("li",[t._v("ロックなど排他制御の実装を行う際は自前で実装せず、必ずライブラリを利用しましょう")])]),t._v(" "),s("p",[t._v("並行・並列処理、スレッドプログラミングは非常に複雑になります(これは実行するまで処理がどのような順番で実行されるか分からない非決定性から来ています)。")]),t._v(" "),s("p",[t._v("Webフレームワークはこの並行・並列処理の複雑性を隠蔽するために発達しているという側面があり、推奨される書き方をすることで複雑性を意識しなくても実装が可能なようになっています。\nしかし自分が今どのような仕組みの上で実装しているのかを意識していないと、思わぬ事故を起こす可能性があります。")]),t._v(" "),s("p",[t._v("フレームワークが提供するレールに乗りながらも、自分がスレッドプログラミングをしていることを常に意識することが大切です。")]),t._v(" "),s("credit-footer")],1)}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/26.db85e0d0.js b/assets/js/26.381232ac.js similarity index 99% rename from assets/js/26.db85e0d0.js rename to assets/js/26.381232ac.js index f32208c9..dfc353bc 100644 --- a/assets/js/26.db85e0d0.js +++ b/assets/js/26.381232ac.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{470:function(s,t,a){s.exports=a.p+"assets/img/white-label-error.c9ee4513.png"},471:function(s,t,a){s.exports=a.p+"assets/img/http-handler.0b72f6d9.png"},553:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"始めに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#始めに"}},[s._v("#")]),s._v(" 始めに")]),s._v(" "),t("ul",[t("li",[s._v("Javaの基本")]),s._v(" "),t("li",[s._v("Spring Bootの基本")]),s._v(" "),t("li",[s._v("Spring Bootハンズオン")])]),s._v(" "),t("p",[s._v("本講義では上記について紹介し、Javaというプログラミング言語とそのWebフレームワークであるSpring Bootといった技術的な選択肢を増やすことを目的としています。")]),s._v(" "),t("h3",{attrs:{id:"本講義の前提"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の前提"}},[s._v("#")]),s._v(" 本講義の前提")]),s._v(" "),t("p",[s._v("本講義ではプログラム言語共通の制御構文や概念、たとえばif文や型など、の解説は行いません。そのため、受講者は何らかのプログラミング言語で簡単な制御構文が書けることを前提とさせてください。")]),s._v(" "),t("h3",{attrs:{id:"この資料のお約束"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#この資料のお約束"}},[s._v("#")]),s._v(" この資料のお約束")]),s._v(" "),t("p",[s._v("💻 は自分で操作する箇所を示しています。 また"),t("code",[s._v("$")]),s._v("はホストマシンのプロンプトを意味し、"),t("code",[s._v("❯")]),s._v("はコンテナ内部でのプロンプトを意味します。")]),s._v(" "),t("p",[s._v("たとえば下記の通りです。")]),s._v(" "),t("div",{staticClass:"language-shell line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ホストマシン上で git clone git@github.com:iij/bootcamp.git を実行する")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" clone git@github.com:iij/bootcamp.git\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナ上で curl localhost:8080 を実行する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"下準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#下準備"}},[s._v("#")]),s._v(" 下準備")]),s._v(" "),t("p",[s._v("講義を受講する前にコンテナイメージのpullと起動をしておくことをお勧めしています。\nまた、Dockerの実行環境があることを前提として本講義を進めます。")]),s._v(" "),t("h4",{attrs:{id:"手順"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#手順"}},[s._v("#")]),s._v(" 手順")]),s._v(" "),t("ol",[t("li",[s._v("ハンズオン用のDockerイメージをpullしてくる")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# やや重たいので注意してください")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull tamago0224/bootcamp-springboot:2023\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("ol",{attrs:{start:"2"}},[t("li",[s._v("コンテナを起動する")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# プロキシ環境下にいる人はプロキシの設定をする")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("PROXY_HOST")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("YOUR.PROXY.HOST\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("PROXY_PORT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("YOUR_PROXY_PORT\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("JAVA_OPT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"-Dhttp.proxyHost='),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_HOST}")]),s._v(" -Dhttp.proxyPort="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_PORT}")]),s._v(" -Dhttps.proxyHost="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_HOST}")]),s._v(" -Dhttps.proxyPort="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_PORT}")]),s._v('"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# プロキシ設定ここまで")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナを起動する")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name bootcamp-springboot -it -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":8080 -e "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("JAVA_OPT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"'),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${JAVA_OPT}")]),s._v('"')]),s._v(" tamago0224/bootcamp-springboot:2023\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("ol",{attrs:{start:"3"}},[t("li",[s._v("アプリケーションの起動チェック")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootを起動する")]),s._v("\n❯ ./gradlew bootrun\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ...")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# いろんなログが流れる")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("ol",{attrs:{start:"4"}},[t("li",[s._v("動作チェック")])]),s._v(" "),t("p",[s._v("ホストマシンの適当なブラウザから"),t("a",{attrs:{href:"http://localhost:8080",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8080"),t("OutboundLink")],1),s._v("にアクセスし、下記のようなエラーページが表示されることを確認してください。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(470),alt:"初回起動 - WhitelabelErrorPage"}})]),s._v(" "),t("h2",{attrs:{id:"javaの基本"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#javaの基本"}},[s._v("#")]),s._v(" Javaの基本")]),s._v(" "),t("p",[s._v("JavaはOpenJDKコミュニティによって開発され、各ベンダからリリースされているプログラミング言語です。\n古くから利用されているプログラミング言語/プラットフォームである一方、2022年現在でもSIや大規模開発の現場などでよく利用されています。")]),s._v(" "),t("h3",{attrs:{id:"言語としての特徴"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#言語としての特徴"}},[s._v("#")]),s._v(" 言語としての特徴")]),s._v(" "),t("p",[s._v("JavaはC言語やRustと同じ静的型付き言語です。そのため文法や記法はC言語を踏襲した書き方となっています。"),t("br"),s._v("\nJavaの言語のパラダイムとしては、クラスの継承の概念やインタフェースなどの言語仕様を持つためオブジェクト指向プログラミングであります。その一方、Javaのバージョン8以降は関数型インタフェースやパターンマッチなど、関数型プログラミングを楽しめるような仕様も導入されています。")]),s._v(" "),t("details",{staticClass:"custom-block details"},[t("summary",[s._v("Javaのサンプルコード")]),s._v(" "),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// パッケージ名(世界でユニークであると良い)")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 自社ドメインを持つ企業での製造物には自社ドメインをそのまま利用することが多い")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 外部モジュールの利用(JavaではIDEに任せてしまうのが一般的)")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * 複数行に渡るコメント文\n *\n * - 一般的にはJavaのクラス名の命名はパスカルケース - クラス名とjavaファイルの名前は一致させる方が良い(1クラス1ファイル)\n */")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SampleClass")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("extends")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Object")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// アクセス修飾子はprivate/protected/public")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamPrivate"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Javaの変数・メソッドの命名は(ローワー)キャメルケース")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("protected")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamProtected"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamPublic"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// アノテーション。そのものに効果があるものではなく、横断的に処理したりする際の目印として使うことが多い")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SuppressWarnings")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"unused"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("sampleMethod")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" a"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" b "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("a"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n b "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" b"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 残念ながらJavaはnull安全な言語ではない(NullPointerExceptionの危機)")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// クラスの中にクラスを定義することもできる(インナークラス)")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("final")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" finalizedString"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// final化(不変化)ができる")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" arg"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// コンストラクタはクラス名を同一にすることで表現")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("finalizedString "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" arg"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getFinalizedString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("finalizedString"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("makeInstance")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('// クラスの具体的な値(オブジェクト)のことを"インスタンス"と言います')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" ins "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Java11から型推論が使える")]),s._v("\n ins"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getFinalizedString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// → hello")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br")])])]),s._v(" "),t("h3",{attrs:{id:"プロジェクト構成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#プロジェクト構成"}},[s._v("#")]),s._v(" プロジェクト構成")]),s._v(" "),t("p",[s._v('Javaのプロジェクトのディレクトリ構成は、ほかのプログラミング言語と異なり言語として定められています。名前空間として定義できる"パッケージ"がそのままディレクトリに反映されるようにディレクトリを構成する必要があります。')]),s._v(" "),t("p",[s._v("参考: "),t("a",{attrs:{href:"https://docs.oracle.com/javase/tutorial/java/package/managingfiles.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.oracle.com/javase/tutorial/java/package/managingfiles.html"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("たとえば"),t("code",[s._v("com.github.iij.bootcamp")]),s._v("パッケージ(=名前空間)配下に"),t("code",[s._v("Hoge")]),s._v("と"),t("code",[s._v("Huga")]),s._v("というクラスを作成する場合、下記のようなディレクトリ構造/ファイル構造になります。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(".\n├── build.gradle\n└── src\n └── main\n └── java\n └── com\n └── github\n └── iij\n └── bootcamp\n └── Hoge.java\n │ └── class Hoge { ... }\n └── Huga.java\n └── class Huga { ... }\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br")])]),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("パッケージ")]),s._v(" "),t("p",[s._v("Javaにおけるパッケージとは、DNS形式で定義できる名前空間のような概念です。"),t("code",[s._v(".")]),s._v("で区切ることでパッケージ間の親子関係を定義できます。たとえば"),t("code",[s._v("com.example.iij")]),s._v("パッケージと"),t("code",[s._v("com.example.iij.bootcamp")]),s._v("パッケージでは前者が親で後者が子といった関係があります。")])]),s._v(" "),t("h3",{attrs:{id:"gradle"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#gradle"}},[s._v("#")]),s._v(" Gradle")]),s._v(" "),t("p",[s._v("Pythonであればpip、Rustであればcargo、Node.jsであればnpmのようにプログラミング言語にはそれぞれ依存関係を解決しビルドを自動化する自動化システムツールが用意されています。")]),s._v(" "),t("p",[s._v("JavaではMavenとGradleという2つの種類の自動化システムツールがよく利用されています。本講義ではGradleを利用して話を進めていきます。")]),s._v(" "),t("div",{staticClass:"custom-block warning"},[t("p",{staticClass:"custom-block-title"},[s._v("WARNING")]),s._v(" "),t("p",[s._v("Mavenを使わずにGradleを利用する理由は、Gradleの方が優れている/イケているからではなく、単に筆者がXMLが嫌いであるためである。")])]),s._v(" "),t("h2",{attrs:{id:"spring-bootの基本"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#spring-bootの基本"}},[s._v("#")]),s._v(" Spring Bootの基本")]),s._v(" "),t("p",[s._v("Spring BootはJavaのWebフレームワークのひとつです。Spring Bootの規約に従ってアプリケーションロジックを実装することで簡単にWebアプリケーションを構築できます。")]),s._v(" "),t("p",[s._v("このフレームワークは大規模な主幹システムやWebアプリケーションを実装/構築する際によく利用されており、IIJがホストしているいくつかのサービスもSpring Bootを利用して実装されています。")]),s._v(" "),t("h3",{attrs:{id:"特徴"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#特徴"}},[s._v("#")]),s._v(" 特徴")]),s._v(" "),t("p",[s._v("Spring Bootは複雑な業務要件や非機能要件をクリアするためのさまざまな機能を有しています。依存関係を宣言して注入してくれるDIコンテナや横断的な関心事を解決するAOPのサポート、数多く公開されているstarterパッケージなどはその最たる例です。")]),s._v(" "),t("p",[s._v("残念ながらこのBootcampですべての要素に触れることはできないため、興味のあるほうはドキュメントを読んでみることをお勧めします。")]),s._v(" "),t("h2",{attrs:{id:"spring-bootハンズオン"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#spring-bootハンズオン"}},[s._v("#")]),s._v(" Spring Bootハンズオン")]),s._v(" "),t("p",[s._v("本ハンズオンではブラウザで閲覧できるWeb UIの機能を持たない、HTTP API(Application Programming Interface)だけを持つAPIサーバを構築していきます。")]),s._v(" "),t("p",[s._v("それでは始めましょう。")]),s._v(" "),t("h3",{attrs:{id:"簡単なクラスを作ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なクラスを作ってみる"}},[s._v("#")]),s._v(" 簡単なクラスを作ってみる")]),s._v(" "),t("p",[s._v("まず始めに、Java言語のウォーミングアップとして純粋なJavaのクラスを作ってみましょう。")]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("User")]),s._v("クラスを作成します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/User.java\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name: "')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('","')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id: "')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br")])]),t("p",[s._v("これで"),t("code",[s._v("User")]),s._v("クラスを作成できました。次にこのクラスを実際にインスタンス化してみます。"),t("br"),s._v("\nSpring Bootアプリケーションのmain関数は"),t("code",[s._v("com.github.iij.bootcamp.serverapp.ServerAppApplication")]),s._v("にあります。ためしにこのmain関数の中で"),t("code",[s._v("User")]),s._v("クラスをインスタンス化してみます。")]),s._v(" "),t("p",[s._v("💻 ServerAppApplication.javaを修正してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/ServerAppApplication.java \n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("autoconfigure"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringBootApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SpringBootApplication")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n\t"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("main")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("System")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("out"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("println")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("run")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v('再起動時にログに"name: アリス,id: alice"と表示されていればOKです。')]),s._v(" "),t("h4",{attrs:{id:"チェックポイント"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[s._v("Javaのクラスを作成した")]),s._v(" "),t("li",[s._v("クラスをインスタンス化し、標準出力に文字列を表示した")])]),s._v(" "),t("h4",{attrs:{id:"解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("純粋なJavaのクラスを作成し、インスタンス化とメソッドの呼び出しを行いました。"),t("code",[s._v("User")]),s._v("クラスを眺めてもらうとわかるとおり、"),t("code",[s._v("getName")]),s._v("や"),t("code",[s._v("getId")]),s._v("などJavaにはかなり冗長なコードが多いです。これらのコードはよく「ボイラープレート」と呼ばれ、開発者が嫌うコードです。")]),s._v(" "),t("p",[s._v("JavaにはLombokなどのボイラープレートを解消するツールなどありますが、本講義はあえて紹介しません。興味がある人は調べてみてください。")]),s._v(" "),t("h3",{attrs:{id:"簡単なhttpのインタフェースを作成してみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なhttpのインタフェースを作成してみる"}},[s._v("#")]),s._v(" 簡単なHTTPのインタフェースを作成してみる")]),s._v(" "),t("p",[s._v("それではSpring Bootを使ってみましょう。"),t("br"),s._v("\n簡単なHTTPのインタフェースを作成し、実際にSpring Bootがどのように動作しているのかを見てみます。")]),s._v(" "),t("p",[s._v("💻 ServerAppApplication.javaを修正し、サーバを再起動してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/ServerAppApplication.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("autoconfigure"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringBootApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SpringBootApplication")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("main")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("System")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("out"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("println")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("run")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("helloWorld")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080 -X GET\nhello world\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v('"hello world"が返ってきたら成功です。')]),s._v(" "),t("h4",{attrs:{id:"チェックポイント-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-2"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@RestController")]),s._v("アノテーションをクラスに付与してHTTPのインタフェースを作成した")])]),s._v(" "),t("h4",{attrs:{id:"解説-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-2"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[t("code",[s._v("bootRun")]),s._v("コマンドによりSpring Bootが起動します。すると、Spring Bootの機能により "),t("code",[s._v("@RestController")]),s._v("アノテーションが付いている"),t("code",[s._v("ServerAppApplication.HelloController")]),s._v("がHTTPのインタフェースとして登録されます。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(471),alt:"handler"}})]),s._v(" "),t("p",[s._v("その結果、このSpring Bootが動いている8080番ポート宛のHTTPリクエストと"),t("code",[s._v("ServerAppApplication.HelloController#helloWorld")]),s._v("が紐づけられることになり、"),t("code",[s._v("GET /")]),s._v('へのリクエストのレスポンスとして"hello world"が返ってきました。')]),s._v(" "),t("details",{staticClass:"custom-block details"},[t("summary",[s._v("Spring BootとDIコンテナ")]),s._v(" "),t("p",[s._v("Spring Bootは起動時に起動クラスのパッケージ配下のJavaファイルから特殊なアノテーション("),t("code",[s._v("@Contoroller")]),s._v(" / "),t("code",[s._v("@Component")]),s._v(" / etc...)がついたクラスを探し出します。そしてSpring Bootはそのクラスを適当な方法でインスタンス化し、自身の管理下(=DI コンテナ)に置きます。")]),s._v(" "),t("p",[s._v("(「適当な方法」を指定することもできます > "),t("a",{attrs:{href:"https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Bean Annotation"),t("OutboundLink")],1),s._v(")")]),s._v(" "),t("p",[s._v("DIコンテナに格納されたインスタンスは、後述する"),t("code",[s._v("@Autowired")]),s._v("アノテーションを使って引き出すことができます。")]),s._v(" "),t("p",[s._v("特に、"),t("code",[s._v("@Controller")]),s._v(" "),t("code",[s._v("@RestController")]),s._v("アノテーションが付与されたクラスから生成されたインスタンスはHTTPのインタフェースとして働くことになります。")]),s._v(" "),t("p",[s._v("今回の例では、 "),t("code",[s._v("ServerAppApplication")]),s._v("クラスに"),t("code",[s._v("@SpringBootApplication")]),s._v("アノテーションが付与されているので"),t("code",[s._v("ServerAppApplication")]),s._v("クラスのパッケージ"),t("code",[s._v("com.github.iij.bootcamp.serverapp")]),s._v("配下のクラスから上述の特殊なアノテーションがついているクラスを探索します。")]),s._v(" "),t("p",[t("code",[s._v("HelloController")]),s._v("クラスは"),t("code",[s._v("@RestController")]),s._v("アノテーションが付与されているため、Spring Boot起動時に"),t("code",[s._v("HelloController")]),s._v("がSpring Bootによってインスタンス化されDIコンテナに登録されました。\nその結果、"),t("code",[s._v("GET /")]),s._v("のリクエストをSpring Bootが受け取ると"),t("code",[s._v("HelloController#helloWorld")]),s._v(" が実行されるようになっていたというわけです。")])]),s._v(" "),t("h3",{attrs:{id:"簡単なリクエストを受け取ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なリクエストを受け取ってみる"}},[s._v("#")]),s._v(" 簡単なリクエストを受け取ってみる")]),s._v(" "),t("p",[s._v("次にクエリパラメータから情報を取得してみましょう。")]),s._v(" "),t("p",[s._v("💻 UserController.javaを作成し、サーバを再起動してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" secretUser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("secretUser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("else")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=bob'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-3"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@GetMapping")]),s._v("アノテーションを持つメソッドを作成し、HTTPのハンドラとして登録した")]),s._v(" "),t("li",[s._v("HTTPのハンドラとして登録されたメソッドの引数に"),t("code",[s._v("@RequestParam")]),s._v("アノテーションを付与することでクエリパラメータを実装した")])]),s._v(" "),t("h4",{attrs:{id:"解説-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-3"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("新しいクラス"),t("code",[s._v("UserController")]),s._v("を作成しました。このクラスにも"),t("code",[s._v("@RestController")]),s._v("アノテーションが付いているためHTTPのインタフェースとして振る舞います。")]),s._v(" "),t("p",[t("code",[s._v("UserController")]),s._v("クラスの持つメソッド"),t("code",[s._v("find")]),s._v("には"),t("code",[s._v("@GetMapping")]),s._v("アノテーションがついているため、"),t("code",[s._v("GET /user")]),s._v("宛のリクエストのハンドラとして登録されることになります。そのため、Spring Bootアプリケーションの"),t("code",[s._v("/user")]),s._v("へGETリクエストを送ることでこの"),t("code",[s._v("find")]),s._v("メソッドがコールされます。")]),s._v(" "),t("p",[s._v("さらに"),t("code",[s._v("find")]),s._v("メソッドの引数"),t("code",[s._v("id")]),s._v("に"),t("code",[s._v("@RequestParam")]),s._v("アノテーションが付与されています。これにより、HTTPリクエストのクエリパラメータの値がこの変数に注入されます。つまり"),t("code",[s._v("GET /user?id=bob")]),s._v("へのリクエストをSpring Bootアプリケーションへ送ることで"),t("code",[s._v("find")]),s._v("メソッドがコールされ引数"),t("code",[s._v("id=bob")]),s._v("が引き渡されます。")]),s._v(" "),t("h3",{attrs:{id:"責任を分離する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#責任を分離する"}},[s._v("#")]),s._v(" 責任を分離する")]),s._v(" "),t("p",[s._v("さて、前章まで基本的なHTTPのインタフェースの作り方と使い方について解説してきました。もう少し実装を深めていきましょう。"),t("br"),s._v("\n現在"),t("code",[s._v("UserController")]),s._v("クラスはHTTPのインタフェースとデータソースの管理の2つの責務を持っています。これは単一責務の原理から外れているためリファクタリングする対象です。")]),s._v(" "),t("p",[s._v("今回はシンプルに"),t("code",[s._v("UserController#find")]),s._v("の処理を抽出して別のクラスに分離、処理そのものを"),t("code",[s._v("UserController")]),s._v("クラスの外から与えてあげるようにしましょう。")]),s._v(" "),t("p",[s._v("💻 UserService.javaを作成、UserController.javaを修正し、サーバを再起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 新しいクラスUserServiceを作成する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserService.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# UserControllerクラスを修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stereotype"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Component")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Component")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// データソースに該当する部分")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v(" userPool "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("asList")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールからid値で検索し、その結果を返却します idと一致するユーザーが見つからない場合nullを返却します\n * TODO 本当にこの実装で問題ないか、考えてみましょう\n * - 同一のidをもつインスタンスがいる場合は?データ構造はこれで良いか?\n * - nullは`User`インスタンスではない、では見つからない場合は何を返すべき?\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Java8から導入されたStreamAPI")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("stream")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Streamを作成")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("filter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// idと一致する`User`インスタンスのみを抽出")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findFirst")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 抽出結果の先頭1つだけを取り出す")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("orElse")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// もし抽出した結果何も残らなかった場合、nullを返却する")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("beans"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("factory"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Autowired")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 修正BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Autowired")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 修正END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=bob'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-4"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@Component")]),s._v("アノテーションをクラスに付与し、Spring Boot起動時に自動的にインスタンス化した")]),s._v(" "),t("li",[t("code",[s._v("@Autowired")]),s._v("アノテーションをフィールドに付与し、そのフィールドにSpring Bootがインスタンス化したインスタンスの中から適切なインスタンスを注入した")])]),s._v(" "),t("h4",{attrs:{id:"解説-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-4"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("新しいクラス"),t("code",[s._v("UserService")]),s._v("を作成しました。このクラスには"),t("code",[s._v("@Component")]),s._v("アノテーションが付与されているため、Spring Boot起動時にSpring Bootによって自動的にインスタンス化されSpring Bootの管理下に入ります。このようにSpring Bootに管理されるようになったインスタンスはほかのクラスから"),t("code",[s._v("@Autowired")]),s._v("を利用することで利用されます。")]),s._v(" "),t("p",[s._v("今回の例では"),t("code",[s._v("UserController")]),s._v("クラスが"),t("code",[s._v("userService")]),s._v("フィールドに"),t("code",[s._v("@Autowired")]),s._v("アノテーションを付与しているため自動的に作成された"),t("code",[s._v("UserService")]),s._v("クラスのインスタンスが"),t("code",[s._v("UserController.userService")]),s._v("に代入されることになりました。このように依存関係を分離、外から依存関係を持ち込む構成のことをDI(Dependency Injection)と呼びます。")]),s._v(" "),t("p",[s._v('注意点として、デフォルトの挙動ではSpring Bootが管理するインスタンスは各クラス"1つ"となっています。つまり'),t("code",[s._v("UserController")]),s._v("クラスが引っ張ってきている"),t("code",[s._v("userService")]),s._v("とほかのクラスが引っ張ってこれる"),t("code",[s._v("UserService")]),s._v('インスタンスは完全に一致しています。このようにひとつのインスタンスを使い回す構成のことを"シングルトン"と呼びます。')]),s._v(" "),t("h3",{attrs:{id:"少し複雑なリクエストを受け取ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#少し複雑なリクエストを受け取ってみる"}},[s._v("#")]),s._v(" 少し複雑なリクエストを受け取ってみる")]),s._v(" "),t("p",[s._v("最後に、POSTリクエストとリクエストボディを指定して"),t("code",[s._v("User")]),s._v("インスタンスを登録してみましょう。"),t("br"),s._v(" "),t("code",[s._v("User")]),s._v("インスタンスには"),t("code",[s._v("id")]),s._v("と"),t("code",[s._v("name")]),s._v("の値を指定する必要があるので、これらを与えられるエンドポイントを用意します。")]),s._v(" "),t("p",[s._v("💻 UserService.javaとUserController.javaを修正し、サーバを再起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserService.java\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stereotype"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Component")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Component")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// データソースに該当する部分")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v(" userPool "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("asList")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールからid値で検索し、その結果を返却します idと一致するユーザーが見つからない場合nullを返却します\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("stream")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("filter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findFirst")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("orElse")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールにUserを追加します\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("add")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("beans"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("factory"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Autowired")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("PostMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestBody")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Autowired")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// POST /user 宛のリクエストボディスキーマ")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserCreateRequest")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@PostMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("create")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestBody")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserCreateRequest")]),s._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" newUser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("newUser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br"),t("span",{staticClass:"line-number"},[s._v("49")]),t("br"),t("span",{staticClass:"line-number"},[s._v("50")]),t("br"),t("span",{staticClass:"line-number"},[s._v("51")]),t("br"),t("span",{staticClass:"line-number"},[s._v("52")]),t("br"),t("span",{staticClass:"line-number"},[s._v("53")]),t("br"),t("span",{staticClass:"line-number"},[s._v("54")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080/user -X POST -H "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type: application/json'")]),s._v(" -d "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('\'{"name": "アリス", "id": "alice"}\'')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=alice'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-5"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-5"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@PostMapping")]),s._v("アノテーションをメソッドに付与し、POSTリクエストのHTTPハンドラとして登録した")]),s._v(" "),t("li",[s._v("JavaのPOJOを用いてPOSTリクエストのリクエストボディのスキーマを表現した")])]),s._v(" "),t("h2",{attrs:{id:"まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),t("p",[s._v("以上でSpring Bootのハンズオンは終了です。")]),s._v(" "),t("p",[s._v("本講義ではJavaの基本的な知識や書き方、Spring Bootの使い方など、基本的な機能や文法に触れてもらいました。しかしSpring Bootには多様な機能がまだまだ存在しており、データベースとの接続や非同期処理などさまざまなプロダクション環境で活躍できるポテンシャルを持っているフレームワークです。")]),s._v(" "),t("p",[s._v("本講義が、受講者のみなさまの今後の技術選定の手助けになれれば幸いです。")]),s._v(" "),t("h3",{attrs:{id:"追加の資料"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#追加の資料"}},[s._v("#")]),s._v(" 追加の資料")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://spring.pleiades.io/spring-boot/docs/current/reference/html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Spring Bootリファレンスドキュメント"),t("OutboundLink")],1),s._v(" "),t("ul",[t("li",[s._v("多くのSpring Boot開発者がお世話になる公式ドキュメントです。アプリケーションの開発からデプロイ方法まで、幅広く情報が提供されています。")])])]),s._v(" "),t("li",[t("a",{attrs:{href:"https://spring.pleiades.io/guides",target:"_blank",rel:"noopener noreferrer"}},[s._v("Spring Boot Guides"),t("OutboundLink")],1),s._v(" "),t("ul",[t("li",[s._v("Spring Bootの各種機能を試してみるチュートリアルが公開されています。Pub/SubやMongoDB、Dockerとの連携などSpring Bootの拡張が多種公開されています。興味のある項目に触ってみてください。")])])])]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{469:function(s,t,a){s.exports=a.p+"assets/img/white-label-error.c9ee4513.png"},470:function(s,t,a){s.exports=a.p+"assets/img/http-handler.0b72f6d9.png"},551:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"始めに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#始めに"}},[s._v("#")]),s._v(" 始めに")]),s._v(" "),t("ul",[t("li",[s._v("Javaの基本")]),s._v(" "),t("li",[s._v("Spring Bootの基本")]),s._v(" "),t("li",[s._v("Spring Bootハンズオン")])]),s._v(" "),t("p",[s._v("本講義では上記について紹介し、Javaというプログラミング言語とそのWebフレームワークであるSpring Bootといった技術的な選択肢を増やすことを目的としています。")]),s._v(" "),t("h3",{attrs:{id:"本講義の前提"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の前提"}},[s._v("#")]),s._v(" 本講義の前提")]),s._v(" "),t("p",[s._v("本講義ではプログラム言語共通の制御構文や概念、たとえばif文や型など、の解説は行いません。そのため、受講者は何らかのプログラミング言語で簡単な制御構文が書けることを前提とさせてください。")]),s._v(" "),t("h3",{attrs:{id:"この資料のお約束"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#この資料のお約束"}},[s._v("#")]),s._v(" この資料のお約束")]),s._v(" "),t("p",[s._v("💻 は自分で操作する箇所を示しています。 また"),t("code",[s._v("$")]),s._v("はホストマシンのプロンプトを意味し、"),t("code",[s._v("❯")]),s._v("はコンテナ内部でのプロンプトを意味します。")]),s._v(" "),t("p",[s._v("たとえば下記の通りです。")]),s._v(" "),t("div",{staticClass:"language-shell line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ホストマシン上で git clone git@github.com:iij/bootcamp.git を実行する")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" clone git@github.com:iij/bootcamp.git\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナ上で curl localhost:8080 を実行する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"下準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#下準備"}},[s._v("#")]),s._v(" 下準備")]),s._v(" "),t("p",[s._v("講義を受講する前にコンテナイメージのpullと起動をしておくことをお勧めしています。\nまた、Dockerの実行環境があることを前提として本講義を進めます。")]),s._v(" "),t("h4",{attrs:{id:"手順"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#手順"}},[s._v("#")]),s._v(" 手順")]),s._v(" "),t("ol",[t("li",[s._v("ハンズオン用のDockerイメージをpullしてくる")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# やや重たいので注意してください")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull tamago0224/bootcamp-springboot:2023\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("ol",{attrs:{start:"2"}},[t("li",[s._v("コンテナを起動する")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# プロキシ環境下にいる人はプロキシの設定をする")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("PROXY_HOST")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("YOUR.PROXY.HOST\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("PROXY_PORT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("YOUR_PROXY_PORT\n$ "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("JAVA_OPT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"-Dhttp.proxyHost='),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_HOST}")]),s._v(" -Dhttp.proxyPort="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_PORT}")]),s._v(" -Dhttps.proxyHost="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_HOST}")]),s._v(" -Dhttps.proxyPort="),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${PROXY_PORT}")]),s._v('"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# プロキシ設定ここまで")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナを起動する")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name bootcamp-springboot -it -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":8080 -e "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("JAVA_OPT")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"'),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("${JAVA_OPT}")]),s._v('"')]),s._v(" tamago0224/bootcamp-springboot:2023\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("ol",{attrs:{start:"3"}},[t("li",[s._v("アプリケーションの起動チェック")])]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootを起動する")]),s._v("\n❯ ./gradlew bootrun\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ...")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# いろんなログが流れる")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("ol",{attrs:{start:"4"}},[t("li",[s._v("動作チェック")])]),s._v(" "),t("p",[s._v("ホストマシンの適当なブラウザから"),t("a",{attrs:{href:"http://localhost:8080",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8080"),t("OutboundLink")],1),s._v("にアクセスし、下記のようなエラーページが表示されることを確認してください。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(469),alt:"初回起動 - WhitelabelErrorPage"}})]),s._v(" "),t("h2",{attrs:{id:"javaの基本"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#javaの基本"}},[s._v("#")]),s._v(" Javaの基本")]),s._v(" "),t("p",[s._v("JavaはOpenJDKコミュニティによって開発され、各ベンダからリリースされているプログラミング言語です。\n古くから利用されているプログラミング言語/プラットフォームである一方、2022年現在でもSIや大規模開発の現場などでよく利用されています。")]),s._v(" "),t("h3",{attrs:{id:"言語としての特徴"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#言語としての特徴"}},[s._v("#")]),s._v(" 言語としての特徴")]),s._v(" "),t("p",[s._v("JavaはC言語やRustと同じ静的型付き言語です。そのため文法や記法はC言語を踏襲した書き方となっています。"),t("br"),s._v("\nJavaの言語のパラダイムとしては、クラスの継承の概念やインタフェースなどの言語仕様を持つためオブジェクト指向プログラミングであります。その一方、Javaのバージョン8以降は関数型インタフェースやパターンマッチなど、関数型プログラミングを楽しめるような仕様も導入されています。")]),s._v(" "),t("details",{staticClass:"custom-block details"},[t("summary",[s._v("Javaのサンプルコード")]),s._v(" "),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// パッケージ名(世界でユニークであると良い)")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 自社ドメインを持つ企業での製造物には自社ドメインをそのまま利用することが多い")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 外部モジュールの利用(JavaではIDEに任せてしまうのが一般的)")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * 複数行に渡るコメント文\n *\n * - 一般的にはJavaのクラス名の命名はパスカルケース - クラス名とjavaファイルの名前は一致させる方が良い(1クラス1ファイル)\n */")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SampleClass")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("extends")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Object")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// アクセス修飾子はprivate/protected/public")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamPrivate"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Javaの変数・メソッドの命名は(ローワー)キャメルケース")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("protected")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamProtected"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" iamPublic"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// アノテーション。そのものに効果があるものではなく、横断的に処理したりする際の目印として使うことが多い")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SuppressWarnings")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"unused"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("sampleMethod")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" a"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" b "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("a"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n b "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" b"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 残念ながらJavaはnull安全な言語ではない(NullPointerExceptionの危機)")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// クラスの中にクラスを定義することもできる(インナークラス)")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("final")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" finalizedString"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// final化(不変化)ができる")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" arg"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// コンストラクタはクラス名を同一にすることで表現")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("finalizedString "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" arg"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getFinalizedString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("finalizedString"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("makeInstance")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('// クラスの具体的な値(オブジェクト)のことを"インスタンス"と言います')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" ins "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyInnerClass")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Java11から型推論が使える")]),s._v("\n ins"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getFinalizedString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// → hello")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br")])])]),s._v(" "),t("h3",{attrs:{id:"プロジェクト構成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#プロジェクト構成"}},[s._v("#")]),s._v(" プロジェクト構成")]),s._v(" "),t("p",[s._v('Javaのプロジェクトのディレクトリ構成は、ほかのプログラミング言語と異なり言語として定められています。名前空間として定義できる"パッケージ"がそのままディレクトリに反映されるようにディレクトリを構成する必要があります。')]),s._v(" "),t("p",[s._v("参考: "),t("a",{attrs:{href:"https://docs.oracle.com/javase/tutorial/java/package/managingfiles.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.oracle.com/javase/tutorial/java/package/managingfiles.html"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("たとえば"),t("code",[s._v("com.github.iij.bootcamp")]),s._v("パッケージ(=名前空間)配下に"),t("code",[s._v("Hoge")]),s._v("と"),t("code",[s._v("Huga")]),s._v("というクラスを作成する場合、下記のようなディレクトリ構造/ファイル構造になります。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(".\n├── build.gradle\n└── src\n └── main\n └── java\n └── com\n └── github\n └── iij\n └── bootcamp\n └── Hoge.java\n │ └── class Hoge { ... }\n └── Huga.java\n └── class Huga { ... }\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br")])]),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("パッケージ")]),s._v(" "),t("p",[s._v("Javaにおけるパッケージとは、DNS形式で定義できる名前空間のような概念です。"),t("code",[s._v(".")]),s._v("で区切ることでパッケージ間の親子関係を定義できます。たとえば"),t("code",[s._v("com.example.iij")]),s._v("パッケージと"),t("code",[s._v("com.example.iij.bootcamp")]),s._v("パッケージでは前者が親で後者が子といった関係があります。")])]),s._v(" "),t("h3",{attrs:{id:"gradle"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#gradle"}},[s._v("#")]),s._v(" Gradle")]),s._v(" "),t("p",[s._v("Pythonであればpip、Rustであればcargo、Node.jsであればnpmのようにプログラミング言語にはそれぞれ依存関係を解決しビルドを自動化する自動化システムツールが用意されています。")]),s._v(" "),t("p",[s._v("JavaではMavenとGradleという2つの種類の自動化システムツールがよく利用されています。本講義ではGradleを利用して話を進めていきます。")]),s._v(" "),t("div",{staticClass:"custom-block warning"},[t("p",{staticClass:"custom-block-title"},[s._v("WARNING")]),s._v(" "),t("p",[s._v("Mavenを使わずにGradleを利用する理由は、Gradleの方が優れている/イケているからではなく、単に筆者がXMLが嫌いであるためである。")])]),s._v(" "),t("h2",{attrs:{id:"spring-bootの基本"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#spring-bootの基本"}},[s._v("#")]),s._v(" Spring Bootの基本")]),s._v(" "),t("p",[s._v("Spring BootはJavaのWebフレームワークのひとつです。Spring Bootの規約に従ってアプリケーションロジックを実装することで簡単にWebアプリケーションを構築できます。")]),s._v(" "),t("p",[s._v("このフレームワークは大規模な主幹システムやWebアプリケーションを実装/構築する際によく利用されており、IIJがホストしているいくつかのサービスもSpring Bootを利用して実装されています。")]),s._v(" "),t("h3",{attrs:{id:"特徴"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#特徴"}},[s._v("#")]),s._v(" 特徴")]),s._v(" "),t("p",[s._v("Spring Bootは複雑な業務要件や非機能要件をクリアするためのさまざまな機能を有しています。依存関係を宣言して注入してくれるDIコンテナや横断的な関心事を解決するAOPのサポート、数多く公開されているstarterパッケージなどはその最たる例です。")]),s._v(" "),t("p",[s._v("残念ながらこのBootcampですべての要素に触れることはできないため、興味のあるほうはドキュメントを読んでみることをお勧めします。")]),s._v(" "),t("h2",{attrs:{id:"spring-bootハンズオン"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#spring-bootハンズオン"}},[s._v("#")]),s._v(" Spring Bootハンズオン")]),s._v(" "),t("p",[s._v("本ハンズオンではブラウザで閲覧できるWeb UIの機能を持たない、HTTP API(Application Programming Interface)だけを持つAPIサーバを構築していきます。")]),s._v(" "),t("p",[s._v("それでは始めましょう。")]),s._v(" "),t("h3",{attrs:{id:"簡単なクラスを作ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なクラスを作ってみる"}},[s._v("#")]),s._v(" 簡単なクラスを作ってみる")]),s._v(" "),t("p",[s._v("まず始めに、Java言語のウォーミングアップとして純粋なJavaのクラスを作ってみましょう。")]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("User")]),s._v("クラスを作成します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/User.java\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name: "')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('","')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id: "')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br")])]),t("p",[s._v("これで"),t("code",[s._v("User")]),s._v("クラスを作成できました。次にこのクラスを実際にインスタンス化してみます。"),t("br"),s._v("\nSpring Bootアプリケーションのmain関数は"),t("code",[s._v("com.github.iij.bootcamp.serverapp.ServerAppApplication")]),s._v("にあります。ためしにこのmain関数の中で"),t("code",[s._v("User")]),s._v("クラスをインスタンス化してみます。")]),s._v(" "),t("p",[s._v("💻 ServerAppApplication.javaを修正してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/ServerAppApplication.java \n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("autoconfigure"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringBootApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SpringBootApplication")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n\t"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("main")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("System")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("out"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("println")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\t\t"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("run")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\t"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v('再起動時にログに"name: アリス,id: alice"と表示されていればOKです。')]),s._v(" "),t("h4",{attrs:{id:"チェックポイント"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[s._v("Javaのクラスを作成した")]),s._v(" "),t("li",[s._v("クラスをインスタンス化し、標準出力に文字列を表示した")])]),s._v(" "),t("h4",{attrs:{id:"解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("純粋なJavaのクラスを作成し、インスタンス化とメソッドの呼び出しを行いました。"),t("code",[s._v("User")]),s._v("クラスを眺めてもらうとわかるとおり、"),t("code",[s._v("getName")]),s._v("や"),t("code",[s._v("getId")]),s._v("などJavaにはかなり冗長なコードが多いです。これらのコードはよく「ボイラープレート」と呼ばれ、開発者が嫌うコードです。")]),s._v(" "),t("p",[s._v("JavaにはLombokなどのボイラープレートを解消するツールなどありますが、本講義はあえて紹介しません。興味がある人は調べてみてください。")]),s._v(" "),t("h3",{attrs:{id:"簡単なhttpのインタフェースを作成してみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なhttpのインタフェースを作成してみる"}},[s._v("#")]),s._v(" 簡単なHTTPのインタフェースを作成してみる")]),s._v(" "),t("p",[s._v("それではSpring Bootを使ってみましょう。"),t("br"),s._v("\n簡単なHTTPのインタフェースを作成し、実際にSpring Bootがどのように動作しているのかを見てみます。")]),s._v(" "),t("p",[s._v("💻 ServerAppApplication.javaを修正し、サーバを再起動してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/ServerAppApplication.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("boot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("autoconfigure"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringBootApplication")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@SpringBootApplication")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("main")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("System")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("out"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("println")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("toString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("SpringApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("run")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ServerAppApplication")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" args"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("helloWorld")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080 -X GET\nhello world\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v('"hello world"が返ってきたら成功です。')]),s._v(" "),t("h4",{attrs:{id:"チェックポイント-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-2"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@RestController")]),s._v("アノテーションをクラスに付与してHTTPのインタフェースを作成した")])]),s._v(" "),t("h4",{attrs:{id:"解説-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-2"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[t("code",[s._v("bootRun")]),s._v("コマンドによりSpring Bootが起動します。すると、Spring Bootの機能により "),t("code",[s._v("@RestController")]),s._v("アノテーションが付いている"),t("code",[s._v("ServerAppApplication.HelloController")]),s._v("がHTTPのインタフェースとして登録されます。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(470),alt:"handler"}})]),s._v(" "),t("p",[s._v("その結果、このSpring Bootが動いている8080番ポート宛のHTTPリクエストと"),t("code",[s._v("ServerAppApplication.HelloController#helloWorld")]),s._v("が紐づけられることになり、"),t("code",[s._v("GET /")]),s._v('へのリクエストのレスポンスとして"hello world"が返ってきました。')]),s._v(" "),t("details",{staticClass:"custom-block details"},[t("summary",[s._v("Spring BootとDIコンテナ")]),s._v(" "),t("p",[s._v("Spring Bootは起動時に起動クラスのパッケージ配下のJavaファイルから特殊なアノテーション("),t("code",[s._v("@Contoroller")]),s._v(" / "),t("code",[s._v("@Component")]),s._v(" / etc...)がついたクラスを探し出します。そしてSpring Bootはそのクラスを適当な方法でインスタンス化し、自身の管理下(=DI コンテナ)に置きます。")]),s._v(" "),t("p",[s._v("(「適当な方法」を指定することもできます > "),t("a",{attrs:{href:"https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Bean Annotation"),t("OutboundLink")],1),s._v(")")]),s._v(" "),t("p",[s._v("DIコンテナに格納されたインスタンスは、後述する"),t("code",[s._v("@Autowired")]),s._v("アノテーションを使って引き出すことができます。")]),s._v(" "),t("p",[s._v("特に、"),t("code",[s._v("@Controller")]),s._v(" "),t("code",[s._v("@RestController")]),s._v("アノテーションが付与されたクラスから生成されたインスタンスはHTTPのインタフェースとして働くことになります。")]),s._v(" "),t("p",[s._v("今回の例では、 "),t("code",[s._v("ServerAppApplication")]),s._v("クラスに"),t("code",[s._v("@SpringBootApplication")]),s._v("アノテーションが付与されているので"),t("code",[s._v("ServerAppApplication")]),s._v("クラスのパッケージ"),t("code",[s._v("com.github.iij.bootcamp.serverapp")]),s._v("配下のクラスから上述の特殊なアノテーションがついているクラスを探索します。")]),s._v(" "),t("p",[t("code",[s._v("HelloController")]),s._v("クラスは"),t("code",[s._v("@RestController")]),s._v("アノテーションが付与されているため、Spring Boot起動時に"),t("code",[s._v("HelloController")]),s._v("がSpring Bootによってインスタンス化されDIコンテナに登録されました。\nその結果、"),t("code",[s._v("GET /")]),s._v("のリクエストをSpring Bootが受け取ると"),t("code",[s._v("HelloController#helloWorld")]),s._v(" が実行されるようになっていたというわけです。")])]),s._v(" "),t("h3",{attrs:{id:"簡単なリクエストを受け取ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単なリクエストを受け取ってみる"}},[s._v("#")]),s._v(" 簡単なリクエストを受け取ってみる")]),s._v(" "),t("p",[s._v("次にクエリパラメータから情報を取得してみましょう。")]),s._v(" "),t("p",[s._v("💻 UserController.javaを作成し、サーバを再起動してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" secretUser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("secretUser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("else")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=bob'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-3"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@GetMapping")]),s._v("アノテーションを持つメソッドを作成し、HTTPのハンドラとして登録した")]),s._v(" "),t("li",[s._v("HTTPのハンドラとして登録されたメソッドの引数に"),t("code",[s._v("@RequestParam")]),s._v("アノテーションを付与することでクエリパラメータを実装した")])]),s._v(" "),t("h4",{attrs:{id:"解説-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-3"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("新しいクラス"),t("code",[s._v("UserController")]),s._v("を作成しました。このクラスにも"),t("code",[s._v("@RestController")]),s._v("アノテーションが付いているためHTTPのインタフェースとして振る舞います。")]),s._v(" "),t("p",[t("code",[s._v("UserController")]),s._v("クラスの持つメソッド"),t("code",[s._v("find")]),s._v("には"),t("code",[s._v("@GetMapping")]),s._v("アノテーションがついているため、"),t("code",[s._v("GET /user")]),s._v("宛のリクエストのハンドラとして登録されることになります。そのため、Spring Bootアプリケーションの"),t("code",[s._v("/user")]),s._v("へGETリクエストを送ることでこの"),t("code",[s._v("find")]),s._v("メソッドがコールされます。")]),s._v(" "),t("p",[s._v("さらに"),t("code",[s._v("find")]),s._v("メソッドの引数"),t("code",[s._v("id")]),s._v("に"),t("code",[s._v("@RequestParam")]),s._v("アノテーションが付与されています。これにより、HTTPリクエストのクエリパラメータの値がこの変数に注入されます。つまり"),t("code",[s._v("GET /user?id=bob")]),s._v("へのリクエストをSpring Bootアプリケーションへ送ることで"),t("code",[s._v("find")]),s._v("メソッドがコールされ引数"),t("code",[s._v("id=bob")]),s._v("が引き渡されます。")]),s._v(" "),t("h3",{attrs:{id:"責任を分離する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#責任を分離する"}},[s._v("#")]),s._v(" 責任を分離する")]),s._v(" "),t("p",[s._v("さて、前章まで基本的なHTTPのインタフェースの作り方と使い方について解説してきました。もう少し実装を深めていきましょう。"),t("br"),s._v("\n現在"),t("code",[s._v("UserController")]),s._v("クラスはHTTPのインタフェースとデータソースの管理の2つの責務を持っています。これは単一責務の原理から外れているためリファクタリングする対象です。")]),s._v(" "),t("p",[s._v("今回はシンプルに"),t("code",[s._v("UserController#find")]),s._v("の処理を抽出して別のクラスに分離、処理そのものを"),t("code",[s._v("UserController")]),s._v("クラスの外から与えてあげるようにしましょう。")]),s._v(" "),t("p",[s._v("💻 UserService.javaを作成、UserController.javaを修正し、サーバを再起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 新しいクラスUserServiceを作成する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserService.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# UserControllerクラスを修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stereotype"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Component")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Component")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// データソースに該当する部分")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v(" userPool "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("asList")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールからid値で検索し、その結果を返却します idと一致するユーザーが見つからない場合nullを返却します\n * TODO 本当にこの実装で問題ないか、考えてみましょう\n * - 同一のidをもつインスタンスがいる場合は?データ構造はこれで良いか?\n * - nullは`User`インスタンスではない、では見つからない場合は何を返すべき?\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Java8から導入されたStreamAPI")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("stream")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Streamを作成")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("filter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// idと一致する`User`インスタンスのみを抽出")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findFirst")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 抽出結果の先頭1つだけを取り出す")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("orElse")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// もし抽出した結果何も残らなかった場合、nullを返却する")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("beans"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("factory"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Autowired")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 修正BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Autowired")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 修正END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=bob'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-4"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@Component")]),s._v("アノテーションをクラスに付与し、Spring Boot起動時に自動的にインスタンス化した")]),s._v(" "),t("li",[t("code",[s._v("@Autowired")]),s._v("アノテーションをフィールドに付与し、そのフィールドにSpring Bootがインスタンス化したインスタンスの中から適切なインスタンスを注入した")])]),s._v(" "),t("h4",{attrs:{id:"解説-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解説-4"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),t("p",[s._v("新しいクラス"),t("code",[s._v("UserService")]),s._v("を作成しました。このクラスには"),t("code",[s._v("@Component")]),s._v("アノテーションが付与されているため、Spring Boot起動時にSpring Bootによって自動的にインスタンス化されSpring Bootの管理下に入ります。このようにSpring Bootに管理されるようになったインスタンスはほかのクラスから"),t("code",[s._v("@Autowired")]),s._v("を利用することで利用されます。")]),s._v(" "),t("p",[s._v("今回の例では"),t("code",[s._v("UserController")]),s._v("クラスが"),t("code",[s._v("userService")]),s._v("フィールドに"),t("code",[s._v("@Autowired")]),s._v("アノテーションを付与しているため自動的に作成された"),t("code",[s._v("UserService")]),s._v("クラスのインスタンスが"),t("code",[s._v("UserController.userService")]),s._v("に代入されることになりました。このように依存関係を分離、外から依存関係を持ち込む構成のことをDI(Dependency Injection)と呼びます。")]),s._v(" "),t("p",[s._v('注意点として、デフォルトの挙動ではSpring Bootが管理するインスタンスは各クラス"1つ"となっています。つまり'),t("code",[s._v("UserController")]),s._v("クラスが引っ張ってきている"),t("code",[s._v("userService")]),s._v("とほかのクラスが引っ張ってこれる"),t("code",[s._v("UserService")]),s._v('インスタンスは完全に一致しています。このようにひとつのインスタンスを使い回す構成のことを"シングルトン"と呼びます。')]),s._v(" "),t("h3",{attrs:{id:"少し複雑なリクエストを受け取ってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#少し複雑なリクエストを受け取ってみる"}},[s._v("#")]),s._v(" 少し複雑なリクエストを受け取ってみる")]),s._v(" "),t("p",[s._v("最後に、POSTリクエストとリクエストボディを指定して"),t("code",[s._v("User")]),s._v("インスタンスを登録してみましょう。"),t("br"),s._v(" "),t("code",[s._v("User")]),s._v("インスタンスには"),t("code",[s._v("id")]),s._v("と"),t("code",[s._v("name")]),s._v("の値を指定する必要があるので、これらを与えられるエンドポイントを用意します。")]),s._v(" "),t("p",[s._v("💻 UserService.javaとUserController.javaを修正し、サーバを再起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下記の通りに修正する")]),s._v("\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserService.java\n❯ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")]),s._v(" src/main/java/com/github/iij/bootcamp/serverapp/UserController.java\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Spring Bootサーバーを再起動する")]),s._v("\n❯ ./gradlew bootRun\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("java"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("util"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stereotype"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Component")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Component")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// データソースに該当する部分")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("List")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v(" userPool "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ArrayList")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Arrays")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("asList")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ボブ"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bob"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールからid値で検索し、その結果を返却します idと一致するユーザーが見つからない場合nullを返却します\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("stream")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("filter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("equals")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("u"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findFirst")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("orElse")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/**\n * ユーザープールにUserを追加します\n */")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userPool"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("add")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" user"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br")])]),t("div",{staticClass:"language-java line-numbers-mode"},[t("div",{staticClass:"highlight-lines"},[t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("br"),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("div",{staticClass:"highlighted"},[s._v(" ")]),t("br"),t("br"),t("br")]),t("pre",{pre:!0,attrs:{class:"language-java"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("package")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("com"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("github"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("iij"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("serverapp")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("beans"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("factory"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Autowired")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("GetMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RestController")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestParam")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("PostMapping")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token import"}},[t("span",{pre:!0,attrs:{class:"token namespace"}},[s._v("org"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("springframework"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("web"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("bind"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("annotation"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")])]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("RequestBody")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RestController")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserController")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@Autowired")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserService")]),s._v(" userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@GetMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestParam")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("findById")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記BEGIN")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// POST /user 宛のリクエストボディスキーマ")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserCreateRequest")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("void")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" id"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@PostMapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/user"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("create")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token annotation punctuation"}},[s._v("@RequestBody")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("UserCreateRequest")]),s._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),s._v(" newUser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("User")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getName")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("getId")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("this")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("userService"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("newUser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 追記END")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br"),t("span",{staticClass:"line-number"},[s._v("38")]),t("br"),t("span",{staticClass:"line-number"},[s._v("39")]),t("br"),t("span",{staticClass:"line-number"},[s._v("40")]),t("br"),t("span",{staticClass:"line-number"},[s._v("41")]),t("br"),t("span",{staticClass:"line-number"},[s._v("42")]),t("br"),t("span",{staticClass:"line-number"},[s._v("43")]),t("br"),t("span",{staticClass:"line-number"},[s._v("44")]),t("br"),t("span",{staticClass:"line-number"},[s._v("45")]),t("br"),t("span",{staticClass:"line-number"},[s._v("46")]),t("br"),t("span",{staticClass:"line-number"},[s._v("47")]),t("br"),t("span",{staticClass:"line-number"},[s._v("48")]),t("br"),t("span",{staticClass:"line-number"},[s._v("49")]),t("br"),t("span",{staticClass:"line-number"},[s._v("50")]),t("br"),t("span",{staticClass:"line-number"},[s._v("51")]),t("br"),t("span",{staticClass:"line-number"},[s._v("52")]),t("br"),t("span",{staticClass:"line-number"},[s._v("53")]),t("br"),t("span",{staticClass:"line-number"},[s._v("54")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 動作確認")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080/user -X POST -H "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type: application/json'")]),s._v(" -d "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('\'{"name": "アリス", "id": "alice"}\'')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'localhost:8080/user?id=alice'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"アリス"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"alice"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("h4",{attrs:{id:"チェックポイント-5"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#チェックポイント-5"}},[s._v("#")]),s._v(" チェックポイント")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("@PostMapping")]),s._v("アノテーションをメソッドに付与し、POSTリクエストのHTTPハンドラとして登録した")]),s._v(" "),t("li",[s._v("JavaのPOJOを用いてPOSTリクエストのリクエストボディのスキーマを表現した")])]),s._v(" "),t("h2",{attrs:{id:"まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),t("p",[s._v("以上でSpring Bootのハンズオンは終了です。")]),s._v(" "),t("p",[s._v("本講義ではJavaの基本的な知識や書き方、Spring Bootの使い方など、基本的な機能や文法に触れてもらいました。しかしSpring Bootには多様な機能がまだまだ存在しており、データベースとの接続や非同期処理などさまざまなプロダクション環境で活躍できるポテンシャルを持っているフレームワークです。")]),s._v(" "),t("p",[s._v("本講義が、受講者のみなさまの今後の技術選定の手助けになれれば幸いです。")]),s._v(" "),t("h3",{attrs:{id:"追加の資料"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#追加の資料"}},[s._v("#")]),s._v(" 追加の資料")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://spring.pleiades.io/spring-boot/docs/current/reference/html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Spring Bootリファレンスドキュメント"),t("OutboundLink")],1),s._v(" "),t("ul",[t("li",[s._v("多くのSpring Boot開発者がお世話になる公式ドキュメントです。アプリケーションの開発からデプロイ方法まで、幅広く情報が提供されています。")])])]),s._v(" "),t("li",[t("a",{attrs:{href:"https://spring.pleiades.io/guides",target:"_blank",rel:"noopener noreferrer"}},[s._v("Spring Boot Guides"),t("OutboundLink")],1),s._v(" "),t("ul",[t("li",[s._v("Spring Bootの各種機能を試してみるチュートリアルが公開されています。Pub/SubやMongoDB、Dockerとの連携などSpring Bootの拡張が多種公開されています。興味のある項目に触ってみてください。")])])])]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/28.2a99b424.js b/assets/js/28.640b9315.js similarity index 99% rename from assets/js/28.2a99b424.js rename to assets/js/28.640b9315.js index 02a5c12f..4d3848ca 100644 --- a/assets/js/28.2a99b424.js +++ b/assets/js/28.640b9315.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{278:function(a,t,s){a.exports=s.p+"assets/img/network.drawio.fc5d6a32.png"},516:function(a,t,s){"use strict";s.r(t);var _=s(10),e=Object(_.a)({},(function(){var a=this,t=a._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[t("h1",{attrs:{id:"ansible-による-it自動化"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ansible-による-it自動化"}},[a._v("#")]),a._v(" Ansible による IT自動化")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#ansible-%E3%81%AB%E3%82%88%E3%82%8B-it%E8%87%AA%E5%8B%95%E5%8C%96"}},[a._v("Ansible による IT自動化")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB"}},[a._v("はじめに")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E6%9C%AC%E8%AC%9B%E7%BE%A9%E3%81%A7%E6%89%B1%E3%81%86%E3%81%93%E3%81%A8"}},[a._v("本講義で扱うこと")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E6%9C%AC%E8%AC%9B%E7%BE%A9%E3%81%A7%E6%89%B1%E3%82%8F%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8"}},[a._v("本講義で扱わないこと")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E6%BC%94%E7%BF%92%E7%92%B0%E5%A2%83"}},[a._v("演習環境")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%A7%8B%E6%88%90"}},[a._v("システム構成")])])])]),a._v(" "),t("li",[t("a",{attrs:{href:"#0-%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99"}},[a._v("0. 事前準備")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#1-ansible-%E6%A6%82%E8%A6%81%E3%81%A8%E5%B0%8E%E5%85%A5"}},[a._v("1. Ansible 概要と導入")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#2-%E3%82%A4%E3%83%B3%E3%83%99%E3%83%B3%E3%83%88%E3%83%AA%E3%81%AE%E4%BD%9C%E6%88%90"}},[a._v("2. インベントリの作成")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#3-ansible-%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%AE%A1%E7%90%86"}},[a._v("3. Ansible 設定ファイルの管理")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#4-ansible-playbook-%E3%81%AE%E4%BD%9C%E6%88%90"}},[a._v("4. Ansible playbook の作成")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#5-ansible-%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%90%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97"}},[a._v("5. Ansible によるサーバセットアップ")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#6-%E5%A4%89%E6%95%B0%E3%82%84%E3%83%AB%E3%83%BC%E3%83%97%E5%87%A6%E7%90%86%E3%81%AE%E5%AE%9F%E8%A1%8C"}},[a._v("6. 変数やループ処理の実行")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#7-%E6%AD%A3%E3%81%97%E3%81%84-playbook-%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AB"}},[a._v("7. 正しい Playbook を書くために")])])])])]),a._v(" "),t("h2",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[a._v("#")]),a._v(" はじめに")]),a._v(" "),t("p",[a._v("この講義ではハンズオン形式で Ansible について学びます。")]),a._v(" "),t("p",[a._v("講義を受けるにあたり、事前に環境準備が必要です。\n講義当日までに"),t("strong",[a._v("0.事前準備")]),a._v("を終え、Dockerが実行可能な環境を整えてください。\nハンズオン用の教材は"),t("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[a._v("こちら"),t("OutboundLink")],1),a._v("になります。")]),a._v(" "),t("h3",{attrs:{id:"本講義で扱うこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱うこと"}},[a._v("#")]),a._v(" 本講義で扱うこと")]),a._v(" "),t("ul",[t("li",[a._v("Ansible概論")]),a._v(" "),t("li",[a._v("Ansibleの導入")]),a._v(" "),t("li",[a._v("Ansibleの実行")])]),a._v(" "),t("h3",{attrs:{id:"本講義で扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱わないこと"}},[a._v("#")]),a._v(" 本講義で扱わないこと")]),a._v(" "),t("ul",[t("li",[t("code",[a._v("ansible-navigator")]),a._v("の活用")]),a._v(" "),t("li",[a._v("Ansible CICD")]),a._v(" "),t("li",[a._v("Ansible モジュール/プラグインの開発")]),a._v(" "),t("li",[a._v("Ansible Automation Platform全般")])]),a._v(" "),t("h3",{attrs:{id:"演習環境"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習環境"}},[a._v("#")]),a._v(" 演習環境")]),a._v(" "),t("p",[a._v("本講義では"),t("code",[a._v("vscode")]),a._v(" を使って演習を行います。\nvim等による開発でも問題ありませんが、講義においては講師の環境を"),t("code",[a._v("vscode")]),a._v("にて説明を行うため、\nvscode以外で開発を行う場合は適宜自身で読み替えてください。")]),a._v(" "),t("h3",{attrs:{id:"システム構成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#システム構成"}},[a._v("#")]),a._v(" システム構成")]),a._v(" "),t("p",[a._v("この講義で使用するコンテナのネットワーク図です。\nコンテナを VM(Virtual Machine)に見立て、ハンズオンを実施します。")]),a._v(" "),t("p",[t("img",{attrs:{src:s(278),alt:"ネットワーク図"}})]),a._v(" "),t("p",[a._v("この講義では図中の "),t("code",[a._v("console")]),a._v(" コンテナから各ホストを管理します。")]),a._v(" "),t("h2",{attrs:{id:"_0-事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-事前準備"}},[a._v("#")]),a._v(" 0. 事前準備")]),a._v(" "),t("p",[t("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[a._v("ハンズオン用の教材"),t("OutboundLink")],1),a._v("を参照し\nREADMEに従ってansible演習環境のセットアップを行ってください。")]),a._v(" "),t("h2",{attrs:{id:"_1-ansible-概要と導入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-ansible-概要と導入"}},[a._v("#")]),a._v(" 1. Ansible 概要と導入")]),a._v(" "),t("p",[a._v("Ansibleの概要とインストール方法について学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/INTRODUCTION.html"}},[a._v("Ansible 概要")])],1)]),a._v(" "),t("h2",{attrs:{id:"_2-インベントリの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-インベントリの作成"}},[a._v("#")]),a._v(" 2. インベントリの作成")]),a._v(" "),t("p",[a._v("Ansibleインベントリの概念とインベントリファイルの作成方法を学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_INVENTORY.html"}},[a._v("インベントリの作成")])],1)]),a._v(" "),t("h2",{attrs:{id:"_3-ansible-設定ファイルの管理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-ansible-設定ファイルの管理"}},[a._v("#")]),a._v(" 3. Ansible 設定ファイルの管理")]),a._v(" "),t("p",[a._v("Ansibleの基本動作仕様とその変更方法について学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/MANAGE_SETTINGS.html"}},[a._v("Ansible 設定ファイルの管理")])],1)]),a._v(" "),t("h2",{attrs:{id:"_4-ansible-playbook-の作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-ansible-playbook-の作成"}},[a._v("#")]),a._v(" 4. Ansible playbook の作成")]),a._v(" "),t("p",[a._v("Ansible playbookの概念と作成方法を学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_PLAYBOOK.html"}},[a._v("Ansible playbookの作成")])],1)]),a._v(" "),t("h2",{attrs:{id:"_5-ansible-によるサーバセットアップ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-ansible-によるサーバセットアップ"}},[a._v("#")]),a._v(" 5. Ansible によるサーバセットアップ")]),a._v(" "),t("p",[a._v("Ansible playbookを通じて実際にサーバに設定を加えていきます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_SERVER.html"}},[a._v("Ansible によるサーバセットアップ")])],1)]),a._v(" "),t("h2",{attrs:{id:"_6-変数やループ処理の実行"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-変数やループ処理の実行"}},[a._v("#")]),a._v(" 6. 変数やループ処理の実行")]),a._v(" "),t("p",[a._v("Ansible playbookを通じてWebサーバを作ってみましょう")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/USE_VARIABLE.html"}},[a._v("Ansible によるWebサーバセットアップ")])],1)]),a._v(" "),t("h2",{attrs:{id:"_7-正しい-playbook-を書くために"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-正しい-playbook-を書くために"}},[a._v("#")]),a._v(" 7. 正しい Playbook を書くために")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/ANSIBLE_CODE_STYLE.html"}},[a._v("正しい Playbook を書くために")])],1)]),a._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{278:function(a,t,s){a.exports=s.p+"assets/img/network.drawio.fc5d6a32.png"},515:function(a,t,s){"use strict";s.r(t);var _=s(10),e=Object(_.a)({},(function(){var a=this,t=a._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[t("h1",{attrs:{id:"ansible-による-it自動化"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ansible-による-it自動化"}},[a._v("#")]),a._v(" Ansible による IT自動化")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#ansible-%E3%81%AB%E3%82%88%E3%82%8B-it%E8%87%AA%E5%8B%95%E5%8C%96"}},[a._v("Ansible による IT自動化")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB"}},[a._v("はじめに")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E6%9C%AC%E8%AC%9B%E7%BE%A9%E3%81%A7%E6%89%B1%E3%81%86%E3%81%93%E3%81%A8"}},[a._v("本講義で扱うこと")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E6%9C%AC%E8%AC%9B%E7%BE%A9%E3%81%A7%E6%89%B1%E3%82%8F%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8"}},[a._v("本講義で扱わないこと")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E6%BC%94%E7%BF%92%E7%92%B0%E5%A2%83"}},[a._v("演習環境")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%A7%8B%E6%88%90"}},[a._v("システム構成")])])])]),a._v(" "),t("li",[t("a",{attrs:{href:"#0-%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99"}},[a._v("0. 事前準備")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#1-ansible-%E6%A6%82%E8%A6%81%E3%81%A8%E5%B0%8E%E5%85%A5"}},[a._v("1. Ansible 概要と導入")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#2-%E3%82%A4%E3%83%B3%E3%83%99%E3%83%B3%E3%83%88%E3%83%AA%E3%81%AE%E4%BD%9C%E6%88%90"}},[a._v("2. インベントリの作成")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#3-ansible-%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%AE%A1%E7%90%86"}},[a._v("3. Ansible 設定ファイルの管理")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#4-ansible-playbook-%E3%81%AE%E4%BD%9C%E6%88%90"}},[a._v("4. Ansible playbook の作成")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#5-ansible-%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%90%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97"}},[a._v("5. Ansible によるサーバセットアップ")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#6-%E5%A4%89%E6%95%B0%E3%82%84%E3%83%AB%E3%83%BC%E3%83%97%E5%87%A6%E7%90%86%E3%81%AE%E5%AE%9F%E8%A1%8C"}},[a._v("6. 変数やループ処理の実行")])]),a._v(" "),t("li",[t("a",{attrs:{href:"#7-%E6%AD%A3%E3%81%97%E3%81%84-playbook-%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AB"}},[a._v("7. 正しい Playbook を書くために")])])])])]),a._v(" "),t("h2",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[a._v("#")]),a._v(" はじめに")]),a._v(" "),t("p",[a._v("この講義ではハンズオン形式で Ansible について学びます。")]),a._v(" "),t("p",[a._v("講義を受けるにあたり、事前に環境準備が必要です。\n講義当日までに"),t("strong",[a._v("0.事前準備")]),a._v("を終え、Dockerが実行可能な環境を整えてください。\nハンズオン用の教材は"),t("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[a._v("こちら"),t("OutboundLink")],1),a._v("になります。")]),a._v(" "),t("h3",{attrs:{id:"本講義で扱うこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱うこと"}},[a._v("#")]),a._v(" 本講義で扱うこと")]),a._v(" "),t("ul",[t("li",[a._v("Ansible概論")]),a._v(" "),t("li",[a._v("Ansibleの導入")]),a._v(" "),t("li",[a._v("Ansibleの実行")])]),a._v(" "),t("h3",{attrs:{id:"本講義で扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱わないこと"}},[a._v("#")]),a._v(" 本講義で扱わないこと")]),a._v(" "),t("ul",[t("li",[t("code",[a._v("ansible-navigator")]),a._v("の活用")]),a._v(" "),t("li",[a._v("Ansible CICD")]),a._v(" "),t("li",[a._v("Ansible モジュール/プラグインの開発")]),a._v(" "),t("li",[a._v("Ansible Automation Platform全般")])]),a._v(" "),t("h3",{attrs:{id:"演習環境"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習環境"}},[a._v("#")]),a._v(" 演習環境")]),a._v(" "),t("p",[a._v("本講義では"),t("code",[a._v("vscode")]),a._v(" を使って演習を行います。\nvim等による開発でも問題ありませんが、講義においては講師の環境を"),t("code",[a._v("vscode")]),a._v("にて説明を行うため、\nvscode以外で開発を行う場合は適宜自身で読み替えてください。")]),a._v(" "),t("h3",{attrs:{id:"システム構成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#システム構成"}},[a._v("#")]),a._v(" システム構成")]),a._v(" "),t("p",[a._v("この講義で使用するコンテナのネットワーク図です。\nコンテナを VM(Virtual Machine)に見立て、ハンズオンを実施します。")]),a._v(" "),t("p",[t("img",{attrs:{src:s(278),alt:"ネットワーク図"}})]),a._v(" "),t("p",[a._v("この講義では図中の "),t("code",[a._v("console")]),a._v(" コンテナから各ホストを管理します。")]),a._v(" "),t("h2",{attrs:{id:"_0-事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-事前準備"}},[a._v("#")]),a._v(" 0. 事前準備")]),a._v(" "),t("p",[t("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[a._v("ハンズオン用の教材"),t("OutboundLink")],1),a._v("を参照し\nREADMEに従ってansible演習環境のセットアップを行ってください。")]),a._v(" "),t("h2",{attrs:{id:"_1-ansible-概要と導入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-ansible-概要と導入"}},[a._v("#")]),a._v(" 1. Ansible 概要と導入")]),a._v(" "),t("p",[a._v("Ansibleの概要とインストール方法について学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/INTRODUCTION.html"}},[a._v("Ansible 概要")])],1)]),a._v(" "),t("h2",{attrs:{id:"_2-インベントリの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-インベントリの作成"}},[a._v("#")]),a._v(" 2. インベントリの作成")]),a._v(" "),t("p",[a._v("Ansibleインベントリの概念とインベントリファイルの作成方法を学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_INVENTORY.html"}},[a._v("インベントリの作成")])],1)]),a._v(" "),t("h2",{attrs:{id:"_3-ansible-設定ファイルの管理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-ansible-設定ファイルの管理"}},[a._v("#")]),a._v(" 3. Ansible 設定ファイルの管理")]),a._v(" "),t("p",[a._v("Ansibleの基本動作仕様とその変更方法について学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/MANAGE_SETTINGS.html"}},[a._v("Ansible 設定ファイルの管理")])],1)]),a._v(" "),t("h2",{attrs:{id:"_4-ansible-playbook-の作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-ansible-playbook-の作成"}},[a._v("#")]),a._v(" 4. Ansible playbook の作成")]),a._v(" "),t("p",[a._v("Ansible playbookの概念と作成方法を学びます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_PLAYBOOK.html"}},[a._v("Ansible playbookの作成")])],1)]),a._v(" "),t("h2",{attrs:{id:"_5-ansible-によるサーバセットアップ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-ansible-によるサーバセットアップ"}},[a._v("#")]),a._v(" 5. Ansible によるサーバセットアップ")]),a._v(" "),t("p",[a._v("Ansible playbookを通じて実際にサーバに設定を加えていきます")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/CREATE_SERVER.html"}},[a._v("Ansible によるサーバセットアップ")])],1)]),a._v(" "),t("h2",{attrs:{id:"_6-変数やループ処理の実行"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-変数やループ処理の実行"}},[a._v("#")]),a._v(" 6. 変数やループ処理の実行")]),a._v(" "),t("p",[a._v("Ansible playbookを通じてWebサーバを作ってみましょう")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/USE_VARIABLE.html"}},[a._v("Ansible によるWebサーバセットアップ")])],1)]),a._v(" "),t("h2",{attrs:{id:"_7-正しい-playbook-を書くために"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-正しい-playbook-を書くために"}},[a._v("#")]),a._v(" 7. 正しい Playbook を書くために")]),a._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/cicd_infra/ansible/ANSIBLE_CODE_STYLE.html"}},[a._v("正しい Playbook を書くために")])],1)]),a._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/29.abc83417.js b/assets/js/29.d895ab7b.js similarity index 99% rename from assets/js/29.abc83417.js rename to assets/js/29.d895ab7b.js index 88d4ecb0..e78da236 100644 --- a/assets/js/29.abc83417.js +++ b/assets/js/29.d895ab7b.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{237:function(s,e,a){s.exports=a.p+"assets/img/getting-started.a1278e29.png"},532:function(s,e,a){"use strict";a.r(e);var t=a(10),r=Object(t.a)({},(function(){var s=this,e=s._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[e("header-table"),s._v(" "),e("h2",{attrs:{id:"おさらい"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#おさらい"}},[s._v("#")]),s._v(" おさらい")]),s._v(" "),e("p",[s._v("それでは実際にDockerを使って仮想環境プラットフォームを作ってみましょう。\n事前準備の項を済ませているならばDocker環境は構築されているはずです。\n下記コマンドを入力し、コマンドが実行できるか確認してください。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" version\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("p",[s._v("上記コマンドが実行できない方は事前にDocker(及びコマンド)のインストールが終わっているか否か確認し、未完了の人は、Docker のインストールを行ってください。")]),s._v(" "),e("h2",{attrs:{id:"docker-コンテナを起動する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#docker-コンテナを起動する"}},[s._v("#")]),s._v(" Docker コンテナを起動する")]),s._v(" "),e("p",[s._v("Dockerコンテナを使って仮想環境プラットフォームを構築するには、大きく分けて以下のステップを踏む必要があります。")]),s._v(" "),e("ul",[e("li",[s._v("Dockerイメージのビルド")]),s._v(" "),e("li",[s._v("Dockerコンテナの作成")]),s._v(" "),e("li",[s._v("Dockerコンテナの起動")])]),s._v(" "),e("p",[s._v("従って、Dockerコンテナを起動する為に最初にすべきことは"),e("strong",[s._v("Dockerコンテナイメージ")]),s._v("を取得する事になります。")]),s._v(" "),e("p",[s._v("Dockerコンテナイメージは、自分で作成(ビルド)して取得する方法と、作成済みのDockerコンテナイメージを取得する方法の2種類があります。\nまずは、作成済みのイメージを利用してコンテナを起動することを試してみましょう。")]),s._v(" "),e("h3",{attrs:{id:"演習1-dockerコンテナを起動する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習1-dockerコンテナを起動する"}},[s._v("#")]),s._v(" 演習1 Dockerコンテナを起動する")]),s._v(" "),e("ul",[e("li",[e("strong",[s._v("docker run")]),s._v("コマンドを使用して"),e("strong",[s._v("getting-started")]),s._v("コンテナを起動する"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])])]),s._v(" "),e("details",[e("summary",[s._v("実行中のログ")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("Unable to find image 'docker/getting-started:latest' locally\nlatest: Pulling from docker/getting-started\nc158987b0551: Pull complete\n1e35f6679fab: Pull complete\ncb9626c74200: Pull complete\nb6334b6ace34: Pull complete\nf1d1c9928c82: Pull complete\n9b6f639ec6ea: Pull complete\nee68d3549ec8: Pull complete\n33e0cbbb4673: Pull complete\n4f7e34c2de10: Pull complete\nDigest: sha256:d79336f4812b6547a53e735480dde67f8f8f7071b414fbd9297609ffb989abc1\nStatus: Downloaded newer image for docker/getting-started:latest\n89e2c9780f5caf3b5250013e002e8aaf9f8ea74c2e940eca49b890dfc019ab5e\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br"),e("span",{staticClass:"line-number"},[s._v("3")]),e("br"),e("span",{staticClass:"line-number"},[s._v("4")]),e("br"),e("span",{staticClass:"line-number"},[s._v("5")]),e("br"),e("span",{staticClass:"line-number"},[s._v("6")]),e("br"),e("span",{staticClass:"line-number"},[s._v("7")]),e("br"),e("span",{staticClass:"line-number"},[s._v("8")]),e("br"),e("span",{staticClass:"line-number"},[s._v("9")]),e("br"),e("span",{staticClass:"line-number"},[s._v("10")]),e("br"),e("span",{staticClass:"line-number"},[s._v("11")]),e("br"),e("span",{staticClass:"line-number"},[s._v("12")]),e("br"),e("span",{staticClass:"line-number"},[s._v("13")]),e("br"),e("span",{staticClass:"line-number"},[s._v("14")]),e("br")])])]),s._v(" "),e("ul",[e("li",[s._v("起動の確認\n"),e("ul",[e("li",[s._v("ブラウザを開き、以下のURLを入力します"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("http://localhost:80\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("以下のような画面が表示されれば成功です\n"),e("img",{attrs:{src:a(237),alt:"getting-started"}})])])]),s._v(" "),e("li",[s._v("コンテナの終了\n"),e("strong",[s._v("Ctrl + c")]),s._v(" を押す")]),s._v(" "),e("li",[s._v("ターミナルが戻ってくる")])]),s._v(" "),e("h3",{attrs:{id:"発展課題1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#発展課題1"}},[s._v("#")]),s._v(" 発展課題1")]),s._v(" "),e("p",[s._v("先ほどの作業ではフォアグラウンドで実行している為、ターミナルが占有されてしまいます。\nまた、このような起動では例えばssh等で接続している場合はセッション切断と共にコンテナが停止してしまう為、発展課題ではこれを永続化する事をやってみましょう。")]),s._v(" "),e("p",[s._v("デーモン起動をすると、ターミナルは返ってきてしまうため起動確認は "),e("code",[s._v("docker ps")]),s._v("を使って確認します。")]),s._v(" "),e("ul",[e("li",[s._v("コンテナのデーモン起動"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("コンテナの起動確認"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n38ebcf110f45 docker/getting-started "),e("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/docker-entrypoint.…"')]),s._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" seconds ago Up "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" seconds "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("0.0")]),s._v(".0.0:80-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp, :::80-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp fervent_shaw\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("ul",[e("li",[s._v("ここで"),e("strong",[s._v("NAMES")]),s._v("に表示されている値を記憶、若しくは記録しておいてください")])])]),s._v(" "),e("li",[s._v("起動の確認\n"),e("ul",[e("li",[s._v("ブラウザを開き、以下のURLを入力します"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("http://localhost:80\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("以下のような画面が表示されれば成功です\n"),e("img",{attrs:{src:a(237),alt:"getting-started"}})])])]),s._v(" "),e("li",[s._v("コンテナの終了\n"),e("ul",[e("li",[e("strong",[s._v("docker stop")]),s._v("コマンドを用いてdockerコンテナを停止します")])]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("docker stop \n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("コンテナが停止したことの確認\n"),e("ul",[e("li",[s._v("ブラウザにて "),e("strong",[e("a",{attrs:{href:"http://localhost",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost"),e("OutboundLink")],1)]),s._v("にアクセスし、アクセスできないことを確認する")]),s._v(" "),e("li",[e("strong",[s._v("docker ps")]),s._v("コマンドを用いて、何も表示されないことを確認する"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("docker ps\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])])])])]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{237:function(s,e,a){s.exports=a.p+"assets/img/getting-started.a1278e29.png"},530:function(s,e,a){"use strict";a.r(e);var t=a(10),r=Object(t.a)({},(function(){var s=this,e=s._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[e("header-table"),s._v(" "),e("h2",{attrs:{id:"おさらい"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#おさらい"}},[s._v("#")]),s._v(" おさらい")]),s._v(" "),e("p",[s._v("それでは実際にDockerを使って仮想環境プラットフォームを作ってみましょう。\n事前準備の項を済ませているならばDocker環境は構築されているはずです。\n下記コマンドを入力し、コマンドが実行できるか確認してください。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" version\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("p",[s._v("上記コマンドが実行できない方は事前にDocker(及びコマンド)のインストールが終わっているか否か確認し、未完了の人は、Docker のインストールを行ってください。")]),s._v(" "),e("h2",{attrs:{id:"docker-コンテナを起動する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#docker-コンテナを起動する"}},[s._v("#")]),s._v(" Docker コンテナを起動する")]),s._v(" "),e("p",[s._v("Dockerコンテナを使って仮想環境プラットフォームを構築するには、大きく分けて以下のステップを踏む必要があります。")]),s._v(" "),e("ul",[e("li",[s._v("Dockerイメージのビルド")]),s._v(" "),e("li",[s._v("Dockerコンテナの作成")]),s._v(" "),e("li",[s._v("Dockerコンテナの起動")])]),s._v(" "),e("p",[s._v("従って、Dockerコンテナを起動する為に最初にすべきことは"),e("strong",[s._v("Dockerコンテナイメージ")]),s._v("を取得する事になります。")]),s._v(" "),e("p",[s._v("Dockerコンテナイメージは、自分で作成(ビルド)して取得する方法と、作成済みのDockerコンテナイメージを取得する方法の2種類があります。\nまずは、作成済みのイメージを利用してコンテナを起動することを試してみましょう。")]),s._v(" "),e("h3",{attrs:{id:"演習1-dockerコンテナを起動する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習1-dockerコンテナを起動する"}},[s._v("#")]),s._v(" 演習1 Dockerコンテナを起動する")]),s._v(" "),e("ul",[e("li",[e("strong",[s._v("docker run")]),s._v("コマンドを使用して"),e("strong",[s._v("getting-started")]),s._v("コンテナを起動する"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])])]),s._v(" "),e("details",[e("summary",[s._v("実行中のログ")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("Unable to find image 'docker/getting-started:latest' locally\nlatest: Pulling from docker/getting-started\nc158987b0551: Pull complete\n1e35f6679fab: Pull complete\ncb9626c74200: Pull complete\nb6334b6ace34: Pull complete\nf1d1c9928c82: Pull complete\n9b6f639ec6ea: Pull complete\nee68d3549ec8: Pull complete\n33e0cbbb4673: Pull complete\n4f7e34c2de10: Pull complete\nDigest: sha256:d79336f4812b6547a53e735480dde67f8f8f7071b414fbd9297609ffb989abc1\nStatus: Downloaded newer image for docker/getting-started:latest\n89e2c9780f5caf3b5250013e002e8aaf9f8ea74c2e940eca49b890dfc019ab5e\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br"),e("span",{staticClass:"line-number"},[s._v("3")]),e("br"),e("span",{staticClass:"line-number"},[s._v("4")]),e("br"),e("span",{staticClass:"line-number"},[s._v("5")]),e("br"),e("span",{staticClass:"line-number"},[s._v("6")]),e("br"),e("span",{staticClass:"line-number"},[s._v("7")]),e("br"),e("span",{staticClass:"line-number"},[s._v("8")]),e("br"),e("span",{staticClass:"line-number"},[s._v("9")]),e("br"),e("span",{staticClass:"line-number"},[s._v("10")]),e("br"),e("span",{staticClass:"line-number"},[s._v("11")]),e("br"),e("span",{staticClass:"line-number"},[s._v("12")]),e("br"),e("span",{staticClass:"line-number"},[s._v("13")]),e("br"),e("span",{staticClass:"line-number"},[s._v("14")]),e("br")])])]),s._v(" "),e("ul",[e("li",[s._v("起動の確認\n"),e("ul",[e("li",[s._v("ブラウザを開き、以下のURLを入力します"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("http://localhost:80\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("以下のような画面が表示されれば成功です\n"),e("img",{attrs:{src:a(237),alt:"getting-started"}})])])]),s._v(" "),e("li",[s._v("コンテナの終了\n"),e("strong",[s._v("Ctrl + c")]),s._v(" を押す")]),s._v(" "),e("li",[s._v("ターミナルが戻ってくる")])]),s._v(" "),e("h3",{attrs:{id:"発展課題1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#発展課題1"}},[s._v("#")]),s._v(" 発展課題1")]),s._v(" "),e("p",[s._v("先ほどの作業ではフォアグラウンドで実行している為、ターミナルが占有されてしまいます。\nまた、このような起動では例えばssh等で接続している場合はセッション切断と共にコンテナが停止してしまう為、発展課題ではこれを永続化する事をやってみましょう。")]),s._v(" "),e("p",[s._v("デーモン起動をすると、ターミナルは返ってきてしまうため起動確認は "),e("code",[s._v("docker ps")]),s._v("を使って確認します。")]),s._v(" "),e("ul",[e("li",[s._v("コンテナのデーモン起動"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("コンテナの起動確認"),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n38ebcf110f45 docker/getting-started "),e("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/docker-entrypoint.…"')]),s._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" seconds ago Up "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" seconds "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("0.0")]),s._v(".0.0:80-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp, :::80-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp fervent_shaw\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("ul",[e("li",[s._v("ここで"),e("strong",[s._v("NAMES")]),s._v("に表示されている値を記憶、若しくは記録しておいてください")])])]),s._v(" "),e("li",[s._v("起動の確認\n"),e("ul",[e("li",[s._v("ブラウザを開き、以下のURLを入力します"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("http://localhost:80\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("以下のような画面が表示されれば成功です\n"),e("img",{attrs:{src:a(237),alt:"getting-started"}})])])]),s._v(" "),e("li",[s._v("コンテナの終了\n"),e("ul",[e("li",[e("strong",[s._v("docker stop")]),s._v("コマンドを用いてdockerコンテナを停止します")])]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("docker stop \n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])])]),s._v(" "),e("li",[s._v("コンテナが停止したことの確認\n"),e("ul",[e("li",[s._v("ブラウザにて "),e("strong",[e("a",{attrs:{href:"http://localhost",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost"),e("OutboundLink")],1)]),s._v("にアクセスし、アクセスできないことを確認する")]),s._v(" "),e("li",[e("strong",[s._v("docker ps")]),s._v("コマンドを用いて、何も表示されないことを確認する"),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("docker ps\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])])])])]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/32.7a60ca4d.js b/assets/js/32.b5197f93.js similarity index 99% rename from assets/js/32.7a60ca4d.js rename to assets/js/32.b5197f93.js index 86eb0ec3..1006d3d9 100644 --- a/assets/js/32.7a60ca4d.js +++ b/assets/js/32.b5197f93.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{469:function(s,t,a){s.exports=a.p+"assets/img/event-loop.08b77931.png"},552:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"下準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#下準備"}},[s._v("#")]),s._v(" 下準備")]),s._v(" "),t("h3",{attrs:{id:"dockerコンテナの立ち上げ方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの立ち上げ方"}},[s._v("#")]),s._v(" dockerコンテナの立ち上げ方")]),s._v(" "),t("p",[s._v("以下のコマンドでdockerコンテナを立ち上げてログインしてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name bootcamp-node --rm -it regunorf/bootcamp-node "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("同じdockerコンテナに複数のターミナルを接続するには以下のようにします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it bootcamp-node "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ハンズオンでは同じコンテナに複数ターミナルでログインしていることを前提に記述している部分があるので、上記のように複数ターミナルを開いておいてください。")]),s._v(" "),t("h2",{attrs:{id:"node-js-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#node-js-とは"}},[s._v("#")]),s._v(" Node.js とは")]),s._v(" "),t("p",[s._v("Node.js はサーバサイドで動作するJavaScriptのエンジンです。従来ブラウザ上でWebページを動的に動かすために使われていたJavaScriptを、ブラウザ上だけでなくサーバ上でも動かすためNode.jsなどが生まれました。")]),s._v(" "),t("p",[s._v("ブラウザ以外で動作するJavaScriptの実装としてはほかにもRhinoなどがあります。\n最近ではTypeScriptがそのまま動作する "),t("a",{attrs:{href:"https://github.com/denoland/deno",target:"_blank",rel:"noopener noreferrer"}},[s._v("Deno"),t("OutboundLink")],1),s._v(" も登場しています。")]),s._v(" "),t("p",[s._v("Node.jsの特徴としては、何よりもまず実装全体がイベント駆動で動作するという点です。これを説明するにはまずC10K問題から話をする必要があります。少し長くなりますが、Node.jsの立ち位置を知るためには必要なので簡単に解説します。")]),s._v(" "),t("h3",{attrs:{id:"c10k-問題"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#c10k-問題"}},[s._v("#")]),s._v(" C10K 問題")]),s._v(" "),t("p",[s._v("Webサーバーは当然の事ながらたくさんのユーザーからのアクセスに答えて、HTMLを返したりサーバーサイドのプログラム(C言語やJavaやRubyなどで書かれた)を動かしてレスポンスを返す必要があります。その際に1つ1つ順番に処理していたのではキリがないため、たくさんのアクセスを並列に処理する必要があります。")]),s._v(" "),t("p",[s._v("プログラムを並列に動かすやり方はいくつかありますが、たとえばApacheではスレッド・プロセスベースの並列処理が主要でした。\n1つのリクエストに対してOSのプロセスを立ち上げ、それぞれのプロセスで別々のプログラムを動かすことで大量のリクエストをさばくことができます。\nしかし1つのサーバ上で起動できるプロセスの数には限度があります。そのためたとえば1万人の同時リクエストを1台のサーバで(処理能力に余裕があったとしても)さばけないのが問題となりました。\nプロセスを起動すること自体のオーバーヘッドも無視できません。")]),s._v(" "),t("p",[s._v("インターネットが普及して利用者が増えたことや、IoTなど大量の小さなデータを送信する機会が増えたことで、より効率の良い方法が求められました。")]),s._v(" "),t("h3",{attrs:{id:"イベント駆動"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#イベント駆動"}},[s._v("#")]),s._v(" イベント駆動")]),s._v(" "),t("p",[s._v("C10K 問題に対してNode.jsは「イベントループ」と「ノンブロッキングI/O」の2つの仕様で解決しています。下の図はイベントループとノンブロッキングI/Oを組み合わせたNode.jsにおける並行処理の概念図です。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(469),alt:"イベントループ",title:"イベントループ"}})]),s._v(" "),t("p",[s._v("まずNode.jsは基本的にシングルスレッドで動作します。Node.jsは動作中常にイベントループと呼ばれる無限ループで「diskへの書き込みが終わった」「リクエストを受けとった」のようなイベントの発生を監視しています。そして検知したイベントに対して指定されたcallback関数を実行します。")]),s._v(" "),t("p",[s._v("このようにイベントの発生に対して処理を行うプログラムを「イベント駆動型」と呼びます。")]),s._v(" "),t("p",[s._v("またNode.jsにおいて、diskへの書き込みやネットワークアクセスなど時間のかかる処理は全て非同期に実行されます。つまりdiskへの書き込みを始めた時点でプログラムは他の処理を始め、書き込みが行われている間別の処理を実行します。\nそして「書き込みが完了した」というイベントが発生すると続きの処理を行います。")]),s._v(" "),t("p",[s._v("この非同期な処理はノンブロッキングIOと呼ばれます。")]),s._v(" "),t("p",[s._v("このようにNode.jsは「イベントループ」と「ノンブロッキングI/O」の2つによってシングルスレッドで多くのリクエストを並行して処理することで、C10K問題におけるスレッドの割り当てコストを解決しています。")]),s._v(" "),t("p",[s._v("ただし逆に気をつけないといけない点は、Node.jsはシングルスレッドで動作するため、CPUを多く使うような重たい処理を行うとアプリケーション全体が遅くなってしまうことです。\nその点スレッドプログラミングでは、あるスレッドがどれだけ重くなっても他のスレッドには影響がないためアプリケーション全体のパフォーマンスが落ちることはありません。")]),s._v(" "),t("h2",{attrs:{id:"first-step"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#first-step"}},[s._v("#")]),s._v(" first step")]),s._v(" "),t("p",[s._v("まずは簡単なWebサーバを作ってみましょう。")]),s._v(" "),t("p",[s._v("下準備で立ち上げたdockerに入り、"),t("code",[s._v("bootcamp-node")]),s._v("というディレクトリを作成してください。\nそしてそのディレクトリ内に以下のファイルを"),t("code",[s._v("app.js")]),s._v("という名前で作成してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("mkdir bootcamp-node\ncd bootcamp-node/\nvi app.js\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" http "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'http'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" hostname "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'127.0.0.1'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" server "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" http"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("createServer")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("statusCode "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setHeader")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("end")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello World\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\nserver"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" hostname"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n console"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("log")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token template-string"}},[t("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[s._v("`")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("Server running at http://")]),t("span",{pre:!0,attrs:{class:"token interpolation"}},[t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("${")]),s._v("hostname"),t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("}")])]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token interpolation"}},[t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("${")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("}")])]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("/")]),t("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[s._v("`")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("ファイルを作成したら以下のコマンドでサーバーを起動します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" app.js\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("code",[s._v("Server running at http://127.0.0.1:3000/")]),s._v("というメッセージが表示されれば無事にサーバーが立ち上がっています。curlコマンドでリクエストを投げてみましょう。"),t("code",[s._v("Hello World")]),s._v(" が表示されたはずです。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" http://127.0.0.1:3000/\nHello World\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("h3",{attrs:{id:"簡単な解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単な解説"}},[s._v("#")]),s._v(" 簡単な解説")]),s._v(" "),t("p",[s._v("この短いコードでもNode.jsの特徴が出ています。コード中の")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" server "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" http"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("createServer")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("statusCode "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setHeader")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("end")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello World\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("p",[s._v("の部分は"),t("code",[s._v("createServer")]),s._v("というメソッドにcallbackとして無名関数を渡すことで、リクエストが来た際の動作を定義します。今回は"),t("code",[s._v("Hello World")]),s._v("という文字列を返すようにしました。")]),s._v(" "),t("p",[s._v("このようにNode.jsでは基本的にcallback関数を指定することでサーバーの挙動を定義して行きます。")]),s._v(" "),t("h2",{attrs:{id:"npm"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#npm"}},[s._v("#")]),s._v(" NPM")]),s._v(" "),t("p",[s._v("もう少し複雑なサーバーを作りたいところですが、素のNode.jsだと色々と機能が足りないので"),t("code",[s._v("express")]),s._v("という定番ライブラリを導入します。"),t("code",[s._v("express")]),s._v("はWebサーバーを作る際の基本的な機能を提供してくれる他、pluginとして様々な機能を組み合わせることができます。")]),s._v(" "),t("p",[s._v("Node.jsで追加ライブラリを導入するには"),t("code",[s._v("NPM (node package manager)")]),s._v("と呼ばれるパッケージマネージャを使います。npmはNode.jsをインストールすると自動的に追加されます。")]),s._v(" "),t("p",[s._v("まずは"),t("code",[s._v("bootcamp-node")]),s._v("ディレクトリ以下で"),t("code",[s._v("init")]),s._v("コマンドを実行し、npmで管理するアプリケーションの定義を作成しましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" init\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("色々聞かれますが全てenterで構いません。最後まで行くと"),t("code",[s._v("package.json")]),s._v("というファイルが作成されます。\nnpmはパッケージマネージャですが、このようにアプリケーションのメインファイルを指定したり"),t("code",[s._v("npm script")]),s._v("と呼ばれるコマンドを登録できるなど、Node.jsアプリケーション全体の管理もしてくれます。")]),s._v(" "),t("p",[s._v("以下のコマンドでアプリケーションに"),t("code",[s._v("express")]),s._v("を追加してみましょう。")]),s._v(" "),t("p",[s._v("必要であればプロキシを設定してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("npm -g config set proxy http://proxy.iiji.jp:8080\nnpm -g config set https-proxy http://proxy.iiji.jp:8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" express\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("すると"),t("code",[s._v("node_modules")]),s._v("というディレクトリ内に"),t("code",[s._v("express")]),s._v("と"),t("code",[s._v("express")]),s._v("が依存するライブラリの本体がダウンロードされ、アプリケーションから使えるようになります。")]),s._v(" "),t("p",[s._v("また"),t("code",[s._v("package.json")]),s._v("を見ると"),t("code",[s._v("dependencies")]),s._v("に"),t("code",[s._v("express")]),s._v("が追加されているのが分かります。このように依存するライブラリが"),t("code",[s._v("package.json")]),s._v("で一元管理されるため、他の場所でアプリケーションを動かそうとする場合に依存ライブラリを簡単に導入することができます。")]),s._v(" "),t("p",[s._v("npmの詳しい使い方は「npm 入門」などで検索したサイトなどを見てみてください("),t("a",{attrs:{href:"https://docs.npmjs.com/cli-documentation/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のドキュメント"),t("OutboundLink")],1),s._v("は非常に分かりにくいです・・・)。")]),s._v(" "),t("h2",{attrs:{id:"express-アプリケーションの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#express-アプリケーションの作成"}},[s._v("#")]),s._v(" express アプリケーションの作成")]),s._v(" "),t("p",[s._v("npmでexpressを導入できたので、簡単なアプリケーションを作成してみましょう。"),t("code",[s._v("express.js")]),s._v("という名前で以下のようなファイルを作成してください。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" express "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'express'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" app "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("express")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/bootcamp1'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'BOOTCAMP 1\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/bootcamp2'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'BOOTCAMP 2\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("post")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/create'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Create Bootcamp\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v("修正したら先ほどと同じように"),t("code",[s._v("node express.js")]),s._v("で起動しましょう。今度はアクセスするパスやメソッドによって実行されるcallback関数が変わります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/bootcamp1\nBOOTCAMP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/bootcamp2\nBOOTCAMP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -X POST "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/create\nCreate Bootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("p",[s._v("さきほどよりも少ないコードで多くの機能が実現できました。expressは人気のフレームワークで様々なプラグインやラップしたフレームワークが作られています。\nもし興味があれば調べてみてください。")]),s._v(" "),t("h2",{attrs:{id:"データベースアクセス"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#データベースアクセス"}},[s._v("#")]),s._v(" データベースアクセス")]),s._v(" "),t("p",[s._v("最後にデータベースへのアクセスを実現してみましょう。データベースにはMongoDBを使用してみます。\n(最近はあまり言われませんが、かつてはMongoDB・Express・AngularJS・Node.jsを合わせて「MEANスタック」と呼ばれたりしていました。)")]),s._v(" "),t("p",[s._v("事前準備で作られたvagrantやdockerコンテナにはmongodbが既にinstallされています。以下のコマンドでMongoDBを起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" mongodb start\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("node.jsからMongoDBにアクセスするために"),t("code",[s._v("mongoose")]),s._v("というライブラリを使います。\n先ほどと同じようにnpmでインストールしましょう。ついでにjsonをパースするための"),t("code",[s._v("body-parser")]),s._v("も追加します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" mongoose body-parser\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("今回は"),t("code",[s._v("peoples")]),s._v("というテーブルに"),t("code",[s._v("_id")]),s._v("と"),t("code",[s._v("name")]),s._v("属性をもつドキュメントを作成し、その一覧を取得するAPIを作ってみます。")]),s._v(" "),t("p",[s._v("まずはmongooseを使うためにmodelの定義を行います。以下の内容を"),t("code",[s._v("model.js")]),s._v("として作成してください。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" mongoose "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'mongoose'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" Schema "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" mongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("Schema"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Peopleの定義")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" People "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Schema")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" String"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// MongoDBへの接続")]),s._v("\nmongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("connect")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'mongodb://127.0.0.1:27017/bootcamp'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("useNewUrlParser")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// モデルをエクスポート")]),s._v("\nexports"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" mongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("model")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'People'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" People"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br")])]),t("p",[s._v("次に"),t("code",[s._v("mongo.js")]),s._v("を以下のように作成してAPIを定義しましょう。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" bodyParser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'body-parser'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" express "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'express'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" app "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("express")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" model "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'./model'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("use")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("bodyParser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("urlencoded")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("extended")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("use")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("bodyParser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/peoples'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" peoples")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("peoples"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("post")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/peoples'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" people "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n people"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n people"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("err")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("message")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'created!\\n'")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br")])]),t("p",[t("code",[s._v("node mongo.js")]),s._v("でサーバーを起動してください。そして別のウィンドウから以下のようにcurlでリクエストを投げることができます。")]),s._v(" "),t("p",[t("code",[s._v("name=Alice")]),s._v("なデータを作成")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -X POST -H "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('\'{"name": "Alice"}\'')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/peoples\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"message"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"created!"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("一覧で取得")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/peoples\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"_id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"5cf3f435a47f5c0cdb9023a6"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Alice"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"__v"')]),s._v(":0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h2",{attrs:{id:"最後に"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最後に"}},[s._v("#")]),s._v(" 最後に")]),s._v(" "),t("p",[s._v("ここまで簡単なNode.jsを使ったAPIサーバーの実装を行ってみました。なんとなくNode.jsのやり方が掴めたでしょうか。")]),s._v(" "),t("p",[s._v("ただし実際のプロダクトで使用する場合は、TypeScriptを使って開発したりAPIのcallback関数を別ファイルにする、ロギングの仕組みを整えるなど様々なことを考える必要があります。")]),s._v(" "),t("p",[s._v("今回の内容は本当に基礎的なものでしかないため、もし興味があれば様々なフレームワークや実装例を調べてみてください。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{471:function(s,t,a){s.exports=a.p+"assets/img/event-loop.08b77931.png"},552:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"下準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#下準備"}},[s._v("#")]),s._v(" 下準備")]),s._v(" "),t("h3",{attrs:{id:"dockerコンテナの立ち上げ方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの立ち上げ方"}},[s._v("#")]),s._v(" dockerコンテナの立ち上げ方")]),s._v(" "),t("p",[s._v("以下のコマンドでdockerコンテナを立ち上げてログインしてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name bootcamp-node --rm -it regunorf/bootcamp-node "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("同じdockerコンテナに複数のターミナルを接続するには以下のようにします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it bootcamp-node "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ハンズオンでは同じコンテナに複数ターミナルでログインしていることを前提に記述している部分があるので、上記のように複数ターミナルを開いておいてください。")]),s._v(" "),t("h2",{attrs:{id:"node-js-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#node-js-とは"}},[s._v("#")]),s._v(" Node.js とは")]),s._v(" "),t("p",[s._v("Node.js はサーバサイドで動作するJavaScriptのエンジンです。従来ブラウザ上でWebページを動的に動かすために使われていたJavaScriptを、ブラウザ上だけでなくサーバ上でも動かすためNode.jsなどが生まれました。")]),s._v(" "),t("p",[s._v("ブラウザ以外で動作するJavaScriptの実装としてはほかにもRhinoなどがあります。\n最近ではTypeScriptがそのまま動作する "),t("a",{attrs:{href:"https://github.com/denoland/deno",target:"_blank",rel:"noopener noreferrer"}},[s._v("Deno"),t("OutboundLink")],1),s._v(" も登場しています。")]),s._v(" "),t("p",[s._v("Node.jsの特徴としては、何よりもまず実装全体がイベント駆動で動作するという点です。これを説明するにはまずC10K問題から話をする必要があります。少し長くなりますが、Node.jsの立ち位置を知るためには必要なので簡単に解説します。")]),s._v(" "),t("h3",{attrs:{id:"c10k-問題"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#c10k-問題"}},[s._v("#")]),s._v(" C10K 問題")]),s._v(" "),t("p",[s._v("Webサーバーは当然の事ながらたくさんのユーザーからのアクセスに答えて、HTMLを返したりサーバーサイドのプログラム(C言語やJavaやRubyなどで書かれた)を動かしてレスポンスを返す必要があります。その際に1つ1つ順番に処理していたのではキリがないため、たくさんのアクセスを並列に処理する必要があります。")]),s._v(" "),t("p",[s._v("プログラムを並列に動かすやり方はいくつかありますが、たとえばApacheではスレッド・プロセスベースの並列処理が主要でした。\n1つのリクエストに対してOSのプロセスを立ち上げ、それぞれのプロセスで別々のプログラムを動かすことで大量のリクエストをさばくことができます。\nしかし1つのサーバ上で起動できるプロセスの数には限度があります。そのためたとえば1万人の同時リクエストを1台のサーバで(処理能力に余裕があったとしても)さばけないのが問題となりました。\nプロセスを起動すること自体のオーバーヘッドも無視できません。")]),s._v(" "),t("p",[s._v("インターネットが普及して利用者が増えたことや、IoTなど大量の小さなデータを送信する機会が増えたことで、より効率の良い方法が求められました。")]),s._v(" "),t("h3",{attrs:{id:"イベント駆動"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#イベント駆動"}},[s._v("#")]),s._v(" イベント駆動")]),s._v(" "),t("p",[s._v("C10K 問題に対してNode.jsは「イベントループ」と「ノンブロッキングI/O」の2つの仕様で解決しています。下の図はイベントループとノンブロッキングI/Oを組み合わせたNode.jsにおける並行処理の概念図です。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(471),alt:"イベントループ",title:"イベントループ"}})]),s._v(" "),t("p",[s._v("まずNode.jsは基本的にシングルスレッドで動作します。Node.jsは動作中常にイベントループと呼ばれる無限ループで「diskへの書き込みが終わった」「リクエストを受けとった」のようなイベントの発生を監視しています。そして検知したイベントに対して指定されたcallback関数を実行します。")]),s._v(" "),t("p",[s._v("このようにイベントの発生に対して処理を行うプログラムを「イベント駆動型」と呼びます。")]),s._v(" "),t("p",[s._v("またNode.jsにおいて、diskへの書き込みやネットワークアクセスなど時間のかかる処理は全て非同期に実行されます。つまりdiskへの書き込みを始めた時点でプログラムは他の処理を始め、書き込みが行われている間別の処理を実行します。\nそして「書き込みが完了した」というイベントが発生すると続きの処理を行います。")]),s._v(" "),t("p",[s._v("この非同期な処理はノンブロッキングIOと呼ばれます。")]),s._v(" "),t("p",[s._v("このようにNode.jsは「イベントループ」と「ノンブロッキングI/O」の2つによってシングルスレッドで多くのリクエストを並行して処理することで、C10K問題におけるスレッドの割り当てコストを解決しています。")]),s._v(" "),t("p",[s._v("ただし逆に気をつけないといけない点は、Node.jsはシングルスレッドで動作するため、CPUを多く使うような重たい処理を行うとアプリケーション全体が遅くなってしまうことです。\nその点スレッドプログラミングでは、あるスレッドがどれだけ重くなっても他のスレッドには影響がないためアプリケーション全体のパフォーマンスが落ちることはありません。")]),s._v(" "),t("h2",{attrs:{id:"first-step"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#first-step"}},[s._v("#")]),s._v(" first step")]),s._v(" "),t("p",[s._v("まずは簡単なWebサーバを作ってみましょう。")]),s._v(" "),t("p",[s._v("下準備で立ち上げたdockerに入り、"),t("code",[s._v("bootcamp-node")]),s._v("というディレクトリを作成してください。\nそしてそのディレクトリ内に以下のファイルを"),t("code",[s._v("app.js")]),s._v("という名前で作成してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("mkdir bootcamp-node\ncd bootcamp-node/\nvi app.js\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" http "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'http'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" hostname "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'127.0.0.1'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" server "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" http"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("createServer")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("statusCode "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setHeader")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("end")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello World\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\nserver"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" hostname"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n console"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("log")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token template-string"}},[t("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[s._v("`")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("Server running at http://")]),t("span",{pre:!0,attrs:{class:"token interpolation"}},[t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("${")]),s._v("hostname"),t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("}")])]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token interpolation"}},[t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("${")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[s._v("}")])]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("/")]),t("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[s._v("`")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("ファイルを作成したら以下のコマンドでサーバーを起動します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("node")]),s._v(" app.js\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("code",[s._v("Server running at http://127.0.0.1:3000/")]),s._v("というメッセージが表示されれば無事にサーバーが立ち上がっています。curlコマンドでリクエストを投げてみましょう。"),t("code",[s._v("Hello World")]),s._v(" が表示されたはずです。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" http://127.0.0.1:3000/\nHello World\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("h3",{attrs:{id:"簡単な解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#簡単な解説"}},[s._v("#")]),s._v(" 簡単な解説")]),s._v(" "),t("p",[s._v("この短いコードでもNode.jsの特徴が出ています。コード中の")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" server "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" http"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("createServer")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("statusCode "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("setHeader")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'text/plain'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("end")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Hello World\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("p",[s._v("の部分は"),t("code",[s._v("createServer")]),s._v("というメソッドにcallbackとして無名関数を渡すことで、リクエストが来た際の動作を定義します。今回は"),t("code",[s._v("Hello World")]),s._v("という文字列を返すようにしました。")]),s._v(" "),t("p",[s._v("このようにNode.jsでは基本的にcallback関数を指定することでサーバーの挙動を定義して行きます。")]),s._v(" "),t("h2",{attrs:{id:"npm"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#npm"}},[s._v("#")]),s._v(" NPM")]),s._v(" "),t("p",[s._v("もう少し複雑なサーバーを作りたいところですが、素のNode.jsだと色々と機能が足りないので"),t("code",[s._v("express")]),s._v("という定番ライブラリを導入します。"),t("code",[s._v("express")]),s._v("はWebサーバーを作る際の基本的な機能を提供してくれる他、pluginとして様々な機能を組み合わせることができます。")]),s._v(" "),t("p",[s._v("Node.jsで追加ライブラリを導入するには"),t("code",[s._v("NPM (node package manager)")]),s._v("と呼ばれるパッケージマネージャを使います。npmはNode.jsをインストールすると自動的に追加されます。")]),s._v(" "),t("p",[s._v("まずは"),t("code",[s._v("bootcamp-node")]),s._v("ディレクトリ以下で"),t("code",[s._v("init")]),s._v("コマンドを実行し、npmで管理するアプリケーションの定義を作成しましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" init\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("色々聞かれますが全てenterで構いません。最後まで行くと"),t("code",[s._v("package.json")]),s._v("というファイルが作成されます。\nnpmはパッケージマネージャですが、このようにアプリケーションのメインファイルを指定したり"),t("code",[s._v("npm script")]),s._v("と呼ばれるコマンドを登録できるなど、Node.jsアプリケーション全体の管理もしてくれます。")]),s._v(" "),t("p",[s._v("以下のコマンドでアプリケーションに"),t("code",[s._v("express")]),s._v("を追加してみましょう。")]),s._v(" "),t("p",[s._v("必要であればプロキシを設定してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("npm -g config set proxy http://proxy.iiji.jp:8080\nnpm -g config set https-proxy http://proxy.iiji.jp:8080\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" express\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("すると"),t("code",[s._v("node_modules")]),s._v("というディレクトリ内に"),t("code",[s._v("express")]),s._v("と"),t("code",[s._v("express")]),s._v("が依存するライブラリの本体がダウンロードされ、アプリケーションから使えるようになります。")]),s._v(" "),t("p",[s._v("また"),t("code",[s._v("package.json")]),s._v("を見ると"),t("code",[s._v("dependencies")]),s._v("に"),t("code",[s._v("express")]),s._v("が追加されているのが分かります。このように依存するライブラリが"),t("code",[s._v("package.json")]),s._v("で一元管理されるため、他の場所でアプリケーションを動かそうとする場合に依存ライブラリを簡単に導入することができます。")]),s._v(" "),t("p",[s._v("npmの詳しい使い方は「npm 入門」などで検索したサイトなどを見てみてください("),t("a",{attrs:{href:"https://docs.npmjs.com/cli-documentation/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のドキュメント"),t("OutboundLink")],1),s._v("は非常に分かりにくいです・・・)。")]),s._v(" "),t("h2",{attrs:{id:"express-アプリケーションの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#express-アプリケーションの作成"}},[s._v("#")]),s._v(" express アプリケーションの作成")]),s._v(" "),t("p",[s._v("npmでexpressを導入できたので、簡単なアプリケーションを作成してみましょう。"),t("code",[s._v("express.js")]),s._v("という名前で以下のようなファイルを作成してください。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" express "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'express'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" app "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("express")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/bootcamp1'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'BOOTCAMP 1\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/bootcamp2'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'BOOTCAMP 2\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("post")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/create'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Create Bootcamp\\n'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v("修正したら先ほどと同じように"),t("code",[s._v("node express.js")]),s._v("で起動しましょう。今度はアクセスするパスやメソッドによって実行されるcallback関数が変わります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/bootcamp1\nBOOTCAMP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/bootcamp2\nBOOTCAMP "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -X POST "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/create\nCreate Bootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("p",[s._v("さきほどよりも少ないコードで多くの機能が実現できました。expressは人気のフレームワークで様々なプラグインやラップしたフレームワークが作られています。\nもし興味があれば調べてみてください。")]),s._v(" "),t("h2",{attrs:{id:"データベースアクセス"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#データベースアクセス"}},[s._v("#")]),s._v(" データベースアクセス")]),s._v(" "),t("p",[s._v("最後にデータベースへのアクセスを実現してみましょう。データベースにはMongoDBを使用してみます。\n(最近はあまり言われませんが、かつてはMongoDB・Express・AngularJS・Node.jsを合わせて「MEANスタック」と呼ばれたりしていました。)")]),s._v(" "),t("p",[s._v("事前準備で作られたvagrantやdockerコンテナにはmongodbが既にinstallされています。以下のコマンドでMongoDBを起動してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" mongodb start\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("node.jsからMongoDBにアクセスするために"),t("code",[s._v("mongoose")]),s._v("というライブラリを使います。\n先ほどと同じようにnpmでインストールしましょう。ついでにjsonをパースするための"),t("code",[s._v("body-parser")]),s._v("も追加します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("npm")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" mongoose body-parser\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("今回は"),t("code",[s._v("peoples")]),s._v("というテーブルに"),t("code",[s._v("_id")]),s._v("と"),t("code",[s._v("name")]),s._v("属性をもつドキュメントを作成し、その一覧を取得するAPIを作ってみます。")]),s._v(" "),t("p",[s._v("まずはmongooseを使うためにmodelの定義を行います。以下の内容を"),t("code",[s._v("model.js")]),s._v("として作成してください。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" mongoose "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'mongoose'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" Schema "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" mongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("Schema"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Peopleの定義")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" People "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Schema")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("name")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("type")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" String"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// MongoDBへの接続")]),s._v("\nmongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("connect")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'mongodb://127.0.0.1:27017/bootcamp'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("useNewUrlParser")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// モデルをエクスポート")]),s._v("\nexports"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" mongoose"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("model")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'People'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" People"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br")])]),t("p",[s._v("次に"),t("code",[s._v("mongo.js")]),s._v("を以下のように作成してAPIを定義しましょう。")]),s._v(" "),t("div",{staticClass:"language-javascript line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-javascript"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" bodyParser "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'body-parser'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" express "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'express'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" app "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("express")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" model "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("require")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'./model'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("use")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("bodyParser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("urlencoded")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("extended")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("use")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("bodyParser"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/peoples'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" peoples")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("peoples"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("post")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/peoples'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" res")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("const")]),s._v(" people "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("new")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("People")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n people"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" req"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n people"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("save")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token parameter"}},[s._v("err")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=>")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("send")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("err"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("json")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token literal-property property"}},[s._v("message")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'created!\\n'")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\napp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("listen")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3000")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br")])]),t("p",[t("code",[s._v("node mongo.js")]),s._v("でサーバーを起動してください。そして別のウィンドウから以下のようにcurlでリクエストを投げることができます。")]),s._v(" "),t("p",[t("code",[s._v("name=Alice")]),s._v("なデータを作成")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -X POST -H "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('\'{"name": "Alice"}\'')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/peoples\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"message"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"created!"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("一覧で取得")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:3000/peoples\n\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"_id"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"5cf3f435a47f5c0cdb9023a6"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Alice"')]),s._v(","),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"__v"')]),s._v(":0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("h2",{attrs:{id:"最後に"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最後に"}},[s._v("#")]),s._v(" 最後に")]),s._v(" "),t("p",[s._v("ここまで簡単なNode.jsを使ったAPIサーバーの実装を行ってみました。なんとなくNode.jsのやり方が掴めたでしょうか。")]),s._v(" "),t("p",[s._v("ただし実際のプロダクトで使用する場合は、TypeScriptを使って開発したりAPIのcallback関数を別ファイルにする、ロギングの仕組みを整えるなど様々なことを考える必要があります。")]),s._v(" "),t("p",[s._v("今回の内容は本当に基礎的なものでしかないため、もし興味があれば様々なフレームワークや実装例を調べてみてください。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/33.b6521a6a.js b/assets/js/33.af6ac3d9.js similarity index 99% rename from assets/js/33.b6521a6a.js rename to assets/js/33.af6ac3d9.js index 97182b47..c7bc6451 100644 --- a/assets/js/33.b6521a6a.js +++ b/assets/js/33.af6ac3d9.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{484:function(s,t,a){s.exports=a.p+"assets/img/figure1.959b588c.png"},555:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h1",{attrs:{id:"目次"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#目次"}},[s._v("#")]),s._v(" 目次")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB"}},[s._v("はじめに")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E6%A6%82%E8%AB%96"}},[s._v("概論")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AA%E3%81%9C%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E8%A1%8C%E3%81%86%E3%81%AE%E3%81%8B"}},[s._v("なぜテストを行うのか")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E5%8A%B9%E7%8E%87%E7%9A%84%E3%81%AA%E3%83%86%E3%82%B9%E3%83%88%E3%81%A8%E3%81%AF"}},[s._v("効率的なテストとは")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%81%84%E3%81%A4%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E3%82%8B%E3%81%AE%E3%81%8B"}},[s._v("いつテストを作るのか")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E6%BA%96%E5%82%99"}},[s._v("準備")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E5%AE%9F%E8%A1%8C%E6%96%B9%E6%B3%95"}},[s._v("テストの実行")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E9%96%A2%E6%95%B0%E3%83%BB%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E4%BF%AE%E6%AD%A3%E6%96%B9%E6%B3%95"}},[s._v("関数・テストの修正方法")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B"}},[s._v("テストを実行する")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E5%90%8C%E5%80%A4%E3%82%AF%E3%83%A9%E3%82%B9%E3%83%BB%E5%A2%83%E7%95%8C%E5%80%A4%E3%83%86%E3%82%B9%E3%83%88"}},[s._v("同値クラス・境界値テスト")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#api%E3%81%A8%E9%96%A2%E6%95%B0%E3%81%AE%E3%83%A2%E3%83%83%E3%82%AF"}},[s._v("APIと関数のモック")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#tdd%E3%82%92%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B"}},[s._v("TDDをやってみる")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB"}},[s._v("おわりに")])])]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[s._v("#")]),s._v(" はじめに")]),s._v(" "),t("p",[s._v("本講義はdockerを使用します。"),t("br"),s._v("\ndockerコンテナのpullには時間を要するため、概論の聴講と並行して「準備 ⇒ "),t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")]),s._v("」を実施することを推奨します。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"概論"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#概論"}},[s._v("#")]),s._v(" 概論")]),s._v(" "),t("h2",{attrs:{id:"なぜテストを行うのか"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#なぜテストを行うのか"}},[s._v("#")]),s._v(" なぜテストを行うのか")]),s._v(" "),t("p",[s._v("昨今ではIT技術が普及し、炊飯器・電子レンジ・洗濯機といった身の回りのものから、航空機や車など、普段の生活に必須になるものにまで。ソフトウェアが使用されています。"),t("br"),s._v("\nまた、世の中に流通しているソフトウェアはテストが実施されており、その挙動で問題が起こらないことを保証されています。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えばソフトウェアに対し、テストを行われていないと仮定して、個人的に運営しているブログなどで不具合が発生した場合はどうなるでしょうか。"),t("br"),s._v("\nその不具合によってサーバがダウンしている間は、運営者に広告費が入らないなど、ある程度小規模で済みます。"),t("br"),s._v("\n(ブログ収益で生計を立てている場合、致命的ですが。)")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば自動車や医療機器などで不具合が発生してしまった場合、どうなるでしょうか。"),t("br"),s._v("\n最悪の場合、ブレーキが効かない、医療機器のレーザーの出力が多すぎたなど、ソフトウェアの欠陥によって人命が失われてしまう可能性もあります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("上記2つの例を上げましたが、大なり小なり、ソフトウェアの不具合によって、どこかの誰かが被害を被ってしまいます。"),t("br"),s._v("\nそのため、自身が作成するプログラムでは必ず動作のテストを行い、極力不具合を発生させないソフトウェアを作ることを目指す必要があります。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"効率的なテストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#効率的なテストとは"}},[s._v("#")]),s._v(" 効率的なテストとは")]),s._v(" "),t("p",[s._v("テストを作成する場合には、不具合をなくすことも重要ですが、テストにコストをかけないことも重要になります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば、あるプロダクトに使用される、以下のような仕様の関数"),t("code",[s._v("f(x)")]),s._v("があるとします。")]),s._v(" "),t("ul",[t("li",[s._v("関数"),t("code",[s._v("f")]),s._v("は、任意の数字"),t("code",[s._v("x")]),s._v("の値を取ります。")]),s._v(" "),t("li",[s._v("任意の数字"),t("code",[s._v("x")]),s._v("は、int型であり、 "),t("em",[s._v("-2,147,483,648")]),s._v(" から "),t("em",[s._v("2,147,483,647")]),s._v(" の範囲の値を格納できます。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("f")]),s._v("は、与えられた数字が "),t("em",[s._v("0")]),s._v(" から "),t("em",[s._v("100")]),s._v(" の間であれば"),t("code",[s._v("True")]),s._v("、そうでなければ"),t("code",[s._v("False")]),s._v("を返却します。")])]),s._v(" "),t("p",[s._v("上記の"),t("code",[s._v("f(x)")]),s._v("の挙動を100%確かめるためには、 "),t("em",[s._v("4,294,967,296")]),s._v(" 件のテストを行わなければなりません。"),t("br"),s._v("\nしかし、実際のプロダクトを作成する場合、1つの関数に対し40億回もテストを実施してしまうと、プロダクトの売上以上に人件費や計算機の運用コストがかかってしまい、会社は倒産の危機に瀕してしまいます。")]),s._v(" "),t("p",[s._v("そのため、後述する「同値クラス・境界値テスト」などの手法によって、最低限かつ最適な回数でテストを行うことが求められます。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"いつテストを作るのか"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#いつテストを作るのか"}},[s._v("#")]),s._v(" いつテストを作るのか")]),s._v(" "),t("p",[s._v("開発を行う際、ウォーターフォール型の開発では、下記の流れになるかと思います。"),t("br"),s._v("\n右下向きの矢印が設計工程、中央が開発工程、右上向きの矢印がテスト工程になります。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(484),alt:"figure1.png",title:"figure1"}})]),s._v(" "),t("p",[s._v("設計における各要素は、テスト工程の各要素に対応することになります。"),t("br"),s._v("\n例えば、まずは要求定義を行い、ソフトウェアに必要な要件を決めますが、この時点で明確な要件が定義できているのであれば、システムテストで実施するテスト項目を作成しておくことができます。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("このように各設計段階でテスト項目を作成することで、要件に沿ったテストを作成することができます。"),t("br"),s._v("\n(何年も開発を行っているとわかるのですが、ものを作ってからテストを作成すると、「今動くものを通すテスト」を無意識的に書いてしまい、要件も網羅できないテストを作ってしまう傾向が出てきます。)")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("また、後述するTDD(テスト駆動開発)のように、テストを作成して開発を進める手法もあります。"),t("br"),s._v("\n今回のハンズオンではコーディング・単体テスト段階で実施するテストプログラミングをやってみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#準備"}},[s._v("#")]),s._v(" 準備")]),s._v(" "),t("h2",{attrs:{id:"dockerコンテナの立ち上げ方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの立ち上げ方"}},[s._v("#")]),s._v(" dockerコンテナの立ち上げ方")]),s._v(" "),t("p",[s._v("下記のコマンドでdockerコンテナを立ち上げます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# リポジトリのクローン")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" clone git@github.com:iij/bootcamp.git\n$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" bootcamp/src/server-app/test-hands-on\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナの立ち上げ")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker-compose")]),s._v(" up --build\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("br"),s._v(" "),t("h2",{attrs:{id:"テストの実行方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テストの実行方法"}},[s._v("#")]),s._v(" テストの実行方法")]),s._v(" "),t("p",[s._v("この項では、任意の「"),t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B"}},[s._v("テストを実行する")]),s._v("」の項のテストを実行します。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("「"),t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")]),s._v("」で、起動中のコンソールとは別のコンソールを開き、実行中のコンテナにアクセスします。"),t("br"),s._v("\nコマンドを実行すると、コンテナ内のbashが実行されます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" bootcamp/src/server-app/test-hands-on\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker-compose")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" bootcamp-test "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("下記のコマンドで、テストを実行してみましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# ソースは全て"/test-hands-on"配下にあります。')]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" /test-hands-on\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 任意のテストを実行します。")]),s._v("\n$ python -m unittest -v exercises.exercise0.test_challenge\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("br"),s._v(" "),t("h2",{attrs:{id:"関数・テストの修正方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#関数・テストの修正方法"}},[s._v("#")]),s._v(" 関数・テストの修正方法")]),s._v(" "),t("p",[s._v("「テストの実行方法」の項でテストを行うと、初回は下記のようにテストが失敗してしまいます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v exercises.exercise0.test_challenge\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v("\nFAIL: test_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n----------------------------------------------------------------------\nTraceback "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("most recent call last"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n File "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/test-hands-on/exercises/exercise0/test_challenge.py"')]),s._v(", line "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("7")]),s._v(", "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" test_success\n self.assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nAssertionError: "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),s._v("\n- hello world\n+ goodbye world?\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("試しに、このテストを修正してみましょう。\nテストソースである、 "),t("code",[s._v("/test-hands-on/exercises/exercise0/test_challenge.py")]),s._v(" を開いてみます。")]),s._v(" "),t("p",[s._v("内容は下記のようになっており、ソース内でimportしている "),t("code",[s._v("hello()")]),s._v(' 関数に対し、文字列"goodbye world?"が来ることを期待してテストを行っているようです。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("challenge "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" hello\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("では、テスト対象である "),t("code",[s._v("hello()")]),s._v(" 関数を見てみましょう。"),t("br"),s._v('\nどうやら、この関数は文字列"hello world"を返すようです。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("hello")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("しかし、これではテストソースで期待されている関数の返り値と、実際の関数の返り値が異なってしまっています。"),t("br"),s._v('\nこれがテストが失敗してしまう原因となるため、テストの期待する値を"goodbye world?"から"hello world"に変えてみましょう。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("challenge "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" hello\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("p",[s._v("このテストを実行してみると、先程まで失敗していたテストが成功しました。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("python -m unittest -v exercises.exercise0.test_challenge\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n\n----------------------------------------------------------------------\nRan "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("test")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(".000s\n\nOK\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("このように、テストソースというものは、テストを実施したい関数に対して動作を確認するように作成・実行します。")]),s._v(" "),t("p",[s._v("本講義では、テストを実施したい関数に対し、テストソースで期待する返り値を設定し、関数の動作確認を行っていきます。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("ちなみに、ローカルのソースファイルの変更は、コンテナ内にも自動で同期されます。"),t("br"),s._v("\n以降はローカルでファイルを変更し、コンテナ内でテストを実行してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"テストを実行する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テストを実行する"}},[s._v("#")]),s._v(" テストを実行する")]),s._v(" "),t("h2",{attrs:{id:"_1-同値クラス・境界値テスト"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-同値クラス・境界値テスト"}},[s._v("#")]),s._v(" 1. 同値クラス・境界値テスト")]),s._v(" "),t("p",[s._v("この項では「同値クラステスト」と「境界値テスト」という手法のテストを実施し、効率的なテストについて学びます。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"同値クラステストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#同値クラステストとは"}},[s._v("#")]),s._v(" 同値クラステストとは")]),s._v(" "),t("p",[s._v("同値クラステストとは「任意の関数"),t("code",[s._v("g(x)")]),s._v("の引数"),t("code",[s._v("x")]),s._v("に対し、有効である値、無効である値のグループ(有効同値クラス、無効同値クラス)を定義してテストを実施する」ものになります。")]),s._v(" "),t("p",[s._v("例えば、本書の冒頭で出てきた、関数"),t("code",[s._v("f(x)")]),s._v("では、"),t("code",[s._v("x")]),s._v("の値が "),t("em",[s._v("0")]),s._v(" から "),t("em",[s._v("100")]),s._v(" の間であれば有効同値クラス、そうでなければ無効同値クラス、と定義できます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("関数fは、任意の数字xの値を取ります。\n任意の数字xは、int型であり、 -2,147,483,648 から 2,147,483,647 の範囲の値を格納できます。\n関数fは、与えられた数字が 0 から 100 の間であればTrue、そうでなければFalseを返却します。\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("仮に「有効同値クラス内の値が入力された場合は正常終了、無効同値クラス内の値が入力された場合は異常終了する」と見た場合、終了の仕方は「正常終了か異常終了か」の2択と見ることができます。")]),s._v(" "),t("p",[s._v("すなわち、関数"),t("code",[s._v("f(x)")]),s._v("に対する同値クラステストとは、有効同値である "),t("em",[s._v("10")]),s._v(" , "),t("em",[s._v("50")]),s._v(" , "),t("em",[s._v("90")]),s._v(" など、いくつかの値のグループと、無効同値である "),t("em",[s._v("-500")]),s._v(" , "),t("em",[s._v("-10")]),s._v(" , "),t("em",[s._v("110")]),s._v(" , "),t("em",[s._v("500")]),s._v(" などの値のグループのテストを実施すればよいことになります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"境界値テストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#境界値テストとは"}},[s._v("#")]),s._v(" 境界値テストとは")]),s._v(" "),t("p",[s._v("同値クラステストでは「有効/無効と定義した値に対する処理が正しく動くか」を確認できました。")]),s._v(" "),t("p",[s._v("しかし、これでは「有効/無効の範囲は正しいか」が確認できていません。"),t("br"),s._v("\nこういった場合は境界値テストを実施し、有効値/無効値の境界が、正しく実行されるかのテストを行います。")]),s._v(" "),t("p",[s._v("本書冒頭の関数"),t("code",[s._v("f(x)")]),s._v("を例にすると、下限の境界値は "),t("em",[s._v("-1")]),s._v(" , "),t("em",[s._v("0")]),s._v(" 、上限の境界値は "),t("em",[s._v("100")]),s._v(" , "),t("em",[s._v("101")]),s._v(" となります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("本書冒頭で定義した、関数"),t("code",[s._v("f(x)")]),s._v("がPythonで以下のように定義されているとします。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<=")]),s._v(" x "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("else")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記の関数に対し、同値クラスのテストを定義すると、下記のように書くことができます。"),t("br"),s._v("\n下記のテストでは、関数"),t("code",[s._v("f(x)")]),s._v("に有効同値クラスの値を入力すると"),t("code",[s._v("True")]),s._v("、そうでない値を入力すると"),t("code",[s._v("False")]),s._v("が返却されることを確認しています。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_equivalence_partitioning")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 有効同値のテスト")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("50")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("90")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 無効同値のテスト")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("500")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("110")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("500")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("境界値テストを定義すると、下記のように書くことができます。"),t("br"),s._v("\n下記のテストでは、関数"),t("code",[s._v("f(x)")]),s._v("に下限の境界値 "),t("em",[s._v("-1")]),s._v(" , "),t("em",[s._v("0")]),s._v(" 、上限の境界値 "),t("em",[s._v("100")]),s._v(" , "),t("em",[s._v("101")]),s._v(" を入力し、適宜"),t("code",[s._v("True")]),s._v("か"),t("code",[s._v("False")]),s._v("が返却されることを確認しています。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_equivalence_partitioning")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下限の境界値")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 上限の境界値")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("101")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise1/challenge.py")]),s._v("に、商品の申し込みを行う関数"),t("code",[s._v("apply(quantity)")]),s._v("が定義されています。")]),s._v(" "),t("p",[s._v("関数は以下の仕様になっています。")]),s._v(" "),t("ul",[t("li",[s._v("この関数は、int型の引数"),t("code",[s._v("quantity")]),s._v("を取ります。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("apply()")]),s._v("は、10以上、100以下の値が入力されると、申し込みが成功し、文字列"),t("code",[s._v('"accepted"')]),s._v("が返却されます。")]),s._v(" "),t("li",[s._v("申し込みに失敗した場合は、文字列"),t("code",[s._v('"not accepted"')]),s._v("が返却されます。")]),s._v(" "),t("li",[s._v("int型以外のデータが入力された場合、例外"),t("code",[s._v("TypeError()")]),s._v("が発生し、プログラムが異常終了します。")])]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise1/test_challenge.py")]),s._v("に、作成途中のテストクラス"),t("code",[s._v("ApplyTestCase")]),s._v("が定義されているため、関数"),t("code",[s._v("apply(quantity)")]),s._v("に対するテストを作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"_2-apiと関数のモック"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-apiと関数のモック"}},[s._v("#")]),s._v(" 2. APIと関数のモック")]),s._v(" "),t("p",[s._v("この項では、Pythonで実行できるAPI(FastAPI)のフレームワークを使用し、APIに対するテストや、関数のモックに触れてみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"モックとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#モックとは"}},[s._v("#")]),s._v(" モックとは")]),s._v(" "),t("p",[s._v("「モックアップ」の略称であり、工業製品などの試作や、店頭展示などのためにつくられる実物大模型のことを指します。"),t("br"),s._v("\n「"),t("a",{attrs:{href:"https://dictionary.goo.ne.jp/word/%e3%83%a2%e3%83%83%e3%82%af%e3%82%a2%e3%83%83%e3%83%97/",target:"_blank",rel:"noopener noreferrer"}},[s._v("goo辞書 モックアップ(mock-up)"),t("OutboundLink")],1),s._v("」より")]),s._v(" "),t("p",[s._v("テストにおけるモックとは、主にクラスや関数の動作をシミュレートするためのオブジェクトになります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば、以下のような仕様の関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("があるとします。")]),s._v(" "),t("ul",[t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("は、じゃんけんを行う関数で、引数"),t("code",[s._v("shoot")]),s._v('は文字列"rock", "paper", "scissors"の、いずれかを取ります。')]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("は、内部で引数に対してじゃんけんの手を出す関数"),t("code",[s._v("my_shoot()")]),s._v("が実行されます。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("my_shoot()")]),s._v("は、それぞれ "),t("em",[s._v("1/3")]),s._v(' の確率で"rock", "paper", "scissors"のいずれかを取得します。')]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("は、入力された引数"),t("code",[s._v("shoot")]),s._v("が、関数"),t("code",[s._v("my_shoot()")]),s._v("の返り値に勝利できる場合 "),t("em",[s._v("1")]),s._v(" 、引き分けであれば "),t("em",[s._v("0")]),s._v(" 、敗北であれば "),t("em",[s._v("-1")]),s._v(" を返します。")])]),s._v(" "),t("p",[s._v("上記の関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("をテストする場合、内部の関数の返り値が乱数で決定されてしまうため、通常であればテストが実行できません。"),t("br"),s._v("\n(例えば、1回目の"),t("code",[s._v("my_shoot()")]),s._v('を実行した時に"rock"が返却されたとしても、2回目も"rock"が返却されるとは限らないですよね)')]),s._v(" "),t("p",[s._v("こういった場合、関数のモックを使用して、テスト対象の関数内で使用されているクラスや関数をモックし、返り値を固定してシミュレーションを行う必要があります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-2"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("が、Pythonで以下のように定義されているとします。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("rock_paper_scissors")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("shoot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# 1/3で"rock", "paper", "scissors"が格納される')]),s._v("\n my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" my_shoot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# あいこ")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" my_shoot_result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 勝利")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 敗北")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記の関数に対し、モックを使用したテストを定義すると、下記のように書くことができます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" unittest "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" mock\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 関数rock_paper_scissors(), my_shoot()は、exampleパッケージに含まれているとする")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" example\n\nrock_paper_scissors "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("rock_paper_scissors\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_rock_paper_scissors")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# あいこのテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 勝利のテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 敗北のテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"fastapiについて"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#fastapiについて"}},[s._v("#")]),s._v(" FastAPIについて")]),s._v(" "),t("p",[s._v("IIJ Bootcamp「FastAPI でwebアプリを作る」にて紹介されているため、詳細の説明は省きます。")]),s._v(" "),t("p",[s._v("下記「テスト実装例」にサンプルを記載するように、簡単にAPIを実装できるフレームワークになっています。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-3"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("FastAPIは、下記のようにAPIを実装できます。"),t("br"),s._v("\n下記は、ブラウザで"),t("code",[s._v("http://localhost:8000/hello")]),s._v("にアクセスすると、データ"),t("code",[s._v('{"response": "hello"}')]),s._v("を返却します。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" fastapi "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FastAPI\n\napp "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FastAPI"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[s._v("@app"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("async")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get_hello")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"response"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記のAPIに対し、HTTPステータスやレスポンスを検証するテストは、下記のように書くことができます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_api")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# パス"/hello"に接続する')]),s._v("\n res "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" client"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("get"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# HTTPステータスと、レスポンスの取得")]),s._v("\n status "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("status_code\n data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# HTTPステータスと、レスポンスの検証")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("data"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"response"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう-2"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise2/challenge.py")]),s._v("に、FastAPIと、いくつかのエンドポイントが定義されています。")]),s._v(" "),t("p",[s._v("上記のAPIは、コンテナから下記のコマンドで実行することができます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python3 -m uvicorn exercises.exercise2.challenge:app --reload --host "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("API実行後は、ブラウザに下記のURLを入力すると、APIにアクセスできます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("http://localhost:8000/\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("また、APIは下記のエンドポイントがあります。")]),s._v(" "),t("table",[t("thead",[t("tr",[t("th",[s._v("パス")]),s._v(" "),t("th",[s._v("詳細")])])]),s._v(" "),t("tbody",[t("tr",[t("td",[s._v("/")]),s._v(" "),t("td",[t("code",[s._v('{"message": "hello world"}')]),s._v("が返却されます。")])]),s._v(" "),t("tr",[t("td",[s._v("/echo/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"message": "got the message: {data}"}')]),s._v("が返却されます。"),t("br"),s._v("※"),t("code",[s._v("{data}")]),s._v("は、任意の値が代入されます。")])]),s._v(" "),t("tr",[t("td",[s._v("/gacha")]),s._v(" "),t("td",[t("code",[s._v('{"message": "{result}"')]),s._v("が返却されます。"),t("br"),s._v("※"),t("code",[s._v("{result}")]),s._v("は、 "),t("em",[s._v("1/100")]),s._v(' で文字列"you win"、それ以外で文字列"you lose"が代入されます。')])])])]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise2/test_challenge.py")]),s._v("に、作成途中のテストクラス"),t("code",[s._v("ApiTestCase")]),s._v("が定義されているため、上記の仕様のAPIに対するテストを作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"_3-tddをやってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-tddをやってみる"}},[s._v("#")]),s._v(" 3. TDDをやってみる")]),s._v(" "),t("p",[s._v("TDDとは、「テスト駆動開発( "),t("em",[s._v("Test-Driven")]),s._v(" )」のことを指し「テストファースト(テスト優先)」を掲げて開発を行う、 "),t("strong",[s._v("開発手法")]),s._v(" のことになります。")]),s._v(" "),t("p",[s._v("テスト手法じゃないよ !!!!")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"tddのやり方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#tddのやり方"}},[s._v("#")]),s._v(" TDDのやり方")]),s._v(" "),t("p",[s._v("TDDは、任意の開発を行う設計があるうえで、下記のサイクルで開発を行っていきます。")]),s._v(" "),t("ol",[t("li",[s._v("Red\n"),t("ul",[t("li",[s._v("動作をしないテストを書く。")])])]),s._v(" "),t("li",[s._v("Green\n"),t("ul",[t("li",[s._v("迅速に、テストを実行できるコードを書いてテストを通すようにする。"),t("br"),s._v("\n※コードが汚くても良い。")])])]),s._v(" "),t("li",[s._v("Refactoring\n"),t("ul",[t("li",[s._v("リファクタリングを行い、コード内から重複を削除する。")])])])]),s._v(" "),t("p",[s._v("上記 "),t("em",[s._v("1~3")]),s._v(" のサイクルを実行し、動きつつコードとリファクタリングによって最適化されたコードを、着実に作っていく手法になります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-4"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("この項で実際に、TDDのサイクルを見てみましょう。")]),s._v(" "),t("p",[s._v("例えば、以下の仕様のソースを作りたいとします。")]),s._v(" "),t("ul",[t("li",[s._v("クラス内の"),t("code",[s._v("do()")]),s._v('関数の、実行回数が3の倍数なら"Fizz"、5の倍数なら"Buzz"を返す、クラス'),t("code",[s._v("FizzBuzz")]),s._v("を実装します。")]),s._v(" "),t("li",[s._v("このクラスは"),t("code",[s._v("do()")]),s._v("の実行回数を、内部でカウントします。")]),s._v(" "),t("li",[s._v("3でも5の倍数でもないカウントに対しては、そのカウントを返します。")])]),s._v(" "),t("p",[s._v("上記のコードをTDDで作成していきましょう。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-red"}},[s._v("#")]),s._v(" サイクル1 Red")]),s._v(" "),t("p",[s._v("まずは、クラス"),t("code",[s._v("FizzBuzz")]),s._v("の関数を作成します。"),t("br"),s._v("\n下記のコードは、"),t("code",[s._v("example.py")]),s._v("のようなパッケージにあると考えてください。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("pass")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("次に、動作をしないテストを書きましょう。"),t("br"),s._v(" "),t("code",[s._v("do()")]),s._v("が最初に実行する時は、下記の仕様が適用されます。")]),s._v(" "),t("blockquote",[t("p",[s._v("3でも5の倍数でもないカウントに対しては、そのカウントを返します。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("とりあえず1回目の実行では「1」が返ってくるはずなので、テストでは「1」を期待してみます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("fizzbuzz "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FizzBuzz\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n fb "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FizzBuzz"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("これを実行すると、当然のようにコケますね。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-green"}},[s._v("#")]),s._v(" サイクル1 Green")]),s._v(" "),t("p",[s._v("次は「とりあえず動くコードを書く」ことをします。"),t("br"),s._v("\nテストでは1が返却されることを期待しているので、1を返しましょう。"),t("br"),s._v("\nTDDって簡単ですね。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("動いたよ!!!!やったね!!!!!!!!!")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-refactoring"}},[s._v("#")]),s._v(" サイクル1 Refactoring")]),s._v(" "),t("p",[s._v("現在の"),t("code",[s._v("FizzBuzz")]),s._v("は、この世界に存在するどんなものよりも洗練されているため、リファクタリングは必要ないですね。"),t("br"),s._v("\n素晴らしい。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-red"}},[s._v("#")]),s._v(" サイクル2 Red")]),s._v(" "),t("p",[s._v("2サイクル目に来ました。"),t("br"),s._v("\nRedでは、あえてテストを失敗させなければならないため、泣く泣く完成されたテストコードに手を加えましょう。")]),s._v(" "),t("p",[s._v("サイクル1 Redでも確認した通り、どうやら"),t("code",[s._v("do()")]),s._v("を実行するごとに実行回数を返してくれるそうです。"),t("br"),s._v("\n実行回数毎に、期待する値を増加させてみましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("fizzbuzz "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FizzBuzz\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n fb "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FizzBuzz"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("4")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("6")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("7")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("11")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("12")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("13")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("14")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br")])]),t("br"),s._v(" "),t("p",[t("code",[s._v("FizzBuzz")]),s._v("は既に完成されているため、テストをいくら加えようが失敗するはずがないのですが、試してみましょう。"),t("br"),s._v("\nまあ、やる意味はないと思うのですが(笑)")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-green"}},[s._v("#")]),s._v(" サイクル2 Green")]),s._v(" "),t("p",[s._v("なんということでしょうか。"),t("br"),s._v("\n完成されていたと思われた"),t("code",[s._v("do()")]),s._v("は、何回実行しても「1」しか返してくれないではないですか。")]),s._v(" "),t("p",[s._v("誰ですか、こんな実装にしたのは(怒)")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("global")]),s._v(" count\n count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("だいぶ雑なコードですが、たぶん動くと思うからテストしましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-refactoring"}},[s._v("#")]),s._v(" サイクル2 Refactoring")]),s._v(" "),t("p",[s._v("現在の"),t("code",[s._v("FizzBuzz")]),s._v("はグローバル変数が使用されているなど、あまり美しくありません。"),t("br"),s._v("\nクラスで値を持たせて、インスタンス毎に値を共有させないようにしましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("コードを変更しましたが、テストの結果が成功のままであることを確認します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-red"}},[s._v("#")]),s._v(" サイクル3 Red")]),s._v(" "),t("p",[s._v("さて、ここまでで以下の仕様を実装することができました。")]),s._v(" "),t("ul",[t("li",[s._v("内部でカウントを保持する。")]),s._v(" "),t("li",[s._v("3でも5でもないカウントは、その値を返す。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v('次は「3の倍数なら"Fizz"を返す」を実装してみましょう。')]),s._v(" "),t("p",[s._v("失敗するテストを書きます。\nテスト全体を書くと文字量が多くなりますので、以降は差分で表記します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('- self.assertEqual(fb.do(), 3)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 6)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 9)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 12)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 15)\n+ self.assertEqual(fb.do(), "Fizz")\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストは失敗しますね。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-green"}},[s._v("#")]),s._v(" サイクル3 Green")]),s._v(" "),t("p",[s._v('さて、"Fizz"を返せるように'),t("code",[s._v("FizzBuzz")]),s._v("を修正しましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストはオールグリーンです。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-refactoring"}},[s._v("#")]),s._v(" サイクル3 Refactoring")]),s._v(" "),t("p",[s._v("特にリファクタリング箇所がないので省きます。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-red"}},[s._v("#")]),s._v(" サイクル4 Red")]),s._v(" "),t("p",[s._v("最後の仕様になります。"),t("br"),s._v('\n最後は「5の倍数なら"Buzz"を返す」を実装します。')]),s._v(" "),t("p",[s._v('ただし、3かつ5の倍数であれば"FizzBuzz"が返ることに注意してください。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('- self.assertEqual(fb.do(), 5)\n+ self.assertEqual(fb.do(), "Buzz")\n\n- self.assertEqual(fb.do(), 10)\n+ self.assertEqual(fb.do(), "Buzz")\n\n- self.assertEqual(fb.do(), 15)\n+ self.assertEqual(fb.do(), "FizzBuzz")\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("「手がかかる子ほど可愛い」というのは、このことを言うのでしょうか。"),t("br"),s._v("\nだんだんコンソールに出力される「FAIL」が愛おしく思えてきました。")]),s._v(" "),t("p",[s._v("きっと「失敗の後は必ず成功する」ということが約束されているからでしょう。")]),s._v(" "),t("p",[s._v("みなさんも、そう思いませんか?")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-green"}},[s._v("#")]),s._v(" サイクル4 Green")]),s._v(" "),t("p",[s._v('"Buzz"および"FizzBuzz"を返せるようにしましょう。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"FizzBuzz"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Buzz"')]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストも通ります。"),t("br"),s._v("\nやったか!?(まだ終わりじゃないです。)")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-refactoring"}},[s._v("#")]),s._v(" サイクル4 Refactoring")]),s._v(" "),t("p",[s._v("最後のリファクタリングになります。"),t("br"),s._v("\nこれで全てを終わらせて、あなたは次のステージへ進むことになるでしょう。")]),s._v(" "),t("p",[s._v("先程書いた"),t("code",[s._v("FizzBuzz")]),s._v("では、まだ手続きを共通化し、最適にできる部分があります。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#####################################")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# このコードは一例です。")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# みんなが読みやすいコードを書こうね!")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#####################################")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("_divided")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" div"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("int")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("not")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" div"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Buzz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"FizzBuzz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n index "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_divided"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<<")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" \\\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_divided"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("index"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("多分これが一番美しいと思います。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう-3"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise3/challenge.py")]),s._v("には、FastAPIで書かれた作りかけのAPIがあります。")]),s._v(" "),t("p",[s._v("上記のAPIは、コンテナから下記のコマンドで実行することができます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python3 -m uvicorn exercises.exercise3.challenge:app --reload --host "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("API実行後は、ブラウザに下記のURLを入力すると、APIにアクセスできます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("http://localhost:8000/\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("みなさんには、TDDを使って上記のAPIを完成させてもらいます。"),t("br"),s._v("\n作ってもらいたいAPIの仕様は、下記のものになります。")]),s._v(" "),t("ul",[t("li",[s._v("このAPIは、"),t("code",[s._v("/, /add, /sub, /mul, /div")]),s._v("の5つのエンドポイントがあります。")]),s._v(" "),t("li",[s._v("このAPIはサーバ内部でint型の値を保持し、現在設定されている値を"),t("code",[s._v("/")]),s._v("にアクセスすることで、確認することができます。\nまた、値は0始まりになります。")]),s._v(" "),t("li",[t("code",[s._v("/add, /sub, /mul, /div")]),s._v("にパスパラメータを与えると、保持されている値に対し、四則演算を行います(後述)。")]),s._v(" "),t("li",[s._v("本APIでは、全てint型で計算を行います。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("以下、APIのパスについて")]),s._v(" "),t("table",[t("thead",[t("tr",[t("th",[s._v("パス")]),s._v(" "),t("th",[s._v("詳細")])])]),s._v(" "),t("tbody",[t("tr",[t("td",[s._v("/")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{数値}には、サーバで保持されている値が入ります。")])]),s._v(" "),t("tr",[t("td",[s._v("/add/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値に加算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/sub/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値から減算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/mul/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値に乗算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/div/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値から除算します。")])])])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("サーバでの値の保持・取得関数は、ソース内に定義されています。"),t("br"),s._v("\n以下に、使い方の例を記載します。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバ内に保持されている値を記録します。")]),s._v("\nset_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバ内に保持されている値を取得します")]),s._v("\ngot_data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" get_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# got_data = 1")]),s._v("\n\nset_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("123")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("456")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\ngot_data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" get_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# got_data = 579")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise3/test_challenge.py")]),s._v("には、本APIが完成すると通るようになる、テスト"),t("code",[s._v("test_success()")]),s._v("が定義されています。")]),s._v(" "),t("p",[s._v("上記のテストがOKになるよう、各種APIをTDDを使って作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"おわりに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#おわりに"}},[s._v("#")]),s._v(" おわりに")]),s._v(" "),t("p",[s._v("一般的にソフトウェアテストというと、専門のテスト部隊があって「Excelにスクショをペタペタ貼るだけでしょ?」というようなイメージを持ち、敬遠される方も少なくはないと思います。")]),s._v(" "),t("p",[s._v("開発者がテストについて知識を持ち、単体テストで可能な限りの不具合をなくしておくと、後の工程で不具合が少なく済ますことができたり、メリットがあります。"),t("br"),s._v("\nまた、後の工程で発生した不具合の内容を聞いた・見ただけで、どのモジュール同士で問題が起こっているのか目星がつくなど、効率的なトラブルシュートやソフトウェアの理解にも繋がります。")]),s._v(" "),t("p",[s._v("冒頭でも述べましたが、ソフトウェアにも品質というものがあり、この品質次第で会社の売上に影響が出たり、企業のセキュリティや人命に影響を及ぼしてしまう懸念もあります。")]),s._v(" "),t("p",[s._v("そのため。開発を行う際には是非テストにも注力をしていただき、ユーザーの満足できるソフトウェアを作れるよう、目指してみてください。")]),s._v(" "),t("p",[s._v("良いエンジニアライフを!👍")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{487:function(s,t,a){s.exports=a.p+"assets/img/figure1.959b588c.png"},557:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h1",{attrs:{id:"目次"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#目次"}},[s._v("#")]),s._v(" 目次")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB"}},[s._v("はじめに")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E6%A6%82%E8%AB%96"}},[s._v("概論")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E3%81%AA%E3%81%9C%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E8%A1%8C%E3%81%86%E3%81%AE%E3%81%8B"}},[s._v("なぜテストを行うのか")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E5%8A%B9%E7%8E%87%E7%9A%84%E3%81%AA%E3%83%86%E3%82%B9%E3%83%88%E3%81%A8%E3%81%AF"}},[s._v("効率的なテストとは")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%81%84%E3%81%A4%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E3%82%8B%E3%81%AE%E3%81%8B"}},[s._v("いつテストを作るのか")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E6%BA%96%E5%82%99"}},[s._v("準備")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E5%AE%9F%E8%A1%8C%E6%96%B9%E6%B3%95"}},[s._v("テストの実行")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E9%96%A2%E6%95%B0%E3%83%BB%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E4%BF%AE%E6%AD%A3%E6%96%B9%E6%B3%95"}},[s._v("関数・テストの修正方法")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B"}},[s._v("テストを実行する")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#%E5%90%8C%E5%80%A4%E3%82%AF%E3%83%A9%E3%82%B9%E3%83%BB%E5%A2%83%E7%95%8C%E5%80%A4%E3%83%86%E3%82%B9%E3%83%88"}},[s._v("同値クラス・境界値テスト")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#api%E3%81%A8%E9%96%A2%E6%95%B0%E3%81%AE%E3%83%A2%E3%83%83%E3%82%AF"}},[s._v("APIと関数のモック")])]),s._v(" "),t("li",[t("a",{attrs:{href:"#tdd%E3%82%92%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B"}},[s._v("TDDをやってみる")])])])]),s._v(" "),t("li",[t("a",{attrs:{href:"#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB"}},[s._v("おわりに")])])]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[s._v("#")]),s._v(" はじめに")]),s._v(" "),t("p",[s._v("本講義はdockerを使用します。"),t("br"),s._v("\ndockerコンテナのpullには時間を要するため、概論の聴講と並行して「準備 ⇒ "),t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")]),s._v("」を実施することを推奨します。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"概論"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#概論"}},[s._v("#")]),s._v(" 概論")]),s._v(" "),t("h2",{attrs:{id:"なぜテストを行うのか"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#なぜテストを行うのか"}},[s._v("#")]),s._v(" なぜテストを行うのか")]),s._v(" "),t("p",[s._v("昨今ではIT技術が普及し、炊飯器・電子レンジ・洗濯機といった身の回りのものから、航空機や車など、普段の生活に必須になるものにまで。ソフトウェアが使用されています。"),t("br"),s._v("\nまた、世の中に流通しているソフトウェアはテストが実施されており、その挙動で問題が起こらないことを保証されています。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えばソフトウェアに対し、テストを行われていないと仮定して、個人的に運営しているブログなどで不具合が発生した場合はどうなるでしょうか。"),t("br"),s._v("\nその不具合によってサーバがダウンしている間は、運営者に広告費が入らないなど、ある程度小規模で済みます。"),t("br"),s._v("\n(ブログ収益で生計を立てている場合、致命的ですが。)")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば自動車や医療機器などで不具合が発生してしまった場合、どうなるでしょうか。"),t("br"),s._v("\n最悪の場合、ブレーキが効かない、医療機器のレーザーの出力が多すぎたなど、ソフトウェアの欠陥によって人命が失われてしまう可能性もあります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("上記2つの例を上げましたが、大なり小なり、ソフトウェアの不具合によって、どこかの誰かが被害を被ってしまいます。"),t("br"),s._v("\nそのため、自身が作成するプログラムでは必ず動作のテストを行い、極力不具合を発生させないソフトウェアを作ることを目指す必要があります。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"効率的なテストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#効率的なテストとは"}},[s._v("#")]),s._v(" 効率的なテストとは")]),s._v(" "),t("p",[s._v("テストを作成する場合には、不具合をなくすことも重要ですが、テストにコストをかけないことも重要になります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば、あるプロダクトに使用される、以下のような仕様の関数"),t("code",[s._v("f(x)")]),s._v("があるとします。")]),s._v(" "),t("ul",[t("li",[s._v("関数"),t("code",[s._v("f")]),s._v("は、任意の数字"),t("code",[s._v("x")]),s._v("の値を取ります。")]),s._v(" "),t("li",[s._v("任意の数字"),t("code",[s._v("x")]),s._v("は、int型であり、 "),t("em",[s._v("-2,147,483,648")]),s._v(" から "),t("em",[s._v("2,147,483,647")]),s._v(" の範囲の値を格納できます。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("f")]),s._v("は、与えられた数字が "),t("em",[s._v("0")]),s._v(" から "),t("em",[s._v("100")]),s._v(" の間であれば"),t("code",[s._v("True")]),s._v("、そうでなければ"),t("code",[s._v("False")]),s._v("を返却します。")])]),s._v(" "),t("p",[s._v("上記の"),t("code",[s._v("f(x)")]),s._v("の挙動を100%確かめるためには、 "),t("em",[s._v("4,294,967,296")]),s._v(" 件のテストを行わなければなりません。"),t("br"),s._v("\nしかし、実際のプロダクトを作成する場合、1つの関数に対し40億回もテストを実施してしまうと、プロダクトの売上以上に人件費や計算機の運用コストがかかってしまい、会社は倒産の危機に瀕してしまいます。")]),s._v(" "),t("p",[s._v("そのため、後述する「同値クラス・境界値テスト」などの手法によって、最低限かつ最適な回数でテストを行うことが求められます。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"いつテストを作るのか"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#いつテストを作るのか"}},[s._v("#")]),s._v(" いつテストを作るのか")]),s._v(" "),t("p",[s._v("開発を行う際、ウォーターフォール型の開発では、下記の流れになるかと思います。"),t("br"),s._v("\n右下向きの矢印が設計工程、中央が開発工程、右上向きの矢印がテスト工程になります。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(487),alt:"figure1.png",title:"figure1"}})]),s._v(" "),t("p",[s._v("設計における各要素は、テスト工程の各要素に対応することになります。"),t("br"),s._v("\n例えば、まずは要求定義を行い、ソフトウェアに必要な要件を決めますが、この時点で明確な要件が定義できているのであれば、システムテストで実施するテスト項目を作成しておくことができます。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("このように各設計段階でテスト項目を作成することで、要件に沿ったテストを作成することができます。"),t("br"),s._v("\n(何年も開発を行っているとわかるのですが、ものを作ってからテストを作成すると、「今動くものを通すテスト」を無意識的に書いてしまい、要件も網羅できないテストを作ってしまう傾向が出てきます。)")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("また、後述するTDD(テスト駆動開発)のように、テストを作成して開発を進める手法もあります。"),t("br"),s._v("\n今回のハンズオンではコーディング・単体テスト段階で実施するテストプログラミングをやってみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#準備"}},[s._v("#")]),s._v(" 準備")]),s._v(" "),t("h2",{attrs:{id:"dockerコンテナの立ち上げ方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの立ち上げ方"}},[s._v("#")]),s._v(" dockerコンテナの立ち上げ方")]),s._v(" "),t("p",[s._v("下記のコマンドでdockerコンテナを立ち上げます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# リポジトリのクローン")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" clone git@github.com:iij/bootcamp.git\n$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" bootcamp/src/server-app/test-hands-on\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# コンテナの立ち上げ")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker-compose")]),s._v(" up --build\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("br"),s._v(" "),t("h2",{attrs:{id:"テストの実行方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テストの実行方法"}},[s._v("#")]),s._v(" テストの実行方法")]),s._v(" "),t("p",[s._v("この項では、任意の「"),t("a",{attrs:{href:"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B"}},[s._v("テストを実行する")]),s._v("」の項のテストを実行します。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("「"),t("a",{attrs:{href:"#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E6%96%B9"}},[s._v("dockerコンテナの立ち上げ方")]),s._v("」で、起動中のコンソールとは別のコンソールを開き、実行中のコンテナにアクセスします。"),t("br"),s._v("\nコマンドを実行すると、コンテナ内のbashが実行されます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" bootcamp/src/server-app/test-hands-on\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker-compose")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" bootcamp-test "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("bash")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("下記のコマンドで、テストを実行してみましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# ソースは全て"/test-hands-on"配下にあります。')]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" /test-hands-on\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 任意のテストを実行します。")]),s._v("\n$ python -m unittest -v exercises.exercise0.test_challenge\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("br"),s._v(" "),t("h2",{attrs:{id:"関数・テストの修正方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#関数・テストの修正方法"}},[s._v("#")]),s._v(" 関数・テストの修正方法")]),s._v(" "),t("p",[s._v("「テストの実行方法」の項でテストを行うと、初回は下記のようにテストが失敗してしまいます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v exercises.exercise0.test_challenge\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v("\nFAIL: test_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n----------------------------------------------------------------------\nTraceback "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("most recent call last"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n File "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/test-hands-on/exercises/exercise0/test_challenge.py"')]),s._v(", line "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("7")]),s._v(", "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" test_success\n self.assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nAssertionError: "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),s._v("\n- hello world\n+ goodbye world?\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("試しに、このテストを修正してみましょう。\nテストソースである、 "),t("code",[s._v("/test-hands-on/exercises/exercise0/test_challenge.py")]),s._v(" を開いてみます。")]),s._v(" "),t("p",[s._v("内容は下記のようになっており、ソース内でimportしている "),t("code",[s._v("hello()")]),s._v(' 関数に対し、文字列"goodbye world?"が来ることを期待してテストを行っているようです。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("challenge "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" hello\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"goodbye world?"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("では、テスト対象である "),t("code",[s._v("hello()")]),s._v(" 関数を見てみましょう。"),t("br"),s._v('\nどうやら、この関数は文字列"hello world"を返すようです。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("hello")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("しかし、これではテストソースで期待されている関数の返り値と、実際の関数の返り値が異なってしまっています。"),t("br"),s._v('\nこれがテストが失敗してしまう原因となるため、テストの期待する値を"goodbye world?"から"hello world"に変えてみましょう。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("challenge "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" hello\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("HelloTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("hello"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello world"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("p",[s._v("このテストを実行してみると、先程まで失敗していたテストが成功しました。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("python -m unittest -v exercises.exercise0.test_challenge\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("exercises.exercise0.test_challenge.HelloTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n\n----------------------------------------------------------------------\nRan "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("test")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(".000s\n\nOK\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("このように、テストソースというものは、テストを実施したい関数に対して動作を確認するように作成・実行します。")]),s._v(" "),t("p",[s._v("本講義では、テストを実施したい関数に対し、テストソースで期待する返り値を設定し、関数の動作確認を行っていきます。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("ちなみに、ローカルのソースファイルの変更は、コンテナ内にも自動で同期されます。"),t("br"),s._v("\n以降はローカルでファイルを変更し、コンテナ内でテストを実行してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"テストを実行する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テストを実行する"}},[s._v("#")]),s._v(" テストを実行する")]),s._v(" "),t("h2",{attrs:{id:"_1-同値クラス・境界値テスト"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-同値クラス・境界値テスト"}},[s._v("#")]),s._v(" 1. 同値クラス・境界値テスト")]),s._v(" "),t("p",[s._v("この項では「同値クラステスト」と「境界値テスト」という手法のテストを実施し、効率的なテストについて学びます。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"同値クラステストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#同値クラステストとは"}},[s._v("#")]),s._v(" 同値クラステストとは")]),s._v(" "),t("p",[s._v("同値クラステストとは「任意の関数"),t("code",[s._v("g(x)")]),s._v("の引数"),t("code",[s._v("x")]),s._v("に対し、有効である値、無効である値のグループ(有効同値クラス、無効同値クラス)を定義してテストを実施する」ものになります。")]),s._v(" "),t("p",[s._v("例えば、本書の冒頭で出てきた、関数"),t("code",[s._v("f(x)")]),s._v("では、"),t("code",[s._v("x")]),s._v("の値が "),t("em",[s._v("0")]),s._v(" から "),t("em",[s._v("100")]),s._v(" の間であれば有効同値クラス、そうでなければ無効同値クラス、と定義できます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("関数fは、任意の数字xの値を取ります。\n任意の数字xは、int型であり、 -2,147,483,648 から 2,147,483,647 の範囲の値を格納できます。\n関数fは、与えられた数字が 0 から 100 の間であればTrue、そうでなければFalseを返却します。\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("仮に「有効同値クラス内の値が入力された場合は正常終了、無効同値クラス内の値が入力された場合は異常終了する」と見た場合、終了の仕方は「正常終了か異常終了か」の2択と見ることができます。")]),s._v(" "),t("p",[s._v("すなわち、関数"),t("code",[s._v("f(x)")]),s._v("に対する同値クラステストとは、有効同値である "),t("em",[s._v("10")]),s._v(" , "),t("em",[s._v("50")]),s._v(" , "),t("em",[s._v("90")]),s._v(" など、いくつかの値のグループと、無効同値である "),t("em",[s._v("-500")]),s._v(" , "),t("em",[s._v("-10")]),s._v(" , "),t("em",[s._v("110")]),s._v(" , "),t("em",[s._v("500")]),s._v(" などの値のグループのテストを実施すればよいことになります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"境界値テストとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#境界値テストとは"}},[s._v("#")]),s._v(" 境界値テストとは")]),s._v(" "),t("p",[s._v("同値クラステストでは「有効/無効と定義した値に対する処理が正しく動くか」を確認できました。")]),s._v(" "),t("p",[s._v("しかし、これでは「有効/無効の範囲は正しいか」が確認できていません。"),t("br"),s._v("\nこういった場合は境界値テストを実施し、有効値/無効値の境界が、正しく実行されるかのテストを行います。")]),s._v(" "),t("p",[s._v("本書冒頭の関数"),t("code",[s._v("f(x)")]),s._v("を例にすると、下限の境界値は "),t("em",[s._v("-1")]),s._v(" , "),t("em",[s._v("0")]),s._v(" 、上限の境界値は "),t("em",[s._v("100")]),s._v(" , "),t("em",[s._v("101")]),s._v(" となります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("本書冒頭で定義した、関数"),t("code",[s._v("f(x)")]),s._v("がPythonで以下のように定義されているとします。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<=")]),s._v(" x "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("else")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記の関数に対し、同値クラスのテストを定義すると、下記のように書くことができます。"),t("br"),s._v("\n下記のテストでは、関数"),t("code",[s._v("f(x)")]),s._v("に有効同値クラスの値を入力すると"),t("code",[s._v("True")]),s._v("、そうでない値を入力すると"),t("code",[s._v("False")]),s._v("が返却されることを確認しています。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_equivalence_partitioning")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 有効同値のテスト")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("50")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("90")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 無効同値のテスト")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("500")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("110")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("500")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("境界値テストを定義すると、下記のように書くことができます。"),t("br"),s._v("\n下記のテストでは、関数"),t("code",[s._v("f(x)")]),s._v("に下限の境界値 "),t("em",[s._v("-1")]),s._v(" , "),t("em",[s._v("0")]),s._v(" 、上限の境界値 "),t("em",[s._v("100")]),s._v(" , "),t("em",[s._v("101")]),s._v(" を入力し、適宜"),t("code",[s._v("True")]),s._v("か"),t("code",[s._v("False")]),s._v("が返却されることを確認しています。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_equivalence_partitioning")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 下限の境界値")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 上限の境界値")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("100")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("True")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("f"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("101")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("False")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise1/challenge.py")]),s._v("に、商品の申し込みを行う関数"),t("code",[s._v("apply(quantity)")]),s._v("が定義されています。")]),s._v(" "),t("p",[s._v("関数は以下の仕様になっています。")]),s._v(" "),t("ul",[t("li",[s._v("この関数は、int型の引数"),t("code",[s._v("quantity")]),s._v("を取ります。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("apply()")]),s._v("は、10以上、100以下の値が入力されると、申し込みが成功し、文字列"),t("code",[s._v('"accepted"')]),s._v("が返却されます。")]),s._v(" "),t("li",[s._v("申し込みに失敗した場合は、文字列"),t("code",[s._v('"not accepted"')]),s._v("が返却されます。")]),s._v(" "),t("li",[s._v("int型以外のデータが入力された場合、例外"),t("code",[s._v("TypeError()")]),s._v("が発生し、プログラムが異常終了します。")])]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise1/test_challenge.py")]),s._v("に、作成途中のテストクラス"),t("code",[s._v("ApplyTestCase")]),s._v("が定義されているため、関数"),t("code",[s._v("apply(quantity)")]),s._v("に対するテストを作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"_2-apiと関数のモック"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-apiと関数のモック"}},[s._v("#")]),s._v(" 2. APIと関数のモック")]),s._v(" "),t("p",[s._v("この項では、Pythonで実行できるAPI(FastAPI)のフレームワークを使用し、APIに対するテストや、関数のモックに触れてみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"モックとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#モックとは"}},[s._v("#")]),s._v(" モックとは")]),s._v(" "),t("p",[s._v("「モックアップ」の略称であり、工業製品などの試作や、店頭展示などのためにつくられる実物大模型のことを指します。"),t("br"),s._v("\n「"),t("a",{attrs:{href:"https://dictionary.goo.ne.jp/word/%e3%83%a2%e3%83%83%e3%82%af%e3%82%a2%e3%83%83%e3%83%97/",target:"_blank",rel:"noopener noreferrer"}},[s._v("goo辞書 モックアップ(mock-up)"),t("OutboundLink")],1),s._v("」より")]),s._v(" "),t("p",[s._v("テストにおけるモックとは、主にクラスや関数の動作をシミュレートするためのオブジェクトになります。")]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("例えば、以下のような仕様の関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("があるとします。")]),s._v(" "),t("ul",[t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("は、じゃんけんを行う関数で、引数"),t("code",[s._v("shoot")]),s._v('は文字列"rock", "paper", "scissors"の、いずれかを取ります。')]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("は、内部で引数に対してじゃんけんの手を出す関数"),t("code",[s._v("my_shoot()")]),s._v("が実行されます。")]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("my_shoot()")]),s._v("は、それぞれ "),t("em",[s._v("1/3")]),s._v(' の確率で"rock", "paper", "scissors"のいずれかを取得します。')]),s._v(" "),t("li",[s._v("関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("は、入力された引数"),t("code",[s._v("shoot")]),s._v("が、関数"),t("code",[s._v("my_shoot()")]),s._v("の返り値に勝利できる場合 "),t("em",[s._v("1")]),s._v(" 、引き分けであれば "),t("em",[s._v("0")]),s._v(" 、敗北であれば "),t("em",[s._v("-1")]),s._v(" を返します。")])]),s._v(" "),t("p",[s._v("上記の関数"),t("code",[s._v("rock_paper_scissors()")]),s._v("をテストする場合、内部の関数の返り値が乱数で決定されてしまうため、通常であればテストが実行できません。"),t("br"),s._v("\n(例えば、1回目の"),t("code",[s._v("my_shoot()")]),s._v('を実行した時に"rock"が返却されたとしても、2回目も"rock"が返却されるとは限らないですよね)')]),s._v(" "),t("p",[s._v("こういった場合、関数のモックを使用して、テスト対象の関数内で使用されているクラスや関数をモックし、返り値を固定してシミュレーションを行う必要があります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-2"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("関数"),t("code",[s._v("rock_paper_scissors(shoot)")]),s._v("が、Pythonで以下のように定義されているとします。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("rock_paper_scissors")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("shoot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# 1/3で"rock", "paper", "scissors"が格納される')]),s._v("\n my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" my_shoot"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# あいこ")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" my_shoot_result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 勝利")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" shoot "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("and")]),s._v(" my_shoot_result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 敗北")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記の関数に対し、モックを使用したテストを定義すると、下記のように書くことができます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" unittest "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" mock\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 関数rock_paper_scissors(), my_shoot()は、exampleパッケージに含まれているとする")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" example\n\nrock_paper_scissors "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("rock_paper_scissors\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_rock_paper_scissors")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# あいこのテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 勝利のテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"scissors"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 敗北のテスト")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("with")]),s._v(" mock"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("patch"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("object")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'my_shoot'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" return_value"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"paper"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("rock_paper_scissors"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"rock"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"fastapiについて"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#fastapiについて"}},[s._v("#")]),s._v(" FastAPIについて")]),s._v(" "),t("p",[s._v("IIJ Bootcamp「FastAPI でwebアプリを作る」にて紹介されているため、詳細の説明は省きます。")]),s._v(" "),t("p",[s._v("下記「テスト実装例」にサンプルを記載するように、簡単にAPIを実装できるフレームワークになっています。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-3"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("FastAPIは、下記のようにAPIを実装できます。"),t("br"),s._v("\n下記は、ブラウザで"),t("code",[s._v("http://localhost:8000/hello")]),s._v("にアクセスすると、データ"),t("code",[s._v('{"response": "hello"}')]),s._v("を返却します。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" fastapi "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FastAPI\n\napp "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FastAPI"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[s._v("@app"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("get")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("async")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("get_hello")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"response"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("上記のAPIに対し、HTTPステータスやレスポンスを検証するテストは、下記のように書くことができます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_api")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v('# パス"/hello"に接続する')]),s._v("\n res "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" client"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("get"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# HTTPステータスと、レスポンスの取得")]),s._v("\n status "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("status_code\n data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" res"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# HTTPステータスと、レスポンスの検証")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("status"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("data"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"response"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"hello"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう-2"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise2/challenge.py")]),s._v("に、FastAPIと、いくつかのエンドポイントが定義されています。")]),s._v(" "),t("p",[s._v("上記のAPIは、コンテナから下記のコマンドで実行することができます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python3 -m uvicorn exercises.exercise2.challenge:app --reload --host "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("API実行後は、ブラウザに下記のURLを入力すると、APIにアクセスできます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("http://localhost:8000/\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("また、APIは下記のエンドポイントがあります。")]),s._v(" "),t("table",[t("thead",[t("tr",[t("th",[s._v("パス")]),s._v(" "),t("th",[s._v("詳細")])])]),s._v(" "),t("tbody",[t("tr",[t("td",[s._v("/")]),s._v(" "),t("td",[t("code",[s._v('{"message": "hello world"}')]),s._v("が返却されます。")])]),s._v(" "),t("tr",[t("td",[s._v("/echo/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"message": "got the message: {data}"}')]),s._v("が返却されます。"),t("br"),s._v("※"),t("code",[s._v("{data}")]),s._v("は、任意の値が代入されます。")])]),s._v(" "),t("tr",[t("td",[s._v("/gacha")]),s._v(" "),t("td",[t("code",[s._v('{"message": "{result}"')]),s._v("が返却されます。"),t("br"),s._v("※"),t("code",[s._v("{result}")]),s._v("は、 "),t("em",[s._v("1/100")]),s._v(' で文字列"you win"、それ以外で文字列"you lose"が代入されます。')])])])]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise2/test_challenge.py")]),s._v("に、作成途中のテストクラス"),t("code",[s._v("ApiTestCase")]),s._v("が定義されているため、上記の仕様のAPIに対するテストを作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h2",{attrs:{id:"_3-tddをやってみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-tddをやってみる"}},[s._v("#")]),s._v(" 3. TDDをやってみる")]),s._v(" "),t("p",[s._v("TDDとは、「テスト駆動開発( "),t("em",[s._v("Test-Driven")]),s._v(" )」のことを指し「テストファースト(テスト優先)」を掲げて開発を行う、 "),t("strong",[s._v("開発手法")]),s._v(" のことになります。")]),s._v(" "),t("p",[s._v("テスト手法じゃないよ !!!!")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"tddのやり方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#tddのやり方"}},[s._v("#")]),s._v(" TDDのやり方")]),s._v(" "),t("p",[s._v("TDDは、任意の開発を行う設計があるうえで、下記のサイクルで開発を行っていきます。")]),s._v(" "),t("ol",[t("li",[s._v("Red\n"),t("ul",[t("li",[s._v("動作をしないテストを書く。")])])]),s._v(" "),t("li",[s._v("Green\n"),t("ul",[t("li",[s._v("迅速に、テストを実行できるコードを書いてテストを通すようにする。"),t("br"),s._v("\n※コードが汚くても良い。")])])]),s._v(" "),t("li",[s._v("Refactoring\n"),t("ul",[t("li",[s._v("リファクタリングを行い、コード内から重複を削除する。")])])])]),s._v(" "),t("p",[s._v("上記 "),t("em",[s._v("1~3")]),s._v(" のサイクルを実行し、動きつつコードとリファクタリングによって最適化されたコードを、着実に作っていく手法になります。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"テスト実装例-4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#テスト実装例-4"}},[s._v("#")]),s._v(" テスト実装例")]),s._v(" "),t("p",[s._v("この項で実際に、TDDのサイクルを見てみましょう。")]),s._v(" "),t("p",[s._v("例えば、以下の仕様のソースを作りたいとします。")]),s._v(" "),t("ul",[t("li",[s._v("クラス内の"),t("code",[s._v("do()")]),s._v('関数の、実行回数が3の倍数なら"Fizz"、5の倍数なら"Buzz"を返す、クラス'),t("code",[s._v("FizzBuzz")]),s._v("を実装します。")]),s._v(" "),t("li",[s._v("このクラスは"),t("code",[s._v("do()")]),s._v("の実行回数を、内部でカウントします。")]),s._v(" "),t("li",[s._v("3でも5の倍数でもないカウントに対しては、そのカウントを返します。")])]),s._v(" "),t("p",[s._v("上記のコードをTDDで作成していきましょう。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-red"}},[s._v("#")]),s._v(" サイクル1 Red")]),s._v(" "),t("p",[s._v("まずは、クラス"),t("code",[s._v("FizzBuzz")]),s._v("の関数を作成します。"),t("br"),s._v("\n下記のコードは、"),t("code",[s._v("example.py")]),s._v("のようなパッケージにあると考えてください。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("pass")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("次に、動作をしないテストを書きましょう。"),t("br"),s._v(" "),t("code",[s._v("do()")]),s._v("が最初に実行する時は、下記の仕様が適用されます。")]),s._v(" "),t("blockquote",[t("p",[s._v("3でも5の倍数でもないカウントに対しては、そのカウントを返します。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("とりあえず1回目の実行では「1」が返ってくるはずなので、テストでは「1」を期待してみます。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("fizzbuzz "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FizzBuzz\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n fb "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FizzBuzz"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("これを実行すると、当然のようにコケますね。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-green"}},[s._v("#")]),s._v(" サイクル1 Green")]),s._v(" "),t("p",[s._v("次は「とりあえず動くコードを書く」ことをします。"),t("br"),s._v("\nテストでは1が返却されることを期待しているので、1を返しましょう。"),t("br"),s._v("\nTDDって簡単ですね。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("動いたよ!!!!やったね!!!!!!!!!")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル1-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル1-refactoring"}},[s._v("#")]),s._v(" サイクル1 Refactoring")]),s._v(" "),t("p",[s._v("現在の"),t("code",[s._v("FizzBuzz")]),s._v("は、この世界に存在するどんなものよりも洗練されているため、リファクタリングは必要ないですね。"),t("br"),s._v("\n素晴らしい。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-red"}},[s._v("#")]),s._v(" サイクル2 Red")]),s._v(" "),t("p",[s._v("2サイクル目に来ました。"),t("br"),s._v("\nRedでは、あえてテストを失敗させなければならないため、泣く泣く完成されたテストコードに手を加えましょう。")]),s._v(" "),t("p",[s._v("サイクル1 Redでも確認した通り、どうやら"),t("code",[s._v("do()")]),s._v("を実行するごとに実行回数を返してくれるそうです。"),t("br"),s._v("\n実行回数毎に、期待する値を増加させてみましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" unittest\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("from")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("fizzbuzz "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("import")]),s._v(" FizzBuzz\n\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ExampleTestCase")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("unittest"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("TestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("test_success")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n fb "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" FizzBuzz"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("4")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("6")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("7")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("9")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("10")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("11")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("12")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("13")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("14")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("assertEqual"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("fb"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("do"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br")])]),t("br"),s._v(" "),t("p",[t("code",[s._v("FizzBuzz")]),s._v("は既に完成されているため、テストをいくら加えようが失敗するはずがないのですが、試してみましょう。"),t("br"),s._v("\nまあ、やる意味はないと思うのですが(笑)")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-green"}},[s._v("#")]),s._v(" サイクル2 Green")]),s._v(" "),t("p",[s._v("なんということでしょうか。"),t("br"),s._v("\n完成されていたと思われた"),t("code",[s._v("do()")]),s._v("は、何回実行しても「1」しか返してくれないではないですか。")]),s._v(" "),t("p",[s._v("誰ですか、こんな実装にしたのは(怒)")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("global")]),s._v(" count\n count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("だいぶ雑なコードですが、たぶん動くと思うからテストしましょう。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル2-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル2-refactoring"}},[s._v("#")]),s._v(" サイクル2 Refactoring")]),s._v(" "),t("p",[s._v("現在の"),t("code",[s._v("FizzBuzz")]),s._v("はグローバル変数が使用されているなど、あまり美しくありません。"),t("br"),s._v("\nクラスで値を持たせて、インスタンス毎に値を共有させないようにしましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("コードを変更しましたが、テストの結果が成功のままであることを確認します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-red"}},[s._v("#")]),s._v(" サイクル3 Red")]),s._v(" "),t("p",[s._v("さて、ここまでで以下の仕様を実装することができました。")]),s._v(" "),t("ul",[t("li",[s._v("内部でカウントを保持する。")]),s._v(" "),t("li",[s._v("3でも5でもないカウントは、その値を返す。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v('次は「3の倍数なら"Fizz"を返す」を実装してみましょう。')]),s._v(" "),t("p",[s._v("失敗するテストを書きます。\nテスト全体を書くと文字量が多くなりますので、以降は差分で表記します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('- self.assertEqual(fb.do(), 3)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 6)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 9)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 12)\n+ self.assertEqual(fb.do(), "Fizz")\n\n- self.assertEqual(fb.do(), 15)\n+ self.assertEqual(fb.do(), "Fizz")\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストは失敗しますね。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-green"}},[s._v("#")]),s._v(" サイクル3 Green")]),s._v(" "),t("p",[s._v('さて、"Fizz"を返せるように'),t("code",[s._v("FizzBuzz")]),s._v("を修正しましょう。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストはオールグリーンです。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル3-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル3-refactoring"}},[s._v("#")]),s._v(" サイクル3 Refactoring")]),s._v(" "),t("p",[s._v("特にリファクタリング箇所がないので省きます。")]),s._v(" "),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-red"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-red"}},[s._v("#")]),s._v(" サイクル4 Red")]),s._v(" "),t("p",[s._v("最後の仕様になります。"),t("br"),s._v('\n最後は「5の倍数なら"Buzz"を返す」を実装します。')]),s._v(" "),t("p",[s._v('ただし、3かつ5の倍数であれば"FizzBuzz"が返ることに注意してください。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('- self.assertEqual(fb.do(), 5)\n+ self.assertEqual(fb.do(), "Buzz")\n\n- self.assertEqual(fb.do(), 10)\n+ self.assertEqual(fb.do(), "Buzz")\n\n- self.assertEqual(fb.do(), 15)\n+ self.assertEqual(fb.do(), "FizzBuzz")\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("「手がかかる子ほど可愛い」というのは、このことを言うのでしょうか。"),t("br"),s._v("\nだんだんコンソールに出力される「FAIL」が愛おしく思えてきました。")]),s._v(" "),t("p",[s._v("きっと「失敗の後は必ず成功する」ということが約束されているからでしょう。")]),s._v(" "),t("p",[s._v("みなさんも、そう思いませんか?")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". FAIL\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-green"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-green"}},[s._v("#")]),s._v(" サイクル4 Green")]),s._v(" "),t("p",[s._v('"Buzz"および"FizzBuzz"を返せるようにしましょう。')]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"FizzBuzz"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Buzz"')]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("テストも通ります。"),t("br"),s._v("\nやったか!?(まだ終わりじゃないです。)")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"サイクル4-refactoring"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#サイクル4-refactoring"}},[s._v("#")]),s._v(" サイクル4 Refactoring")]),s._v(" "),t("p",[s._v("最後のリファクタリングになります。"),t("br"),s._v("\nこれで全てを終わらせて、あなたは次のステージへ進むことになるでしょう。")]),s._v(" "),t("p",[s._v("先程書いた"),t("code",[s._v("FizzBuzz")]),s._v("では、まだ手続きを共通化し、最適にできる部分があります。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#####################################")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# このコードは一例です。")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# みんなが読みやすいコードを書こうね!")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#####################################")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("class")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("FizzBuzz")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("__init__")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("_divided")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" div"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin"}},[s._v("int")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("not")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("%")]),s._v(" div"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("def")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("do")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("\n result "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Fizz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"Buzz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"FizzBuzz"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n index "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_divided"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("5")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<<")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" \\\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_divided"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("self"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("count"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("index"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("多分これが一番美しいと思います。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python -m unittest -v example.test_fizzbuzz\ntest_success "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("example.test_fizzbuzz.ExampleTestCase"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". ok\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("br"),s._v(" "),t("h3",{attrs:{id:"問題にチャレンジしよう-3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#問題にチャレンジしよう-3"}},[s._v("#")]),s._v(" 問題にチャレンジしよう")]),s._v(" "),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise3/challenge.py")]),s._v("には、FastAPIで書かれた作りかけのAPIがあります。")]),s._v(" "),t("p",[s._v("上記のAPIは、コンテナから下記のコマンドで実行することができます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ python3 -m uvicorn exercises.exercise3.challenge:app --reload --host "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("API実行後は、ブラウザに下記のURLを入力すると、APIにアクセスできます。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("http://localhost:8000/\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("br"),s._v(" "),t("p",[s._v("みなさんには、TDDを使って上記のAPIを完成させてもらいます。"),t("br"),s._v("\n作ってもらいたいAPIの仕様は、下記のものになります。")]),s._v(" "),t("ul",[t("li",[s._v("このAPIは、"),t("code",[s._v("/, /add, /sub, /mul, /div")]),s._v("の5つのエンドポイントがあります。")]),s._v(" "),t("li",[s._v("このAPIはサーバ内部でint型の値を保持し、現在設定されている値を"),t("code",[s._v("/")]),s._v("にアクセスすることで、確認することができます。\nまた、値は0始まりになります。")]),s._v(" "),t("li",[t("code",[s._v("/add, /sub, /mul, /div")]),s._v("にパスパラメータを与えると、保持されている値に対し、四則演算を行います(後述)。")]),s._v(" "),t("li",[s._v("本APIでは、全てint型で計算を行います。")])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("以下、APIのパスについて")]),s._v(" "),t("table",[t("thead",[t("tr",[t("th",[s._v("パス")]),s._v(" "),t("th",[s._v("詳細")])])]),s._v(" "),t("tbody",[t("tr",[t("td",[s._v("/")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{数値}には、サーバで保持されている値が入ります。")])]),s._v(" "),t("tr",[t("td",[s._v("/add/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値に加算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/sub/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値から減算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/mul/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値に乗算します。")])]),s._v(" "),t("tr",[t("td",[s._v("/div/{data}")]),s._v(" "),t("td",[t("code",[s._v('{"current_number": {数値}}')]),s._v("が返却されます。"),t("br"),s._v("{data}に渡された値をサーバで保持している値から除算します。")])])])]),s._v(" "),t("br"),s._v(" "),t("p",[s._v("サーバでの値の保持・取得関数は、ソース内に定義されています。"),t("br"),s._v("\n以下に、使い方の例を記載します。")]),s._v(" "),t("div",{staticClass:"language-python line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバ内に保持されている値を記録します。")]),s._v("\nset_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバ内に保持されている値を取得します")]),s._v("\ngot_data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" get_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# got_data = 1")]),s._v("\n\nset_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("123")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("+")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("456")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\ngot_data "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" get_current_number"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# got_data = 579")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("p",[s._v("dockerコンテナ内の"),t("code",[s._v("/test-hands-on/exercises/exercise3/test_challenge.py")]),s._v("には、本APIが完成すると通るようになる、テスト"),t("code",[s._v("test_success()")]),s._v("が定義されています。")]),s._v(" "),t("p",[s._v("上記のテストがOKになるよう、各種APIをTDDを使って作成してみましょう。")]),s._v(" "),t("br"),s._v(" "),t("h1",{attrs:{id:"おわりに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#おわりに"}},[s._v("#")]),s._v(" おわりに")]),s._v(" "),t("p",[s._v("一般的にソフトウェアテストというと、専門のテスト部隊があって「Excelにスクショをペタペタ貼るだけでしょ?」というようなイメージを持ち、敬遠される方も少なくはないと思います。")]),s._v(" "),t("p",[s._v("開発者がテストについて知識を持ち、単体テストで可能な限りの不具合をなくしておくと、後の工程で不具合が少なく済ますことができたり、メリットがあります。"),t("br"),s._v("\nまた、後の工程で発生した不具合の内容を聞いた・見ただけで、どのモジュール同士で問題が起こっているのか目星がつくなど、効率的なトラブルシュートやソフトウェアの理解にも繋がります。")]),s._v(" "),t("p",[s._v("冒頭でも述べましたが、ソフトウェアにも品質というものがあり、この品質次第で会社の売上に影響が出たり、企業のセキュリティや人命に影響を及ぼしてしまう懸念もあります。")]),s._v(" "),t("p",[s._v("そのため。開発を行う際には是非テストにも注力をしていただき、ユーザーの満足できるソフトウェアを作れるよう、目指してみてください。")]),s._v(" "),t("p",[s._v("良いエンジニアライフを!👍")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/34.93dc731b.js b/assets/js/34.cef35ed3.js similarity index 99% rename from assets/js/34.93dc731b.js rename to assets/js/34.cef35ed3.js index 8a4717cd..b15314a2 100644 --- a/assets/js/34.93dc731b.js +++ b/assets/js/34.cef35ed3.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{495:function(s,t,a){s.exports=a.p+"assets/img/nginx_html.ae5a2fc7.png"},562:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("h1",{attrs:{id:"ssl-tls-を触ってみよう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ssl-tls-を触ってみよう"}},[s._v("#")]),s._v(" SSL/TLS を触ってみよう")]),s._v(" "),t("h2",{attrs:{id:"事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),t("p",[s._v("このハンズオンでは、dockerをただの隔離環境として扱っています。")]),s._v(" "),t("p",[s._v("apache/nginx のハンズオンの際のものが残っているのであればそのまま流用できますので、以下の手順はスキップしてください。")]),s._v(" "),t("p",[s._v("以下のように"),t("code",[s._v("docker pull")]),s._v("をしたあと、ハンズオン用のコンテナを立ち上げてログインしてください。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.17-bookworm")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("3.8.17-bookworm: Pulling from library/python\nd52e4f012db1: Pull complete\n7dd206bea61f: Pull complete\n2320f9be4a9c: Pull complete\n6e5565e0ba8d: Pull complete\nd3797e13cc41: Pull complete\n9d8ab9ac5a7d: Pull complete\n43ed38f1d568: Pull complete\n164b4060be55: Pull complete\nDigest: sha256:2ee706fa11ec6907a27f1c5116e9749ad1267336b3b0d53fc35cfba936fae32e\nStatus: Downloaded newer image for python:3.8.17-bookworm\ndocker.io/library/python:3.8.17-bookworm\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -itd --name test-debian -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8082")]),s._v(":82 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8088")]),s._v(":88 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8089")]),s._v(":89 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8443")]),s._v(":443 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8444")]),s._v(":444 python:3.8.17-bookworm /bin/bash")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("a0da070e286fd52ebb323e5faff9c960014bfcd8eb1e509cb5a12daa9fb9a85e\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it test-debian /bin/bash")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("root@a0da070e286f:/#\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br")])]),t("p",[s._v("nginxをインストールします。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]\nGet:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]\nGet:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]\nGet:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8906 kB]\nGet:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [4732 B]\nGet:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [48.0 kB]\nFetched 9210 kB in 3s (3184 kB/s)\nReading package lists... Done\nBuilding dependency tree... Done\nReading state information... Done\n10 packages can be upgraded. Run 'apt list --upgradable' to see them.\n\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2 apache2-dev nginx neovim")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("Reading package lists... Done\nBuilding dependency tree... Done\nReading state information... Done\nThe following additional packages will be installed:\n apache2-bin apache2-data apache2-utils autopoint bsdextrautils debhelper dh-autoreconf dh-strip-nondeterminism dwz gettext gettext-base groff-base intltool-debian iproute2\n libapr1-dev libaprutil1-dbd-sqlite3 libaprutil1-dev libaprutil1-ldap libarchive-cpio-perl libarchive-zip-perl libatm1 libbpf1 libcap2-bin libdebhelper-perl\n libfile-stripnondeterminism-perl libgpm2 libldap-dev libldap2-dev liblua5.3-0 libmail-sendmail-perl libmnl0 libpam-cap libpipeline1 libsctp-dev libsctp1 libsodium23\n\n~~~略~~~\n\nSetting up libapr1-dev (1.7.2-3) ...\nSetting up libaprutil1-dev (1.6.3-1) ...\nSetting up debhelper (13.11.4) ...\nSetting up apache2-dev (2.4.57-2) ...\nProcessing triggers for libc-bin (2.36-9) ...\nProcessing triggers for hicolor-icon-theme (0.17-2) ...\nroot@a0da070e286f:/#\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br")])]),t("p",[s._v("以下のコマンドでバージョンが表示されれば成功です。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("nginx -v")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("nginx version: nginx/1.22.1\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("h3",{attrs:{id:"nginx-の初期設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#nginx-の初期設定"}},[s._v("#")]),s._v(" nginx の初期設定")]),s._v(" "),t("p",[s._v("nginxのサイトは88 portでリクエストを受け付けるようにします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'Hello Bootcamp!!' > /var/www/html/index.html")]),s._v("\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/sites-enabled/default")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-nginx line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-nginx"}},[t("code",[t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("88")]),s._v(" default_server")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:88 default_server")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("root")]),s._v(" /var/www/html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("try_files")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ =404")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("変更したらnginxを起動しましょう。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx start")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Starting nginx: nginx.\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[t("a",{attrs:{href:"http://localhost:8088",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8088"),t("OutboundLink")],1),s._v(" にアクセスしてみてください。"),t("code",[s._v("Hello Bootcamp!!")]),s._v("のHTMLが見えていれば成功です。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(495),alt:"nginx_html"}})]),s._v(" "),t("p",[s._v("アクセスログも確認してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/nginx/access.log")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ここまでが事前作業となります。間に合わなければ、座学の間で準備してください。")]),s._v(" "),t("h2",{attrs:{id:"ssl-tls-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ssl-tls-とは"}},[s._v("#")]),s._v(" SSL/TLS とは")]),s._v(" "),t("p",[s._v("例えば、HTTP は基本的に平文でデータをやりとりします。")]),s._v(" "),t("p",[s._v("ということは、途中でパケットキャプチャをすると、やり取りの内容を読み取ることができます。")]),s._v(" "),t("p",[s._v("もしそこにパスワード情報など見られてはいけない情報が含まれていたら...怖いですね。")]),s._v(" "),t("p",[s._v("そこで、SSL/TLS (Secure Socket Layer/Transport Layer Securityの技術)を用いて通信路の暗号化を行うHTTP over SSL いわゆるHTTPS を重要な情報のやりとりを行う際には用いるのが一般的となっています。")]),s._v(" "),t("p",[s._v("SSL/TLSは通信路の暗号化として汎用性のあるものとなっており、HTTPに限らず様々なプロトコルでの暗号化に用いられています。")]),s._v(" "),t("ul",[t("li",[s._v("ファイル転送: FTP -> FTPS")]),s._v(" "),t("li",[s._v("メール: SMTP -> SMTPS, POP3 -> POP3S, IMAP -> IMAPS")])]),s._v(" "),t("p",[s._v("プロトコルとまではなっていなくても、mysqlなどでもレプリケーションの経路を暗号化するために用いられていたりもします。")]),s._v(" "),t("p",[s._v("では、そのSSL/TLS の概要と歴史について、はIPAの資料がよくまとまっているため、そちらの2章1節を読むのがよいでしょう。\nざっくりというと、公開鍵暗号を用いて共通鍵を共有し、その共通鍵を用いて以後の経路の暗号化を行う、というものになります。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://www.ipa.go.jp/security/crypto/guideline/ssl_crypt_config.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("TLS 暗号設定ガイドライン"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("この講義では、主にHTTPSをメインに触っていこうと思います。\nまた、以下ではSSL/TLSの総称としてTLSということにします。")]),s._v(" "),t("h2",{attrs:{id:"証明書-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書-とは"}},[s._v("#")]),s._v(" 証明書 とは")]),s._v(" "),t("p",[s._v("TLSは公開鍵暗号と共通鍵暗号の組み合わせという話をしました。\nこの公開鍵暗号の部分を担う重要なパーツとして証明書があります。")]),s._v(" "),t("p",[s._v("証明書には、以下の役割があります。")]),s._v(" "),t("ul",[t("li",[s._v("公開鍵暗号の公開鍵情報の共有手段")]),s._v(" "),t("li",[s._v("通信相手が確かにアクセスしようとしたドメインのサーバであることの確認手段")])]),s._v(" "),t("p",[s._v("後者について、どのように確認が行えるのでしょうか?")]),s._v(" "),t("p",[s._v("試しに"),t("a",{attrs:{href:"https://www.iij.ad.jp",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIJの公式サイト"),t("OutboundLink")],1),s._v("の証明書を覗いてみましょう。")]),s._v(" "),t("p",[s._v("ブラウザでHTTPSなサイトを開くと、アドレスバーの横に鍵のマークが出ているかと思います。\nそこから、証明書の情報を見ることができます。")]),s._v(" "),t("p",[s._v("まずは、本体の証明書を見てみましょう。\n見るところは色々ありますが、この辺りを注意してみましょう。\nブラウザによって表記が異なる場合がありますので、いくつか名称は並べて書きます")]),s._v(" "),t("ul",[t("li",[s._v("共通名、一般名、Common Name(CN)\n"),t("ul",[t("li",[s._v("基本的にはサイトのドメイン")]),s._v(" "),t("li",[s._v("昔は、ここのドメインをもって確認をしても良いことになっていましたが、現在では禁止されています(RFC9110)\n"),t("ul",[t("li",[s._v("参考: "),t("a",{attrs:{href:"https://eng-blog.iij.ad.jp/archives/14820",target:"_blank",rel:"noopener noreferrer"}},[s._v("エンジニアブログ"),t("OutboundLink")],1)])])])])]),s._v(" "),t("li",[s._v("組織、Organization(O)\n"),t("ul",[t("li",[s._v("この証明書を用いたサイトの管理組織")])])]),s._v(" "),t("li",[s._v("有効期間\n"),t("ul",[t("li",[s._v("この証明書の有効期間")]),s._v(" "),t("li",[s._v("昔は5年などもありましたが、2024年現在では13か月が最長となっています。90日まで短縮する議論もあるようです。")])])]),s._v(" "),t("li",[s._v("主体者代替名、サブジェクトの代替名、サブジェクトの別名、Subject Alternative Names(SANs)\n"),t("ul",[t("li",[s._v("いくつか項目があるが、このうち重要なのはDNS名。この証明書が有効であるサイトを示しています。")]),s._v(" "),t("li",[s._v("複数存在しうる")]),s._v(" "),t("li",[s._v("現在では、証明書が管理するサイトを示す唯一の項目となります。")])])]),s._v(" "),t("li",[s._v("公開鍵\n"),t("ul",[t("li",[s._v("この証明書が提供する公開鍵。これを用いて共通鍵の共有を行います。")])])])]),s._v(" "),t("p",[s._v("ここまで見たところで、証明書に添付された公開鍵を用いて暗号化通信を始めてよいのでしょうか?\nまだ、単に私はこのサイトの管理者だぞ!と自称しているに過ぎません。\n信用して暗号化した経路でクレジットカードの情報を送ったのに、送った相手は偽サイトを運用する悪人だった、\nとなったら目も当てられません。\n確かにこのサイトの管理者だと信じるには、信頼できる第三者の担保が欲しいですね。")]),s._v(" "),t("p",[s._v("ここで登場するのが、認証局(Certification Authority: CA)です。")]),s._v(" "),t("p",[s._v("証明書に戻ると、発行者(発行元、Issuer)と証明書の署名という項目があるのがわかります。\n具体的な検証手段は割愛しますが、この署名を検証することで、確かに証明書が発行者に認められたものであることがわかります。")]),s._v(" "),t("p",[s._v("では、その発行者は信頼できるのでしょうか?\n証明書の情報を見ると、階層構造のようになっている部分があり、本体の証明書の上に発行者の証明書が表示されているかと思います。\n発行者の証明書もまた同じような構造をしており、有効期間や組織などの情報はもちろん、またその上位の発行者と署名があります。\nこうして証明書のチェインがつながっていった結果、最終的にRootCA の証明書に行きつきます。")]),s._v(" "),t("p",[s._v("ブラウザのユーザは、少なくともそのブラウザは信用して使っているのだと思います。\nブラウザは、そのブラウザを含めて世界的に信用している認証局の証明書を、リストとして保持しています。\nその認証局がRootCAと呼ばれているものです。\n証明書のチェインがつながって、RootCAのものまで辿れれば、それは信頼できる認証局が証明したものとして信用できる、と判断できるようになります。")]),s._v(" "),t("p",[s._v("このような証明書のチェインを通じて公開鍵の正当性を担保するための枠組みを公開鍵基盤(Public Key Infrastrcture:PKI)と呼びます。")]),s._v(" "),t("p",[s._v("余談ですが、政府がRootとして正当性を担保する、政府認証基盤(Government PKI:GPKI)というものもあります。\n例えばマイナンバーカードの証明書は公的個人認証サービス(JPKI)というものが発行していますが、JPKIの正当性はGPKIと相互認証する形で担保していたりします。")]),s._v(" "),t("h2",{attrs:{id:"実際に手を動かしてみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#実際に手を動かしてみる"}},[s._v("#")]),s._v(" 実際に手を動かしてみる")]),s._v(" "),t("h3",{attrs:{id:"証明書と秘密鍵を作ってみる-check1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書と秘密鍵を作ってみる-check1"}},[s._v("#")]),s._v(" 証明書と秘密鍵を作ってみる (check1)")]),s._v(" "),t("p",[s._v("通常、証明書は以下の手順で入手します。")]),s._v(" "),t("ol",[t("li",[s._v("秘密鍵を生成する")]),s._v(" "),t("li",[s._v("秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),t("li",[s._v("CSR を証明局に提出し、審査を受け、証明局の持つ秘密鍵で署名された証明書を発行してもらう")])]),s._v(" "),t("p",[s._v("ここでは、3を簡略化して1 で生成した鍵で署名する、自己署名証明書(いわゆるオレオレ証明書)を作ります。\nこのdocker image に既にインストールされている、openssl ツールで一通りの操作を行うことができます。")]),s._v(" "),t("h4",{attrs:{id:"_1-秘密鍵を生成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-秘密鍵を生成する"}},[s._v("#")]),s._v(" 1. 秘密鍵を生成する")]),s._v(" "),t("p",[s._v("ここではRSA の2048 bit の秘密鍵を生成します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるgenrsa はRSA 暗号の秘密鍵を生成するものとなります。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /etc/nginx/ssl")]),s._v("\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl genrsa 2048 > /etc/nginx/ssl/private.key")]),s._v("\nGenerating RSA private key, "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2048")]),s._v(" bit long modulus "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" primes"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v("+++++\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".+++++\ne is "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("65537")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x010001"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("h4",{attrs:{id:"_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[s._v("#")]),s._v(" 2. 秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),t("p",[s._v("1 で作った秘密鍵から、CSR を生成します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるreq はCSR を扱うためのものとなります。")])]),s._v(" "),t("p",[s._v("証明書で表示する情報をここで入力することになります。\n実際に発行する際は、正当性を担保したい対象であるCommon Name は特に間違わないようにしましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl req -new -sha256 -key /etc/nginx/ssl/private.key -out /etc/nginx/ssl/server.csr")]),s._v("\nYou are about to be asked to enter information that will be incorporated\ninto your certificate request.\nWhat you are about to enter is what is called a Distinguished Name or a DN.\nThere are quite a few fields but you can leave some blank\nFor some fields there will be a default value,\nIf you enter "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'.'")]),s._v(", the field will be left blank.\n-----\nCountry Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" letter code"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("AU"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":JP\nState or Province Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("full name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Some-State"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Tokyo\nLocality Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, city"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Chiyoda\nOrganization Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, company"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Internet Widgits Pty Ltd"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":IIJ\nOrganizational Unit Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, section"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":TU\nCommon Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("e.g. server FQDN or YOUR name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":localhost\nEmail Address "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n\nPlease enter the following "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'extra'")]),s._v(" attributes\nto be sent with your certificate request\nA challenge password "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\nAn optional company name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("h4",{attrs:{id:"_3-署名された証明書を発行する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-署名された証明書を発行する"}},[s._v("#")]),s._v(" 3. 署名された証明書を発行する")]),s._v(" "),t("p",[s._v("1 で作った秘密鍵、2 で作ったCSR から証明書を発行します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるx509 は、証明書の標準規格を指しています。\n-req でinput がCSR であることを示し、signkey に1 で作った秘密鍵を指定することでこれで署名します。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -req -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt -signkey /etc/nginx/ssl/private.key -days 365")]),s._v("\nCertificate request self-signature ok\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("subject")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("出来上がったら、証明書の中を覗いてみましょう。text オプションでテキスト出力をすることができます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -text")]),s._v("\nCertificate:\n Data:\n Version: "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Serial Number:\n "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("45")]),s._v(":ef:45:48:8c:89:e0:e5:38:74:f7:fc:21:32:35:eb:2b:bc:10:6b\n Signature Algorithm: sha256WithRSAEncryption\n Issuer: C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Validity\n Not Before: Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2022")]),s._v(" GMT\n Not After "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(" GMT\n Subject: C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Subject Public Key Info:\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v("."),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("実際に発行されたものを確認する際は、期間(Not BeforeとNot After)とSubject (CN が正しいか)に特に注意しましょう。")]),s._v(" "),t("p",[s._v("秘密鍵と証明書のペアが正しいかを確認するには、RSA のものならmodulus を比較するのが簡単です。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl rsa -in /etc/nginx/ssl/private.key -modulus -noout")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -modulus -noout")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"https-の設定-check2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#https-の設定-check2"}},[s._v("#")]),s._v(" https の設定(check2)")]),s._v(" "),t("p",[s._v("事前作業で作ったhttp で受けていたサイトをhttps でも受けられるようにしてみます。")]),s._v(" "),t("p",[t("code",[s._v("/etc/nginx/sites-enabled/default")]),s._v(" の一番下に以下を追記していきます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("server "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/server.crt"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/private.key"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n root /var/www/html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n server_name _"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n location / "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n try_files "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("404")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br")])]),t("p",[s._v("追記したら、nginx をリスタートしましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@dea1ac0e1edb:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v(" ok "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" Restarting nginx: nginx.\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("443 は8443 にポートフォワードの設定が入っているため、8443 ポートにアクセスしてみましょう。\nhttps での通信となるため、URL の先頭がhttp ではなくhttps となっています。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://localhost:8443/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443/"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("今回は自己署名証明書であるため、ほとんどのブラウザは正当な証明書ではないと判断し、注意喚起の画面が表示されます。\n危険性を承知で閲覧すると、"),t("code",[s._v("Hello Bootcamp!!")]),s._v("の表示が確認できるかと思います。")]),s._v(" "),t("p",[s._v("また、ブラウザ上で暗号化に使っている証明書の内容が確認できるので、確認もしてみましょう。")]),s._v(" "),t("h3",{attrs:{id:"詳細な暗号設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#詳細な暗号設定"}},[s._v("#")]),s._v(" 詳細な暗号設定")]),s._v(" "),t("p",[s._v("TLSの設定としては、主に、プロトコルのバージョン、暗号スイート、証明書、に何を使うかが大事になります。")]),s._v(" "),t("h4",{attrs:{id:"protocol-を設定してみる-check3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#protocol-を設定してみる-check3"}},[s._v("#")]),s._v(" protocol を設定してみる(check3)")]),s._v(" "),t("p",[s._v("プロトコルについては、既に1.1 までは2021に禁止扱いになっていたりします。")]),s._v(" "),t("p",[s._v("nginx だと、"),t("code",[s._v("ssl_protocols")]),s._v("で設定します。")]),s._v(" "),t("p",[s._v("まずは、デフォルトの状態で接続を確認してみましょう。TLSv1.2で接続できています。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("nginx上ではデフォルトで後述設定の通りTLSv1, 1.1 が有効に見えますが、接続できないようです。\nそのため、ここでは差異が見えるようわざとTLSv1.2 で試してみています。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Server hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" GET / HTTP/1.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" Host: localhost\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" User-Agent: curl/7.88.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" Accept: */*\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" HTTP/1.1 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),s._v(" OK\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Server: nginx/1.22.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Date: Thu, 01 Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2024")]),s._v(" 01:58:28 GMT\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Content-Type: text/html\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Content-Length: "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("17")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Last-Modified: Mon, "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("29")]),s._v(" Jul "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2024")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("23")]),s._v(":13:24 GMT\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Connection: keep-alive\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" ETag: "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"66a82214-11"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Accept-Ranges: bytes\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("\nHello Bootcamp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("\n* Connection "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0 to host localhost left intact")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br")])]),t("p",[s._v("では、/etc/nginx/nginx.conf に設定があるので、書き換えてみましょう。\n書き換えたら再起動して反映させます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/nginx.conf")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# SSL Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Dropping SSLv3, ref: POODLE <= ここからTLSv1 TLSv1.1 TLSv1.2 を消してみる")]),s._v("\n ssl_prefer_server_ciphers on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Logging Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("設定してみたら、接続できなくなることを確認してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS alert, protocol version "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("582")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version\n* Closing connection "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\ncurl: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("35")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("h4",{attrs:{id:"暗号スイートを設定してみる-check4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#暗号スイートを設定してみる-check4"}},[s._v("#")]),s._v(" 暗号スイートを設定してみる(check4)")]),s._v(" "),t("p",[s._v("暗号スイートは、")]),s._v(" "),t("ul",[t("li",[s._v("鍵交換方式")]),s._v(" "),t("li",[s._v("署名方式")]),s._v(" "),t("li",[s._v("暗号化方式")]),s._v(" "),t("li",[s._v("ハッシュ関数")])]),s._v(" "),t("p",[s._v("の組で構成されます。TLS1.3からは、ここから鍵交換方式、署名方式が外されて暗号化方式、ハッシュ関数で構成されます。(鍵交換や署名の部分がプロトコルに最初から組み込まれるようになったため、指定する必要がなくなりました)")]),s._v(" "),t("p",[s._v("openssl コマンドで、扱える暗号スイートの一覧を見ることが出来るので、見てみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl ciphers -v")]),s._v("\nTLS_AES_256_GCM_SHA384 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nTLS_CHACHA20_POLY1305_SHA256 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("CHACHA20/POLY1305"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nTLS_AES_128_GCM_SHA256 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("128")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("RSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nDHE-RSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("DH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("RSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("以下略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("p",[s._v("一番上のものは、TLS1.3 のものなので、暗号化がAES256のGCMモード、ハッシュ関数がSHA384\n4番目のものは、TLS1.2 のものなので、鍵交換がECDHE、署名がECDSA、暗号化がAES256のGCMモード、ハッシュ関数がSHA384\nといった感じになります。")]),s._v(" "),t("p",[s._v("注意する点として、この表記はOpenSSL流のものですが、世の中にはIANAが定めた表記も存在しています。\n技術文書で出てきたものが見当たらないな、と思ったら別表記のものかもしれません。")]),s._v(" "),t("p",[s._v("参考")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://testssl.sh/openssl-iana.mapping.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("testssl.sh が出してるマッピング"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://ciphersuite.info/",target:"_blank",rel:"noopener noreferrer"}},[s._v("変換してくれるサイト"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("nginxでは、"),t("code",[s._v("ssl_ciphers")]),s._v("を用いて設定します。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/nginx.conf")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# SSL Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n\n ssl_protocols TLSv1.2 TLSv1.3"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Dropping SSLv3, ref: POODLE <= TLSv1.2 を元に戻す")]),s._v("\n ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ↑↑↑ この行を追加")]),s._v("\n ssl_prefer_server_ciphers on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Logging Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v('ssl_ciphers の設定部分は、openssl ciphers の引数に食わせることで、この設定で使えるcipher の一覧を表示させることができます。\n今回は列挙した形ですが、!EXP のようにブラックリストな書き方もできるため、よくわからなくなったらこれで実際に設定されるものを確認するとよいでしょう。\nopenssl ciphers -v "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"')])]),s._v(" "),t("p",[t("code",[s._v("openssl ciphers")]),s._v("の一覧から適当なものを選んで接続してみて、nginx.confで設定したものは接続でき、設定しなかったものは接続できないことを確認してください。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Server hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nHello Bootcamp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("\n\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 --ciphers AES256-SHA256 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* Cipher selection: AES256-SHA256\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS alert, handshake failure "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("552")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure\n* Closing connection "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\ncurl: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("35")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br")])]),t("h4",{attrs:{id:"証明書について-check5"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書について-check5"}},[s._v("#")]),s._v(" 証明書について(check5)")]),s._v(" "),t("p",[s._v("証明書については、発行形式は証明局側がよしなにするので、こちらで主に気にすべきは秘密鍵の方です。\n鍵長長ければ長いほどセキュリティ強度は高まりますが、復号するために余計にリソースを消費することになるため、\nそこはトレードオフとなります。")]),s._v(" "),t("p",[s._v("ECDSAは暗号の特性上、短い鍵長でも高いセキュリティ強度を保つことができます。\nRSAであれば2048bit、ECDSAであれば256bit のものを使うのがよいでしょう。")]),s._v(" "),t("p",[s._v("ここでは、試しにECDSAの秘密鍵を用いて証明書を作って使用してみます。\nopenssl コマンドで、ECDSAの秘密鍵も生成可能です。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl ecparam -genkey -name prime256v1 > /etc/nginx/ssl/ecdsa.key")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("生成した鍵を見てみると、形式がRSAのものと異なり、短くなっていることが見て取れるかと思います。\nここから先の手順は、RSAの時と同様です。出力も同様なので省略します。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl req -new -sha256 -key /etc/nginx/ssl/ecdsa.key -out /etc/nginx/ssl/ecdsa.csr")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -req -in /etc/nginx/ssl/ecdsa.csr -out /etc/nginx/ssl/ecdsa.crt -signkey /etc/nginx/ssl/ecdsa.key -days 365")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("出来上がったものを見てみると、Public Key Info が変わっています。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/ecdsa.crt -text")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Subject Public Key Info:\n Public Key Algorithm: id-ecPublicKey\n Public-Key: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),s._v(" bit"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n pub:\n 04:6b:b0:f6:cc:b5:8d:6a:b9:93:f2:1d:50:ec:4e:\n "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("59")]),s._v(":82:39:70:33:61:f3:b4:3b:13:42:39:1d:68:27:\n 5a:27:3d:0a:df:14:78:3d:aa:fb:ec:f5:a8:8b:87:\n 3b:bc:cc:f7:47:b3:84:db:85:a5:e5:2e:9c:03:44:\n 8f:37:65:2e:e4\n ASN1 OID: prime256v1\n NIST CURVE: P-256\nSignature Algorithm: ecdsa-with-SHA256\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("後は、nginx の参照を変更し、この証明書を使ってhttpsを提供してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/sites-enabled/default")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\nserver "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/ecdsa.crt"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# <= ここを書き換え")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/ecdsa.key"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# <= ここを書き換え")]),s._v("\n\n root /var/www/html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("後略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v("ECDSAの鍵を用いたものでも問題なく接続できるかと思います。")]),s._v(" "),t("p",[s._v("パフォーマンスの観点からはECDSAの方が有利ですが、中にはECDSAに対応していないサービスもあります。\n利用してみたい場合も証明書を使用するサービスで使えるかどうか確認してから発行するようにしましょう。")]),s._v(" "),t("h4",{attrs:{id:"安全とされている設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#安全とされている設定"}},[s._v("#")]),s._v(" 安全とされている設定")]),s._v(" "),t("p",[s._v("日本における暗号設定のセキュリティ上の最低ラインは先の暗号設定ガイドラインの3.1に記載されています。\n\n"),t("a",{attrs:{href:"https://www.ipa.go.jp/security/crypto/guideline/ssl_crypt_config.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("TLS 暗号設定ガイドライン"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("表14がわかりやすいでしょう。ビットセキュリティについては、2.6の表11がわかりやすいです。")]),s._v(" "),t("p",[s._v("mozilla の推奨暗号スイートも参考になります。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://wiki.mozilla.org/Security/Server_Side_TLS",target:"_blank",rel:"noopener noreferrer"}},[s._v("mozilla Server Side TLS"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("今回のcipher設定についてはmozilla のintermediate のものを利用しています。\n"),t("a",{attrs:{href:"https://ssl-config.mozilla.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("こちら"),t("OutboundLink")],1),s._v("で対応する設定を出力してくれるのも便利です。")])])}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{495:function(s,t,a){s.exports=a.p+"assets/img/nginx_html.ae5a2fc7.png"},561:function(s,t,a){"use strict";a.r(t);var n=a(10),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("h1",{attrs:{id:"ssl-tls-を触ってみよう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ssl-tls-を触ってみよう"}},[s._v("#")]),s._v(" SSL/TLS を触ってみよう")]),s._v(" "),t("h2",{attrs:{id:"事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),t("p",[s._v("このハンズオンでは、dockerをただの隔離環境として扱っています。")]),s._v(" "),t("p",[s._v("apache/nginx のハンズオンの際のものが残っているのであればそのまま流用できますので、以下の手順はスキップしてください。")]),s._v(" "),t("p",[s._v("以下のように"),t("code",[s._v("docker pull")]),s._v("をしたあと、ハンズオン用のコンテナを立ち上げてログインしてください。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.17-bookworm")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("3.8.17-bookworm: Pulling from library/python\nd52e4f012db1: Pull complete\n7dd206bea61f: Pull complete\n2320f9be4a9c: Pull complete\n6e5565e0ba8d: Pull complete\nd3797e13cc41: Pull complete\n9d8ab9ac5a7d: Pull complete\n43ed38f1d568: Pull complete\n164b4060be55: Pull complete\nDigest: sha256:2ee706fa11ec6907a27f1c5116e9749ad1267336b3b0d53fc35cfba936fae32e\nStatus: Downloaded newer image for python:3.8.17-bookworm\ndocker.io/library/python:3.8.17-bookworm\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --rm -itd --name test-debian -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8082")]),s._v(":82 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8088")]),s._v(":88 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8089")]),s._v(":89 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8443")]),s._v(":443 -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8444")]),s._v(":444 python:3.8.17-bookworm /bin/bash")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("a0da070e286fd52ebb323e5faff9c960014bfcd8eb1e509cb5a12daa9fb9a85e\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exec")]),s._v(" -it test-debian /bin/bash")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("root@a0da070e286f:/#\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br")])]),t("p",[s._v("nginxをインストールします。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]\nGet:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]\nGet:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]\nGet:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8906 kB]\nGet:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [4732 B]\nGet:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [48.0 kB]\nFetched 9210 kB in 3s (3184 kB/s)\nReading package lists... Done\nBuilding dependency tree... Done\nReading state information... Done\n10 packages can be upgraded. Run 'apt list --upgradable' to see them.\n\n")]),t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2 apache2-dev nginx neovim")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("Reading package lists... Done\nBuilding dependency tree... Done\nReading state information... Done\nThe following additional packages will be installed:\n apache2-bin apache2-data apache2-utils autopoint bsdextrautils debhelper dh-autoreconf dh-strip-nondeterminism dwz gettext gettext-base groff-base intltool-debian iproute2\n libapr1-dev libaprutil1-dbd-sqlite3 libaprutil1-dev libaprutil1-ldap libarchive-cpio-perl libarchive-zip-perl libatm1 libbpf1 libcap2-bin libdebhelper-perl\n libfile-stripnondeterminism-perl libgpm2 libldap-dev libldap2-dev liblua5.3-0 libmail-sendmail-perl libmnl0 libpam-cap libpipeline1 libsctp-dev libsctp1 libsodium23\n\n~~~略~~~\n\nSetting up libapr1-dev (1.7.2-3) ...\nSetting up libaprutil1-dev (1.6.3-1) ...\nSetting up debhelper (13.11.4) ...\nSetting up apache2-dev (2.4.57-2) ...\nProcessing triggers for libc-bin (2.36-9) ...\nProcessing triggers for hicolor-icon-theme (0.17-2) ...\nroot@a0da070e286f:/#\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br")])]),t("p",[s._v("以下のコマンドでバージョンが表示されれば成功です。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("nginx -v")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("nginx version: nginx/1.22.1\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("h3",{attrs:{id:"nginx-の初期設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#nginx-の初期設定"}},[s._v("#")]),s._v(" nginx の初期設定")]),s._v(" "),t("p",[s._v("nginxのサイトは88 portでリクエストを受け付けるようにします。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'Hello Bootcamp!!' > /var/www/html/index.html")]),s._v("\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/sites-enabled/default")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("div",{staticClass:"language-nginx line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-nginx"}},[t("code",[t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("88")]),s._v(" default_server")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:88 default_server")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("root")]),s._v(" /var/www/html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token directive"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("try_files")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ =404")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("変更したらnginxを起動しましょう。")]),s._v(" "),t("div",{staticClass:"language-shell-session line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-shell-session"}},[t("code",[t("span",{pre:!0,attrs:{class:"token command"}},[t("span",{pre:!0,attrs:{class:"token info punctuation"}},[t("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),t("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token bash language-bash"}},[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx start")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Starting nginx: nginx.\n")])])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[t("a",{attrs:{href:"http://localhost:8088",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8088"),t("OutboundLink")],1),s._v(" にアクセスしてみてください。"),t("code",[s._v("Hello Bootcamp!!")]),s._v("のHTMLが見えていれば成功です。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(495),alt:"nginx_html"}})]),s._v(" "),t("p",[s._v("アクセスログも確認してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/nginx/access.log")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("ここまでが事前作業となります。間に合わなければ、座学の間で準備してください。")]),s._v(" "),t("h2",{attrs:{id:"ssl-tls-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#ssl-tls-とは"}},[s._v("#")]),s._v(" SSL/TLS とは")]),s._v(" "),t("p",[s._v("例えば、HTTP は基本的に平文でデータをやりとりします。")]),s._v(" "),t("p",[s._v("ということは、途中でパケットキャプチャをすると、やり取りの内容を読み取ることができます。")]),s._v(" "),t("p",[s._v("もしそこにパスワード情報など見られてはいけない情報が含まれていたら...怖いですね。")]),s._v(" "),t("p",[s._v("そこで、SSL/TLS (Secure Socket Layer/Transport Layer Securityの技術)を用いて通信路の暗号化を行うHTTP over SSL いわゆるHTTPS を重要な情報のやりとりを行う際には用いるのが一般的となっています。")]),s._v(" "),t("p",[s._v("SSL/TLSは通信路の暗号化として汎用性のあるものとなっており、HTTPに限らず様々なプロトコルでの暗号化に用いられています。")]),s._v(" "),t("ul",[t("li",[s._v("ファイル転送: FTP -> FTPS")]),s._v(" "),t("li",[s._v("メール: SMTP -> SMTPS, POP3 -> POP3S, IMAP -> IMAPS")])]),s._v(" "),t("p",[s._v("プロトコルとまではなっていなくても、mysqlなどでもレプリケーションの経路を暗号化するために用いられていたりもします。")]),s._v(" "),t("p",[s._v("では、そのSSL/TLS の概要と歴史について、はIPAの資料がよくまとまっているため、そちらの2章1節を読むのがよいでしょう。\nざっくりというと、公開鍵暗号を用いて共通鍵を共有し、その共通鍵を用いて以後の経路の暗号化を行う、というものになります。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://www.ipa.go.jp/security/crypto/guideline/ssl_crypt_config.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("TLS 暗号設定ガイドライン"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("この講義では、主にHTTPSをメインに触っていこうと思います。\nまた、以下ではSSL/TLSの総称としてTLSということにします。")]),s._v(" "),t("h2",{attrs:{id:"証明書-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書-とは"}},[s._v("#")]),s._v(" 証明書 とは")]),s._v(" "),t("p",[s._v("TLSは公開鍵暗号と共通鍵暗号の組み合わせという話をしました。\nこの公開鍵暗号の部分を担う重要なパーツとして証明書があります。")]),s._v(" "),t("p",[s._v("証明書には、以下の役割があります。")]),s._v(" "),t("ul",[t("li",[s._v("公開鍵暗号の公開鍵情報の共有手段")]),s._v(" "),t("li",[s._v("通信相手が確かにアクセスしようとしたドメインのサーバであることの確認手段")])]),s._v(" "),t("p",[s._v("後者について、どのように確認が行えるのでしょうか?")]),s._v(" "),t("p",[s._v("試しに"),t("a",{attrs:{href:"https://www.iij.ad.jp",target:"_blank",rel:"noopener noreferrer"}},[s._v("IIJの公式サイト"),t("OutboundLink")],1),s._v("の証明書を覗いてみましょう。")]),s._v(" "),t("p",[s._v("ブラウザでHTTPSなサイトを開くと、アドレスバーの横に鍵のマークが出ているかと思います。\nそこから、証明書の情報を見ることができます。")]),s._v(" "),t("p",[s._v("まずは、本体の証明書を見てみましょう。\n見るところは色々ありますが、この辺りを注意してみましょう。\nブラウザによって表記が異なる場合がありますので、いくつか名称は並べて書きます")]),s._v(" "),t("ul",[t("li",[s._v("共通名、一般名、Common Name(CN)\n"),t("ul",[t("li",[s._v("基本的にはサイトのドメイン")]),s._v(" "),t("li",[s._v("昔は、ここのドメインをもって確認をしても良いことになっていましたが、現在では禁止されています(RFC9110)\n"),t("ul",[t("li",[s._v("参考: "),t("a",{attrs:{href:"https://eng-blog.iij.ad.jp/archives/14820",target:"_blank",rel:"noopener noreferrer"}},[s._v("エンジニアブログ"),t("OutboundLink")],1)])])])])]),s._v(" "),t("li",[s._v("組織、Organization(O)\n"),t("ul",[t("li",[s._v("この証明書を用いたサイトの管理組織")])])]),s._v(" "),t("li",[s._v("有効期間\n"),t("ul",[t("li",[s._v("この証明書の有効期間")]),s._v(" "),t("li",[s._v("昔は5年などもありましたが、2024年現在では13か月が最長となっています。90日まで短縮する議論もあるようです。")])])]),s._v(" "),t("li",[s._v("主体者代替名、サブジェクトの代替名、サブジェクトの別名、Subject Alternative Names(SANs)\n"),t("ul",[t("li",[s._v("いくつか項目があるが、このうち重要なのはDNS名。この証明書が有効であるサイトを示しています。")]),s._v(" "),t("li",[s._v("複数存在しうる")]),s._v(" "),t("li",[s._v("現在では、証明書が管理するサイトを示す唯一の項目となります。")])])]),s._v(" "),t("li",[s._v("公開鍵\n"),t("ul",[t("li",[s._v("この証明書が提供する公開鍵。これを用いて共通鍵の共有を行います。")])])])]),s._v(" "),t("p",[s._v("ここまで見たところで、証明書に添付された公開鍵を用いて暗号化通信を始めてよいのでしょうか?\nまだ、単に私はこのサイトの管理者だぞ!と自称しているに過ぎません。\n信用して暗号化した経路でクレジットカードの情報を送ったのに、送った相手は偽サイトを運用する悪人だった、\nとなったら目も当てられません。\n確かにこのサイトの管理者だと信じるには、信頼できる第三者の担保が欲しいですね。")]),s._v(" "),t("p",[s._v("ここで登場するのが、認証局(Certification Authority: CA)です。")]),s._v(" "),t("p",[s._v("証明書に戻ると、発行者(発行元、Issuer)と証明書の署名という項目があるのがわかります。\n具体的な検証手段は割愛しますが、この署名を検証することで、確かに証明書が発行者に認められたものであることがわかります。")]),s._v(" "),t("p",[s._v("では、その発行者は信頼できるのでしょうか?\n証明書の情報を見ると、階層構造のようになっている部分があり、本体の証明書の上に発行者の証明書が表示されているかと思います。\n発行者の証明書もまた同じような構造をしており、有効期間や組織などの情報はもちろん、またその上位の発行者と署名があります。\nこうして証明書のチェインがつながっていった結果、最終的にRootCA の証明書に行きつきます。")]),s._v(" "),t("p",[s._v("ブラウザのユーザは、少なくともそのブラウザは信用して使っているのだと思います。\nブラウザは、そのブラウザを含めて世界的に信用している認証局の証明書を、リストとして保持しています。\nその認証局がRootCAと呼ばれているものです。\n証明書のチェインがつながって、RootCAのものまで辿れれば、それは信頼できる認証局が証明したものとして信用できる、と判断できるようになります。")]),s._v(" "),t("p",[s._v("このような証明書のチェインを通じて公開鍵の正当性を担保するための枠組みを公開鍵基盤(Public Key Infrastrcture:PKI)と呼びます。")]),s._v(" "),t("p",[s._v("余談ですが、政府がRootとして正当性を担保する、政府認証基盤(Government PKI:GPKI)というものもあります。\n例えばマイナンバーカードの証明書は公的個人認証サービス(JPKI)というものが発行していますが、JPKIの正当性はGPKIと相互認証する形で担保していたりします。")]),s._v(" "),t("h2",{attrs:{id:"実際に手を動かしてみる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#実際に手を動かしてみる"}},[s._v("#")]),s._v(" 実際に手を動かしてみる")]),s._v(" "),t("h3",{attrs:{id:"証明書と秘密鍵を作ってみる-check1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書と秘密鍵を作ってみる-check1"}},[s._v("#")]),s._v(" 証明書と秘密鍵を作ってみる (check1)")]),s._v(" "),t("p",[s._v("通常、証明書は以下の手順で入手します。")]),s._v(" "),t("ol",[t("li",[s._v("秘密鍵を生成する")]),s._v(" "),t("li",[s._v("秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),t("li",[s._v("CSR を証明局に提出し、審査を受け、証明局の持つ秘密鍵で署名された証明書を発行してもらう")])]),s._v(" "),t("p",[s._v("ここでは、3を簡略化して1 で生成した鍵で署名する、自己署名証明書(いわゆるオレオレ証明書)を作ります。\nこのdocker image に既にインストールされている、openssl ツールで一通りの操作を行うことができます。")]),s._v(" "),t("h4",{attrs:{id:"_1-秘密鍵を生成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-秘密鍵を生成する"}},[s._v("#")]),s._v(" 1. 秘密鍵を生成する")]),s._v(" "),t("p",[s._v("ここではRSA の2048 bit の秘密鍵を生成します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるgenrsa はRSA 暗号の秘密鍵を生成するものとなります。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /etc/nginx/ssl")]),s._v("\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl genrsa 2048 > /etc/nginx/ssl/private.key")]),s._v("\nGenerating RSA private key, "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2048")]),s._v(" bit long modulus "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" primes"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v("+++++\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".+++++\ne is "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("65537")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x010001"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br")])]),t("h4",{attrs:{id:"_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[s._v("#")]),s._v(" 2. 秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),t("p",[s._v("1 で作った秘密鍵から、CSR を生成します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるreq はCSR を扱うためのものとなります。")])]),s._v(" "),t("p",[s._v("証明書で表示する情報をここで入力することになります。\n実際に発行する際は、正当性を担保したい対象であるCommon Name は特に間違わないようにしましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl req -new -sha256 -key /etc/nginx/ssl/private.key -out /etc/nginx/ssl/server.csr")]),s._v("\nYou are about to be asked to enter information that will be incorporated\ninto your certificate request.\nWhat you are about to enter is what is called a Distinguished Name or a DN.\nThere are quite a few fields but you can leave some blank\nFor some fields there will be a default value,\nIf you enter "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'.'")]),s._v(", the field will be left blank.\n-----\nCountry Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" letter code"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("AU"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":JP\nState or Province Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("full name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Some-State"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Tokyo\nLocality Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, city"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Chiyoda\nOrganization Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, company"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Internet Widgits Pty Ltd"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":IIJ\nOrganizational Unit Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, section"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":TU\nCommon Name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("e.g. server FQDN or YOUR name"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":localhost\nEmail Address "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n\nPlease enter the following "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'extra'")]),s._v(" attributes\nto be sent with your certificate request\nA challenge password "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\nAn optional company name "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br")])]),t("h4",{attrs:{id:"_3-署名された証明書を発行する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-署名された証明書を発行する"}},[s._v("#")]),s._v(" 3. 署名された証明書を発行する")]),s._v(" "),t("p",[s._v("1 で作った秘密鍵、2 で作ったCSR から証明書を発行します。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("サブコマンドであるx509 は、証明書の標準規格を指しています。\n-req でinput がCSR であることを示し、signkey に1 で作った秘密鍵を指定することでこれで署名します。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -req -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt -signkey /etc/nginx/ssl/private.key -days 365")]),s._v("\nCertificate request self-signature ok\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("subject")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])]),t("p",[s._v("出来上がったら、証明書の中を覗いてみましょう。text オプションでテキスト出力をすることができます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -text")]),s._v("\nCertificate:\n Data:\n Version: "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x0"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Serial Number:\n "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("45")]),s._v(":ef:45:48:8c:89:e0:e5:38:74:f7:fc:21:32:35:eb:2b:bc:10:6b\n Signature Algorithm: sha256WithRSAEncryption\n Issuer: C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Validity\n Not Before: Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2022")]),s._v(" GMT\n Not After "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(" GMT\n Subject: C "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Subject Public Key Info:\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v("."),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("実際に発行されたものを確認する際は、期間(Not BeforeとNot After)とSubject (CN が正しいか)に特に注意しましょう。")]),s._v(" "),t("p",[s._v("秘密鍵と証明書のペアが正しいかを確認するには、RSA のものならmodulus を比較するのが簡単です。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl rsa -in /etc/nginx/ssl/private.key -modulus -noout")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n\nroot@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -modulus -noout")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"https-の設定-check2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#https-の設定-check2"}},[s._v("#")]),s._v(" https の設定(check2)")]),s._v(" "),t("p",[s._v("事前作業で作ったhttp で受けていたサイトをhttps でも受けられるようにしてみます。")]),s._v(" "),t("p",[t("code",[s._v("/etc/nginx/sites-enabled/default")]),s._v(" の一番下に以下を追記していきます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("server "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/server.crt"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/private.key"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n root /var/www/html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n server_name _"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n location / "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n try_files "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("404")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br")])]),t("p",[s._v("追記したら、nginx をリスタートしましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@dea1ac0e1edb:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v(" ok "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" Restarting nginx: nginx.\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("443 は8443 にポートフォワードの設定が入っているため、8443 ポートにアクセスしてみましょう。\nhttps での通信となるため、URL の先頭がhttp ではなくhttps となっています。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://localhost:8443/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443/"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("今回は自己署名証明書であるため、ほとんどのブラウザは正当な証明書ではないと判断し、注意喚起の画面が表示されます。\n危険性を承知で閲覧すると、"),t("code",[s._v("Hello Bootcamp!!")]),s._v("の表示が確認できるかと思います。")]),s._v(" "),t("p",[s._v("また、ブラウザ上で暗号化に使っている証明書の内容が確認できるので、確認もしてみましょう。")]),s._v(" "),t("h3",{attrs:{id:"詳細な暗号設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#詳細な暗号設定"}},[s._v("#")]),s._v(" 詳細な暗号設定")]),s._v(" "),t("p",[s._v("TLSの設定としては、主に、プロトコルのバージョン、暗号スイート、証明書、に何を使うかが大事になります。")]),s._v(" "),t("h4",{attrs:{id:"protocol-を設定してみる-check3"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#protocol-を設定してみる-check3"}},[s._v("#")]),s._v(" protocol を設定してみる(check3)")]),s._v(" "),t("p",[s._v("プロトコルについては、既に1.1 までは2021に禁止扱いになっていたりします。")]),s._v(" "),t("p",[s._v("nginx だと、"),t("code",[s._v("ssl_protocols")]),s._v("で設定します。")]),s._v(" "),t("p",[s._v("まずは、デフォルトの状態で接続を確認してみましょう。TLSv1.2で接続できています。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("nginx上ではデフォルトで後述設定の通りTLSv1, 1.1 が有効に見えますが、接続できないようです。\nそのため、ここでは差異が見えるようわざとTLSv1.2 で試してみています。")])]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Server hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" GET / HTTP/1.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" Host: localhost\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" User-Agent: curl/7.88.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" Accept: */*\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" HTTP/1.1 "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("200")]),s._v(" OK\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Server: nginx/1.22.1\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Date: Thu, 01 Aug "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2024")]),s._v(" 01:58:28 GMT\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Content-Type: text/html\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Content-Length: "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("17")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Last-Modified: Mon, "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("29")]),s._v(" Jul "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2024")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("23")]),s._v(":13:24 GMT\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Connection: keep-alive\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" ETag: "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"66a82214-11"')]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v(" Accept-Ranges: bytes\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("\nHello Bootcamp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("\n* Connection "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0 to host localhost left intact")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br")])]),t("p",[s._v("では、/etc/nginx/nginx.conf に設定があるので、書き換えてみましょう。\n書き換えたら再起動して反映させます。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/nginx.conf")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# SSL Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Dropping SSLv3, ref: POODLE <= ここからTLSv1 TLSv1.1 TLSv1.2 を消してみる")]),s._v("\n ssl_prefer_server_ciphers on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Logging Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("設定してみたら、接続できなくなることを確認してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS alert, protocol version "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("582")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version\n* Closing connection "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\ncurl: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("35")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br")])]),t("h4",{attrs:{id:"暗号スイートを設定してみる-check4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#暗号スイートを設定してみる-check4"}},[s._v("#")]),s._v(" 暗号スイートを設定してみる(check4)")]),s._v(" "),t("p",[s._v("暗号スイートは、")]),s._v(" "),t("ul",[t("li",[s._v("鍵交換方式")]),s._v(" "),t("li",[s._v("署名方式")]),s._v(" "),t("li",[s._v("暗号化方式")]),s._v(" "),t("li",[s._v("ハッシュ関数")])]),s._v(" "),t("p",[s._v("の組で構成されます。TLS1.3からは、ここから鍵交換方式、署名方式が外されて暗号化方式、ハッシュ関数で構成されます。(鍵交換や署名の部分がプロトコルに最初から組み込まれるようになったため、指定する必要がなくなりました)")]),s._v(" "),t("p",[s._v("openssl コマンドで、扱える暗号スイートの一覧を見ることが出来るので、見てみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl ciphers -v")]),s._v("\nTLS_AES_256_GCM_SHA384 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nTLS_CHACHA20_POLY1305_SHA256 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("CHACHA20/POLY1305"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nTLS_AES_128_GCM_SHA256 TLSv1.3 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("any "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("128")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("ECDH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("RSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\nDHE-RSA-AES256-GCM-SHA384 TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Kx")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("DH "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Au")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("RSA "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Enc")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AESGCM"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Mac")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("AEAD\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("以下略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br")])]),t("p",[s._v("一番上のものは、TLS1.3 のものなので、暗号化がAES256のGCMモード、ハッシュ関数がSHA384\n4番目のものは、TLS1.2 のものなので、鍵交換がECDHE、署名がECDSA、暗号化がAES256のGCMモード、ハッシュ関数がSHA384\nといった感じになります。")]),s._v(" "),t("p",[s._v("注意する点として、この表記はOpenSSL流のものですが、世の中にはIANAが定めた表記も存在しています。\n技術文書で出てきたものが見当たらないな、と思ったら別表記のものかもしれません。")]),s._v(" "),t("p",[s._v("参考")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://testssl.sh/openssl-iana.mapping.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("testssl.sh が出してるマッピング"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://ciphersuite.info/",target:"_blank",rel:"noopener noreferrer"}},[s._v("変換してくれるサイト"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("nginxでは、"),t("code",[s._v("ssl_ciphers")]),s._v("を用いて設定します。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/nginx.conf")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# SSL Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n\n ssl_protocols TLSv1.2 TLSv1.3"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Dropping SSLv3, ref: POODLE <= TLSv1.2 を元に戻す")]),s._v("\n ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ↑↑↑ この行を追加")]),s._v("\n ssl_prefer_server_ciphers on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Logging Settings")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("##")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("前後省略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v('ssl_ciphers の設定部分は、openssl ciphers の引数に食わせることで、この設定で使えるcipher の一覧を表示させることができます。\n今回は列挙した形ですが、!EXP のようにブラックリストな書き方もできるため、よくわからなくなったらこれで実際に設定されるものを確認するとよいでしょう。\nopenssl ciphers -v "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"')])]),s._v(" "),t("p",[t("code",[s._v("openssl ciphers")]),s._v("の一覧から適当なものを選んで接続してみて、nginx.confで設定したものは接続でき、設定しなかったものは接続できないことを確認してください。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Server hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nHello Bootcamp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("\n\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl -vvv -k --tls-max 1.2 --ciphers AES256-SHA256 https://localhost:443")]),s._v("\n* Trying "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:443"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\n* Connected to localhost "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" port "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("#0)")]),s._v("\n* ALPN: offers h2,http/1.1\n* Cipher selection: AES256-SHA256\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("OUT"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS handshake, Client hello "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* TLSv1.2 "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("IN"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(", TLS alert, handshake failure "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("552")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(":\n* OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure\n* Closing connection "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\ncurl: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("35")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure\n\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br")])]),t("h4",{attrs:{id:"証明書について-check5"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#証明書について-check5"}},[s._v("#")]),s._v(" 証明書について(check5)")]),s._v(" "),t("p",[s._v("証明書については、発行形式は証明局側がよしなにするので、こちらで主に気にすべきは秘密鍵の方です。\n鍵長長ければ長いほどセキュリティ強度は高まりますが、復号するために余計にリソースを消費することになるため、\nそこはトレードオフとなります。")]),s._v(" "),t("p",[s._v("ECDSAは暗号の特性上、短い鍵長でも高いセキュリティ強度を保つことができます。\nRSAであれば2048bit、ECDSAであれば256bit のものを使うのがよいでしょう。")]),s._v(" "),t("p",[s._v("ここでは、試しにECDSAの秘密鍵を用いて証明書を作って使用してみます。\nopenssl コマンドで、ECDSAの秘密鍵も生成可能です。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@a0da070e286f:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl ecparam -genkey -name prime256v1 > /etc/nginx/ssl/ecdsa.key")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("生成した鍵を見てみると、形式がRSAのものと異なり、短くなっていることが見て取れるかと思います。\nここから先の手順は、RSAの時と同様です。出力も同様なので省略します。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl req -new -sha256 -key /etc/nginx/ssl/ecdsa.key -out /etc/nginx/ssl/ecdsa.csr")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -req -in /etc/nginx/ssl/ecdsa.csr -out /etc/nginx/ssl/ecdsa.crt -signkey /etc/nginx/ssl/ecdsa.key -days 365")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("出来上がったものを見てみると、Public Key Info が変わっています。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/ecdsa.crt -text")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Subject Public Key Info:\n Public Key Algorithm: id-ecPublicKey\n Public-Key: "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("256")]),s._v(" bit"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n pub:\n 04:6b:b0:f6:cc:b5:8d:6a:b9:93:f2:1d:50:ec:4e:\n "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("59")]),s._v(":82:39:70:33:61:f3:b4:3b:13:42:39:1d:68:27:\n 5a:27:3d:0a:df:14:78:3d:aa:fb:ec:f5:a8:8b:87:\n 3b:bc:cc:f7:47:b3:84:db:85:a5:e5:2e:9c:03:44:\n 8f:37:65:2e:e4\n ASN1 OID: prime256v1\n NIST CURVE: P-256\nSignature Algorithm: ecdsa-with-SHA256\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br")])]),t("p",[s._v("後は、nginx の参照を変更し、この証明書を使ってhttpsを提供してみましょう。")]),s._v(" "),t("div",{staticClass:"language-sh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-sh"}},[t("code",[s._v("root@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# nvim /etc/nginx/sites-enabled/default")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("中略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\nserver "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/ecdsa.crt"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# <= ここを書き換え")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/ecdsa.key"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# <= ここを書き換え")]),s._v("\n\n root /var/www/html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("後略"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nroot@34cfcf7b6f05:/"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br")])]),t("p",[s._v("ECDSAの鍵を用いたものでも問題なく接続できるかと思います。")]),s._v(" "),t("p",[s._v("パフォーマンスの観点からはECDSAの方が有利ですが、中にはECDSAに対応していないサービスもあります。\n利用してみたい場合も証明書を使用するサービスで使えるかどうか確認してから発行するようにしましょう。")]),s._v(" "),t("h4",{attrs:{id:"安全とされている設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#安全とされている設定"}},[s._v("#")]),s._v(" 安全とされている設定")]),s._v(" "),t("p",[s._v("日本における暗号設定のセキュリティ上の最低ラインは先の暗号設定ガイドラインの3.1に記載されています。\n\n"),t("a",{attrs:{href:"https://www.ipa.go.jp/security/crypto/guideline/ssl_crypt_config.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("TLS 暗号設定ガイドライン"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("表14がわかりやすいでしょう。ビットセキュリティについては、2.6の表11がわかりやすいです。")]),s._v(" "),t("p",[s._v("mozilla の推奨暗号スイートも参考になります。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://wiki.mozilla.org/Security/Server_Side_TLS",target:"_blank",rel:"noopener noreferrer"}},[s._v("mozilla Server Side TLS"),t("OutboundLink")],1)]),s._v(" "),t("p",[s._v("今回のcipher設定についてはmozilla のintermediate のものを利用しています。\n"),t("a",{attrs:{href:"https://ssl-config.mozilla.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("こちら"),t("OutboundLink")],1),s._v("で対応する設定を出力してくれるのも便利です。")])])}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/37.e33dc2ba.js b/assets/js/37.2755cfa3.js similarity index 98% rename from assets/js/37.e33dc2ba.js rename to assets/js/37.2755cfa3.js index 52dd44c7..afc5bb48 100644 --- a/assets/js/37.e33dc2ba.js +++ b/assets/js/37.2755cfa3.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{508:function(t,r,e){"use strict";e.r(r);var a=e(10),o=Object(a.a)({},(function(){var t=this,r=t._self._c;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"iij-bootcampとは"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#iij-bootcampとは"}},[t._v("#")]),t._v(" IIJ Bootcampとは")]),t._v(" "),r("h2",{attrs:{id:"目的"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#目的"}},[t._v("#")]),t._v(" 目的")]),t._v(" "),r("p",[r("RouterLink",{attrs:{to:"/"}},[t._v("IIJ Bootcamp")]),t._v("は "),r("a",{attrs:{href:"https://www.iij.ad.jp/",target:"_blank",rel:"noopener noreferrer"}},[t._v("IIJ"),r("OutboundLink")],1),t._v(" で開催しているハンズオン勉強会です。\n各技術が誕生した経緯・歴史、ほかの技術と比較といった知識を得るためのきっかけとして、さまざまな言語・フレームワーク・ツールに触れて実際に動かすハンズオンを行っています。\nカリキュラムにはハンズオンだけでなく、「overview」として技術ジャンルの全体像や歴史などを紹介する回も設けています。")],1),t._v(" "),r("p",[t._v("このサイトでは"),r("RouterLink",{attrs:{to:"/"}},[t._v("IIJ Bootcamp")]),t._v("用に社内で作成したハンズオン資料を"),r("a",{attrs:{href:"https://creativecommons.org/licenses/by-sa/4.0/",target:"_blank",rel:"noopener noreferrer"}},[t._v("CC BY-SA"),r("OutboundLink")],1),t._v("ライセンスで公開しています(GitHubリポジトリは"),r("a",{attrs:{href:"https://github.com/iij/bootcamp",target:"_blank",rel:"noopener noreferrer"}},[t._v("こちら"),r("OutboundLink")],1),t._v(")。")],1),t._v(" "),r("p",[t._v("カリキュラムの全体像や資料へのリンクは"),r("RouterLink",{attrs:{to:"/"}},[t._v("トップページ")]),t._v("をご覧ください。")],1),t._v(" "),r("h2",{attrs:{id:"資料の構成"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#資料の構成"}},[t._v("#")]),t._v(" 資料の構成")]),t._v(" "),r("p",[t._v("ハンズオン資料は以下のカテゴリに分かれています。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th",[t._v("カテゴリ")]),t._v(" "),r("th",[t._v("概要")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[t._v("開発系")]),t._v(" "),r("td",[t._v("開発に必須となるGitやdockerのハンズオン")])]),t._v(" "),r("tr",[r("td",[t._v("CI/CD + 構成管理")]),t._v(" "),r("td",[t._v("CIサーバのハンズオンとansibleやk8sによるアプリケーションデプロイ")])]),t._v(" "),r("tr",[r("td",[t._v("データベース")]),t._v(" "),r("td",[t._v("MySQL, MongoDB, Redis ハンズオン")])]),t._v(" "),r("tr",[r("td",[t._v("Webサーバ構築")]),t._v(" "),r("td",[t._v("Apacheやnginxを使ったWebサーバの構築")])]),t._v(" "),r("tr",[r("td",[t._v("サーバサイドアプリケーション")]),t._v(" "),r("td",[t._v("DjangoやJava、golangを使ったサーバサイドアプリケーションの構築")])]),t._v(" "),r("tr",[r("td",[t._v("フロントエンド")]),t._v(" "),r("td",[t._v("流行りのWebフレームワークを一通り触ってみる")])]),t._v(" "),r("tr",[r("td",[t._v("セキュリティ")]),t._v(" "),r("td",[t._v("Webにおけるセキュリティについてと、脆弱なサーバを作らないためのハンズオン")])])])]),t._v(" "),r("h2",{attrs:{id:"資料の利用と修正"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#資料の利用と修正"}},[t._v("#")]),t._v(" 資料の利用と修正")]),t._v(" "),r("p",[t._v("資料は "),r("a",{attrs:{href:"https://github.com/iij/bootcamp/blob/master/LICENSE.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("CC BY-SAライセンス"),r("OutboundLink")],1),t._v(" で公開されており、勉強会などでの2次利用が可能です。その際資料の間違いや最新情報への更新などが必要な場合は、"),r("a",{attrs:{href:"https://github.com/iij/bootcamp/issues",target:"_blank",rel:"noopener noreferrer"}},[t._v("issue"),r("OutboundLink")],1),t._v("で気軽にお知らせください。")]),t._v(" "),r("p",[t._v("またPullRequestについても歓迎しています。その際は "),r("a",{attrs:{href:"https://github.com/iij/bootcamp/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("CONTRIBUTING.md"),r("OutboundLink")],1),t._v(" をご一読ください。")])])}),[],!1,null,null,null);r.default=o.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{509:function(t,r,e){"use strict";e.r(r);var a=e(10),o=Object(a.a)({},(function(){var t=this,r=t._self._c;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"iij-bootcampとは"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#iij-bootcampとは"}},[t._v("#")]),t._v(" IIJ Bootcampとは")]),t._v(" "),r("h2",{attrs:{id:"目的"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#目的"}},[t._v("#")]),t._v(" 目的")]),t._v(" "),r("p",[r("RouterLink",{attrs:{to:"/"}},[t._v("IIJ Bootcamp")]),t._v("は "),r("a",{attrs:{href:"https://www.iij.ad.jp/",target:"_blank",rel:"noopener noreferrer"}},[t._v("IIJ"),r("OutboundLink")],1),t._v(" で開催しているハンズオン勉強会です。\n各技術が誕生した経緯・歴史、ほかの技術と比較といった知識を得るためのきっかけとして、さまざまな言語・フレームワーク・ツールに触れて実際に動かすハンズオンを行っています。\nカリキュラムにはハンズオンだけでなく、「overview」として技術ジャンルの全体像や歴史などを紹介する回も設けています。")],1),t._v(" "),r("p",[t._v("このサイトでは"),r("RouterLink",{attrs:{to:"/"}},[t._v("IIJ Bootcamp")]),t._v("用に社内で作成したハンズオン資料を"),r("a",{attrs:{href:"https://creativecommons.org/licenses/by-sa/4.0/",target:"_blank",rel:"noopener noreferrer"}},[t._v("CC BY-SA"),r("OutboundLink")],1),t._v("ライセンスで公開しています(GitHubリポジトリは"),r("a",{attrs:{href:"https://github.com/iij/bootcamp",target:"_blank",rel:"noopener noreferrer"}},[t._v("こちら"),r("OutboundLink")],1),t._v(")。")],1),t._v(" "),r("p",[t._v("カリキュラムの全体像や資料へのリンクは"),r("RouterLink",{attrs:{to:"/"}},[t._v("トップページ")]),t._v("をご覧ください。")],1),t._v(" "),r("h2",{attrs:{id:"資料の構成"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#資料の構成"}},[t._v("#")]),t._v(" 資料の構成")]),t._v(" "),r("p",[t._v("ハンズオン資料は以下のカテゴリに分かれています。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th",[t._v("カテゴリ")]),t._v(" "),r("th",[t._v("概要")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[t._v("開発系")]),t._v(" "),r("td",[t._v("開発に必須となるGitやdockerのハンズオン")])]),t._v(" "),r("tr",[r("td",[t._v("CI/CD + 構成管理")]),t._v(" "),r("td",[t._v("CIサーバのハンズオンとansibleやk8sによるアプリケーションデプロイ")])]),t._v(" "),r("tr",[r("td",[t._v("データベース")]),t._v(" "),r("td",[t._v("MySQL, MongoDB, Redis ハンズオン")])]),t._v(" "),r("tr",[r("td",[t._v("Webサーバ構築")]),t._v(" "),r("td",[t._v("Apacheやnginxを使ったWebサーバの構築")])]),t._v(" "),r("tr",[r("td",[t._v("サーバサイドアプリケーション")]),t._v(" "),r("td",[t._v("DjangoやJava、golangを使ったサーバサイドアプリケーションの構築")])]),t._v(" "),r("tr",[r("td",[t._v("フロントエンド")]),t._v(" "),r("td",[t._v("流行りのWebフレームワークを一通り触ってみる")])]),t._v(" "),r("tr",[r("td",[t._v("セキュリティ")]),t._v(" "),r("td",[t._v("Webにおけるセキュリティについてと、脆弱なサーバを作らないためのハンズオン")])])])]),t._v(" "),r("h2",{attrs:{id:"資料の利用と修正"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#資料の利用と修正"}},[t._v("#")]),t._v(" 資料の利用と修正")]),t._v(" "),r("p",[t._v("資料は "),r("a",{attrs:{href:"https://github.com/iij/bootcamp/blob/master/LICENSE.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("CC BY-SAライセンス"),r("OutboundLink")],1),t._v(" で公開されており、勉強会などでの2次利用が可能です。その際資料の間違いや最新情報への更新などが必要な場合は、"),r("a",{attrs:{href:"https://github.com/iij/bootcamp/issues",target:"_blank",rel:"noopener noreferrer"}},[t._v("issue"),r("OutboundLink")],1),t._v("で気軽にお知らせください。")]),t._v(" "),r("p",[t._v("またPullRequestについても歓迎しています。その際は "),r("a",{attrs:{href:"https://github.com/iij/bootcamp/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("CONTRIBUTING.md"),r("OutboundLink")],1),t._v(" をご一読ください。")])])}),[],!1,null,null,null);r.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/38.4c3ac00e.js b/assets/js/38.854b6729.js similarity index 96% rename from assets/js/38.4c3ac00e.js rename to assets/js/38.854b6729.js index 01466d02..90cd6876 100644 --- a/assets/js/38.4c3ac00e.js +++ b/assets/js/38.854b6729.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{510:function(t,o,n){"use strict";n.r(o);var l=n(10),e=Object(l.a)({},(function(){var t=this,o=t._self._c;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("h2",{attrs:{id:"_6-正しい-playbook-を書くために"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#_6-正しい-playbook-を書くために"}},[t._v("#")]),t._v(" 6. 正しい Playbook を書くために")]),t._v(" "),o("p",[t._v("Ansible とは、IT 自動化ツールです。\nAnsible を使うと言うことはより多くのシステムを簡単に扱えるようにするということになります。\nIT が常に変化するものである以上、Ansible の Playbook もまた常に変化するシステムに対応するべく、加筆修正を加えていくことが欠かせません。")]),t._v(" "),o("p",[t._v("そのために Ansible は Inventory や Roles といった形で分解することができるようなっており、\nまた、それらを git で管理したりすることができるようになっています。")]),t._v(" "),o("p",[t._v("Playbook が様々な形で分解できると言うことは Playbook を複数人でメンテナンスする事ができるということであり、そのためには統一された書式で記載することが欠かせません。")]),t._v(" "),o("ol",{attrs:{start:"5"}},[o("li",[t._v("でご案内したディレクトリ構造のベストプラクティスもその一つですが\nInventory の分け方や変数の記述についても様々な形で記載しています。")])]),t._v(" "),o("p",[t._v("これらの書式チェックに有効な"),o("code",[t._v("ansible-lint")]),t._v("を活用するなど、できる限り「動くから良い」ではなく「他の人が使う可能性がある」と言ったことを考慮し、ベストプラクティスに沿った形で Playbook を作成して頂ければと思います。")]),t._v(" "),o("credit-footer")],1)}),[],!1,null,null,null);o.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{508:function(t,o,n){"use strict";n.r(o);var l=n(10),e=Object(l.a)({},(function(){var t=this,o=t._self._c;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("h2",{attrs:{id:"_6-正しい-playbook-を書くために"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#_6-正しい-playbook-を書くために"}},[t._v("#")]),t._v(" 6. 正しい Playbook を書くために")]),t._v(" "),o("p",[t._v("Ansible とは、IT 自動化ツールです。\nAnsible を使うと言うことはより多くのシステムを簡単に扱えるようにするということになります。\nIT が常に変化するものである以上、Ansible の Playbook もまた常に変化するシステムに対応するべく、加筆修正を加えていくことが欠かせません。")]),t._v(" "),o("p",[t._v("そのために Ansible は Inventory や Roles といった形で分解することができるようなっており、\nまた、それらを git で管理したりすることができるようになっています。")]),t._v(" "),o("p",[t._v("Playbook が様々な形で分解できると言うことは Playbook を複数人でメンテナンスする事ができるということであり、そのためには統一された書式で記載することが欠かせません。")]),t._v(" "),o("ol",{attrs:{start:"5"}},[o("li",[t._v("でご案内したディレクトリ構造のベストプラクティスもその一つですが\nInventory の分け方や変数の記述についても様々な形で記載しています。")])]),t._v(" "),o("p",[t._v("これらの書式チェックに有効な"),o("code",[t._v("ansible-lint")]),t._v("を活用するなど、できる限り「動くから良い」ではなく「他の人が使う可能性がある」と言ったことを考慮し、ベストプラクティスに沿った形で Playbook を作成して頂ければと思います。")]),t._v(" "),o("credit-footer")],1)}),[],!1,null,null,null);o.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/39.527c83d0.js b/assets/js/39.49702a9c.js similarity index 99% rename from assets/js/39.527c83d0.js rename to assets/js/39.49702a9c.js index 673e6de0..dbcb96c1 100644 --- a/assets/js/39.527c83d0.js +++ b/assets/js/39.49702a9c.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{509:function(s,a,n){"use strict";n.r(a);var t=n(10),e=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_2-インベントリ-の作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2-インベントリ-の作成"}},[s._v("#")]),s._v(" 2. インベントリ の作成")]),s._v(" "),a("p",[s._v("ansibleを実行する為にはまずインベントリを作成しなければなりません。\nインベントリとは、Ansible が管理するホストの集合を定義します。\nインベントリを用いることでansibleによって、ホストをまとめて管理することができます。")]),s._v(" "),a("p",[s._v("インベントリでは管理ホストをグループに割り当てることが可能なほか\nグループは子グループを含むことも可能で、ホストは複数のグループのメンバーになることができます。\nまた、インベントリでは、それが定義するホストとグループに適用される、変数の設定も行います。")]),s._v(" "),a("h3",{attrs:{id:"インベントリを作成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#インベントリを作成する"}},[s._v("#")]),s._v(" インベントリを作成する")]),s._v(" "),a("p",[s._v("まずはじめに Inventory ファイルと呼ばれる物を作成します。\nAnsible において、Inventory ファイルは対象を示していて実行に欠かせない要素です。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[s._v("教材"),a("OutboundLink")],1),s._v("のフォルダには ansible フォルダの配下にインベントリファイルを置くためのフォルダ(inventories)があります。\n試しに開いてみましょう。")]),s._v(" "),a("p",[s._v("Ansible の Inventory ファイルは INI 形式に近い記述によって作成されます。\nインベントリファイルの括弧内の見出し("),a("code",[s._v("[app]")]),s._v("など)はグループ名を表し、\nホストをグルーピングすることができます。\nなお、[]に属さないホストはデフォルトである"),a("code",[s._v("all")]),s._v("グループに属することになります。")]),s._v(" "),a("h2",{attrs:{id:"演習-インベントリの作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-インベントリの作成"}},[s._v("#")]),s._v(" [演習]インベントリの作成")]),s._v(" "),a("p",[s._v("では、実際にインベントリファイルを作成してみましょう。\n"),a("code",[s._v("hosts")]),s._v(" というファイルを作成し "),a("code",[s._v("host00")]),s._v(", "),a("code",[s._v("host01")]),s._v(" を追加します。\nグループ名は"),a("code",[s._v("exercise")]),s._v("として下さい。")]),s._v(" "),a("ul",[a("li",[s._v("inventoryファイルを作成する"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vi")]),s._v(" hosts\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("記載内容"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("exercise"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nhost00\nhost01\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])])]),s._v(" "),a("li",[s._v("インベントリの動作確認\n"),a("ul",[a("li",[s._v("先ほど作成したインベントリが正しいことを確かめるために、"),a("code",[s._v("ansible")]),s._v("コマンドで ping モジュールを実行してみます。 コマンドと実行結果は下記のようになるはずです。"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible -i hosts exercise -m "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ping")]),s._v(" -k\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("パスワードを聞かれるため事前準備で設定したパスワード(ansible)を入力します")])])]),s._v(" "),a("li",[s._v("出力結果"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("host01 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\nhost00 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br")])])])]),s._v(" "),a("p",[s._v("正しく実行されれば "),a("em",[s._v("SUCCESS")]),s._v(" と出力されます。")]),s._v(" "),a("h2",{attrs:{id:"発展演習-インベントリを-yaml-で書く"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#発展演習-インベントリを-yaml-で書く"}},[s._v("#")]),s._v(" [発展演習] インベントリを YAML で書く")]),s._v(" "),a("p",[s._v("Ansible のターゲットホストの情報を定義するインベントリファイルは、INI 形式の他にも YAML 形式でも定義できす。")]),s._v(" "),a("p",[s._v("先ほど利用した INI 形式の Inventory ファイルを YAML 形式で記述すると以下の通りになります。\nYAML 形式で記述すると全てのグループが"),a("code",[s._v("all")]),s._v("グループの配下にあることが分かります。")]),s._v(" "),a("div",{staticClass:"language-yaml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("all")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("children")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("exercise")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("host00")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("host01")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br")])]),a("p",[s._v("なお、YAML書式についてはiniファイルのインベントリファイルがあれば以下のようなコマンドで作成することが可能です")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-inventory -i inventories/hosts --list -y\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{510:function(s,a,n){"use strict";n.r(a);var t=n(10),e=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_2-インベントリ-の作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2-インベントリ-の作成"}},[s._v("#")]),s._v(" 2. インベントリ の作成")]),s._v(" "),a("p",[s._v("ansibleを実行する為にはまずインベントリを作成しなければなりません。\nインベントリとは、Ansible が管理するホストの集合を定義します。\nインベントリを用いることでansibleによって、ホストをまとめて管理することができます。")]),s._v(" "),a("p",[s._v("インベントリでは管理ホストをグループに割り当てることが可能なほか\nグループは子グループを含むことも可能で、ホストは複数のグループのメンバーになることができます。\nまた、インベントリでは、それが定義するホストとグループに適用される、変数の設定も行います。")]),s._v(" "),a("h3",{attrs:{id:"インベントリを作成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#インベントリを作成する"}},[s._v("#")]),s._v(" インベントリを作成する")]),s._v(" "),a("p",[s._v("まずはじめに Inventory ファイルと呼ばれる物を作成します。\nAnsible において、Inventory ファイルは対象を示していて実行に欠かせない要素です。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/iij/ansible-exercise",target:"_blank",rel:"noopener noreferrer"}},[s._v("教材"),a("OutboundLink")],1),s._v("のフォルダには ansible フォルダの配下にインベントリファイルを置くためのフォルダ(inventories)があります。\n試しに開いてみましょう。")]),s._v(" "),a("p",[s._v("Ansible の Inventory ファイルは INI 形式に近い記述によって作成されます。\nインベントリファイルの括弧内の見出し("),a("code",[s._v("[app]")]),s._v("など)はグループ名を表し、\nホストをグルーピングすることができます。\nなお、[]に属さないホストはデフォルトである"),a("code",[s._v("all")]),s._v("グループに属することになります。")]),s._v(" "),a("h2",{attrs:{id:"演習-インベントリの作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-インベントリの作成"}},[s._v("#")]),s._v(" [演習]インベントリの作成")]),s._v(" "),a("p",[s._v("では、実際にインベントリファイルを作成してみましょう。\n"),a("code",[s._v("hosts")]),s._v(" というファイルを作成し "),a("code",[s._v("host00")]),s._v(", "),a("code",[s._v("host01")]),s._v(" を追加します。\nグループ名は"),a("code",[s._v("exercise")]),s._v("として下さい。")]),s._v(" "),a("ul",[a("li",[s._v("inventoryファイルを作成する"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vi")]),s._v(" hosts\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("記載内容"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("exercise"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nhost00\nhost01\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br")])])]),s._v(" "),a("li",[s._v("インベントリの動作確認\n"),a("ul",[a("li",[s._v("先ほど作成したインベントリが正しいことを確かめるために、"),a("code",[s._v("ansible")]),s._v("コマンドで ping モジュールを実行してみます。 コマンドと実行結果は下記のようになるはずです。"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible -i hosts exercise -m "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ping")]),s._v(" -k\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("パスワードを聞かれるため事前準備で設定したパスワード(ansible)を入力します")])])]),s._v(" "),a("li",[s._v("出力結果"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("host01 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\nhost00 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br")])])])]),s._v(" "),a("p",[s._v("正しく実行されれば "),a("em",[s._v("SUCCESS")]),s._v(" と出力されます。")]),s._v(" "),a("h2",{attrs:{id:"発展演習-インベントリを-yaml-で書く"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#発展演習-インベントリを-yaml-で書く"}},[s._v("#")]),s._v(" [発展演習] インベントリを YAML で書く")]),s._v(" "),a("p",[s._v("Ansible のターゲットホストの情報を定義するインベントリファイルは、INI 形式の他にも YAML 形式でも定義できす。")]),s._v(" "),a("p",[s._v("先ほど利用した INI 形式の Inventory ファイルを YAML 形式で記述すると以下の通りになります。\nYAML 形式で記述すると全てのグループが"),a("code",[s._v("all")]),s._v("グループの配下にあることが分かります。")]),s._v(" "),a("div",{staticClass:"language-yaml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("all")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("children")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("exercise")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("host00")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("host01")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br")])]),a("p",[s._v("なお、YAML書式についてはiniファイルのインベントリファイルがあれば以下のようなコマンドで作成することが可能です")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-inventory -i inventories/hosts --list -y\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/42.b0391732.js b/assets/js/42.72a6a0d7.js similarity index 99% rename from assets/js/42.b0391732.js rename to assets/js/42.72a6a0d7.js index 6d814a4a..7e52e3b8 100644 --- a/assets/js/42.b0391732.js +++ b/assets/js/42.72a6a0d7.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{513:function(s,a,e){"use strict";e.r(a);var t=e(10),n=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"ansible-とは"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-とは"}},[s._v("#")]),s._v(" Ansible とは")]),s._v(" "),a("p",[s._v("Ansible とは、IT 自動化ツールです。\nAnsible を利用するとシステムの構成、ソフトウェアの展開、より高度な IT タスク (継続的なデプロイメントやダウンタイムなしのローリング更新など) のオーケストレーションが可能になります。")]),s._v(" "),a("p",[s._v("Ansible docs: "),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/index.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.ansible.com/ansible/latest/index.html"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("Ansible is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates.\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("github: "),a("a",{attrs:{href:"https://github.com/ansible/ansible",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://github.com/ansible/ansible"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("Ansible is a radically simple IT automation system. It handles configuration management, application deployment, cloud provisioning, ad-hoc task execution, network automation, and multi-node orchestration. Ansible makes complex changes like zero-downtime rolling updates with load balancers easy. More information on the Ansible website.\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h2",{attrs:{id:"ansible-の特徴"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の特徴"}},[s._v("#")]),s._v(" Ansible の特徴")]),s._v(" "),a("ul",[a("li",[s._v("エージェントレスである\n"),a("ul",[a("li",[s._v("Ansible は対象の管理は基本的に"),a("a",{attrs:{href:"https://www.openssh.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("OpenSSH"),a("OutboundLink")],1),s._v("を利用する為、専用のクライアント/デーモンを必要としません")])])]),s._v(" "),a("li",[s._v("状態を管理しない\n"),a("ul",[a("li",[s._v("Ansible には対象となるマシン情報(Inventory) を持ちますが、そのマシンの状態を保有することはありません。Inventory ファイルは ansible playbooks などで利用するためのものであり、状態を管理・監視することはありません")])])]),s._v(" "),a("li",[s._v("冪等性を持つ\n"),a("ul",[a("li",[s._v("Ansible は playbooks を基本的に何回実行しても結果は同じになります。例えば httpd のインストールタスク等はインストール済みであれば複数回実行しても 2 回目以降は実行済みとして処理され、二度実行されることがありません。")])])])]),s._v(" "),a("p",[s._v("Ansible の記述には基本的に"),a("a",{attrs:{href:"https://yaml.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("YAML"),a("OutboundLink")],1),s._v("と呼ばれる記述(解読)言語が用いられておりユーザにも読み書きしやすいようになっています。")]),s._v(" "),a("h2",{attrs:{id:"ansible-の構成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の構成"}},[s._v("#")]),s._v(" Ansible の構成")]),s._v(" "),a("p",[s._v("Ansibleは、コントロールノードと管理対象ホストという 2 種類のマシンで構成されています。Ansible はコントロールノードからインストールおよび実行されます。")]),s._v(" "),a("p",[s._v("Ansibleはエージェントレス、ということからAnsibleのインストールはコントロールノードのみに行えばよく、管理対象にインストールする必要はありません。")]),s._v(" "),a("p",[s._v("Ansible のインストールはRPMによる提供もされていますが、現在では"),a("code",[s._v("pip")]),s._v("によるインストールが推奨されています。")]),s._v(" "),a("p",[s._v("Ansibleパッケージは長らく単一のパッケージとなっていましたが、2.10 以降、Ansible のパッケージ方針には大幅な変更がなされ、Ansibleの実行部分と、タスクを実行する(モジュール)の分離がなされ、それぞれ別のパッケージで提供されるようになっています。")]),s._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("モジュール一覧"),a("OutboundLink")],1)]),s._v(" "),a("li",[a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/plugins/plugins.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("プラグイン一覧"),a("OutboundLink")],1)])]),s._v(" "),a("h3",{attrs:{id:"ansible-の基本用語"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の基本用語"}},[s._v("#")]),s._v(" Ansible の基本用語")]),s._v(" "),a("p",[s._v("本講義に限らず、Ansible に登場する基本的な用語の解説。\n基本的には以下の引用ですが、一部追加・割愛しています。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://www.redhat.com/ja/explore/ansible/trailmap/yaml/step3",target:"_blank",rel:"noopener noreferrer"}},[s._v("Ansible Trail Map"),a("OutboundLink")],1)]),s._v(" "),a("h4",{attrs:{id:"target-node-host"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#target-node-host"}},[s._v("#")]),s._v(" Target Node / Host")]),s._v(" "),a("p",[s._v("Ansible から操作する対象で、ホストとも表現されます。Ansible では、Target Node に特別なエージェントなどのインストールは必要ありません。Ansible は、Linux サーバやネットワーク機器、クラウド等の様々なものを操作対象にできます。")]),s._v(" "),a("h4",{attrs:{id:"inventory"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#inventory"}},[s._v("#")]),s._v(" Inventory")]),s._v(" "),a("p",[s._v("操作・管理対象とする Target Node を纏めたリストを指します。Inventory 内において Target Node は複数のグループに分類して登録することができます。この Inventory は、Ansible による処理を実施する場合に必要です。")]),s._v(" "),a("h4",{attrs:{id:"roles"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#roles"}},[s._v("#")]),s._v(" Roles")]),s._v(" "),a("p",[s._v("既知のファイル構造に基づいて特定の変数、タスク、およびハンドラーを自動的に読み込むようひとまとめにしたものです。 ロールでコンテンツをグループ化すると、他のユーザーとのロールの共有が容易になります。")]),s._v(" "),a("h4",{attrs:{id:"task"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#task"}},[s._v("#")]),s._v(" Task")]),s._v(" "),a("p",[s._v("Ansible における、処理(ステップ)の最小単位です。実行対象に対してあってほしい状態や実施する処理を記述します。")]),s._v(" "),a("h4",{attrs:{id:"module"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#module"}},[s._v("#")]),s._v(" Module")]),s._v(" "),a("p",[s._v("Ansible において対象に対して何かを実施する際に用いる最小の部品(プログラム)で、Task ごとに一つの Module を指定して利用します。")]),s._v(" "),a("h4",{attrs:{id:"play"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#play"}},[s._v("#")]),s._v(" Play")]),s._v(" "),a("p",[s._v("インベントリ内の対象範囲と実施する 1 つ以上の Task を記載します。")]),s._v(" "),a("h4",{attrs:{id:"playbook"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook"}},[s._v("#")]),s._v(" Playbook")]),s._v(" "),a("p",[s._v("Ansible において、自動化の手順書にあたります。")]),s._v(" "),a("p",[s._v("Playbook の中には、1 つ以上の Play が含まれます。このトレイルマップでは Playbook には 1 つの Play のみの構成となっています。")]),s._v(" "),a("h2",{attrs:{id:"演習-ansible-の導入"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-ansible-の導入"}},[s._v("#")]),s._v(" [演習]Ansible の導入")]),s._v(" "),a("p",[s._v("では、実際にAnsibleを使ってみましょう。")]),s._v(" "),a("h3",{attrs:{id:"ansible-のインストール"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-のインストール"}},[s._v("#")]),s._v(" Ansible のインストール")]),s._v(" "),a("p",[s._v("これまで ansible のインストールは "),a("code",[s._v("dnf(yum)")]),s._v(" によるインストールでした。\nしかしながら昨今のansible は pip にてインストールすることが推奨されています。\n従って、今回は"),a("code",[s._v("pip")]),s._v("を用いてインストールしてみましょう。")]),s._v(" "),a("p",[s._v("ただし、演習環境には"),a("code",[s._v("pip")]),s._v("コマンドがインストールされていない為、\nまずは"),a("code",[s._v("pip")]),s._v("のインストールから行う必要があります。\n以下をそれぞれ実行し、ansible のインストールまで行ってください")]),s._v(" "),a("ul",[a("li",[s._v("pip のインストール"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("dnf "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" python3-pip\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("ansible のインストール"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" ansible\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("ansible のインストール確認"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible --version\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])]),s._v(" "),a("p",[s._v("ここまで実行したならば以下のような出力が得られるはずです。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v(" ansible --version\nansible "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("core "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2.15")]),s._v(".3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n config "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("file")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /ansible/ansible.cfg\n configured module search path "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/root/.ansible/plugins/modules'")]),s._v(", "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/usr/share/ansible/plugins/modules'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n ansible python module location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /usr/local/lib/python3.9/site-packages/ansible\n ansible collection location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /root/.ansible/collections:/usr/share/ansible/collections\n executable location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /usr/local/bin/ansible\n python version "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3.9")]),s._v(".16 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("main, May "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("29")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(", 00:00:00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("GCC "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("11.3")]),s._v(".1 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("20221121")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("Red Hat "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("11.3")]),s._v(".1-4"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("/usr/bin/python3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n jinja version "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(".\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("ansible-core の "),a("code",[s._v("2.5.13")]),s._v("やそれぞれのバージョンには実行する時期によって異なることがありますが問題ありません。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{514:function(s,a,e){"use strict";e.r(a);var t=e(10),n=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"ansible-とは"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-とは"}},[s._v("#")]),s._v(" Ansible とは")]),s._v(" "),a("p",[s._v("Ansible とは、IT 自動化ツールです。\nAnsible を利用するとシステムの構成、ソフトウェアの展開、より高度な IT タスク (継続的なデプロイメントやダウンタイムなしのローリング更新など) のオーケストレーションが可能になります。")]),s._v(" "),a("p",[s._v("Ansible docs: "),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/index.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://docs.ansible.com/ansible/latest/index.html"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("Ansible is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates.\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("github: "),a("a",{attrs:{href:"https://github.com/ansible/ansible",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://github.com/ansible/ansible"),a("OutboundLink")],1)]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("Ansible is a radically simple IT automation system. It handles configuration management, application deployment, cloud provisioning, ad-hoc task execution, network automation, and multi-node orchestration. Ansible makes complex changes like zero-downtime rolling updates with load balancers easy. More information on the Ansible website.\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h2",{attrs:{id:"ansible-の特徴"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の特徴"}},[s._v("#")]),s._v(" Ansible の特徴")]),s._v(" "),a("ul",[a("li",[s._v("エージェントレスである\n"),a("ul",[a("li",[s._v("Ansible は対象の管理は基本的に"),a("a",{attrs:{href:"https://www.openssh.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("OpenSSH"),a("OutboundLink")],1),s._v("を利用する為、専用のクライアント/デーモンを必要としません")])])]),s._v(" "),a("li",[s._v("状態を管理しない\n"),a("ul",[a("li",[s._v("Ansible には対象となるマシン情報(Inventory) を持ちますが、そのマシンの状態を保有することはありません。Inventory ファイルは ansible playbooks などで利用するためのものであり、状態を管理・監視することはありません")])])]),s._v(" "),a("li",[s._v("冪等性を持つ\n"),a("ul",[a("li",[s._v("Ansible は playbooks を基本的に何回実行しても結果は同じになります。例えば httpd のインストールタスク等はインストール済みであれば複数回実行しても 2 回目以降は実行済みとして処理され、二度実行されることがありません。")])])])]),s._v(" "),a("p",[s._v("Ansible の記述には基本的に"),a("a",{attrs:{href:"https://yaml.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("YAML"),a("OutboundLink")],1),s._v("と呼ばれる記述(解読)言語が用いられておりユーザにも読み書きしやすいようになっています。")]),s._v(" "),a("h2",{attrs:{id:"ansible-の構成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の構成"}},[s._v("#")]),s._v(" Ansible の構成")]),s._v(" "),a("p",[s._v("Ansibleは、コントロールノードと管理対象ホストという 2 種類のマシンで構成されています。Ansible はコントロールノードからインストールおよび実行されます。")]),s._v(" "),a("p",[s._v("Ansibleはエージェントレス、ということからAnsibleのインストールはコントロールノードのみに行えばよく、管理対象にインストールする必要はありません。")]),s._v(" "),a("p",[s._v("Ansible のインストールはRPMによる提供もされていますが、現在では"),a("code",[s._v("pip")]),s._v("によるインストールが推奨されています。")]),s._v(" "),a("p",[s._v("Ansibleパッケージは長らく単一のパッケージとなっていましたが、2.10 以降、Ansible のパッケージ方針には大幅な変更がなされ、Ansibleの実行部分と、タスクを実行する(モジュール)の分離がなされ、それぞれ別のパッケージで提供されるようになっています。")]),s._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("モジュール一覧"),a("OutboundLink")],1)]),s._v(" "),a("li",[a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/plugins/plugins.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("プラグイン一覧"),a("OutboundLink")],1)])]),s._v(" "),a("h3",{attrs:{id:"ansible-の基本用語"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-の基本用語"}},[s._v("#")]),s._v(" Ansible の基本用語")]),s._v(" "),a("p",[s._v("本講義に限らず、Ansible に登場する基本的な用語の解説。\n基本的には以下の引用ですが、一部追加・割愛しています。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://www.redhat.com/ja/explore/ansible/trailmap/yaml/step3",target:"_blank",rel:"noopener noreferrer"}},[s._v("Ansible Trail Map"),a("OutboundLink")],1)]),s._v(" "),a("h4",{attrs:{id:"target-node-host"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#target-node-host"}},[s._v("#")]),s._v(" Target Node / Host")]),s._v(" "),a("p",[s._v("Ansible から操作する対象で、ホストとも表現されます。Ansible では、Target Node に特別なエージェントなどのインストールは必要ありません。Ansible は、Linux サーバやネットワーク機器、クラウド等の様々なものを操作対象にできます。")]),s._v(" "),a("h4",{attrs:{id:"inventory"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#inventory"}},[s._v("#")]),s._v(" Inventory")]),s._v(" "),a("p",[s._v("操作・管理対象とする Target Node を纏めたリストを指します。Inventory 内において Target Node は複数のグループに分類して登録することができます。この Inventory は、Ansible による処理を実施する場合に必要です。")]),s._v(" "),a("h4",{attrs:{id:"roles"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#roles"}},[s._v("#")]),s._v(" Roles")]),s._v(" "),a("p",[s._v("既知のファイル構造に基づいて特定の変数、タスク、およびハンドラーを自動的に読み込むようひとまとめにしたものです。 ロールでコンテンツをグループ化すると、他のユーザーとのロールの共有が容易になります。")]),s._v(" "),a("h4",{attrs:{id:"task"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#task"}},[s._v("#")]),s._v(" Task")]),s._v(" "),a("p",[s._v("Ansible における、処理(ステップ)の最小単位です。実行対象に対してあってほしい状態や実施する処理を記述します。")]),s._v(" "),a("h4",{attrs:{id:"module"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#module"}},[s._v("#")]),s._v(" Module")]),s._v(" "),a("p",[s._v("Ansible において対象に対して何かを実施する際に用いる最小の部品(プログラム)で、Task ごとに一つの Module を指定して利用します。")]),s._v(" "),a("h4",{attrs:{id:"play"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#play"}},[s._v("#")]),s._v(" Play")]),s._v(" "),a("p",[s._v("インベントリ内の対象範囲と実施する 1 つ以上の Task を記載します。")]),s._v(" "),a("h4",{attrs:{id:"playbook"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook"}},[s._v("#")]),s._v(" Playbook")]),s._v(" "),a("p",[s._v("Ansible において、自動化の手順書にあたります。")]),s._v(" "),a("p",[s._v("Playbook の中には、1 つ以上の Play が含まれます。このトレイルマップでは Playbook には 1 つの Play のみの構成となっています。")]),s._v(" "),a("h2",{attrs:{id:"演習-ansible-の導入"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-ansible-の導入"}},[s._v("#")]),s._v(" [演習]Ansible の導入")]),s._v(" "),a("p",[s._v("では、実際にAnsibleを使ってみましょう。")]),s._v(" "),a("h3",{attrs:{id:"ansible-のインストール"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-のインストール"}},[s._v("#")]),s._v(" Ansible のインストール")]),s._v(" "),a("p",[s._v("これまで ansible のインストールは "),a("code",[s._v("dnf(yum)")]),s._v(" によるインストールでした。\nしかしながら昨今のansible は pip にてインストールすることが推奨されています。\n従って、今回は"),a("code",[s._v("pip")]),s._v("を用いてインストールしてみましょう。")]),s._v(" "),a("p",[s._v("ただし、演習環境には"),a("code",[s._v("pip")]),s._v("コマンドがインストールされていない為、\nまずは"),a("code",[s._v("pip")]),s._v("のインストールから行う必要があります。\n以下をそれぞれ実行し、ansible のインストールまで行ってください")]),s._v(" "),a("ul",[a("li",[s._v("pip のインストール"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("dnf "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" python3-pip\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("ansible のインストール"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" ansible\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("ansible のインストール確認"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible --version\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])]),s._v(" "),a("p",[s._v("ここまで実行したならば以下のような出力が得られるはずです。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v(" ansible --version\nansible "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("core "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2.15")]),s._v(".3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n config "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("file")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /ansible/ansible.cfg\n configured module search path "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/root/.ansible/plugins/modules'")]),s._v(", "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'/usr/share/ansible/plugins/modules'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n ansible python module location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /usr/local/lib/python3.9/site-packages/ansible\n ansible collection location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /root/.ansible/collections:/usr/share/ansible/collections\n executable location "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" /usr/local/bin/ansible\n python version "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3.9")]),s._v(".16 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("main, May "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("29")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(", 00:00:00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("GCC "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("11.3")]),s._v(".1 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("20221121")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("Red Hat "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("11.3")]),s._v(".1-4"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("/usr/bin/python3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n jinja version "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(".\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br")])]),a("p",[s._v("ansible-core の "),a("code",[s._v("2.5.13")]),s._v("やそれぞれのバージョンには実行する時期によって異なることがありますが問題ありません。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/43.befa18f7.js b/assets/js/43.1b335751.js similarity index 99% rename from assets/js/43.befa18f7.js rename to assets/js/43.1b335751.js index 6c42ece0..a164ae91 100644 --- a/assets/js/43.befa18f7.js +++ b/assets/js/43.1b335751.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[43],{514:function(s,a,n){"use strict";n.r(a);var t=n(10),e=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_3-ansible-設定ファイルの管理"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3-ansible-設定ファイルの管理"}},[s._v("#")]),s._v(" 3. ansible 設定ファイルの管理")]),s._v(" "),a("p",[s._v("先ほど、アドホックにansibleコマンドを実行した際に、"),a("code",[s._v("-k")]),s._v("オプションを使っていました。\nこれはansibleを実行する際にsshのパスワードを入力させるというオプションになります。")]),s._v(" "),a("p",[s._v("Ansibleは通常、sshのログインにパスワードなしの鍵認証を想定しているため、オプション無しで実行した場合は\nsshのパスワードを入力させることはありません。")]),s._v(" "),a("p",[s._v("しかし、今回の演習ではパスワードログインによって実行していますので\n毎回"),a("code",[s._v("-k")]),s._v("オプションを実行するのは手間になります。")]),s._v(" "),a("p",[s._v("これらの設定についてAnsibleでは設定ファイルの設定を変更することでカスタマイズできます。")]),s._v(" "),a("p",[s._v("ansibleの設定ファイルは "),a("code",[s._v("ansible.cfg")]),s._v(" という名前になっています。\n従って今回は設定ファイルを編集し、"),a("code",[s._v("-k")]),s._v("オプションなしでもsshのパスワードを入力させるように変更してみましょう。")]),s._v(" "),a("h2",{attrs:{id:"演習-設定ファイルの編集"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-設定ファイルの編集"}},[s._v("#")]),s._v(" [演習] 設定ファイルの編集")]),s._v(" "),a("p",[s._v("では、実際に設定ファイルを編集してみましょう。\n設定ファイルは "),a("code",[s._v("ansible.cfg")]),s._v(" となります。")]),s._v(" "),a("ul",[a("li",[s._v("ansible.cfg の作成"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vi")]),s._v(" ansible.cfg\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[a("code",[s._v("askpass = True")]),s._v("を "),a("code",[s._v("[defaults]")]),s._v("セクションに記載する"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("defaults"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nask_pass "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" True\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])])]),s._v(" "),a("li",[s._v("動作確認\n"),a("ul",[a("li",[s._v("先ほど作成した設定が正しいことを確かめるために、"),a("code",[s._v("ansible")]),s._v("コマンドで Ping モジュールを実行してみます。 "),a("code",[s._v("-k")]),s._v("オプションを指定しなくてもパスワードを求められるはずです"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible -i inventories/hosts exercise -m "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ping")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("パスワードを聞かれるため事前準備で設定したパスワード(ansible)を入力します")])])]),s._v(" "),a("li",[s._v("出力結果"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("host01 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\nhost00 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br")])])])]),s._v(" "),a("p",[s._v("正しく実行されれば "),a("em",[s._v("SUCCESS")]),s._v(" と出力されます。")]),s._v(" "),a("h2",{attrs:{id:"参考情報"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#参考情報"}},[s._v("#")]),s._v(" 参考情報")]),s._v(" "),a("h3",{attrs:{id:"ansible-cfg-パラメータの確認"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-cfg-パラメータの確認"}},[s._v("#")]),s._v(" ansible.cfg パラメータの確認")]),s._v(" "),a("p",[s._v("Ansibleには様々な設定を施すことが可能な他、デフォルト値が定められている物もあります。\nこれらを確認するにはどうすれば良いのでしょうか。\nまた、どのような設定が可能なのか全ての設定値を覚えておくことは現実的ではありません。")]),s._v(" "),a("p",[s._v("こういったときのために"),a("code",[s._v("ansible-config")]),s._v(" というコマンドを用いることができます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v(" ansible-config\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[43],{513:function(s,a,n){"use strict";n.r(a);var t=n(10),e=Object(t.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_3-ansible-設定ファイルの管理"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3-ansible-設定ファイルの管理"}},[s._v("#")]),s._v(" 3. ansible 設定ファイルの管理")]),s._v(" "),a("p",[s._v("先ほど、アドホックにansibleコマンドを実行した際に、"),a("code",[s._v("-k")]),s._v("オプションを使っていました。\nこれはansibleを実行する際にsshのパスワードを入力させるというオプションになります。")]),s._v(" "),a("p",[s._v("Ansibleは通常、sshのログインにパスワードなしの鍵認証を想定しているため、オプション無しで実行した場合は\nsshのパスワードを入力させることはありません。")]),s._v(" "),a("p",[s._v("しかし、今回の演習ではパスワードログインによって実行していますので\n毎回"),a("code",[s._v("-k")]),s._v("オプションを実行するのは手間になります。")]),s._v(" "),a("p",[s._v("これらの設定についてAnsibleでは設定ファイルの設定を変更することでカスタマイズできます。")]),s._v(" "),a("p",[s._v("ansibleの設定ファイルは "),a("code",[s._v("ansible.cfg")]),s._v(" という名前になっています。\n従って今回は設定ファイルを編集し、"),a("code",[s._v("-k")]),s._v("オプションなしでもsshのパスワードを入力させるように変更してみましょう。")]),s._v(" "),a("h2",{attrs:{id:"演習-設定ファイルの編集"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-設定ファイルの編集"}},[s._v("#")]),s._v(" [演習] 設定ファイルの編集")]),s._v(" "),a("p",[s._v("では、実際に設定ファイルを編集してみましょう。\n設定ファイルは "),a("code",[s._v("ansible.cfg")]),s._v(" となります。")]),s._v(" "),a("ul",[a("li",[s._v("ansible.cfg の作成"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vi")]),s._v(" ansible.cfg\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[a("code",[s._v("askpass = True")]),s._v("を "),a("code",[s._v("[defaults]")]),s._v("セクションに記載する"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("defaults"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nask_pass "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" True\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br")])])]),s._v(" "),a("li",[s._v("動作確認\n"),a("ul",[a("li",[s._v("先ほど作成した設定が正しいことを確かめるために、"),a("code",[s._v("ansible")]),s._v("コマンドで Ping モジュールを実行してみます。 "),a("code",[s._v("-k")]),s._v("オプションを指定しなくてもパスワードを求められるはずです"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible -i inventories/hosts exercise -m "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ping")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])]),s._v(" "),a("li",[s._v("パスワードを聞かれるため事前準備で設定したパスワード(ansible)を入力します")])])]),s._v(" "),a("li",[s._v("出力結果"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("host01 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\nhost00 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" SUCCESS "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ansible_facts"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"discovered_interpreter_python"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/usr/bin/python"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"changed"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" false,\n "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ping"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"pong"')]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br"),a("span",{staticClass:"line-number"},[s._v("2")]),a("br"),a("span",{staticClass:"line-number"},[s._v("3")]),a("br"),a("span",{staticClass:"line-number"},[s._v("4")]),a("br"),a("span",{staticClass:"line-number"},[s._v("5")]),a("br"),a("span",{staticClass:"line-number"},[s._v("6")]),a("br"),a("span",{staticClass:"line-number"},[s._v("7")]),a("br"),a("span",{staticClass:"line-number"},[s._v("8")]),a("br"),a("span",{staticClass:"line-number"},[s._v("9")]),a("br"),a("span",{staticClass:"line-number"},[s._v("10")]),a("br"),a("span",{staticClass:"line-number"},[s._v("11")]),a("br"),a("span",{staticClass:"line-number"},[s._v("12")]),a("br"),a("span",{staticClass:"line-number"},[s._v("13")]),a("br"),a("span",{staticClass:"line-number"},[s._v("14")]),a("br")])])])]),s._v(" "),a("p",[s._v("正しく実行されれば "),a("em",[s._v("SUCCESS")]),s._v(" と出力されます。")]),s._v(" "),a("h2",{attrs:{id:"参考情報"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#参考情報"}},[s._v("#")]),s._v(" 参考情報")]),s._v(" "),a("h3",{attrs:{id:"ansible-cfg-パラメータの確認"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ansible-cfg-パラメータの確認"}},[s._v("#")]),s._v(" ansible.cfg パラメータの確認")]),s._v(" "),a("p",[s._v("Ansibleには様々な設定を施すことが可能な他、デフォルト値が定められている物もあります。\nこれらを確認するにはどうすれば良いのでしょうか。\nまた、どのような設定が可能なのか全ての設定値を覚えておくことは現実的ではありません。")]),s._v(" "),a("p",[s._v("こういったときのために"),a("code",[s._v("ansible-config")]),s._v(" というコマンドを用いることができます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v(" ansible-config\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/44.86fae5ed.js b/assets/js/44.1545b521.js similarity index 99% rename from assets/js/44.86fae5ed.js rename to assets/js/44.1545b521.js index 9b71eebf..9a5a5bf8 100644 --- a/assets/js/44.86fae5ed.js +++ b/assets/js/44.1545b521.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[44],{515:function(s,n,t){"use strict";t.r(n);var a=t(10),e=Object(a.a)({},(function(){var s=this,n=s._self._c;return n("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[n("h2",{attrs:{id:"_5-リバースプロキシの導入-運用"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_5-リバースプロキシの導入-運用"}},[s._v("#")]),s._v(" 5. リバースプロキシの導入/運用")]),s._v(" "),n("p",[s._v("それではいよいよ本格的に Ansible を活用してみます。\nさきほどのセクションで構築したデータベースとアプリケーションを流用し変更を加えてみます。")]),s._v(" "),n("p",[s._v("現在の状態は、ブラウザから直接アプリケーションに"),n("code",[s._v("HTTP")]),s._v("でアクセスしている状態です。\nnginx を導入し、"),n("code",[s._v("HTTPS")]),s._v("でアクセスしてみましょう。")]),s._v(" "),n("p",[s._v("今回、新たに Reverse proxy サーバを構築するにあたっては\n既存の"),n("code",[s._v("site.yml")]),s._v("への追記が必要となります。")]),s._v(" "),n("p",[s._v("前回までで記述ルールのいくつかはご説明致しましたが\n本格的に作業に取り組む前に一つ確認をしておきます。")]),s._v(" "),n("h3",{attrs:{id:"ansible-playbook-ディレクトリ構造"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#ansible-playbook-ディレクトリ構造"}},[s._v("#")]),s._v(" Ansible playbook ディレクトリ構造")]),s._v(" "),n("p",[s._v("作業に取りかかる前にまず、ディレクトリ構造を確認してみましょう。\n下記は教材のディレクトリ構造から Ansible に関わるファイルやディレクトリのみを抜粋しています。")]),s._v(" "),n("p",[s._v("それぞれのファイル・ディレクトリに役割説明を記載しています。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("├── ansible.cfg "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Ansibleの設定ファイル")]),s._v("\n├── inventories "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 管理対象サーバの情報を格納するディレクトリ")]),s._v("\n│ ├── group_vars "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# グループごとの変数ファイルを格納するファイル")]),s._v("\n│ │ └── all.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# すべてのグループに適用される変数ファイル")]),s._v("\n│ └── hosts "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバのIPやSSHのユーザやグループなどを記述するファイル")]),s._v("\n├── playbooks "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 実行用ファイルを格納するディレクトリ。各ファイルはsite.ymlからインポートします。")]),s._v("\n│ ├── acl.yml\n│ ├── app.yml\n│ ├── db.yml\n│ └── rp.yml\n├── roles "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ロールを格納するディレクトリ。Ansibleの実行はロールと呼ばれるまとまりで管理されます。")]),s._v("\n│ └── webapp\n│ ├── defaults\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ロール内で使用される変数のデフォルト値を定義するファイル")]),s._v("\n│ ├── files "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバへ配布したいファイルを格納するディレクトリ")]),s._v("\n│ ├── handlers\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ハンドラタスクを定義するファイル")]),s._v("\n│ ├── tasks\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Ansibleの具体的な処理内容を記述するファイル")]),s._v("\n│ └── templates "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバへ配布したいJinja2テンプレートを使ったファイルを格納するディレクトリ")]),s._v("\n├── site.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# playbookファイル")]),s._v("\n└── vars\n └── proxy.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Proxy配下でハンズオンを実施するためのファイル")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br")])]),n("p",[s._v("なぜ、このような構成にしているかと言うと、\nAnsible ではより多くの人たちが正しく仕えるように推奨の記述方式を定めています。")]),s._v(" "),n("p",[s._v("ディレクトリ構成もその一部であり、Inventory や Role 等は Ansible を実行する上で\nそれぞれのファイルを探すデフォルトパスになっていますので、よほどの理由が無い限り\nベストプラクティスに沿って作成する事が推奨されます。")]),s._v(" "),n("p",[s._v("詳しく知りたい人は Ansible の"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("ベストプラクティス"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h3",{attrs:{id:"ファイル編集"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#ファイル編集"}},[s._v("#")]),s._v(" ファイル編集")]),s._v(" "),n("p",[s._v("それではファイルを作成してみましょう。\nTLS の証明書に加え、いくつかの設定ファイルはすでに用意してあります。\n皆さんには下記 4 つのファイルを編集してもらいます。")]),s._v(" "),n("h4",{attrs:{id:"roles-nginx-tasks-main-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#roles-nginx-tasks-main-yml"}},[s._v("#")]),s._v(" roles/nginx/tasks/main.yml")]),s._v(" "),n("p",[s._v("nginx を構築するためのタスクファイルです。\n"),n("a",{attrs:{href:"http://nginx.org/en/linux_packages.html#RHEL-CentOS",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("のインストール手順に加え、設定ファイルや証明書の配布を行っています。")]),s._v(" "),n("p",[s._v("Ansible はモジュールと呼ばれるものを使って、さまざまな処理を行います。\nAnsible に組込み済みのモジュールは"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("にリストアップされており、各モジュールごとの書き方が載っています。\nモジュールを自作して使うこともできますが、基本的には上記のサイトから行いたい処理に合ったモジュールを探し、タスクファイルを作ります。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("roles/nginx/tasks/main.yml")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" install yum"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("utils\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("yum")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" yum"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("utils\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" present\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("environment")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ proxy_env | default({}) }}"')]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" add Nginx repository\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"etc/yum.repos.d/nginx.repo"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/etc/yum.repos.d/nginx.repo"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0644")]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" install Nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("yum")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"nginx"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" present\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("environment")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ proxy_env | default({}) }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ ansible_check_mode }}"')]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" create ssl directory\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("file")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" directory\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("path")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/ssl\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("owner")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("group")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0600")]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" deploy ssl files\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"etc/nginx/ssl/{{ item }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/etc/nginx/ssl/{{ item }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0400")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("with_items")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" server.crt\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" server.key\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" dhparam.pem\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" remove default config file\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("file")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" absent\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("path")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/conf.d/default.conf\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("notify")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" reload nginx\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" deploy config file\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" etc/nginx/conf.d/app.conf.j2\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/conf.d/app.conf\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("owner")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("group")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0644")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("notify")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" reload nginx\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" start nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("systemd")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" started\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("enabled")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" yes\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br"),n("span",{staticClass:"line-number"},[s._v("24")]),n("br"),n("span",{staticClass:"line-number"},[s._v("25")]),n("br"),n("span",{staticClass:"line-number"},[s._v("26")]),n("br"),n("span",{staticClass:"line-number"},[s._v("27")]),n("br"),n("span",{staticClass:"line-number"},[s._v("28")]),n("br"),n("span",{staticClass:"line-number"},[s._v("29")]),n("br"),n("span",{staticClass:"line-number"},[s._v("30")]),n("br"),n("span",{staticClass:"line-number"},[s._v("31")]),n("br"),n("span",{staticClass:"line-number"},[s._v("32")]),n("br"),n("span",{staticClass:"line-number"},[s._v("33")]),n("br"),n("span",{staticClass:"line-number"},[s._v("34")]),n("br"),n("span",{staticClass:"line-number"},[s._v("35")]),n("br"),n("span",{staticClass:"line-number"},[s._v("36")]),n("br"),n("span",{staticClass:"line-number"},[s._v("37")]),n("br"),n("span",{staticClass:"line-number"},[s._v("38")]),n("br"),n("span",{staticClass:"line-number"},[s._v("39")]),n("br"),n("span",{staticClass:"line-number"},[s._v("40")]),n("br"),n("span",{staticClass:"line-number"},[s._v("41")]),n("br"),n("span",{staticClass:"line-number"},[s._v("42")]),n("br"),n("span",{staticClass:"line-number"},[s._v("43")]),n("br"),n("span",{staticClass:"line-number"},[s._v("44")]),n("br"),n("span",{staticClass:"line-number"},[s._v("45")]),n("br"),n("span",{staticClass:"line-number"},[s._v("46")]),n("br"),n("span",{staticClass:"line-number"},[s._v("47")]),n("br"),n("span",{staticClass:"line-number"},[s._v("48")]),n("br"),n("span",{staticClass:"line-number"},[s._v("49")]),n("br"),n("span",{staticClass:"line-number"},[s._v("50")]),n("br"),n("span",{staticClass:"line-number"},[s._v("51")]),n("br"),n("span",{staticClass:"line-number"},[s._v("52")]),n("br"),n("span",{staticClass:"line-number"},[s._v("53")]),n("br"),n("span",{staticClass:"line-number"},[s._v("54")]),n("br"),n("span",{staticClass:"line-number"},[s._v("55")]),n("br"),n("span",{staticClass:"line-number"},[s._v("56")]),n("br"),n("span",{staticClass:"line-number"},[s._v("57")]),n("br"),n("span",{staticClass:"line-number"},[s._v("58")]),n("br")])]),n("p",[s._v("nginx の構築に使用したモジュールの一覧は以下になります。")]),s._v(" "),n("table",[n("thead",[n("tr",[n("th",{staticStyle:{"text-align":"center"}},[s._v("モジュール名")]),s._v(" "),n("th",{staticStyle:{"text-align":"left"}},[s._v("説明")])])]),s._v(" "),n("tbody",[n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("yum"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("yum コマンドを使って RPM パッケージを操作できるモジュールです。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("file"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバのファイルシステムを操作できるモジュールです。"),n("br"),s._v("ディレクトリやファイルを作成/削除したり、ファイルのオーナーやパーミッションを変更できたりします。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("copy"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバへファイルをコピーできるモジュールです。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/template_module.html#template-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("template"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバへファイルをコピーできるモジュールです。"),n("code",[s._v("copy")]),s._v("との違いはファイル内に"),n("code",[s._v("Jinja2")]),s._v("テンプレートが使える点です。"),n("br"),s._v("ファイル内で変数を使いたい場合はこちらを使います。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/systemd_module.html#systemd-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("systemd"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[n("a",{attrs:{href:"https://wiki.archlinux.jp/index.php/Systemd",target:"_blank",rel:"noopener noreferrer"}},[s._v("systmed"),n("OutboundLink")],1),s._v("によって管理されるプロセスを操作できるモジュールです。"),n("br"),s._v("基本的には Linux 環境でプロセスを常駐させる場合はこれを使います。")])])])]),s._v(" "),n("h4",{attrs:{id:"roles-nginx-templates-etc-nginx-conf-d-app-conf-j2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#roles-nginx-templates-etc-nginx-conf-d-app-conf-j2"}},[s._v("#")]),s._v(" roles/nginx/templates/etc/nginx/conf.d/app.conf.j2")]),s._v(" "),n("p",[s._v("nginx の設定ファイルです。\nファイル内で変数を使用しているので"),n("code",[s._v("templates")]),s._v("下に配置しています。")]),s._v(" "),n("p",[s._v("分かりやすさのために、配置するディレクトリをコピー先のパスと同じにしています。\nAnsible やサーバの構築/運用に慣れないうちは、こうしておくことをお勧めします。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("roles/nginx/templates/etc/nginx/conf.d/app.conf.j2")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-jinja line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[s._v("upstream app_backend {\n {% for app in nginx_backends %}\n server {{ app.host }}:{{ app.port }} max_fails=1 fail_timeout=3s;\n {% endfor %}\n}\n\nserver {\n listen {{ nginx_https_port }} ssl;\n\n ssl_protocols TLSv1.2 TLSv1.3;\n ssl_certificate /etc/nginx/ssl/server.crt;\n ssl_certificate_key /etc/nginx/ssl/server.key;\n ssl_dhparam /etc/nginx/ssl/dhparam.pem;\n\n location / {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-Host $server_name;\n proxy_set_header X-Forwarded-Server $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Port {{ nginx_https_port }};\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_pass http://app_backend;\n }\n\n error_page 404 /404.html;\n\n error_page 500 502 503 504 /50x.html;\n location = /50x.html {\n root /usr/share/nginx/html;\n }\n}\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br"),n("span",{staticClass:"line-number"},[s._v("24")]),n("br"),n("span",{staticClass:"line-number"},[s._v("25")]),n("br"),n("span",{staticClass:"line-number"},[s._v("26")]),n("br"),n("span",{staticClass:"line-number"},[s._v("27")]),n("br"),n("span",{staticClass:"line-number"},[s._v("28")]),n("br"),n("span",{staticClass:"line-number"},[s._v("29")]),n("br"),n("span",{staticClass:"line-number"},[s._v("30")]),n("br"),n("span",{staticClass:"line-number"},[s._v("31")]),n("br"),n("span",{staticClass:"line-number"},[s._v("32")]),n("br")])]),n("p",[s._v("nginx の設定について説明することは主題から外れるので、この講義では行いません。\n興味のある人は nginx の"),n("a",{attrs:{href:"http://nginx.org/en/docs/http/configuring_https_servers.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h4",{attrs:{id:"playbooks-rp-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#playbooks-rp-yml"}},[s._v("#")]),s._v(" playbooks/rp.yml")]),s._v(" "),n("p",[s._v("nginx を構築するための実行ファイルです。\n対象サーバとロールを指定することで、指定したサーバに nginx を構築できます。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("playbooks/rp.yml")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" set up the reverse proxy server\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rp\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("become")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" no\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("gather_facts")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" no\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("vars_files")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" ../vars/proxy.yml\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("roles")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" roles/nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tags")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" nginx\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br")])]),n("h4",{attrs:{id:"site-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#site-yml"}},[s._v("#")]),s._v(" site.yml")]),s._v(" "),n("p",[s._v("最後に、さきほど作った"),n("code",[s._v("playbooks/rp.yml")]),s._v("をインポートする様に教材の"),n("code",[s._v("site.yml")]),s._v("に下記を追記します。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("import_playbook")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" playbooks/rp.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("h3",{attrs:{id:"実行"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#実行"}},[s._v("#")]),s._v(" 実行")]),s._v(" "),n("p",[s._v("さて、ファイルがすべて準備できたらいよいよ Ansible を実行します。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -i inventories/hosts site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[n("code",[s._v("failed=0")]),s._v("と表示されれば実行は成功です。")]),s._v(" "),n("p",[s._v("ブラウザで"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスして確認します。\n自己署名証明書を使っているためブラウザから注意文言が表示されますが、スキップしてください。\nすると、さきほどと同じ画面が表示されます。")]),s._v(" "),n("p",[s._v("ここで、もう一つ注目してほしい部分があります。\nAnsible の実行ログを眺めると、データベースや Java アプリケーションの構築用のタスクもステータスが"),n("code",[s._v("ok")]),s._v("の状態で、実行されているのが分かると思います。\nまたさきほど実行した"),n("code",[s._v("ansible-playbook site.yml")]),s._v("をもう一度実行すると、nginx の構築タスクも含め、すべてのタスクのステータスが"),n("code",[s._v("ok")]),s._v("になります。")]),s._v(" "),n("p",[s._v("ある操作を何回行っても等価であるとき、その操作を"),n("code",[s._v("冪等")]),s._v("であると言います。\n"),n("strong",[s._v("Ansible の良さの 1 つがこの"),n("code",[s._v("冪等")]),s._v("性です。")]),s._v("\n基本的に Ansible は同じタスクを何回実行しても、差分のないタスクは実行されません。\nすなわち、ファイルを変更しない限りホストに対して余計な変更が加わらず、安全です。")]),s._v(" "),n("p",[s._v("しかしこの"),n("code",[s._v("冪等")]),s._v("性は意識していないと壊れてしまう場合があります。\nAnsible を使うときは"),n("code",[s._v("冪等")]),s._v("性に注意を払いましょう。")]),s._v(" "),n("h2",{attrs:{id:"_3-ケース-2-バージョンアップ-スケーリング"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_3-ケース-2-バージョンアップ-スケーリング"}},[s._v("#")]),s._v(" 3. [ケース 2] バージョンアップ & スケーリング")]),s._v(" "),n("p",[s._v("さて、nginx を導入して初期構築タスクを自動化できました。\n次は変数やホスト情報を編集して、運用タスクを自動化してみましょう。")]),s._v(" "),n("h3",{attrs:{id:"バージョンアップ"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#バージョンアップ"}},[s._v("#")]),s._v(" バージョンアップ")]),s._v(" "),n("p",[s._v("まずは nginx のバージョンアップをしてみましょう。")]),s._v(" "),n("p",[s._v("さきほどインストールされた nginx のバージョンは少し古めの"),n("code",[s._v("1.20.0")]),s._v("です。\nこのバージョンは"),n("code",[s._v("roles/nginx/defaults/main.yml")]),s._v("に"),n("code",[s._v("nginx_version")]),s._v("という変数として定義されています。\nまた、下記のコマンドを実行することで、確認することもできます。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible rp1 -m "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("command")]),s._v(" -a "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v("'nginx -V'")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("これを"),n("code",[s._v("1.22.0")]),s._v("にアップデートしてみたいと思います。\n教材の"),n("code",[s._v("inventories/group_vars/all.yml")]),s._v("に下記を追記しましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nginx_version")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" 1.22.0\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("Ansible には変数の優先度が設定されており、同じ名前の変数は優先度の高いほうが有効になります。\nこれを利用することでロールの完全性を担保しつつ、タスクの内容を編集できます。")]),s._v(" "),n("p",[s._v("具体的な優先度は"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("p",[s._v("ファイルの準備ができたら Ansible を実行します。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -i inventories/hosts site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("本実行が完了したら、さきほどの確認コマンドを実行してバージョンが上がったことを確認してみましょう。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible rp1 -m "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("command")]),s._v(" -a "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v("'nginx -V'")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("h3",{attrs:{id:"スケールアップ"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#スケールアップ"}},[s._v("#")]),s._v(" スケールアップ")]),s._v(" "),n("p",[s._v("次はアプリケーションサーバをスケールアップ(増設)してみましょう。\nと言っても増設用のサーバはすでにコンテナとして起動してあるので、皆さんがすべきことは下記 2 つのファイルの編集です。")]),s._v(" "),n("h4",{attrs:{id:"inventories-hosts"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#inventories-hosts"}},[s._v("#")]),s._v(" inventories/hosts")]),s._v(" "),n("p",[s._v("まずは Ansible の管理対象にアプリケーション増設用のサーバ(app2)を追加します。\n教材の"),n("code",[s._v("inventories/hosts")]),s._v("を下記のように追記してください。")]),s._v(" "),n("div",{staticClass:"language-diff line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-diff"}},[n("code",[s._v("[app]\napp1\n"),n("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" app2\n")])])])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br")])]),n("p",[s._v("これで"),n("code",[s._v("app")]),s._v("グループの中に"),n("code",[s._v("app2")]),s._v("サーバを追加できました。")]),s._v(" "),n("p",[s._v("Ansible には管理対象のホストをグルーピングし、設定したグループ単位で Ansible を実行したり、変数を設定できたりします。\nこうすることで、柔軟かつ効率的にサーバを管理できます。\nこの講義ではそれぞれ 1 台しかありませんが、データベースサーバやリバースプロキシサーバもグループに分けられています。")]),s._v(" "),n("p",[s._v("Ansible のグループやホストについての詳細は"),n("RouterLink",{attrs:{to:"hhttps://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#inventory-basics-formats-hosts-and-groups"}},[s._v("公式ドキュメント")]),s._v("をご覧ください。")],1),s._v(" "),n("h4",{attrs:{id:"inventories-group-vars-all-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#inventories-group-vars-all-yml"}},[s._v("#")]),s._v(" inventories/group_vars/all.yml")]),s._v(" "),n("p",[s._v("次に変数ファイルを編集します。\nさきほども編集した"),n("code",[s._v("inventories/group_vars/all.yml")]),s._v("に下記のように追記します。")]),s._v(" "),n("div",{staticClass:"language-diff line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-diff"}},[n("code",[s._v("nginx_backends:\n"),n("span",{pre:!0,attrs:{class:"token unchanged"}},[n("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[s._v(" ")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" - host: 192.0.2.12\n")]),n("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[s._v(" ")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(' port: "{{ server_port }}"\n')])]),n("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" - host: 192.0.2.13\n")]),n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(' port: "{{ server_port }}"\n')])])])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br")])]),n("p",[s._v("これは nginx が通信を経由させるサーバのリストを格納している変数です。\nここに新しく"),n("code",[s._v("app2")]),s._v("サーバの情報を追記することで、nginx が"),n("code",[s._v("app2")]),s._v("サーバにも通信を流してくれます。")]),s._v(" "),n("p",[s._v("nginx はロードバランサ(負荷分散装置)としての機能も備えているので、\nこうすることで今まで"),n("code",[s._v("app1")]),s._v("サーバにしかいかなかった通信が"),n("code",[s._v("app2")]),s._v("にも行くようになり、負荷を分散できます。")]),s._v(" "),n("p",[s._v("nginx の機能について詳しく説明することは主題から外れてしまいますので、この講義では行いません。\nさらに詳しく知りたい人は nginx の"),n("a",{attrs:{href:"http://nginx.org/en/docs/http/load_balancing.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h4",{attrs:{id:"実行-2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#実行-2"}},[s._v("#")]),s._v(" 実行")]),s._v(" "),n("p",[s._v("さて、ファイルの準備ができたら Ansible を実行します。\n今回は事前にチェックを行ってみましょう。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -C site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[n("code",[s._v("dryrun")]),s._v("で結果を確認すると、新たに"),n("code",[s._v("app2")]),s._v("サーバが増えていることが確認できます。\nまた、nginx の設定ファイルの差分も確認できるはずです。")]),s._v(" "),n("p",[n("code",[s._v("failed=0")]),s._v("と表示されていれば問題ありません。\n"),n("code",[s._v("-C(--check)")]),s._v("のオプションを外し、本実行を行いましょう。")]),s._v(" "),n("p",[s._v("コマンドが終了したら、ブラウザで"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスして確認してみましょう。")]),s._v(" "),n("p",[s._v("この Web アプリケーションは動作しているサーバのホスト名を画面に表示しています。\n新しくブラウザを開き"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスすると、ホスト名の表記が変わっているはずです。")]),s._v(" "),n("p",[s._v("これにより、nginx が正常に負荷を分散してくれていることが確認できます。")]),s._v(" "),n("p",[s._v("ここで、もう一つ注目していただきたいところがあります。\nAnsible の実行ログの中に、"),n("code",[s._v("RUNNING HANDLER")]),s._v("という表記が確認できると思います。\nさきほどと同じ様に"),n("code",[s._v("ansible-playbook site.yml")]),s._v("と実行した場合、この表記がなくなると思います。")]),s._v(" "),n("p",[s._v("これは Ansible のハンドラという機能になります。\nこれは、ハンドラを設定したタスクが"),n("code",[s._v("changed")]),s._v("のときにのみ実行されるタスクを設定できる機能です。\n実際に実行されるタスクは各ロールの"),n("code",[s._v("handlers/main.yml")]),s._v("に書かれています。")]),s._v(" "),n("p",[s._v("これにより設定ファイルが変更された場合のみ、アプリケーションやデータベースなどのプロセスを再起動できます。\n影響範囲を限定できるため、安全にホストに対して変更を加えることができます。")]),s._v(" "),n("p",[s._v("詳細は"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#handlers-running-operations-on-change",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h3",{attrs:{id:"解答例"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#解答例"}},[s._v("#")]),s._v(" 解答例")]),s._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/iij/ansible-exercise/tree/solution",target:"_blank",rel:"noopener noreferrer"}},[s._v("教材の solution ブランチ"),n("OutboundLink")],1),s._v("が解答例になっています。\nこの解答例以外にも多くの実現方法がありますが、参考にしてもらえればと思います。")]),s._v(" "),n("p",[s._v("他のファイルについても、この講義を受けた後の最終的なファイルの内容になっています。\nハンズオンを進める中でつまずくことがあれば、参考にしてもらえればと思います。")]),s._v(" "),n("h3",{attrs:{id:"コラム-特定の-playbook-のみ実行する"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#コラム-特定の-playbook-のみ実行する"}},[s._v("#")]),s._v(" コラム 特定の playbook のみ実行する")]),s._v(" "),n("p",[s._v("Ansible には冪等性を担保する為の仕組みが備わっているというのは記述したとおりです。\nしかしながら、実行する前から不要と分かっている作業まで読み込ませ、Ansible に実行判断を任せるというのは\n万が一を気にする運用の場では不安を感じたり、処理時間が惜しいと感じることもあるでしょう。")]),s._v(" "),n("p",[s._v("Ansible ではそのような際に、実行タスクを分けて実行する事も可能になっています。\n今回のケースでは以下のように実行する事で nginx のみのタスクだけ実行する、といった事が可能になります。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -C site.yml -t nginx\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("これはどのように実行しているのでしょうか。\n実は Ansible には"),n("code",[s._v("タグ")]),s._v("という機能があり、タスクやロールなどに任意の値(タグ)を付けることができます。\nこれを利用することで、Ansible 実行時に任意のタスクのみを実行する/しないことができます。\n実はさきほどの"),n("code",[s._v("playbooks/rp.yml")]),s._v("にタグが設定してありました。")]),s._v(" "),n("p",[s._v("タグは増やしすぎても管理がたいへんになるので、ある程度のまとまりやよく使うタスクにのみ付与するのが良いでしょう。\nタグの詳細については"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("credit-footer")],1)}),[],!1,null,null,null);n.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[44],{516:function(s,n,t){"use strict";t.r(n);var a=t(10),e=Object(a.a)({},(function(){var s=this,n=s._self._c;return n("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[n("h2",{attrs:{id:"_5-リバースプロキシの導入-運用"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_5-リバースプロキシの導入-運用"}},[s._v("#")]),s._v(" 5. リバースプロキシの導入/運用")]),s._v(" "),n("p",[s._v("それではいよいよ本格的に Ansible を活用してみます。\nさきほどのセクションで構築したデータベースとアプリケーションを流用し変更を加えてみます。")]),s._v(" "),n("p",[s._v("現在の状態は、ブラウザから直接アプリケーションに"),n("code",[s._v("HTTP")]),s._v("でアクセスしている状態です。\nnginx を導入し、"),n("code",[s._v("HTTPS")]),s._v("でアクセスしてみましょう。")]),s._v(" "),n("p",[s._v("今回、新たに Reverse proxy サーバを構築するにあたっては\n既存の"),n("code",[s._v("site.yml")]),s._v("への追記が必要となります。")]),s._v(" "),n("p",[s._v("前回までで記述ルールのいくつかはご説明致しましたが\n本格的に作業に取り組む前に一つ確認をしておきます。")]),s._v(" "),n("h3",{attrs:{id:"ansible-playbook-ディレクトリ構造"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#ansible-playbook-ディレクトリ構造"}},[s._v("#")]),s._v(" Ansible playbook ディレクトリ構造")]),s._v(" "),n("p",[s._v("作業に取りかかる前にまず、ディレクトリ構造を確認してみましょう。\n下記は教材のディレクトリ構造から Ansible に関わるファイルやディレクトリのみを抜粋しています。")]),s._v(" "),n("p",[s._v("それぞれのファイル・ディレクトリに役割説明を記載しています。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("├── ansible.cfg "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Ansibleの設定ファイル")]),s._v("\n├── inventories "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 管理対象サーバの情報を格納するディレクトリ")]),s._v("\n│ ├── group_vars "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# グループごとの変数ファイルを格納するファイル")]),s._v("\n│ │ └── all.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# すべてのグループに適用される変数ファイル")]),s._v("\n│ └── hosts "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバのIPやSSHのユーザやグループなどを記述するファイル")]),s._v("\n├── playbooks "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 実行用ファイルを格納するディレクトリ。各ファイルはsite.ymlからインポートします。")]),s._v("\n│ ├── acl.yml\n│ ├── app.yml\n│ ├── db.yml\n│ └── rp.yml\n├── roles "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ロールを格納するディレクトリ。Ansibleの実行はロールと呼ばれるまとまりで管理されます。")]),s._v("\n│ └── webapp\n│ ├── defaults\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ロール内で使用される変数のデフォルト値を定義するファイル")]),s._v("\n│ ├── files "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバへ配布したいファイルを格納するディレクトリ")]),s._v("\n│ ├── handlers\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ハンドラタスクを定義するファイル")]),s._v("\n│ ├── tasks\n│ │ └── main.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Ansibleの具体的な処理内容を記述するファイル")]),s._v("\n│ └── templates "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# サーバへ配布したいJinja2テンプレートを使ったファイルを格納するディレクトリ")]),s._v("\n├── site.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# playbookファイル")]),s._v("\n└── vars\n └── proxy.yml "),n("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Proxy配下でハンズオンを実施するためのファイル")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br")])]),n("p",[s._v("なぜ、このような構成にしているかと言うと、\nAnsible ではより多くの人たちが正しく仕えるように推奨の記述方式を定めています。")]),s._v(" "),n("p",[s._v("ディレクトリ構成もその一部であり、Inventory や Role 等は Ansible を実行する上で\nそれぞれのファイルを探すデフォルトパスになっていますので、よほどの理由が無い限り\nベストプラクティスに沿って作成する事が推奨されます。")]),s._v(" "),n("p",[s._v("詳しく知りたい人は Ansible の"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("ベストプラクティス"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h3",{attrs:{id:"ファイル編集"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#ファイル編集"}},[s._v("#")]),s._v(" ファイル編集")]),s._v(" "),n("p",[s._v("それではファイルを作成してみましょう。\nTLS の証明書に加え、いくつかの設定ファイルはすでに用意してあります。\n皆さんには下記 4 つのファイルを編集してもらいます。")]),s._v(" "),n("h4",{attrs:{id:"roles-nginx-tasks-main-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#roles-nginx-tasks-main-yml"}},[s._v("#")]),s._v(" roles/nginx/tasks/main.yml")]),s._v(" "),n("p",[s._v("nginx を構築するためのタスクファイルです。\n"),n("a",{attrs:{href:"http://nginx.org/en/linux_packages.html#RHEL-CentOS",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("のインストール手順に加え、設定ファイルや証明書の配布を行っています。")]),s._v(" "),n("p",[s._v("Ansible はモジュールと呼ばれるものを使って、さまざまな処理を行います。\nAnsible に組込み済みのモジュールは"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("にリストアップされており、各モジュールごとの書き方が載っています。\nモジュールを自作して使うこともできますが、基本的には上記のサイトから行いたい処理に合ったモジュールを探し、タスクファイルを作ります。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("roles/nginx/tasks/main.yml")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" install yum"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("utils\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("yum")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" yum"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("utils\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" present\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("environment")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ proxy_env | default({}) }}"')]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" add Nginx repository\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"etc/yum.repos.d/nginx.repo"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/etc/yum.repos.d/nginx.repo"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0644")]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" install Nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("yum")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"nginx"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" present\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("environment")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ proxy_env | default({}) }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{{ ansible_check_mode }}"')]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" create ssl directory\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("file")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" directory\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("path")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/ssl\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("owner")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("group")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0600")]),s._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" deploy ssl files\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"etc/nginx/ssl/{{ item }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/etc/nginx/ssl/{{ item }}"')]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0400")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("with_items")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" server.crt\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" server.key\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" dhparam.pem\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" remove default config file\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("file")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" absent\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("path")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/conf.d/default.conf\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("notify")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" reload nginx\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" deploy config file\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("template")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("src")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" etc/nginx/conf.d/app.conf.j2\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("dest")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" /etc/nginx/conf.d/app.conf\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("owner")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("group")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" root\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[s._v("0644")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("notify")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" reload nginx\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" start nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("systemd")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("state")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" started\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("enabled")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" yes\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br"),n("span",{staticClass:"line-number"},[s._v("24")]),n("br"),n("span",{staticClass:"line-number"},[s._v("25")]),n("br"),n("span",{staticClass:"line-number"},[s._v("26")]),n("br"),n("span",{staticClass:"line-number"},[s._v("27")]),n("br"),n("span",{staticClass:"line-number"},[s._v("28")]),n("br"),n("span",{staticClass:"line-number"},[s._v("29")]),n("br"),n("span",{staticClass:"line-number"},[s._v("30")]),n("br"),n("span",{staticClass:"line-number"},[s._v("31")]),n("br"),n("span",{staticClass:"line-number"},[s._v("32")]),n("br"),n("span",{staticClass:"line-number"},[s._v("33")]),n("br"),n("span",{staticClass:"line-number"},[s._v("34")]),n("br"),n("span",{staticClass:"line-number"},[s._v("35")]),n("br"),n("span",{staticClass:"line-number"},[s._v("36")]),n("br"),n("span",{staticClass:"line-number"},[s._v("37")]),n("br"),n("span",{staticClass:"line-number"},[s._v("38")]),n("br"),n("span",{staticClass:"line-number"},[s._v("39")]),n("br"),n("span",{staticClass:"line-number"},[s._v("40")]),n("br"),n("span",{staticClass:"line-number"},[s._v("41")]),n("br"),n("span",{staticClass:"line-number"},[s._v("42")]),n("br"),n("span",{staticClass:"line-number"},[s._v("43")]),n("br"),n("span",{staticClass:"line-number"},[s._v("44")]),n("br"),n("span",{staticClass:"line-number"},[s._v("45")]),n("br"),n("span",{staticClass:"line-number"},[s._v("46")]),n("br"),n("span",{staticClass:"line-number"},[s._v("47")]),n("br"),n("span",{staticClass:"line-number"},[s._v("48")]),n("br"),n("span",{staticClass:"line-number"},[s._v("49")]),n("br"),n("span",{staticClass:"line-number"},[s._v("50")]),n("br"),n("span",{staticClass:"line-number"},[s._v("51")]),n("br"),n("span",{staticClass:"line-number"},[s._v("52")]),n("br"),n("span",{staticClass:"line-number"},[s._v("53")]),n("br"),n("span",{staticClass:"line-number"},[s._v("54")]),n("br"),n("span",{staticClass:"line-number"},[s._v("55")]),n("br"),n("span",{staticClass:"line-number"},[s._v("56")]),n("br"),n("span",{staticClass:"line-number"},[s._v("57")]),n("br"),n("span",{staticClass:"line-number"},[s._v("58")]),n("br")])]),n("p",[s._v("nginx の構築に使用したモジュールの一覧は以下になります。")]),s._v(" "),n("table",[n("thead",[n("tr",[n("th",{staticStyle:{"text-align":"center"}},[s._v("モジュール名")]),s._v(" "),n("th",{staticStyle:{"text-align":"left"}},[s._v("説明")])])]),s._v(" "),n("tbody",[n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("yum"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("yum コマンドを使って RPM パッケージを操作できるモジュールです。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("file"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバのファイルシステムを操作できるモジュールです。"),n("br"),s._v("ディレクトリやファイルを作成/削除したり、ファイルのオーナーやパーミッションを変更できたりします。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("copy"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバへファイルをコピーできるモジュールです。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/template_module.html#template-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("template"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[s._v("対象サーバへファイルをコピーできるモジュールです。"),n("code",[s._v("copy")]),s._v("との違いはファイル内に"),n("code",[s._v("Jinja2")]),s._v("テンプレートが使える点です。"),n("br"),s._v("ファイル内で変数を使いたい場合はこちらを使います。")])]),s._v(" "),n("tr",[n("td",{staticStyle:{"text-align":"center"}},[n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/modules/systemd_module.html#systemd-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("systemd"),n("OutboundLink")],1)]),s._v(" "),n("td",{staticStyle:{"text-align":"left"}},[n("a",{attrs:{href:"https://wiki.archlinux.jp/index.php/Systemd",target:"_blank",rel:"noopener noreferrer"}},[s._v("systmed"),n("OutboundLink")],1),s._v("によって管理されるプロセスを操作できるモジュールです。"),n("br"),s._v("基本的には Linux 環境でプロセスを常駐させる場合はこれを使います。")])])])]),s._v(" "),n("h4",{attrs:{id:"roles-nginx-templates-etc-nginx-conf-d-app-conf-j2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#roles-nginx-templates-etc-nginx-conf-d-app-conf-j2"}},[s._v("#")]),s._v(" roles/nginx/templates/etc/nginx/conf.d/app.conf.j2")]),s._v(" "),n("p",[s._v("nginx の設定ファイルです。\nファイル内で変数を使用しているので"),n("code",[s._v("templates")]),s._v("下に配置しています。")]),s._v(" "),n("p",[s._v("分かりやすさのために、配置するディレクトリをコピー先のパスと同じにしています。\nAnsible やサーバの構築/運用に慣れないうちは、こうしておくことをお勧めします。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("roles/nginx/templates/etc/nginx/conf.d/app.conf.j2")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-jinja line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[s._v("upstream app_backend {\n {% for app in nginx_backends %}\n server {{ app.host }}:{{ app.port }} max_fails=1 fail_timeout=3s;\n {% endfor %}\n}\n\nserver {\n listen {{ nginx_https_port }} ssl;\n\n ssl_protocols TLSv1.2 TLSv1.3;\n ssl_certificate /etc/nginx/ssl/server.crt;\n ssl_certificate_key /etc/nginx/ssl/server.key;\n ssl_dhparam /etc/nginx/ssl/dhparam.pem;\n\n location / {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-Host $server_name;\n proxy_set_header X-Forwarded-Server $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Port {{ nginx_https_port }};\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_pass http://app_backend;\n }\n\n error_page 404 /404.html;\n\n error_page 500 502 503 504 /50x.html;\n location = /50x.html {\n root /usr/share/nginx/html;\n }\n}\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br"),n("span",{staticClass:"line-number"},[s._v("11")]),n("br"),n("span",{staticClass:"line-number"},[s._v("12")]),n("br"),n("span",{staticClass:"line-number"},[s._v("13")]),n("br"),n("span",{staticClass:"line-number"},[s._v("14")]),n("br"),n("span",{staticClass:"line-number"},[s._v("15")]),n("br"),n("span",{staticClass:"line-number"},[s._v("16")]),n("br"),n("span",{staticClass:"line-number"},[s._v("17")]),n("br"),n("span",{staticClass:"line-number"},[s._v("18")]),n("br"),n("span",{staticClass:"line-number"},[s._v("19")]),n("br"),n("span",{staticClass:"line-number"},[s._v("20")]),n("br"),n("span",{staticClass:"line-number"},[s._v("21")]),n("br"),n("span",{staticClass:"line-number"},[s._v("22")]),n("br"),n("span",{staticClass:"line-number"},[s._v("23")]),n("br"),n("span",{staticClass:"line-number"},[s._v("24")]),n("br"),n("span",{staticClass:"line-number"},[s._v("25")]),n("br"),n("span",{staticClass:"line-number"},[s._v("26")]),n("br"),n("span",{staticClass:"line-number"},[s._v("27")]),n("br"),n("span",{staticClass:"line-number"},[s._v("28")]),n("br"),n("span",{staticClass:"line-number"},[s._v("29")]),n("br"),n("span",{staticClass:"line-number"},[s._v("30")]),n("br"),n("span",{staticClass:"line-number"},[s._v("31")]),n("br"),n("span",{staticClass:"line-number"},[s._v("32")]),n("br")])]),n("p",[s._v("nginx の設定について説明することは主題から外れるので、この講義では行いません。\n興味のある人は nginx の"),n("a",{attrs:{href:"http://nginx.org/en/docs/http/configuring_https_servers.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h4",{attrs:{id:"playbooks-rp-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#playbooks-rp-yml"}},[s._v("#")]),s._v(" playbooks/rp.yml")]),s._v(" "),n("p",[s._v("nginx を構築するための実行ファイルです。\n対象サーバとロールを指定することで、指定したサーバに nginx を構築できます。")]),s._v(" "),n("p",[s._v("下記の内容を教材の"),n("code",[s._v("playbooks/rp.yml")]),s._v("にコピーしましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("---")]),s._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" set up the reverse proxy server\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" rp\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("become")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" no\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("gather_facts")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" no\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("vars_files")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" ../vars/proxy.yml\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("roles")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("role")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" roles/nginx\n "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tags")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" nginx\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br"),n("span",{staticClass:"line-number"},[s._v("6")]),n("br"),n("span",{staticClass:"line-number"},[s._v("7")]),n("br"),n("span",{staticClass:"line-number"},[s._v("8")]),n("br"),n("span",{staticClass:"line-number"},[s._v("9")]),n("br"),n("span",{staticClass:"line-number"},[s._v("10")]),n("br")])]),n("h4",{attrs:{id:"site-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#site-yml"}},[s._v("#")]),s._v(" site.yml")]),s._v(" "),n("p",[s._v("最後に、さきほど作った"),n("code",[s._v("playbooks/rp.yml")]),s._v("をインポートする様に教材の"),n("code",[s._v("site.yml")]),s._v("に下記を追記します。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("import_playbook")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" playbooks/rp.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("h3",{attrs:{id:"実行"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#実行"}},[s._v("#")]),s._v(" 実行")]),s._v(" "),n("p",[s._v("さて、ファイルがすべて準備できたらいよいよ Ansible を実行します。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -i inventories/hosts site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[n("code",[s._v("failed=0")]),s._v("と表示されれば実行は成功です。")]),s._v(" "),n("p",[s._v("ブラウザで"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスして確認します。\n自己署名証明書を使っているためブラウザから注意文言が表示されますが、スキップしてください。\nすると、さきほどと同じ画面が表示されます。")]),s._v(" "),n("p",[s._v("ここで、もう一つ注目してほしい部分があります。\nAnsible の実行ログを眺めると、データベースや Java アプリケーションの構築用のタスクもステータスが"),n("code",[s._v("ok")]),s._v("の状態で、実行されているのが分かると思います。\nまたさきほど実行した"),n("code",[s._v("ansible-playbook site.yml")]),s._v("をもう一度実行すると、nginx の構築タスクも含め、すべてのタスクのステータスが"),n("code",[s._v("ok")]),s._v("になります。")]),s._v(" "),n("p",[s._v("ある操作を何回行っても等価であるとき、その操作を"),n("code",[s._v("冪等")]),s._v("であると言います。\n"),n("strong",[s._v("Ansible の良さの 1 つがこの"),n("code",[s._v("冪等")]),s._v("性です。")]),s._v("\n基本的に Ansible は同じタスクを何回実行しても、差分のないタスクは実行されません。\nすなわち、ファイルを変更しない限りホストに対して余計な変更が加わらず、安全です。")]),s._v(" "),n("p",[s._v("しかしこの"),n("code",[s._v("冪等")]),s._v("性は意識していないと壊れてしまう場合があります。\nAnsible を使うときは"),n("code",[s._v("冪等")]),s._v("性に注意を払いましょう。")]),s._v(" "),n("h2",{attrs:{id:"_3-ケース-2-バージョンアップ-スケーリング"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_3-ケース-2-バージョンアップ-スケーリング"}},[s._v("#")]),s._v(" 3. [ケース 2] バージョンアップ & スケーリング")]),s._v(" "),n("p",[s._v("さて、nginx を導入して初期構築タスクを自動化できました。\n次は変数やホスト情報を編集して、運用タスクを自動化してみましょう。")]),s._v(" "),n("h3",{attrs:{id:"バージョンアップ"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#バージョンアップ"}},[s._v("#")]),s._v(" バージョンアップ")]),s._v(" "),n("p",[s._v("まずは nginx のバージョンアップをしてみましょう。")]),s._v(" "),n("p",[s._v("さきほどインストールされた nginx のバージョンは少し古めの"),n("code",[s._v("1.20.0")]),s._v("です。\nこのバージョンは"),n("code",[s._v("roles/nginx/defaults/main.yml")]),s._v("に"),n("code",[s._v("nginx_version")]),s._v("という変数として定義されています。\nまた、下記のコマンドを実行することで、確認することもできます。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible rp1 -m "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("command")]),s._v(" -a "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v("'nginx -V'")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("これを"),n("code",[s._v("1.22.0")]),s._v("にアップデートしてみたいと思います。\n教材の"),n("code",[s._v("inventories/group_vars/all.yml")]),s._v("に下記を追記しましょう。")]),s._v(" "),n("div",{staticClass:"language-yml line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-yml"}},[n("code",[n("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("nginx_version")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" 1.22.0\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("Ansible には変数の優先度が設定されており、同じ名前の変数は優先度の高いほうが有効になります。\nこれを利用することでロールの完全性を担保しつつ、タスクの内容を編集できます。")]),s._v(" "),n("p",[s._v("具体的な優先度は"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("p",[s._v("ファイルの準備ができたら Ansible を実行します。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -i inventories/hosts site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("本実行が完了したら、さきほどの確認コマンドを実行してバージョンが上がったことを確認してみましょう。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible rp1 -m "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("command")]),s._v(" -a "),n("span",{pre:!0,attrs:{class:"token string"}},[s._v("'nginx -V'")]),s._v("\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("h3",{attrs:{id:"スケールアップ"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#スケールアップ"}},[s._v("#")]),s._v(" スケールアップ")]),s._v(" "),n("p",[s._v("次はアプリケーションサーバをスケールアップ(増設)してみましょう。\nと言っても増設用のサーバはすでにコンテナとして起動してあるので、皆さんがすべきことは下記 2 つのファイルの編集です。")]),s._v(" "),n("h4",{attrs:{id:"inventories-hosts"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#inventories-hosts"}},[s._v("#")]),s._v(" inventories/hosts")]),s._v(" "),n("p",[s._v("まずは Ansible の管理対象にアプリケーション増設用のサーバ(app2)を追加します。\n教材の"),n("code",[s._v("inventories/hosts")]),s._v("を下記のように追記してください。")]),s._v(" "),n("div",{staticClass:"language-diff line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-diff"}},[n("code",[s._v("[app]\napp1\n"),n("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" app2\n")])])])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br")])]),n("p",[s._v("これで"),n("code",[s._v("app")]),s._v("グループの中に"),n("code",[s._v("app2")]),s._v("サーバを追加できました。")]),s._v(" "),n("p",[s._v("Ansible には管理対象のホストをグルーピングし、設定したグループ単位で Ansible を実行したり、変数を設定できたりします。\nこうすることで、柔軟かつ効率的にサーバを管理できます。\nこの講義ではそれぞれ 1 台しかありませんが、データベースサーバやリバースプロキシサーバもグループに分けられています。")]),s._v(" "),n("p",[s._v("Ansible のグループやホストについての詳細は"),n("RouterLink",{attrs:{to:"hhttps://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#inventory-basics-formats-hosts-and-groups"}},[s._v("公式ドキュメント")]),s._v("をご覧ください。")],1),s._v(" "),n("h4",{attrs:{id:"inventories-group-vars-all-yml"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#inventories-group-vars-all-yml"}},[s._v("#")]),s._v(" inventories/group_vars/all.yml")]),s._v(" "),n("p",[s._v("次に変数ファイルを編集します。\nさきほども編集した"),n("code",[s._v("inventories/group_vars/all.yml")]),s._v("に下記のように追記します。")]),s._v(" "),n("div",{staticClass:"language-diff line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-diff"}},[n("code",[s._v("nginx_backends:\n"),n("span",{pre:!0,attrs:{class:"token unchanged"}},[n("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[s._v(" ")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" - host: 192.0.2.12\n")]),n("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[s._v(" ")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(' port: "{{ server_port }}"\n')])]),n("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(" - host: 192.0.2.13\n")]),n("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),n("span",{pre:!0,attrs:{class:"token line"}},[s._v(' port: "{{ server_port }}"\n')])])])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br"),n("span",{staticClass:"line-number"},[s._v("2")]),n("br"),n("span",{staticClass:"line-number"},[s._v("3")]),n("br"),n("span",{staticClass:"line-number"},[s._v("4")]),n("br"),n("span",{staticClass:"line-number"},[s._v("5")]),n("br")])]),n("p",[s._v("これは nginx が通信を経由させるサーバのリストを格納している変数です。\nここに新しく"),n("code",[s._v("app2")]),s._v("サーバの情報を追記することで、nginx が"),n("code",[s._v("app2")]),s._v("サーバにも通信を流してくれます。")]),s._v(" "),n("p",[s._v("nginx はロードバランサ(負荷分散装置)としての機能も備えているので、\nこうすることで今まで"),n("code",[s._v("app1")]),s._v("サーバにしかいかなかった通信が"),n("code",[s._v("app2")]),s._v("にも行くようになり、負荷を分散できます。")]),s._v(" "),n("p",[s._v("nginx の機能について詳しく説明することは主題から外れてしまいますので、この講義では行いません。\nさらに詳しく知りたい人は nginx の"),n("a",{attrs:{href:"http://nginx.org/en/docs/http/load_balancing.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h4",{attrs:{id:"実行-2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#実行-2"}},[s._v("#")]),s._v(" 実行")]),s._v(" "),n("p",[s._v("さて、ファイルの準備ができたら Ansible を実行します。\n今回は事前にチェックを行ってみましょう。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -C site.yml\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[n("code",[s._v("dryrun")]),s._v("で結果を確認すると、新たに"),n("code",[s._v("app2")]),s._v("サーバが増えていることが確認できます。\nまた、nginx の設定ファイルの差分も確認できるはずです。")]),s._v(" "),n("p",[n("code",[s._v("failed=0")]),s._v("と表示されていれば問題ありません。\n"),n("code",[s._v("-C(--check)")]),s._v("のオプションを外し、本実行を行いましょう。")]),s._v(" "),n("p",[s._v("コマンドが終了したら、ブラウザで"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスして確認してみましょう。")]),s._v(" "),n("p",[s._v("この Web アプリケーションは動作しているサーバのホスト名を画面に表示しています。\n新しくブラウザを開き"),n("a",{attrs:{href:"https://localhost:8443",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443"),n("OutboundLink")],1),s._v("にアクセスすると、ホスト名の表記が変わっているはずです。")]),s._v(" "),n("p",[s._v("これにより、nginx が正常に負荷を分散してくれていることが確認できます。")]),s._v(" "),n("p",[s._v("ここで、もう一つ注目していただきたいところがあります。\nAnsible の実行ログの中に、"),n("code",[s._v("RUNNING HANDLER")]),s._v("という表記が確認できると思います。\nさきほどと同じ様に"),n("code",[s._v("ansible-playbook site.yml")]),s._v("と実行した場合、この表記がなくなると思います。")]),s._v(" "),n("p",[s._v("これは Ansible のハンドラという機能になります。\nこれは、ハンドラを設定したタスクが"),n("code",[s._v("changed")]),s._v("のときにのみ実行されるタスクを設定できる機能です。\n実際に実行されるタスクは各ロールの"),n("code",[s._v("handlers/main.yml")]),s._v("に書かれています。")]),s._v(" "),n("p",[s._v("これにより設定ファイルが変更された場合のみ、アプリケーションやデータベースなどのプロセスを再起動できます。\n影響範囲を限定できるため、安全にホストに対して変更を加えることができます。")]),s._v(" "),n("p",[s._v("詳細は"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#handlers-running-operations-on-change",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("h3",{attrs:{id:"解答例"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#解答例"}},[s._v("#")]),s._v(" 解答例")]),s._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/iij/ansible-exercise/tree/solution",target:"_blank",rel:"noopener noreferrer"}},[s._v("教材の solution ブランチ"),n("OutboundLink")],1),s._v("が解答例になっています。\nこの解答例以外にも多くの実現方法がありますが、参考にしてもらえればと思います。")]),s._v(" "),n("p",[s._v("他のファイルについても、この講義を受けた後の最終的なファイルの内容になっています。\nハンズオンを進める中でつまずくことがあれば、参考にしてもらえればと思います。")]),s._v(" "),n("h3",{attrs:{id:"コラム-特定の-playbook-のみ実行する"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#コラム-特定の-playbook-のみ実行する"}},[s._v("#")]),s._v(" コラム 特定の playbook のみ実行する")]),s._v(" "),n("p",[s._v("Ansible には冪等性を担保する為の仕組みが備わっているというのは記述したとおりです。\nしかしながら、実行する前から不要と分かっている作業まで読み込ませ、Ansible に実行判断を任せるというのは\n万が一を気にする運用の場では不安を感じたり、処理時間が惜しいと感じることもあるでしょう。")]),s._v(" "),n("p",[s._v("Ansible ではそのような際に、実行タスクを分けて実行する事も可能になっています。\n今回のケースでは以下のように実行する事で nginx のみのタスクだけ実行する、といった事が可能になります。")]),s._v(" "),n("div",{staticClass:"language-sh line-numbers-mode"},[n("pre",{pre:!0,attrs:{class:"language-sh"}},[n("code",[s._v("ansible-playbook -C site.yml -t nginx\n")])]),s._v(" "),n("div",{staticClass:"line-numbers-wrapper"},[n("span",{staticClass:"line-number"},[s._v("1")]),n("br")])]),n("p",[s._v("これはどのように実行しているのでしょうか。\n実は Ansible には"),n("code",[s._v("タグ")]),s._v("という機能があり、タスクやロールなどに任意の値(タグ)を付けることができます。\nこれを利用することで、Ansible 実行時に任意のタスクのみを実行する/しないことができます。\n実はさきほどの"),n("code",[s._v("playbooks/rp.yml")]),s._v("にタグが設定してありました。")]),s._v(" "),n("p",[s._v("タグは増やしすぎても管理がたいへんになるので、ある程度のまとまりやよく使うタスクにのみ付与するのが良いでしょう。\nタグの詳細については"),n("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式ドキュメント"),n("OutboundLink")],1),s._v("をご覧ください。")]),s._v(" "),n("credit-footer")],1)}),[],!1,null,null,null);n.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/47.060fe2a7.js b/assets/js/47.ac364221.js similarity index 90% rename from assets/js/47.060fe2a7.js rename to assets/js/47.ac364221.js index f35747cb..0f0f97ce 100644 --- a/assets/js/47.060fe2a7.js +++ b/assets/js/47.ac364221.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{523:function(t,e,r){"use strict";r.r(e);var a=r(10),n=Object(a.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("header-table"),t._v(" "),e("h1",{attrs:{id:"page-frontmatter-title"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[t._v("#")]),t._v(" "+t._s(t.$page.frontmatter.title))]),t._v(" "),e("p",[e("a",{attrs:{href:"https://github.com/isfukuda/bootcamp_mysql/",target:"_blank",rel:"noopener noreferrer"}},[t._v("資料はこちら"),e("OutboundLink")],1)]),t._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{525:function(t,e,r){"use strict";r.r(e);var a=r(10),n=Object(a.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("header-table"),t._v(" "),e("h1",{attrs:{id:"page-frontmatter-title"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[t._v("#")]),t._v(" "+t._s(t.$page.frontmatter.title))]),t._v(" "),e("p",[e("a",{attrs:{href:"https://github.com/isfukuda/bootcamp_mysql/",target:"_blank",rel:"noopener noreferrer"}},[t._v("資料はこちら"),e("OutboundLink")],1)]),t._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/49.97d4cc5a.js b/assets/js/49.2d1b928e.js similarity index 99% rename from assets/js/49.97d4cc5a.js rename to assets/js/49.2d1b928e.js index 87d0763e..7d704bd9 100644 --- a/assets/js/49.97d4cc5a.js +++ b/assets/js/49.2d1b928e.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{528:function(s,t,a){"use strict";a.r(t);var e=a(10),n=Object(e.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),t("p",[s._v("この講義では "),t("code",[s._v("docker compose (docker-compose)")]),s._v("はコマンドを使います。\n従って講義を受けるにあたり、**docker comose(docker-compose)**のインストールを済ませておいてください")]),s._v(" "),t("ul",[t("li",[s._v("環境ごとに インストール方法が異なるので、自身の環境に合わせて導入してください。")]),s._v(" "),t("li",[s._v("インストールが完了したら下記コマンドを実際に入力し、コマンドが実行できるか確認してください。"),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose version\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])])])]),s._v(" "),t("h3",{attrs:{id:"windows-macos向け"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#windows-macos向け"}},[s._v("#")]),s._v(" Windows, macOS向け")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://iij.github.io/bootcamp/init/hello-bootcamp/",target:"_blank",rel:"noopener noreferrer"}},[s._v("ハンズオン事前準備"),t("OutboundLink")],1),s._v(" で Docker Desktop for Windowsや、Docker Desktop for Mac を導入済みであれば、すでにインストールされているはずです。")])]),s._v(" "),t("h3",{attrs:{id:"linux向け"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#linux向け"}},[s._v("#")]),s._v(" Linux向け")]),s._v(" "),t("ul",[t("li",[s._v("以下の手順に従って、"),t("code",[s._v("docker compose")]),s._v(" をインストールしてください。")]),s._v(" "),t("li",[t("a",{attrs:{href:"https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/"),t("OutboundLink")],1)])]),s._v(" "),t("h2",{attrs:{id:"docker-compose-概要"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-compose-概要"}},[s._v("#")]),s._v(" Docker Compose 概要")]),s._v(" "),t("p",[s._v("Docker composeは、Dockerコンテナを管理し、複数のコンテナから成るアプリケーションを定義、実行、管理するためのツールです。\nDocker composeを使用することで、複数のDockerコンテナを1つのアプリケーションとして簡単に扱うことができます。\nDocker composeの主な機能は以下の通りです。")]),s._v(" "),t("ul",[t("li",[s._v("アプリケーションの定義\n"),t("ul",[t("li",[s._v("docker-composeでは、YAML形式のファイルである"),t("code",[s._v("docker-compose.yml")]),s._v("を使用して、アプリケーションの構成やサービスの定義を行います。")]),s._v(" "),t("li",[s._v("このファイルには、各コンテナのイメージ、ポートマッピング、環境変数、ボリュームのマウントなど、アプリケーションの構成情報が含まれます。")])])]),s._v(" "),t("li",[s._v("複数コンテナの一括管理\n"),t("ul",[t("li",[s._v("docker-composeは、複数のコンテナを一括して管理するためのコマンドを提供します。\n"),t("ul",[t("li",[s._v("例えば"),t("code",[s._v("docker-compose up")]),s._v("コマンドを実行すると、"),t("code",[s._v("docker-compose.yml")]),s._v("に定義されたすべてのコンテナが起動します。")]),s._v(" "),t("li",[s._v("同様に"),t("code",[s._v("docker-compose stop")]),s._v("コマンドを使用すると、"),t("code",[s._v("docker-compose.yml")]),s._v("に定義されたすべてのコンテナが停止されます。")])])])])]),s._v(" "),t("li",[s._v("サービス間の依存関係の管理\n"),t("ul",[t("li",[s._v("docker-composeでは、複数のコンテナ間の依存関係を簡単に管理することができます。\n"),t("ul",[t("li",[s._v("例えば、アプリケーションがデータベースコンテナとWebサーバーコンテナから成る場合、データベースが正しく起動した後にWebサーバーが起動するようにする、といったことができます")])])])])]),s._v(" "),t("li",[s._v("環境変数とシークレットの管理\n"),t("ul",[t("li",[s._v("docker-composeでは、環境変数やシークレットの値を"),t("code",[s._v("docker-compose.yml")]),s._v("ファイルに定義することができます\n"),t("ul",[t("li",[s._v("これにより、アプリケーションの設定情報や機密情報を簡単かつ安全に管理することができます。")])])])])]),s._v(" "),t("li",[s._v("スケーリングと更新\n"),t("ul",[t("li",[s._v("docker-composeを使用すると、アプリケーションのスケーリングや更新も簡単に行うことができます。\n"),t("ul",[t("li",[s._v("例えば、"),t("code",[s._v("docker-compose scale")]),s._v("コマンドを使用すると、特定のサービスのコンテナ数をスケールアップまたはスケールダウンすることができます。また、新しいイメージのビルドや既存のコンテナの更新もdocker-composeコマンドで行うことができます。")])])])])])]),s._v(" "),t("p",[s._v("このようにdocker-composeを使用することで、開発、テスト、本番環境など、さまざまな環境でアプリケーションを簡単かつ一貫して管理することができます。")]),s._v(" "),t("h3",{attrs:{id:"依存関係を用いたdocker-compose-の使い方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#依存関係を用いたdocker-compose-の使い方"}},[s._v("#")]),s._v(" 依存関係を用いたDocker compose の使い方")]),s._v(" "),t("p",[s._v("前項で説明したように以下の2つのコンポーネントから構成されるWebサービスをDockerコンテナを用いた場合を想定してみましょう。")]),s._v(" "),t("ul",[t("li",[s._v("フロントエンド(Flask)")]),s._v(" "),t("li",[s._v("バックエンド(Rails)")])]),s._v(" "),t("p",[s._v("通常、この構成のWeb サービスを起動する際、各コンテナを立ち上げるため、"),t("code",[s._v("docker run")]),s._v(" コマンドを2回実行する必要がでてきます。従って停止する際も同様に2回の操作が必要です。")]),s._v(" "),t("p",[s._v("しかし、Docker Compose を用いて管理を行うと、各コンテナの定義をした設定ファイルである「"),t("strong",[s._v("docker-compose.yml")]),s._v("」に基づいて一括管理することが可能となります。\n具体的には、上記の各コンテナの起動・停止などは、"),t("code",[s._v("docker compose")]),s._v(" コマンドを1回実行するだけで済みます。")]),s._v(" "),t("p",[s._v("また、コンテナの起動順序も先にデータベースを起動し、後からWebサーバを起動する、といったように適切な順番で起動することが可能となります。")]),s._v(" "),t("p",[s._v("これらは特に開発やテストなどの際に、サービスの起動停止は複数回繰り返したりする為、作業の効率化につながります。")]),s._v(" "),t("p",[s._v("本講義では、実際に複数コンテナをDocker Composeを用いて管理を行っていきます。Docker Composeを使ったアプリケーションを実行するまでの一般的な流れは以下の通りです。")]),s._v(" "),t("ol",[t("li",[s._v("各コンテナのDockerfile の作成")]),s._v(" "),t("li",[s._v("docker-compose.yml の作成")]),s._v(" "),t("li",[t("code",[s._v("docker compose")]),s._v(" コマンドを使った複数のコンテナの管理")])]),s._v(" "),t("p",[s._v("そこで本講義でも上記の流れに沿って講義を進めていきます。")]),s._v(" "),t("h2",{attrs:{id:"docker-compose-演習1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-compose-演習1"}},[s._v("#")]),s._v(" Docker Compose 演習1")]),s._v(" "),t("p",[s._v("本章では、実際に"),t("code",[s._v("docker compose")]),s._v(" コマンドを使って複数コンテナの立ち上げや停止などをしていただきます。\n今回題材のサービスは、Python製のWebAPIフレームワークとしてFlask を利用したWebアプリケーションです。\nWebアプリケーション自体の作成は本質ではないので、サンプルコードをそのまま利用していただきます。Web アプリケーションの機能は以下の2点です。")]),s._v(" "),t("p",[s._v("作業の過程で作成・取得するそれぞれのファイルは以下のように配置してください")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n├── app.py\n├── docker-compose.yml\n├── Dockerfile\n└── requirements.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"_1-1-サンプルアプリケーションの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-1-サンプルアプリケーションの作成"}},[s._v("#")]),s._v(" 1-1. サンプルアプリケーションの作成")]),s._v(" "),t("p",[s._v("今回は Docker composeの項であるため Pythonアプリケーションについては言及しません。\nアプリケーションはそれぞれ以下から取得してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/app.py -o app.py\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/requirements.txt -o requirements.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h3",{attrs:{id:"_1-2-dockerfile-の作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-2-dockerfile-の作成"}},[s._v("#")]),s._v(" 1-2. Dockerfile の作成")]),s._v(" "),t("p",[s._v("この節では、前述したコンテナのDockerfile を作成します。\nDockerfile の作成については、前講義「Docker を触ってみよう」で行いましたので、各命令などの詳細な説明は割愛します。")]),s._v(" "),t("p",[s._v("以下の内容をファイル名「Dockerfile」で作成してください。")]),s._v(" "),t("div",{staticClass:"language-Dockerfile line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-dockerfile"}},[t("code",[t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("FROM")]),s._v(" python:3.11-slim")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("WORKDIR")]),s._v(" /code")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENV")]),s._v(" FLASK_APP=app.py")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENV")]),s._v(" FLASK_RUN_HOST=0.0.0.0")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" requirements.txt requirements.txt")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("RUN")]),s._v(" pip install -r requirements.txt")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("EXPOSE")]),s._v(" 5000")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" app.py app.py")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("CMD")]),s._v(" ["),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"flask"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"run"')]),s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("h3",{attrs:{id:"_1-3-docker-compose-yml-の作成と解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-3-docker-compose-yml-の作成と解説"}},[s._v("#")]),s._v(" 1-3. docker-compose.yml の作成と解説")]),s._v(" "),t("p",[s._v("次に、以下の内容をファイル名「docker-compose.yml」で新規作成してください。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("version")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'3'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("services")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("web")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("container_name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" iijbootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("flask\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("build")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" .\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - iijbootcamp")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"8088:5000"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# volumes:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - ./index /app/index")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("logging")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("driver")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"json-file"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# defaults if not specified")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("options")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-size")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1m"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-file")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"30"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("redis")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("container_name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" iijbootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("backend\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"redis:alpine"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - iijbootcamp")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("logging")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("driver")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"json-file"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# defaults if not specified")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("options")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-size")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1m"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-file")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"30"')]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# iijbootcamp:")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# external: true")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br")])]),t("p",[s._v("では、ファイルの各設定について見ていきたいと思います。")]),s._v(" "),t("ul",[t("li",[t("p",[t("code",[s._v("version")]),s._v(" では、docker-compose.yml自体のバージョンを設定します。現在の最新版は、3系です。ここで指定するバージョンによって機能の違いが生じるため、調べる際には気を付けてください。")])]),s._v(" "),t("li",[t("p",[t("code",[s._v("services")]),s._v(" では、Docker Compose で管理する各サービスを子要素として定義していきます。本設定の子要素になっている"),t("code",[s._v("web")]),s._v(" と"),t("code",[s._v("redis")]),s._v(" が、それぞれflaskとRedisのWebアプリケーション用のサービスです。ここは、好きな名前を設定できます。")])])]),s._v(" "),t("p",[s._v("次に、"),t("code",[s._v("redis")]),s._v(" の子要素について見ていきます。")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("container_name")]),s._v(" は、作成されるコンテナ名を設定しています。この値やサービス名は、別コンテナからアクセスされる際のホスト名としても利用可能となります。")]),s._v(" "),t("li",[t("code",[s._v("image")]),s._v(" では、Docker イメージを指定します。Dockerfile と同様ローカルにDocker イメージが存在しない場合は、DockerHub などからダウンロードしてきます。")])]),s._v(" "),t("p",[s._v("最後に、"),t("code",[s._v("web")]),s._v(" の子要素について見ていきます。")]),s._v(" "),t("ul",[t("li",[t("p",[t("code",[s._v("build")]),s._v(" は、独自にDockerfile などを用いてDocker イメージを作成する際に使う設定です。"),t("code",[s._v("build")]),s._v(" の子要素に、Dockerfile が格納されているディレクトリを示す"),t("code",[s._v("context")]),s._v(" が必須となっています。また、今回のようにDockerfile に「Dockerfile」以外の名前を使っている場合は、"),t("code",[s._v("dockerfile")]),s._v(" で対象ファイル名を指定する必要があります。")])]),s._v(" "),t("li",[t("p",[t("code",[s._v("ports")]),s._v(" は、ホストとコンテナのポートをマッピングする設定です。今回の場合、backend サービスは、コンテナ内でポート80で起動しているので、ホストのポート8088へアクセスしたらコンテナ内の80に接続されるように設定しています。ここに記載する値は、文字列を推奨します。なぜならば、YAML の仕様では、"),t("code",[s._v("XX:YY")]),s._v(" は、60進数として解釈されてしまうため、意図しない値になる可能性があるためです。")])])]),s._v(" "),t("p",[s._v("その他詳しい機能について知りたい方は、"),t("a",{attrs:{href:"https://docs.docker.com/compose/compose-file/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のリファレンス"),t("OutboundLink")],1),s._v("をご参照ください。")]),s._v(" "),t("h3",{attrs:{id:"_1-4-docker-compose-コマンドを用いて起動する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-4-docker-compose-コマンドを用いて起動する"}},[s._v("#")]),s._v(" 1-4. docker compose コマンドを用いて起動する")]),s._v(" "),t("p",[s._v("では、必要なものはすべて揃ったので、「docker-compose.yml」が存在するディレクトリで、以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose build\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose up\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("初回実行時は必要な image の取得や Dockerfile.backend を利用した docker build などが実行されるため、時間がかかります。")]),s._v(" "),t("p",[s._v("また、もし プロキシ 環境下で 正常に apk 等が成功しない場合は 以下のように "),t("code",[s._v("docker compose build")]),s._v(" してから試してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose build --build-arg "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("https_proxy")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("http://"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("proxy"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(":"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose up\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[t("code",[s._v("docker compose up")]),s._v(" コマンドは、docker-compose.yml ファイルに基づきコンテナを新規作成し、起動するコマンドです。\n"),t("code",[s._v("-d")]),s._v(" オプションを利用することで、デーモンとして起動することが可能です。\nデーモンで起動している際は、ログが表示されなくなってしまうので、見たい場合は"),t("code",[s._v("docker compose logs")]),s._v(" コマンドで閲覧可能です。\nまた、"),t("code",[s._v("-f")]),s._v(" オプションを渡すことで、ログを流し続けることができます。")]),s._v(" "),t("p",[s._v("別のターミナル環境を開いて、「docker-compose.yml」が存在するディレクトリ で以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS\niijbootcamp-backend redis:alpine "docker-entrypoint.s…" redis 8 seconds ago Up 7 seconds 6379/tcp\niijbootcamp-flask solution-web "flask run" web 8 seconds ago Up 7 seconds 0.0.0.0:8088->5000/tcp, :::8088->5000/tcp\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])])]),s._v(" "),t("p",[t("code",[s._v("docker compose ps")]),s._v(" コマンドでは、Docker Compose で管理してる各コンテナの状態を一覧で見ることができます。「State」が「Up」になっていれば立ち上がっている状態です。その他のカラムは"),t("code",[s._v("docker ps")]),s._v(" の意味と同様です。")]),s._v(" "),t("h4",{attrs:{id:"_1-5-webアプリケーションの動作確認方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-5-webアプリケーションの動作確認方法"}},[s._v("#")]),s._v(" 1-5. Webアプリケーションの動作確認方法")]),s._v(" "),t("p",[s._v("無事に起動できたならば "),t("code",[s._v("http://localhost:8088")]),s._v("にて画面が表示される為、ブラウザでアクセスしてみてください。\n内部では表示回数をカウントしているため、リロードなどをする度に数が増える事でしょう。")]),s._v(" "),t("h2",{attrs:{id:"演習2-docker-のネットワークを確認する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習2-docker-のネットワークを確認する"}},[s._v("#")]),s._v(" 演習2 Docker のネットワークを確認する")]),s._v(" "),t("p",[s._v("前章では、Docker Composeでコンテナ間接続を体験しました。\n本章では、Docker がどのようにしてネットワークを構築し、ホストとコンテナやコンテナ間を接続しているのかをご紹介します。")]),s._v(" "),t("h3",{attrs:{id:"_2-1-docker-network-ls"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-docker-network-ls"}},[s._v("#")]),s._v(" 2-1. docker network ls")]),s._v(" "),t("p",[t("code",[s._v("docker network")]),s._v(" コマンドは、Docker ネットワークを管理するためのコマンドです。\n"),t("code",[s._v("ls")]),s._v(" サブコマンドは、Docker が把握しているすべてのネットワーク一覧を表示するコマンドです。\nDocker をインストールすると、自動的に以下の名前の3つのネットワークを作成します。")]),s._v(" "),t("ol",[t("li",[s._v("bridge")]),s._v(" "),t("li",[s._v("none")]),s._v(" "),t("li",[s._v("host")])]),s._v(" "),t("p",[t("code",[s._v("docker run")]),s._v(" コマンドを実行する際に、"),t("code",[s._v("--net")]),s._v(" オプションで、これらの値を設定することができます。\nデフォルト値では、"),t("code",[s._v("bridge")]),s._v(" になっています。Docker がインストールされた今回の環境では、ホストに「"),t("strong",[s._v("docker0")]),s._v("」というブリッジネットワークが表れます。\nこれが「bridge」に接続されており、Docker はデフォルトでこのネットワークにコンテナを接続します。\nそのため、ホストからコンテナへの接続やコンテナ間の接続が可能となります。"),t("code",[s._v("none")]),s._v(" は、ネットワークの接続を必要としないコンテナを作成する際に利用します。\n"),t("code",[s._v("host")]),s._v(" は、コンテナがホストと同じインタフェースやIPアドレスを持たせたい際に利用します。")]),s._v(" "),t("p",[s._v("上記結果の中で、「default」で終わるネットワークは、"),t("code",[s._v("docker compose")]),s._v(" コマンドによって自動的に作成されたネットワークのことです。「default」の前には、プロジェクト名(docker-compose.ymlファイルが存在するディレクトリ名)が利用されます。")]),s._v(" "),t("p",[s._v("それでは、以下のコマンドを実行してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" network "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("NETWORK ID NAME DRIVER SCOPE\n420d7b4e475a bridge bridge local\n24bd91406a30 docker-compose_default bridge local\n7b99e3f7a971 host host local\nf70accdb164f none null local\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])])]),s._v(" "),t("h3",{attrs:{id:"_2-2-docker-networkの詳細を知る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-docker-networkの詳細を知る"}},[s._v("#")]),s._v(" 2-2. docker networkの詳細を知る")]),s._v(" "),t("p",[s._v("では、それぞれのネットワークがどのような空間・アドレスレンジを持っているのか確認するにはどうすればいいでしょうか。\n"),t("code",[s._v("docker network")]),s._v("には"),t("code",[s._v("inspect")]),s._v("というサブコマンドがあり、詳細を確認することができるようになっています。")]),s._v(" "),t("p",[t("code",[s._v("inspect")]),s._v(" サブコマンドでは、引数に取ったネットワークやコンテナの情報を表示できます。本コマンドによって、サブネットやゲートウェイといった情報などが閲覧できます。\n割愛しますが、"),t("code",[s._v("docker compose")]),s._v(" コマンドによって生成されたbridgeネットワークと各コンテナのIPアドレスを"),t("code",[s._v("inspect")]),s._v(" サブコマンドで確認してみると同一ネットワークにいることが確認できると思います。")]),s._v(" "),t("p",[s._v("以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" network inspect bridge\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language-json line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-json"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Name"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bridge"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Id"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"420d7b4e475aa6a17e94a33cbda837af886dafd98339176e0acd31252904aed6"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Created"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"2019-02-21T06:07:03.697181196Z"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Scope"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"local"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Driver"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bridge"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"EnableIPv6"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"IPAM"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Driver"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"default"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Options"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token null keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Config"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Subnet"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"172.17.0.0/16"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Gateway"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"172.17.0.1"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Internal"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Attachable"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Ingress"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"ConfigFrom"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Network"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"ConfigOnly"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Containers"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Options"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.default_bridge"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.enable_icc"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.enable_ip_masquerade"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.host_binding_ipv4"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.name"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"docker0"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.driver.mtu"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1500"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Labels"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br")])])]),s._v(" "),t("h2",{attrs:{id:"_4-まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-まとめ"}},[s._v("#")]),s._v(" 4. まとめ")]),s._v(" "),t("p",[s._v("本講義では、Docker Compose を紹介し、実際に"),t("code",[s._v("docker compose")]),s._v(" コマンドを使って、複数のサービスを管理していただきました。\n複数のDocker コンテナを管理する場合、Docker Compose を用いるとDocker単独で利用するよりも効率的に管理することができるためぜひ利用してください。\nまた、OSS の中ではDocker イメージを始め、"),t("code",[s._v("docker-compose.yml")]),s._v(" を公開しているものも多いため、それらを使って簡単に検証作業や環境構築などを行うことができます。\nぜひ有効活用してみてください。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{529:function(s,t,a){"use strict";a.r(t);var e=a(10),n=Object(e.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[s._v("#")]),s._v(" "+s._s(s.$page.frontmatter.title))]),s._v(" "),t("h2",{attrs:{id:"事前準備"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[s._v("#")]),s._v(" 事前準備")]),s._v(" "),t("p",[s._v("この講義では "),t("code",[s._v("docker compose (docker-compose)")]),s._v("はコマンドを使います。\n従って講義を受けるにあたり、**docker comose(docker-compose)**のインストールを済ませておいてください")]),s._v(" "),t("ul",[t("li",[s._v("環境ごとに インストール方法が異なるので、自身の環境に合わせて導入してください。")]),s._v(" "),t("li",[s._v("インストールが完了したら下記コマンドを実際に入力し、コマンドが実行できるか確認してください。"),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose version\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])])])]),s._v(" "),t("h3",{attrs:{id:"windows-macos向け"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#windows-macos向け"}},[s._v("#")]),s._v(" Windows, macOS向け")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://iij.github.io/bootcamp/init/hello-bootcamp/",target:"_blank",rel:"noopener noreferrer"}},[s._v("ハンズオン事前準備"),t("OutboundLink")],1),s._v(" で Docker Desktop for Windowsや、Docker Desktop for Mac を導入済みであれば、すでにインストールされているはずです。")])]),s._v(" "),t("h3",{attrs:{id:"linux向け"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#linux向け"}},[s._v("#")]),s._v(" Linux向け")]),s._v(" "),t("ul",[t("li",[s._v("以下の手順に従って、"),t("code",[s._v("docker compose")]),s._v(" をインストールしてください。")]),s._v(" "),t("li",[t("a",{attrs:{href:"https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/"),t("OutboundLink")],1)])]),s._v(" "),t("h2",{attrs:{id:"docker-compose-概要"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-compose-概要"}},[s._v("#")]),s._v(" Docker Compose 概要")]),s._v(" "),t("p",[s._v("Docker composeは、Dockerコンテナを管理し、複数のコンテナから成るアプリケーションを定義、実行、管理するためのツールです。\nDocker composeを使用することで、複数のDockerコンテナを1つのアプリケーションとして簡単に扱うことができます。\nDocker composeの主な機能は以下の通りです。")]),s._v(" "),t("ul",[t("li",[s._v("アプリケーションの定義\n"),t("ul",[t("li",[s._v("docker-composeでは、YAML形式のファイルである"),t("code",[s._v("docker-compose.yml")]),s._v("を使用して、アプリケーションの構成やサービスの定義を行います。")]),s._v(" "),t("li",[s._v("このファイルには、各コンテナのイメージ、ポートマッピング、環境変数、ボリュームのマウントなど、アプリケーションの構成情報が含まれます。")])])]),s._v(" "),t("li",[s._v("複数コンテナの一括管理\n"),t("ul",[t("li",[s._v("docker-composeは、複数のコンテナを一括して管理するためのコマンドを提供します。\n"),t("ul",[t("li",[s._v("例えば"),t("code",[s._v("docker-compose up")]),s._v("コマンドを実行すると、"),t("code",[s._v("docker-compose.yml")]),s._v("に定義されたすべてのコンテナが起動します。")]),s._v(" "),t("li",[s._v("同様に"),t("code",[s._v("docker-compose stop")]),s._v("コマンドを使用すると、"),t("code",[s._v("docker-compose.yml")]),s._v("に定義されたすべてのコンテナが停止されます。")])])])])]),s._v(" "),t("li",[s._v("サービス間の依存関係の管理\n"),t("ul",[t("li",[s._v("docker-composeでは、複数のコンテナ間の依存関係を簡単に管理することができます。\n"),t("ul",[t("li",[s._v("例えば、アプリケーションがデータベースコンテナとWebサーバーコンテナから成る場合、データベースが正しく起動した後にWebサーバーが起動するようにする、といったことができます")])])])])]),s._v(" "),t("li",[s._v("環境変数とシークレットの管理\n"),t("ul",[t("li",[s._v("docker-composeでは、環境変数やシークレットの値を"),t("code",[s._v("docker-compose.yml")]),s._v("ファイルに定義することができます\n"),t("ul",[t("li",[s._v("これにより、アプリケーションの設定情報や機密情報を簡単かつ安全に管理することができます。")])])])])]),s._v(" "),t("li",[s._v("スケーリングと更新\n"),t("ul",[t("li",[s._v("docker-composeを使用すると、アプリケーションのスケーリングや更新も簡単に行うことができます。\n"),t("ul",[t("li",[s._v("例えば、"),t("code",[s._v("docker-compose scale")]),s._v("コマンドを使用すると、特定のサービスのコンテナ数をスケールアップまたはスケールダウンすることができます。また、新しいイメージのビルドや既存のコンテナの更新もdocker-composeコマンドで行うことができます。")])])])])])]),s._v(" "),t("p",[s._v("このようにdocker-composeを使用することで、開発、テスト、本番環境など、さまざまな環境でアプリケーションを簡単かつ一貫して管理することができます。")]),s._v(" "),t("h3",{attrs:{id:"依存関係を用いたdocker-compose-の使い方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#依存関係を用いたdocker-compose-の使い方"}},[s._v("#")]),s._v(" 依存関係を用いたDocker compose の使い方")]),s._v(" "),t("p",[s._v("前項で説明したように以下の2つのコンポーネントから構成されるWebサービスをDockerコンテナを用いた場合を想定してみましょう。")]),s._v(" "),t("ul",[t("li",[s._v("フロントエンド(Flask)")]),s._v(" "),t("li",[s._v("バックエンド(Rails)")])]),s._v(" "),t("p",[s._v("通常、この構成のWeb サービスを起動する際、各コンテナを立ち上げるため、"),t("code",[s._v("docker run")]),s._v(" コマンドを2回実行する必要がでてきます。従って停止する際も同様に2回の操作が必要です。")]),s._v(" "),t("p",[s._v("しかし、Docker Compose を用いて管理を行うと、各コンテナの定義をした設定ファイルである「"),t("strong",[s._v("docker-compose.yml")]),s._v("」に基づいて一括管理することが可能となります。\n具体的には、上記の各コンテナの起動・停止などは、"),t("code",[s._v("docker compose")]),s._v(" コマンドを1回実行するだけで済みます。")]),s._v(" "),t("p",[s._v("また、コンテナの起動順序も先にデータベースを起動し、後からWebサーバを起動する、といったように適切な順番で起動することが可能となります。")]),s._v(" "),t("p",[s._v("これらは特に開発やテストなどの際に、サービスの起動停止は複数回繰り返したりする為、作業の効率化につながります。")]),s._v(" "),t("p",[s._v("本講義では、実際に複数コンテナをDocker Composeを用いて管理を行っていきます。Docker Composeを使ったアプリケーションを実行するまでの一般的な流れは以下の通りです。")]),s._v(" "),t("ol",[t("li",[s._v("各コンテナのDockerfile の作成")]),s._v(" "),t("li",[s._v("docker-compose.yml の作成")]),s._v(" "),t("li",[t("code",[s._v("docker compose")]),s._v(" コマンドを使った複数のコンテナの管理")])]),s._v(" "),t("p",[s._v("そこで本講義でも上記の流れに沿って講義を進めていきます。")]),s._v(" "),t("h2",{attrs:{id:"docker-compose-演習1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-compose-演習1"}},[s._v("#")]),s._v(" Docker Compose 演習1")]),s._v(" "),t("p",[s._v("本章では、実際に"),t("code",[s._v("docker compose")]),s._v(" コマンドを使って複数コンテナの立ち上げや停止などをしていただきます。\n今回題材のサービスは、Python製のWebAPIフレームワークとしてFlask を利用したWebアプリケーションです。\nWebアプリケーション自体の作成は本質ではないので、サンプルコードをそのまま利用していただきます。Web アプリケーションの機能は以下の2点です。")]),s._v(" "),t("p",[s._v("作業の過程で作成・取得するそれぞれのファイルは以下のように配置してください")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n├── app.py\n├── docker-compose.yml\n├── Dockerfile\n└── requirements.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])]),t("h3",{attrs:{id:"_1-1-サンプルアプリケーションの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-1-サンプルアプリケーションの作成"}},[s._v("#")]),s._v(" 1-1. サンプルアプリケーションの作成")]),s._v(" "),t("p",[s._v("今回は Docker composeの項であるため Pythonアプリケーションについては言及しません。\nアプリケーションはそれぞれ以下から取得してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/app.py -o app.py\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/requirements.txt -o requirements.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h3",{attrs:{id:"_1-2-dockerfile-の作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-2-dockerfile-の作成"}},[s._v("#")]),s._v(" 1-2. Dockerfile の作成")]),s._v(" "),t("p",[s._v("この節では、前述したコンテナのDockerfile を作成します。\nDockerfile の作成については、前講義「Docker を触ってみよう」で行いましたので、各命令などの詳細な説明は割愛します。")]),s._v(" "),t("p",[s._v("以下の内容をファイル名「Dockerfile」で作成してください。")]),s._v(" "),t("div",{staticClass:"language-Dockerfile line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-dockerfile"}},[t("code",[t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("FROM")]),s._v(" python:3.11-slim")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("WORKDIR")]),s._v(" /code")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENV")]),s._v(" FLASK_APP=app.py")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENV")]),s._v(" FLASK_RUN_HOST=0.0.0.0")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" requirements.txt requirements.txt")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("RUN")]),s._v(" pip install -r requirements.txt")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("EXPOSE")]),s._v(" 5000")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" app.py app.py")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("CMD")]),s._v(" ["),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"flask"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"run"')]),s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("h3",{attrs:{id:"_1-3-docker-compose-yml-の作成と解説"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-3-docker-compose-yml-の作成と解説"}},[s._v("#")]),s._v(" 1-3. docker-compose.yml の作成と解説")]),s._v(" "),t("p",[s._v("次に、以下の内容をファイル名「docker-compose.yml」で新規作成してください。")]),s._v(" "),t("div",{staticClass:"language-yaml line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("version")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'3'")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("services")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("web")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("container_name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" iijbootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("flask\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("build")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" .\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - iijbootcamp")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ports")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"8088:5000"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# volumes:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - ./index /app/index")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("logging")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("driver")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"json-file"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# defaults if not specified")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("options")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-size")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1m"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-file")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"30"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("redis")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("container_name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" iijbootcamp"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v("backend\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("image")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"redis:alpine"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# - iijbootcamp")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("logging")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("driver")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"json-file"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# defaults if not specified")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("options")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-size")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1m"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("max-file")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"30"')]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# networks:")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# iijbootcamp:")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# external: true")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br")])]),t("p",[s._v("では、ファイルの各設定について見ていきたいと思います。")]),s._v(" "),t("ul",[t("li",[t("p",[t("code",[s._v("version")]),s._v(" では、docker-compose.yml自体のバージョンを設定します。現在の最新版は、3系です。ここで指定するバージョンによって機能の違いが生じるため、調べる際には気を付けてください。")])]),s._v(" "),t("li",[t("p",[t("code",[s._v("services")]),s._v(" では、Docker Compose で管理する各サービスを子要素として定義していきます。本設定の子要素になっている"),t("code",[s._v("web")]),s._v(" と"),t("code",[s._v("redis")]),s._v(" が、それぞれflaskとRedisのWebアプリケーション用のサービスです。ここは、好きな名前を設定できます。")])])]),s._v(" "),t("p",[s._v("次に、"),t("code",[s._v("redis")]),s._v(" の子要素について見ていきます。")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("container_name")]),s._v(" は、作成されるコンテナ名を設定しています。この値やサービス名は、別コンテナからアクセスされる際のホスト名としても利用可能となります。")]),s._v(" "),t("li",[t("code",[s._v("image")]),s._v(" では、Docker イメージを指定します。Dockerfile と同様ローカルにDocker イメージが存在しない場合は、DockerHub などからダウンロードしてきます。")])]),s._v(" "),t("p",[s._v("最後に、"),t("code",[s._v("web")]),s._v(" の子要素について見ていきます。")]),s._v(" "),t("ul",[t("li",[t("p",[t("code",[s._v("build")]),s._v(" は、独自にDockerfile などを用いてDocker イメージを作成する際に使う設定です。"),t("code",[s._v("build")]),s._v(" の子要素に、Dockerfile が格納されているディレクトリを示す"),t("code",[s._v("context")]),s._v(" が必須となっています。また、今回のようにDockerfile に「Dockerfile」以外の名前を使っている場合は、"),t("code",[s._v("dockerfile")]),s._v(" で対象ファイル名を指定する必要があります。")])]),s._v(" "),t("li",[t("p",[t("code",[s._v("ports")]),s._v(" は、ホストとコンテナのポートをマッピングする設定です。今回の場合、backend サービスは、コンテナ内でポート80で起動しているので、ホストのポート8088へアクセスしたらコンテナ内の80に接続されるように設定しています。ここに記載する値は、文字列を推奨します。なぜならば、YAML の仕様では、"),t("code",[s._v("XX:YY")]),s._v(" は、60進数として解釈されてしまうため、意図しない値になる可能性があるためです。")])])]),s._v(" "),t("p",[s._v("その他詳しい機能について知りたい方は、"),t("a",{attrs:{href:"https://docs.docker.com/compose/compose-file/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のリファレンス"),t("OutboundLink")],1),s._v("をご参照ください。")]),s._v(" "),t("h3",{attrs:{id:"_1-4-docker-compose-コマンドを用いて起動する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-4-docker-compose-コマンドを用いて起動する"}},[s._v("#")]),s._v(" 1-4. docker compose コマンドを用いて起動する")]),s._v(" "),t("p",[s._v("では、必要なものはすべて揃ったので、「docker-compose.yml」が存在するディレクトリで、以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose build\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose up\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[s._v("初回実行時は必要な image の取得や Dockerfile.backend を利用した docker build などが実行されるため、時間がかかります。")]),s._v(" "),t("p",[s._v("また、もし プロキシ 環境下で 正常に apk 等が成功しない場合は 以下のように "),t("code",[s._v("docker compose build")]),s._v(" してから試してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose build --build-arg "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("https_proxy")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("http://"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("proxy"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(":"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("port"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose up\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])]),t("p",[t("code",[s._v("docker compose up")]),s._v(" コマンドは、docker-compose.yml ファイルに基づきコンテナを新規作成し、起動するコマンドです。\n"),t("code",[s._v("-d")]),s._v(" オプションを利用することで、デーモンとして起動することが可能です。\nデーモンで起動している際は、ログが表示されなくなってしまうので、見たい場合は"),t("code",[s._v("docker compose logs")]),s._v(" コマンドで閲覧可能です。\nまた、"),t("code",[s._v("-f")]),s._v(" オプションを渡すことで、ログを流し続けることができます。")]),s._v(" "),t("p",[s._v("別のターミナル環境を開いて、「docker-compose.yml」が存在するディレクトリ で以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" compose "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS\niijbootcamp-backend redis:alpine "docker-entrypoint.s…" redis 8 seconds ago Up 7 seconds 6379/tcp\niijbootcamp-flask solution-web "flask run" web 8 seconds ago Up 7 seconds 0.0.0.0:8088->5000/tcp, :::8088->5000/tcp\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])])]),s._v(" "),t("p",[t("code",[s._v("docker compose ps")]),s._v(" コマンドでは、Docker Compose で管理してる各コンテナの状態を一覧で見ることができます。「State」が「Up」になっていれば立ち上がっている状態です。その他のカラムは"),t("code",[s._v("docker ps")]),s._v(" の意味と同様です。")]),s._v(" "),t("h4",{attrs:{id:"_1-5-webアプリケーションの動作確認方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-5-webアプリケーションの動作確認方法"}},[s._v("#")]),s._v(" 1-5. Webアプリケーションの動作確認方法")]),s._v(" "),t("p",[s._v("無事に起動できたならば "),t("code",[s._v("http://localhost:8088")]),s._v("にて画面が表示される為、ブラウザでアクセスしてみてください。\n内部では表示回数をカウントしているため、リロードなどをする度に数が増える事でしょう。")]),s._v(" "),t("h2",{attrs:{id:"演習2-docker-のネットワークを確認する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習2-docker-のネットワークを確認する"}},[s._v("#")]),s._v(" 演習2 Docker のネットワークを確認する")]),s._v(" "),t("p",[s._v("前章では、Docker Composeでコンテナ間接続を体験しました。\n本章では、Docker がどのようにしてネットワークを構築し、ホストとコンテナやコンテナ間を接続しているのかをご紹介します。")]),s._v(" "),t("h3",{attrs:{id:"_2-1-docker-network-ls"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-docker-network-ls"}},[s._v("#")]),s._v(" 2-1. docker network ls")]),s._v(" "),t("p",[t("code",[s._v("docker network")]),s._v(" コマンドは、Docker ネットワークを管理するためのコマンドです。\n"),t("code",[s._v("ls")]),s._v(" サブコマンドは、Docker が把握しているすべてのネットワーク一覧を表示するコマンドです。\nDocker をインストールすると、自動的に以下の名前の3つのネットワークを作成します。")]),s._v(" "),t("ol",[t("li",[s._v("bridge")]),s._v(" "),t("li",[s._v("none")]),s._v(" "),t("li",[s._v("host")])]),s._v(" "),t("p",[t("code",[s._v("docker run")]),s._v(" コマンドを実行する際に、"),t("code",[s._v("--net")]),s._v(" オプションで、これらの値を設定することができます。\nデフォルト値では、"),t("code",[s._v("bridge")]),s._v(" になっています。Docker がインストールされた今回の環境では、ホストに「"),t("strong",[s._v("docker0")]),s._v("」というブリッジネットワークが表れます。\nこれが「bridge」に接続されており、Docker はデフォルトでこのネットワークにコンテナを接続します。\nそのため、ホストからコンテナへの接続やコンテナ間の接続が可能となります。"),t("code",[s._v("none")]),s._v(" は、ネットワークの接続を必要としないコンテナを作成する際に利用します。\n"),t("code",[s._v("host")]),s._v(" は、コンテナがホストと同じインタフェースやIPアドレスを持たせたい際に利用します。")]),s._v(" "),t("p",[s._v("上記結果の中で、「default」で終わるネットワークは、"),t("code",[s._v("docker compose")]),s._v(" コマンドによって自動的に作成されたネットワークのことです。「default」の前には、プロジェクト名(docker-compose.ymlファイルが存在するディレクトリ名)が利用されます。")]),s._v(" "),t("p",[s._v("それでは、以下のコマンドを実行してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" network "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ls")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("NETWORK ID NAME DRIVER SCOPE\n420d7b4e475a bridge bridge local\n24bd91406a30 docker-compose_default bridge local\n7b99e3f7a971 host host local\nf70accdb164f none null local\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br")])])]),s._v(" "),t("h3",{attrs:{id:"_2-2-docker-networkの詳細を知る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-docker-networkの詳細を知る"}},[s._v("#")]),s._v(" 2-2. docker networkの詳細を知る")]),s._v(" "),t("p",[s._v("では、それぞれのネットワークがどのような空間・アドレスレンジを持っているのか確認するにはどうすればいいでしょうか。\n"),t("code",[s._v("docker network")]),s._v("には"),t("code",[s._v("inspect")]),s._v("というサブコマンドがあり、詳細を確認することができるようになっています。")]),s._v(" "),t("p",[t("code",[s._v("inspect")]),s._v(" サブコマンドでは、引数に取ったネットワークやコンテナの情報を表示できます。本コマンドによって、サブネットやゲートウェイといった情報などが閲覧できます。\n割愛しますが、"),t("code",[s._v("docker compose")]),s._v(" コマンドによって生成されたbridgeネットワークと各コンテナのIPアドレスを"),t("code",[s._v("inspect")]),s._v(" サブコマンドで確認してみると同一ネットワークにいることが確認できると思います。")]),s._v(" "),t("p",[s._v("以下のコマンドを入力してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" network inspect bridge\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language-json line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-json"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Name"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bridge"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Id"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"420d7b4e475aa6a17e94a33cbda837af886dafd98339176e0acd31252904aed6"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Created"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"2019-02-21T06:07:03.697181196Z"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Scope"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"local"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Driver"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bridge"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"EnableIPv6"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"IPAM"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Driver"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"default"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Options"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token null keyword"}},[s._v("null")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Config"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Subnet"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"172.17.0.0/16"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Gateway"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"172.17.0.1"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Internal"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Attachable"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Ingress"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"ConfigFrom"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Network"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"ConfigOnly"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Containers"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Options"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.default_bridge"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.enable_icc"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.enable_ip_masquerade"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"true"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.host_binding_ipv4"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"0.0.0.0"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.bridge.name"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"docker0"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"com.docker.network.driver.mtu"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1500"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token property"}},[s._v('"Labels"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br"),t("span",{staticClass:"line-number"},[s._v("13")]),t("br"),t("span",{staticClass:"line-number"},[s._v("14")]),t("br"),t("span",{staticClass:"line-number"},[s._v("15")]),t("br"),t("span",{staticClass:"line-number"},[s._v("16")]),t("br"),t("span",{staticClass:"line-number"},[s._v("17")]),t("br"),t("span",{staticClass:"line-number"},[s._v("18")]),t("br"),t("span",{staticClass:"line-number"},[s._v("19")]),t("br"),t("span",{staticClass:"line-number"},[s._v("20")]),t("br"),t("span",{staticClass:"line-number"},[s._v("21")]),t("br"),t("span",{staticClass:"line-number"},[s._v("22")]),t("br"),t("span",{staticClass:"line-number"},[s._v("23")]),t("br"),t("span",{staticClass:"line-number"},[s._v("24")]),t("br"),t("span",{staticClass:"line-number"},[s._v("25")]),t("br"),t("span",{staticClass:"line-number"},[s._v("26")]),t("br"),t("span",{staticClass:"line-number"},[s._v("27")]),t("br"),t("span",{staticClass:"line-number"},[s._v("28")]),t("br"),t("span",{staticClass:"line-number"},[s._v("29")]),t("br"),t("span",{staticClass:"line-number"},[s._v("30")]),t("br"),t("span",{staticClass:"line-number"},[s._v("31")]),t("br"),t("span",{staticClass:"line-number"},[s._v("32")]),t("br"),t("span",{staticClass:"line-number"},[s._v("33")]),t("br"),t("span",{staticClass:"line-number"},[s._v("34")]),t("br"),t("span",{staticClass:"line-number"},[s._v("35")]),t("br"),t("span",{staticClass:"line-number"},[s._v("36")]),t("br"),t("span",{staticClass:"line-number"},[s._v("37")]),t("br")])])]),s._v(" "),t("h2",{attrs:{id:"_4-まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-まとめ"}},[s._v("#")]),s._v(" 4. まとめ")]),s._v(" "),t("p",[s._v("本講義では、Docker Compose を紹介し、実際に"),t("code",[s._v("docker compose")]),s._v(" コマンドを使って、複数のサービスを管理していただきました。\n複数のDocker コンテナを管理する場合、Docker Compose を用いるとDocker単独で利用するよりも効率的に管理することができるためぜひ利用してください。\nまた、OSS の中ではDocker イメージを始め、"),t("code",[s._v("docker-compose.yml")]),s._v(" を公開しているものも多いため、それらを使って簡単に検証作業や環境構築などを行うことができます。\nぜひ有効活用してみてください。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/50.5738bd9f.js b/assets/js/50.187ee7aa.js similarity index 99% rename from assets/js/50.5738bd9f.js rename to assets/js/50.187ee7aa.js index 53e2f5d4..eea9cb3d 100644 --- a/assets/js/50.5738bd9f.js +++ b/assets/js/50.187ee7aa.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[50],{529:function(s,t,a){"use strict";a.r(t);var e=a(10),r=Object(e.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h2",{attrs:{id:"dockerイメージの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerイメージの作成"}},[s._v("#")]),s._v(" Dockerイメージの作成")]),s._v(" "),t("p",[s._v("Docker では、「"),t("strong",[s._v("Dockerfile")]),s._v("」というファイルを用いて、Docker イメージの作成を行うことができます。Dockerfile は専用言語で記述します。このようにコンテナを初めとするサーバ環境やミドルウェアなどをコード(プログラム、設定ファイルなど)で構成管理することを「"),t("strong",[s._v("Infrastructure as Code(IaC)")]),s._v("」と呼びます。Dockerfile のように環境をコード化して管理することにより、以下のようなメリットが挙げられます。")]),s._v(" "),t("ul",[t("li",[s._v("新規メンバーの開発環境が、Dockerfile を共有するだけで構築可能")]),s._v(" "),t("li",[s._v("複雑なインフラ環境がテキストベースで管理でき、git 等での管理が容易に")]),s._v(" "),t("li",[s._v("構築手順などがコード化されるため、漏れや間違いが発見しやすくなる")])]),s._v(" "),t("p",[s._v("今回は、ubuntu というDocker イメージを元にカスタマイズしながら、nginx によるWeb サーバのDocker イメージを作成します。また、作成したDocker イメージを使ってDocker コンテナを立ち上げ、HTML ファイルがレスポンスされることを確認します。")]),s._v(" "),t("h3",{attrs:{id:"演習7-dockerfileを記述し、コンテナを作る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習7-dockerfileを記述し、コンテナを作る"}},[s._v("#")]),s._v(" 演習7. DockerFileを記述し、コンテナを作る")]),s._v(" "),t("p",[s._v("では、実際にDockerfile を作成し、Docker イメージを作成していきましょう。")]),s._v(" "),t("h4",{attrs:{id:"_7-1-dockerfile作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-1-dockerfile作成"}},[s._v("#")]),s._v(" 7-1 DockerFile作成")]),s._v(" "),t("p",[s._v("以下の内容をファイル名「Dockerfile」として作成してください。")]),s._v(" "),t("div",{staticClass:"language-Dockerfile line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-dockerfile"}},[t("code",[t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("FROM")]),s._v(" ubuntu")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("LABEL")]),s._v(" maintainer="),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"your_email"')])]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("RUN")]),s._v(" apt-get update && "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("\\")]),s._v("\n apt-get install nginx -y && "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("\\")]),s._v("\n rm /var/www/html/index.nginx-debian.html")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" index.html /var/www/html/")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("EXPOSE")]),s._v(" 80")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENTRYPOINT")]),s._v(" ["),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"nginx"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"-g"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"daemon off;"')]),s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("h4",{attrs:{id:"_7-2-index-html作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-2-index-html作成"}},[s._v("#")]),s._v(" 7-2 index.html作成")]),s._v(" "),t("p",[s._v("また、"),t("code",[s._v("COPY")]),s._v(" コマンドで利用する「index.html」を以下のように作成してください(あくまで一例です)。")]),s._v(" "),t("div",{staticClass:"language-html line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-html"}},[t("code",[t("span",{pre:!0,attrs:{class:"token doctype"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("head")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("meta")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token attr-name"}},[s._v("charset")]),t("span",{pre:!0,attrs:{class:"token attr-value"}},[t("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v('"')]),s._v("utf-8"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v('"')])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("title")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("Hello world"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("body")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("h1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("IIJ Boot Camp"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("p")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("Hello World from docker container!!"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("p",[s._v("作成したDockerfile の内容を先頭から読み解いていきましょう。")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("FROM")]),s._v(" 命令は、ベースとなるDocker イメージを設定する命令です。今回は、ubuntu を利用しています。Dockerfile は必ず"),t("code",[s._v("FROM")]),s._v(" 命令から始まっていなければなりません。")]),s._v(" "),t("li",[t("code",[s._v("LABEL")]),s._v(" 命令は、Docker イメージにメタデータを付加するときに利用します。今回は、"),t("code",[s._v("maintainer")]),s._v(" つまり管理者を追加しています。昔のDockerfile においては、"),t("code",[s._v("MAINTAINER")]),s._v(" 命令を使って設定していましたが、現在は非推奨となっています。")]),s._v(" "),t("li",[t("code",[s._v("RUN")]),s._v(" 命令は、Dockerfile の中でもとても重要な命令で、Docker イメージ作成時に実行する命令を記述します。今回はパッケージのアップデートとnginx のインストール、nginx のデフォルトウェブページであるHTML ファイルの削除を行っています。この行で重要なのは、"),t("strong",[s._v("&&")]),s._v(" で繋いで実行している点です。Dockerfile からDocker イメージを作成する際、"),t("code",[s._v("RUN")]),s._v(" 命令などの実行した状態をキャッシュとして保存します。そのため、"),t("code",[s._v("apt-get update")]),s._v(" と"),t("code",[s._v("apt-get install nginx -y")]),s._v(" が異なる行で書かれていると、新しくパッケージを追加するために、"),t("code",[s._v("apt-get install nginx -y")]),s._v("の行を更新して、再度Docker イメージを作成しようとした際に、"),t("code",[s._v("apt-get update")]),s._v(" で作成されたキャッシュを利用してしまい、"),t("strong",[s._v("最新版ではないnginx やパッケージがインストールされてしまう")]),s._v("可能性があります。")]),s._v(" "),t("li",[t("code",[s._v("COPY")]),s._v(" 命令は、ローカルにあるファイルなどをコンテナの内部へコピーする際に利用します。")]),s._v(" "),t("li",[t("code",[s._v("EXPOSE")]),s._v(" 命令は、コンテナ実行時にポートで待ち受けることをdocker に伝えます。注意点として、この命令を書いただけでは、ホスト環境からアクセスはできません。実際にアクセスするためには、後述する"),t("code",[s._v("docker run")]),s._v(" コマンドのオプションでポートを設定する必要があります。")]),s._v(" "),t("li",[t("code",[s._v("ENTRYPOINT")]),s._v(" 命令は、"),t("code",[s._v("RUN")]),s._v(" 命令と異なり、Dockerイメージから実際のコンテナを作成する際に実行されます。")])]),s._v(" "),t("p",[s._v("その他詳しい機能について知りたい方は、"),t("a",{attrs:{href:"https://docs.docker.com/engine/reference/builder/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のリファレンス"),t("OutboundLink")],1),s._v("をご参照ください。")]),s._v(" "),t("h4",{attrs:{id:"_7-3-イメージのビルド"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-3-イメージのビルド"}},[s._v("#")]),s._v(" 7-3. イメージのビルド")]),s._v(" "),t("p",[s._v("では、先ほど作成したDockerfileが存在するディレクトリ内で以下のコマンドを実行してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" build -t iijbootcamp "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("このコマンドによって、Dockerfile からDocker イメージが作成されます。"),t("code",[s._v("-t")]),s._v(" オプションでイメージ名を決めることができます。また、最後の"),t("code",[s._v(".")]),s._v(" は、Dockerfile が存在するディレクトリ(カレントディレクトリ)を意味しています。")]),s._v(" "),t("p",[s._v("実際にDockerイメージが作成されているかコマンドで確認してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" images\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("REPOSITORY TAG IMAGE ID CREATED SIZE\niijbootcamp latest 417ab982faaa 6 days ago 170MB\nubuntu latest 93fd78260bd1 6 days ago 86.2MB\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])])]),s._v(" "),t("p",[s._v("「REPOSITORY」がubuntu とiijbootcamp の2つのイメージが新たに作られていると思います。ubuntu は"),t("code",[s._v("FROM")]),s._v(" で指定したDocker イメージです。iijbootcamp は、ubuntu のDocker イメージを元にnginx やHTML ファイルを追加して今回作成したDocker イメージです。")]),s._v(" "),t("h4",{attrs:{id:"_7-4-コンテナの起動"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-4-コンテナの起動"}},[s._v("#")]),s._v(" 7-4. コンテナの起動")]),s._v(" "),t("p",[s._v("では、実際にこのコンテナを起動してアクセスしてみましょう。次のコマンドを実行してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -d -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8888")]),s._v(":80 iijbootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("code",[s._v("-d")]),s._v(" オプションはコンテナをデタッチドモードで起動することを意味しています。また、nginx はデフォルトでデーモンとして起動してしまうため、Dockerfile ではnginx をデーモンとして起動しないオプションで起動しています。また、"),t("code",[s._v("-p")]),s._v(" オプションでは、ホスト側とコンテナ側のポートを対応付けています。「:」の左側がホスト環境、右側がコンテナ環境のポートを示しており、"),t("strong",[s._v("ホスト環境の8888ポートへアクセスすると、コンテナの80ポートにつながる")]),s._v("ようになっています。")]),s._v(" "),t("p",[s._v("コンテナが起動していることを確認してください。正しく実行されていると以下のような画面が表示されます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n171c3b25c75e iijbootcamp:latest "nginx -g \'daemon of…" 15 minutes ago Up 15 minutes 0.0.0.0:8888->80/tcp condescending_wilson\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])])]),s._v(" "),t("h4",{attrs:{id:"_7-5-コンテナへのアクセス"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-5-コンテナへのアクセス"}},[s._v("#")]),s._v(" 7-5. コンテナへのアクセス")]),s._v(" "),t("p",[s._v("では、実際にコンテナに対してアクセスしてみましょう。お好きな方法(Webブラウザでもcurlコマンド等でも)で「 "),t("a",{attrs:{href:"http://localhost:8888",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost:8888"),t("OutboundLink")],1),s._v(" 」にアクセスしてみましょう。以下にcurl コマンドを用いた例を示します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" http://localhost:8888\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("DOCTYPE html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("head"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("meta "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("charset")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"utf-8"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("title"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("Hello world"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/title"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/head"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("body"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("h"),t("span",{pre:!0,attrs:{class:"token operator"}},[t("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[s._v("1")]),s._v(">")]),s._v("IIJ Boot Camp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/h"),t("span",{pre:!0,attrs:{class:"token operator"}},[t("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[s._v("1")]),s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("p"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("Hello World from "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" container"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/p"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/body"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("p",[s._v("上記のように実際に作成したHTMLが表示されていれば成功です。\nプロキシを設定している場合は下記のように"),t("code",[s._v("--noproxy")]),s._v("オプションを指定する必要があります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" --noproxy localhost http://localhost:8888\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h2",{attrs:{id:"参考情報"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#参考情報"}},[s._v("#")]),s._v(" 参考情報")]),s._v(" "),t("h3",{attrs:{id:"docker-イメージの共有方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-イメージの共有方法"}},[s._v("#")]),s._v(" Docker イメージの共有方法")]),s._v(" "),t("p",[s._v("皆さんが作成したDocker イメージなどを他の人に共有したい場合、Dockerfile をファイルサーバやGitHub 等で共有する以外に、"),t("a",{attrs:{href:"https://hub.docker.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Docker Hub"),t("OutboundLink")],1),s._v("を始めとする「"),t("strong",[s._v("Docker イメージレジストリ")]),s._v("」で公開し、それを利用してもらうことが可能です。例えば、本講義で利用した「getting-started」や「ubuntu」のDocker イメージは、Docker Hubで公開されているものを利用しています。")]),s._v(" "),t("p",[s._v("Docker イメージレジストリに自分のDocker イメージを公開する際には、"),t("code",[s._v("docker push")]),s._v(" コマンドを利用します。逆に、Docker イメージをダウンロードしたい場合は、"),t("code",[s._v("docker pull")]),s._v(" コマンドを利用します。また、Dockerイメージを"),t("code",[s._v("docker save")]),s._v(" コマンドでtarファイルとして保存し、"),t("code",[s._v("docker load")]),s._v(" コマンドでtarからロードするといったことも行うことができます。")]),s._v(" "),t("h2",{attrs:{id:"まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),t("p",[s._v("今回は、仮想環境プラットフォームであるDocker を実際に触っていただきました。Docker の基礎知識、Docker コンテナ、イメージの管理方法に加え、Dockerfile を作成し自身でコンテナを作っていただきました。仮想マシンに比べ軽く高速で作成することが可能なため使いどころは多いでしょう。しかしながら、実際のサービスでは、単一のアプリケーションのみで1つのコンテナで完成することはありえません。Web サーバやWeb アプリケーション、データベースなど様々なものが1つに組み合わさって初めてサービスとして完成します。Docker コンテナでは、「1コンテナ1プロセス」や「1コンテナ1ロール」が基本となっています。そのため複数のコンテナを組み合わせて構築する必要があります。次の講義「開発環境をdocker-composeで構築」では、複数のコンテナを組み合わせて管理する手法について学びます。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[50],{528:function(s,t,a){"use strict";a.r(t);var e=a(10),r=Object(e.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("header-table"),s._v(" "),t("h2",{attrs:{id:"dockerイメージの作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dockerイメージの作成"}},[s._v("#")]),s._v(" Dockerイメージの作成")]),s._v(" "),t("p",[s._v("Docker では、「"),t("strong",[s._v("Dockerfile")]),s._v("」というファイルを用いて、Docker イメージの作成を行うことができます。Dockerfile は専用言語で記述します。このようにコンテナを初めとするサーバ環境やミドルウェアなどをコード(プログラム、設定ファイルなど)で構成管理することを「"),t("strong",[s._v("Infrastructure as Code(IaC)")]),s._v("」と呼びます。Dockerfile のように環境をコード化して管理することにより、以下のようなメリットが挙げられます。")]),s._v(" "),t("ul",[t("li",[s._v("新規メンバーの開発環境が、Dockerfile を共有するだけで構築可能")]),s._v(" "),t("li",[s._v("複雑なインフラ環境がテキストベースで管理でき、git 等での管理が容易に")]),s._v(" "),t("li",[s._v("構築手順などがコード化されるため、漏れや間違いが発見しやすくなる")])]),s._v(" "),t("p",[s._v("今回は、ubuntu というDocker イメージを元にカスタマイズしながら、nginx によるWeb サーバのDocker イメージを作成します。また、作成したDocker イメージを使ってDocker コンテナを立ち上げ、HTML ファイルがレスポンスされることを確認します。")]),s._v(" "),t("h3",{attrs:{id:"演習7-dockerfileを記述し、コンテナを作る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#演習7-dockerfileを記述し、コンテナを作る"}},[s._v("#")]),s._v(" 演習7. DockerFileを記述し、コンテナを作る")]),s._v(" "),t("p",[s._v("では、実際にDockerfile を作成し、Docker イメージを作成していきましょう。")]),s._v(" "),t("h4",{attrs:{id:"_7-1-dockerfile作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-1-dockerfile作成"}},[s._v("#")]),s._v(" 7-1 DockerFile作成")]),s._v(" "),t("p",[s._v("以下の内容をファイル名「Dockerfile」として作成してください。")]),s._v(" "),t("div",{staticClass:"language-Dockerfile line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-dockerfile"}},[t("code",[t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("FROM")]),s._v(" ubuntu")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("LABEL")]),s._v(" maintainer="),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"your_email"')])]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("RUN")]),s._v(" apt-get update && "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("\\")]),s._v("\n apt-get install nginx -y && "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("\\")]),s._v("\n rm /var/www/html/index.nginx-debian.html")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("COPY")]),s._v(" index.html /var/www/html/")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("EXPOSE")]),s._v(" 80")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token instruction"}},[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("ENTRYPOINT")]),s._v(" ["),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"nginx"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"-g"')]),s._v(", "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"daemon off;"')]),s._v("]")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("h4",{attrs:{id:"_7-2-index-html作成"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-2-index-html作成"}},[s._v("#")]),s._v(" 7-2 index.html作成")]),s._v(" "),t("p",[s._v("また、"),t("code",[s._v("COPY")]),s._v(" コマンドで利用する「index.html」を以下のように作成してください(あくまで一例です)。")]),s._v(" "),t("div",{staticClass:"language-html line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-html"}},[t("code",[t("span",{pre:!0,attrs:{class:"token doctype"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("html")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("head")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("meta")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token attr-name"}},[s._v("charset")]),t("span",{pre:!0,attrs:{class:"token attr-value"}},[t("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v('"')]),s._v("utf-8"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v('"')])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("title")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("Hello world"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("body")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("h1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("IIJ Boot Camp"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("<")]),s._v("p")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("Hello World from docker container!!"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token tag"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("")])]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br")])]),t("p",[s._v("作成したDockerfile の内容を先頭から読み解いていきましょう。")]),s._v(" "),t("ul",[t("li",[t("code",[s._v("FROM")]),s._v(" 命令は、ベースとなるDocker イメージを設定する命令です。今回は、ubuntu を利用しています。Dockerfile は必ず"),t("code",[s._v("FROM")]),s._v(" 命令から始まっていなければなりません。")]),s._v(" "),t("li",[t("code",[s._v("LABEL")]),s._v(" 命令は、Docker イメージにメタデータを付加するときに利用します。今回は、"),t("code",[s._v("maintainer")]),s._v(" つまり管理者を追加しています。昔のDockerfile においては、"),t("code",[s._v("MAINTAINER")]),s._v(" 命令を使って設定していましたが、現在は非推奨となっています。")]),s._v(" "),t("li",[t("code",[s._v("RUN")]),s._v(" 命令は、Dockerfile の中でもとても重要な命令で、Docker イメージ作成時に実行する命令を記述します。今回はパッケージのアップデートとnginx のインストール、nginx のデフォルトウェブページであるHTML ファイルの削除を行っています。この行で重要なのは、"),t("strong",[s._v("&&")]),s._v(" で繋いで実行している点です。Dockerfile からDocker イメージを作成する際、"),t("code",[s._v("RUN")]),s._v(" 命令などの実行した状態をキャッシュとして保存します。そのため、"),t("code",[s._v("apt-get update")]),s._v(" と"),t("code",[s._v("apt-get install nginx -y")]),s._v(" が異なる行で書かれていると、新しくパッケージを追加するために、"),t("code",[s._v("apt-get install nginx -y")]),s._v("の行を更新して、再度Docker イメージを作成しようとした際に、"),t("code",[s._v("apt-get update")]),s._v(" で作成されたキャッシュを利用してしまい、"),t("strong",[s._v("最新版ではないnginx やパッケージがインストールされてしまう")]),s._v("可能性があります。")]),s._v(" "),t("li",[t("code",[s._v("COPY")]),s._v(" 命令は、ローカルにあるファイルなどをコンテナの内部へコピーする際に利用します。")]),s._v(" "),t("li",[t("code",[s._v("EXPOSE")]),s._v(" 命令は、コンテナ実行時にポートで待ち受けることをdocker に伝えます。注意点として、この命令を書いただけでは、ホスト環境からアクセスはできません。実際にアクセスするためには、後述する"),t("code",[s._v("docker run")]),s._v(" コマンドのオプションでポートを設定する必要があります。")]),s._v(" "),t("li",[t("code",[s._v("ENTRYPOINT")]),s._v(" 命令は、"),t("code",[s._v("RUN")]),s._v(" 命令と異なり、Dockerイメージから実際のコンテナを作成する際に実行されます。")])]),s._v(" "),t("p",[s._v("その他詳しい機能について知りたい方は、"),t("a",{attrs:{href:"https://docs.docker.com/engine/reference/builder/",target:"_blank",rel:"noopener noreferrer"}},[s._v("公式のリファレンス"),t("OutboundLink")],1),s._v("をご参照ください。")]),s._v(" "),t("h4",{attrs:{id:"_7-3-イメージのビルド"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-3-イメージのビルド"}},[s._v("#")]),s._v(" 7-3. イメージのビルド")]),s._v(" "),t("p",[s._v("では、先ほど作成したDockerfileが存在するディレクトリ内で以下のコマンドを実行してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" build -t iijbootcamp "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("このコマンドによって、Dockerfile からDocker イメージが作成されます。"),t("code",[s._v("-t")]),s._v(" オプションでイメージ名を決めることができます。また、最後の"),t("code",[s._v(".")]),s._v(" は、Dockerfile が存在するディレクトリ(カレントディレクトリ)を意味しています。")]),s._v(" "),t("p",[s._v("実際にDockerイメージが作成されているかコマンドで確認してみてください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" images\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("REPOSITORY TAG IMAGE ID CREATED SIZE\niijbootcamp latest 417ab982faaa 6 days ago 170MB\nubuntu latest 93fd78260bd1 6 days ago 86.2MB\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br")])])]),s._v(" "),t("p",[s._v("「REPOSITORY」がubuntu とiijbootcamp の2つのイメージが新たに作られていると思います。ubuntu は"),t("code",[s._v("FROM")]),s._v(" で指定したDocker イメージです。iijbootcamp は、ubuntu のDocker イメージを元にnginx やHTML ファイルを追加して今回作成したDocker イメージです。")]),s._v(" "),t("h4",{attrs:{id:"_7-4-コンテナの起動"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-4-コンテナの起動"}},[s._v("#")]),s._v(" 7-4. コンテナの起動")]),s._v(" "),t("p",[s._v("では、実際にこのコンテナを起動してアクセスしてみましょう。次のコマンドを実行してください。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -d -p "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("8888")]),s._v(":80 iijbootcamp\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("code",[s._v("-d")]),s._v(" オプションはコンテナをデタッチドモードで起動することを意味しています。また、nginx はデフォルトでデーモンとして起動してしまうため、Dockerfile ではnginx をデーモンとして起動しないオプションで起動しています。また、"),t("code",[s._v("-p")]),s._v(" オプションでは、ホスト側とコンテナ側のポートを対応付けています。「:」の左側がホスト環境、右側がコンテナ環境のポートを示しており、"),t("strong",[s._v("ホスト環境の8888ポートへアクセスすると、コンテナの80ポートにつながる")]),s._v("ようになっています。")]),s._v(" "),t("p",[s._v("コンテナが起動していることを確認してください。正しく実行されていると以下のような画面が表示されます。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("details",[t("summary",[s._v("実行例")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n171c3b25c75e iijbootcamp:latest "nginx -g \'daemon of…" 15 minutes ago Up 15 minutes 0.0.0.0:8888->80/tcp condescending_wilson\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br")])])]),s._v(" "),t("h4",{attrs:{id:"_7-5-コンテナへのアクセス"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-5-コンテナへのアクセス"}},[s._v("#")]),s._v(" 7-5. コンテナへのアクセス")]),s._v(" "),t("p",[s._v("では、実際にコンテナに対してアクセスしてみましょう。お好きな方法(Webブラウザでもcurlコマンド等でも)で「 "),t("a",{attrs:{href:"http://localhost:8888",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost:8888"),t("OutboundLink")],1),s._v(" 」にアクセスしてみましょう。以下にcurl コマンドを用いた例を示します。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" http://localhost:8888\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),s._v("DOCTYPE html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("head"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("meta "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("charset")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"utf-8"')]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("title"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("Hello world"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/title"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/head"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("body"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("h"),t("span",{pre:!0,attrs:{class:"token operator"}},[t("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[s._v("1")]),s._v(">")]),s._v("IIJ Boot Camp"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/h"),t("span",{pre:!0,attrs:{class:"token operator"}},[t("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[s._v("1")]),s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("p"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("Hello World from "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" container"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/p"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/body"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("/html"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br"),t("span",{staticClass:"line-number"},[s._v("2")]),t("br"),t("span",{staticClass:"line-number"},[s._v("3")]),t("br"),t("span",{staticClass:"line-number"},[s._v("4")]),t("br"),t("span",{staticClass:"line-number"},[s._v("5")]),t("br"),t("span",{staticClass:"line-number"},[s._v("6")]),t("br"),t("span",{staticClass:"line-number"},[s._v("7")]),t("br"),t("span",{staticClass:"line-number"},[s._v("8")]),t("br"),t("span",{staticClass:"line-number"},[s._v("9")]),t("br"),t("span",{staticClass:"line-number"},[s._v("10")]),t("br"),t("span",{staticClass:"line-number"},[s._v("11")]),t("br"),t("span",{staticClass:"line-number"},[s._v("12")]),t("br")])]),t("p",[s._v("上記のように実際に作成したHTMLが表示されていれば成功です。\nプロキシを設定している場合は下記のように"),t("code",[s._v("--noproxy")]),s._v("オプションを指定する必要があります。")]),s._v(" "),t("div",{staticClass:"language-bash line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-bash"}},[t("code",[s._v("$ "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" --noproxy localhost http://localhost:8888\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h2",{attrs:{id:"参考情報"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#参考情報"}},[s._v("#")]),s._v(" 参考情報")]),s._v(" "),t("h3",{attrs:{id:"docker-イメージの共有方法"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-イメージの共有方法"}},[s._v("#")]),s._v(" Docker イメージの共有方法")]),s._v(" "),t("p",[s._v("皆さんが作成したDocker イメージなどを他の人に共有したい場合、Dockerfile をファイルサーバやGitHub 等で共有する以外に、"),t("a",{attrs:{href:"https://hub.docker.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Docker Hub"),t("OutboundLink")],1),s._v("を始めとする「"),t("strong",[s._v("Docker イメージレジストリ")]),s._v("」で公開し、それを利用してもらうことが可能です。例えば、本講義で利用した「getting-started」や「ubuntu」のDocker イメージは、Docker Hubで公開されているものを利用しています。")]),s._v(" "),t("p",[s._v("Docker イメージレジストリに自分のDocker イメージを公開する際には、"),t("code",[s._v("docker push")]),s._v(" コマンドを利用します。逆に、Docker イメージをダウンロードしたい場合は、"),t("code",[s._v("docker pull")]),s._v(" コマンドを利用します。また、Dockerイメージを"),t("code",[s._v("docker save")]),s._v(" コマンドでtarファイルとして保存し、"),t("code",[s._v("docker load")]),s._v(" コマンドでtarからロードするといったことも行うことができます。")]),s._v(" "),t("h2",{attrs:{id:"まとめ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),t("p",[s._v("今回は、仮想環境プラットフォームであるDocker を実際に触っていただきました。Docker の基礎知識、Docker コンテナ、イメージの管理方法に加え、Dockerfile を作成し自身でコンテナを作っていただきました。仮想マシンに比べ軽く高速で作成することが可能なため使いどころは多いでしょう。しかしながら、実際のサービスでは、単一のアプリケーションのみで1つのコンテナで完成することはありえません。Web サーバやWeb アプリケーション、データベースなど様々なものが1つに組み合わさって初めてサービスとして完成します。Docker コンテナでは、「1コンテナ1プロセス」や「1コンテナ1ロール」が基本となっています。そのため複数のコンテナを組み合わせて構築する必要があります。次の講義「開発環境をdocker-composeで構築」では、複数のコンテナを組み合わせて管理する手法について学びます。")]),s._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/51.185e40a9.js b/assets/js/51.9faf5c7e.js similarity index 99% rename from assets/js/51.185e40a9.js rename to assets/js/51.9faf5c7e.js index 56cfb517..90bddc32 100644 --- a/assets/js/51.185e40a9.js +++ b/assets/js/51.9faf5c7e.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{530:function(s,e,a){"use strict";a.r(e);var r=a(10),t=Object(r.a)({},(function(){var s=this,e=s._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[e("header-table"),s._v(" "),e("h2",{attrs:{id:"dockerコンテナの管理"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの管理"}},[s._v("#")]),s._v(" Dockerコンテナの管理")]),s._v(" "),e("p",[s._v("前回は、Dockerコンテナのイメージの取得からコンテナの構築までを行いました。\nしかし、先ほどの講義では以下の項目についての確認には言及していません。")]),s._v(" "),e("ul",[e("li",[s._v("取得したイメージがきちんと取得できているのか?")]),s._v(" "),e("li",[s._v("起動したコンテナが間違いなく起動しているのか?")]),s._v(" "),e("li",[s._v("作業完了後に後片付け・余計なリソースやプロセスが残っていないか?")])]),s._v(" "),e("p",[s._v("実際のコンテナの活用にはこういったDockerコンテナの管理が必要不可欠になります。\n従って本講では、Dockerコンテナを管理する為のコマンドを学習します。")]),s._v(" "),e("h3",{attrs:{id:"演習3-docker-images"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習3-docker-images"}},[s._v("#")]),s._v(" 演習3 docker images")]),s._v(" "),e("p",[e("code",[s._v("docker images")]),s._v(" コマンドは、ローカル環境に存在するDocker イメージの一覧を表示するコマンドです。\nこれを用いることでコンテナの元となるイメージファイルの有無を確認することができます。")]),s._v(" "),e("p",[s._v("なお、Dockerイメージには、名前の他に「TAG」を付けることができます。\nTAGとして使われる文字列にはバージョンを記載することが一般的です。\n今回表示されている「latest」は最新版であることを意味しています。")]),s._v(" "),e("p",[s._v("コマンドを実行し、手元にどんなイメージがあるか確認してみてください")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" images\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("REPOSITORY TAG IMAGE ID CREATED SIZE\ndocker/getting-started latest cb90f98fd791 2 months ago 28.8MB\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])]),s._v(" "),e("h3",{attrs:{id:"演習4-docker-ps"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習4-docker-ps"}},[s._v("#")]),s._v(" 演習4 docker ps")]),s._v(" "),e("p",[e("code",[s._v("docker ps")]),s._v(" コマンドは、ローカル環境に存在するDockerコンテナに関する情報を表示してくれます。\n現在起動しているコンテナは"),e("code",[s._v("docker ps")]),s._v(" と入力すると表示されます。\nこちらもコマンドを実行してみましょう。")]),s._v(" "),e("p",[e("code",[s._v("docker ps")]),s._v("コマンドのデフォルトの動作で表示されるのは永続的に稼働しているコンテナのみ表示されるため、\n本講義に従って実行した場合は何も表示されないと思います。\n既に終了してしまったり、エラーで起動できていないコンテナを確認するためには、\nオプションで"),e("code",[s._v("-a")]),s._v(" を付けることで表示可能です。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v(" -a\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v('CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nf908c593f036 docker/getting-started "/docker-entrypoint.…" 2 seconds ago Created getting-started\n')])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])]),s._v(" "),e("h3",{attrs:{id:"演習5-docker-start、stop"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習5-docker-start、stop"}},[s._v("#")]),s._v(" 演習5 docker start、stop")]),s._v(" "),e("p",[e("code",[s._v("docker start")]),s._v(" は、コンテナの起動を行うコマンドで、"),e("code",[s._v("docker stop")]),s._v(" は、コンテナを停止するコマンドです。\nこれらのコマンドでは、起動・停止対象のコンテナを選択する必要があるため、引数として"),e("code",[s._v("docker ps")]),s._v(" の表示結果にあった「CONTAINER ID」を設定します。")]),s._v(" "),e("p",[s._v("先ほどの項では"),e("code",[s._v("docker/getting-started")]),s._v("コンテナの起動に"),e("code",[s._v("docker run")]),s._v("を使用していましたが、"),e("code",[s._v("docker run")]),s._v("と"),e("code",[s._v("docker start")]),s._v("には大きな違いがあります。")]),s._v(" "),e("p",[e("code",[s._v("docker run")]),s._v("は、"),e("code",[s._v("docker start")]),s._v("だけでなく、"),e("code",[s._v("docker create")]),s._v("を兼ねるコマンドとなっており、実行対象のコンテナが存在しない場合はイメージからコンテナ化を試みるようになっています。")]),s._v(" "),e("p",[s._v("このように大変便利なコマンドですが、既にコンテナ化されており停止しているだけの物を起動したいと思った時に"),e("code",[s._v("docker run")]),s._v("を使用すると、既にコンテナ化されていものがあるにも関わらず新たにイメージからコンテナを作り出そうとするなど、思わぬ動作を引き起こすことがあります。")]),s._v(" "),e("p",[s._v("従って、"),e("code",[s._v("docker run")]),s._v("は初回起動時に使う物として考え、\n"),e("code",[s._v("docker stop")]),s._v("で停止しただけのコンテナを再び起動させたい場合は"),e("code",[s._v("docker start")]),s._v("を用いましょう。")]),s._v(" "),e("p",[s._v("では、実際に"),e("code",[s._v("docker stop")]),s._v("と"),e("code",[s._v("docker start")]),s._v("を使う演習をしてみましょう。")]),s._v(" "),e("h4",{attrs:{id:"_5-1-コンテナの起動"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-コンテナの起動"}},[s._v("#")]),s._v(" 5-1. コンテナの起動")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name iij-bootcamp_docker01 -d -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n6d868b858fd7 docker/getting-started "),e("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/docker-entrypoint.…"')]),s._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("4")]),s._v(" seconds ago Up "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" seconds "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("0.0")]),s._v(".0.0:8080-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp, :::8080-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp interesting_darwin\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br"),e("span",{staticClass:"line-number"},[s._v("3")]),e("br")])])]),s._v(" "),e("h4",{attrs:{id:"_5-2-コンテナの停止"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-2-コンテナの停止"}},[s._v("#")]),s._v(" 5-2. コンテナの停止")]),s._v(" "),e("p",[s._v("先ほど実行したコンテナを停止します。 **"),e("CONTAINER",{attrs:{ID:""}},[s._v("**については先ほどの演習で行った"),e("code",[s._v("docker ps")]),s._v("等を用いて確認してください")])],1),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" stop "),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("CONTAINER ID"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("h4",{attrs:{id:"_5-3-コンテナの開始-再開"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-3-コンテナの開始-再開"}},[s._v("#")]),s._v(" 5-3. コンテナの開始(再開)")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" start "),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("CONTAINER ID"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("h3",{attrs:{id:"演習6-docker-rm、rmi"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習6-docker-rm、rmi"}},[s._v("#")]),s._v(" 演習6 docker rm、rmi")]),s._v(" "),e("p",[e("code",[s._v("docker rm")]),s._v(" と"),e("code",[s._v("docker rmi")]),s._v(" は、それぞれDocker コンテナ、Docker イメージの削除を行うコマンドです。\n軽量であるとは言え、コンテナイメージも一定の容量を持つため作成するだけで削除を行わないといずれディスクを圧迫し、溢れてしまいます。\n従って、不要なコンテナやイメージは削除するよう心がけましょう。\nただし、削除したいDocker イメージを元に作成したDocker コンテナが存在する場合削除できません。\nその際は、事前にDocker コンテナを削除した後に、Docker イメージを削除してください。")]),s._v(" "),e("p",[s._v("なお、削除には"),e("code",[s._v("docker stop")]),s._v("等と同様に引数に「CONTAINER ID」や「IMAGE ID」を設定する必要があります。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("rm")]),s._v(" iij-bootcamp_docker01\niij-bootcamp_docker01\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" rmi docker/getting-started\ndocker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("h2",{attrs:{id:"まとめ"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),e("p",[s._v("ここまで実施することでDockerコンテナの取得から後片付けまでの一通りの作業ができるようになりました。\nしかし、実際に自分好みのDockerイメージを作るにはどうすれば良いでしょうか。\n次項ではDockerイメージの作り方について学びたいと思います。")]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=t.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{531:function(s,e,a){"use strict";a.r(e);var r=a(10),t=Object(r.a)({},(function(){var s=this,e=s._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[e("header-table"),s._v(" "),e("h2",{attrs:{id:"dockerコンテナの管理"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#dockerコンテナの管理"}},[s._v("#")]),s._v(" Dockerコンテナの管理")]),s._v(" "),e("p",[s._v("前回は、Dockerコンテナのイメージの取得からコンテナの構築までを行いました。\nしかし、先ほどの講義では以下の項目についての確認には言及していません。")]),s._v(" "),e("ul",[e("li",[s._v("取得したイメージがきちんと取得できているのか?")]),s._v(" "),e("li",[s._v("起動したコンテナが間違いなく起動しているのか?")]),s._v(" "),e("li",[s._v("作業完了後に後片付け・余計なリソースやプロセスが残っていないか?")])]),s._v(" "),e("p",[s._v("実際のコンテナの活用にはこういったDockerコンテナの管理が必要不可欠になります。\n従って本講では、Dockerコンテナを管理する為のコマンドを学習します。")]),s._v(" "),e("h3",{attrs:{id:"演習3-docker-images"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習3-docker-images"}},[s._v("#")]),s._v(" 演習3 docker images")]),s._v(" "),e("p",[e("code",[s._v("docker images")]),s._v(" コマンドは、ローカル環境に存在するDocker イメージの一覧を表示するコマンドです。\nこれを用いることでコンテナの元となるイメージファイルの有無を確認することができます。")]),s._v(" "),e("p",[s._v("なお、Dockerイメージには、名前の他に「TAG」を付けることができます。\nTAGとして使われる文字列にはバージョンを記載することが一般的です。\n今回表示されている「latest」は最新版であることを意味しています。")]),s._v(" "),e("p",[s._v("コマンドを実行し、手元にどんなイメージがあるか確認してみてください")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" images\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v("REPOSITORY TAG IMAGE ID CREATED SIZE\ndocker/getting-started latest cb90f98fd791 2 months ago 28.8MB\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])]),s._v(" "),e("h3",{attrs:{id:"演習4-docker-ps"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習4-docker-ps"}},[s._v("#")]),s._v(" 演習4 docker ps")]),s._v(" "),e("p",[e("code",[s._v("docker ps")]),s._v(" コマンドは、ローカル環境に存在するDockerコンテナに関する情報を表示してくれます。\n現在起動しているコンテナは"),e("code",[s._v("docker ps")]),s._v(" と入力すると表示されます。\nこちらもコマンドを実行してみましょう。")]),s._v(" "),e("p",[e("code",[s._v("docker ps")]),s._v("コマンドのデフォルトの動作で表示されるのは永続的に稼働しているコンテナのみ表示されるため、\n本講義に従って実行した場合は何も表示されないと思います。\n既に終了してしまったり、エラーで起動できていないコンテナを確認するためには、\nオプションで"),e("code",[s._v("-a")]),s._v(" を付けることで表示可能です。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v(" -a\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language- line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[s._v('CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nf908c593f036 docker/getting-started "/docker-entrypoint.…" 2 seconds ago Created getting-started\n')])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])])]),s._v(" "),e("h3",{attrs:{id:"演習5-docker-start、stop"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習5-docker-start、stop"}},[s._v("#")]),s._v(" 演習5 docker start、stop")]),s._v(" "),e("p",[e("code",[s._v("docker start")]),s._v(" は、コンテナの起動を行うコマンドで、"),e("code",[s._v("docker stop")]),s._v(" は、コンテナを停止するコマンドです。\nこれらのコマンドでは、起動・停止対象のコンテナを選択する必要があるため、引数として"),e("code",[s._v("docker ps")]),s._v(" の表示結果にあった「CONTAINER ID」を設定します。")]),s._v(" "),e("p",[s._v("先ほどの項では"),e("code",[s._v("docker/getting-started")]),s._v("コンテナの起動に"),e("code",[s._v("docker run")]),s._v("を使用していましたが、"),e("code",[s._v("docker run")]),s._v("と"),e("code",[s._v("docker start")]),s._v("には大きな違いがあります。")]),s._v(" "),e("p",[e("code",[s._v("docker run")]),s._v("は、"),e("code",[s._v("docker start")]),s._v("だけでなく、"),e("code",[s._v("docker create")]),s._v("を兼ねるコマンドとなっており、実行対象のコンテナが存在しない場合はイメージからコンテナ化を試みるようになっています。")]),s._v(" "),e("p",[s._v("このように大変便利なコマンドですが、既にコンテナ化されており停止しているだけの物を起動したいと思った時に"),e("code",[s._v("docker run")]),s._v("を使用すると、既にコンテナ化されていものがあるにも関わらず新たにイメージからコンテナを作り出そうとするなど、思わぬ動作を引き起こすことがあります。")]),s._v(" "),e("p",[s._v("従って、"),e("code",[s._v("docker run")]),s._v("は初回起動時に使う物として考え、\n"),e("code",[s._v("docker stop")]),s._v("で停止しただけのコンテナを再び起動させたい場合は"),e("code",[s._v("docker start")]),s._v("を用いましょう。")]),s._v(" "),e("p",[s._v("では、実際に"),e("code",[s._v("docker stop")]),s._v("と"),e("code",[s._v("docker start")]),s._v("を使う演習をしてみましょう。")]),s._v(" "),e("h4",{attrs:{id:"_5-1-コンテナの起動"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-コンテナの起動"}},[s._v("#")]),s._v(" 5-1. コンテナの起動")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run --name iij-bootcamp_docker01 -d -p "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":80 docker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("details",[e("summary",[s._v("実行例")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v("\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n6d868b858fd7 docker/getting-started "),e("span",{pre:!0,attrs:{class:"token string"}},[s._v('"/docker-entrypoint.…"')]),s._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("4")]),s._v(" seconds ago Up "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("3")]),s._v(" seconds "),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("0.0")]),s._v(".0.0:8080-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp, :::8080-"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),e("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v("/tcp interesting_darwin\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br"),e("span",{staticClass:"line-number"},[s._v("3")]),e("br")])])]),s._v(" "),e("h4",{attrs:{id:"_5-2-コンテナの停止"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-2-コンテナの停止"}},[s._v("#")]),s._v(" 5-2. コンテナの停止")]),s._v(" "),e("p",[s._v("先ほど実行したコンテナを停止します。 **"),e("CONTAINER",{attrs:{ID:""}},[s._v("**については先ほどの演習で行った"),e("code",[s._v("docker ps")]),s._v("等を用いて確認してください")])],1),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" stop "),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("CONTAINER ID"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("h4",{attrs:{id:"_5-3-コンテナの開始-再開"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_5-3-コンテナの開始-再開"}},[s._v("#")]),s._v(" 5-3. コンテナの開始(再開)")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v(" $ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" start "),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("CONTAINER ID"),e("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br")])]),e("h3",{attrs:{id:"演習6-docker-rm、rmi"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習6-docker-rm、rmi"}},[s._v("#")]),s._v(" 演習6 docker rm、rmi")]),s._v(" "),e("p",[e("code",[s._v("docker rm")]),s._v(" と"),e("code",[s._v("docker rmi")]),s._v(" は、それぞれDocker コンテナ、Docker イメージの削除を行うコマンドです。\n軽量であるとは言え、コンテナイメージも一定の容量を持つため作成するだけで削除を行わないといずれディスクを圧迫し、溢れてしまいます。\n従って、不要なコンテナやイメージは削除するよう心がけましょう。\nただし、削除したいDocker イメージを元に作成したDocker コンテナが存在する場合削除できません。\nその際は、事前にDocker コンテナを削除した後に、Docker イメージを削除してください。")]),s._v(" "),e("p",[s._v("なお、削除には"),e("code",[s._v("docker stop")]),s._v("等と同様に引数に「CONTAINER ID」や「IMAGE ID」を設定する必要があります。")]),s._v(" "),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("rm")]),s._v(" iij-bootcamp_docker01\niij-bootcamp_docker01\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("div",{staticClass:"language-bash line-numbers-mode"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[s._v("$ "),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" rmi docker/getting-started\ndocker/getting-started\n")])]),s._v(" "),e("div",{staticClass:"line-numbers-wrapper"},[e("span",{staticClass:"line-number"},[s._v("1")]),e("br"),e("span",{staticClass:"line-number"},[s._v("2")]),e("br")])]),e("h2",{attrs:{id:"まとめ"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#まとめ"}},[s._v("#")]),s._v(" まとめ")]),s._v(" "),e("p",[s._v("ここまで実施することでDockerコンテナの取得から後片付けまでの一通りの作業ができるようになりました。\nしかし、実際に自分好みのDockerイメージを作るにはどうすれば良いでしょうか。\n次項ではDockerイメージの作り方について学びたいと思います。")]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=t.exports}}]); \ No newline at end of file diff --git a/assets/js/52.11d5174a.js b/assets/js/52.208c9413.js similarity index 99% rename from assets/js/52.11d5174a.js rename to assets/js/52.208c9413.js index 87043a6a..8bf56a47 100644 --- a/assets/js/52.11d5174a.js +++ b/assets/js/52.208c9413.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[52],{531:function(r,t,e){"use strict";e.r(t);var v=e(10),_=Object(v.a)({},(function(){var r=this,t=r._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":r.$parent.slotKey}},[t("header-table"),r._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[r._v("#")]),r._v(" "+r._s(r.$page.frontmatter.title))]),r._v(" "),t("h2",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[r._v("#")]),r._v(" はじめに")]),r._v(" "),t("p",[r._v("Docker は、Docker, Incが開発しているコンテナ型の仮想環境プラットフォームです。\nコンテナとは仮想マシンのように1台のコンピュータの上で、仮想的に複数のアプリケーションを動作させる技術・仕組みの1つです。")]),r._v(" "),t("p",[r._v("Docker(コンテナ仮想化プラットフォーム)を使用すると、アプリケーションやその依存関係をコンテナと呼ばれる独立した環境にパッケージ化することができます。\nでは、コンテナと仮想マシンは何が異なるのでしょう?")]),r._v(" "),t("p",[r._v("一般的に、仮想マシンと呼ばれるソフトウェアでは、ホストOS上で仮想マシン用のソフトウェア(VirtualBox等)を動かすことでホストOSとは異なるOSを起動します。\n一方、コンテナによる仮想化は、ホストOS上にアプリケーション実行用の専用領域を作成し、その中で実行する仕組みとなっています。")]),r._v(" "),t("h3",{attrs:{id:"本講義の目的"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の目的"}},[r._v("#")]),r._v(" 本講義の目的")]),r._v(" "),t("ul",[t("li",[r._v("Dockerについて正しい基礎知識と理解を得る")]),r._v(" "),t("li",[r._v("DockerFile および dockerコマンドを用いてdocker containerを作成・操作できるようにする")])]),r._v(" "),t("h4",{attrs:{id:"本講義で扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱わないこと"}},[r._v("#")]),r._v(" 本講義で扱わないこと")]),r._v(" "),t("ul",[t("li",[r._v("dockerコマンドのオプション・細かい操作に対する解説")]),r._v(" "),t("li",[r._v("コンテナイメージレイヤーの細かい解説")])]),r._v(" "),t("h3",{attrs:{id:"本講義の目標"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の目標"}},[r._v("#")]),r._v(" 本講義の目標")]),r._v(" "),t("ul",[t("li",[r._v("dockerコマンドを利用してコンテナの取得・起動・停止ができるようになる")]),r._v(" "),t("li",[r._v("DockerFileの読み方・書き方を知り、自分で作成もしくは加筆修正するなど、自分の目的に沿ったコンテナを作成できるようになる")])]),r._v(" "),t("h3",{attrs:{id:"docker-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-とは"}},[r._v("#")]),r._v(' "Docker" とは')]),r._v(" "),t("p",[t("strong",[r._v("はじめに")]),r._v(' で Docker = コンテナ型仮想環境プラットフォーム、として説明しましたが、本講義で扱う"Docker"は、コンテナ仮想プラットフォームを扱う上での技術的な概念やコマンド等を示します。')]),r._v(" "),t("p",[r._v("以下にDockerを扱う上で重要となる概念を記載します。")]),r._v(" "),t("ul",[t("li",[t("p",[r._v("Docker コンテナ")]),r._v(" "),t("ul",[t("li",[r._v("Dockerは、アプリケーションとその依存関係をコンテナと呼ばれる軽量な仮想環境にパッケージ化します。コンテナは、ホストマシンとは独立して実行され、一貫した動作を提供します。コンテナはポータブルであり、異なる環境やプラットフォームで実行できます。")])])]),r._v(" "),t("li",[t("p",[r._v("Docker イメージ")]),r._v(" "),t("ul",[t("li",[r._v("Dockerコンテナは、Dockerイメージから作成されます。イメージは、アプリケーションの実行に必要なすべての依存関係と設定を含むファイルシステムのスナップショットです。イメージは、"),t("strong",[r._v("Dockerfile")]),r._v("と呼ばれるテキストファイルに定義され、ビルドコマンドを使用して作成されます。")])])]),r._v(" "),t("li",[t("p",[r._v("コンテナオーケストレーション")]),r._v(" "),t("ul",[t("li",[r._v("Dockerは、コンテナのデプロイメントと管理を容易にするための機能を提供します。複数のコンテナを管理するためのツールとして、"),t("strong",[r._v("Docker Compose")]),r._v("や"),t("strong",[r._v("Kubernetes")]),r._v("(*)などがあります。これらのツールを使用すると、複雑なマルチコンテナ環境を構築し、スケーリングやロードバランシングなどの機能を実現することができます。")])])]),r._v(" "),t("li",[t("p",[r._v("イメージの共有")]),r._v(" "),t("ul",[t("li",[r._v("Docker HubやDocker Registryなどのオンラインリポジトリを使用することで、Dockerイメージを共有および配布することができます。これにより、他の開発者との協力や、既存のイメージを再利用することが容易になります。")])])])]),r._v(" "),t("p",[r._v("本講では主に仮想環境プラットフォームである「"),t("strong",[r._v("Docker コンテナ")]),r._v("」を中心に行いますが、一方で「"),t("strong",[r._v("Docker イメージ")]),r._v("」についても学び、自身にとって扱いやすい仮想環境プラットフォームを扱えることを目的と致します。")]),r._v(" "),t("p",[r._v("「"),t("strong",[r._v("コンテナオーケストレーション")]),r._v("」については続く docker-compose の項で扱います。")]),r._v(" "),t("h3",{attrs:{id:"講義の進め方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#講義の進め方"}},[r._v("#")]),r._v(" 講義の進め方")]),r._v(" "),t("p",[r._v("本講義では、予めDockerがインストールされていることを前提としています。\nDockerのインストールが完了していない方は、「ハンズオン事前準備」を済ませてください。")]),r._v(" "),t("h2",{attrs:{id:"chapters"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#chapters"}},[r._v("#")]),r._v(" Chapters")]),r._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/GETSTART.html"}},[r._v("Dockerコンテナで仮想環境プラットフォームを構築する")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/RUN_AS_IMAGE.html"}},[r._v("Dockerコンテナイメージを作成して起動する")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/OPERATION.html"}},[r._v("Dockerコンテナの管理")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/BUILD.html"}},[r._v("Dockerイメージの作成")])],1)]),r._v(" "),t("h2",{attrs:{id:"参考"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#参考"}},[r._v("#")]),r._v(" 参考")]),r._v(" "),t("h3",{attrs:{id:"仮想マシン-vs-コンテナ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#仮想マシン-vs-コンテナ"}},[r._v("#")]),r._v(" 仮想マシン vs コンテナ")]),r._v(" "),t("p",[r._v("仮想マシンとコンテナ、どちらが優れており、どちらを使うべきなのでしょう?")]),r._v(" "),t("p",[r._v("それは一概にどちらが優れているからそうすべき、といった類いの物ではありません。\nコンテナは仮想マシンと比べて後発であるため、仮想マシンが抱えていた問題を解決しているのは確かですが、上位互換という事ではありません。\nコンテナには仮想マシンには無いメリットもありますがデメリットもあります。\nコンテナは仮想マシンと異なり、OS の中で別のOSを起動するという事が無いため、セットアップや起動時においてもOSに関わる処理を必要としない為、時間を大幅に短縮することが可能です。また、ハードウェアにアクセスする際もゲストOSを介することが無いため、オーバーヘッドが少なく高速な動作が期待できます。")]),r._v(" "),t("p",[r._v("しかし、その一方で仮想マシンと比べてホストOSとの独立性が少ないため、ホストOSとアーキテクチャが大きく異なるコンテナを共存させることはできません。")]),r._v(" "),t("p",[r._v("例)Linux 上で Windows コンテナを起動・実行する")]),r._v(" "),t("h3",{attrs:{id:"docker-のアーキテクチャ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-のアーキテクチャ"}},[r._v("#")]),r._v(" Docker のアーキテクチャ")]),r._v(" "),t("p",[t("img",{attrs:{src:"https://docs.docker.com/guides/images/docker-architecture.webp",alt:"Docker Image"}})]),r._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=_.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[52],{532:function(r,t,e){"use strict";e.r(t);var v=e(10),_=Object(v.a)({},(function(){var r=this,t=r._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":r.$parent.slotKey}},[t("header-table"),r._v(" "),t("h1",{attrs:{id:"page-frontmatter-title"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[r._v("#")]),r._v(" "+r._s(r.$page.frontmatter.title))]),r._v(" "),t("h2",{attrs:{id:"はじめに"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#はじめに"}},[r._v("#")]),r._v(" はじめに")]),r._v(" "),t("p",[r._v("Docker は、Docker, Incが開発しているコンテナ型の仮想環境プラットフォームです。\nコンテナとは仮想マシンのように1台のコンピュータの上で、仮想的に複数のアプリケーションを動作させる技術・仕組みの1つです。")]),r._v(" "),t("p",[r._v("Docker(コンテナ仮想化プラットフォーム)を使用すると、アプリケーションやその依存関係をコンテナと呼ばれる独立した環境にパッケージ化することができます。\nでは、コンテナと仮想マシンは何が異なるのでしょう?")]),r._v(" "),t("p",[r._v("一般的に、仮想マシンと呼ばれるソフトウェアでは、ホストOS上で仮想マシン用のソフトウェア(VirtualBox等)を動かすことでホストOSとは異なるOSを起動します。\n一方、コンテナによる仮想化は、ホストOS上にアプリケーション実行用の専用領域を作成し、その中で実行する仕組みとなっています。")]),r._v(" "),t("h3",{attrs:{id:"本講義の目的"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の目的"}},[r._v("#")]),r._v(" 本講義の目的")]),r._v(" "),t("ul",[t("li",[r._v("Dockerについて正しい基礎知識と理解を得る")]),r._v(" "),t("li",[r._v("DockerFile および dockerコマンドを用いてdocker containerを作成・操作できるようにする")])]),r._v(" "),t("h4",{attrs:{id:"本講義で扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義で扱わないこと"}},[r._v("#")]),r._v(" 本講義で扱わないこと")]),r._v(" "),t("ul",[t("li",[r._v("dockerコマンドのオプション・細かい操作に対する解説")]),r._v(" "),t("li",[r._v("コンテナイメージレイヤーの細かい解説")])]),r._v(" "),t("h3",{attrs:{id:"本講義の目標"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#本講義の目標"}},[r._v("#")]),r._v(" 本講義の目標")]),r._v(" "),t("ul",[t("li",[r._v("dockerコマンドを利用してコンテナの取得・起動・停止ができるようになる")]),r._v(" "),t("li",[r._v("DockerFileの読み方・書き方を知り、自分で作成もしくは加筆修正するなど、自分の目的に沿ったコンテナを作成できるようになる")])]),r._v(" "),t("h3",{attrs:{id:"docker-とは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-とは"}},[r._v("#")]),r._v(' "Docker" とは')]),r._v(" "),t("p",[t("strong",[r._v("はじめに")]),r._v(' で Docker = コンテナ型仮想環境プラットフォーム、として説明しましたが、本講義で扱う"Docker"は、コンテナ仮想プラットフォームを扱う上での技術的な概念やコマンド等を示します。')]),r._v(" "),t("p",[r._v("以下にDockerを扱う上で重要となる概念を記載します。")]),r._v(" "),t("ul",[t("li",[t("p",[r._v("Docker コンテナ")]),r._v(" "),t("ul",[t("li",[r._v("Dockerは、アプリケーションとその依存関係をコンテナと呼ばれる軽量な仮想環境にパッケージ化します。コンテナは、ホストマシンとは独立して実行され、一貫した動作を提供します。コンテナはポータブルであり、異なる環境やプラットフォームで実行できます。")])])]),r._v(" "),t("li",[t("p",[r._v("Docker イメージ")]),r._v(" "),t("ul",[t("li",[r._v("Dockerコンテナは、Dockerイメージから作成されます。イメージは、アプリケーションの実行に必要なすべての依存関係と設定を含むファイルシステムのスナップショットです。イメージは、"),t("strong",[r._v("Dockerfile")]),r._v("と呼ばれるテキストファイルに定義され、ビルドコマンドを使用して作成されます。")])])]),r._v(" "),t("li",[t("p",[r._v("コンテナオーケストレーション")]),r._v(" "),t("ul",[t("li",[r._v("Dockerは、コンテナのデプロイメントと管理を容易にするための機能を提供します。複数のコンテナを管理するためのツールとして、"),t("strong",[r._v("Docker Compose")]),r._v("や"),t("strong",[r._v("Kubernetes")]),r._v("(*)などがあります。これらのツールを使用すると、複雑なマルチコンテナ環境を構築し、スケーリングやロードバランシングなどの機能を実現することができます。")])])]),r._v(" "),t("li",[t("p",[r._v("イメージの共有")]),r._v(" "),t("ul",[t("li",[r._v("Docker HubやDocker Registryなどのオンラインリポジトリを使用することで、Dockerイメージを共有および配布することができます。これにより、他の開発者との協力や、既存のイメージを再利用することが容易になります。")])])])]),r._v(" "),t("p",[r._v("本講では主に仮想環境プラットフォームである「"),t("strong",[r._v("Docker コンテナ")]),r._v("」を中心に行いますが、一方で「"),t("strong",[r._v("Docker イメージ")]),r._v("」についても学び、自身にとって扱いやすい仮想環境プラットフォームを扱えることを目的と致します。")]),r._v(" "),t("p",[r._v("「"),t("strong",[r._v("コンテナオーケストレーション")]),r._v("」については続く docker-compose の項で扱います。")]),r._v(" "),t("h3",{attrs:{id:"講義の進め方"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#講義の進め方"}},[r._v("#")]),r._v(" 講義の進め方")]),r._v(" "),t("p",[r._v("本講義では、予めDockerがインストールされていることを前提としています。\nDockerのインストールが完了していない方は、「ハンズオン事前準備」を済ませてください。")]),r._v(" "),t("h2",{attrs:{id:"chapters"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#chapters"}},[r._v("#")]),r._v(" Chapters")]),r._v(" "),t("ul",[t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/GETSTART.html"}},[r._v("Dockerコンテナで仮想環境プラットフォームを構築する")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/RUN_AS_IMAGE.html"}},[r._v("Dockerコンテナイメージを作成して起動する")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/OPERATION.html"}},[r._v("Dockerコンテナの管理")])],1),r._v(" "),t("li",[t("RouterLink",{attrs:{to:"/development/docker/docker/BUILD.html"}},[r._v("Dockerイメージの作成")])],1)]),r._v(" "),t("h2",{attrs:{id:"参考"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#参考"}},[r._v("#")]),r._v(" 参考")]),r._v(" "),t("h3",{attrs:{id:"仮想マシン-vs-コンテナ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#仮想マシン-vs-コンテナ"}},[r._v("#")]),r._v(" 仮想マシン vs コンテナ")]),r._v(" "),t("p",[r._v("仮想マシンとコンテナ、どちらが優れており、どちらを使うべきなのでしょう?")]),r._v(" "),t("p",[r._v("それは一概にどちらが優れているからそうすべき、といった類いの物ではありません。\nコンテナは仮想マシンと比べて後発であるため、仮想マシンが抱えていた問題を解決しているのは確かですが、上位互換という事ではありません。\nコンテナには仮想マシンには無いメリットもありますがデメリットもあります。\nコンテナは仮想マシンと異なり、OS の中で別のOSを起動するという事が無いため、セットアップや起動時においてもOSに関わる処理を必要としない為、時間を大幅に短縮することが可能です。また、ハードウェアにアクセスする際もゲストOSを介することが無いため、オーバーヘッドが少なく高速な動作が期待できます。")]),r._v(" "),t("p",[r._v("しかし、その一方で仮想マシンと比べてホストOSとの独立性が少ないため、ホストOSとアーキテクチャが大きく異なるコンテナを共存させることはできません。")]),r._v(" "),t("p",[r._v("例)Linux 上で Windows コンテナを起動・実行する")]),r._v(" "),t("h3",{attrs:{id:"docker-のアーキテクチャ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#docker-のアーキテクチャ"}},[r._v("#")]),r._v(" Docker のアーキテクチャ")]),r._v(" "),t("p",[t("img",{attrs:{src:"https://docs.docker.com/guides/images/docker-architecture.webp",alt:"Docker Image"}})]),r._v(" "),t("credit-footer")],1)}),[],!1,null,null,null);t.default=_.exports}}]); \ No newline at end of file diff --git a/assets/js/53.100b8756.js b/assets/js/53.86cf1bbe.js similarity index 99% rename from assets/js/53.100b8756.js rename to assets/js/53.86cf1bbe.js index eac8514c..dc5fe81e 100644 --- a/assets/js/53.100b8756.js +++ b/assets/js/53.86cf1bbe.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[53],{539:function(t,s,a){"use strict";a.r(s);var n=a(10),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("header-table"),t._v(" "),s("h1",{attrs:{id:"page-frontmatter-title"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#page-frontmatter-title"}},[t._v("#")]),t._v(" "+t._s(t.$page.frontmatter.title))]),t._v(" "),s("h2",{attrs:{id:"事前準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[t._v("#")]),t._v(" 事前準備")]),t._v(" "),s("p",[t._v("この講義ではコマンドライン(シェル)操作は必須ではありません。")]),t._v(" "),s("p",[t._v("このハンズオンでは、以下のような操作を行います。")]),t._v(" "),s("ul",[s("li",[t._v("手元で HTML, JavaScript ファイルを作成、編集する")]),t._v(" "),s("li",[t._v("その HTML ファイルをブラウザから以下のような URL で開く\n"),s("ul",[s("li",[t._v("file:///ローカルのパス/iij-bootcamp-jquery/index.html")])])]),t._v(" "),s("li",[t._v("ブラウザの開発者ツールでDOMツリーの表示、コンソールからの操作を行う")])]),t._v(" "),s("p",[t._v("これらの操作を行えればよいので、特別な準備は必要ありません。")]),t._v(" "),s("p",[t._v("ブラウザも自由ですが、最新版の Firefox または Chrome の利用を想定して解説する予定です。")]),t._v(" "),s("h2",{attrs:{id:"_1章-作業用ディレクトリを作成しよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1章-作業用ディレクトリを作成しよう"}},[t._v("#")]),t._v(" 1章:作業用ディレクトリを作成しよう")]),t._v(" "),s("p",[t._v("デスクトップ上でもどこでもよいので、次のようなディレクトリと以下のファイルを作成してください。")]),t._v(" "),s("h3",{attrs:{id:"ディレクトリ構造"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ディレクトリ構造"}},[t._v("#")]),t._v(" ディレクトリ構造")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("iij-bootcamp-jquery/\n├── index.html\n└── study.js\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br")])]),s("h3",{attrs:{id:"index-html-の中身"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#index-html-の中身"}},[t._v("#")]),t._v(" index.html の中身")]),t._v(" "),s("div",{staticClass:"language-html line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token doctype"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("lang")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("charset")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("utf-8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("title")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("iij-bootcamp jQuery"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("https://code.jquery.com/jquery-3.4.1.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("integrity")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("crossorigin")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("anonymous"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("study.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("header")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("page-header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("h1")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("heading"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Welcome to iij-bootcamp!"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("lead"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("JavaScriptと開発者ツールを使ってみよう!"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("description"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("このハンズオンでは jQuery を使いながら、HTML, CSS, JavaScript とブラウザの開発者ツールの使い方を覚えていきます。"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("main")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("page-main"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("1章:JavaScript を読み込む(開発者ツールのコンソールを確認しよう)"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br")])]),s("h4",{attrs:{id:"study-js-の中身"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#study-js-の中身"}},[t._v("#")]),t._v(" study.js の中身")]),t._v(" "),s("div",{staticClass:"language-javascript line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-javascript"}},[s("code",[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'use strict'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("alert")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'study.js を呼び出した(コンソールを開いてください)'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'console.log は開発者ツールのコンソール欄に出力される'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'apple'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'orange'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'peach'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hoge'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("123")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token string-property property"}},[t._v("'border'")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'1px solid red'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'console.log の引数は何個でも書ける'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'jQuery のバージョンは %c%s%c です'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'color: red; background: #ff0;'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" $"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fn"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("jquery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("''")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'object や function の値をデバッグ出力をしたいときに、console.log だと文字列になってしまう場合は console.dir を使う'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("$"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("dir")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("$"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("jQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br")])]),s("p",[t._v("ブラウザ上で開発者ツールを起動(表示)できたでしょうか。もしウィンドウの横に表示されている場合は、ウィンドウの下部に表示した方が良いかもしれません。開発者ツールを別ウィンドウで表示しても構いませんが、コンソールがある程度広く表示されるようにしておいてください。")]),t._v(" "),s("p",[t._v("コンソール出力の結果は、Chrome よりも Firefox の方が使いやすいかもしれません(Firefox では "),s("code",[t._v("console.log")]),t._v(" の出力結果が "),s("code",[t._v("console.dir")]),t._v(" と同じように扱えます)。")]),t._v(" "),s("p",[t._v("余談ですが "),s("code",[t._v("console.log")]),t._v(" はフォーマット指定に対応しています。第1引数に "),s("code",[t._v("%s")]),t._v(" など置換文字列を含む値を渡すと、他の言語でサポートされている "),s("code",[t._v("printf")]),t._v(" のように機能します。")]),t._v(" "),s("h3",{attrs:{id:"この講義での-html-と-js-について"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#この講義での-html-と-js-について"}},[t._v("#")]),t._v(" この講義での HTML と JS について")]),t._v(" "),s("p",[t._v("この講義では「DOM を操作してみる」「jQuery を使ってみる」ことを目的にしています。")]),t._v(" "),s("p",[t._v("JavaScript の文法やコーディングパターンを解説するつもりはありませんが、この資料では、筆者が適当にサンプルコードを書いて用意しています。")]),t._v(" "),s("ul",[s("li",[t._v("JavaScript ファイル冒頭の即時関数 "),s("code",[t._v("(function($) { ... }(jQuery))")]),t._v(" という書き方や "),s("code",[t._v("'use strict';")]),t._v(" は必須なものではありません。")]),t._v(" "),s("li",[t._v("以後のサンプルコードでは、変数宣言には "),s("code",[t._v("const")]),t._v(" や "),s("code",[t._v("let")]),t._v(" を使わず "),s("code",[t._v("var")]),t._v(" を使うことにします。")]),t._v(" "),s("li",[t._v("HTML は XHTML5 で記述し、HTML ファイル中では "),s("code",[t._v("study.js")]),t._v(" を body 要素の末尾ではなく head 要素内で読み込んでいます。このあたりは筆者の好みです。")]),t._v(" "),s("li",[t._v("ファイルの文字コードは UTF-8 を前提にしています。改行コードは指定していませんが CR+LF よりも LF を推奨します。")])]),t._v(" "),s("p",[t._v("講義では本筋ではないので説明を割愛しますが、こうした細かい部分の意味や違いについて気になった方は質問してもらっても構いません。")]),t._v(" "),s("h3",{attrs:{id:"_1章-まとめ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1章-まとめ"}},[t._v("#")]),t._v(" 1章:まとめ")]),t._v(" "),s("p",[t._v("作成した index.html をブラウザで開いてみてください。")]),t._v(" "),s("p",[t._v("Firefox や Chrome を起動して、index.html ファイルをドラッグすれば "),s("code",[t._v("file:///ローカルのパス/iij-bootcamp-jquery/index.html")]),t._v(" のような URL でページが開かれると思います。")]),t._v(" "),s("p",[s("code",[t._v("alert")]),t._v(" によるポップアップが表示されれば成功です。ブラウザの開発者ツールを開いてみましょう。")]),t._v(" "),s("p",[t._v("開発者ツールは、Windows であれば "),s("code",[t._v("Ctrl + Shift + C")]),t._v("、Mac であれば "),s("code",[t._v("Command + Shift + C")]),t._v(" などでブラウザ内に表示されます。開発者ツールには、")]),t._v(" "),s("ul",[s("li",[t._v("「インスペクター(Elements)」")]),t._v(" "),s("li",[t._v("「コンソール(Console)」")]),t._v(" "),s("li",[t._v("「ネットワーク...」")])]),t._v(" "),s("p",[t._v("などのタブが表示されると思いますが、この講義では「インスペクター(Elements)」と「コンソール(Console)」しか使いません。")]),t._v(" "),s("p",[t._v("コンソールに "),s("code",[t._v("study.js")]),t._v(" で記述したデバッグ出力の内容が表示されていることが確認できたら、次の章に進みましょう。")]),t._v(" "),s("h2",{attrs:{id:"_2章-jquery-とは何か"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2章-jquery-とは何か"}},[t._v("#")]),t._v(" 2章:jQuery とは何か")]),t._v(" "),s("p",[t._v('jQuery(ジェイクエリー)です。"JQuery" でも "Jquery" でもありません。')]),t._v(" "),s("p",[t._v("2006年頃に登場した JavaScript のライブラリです。")]),t._v(" "),s("p",[t._v("フレームワークという言葉とライブラリという言葉の使い分けについて、一般のプログラミング言語がどうかはわかりませんが JavaScript ではあまり区別されていない印象があります。筆者の個人的な感覚では(MVC / MVVM 等の)Webアプリケーションを構築するための構造的な機能を提供しているものをフレームワークと呼び、それ以外はライブラリと呼ぶのが分かりやすいと思っています。ここでは jQuery はライブラリと呼ぶことにします。")]),t._v(" "),s("p",[t._v("現在では JavaScript の(MVC / MVVM 等の)フレームワークとして Angular や React, Vue.js が有名で、一昔前は Backbone.js や Underscore.js がありました(これ以外にもいくつもありますが)。")]),t._v(" "),s("ul",[s("li",[t._v("(MVC / MVVM 等の)フロントエンド Web アプリケーションフレームワーク\n"),s("ul",[s("li",[t._v("Angular, React, Vue.js")]),t._v(" "),s("li",[t._v("Backbone.js")])])]),t._v(" "),s("li",[t._v("ユーティリティ関数 / ヘルパー関数を提供するライブラリ\n"),s("ul",[s("li",[t._v("Underscore.js")])])]),t._v(" "),s("li",[t._v("DOM を操作する(DOM Manipulator)ライブラリ\n"),s("ul",[s("li",[t._v("jQuery")]),t._v(" "),s("li",[t._v("prototype.js")])])])]),t._v(" "),s("p",[t._v("jQuery は DOM Manipulator です。DOM を操作することを目的としたライブラリとして登場しました。また、jQuery ではユーティリティ関数もいくつか提供されています。")]),t._v(" "),s("p",[t._v("jQuery は React などのフレームワークとは目的が違うライブラリです。「2019年にもなって jQuery を使うのはどうなのか?」という発言を巷で見かけますが、jQuery を使うことが不適切だというわけではありません。")]),t._v(" "),s("p",[t._v("Webサイトを JavaScript を利用してWebアプリケーションとして構築したいというような場合には jQuery では力不足でしょう。そういう場面では Angular や React, Vue.js などのフレームワークを使うことを検討すべきかもしれません。")]),t._v(" "),s("p",[t._v("しかし、Webページにちょっとしたスクリプトを書いて処理をさせたい、DOM を操作したいというだけであれば、ライブラリとして jQuery を採用するのは選択肢として悪くない判断です。")]),t._v(" "),s("p",[t._v("現在ではネイティブの JavaScript DOM API の実装が充実してきているのでネイティブのメソッドだけでも比較的簡単にコードが書けますが、第二次ブラウザ戦争(詳しくは "),s("a",{attrs:{href:"/frontend/overview"}},[t._v("フロントエンド Overview")]),t._v(" の資料を参照)の時代以前はネイティブの API だけでは不便であったり、ブラウザ間で使える API に違いがあったりしました。")]),t._v(" "),s("p",[t._v("jQuery が登場したことで何が便利になったのかを理解するために、ブラウザがコンテンツを表示するまでの仕組みと DOM について確認していきましょう。")]),t._v(" "),s("h2",{attrs:{id:"_3章-dom-とは何か"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3章-dom-とは何か"}},[t._v("#")]),t._v(" 3章:DOM とは何か")]),t._v(" "),s("p",[t._v("DOM(ドム)とは、Document Object Model の略で、HTML で表現されているデータを、ブラウザ内部で表現するためのデータモデルです。ツリー構造で表現されるため DOM ツリーと呼ばれることもあります。")]),t._v(" "),s("p",[t._v("普段、Webブラウザで表示しているページは HTML ソースコードでできている「HTML ページ」です。")]),t._v(" "),s("p",[t._v("ブラウザは、HTML ソースコードを受け取ると、次のような順序で処理を行ってコンテンツを逐次表示しています。")]),t._v(" "),s("ul",[s("li",[t._v("(1) HTML ソースコードの解析(パース)を開始する。")]),t._v(" "),s("li",[t._v("(2) 解析した順に DOM ツリーを構築する。")]),t._v(" "),s("li",[t._v("(3) 画像や CSS, JS ファイルなどリソース読み込みをするものがあれば、そのリソースの取得を開始する。")]),t._v(" "),s("li",[t._v("(4) 同期読み込みリソースであれば、読み込みが完了するまで HTML の解析処理を止める。非同期読み込みリソースであれば、HTML の解析を進めながらリソースの読み込みを行う。\n"),s("ul",[s("li",[t._v("リソースの読み込みが完了したら、CSS や JS であればスタイルの適用、スクリプトの実行を行う。画像であれば表示処理を行う。")])])]),t._v(" "),s("li",[t._v("(5) HTML ソースコードを最後まで解析したら、DOM の構築を完了する。")]),t._v(" "),s("li",[t._v("(6) HTML ソースコードにあるリソースの取得が全て完了したら、ページの読み込みを完了する。")])]),t._v(" "),s("p",[t._v("次の HTML を例に見ていきましょう。")]),t._v(" "),s("h3",{attrs:{id:"index-html-を書いてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#index-html-を書いてみる"}},[t._v("#")]),t._v(" index.html を書いてみる")]),t._v(" "),s("p",[t._v("index.html に以下の内容を書いてみてください。")]),t._v(" "),s("div",{staticClass:"language-html line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token doctype"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("lang")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("charset")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("utf-8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("title")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("What is DOM?"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("https://code.jquery.com/jquery-3.4.1.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("integrity")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("crossorigin")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("anonymous"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("ul")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("animals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("li")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("alice"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("https://placekitten.com/300/150"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("alt")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("Alice"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("li")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("bob"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("https://placekitten.com/300/160"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("alt")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("Bob"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("li")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("carol"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("https://placekitten.com/300/180"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("alt")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("Carol"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("study.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("message"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Cats are Cute!"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br")])]),s("p",[t._v("このような HTML があったとき、最終的には次のような DOM ツリーが構築されます。")]),t._v(" "),s("p",[t._v("ツリーであるというのは、ある要素(ノード)が唯一の親を持つような、親子関係があるということです。DOM ツリーの頂点は常に Document というノードになり、その下に HTML の要素が DOM としてぶらさがっています。")]),t._v(" "),s("div",{staticClass:"language-terminal line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('[Document]\n └ [HTML]{lang: "ja"}\n ├ [HEAD]\n │ ├ [META]{charset: "utf-8"}\n │ ├ [TITLE]\n │ │ └ (TEXT) "What is DOM?"\n │ └ [SCRIPT]{src: ".../jquery-3.4.1.js", ...}\n └ [BODY]\n ├ [UL]{class: "animals"}\n │ ├ [LI]{class: "alice"}\n │ │ └ [IMG]{src: ".../300/150", alt: "Alice"}\n │ ├ [LI]{class: "bob"}\n │ │ └ [IMG]{src: ".../300/160", alt: "Bob"}\n │ └ [LI]{class: "carol"}\n │ └ [IMG]{src: ".../300/180", alt: "Carol"}\n ├ [SCRIPT]{src: "study.js"}\n └ [P]{class: "message"}\n └ (TEXT) "Cats are Cute!"\n')])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br")])]),s("p",[t._v("上記の HTML では、"),s("code",[t._v("study.js")]),t._v(" の記述位置を "),s("code",[t._v("ul")]),t._v(" 要素の後に書いています。")]),t._v(" "),s("h3",{attrs:{id:"study-js-を書いてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#study-js-を書いてみる"}},[t._v("#")]),t._v(" study.js を書いてみる")]),t._v(" "),s("p",[t._v("この HTML の情報を "),s("code",[t._v("study.js")]),t._v(" から取得してみましょう。JavaScript では DOM API メソッドを用いて、構築された DOM ツリーにアクセスすることができます。")]),t._v(" "),s("p",[t._v("試しに、2つの簡単な処理を行ってみます。")]),t._v(" "),s("ul",[s("li",[t._v("animals_info 関数\n"),s("ul",[s("li",[s("code",[t._v('class="animals"')]),t._v(" である "),s("code",[t._v("ul")]),t._v(" 要素に含まれるリストアイテムの数を出力する")])])]),t._v(" "),s("li",[t._v("message_info 関数\n"),s("ul",[s("li",[s("code",[t._v('class="message"')]),t._v(" である要素のテキストを出力する")])])])]),t._v(" "),s("p",[s("code",[t._v("class 属性値")]),t._v(" にマッチする要素は1個であるという前提で以下のようなコードを書いてみます。")]),t._v(" "),s("div",{staticClass:"language-javascript line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-javascript"}},[s("code",[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'use strict'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("section3_works")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("section3_works")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animals_info")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("message_info")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animals_info")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" ul "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getElementsByClassName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'animals'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ul "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'class=\"animals\" の要素が見つかりません'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" li_elems "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ul"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getElementsByTagName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'li'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("li_elems "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'li 要素が見つかりません'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'動物の画像は '")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" li_elems"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("' 枚あります'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("message_info")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getElementsByClassName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'message'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'class=\"message\" の要素が見つかりません'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'メッセージは \"'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textContent "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'\" です'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br"),s("span",{staticClass:"line-number"},[t._v("3")]),s("br"),s("span",{staticClass:"line-number"},[t._v("4")]),s("br"),s("span",{staticClass:"line-number"},[t._v("5")]),s("br"),s("span",{staticClass:"line-number"},[t._v("6")]),s("br"),s("span",{staticClass:"line-number"},[t._v("7")]),s("br"),s("span",{staticClass:"line-number"},[t._v("8")]),s("br"),s("span",{staticClass:"line-number"},[t._v("9")]),s("br"),s("span",{staticClass:"line-number"},[t._v("10")]),s("br"),s("span",{staticClass:"line-number"},[t._v("11")]),s("br"),s("span",{staticClass:"line-number"},[t._v("12")]),s("br"),s("span",{staticClass:"line-number"},[t._v("13")]),s("br"),s("span",{staticClass:"line-number"},[t._v("14")]),s("br"),s("span",{staticClass:"line-number"},[t._v("15")]),s("br"),s("span",{staticClass:"line-number"},[t._v("16")]),s("br"),s("span",{staticClass:"line-number"},[t._v("17")]),s("br"),s("span",{staticClass:"line-number"},[t._v("18")]),s("br"),s("span",{staticClass:"line-number"},[t._v("19")]),s("br"),s("span",{staticClass:"line-number"},[t._v("20")]),s("br"),s("span",{staticClass:"line-number"},[t._v("21")]),s("br"),s("span",{staticClass:"line-number"},[t._v("22")]),s("br"),s("span",{staticClass:"line-number"},[t._v("23")]),s("br"),s("span",{staticClass:"line-number"},[t._v("24")]),s("br")])]),s("p",[t._v("この状態で index.html をブラウザで表示して、コンソールを開くと何が出力されるでしょうか。")]),t._v(" "),s("h3",{attrs:{id:"_3章-まとめ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3章-まとめ"}},[t._v("#")]),t._v(" 3章:まとめ")]),t._v(" "),s("p",[t._v("コンソールには、")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('動物の画像は 3 枚あります\nclass="message" の要素が見つかりません\n')])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br"),s("span",{staticClass:"line-number"},[t._v("2")]),s("br")])]),s("p",[t._v("と表示されるはずです。"),s("code",[t._v("animals_info")]),t._v(" の方は期待通り動きましたが、"),s("code",[t._v("message_info")]),t._v(" の方はメッセージを取得することに失敗してしまいました。")]),t._v(" "),s("p",[t._v("この原因と仕組みを次の章で確認していきます。")]),t._v(" "),s("h2",{attrs:{id:"_4章-イベント処理とは何か"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4章-イベント処理とは何か"}},[t._v("#")]),t._v(" 4章:イベント処理とは何か")]),t._v(" "),s("p",[t._v("3章の study.js では、メッセージの取得に失敗してしまいました。"),s("code",[t._v('class="message"')]),t._v(" の綴りを間違えたりしているわけでもありません。")]),t._v(" "),s("p",[t._v("もう一度、ブラウザの内部処理の流れを確認してみましょう。")]),t._v(" "),s("ul",[s("li",[t._v("(1) HTML ソースコードの解析(パース)を開始する。")]),t._v(" "),s("li",[t._v("(2) 解析した順に DOM ツリーを構築する。")]),t._v(" "),s("li",[t._v("(3) 画像や CSS, JS ファイルなどリソース読み込みをするものがあれば、そのリソースの取得を開始する。")]),t._v(" "),s("li",[t._v("(4) 同期読み込みリソースであれば、読み込みが完了するまで HTML の解析処理を止める。非同期読み込みリソースであれば、HTML の解析を進めながらリソースの読み込みを行う。\n"),s("ul",[s("li",[t._v("リソースの読み込みが完了したら、CSS や JS であればスタイルの適用、スクリプトの実行を行う。画像であれば表示処理を行う。")])])]),t._v(" "),s("li",[t._v("(5) HTML ソースコードを最後まで解析したら、DOM の構築を完了する。")]),t._v(" "),s("li",[t._v("(6) HTML ソースコードにあるリソースの取得が全て完了したら、ページの読み込みを完了する。")])]),t._v(" "),s("p",[t._v("この (2) の動作がここでは重要です。3章で書いた HTML では、"),s("code",[t._v(' +Inventory の分け方や変数の記述についても様々な形で記載しています。

これらの書式チェックに有効なansible-lintを活用するなど、できる限り「動くから良い」ではなく「他の人が使う可能性がある」と言ったことを考慮し、ベストプラクティスに沿った形で Playbook を作成して頂ければと思います。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/ansible/CREATE_INVENTORY.html b/cicd_infra/ansible/CREATE_INVENTORY.html index 5fa0250f..ad39f4fe 100644 --- a/cicd_infra/ansible/CREATE_INVENTORY.html +++ b/cicd_infra/ansible/CREATE_INVENTORY.html @@ -8,7 +8,7 @@ - + @@ -55,7 +55,7 @@ host00: host01:
1
2
3
4
5
6

なお、YAML書式についてはiniファイルのインベントリファイルがあれば以下のようなコマンドで作成することが可能です

ansible-inventory -i inventories/hosts --list -y
-
1
- +
1
+ diff --git a/cicd_infra/ansible/CREATE_PLAYBOOK.html b/cicd_infra/ansible/CREATE_PLAYBOOK.html index 93c6de6e..3c59e42e 100644 --- a/cicd_infra/ansible/CREATE_PLAYBOOK.html +++ b/cicd_infra/ansible/CREATE_PLAYBOOK.html @@ -8,7 +8,7 @@ - + @@ -67,7 +67,7 @@
  • 記載する場所どこでも構いませんがhosts等のセクションと同じレベル(同じインデントレベル)で宣言する必要があります。
gather_facts: false
 
1

# 設定ファイルでの停止

  • ansible.cfg に以下の通り記載することでこのplaybookに宣言しなくともgatherを停止することができます
    • 宣言する箇所は[defaults]セクションに宣言してください
    gathering = explicit
    -
    1
- +
1
+ diff --git a/cicd_infra/ansible/CREATE_SERVER.html b/cicd_infra/ansible/CREATE_SERVER.html index 07d9b689..0798ea55 100644 --- a/cicd_infra/ansible/CREATE_SERVER.html +++ b/cicd_infra/ansible/CREATE_SERVER.html @@ -8,7 +8,7 @@ - + @@ -93,7 +93,7 @@ Ansibleにおけるdryrunの実装はモジュールに委ねられており、Ansible全体としての動作を保証していません。 例えば、コマンドモジュール(command)のようなモジュールはチェックモードの分岐がなく、処理そのものがスキップされてしまいます。 こういったタスクが含まれている場合、コマンドの結果を次のタスクに受け渡す、 -といったタスクが失敗してしまうので注意して使う必要があります。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +といったタスクが失敗してしまうので注意して使う必要があります。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/ansible/INTRODUCTION.html b/cicd_infra/ansible/INTRODUCTION.html index 1b551b4b..d107a520 100644 --- a/cicd_infra/ansible/INTRODUCTION.html +++ b/cicd_infra/ansible/INTRODUCTION.html @@ -8,7 +8,7 @@ - + @@ -39,7 +39,7 @@ executable location = /usr/local/bin/ansible python version = 3.9.16 (main, May 29 2023, 00:00:00) [GCC 11.3.1 20221121 (Red Hat 11.3.1-4)] (/usr/bin/python3) jinja version = 3. -
1
2
3
4
5
6
7
8
9

ansible-core の 2.5.13やそれぞれのバージョンには実行する時期によって異なることがありますが問題ありません。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +
1
2
3
4
5
6
7
8
9

ansible-core の 2.5.13やそれぞれのバージョンには実行する時期によって異なることがありますが問題ありません。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/ansible/MANAGE_SETTINGS.html b/cicd_infra/ansible/MANAGE_SETTINGS.html index f15d86b3..ae4d4c58 100644 --- a/cicd_infra/ansible/MANAGE_SETTINGS.html +++ b/cicd_infra/ansible/MANAGE_SETTINGS.html @@ -8,7 +8,7 @@ - + @@ -43,7 +43,7 @@
1
2
3
4
5
6
7
8
9
10
11
12
13
14

正しく実行されれば SUCCESS と出力されます。

# 参考情報

# ansible.cfg パラメータの確認

Ansibleには様々な設定を施すことが可能な他、デフォルト値が定められている物もあります。 これらを確認するにはどうすれば良いのでしょうか。 また、どのような設定が可能なのか全ての設定値を覚えておくことは現実的ではありません。

こういったときのためにansible-config というコマンドを用いることができます。

 ansible-config
-
1
- +
1
+ diff --git a/cicd_infra/ansible/REVERSE_PROXY.html b/cicd_infra/ansible/REVERSE_PROXY.html index 0066f6cb..de41fd64 100644 --- a/cicd_infra/ansible/REVERSE_PROXY.html +++ b/cicd_infra/ansible/REVERSE_PROXY.html @@ -8,7 +8,7 @@ - + @@ -211,7 +211,7 @@ 実は Ansible にはタグという機能があり、タスクやロールなどに任意の値(タグ)を付けることができます。 これを利用することで、Ansible 実行時に任意のタスクのみを実行する/しないことができます。 実はさきほどのplaybooks/rp.ymlにタグが設定してありました。

タグは増やしすぎても管理がたいへんになるので、ある程度のまとまりやよく使うタスクにのみ付与するのが良いでしょう。 -タグの詳細については公式ドキュメント (opens new window)をご覧ください。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +タグの詳細については公式ドキュメント (opens new window)をご覧ください。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/ansible/SAMPLE_RUN.html b/cicd_infra/ansible/SAMPLE_RUN.html index 4d9f3fe9..60595f9b 100644 --- a/cicd_infra/ansible/SAMPLE_RUN.html +++ b/cicd_infra/ansible/SAMPLE_RUN.html @@ -8,7 +8,7 @@ - + @@ -32,7 +32,7 @@ "ping": "pong" }
1
2
3
4
5
6
7

Ansibleは基本的に実行すべき内容(task)を記載したplaybookを作成し、ansible-playbookコマンドを用いて実行するものですが、上記の通りplaybookを作成しなくとも実行できるコマンドもあるため、細かな日々の運用作業や確認作業などに使えます。

なお、ansible-playbookコマンド (opens new window)playbookと呼ばれるYAMLファイルにしたがってAnsibleを実行するコマンドになります。 -今回はAnsibleの実行イメージと出力のイメージを掴んで頂くためにアドホックで実行しましたが以後、このハンズオンでは主にansible-playbookコマンドを使っていきます。

- +今回はAnsibleの実行イメージと出力のイメージを掴んで頂くためにアドホックで実行しましたが以後、このハンズオンでは主にansible-playbookコマンドを使っていきます。

+ diff --git a/cicd_infra/ansible/USE_VARIABLE.html b/cicd_infra/ansible/USE_VARIABLE.html index b2b0e2a6..149de9a9 100644 --- a/cicd_infra/ansible/USE_VARIABLE.html +++ b/cicd_infra/ansible/USE_VARIABLE.html @@ -8,7 +8,7 @@ - + @@ -48,7 +48,7 @@ tasks: ---ここにtaskを記載する---
1
2
3
4
5

# tasks作成のヒント

# 動作確認

playbookが作成できたならば以下の通り実行します

ansible-playbook use_variable.yml
-
1

CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +
1

CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/ansible/index.html b/cicd_infra/ansible/index.html index 31535413..ee7e4930 100644 --- a/cicd_infra/ansible/index.html +++ b/cicd_infra/ansible/index.html @@ -8,7 +8,7 @@ - + @@ -22,7 +22,7 @@ vim等による開発でも問題ありませんが、講義においては講師の環境をvscodeにて説明を行うため、 vscode以外で開発を行う場合は適宜自身で読み替えてください。

# システム構成

この講義で使用するコンテナのネットワーク図です。 コンテナを VM(Virtual Machine)に見立て、ハンズオンを実施します。

ネットワーク図

この講義では図中の console コンテナから各ホストを管理します。

# 0. 事前準備

ハンズオン用の教材 (opens new window)を参照し -READMEに従ってansible演習環境のセットアップを行ってください。

# 1. Ansible 概要と導入

Ansibleの概要とインストール方法について学びます

# 2. インベントリの作成

Ansibleインベントリの概念とインベントリファイルの作成方法を学びます

# 3. Ansible 設定ファイルの管理

Ansibleの基本動作仕様とその変更方法について学びます

# 4. Ansible playbook の作成

Ansible playbookの概念と作成方法を学びます

# 5. Ansible によるサーバセットアップ

Ansible playbookを通じて実際にサーバに設定を加えていきます

# 6. 変数やループ処理の実行

Ansible playbookを通じてWebサーバを作ってみましょう

# 7. 正しい Playbook を書くために


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +READMEに従ってansible演習環境のセットアップを行ってください。

# 1. Ansible 概要と導入

Ansibleの概要とインストール方法について学びます

# 2. インベントリの作成

Ansibleインベントリの概念とインベントリファイルの作成方法を学びます

# 3. Ansible 設定ファイルの管理

Ansibleの基本動作仕様とその変更方法について学びます

# 4. Ansible playbook の作成

Ansible playbookの概念と作成方法を学びます

# 5. Ansible によるサーバセットアップ

Ansible playbookを通じて実際にサーバに設定を加えていきます

# 6. 変数やループ処理の実行

Ansible playbookを通じてWebサーバを作ってみましょう

# 7. 正しい Playbook を書くために


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/drone/index.html b/cicd_infra/drone/index.html index c3c195e1..70106a5f 100644 --- a/cicd_infra/drone/index.html +++ b/cicd_infra/drone/index.html @@ -8,7 +8,7 @@ - + @@ -177,7 +177,7 @@ socket: null database: drone-exercise-rails_test
1
2
3
4
5
6

このファイルで接続先MySQLサーバのホストを指定しているのですが、
-ここでは.drone.ymlで指定したdbがホスト名になっていて、この名前で接続できます。

チェックポイント7 🏁

テスト実行中にデータベースはどこで実行されているでしょうか?

# 8. 参考情報

# 続き

- +ここでは.drone.ymlで指定したdbがホスト名になっていて、この名前で接続できます。

チェックポイント7 🏁

テスト実行中にデータベースはどこで実行されているでしょうか?

# 8. 参考情報

# 続き

+ diff --git a/cicd_infra/github_actions/index.html b/cicd_infra/github_actions/index.html index 931d85c2..d93ccab6 100644 --- a/cicd_infra/github_actions/index.html +++ b/cicd_infra/github_actions/index.html @@ -8,7 +8,7 @@ - + @@ -244,7 +244,7 @@ Ruby をインストールします。 このあとの run では Ruby のコマンドを利用できます。

また、 一部 with で パラメータを与えられるものもあります。
(Ruby のバージョンを3にしてみるのもよいですね。)

なお、 この action の @v2 や @v1 は branch や tag などの Git 的 refs を意味します。
つまり、 GitHub 上の ある repository の内容を利用しているということです。
-そのため、 repository 側の更新により、意図せず振る舞いが変わる可能性があります。

TIP

公式でも「リリースされたアクションバージョンのコミットSHAを使用するのが、安定性とセキュリティのうえで最も安全です。」としています。

https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses (opens new window)

# 5. ほかにも

GitHub Actions の最大の特徴は action を組み合わせて workflow を実現できる点でした。

https://github.com/marketplace (opens new window) では さまざまな action を探すことができます

さらには 自分で action を作ることも可能です。

いろいろ探してみてください。

ちなみに、この資料も GitHub Actions で作られています。

https://github.com/iij/bootcamp/actions (opens new window)
https://github.com/iij/bootcamp/tree/master/.github/workflows (opens new window)

# 6. 参考情報

- +そのため、 repository 側の更新により、意図せず振る舞いが変わる可能性があります。

TIP

公式でも「リリースされたアクションバージョンのコミットSHAを使用するのが、安定性とセキュリティのうえで最も安全です。」としています。

https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses (opens new window)

# 5. ほかにも

GitHub Actions の最大の特徴は action を組み合わせて workflow を実現できる点でした。

https://github.com/marketplace (opens new window) では さまざまな action を探すことができます

さらには 自分で action を作ることも可能です。

いろいろ探してみてください。

ちなみに、この資料も GitHub Actions で作られています。

https://github.com/iij/bootcamp/actions (opens new window)
https://github.com/iij/bootcamp/tree/master/.github/workflows (opens new window)

# 6. 参考情報

+ diff --git a/cicd_infra/jenkins/index.html b/cicd_infra/jenkins/index.html index 979476ae..38a974fd 100644 --- a/cicd_infra/jenkins/index.html +++ b/cicd_infra/jenkins/index.html @@ -8,7 +8,7 @@ - + @@ -72,7 +72,7 @@ welcome to bootcamp bootcamp hogehoge <- パスワード -
1
2
3
4

ちなみに Jenkins の実行結果画面では、秘密情報はマスクされて見えないようになっています。

password3

# 最後に

Jenkins は非常にさまざまなことができますが、そのぶん使いこなすのが難しいです。さらに環境によってバージョンの違いや入っているプラグインの違いで微妙に使い勝手が違ったり、使い方が分かりにくかったり苦労することもあります。

ただしきちんと運用された Jenkins サーバを適切に使うと、さまざまなシステムと連携して自由度の高いビルドを実施できるため開発などの業務を効率化できます。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +
1
2
3
4

ちなみに Jenkins の実行結果画面では、秘密情報はマスクされて見えないようになっています。

password3

# 最後に

Jenkins は非常にさまざまなことができますが、そのぶん使いこなすのが難しいです。さらに環境によってバージョンの違いや入っているプラグインの違いで微妙に使い勝手が違ったり、使い方が分かりにくかったり苦労することもあります。

ただしきちんと運用された Jenkins サーバを適切に使うと、さまざまなシステムと連携して自由度の高いビルドを実施できるため開発などの業務を効率化できます。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/cicd_infra/prometheus/index.html b/cicd_infra/prometheus/index.html index 3466cf22..0a97baa6 100644 --- a/cicd_infra/prometheus/index.html +++ b/cicd_infra/prometheus/index.html @@ -8,7 +8,7 @@ - + @@ -216,7 +216,7 @@ replacement: blackbox_exporter:9115
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

relabel_configsの箇所ではblackbox_exporter自身のラベルを監視対象のラベルに置き換えてます。これをしないとPrometheusに収集されるデータがwordpressに関するものではなく、blackbox_exporterのものだと判断されてしまいます。

これで準備は完了です。docker-compose up -dでblackbox_exporterを起動させたのち、docker-compose restart prometheusでprometheusのコンフィグファイルを再読み込みしてください。localhost:9090でPrometheusサーバにアクセスし、StatusからTartgetを表示し、blackboxが存在すれば成功です。

blackbox_exporter

本当にPrometheusが取得できているかグラフを表示させてみましょう。まずPrometheusホーム画面のExpressionprobe_successと入力し、Excuteを押してください。

probe_success_table

タブをTableからGraphにし、値が1になっていれば成功です。0は取得に失敗しています。

probe_success

他にもprobe_http_status_codeを使えばそのサイトのHTTPステータスコードを確認できます。

probe_status

最後にGrafanaでHTTPステータスコードを表示して終わりにします。Grafanaをlocalhost:3000で開き、追加したいダッシュボードを選択します。 次にAdd Panelからパネル追加画面を開き、Metricsprobe_http_status_codeと入力します。

grafana_status_graph

このままだとステータスコードの数字をグラフにしてるだけで味気ないので、InstantをONにし、右のカラムのVisualizationStatに変更します。

grafana_status

最後にステータスコードごとに色を変えます。右のカラムのタブをFieldにし、Thresholdsを開きます。

thresholds

既存の設定は削除し、300を黄色、400を赤とします。このように設定することでこの値を閾値として、HTTPステータスコードが値を超えると値の色が変ります。下の画像では「ベースは赤(ここでは200未満)」「200以上300未満は青」「300以上400未満は黄色」「400以上は赤」となるように設定しています。

http_status

試しにwordpressの中身を書き換えてみましょう。

# docker exec -it wordpress_1 mv /var/www/html/wp-admin /var/www/html/wp-admins
-
1

を実行すればWebサーバの中身が書き換えられ、HTTPステータスコードが500になり、赤くなるはずです。

http_status

最後にApplyすることで簡単な監視ダッシュボードが完成しました。

all

# 2-4. まとめ

Prometheusは非常に柔軟性の高い有力なツールです。実は今回の講義ではPrometheusの持つ機能の一つであるサービスディスカバリの恩恵を最大限体験することが出来ていません。ほかにも閾値を超えたらアラートを投げるAlertmanagerなど、重要な機能がまだまだたくさんありますが、dockerにて用意する環境に限界があるため、今回は省略しました。Kubernetes上ではサービスディスカバリやAlertmanagerを比較的簡単に体験することができるため、興味のある方は公式サイト (opens new window)などを参考に、体験してみてください。

# 3. 最後に

本講義では「監視そのものの概念説明」から「実際に監視システムを構築する」ところまでを簡単に紹介しました。今回Prometheusをハンズオンにて使用しましたが、Prometheusは決して銀の弾丸にはなりえないということに注意してください。これまでの章で紹介をしましたがツール依存は代表的なアンチパターンです。監視しなければならない対象を明確にし、必要な機能を組み合わせて(時には自分で開発して)監視システムを構築する必要があります。

例えばSRE(Site Reliability Engineering)という考えのエンジニアリング手法では「ユーザに対するサービスレベルの定義」がとても重要になります。サービスレベルを客観的に評価してサービス品質を継続改善するために、指標であるSLI(Indicator)、SLIから算出される目標値であるSLO(Objectives)、そして必要に応じて契約としての合意であるSLA(Agreement)が定義されます。測定できないことは評価することができないため、監視システムが重要視されます。(評価できないと、システムが良いのか悪いのか、良くなったのか悪くなったのか、がわからないので、次のアクションを取ることができません)

監視という概念について詳しく知りたい方はオーライリーの「入門 監視」を読んでみるといいと思います。Prometheusについてもっと詳しく知りたい方はまだまだ日本語のドキュメントが充実していないため、手探りにはなりますが公式サイトを漁ってみたり、「Prometheus Meetup(JP)」などに参加して導入事例を聞いてみるといいと思います。一応「入門Prometheus」という本が出ています。

参考文献

  1. 入門 監視/Mike Julian(オーライリージャパン)
  2. わかばちゃんと学ぶサーバー監視/湊川あい(C&R研究所)
  3. SREサイトリライアビリティエンジニアリング/Betsy Beyer,Chris Jones,Jennifer Petoff,Niall Richard Murphy(オーライリージャパン)
  4. クラウドネイティブ・アーキテクチャ/Tom Laszewki,Kamal Arora,Erik Farr,Piyum Zonooz(インプレス)
- +
1

を実行すればWebサーバの中身が書き換えられ、HTTPステータスコードが500になり、赤くなるはずです。

http_status

最後にApplyすることで簡単な監視ダッシュボードが完成しました。

all

# 2-4. まとめ

Prometheusは非常に柔軟性の高い有力なツールです。実は今回の講義ではPrometheusの持つ機能の一つであるサービスディスカバリの恩恵を最大限体験することが出来ていません。ほかにも閾値を超えたらアラートを投げるAlertmanagerなど、重要な機能がまだまだたくさんありますが、dockerにて用意する環境に限界があるため、今回は省略しました。Kubernetes上ではサービスディスカバリやAlertmanagerを比較的簡単に体験することができるため、興味のある方は公式サイト (opens new window)などを参考に、体験してみてください。

# 3. 最後に

本講義では「監視そのものの概念説明」から「実際に監視システムを構築する」ところまでを簡単に紹介しました。今回Prometheusをハンズオンにて使用しましたが、Prometheusは決して銀の弾丸にはなりえないということに注意してください。これまでの章で紹介をしましたがツール依存は代表的なアンチパターンです。監視しなければならない対象を明確にし、必要な機能を組み合わせて(時には自分で開発して)監視システムを構築する必要があります。

例えばSRE(Site Reliability Engineering)という考えのエンジニアリング手法では「ユーザに対するサービスレベルの定義」がとても重要になります。サービスレベルを客観的に評価してサービス品質を継続改善するために、指標であるSLI(Indicator)、SLIから算出される目標値であるSLO(Objectives)、そして必要に応じて契約としての合意であるSLA(Agreement)が定義されます。測定できないことは評価することができないため、監視システムが重要視されます。(評価できないと、システムが良いのか悪いのか、良くなったのか悪くなったのか、がわからないので、次のアクションを取ることができません)

監視という概念について詳しく知りたい方はオーライリーの「入門 監視」を読んでみるといいと思います。Prometheusについてもっと詳しく知りたい方はまだまだ日本語のドキュメントが充実していないため、手探りにはなりますが公式サイトを漁ってみたり、「Prometheus Meetup(JP)」などに参加して導入事例を聞いてみるといいと思います。一応「入門Prometheus」という本が出ています。

参考文献

  1. 入門 監視/Mike Julian(オーライリージャパン)
  2. わかばちゃんと学ぶサーバー監視/湊川あい(C&R研究所)
  3. SREサイトリライアビリティエンジニアリング/Betsy Beyer,Chris Jones,Jennifer Petoff,Niall Richard Murphy(オーライリージャパン)
  4. クラウドネイティブ・アーキテクチャ/Tom Laszewki,Kamal Arora,Erik Farr,Piyum Zonooz(インプレス)
+ diff --git a/database/mongodb/index.html b/database/mongodb/index.html index 0b8e2c49..34b0f8ef 100644 --- a/database/mongodb/index.html +++ b/database/mongodb/index.html @@ -8,7 +8,7 @@ - + @@ -216,7 +216,7 @@ mongo-set [direct: secondary] bootcamp-db> mongo-set [direct: secondary] bootcamp-db> -
1
2
3
4
5
6
7
8
9

これはrs.reconfig()で設定したpriorityというパラメータに従い、より値の大きいホストがPrimaryになるように設定されているためです。

このようにMongoDBのレプリカセットでは、一台が落ちても自動的に他がPrimaryに昇格し、データの保存を継続できる構成を簡単に作ることができます。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
- +
1
2
3
4
5
6
7
8
9

これはrs.reconfig()で設定したpriorityというパラメータに従い、より値の大きいホストがPrimaryになるように設定されているためです。

このようにMongoDBのレプリカセットでは、一台が落ちても自動的に他がPrimaryに昇格し、データの保存を継続できる構成を簡単に作ることができます。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/database/mysql/index.html b/database/mysql/index.html index bf160397..9a2ef782 100644 --- a/database/mysql/index.html +++ b/database/mysql/index.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ GitHub (opens new window)
このハンズオンでやることMySQL を立ち上げSQLを触ることで、RDBの勘所を学びます。
想定時間1h
前提知識・用語なし

# MySQLを触ってみる

資料はこちら (opens new window)


CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
- + (opens new window)
このハンズオンでやることMySQL を立ち上げSQLを触ることで、RDBの勘所を学びます。
想定時間1h
前提知識・用語なし

# MySQLを触ってみる

資料はこちら (opens new window)


CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
+ diff --git a/database/overview/index.html b/database/overview/index.html index 67aa15b2..1a97a088 100644 --- a/database/overview/index.html +++ b/database/overview/index.html @@ -8,7 +8,7 @@ - + @@ -46,7 +46,7 @@
dead_lock_01
  • トランザクション2がBさんの口座をロックし5万円減額し15万円にする
    dead_lock_02
  • トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機
    dead_lock_03
  • トランザクション2がAさんの口座を5万増額しようとするが、トランザクション1が口座をロック中のため待機 -
    dead_lock_04
  • 上記例は、トランザクション1はBさんの口座のロック解除を待つ必要があるが、それにはトランザクション2が終了する必要があり、トランザクション2はAさんの口座のロック解除を待つしか無いが、それにはトランザクション1が終了する必要があり、処理が永久に進まなくなる。そのため、データベースには、デッドロック状態を検知し、デッドロックとなったトランザクションの片方を強制中断させることで、デッドロックを解消する機能が備えられている。

    デッドロックによって強制中断したトランザクションは、データベースによって再度トランザクションが実行されることはないので、アプリケーション側でデッドロックによる中断を検知したらトランザクションを再実行する必要がある。デッドロックは、複数トランザクションを同時実行するのであれば必ず発生するため、アプリケーション作成時にデッドロックが起きる想定で設計しておく必要がある。

    # 分離レベル(isolation level)とは

    トランザクション間の分離・独立性をどのぐらい緩めるか4段階で定義したもの。

    分離レベルの名称 分離レベルの詳細 ダーティーリード ノンリピータブルリード ファンタムリード
    READ UNCOMMITTED 他のトランザクションの確定してない(コミットされていない)データを読み取れる 起きる 起きる 起きる
    READ COMMITTED 他のトランザクションが確定した(コミットした)データを読み取れる - 起きる 起きる
    REPEATABLE READ トランザクション内では同じデータは何度読み取っても同じデータになる - - 起きる
    SERIALIZABLE 複数トランザクションを同時実行しても1つずつ逐次実行した時と同じ結果になる - - -

    SERIALIZABLE以外の分離レベルでは、分離性・独立性を緩めることになるため、分離性・独立性に違反する現象が発生することを理解する必要がある(データベースの実装によって上記表とは異なるケースもある。例えば、MySQLはREPEATABLE READでもファンタムリードは発生しない実装になっている)

    • ダーティーリード(dirty read)

      トランザクション1がデータAを更新した後、トランザクション2がデータAを読み取った状態で、トランザクション1が実行されなければ(ロールバックされる)、トランザクション2が読み取ったデータは実在しないデータを読み取ることになってしまう現象。

      取得データを元に何らかの処理を行う場合は、実在しないデータを元に処理を実施してしまう状況になるため、意図しない結果を出力してしまう恐れがある。

    • ノンリピーダブルリード(non-repeatable read)

      トランザクション1がデータAを読み取ったあと、トランザクション2がデータAを変更して確定(コミット)し、トランザクション1が再度データAを読み取った場合、1回目の読み取りと2回目の読み取り結果が異なってしまう現象。

      読み取るタイミングによってデータ異なってしまうため、アプリケーションの実装によっては、意図しない結果を出力してしまう恐れがある。

    • ファンタムリード(phantom read)

      トランザクション1が条件Aで検索してデータを読み取ると、データα・βが取得できるとする。トランザクション2が、条件Aに該当するデータγを追加し確定(コミット)した場合、トランザクション1が再び条件Aで検索してデータを読み取ると、データα・β以外に、1回目の読み取りでは存在してなかった、トランザクション2が追加したデータγも読み取ってしまうような現象。

      登録データ数などを根拠に処理を行うような実装などで意図しない結果を出力してしまう恐れがある。

    高い分離レベルを実現するためには、ロックをかけてデータを保護するため必要があるため、同時実行速度が犠牲になる。一方で低い分離レベルは、ロックをかける必要がなくなるため、同時実行時の速度が向上する。データベース製品ごとにどのように分離レベルが実装されているかを理解しつつ、上記のような現象が発生するリスクを許容できるかアプリケーション設計者がよく検討した上で、データの安全性よりも実行速度を重視したい場合、分離レベルを低くすることも1つの選択肢になる。

    # 永続性(Durability)とは

    永続性とは、トランザクションが確定(コミット)したら、それ以降にデータが失われないように保証する特性。

    トランザクションにおいて、処理が全て完了し確定(コミット)する場合、確定(コミット)が成功したら、それ以降は確定されたデータは永久に消えない状態になっている必要がある。

    しかし、一般的に永続ディスク(ハードディスク・SSDなど)への書き込みは低速で時間のかかる処理とされている。そのため、LinuxなどのOSは、書き込み処理を高速化するため、永続ディスクよりも高速に書き込みが可能なキャッシュ領域にデータを書き込むことでアプリケーションに対しては書き込み処理完了とするような実装になっている。そして、キャッシュ領域から永続ディスクへは、一定間隔でまとめて書き込みを行う(フラッシュ処理)実装になっているが、キャッシュ領域で使われるのは、揮発性のメモリー(RAM)であるため、電源断が生じるとデータが消失してしまう。

    仮に、確定(コミット)が成功したかの根拠となるデータの書き込み成功が、OSが用意しているキャッシュ(揮発性メモリー)領域への書き込みだった場合、電源断などの障害によってデータが消失する可能性があり、永続性の原則に違反する状況が発生してしまう。データベースでは、こうした状況を防ぐため確定(コミット)する際に、OSのキャッシュ領域ではなく、永続ディスクへの書き込みになるように制御するような機能が備わっている。

    # まとめ

    # データを扱うことの難しさを知る

    データを確実に保存し、必要な場面で確実に利用するためには、様々なことに注意を払う必要があります。アプリケーション開発者は、これまで見てきたようなデータに関して起きうる全ての状況を想定しながら、アプリケーションの設計・実装していく必要がありますが、データに関して起きうる全ての事象に対応したアプリケーションを1から実装するのは非現実的です(いわゆる車輪の再発明 (opens new window))。

    # データベースを学び利用する

    データベースを学ぶことは、データを扱う上で起きうる事象を学ぶことができ、アプリケーション開発の中でデータを扱う際に何に配慮すれば良いか見えて来ると思います。また、データベースを利用することで、データを扱う上で起きうる事象への配慮をデータベースに任せることができれば、アプリケーション開発者はアプリケーションに求められる本質的な機能の実装に集中でき、より良いアプリケーション開発を行うことができるようになると思います。

    # 必要な機能を備えたデータベース製品を選定する

    昨今、様々なデータベース製品がリリースされています。それらの製品ごとにメリット・デメリット、得意・不得意な領域が存在します。データの安全性を重視するのか、それとも速度を重視するかなど、アプリケーションごとにデータに対する要求の方向性は変わってきますが、求める方向性に合致するデータベース製品を選定できるようになるためにも、データベースの知識を持つことが重要だと思います。

    - +
    dead_lock_04

    上記例は、トランザクション1はBさんの口座のロック解除を待つ必要があるが、それにはトランザクション2が終了する必要があり、トランザクション2はAさんの口座のロック解除を待つしか無いが、それにはトランザクション1が終了する必要があり、処理が永久に進まなくなる。そのため、データベースには、デッドロック状態を検知し、デッドロックとなったトランザクションの片方を強制中断させることで、デッドロックを解消する機能が備えられている。

    デッドロックによって強制中断したトランザクションは、データベースによって再度トランザクションが実行されることはないので、アプリケーション側でデッドロックによる中断を検知したらトランザクションを再実行する必要がある。デッドロックは、複数トランザクションを同時実行するのであれば必ず発生するため、アプリケーション作成時にデッドロックが起きる想定で設計しておく必要がある。

    # 分離レベル(isolation level)とは

    トランザクション間の分離・独立性をどのぐらい緩めるか4段階で定義したもの。

    分離レベルの名称 分離レベルの詳細 ダーティーリード ノンリピータブルリード ファンタムリード
    READ UNCOMMITTED 他のトランザクションの確定してない(コミットされていない)データを読み取れる 起きる 起きる 起きる
    READ COMMITTED 他のトランザクションが確定した(コミットした)データを読み取れる - 起きる 起きる
    REPEATABLE READ トランザクション内では同じデータは何度読み取っても同じデータになる - - 起きる
    SERIALIZABLE 複数トランザクションを同時実行しても1つずつ逐次実行した時と同じ結果になる - - -

    SERIALIZABLE以外の分離レベルでは、分離性・独立性を緩めることになるため、分離性・独立性に違反する現象が発生することを理解する必要がある(データベースの実装によって上記表とは異なるケースもある。例えば、MySQLはREPEATABLE READでもファンタムリードは発生しない実装になっている)

    • ダーティーリード(dirty read)

      トランザクション1がデータAを更新した後、トランザクション2がデータAを読み取った状態で、トランザクション1が実行されなければ(ロールバックされる)、トランザクション2が読み取ったデータは実在しないデータを読み取ることになってしまう現象。

      取得データを元に何らかの処理を行う場合は、実在しないデータを元に処理を実施してしまう状況になるため、意図しない結果を出力してしまう恐れがある。

    • ノンリピーダブルリード(non-repeatable read)

      トランザクション1がデータAを読み取ったあと、トランザクション2がデータAを変更して確定(コミット)し、トランザクション1が再度データAを読み取った場合、1回目の読み取りと2回目の読み取り結果が異なってしまう現象。

      読み取るタイミングによってデータ異なってしまうため、アプリケーションの実装によっては、意図しない結果を出力してしまう恐れがある。

    • ファンタムリード(phantom read)

      トランザクション1が条件Aで検索してデータを読み取ると、データα・βが取得できるとする。トランザクション2が、条件Aに該当するデータγを追加し確定(コミット)した場合、トランザクション1が再び条件Aで検索してデータを読み取ると、データα・β以外に、1回目の読み取りでは存在してなかった、トランザクション2が追加したデータγも読み取ってしまうような現象。

      登録データ数などを根拠に処理を行うような実装などで意図しない結果を出力してしまう恐れがある。

    高い分離レベルを実現するためには、ロックをかけてデータを保護するため必要があるため、同時実行速度が犠牲になる。一方で低い分離レベルは、ロックをかける必要がなくなるため、同時実行時の速度が向上する。データベース製品ごとにどのように分離レベルが実装されているかを理解しつつ、上記のような現象が発生するリスクを許容できるかアプリケーション設計者がよく検討した上で、データの安全性よりも実行速度を重視したい場合、分離レベルを低くすることも1つの選択肢になる。

    # 永続性(Durability)とは

    永続性とは、トランザクションが確定(コミット)したら、それ以降にデータが失われないように保証する特性。

    トランザクションにおいて、処理が全て完了し確定(コミット)する場合、確定(コミット)が成功したら、それ以降は確定されたデータは永久に消えない状態になっている必要がある。

    しかし、一般的に永続ディスク(ハードディスク・SSDなど)への書き込みは低速で時間のかかる処理とされている。そのため、LinuxなどのOSは、書き込み処理を高速化するため、永続ディスクよりも高速に書き込みが可能なキャッシュ領域にデータを書き込むことでアプリケーションに対しては書き込み処理完了とするような実装になっている。そして、キャッシュ領域から永続ディスクへは、一定間隔でまとめて書き込みを行う(フラッシュ処理)実装になっているが、キャッシュ領域で使われるのは、揮発性のメモリー(RAM)であるため、電源断が生じるとデータが消失してしまう。

    仮に、確定(コミット)が成功したかの根拠となるデータの書き込み成功が、OSが用意しているキャッシュ(揮発性メモリー)領域への書き込みだった場合、電源断などの障害によってデータが消失する可能性があり、永続性の原則に違反する状況が発生してしまう。データベースでは、こうした状況を防ぐため確定(コミット)する際に、OSのキャッシュ領域ではなく、永続ディスクへの書き込みになるように制御するような機能が備わっている。

    # まとめ

    # データを扱うことの難しさを知る

    データを確実に保存し、必要な場面で確実に利用するためには、様々なことに注意を払う必要があります。アプリケーション開発者は、これまで見てきたようなデータに関して起きうる全ての状況を想定しながら、アプリケーションの設計・実装していく必要がありますが、データに関して起きうる全ての事象に対応したアプリケーションを1から実装するのは非現実的です(いわゆる車輪の再発明 (opens new window))。

    # データベースを学び利用する

    データベースを学ぶことは、データを扱う上で起きうる事象を学ぶことができ、アプリケーション開発の中でデータを扱う際に何に配慮すれば良いか見えて来ると思います。また、データベースを利用することで、データを扱う上で起きうる事象への配慮をデータベースに任せることができれば、アプリケーション開発者はアプリケーションに求められる本質的な機能の実装に集中でき、より良いアプリケーション開発を行うことができるようになると思います。

    # 必要な機能を備えたデータベース製品を選定する

    昨今、様々なデータベース製品がリリースされています。それらの製品ごとにメリット・デメリット、得意・不得意な領域が存在します。データの安全性を重視するのか、それとも速度を重視するかなど、アプリケーションごとにデータに対する要求の方向性は変わってきますが、求める方向性に合致するデータベース製品を選定できるようになるためにも、データベースの知識を持つことが重要だと思います。

    + diff --git a/database/postgresql/index.html b/database/postgresql/index.html index dd48f9d0..82824354 100644 --- a/database/postgresql/index.html +++ b/database/postgresql/index.html @@ -8,7 +8,7 @@ - + @@ -213,7 +213,7 @@ ※再度利用する際には docker start some-postgres にて起動可能させます。

    docker stop some-postgres
     
     docker ps -a
    -
    1
    2
    3

    以上

    - +
    1
    2
    3

    以上

    + diff --git a/database/redis/index.html b/database/redis/index.html index 28d88a3f..959ba234 100644 --- a/database/redis/index.html +++ b/database/redis/index.html @@ -8,7 +8,7 @@ - + @@ -284,7 +284,7 @@ }
    1
    2
    3
    4
    5

    サンプル: https://github.com/iij/bootcamp/blob/master/src/database/redis/ex02.py (opens new window)

    # Expire を試してみる

    # メッセージキューを実装する

    • メッセージキュー的なものを実装してみよう

      • 依頼側: ランダム秒 sleep して、メッセージをキューに入れる
      • 取り出し側: ランダム秒 sleep して、メッセージをキューから取り出す
    • 使用例: web クローラー

      • 依頼側: URL をどんどんキューに入れる
      • 取り出し側: キューを見て URL が入っていたら、取得してファイルに保存する -
        • 結果(ファイルの場所)を別のキューで戻してもいいですね

    # Advanced: Redis Pub/Sub, Streams を使ってみよう

    # Advanced: GIS で遊んでみよう

    # 参考資料


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +
    • 結果(ファイルの場所)を別のキューで戻してもいいですね

    # Advanced: Redis Pub/Sub, Streams を使ってみよう

    # Advanced: GIS で遊んでみよう

    # 参考資料


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker-compose/index.html b/development/docker/docker-compose/index.html index c6fa20bc..4402877c 100644 --- a/development/docker/docker-compose/index.html +++ b/development/docker/docker-compose/index.html @@ -8,7 +8,7 @@ - + @@ -151,7 +151,7 @@
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

    # 4. まとめ

    本講義では、Docker Compose を紹介し、実際にdocker compose コマンドを使って、複数のサービスを管理していただきました。 複数のDocker コンテナを管理する場合、Docker Compose を用いるとDocker単独で利用するよりも効率的に管理することができるためぜひ利用してください。 また、OSS の中ではDocker イメージを始め、docker-compose.yml を公開しているものも多いため、それらを使って簡単に検証作業や環境構築などを行うことができます。 -ぜひ有効活用してみてください。


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    - +ぜひ有効活用してみてください。


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker/BUILD.html b/development/docker/docker/BUILD.html index 0919df1f..382a227b 100644 --- a/development/docker/docker/BUILD.html +++ b/development/docker/docker/BUILD.html @@ -8,7 +8,7 @@ - + @@ -61,7 +61,7 @@ </html>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    上記のように実際に作成したHTMLが表示されていれば成功です。 プロキシを設定している場合は下記のように--noproxyオプションを指定する必要があります。

    $ curl --noproxy localhost http://localhost:8888
    -
    1

    # 参考情報

    # Docker イメージの共有方法

    皆さんが作成したDocker イメージなどを他の人に共有したい場合、Dockerfile をファイルサーバやGitHub 等で共有する以外に、Docker Hub (opens new window)を始めとする「Docker イメージレジストリ」で公開し、それを利用してもらうことが可能です。例えば、本講義で利用した「getting-started」や「ubuntu」のDocker イメージは、Docker Hubで公開されているものを利用しています。

    Docker イメージレジストリに自分のDocker イメージを公開する際には、docker push コマンドを利用します。逆に、Docker イメージをダウンロードしたい場合は、docker pull コマンドを利用します。また、Dockerイメージをdocker save コマンドでtarファイルとして保存し、docker load コマンドでtarからロードするといったことも行うことができます。

    # まとめ

    今回は、仮想環境プラットフォームであるDocker を実際に触っていただきました。Docker の基礎知識、Docker コンテナ、イメージの管理方法に加え、Dockerfile を作成し自身でコンテナを作っていただきました。仮想マシンに比べ軽く高速で作成することが可能なため使いどころは多いでしょう。しかしながら、実際のサービスでは、単一のアプリケーションのみで1つのコンテナで完成することはありえません。Web サーバやWeb アプリケーション、データベースなど様々なものが1つに組み合わさって初めてサービスとして完成します。Docker コンテナでは、「1コンテナ1プロセス」や「1コンテナ1ロール」が基本となっています。そのため複数のコンテナを組み合わせて構築する必要があります。次の講義「開発環境をdocker-composeで構築」では、複数のコンテナを組み合わせて管理する手法について学びます。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +
    1

    # 参考情報

    # Docker イメージの共有方法

    皆さんが作成したDocker イメージなどを他の人に共有したい場合、Dockerfile をファイルサーバやGitHub 等で共有する以外に、Docker Hub (opens new window)を始めとする「Docker イメージレジストリ」で公開し、それを利用してもらうことが可能です。例えば、本講義で利用した「getting-started」や「ubuntu」のDocker イメージは、Docker Hubで公開されているものを利用しています。

    Docker イメージレジストリに自分のDocker イメージを公開する際には、docker push コマンドを利用します。逆に、Docker イメージをダウンロードしたい場合は、docker pull コマンドを利用します。また、Dockerイメージをdocker save コマンドでtarファイルとして保存し、docker load コマンドでtarからロードするといったことも行うことができます。

    # まとめ

    今回は、仮想環境プラットフォームであるDocker を実際に触っていただきました。Docker の基礎知識、Docker コンテナ、イメージの管理方法に加え、Dockerfile を作成し自身でコンテナを作っていただきました。仮想マシンに比べ軽く高速で作成することが可能なため使いどころは多いでしょう。しかしながら、実際のサービスでは、単一のアプリケーションのみで1つのコンテナで完成することはありえません。Web サーバやWeb アプリケーション、データベースなど様々なものが1つに組み合わさって初めてサービスとして完成します。Docker コンテナでは、「1コンテナ1プロセス」や「1コンテナ1ロール」が基本となっています。そのため複数のコンテナを組み合わせて構築する必要があります。次の講義「開発環境をdocker-composeで構築」では、複数のコンテナを組み合わせて管理する手法について学びます。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker/GETSTART.html b/development/docker/docker/GETSTART.html index e676f100..5ff72d28 100644 --- a/development/docker/docker/GETSTART.html +++ b/development/docker/docker/GETSTART.html @@ -8,7 +8,7 @@ - + @@ -51,7 +51,7 @@
    1
  • コンテナが停止したことの確認
    • ブラウザにて http://localhost (opens new window)にアクセスし、アクセスできないことを確認する
    • docker psコマンドを用いて、何も表示されないことを確認する
      docker ps
       CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
      -
      1
      2

  • CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +
    1
    2

    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker/OPERATION.html b/development/docker/docker/OPERATION.html index aaab6d67..af0d552f 100644 --- a/development/docker/docker/OPERATION.html +++ b/development/docker/docker/OPERATION.html @@ -8,7 +8,7 @@ - + @@ -50,7 +50,7 @@ docker/getting-started
    1
    2

    # まとめ

    ここまで実施することでDockerコンテナの取得から後片付けまでの一通りの作業ができるようになりました。 しかし、実際に自分好みのDockerイメージを作るにはどうすれば良いでしょうか。 -次項ではDockerイメージの作り方について学びたいと思います。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +次項ではDockerイメージの作り方について学びたいと思います。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker/RUN_AS_IMAGE.html b/development/docker/docker/RUN_AS_IMAGE.html index 3f782072..0fe9949c 100644 --- a/development/docker/docker/RUN_AS_IMAGE.html +++ b/development/docker/docker/RUN_AS_IMAGE.html @@ -8,7 +8,7 @@ - + @@ -50,7 +50,7 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    1
    2

    # 参考

    # Docker イメージのビルド

    Dockerコンテナを使って仮想環境プラットフォームを作成するためには、Dockerイメージが必要となります。

    通常であれば、Dockerfileを使用して自分のアプリケーションのDockerイメージを作成します。 Dockerfileは、アプリケーションの依存関係や設定、実行コマンドなどを指定するためのテキストファイルです。 -DockerFileが作成できたらDockerイメージをビルドして作成します。その際に使うコマンドはdocker buildになります。

    通常であればテキストエディタを開いてDockerFileを作成しますが、完全にゼロの状態からDockerFileを作成するのは難しい為、先ずはチュートリアル用に公開されている物を使用すると良いでしょう


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +DockerFileが作成できたらDockerイメージをビルドして作成します。その際に使うコマンドはdocker buildになります。

    通常であればテキストエディタを開いてDockerFileを作成しますが、完全にゼロの状態からDockerFileを作成するのは難しい為、先ずはチュートリアル用に公開されている物を使用すると良いでしょう


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/development/docker/docker/index.html b/development/docker/docker/index.html index 00f43c78..7e17c2cf 100644 --- a/development/docker/docker/index.html +++ b/development/docker/docker/index.html @@ -8,7 +8,7 @@ - + @@ -23,7 +23,7 @@ Dockerのインストールが完了していない方は、「ハンズオン事前準備」を済ませてください。

    # Chapters

    # 参考

    # 仮想マシン vs コンテナ

    仮想マシンとコンテナ、どちらが優れており、どちらを使うべきなのでしょう?

    それは一概にどちらが優れているからそうすべき、といった類いの物ではありません。 コンテナは仮想マシンと比べて後発であるため、仮想マシンが抱えていた問題を解決しているのは確かですが、上位互換という事ではありません。 コンテナには仮想マシンには無いメリットもありますがデメリットもあります。 -コンテナは仮想マシンと異なり、OS の中で別のOSを起動するという事が無いため、セットアップや起動時においてもOSに関わる処理を必要としない為、時間を大幅に短縮することが可能です。また、ハードウェアにアクセスする際もゲストOSを介することが無いため、オーバーヘッドが少なく高速な動作が期待できます。

    しかし、その一方で仮想マシンと比べてホストOSとの独立性が少ないため、ホストOSとアーキテクチャが大きく異なるコンテナを共存させることはできません。

    例)Linux 上で Windows コンテナを起動・実行する

    # Docker のアーキテクチャ

    Docker Image


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +コンテナは仮想マシンと異なり、OS の中で別のOSを起動するという事が無いため、セットアップや起動時においてもOSに関わる処理を必要としない為、時間を大幅に短縮することが可能です。また、ハードウェアにアクセスする際もゲストOSを介することが無いため、オーバーヘッドが少なく高速な動作が期待できます。

    しかし、その一方で仮想マシンと比べてホストOSとの独立性が少ないため、ホストOSとアーキテクチャが大きく異なるコンテナを共存させることはできません。

    例)Linux 上で Windows コンテナを起動・実行する

    # Docker のアーキテクチャ

    Docker Image


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/development/git/index.html b/development/git/index.html index adb374b6..2460d717 100644 --- a/development/git/index.html +++ b/development/git/index.html @@ -8,7 +8,7 @@ - + @@ -171,7 +171,7 @@ こんにちは ぎっと おやすみ ぎっと
    1
    2
    3

    Git の基本操作を一通り流してやってみました。 -Git ハンズオンは以上で終了です。

    次は GitHub ハンズオン に進んでください。

    # 7. 参考文献

    さらに高度な使い方を知りたい方はPro Git (opens new window)が便利です。日本語版が公開されています。

    こんな説明じゃブランチがわからん!という人は Learn Git Branching (日本語版) (opens new window) で練習できます。

    # 7.1. GUIクライアント

    Gitには、機能が限定されますがGUIクライアントも用意されています。おすすめは以下の3つのクライアントです。

    また各種統合開発環境にもGitを操作する機能が含まれています。

    これらのクライアントの利用も検討してください。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +Git ハンズオンは以上で終了です。

    次は GitHub ハンズオン に進んでください。

    # 7. 参考文献

    さらに高度な使い方を知りたい方はPro Git (opens new window)が便利です。日本語版が公開されています。

    こんな説明じゃブランチがわからん!という人は Learn Git Branching (日本語版) (opens new window) で練習できます。

    # 7.1. GUIクライアント

    Gitには、機能が限定されますがGUIクライアントも用意されています。おすすめは以下の3つのクライアントです。

    また各種統合開発環境にもGitを操作する機能が含まれています。

    これらのクライアントの利用も検討してください。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/development/github/index.html b/development/github/index.html index 525f2ba2..ec10ab9b 100644 --- a/development/github/index.html +++ b/development/github/index.html @@ -8,7 +8,7 @@ - + @@ -100,7 +100,7 @@ (opens new window)

    コラボレーターを個人リポジトリに招待する(GitHub Enterprise版) (opens new window)

    1. ローカルブランチを作成する
    2. なにかを修正する
    3. ローカルブランチにコミットする
    4. pushする
    5. mainブランチに対してPull Requestを作成する
    6. TAがレビューする
    7. 必要に応じてさらに修正する
    8. レビューが承認される
    9. mainにマージされる
    10. (オプション) TAがコミットするのであなたがレビューしてください!

    # 7. Gist

    Gist (opens new window) はテキストデータを保存し、公開することのできる「Pastebin」と呼ばれるサービスの一種です。 GitHubのアカウントを持っている人は自由に作成でき、参照は自由にできます。 コードハイライトを利用することもできます。

    IIJでは社内での情報共有のためConfluence (opens new window)が導入されていますが、 -confluenceにgistのURLを貼ると内容が自動で展開されるマクロが導入されています。

    gistをcfに貼り付けた様子

    # 8. Next Action

    このハンズオンが終わったら自分のメンターに相談して、所属部署のリポジトリにPull Requestをしてみましょう。

    # 9. 参考資料


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +confluenceにgistのURLを貼ると内容が自動で展開されるマクロが導入されています。

    gistをcfに貼り付けた様子

    # 8. Next Action

    このハンズオンが終わったら自分のメンターに相談して、所属部署のリポジトリにPull Requestをしてみましょう。

    # 9. 参考資料


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/development/kubernetes/index.html b/development/kubernetes/index.html index 0b3f2e90..2546716a 100644 --- a/development/kubernetes/index.html +++ b/development/kubernetes/index.html @@ -8,7 +8,7 @@ - + @@ -415,7 +415,7 @@ prometheus_main-nemu これでKuberentes上の各コンポーネントに対して監視を行うことが出来ました。

    【Prometheus講義を受講した人向け】

    式ブラウザから各種APIオブジェクトのメトリクス情報を取得してみてください。 また、Kubernetes上で動くアプリケーションの監視にはkube-state-metricsやcAdvidsorといったエクスポートを利用します。余裕のある人はPodの監視も行ってみてください。

    # 5. 最後に

    Kubernetesの紹介と代表的なオブジェクトであるDeploymentServiceについて簡単に触ってみました。 -Kubetenetesでは他にも様々なオブジェクトや設定を使います。

    例えば

    • アプリケーションの環境変数を設定するためにenvを使う
    • データベースなどステートフルなpodを稼働させるためにStatefulSetを使う
    • データの永続化のためPersistentVolumePersistentVolumeClaimを使う
    • アプリケーションのコンフィグファイルを管理するのにConfigMapを使う
    • APIキーやパスワードなど秘密情報を扱うためにSecretを使う(ただしキー値はbase64でエンコードされた文字列)

    などなどです。PersistentVolumePersistentVolumeClaimなどはディストリビューションごとに扱い方が違ったりしますし、構築したいアプリケーションによってマニフェストファイルの書き方は様々なので今回は割愛しました。

    社内でKubetenetesを使ってアプリケーションを構築する場合はIKEが使いやすいかと思いますので、ぜひチュートリアルなどを眺めてみてください。

    【参考文献】

    1. Kubernetes完全ガイド/青山信也(インプレス)
    2. イラストでわかるDockerとKubernetes/徳永航平(技術評論社)
    3. Docker/Kubernetes実践コンテナ開発入門/山田明憲(技術評論社)
    4. Kubernetes公式ドキュメント (opens new window)/CNCF
    5. Prometheus実践ガイド/仲亀拓馬(テッキーメディア)
    - +Kubetenetesでは他にも様々なオブジェクトや設定を使います。

    例えば

    • アプリケーションの環境変数を設定するためにenvを使う
    • データベースなどステートフルなpodを稼働させるためにStatefulSetを使う
    • データの永続化のためPersistentVolumePersistentVolumeClaimを使う
    • アプリケーションのコンフィグファイルを管理するのにConfigMapを使う
    • APIキーやパスワードなど秘密情報を扱うためにSecretを使う(ただしキー値はbase64でエンコードされた文字列)

    などなどです。PersistentVolumePersistentVolumeClaimなどはディストリビューションごとに扱い方が違ったりしますし、構築したいアプリケーションによってマニフェストファイルの書き方は様々なので今回は割愛しました。

    社内でKubetenetesを使ってアプリケーションを構築する場合はIKEが使いやすいかと思いますので、ぜひチュートリアルなどを眺めてみてください。

    【参考文献】

    1. Kubernetes完全ガイド/青山信也(インプレス)
    2. イラストでわかるDockerとKubernetes/徳永航平(技術評論社)
    3. Docker/Kubernetes実践コンテナ開発入門/山田明憲(技術評論社)
    4. Kubernetes公式ドキュメント (opens new window)/CNCF
    5. Prometheus実践ガイド/仲亀拓馬(テッキーメディア)
    + diff --git a/frontend/angular/index.html b/frontend/angular/index.html index 1c989500..7d000767 100644 --- a/frontend/angular/index.html +++ b/frontend/angular/index.html @@ -8,7 +8,7 @@ - + @@ -307,7 +307,7 @@ ).subscribe(num => { console.log(num); // 「6」「8」「10」 が順番に表示される }); -
    1
    2
    3
    4
    5
    6
    7

    やはり詳しくは 公式ドキュメント (opens new window) を参照してください(若干分かりにくいかもしれません)。

    Angular ではHTTPリクエストはもちろん、URLの変更やform要素の入力、ユーザーイベントなど全ての変更がObservableで処理されています。


    - +
    1
    2
    3
    4
    5
    6
    7

    やはり詳しくは 公式ドキュメント (opens new window) を参照してください(若干分かりにくいかもしれません)。

    Angular ではHTTPリクエストはもちろん、URLの変更やform要素の入力、ユーザーイベントなど全ての変更がObservableで処理されています。


    + diff --git a/frontend/dom/index.html b/frontend/dom/index.html index 43f2c8ca..14f040c6 100644 --- a/frontend/dom/index.html +++ b/frontend/dom/index.html @@ -8,7 +8,7 @@ - + @@ -469,7 +469,7 @@
    1

    最後に、ここまでで中身を組み立てたp要素が画面上で見えるよう、bodyに追加します。bodyもDOM要素の一つなので、やることは先ほどと同様.appendChildを、今度はp要素に対して呼ぶだけです:

    document.body.appendChild(p);
     
    1

    これで、メッセージを投稿するSNSっぽいものができました🎉!

    # 🚩チェックポイント

    スクリーンショットのように、「投稿」と書かれたボタンをクリックする度、入力欄に入力したメッセージを追記する機能が実装できた。

    # 要素を削除する

    人生、誰しもやり直したいことがありますよね。メッセージを追記する機能を実装したまではよかったものの、もしかしたら間違ってあらぬことを投稿してしまって、削除したくなるかも知れません。そこで最後は、DOM要素を削除する方法を学ぶことで、追記したメッセージを削除する機能を作ってみましょう!

    まずは下準備として、前節で作成した5.html6.htmlという名前にコピーします。それから、再び投稿するbutton要素のイベントリスナーの本体を編集します。

    下準備ができたら、先ほどのイベントリスナーでp要素を作った後、コード例で言うところのconst p = document.createElement("p");という行の下で、新しくbutton要素を作り、p要素にappendChildしてください。そうすることで、一つ一つのメッセージの左側にボタンが作られます。button要素の中身は、Xと一文字だけ入ったテキストノードにしましょう。後でこれにイベントリスナーを追加することで、「削除ボタン」として機能してもらいます。

    これまでの総復習のようなコードになるのでよく思い出しながら書いてください。うまくいっていれば、次👇のスクリーンショットにあるようなボタンが、各メッセージの左に表示されるはずです。うまくいかなかったら、TAの方などに聞いてみてください!

    あと一歩です。仕上げとして、新たに追加したボタンの、clickイベントに対してイベントリスナーを追加します。そして、そのイベントリスナーの関数本体で、removeChild (opens new window)というメソッドを呼び出します:

    document.body.removeChild(p);
     
    1

    上記のremoveChildというメソッドが、まさしく要素を削除するためのメソッドです。removeChildは、呼び出し元の要素における直接の子ノードを引数として与えることで、引数として指定したノードを削除します。

    上記の場合、削除するp要素はbodyに属しているので、document.bodyからremoveChildを呼び出すことで、削除しています。

    全てがうまくいっていれば、下記のように動作する削除ボタンができてるはずです!

    # 🚩チェックポイント

    スクリーンショットのように、「X」と書かれたボタンをクリックすると、追記した要素を削除する機能を実装できた。

    # ここまでのまとめ

    • ユーザーなどがDOMの要素に対して何らかの操作を行った際実行するJavaScriptのコードを設定するには、要素のaddEventListenerというメソッドを使う
    • addEventListenerに渡した関数が受け取るEventオブジェクトは、対象のイベントが発生したときの詳細な情報を含む -
      • 例えば、イベントオブジェクトのtargetというキーを通じて、イベントが発生した要素を取得することができる
    • DOMツリーにおける、決まった要素に簡単にアクセスするには、HTMLのid属性やclass属性と、document.getElementByIdメソッドやdocument.getElementsByClassNameメソッドを組み合わせて使う
    • 新しく要素を作ってDOMツリーに追加するには、document.createElementメソッドと、親の要素.appendChildメソッドを使う
    • 新しく作った要素の内容として、テキストノードを作りたい場合はdocument.createTextNodeメソッドを使う
    • 要素を削除するには親の要素.removeChildメソッドを使う

    # 参考文献(本文中で言及しなかったもののみ)

    - +
    • 例えば、イベントオブジェクトのtargetというキーを通じて、イベントが発生した要素を取得することができる
  • DOMツリーにおける、決まった要素に簡単にアクセスするには、HTMLのid属性やclass属性と、document.getElementByIdメソッドやdocument.getElementsByClassNameメソッドを組み合わせて使う
  • 新しく要素を作ってDOMツリーに追加するには、document.createElementメソッドと、親の要素.appendChildメソッドを使う
  • 新しく作った要素の内容として、テキストノードを作りたい場合はdocument.createTextNodeメソッドを使う
  • 要素を削除するには親の要素.removeChildメソッドを使う
  • # 参考文献(本文中で言及しなかったもののみ)

    + diff --git a/frontend/jquery/index.html b/frontend/jquery/index.html index f035fa02..6fbd0875 100644 --- a/frontend/jquery/index.html +++ b/frontend/jquery/index.html @@ -8,7 +8,7 @@ - + @@ -295,7 +295,7 @@ console.log($p.get(0)); // p == $p.get(0) であり、 $p == $(p) == $($p.get(0)) です -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    一般的にはあまり推奨されませんが、jQuery を使った JavaScript コードでは(システム)ハンガリアン記法がしばしば採用されます。jQuery オブジェクトを保持する変数は $p のように変数名を $ から始めるというものです。これは好みの問題なので変数名はどのように書いても構いません。

    # おわりに

    今回は DOM 操作以外のテーマについてあまり触れられませんでした。

    • DOM 操作
    • スクロール制御(ページ内リンクのスムーススクロール)
    • jQuery アニメーション(フェード、スライドショー、イージング)
    • Ajax 通信(ajax メソッド、load メソッド)

    jQuery は DOM Manipulator ライブラリであり、DOM 操作を中心に上記のような応用をする際には有用なライブラリです。

    一方で、途中でも述べましたが Angular や React, Vue.js は JavaScript ベースの Web アプリケーションを開発する際に有用なフレームワークです。それらの使い方は jQuery とは目的が異なるため、「Vue.js と jQuery のどちらを使うべきか」という単純な比較はあまり意味がありません。

    また、最近では IE6 〜 IE8 のような古いブラウザを考慮する必要がなくなったため、jQuery を使わずともネイティブの JavaScript メソッドや HTML5, CSS3 の時代に使えるようになった機能を用いることで、ブラウザごとの実装状況や不具合に悩まされずに JavaScript 開発ができるようになってきました。

    ブラウザ戦争の歴史や JavaScript の歴史を知ると、どういう JavaScript フレームワークがなぜ便利で、どのような場面で使うべきなのかというのを判断できるようになるかもしれません。そのような歴史や背景についても興味を持っていただけたら幸いです。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    一般的にはあまり推奨されませんが、jQuery を使った JavaScript コードでは(システム)ハンガリアン記法がしばしば採用されます。jQuery オブジェクトを保持する変数は $p のように変数名を $ から始めるというものです。これは好みの問題なので変数名はどのように書いても構いません。

    # おわりに

    今回は DOM 操作以外のテーマについてあまり触れられませんでした。

    • DOM 操作
    • スクロール制御(ページ内リンクのスムーススクロール)
    • jQuery アニメーション(フェード、スライドショー、イージング)
    • Ajax 通信(ajax メソッド、load メソッド)

    jQuery は DOM Manipulator ライブラリであり、DOM 操作を中心に上記のような応用をする際には有用なライブラリです。

    一方で、途中でも述べましたが Angular や React, Vue.js は JavaScript ベースの Web アプリケーションを開発する際に有用なフレームワークです。それらの使い方は jQuery とは目的が異なるため、「Vue.js と jQuery のどちらを使うべきか」という単純な比較はあまり意味がありません。

    また、最近では IE6 〜 IE8 のような古いブラウザを考慮する必要がなくなったため、jQuery を使わずともネイティブの JavaScript メソッドや HTML5, CSS3 の時代に使えるようになった機能を用いることで、ブラウザごとの実装状況や不具合に悩まされずに JavaScript 開発ができるようになってきました。

    ブラウザ戦争の歴史や JavaScript の歴史を知ると、どういう JavaScript フレームワークがなぜ便利で、どのような場面で使うべきなのかというのを判断できるようになるかもしれません。そのような歴史や背景についても興味を持っていただけたら幸いです。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/frontend/overview/index.html b/frontend/overview/index.html index 9d645c6c..073f2ba8 100644 --- a/frontend/overview/index.html +++ b/frontend/overview/index.html @@ -8,7 +8,7 @@ - + @@ -31,7 +31,7 @@ VueやReactなどにはSSR用のライブラリが存在しており、SSRでの生成と表示後の動的な書き換えそれぞれをスムーズに実装できるようになっています。

    SSRはSEO対策だけでなく、初期読み込み時の何も表示されない時間を減らすなど、ユーザ体験向上にも有効です。 一方でSEO対策がほぼ不要で初期読み込みよりも利用中の挙動を優先するWebサービスの管理画面では、これまで通りSPAが有効です。

    # wasm

    最近ではC言語やRust, Goなどで書かれたプログラムをバイナリファイルにコンパイルし、それをブラウザ上で動作させるwasmという仕組みが存在します。 JavaScriptのコードはどうしてもパフォーマンスに限界がありますが、wasmの場合はほぼネイティブコンパイルされたプログラムと同じようなパフォーマンスで動作させることができます。 -問題になったBitcoinのマイニングプログラムや機械学習系など、CPUパワーが必要な場合に利用されます。

    # JavaScript体験

    今回はjQueryを利用し、DOMを書き換える体験をしてみましょう。

    DOMとjQuery


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +問題になったBitcoinのマイニングプログラムや機械学習系など、CPUパワーが必要な場合に利用されます。

    # JavaScript体験

    今回はjQueryを利用し、DOMを書き換える体験をしてみましょう。

    DOMとjQuery


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/frontend/react/index.html b/frontend/react/index.html index 3fbc9311..76255cda 100644 --- a/frontend/react/index.html +++ b/frontend/react/index.html @@ -8,7 +8,7 @@ - + @@ -1030,7 +1030,7 @@ # コンテナを動かし9000番ポートでウェブアプリを公開する。 docker run --rm -p 9000:80 todo-web -
    1
    2
    3
    4
    5
    6
    7
    8

    この状態でhttp://localhost:9000 (opens new window)にアクセスして、今までと同様のウェブアプリが表示されれば成功です😉

    アプリをコンテナイメージとしてまとめることで、利用者がアプリを動かすためのセットアップが簡単にできたり、Kubernetes環境でアプリを動作させることができます。

    えっ、ここまでできたんですか?

    ちょっと、できる人すぎですよ😉

    # 参考になるサイト

    # React

    # JavaScript

    # TypeScript

    以上で講義は終わりです。よいReactライフを😉


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    - +
    1
    2
    3
    4
    5
    6
    7
    8

    この状態でhttp://localhost:9000 (opens new window)にアクセスして、今までと同様のウェブアプリが表示されれば成功です😉

    アプリをコンテナイメージとしてまとめることで、利用者がアプリを動かすためのセットアップが簡単にできたり、Kubernetes環境でアプリを動作させることができます。

    えっ、ここまでできたんですか?

    ちょっと、できる人すぎですよ😉

    # 参考になるサイト

    # React

    # JavaScript

    # TypeScript

    以上で講義は終わりです。よいReactライフを😉


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    + diff --git a/frontend/svelte/index.html b/frontend/svelte/index.html index 29ba44b6..ff4bddb4 100644 --- a/frontend/svelte/index.html +++ b/frontend/svelte/index.html @@ -8,7 +8,7 @@ - + @@ -194,7 +194,7 @@ </label> <br /> <NabeatsuButton onClick={incrementCount} count={count} divisor={divisor}/> -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    <input>要素にはbind:value={divisor}と書くだけでよくなり、updateDivisorのような<input>要素の値をdivisorにセットするだけの関数もなくなりました!bind:value={divisor}は、対象の<input>要素のvalue属性と変数divisorを双方向に紐付ける(バインドする)ための設定です。紐付けた変数divisorの値をvalue属性で表示して、ユーザーの入力によってvalue属性が更新されたらdivisorの値も更新し、また新たな値をvalue属性の値として表示する...という処理をこれ一つで賄ってくれます。

    補足

    更にSvelteは気を遣ってくれまして、type="number"<input>要素については、入力された値を自動で数値に変換した上で紐付けた変数に代入してくれます。

    動作例:

    ※ステップ10と同じなので省略

    # 🚩ここまでにできたこと

    • 「双方向バインディング」を使うことで、紐付けた変数と<input>要素のvalue属性の値を簡単に同期させることができた

    # おわりに

    Svelteの機能はまだまだたくさんあります。気になる方は、とてもうまく翻訳された日本語のチュートリアル (opens new window)を軽く通してみるといいでしょう。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    <input>要素にはbind:value={divisor}と書くだけでよくなり、updateDivisorのような<input>要素の値をdivisorにセットするだけの関数もなくなりました!bind:value={divisor}は、対象の<input>要素のvalue属性と変数divisorを双方向に紐付ける(バインドする)ための設定です。紐付けた変数divisorの値をvalue属性で表示して、ユーザーの入力によってvalue属性が更新されたらdivisorの値も更新し、また新たな値をvalue属性の値として表示する...という処理をこれ一つで賄ってくれます。

    補足

    更にSvelteは気を遣ってくれまして、type="number"<input>要素については、入力された値を自動で数値に変換した上で紐付けた変数に代入してくれます。

    動作例:

    ※ステップ10と同じなので省略

    # 🚩ここまでにできたこと

    • 「双方向バインディング」を使うことで、紐付けた変数と<input>要素のvalue属性の値を簡単に同期させることができた

    # おわりに

    Svelteの機能はまだまだたくさんあります。気になる方は、とてもうまく翻訳された日本語のチュートリアル (opens new window)を軽く通してみるといいでしょう。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/frontend/vue/index.html b/frontend/vue/index.html index 03935463..a9045544 100644 --- a/frontend/vue/index.html +++ b/frontend/vue/index.html @@ -8,7 +8,7 @@ - + @@ -310,7 +310,7 @@ } }) </script> -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

    ここでは1個目のscriptタグでpeople-tableというコンポーネントを定義しています。コンポーネントはtemplateというプロパティがあるのが特徴で、ここにコンポーネント内に表示したいHTMLを記述します。

    さらにpropsでこのコンポーネントのインプットを定義しています。今回のpeople-tableは、peoplesという変数で配列を受け取り、それをテーブルとして表示するコンポーネントです。

    このようにアプリケーションをコンポーネントと呼ばれる小さな単位に分割していくとで、1つ1つのコンポーネントをシンプルに保ちつつ、アプリケーション全体を構築していくことが可能です。


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    - +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

    ここでは1個目のscriptタグでpeople-tableというコンポーネントを定義しています。コンポーネントはtemplateというプロパティがあるのが特徴で、ここにコンポーネント内に表示したいHTMLを記述します。

    さらにpropsでこのコンポーネントのインプットを定義しています。今回のpeople-tableは、peoplesという変数で配列を受け取り、それをテーブルとして表示するコンポーネントです。

    このようにアプリケーションをコンポーネントと呼ばれる小さな単位に分割していくとで、1つ1つのコンポーネントをシンプルに保ちつつ、アプリケーション全体を構築していくことが可能です。


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    + diff --git a/index.html b/index.html index 0efa1648..8a3c8277 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - + @@ -25,6 +25,6 @@

    # 2023年度カリキュラム一覧

    カテゴリ 1枠目 2枠目 3枠目 4枠目 5枠目 6枠目
    イントロ イントロダクション
    (資料非公開)
    事前準備
    (資料非公開)
    事前準備
    (資料非公開)
    開発 Gitの使い方+GitHubを使った開発手法
    (最新版は非公開)
    開発環境をDockerで構築+docker-compose Jenkinsを触ってみる
    CI/CD + 構成管理 GitHub Actions でCIテスト・デプロイを回す ansibleによるIT自動化 Kubernetes でアプリケーション開発
    監視・モニタリング 監視Overview Prometheusでアプリケーションを監視してみよう
    (監視Overviewと資料共通)
    Splunkハンズオン
    (資料非公開)
    データベース データベース界隈Overview リレーショナルDBを触ってみる MongoDBを触ってみる redisを触ってみる
    Webサーバー HTTP Overview
    (資料非公開)
    Apache + Nginx を触ってみよう
    バックエンドアプリ サーバアプリケーション界隈Overview FastAPIでWebアプリを作る Java: Spring でWebアプリを作る GoでWebアプリを作る 並行処理ハンズオン テストプログラミングハンズオン
    フロントエンド 素のJavaScript + DOM入門 Svelteを触ってみよう Reactを触ってみよう Angularを触ってみよう
    (過去講座)
    Vueを触ってみよう
    (過去講座)
    セキュリティ WebアプリとセキュリティOverview Webアプリの脆弱性を解消してみよう
    (資料未公開)
    アプリに認証を導入するハンズオン
    (資料未公開)
    - + diff --git a/init/hello-bootcamp/index.html b/init/hello-bootcamp/index.html index 31096d84..514a97af 100644 --- a/init/hello-bootcamp/index.html +++ b/init/hello-bootcamp/index.html @@ -8,7 +8,7 @@ - + @@ -86,7 +86,7 @@
    1
    2
    3

    エラーなく起動できたら 起動したコンテナのID が表示されます。ここは実行ごとに変わります。

    ブラウザを開き、localhost:8080 (opens new window)localhostまたは ssh 先の IP アドレス)にアクセスしてみましょう。「Hello Bootcamp!」が表示されていれば成功です。

    なお、-d オプションを付けているので、 バックグラウンドで起動し続けています。 再度起動し直してみたい場合は 一度 docker stop する必要があります。お掃除の手順を参照してください。

    # HTML ファイルを書き換える

    先ほど作成したindex.htmlを編集してみましょう。

    Hello Bootcamp!!!!!!
     
    1

    保存してブラウザをリロードしてみてください。ページが更新されていれば成功です。

    # Docker コンテナのお掃除

    最後に Docker コンテナを止めて掃除しておきます。

    $ docker stop test-nginx
     $ docker rm test-nginx
    -
    1
    2

    以上で事前準備は完了です。お疲れ様でした。

    - +
    1
    2

    以上で事前準備は完了です。お疲れ様でした。

    + diff --git a/security/overview/index.html b/security/overview/index.html index 27806399..2f9c6b0f 100644 --- a/security/overview/index.html +++ b/security/overview/index.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ GitHub (opens new window)

    # Webアプリとセキュリティ Overview

    資料はこちら


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    - + (opens new window)

    # Webアプリとセキュリティ Overview

    資料はこちら


    CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
    + diff --git a/server-app/concurrent/index.html b/server-app/concurrent/index.html index 82bd17fd..fc5c3eb3 100644 --- a/server-app/concurrent/index.html +++ b/server-app/concurrent/index.html @@ -8,7 +8,7 @@ - + @@ -327,7 +327,7 @@
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

    # 最後に

    この資料で伝えたいのは以下のことです。

    • ライブラリやフレームワークを利用する際はその仕組みをきちんと理解しましょう
      • webプログラミングにおいては特に並行・並列処理をどのように実装しているのか確認しましょう
    • スレッドプログラミングを書いているという認識を持ち、書こうとしている処理がスレッドセーフなのか常に確認しましょう
      • Webフレームワークが推奨する書き方に習うことで、トラブルを防ぐことができます
    • ロックなど排他制御の実装を行う際は自前で実装せず、必ずライブラリを利用しましょう

    並行・並列処理、スレッドプログラミングは非常に複雑になります(これは実行するまで処理がどのような順番で実行されるか分からない非決定性から来ています)。

    Webフレームワークはこの並行・並列処理の複雑性を隠蔽するために発達しているという側面があり、推奨される書き方をすることで複雑性を意識しなくても実装が可能なようになっています。 -しかし自分が今どのような仕組みの上で実装しているのかを意識していないと、思わぬ事故を起こす可能性があります。

    フレームワークが提供するレールに乗りながらも、自分がスレッドプログラミングをしていることを常に意識することが大切です。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    - +しかし自分が今どのような仕組みの上で実装しているのかを意識していないと、思わぬ事故を起こす可能性があります。

    フレームワークが提供するレールに乗りながらも、自分がスレッドプログラミングをしていることを常に意識することが大切です。


    CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
    + diff --git a/server-app/fastapi/index.html b/server-app/fastapi/index.html index 2c1c605d..18d429dd 100644 --- a/server-app/fastapi/index.html +++ b/server-app/fastapi/index.html @@ -8,7 +8,7 @@ - + @@ -265,7 +265,7 @@ background_tasks.add_task(print_wait_time, count) return {"status": "See the console log for wait time."}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    参考: https://fastapi.tiangolo.com/ja/tutorial/background-tasks/ (opens new window)

    *)ASGIの公式ドキュメントにもspiritualと書いてあり、「精神的な」後継である事が明記されている。

    # まとめ

    今回は、FastAPIを使ってWebアプリケーションを作ってみました。 -FastAPIは決して大規模なWebアプリケーションを作るために作られた物ではありませんが、作り方次第で多くの処理を任せることができます。

    BootCampではあくまでチュートリアルのような形で作成したため、全てを一つのmain.pyに記載しましたが開発をスムーズに行う上ではpydanticで定義するモデルファイルは分ける。エンドポイントも階層化に合わせてファイルを分ける、といったテクニックが必要になってきますのでそういった所を今後は学んでいって頂ければと思います。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    - +FastAPIは決して大規模なWebアプリケーションを作るために作られた物ではありませんが、作り方次第で多くの処理を任せることができます。

    BootCampではあくまでチュートリアルのような形で作成したため、全てを一つのmain.pyに記載しましたが開発をスムーズに行う上ではpydanticで定義するモデルファイルは分ける。エンドポイントも階層化に合わせてファイルを分ける、といったテクニックが必要になってきますのでそういった所を今後は学んでいって頂ければと思います。


    CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
    + diff --git a/server-app/go/index.html b/server-app/go/index.html index c4e75d3c..ec1c7c12 100644 --- a/server-app/go/index.html +++ b/server-app/go/index.html @@ -8,7 +8,7 @@ - + @@ -49,24 +49,24 @@ もしコンテナを実行していないようであれあば、以下のコマンドを実行してください。

    :# TERMINAL 0
     
     :# vim,emacs,nano派の人はこちら
    -$ docker run --name go-tutor -it --rm jo7oem/go-tutor:v2022 /bin/bash
    +$ docker run --name go-tutor-vscode -p 5009:8888 -d --rm jo7oem/go-tutor-vscode:2024 /bin/bash
     
     :# VSCode派の人はこちら
    -$ docker run --name go-tutor-vscode -p 8888:8888 -d --rm jo7oem/go-tutor-vscode:v2022
    +$ docker run --name go-tutor-vscode -p 5009:8888 -d --rm jo7oem/go-tutor-vscode:2024
     
    1
    2
    3
    4
    5
    6
    7

    ハンズオンでは、こちらから指示したpathに、ディレクトリやファイルを作成してもらい、Go言語に触れてもらいます。
    -基本的なフォルダ構成は、 /go/src/go_tutorial/<セクション名>/<プログラム名>/***.go のようになります。

    講師側が説明で用いるソースコード(答え)は、/go/src/samples/<セクション名>/<プログラム名>/***.go の形で格納してあります。
    -講師側が想定している出力結果を確認したい際は、/go/src/samples/配下を実行することで、容易に確認できます。
    +基本的なフォルダ構成は、 /root/go-tutor/go_tutorial/<セクション名>/<プログラム名>/***.go のようになります。

    講師側が説明で用いるソースコード(答え)は、/root/go-tutor/samples/<セクション名>/<プログラム名>/***.go の形で格納してあります。
    +講師側が想定している出力結果を確認したい際は、/root/go-tutor/samples/配下を実行することで、容易に確認できます。
    また、ハンズオンがうまく行かない際には、以下のように差分を確認することで、課題解決を助ける可能性があります。

    :# TERMINAL 0
    -$ diff /go/src/go_tutorial/<セクション名>/<プログラム名>/***.go /go/src/samples/<セクション名>/<プログラム名>/***.go
    +$ diff /root/go-tutor/go_tutorial/<セクション名>/<プログラム名>/***.go /root/go-tutor/samples/<セクション名>/<プログラム名>/***.go
     
    1
    2

    # 2. Hello, World ( 10 min )

    本章では、Go言語の実行方法とコンパイル方法を確認します。

    # 2.1. Goの実行

    # 2.1.1. Goを動かす

    Go言語で作成されたソースコードの実行方法は2つあります。
    ソースコードをコンパイル(go build)し、実行形式ファイル(.exe等)を実行する方法と、
    ソースコードをスクリプト言語のように実行するgo runコマンドを用いる方法です。

    # 💻 2.1.1.1. 以下のコマンドを実行して、Goを動かしてみよう

    :# TERMINAL 0
    -:# WORKPATH /go/src/go_tutorial/2_helloworld/hello/
    +:# WORKPATH /root/go-tutor/go_tutorial/2_helloworld/hello/
     
    -$ cd /go/src/go_tutorial/2_helloworld/hello/ 
    +$ cd /root/go-tutor/go_tutorial/2_helloworld/hello/ 
     $ <お好きなエディタ> main.go
     $ go run main.go
    -
    1
    2
    3
    4
    5
    6
    • /go/src/go_tutorial/2_helloworld/hello/main.go
      package main
      +
      1
      2
      3
      4
      5
      6
      • /root/go-tutor/go_tutorial/2_helloworld/hello/main.go
        package main
         
         import "fmt"
         
        @@ -78,14 +78,14 @@
         $ go run main.go
         Hello, W0rld!!
         
        1
        2
        3
        4

        # 💻 2.1.1.2. 以下のコマンドを実行して、Goをコンパイルしてみよう。

        :# TERMINAL 0
        -:# WORKPATH /go/src/go_tutorial/2_helloworld/hello/
        +:# WORKPATH /root/go-tutor/go_tutorial/2_helloworld/hello/
         
         $ go build main.go
         $ ls
         $ file ./main
         $ ./main
         
        1
        2
        3
        4
        5
        6
        7

        ♻️ 2.1.1.2. 結果

        :# TERMINAL 0
        -:# WORKPATH /go/src/go_tutorial/2_helloworld/hello/
        +:# WORKPATH /root/go-tutor/go_tutorial/2_helloworld/hello/
         
         $ go build main.go
         $ ls
        @@ -96,13 +96,13 @@
         Hello, W0rld!!
         
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10

        おめでとうございます。これで、あなたもGopherの友達です。
        記念にぬいぐるみのGopherを机に飾っても良いでしょう。

        # 2.2. クロスコンパイルの体験

        きっと便利ツールを作ってお手元のWindowsで動かしたくなることもあるでしょう。
        1.1.3. クロスコンパイルが容易 でも触れた通り、簡単に作成できることを確認してもらいます。

        # 💻 2.2.1. 以下のコマンドを実行して、Goをコンパイルしてみましょう。

        :# TERMINAL 0
        -:# WORKPATH /go/src/go_tutorial/2_helloworld/hello/
        +:# WORKPATH /root/go-tutor/go_tutorial/2_helloworld/hello/
         
         $ GOOS=windows GOARCH=amd64 go build main.go
         $ ls
         $ file ./main.exe
         
        1
        2
        3
        4
        5
        6

        ♻️ 2.2.1. 結果

        :# TERMINAL 0
        -:# WORKPATH /go/src/go_tutorial/2_helloworld/hello/
        +:# WORKPATH /root/go-tutor/go_tutorial/2_helloworld/hello/
         
         $ GOOS=windows GOARCH=amd64 go build main.go
         $ ls
        @@ -127,32 +127,30 @@
         
        • error
    • コンポジット型

      0個以上の変数をひとまとまりの集合として表した型です

      • 構造体 (struct)
      • 配列 (array)
      • スライス (slice)
      • マップ (map)
      • チャンネル (channel)
    • ユーザー定義型

      組み込み型やコンポジット型を元にユーザーが定義した型です

    • Interface型

      🚀 これまで説明した型はデータがメモリ上にどのように表現されているかという観点から区別されていました。 このinterface型は方がどう振る舞うか(型にどんなメソッドが実装されているか)という観点で区別され、0個以上のメソッドから構成されます。 また、Go1.18からGenericsが追加 (opens new window)されました。これにより、interfaceに型の情報を組み込むことができるようになりました。

    # 3.2. 変数定義方法

    Go言語では、変数の定義方法が3つあります。

    1. var <変数名> <型>
      • 例: var Hensu string
      • 型指定有り。変数初期値の指定無し(stringなので、""になります。)
    2. <変数名> := <初期値>
      • 例: Hensu := "myValue"
      • 型指定無し。代入元の型を引き継ぐ
    3. var <変数名> <型> = <初期値>
      • 例: var Hensu string = "myValue"
      • 型指定有り。変数初期値の指定有り

    予期せぬ型が変数に定義されないよう、最初のうち(書いている型をイメージできるまで)は、1か3の書き方をお勧めします。
    -予期せぬ型が変数に定義されうる例として、interface型 があります。
    -本講義では、interface型 は扱わない為説明は割愛しますが、なんでも型(any) のような使い方ができます。
    -変数なんでも型が示しているのは、int型だと思っていたらstring型だった。というようなケースもおきえるので、注意が必要です。

    # 🚀 Tips: privateとPublicの指定方法

    Go言語の名前空間は、privateは先頭小文字。Publicが先頭大文字と決まっています。
    +予期せぬ型が変数に定義されうる例として、interface型 があります。

    # 🚀 Tips: privateとPublicの指定方法

    Go言語の名前空間は、privateは先頭小文字。Publicが先頭大文字と決まっています。
    packageに含める要素(変数や関数、構造体や構造体の要素など)をpackageの外部から参照させたい場合、先頭大文字の変数名とするようご注意ください。

    # 3.3. 不具合箇所は、最高の講師に教えてもらおう

    Go言語では、書き方を間違えているととても丁寧に教えてくれる強い味方がいます。
    それは、コンパイラ(go build)です。
    「うーん、あ、この辺のソースみた?」とだけ返してくる先輩に比べ、「3行目、6文字目。変数定義されていないよ!?」と場所まで指定して教えてくれます。
    変数定義方法で、1や3を勧めた理由の1つとして、型が異なる場合にコンパイラに教えてもらえる可能性が高まるためというのもあります。

    ちょっと厳しい点があるとすれば、指摘が英語な点でしょうか。
    中学生レベルの英語と、単語を調べる力があれば解決できる文章しか出てこないので、ぜひgo build大先生に弟子入りしてみてください。

    # Tips: 一度に教えてくれる量は限りがある

    エラーが多いと、数個のエラーの後にtoo many error....と続き、全てのエラーを教えてくれないことがあります。
    しょうがないので、教えてもらっているエラーから対処していきましょう。

    # 💻 3.3.1. 以下のコマンドを実行して、修正箇所を認識てみよう。

    :# TERMINAL 0
    -:# WORKPATH /go/src/go_tutorial/3_var/plzfixme/
    +:# WORKPATH /root/go-tutor/go_tutorial/3_var/plzfixme/
     
    -$ cd /go/src/go_tutorial/3_var/plzfixme/
    +$ cd /root/go-tutor/go_tutorial/3_var/plzfixme/
     $ go run main.go
     
    1
    2
    3
    4
    5

    ♻️ 3.3.1. 結果

    :# TERMINAL 0
    -:# WORKPATH /go/src/go_tutorial/3_var/plzfixme/
    +:# WORKPATH /root/go-tutor/go_tutorial/3_var/plzfixme/
     
     $ go run main.go
     # command-line-arguments
     ./main.go:6:2: undefined: WatashiNoHensu
     ./main.go:7:14: undefined: WatashiNoHensu
     
    1
    2
    3
    4
    5
    6
    7

    # 3.4. 不具合の修正

    # 💻 3.3.1. ソースコードを修正し、エラーを無くしてみよう。

    :# TERMINAL 0
    -:# WORKPATH /go/src/go_tutorial/3_var/plzfixme/
    +:# WORKPATH /root/go-tutor/go_tutorial/3_var/plzfixme/
     
     $ <お好きなエディタ> main.go
     $ go run main.go
    -
    1
    2
    3
    4
    5
    • /go/src/go_tutorial/3_var/plzfixme/main.go
      package main
      +
      1
      2
      3
      4
      5
      • /root/go-tutor/go_tutorial/3_var/plzfixme/main.go
        package main
         
         import "fmt"
         
        @@ -161,7 +159,7 @@
         	fmt.Println(Watashi_no_Hensu)     //./main.go:7:14: undefined: Watashi_no_Hensu
         }
         
        1
        2
        3
        4
        5
        6
        7
        8

      ♻️ 3.4.1. 結果

      :# TERMINAL 0
      -:# WORKPATH /go/src/go_tutorial/3_var/plzfixme/
      +:# WORKPATH /root/go-tutor/go_tutorial/3_var/plzfixme/
       
       $ <お好きなエディタ> main.go
       $ go run main.go
      @@ -225,12 +223,12 @@
       	}
       }
       
      1
      2
      3
      4
      5
      6
      7
      8

      myTest()が、bool値(True or False) を返却し、resへ代入します。そして、;の後に続く条件式(!res)の結果に応じて条件分岐します。

      # 4.2.1. 💻 関数のコーディングを行う

      :# TERMINAL 0
      -:# WORKPATH /go/src/go_tutorial/4_funcy/monkey/
      +:# WORKPATH /root/go-tutor/go_tutorial/4_funcy/monkey/
       
      -$ cd /go/src/go_tutorial/4_funcy/monkey/
      +$ cd /root/go-tutor/go_tutorial/4_funcy/monkey/
       $ <お好きなエディタ> eaters.go
       $ go run eaters.go
      -
      1
      2
      3
      4
      5
      6
      • /go/src/go_tutorial/4_funcy/monkey/eaters.go
        package main
        +
        1
        2
        3
        4
        5
        6
        • /root/go-tutor/go_tutorial/4_funcy/monkey/eaters.go
          package main
           
           import "fmt"
           
          @@ -254,7 +252,7 @@
           	}
           }
           
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23

        ♻️ 4.2.1. 結果

        :# TERMINAL 0
        -:# WORKPATH /go/src/go_tutorial/4_funcy/monkey/
        +:# WORKPATH /root/go-tutor/go_tutorial/4_funcy/monkey/
         
         $ go run eaters.go
         GYUDON
        @@ -277,14 +275,14 @@
         	fmt.Println("cannot write")
         }
         
        1
        2
        3
        4
        5

        # 4.2.2.1. 💻 Goっぽい関数を実行してみる

        :# TERMINAL 0
        -:# COPY /go/src/go_tutorial/4_funcy/monkey/eaters.go /go/src/go_tutorial/4_funcy/likego/eaters.go
        -:# WORKPATH /go/src/go_tutorial/4_funcy/likego/
        +:# COPY /root/go-tutor/go_tutorial/4_funcy/monkey/eaters.go /root/go-tutor/go_tutorial/4_funcy/likego/eaters.go
        +:# WORKPATH /root/go-tutor/go_tutorial/4_funcy/likego/
         
        -$ cp /go/src/go_tutorial/4_funcy/monkey/eaters.go /go/src/go_tutorial/4_funcy/likego/eaters.go
        -$ cd /go/src/go_tutorial/4_funcy/likego/
        +$ cp /root/go-tutor/go_tutorial/4_funcy/monkey/eaters.go /root/go-tutor/go_tutorial/4_funcy/likego/eaters.go
        +$ cd /root/go-tutor/go_tutorial/4_funcy/likego/
         $ <お好きなエディタ> eaters.go
         $ go run eaters.go
        -
        1
        2
        3
        4
        5
        6
        7
        8
        • /go/src/go_tutorial/4_funcy/likego/eaters.go
          package main
          +
          1
          2
          3
          4
          5
          6
          7
          8
          • /root/go-tutor/go_tutorial/4_funcy/likego/eaters.go
            package main
             
             import "fmt"
             
            @@ -308,7 +306,7 @@
             	}
             }
             
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23

          ♻️ 4.2.2.1. 結果

          :# TERMINAL 0
          -:# WORKPATH /go/src/go_tutorial/4_funcy/likego/
          +:# WORKPATH /root/go-tutor/go_tutorial/4_funcy/likego/
           
           $ go run eaters.go
           GYUDON
          @@ -355,13 +353,13 @@
           それは、エラー時の出力先と、正常時の出力先が同じstdoutであることです。

          fmtに含まれる、Print関係の関数には、出力先を指定できるものが存在します。
          出力先を指定できる関数を用い、エラー出力をstderrに出力するよう修正してもらいます。
          実行時は、stdoutを捨てる(> /dev/null)すると、差分がわかることでしょう。

          なお、Go言語のstderr定数は、osパッケージの、os.Stderrとして存在するため、追加で、osパッケージのインポートください。

          :# TERMINAL 0
          -:# WORKPATH /go/src/go_tutorial/5_package/fixFunckyMonkey/
          +:# WORKPATH /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/
           
          -$ cd /go/src/go_tutorial/5_package/fixFunckyMonkey/
          +$ cd /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/
           $ <お好きなエディタ> eaters.go
           $ go run eaters.go
           $ go run eaters.go > /dev/null
          -
          1
          2
          3
          4
          5
          6
          7
          • /go/src/go_tutorial/5_package/fixFunckyMonkey/eaters.go
            package main
            +
            1
            2
            3
            4
            5
            6
            7
            • /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/eaters.go
              package main
               
               import (
               	"fmt"
              @@ -388,7 +386,7 @@
               	}
               }
               
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26

            ♻️ 5.1.1 結果

            :# TERMINAL 0
            -:# WORKPATH /go/src/go_tutorial/5_package/fixFunckyMonkey/
            +:# WORKPATH /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/
             
             $ go run eaters.go
             GYUDON
            @@ -398,15 +396,15 @@
             
            1
            2
            3
            4
            5
            6
            7
            8

            # 5.2. パッケージを作る

            実は既に、mainパッケージを何度も作成しています。
            1行目に書いているpackage main です。
            package <パッケージ名>と、先頭に書くことで、パッケージ名を定義できます。
            package mainは、それを起点として実行できる決められたパッケージ名です。
            今回は、main以外の任意の名前が指定可能な、起点から呼び出されるパッケージを作成してもらいます。

            # 5.2.1. 💻 関数Eatのshopパッケージ化

            :# TERMINAL 0
            -:# COPY /go/src/go_tutorial/5_package/fixFunckyMonkey/eaters.go /go/src/go_tutorial/5_package/notKinkyuJi/eaters.go
            -:# WORKPATH /go/src/go_tutorial/5_package/notKinkyuJi/
            +:# COPY /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/eaters.go /root/go-tutor/go_tutorial/5_package/notKinkyuJi/eaters.go
            +:# WORKPATH /root/go-tutor/go_tutorial/5_package/notKinkyuJi/
             
            -$ cp /go/src/go_tutorial/5_package/fixFunckyMonkey/eaters.go /go/src/go_tutorial/5_package/notKinkyuJi/eaters.go
            -$ cd /go/src/go_tutorial/5_package/notKinkyuJi/ 
            +$ cp /root/go-tutor/go_tutorial/5_package/fixFunckyMonkey/eaters.go /root/go-tutor/go_tutorial/5_package/notKinkyuJi/eaters.go
            +$ cd /root/go-tutor/go_tutorial/5_package/notKinkyuJi/ 
             $ <お好きなエディタ> shop/shop.go
             $ <お好きなエディタ> eaters.go
             $ go run eaters.go
            -
            1
            2
            3
            4
            5
            6
            7
            8
            9
            • /go/src/go_tutorial/5_package/notKinkyuJi/shop/shop.go
              package shop
              +
              1
              2
              3
              4
              5
              6
              7
              8
              9
              • /root/go-tutor/go_tutorial/5_package/notKinkyuJi/shop/shop.go
                package shop
                 
                 import (
                 	"fmt"
                @@ -419,7 +417,7 @@
                 	fmt.Println(name)
                 	return true, nil
                 }
                -
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
              • /go/src/go_tutorial/5_package/notKinkyuJi/eaters.go
                package main
                +
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
              • /root/go-tutor/go_tutorial/5_package/notKinkyuJi/eaters.go
                package main
                 
                 import (
                 	"os"
                @@ -439,7 +437,7 @@
                 	}
                 }
                 
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19

              ♻️ 5.2.1. 結果

              :# TERMINAL 0
              -:# WORKPATH /go/src/go_tutorial/5_package/notKinkyuJi/
              +:# WORKPATH /root/go-tutor/go_tutorial/5_package/notKinkyuJi/
               
               $ <お好きなエディタ> shop/shop.go
               $ <お好きなエディタ> eaters.go
              @@ -552,18 +550,18 @@
               	#...省略
               
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14

              レジや席がいくつか存在し、厨房やメニューがあることでしょう。あとは、所在の郵便番号(ZipCode)。他にも、電話番号や社員の一覧など、構成要素はまだまだありそうです。
              牛丼屋を完璧にシミュレーションするコードを作成したければ、もっと沢山の構成要素を意識する必要がありますが、牛丼屋を考える講義でも無く、執筆者が牛丼屋で働いたこともないので、もう少しシンプルな実習コードとします。

              # 6.3.1. 💻 お店で食べられる牛丼屋型を実行する

              :# TERMINAL 0
              -:# COPY /go/src/go_tutorial/5_package/notKinkyuJi/eaters.go /go/src/go_tutorial/6_struct/weakShop/eaters.go
              -:# COPY /go/src/go_tutorial/5_package/notKinkyuJi/shop/shop.go /go/src/go_tutorial/6_struct/weakShop/shop/shop.go
              -:# WORKPATH /go/src/go_tutorial/6_struct/weakShop/
              +:# COPY /root/go-tutor/go_tutorial/5_package/notKinkyuJi/eaters.go /root/go-tutor/go_tutorial/6_struct/weakShop/eaters.go
              +:# COPY /root/go-tutor/go_tutorial/5_package/notKinkyuJi/shop/shop.go /root/go-tutor/go_tutorial/6_struct/weakShop/shop/shop.go
              +:# WORKPATH /root/go-tutor/go_tutorial/6_struct/weakShop/
               
              -$ cp /go/src/go_tutorial/5_package/notKinkyuJi/eaters.go /go/src/go_tutorial/6_struct/weakShop/eaters.go
              -$ cp /go/src/go_tutorial/5_package/notKinkyuJi/shop/shop.go /go/src/go_tutorial/6_struct/weakShop/shop/shop.go
              -$ cd /go/src/go_tutorial/6_struct/weakShop/
              +$ cp /root/go-tutor/go_tutorial/5_package/notKinkyuJi/eaters.go /root/go-tutor/go_tutorial/6_struct/weakShop/eaters.go
              +$ cp /root/go-tutor/go_tutorial/5_package/notKinkyuJi/shop/shop.go /root/go-tutor/go_tutorial/6_struct/weakShop/shop/shop.go
              +$ cd /root/go-tutor/go_tutorial/6_struct/weakShop/
               $ <お好きなエディタ> shop/shop.go
               $ <お好きなエディタ> eaters.go
               $ go run eaters.go
               :# 10秒程度待機する
              -
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              • /go/src/go_tutorial/6_struct/weakShop/shop/shop.go
                package shop
                +
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                • /root/go-tutor/go_tutorial/6_struct/weakShop/shop/shop.go
                  package shop
                   
                   import (
                   	"fmt"
                  @@ -589,7 +587,7 @@
                   	fmt.Println(self.menu)
                   	return true, nil
                   }
                  -
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  17
                  18
                  19
                  20
                  21
                  22
                  23
                  24
                  25
                  26
                • /go/src/go_tutorial/6_struct/weakShop/eaters.go
                  package main
                  +
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  17
                  18
                  19
                  20
                  21
                  22
                  23
                  24
                  25
                  26
                • /root/go-tutor/go_tutorial/6_struct/weakShop/eaters.go
                  package main
                   
                   import (
                   	"os"
                  @@ -604,7 +602,7 @@
                   	}
                   }
                   
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14

                ♻️ 6.3.1. 結果

                :# TERMINAL 0
                -:# WORKPATH /go/src/go_tutorial/6_struct/weakShop/
                +:# WORKPATH /root/go-tutor/go_tutorial/6_struct/weakShop/
                 
                 $ <お好きなエディタ> shop/shop.go
                 $ <お好きなエディタ> gyudon-httpd.go
                @@ -633,13 +631,13 @@
                 }
                 
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11

                # 7.2. はじめてのGo言語Webアプリケーション起動

                では、Webアプリケーションサーバの書き方も知ってもらったので、実際にコーディングしてもらいましょう。
                理由は後ほど説明しますが、本講義では、net/http (opens new window)パッケージではなく、講義用の下位互換httpパッケージ(zakohttp) を参照してもらいます。

                :# TERMINAL 0
                -:# COPY /go/src/go_tutorial/6_struct/weakShop/shop/shop.go /go/src/go_tutorial/7_webapp/weakShop/shop/shop.go
                -:# COPY /go/src/go_tutorial/6_struct/weakShop/eaters.go /go/src/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                +:# COPY /root/go-tutor/go_tutorial/6_struct/weakShop/shop/shop.go /root/go-tutor/go_tutorial/7_webapp/weakShop/shop/shop.go
                +:# COPY /root/go-tutor/go_tutorial/6_struct/weakShop/eaters.go /root/go-tutor/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                 
                -$ cp /go/src/go_tutorial/6_struct/weakShop/shop/shop.go /go/src/go_tutorial/7_webapp/weakShop/shop/shop.go
                -$ cp /go/src/go_tutorial/6_struct/weakShop/eaters.go /go/src/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                -$ cd /go/src/go_tutorial/7_webapp/weakShop/
                +$ cp /root/go-tutor/go_tutorial/6_struct/weakShop/shop/shop.go /root/go-tutor/go_tutorial/7_webapp/weakShop/shop/shop.go
                +$ cp /root/go-tutor/go_tutorial/6_struct/weakShop/eaters.go /root/go-tutor/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                +$ cd /root/go-tutor/go_tutorial/7_webapp/weakShop/
                 $ <お好きなエディタ> shop/shop.go
                 $ <お好きなエディタ> gyudon-httpd.go
                 $ go run gyudon-httpd.go
                @@ -648,7 +646,7 @@
                 :# TERMINAL 1
                 $ curl http://localhost:8080/
                 :# 10秒程度待機する
                -
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                • /go/src/go_tutorial/7_webapp/weakShop/shop/shop.go
                  package shop
                  +
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  • /root/go-tutor/go_tutorial/7_webapp/weakShop/shop/shop.go
                    package shop
                     
                     import (
                     	"fmt"
                    @@ -675,7 +673,7 @@
                     	fmt.Fprintf(w, "'%s'\n", self.menu) //食べた事を報告
                     	return
                     }
                    -
                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    27
                  • /go/src/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                    package main
                    +
                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    27
                  • /root/go-tutor/go_tutorial/7_webapp/weakShop/gyudon-httpd.go
                    package main
                     
                     import (
                     	"./shop"
                    @@ -691,7 +689,7 @@
                     	}
                     }
                     
                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15

                  ♻️ 7.2. 結果

                  :# TERMINAL 0
                  -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                  +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                   
                   $ <お好きなエディタ> shop/shop.go
                   $ <お好きなエディタ> gyudon-httpd.go
                  @@ -711,7 +709,7 @@
                   そのため、1つの座席しか無い牛丼屋と同じ状況が起きます。
                  何も考えずに、HTTPサーバを開発すると、Aさんの画面が表示されるまで、Bさんの画面はずっと読み込み中でくるくる(待ち)になってしまいます。

                  めちゃくちゃ美味しい隠れた名店で、1席しか無いような状況であれば、執筆者は我慢できますが、
                  HTTPサーバで、他者の処理が終わらないと利用できないなんて、使えたものではありません。

                  # 7.3.1. 💻 1座席なWebアプリケーションサーバ体験

                  試しに、2つのリクエストを送ると、1つ目のリクエストが10秒、2つ目のリクエストが約20秒かかることがわかると思います。

                  :# TERMINAL 0
                  -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                  +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                   
                   $ go run gyudon-httpd.go
                   
                  @@ -725,7 +723,7 @@
                   $ time curl http://localhost:8080/
                   :# 10秒程度待機する
                   
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14

                  ♻️ 7.3.1. 結果

                  :# TERMINAL 0
                  -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                  +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                   
                   $ go run gyudon-httpd.go
                   :# 何も出力されない場合、実行中です。続くハンズオンを実施ください
                  @@ -757,9 +755,9 @@
                   (現実では難しいでしょうが、不思議なポッケで叶えてもらったと思ってください。)

                  プログラムでも、複数の処理をおおむね同時に実行する(厳密には、したように見せる。が正しい) 並行プログラミングがあります。
                  並行プログラミングなコードを自身で作成するためには、領域の管理を考えた数十行のコードを作成する必要があります。
                  牛丼屋の例で考えると、座席をどう確保すべきか、どういった要素が必要か。客が離れたら座席をどのように撤去するべきか。1つしかない厨房から牛丼をどのような形で提供するか。辺りです。

                  既に特徴で紹介していますが、Go言語では、Goroutine を用いることで、とても低コストに並行プログラミングを行えます。
                  -🚀 Goroutineだけではなく、本講義では触れないChannelや、GCによって、低コストに並行プログラミングができます。

                  実際に、食べる処理(Eat関数) を、並行プログラミングにして、2つ目のリクエストが、1つ目のリクエストを待たなくても良いように、アップグレードしましょう

                  # 7.3.3. 💻 並行動作するWebアプリケーションサーバ体験

                  /go/src/go_tutorial/7_webapp/weakShop/http/zakohttp.go の、c.serve(self.ctx) が、Eat関数を呼び出しています。
                  +🚀 Goroutineだけではなく、本講義では触れないChannelや、GCによって、低コストに並行プログラミングができます。

                  実際に、食べる処理(Eat関数) を、並行プログラミングにして、2つ目のリクエストが、1つ目のリクエストを待たなくても良いように、アップグレードしましょう

                  # 7.3.3. 💻 並行動作するWebアプリケーションサーバ体験

                  /root/go-tutor/go_tutorial/7_webapp/weakShop/http/zakohttp.go の、c.serve(self.ctx) が、Eat関数を呼び出しています。
                  ここをGoroutine化し、その先にあるEat関数を新しく誕生させた座席(Goroutine)で動作させるようにしましょう。

                  :# TERMINAL 0
                  -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                  +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                   
                   $ <お好きなエディタ> http/zakohttp.go
                   $ go run gyudon-httpd.go
                  @@ -773,10 +771,10 @@
                   :# TERMINAL 2
                   $ time curl http://localhost:8080/
                   :# 10秒程度待機する
                  -
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  • /go/src/go_tutorial/7_webapp/weakShop/http/zakohttp.go
                    c.serve(self.ctx)    //Line56 もともとの書かれ方
                    +
                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    • /root/go-tutor/go_tutorial/7_webapp/weakShop/http/zakohttp.go
                      c.serve(self.ctx)    //Line56 もともとの書かれ方
                       go c.serve(self.ctx) //Line56 変更後。go と、加筆する
                       
                      1
                      2

                    ♻️ 7.3.3. 結果

                    :# TERMINAL 0
                    -:# WORKPATH /go/src/go_tutorial/7_webapp/weakShop/
                    +:# WORKPATH /root/go-tutor/go_tutorial/7_webapp/weakShop/
                     
                     $ <お好きなエディタ> http/zakohttp.go
                     $ go run gyudon-httpd.go
                    @@ -835,12 +833,12 @@
                     }
                     
                    1
                    2
                    3
                    4
                    5
                    6
                    7

                    # 8.2. 💻 テストの実行と修正

                    では、基本的なテストのやり方を知ってもらったので、実際に試してみましょう。
                    本講義では5章で作った関数Eatにテストを追加する形で進めます。

                    手始めに関数TestGyudon_EatSimpleをいじってテストの挙動を確認してみましょう。

                    # 試してみてほしいこと

                    1. wantgotを比較して、違っていたらt.Errorf()にメッセージを表示するように修正する
                    2. テストを実行する
                    3. 文字が一致してテストが成功するパターンを試す
                    4. 文字が一致せずテストが失敗するパターンを試す
                    :# TERMINAL 0
                    -:# WORKPATH /go/src/go_tutorial/8_test/test/
                    +:# WORKPATH /root/go-tutor/go_tutorial/8_test/test/
                     
                    -$ cd /go/src/go_tutorial/8_test/test/
                    +$ cd /root/go-tutor/go_tutorial/8_test/test/
                     $ <お好きなエディタ> shop/shop_test.go
                     $ go test ./...
                    -
                    1
                    2
                    3
                    4
                    5
                    6
                    • /go/src/go_tutorial/8_test/test/shop/shop_test.go
                      func TestGyudon_EatSimple(t *testing.T) {
                      +
                      1
                      2
                      3
                      4
                      5
                      6
                      • /root/go-tutor/go_tutorial/8_test/test/shop/shop_test.go
                        func TestGyudon_EatSimple(t *testing.T) {
                         w := bytes.Buffer{}
                         r := http.Request{}
                         
                        @@ -853,12 +851,12 @@
                         /// 判定処理を書く
                         }
                         
                        1
                        2
                        3
                        4
                        5
                        6
                        7
                        8
                        9
                        10
                        11
                        12

                      ♻️ 8.2. 結果

                      :# TERMINAL 0
                      -:# WORKPATH /go/src/go_tutorial/8_test/test/
                      +:# WORKPATH /root/go-tutor/go_tutorial/8_test/test/
                       
                      -$ cd /go/src/go_tutorial/8_test/test/
                      +$ cd /root/go-tutor/go_tutorial/8_test/test/
                       $ <お好きなエディタ> shop/shop_test.go
                       $ go test ./...
                      -
                      1
                      2
                      3
                      4
                      5
                      6
                      • /go/src/go_tutorial/8_test/test/shop/shop_test.go
                        if want != got {
                        +
                        1
                        2
                        3
                        4
                        5
                        6
                        • /root/go-tutor/go_tutorial/8_test/test/shop/shop_test.go
                          if want != got {
                               t.Errorf("want = %s, got = %s\n", want, got)
                           }   
                           
                          1
                          2
                          3

                        # 8.3. 🚀 その他のテストの書き方

                        8.2.で試したテスト関数 TestGyudon_EatSimple のやり方の場合、引きすうごとにテストの関数を追加せねばならず不便です。 @@ -871,7 +869,7 @@ より素晴らしい例を見つけたら、本リポジトリにPRしていただいても大丈夫です。

                        また、Go言語の構造から迫るアプローチ以外として、その他外部のコミュニティから情報を得るのも良いでしょう。

                        もちろん、本講義開催の講師陣に質問くださっても問題ありません。
                        情報源はたくさんあるので、貪欲にGoを知ってみてください。
                        -ではみなさん、Let's Go!!


                        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
          - +ではみなさん、Let's Go!!


          CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
          + diff --git a/server-app/go/src/go-tutor/index.html b/server-app/go/src/go-tutor/index.html index 4765a23b..a9a53909 100644 --- a/server-app/go/src/go-tutor/index.html +++ b/server-app/go/src/go-tutor/index.html @@ -8,7 +8,7 @@ - + @@ -36,7 +36,7 @@ docker buildx stop <buildername> docker buildx rm <buildername>
          1
          2
          3
          4
        • # 6. リンクの更新

          - +
        + diff --git a/server-app/go/var/md/init.html b/server-app/go/var/md/init.html index 78c529e7..a00824d6 100644 --- a/server-app/go/var/md/init.html +++ b/server-app/go/var/md/init.html @@ -8,7 +8,7 @@ - + @@ -24,7 +24,7 @@ root@<container id>:/go/src/# emacs
        1
        2
        3

        # 4.2. VSCodeで作業する場合

        # 4.2.1. VSCode Serverを起動します
        :# VSCode Server の起動
         $  docker compose up -d 
        -
        1
        2
        # 4.2.2. ブラウザでhttp://localhost:80にアクセス

        パスワードを聞かれた場合はiij-bootcampと入力

        VScodeの画面が表示されたら /go/src/go_tutorial/ ディレクトリを開いてください。

        - +
        1
        2
        # 4.2.2. ブラウザでhttp://localhost:80にアクセス

        パスワードを聞かれた場合はiij-bootcampと入力

        VScodeの画面が表示されたら /go/src/go_tutorial/ ディレクトリを開いてください。

        + diff --git a/server-app/java/DI.html b/server-app/java/DI.html index a51ddfb9..55c0ca6d 100644 --- a/server-app/java/DI.html +++ b/server-app/java/DI.html @@ -8,7 +8,7 @@ - + @@ -173,7 +173,7 @@ this.slackClient.execute(...); } } -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        - +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        + diff --git a/server-app/java/index.html b/server-app/java/index.html index de209603..4f52a340 100644 --- a/server-app/java/index.html +++ b/server-app/java/index.html @@ -8,7 +8,7 @@ - + @@ -381,7 +381,7 @@ $ curl 'localhost:8080/user?id=alice' {"name": "アリス", "id": "alice"} -
        1
        2
        3
        4
        5
        6

        # チェックポイント

        • @PostMappingアノテーションをメソッドに付与し、POSTリクエストのHTTPハンドラとして登録した
        • JavaのPOJOを用いてPOSTリクエストのリクエストボディのスキーマを表現した

        # まとめ

        以上でSpring Bootのハンズオンは終了です。

        本講義ではJavaの基本的な知識や書き方、Spring Bootの使い方など、基本的な機能や文法に触れてもらいました。しかしSpring Bootには多様な機能がまだまだ存在しており、データベースとの接続や非同期処理などさまざまなプロダクション環境で活躍できるポテンシャルを持っているフレームワークです。

        本講義が、受講者のみなさまの今後の技術選定の手助けになれれば幸いです。

        # 追加の資料

        • Spring Bootリファレンスドキュメント (opens new window)
          • 多くのSpring Boot開発者がお世話になる公式ドキュメントです。アプリケーションの開発からデプロイ方法まで、幅広く情報が提供されています。
        • Spring Boot Guides (opens new window)
          • Spring Bootの各種機能を試してみるチュートリアルが公開されています。Pub/SubやMongoDB、Dockerとの連携などSpring Bootの拡張が多種公開されています。興味のある項目に触ってみてください。

        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        - +
        1
        2
        3
        4
        5
        6

        # チェックポイント

        • @PostMappingアノテーションをメソッドに付与し、POSTリクエストのHTTPハンドラとして登録した
        • JavaのPOJOを用いてPOSTリクエストのリクエストボディのスキーマを表現した

        # まとめ

        以上でSpring Bootのハンズオンは終了です。

        本講義ではJavaの基本的な知識や書き方、Spring Bootの使い方など、基本的な機能や文法に触れてもらいました。しかしSpring Bootには多様な機能がまだまだ存在しており、データベースとの接続や非同期処理などさまざまなプロダクション環境で活躍できるポテンシャルを持っているフレームワークです。

        本講義が、受講者のみなさまの今後の技術選定の手助けになれれば幸いです。

        # 追加の資料

        • Spring Bootリファレンスドキュメント (opens new window)
          • 多くのSpring Boot開発者がお世話になる公式ドキュメントです。アプリケーションの開発からデプロイ方法まで、幅広く情報が提供されています。
        • Spring Boot Guides (opens new window)
          • Spring Bootの各種機能を試してみるチュートリアルが公開されています。Pub/SubやMongoDB、Dockerとの連携などSpring Bootの拡張が多種公開されています。興味のある項目に触ってみてください。

        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        + diff --git a/server-app/node/index.html b/server-app/node/index.html index 605ccf1f..b6185773 100644 --- a/server-app/node/index.html +++ b/server-app/node/index.html @@ -8,7 +8,7 @@ - + @@ -131,7 +131,7 @@
        1
        2
        3

        一覧で取得

        $ curl 127.0.0.1:3000/peoples
         
         [{"_id":"5cf3f435a47f5c0cdb9023a6","name":"Alice","__v":0}]
        -
        1
        2
        3

        # 最後に

        ここまで簡単なNode.jsを使ったAPIサーバーの実装を行ってみました。なんとなくNode.jsのやり方が掴めたでしょうか。

        ただし実際のプロダクトで使用する場合は、TypeScriptを使って開発したりAPIのcallback関数を別ファイルにする、ロギングの仕組みを整えるなど様々なことを考える必要があります。

        今回の内容は本当に基礎的なものでしかないため、もし興味があれば様々なフレームワークや実装例を調べてみてください。


        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        - +
        1
        2
        3

        # 最後に

        ここまで簡単なNode.jsを使ったAPIサーバーの実装を行ってみました。なんとなくNode.jsのやり方が掴めたでしょうか。

        ただし実際のプロダクトで使用する場合は、TypeScriptを使って開発したりAPIのcallback関数を別ファイルにする、ロギングの仕組みを整えるなど様々なことを考える必要があります。

        今回の内容は本当に基礎的なものでしかないため、もし興味があれば様々なフレームワークや実装例を調べてみてください。


        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        + diff --git a/server-app/overview/index.html b/server-app/overview/index.html index 15aa7938..8f3e6260 100644 --- a/server-app/overview/index.html +++ b/server-app/overview/index.html @@ -8,7 +8,7 @@ - + @@ -322,7 +322,7 @@ string profile = 3; } } -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20

        このproto定義から各言語向けに型定義を含めたコードを生成するため、実装が自由なOpenAPIに比べるとより厳密にAPIを実装できます。


        CC BY-SA Licensed | Copyright, Internet Initiative Japan Inc.
        - +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20

        このproto定義から各言語向けに型定義を含めたコードを生成するため、実装が自由なOpenAPIに比べるとより厳密にAPIを実装できます。


        CC BY-SA Licensed | Copyright, Internet Initiative Japan Inc.
        + diff --git a/server-app/rails/index.html b/server-app/rails/index.html index a043175a..63a58db8 100644 --- a/server-app/rails/index.html +++ b/server-app/rails/index.html @@ -8,7 +8,7 @@ - + @@ -228,7 +228,7 @@ めには数百時間の学習と実践が必要になると思われます。

        Railsは、しかし、その後のWebアプリケーション開発のお手本になった部分が 多々あり、Railsを学んでおくと、後発のWebアプリケーション開発フレームワー クの習得が容易になるという側面もあります。

        機会を見つけて、Railsにチャレンジしてみることは、現在でも意義のあるこ -とだと思います。


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        - +とだと思います。


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        + diff --git a/server-app/test-hands-on/index.html b/server-app/test-hands-on/index.html index ba402c55..68fe425b 100644 --- a/server-app/test-hands-on/index.html +++ b/server-app/test-hands-on/index.html @@ -8,7 +8,7 @@ - + @@ -348,7 +348,7 @@ got_data = get_current_number() # got_data = 579
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10

        dockerコンテナ内の/test-hands-on/exercises/exercise3/test_challenge.pyには、本APIが完成すると通るようになる、テストtest_success()が定義されています。

        上記のテストがOKになるよう、各種APIをTDDを使って作成してみましょう。


        # おわりに

        一般的にソフトウェアテストというと、専門のテスト部隊があって「Excelにスクショをペタペタ貼るだけでしょ?」というようなイメージを持ち、敬遠される方も少なくはないと思います。

        開発者がテストについて知識を持ち、単体テストで可能な限りの不具合をなくしておくと、後の工程で不具合が少なく済ますことができたり、メリットがあります。
        -また、後の工程で発生した不具合の内容を聞いた・見ただけで、どのモジュール同士で問題が起こっているのか目星がつくなど、効率的なトラブルシュートやソフトウェアの理解にも繋がります。

        冒頭でも述べましたが、ソフトウェアにも品質というものがあり、この品質次第で会社の売上に影響が出たり、企業のセキュリティや人命に影響を及ぼしてしまう懸念もあります。

        そのため。開発を行う際には是非テストにも注力をしていただき、ユーザーの満足できるソフトウェアを作れるよう、目指してみてください。

        良いエンジニアライフを!👍


        CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
        - +また、後の工程で発生した不具合の内容を聞いた・見ただけで、どのモジュール同士で問題が起こっているのか目星がつくなど、効率的なトラブルシュートやソフトウェアの理解にも繋がります。

        冒頭でも述べましたが、ソフトウェアにも品質というものがあり、この品質次第で会社の売上に影響が出たり、企業のセキュリティや人命に影響を及ぼしてしまう懸念もあります。

        そのため。開発を行う際には是非テストにも注力をしていただき、ユーザーの満足できるソフトウェアを作れるよう、目指してみてください。

        良いエンジニアライフを!👍


        CC BY-SA Licensed | Copyright (c) 2022, Internet Initiative Japan Inc.
        + diff --git a/web-server/apache/index.html b/web-server/apache/index.html index 26ea1065..d4f2faab 100644 --- a/web-server/apache/index.html +++ b/web-server/apache/index.html @@ -8,7 +8,7 @@ - + @@ -167,7 +167,7 @@ apachectl -V | grep MPM #Server MPM: prefork -
        1
        2
        3
        4
        5
        6

        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        - +
        1
        2
        3
        4
        5
        6

        CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.
        + diff --git a/web-server/apache_nginx/index.html b/web-server/apache_nginx/index.html index 13e9a438..33d0e743 100644 --- a/web-server/apache_nginx/index.html +++ b/web-server/apache_nginx/index.html @@ -8,7 +8,7 @@ - + @@ -250,7 +250,7 @@ apachectl -V | grep MPM #Server MPM: prefork -
        1
        2
        3
        4
        5
        6

        CC BY-SA Licensed | Copyright, Internet Initiative Japan Inc.
        - +
        1
        2
        3
        4
        5
        6

        CC BY-SA Licensed | Copyright, Internet Initiative Japan Inc.
        + diff --git a/web-server/hosting/index.html b/web-server/hosting/index.html index 0af0ebfe..9e0aaae2 100644 --- a/web-server/hosting/index.html +++ b/web-server/hosting/index.html @@ -8,7 +8,7 @@ - + @@ -118,7 +118,7 @@ </Directory>
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19

        apacheを起動します。ServerNameについて警告が出ますがひとまず問題ありません。

        root@0111e4e14e6d:/app# apachectl restart
         
        1

        無事起動できたらホストのブラウザなどからアクセスしてみましょう。sinatraアプリが動いていれば成功です。

        $ curl -H 'Content-Type:application/json' -d "{"bootcamp":"true"}" localhost
        -
        1

        1つ目の例と違うのは、sinatraアプリケーションが単体で起動してlocalhostなどをlistenしているのではなく、apacheの一部として起動している点です。この場合はechoアプリケーションを再起動するためにはApacheごと再起動しなければいけません。

        # 追加

        • apacheが起動している状態で/app以下のapp.rbを変更してみましょう。
        • pumaまたはunicornを使ってapacheまたはnginxとrubyアプリケーションを連携させてみましょう。

        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        - +
        1

        1つ目の例と違うのは、sinatraアプリケーションが単体で起動してlocalhostなどをlistenしているのではなく、apacheの一部として起動している点です。この場合はechoアプリケーションを再起動するためにはApacheごと再起動しなければいけません。

        # 追加

        • apacheが起動している状態で/app以下のapp.rbを変更してみましょう。
        • pumaまたはunicornを使ってapacheまたはnginxとrubyアプリケーションを連携させてみましょう。

        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        + diff --git a/web-server/nginx/index.html b/web-server/nginx/index.html index d5c576fe..a9d0e98d 100644 --- a/web-server/nginx/index.html +++ b/web-server/nginx/index.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ GitHub (opens new window)
        このハンズオンでやることnginxの紹介と実際に動かしてみるハンズオンです
        想定時間1h
        前提知識・用語なし

        # nginxを触ってみよう

        資料はこちら


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        - + (opens new window)
        このハンズオンでやることnginxの紹介と実際に動かしてみるハンズオンです
        想定時間1h
        前提知識・用語なし

        # nginxを触ってみよう

        資料はこちら


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        + diff --git a/web-server/overview/index.html b/web-server/overview/index.html index 70d81a85..f0d3daa0 100644 --- a/web-server/overview/index.html +++ b/web-server/overview/index.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ GitHub (opens new window)
        このハンズオンでやること世の中で使われるWebサーバーの種類や実際に使う際の構成例について紹介します
        想定時間1h
        前提知識・用語なし

        # Webサーバー Overview

        資料はこちら


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        - + (opens new window)
        このハンズオンでやること世の中で使われるWebサーバーの種類や実際に使う際の構成例について紹介します
        想定時間1h
        前提知識・用語なし

        # Webサーバー Overview

        資料はこちら


        CC BY-SA Licensed | Copyright (c) 2019, Internet Initiative Japan Inc.
        + diff --git a/web-server/tls/index.html b/web-server/tls/index.html index eb4c4e63..45317f24 100644 --- a/web-server/tls/index.html +++ b/web-server/tls/index.html @@ -8,7 +8,7 @@ - + @@ -324,7 +324,7 @@ 利用してみたい場合も証明書を使用するサービスで使えるかどうか確認してから発行するようにしましょう。

        # 安全とされている設定

        日本における暗号設定のセキュリティ上の最低ラインは先の暗号設定ガイドラインの3.1に記載されています。  TLS 暗号設定ガイドライン (opens new window)

        表14がわかりやすいでしょう。ビットセキュリティについては、2.6の表11がわかりやすいです。

        mozilla の推奨暗号スイートも参考になります。

        mozilla Server Side TLS (opens new window)

        今回のcipher設定についてはmozilla のintermediate のものを利用しています。 -こちら (opens new window)で対応する設定を出力してくれるのも便利です。

        - +こちら (opens new window)で対応する設定を出力してくれるのも便利です。

        +