From 8be1c24b130950bbc86d19761754f6d8635c1421 Mon Sep 17 00:00:00 2001 From: igrep Date: Mon, 22 Jul 2024 08:25:05 +0000 Subject: [PATCH] deploy: ac4cab1c994c10f78642cb8f1c5e21262591d8b6 --- 404.html | 6 +++--- about.html | 6 +++--- assets/js/{11.b8721122.js => 11.3afb67c4.js} | 2 +- assets/js/{12.a638d493.js => 12.ecb969fe.js} | 2 +- assets/js/{13.dd6eb0fe.js => 13.c0592f38.js} | 2 +- assets/js/{14.33573406.js => 14.433600fc.js} | 2 +- assets/js/{15.b528c6e2.js => 15.fbc41b18.js} | 2 +- assets/js/{19.86a73a9d.js => 19.21875a6e.js} | 2 +- assets/js/{25.59e28d48.js => 25.1dc895d7.js} | 2 +- assets/js/{26.addb59f2.js => 26.b037d27f.js} | 2 +- assets/js/{28.67fdfc00.js => 28.76e1a8ff.js} | 2 +- assets/js/{29.cee133e1.js => 29.d0f11907.js} | 2 +- assets/js/{31.933bab7f.js => 31.9ffadeca.js} | 2 +- assets/js/{36.53033b52.js => 36.0934d1ed.js} | 2 +- assets/js/{37.3f9bf71e.js => 37.d1dd3600.js} | 2 +- assets/js/{38.0654fab9.js => 38.88f91ac8.js} | 2 +- assets/js/{39.fd14a75e.js => 39.03c1bce8.js} | 2 +- assets/js/{4.20506e07.js => 4.0c1e113d.js} | 2 +- assets/js/{40.8e845cbf.js => 40.dcac10a1.js} | 2 +- assets/js/{45.6d5d6837.js => 45.57c713a6.js} | 2 +- assets/js/{48.a479bfd6.js => 48.d4bd9446.js} | 2 +- assets/js/{49.4a2e7bee.js => 49.22c32d4e.js} | 2 +- assets/js/{5.fcef7a00.js => 5.785ef6e1.js} | 2 +- assets/js/{50.1b4953d3.js => 50.a22ac7ee.js} | 2 +- assets/js/{51.c75eeeb0.js => 51.6f797472.js} | 2 +- assets/js/{52.df3c3eec.js => 52.ff1c2e64.js} | 2 +- assets/js/{53.ca33ed0f.js => 53.2e581c27.js} | 2 +- assets/js/{59.47e75bb6.js => 59.3949286c.js} | 2 +- assets/js/{60.edf1058a.js => 60.7a63bded.js} | 2 +- assets/js/{62.ce5311e9.js => 62.4d6758f8.js} | 2 +- assets/js/{app.cf20b39e.js => app.dbcace2b.js} | 4 ++-- cicd_infra/ansible/ANSIBLE_CODE_STYLE.html | 6 +++--- cicd_infra/ansible/CREATE_INVENTORY.html | 6 +++--- cicd_infra/ansible/CREATE_PLAYBOOK.html | 6 +++--- cicd_infra/ansible/CREATE_SERVER.html | 6 +++--- cicd_infra/ansible/INTRODUCTION.html | 6 +++--- cicd_infra/ansible/MANAGE_SETTINGS.html | 6 +++--- cicd_infra/ansible/REVERSE_PROXY.html | 6 +++--- cicd_infra/ansible/SAMPLE_RUN.html | 6 +++--- cicd_infra/ansible/USE_VARIABLE.html | 6 +++--- cicd_infra/ansible/index.html | 6 +++--- cicd_infra/drone/index.html | 6 +++--- cicd_infra/github_actions/index.html | 6 +++--- cicd_infra/jenkins/index.html | 6 +++--- cicd_infra/prometheus/index.html | 6 +++--- database/mongodb/index.html | 6 +++--- database/mysql/index.html | 6 +++--- database/overview/index.html | 6 +++--- database/postgresql/index.html | 6 +++--- database/redis/index.html | 6 +++--- development/docker/docker-compose/index.html | 10 +++++----- development/docker/docker/BUILD.html | 6 +++--- development/docker/docker/GETSTART.html | 6 +++--- development/docker/docker/OPERATION.html | 6 +++--- development/docker/docker/RUN_AS_IMAGE.html | 6 +++--- development/docker/docker/index.html | 6 +++--- development/git/index.html | 6 +++--- development/github/index.html | 6 +++--- development/kubernetes/index.html | 6 +++--- frontend/angular/index.html | 6 +++--- frontend/dom/index.html | 6 +++--- frontend/jquery/index.html | 6 +++--- frontend/overview/index.html | 6 +++--- frontend/react/index.html | 6 +++--- frontend/svelte/index.html | 6 +++--- frontend/vue/index.html | 6 +++--- index.html | 4 ++-- init/hello-bootcamp/index.html | 6 +++--- security/overview/index.html | 6 +++--- server-app/concurrent/index.html | 6 +++--- server-app/fastapi/index.html | 6 +++--- server-app/go/index.html | 6 +++--- server-app/go/src/go-tutor/index.html | 6 +++--- server-app/go/var/md/init.html | 6 +++--- server-app/java/DI.html | 6 +++--- server-app/java/index.html | 6 +++--- server-app/node/index.html | 6 +++--- server-app/overview/index.html | 6 +++--- server-app/rails/index.html | 6 +++--- server-app/test-hands-on/index.html | 6 +++--- web-server/apache/index.html | 6 +++--- web-server/apache_nginx/index.html | 6 +++--- web-server/hosting/index.html | 6 +++--- web-server/nginx/index.html | 6 +++--- web-server/overview/index.html | 6 +++--- 85 files changed, 199 insertions(+), 199 deletions(-) rename assets/js/{11.b8721122.js => 11.3afb67c4.js} (99%) rename assets/js/{12.a638d493.js => 12.ecb969fe.js} (99%) rename assets/js/{13.dd6eb0fe.js => 13.c0592f38.js} (98%) rename assets/js/{14.33573406.js => 14.433600fc.js} (99%) rename assets/js/{15.b528c6e2.js => 15.fbc41b18.js} (99%) rename assets/js/{19.86a73a9d.js => 19.21875a6e.js} (99%) rename assets/js/{25.59e28d48.js => 25.1dc895d7.js} (99%) rename assets/js/{26.addb59f2.js => 26.b037d27f.js} (99%) rename assets/js/{28.67fdfc00.js => 28.76e1a8ff.js} (99%) rename assets/js/{29.cee133e1.js => 29.d0f11907.js} (99%) rename assets/js/{31.933bab7f.js => 31.9ffadeca.js} (99%) rename assets/js/{36.53033b52.js => 36.0934d1ed.js} (96%) rename assets/js/{37.3f9bf71e.js => 37.d1dd3600.js} (99%) rename assets/js/{38.0654fab9.js => 38.88f91ac8.js} (99%) rename assets/js/{39.fd14a75e.js => 39.03c1bce8.js} (99%) rename assets/js/{4.20506e07.js => 4.0c1e113d.js} (99%) rename assets/js/{40.8e845cbf.js => 40.dcac10a1.js} (99%) rename assets/js/{45.6d5d6837.js => 45.57c713a6.js} (90%) rename assets/js/{48.a479bfd6.js => 48.d4bd9446.js} (99%) rename assets/js/{49.4a2e7bee.js => 49.22c32d4e.js} (99%) rename assets/js/{5.fcef7a00.js => 5.785ef6e1.js} (96%) rename assets/js/{50.1b4953d3.js => 50.a22ac7ee.js} (99%) rename assets/js/{51.c75eeeb0.js => 51.6f797472.js} (99%) rename assets/js/{52.df3c3eec.js => 52.ff1c2e64.js} (99%) rename assets/js/{53.ca33ed0f.js => 53.2e581c27.js} (99%) rename assets/js/{59.47e75bb6.js => 59.3949286c.js} (99%) rename assets/js/{60.edf1058a.js => 60.7a63bded.js} (99%) rename assets/js/{62.ce5311e9.js => 62.4d6758f8.js} (89%) rename assets/js/{app.cf20b39e.js => app.dbcace2b.js} (79%) diff --git a/404.html b/404.html index 776e6850..e2e9863c 100644 --- a/404.html +++ b/404.html @@ -8,13 +8,13 @@ - + -

404

How did we get here?
+ - + diff --git a/about.html b/about.html index 9dde51db..27a7e7e3 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.b8721122.js b/assets/js/11.3afb67c4.js similarity index 99% rename from assets/js/11.b8721122.js rename to assets/js/11.3afb67c4.js index 609f55ff..90c3f794 100644 --- a/assets/js/11.b8721122.js +++ b/assets/js/11.3afb67c4.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{353:function(t,s,a){t.exports=a.p+"assets/img/docker01.9c3fed93.jpg"},354:function(t,s){t.exports="data:image/jpeg;base64,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"},355:function(t,s){t.exports="data:image/jpeg;base64,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"},356:function(t,s){t.exports="data:image/jpeg;base64,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"},357:function(t,s){t.exports="data:image/jpeg;base64,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"},358:function(t,s,a){t.exports=a.p+"assets/img/docker06.f94651f1.jpg"},359:function(t,s,a){t.exports=a.p+"assets/img/DB00.3fdfd46b.jpg"},360:function(t,s,a){t.exports=a.p+"assets/img/DB01.e9d2dca0.jpg"},361:function(t,s,a){t.exports=a.p+"assets/img/DB02.deae098c.jpg"},362:function(t,s,a){t.exports=a.p+"assets/img/DB03.67543a21.jpg"},363:function(t,s,a){t.exports=a.p+"assets/img/DB04.195ebfdf.jpg"},519:function(t,s,a){"use strict";a.r(s);var e=a(10),r=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"リレーショナルdbを触ってみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#リレーショナルdbを触ってみる"}},[t._v("#")]),t._v(" リレーショナルDBを触ってみる")]),t._v(" "),s("hr"),t._v(" "),s("h2",{attrs:{id:"事前準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[t._v("#")]),t._v(" <事前準備>")]),t._v(" "),s("ul",[s("li",[t._v("Dockerがインストールされていること")]),t._v(" "),s("li",[t._v("PowerShellが利用できる状態であること")]),t._v(" "),s("li",[t._v("インターネットに接続できていること")])]),t._v(" "),s("h1",{attrs:{id:"postgresqlについて"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#postgresqlについて"}},[t._v("#")]),t._v(" PostgreSQLについて")]),t._v(" "),s("h3",{attrs:{id:"呼び方"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#呼び方"}},[t._v("#")]),t._v(" 呼び方")]),t._v(" "),s("p",[t._v("「ポストグレス キューエル」らしいのですが、この呼び方している人は少ないように感じます。"),s("br"),t._v("\nよく聞くのは「ポスグレ」、「ポストグレス」です。")]),t._v(" "),s("h3",{attrs:{id:"日本postgresqlユーザ会"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#日本postgresqlユーザ会"}},[t._v("#")]),t._v(" 日本PostgreSQLユーザ会")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://www.postgresql.jp/",target:"_blank",rel:"noopener noreferrer"}},[t._v("日本PostgreSQLユーザ会"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("日本国内でPostgreSQLデータベースを使用するユーザーのコミュニティおよび組織です。")]),t._v(" "),s("li",[t._v("このユーザ会は、PostgreSQLに関心を持つ人々が情報共有や交流を行い、技術的な知識を向上させることを目的としています。")])]),t._v(" "),s("h3",{attrs:{id:"バージョン体系-超抜粋"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#バージョン体系-超抜粋"}},[t._v("#")]),t._v(" バージョン体系(超抜粋)")]),t._v(" "),s("p",[t._v("※8.0よりも前のバージョン(6.0や7.0)については省略します。")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"right"}},[t._v("メジャーバージョン")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("リリース日")]),t._v(" "),s("th",{staticStyle:{"text-align":"right"}},[t._v("最新マイナー版")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("最新版リリース日")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("サポート期限")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("8.0")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2005/11/8")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("8.0.26")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/4")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("8.1.x~8.4.x")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("9.0")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/9/20")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("9.0.23")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2015/10/8")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/8")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("9.1.x~9.6.x")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("10")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2017/10/5")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("10.23")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/11/10")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/11/10")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("11")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2018/10/18")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("11.22")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/11/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/11/9")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("12")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2019/10/3")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("12.19")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/11/14")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("13")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2020/9/24")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("13.15")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2025/11/13")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("14")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2021/9/30")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("14.12")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2026/11/12")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("15")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/10/13")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("15.7")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2027/11/11")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("16")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/9/14")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("16.3")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2028/11/9")])])])]),t._v(" "),s("p",[t._v("※バージョン体系がPostgreSQL10より変更されました。")]),t._v(" "),s("p",[t._v("Postgresql 10以前 ⇒  "),s("span",{staticStyle:{"font-size":"200%",color:"red"}},[s("strong",[t._v("9.0")])]),t._v(" ."),s("span",{staticStyle:{"font-size":"200%",color:"blue"}},[t._v("23")]),s("br"),t._v(" "),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("9.0の部分がメジャーバージョン")]),t._v("で"),s("span",{staticStyle:{"font-size":"100%",color:"blue"}},[t._v("23がマイナーバージョン")]),t._v("となります。"),s("br"),t._v(" "),s("br"),t._v("\nPostgresql 10以降 ⇒  "),s("span",{staticStyle:{"font-size":"200%",color:"red"}},[s("strong",[t._v("10")])]),t._v(" ."),s("span",{staticStyle:{"font-size":"200%",color:"blue"}},[t._v("22")]),s("br"),t._v(" "),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("10の部分がメジャーバージョン")]),t._v("で"),s("span",{staticStyle:{"font-size":"100%",color:"blue"}},[t._v("22がマイナーバージョン")]),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("PostgreSQLは、"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("オープンソースのリレーショナルデータベース")]),t._v("管理システムです。"),s("br"),t._v("\nそのライセンスは、PostgreSQL Global Development Group(PGDG)によって開発されたもので以下の2つのライセンスオプションが提供されています。")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("PostgreSQLライセンス(PostgreSQL License\nこれは、PostgreSQLプロジェクトが採用している独自のライセンスです。\nこのライセンスは、商用利用や非商用利用、修正や再配布を含むあらゆる使用形態に対して、\n自由かつ"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("無償で利用することを許可しています。")]),t._v("また、ソースコードの利用や変更、派生物の作成、\nバイナリ形式での再配布なども可能です。そのため、PostgreSQLを自由に使用し、変更や拡張を行い、\nプロジェクトや製品に組み込むことができます。")])]),t._v(" "),s("li",[s("p",[t._v("MIT(マサチューセッツ工科大学)ライセンス\nPostgreSQLプロジェクトは、PostgreSQLライセンスに加えて、一部のコンポーネントについてはMITライセンスも採用しています。\nMITライセンスは、商用利用や非商用利用、修正や再配布を含むあらゆる使用形態に対して、"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("自由かつ無償で利用することを許可")]),t._v("しています。\nただし、著作権表示および本許諾表示をソフトウェアのすべての複製または重要な部分に記載しなければならず、作者または著作権者は、ソフトウェアに関してなんら責任を負わない。")])])]),t._v(" "),s("h1",{attrs:{id:"ハンズオン研修-【データベースに触れてみよう】"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン研修-【データベースに触れてみよう】"}},[t._v("#")]),t._v(" ハンズオン研修 【データベースに触れてみよう】")]),t._v(" "),s("h2",{attrs:{id:"_0-postgresql環境準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-postgresql環境準備"}},[t._v("#")]),t._v(" 0. PostgreSQL環境準備")]),t._v(" "),s("p",[t._v("本演習にて入力するコマンドはおおきく分けて以下の3つにわかれているため、入力する場所には注意する必要があります。")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("1.Linux環境 【 $ 表示のプロンプト】"),s("br"),t._v("\n →  ★0-1、★0-2 、★0-3 での入力先となります。")])]),t._v(" "),s("li",[s("p",[t._v("2.コンテナ環境 【 root@xxx から始まるプロンプト】"),s("br"),t._v("\n →  ★0-4 ★1-1、及び★A-1、★A-2、★B-1先頭行、★B-2 での入力先となります。")])]),t._v(" "),s("li",[s("p",[t._v("3.PostgreSQL環境 【 postgres=# から始まるプロンプト】"),s("br"),t._v("\n →  ★1-2~演習問題まで及び★B-1drop文 の入力先となります。")])])]),t._v(" "),s("p",[t._v("※出力イメージと異なるような結果が表示された際には入力場所をご確認ください。")]),t._v(" "),s("h3",{attrs:{id:"★0-1-postgresql環境のダウンロード"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-1-postgresql環境のダウンロード"}},[t._v("#")]),t._v(" ★0-1 PostgreSQL環境のダウンロード")]),t._v(" "),s("p",[t._v("以下のコマンドを入力し、PostgreSQL環境をダウンロードします。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker pull postgres\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ\n"),s("img",{attrs:{src:a(353),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-2-コンテナ起動"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-2-コンテナ起動"}},[t._v("#")]),t._v(" ★0-2 コンテナ起動")]),t._v(" "),s("p",[t._v("以下のコマンドを入力し、コンテナ(PostgreSQLも起動します)環境を起動させます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker run --name some-postgres -e POSTGRES_PASSWORD=postgres -d postgres\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(354),alt:""}}),s("br"),t._v("\n※正常に起動すると上記のような64文字の文字列(値は各自で異なります)が表示されます。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-3-コンテナ接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-3-コンテナ接続"}},[t._v("#")]),t._v(" ★0-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("docker exec -it some-postgres /bin/bash\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(355),alt:""}}),s("br"),t._v("\n ※接続に成功すると上記のような最後に「#」が付いた状態で表示されます。"),s("br"),t._v("\n ※「root@」以降の文字列は各自で異なります。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-4-ロケールの確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-4-ロケールの確認"}},[t._v("#")]),t._v(" ★0-4 ロケールの確認")]),t._v(" "),s("p",[t._v("本講義で使用する言語「en_US.utf8」が表示されることを確認します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("locale -a\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(356),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_1-データベースへ接続してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1-データベースへ接続してみよう"}},[t._v("#")]),t._v(" 1. データベースへ接続してみよう")]),t._v(" "),s("h3",{attrs:{id:"★1-1-db接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★1-1-db接続"}},[t._v("#")]),t._v(" ★1-1 DB接続")]),t._v(" "),s("p",[t._v("PostgreSQLデータベースに接続し、SQLを発行するためには「psql」というツールを利用します。"),s("br"),t._v("\n OSユーザ「postgres」にスイッチ後、データベースに接続してみます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("su - postgres\npsql -h localhost -p 5432 -d postgres -U postgres\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("br"),t._v(" "),s("img",{attrs:{src:a(357),alt:""}}),s("br"),t._v("\n※プロンプトが「postgres=#」に変更されることを確認します。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★1-2-データベース確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★1-2-データベース確認"}},[t._v("#")]),t._v(" ★1-2 データベース確認")]),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("\\l+\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(358),alt:""}})]),t._v(" "),s("p",[t._v("現時点ではデータベースが3つ表示されています。"),s("br"),t._v("\n イメージ図としては以下(グレーの四角がデータベース)です。"),s("br"),t._v(" "),s("img",{attrs:{src:a(359),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_2-ユーザ-ロール-を作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-ユーザ-ロール-を作成してみよう"}},[t._v("#")]),t._v(" 2. ユーザ(ロール)を作成してみよう")]),t._v(" "),s("h3",{attrs:{id:"★2-1-ユーザ作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★2-1-ユーザ作成"}},[t._v("#")]),t._v(" ★2-1 ユーザ作成")]),t._v(" "),s("p",[t._v("デフォルトで作成されているユーザ「postgres」以外に以下の3ユーザを作成します。")]),t._v(" "),s("ul",[s("li",[t._v("ユーザ「user01」を作成します")]),t._v(" "),s("li",[t._v("ユーザ「user02」を作成します")]),t._v(" "),s("li",[t._v("スーパーユーザ「suser01」を作成します")])]),t._v(" "),s("p",[t._v("作成後は以下のようなイメージ(緑色の丸枠がユーザ)となります。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(360),alt:""}})]),t._v(" "),s("p",[t._v("「user01」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create role user01 with login password 'user01'; \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("「user02」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" create user user02 with password 'user02';\t\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("「suser01」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" create role suser01 with superuser login password 'suser01';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※全て「CREATE ROLE」と表示されることを確認します。"),s("br"),t._v("\n※作成後、スーパーユーザ(suser01)でログインしなおしましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" \\connect - suser01 \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※PostgreSQLにおいて「ユーザ」と「ロール」は厳密には異なるため、上記のように2種類のコマンドが存在しますが、"),s("br"),t._v("\n 今回は「ユーザ」と「ロール」は同じものであると考えましょう。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_3-データベースを作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-データベースを作成してみよう"}},[t._v("#")]),t._v(" 3. データベースを作成してみよう")]),t._v(" "),s("p",[t._v("「create database」文を利用しデータベースを作成します。"),s("br"),t._v("\n ここでは、データベース名を「db_world」と指定し、template0を使用してデータベースを作成しています。"),s("br"),t._v("\n ※「public」スキーマも同時に作成されます。 (スキーマは後述しますが、「public」スキーマは全ユーザが自由に使えるスキーマです)")]),t._v(" "),s("p",[s("img",{attrs:{src:a(361),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★3-1-データベースの作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★3-1-データベースの作成"}},[t._v("#")]),t._v(" ★3-1 データベースの作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create database db_world owner = suser01 template = template0 encoding = 'UTF8' lc_collate = 'en_US.utf8' lc_ctype = 'en_US.utf8';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※「CREATE DATABASE」と表示されることを確認します。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★3-2-データベースへの接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★3-2-データベースへの接続"}},[t._v("#")]),t._v(" ★3-2 データベースへの接続")]),t._v(" "),s("p",[t._v("次STEPの準備として、作成したデータベース(db_world)にスーパーユーザ(suser01)で接続します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("\\connect db_world suser01 \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("br"),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("データベース内にスキーマ(青色の四角枠)を作成し、ユーザに利用権限(オレンジの矢印)を付与します。"),s("br"),t._v("\n スキーマとは、テーブルやインデックスといったオブジェクトを配置する領域です。"),s("br"),t._v("\n ※「public」スキーマはDB作成時に作られており、テーブル等の作成時にスキーマ名を省略するとこの領域に配置されます。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(362),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★4-1-スキーマの作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★4-1-スキーマの作成"}},[t._v("#")]),t._v(" ★4-1 スキーマの作成")]),t._v(" "),s("p",[t._v("以下の2つのスキーマを作成してみましょう。\n 以下の例では2つのスキーマの所有者に「suser01」を指定しています。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create schema sch_jpn authorization suser01;\t\ncreate schema sch_usa authorization suser01;\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("p",[t._v("※「CREATE SCHEMA」と表示されることを確認してください。")]),t._v(" "),s("h3",{attrs:{id:"★4-2-権限付与"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★4-2-権限付与"}},[t._v("#")]),t._v(" ★4-2 権限付与")]),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("grant all privileges on database db_world to user01;\t\ngrant all privileges on schema sch_jpn to user01;\t\ngrant all privileges on schema sch_jpn to user02;\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("p",[t._v("※「GRANT」と表示されることを確認してください。"),s("br"),t._v("\n  この権限の追加により")]),t._v(" "),s("ul",[s("li",[t._v("「user01」は「db_world」データベース、「sch_jpn」スキーマ内への全権限を有します。")]),t._v(" "),s("li",[t._v("「user02」は「sch_jpn」スキーマ内への全権限を有します。")])]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_5-テーブルを作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-テーブルを作成してみよう"}},[t._v("#")]),t._v(" 5. テーブルを作成してみよう")]),t._v(" "),s("p",[t._v("上記にて作成した「sch_jpn」スキーマ内にテーブル(オレンジ色の四角枠)を作成します。"),s("br"),t._v(" "),s("img",{attrs:{src:a(363),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★5-1-テーブル作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★5-1-テーブル作成"}},[t._v("#")]),t._v(" ★5-1 テーブル作成")]),t._v(" "),s("p",[t._v("「user01」で接続し、テーブルを作成します。"),s("br"),t._v("\n 以下ではテーブル作成時に主キー(PRIMARY KEY)を作成しています。"),s("br"),t._v("\n 主キーを作成した列には同じ値とnullを挿入できなくなります。"),s("br"),t._v("\n ※スキーマ(sch_jpn)は必ず指定してください。省略してしまうと「public」スキーマに作成されます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("\\connect db_world user01\ncreate table sch_jpn.tbl_region(id_reg serial,reg_name varchar(40), PRIMARY KEY (id_reg));\ncreate table sch_jpn.tbl_pref(id_pref serial,pref_name varchar(40),id_reg int, PRIMARY KEY (id_pref));\ncreate table sch_jpn.tbl_food(id_fd serial,fd_name varchar(40),price int, PRIMARY KEY (id_fd));\ncreate table sch_jpn.tbl_proper(id_user serial,username varchar(40),id_pref int,id_fd int, PRIMARY KEY (id_user));\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("p",[t._v("※「CREATE TABLE」と表示されることを確認してください。")]),t._v(" "),s("p",[t._v("※テーブルを削除するコマンドは「drop table <テーブル名>」となります。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_6-テーブルにデータを登録してみよう-insert-into-values"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-テーブルにデータを登録してみよう-insert-into-values"}},[t._v("#")]),t._v(" 6. テーブルにデータを登録してみよう(insert into ~ values~)")]),t._v(" "),s("h3",{attrs:{id:"★6-1-データの登録"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★6-1-データの登録"}},[t._v("#")]),t._v(" ★6-1 データの登録")]),t._v(" "),s("p",[t._v("上記で作成したテーブルにデータを登録します。"),s("br"),t._v("\n データを追加するコマンドはinsert文となります。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("insert into sch_jpn.tbl_region(reg_name) values ('hokkaido');\ninsert into sch_jpn.tbl_region(reg_name) values ('tohoku');\ninsert into sch_jpn.tbl_region(reg_name) values ('kanto');\ninsert into sch_jpn.tbl_region(reg_name) values ('chubu');\ninsert into sch_jpn.tbl_region(reg_name) values ('kinki');\ninsert into sch_jpn.tbl_region(reg_name) values ('shikoku');\ninsert into sch_jpn.tbl_region(reg_name) values ('kyushu');\n\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('hokkaido',1);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('yamanashi',4);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('osaka',5);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('nagasaki',7);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('tokyo',3);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('chiba',3);\n\ninsert into sch_jpn.tbl_food(fd_name,price) values ('hamburger',500);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('ramen',1500);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('takoyaki',800);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('gyoza',350);\n\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('suzuki',1,1);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('satou',2,4);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('tanaka',3,2);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('ito',4,3);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('watanabe',5,4);\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("p",[t._v("※全て「INSERT 0 1」と表示されることを確認してください。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_7-データを参照してみよう-select-from"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_7-データを参照してみよう-select-from"}},[t._v("#")]),t._v(" 7. データを参照してみよう(select ~ from ~)")]),t._v(" "),s("h3",{attrs:{id:"★7-1-データの参照"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-1-データの参照"}},[t._v("#")]),t._v(" ★7-1 データの参照")]),t._v(" "),s("p",[t._v("テーブルに登録されているデータを参照します。データはSELECT文にて参照します。"),s("br"),t._v("\n ※「*」を指定するとテーブルのカラム(列)全てを指定したことと同義になります。")]),t._v(" "),s("p",[t._v("〇7-1-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_region;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_reg | reg_name\n--------+----------\n 1 | hokkaido\n 2 | tohoku\n 3 | kanto\n 4 | chubu\n 5 | kinki\n 6 | shikoku\n 7 | kyushu\n(7 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-2. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_pref;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-2. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_pref | pref_name | id_reg\n---------+-----------+--------\n 1 | hokkaido | 1\n 2 | yamanashi | 4\n 3 | osaka | 5\n 4 | nagasaki | 7\n 5 | tokyo | 3\n 6 | chiba | 3\n(6 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-3. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_food;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-3. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_fd | fd_name | price\n-------+-----------+-------\n 1 | hamburger | 500\n 2 | ramen | 1500\n 3 | takoyaki | 800\n 4 | gyoza | 350\n(4 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-4. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_proper;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-4. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_user | username | id_pref | id_fd\n---------+----------+---------+-------\n 1 | suzuki | 1 | 1\n 2 | satou | 2 | 4\n 3 | tanaka | 3 | 2\n 4 | ito | 4 | 3\n 5 | watanabe | 5 | 4\n(5 rows)\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("hr"),t._v(" "),s("h3",{attrs:{id:"★7-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-2"}},[t._v("#")]),t._v(" ★7-2")]),t._v(" "),s("p",[t._v("テーブルから条件を指定 (where句)してデータを抽出します。")]),t._v(" "),s("p",[t._v("〇7-2-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_pref where id_reg=3;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-2-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_pref | pref_name | id_reg\n---------+-----------+--------\n 5 | tokyo | 3\n 6 | chiba | 3\n(2 rows)\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("br"),t._v(" "),s("h3",{attrs:{id:"★7-3-複数のテーブルからのデータ参照"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-3-複数のテーブルからのデータ参照"}},[t._v("#")]),t._v(" ★7-3 複数のテーブルからのデータ参照")]),t._v(" "),s("p",[t._v("複数のテーブルを結合しデータを抽出します。")]),t._v(" "),s("p",[t._v("〇7-3-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select\n pp.username,\n pr.pref_name,\n rg.reg_name,\n fd.fd_name\nfrom\n sch_jpn.tbl_region rg,\n sch_jpn.tbl_pref pr,\n sch_jpn.tbl_food fd,\n sch_jpn.tbl_proper pp\nwhere\n pp.id_pref=pr.id_pref and\n pp.id_fd=fd.id_fd and\n pr.id_reg=rg.id_reg\nORDER BY\npp.username\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("p",[t._v("●7-3-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" username | pref_name | reg_name | fd_name\n----------+-----------+----------+-----------\n ito | nagasaki | kyushu | takoyaki\n satou | yamanashi | chubu | gyoza\n suzuki | hokkaido | hokkaido | hamburger\n tanaka | osaka | kinki | ramen\n watanabe | tokyo | kanto | gyoza\n(5 rows)\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("h2",{attrs:{id:"_8-データを更新してみよう-update-set"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_8-データを更新してみよう-update-set"}},[t._v("#")]),t._v(" 8. データを更新してみよう(update ~ set ~)")]),t._v(" "),s("h3",{attrs:{id:"★8-1-データの更新"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★8-1-データの更新"}},[t._v("#")]),t._v(" ★8-1 データの更新")]),t._v(" "),s("p",[t._v("データを変更するには「update」文を使用します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("update sch_jpn.tbl_pref set id_reg=4 where id_pref=2;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◆変更されているか確認してみよう。"),s("br"),t._v("\n  ⇒ 上記で使用したSELECT文を使い「tbl_pref」を参照してください。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_9-データを削除してみよう-delete-from"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_9-データを削除してみよう-delete-from"}},[t._v("#")]),t._v(" 9. データを削除してみよう(delete from ~)")]),t._v(" "),s("h3",{attrs:{id:"★9-1-データの削除"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★9-1-データの削除"}},[t._v("#")]),t._v(" ★9-1 データの削除")]),t._v(" "),s("p",[t._v("データを削除するには「delete」文を使用します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("delete from sch_jpn.tbl_food where fd_name='ramen';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◆削除されているか確認してみよう。"),s("br"),t._v("\n  ⇒ 上記で使用したSELECT文を使い「tbl_food」を参照してください。")]),t._v(" "),s("br"),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("h1",{attrs:{id:"演習問題"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#演習問題"}},[t._v("#")]),t._v(" 演習問題")]),t._v(" "),s("h3",{attrs:{id:"問1-自分の好きな食べ物を「tbl-food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl-proper」テーブルに登録してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問1-自分の好きな食べ物を「tbl-food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl-proper」テーブルに登録してください"}},[t._v("#")]),t._v(" 問1)自分の好きな食べ物を「tbl_food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl_proper」テーブルに登録してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: insert into ~ values ~ \n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問2-問1で追加したデータを参照してください-2テーブルを確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問2-問1で追加したデータを参照してください-2テーブルを確認してください"}},[t._v("#")]),t._v(" 問2)問1で追加したデータを参照してください(2テーブルを確認してください)")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: select ~ from ~ where ~\n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問3-「tbl-proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問3-「tbl-proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください"}},[t._v("#")]),t._v(" 問3)「tbl_proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: update ~ set ~\n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問4-「tbl-food」から「takoyaki」のみを削除し、確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問4-「tbl-food」から「takoyaki」のみを削除し、確認してください"}},[t._v("#")]),t._v(" 問4)「tbl_food」から「takoyaki」のみを削除し、確認してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: delete from ~ where ~\n")])])]),s("br"),t._v(" "),s("p",[t._v("▼▼▼▼▼▼▼▼▼▼▼▼▼以降、時間がある人向け▼▼▼▼▼▼▼▼▼▼▼▼▼")]),t._v(" "),s("h2",{attrs:{id:"a-バックアップ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#a-バックアップ"}},[t._v("#")]),t._v(" A.バックアップ")]),t._v(" "),s("h3",{attrs:{id:"★a-1-データベース-バックアップ準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-1-データベース-バックアップ準備"}},[t._v("#")]),t._v(" ★A-1. データベース バックアップ準備")]),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("su - postgres\t\ncd /tmp\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("br"),t._v(" "),s("h3",{attrs:{id:"★a-2-データベースのバックアップ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-データベースのバックアップ"}},[t._v("#")]),t._v(" ★A-2. データベースのバックアップ")]),t._v(" "),s("p",[t._v("ここでは、2通りのバックアップを試してみます。")]),t._v(" "),s("h4",{attrs:{id:"★a-2-1-plain形式での出力-人間が見て分かるような結果が出力されます"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-1-plain形式での出力-人間が見て分かるような結果が出力されます"}},[t._v("#")]),t._v(" ★A-2-1 plain形式での出力\t(人間が見て分かるような結果が出力されます)")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pg_dump -U suser01 -d db_world --format=p --create --file db_world_db.sql\n\ncat db_world_db.sql\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("h4",{attrs:{id:"★a-2-2-バイナリ形式での出力"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-2-バイナリ形式での出力"}},[t._v("#")]),t._v(" ★A-2-2 バイナリ形式での出力")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pg_dump -U suser01 -d db_world --format=c --create --file db_world_db.custom\n\nls -l\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("◎出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("total 20\n-rw-r--r-- 1 postgres postgres 9710 Jul 10 12:00 db_world_db.custom\n-rw-r--r-- 1 postgres postgres 7561 Jul 10 11:59 db_world_db.sql\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("br"),t._v(" "),s("h2",{attrs:{id:"b-リストア"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#b-リストア"}},[t._v("#")]),t._v(" B.リストア")]),t._v(" "),s("h3",{attrs:{id:"★b-1-データベース削除"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★b-1-データベース削除"}},[t._v("#")]),t._v(" ★B-1. データベース削除")]),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("psql -h localhost -p 5432 -d postgres -U postgres\n\n\\l\ndrop database db_world;\n\\l\n\n\\q\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("br"),t._v(" "),s("h3",{attrs:{id:"★b-2-データベースをリストア"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★b-2-データベースをリストア"}},[t._v("#")]),t._v(" ★B-2. データベースをリストア")]),t._v(" "),s("p",[t._v("データベースが削除されているため、「db_world」データベースを作成し、"),s("br"),t._v("\n 「db_world_db.custom」を使ってリストアします。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("createdb --template=template0 --encoding='UTF8' --lc-collate='en_US.utf8' --lc-ctype='en_US.utf8' db_world\n\npg_restore -v -c -d db_world /tmp/db_world_db.custom\n\npsql -h localhost -p 5432 -d postgres -U postgres\n\\l\n\\q\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("p",[t._v("この操作にてバックアップ取得時の状態へ復旧します。")]),t._v(" "),s("p",[t._v("※この他にもバックアップ取得時点ではなく、障害発生直前にまでリカバリできる"),s("br"),t._v("\n  バックアップコマンド(pg_basebackup)も存在しますがここでは割愛します。")]),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("h1",{attrs:{id:"postgresql環境の停止について"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#postgresql環境の停止について"}},[t._v("#")]),t._v(" PostgreSQL環境の停止について")]),t._v(" "),s("p",[t._v("Docker runコマンド実行の際に -d optionをつけている(back ground実行)ため、"),s("br"),t._v("\n★0-3 コンテナ接続"),s("br"),t._v("\nで 開いていた bash で exit してもPostgreSQL(コンテナ)環境は終了しません。")]),t._v(" "),s("p",[t._v("演習が終わりましたらstopコマンドにて停止させてください。"),s("br"),t._v("\npsコマンドにてstatus列 が Exited になっていれば停止となります。"),s("br"),t._v("\n※再度利用する際には docker start some-postgres にて起動可能させます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker stop some-postgres\n\ndocker ps -a\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("以上")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{353:function(t,s,a){t.exports=a.p+"assets/img/docker01.9c3fed93.jpg"},354:function(t,s){t.exports="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAbAlQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDitI0RtRs7/UZ5Hh07T1RrmWNBI4LttQKhZdxJ9wAAec4BtL4etoopb68v5o9JMaPb3MVsJHmLuyqnll12n93NnJwDGcFsqWpaXYXL213qy6dDe2On7PtKzSFVHmEqmQrKx5/untzxWhf2MusaloEVsyRf2nDGlvAxIjtszPFtBHO3cpfOC3znJZss3qNu+5xrYpa3pljpqWJs724uGuYfPeOe2WFolJITIDtywG4dPlZSM7uMmumtvsXiLxpMz/u9OWOZ4Fn3AJDBCxiR9mWwFjRW25YgHBzzWgmm2GuT6dHPf2NxPLqdtaNJpVm9uqxSFt2/MKJuBUbTjJy+cgDac1tx2vscTRXTaZcprl4lrNocNz5ckRtoLKJYOsqIY5JBzsYNt3uWYNs+blt02sNYX3hee6jmhuLu2vYITLDpsVmgDpMW27MGQExqQXVSAOg3EU+bWwrHJ1rQaVaLZwTalfvaNdqWtQkHmqVDFN8hDAou5WHyh2+Vjt+7u02aCx1Kw0X7AlxZXENq84jtle6kaaJHYxuRuDAvhVBC/KoYNltz7fU7bR9H0iPUNNh1SO4je6Qy4V7YCSWNUjbB43KzlXDIxb7gOSycn0BIzNJ8NXup+LYfDpXyrs3DQTcq3lbSd564O0KxwDzjiq2n6fFcwT3l5O9vYwMiSSRxiRy7htqqmVySEY5JAAU85wD3mhaRcab4mVRqNpdXk2urBcyTXsSTGGGdWJMbOWJkkAbH3h5XcPXM6fplwmka1bXiZ023kt5rq7spYpzA/wA6x4AcLID5jghWyDg5+Uqy57j5Sl/YH+keZ9p/4ln2f7Z9q8v5vI8zy8+XnO/f8m3ON38Wz56rahp8VtBBeWc73FjOzpHJJGI3DoF3KyZbBAdTkEghhznIHU3f2b/hHXFn532ceHQFM2NzY1TliBwMnJxk4zjLYyauiWNtqOi6RBdsiwLd6nOxkLBP3dtDIN235tuVGdvzYzjnFHM92FjlrO0nv72Cztk8y4uJFiiTIG5mOAMngcnvWhPpVo1nPNpt+921ooa6DweUoUsE3xksS67mUfMEb5lO3723qdKjtLjWtDvDe6ddXkWs2cQfTbJ4EEbMxIkHlImcqNuPmIL5yANvM6L/AMgnxH/2Dk/9KrenzXCxjUV3mp6bplveapo/2/S5LayW4WCG3s5jeK8SsVLSiH5iSvz5YoAWKhQFK5jNBY6lYaL9gS4sriG1ecR2yvdSNNEjsY3I3BgXwqghflUMGy24U7i5TG0/T4rmCe8vJ3t7GBkSSSOMSOXcNtVUyuSQjHJIACnnOAdA6HplpZNcajq0w/02e0jNhbLcI/lBCXDNInB8wY46CprLVLiPwDqVusdoUW9togWs4mbDpckncV3E8cNnK9iKLS81Gy8FQ3MOmWj2keoyx/bbiOOf53jjJjEbggcRg7tpPYEchhtj0MKGza91SOy0/fO08wht94CM5ZsLkZIUnI7kD1rQm0DyNe1WwkucW2mSSC4ufL52JII9wTPJLFQBnGWGSBlhuro0dr/aF3p8tjDPPaW6QQT38URiFxArzMvmOCVCuYwDk4kzuLITT9dtZLXX/HfmNC3mxySr5UySYBvocBtpO0+qnBHcUue70DlMaDw9bXTGW3v5ms5Le5kt5WtgrNLBH5jxuu/5flwQwLD5l77guLZ2k9/ewWdsnmXFxIsUSZA3MxwBk8Dk967Dw26xaPp0jxJKqtrLGNyQrgWMfBwQcH2IPvR4akGq6ppWpTw28d1a63YwK1tAkCukjOxDKgCkgxjBwD8xyT8u05mrhYxZfD8UujXmpaXePeRae0aXpeERKu8lVaPLEuu4EchW5X5eu3DroFnTWtL1QzWlpB9gt1ntvs0CxlczRxlGI5cYk6uWbKg7uW3bmp6bplveapo/2/S5LayW4WCG3s5jeK8SsVLSiH5iSvz5YoAWKhQFKvmtowtc5PWtPi0vUvs8E7zxNDDOkjxiNiskSyDKgtggPjqelPstJjvNH1K++3wpJZRiT7Lscu4MkaZzjaB+89Scjpg5rppZ0vvE+jaFNaWn2S8t9OgmfyFMx3wQjeJDllK5GApC/KMqctu5/Rf+QT4j/wCwcn/pVb0JuwW1KenWH26SZpJfJtraPzriXbuKJuVeFyNxLMqgcDLDJAyRHqFtFaXjRQXKXMRVHSRccqyhgGAJwwBwwycMCMnGa3fDWqXFloviBIo7Rglkso86zilOTcQLgl1JIwfunjPOM80zw7aW39l3+pSXVjbz280EET38LTRDzFlLfIqPlsR4G4FQC38W0gu02Kxn6Ppttfrfy3l1Nb29nbidmhgErNmRIwApZR1kBzntVW+SxjnUafc3E8W3Ja4gWFg2TxgO/GMc5/CuwhheCa9m0iztNSe40Zrm7ZY2ito9lwC0qJIqbwPKUlANu4sACo2VStLO21eGy1O6t4RK32/zY4EEMcv2e3WZMqmAuS21tu3IAxhssVza3HYwNJsP7T1OG1MvlRnc80u3d5USgs74zztVWOBycYHNX7rStHXQpdStNTvnZZlgiiuLFIhKxBLYYStwoAzgHBdP72ahvLwanpb3E9miXUMyIs9rapDEUZXJVwgC7sqCpxkjfknC40JIdt74b0RbT7Xjyp5bUSbBcS3BV8B+q5i8hCeACpI9S22I5mty/wBK0fTokjm1O+a9a0iuBGlihj3SRLIq7zLnHzAE7fXitDWGsL7wvPdRzQ3F3bXsEJlh02KzQB0mLbdmDICY1ILqpAHQbiKusbu71zw/pOp6TaW1pqlvZR7tiSTSRlViWYS8sh+UEKCANoDK2W3JyHY5yDSrRbOCbUr97RrtS1qEg81SoYpvkIYFF3Kw+UO3ysdv3d2feWk9hez2dynl3FvI0UqZB2spwRkcHkdq6a31O20fR9Ij1DTYdUjuI3ukMuFe2AkljVI2weNys5VwyMW+4Dktu21ja2niO00XVpEkN3fvBJI1jHcS33+kvG0rSuQ8AbGwbGZgUZsAkFjnaC1zzaitnSwlpot9qyxQy3MFxBbxCeJZUUSLKzNsYFSf3YAyCAGJxnBF2BLaZLnV/sPlzwacLvy5IQLeSb7SsO9E6FMNu2/d3qwxsGyqchWOchRZZ443lSJWYKZHBKoCepwCcD2BPtWnfaFLb6/BpFnMl7LcLbmF0BRZGmjRlA3YwMuBk49SB0DNSnS+sra9a08m5eSSOWSGBYoJAoQrtVcKHG4hgABjYcZJJs+IppbfXLSeCR4pY7CwdJEYqysLWIggjoQaLu4EOp6KtlBLNb3DzrbTC1uhJAYWimIYgBW5KnY+CQG+U7lXjM0/h+KN7ixS8d9YtFke4tTCBGPLBaRVk3fMygEnKhTtbazfLun1mK7VLvTFLzSWLG71Sd3+d52KIwbn5hG77B97JaRgcPgbkltDbeLNXbUoru312ayvpZ7TEZhhd7WR2YSBmLAg8LgEbhljt+aeZ2HY4CtDUtPisrfTZ4Z3lW9tPtBDxhCjCR42XgnIzGcHjII4FdZbabpkVxp2lS3+lra3UNs9xA1nNJes0saOSkiwthgXGxVbbwoYHL7sy81VrDQ/D0K2VjMr2DmU3FuJGdftU427jyg68oVb5uvC4fNd6BYybLTbZrIX2pXU1taPI0MRggEzyOoUt8pZQAAy5JOcsMA/MVu3fhafR9T1C21qb7NDYSJFNLAomLM4LRhFyudyqW5K4AOfmwp1tXkHhrS3sbKG3mEGt6hBHLeQJOwRFgGNrgpk4BJ2544IBIMbJBocOp3kVtDcWkkdkDp84Lw77i3aXdydw2YdVIIcbh8/DBlzNhZGN/YH+keZ9p/4ln2f7Z9q8v5vI8zy8+XnO/f8m3ON38Wz56rahp8VtBBeWc73FjOzpHJJGI3DoF3KyZbBAdTkEghhznIHUmGTUJpZbK0mGlXmjPNLbLIhawto7g52E7Q+HhDkYBbewJ3EyU+0XTo/DNrNZq8ttbTXjvd3sEb+Uf8ARAXEB3KxIYRhCxG5g5ZQPlOZhY4mztJ7+9gs7ZPMuLiRYokyBuZjgDJ4HJ71ran4fisjfw2149xdaYxS+jeERqmHEZaNtx3qHIHIVvmU7fvbbWvWMOoX+hjSU82bU7cYP2eO182UzyRD92rFE4VRwcHGTyTVzSrR55rzRm0eGK7spIFureCdlN9tuI4mhd2ZtuWcNlCq5XJU/KUbl1C3Q42uj07wq2o3EGmRXL/23cw+fBaCIeWUMfmKGlLDazJyAFI+ZQSDu22dYawvvC891HNDcXdtewQmWHTYrNAHSYtt2YMgJjUguqkAdBuIq7LOl94n0bQprS0+yXlvp0Ez+QpmO+CEbxIcspXIwFIX5RlTltycnbQEjnINKtFs4JtSv3tGu1LWoSDzVKhim+QhgUXcrD5Q7fKx2/d3PGhpZec2tzzWSR3EloFgiWd2mjxvGN6rhdy5O7ksMA8ldO31O20fR9Ij1DTYdUjuI3ukMuFe2AkljVI2weNys5VwyMW+4DktNqOmSQ2V/FrM121hZazcQLqcKJK8twwXeHjaQHkRqwbPB3A7t2VOZ3Cxmp4etra3vZ9Vv5oY7eS3SNrS2E3miaN5EcbnTAKoDzz8wyAQagstL0y8uNSf+0LtLCytxP5v2NTK+ZI48eX5mBzJ13Hge+K6bUri7htdWuNO0qGazht9Jmea7ZJvsw+yhEBRgFkJ8wjO0gYJwDgrWso7lYZr7TtKt7mW+0R7u6jbasMHl3XMojOAR+5U+X93LH5dvyUuZ2HZHOLptte6nHa6VdTSxmNpJJbuAQ+WFDM5IVnyAqk8cnkAE4zMdDS98ltEnmvUkuI7QrPEsDrNJnYMb2XDbWwd3BU5A4La2nRi+/s7WfktrktfCcWsSRpLHbwLLt2bdgLq7Rn5SpGCVY7t0N9qa3+hNqen2qaRLa38RmismKxyyuJWSRQfmQoIyANxA3EqEy253dxWRmanoq2UEs1vcPOttMLW6EkBhaKYhiAFbkqdj4JAb5TuVeMsvdJjtNFsNRjv4bn7VJLG0cSOPJKLGxDFgMn95zjI44JzxoazFdql3pil5pLFjd6pO7/O87FEYNz8wjd9g+9ktIwOHwKtz/yJWl/9hG8/9F21NN6CsY1FdSzQWOpWGi/YEuLK4htXnEdsr3UjTRI7GNyNwYF8KoIX5VDBsturNOmi6XpZhtLSf7fbtPc/aYFkLYmkjCKTygxH1Qq2WJ3cLtfMFiOz0Wxezsnvry+jur5m+zW1nZLcM6BtgbmReS4dQvJ+TPcZJtEsYdQ1E/2m8mk2MwgN7FArNK53bQiB8EHY5BLAbVznJCmfTIYtP8Q6nfQx3EUWkLLPClyoE8bhxHCWXpuWR4ywPHytwehq23/Ilap/2EbP/wBF3NK7GU9RsPsMkLRy+dbXMfnW8u3aXTcy8rk7SGVlI5GVOCRgl+k6fFfzztczvb2dtC09xMkYkZFyFXC5GSzsi9eN2TwCa3xDF/Yun3zxpLJYaI08SSKGQudQeMFlP3gBITg8EgZBGQcPUp0vrK2vWtPJuXkkjlkhgWKCQKEK7VXChxuIYAAY2HGSSRNvQVi7caBZyW2mnSr27urvUbgwQW1xapAWAIUPnzW4LHaD0yr8jbzHL4fil0a81LS7x7yLT2jS9LwiJV3kqrR5Yl13AjkK3K/L1261v+4+Jeg6YvCaZe2tljtvSUeaQepBlMrDPOGHA6DNWdNa0vVDNaWkH2C3We2+zQLGVzNHGUYjlxiTq5ZsqDu5bcrsdkRz+H4o3uLFLx31i0WR7i1MIEY8sFpFWTd8zKAScqFO1trN8u4g8PxSPb2L3jprF2sb29qIQYz5gDRq0m75WYEEYUqNy7mX5tvRyW0Nt4s1dtSiu7fXZrK+lntMRmGF3tZHZhIGYsCDwuARuGWO35jS7aGPxb4VutYiu4L+f7C1vDEI2imiBWOKUvuJThQShUklTyu4bVzuw7HAVp2Wm2zWQvtSupra0eRoYjBAJnkdQpb5SygABlySc5YYB+YruWdtZWGiaZKb7RoWvoXmuI9RtZZncCWSMKrJE3lrhOqMr5LHPC4reIbaCy0WK1tjMbeHWdQjiMylX2hbcDcCAQcDkEDnsKrmu7E2MLULGXTrxraVkYhUdXQna6OoZGGcHBVgcEAjPIB4qtWz4o/5C0H/AGDrH/0lirGqk7oT3CiiimBZsdQudOnaW2ZAWXY6SRrIjrkHDIwKsMgHkHBAPUCpBq16NXi1QT4vIpEljfYuEKY2gLjaAMABcYAAGMcV6l8KvBPh7xJ4XubzVtP+0XCXrRK/nSJhQiEDCsB1Y1w39k2X/Cz/AOx/I/0D+2fsvlb2/wBV523bnOenGc5rNTi5NdiuVpJnOQzS288c8EjxSxsHSRGKsrA5BBHQg1av9Vu9S8sXBhVI87I4IEhQE4ydiALk4GTjJAA7CtnxvpNlpGrxwWMHlRn7Rld7N927njXqT0VFH4eua5mrTUlcT00NO48Qapc+UXutjxSCYSQxrE7yDpI7qAzuMnDMScsxzyczS+KtYksprIXEMNtN9+K3tYoVzggkBFG0lWKkjBZTtORxXQeK/D+l6b4X068tLXy7iay06V38xjlpUuTIcE45MafTHGMmuGqY8slewO6NCLW7+HTzZJMgi2sgYxIZERs7kWQjeqnLZUEA7myPmOSx1u/02BobWZFXdvQvEjtE+AN8bMCY24HzKQflXngY9i0P4feF7z/hGvP0zf8AbdGe6uP9IlG+UfZ8Nw3H+sfgYHPsKzLfwT4ek1DwBE2n5TVrKWW9HnSfvWFurg/e+X5iTxis/aw2sXySPKdO1C50rUIL6zZEuYG3xs8auFbscMCMjqOODgjkVJb6rd2t7LdwmFXmz5iGBDE4JzgxkbCMgEDGAQCMYFTeI7SCw8UatZ2yeXb297NFEmSdqq5AGTyeB3rMrbR6mexd/ta9/tP+0fP/ANJ6Z2Lt2427NmNuzb8uzG3b8uMcUXGrXtzexXjT+XNDjyTAiwiLByNioAE5JbgDkk9STVKiiyC5dv8AVbvUvLFwYVSPOyOCBIUBOMnYgC5OBk4yQAOwp99rd/qUCw3UyMu7e5SJEaV8Eb5GUAyNyfmYk/M3PJy/w5aQX/ijSbO5TzLe4vYYpUyRuVnAIyORwe1ev3Xw+8Lx/wBq7NMx5Gs2VrH/AKRL8sUn2Xev3uc+a/PXnjoMROcYNJopRcjx2XW7+bTxZPMhi2qhYRIJHRcbUaQDeyjC4UkgbVwPlGCLW7+HTzZJMgi2sgYxIZERs7kWQjeqnLZUEA7myPmOX+I7SCw8UatZ2yeXb297NFEmSdqq5AGTyeB3rMq0k0TqXY9Vu4dIn0tDD9knkWWRTAhYsvQhyNwxz0P8TepyWGq3em+YLcwskmN8c8CTISM4OxwVyMnBxkAkdzVKum1vSbK08D+FtRgg2Xd99r+0Sb2O/ZIFXgnAwPTFJ2Wncauc/wDa5ze/bJH864Mnms8wEm9s5JYNkNk9c5z3q1Nrd/PrMmrvMn22Ri0jrEiq5Iw25ANpDDIYEYbJznJrPop2QrmmviDVI9Tj1GG68i5ijaKJoI1iESsGBCKoCp95j8oHLE9Tmob/AFW71LyxcGFUjzsjggSFATjJ2IAuTgZOMkADsKpUUWQXNC+1u/1KBYbqZGXdvcpEiNK+CN8jKAZG5PzMSfmbnk5Jdbv5tPFk8yGLaqFhEgkdFxtRpAN7KMLhSSBtXA+UYz6KLILmtL4l1ae1FvJcoyrCtujmCPzEiChNiybdyrtGCAQDubOdzZZp2v32lW01vai08ub/AFgmsoZi4ypwS6E4yqnHTIz1rMoo5UF2XbLVbvT7a8t7cwiO8j8qcPAjllznALAlecHjHIB6gYjsdQudOnaW2ZAWXY6SRrIjrkHDIwKsMgHkHBAPUCq1FFkFy7/a17/af9o+f/pPTOxdu3G3Zsxt2bfl2Y27flxjii41a9ub2K8afy5oceSYEWERYORsVAAnJLcAcknqSapUUWQXLt/qt3qXli4MKpHnZHBAkKAnGTsQBcnAycZIAHYVHJqN3LeRXhndbmFY1jlT5GQRqqpgjGCAq89eM9arUUWQXNmXxVrEllNZC4hhtpvvxW9rFCucEEgIo2kqxUkYLKdpyOKrRa3fw6ebJJkEW1kDGJDIiNnciyEb1U5bKggHc2R8xzn0UcqC7NCx1u/02BobWZFXdvQvEjtE+AN8bMCY24HzKQflXngYm07xLq2lJALK5SNoG3QymCNpIuclVcqWVSScqDtO5sg7jnJoosmF2aCa3fx6hJe+cjSyrsdZIkeNkGMIYyCm0bVwuMLtXAGBhn9rXv8Aaf8AaPn/AOk9M7F27cbdmzG3Zt+XZjbt+XGOKpUUWQXLtxqt3dXsV3MYWeHHloIEESAHOBGBsAySSMYJJJzk1ZufEmp3WoWd+8lut1ZsjQSQ2kMRUpt252qNwAVQAcgAYFZNFFkF2ad7r99f2Rs5RaR25kWVkt7KGDcyhgCTGgJwGbr60yXW7+bTxZPMhi2qhYRIJHRcbUaQDeyjC4UkgbVwPlGM+iiyC7NCLW7+HTzZJMgi2sgYxIZERs7kWQjeqnLZUEA7myPmOZoPEurW1nBaR3KeVbKVt98EbNBlixaNipKNubO5SG4Xn5Vxk0UWQXZdsNVu9N8wW5hZJMb454EmQkZwdjgrkZODjIBI7mi31a9tr2W8WfzJps+cZ0WYS5OTvVwQ/IDcg8gHqAapUUWQXLv9rXv9p/2j5/8ApPTOxdu3G3Zsxt2bfl2Y27flxjipv7f1H7b9q82Hd5fleV9nj8nZndt8rb5eN3zY243fN15rMoosguaeo+INU1a2htr268yGH/VosaoAMsVHygZC7mCjooYhcA4ouPEGqXPlF7rY8UgmEkMaxO8g6SO6gM7jJwzEnLMc8nOZRRyoLs2ZfFWsSWU1kLiGG2m+/Fb2sUK5wQSAijaSrFSRgsp2nI4qtFrd/Dp5skmQRbWQMYkMiI2dyLIRvVTlsqCAdzZHzHOfRRyoLs0LHW7/AE2BobWZFXdvQvEjtE+AN8bMCY24HzKQflXngYZYard6b5gtzCySY3xzwJMhIzg7HBXIycHGQCR3NUqKLILl231a9tr2W8WfzJps+cZ0WYS5OTvVwQ/IDcg8gHqAaP7Wvf7T/tHz/wDSemdi7duNuzZjbs2/Lsxt2/LjHFUqKLILl241a9ub2K8afy5oceSYEWERYORsVAAnJLcAcknqSamk1/UXvbS7EsMUlnIJYFgt44kRwQd2xVCk/KuSRkhQDwBWZRRZBc073X76/sjZyi0jtzIsrJb2UMG5lDAEmNATgM3X1om1++n0hNLkFp9kj+6q2UKsD8uTvCbsnYuTnJxzmsyiiyC7NCLW7+HTzZJMgi2sgYxIZERs7kWQjeqnLZUEA7myPmOSx1u/02BobWZFXdvQvEjtE+AN8bMCY24HzKQflXngYz6KLILk0F3PbxXMUT7UuYxFKMA7lDq+Pb5kU8elPsdQudOnaW2ZAWXY6SRrIjrkHDIwKsMgHkHBAPUCq1FOwF3+1r3+0/7R8/8A0npnYu3bjbs2Y27Nvy7Mbdvy4xxUd9qFzqM6y3LISq7ESONY0Rck4VFAVRkk8AZJJ6k1WopWQXJry7nv72e8uX8y4uJGllfAG5mOScDgcntVq+1u/wBSgWG6mRl3b3KRIjSvgjfIygGRuT8zEn5m55Oc+iiyC5oS63fzaeLJ5kMW1ULCJBI6LjajSAb2UYXCkkDauB8owRa3fw6ebJJkEW1kDGJDIiNnciyEb1U5bKggHc2R8xzn0UWQXNCx1u/02BobWZFXdvQvEjtE+AN8bMCY24HzKQflXngYZYard6b5gtzCySY3xzwJMhIzg7HBXIycHGQCR3NUqKLILj5ppbieSeeR5ZZGLvI7FmZickknqSaZRRTAKKKKAP/Z"},355:function(t,s){t.exports="data:image/jpeg;base64,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"},356:function(t,s){t.exports="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABaAOoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDjZtMn1PXtVETwxxwSSTTzTSBVij8wKWPc8sOFBY5wATVbVdJl0o2pe4t547qHz4pLdyylN7IDyBg5QnB5GcEA5A1r7/kCeI/+wzb/APoN1WE8t2dLghcP9iWaRoiUwvmFUD4bHJwI8jPHHrz6kb2X9dDkZWoooqyQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA2ZtTn0zXtVMSQyRzySQzwzRhllj8wMVPccqOVIYYyCDVO/1F77y41ghtbaLPlW0G7YhONx+YliTgZJJOAB0UAaT2FtNqOtX9/LNHZ2twVxCoLTSs7bYgSfkyqyHeQQNvQkgGtq9jYxWdhfaY1w1rcq6yfaCu6OZWy0fGMgI8R3Y+bdnjlViNtCncyaKKKskKKs3VjLaW9lPIyFbyEzxhSchRI8fPvmM/hitDTdO0/7FBearPNHb3Vw1rG8P/LEqELyuMEsFEi4ReW+b5lwNybQWMaitmOy0yzsLOXUxdyPfxmWJrZ1UQIHaPcysD5h3Ix2goMAfN83y5+pWMul6pd6fOyNLazPA5QkqWVipxnHGRQncLFaiiimAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAdTLDLeaf4ntbaN5Z4r+O9dEUkiGPzld/oDKmfYk9ASOfmsZYNPtryRkC3LOI4yTvKrgb8f3SSVB7lHHap9Rmlt/EN3PBI8Usd07pIjFWVg5III6EGqt3eXN/cvc3lxNcXD43SzOXZsDAyTyeABURTshs2vDn2nQfHWk/bfO06SG9h87zswmNCw3bs4wCpOc8YPpTxDfW/gTU4LmO4iij1e3QRyKyqsoinEgwejAbM9/u57VhXd5c39y9zeXE1xcPjdLM5dmwMDJPJ4AFTyavqcts1tJqN29u0aRGJp2KlEOUXGcYUkkDoM8U3FvULnRaxqWvavpGi2Z1K+uVm0yW4nikumKyCOe4YswJwxCxjrz8oA7VWvdX1OXwDpttJqN29u17cxGJp2KlES2KLjOMKSSB0GeKwk1K+i0+TT4724SylbfJbLKwjduOSucE8D8h6UPqV9Lp8enyXtw9lE2+O2aVjGjc8hc4B5P5n1pKIXOm0/Vr7RNK0aDTLdL2O+Z5pIZUaQfag7Rqse0gxyKnlsChV8yAkkbQOd1e3trTWr+2spvOtIbiSOGXcG3oGIVsjg5GDkcVHbalfWUFxBaXtxBFcrsnjilZVlXBGGAPzDBPX1NVqajZ3BsKKKKoQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAad5aT33iO8t7dN8j3EuBkAAAkkkngAAEknAABJwBT4vDWrT6gbGC2Sa58lp0WKeNxMi5yYyGIkIwwwhJyrDGQcadheW1tr+v29xDby/bVkgjS6dkhLidJAHZWUqDsIByACRuIXJGnaTRPdQ6ao0ayMFpqDvHb3Y8oPNb+Uo86SVldiQnCnCg8nO4LlzNIqyucZfafc6dOsVyqAsu9HjkWRHXJGVdSVYZBHBOCCOoNQwytBPHMgQsjBgHQOpIOeVIII9iMGtm6jA8D6YfOtywv7lzEs6GRVZIQpKA7gCY36jsPUZw60TuSzttReOLWvHcSWliscCusKizixEFuo4hs+X5TsdhkYJJyeQDVVZbbTrCOQaXDd6PNZNGLs24d1vXgYHL8FSrkgIeNihwpYhyareW3neI9TS4hki1vd9ljRwZF3XCTHzF6ptCFTnqSNu5csC00+z0u/tr1dRtJ9He3gOoQ/aUMjhkR5YfL4Zju4UqCFO0llZGK59P67FnJ1d/sq7+zWVyRCIb2RooXM6AblKhg3PyY3KfmxwQenNFtp32nTL69+2WkX2Ty/wBxLLtlm3HH7tcfNjqfQVftli1Pw9b6et1b28tpdzXEpuZAimORIl3L/eKmI5UZY7htDc40bJSJNI8L3F9rF/p900MEllHcCVXu4oyJY45CANzfMNyDcRkAZJIHNR6J4dfUfFVro9xNbhWmjWZ4ryEjYzKDsfcVdsNwq5Oe3BrTup4I/iRrchubcxXTX6wzJMrRsZopRH84O0Al1yScLk5xg4raHZQ6b410BZL+0LR3EU903nx+VBtkLEebu2N8iq2QerbeoxUXdn6DsgL2dzrFvZalZ6dA8Hm4a1nQQSfuwYImdDgjeMNIX3Yc7nG3IPENld21k41yxhsNXjuBHDHFbpB5sOG3sUjAUgME2uAN25xlgvy5+izRaXr+bqREMazRLMjCRYpTGypIGXOQrlW3Lk/LlcnFWr9pLHw2NLvrqG4uBcJLapDcpcLbRASeYAykqu9mQ7Qcny8sB8pJazQdDn6KKK0JCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA2bjSNT1XWtT/s7Tru88u4fzPs8DSbMscZ2g4zg/lWZd2dzYXL215bzW9wmN0UyFGXIyMg8jgg101jBHcX3i+KW6htUa2bM0wcqv8ApcJ52Kze3APWprC9jsNLuxYXvn32lWUssOow702edNBGUj3ANgK8pyQMNK5ABAY5qTS+4po5rbe6HqeJ7XybuHrDeWytjI/ijkBHQ5GR6GtO91HXEsibvS7SC3njXEh0a3iysgbaVcRgjIVsEH+EkdKm0y/2eG4bi+i+3W+k6rbNBbSt8uyQSvLHyCMOYU6ggc4HJzDqU94uizxRah/aWm3V6ty87b98c+1+JFY/K7KxJPzBinys2xqe7Az5dC1iDTxqE2lX0dkVVxcvbuIyrY2ncRjByMeuRT4vDmuTeT5WjajJ58fmxbLVz5icfMvHI+ZeRx8w9amuf+RK0v8A7CN5/wCi7atOxuMeDJr0wzG7s45rGCYLwsUrIT7EKHuFYkZBuYvVdo5OwrI5OiiirEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB0f2yxtrnxRDdyXCy3atBAIoVddwnWT5iWGBmMDgH7xPbBzNJ1CKwnnW5ge4s7mFoLiFJBGzrkMuGwcFXVG6c7cHgkVHq/8AyGr/AP6+JP8A0I1TqYr3Rt6nR22raPYT6faw29xcafHfx3V89wibrpUPyr5WSFCq0gxvO7fk44AqyXumWdheRaYbuR7+MRSrcoqiBA6ybVZSfMO5FG4hBgH5fm+XGoo5UFzcurnR38K2ljDdXzXsE0lwVe0RYy0ixKy7hITgeWSDt5yOBWhZ6xocOmW2nSXGoi0aynS8jS0Q7rhyrK6kyg4Bjh9M+SOPnbHJ0UcqC4UUUVQgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/2Q=="},357:function(t,s){t.exports="data:image/jpeg;base64,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"},358:function(t,s,a){t.exports=a.p+"assets/img/docker06.f94651f1.jpg"},359:function(t,s,a){t.exports=a.p+"assets/img/DB00.3fdfd46b.jpg"},360:function(t,s,a){t.exports=a.p+"assets/img/DB01.e9d2dca0.jpg"},361:function(t,s,a){t.exports=a.p+"assets/img/DB02.deae098c.jpg"},362:function(t,s,a){t.exports=a.p+"assets/img/DB03.67543a21.jpg"},363:function(t,s,a){t.exports=a.p+"assets/img/DB04.195ebfdf.jpg"},512:function(t,s,a){"use strict";a.r(s);var e=a(10),r=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"リレーショナルdbを触ってみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#リレーショナルdbを触ってみる"}},[t._v("#")]),t._v(" リレーショナルDBを触ってみる")]),t._v(" "),s("hr"),t._v(" "),s("h2",{attrs:{id:"事前準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#事前準備"}},[t._v("#")]),t._v(" <事前準備>")]),t._v(" "),s("ul",[s("li",[t._v("Dockerがインストールされていること")]),t._v(" "),s("li",[t._v("PowerShellが利用できる状態であること")]),t._v(" "),s("li",[t._v("インターネットに接続できていること")])]),t._v(" "),s("h1",{attrs:{id:"postgresqlについて"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#postgresqlについて"}},[t._v("#")]),t._v(" PostgreSQLについて")]),t._v(" "),s("h3",{attrs:{id:"呼び方"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#呼び方"}},[t._v("#")]),t._v(" 呼び方")]),t._v(" "),s("p",[t._v("「ポストグレス キューエル」らしいのですが、この呼び方している人は少ないように感じます。"),s("br"),t._v("\nよく聞くのは「ポスグレ」、「ポストグレス」です。")]),t._v(" "),s("h3",{attrs:{id:"日本postgresqlユーザ会"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#日本postgresqlユーザ会"}},[t._v("#")]),t._v(" 日本PostgreSQLユーザ会")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://www.postgresql.jp/",target:"_blank",rel:"noopener noreferrer"}},[t._v("日本PostgreSQLユーザ会"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("日本国内でPostgreSQLデータベースを使用するユーザーのコミュニティおよび組織です。")]),t._v(" "),s("li",[t._v("このユーザ会は、PostgreSQLに関心を持つ人々が情報共有や交流を行い、技術的な知識を向上させることを目的としています。")])]),t._v(" "),s("h3",{attrs:{id:"バージョン体系-超抜粋"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#バージョン体系-超抜粋"}},[t._v("#")]),t._v(" バージョン体系(超抜粋)")]),t._v(" "),s("p",[t._v("※8.0よりも前のバージョン(6.0や7.0)については省略します。")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"right"}},[t._v("メジャーバージョン")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("リリース日")]),t._v(" "),s("th",{staticStyle:{"text-align":"right"}},[t._v("最新マイナー版")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("最新版リリース日")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("サポート期限")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("8.0")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2005/11/8")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("8.0.26")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/4")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("8.1.x~8.4.x")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("9.0")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/9/20")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("9.0.23")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2015/10/8")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2010/10/8")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("9.1.x~9.6.x")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("省略")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("10")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2017/10/5")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("10.23")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/11/10")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/11/10")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("11")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2018/10/18")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("11.22")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/11/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/11/9")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("12")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2019/10/3")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("12.19")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/11/14")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("13")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2020/9/24")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("13.15")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2025/11/13")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("14")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2021/9/30")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("14.12")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2026/11/12")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("15")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2022/10/13")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("15.7")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2027/11/11")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"right"}},[t._v("16")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2023/9/14")]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},[t._v("16.3")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2024/5/9")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("2028/11/9")])])])]),t._v(" "),s("p",[t._v("※バージョン体系がPostgreSQL10より変更されました。")]),t._v(" "),s("p",[t._v("Postgresql 10以前 ⇒  "),s("span",{staticStyle:{"font-size":"200%",color:"red"}},[s("strong",[t._v("9.0")])]),t._v(" ."),s("span",{staticStyle:{"font-size":"200%",color:"blue"}},[t._v("23")]),s("br"),t._v(" "),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("9.0の部分がメジャーバージョン")]),t._v("で"),s("span",{staticStyle:{"font-size":"100%",color:"blue"}},[t._v("23がマイナーバージョン")]),t._v("となります。"),s("br"),t._v(" "),s("br"),t._v("\nPostgresql 10以降 ⇒  "),s("span",{staticStyle:{"font-size":"200%",color:"red"}},[s("strong",[t._v("10")])]),t._v(" ."),s("span",{staticStyle:{"font-size":"200%",color:"blue"}},[t._v("22")]),s("br"),t._v(" "),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("10の部分がメジャーバージョン")]),t._v("で"),s("span",{staticStyle:{"font-size":"100%",color:"blue"}},[t._v("22がマイナーバージョン")]),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("PostgreSQLは、"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("オープンソースのリレーショナルデータベース")]),t._v("管理システムです。"),s("br"),t._v("\nそのライセンスは、PostgreSQL Global Development Group(PGDG)によって開発されたもので以下の2つのライセンスオプションが提供されています。")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("PostgreSQLライセンス(PostgreSQL License\nこれは、PostgreSQLプロジェクトが採用している独自のライセンスです。\nこのライセンスは、商用利用や非商用利用、修正や再配布を含むあらゆる使用形態に対して、\n自由かつ"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("無償で利用することを許可しています。")]),t._v("また、ソースコードの利用や変更、派生物の作成、\nバイナリ形式での再配布なども可能です。そのため、PostgreSQLを自由に使用し、変更や拡張を行い、\nプロジェクトや製品に組み込むことができます。")])]),t._v(" "),s("li",[s("p",[t._v("MIT(マサチューセッツ工科大学)ライセンス\nPostgreSQLプロジェクトは、PostgreSQLライセンスに加えて、一部のコンポーネントについてはMITライセンスも採用しています。\nMITライセンスは、商用利用や非商用利用、修正や再配布を含むあらゆる使用形態に対して、"),s("span",{staticStyle:{"font-size":"100%",color:"red"}},[t._v("自由かつ無償で利用することを許可")]),t._v("しています。\nただし、著作権表示および本許諾表示をソフトウェアのすべての複製または重要な部分に記載しなければならず、作者または著作権者は、ソフトウェアに関してなんら責任を負わない。")])])]),t._v(" "),s("h1",{attrs:{id:"ハンズオン研修-【データベースに触れてみよう】"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ハンズオン研修-【データベースに触れてみよう】"}},[t._v("#")]),t._v(" ハンズオン研修 【データベースに触れてみよう】")]),t._v(" "),s("h2",{attrs:{id:"_0-postgresql環境準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-postgresql環境準備"}},[t._v("#")]),t._v(" 0. PostgreSQL環境準備")]),t._v(" "),s("p",[t._v("本演習にて入力するコマンドはおおきく分けて以下の3つにわかれているため、入力する場所には注意する必要があります。")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("1.Linux環境 【 $ 表示のプロンプト】"),s("br"),t._v("\n →  ★0-1、★0-2 、★0-3 での入力先となります。")])]),t._v(" "),s("li",[s("p",[t._v("2.コンテナ環境 【 root@xxx から始まるプロンプト】"),s("br"),t._v("\n →  ★0-4 ★1-1、及び★A-1、★A-2、★B-1先頭行、★B-2 での入力先となります。")])]),t._v(" "),s("li",[s("p",[t._v("3.PostgreSQL環境 【 postgres=# から始まるプロンプト】"),s("br"),t._v("\n →  ★1-2~演習問題まで及び★B-1drop文 の入力先となります。")])])]),t._v(" "),s("p",[t._v("※出力イメージと異なるような結果が表示された際には入力場所をご確認ください。")]),t._v(" "),s("h3",{attrs:{id:"★0-1-postgresql環境のダウンロード"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-1-postgresql環境のダウンロード"}},[t._v("#")]),t._v(" ★0-1 PostgreSQL環境のダウンロード")]),t._v(" "),s("p",[t._v("以下のコマンドを入力し、PostgreSQL環境をダウンロードします。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker pull postgres\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ\n"),s("img",{attrs:{src:a(353),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-2-コンテナ起動"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-2-コンテナ起動"}},[t._v("#")]),t._v(" ★0-2 コンテナ起動")]),t._v(" "),s("p",[t._v("以下のコマンドを入力し、コンテナ(PostgreSQLも起動します)環境を起動させます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker run --name some-postgres -e POSTGRES_PASSWORD=postgres -d postgres\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(354),alt:""}}),s("br"),t._v("\n※正常に起動すると上記のような64文字の文字列(値は各自で異なります)が表示されます。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-3-コンテナ接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-3-コンテナ接続"}},[t._v("#")]),t._v(" ★0-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("docker exec -it some-postgres /bin/bash\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(355),alt:""}}),s("br"),t._v("\n ※接続に成功すると上記のような最後に「#」が付いた状態で表示されます。"),s("br"),t._v("\n ※「root@」以降の文字列は各自で異なります。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★0-4-ロケールの確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★0-4-ロケールの確認"}},[t._v("#")]),t._v(" ★0-4 ロケールの確認")]),t._v(" "),s("p",[t._v("本講義で使用する言語「en_US.utf8」が表示されることを確認します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("locale -a\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(356),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_1-データベースへ接続してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1-データベースへ接続してみよう"}},[t._v("#")]),t._v(" 1. データベースへ接続してみよう")]),t._v(" "),s("h3",{attrs:{id:"★1-1-db接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★1-1-db接続"}},[t._v("#")]),t._v(" ★1-1 DB接続")]),t._v(" "),s("p",[t._v("PostgreSQLデータベースに接続し、SQLを発行するためには「psql」というツールを利用します。"),s("br"),t._v("\n OSユーザ「postgres」にスイッチ後、データベースに接続してみます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("su - postgres\npsql -h localhost -p 5432 -d postgres -U postgres\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("br"),t._v(" "),s("img",{attrs:{src:a(357),alt:""}}),s("br"),t._v("\n※プロンプトが「postgres=#」に変更されることを確認します。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★1-2-データベース確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★1-2-データベース確認"}},[t._v("#")]),t._v(" ★1-2 データベース確認")]),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("\\l+\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◎出力イメージ"),s("br"),t._v(" "),s("img",{attrs:{src:a(358),alt:""}})]),t._v(" "),s("p",[t._v("現時点ではデータベースが3つ表示されています。"),s("br"),t._v("\n イメージ図としては以下(グレーの四角がデータベース)です。"),s("br"),t._v(" "),s("img",{attrs:{src:a(359),alt:""}})]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_2-ユーザ-ロール-を作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-ユーザ-ロール-を作成してみよう"}},[t._v("#")]),t._v(" 2. ユーザ(ロール)を作成してみよう")]),t._v(" "),s("h3",{attrs:{id:"★2-1-ユーザ作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★2-1-ユーザ作成"}},[t._v("#")]),t._v(" ★2-1 ユーザ作成")]),t._v(" "),s("p",[t._v("デフォルトで作成されているユーザ「postgres」以外に以下の3ユーザを作成します。")]),t._v(" "),s("ul",[s("li",[t._v("ユーザ「user01」を作成します")]),t._v(" "),s("li",[t._v("ユーザ「user02」を作成します")]),t._v(" "),s("li",[t._v("スーパーユーザ「suser01」を作成します")])]),t._v(" "),s("p",[t._v("作成後は以下のようなイメージ(緑色の丸枠がユーザ)となります。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(360),alt:""}})]),t._v(" "),s("p",[t._v("「user01」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create role user01 with login password 'user01'; \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("「user02」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" create user user02 with password 'user02';\t\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("「suser01」の作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" create role suser01 with superuser login password 'suser01';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※全て「CREATE ROLE」と表示されることを確認します。"),s("br"),t._v("\n※作成後、スーパーユーザ(suser01)でログインしなおしましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" \\connect - suser01 \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※PostgreSQLにおいて「ユーザ」と「ロール」は厳密には異なるため、上記のように2種類のコマンドが存在しますが、"),s("br"),t._v("\n 今回は「ユーザ」と「ロール」は同じものであると考えましょう。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_3-データベースを作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-データベースを作成してみよう"}},[t._v("#")]),t._v(" 3. データベースを作成してみよう")]),t._v(" "),s("p",[t._v("「create database」文を利用しデータベースを作成します。"),s("br"),t._v("\n ここでは、データベース名を「db_world」と指定し、template0を使用してデータベースを作成しています。"),s("br"),t._v("\n ※「public」スキーマも同時に作成されます。 (スキーマは後述しますが、「public」スキーマは全ユーザが自由に使えるスキーマです)")]),t._v(" "),s("p",[s("img",{attrs:{src:a(361),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★3-1-データベースの作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★3-1-データベースの作成"}},[t._v("#")]),t._v(" ★3-1 データベースの作成")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create database db_world owner = suser01 template = template0 encoding = 'UTF8' lc_collate = 'en_US.utf8' lc_ctype = 'en_US.utf8';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("※「CREATE DATABASE」と表示されることを確認します。")]),t._v(" "),s("br"),t._v(" "),s("h3",{attrs:{id:"★3-2-データベースへの接続"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★3-2-データベースへの接続"}},[t._v("#")]),t._v(" ★3-2 データベースへの接続")]),t._v(" "),s("p",[t._v("次STEPの準備として、作成したデータベース(db_world)にスーパーユーザ(suser01)で接続します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("\\connect db_world suser01 \n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("br"),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("データベース内にスキーマ(青色の四角枠)を作成し、ユーザに利用権限(オレンジの矢印)を付与します。"),s("br"),t._v("\n スキーマとは、テーブルやインデックスといったオブジェクトを配置する領域です。"),s("br"),t._v("\n ※「public」スキーマはDB作成時に作られており、テーブル等の作成時にスキーマ名を省略するとこの領域に配置されます。")]),t._v(" "),s("p",[s("img",{attrs:{src:a(362),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★4-1-スキーマの作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★4-1-スキーマの作成"}},[t._v("#")]),t._v(" ★4-1 スキーマの作成")]),t._v(" "),s("p",[t._v("以下の2つのスキーマを作成してみましょう。\n 以下の例では2つのスキーマの所有者に「suser01」を指定しています。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("create schema sch_jpn authorization suser01;\t\ncreate schema sch_usa authorization suser01;\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("p",[t._v("※「CREATE SCHEMA」と表示されることを確認してください。")]),t._v(" "),s("h3",{attrs:{id:"★4-2-権限付与"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★4-2-権限付与"}},[t._v("#")]),t._v(" ★4-2 権限付与")]),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("grant all privileges on database db_world to user01;\t\ngrant all privileges on schema sch_jpn to user01;\t\ngrant all privileges on schema sch_jpn to user02;\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("p",[t._v("※「GRANT」と表示されることを確認してください。"),s("br"),t._v("\n  この権限の追加により")]),t._v(" "),s("ul",[s("li",[t._v("「user01」は「db_world」データベース、「sch_jpn」スキーマ内への全権限を有します。")]),t._v(" "),s("li",[t._v("「user02」は「sch_jpn」スキーマ内への全権限を有します。")])]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_5-テーブルを作成してみよう"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-テーブルを作成してみよう"}},[t._v("#")]),t._v(" 5. テーブルを作成してみよう")]),t._v(" "),s("p",[t._v("上記にて作成した「sch_jpn」スキーマ内にテーブル(オレンジ色の四角枠)を作成します。"),s("br"),t._v(" "),s("img",{attrs:{src:a(363),alt:""}})]),t._v(" "),s("h3",{attrs:{id:"★5-1-テーブル作成"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★5-1-テーブル作成"}},[t._v("#")]),t._v(" ★5-1 テーブル作成")]),t._v(" "),s("p",[t._v("「user01」で接続し、テーブルを作成します。"),s("br"),t._v("\n 以下ではテーブル作成時に主キー(PRIMARY KEY)を作成しています。"),s("br"),t._v("\n 主キーを作成した列には同じ値とnullを挿入できなくなります。"),s("br"),t._v("\n ※スキーマ(sch_jpn)は必ず指定してください。省略してしまうと「public」スキーマに作成されます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("\\connect db_world user01\ncreate table sch_jpn.tbl_region(id_reg serial,reg_name varchar(40), PRIMARY KEY (id_reg));\ncreate table sch_jpn.tbl_pref(id_pref serial,pref_name varchar(40),id_reg int, PRIMARY KEY (id_pref));\ncreate table sch_jpn.tbl_food(id_fd serial,fd_name varchar(40),price int, PRIMARY KEY (id_fd));\ncreate table sch_jpn.tbl_proper(id_user serial,username varchar(40),id_pref int,id_fd int, PRIMARY KEY (id_user));\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("p",[t._v("※「CREATE TABLE」と表示されることを確認してください。")]),t._v(" "),s("p",[t._v("※テーブルを削除するコマンドは「drop table <テーブル名>」となります。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_6-テーブルにデータを登録してみよう-insert-into-values"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-テーブルにデータを登録してみよう-insert-into-values"}},[t._v("#")]),t._v(" 6. テーブルにデータを登録してみよう(insert into ~ values~)")]),t._v(" "),s("h3",{attrs:{id:"★6-1-データの登録"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★6-1-データの登録"}},[t._v("#")]),t._v(" ★6-1 データの登録")]),t._v(" "),s("p",[t._v("上記で作成したテーブルにデータを登録します。"),s("br"),t._v("\n データを追加するコマンドはinsert文となります。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("insert into sch_jpn.tbl_region(reg_name) values ('hokkaido');\ninsert into sch_jpn.tbl_region(reg_name) values ('tohoku');\ninsert into sch_jpn.tbl_region(reg_name) values ('kanto');\ninsert into sch_jpn.tbl_region(reg_name) values ('chubu');\ninsert into sch_jpn.tbl_region(reg_name) values ('kinki');\ninsert into sch_jpn.tbl_region(reg_name) values ('shikoku');\ninsert into sch_jpn.tbl_region(reg_name) values ('kyushu');\n\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('hokkaido',1);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('yamanashi',4);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('osaka',5);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('nagasaki',7);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('tokyo',3);\ninsert into sch_jpn.tbl_pref(pref_name,id_reg) values ('chiba',3);\n\ninsert into sch_jpn.tbl_food(fd_name,price) values ('hamburger',500);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('ramen',1500);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('takoyaki',800);\ninsert into sch_jpn.tbl_food(fd_name,price) values ('gyoza',350);\n\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('suzuki',1,1);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('satou',2,4);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('tanaka',3,2);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('ito',4,3);\ninsert into sch_jpn.tbl_proper(username,id_pref,id_fd) values ('watanabe',5,4);\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("p",[t._v("※全て「INSERT 0 1」と表示されることを確認してください。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_7-データを参照してみよう-select-from"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_7-データを参照してみよう-select-from"}},[t._v("#")]),t._v(" 7. データを参照してみよう(select ~ from ~)")]),t._v(" "),s("h3",{attrs:{id:"★7-1-データの参照"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-1-データの参照"}},[t._v("#")]),t._v(" ★7-1 データの参照")]),t._v(" "),s("p",[t._v("テーブルに登録されているデータを参照します。データはSELECT文にて参照します。"),s("br"),t._v("\n ※「*」を指定するとテーブルのカラム(列)全てを指定したことと同義になります。")]),t._v(" "),s("p",[t._v("〇7-1-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_region;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_reg | reg_name\n--------+----------\n 1 | hokkaido\n 2 | tohoku\n 3 | kanto\n 4 | chubu\n 5 | kinki\n 6 | shikoku\n 7 | kyushu\n(7 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-2. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_pref;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-2. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_pref | pref_name | id_reg\n---------+-----------+--------\n 1 | hokkaido | 1\n 2 | yamanashi | 4\n 3 | osaka | 5\n 4 | nagasaki | 7\n 5 | tokyo | 3\n 6 | chiba | 3\n(6 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-3. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_food;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-3. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_fd | fd_name | price\n-------+-----------+-------\n 1 | hamburger | 500\n 2 | ramen | 1500\n 3 | takoyaki | 800\n 4 | gyoza | 350\n(4 rows)\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("br"),t._v(" "),s("p",[t._v("〇7-1-4. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_proper;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-1-4. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_user | username | id_pref | id_fd\n---------+----------+---------+-------\n 1 | suzuki | 1 | 1\n 2 | satou | 2 | 4\n 3 | tanaka | 3 | 2\n 4 | ito | 4 | 3\n 5 | watanabe | 5 | 4\n(5 rows)\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("hr"),t._v(" "),s("h3",{attrs:{id:"★7-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-2"}},[t._v("#")]),t._v(" ★7-2")]),t._v(" "),s("p",[t._v("テーブルから条件を指定 (where句)してデータを抽出します。")]),t._v(" "),s("p",[t._v("〇7-2-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select * from sch_jpn.tbl_pref where id_reg=3;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("●7-2-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" id_pref | pref_name | id_reg\n---------+-----------+--------\n 5 | tokyo | 3\n 6 | chiba | 3\n(2 rows)\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("br"),t._v(" "),s("h3",{attrs:{id:"★7-3-複数のテーブルからのデータ参照"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★7-3-複数のテーブルからのデータ参照"}},[t._v("#")]),t._v(" ★7-3 複数のテーブルからのデータ参照")]),t._v(" "),s("p",[t._v("複数のテーブルを結合しデータを抽出します。")]),t._v(" "),s("p",[t._v("〇7-3-1. 入力コマンド")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("select\n pp.username,\n pr.pref_name,\n rg.reg_name,\n fd.fd_name\nfrom\n sch_jpn.tbl_region rg,\n sch_jpn.tbl_pref pr,\n sch_jpn.tbl_food fd,\n sch_jpn.tbl_proper pp\nwhere\n pp.id_pref=pr.id_pref and\n pp.id_fd=fd.id_fd and\n pr.id_reg=rg.id_reg\nORDER BY\npp.username\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("p",[t._v("●7-3-1. 出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v(" username | pref_name | reg_name | fd_name\n----------+-----------+----------+-----------\n ito | nagasaki | kyushu | takoyaki\n satou | yamanashi | chubu | gyoza\n suzuki | hokkaido | hokkaido | hamburger\n tanaka | osaka | kinki | ramen\n watanabe | tokyo | kanto | gyoza\n(5 rows)\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("h2",{attrs:{id:"_8-データを更新してみよう-update-set"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_8-データを更新してみよう-update-set"}},[t._v("#")]),t._v(" 8. データを更新してみよう(update ~ set ~)")]),t._v(" "),s("h3",{attrs:{id:"★8-1-データの更新"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★8-1-データの更新"}},[t._v("#")]),t._v(" ★8-1 データの更新")]),t._v(" "),s("p",[t._v("データを変更するには「update」文を使用します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("update sch_jpn.tbl_pref set id_reg=4 where id_pref=2;\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◆変更されているか確認してみよう。"),s("br"),t._v("\n  ⇒ 上記で使用したSELECT文を使い「tbl_pref」を参照してください。")]),t._v(" "),s("br"),t._v(" "),s("h2",{attrs:{id:"_9-データを削除してみよう-delete-from"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_9-データを削除してみよう-delete-from"}},[t._v("#")]),t._v(" 9. データを削除してみよう(delete from ~)")]),t._v(" "),s("h3",{attrs:{id:"★9-1-データの削除"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★9-1-データの削除"}},[t._v("#")]),t._v(" ★9-1 データの削除")]),t._v(" "),s("p",[t._v("データを削除するには「delete」文を使用します。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("delete from sch_jpn.tbl_food where fd_name='ramen';\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("◆削除されているか確認してみよう。"),s("br"),t._v("\n  ⇒ 上記で使用したSELECT文を使い「tbl_food」を参照してください。")]),t._v(" "),s("br"),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("h1",{attrs:{id:"演習問題"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#演習問題"}},[t._v("#")]),t._v(" 演習問題")]),t._v(" "),s("h3",{attrs:{id:"問1-自分の好きな食べ物を「tbl-food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl-proper」テーブルに登録してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問1-自分の好きな食べ物を「tbl-food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl-proper」テーブルに登録してください"}},[t._v("#")]),t._v(" 問1)自分の好きな食べ物を「tbl_food」テーブルに登録し、そのデータを使って自分の情報をプロパー「tbl_proper」テーブルに登録してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: insert into ~ values ~ \n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問2-問1で追加したデータを参照してください-2テーブルを確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問2-問1で追加したデータを参照してください-2テーブルを確認してください"}},[t._v("#")]),t._v(" 問2)問1で追加したデータを参照してください(2テーブルを確認してください)")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: select ~ from ~ where ~\n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問3-「tbl-proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問3-「tbl-proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください"}},[t._v("#")]),t._v(" 問3)「tbl_proper」の中に存在している「tanaka」さんを「ikeda」さんに変更し、確認してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: update ~ set ~\n")])])]),s("br"),t._v(" "),s("h3",{attrs:{id:"問4-「tbl-food」から「takoyaki」のみを削除し、確認してください"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#問4-「tbl-food」から「takoyaki」のみを削除し、確認してください"}},[t._v("#")]),t._v(" 問4)「tbl_food」から「takoyaki」のみを削除し、確認してください")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",[s("code",[t._v("hint: delete from ~ where ~\n")])])]),s("br"),t._v(" "),s("p",[t._v("▼▼▼▼▼▼▼▼▼▼▼▼▼以降、時間がある人向け▼▼▼▼▼▼▼▼▼▼▼▼▼")]),t._v(" "),s("h2",{attrs:{id:"a-バックアップ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#a-バックアップ"}},[t._v("#")]),t._v(" A.バックアップ")]),t._v(" "),s("h3",{attrs:{id:"★a-1-データベース-バックアップ準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-1-データベース-バックアップ準備"}},[t._v("#")]),t._v(" ★A-1. データベース バックアップ準備")]),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("su - postgres\t\ncd /tmp\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("br"),t._v(" "),s("h3",{attrs:{id:"★a-2-データベースのバックアップ"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-データベースのバックアップ"}},[t._v("#")]),t._v(" ★A-2. データベースのバックアップ")]),t._v(" "),s("p",[t._v("ここでは、2通りのバックアップを試してみます。")]),t._v(" "),s("h4",{attrs:{id:"★a-2-1-plain形式での出力-人間が見て分かるような結果が出力されます"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-1-plain形式での出力-人間が見て分かるような結果が出力されます"}},[t._v("#")]),t._v(" ★A-2-1 plain形式での出力\t(人間が見て分かるような結果が出力されます)")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pg_dump -U suser01 -d db_world --format=p --create --file db_world_db.sql\n\ncat db_world_db.sql\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("h4",{attrs:{id:"★a-2-2-バイナリ形式での出力"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★a-2-2-バイナリ形式での出力"}},[t._v("#")]),t._v(" ★A-2-2 バイナリ形式での出力")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pg_dump -U suser01 -d db_world --format=c --create --file db_world_db.custom\n\nls -l\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("◎出力結果")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("total 20\n-rw-r--r-- 1 postgres postgres 9710 Jul 10 12:00 db_world_db.custom\n-rw-r--r-- 1 postgres postgres 7561 Jul 10 11:59 db_world_db.sql\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("br"),t._v(" "),s("h2",{attrs:{id:"b-リストア"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#b-リストア"}},[t._v("#")]),t._v(" B.リストア")]),t._v(" "),s("h3",{attrs:{id:"★b-1-データベース削除"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★b-1-データベース削除"}},[t._v("#")]),t._v(" ★B-1. データベース削除")]),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("psql -h localhost -p 5432 -d postgres -U postgres\n\n\\l\ndrop database db_world;\n\\l\n\n\\q\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("br"),t._v(" "),s("h3",{attrs:{id:"★b-2-データベースをリストア"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#★b-2-データベースをリストア"}},[t._v("#")]),t._v(" ★B-2. データベースをリストア")]),t._v(" "),s("p",[t._v("データベースが削除されているため、「db_world」データベースを作成し、"),s("br"),t._v("\n 「db_world_db.custom」を使ってリストアします。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("createdb --template=template0 --encoding='UTF8' --lc-collate='en_US.utf8' --lc-ctype='en_US.utf8' db_world\n\npg_restore -v -c -d db_world /tmp/db_world_db.custom\n\npsql -h localhost -p 5432 -d postgres -U postgres\n\\l\n\\q\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("p",[t._v("この操作にてバックアップ取得時の状態へ復旧します。")]),t._v(" "),s("p",[t._v("※この他にもバックアップ取得時点ではなく、障害発生直前にまでリカバリできる"),s("br"),t._v("\n  バックアップコマンド(pg_basebackup)も存在しますがここでは割愛します。")]),t._v(" "),s("br"),t._v(" "),s("hr"),t._v(" "),s("h1",{attrs:{id:"postgresql環境の停止について"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#postgresql環境の停止について"}},[t._v("#")]),t._v(" PostgreSQL環境の停止について")]),t._v(" "),s("p",[t._v("Docker runコマンド実行の際に -d optionをつけている(back ground実行)ため、"),s("br"),t._v("\n★0-3 コンテナ接続"),s("br"),t._v("\nで 開いていた bash で exit してもPostgreSQL(コンテナ)環境は終了しません。")]),t._v(" "),s("p",[t._v("演習が終わりましたらstopコマンドにて停止させてください。"),s("br"),t._v("\npsコマンドにてstatus列 が Exited になっていれば停止となります。"),s("br"),t._v("\n※再度利用する際には docker start some-postgres にて起動可能させます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("docker stop some-postgres\n\ndocker ps -a\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("以上")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/12.a638d493.js b/assets/js/12.ecb969fe.js similarity index 99% rename from assets/js/12.a638d493.js rename to assets/js/12.ecb969fe.js index 7caa2a86..af57db92 100644 --- a/assets/js/12.a638d493.js +++ b/assets/js/12.ecb969fe.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{364:function(s,t,a){s.exports=a.p+"assets/img/git_choosing_editor.65e3a5a8.jpg"},365:function(s,t,a){s.exports=a.p+"assets/img/git_bash_win.e14b57ed.png"},366:function(s,t,a){s.exports=a.p+"assets/img/git_stage.drawio.0ed2f694.svg"},367:function(s,t,a){s.exports=a.p+"assets/img/git_first_commit.drawio.c10d455e.svg"},368:function(s,t,a){s.exports=a.p+"assets/img/git_second_commit.drawio.fc14a864.svg"},369:function(s,t,a){s.exports=a.p+"assets/img/git_branch.drawio.b856c449.svg"},370:function(s,t,a){s.exports=a.p+"assets/img/git_checkout_branch.drawio.39309ca7.svg"},371:function(s,t,a){s.exports=a.p+"assets/img/git_fix_branch_commit.drawio.1c6afc6b.svg"},372:function(s,t,a){s.exports=a.p+"assets/img/git_return_master_branch.drawio.ec0170af.svg"},373:function(s,t,a){s.exports=a.p+"assets/img/git_return_master_branch_commit.drawio.bf34ae5a.svg"},374:function(s,t,a){s.exports=a.p+"assets/img/git_merge.drawio.fc35367e.svg"},522: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("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:"_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("バージョン管理システムとしてGitを利用し、"),t("strong",[s._v("変更")]),s._v("を管理することの大切さを学び、\nGitとGitHubを使った基本的なソフトウェア開発サイクルを回せるようになることがこの講義の目的です。")]),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("手元のPCでGitとGitHubが利用できる状態になっていて、"),t("a",{attrs:{href:"https://gist.github.com/Gab-km/3705015",target:"_blank",rel:"noopener noreferrer"}},[s._v("GitHub Flow"),t("OutboundLink")],1),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("p",[s._v("これからプログラムを書く、またはテキストファイルによる設定ファイル、マニュアル、仕様書などを記述する可能性のある技術者を対象としています。\n主に新卒の技術職採用者を想定しています。中級者向けの応用的な内容は扱いません。")]),s._v(" "),t("p",[s._v("講義にあたって事前に以下の要件を満たすようにしてください。")]),s._v(" "),t("ul",[t("li",[s._v("ITパスポート試験レベルの技術を理解している。")]),s._v(" "),t("li",[s._v("基礎的なコマンドラインの操作ができる。\n"),t("ul",[t("li",[s._v("ls, cd, mkdirを使ってディレクトリの移動、ファイルの操作が行える。")])])])]),s._v(" "),t("p",[s._v("新卒研修の内容を踏まえたものになっています。\nプログラミング言語の知識は必要ありません。\n自分がこの講義を受講したらよいかわからない、受講できるレベルにあるかわからない場合は担当のメンターに相談してください。")]),s._v(" "),t("h3",{attrs:{id:"_0-4-この講義で取り扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-4-この講義で取り扱わないこと"}},[s._v("#")]),s._v(" 0.4. この講義で取り扱わないこと")]),s._v(" "),t("ul",[t("li",[s._v("RCS(Revision Control System)、Subversionなどのバージョン管理システム")])]),s._v(" "),t("h3",{attrs:{id:"_0-5-この資料のお約束"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-5-この資料のお約束"}},[s._v("#")]),s._v(" 0.5. この資料のお約束")]),s._v(" "),t("p",[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("git clone <リモートリポジトリのアドレス>\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("git clone git@github.com:iij/bootcamp.git\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:"_1-gitをインストール"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-gitをインストール"}},[s._v("#")]),s._v(" 1. Gitをインストール")]),s._v(" "),t("p",[s._v("この講義では実際にGitコマンドを操作しながら覚えてもらいます。\n事前に自分のPCにGitをインストールして使えるようにしておきましょう。")]),s._v(" "),t("h3",{attrs:{id:"windows"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#windows"}},[s._v("#")]),s._v(" Windows")]),s._v(" "),t("p",[s._v("先にテキストエディタをインストールしておきましょう。\nメモ帳ではデフォルトの文字コードがUTF-8になっていないことがあります。\nこの講義では"),t("a",{attrs:{href:"https://azure.microsoft.com/ja-jp/products/visual-studio-code/",target:"_blank",rel:"noopener noreferrer"}},[s._v("VSCode"),t("OutboundLink")],1),s._v("を推奨します。\n"),t("a",{attrs:{href:"https://atom.io/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Atom"),t("OutboundLink")],1),s._v("や"),t("a",{attrs:{href:"https://www.sublimetext.com/3",target:"_blank",rel:"noopener noreferrer"}},[s._v("Sublime Text"),t("OutboundLink")],1),s._v("、"),t("a",{attrs:{href:"https://notepad-plus-plus.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Notepad++"),t("OutboundLink")],1),s._v("を使ってもかまいません。Vimに慣れている人はVimを使ってもよいです。\nメモ帳、サクラエディタは非推奨です。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://git-scm.com/download/win",target:"_blank",rel:"noopener noreferrer"}},[s._v("Downloading Git"),t("OutboundLink")],1),s._v("から「64-bit Git for Windows Setup」を選んでインストールしてください。")]),s._v(" "),t("p",[s._v("インストール途中で以下のようにGitで利用するテキストエディタに何を使うのかを聞かれます。\nVSCodeをインストールした人は「Use Visual Studio Code as Git's default editor」を選択してください。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(364),alt:"Choosing default editor"}})]),s._v(" "),t("p",[s._v("他のオプションはすべてデフォルトにしてください。\nインストールすると「Git Bash」が起動できるようになっています。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(365),alt:"Git Bashコマンド"}})]),s._v(" "),t("p",[s._v("このハンズオンではGit Bashコマンドを使って進めてください。")]),s._v(" "),t("h3",{attrs:{id:"macos"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#macos"}},[s._v("#")]),s._v(" macOS")]),s._v(" "),t("p",[s._v("macOSでは標準でGitが利用できます。\nXcode(Command Line Tool)が必要で、入っていない場合は時間がかかりますので事前に入れておきましょう。\n最新版のGitが欲しい場合はHomebrewでインストールすることもできます。")]),s._v(" "),t("div",{staticClass:"language-zsh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("brew install git\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h3",{attrs:{id:"linux"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#linux"}},[s._v("#")]),s._v(" Linux")]),s._v(" "),t("p",[s._v("ディストリビューションごとのパッケージは "),t("a",{attrs:{href:"https://git-scm.com/download/linux",target:"_blank",rel:"noopener noreferrer"}},[s._v("Download for Linux and Unix\n"),t("OutboundLink")],1),s._v(" に記載されています。")]),s._v(" "),t("h4",{attrs:{id:"インストールの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#インストールの確認"}},[s._v("#")]),s._v(" インストールの確認")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("チェックポイント1 🏁")]),s._v(" "),t("p",[s._v("💻 Gitが正しくインストールされているか確認しましょう。")]),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("git")]),s._v(" --version\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" version "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2.21")]),s._v(".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")])])]),s._v(" "),t("p",[s._v("Gitのバージョンは2.20以上を使いましょう。古い場合はアップデートしてください。")]),s._v(" "),t("h2",{attrs:{id:"_2-バージョン管理システムとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-バージョン管理システムとは"}},[s._v("#")]),s._v(" 2. バージョン管理システムとは")]),s._v(" "),t("p",[s._v("バージョン管理システム(Version Control System, VCS)とは、\nファイルの「変更」を記録し、"),t("strong",[s._v("誰が")]),s._v("、"),t("strong",[s._v("いつ")]),s._v("、"),t("strong",[s._v("どんな変更を")]),s._v("行ったかを参照できるようにするソフトウェアのことです。\nVCSにはたくさんの種類がありましたが戦国時代を経て、現在ではGitがデファクトスタンダードと言ってもよい状況となっています。")]),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("ul",[t("li",[t("a",{attrs:{href:"https://git-scm.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Git"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://www.mercurial-scm.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Mercurial"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://subversion.apache.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Apache Subversion"),t("OutboundLink")],1)]),s._v(" "),t("li",[s._v("Visual SourceSafe")]),s._v(" "),t("li",[t("a",{attrs:{href:"http://www.bitkeeper.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("BitKeeper"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"http://www.gnu.org/software/rcs/rcs.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("RCS"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("ではなぜVCSを使うとどんなよいことがあるのでしょうか?")]),s._v(" "),t("p",[s._v("まず、最新の状態が一目瞭然です。もう"),t("code",[s._v("最新_コピー(2)_修正版")]),s._v("のようなファイル名を付ける必要はありません。\nいつでも任意の時点の内容にファイルを戻すことができますので安心してファイルを上書きできます。")]),s._v(" "),t("p",[s._v("また問題が発生した場合にいつから発生していたのかを調べたり、昔のファイルに戻して復旧させたり、\nコードを書いた人を調べて直接質問しに行ったりできます。")]),s._v(" "),t("h2",{attrs:{id:"_3-gitの初期設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-gitの初期設定"}},[s._v("#")]),s._v(" 3. Gitの初期設定")]),s._v(" "),t("p",[s._v("まずは自分の情報を登録しましょう。")]),s._v(" "),t("p",[s._v("自分の名前とメールアドレスを設定します。\n"),t("code",[s._v("--global")]),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(" $ git config --global user.name '<自分の名前>'\n $ git config --global user.email '<自分のメールアドレス>'\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(" $ git config --global user.name 'Kazuki Hamasaki'\n $ git config --global user.email 'kazuki-h@iij.ad.jp'\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git config -l\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:"_4-リポジトリを作成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-リポジトリを作成する"}},[s._v("#")]),s._v(" 4. リポジトリを作成する")]),s._v(" "),t("p",[s._v("ファイルやディレクトリを記録するための場所のことを "),t("strong",[s._v("リポジトリ(Repository)")]),s._v(" と呼びます。\n1つのソフトウェアで1つのリポジトリとすることが多いです。\nEclipseのProjectや、Visual StudioのSolutionの単位と同じと考えてもらうのがよいです。")]),s._v(" "),t("p",[s._v("なにはともあれ、リポジトリがないと始まりません。\n以下の手順でリポジトリを作成してください。")]),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("$ mkdir git_handson\n$ cd git_handson\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("💻 このディレクトリをGitリポジトリとして初期化する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git init\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("これであなたのいるディレクトリはGitリポジトリとなりました。")]),s._v(" "),t("h2",{attrs:{id:"_5-gitの基本-変更の記録"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-gitの基本-変更の記録"}},[s._v("#")]),s._v(" 5. Gitの基本 変更の記録")]),s._v(" "),t("p",[t("img",{attrs:{src:a(366),alt:"gitで管理されるファイルの状態"}})]),s._v(" "),t("p",[s._v("リポジトリの中にGitで変更を管理しないファイルを置いておくこともできます。\nGitで変更を管理することを"),t("code",[s._v("追跡(track)")]),s._v("と呼びます。\n追跡されていないファイルは"),t("code",[s._v("Untracked")]),s._v("です。")]),s._v(" "),t("p",[s._v("追跡されているファイルは"),t("code",[s._v("Unmodified")]),s._v("、"),t("code",[s._v("Modified")]),s._v("、"),t("code",[s._v("Staged")]),s._v("の3つの状態で管理します。")]),s._v(" "),t("ul",[t("li",[s._v("Unmodified\n"),t("ul",[t("li",[s._v("変更されていないファイル")])])]),s._v(" "),t("li",[s._v("Modified\n"),t("ul",[t("li",[s._v("変更されたファイル")])])]),s._v(" "),t("li",[s._v("Staged\n"),t("ul",[t("li",[s._v("ステージされているファイル")])])])]),s._v(" "),t("p",[s._v("変更したファイルはステージングエリアに乗せます。\nステージングエリアに乗っているファイルがまとめてひとつの変更として記録されます。\nファイルの変更を記録することを "),t("strong",[s._v("コミット(Commit)")]),s._v(" と呼びます。")]),s._v(" "),t("h3",{attrs:{id:"_5-1-コミットする"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-コミットする"}},[s._v("#")]),s._v(" 5.1. コミットする")]),s._v(" "),t("p",[s._v("ファイルを作成して、さっそくコミットしましょう。")]),s._v(" "),t("p",[s._v("💻 以下の内容のファイルを作成する。(メモ帳以外のお好きなエディタでどうぞ!)")]),s._v(" "),t("div",{staticClass:"custom-block warning"},[t("p",{staticClass:"custom-block-title"},[s._v("WARNING")]),s._v(" "),t("p",[s._v("テキストファイルの文字コードはUTF-8で作成しましょう。このあとの操作で文字化けすることがあります。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("※typo していますが、そのまま記述してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\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("p",[s._v("Gitがファイルをどのように扱っているか確認してみましょう。")]),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("$ git status\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('Untracked files:\n (use "git add ..." to include in what will be committed)\n\n\thello.txt\n\nnothing added to commit but untracked files present (use "git add" to track)\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("リポジトリの中にファイルを置いただけで追跡されるわけではありません。\nそのためファイルを新しく作った場合には"),t("code",[s._v("Untracked")]),s._v("に表示されます。")]),s._v(" "),t("p",[t("code",[s._v("hello.txt")]),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("$ git add hello.txt\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("Staged")]),s._v("として登録されていることを確認する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git status\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git commit\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("コミット時にはメッセージを記入でき、これを"),t("strong",[s._v("コミットメッセージ")]),s._v("と呼びます。\nあとから見たときにどのような変更を入れたかわかりやすくするためのものです。")]),s._v(" "),t("ul",[t("li",[s._v("テキストエディタが起動するので、コミットメッセージを記入します。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(" 1 はじめてのgit\n 2\n 3 # Please enter the commit message for your changes. Lines starting\n 4 # with '#' will be ignored, and an empty message aborts the commit.\n 5 #\n 6 # Date: Wed Jun 26 22:08:30 2019 +0900\n 7 #\n 8 # On branch master\n 9 #\n 10 # Initial commit\n 11 #\n 12 # Changes to be committed:\n 13 # new file: hello.txt\n 14 #\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("VSCodeで編集した場合はファイルを保存して閉じるとコミットは完了です。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("nanoが起動して困りましたか?\n好きなエディタを使いたい場合は以下のように設定します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("git config --global core.editor 'vim -c \"set fenc=utf-8\"'\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("atomを使いたい人は"),t("code",[s._v("--wait")]),s._v("オプションが必要です。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('git config --global core.editor "atom --wait"\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])])]),s._v(" "),t("p",[t("img",{attrs:{src:a(367),alt:"Gitの最初のコミット"}})]),s._v(" "),t("p",[s._v("まずは開発の歴史に小さな一歩が刻まれました。(小さな一歩のイメージ図)")]),s._v(" "),t("h4",{attrs:{id:"_5-1-1-コミットメッセージには理由を書こう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-1-コミットメッセージには理由を書こう"}},[s._v("#")]),s._v(" 5.1.1. コミットメッセージには理由を書こう")]),s._v(" "),t("p",[s._v("コミットメッセージには"),t("strong",[s._v("なぜその修正を入れたのか")]),s._v("理由を書くようにしましょう。")]),s._v(" "),t("p",[s._v("どんな修正を入れたかは履歴を見れば確認できます。\nなぜその修正を入れる必要があったかは往々にしてコードには現れません。\nそれを残しておくための場所がコミットメッセージです。")]),s._v(" "),t("h3",{attrs:{id:"_5-2-コミットの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-2-コミットの確認"}},[s._v("#")]),s._v(" 5.2. コミットの確認")]),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("$ git log\n\ncommit b8490bfc83a2c91bac6772b2f9e533ccf2455baf (HEAD -> master)\nAuthor: Kazuki Hamasaki \nDate: Wed Jun 26 22:08:30 2019 +0900\n\n はじめてのgit\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",[t("code",[s._v("commit")]),s._v("はコミットごとに発行される一意のIDでコミットハッシュ値と呼ばれています。\n"),t("code",[s._v("Author")]),s._v("はコミットを作成した人です。「2. Gitの初期設定」で設定した値が使われているはずです。\n"),t("code",[s._v("Data")]),s._v("はコミット日時ですね。\nその下にはコミットコメントが表示されています。")]),s._v(" "),t("p",[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("$ git show\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("git show")]),s._v("では変更に関する差分のみが表示されます。\nここの差分はユニファイド形式になっています。\n"),t("code",[s._v("+")]),s._v("が追加された行で、"),t("code",[s._v("-")]),s._v("が削除された行です。\nファイルがまるごと追加されたため、すべての行が追加されたという表示になっています。")]),s._v(" "),t("p",[s._v("よく見たら"),t("a",{attrs:{href:"http://e-words.jp/w/%E3%82%BF%E3%82%A4%E3%83%9D.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("typo"),t("OutboundLink")],1),s._v("してますよね。直してみましょう。")]),s._v(" "),t("h3",{attrs:{id:"_5-3-さらにコミットを積み重ねる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-3-さらにコミットを積み重ねる"}},[s._v("#")]),s._v(" 5.3. さらにコミットを積み重ねる")]),s._v(" "),t("p",[s._v("💻 typoを直しましょう。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\n"),t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("こんばんは じっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[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("p",[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("$ git diff\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("p",[s._v("💻 問題なければコミットします。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git add hello.txt\n$ git status\n$ git commit\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("p",[s._v("💻 コミットの確認")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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("git log")]),s._v("の"),t("code",[s._v("-p")]),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("git log -p\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(368),alt:"Gitで2回目のコミット"}})]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("コミットとコミットとの間の矢印は逆じゃないの?")]),s._v(" "),t("p",[s._v("Gitのコミットオブジェクトは直前のコミットへのポインタを持っています。\n詳しく知りたい人は "),t("a",{attrs:{href:"https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88#r_git_commit_objects",target:"_blank",rel:"noopener noreferrer"}},[s._v("Gitの内側 - Gitオブジェクト コミットオブジェクト"),t("OutboundLink")],1),s._v("をご覧ください。")])]),s._v(" "),t("h2",{attrs:{id:"_6-ブランチ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-ブランチ"}},[s._v("#")]),s._v(" 6. ブランチ")]),s._v(" "),t("h3",{attrs:{id:"_6-1-実はあなたは今、-master-というブランチにいます"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-1-実はあなたは今、-master-というブランチにいます"}},[s._v("#")]),s._v(' 6.1. 実はあなたは今、"master"というブランチにいます')]),s._v(" "),t("p",[t("code",[s._v("ブランチ(Branch)")]),s._v("とは、履歴の流れを分岐させて記録するしくみです。\nGitリポジトリを作成すると、自動的に"),t("code",[s._v("master")]),s._v("ブランチが1つ作成されています。")]),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("$ git branch\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("p",[s._v("ブランチの切り方としては、作業内容単位で切っていくのが一般的です。\n何か作業をするときはとりあえずブランチを切ってそこで作業を行い、\n完了しだい"),t("code",[s._v("master")]),s._v("ブランチに反映させます。")]),s._v(" "),t("p",[t("code",[s._v("master")]),s._v("ブランチをそのままメインブランチとして扱うことが多いです。\n今回もその慣習に従います。")]),s._v(" "),t("h3",{attrs:{id:"_6-2-新たな-branch-を作る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-2-新たな-branch-を作る"}},[s._v("#")]),s._v(" 6.2. 新たな Branch を作る")]),s._v(" "),t("p",[s._v('今度は "ぎっと" を "Git" に直したい。\nせっかくですので別のブランチで作業してみましょう。')]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("fix")]),s._v("というブランチを作成する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch fix\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(369),alt:"fixブランチを作成した"}})]),s._v(" "),t("p",[t("code",[s._v("fix")]),s._v("ブランチを作成したものの、\nあなたはまだ"),t("code",[s._v("master")]),s._v("ブランチにいるままです。")]),s._v(" "),t("h3",{attrs:{id:"_6-3-ブランチを移動する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-3-ブランチを移動する"}},[s._v("#")]),s._v(" 6.3. ブランチを移動する")]),s._v(" "),t("p",[s._v('💻 "fix" Branch へ移動するために Switch する。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch fix\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v('" * " が "fix" を指すようになりましたね?')]),s._v(" "),t("p",[t("img",{attrs:{src:a(370),alt:"fixブランチを作成した"}})]),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("$ git log\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("master")]),s._v("ブランチのコミットが引き継がれているはずです。")]),s._v(" "),t("h3",{attrs:{id:"_6-4-fix-ブランチで-commit-する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-4-fix-ブランチで-commit-する"}},[s._v("#")]),s._v(' 6.4. "fix"ブランチで Commit する')]),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("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("おはよう ぎっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("おはよう Git\n")])]),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("p",[s._v("💻 Commit する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git add hello.txt\n$ git status\n$ git commit\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",[t("img",{attrs:{src:a(371),alt:"fixブランチでコミットした"}})]),s._v(" "),t("p",[s._v("💻 今いるfixブランチのファイルを確認する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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("fix")]),s._v("ブランチで Commit を積むことができました。\n"),t("code",[s._v("master")]),s._v("ブランチに戻るとどう見えるでしょう。")]),s._v(" "),t("h3",{attrs:{id:"_6-5-master-ブランチへ戻る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-5-master-ブランチへ戻る"}},[s._v("#")]),s._v(' 6.5. "master"ブランチへ戻る')]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("master")]),s._v(" へ移動するために Switch します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch master\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(372),alt:"masterブランチに戻った"}})]),s._v(" "),t("p",[s._v('💻 "master"ブランチの履歴を見る。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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('先ほどの「6.4. "fix"ブランチで Commit する」で行ったコミットは"fix"ブランチで行ったものですので、masterブランチでは見えません。')]),s._v(" "),t("p",[s._v("複数人で開発する場合には同時にいくつもの変更が行われていることがあります。\n"),t("code",[s._v("fix")]),s._v("ブランチで修正中ですが、並行して"),t("code",[s._v("master")]),s._v("ブランチでも変更を入れてみましょう。")]),s._v(" "),t("p",[s._v("💻 masterブランチで別のコミットをする")]),s._v(" "),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\n"),t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("こんばんは ぎっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[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("p",[t("img",{attrs:{src:a(373),alt:"masterブランチに戻った"}})]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("master")]),s._v("と"),t("code",[s._v("fix")]),s._v("の2つのブランチを言ったり来たりして、ファイルの状態が変わることを確認しましょう。")]),s._v(" "),t("h3",{attrs:{id:"_6-6-マージする"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-6-マージする"}},[s._v("#")]),s._v(" 6.6. マージする")]),s._v(" "),t("p",[s._v("今いるブランチに他のブランチでの変更を取り込むことをマージ(merge)と呼びます。")]),s._v(" "),t("p",[s._v("修正が完了して問題がないことを完了したのでこの修正を"),t("code",[s._v("master")]),s._v("ブランチに取り込みます。\n"),t("code",[s._v("master")]),s._v('ブランチに "fix"ブランチの変更を取り込みます。')]),s._v(" "),t("p",[t("code",[s._v("master")]),s._v("ブランチの内容は")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\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("p",[s._v("に"),t("code",[s._v("fix")]),s._v("ブランチの内容")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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("p",[s._v("を取り込みます。すると")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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("p",[s._v("となるはずです。歴史は以下のようになります。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(374),alt:"ブランチをマージする"}})]),s._v(" "),t("p",[s._v("ブランチをマージするには"),t("code",[s._v("git merge")]),s._v("コマンドを使います。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git merge <取り込みたいブランチ名>\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v('💻 "master"ブランチへ移動してから"fix"ブランチの変更内容を、メインである"master"ブランチへ取り込みましょう')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch master\n$ git merge fix\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("マージするときのコミットコメントはデフォルトでOKです。")]),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("$ git log\n$ git show\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("fix")]),s._v("ブランチで行ったコミットが取り込まれているでしょうか?")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("チェックポイント2 🏁")]),s._v(" "),t("p",[s._v("hello.txtが以下のようになっていれば正解です!")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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")])])]),s._v(" "),t("p",[s._v("Git の基本操作を一通り流してやってみました。\nGit ハンズオンは以上で終了です。")]),s._v(" "),t("p",[s._v("次は "),t("a",{attrs:{href:"../github"}},[s._v("GitHub ハンズオン")]),s._v(" に進んでください。")]),s._v(" "),t("h2",{attrs:{id:"_7-参考文献"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-参考文献"}},[s._v("#")]),s._v(" 7. 参考文献")]),s._v(" "),t("p",[s._v("さらに高度な使い方を知りたい方は"),t("a",{attrs:{href:"https://git-scm.com/book/ja/v2",target:"_blank",rel:"noopener noreferrer"}},[s._v("Pro Git"),t("OutboundLink")],1),s._v("が便利です。日本語版が公開されています。")]),s._v(" "),t("p",[s._v("こんな説明じゃブランチがわからん!という人は "),t("a",{attrs:{href:"https://learngitbranching.js.org/?locale=ja",target:"_blank",rel:"noopener noreferrer"}},[s._v("Learn Git Branching (日本語版)"),t("OutboundLink")],1),s._v(" で練習できます。")]),s._v(" "),t("h3",{attrs:{id:"_7-1-guiクライアント"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-1-guiクライアント"}},[s._v("#")]),s._v(" 7.1. GUIクライアント")]),s._v(" "),t("p",[s._v("Gitには、機能が限定されますがGUIクライアントも用意されています。おすすめは以下の3つのクライアントです。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://desktop.github.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("GitHub Desktop (Win, macOS)"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://tortoisegit.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("TortoiseGit (Win)"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://www.sourcetreeapp.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("SourceTree (Win, macOS)"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("また各種統合開発環境にもGitを操作する機能が含まれています。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://code.visualstudio.com/docs/editor/versioncontrol",target:"_blank",rel:"noopener noreferrer"}},[s._v("Using Version Control in VS Code"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://pleiades.io/help/idea/using-git-integration.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Git - ヘルプ | IntelliJ IDEA"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("これらのクライアントの利用も検討してください。")]),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([[12],{364:function(s,t,a){s.exports=a.p+"assets/img/git_choosing_editor.65e3a5a8.jpg"},365:function(s,t,a){s.exports=a.p+"assets/img/git_bash_win.e14b57ed.png"},366:function(s,t,a){s.exports=a.p+"assets/img/git_stage.drawio.0ed2f694.svg"},367:function(s,t,a){s.exports=a.p+"assets/img/git_first_commit.drawio.c10d455e.svg"},368:function(s,t,a){s.exports=a.p+"assets/img/git_second_commit.drawio.fc14a864.svg"},369:function(s,t,a){s.exports=a.p+"assets/img/git_branch.drawio.b856c449.svg"},370:function(s,t,a){s.exports=a.p+"assets/img/git_checkout_branch.drawio.39309ca7.svg"},371:function(s,t,a){s.exports=a.p+"assets/img/git_fix_branch_commit.drawio.1c6afc6b.svg"},372:function(s,t,a){s.exports=a.p+"assets/img/git_return_master_branch.drawio.ec0170af.svg"},373:function(s,t,a){s.exports=a.p+"assets/img/git_return_master_branch_commit.drawio.bf34ae5a.svg"},374:function(s,t,a){s.exports=a.p+"assets/img/git_merge.drawio.fc35367e.svg"},521: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("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:"_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("バージョン管理システムとしてGitを利用し、"),t("strong",[s._v("変更")]),s._v("を管理することの大切さを学び、\nGitとGitHubを使った基本的なソフトウェア開発サイクルを回せるようになることがこの講義の目的です。")]),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("手元のPCでGitとGitHubが利用できる状態になっていて、"),t("a",{attrs:{href:"https://gist.github.com/Gab-km/3705015",target:"_blank",rel:"noopener noreferrer"}},[s._v("GitHub Flow"),t("OutboundLink")],1),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("p",[s._v("これからプログラムを書く、またはテキストファイルによる設定ファイル、マニュアル、仕様書などを記述する可能性のある技術者を対象としています。\n主に新卒の技術職採用者を想定しています。中級者向けの応用的な内容は扱いません。")]),s._v(" "),t("p",[s._v("講義にあたって事前に以下の要件を満たすようにしてください。")]),s._v(" "),t("ul",[t("li",[s._v("ITパスポート試験レベルの技術を理解している。")]),s._v(" "),t("li",[s._v("基礎的なコマンドラインの操作ができる。\n"),t("ul",[t("li",[s._v("ls, cd, mkdirを使ってディレクトリの移動、ファイルの操作が行える。")])])])]),s._v(" "),t("p",[s._v("新卒研修の内容を踏まえたものになっています。\nプログラミング言語の知識は必要ありません。\n自分がこの講義を受講したらよいかわからない、受講できるレベルにあるかわからない場合は担当のメンターに相談してください。")]),s._v(" "),t("h3",{attrs:{id:"_0-4-この講義で取り扱わないこと"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-4-この講義で取り扱わないこと"}},[s._v("#")]),s._v(" 0.4. この講義で取り扱わないこと")]),s._v(" "),t("ul",[t("li",[s._v("RCS(Revision Control System)、Subversionなどのバージョン管理システム")])]),s._v(" "),t("h3",{attrs:{id:"_0-5-この資料のお約束"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0-5-この資料のお約束"}},[s._v("#")]),s._v(" 0.5. この資料のお約束")]),s._v(" "),t("p",[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("git clone <リモートリポジトリのアドレス>\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("git clone git@github.com:iij/bootcamp.git\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:"_1-gitをインストール"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-gitをインストール"}},[s._v("#")]),s._v(" 1. Gitをインストール")]),s._v(" "),t("p",[s._v("この講義では実際にGitコマンドを操作しながら覚えてもらいます。\n事前に自分のPCにGitをインストールして使えるようにしておきましょう。")]),s._v(" "),t("h3",{attrs:{id:"windows"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#windows"}},[s._v("#")]),s._v(" Windows")]),s._v(" "),t("p",[s._v("先にテキストエディタをインストールしておきましょう。\nメモ帳ではデフォルトの文字コードがUTF-8になっていないことがあります。\nこの講義では"),t("a",{attrs:{href:"https://azure.microsoft.com/ja-jp/products/visual-studio-code/",target:"_blank",rel:"noopener noreferrer"}},[s._v("VSCode"),t("OutboundLink")],1),s._v("を推奨します。\n"),t("a",{attrs:{href:"https://atom.io/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Atom"),t("OutboundLink")],1),s._v("や"),t("a",{attrs:{href:"https://www.sublimetext.com/3",target:"_blank",rel:"noopener noreferrer"}},[s._v("Sublime Text"),t("OutboundLink")],1),s._v("、"),t("a",{attrs:{href:"https://notepad-plus-plus.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Notepad++"),t("OutboundLink")],1),s._v("を使ってもかまいません。Vimに慣れている人はVimを使ってもよいです。\nメモ帳、サクラエディタは非推奨です。")]),s._v(" "),t("p",[t("a",{attrs:{href:"https://git-scm.com/download/win",target:"_blank",rel:"noopener noreferrer"}},[s._v("Downloading Git"),t("OutboundLink")],1),s._v("から「64-bit Git for Windows Setup」を選んでインストールしてください。")]),s._v(" "),t("p",[s._v("インストール途中で以下のようにGitで利用するテキストエディタに何を使うのかを聞かれます。\nVSCodeをインストールした人は「Use Visual Studio Code as Git's default editor」を選択してください。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(364),alt:"Choosing default editor"}})]),s._v(" "),t("p",[s._v("他のオプションはすべてデフォルトにしてください。\nインストールすると「Git Bash」が起動できるようになっています。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(365),alt:"Git Bashコマンド"}})]),s._v(" "),t("p",[s._v("このハンズオンではGit Bashコマンドを使って進めてください。")]),s._v(" "),t("h3",{attrs:{id:"macos"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#macos"}},[s._v("#")]),s._v(" macOS")]),s._v(" "),t("p",[s._v("macOSでは標準でGitが利用できます。\nXcode(Command Line Tool)が必要で、入っていない場合は時間がかかりますので事前に入れておきましょう。\n最新版のGitが欲しい場合はHomebrewでインストールすることもできます。")]),s._v(" "),t("div",{staticClass:"language-zsh line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("brew install git\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("h3",{attrs:{id:"linux"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#linux"}},[s._v("#")]),s._v(" Linux")]),s._v(" "),t("p",[s._v("ディストリビューションごとのパッケージは "),t("a",{attrs:{href:"https://git-scm.com/download/linux",target:"_blank",rel:"noopener noreferrer"}},[s._v("Download for Linux and Unix\n"),t("OutboundLink")],1),s._v(" に記載されています。")]),s._v(" "),t("h4",{attrs:{id:"インストールの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#インストールの確認"}},[s._v("#")]),s._v(" インストールの確認")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("チェックポイント1 🏁")]),s._v(" "),t("p",[s._v("💻 Gitが正しくインストールされているか確認しましょう。")]),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("git")]),s._v(" --version\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("git")]),s._v(" version "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2.21")]),s._v(".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")])])]),s._v(" "),t("p",[s._v("Gitのバージョンは2.20以上を使いましょう。古い場合はアップデートしてください。")]),s._v(" "),t("h2",{attrs:{id:"_2-バージョン管理システムとは"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-バージョン管理システムとは"}},[s._v("#")]),s._v(" 2. バージョン管理システムとは")]),s._v(" "),t("p",[s._v("バージョン管理システム(Version Control System, VCS)とは、\nファイルの「変更」を記録し、"),t("strong",[s._v("誰が")]),s._v("、"),t("strong",[s._v("いつ")]),s._v("、"),t("strong",[s._v("どんな変更を")]),s._v("行ったかを参照できるようにするソフトウェアのことです。\nVCSにはたくさんの種類がありましたが戦国時代を経て、現在ではGitがデファクトスタンダードと言ってもよい状況となっています。")]),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("ul",[t("li",[t("a",{attrs:{href:"https://git-scm.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Git"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://www.mercurial-scm.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Mercurial"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://subversion.apache.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("Apache Subversion"),t("OutboundLink")],1)]),s._v(" "),t("li",[s._v("Visual SourceSafe")]),s._v(" "),t("li",[t("a",{attrs:{href:"http://www.bitkeeper.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("BitKeeper"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"http://www.gnu.org/software/rcs/rcs.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("RCS"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("ではなぜVCSを使うとどんなよいことがあるのでしょうか?")]),s._v(" "),t("p",[s._v("まず、最新の状態が一目瞭然です。もう"),t("code",[s._v("最新_コピー(2)_修正版")]),s._v("のようなファイル名を付ける必要はありません。\nいつでも任意の時点の内容にファイルを戻すことができますので安心してファイルを上書きできます。")]),s._v(" "),t("p",[s._v("また問題が発生した場合にいつから発生していたのかを調べたり、昔のファイルに戻して復旧させたり、\nコードを書いた人を調べて直接質問しに行ったりできます。")]),s._v(" "),t("h2",{attrs:{id:"_3-gitの初期設定"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-gitの初期設定"}},[s._v("#")]),s._v(" 3. Gitの初期設定")]),s._v(" "),t("p",[s._v("まずは自分の情報を登録しましょう。")]),s._v(" "),t("p",[s._v("自分の名前とメールアドレスを設定します。\n"),t("code",[s._v("--global")]),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(" $ git config --global user.name '<自分の名前>'\n $ git config --global user.email '<自分のメールアドレス>'\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(" $ git config --global user.name 'Kazuki Hamasaki'\n $ git config --global user.email 'kazuki-h@iij.ad.jp'\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git config -l\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:"_4-リポジトリを作成する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4-リポジトリを作成する"}},[s._v("#")]),s._v(" 4. リポジトリを作成する")]),s._v(" "),t("p",[s._v("ファイルやディレクトリを記録するための場所のことを "),t("strong",[s._v("リポジトリ(Repository)")]),s._v(" と呼びます。\n1つのソフトウェアで1つのリポジトリとすることが多いです。\nEclipseのProjectや、Visual StudioのSolutionの単位と同じと考えてもらうのがよいです。")]),s._v(" "),t("p",[s._v("なにはともあれ、リポジトリがないと始まりません。\n以下の手順でリポジトリを作成してください。")]),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("$ mkdir git_handson\n$ cd git_handson\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("💻 このディレクトリをGitリポジトリとして初期化する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git init\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("これであなたのいるディレクトリはGitリポジトリとなりました。")]),s._v(" "),t("h2",{attrs:{id:"_5-gitの基本-変更の記録"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-gitの基本-変更の記録"}},[s._v("#")]),s._v(" 5. Gitの基本 変更の記録")]),s._v(" "),t("p",[t("img",{attrs:{src:a(366),alt:"gitで管理されるファイルの状態"}})]),s._v(" "),t("p",[s._v("リポジトリの中にGitで変更を管理しないファイルを置いておくこともできます。\nGitで変更を管理することを"),t("code",[s._v("追跡(track)")]),s._v("と呼びます。\n追跡されていないファイルは"),t("code",[s._v("Untracked")]),s._v("です。")]),s._v(" "),t("p",[s._v("追跡されているファイルは"),t("code",[s._v("Unmodified")]),s._v("、"),t("code",[s._v("Modified")]),s._v("、"),t("code",[s._v("Staged")]),s._v("の3つの状態で管理します。")]),s._v(" "),t("ul",[t("li",[s._v("Unmodified\n"),t("ul",[t("li",[s._v("変更されていないファイル")])])]),s._v(" "),t("li",[s._v("Modified\n"),t("ul",[t("li",[s._v("変更されたファイル")])])]),s._v(" "),t("li",[s._v("Staged\n"),t("ul",[t("li",[s._v("ステージされているファイル")])])])]),s._v(" "),t("p",[s._v("変更したファイルはステージングエリアに乗せます。\nステージングエリアに乗っているファイルがまとめてひとつの変更として記録されます。\nファイルの変更を記録することを "),t("strong",[s._v("コミット(Commit)")]),s._v(" と呼びます。")]),s._v(" "),t("h3",{attrs:{id:"_5-1-コミットする"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-コミットする"}},[s._v("#")]),s._v(" 5.1. コミットする")]),s._v(" "),t("p",[s._v("ファイルを作成して、さっそくコミットしましょう。")]),s._v(" "),t("p",[s._v("💻 以下の内容のファイルを作成する。(メモ帳以外のお好きなエディタでどうぞ!)")]),s._v(" "),t("div",{staticClass:"custom-block warning"},[t("p",{staticClass:"custom-block-title"},[s._v("WARNING")]),s._v(" "),t("p",[s._v("テキストファイルの文字コードはUTF-8で作成しましょう。このあとの操作で文字化けすることがあります。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("※typo していますが、そのまま記述してください。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\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("p",[s._v("Gitがファイルをどのように扱っているか確認してみましょう。")]),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("$ git status\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('Untracked files:\n (use "git add ..." to include in what will be committed)\n\n\thello.txt\n\nnothing added to commit but untracked files present (use "git add" to track)\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("リポジトリの中にファイルを置いただけで追跡されるわけではありません。\nそのためファイルを新しく作った場合には"),t("code",[s._v("Untracked")]),s._v("に表示されます。")]),s._v(" "),t("p",[t("code",[s._v("hello.txt")]),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("$ git add hello.txt\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("Staged")]),s._v("として登録されていることを確認する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git status\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git commit\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("コミット時にはメッセージを記入でき、これを"),t("strong",[s._v("コミットメッセージ")]),s._v("と呼びます。\nあとから見たときにどのような変更を入れたかわかりやすくするためのものです。")]),s._v(" "),t("ul",[t("li",[s._v("テキストエディタが起動するので、コミットメッセージを記入します。")])]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v(" 1 はじめてのgit\n 2\n 3 # Please enter the commit message for your changes. Lines starting\n 4 # with '#' will be ignored, and an empty message aborts the commit.\n 5 #\n 6 # Date: Wed Jun 26 22:08:30 2019 +0900\n 7 #\n 8 # On branch master\n 9 #\n 10 # Initial commit\n 11 #\n 12 # Changes to be committed:\n 13 # new file: hello.txt\n 14 #\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("VSCodeで編集した場合はファイルを保存して閉じるとコミットは完了です。")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),t("p",[s._v("nanoが起動して困りましたか?\n好きなエディタを使いたい場合は以下のように設定します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("git config --global core.editor 'vim -c \"set fenc=utf-8\"'\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v("atomを使いたい人は"),t("code",[s._v("--wait")]),s._v("オプションが必要です。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v('git config --global core.editor "atom --wait"\n')])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])])]),s._v(" "),t("p",[t("img",{attrs:{src:a(367),alt:"Gitの最初のコミット"}})]),s._v(" "),t("p",[s._v("まずは開発の歴史に小さな一歩が刻まれました。(小さな一歩のイメージ図)")]),s._v(" "),t("h4",{attrs:{id:"_5-1-1-コミットメッセージには理由を書こう"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-1-コミットメッセージには理由を書こう"}},[s._v("#")]),s._v(" 5.1.1. コミットメッセージには理由を書こう")]),s._v(" "),t("p",[s._v("コミットメッセージには"),t("strong",[s._v("なぜその修正を入れたのか")]),s._v("理由を書くようにしましょう。")]),s._v(" "),t("p",[s._v("どんな修正を入れたかは履歴を見れば確認できます。\nなぜその修正を入れる必要があったかは往々にしてコードには現れません。\nそれを残しておくための場所がコミットメッセージです。")]),s._v(" "),t("h3",{attrs:{id:"_5-2-コミットの確認"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-2-コミットの確認"}},[s._v("#")]),s._v(" 5.2. コミットの確認")]),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("$ git log\n\ncommit b8490bfc83a2c91bac6772b2f9e533ccf2455baf (HEAD -> master)\nAuthor: Kazuki Hamasaki \nDate: Wed Jun 26 22:08:30 2019 +0900\n\n はじめてのgit\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",[t("code",[s._v("commit")]),s._v("はコミットごとに発行される一意のIDでコミットハッシュ値と呼ばれています。\n"),t("code",[s._v("Author")]),s._v("はコミットを作成した人です。「2. Gitの初期設定」で設定した値が使われているはずです。\n"),t("code",[s._v("Data")]),s._v("はコミット日時ですね。\nその下にはコミットコメントが表示されています。")]),s._v(" "),t("p",[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("$ git show\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("git show")]),s._v("では変更に関する差分のみが表示されます。\nここの差分はユニファイド形式になっています。\n"),t("code",[s._v("+")]),s._v("が追加された行で、"),t("code",[s._v("-")]),s._v("が削除された行です。\nファイルがまるごと追加されたため、すべての行が追加されたという表示になっています。")]),s._v(" "),t("p",[s._v("よく見たら"),t("a",{attrs:{href:"http://e-words.jp/w/%E3%82%BF%E3%82%A4%E3%83%9D.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("typo"),t("OutboundLink")],1),s._v("してますよね。直してみましょう。")]),s._v(" "),t("h3",{attrs:{id:"_5-3-さらにコミットを積み重ねる"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5-3-さらにコミットを積み重ねる"}},[s._v("#")]),s._v(" 5.3. さらにコミットを積み重ねる")]),s._v(" "),t("p",[s._v("💻 typoを直しましょう。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\n"),t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("こんばんは じっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[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("p",[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("$ git diff\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("p",[s._v("💻 問題なければコミットします。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git add hello.txt\n$ git status\n$ git commit\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("p",[s._v("💻 コミットの確認")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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("git log")]),s._v("の"),t("code",[s._v("-p")]),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("git log -p\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(368),alt:"Gitで2回目のコミット"}})]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("コミットとコミットとの間の矢印は逆じゃないの?")]),s._v(" "),t("p",[s._v("Gitのコミットオブジェクトは直前のコミットへのポインタを持っています。\n詳しく知りたい人は "),t("a",{attrs:{href:"https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88#r_git_commit_objects",target:"_blank",rel:"noopener noreferrer"}},[s._v("Gitの内側 - Gitオブジェクト コミットオブジェクト"),t("OutboundLink")],1),s._v("をご覧ください。")])]),s._v(" "),t("h2",{attrs:{id:"_6-ブランチ"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-ブランチ"}},[s._v("#")]),s._v(" 6. ブランチ")]),s._v(" "),t("h3",{attrs:{id:"_6-1-実はあなたは今、-master-というブランチにいます"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-1-実はあなたは今、-master-というブランチにいます"}},[s._v("#")]),s._v(' 6.1. 実はあなたは今、"master"というブランチにいます')]),s._v(" "),t("p",[t("code",[s._v("ブランチ(Branch)")]),s._v("とは、履歴の流れを分岐させて記録するしくみです。\nGitリポジトリを作成すると、自動的に"),t("code",[s._v("master")]),s._v("ブランチが1つ作成されています。")]),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("$ git branch\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("p",[s._v("ブランチの切り方としては、作業内容単位で切っていくのが一般的です。\n何か作業をするときはとりあえずブランチを切ってそこで作業を行い、\n完了しだい"),t("code",[s._v("master")]),s._v("ブランチに反映させます。")]),s._v(" "),t("p",[t("code",[s._v("master")]),s._v("ブランチをそのままメインブランチとして扱うことが多いです。\n今回もその慣習に従います。")]),s._v(" "),t("h3",{attrs:{id:"_6-2-新たな-branch-を作る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-2-新たな-branch-を作る"}},[s._v("#")]),s._v(" 6.2. 新たな Branch を作る")]),s._v(" "),t("p",[s._v('今度は "ぎっと" を "Git" に直したい。\nせっかくですので別のブランチで作業してみましょう。')]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("fix")]),s._v("というブランチを作成する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch fix\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(369),alt:"fixブランチを作成した"}})]),s._v(" "),t("p",[t("code",[s._v("fix")]),s._v("ブランチを作成したものの、\nあなたはまだ"),t("code",[s._v("master")]),s._v("ブランチにいるままです。")]),s._v(" "),t("h3",{attrs:{id:"_6-3-ブランチを移動する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-3-ブランチを移動する"}},[s._v("#")]),s._v(" 6.3. ブランチを移動する")]),s._v(" "),t("p",[s._v('💻 "fix" Branch へ移動するために Switch する。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch fix\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- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git branch\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v('" * " が "fix" を指すようになりましたね?')]),s._v(" "),t("p",[t("img",{attrs:{src:a(370),alt:"fixブランチを作成した"}})]),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("$ git log\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("master")]),s._v("ブランチのコミットが引き継がれているはずです。")]),s._v(" "),t("h3",{attrs:{id:"_6-4-fix-ブランチで-commit-する"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-4-fix-ブランチで-commit-する"}},[s._v("#")]),s._v(' 6.4. "fix"ブランチで Commit する')]),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("$ code hello.txt\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("おはよう ぎっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("おはよう Git\n")])]),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("p",[s._v("💻 Commit する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git add hello.txt\n$ git status\n$ git commit\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",[t("img",{attrs:{src:a(371),alt:"fixブランチでコミットした"}})]),s._v(" "),t("p",[s._v("💻 今いるfixブランチのファイルを確認する。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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("fix")]),s._v("ブランチで Commit を積むことができました。\n"),t("code",[s._v("master")]),s._v("ブランチに戻るとどう見えるでしょう。")]),s._v(" "),t("h3",{attrs:{id:"_6-5-master-ブランチへ戻る"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-5-master-ブランチへ戻る"}},[s._v("#")]),s._v(' 6.5. "master"ブランチへ戻る')]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("master")]),s._v(" へ移動するために Switch します。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch master\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[t("img",{attrs:{src:a(372),alt:"masterブランチに戻った"}})]),s._v(" "),t("p",[s._v('💻 "master"ブランチの履歴を見る。')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git log\n$ git show\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('先ほどの「6.4. "fix"ブランチで Commit する」で行ったコミットは"fix"ブランチで行ったものですので、masterブランチでは見えません。')]),s._v(" "),t("p",[s._v("複数人で開発する場合には同時にいくつもの変更が行われていることがあります。\n"),t("code",[s._v("fix")]),s._v("ブランチで修正中ですが、並行して"),t("code",[s._v("master")]),s._v("ブランチでも変更を入れてみましょう。")]),s._v(" "),t("p",[s._v("💻 masterブランチで別のコミットをする")]),s._v(" "),t("div",{staticClass:"language-diff line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\n"),t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[s._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[s._v("こんばんは ぎっと\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[s._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[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("p",[t("img",{attrs:{src:a(373),alt:"masterブランチに戻った"}})]),s._v(" "),t("p",[s._v("💻 "),t("code",[s._v("master")]),s._v("と"),t("code",[s._v("fix")]),s._v("の2つのブランチを言ったり来たりして、ファイルの状態が変わることを確認しましょう。")]),s._v(" "),t("h3",{attrs:{id:"_6-6-マージする"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_6-6-マージする"}},[s._v("#")]),s._v(" 6.6. マージする")]),s._v(" "),t("p",[s._v("今いるブランチに他のブランチでの変更を取り込むことをマージ(merge)と呼びます。")]),s._v(" "),t("p",[s._v("修正が完了して問題がないことを完了したのでこの修正を"),t("code",[s._v("master")]),s._v("ブランチに取り込みます。\n"),t("code",[s._v("master")]),s._v('ブランチに "fix"ブランチの変更を取り込みます。')]),s._v(" "),t("p",[t("code",[s._v("master")]),s._v("ブランチの内容は")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう ぎっと\nこんにちは ぎっと\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("p",[s._v("に"),t("code",[s._v("fix")]),s._v("ブランチの内容")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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("p",[s._v("を取り込みます。すると")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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("p",[s._v("となるはずです。歴史は以下のようになります。")]),s._v(" "),t("p",[t("img",{attrs:{src:a(374),alt:"ブランチをマージする"}})]),s._v(" "),t("p",[s._v("ブランチをマージするには"),t("code",[s._v("git merge")]),s._v("コマンドを使います。")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git merge <取り込みたいブランチ名>\n")])]),s._v(" "),t("div",{staticClass:"line-numbers-wrapper"},[t("span",{staticClass:"line-number"},[s._v("1")]),t("br")])]),t("p",[s._v('💻 "master"ブランチへ移動してから"fix"ブランチの変更内容を、メインである"master"ブランチへ取り込みましょう')]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("$ git switch master\n$ git merge fix\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("マージするときのコミットコメントはデフォルトでOKです。")]),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("$ git log\n$ git show\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("fix")]),s._v("ブランチで行ったコミットが取り込まれているでしょうか?")]),s._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[s._v("チェックポイント2 🏁")]),s._v(" "),t("p",[s._v("hello.txtが以下のようになっていれば正解です!")]),s._v(" "),t("div",{staticClass:"language- line-numbers-mode"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("おはよう Git\nこんにちは ぎっと\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")])])]),s._v(" "),t("p",[s._v("Git の基本操作を一通り流してやってみました。\nGit ハンズオンは以上で終了です。")]),s._v(" "),t("p",[s._v("次は "),t("a",{attrs:{href:"../github"}},[s._v("GitHub ハンズオン")]),s._v(" に進んでください。")]),s._v(" "),t("h2",{attrs:{id:"_7-参考文献"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-参考文献"}},[s._v("#")]),s._v(" 7. 参考文献")]),s._v(" "),t("p",[s._v("さらに高度な使い方を知りたい方は"),t("a",{attrs:{href:"https://git-scm.com/book/ja/v2",target:"_blank",rel:"noopener noreferrer"}},[s._v("Pro Git"),t("OutboundLink")],1),s._v("が便利です。日本語版が公開されています。")]),s._v(" "),t("p",[s._v("こんな説明じゃブランチがわからん!という人は "),t("a",{attrs:{href:"https://learngitbranching.js.org/?locale=ja",target:"_blank",rel:"noopener noreferrer"}},[s._v("Learn Git Branching (日本語版)"),t("OutboundLink")],1),s._v(" で練習できます。")]),s._v(" "),t("h3",{attrs:{id:"_7-1-guiクライアント"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_7-1-guiクライアント"}},[s._v("#")]),s._v(" 7.1. GUIクライアント")]),s._v(" "),t("p",[s._v("Gitには、機能が限定されますがGUIクライアントも用意されています。おすすめは以下の3つのクライアントです。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://desktop.github.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("GitHub Desktop (Win, macOS)"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://tortoisegit.org/",target:"_blank",rel:"noopener noreferrer"}},[s._v("TortoiseGit (Win)"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://www.sourcetreeapp.com/",target:"_blank",rel:"noopener noreferrer"}},[s._v("SourceTree (Win, macOS)"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("また各種統合開発環境にもGitを操作する機能が含まれています。")]),s._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://code.visualstudio.com/docs/editor/versioncontrol",target:"_blank",rel:"noopener noreferrer"}},[s._v("Using Version Control in VS Code"),t("OutboundLink")],1)]),s._v(" "),t("li",[t("a",{attrs:{href:"https://pleiades.io/help/idea/using-git-integration.html",target:"_blank",rel:"noopener noreferrer"}},[s._v("Git - ヘルプ | IntelliJ IDEA"),t("OutboundLink")],1)])]),s._v(" "),t("p",[s._v("これらのクライアントの利用も検討してください。")]),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/13.dd6eb0fe.js b/assets/js/13.c0592f38.js similarity index 98% rename from assets/js/13.dd6eb0fe.js rename to assets/js/13.c0592f38.js index 645cf7f0..36ce1f8c 100644 --- a/assets/js/13.dd6eb0fe.js +++ b/assets/js/13.c0592f38.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{256:function(t,s,e){t.exports=e.p+"assets/img/fork-repo.e0dbd328.jpg"},279:function(t,s,e){t.exports=e.p+"assets/img/drone_first_test.31192e41.png"},280:function(t,s,e){t.exports=e.p+"assets/img/drone_steps.6a94640c.png"},281:function(t,s,e){t.exports=e.p+"assets/img/drone_test_failed.3ffdd2f3.png"},282:function(t,s,e){t.exports=e.p+"assets/img/drone_pull_request_button.39571d74.png"},283:function(t,s,e){t.exports=e.p+"assets/img/drone_pull_request_result.6e4f0582.png"},284:function(t,s,e){t.exports=e.p+"assets/img/drone_branch_protection.6e9707f7.png"},285:function(t,s,e){t.exports=e.p+"assets/img/drone_reject_merge.260c0651.png"},286:function(t,s,e){t.exports=e.p+"assets/img/drone_textlint_error.9c7cad75.png"},507:function(t,s,e){"use strict";e.r(s);var a=e(10),n=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"drone-でciテスト・デプロイを回す"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#drone-でciテスト・デプロイを回す"}},[t._v("#")]),t._v(" drone でCIテスト・デプロイを回す")]),t._v(" "),s("h2",{attrs:{id:"_0-この講義について"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-この講義について"}},[t._v("#")]),t._v(" 0. この講義について")]),t._v(" "),s("h3",{attrs:{id:"_0-1-この講義の目的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-1-この講義の目的"}},[t._v("#")]),t._v(" 0.1 この講義の目的")]),t._v(" "),s("p",[t._v("継続的インテグレーション(Continuous Integration)、継続的デリバリ(Continuous Delivery)について理解する。\ndrone を利用したCI/CDを体験し、自分のプロジェクトにCI/CDを自ら導入できるようにする。")]),t._v(" "),s("h3",{attrs:{id:"_0-2-ハンズオンの対象者"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-2-ハンズオンの対象者"}},[t._v("#")]),t._v(" 0.2 ハンズオンの対象者")]),t._v(" "),s("p",[t._v("これからプログラムを書く、またはテキストファイルによる設定ファイル、マニュアル、仕様書などを記述する可能性のある技術者を対象としています。")]),t._v(" "),s("p",[t._v("講義にあたって事前に以下の要件を満たすようにしてください。")]),t._v(" "),s("ul",[s("li",[t._v("YAMLの読み書きができること\n"),s("ul",[s("li",[t._v("知らない場合は"),s("a",{attrs:{href:"https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes",target:"_blank",rel:"noopener noreferrer"}},[t._v("Learn YAML in five minutes!"),s("OutboundLink")],1),t._v("をご覧ください。")])])]),t._v(" "),s("li",[t._v("「Gitの使い方+GitHubを使った開発手法」を受講しておくこと\n"),s("ul",[s("li",[t._v("この講義の中では Git の操作に加え GitHub 上での操作も必要になります。アカウントがない場合は事前に用意してください。")])])])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント1 🏁")]),t._v(" "),s("p",[t._v("Gitの使い方+GitHubを使った開発手法を受講しましたか?"),s("br"),t._v("\ngit clone, checkout, add, commit, push などを利用します。")])]),t._v(" "),s("h3",{attrs:{id:"_0-3-下準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-3-下準備"}},[t._v("#")]),t._v(" 0.3 下準備")]),t._v(" "),s("ul",[s("li",[t._v("Gitを利用できる環境を準備してください。")]),t._v(" "),s("li",[t._v("お好みのテキストエディタを準備してください。\n"),s("ul",[s("li",[t._v("この講義では"),s("a",{attrs:{href:"https://azure.microsoft.com/ja-jp/products/visual-studio-code/",target:"_blank",rel:"noopener noreferrer"}},[t._v("VSCode"),s("OutboundLink")],1),t._v("を推奨します。\n"),s("a",{attrs:{href:"https://atom.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Atom"),s("OutboundLink")],1),t._v("や"),s("a",{attrs:{href:"https://www.sublimetext.com/3",target:"_blank",rel:"noopener noreferrer"}},[t._v("Sublime Text"),s("OutboundLink")],1),t._v("、"),s("a",{attrs:{href:"https://notepad-plus-plus.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Nodepad++"),s("OutboundLink")],1),t._v("を使ってもかまいません。Vimに慣れている人はVimを使ってもよいです。\nメモ帳、サクラエディタ、TeraPadは非推奨です。")])])])]),t._v(" "),s("h3",{attrs:{id:"_0-4-この資料のお約束"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-4-この資料のお約束"}},[t._v("#")]),t._v(" 0.4. この資料のお約束")]),t._v(" "),s("p",[t._v("💻 は自分で操作する箇所を示しています。")]),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("git clone <リモートリポジトリのアドレス>\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),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("git clone git@github.com:iij/bootcamp.git\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("というように置き換えてください。")]),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("継続的インテグレーション(Continuous Integration、以下CI)とは、\nアプリケーションのリリースサイクルにおいてビルドやテストなどを自動化し、\n継続的に実行することで品質改善や納期短縮を実現するための方法です。")]),t._v(" "),s("p",[t._v("もしかしたら自分のプロジェクトがそうかもれませんが、プログラミングは意外と手作業の多い分野でした。\nしかしながら手作業でビルド、テストをしていると不具合が含まれていても発覚するのが遅くなり、手戻りが大きくなってしまいます。\nそこでビルドやテストを自動化し、コードがpushされたらすぐに実行することで早期に不具合を見つけようというのがCIです。\nまた世の中にはビルドが難しいプロダクトというのも多数存在しますので、自動化されているということは開発メンバーを追加するのも楽になります。")]),t._v(" "),s("p",[t._v("継続的デリバリ(Continuous Delivery、以下CD)とはCIをさらに進めてユーザーに製品を届けるまでのリリースプロセス全体を自動化し、\n"),s("strong",[t._v("継続的に顧客に価値を届ける")]),t._v("ことを目的とした手法です。")]),t._v(" "),s("p",[t._v("CI/CDを導入した場合、コードをコミットするとビルドが走り、ユニットテストを通して、コードがテスト環境にデプロイされます。その後結合テスト、\nシステムテストを行い、必要であれば承認後、本番環境にデプロイされます。")]),t._v(" "),s("h2",{attrs:{id:"_2-droneとは"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-droneとは"}},[t._v("#")]),t._v(" 2. droneとは")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone"),s("OutboundLink")],1),t._v(" とはdockerをベースとしたCI/CDのためのプラットフォームです。\nIIJ社内ではdrone v1.0を提供しています。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("社内のプライベートな環境で使えるdroneが用意されていることがあります。\n講師の指示がある場合はそちらの環境を利用しましょう。")])]),t._v(" "),s("ul",[s("li",[t._v("サイト: "),s("a",{attrs:{href:"https://cloud.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://cloud.drone.io/"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("ドキュメント: "),s("a",{attrs:{href:"https://docs.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.drone.io/"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("GitHubと連携して簡単に設定を行うことができ、設定もyamlに記載するシンプルなもので、dockerベースのため環境構築も簡単に行うことができます。")]),t._v(" "),s("h3",{attrs:{id:"_2-1-とりあえず初めてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-とりあえず初めてみる"}},[t._v("#")]),t._v(" 2.1. とりあえず初めてみる")]),t._v(" "),s("h4",{attrs:{id:"_2-1-1-droneの設定を有効化する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-1-droneの設定を有効化する"}},[t._v("#")]),t._v(" 2.1.1. droneの設定を有効化する")]),t._v(" "),s("p",[t._v("このハンズオンのためにCI/CDを行うためのサンプルリポジトリを "),s("a",{attrs:{href:"https://github.com/iij/drone-exercise",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/iij/drone-exercise"),s("OutboundLink")],1),t._v(" に用意しています。")]),t._v(" "),s("p",[t._v("💻 GitHub上で操作し、作業用リポジトリを作成してください。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(256),alt:"droneで最初のテスト"}})]),t._v(" "),s("p",[t._v("上記リポジトリを開いて「Use this template」を押してください。"),s("br"),t._v("\nリポジトリ名は「"),s("code",[t._v("drone-exercise-")]),t._v("」にしましょう。"),s("br"),t._v("\nほかの設定値はデフォルトで良いです。")]),t._v(" "),s("p",[t._v("ここで作成したリポジトリに対して操作をしていきます。")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("Use this template が表示されていない場合は正しくログインできているか確認してください。"),s("br"),t._v("\n講師から repository を作成する organization が指示されていれば、それに従ってください。")])]),t._v(" "),s("p",[t._v("droneはGitHub上のコミットやpushといったイベントが発生するとそれに応じて自動的に処理が走るようになっています。\nこれはWebhookというしくみを用いて実現されていますが、droneを使う前にこの設定が必要です。")]),t._v(" "),s("p",[t._v("💻 droneにリポジトリを登録する。")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://cloud.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://cloud.drone.io/"),s("OutboundLink")],1),t._v(" では 新規のユーザー登録を行っていません。"),s("br"),t._v("\nそのため、以下の手順を実施するためには "),s("a",{attrs:{href:"http://drone.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone.io"),s("OutboundLink")],1),t._v(" の 構築が必要です。"),s("br"),t._v("\n講師は接続先を案内してください。")])]),t._v(" "),s("p",[s("a",{attrs:{href:"http://xn--drone-f83dqcwesos420avc3aymzhv8g.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("講師に案内されたdrone.io"),s("OutboundLink")],1),t._v(" にログインしてください。")]),t._v(" "),s("p",[t._v("初回のログインでは OAuth の連携の許可が必要になるかもしれません。")]),t._v(" "),s("p",[t._v("Repositories の リストから「自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v("」を探してリポジトリ名をクリックし詳細ページを開きましょう。")]),t._v(" "),s("p",[t._v("見つからない場合は「SYNC」ボタンを押してから探してください。")]),t._v(" "),s("p",[t._v("「SETTINGS」タブから「ACTIVATE REPOSITORY」をクリックすると自動で設定が行われ、設定画面が表示されます。")]),t._v(" "),s("p",[t._v("これでdroneを利用する準備が整いました。")]),t._v(" "),s("h4",{attrs:{id:"_2-1-2-droneでテストを実行する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-2-droneでテストを実行する"}},[t._v("#")]),t._v(" 2.1.2. droneでテストを実行する")]),t._v(" "),s("p",[t._v("💻 作成した作業用リポジトリ(自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v(")をローカルにgit cloneしてください。")]),t._v(" "),s("p",[t._v("このリポジトリにはすでにdroneの設定ファイルが置かれています。\n適当に"),s("code",[t._v("README.md")]),t._v("を編集してコミット、pushしてみましょう。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("おさらいです。 編集したあとは git add でステージング したのち commit && push となります。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('date >> README.md\ngit add README.md\ngit commit -m "update README.md"\ngit push origin master\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")])])]),t._v(" "),s("p",[t._v("GitHub に push してしばらくすると drone で テストが実行されます。")]),t._v(" "),s("p",[t._v("先程開いた droneの「自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v("」の 詳細ページから「ACTIVITY FEED」タブを開くとテストの実行ログが表示されます。")]),t._v(" "),s("p",[t._v("クリックして 実行ログを読むとどのようにテストが実行されているかが分かります。")]),t._v(" "),s("p",[t._v("このリポジトリにはRubyで書かれたプログラムと、Ruby用のテストフレームワークである"),s("a",{attrs:{href:"https://rspec.info/",target:"_blank",rel:"noopener noreferrer"}},[t._v("RSpec"),s("OutboundLink")],1),t._v("で\n書かれたテストが置かれています。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(279),alt:"droneで最初のテスト"}})]),t._v(" "),s("p",[t._v("図を見ると、 clone と test の step からなるとわかります。")]),t._v(" "),s("p",[t._v("それぞれクリックすると各 step の詳細が表示されます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント2 🏁")]),t._v(" "),s("p",[t._v("「test」ではどういうメッセージが出力されたでしょうか?")])]),t._v(" "),s("h4",{attrs:{id:"_2-1-3-webhookの設定確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-3-webhookの設定確認"}},[t._v("#")]),t._v(" 2.1.3 Webhookの設定確認")]),t._v(" "),s("p",[t._v("drone と GitHub の連携には Webhook を利用しています。")]),t._v(" "),s("p",[t._v("デフォルトでは"),s("code",[t._v("pr")]),t._v("と"),s("code",[t._v("push")]),t._v("の2つが登録されています。")]),t._v(" "),s("p",[s("code",[t._v("pr")]),t._v("を指定すると、Pull Requestをオープンしたとき、または既存のPRへpushしたときにテストが実行されます。\n"),s("code",[t._v("push")]),t._v(" を指定すると、"),s("code",[t._v("git push")]),t._v(" したときにテストが実行されます。")]),t._v(" "),s("p",[t._v("この設定は 「Settings」->「Hooks」->「Webhooks」-> droneのエントリ -> Edit で確認でき、")]),t._v(" "),s("p",[t._v("設定画面最下部の「Recent Deliveries」では実際に発行されたWebhookを確認 & 再送信できます。")]),t._v(" "),s("h2",{attrs:{id:"_3-droneの基本的な設定"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-droneの基本的な設定"}},[t._v("#")]),t._v(" 3. droneの基本的な設定")]),t._v(" "),s("p",[t._v("drone設定の基本は、どういった環境で、どのようなコマンドを実行するかということを記述することです。")]),t._v(" "),s("p",[t._v("設定はKubernetesライクな書き方になっていますので、Kubernetesの知識があれば読みやすいです。")]),t._v(" "),s("p",[t._v("droneはバージョンによって設定ファイルの書き方が異なりますので、\n既存のプロジェクトを編集するときは気を付けてください。")]),t._v(" "),s("p",[t._v("droneの設定はデフォルトでリポジトリの一番上の階層に"),s("code",[t._v(".drone.yml")]),t._v("という名前で置きます。")]),t._v(" "),s("p",[t._v("2.1.2 でcloneしたリポジトリの"),s("code",[t._v(".drone.yml")]),t._v("を見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\n\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle install\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" rspec\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("p",[t._v("各項目について解説していきます。")]),t._v(" "),s("h3",{attrs:{id:"_3-1-pipeline"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-1-pipeline"}},[t._v("#")]),t._v(" 3.1. Pipeline")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\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("Pipeline")]),t._v("と呼びます。")]),t._v(" "),s("p",[t._v("まずyamlの先頭で"),s("code",[t._v("kind: pipeline")]),t._v("と記載し、この下に書かれる設定値がPipelineのものであることを宣言します。\nただIIJの環境では"),s("code",[t._v("Pipeline")]),t._v("以外の設定は利用できませんのでこの"),s("code",[t._v("kind")]),t._v("と"),s("code",[t._v("name")]),t._v("は固定になります。")]),t._v(" "),s("p",[s("code",[t._v("Pipeline")]),t._v("は複数の"),s("code",[t._v("Step")]),t._v("で構成されます。")]),t._v(" "),s("h3",{attrs:{id:"_3-2-steps"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-2-steps"}},[t._v("#")]),t._v(" 3.2. Steps")]),t._v(" "),s("p",[t._v("ひとつのPipelineで複数のテスト("),s("code",[t._v("Step")]),t._v(")を実行できます。")]),t._v(" "),s("p",[t._v("各"),s("code",[t._v("Step")]),t._v("は別々のdockerコンテナで実行され、各テストは独立した環境でテストできます。")]),t._v(" "),s("p",[t._v("UI上では各"),s("code",[t._v("Step")]),t._v("ごとに結果が分けて表示され、"),s("code",[t._v("name")]),t._v("で名前を付けることができ、これはUI上に表示されます。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(280),alt:"Stepの表示"}})]),t._v(" "),s("p",[s("code",[t._v("image")]),t._v(" はテストに利用するdockerイメージを指定します。")]),t._v(" "),s("p",[s("code",[t._v("image: ruby")]),t._v(" と指定した場合はDocker Hubの"),s("a",{attrs:{href:"https://hub.docker.com/_/ruby",target:"_blank",rel:"noopener noreferrer"}},[t._v("Ruby Official Image"),s("OutboundLink")],1),t._v("が利用されます。\n素性のわからないイメージを利用することはやめましょう。")]),t._v(" "),s("p",[t._v("また、"),s("code",[t._v("image: ruby:3.1.2")]),t._v(" のようにタグを指定して、特定のバージョンを利用できるイメージもありますが意図せず更新される場合があります。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("プライベートなDockerイメージ置き場を自分で作成することもできます。")])]),t._v(" "),s("p",[s("code",[t._v("commands")]),t._v(" にはコンテナ内で実行するコマンドを記述します。")]),t._v(" "),s("p",[s("code",[t._v("bundle install")]),t._v(" ではテスト実行に必要なライブラリをインストールしていて、"),s("code",[t._v("rspec")]),t._v("でテストを実行しています。")]),t._v(" "),s("p",[t._v("各テストが成功したかどうかは各コマンドのExit Codeを見ていて、Code 0以外ではテストは中断され失敗となります。\nテストが失敗すると以下のような表示になります。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(281),alt:"テスト失敗時の表示"}})]),t._v(" "),s("h2",{attrs:{id:"_4-pull-requestと組み合わせる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4-pull-requestと組み合わせる"}},[t._v("#")]),t._v(" 4. Pull Requestと組み合わせる")]),t._v(" "),s("p",[t._v("droneを設定した状態でPull Requestを作成するとどうなるでしょうか。")]),t._v(" "),s("p",[t._v("💻 意図的にテストが失敗するようにコードを修正し、Pull Requestを作成してみましょう。")]),t._v(" "),s("ol",[s("li",[t._v("まず、別の branch へ checkout します")])]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("git checkout -b feature/text-error\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("ol",{attrs:{start:"2"}},[s("li",[t._v("実装である"),s("code",[t._v("hello_world.rb")]),t._v("を以下のように書き換えてみます。")])]),t._v(" "),s("div",{staticClass:"language-diff line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" def world\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" 'Hello World'\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" 'Goodby World'\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" end\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("ol",{attrs:{start:"3"}},[s("li",[t._v("編集した内容を commit し push します。")])]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add hello_world.rb\ngit commit -m "goodby"\ngit push origin feature/text-error\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("💻 GitHub を開いてPull Requestを作成しましょう。")]),t._v(" "),s("p",[t._v("別のbranch に push した内容を develop branch などへ取り込んでもらうためのリクエストを Pull Request(PR) と呼びます。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(282),alt:"Pull Requestを作成しましょう"}})]),t._v(" "),s("p",[t._v("もし、作業リポジトリを fork して作成した場合 PR の送り先が fork 元 repository になっています。")]),t._v(" "),s("p",[t._v("その時は 自分のrepository に PR を送るように base repository (左側) の 表記を見直してください。")]),t._v(" "),s("p",[t._v("無事PRを作成できた場合 PRのページへ遷移します。")]),t._v(" "),s("p",[t._v("ページ下部にdroneのテスト結果が表示されています。")]),t._v(" "),s("p",[t._v("一目でテストが失敗していることがわかるでしょう。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(283),alt:"Pull Requestに表示されたdroneの結果"}})]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント3 🏁")]),t._v(" "),s("p",[t._v("Pull Request は作成できましたか?"),s("br"),t._v(" "),s("a",{attrs:{href:"http://drone.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone.io"),s("OutboundLink")],1),t._v(" は動作しましたか?"),s("br"),t._v("\nテストが失敗しましたか?")])]),t._v(" "),s("h3",{attrs:{id:"_4-1-テストが失敗したらマージできないようにしたい"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-テストが失敗したらマージできないようにしたい"}},[t._v("#")]),t._v(" 4.1. テストが失敗したらマージできないようにしたい")]),t._v(" "),s("p",[t._v("さて、 先程のPRではテストに失敗してしまいました。")]),t._v(" "),s("p",[t._v("この状態でもMergeボタンを押すことは可能ですが、普通は押されたくないはずです。\nこの挙動はGitHubの設定画面から変更できます。")]),t._v(" "),s("p",[t._v("💻 テストが通ったときだけマージできるように設定する")]),t._v(" "),s("ol",[s("li",[t._v("「Settings」->「Branches」->「Branch protection rules」->「Add rule」を押し、")]),t._v(" "),s("li",[t._v("「Branch name pattern」に「master」と記入し、")]),t._v(" "),s("li",[t._v("「Include administrators」にチェックを入れます。")]),t._v(" "),s("li",[t._v("「Require status checks to pass before merging」にチェックを入れて")]),t._v(" "),s("li",[t._v("「Status checks found in the last week for this repository」に出ている\n「continuous-integration/drone/pr」と\n「continuous-integration/drone/push」にチェックを入れます。")]),t._v(" "),s("li",[t._v("「Create」します。")])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("もし、continuous-integration/drone/pr が見つからない場合"),s("br"),t._v("\n先ほど作成したPRによる drone のテストがうまく動いていないかもしれません。"),s("br"),t._v("\nfork した場合は PR の作成先を確認する必要があります。"),s("br"),t._v("\nPR の作成先が間違っているかもしれません。見直してください。")])]),t._v(" "),s("p",[s("img",{attrs:{src:e(284),alt:"Branch protection rules"}})]),t._v(" "),s("p",[t._v("先程作成したPull Requestのページに戻るとマージボタンが押せなくなっています。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(285),alt:"マージできない状態"}})]),t._v(" "),s("p",[t._v("これは複数人で開発するときには便利な機能です。")]),t._v(" "),s("p",[t._v("このあとmasterブランチを利用しますのでBranch protection rulesは削除しておきましょう。")]),t._v(" "),s("p",[t._v("💻 Branch protection rules の一覧ページで"),s("code",[t._v("master")]),t._v("と名前がついたルールの"),s("code",[t._v("delete")]),t._v("ボタンを押す")]),t._v(" "),s("p",[t._v("💻 この後項目のためにmasterブランチに戻っておきましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ git checkout master\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント4 🏁")]),t._v(" "),s("p",[t._v("マージボタンが押せなくなったのはなぜですか?")])]),t._v(" "),s("h2",{attrs:{id:"_5-さまざまなプラグイン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-さまざまなプラグイン"}},[t._v("#")]),t._v(" 5. さまざまなプラグイン")]),t._v(" "),s("p",[s("a",{attrs:{href:"http://plugins.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("droneには様々なプラグインが用意されています。"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("主に外部と連携する機能が用意されています。\n後述する利用のしかたからも分かるとおり plugin は 単なる docker コンテナであるため、自分で開発することもできます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("Plugins are just Docker containers which means you can write plugins in any programming language that runs inside a container. You can even create plugins using simple bash scripting.\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("blockquote",[s("p",[s("a",{attrs:{href:"https://docs.drone.io/plugins/overview/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.drone.io/plugins/overview/"),s("OutboundLink")],1),t._v(" より引用")])]),t._v(" "),s("h3",{attrs:{id:"_5-1-キャッシュプラグイン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-キャッシュプラグイン"}},[t._v("#")]),t._v(" 5.1. キャッシュプラグイン")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("この項目はS3互換のオブジェクトストレージが必要です。\n講師は接続先を案内してください。")])]),t._v(" "),s("p",[t._v("これまでの例ではテストを実行するときに必要なライブラリを"),s("code",[t._v("bundle install")]),t._v("で事前にダウンロードしてから実行していました。\nしかし毎回ダウンロードしていたのではテスト実行に時間がかかりますし、ネットワークの無駄です。")]),t._v(" "),s("p",[s("a",{attrs:{href:"http://plugins.drone.io/drone-plugins/drone-s3-cache/",target:"_blank",rel:"noopener noreferrer"}},[t._v("s3-cache"),s("OutboundLink")],1),t._v("プラグインを使うと特定のディレクトリを\n"),s("a",{attrs:{href:"https://aws.amazon.com/jp/s3/",target:"_blank",rel:"noopener noreferrer"}},[t._v("S3"),s("OutboundLink")],1),t._v("に保存し、テストのたびに復元してくれる機能を提供します。")]),t._v(" "),s("h4",{attrs:{id:"_5-1-1-ライブラリの保存場所を変更する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-1-ライブラリの保存場所を変更する"}},[t._v("#")]),t._v(" 5.1.1. ライブラリの保存場所を変更する")]),t._v(" "),s("p",[t._v("パッケージマネージャーによってはリポジトリの中ではなく、システムの特別な場所に配置するものがあります。\ns3-cacheプラグインはリポジトリ内にあるファイルしかキャッシュできません。")]),t._v(" "),s("p",[t._v("nodejsのパッケージマネージャーであるnpmはデフォルトでリポジトリ直下にライブラリを配置しますが、\nRubyのパッケージマネージャーであるbundlerはシステム領域にライブラリを保存するため、\nそのままではキャッシュさせることができません。\nほとんどの場合、パッケージマネージャーのオプションで保存場所を変更できますので、\nRubyを例に設定してみましょう。")]),t._v(" "),s("p",[t._v("Rubyのパッケージマネージャーであるbundlerは"),s("code",[t._v("--path")]),t._v("オプションで保存場所を指定できます。")]),t._v(" "),s("p",[s("code",[t._v("drone.yaml")]),t._v(" で 実行している "),s("code",[t._v("bundle install")]),t._v(" にオプションを渡し 一般的によく使われる"),s("code",[t._v("vendor/bundle")]),t._v("に保存するように変更してください。")]),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(" - name: test\n image: ruby:2.6.2\n commands:\n - bundle install --path vendor/bundle\n - bundle exec rspec\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("h4",{attrs:{id:"_5-1-2-ライブラリをキャッシュさせてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-2-ライブラリをキャッシュさせてみる"}},[t._v("#")]),t._v(" 5.1.2. ライブラリをキャッシュさせてみる")]),t._v(" "),s("p",[t._v("さっそくキャッシュさせてみましょう。")]),t._v(" "),s("p",[t._v("このプラグインはキャッシュしたデータを戻す"),s("code",[t._v("restore")]),t._v("と、キャッシュを行う"),s("code",[t._v("rebuild")]),t._v("の機能があります。")]),t._v(" "),s("p",[s("code",[t._v(".drone.yml")]),t._v("を編集してキャッシュを組み込んでみましょう。\n"),s("code",[t._v("rebuild")]),t._v("ステップで"),s("code",[t._v("vendor/bundle")]),t._v("ディレクトリをキャッシュし、\n"),s("code",[t._v("restore")]),t._v("ステップでキャッシュされたものを戻します。")]),t._v(" "),s("p",[s("code",[t._v("test")]),t._v("Stepの前後に、"),s("code",[t._v("restore")]),t._v("と"),s("code",[t._v("rebuild")]),t._v("を追加します。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("以下の設定は各環境で用意された接続先へ値を変更してください。")]),t._v(" "),s("p",[t._v("<変数名>で書かれた場所を適切な値で置き換えてください。")])]),t._v(" "),s("p",[t._v("💻 パッケージをキャッシュするように変更する")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" restore\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" plugins/s3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("cache\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("settings")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("pull")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("endpoint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("access_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("secret_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("restore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle install "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("path vendor/bundle\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rspec\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rebuild\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" plugins/s3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("cache\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("settings")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("pull")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("endpoint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("access_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("secret_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("rebuild")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("mount")]),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(" vendor/bundle\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("p",[t._v("💻 コミットして実行してみましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add .drone.yml\ngit commit -m "Cache導入"\ngit push origin master\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("droneのUI上で最新の実行ログを開いて見ましょう。\n"),s("code",[t._v("restore")]),t._v("と"),s("code",[t._v("rebuild")]),t._v("のステップが増えていることを確認してください。")]),t._v(" "),s("p",[t._v("1回目はキャッシュされてないので何も起きませんが、2回目からはキャッシュが使われるので実行が早くなります。\n1回目の実行時間を確認してから、以下のように2回目のテストを実行してみてください。")]),t._v(" "),s("p",[s("code",[t._v("--allow-empty")]),t._v(" はファイルを変更していなくてもコミットを作ることができるオプションです。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git commit --allow-empty -m "Cacheの効果を確認する"\ngit push origin master\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("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("このキャッシュを利用する操作は drone で完結しない処理です。"),s("br"),t._v("\nもしオブジェクトストレージ側にトラブルがあれば 処理が長時間に及ぶ可能性があります。")]),t._v(" "),s("p",[t._v("また、drone 上の ジョブの同時実行数には限りがある場合があります。"),s("br"),t._v("\n処理が長時間に渡る場合は drone 上のジョブ実行結果の確認画面から キャンセルができます")])]),t._v(" "),s("p",[t._v("💻 テスト実行が早くなっていることを確認しましょう。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント5 🏁")]),t._v(" "),s("p",[t._v("テストが速くなったのはなぜですか?")])]),t._v(" "),s("h2",{attrs:{id:"_6-さまざまな応用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-さまざまな応用"}},[t._v("#")]),t._v(" 6. さまざまな応用")]),t._v(" "),s("h3",{attrs:{id:"_6-1-text-lintを使った日本語チェックの例"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-1-text-lintを使った日本語チェックの例"}},[t._v("#")]),t._v(" 6.1. Text Lintを使った日本語チェックの例")]),t._v(" "),s("p",[t._v("droneはプログラムにしか使えないものでしょうか。そうではありません。\ndroneは何でも動かすことができますから、テストをすることだけが仕事ではありません。")]),t._v(" "),s("p",[t._v("ここでは自然言語のチェックを行う "),s("a",{attrs:{href:"https://textlint.github.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("textlint"),s("OutboundLink")],1),t._v(" を使った例を紹介します。")]),t._v(" "),s("p",[t._v("💻 stepsに以下の項目を追加してみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" textlint\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" node\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" npm install textlint\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" npm install textlint"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rule"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("preset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("technical"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("writing\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" $(npm bin)/textlint "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("format pretty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("error "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("preset ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("technical"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("writing README.md\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("p",[t._v("textlintはnodejsで動作しますので、イメージにnodeを指定します。\ntextlintはフレームワークのみでこれ単体で動かすことはできません。どういったものをチェックするかというルールは別に定義しなければいけません。\nここでは日本語の技術文書を書くうえで必要ないくつかのルールをまとめた\n"),s("a",{attrs:{href:"https://github.com/textlint-ja/textlint-rule-preset-ja-technical-writing",target:"_blank",rel:"noopener noreferrer"}},[t._v("textlint-rule-preset-ja-technical-writing"),s("OutboundLink")],1),t._v("\nを利用します。試しに "),s("code",[t._v("README.md")]),t._v("をチェックしてみましょう。")]),t._v(" "),s("p",[t._v("💻 README.mdをTextlintでチェックする。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add .drone.yml\ngit commit -m "textlintによるチェック"\ngit push origin master\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",[s("img",{attrs:{src:e(286),alt:"textlintのエラー"}})]),t._v(" "),s("p",[t._v("このようにチェックする対象はプログラムに限りませんので、マニュアルなどのドキュメントのチェックなどにも活用できます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント6 🏁")]),t._v(" "),s("p",[t._v("droneが利用できる事例としてふさわしいものはどれですか?")])]),t._v(" "),s("h3",{attrs:{id:"_6-2-services"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-2-services"}},[t._v("#")]),t._v(" 6.2 Services")]),t._v(" "),s("p",[t._v("単純なスクリプトやライブラリのテストはこれだけでも十分にdroneでテストできます。")]),t._v(" "),s("p",[t._v("ではデータベース使ったテストはできるでしょうか。")]),t._v(" "),s("p",[t._v("データベースのテストをするならデータベースのプロセスが上がっている必要があります。"),s("br"),t._v("\nでも1つのコンテナでアプリケーションと一緒にデータベースも一緒に立ち上げたくないですよね。")]),t._v(" "),s("p",[t._v("そのために"),s("code",[t._v("Service")]),t._v("というしくみがあります。"),s("br"),t._v("\n同時に複数のコンテナを立ち上げて待機させておき、テスト中利用できます。")]),t._v(" "),s("p",[t._v("サンプルとしてRuby on Rails+MySQLで構成されたアプリケーションを用意しました。")]),t._v(" "),s("p",[t._v("💻 "),s("a",{attrs:{href:"https://github.com/iij/drone-exercise-rails",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/iij/drone-exercise-rails"),s("OutboundLink")],1),t._v(" から 作業用リポジトリを作成し、git cloneしてください。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(256),alt:"rails テスト"}})]),t._v(" "),s("p",[t._v("上記リポジトリを開いて「Use this template」を押してください。")]),t._v(" "),s("p",[t._v("リポジトリ名は「"),s("code",[t._v("drone-exercise-rails-")]),t._v("」にしましょう。"),s("br"),t._v("\nほかの設定値はデフォルトで良いです。")]),t._v(" "),s("p",[t._v("ここで作成したリポジトリに対して操作をしていきます。")]),t._v(" "),s("p",[t._v("Ruby on RailsはWebアプリケーションを作るためのRuby製フレームワークです。\nデータの保存にMySQLなどのデータベースを利用できます。")]),t._v(" "),s("p",[t._v("標準的なWebアプリケーションではデータベースなどの外部サービスにデータを保存し、それを読み取って加工して表示するという動作が多く、\nテストするときもデータベースが動いている必要があります。")]),t._v(" "),s("p",[t._v("上記リポジトリにはテストを行うだけの "),s("code",[t._v(".drone.yml")]),t._v(" が含まれています。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\n\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("RAILS_ENV")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 実行に必要なライブラリをインストールする")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rails db"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("reset "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# テスト用のデータベース、テーブルを作成する")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rails test "),s("span",{pre:!0,attrs:{class:"token comment"}},[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("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("新しいrepository への drone の 有効のしかた"),s("br"),t._v("\n内容の変更を伴わないcommit をする方法を思い出してください。")])]),t._v(" "),s("p",[t._v("ちなみにテストは "),s("code",[t._v("test/models/user_test.rb")]),t._v(" に書いてあります。")]),t._v(" "),s("p",[t._v("この状態ではデータベースが動いていないのでテストが成功しません。")]),t._v(" "),s("p",[t._v("テストしている間横で何か動かしておきたいという場合には "),s("code",[t._v("Service")]),t._v(" を使います。\n今回はMySQLを使いますが、"),s("a",{attrs:{href:"https://hub.docker.com/_/mysql",target:"_blank",rel:"noopener noreferrer"}},[t._v("MySQLは公式でdockerイメージが提供されています"),s("OutboundLink")],1),t._v("のでこれを利用します。")]),t._v(" "),s("p",[t._v("💻 以下の項目を"),s("code",[t._v(".drone.yml")]),t._v("に追加してください。")]),t._v(" "),s("p",[t._v("kind, name, steps と同じ高さでよいです(services の左側にスペースはいりません)")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" db\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mysql"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("8.0.16\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--default-authentication-plugin=mysql_native_password"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# MySQL8.0のデフォルト認証方式にRailsが対応していないため変更")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MYSQL_ALLOW_EMPTY_PASSWORD")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'yes'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[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("p",[s("code",[t._v("name")]),t._v(" で名前をつけて "),s("code",[t._v("image")]),t._v(" で使用するdockerイメージを指定します。")]),t._v(" "),s("p",[t._v("Railsではデータベースの設定を"),s("code",[t._v("config/database.yml")]),t._v("から読み込みます。"),s("br"),t._v("\ndroneで設定したデータベースへ接続するための設定値を見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("test")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("<<")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token important"}},[t._v("*default")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("host")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" db\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3306")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("socket")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token null important"}},[t._v("null")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("database")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" drone"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("exercise"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rails_test\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("p",[t._v("このファイルで接続先MySQLサーバのホストを指定しているのですが、"),s("br"),t._v("\nここでは"),s("code",[t._v(".drone.yml")]),t._v("で指定した"),s("code",[t._v("db")]),t._v("がホスト名になっていて、この名前で接続できます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント7 🏁")]),t._v(" "),s("p",[t._v("テスト実行中にデータベースはどこで実行されているでしょうか?")])]),t._v(" "),s("h2",{attrs:{id:"_8-参考情報"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_8-参考情報"}},[t._v("#")]),t._v(" 8. 参考情報")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://docs.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone Documentation"),s("OutboundLink")],1)])]),t._v(" "),s("h2",{attrs:{id:"続き"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#続き"}},[t._v("#")]),t._v(" 続き")]),t._v(" "),s("ul",[s("li",[s("RouterLink",{attrs:{to:"/cicd_infra/github_actions/"}},[t._v("GitHub Actions でCIテスト・デプロイを回す")])],1)])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{256:function(t,s,e){t.exports=e.p+"assets/img/fork-repo.e0dbd328.jpg"},300:function(t,s,e){t.exports=e.p+"assets/img/drone_first_test.31192e41.png"},301:function(t,s,e){t.exports=e.p+"assets/img/drone_steps.6a94640c.png"},302:function(t,s,e){t.exports=e.p+"assets/img/drone_test_failed.3ffdd2f3.png"},303:function(t,s,e){t.exports=e.p+"assets/img/drone_pull_request_button.39571d74.png"},304:function(t,s,e){t.exports=e.p+"assets/img/drone_pull_request_result.6e4f0582.png"},305:function(t,s,e){t.exports=e.p+"assets/img/drone_branch_protection.6e9707f7.png"},306:function(t,s,e){t.exports=e.p+"assets/img/drone_reject_merge.260c0651.png"},307:function(t,s,e){t.exports=e.p+"assets/img/drone_textlint_error.9c7cad75.png"},508:function(t,s,e){"use strict";e.r(s);var a=e(10),n=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"drone-でciテスト・デプロイを回す"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#drone-でciテスト・デプロイを回す"}},[t._v("#")]),t._v(" drone でCIテスト・デプロイを回す")]),t._v(" "),s("h2",{attrs:{id:"_0-この講義について"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-この講義について"}},[t._v("#")]),t._v(" 0. この講義について")]),t._v(" "),s("h3",{attrs:{id:"_0-1-この講義の目的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-1-この講義の目的"}},[t._v("#")]),t._v(" 0.1 この講義の目的")]),t._v(" "),s("p",[t._v("継続的インテグレーション(Continuous Integration)、継続的デリバリ(Continuous Delivery)について理解する。\ndrone を利用したCI/CDを体験し、自分のプロジェクトにCI/CDを自ら導入できるようにする。")]),t._v(" "),s("h3",{attrs:{id:"_0-2-ハンズオンの対象者"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-2-ハンズオンの対象者"}},[t._v("#")]),t._v(" 0.2 ハンズオンの対象者")]),t._v(" "),s("p",[t._v("これからプログラムを書く、またはテキストファイルによる設定ファイル、マニュアル、仕様書などを記述する可能性のある技術者を対象としています。")]),t._v(" "),s("p",[t._v("講義にあたって事前に以下の要件を満たすようにしてください。")]),t._v(" "),s("ul",[s("li",[t._v("YAMLの読み書きができること\n"),s("ul",[s("li",[t._v("知らない場合は"),s("a",{attrs:{href:"https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes",target:"_blank",rel:"noopener noreferrer"}},[t._v("Learn YAML in five minutes!"),s("OutboundLink")],1),t._v("をご覧ください。")])])]),t._v(" "),s("li",[t._v("「Gitの使い方+GitHubを使った開発手法」を受講しておくこと\n"),s("ul",[s("li",[t._v("この講義の中では Git の操作に加え GitHub 上での操作も必要になります。アカウントがない場合は事前に用意してください。")])])])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント1 🏁")]),t._v(" "),s("p",[t._v("Gitの使い方+GitHubを使った開発手法を受講しましたか?"),s("br"),t._v("\ngit clone, checkout, add, commit, push などを利用します。")])]),t._v(" "),s("h3",{attrs:{id:"_0-3-下準備"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-3-下準備"}},[t._v("#")]),t._v(" 0.3 下準備")]),t._v(" "),s("ul",[s("li",[t._v("Gitを利用できる環境を準備してください。")]),t._v(" "),s("li",[t._v("お好みのテキストエディタを準備してください。\n"),s("ul",[s("li",[t._v("この講義では"),s("a",{attrs:{href:"https://azure.microsoft.com/ja-jp/products/visual-studio-code/",target:"_blank",rel:"noopener noreferrer"}},[t._v("VSCode"),s("OutboundLink")],1),t._v("を推奨します。\n"),s("a",{attrs:{href:"https://atom.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Atom"),s("OutboundLink")],1),t._v("や"),s("a",{attrs:{href:"https://www.sublimetext.com/3",target:"_blank",rel:"noopener noreferrer"}},[t._v("Sublime Text"),s("OutboundLink")],1),t._v("、"),s("a",{attrs:{href:"https://notepad-plus-plus.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Nodepad++"),s("OutboundLink")],1),t._v("を使ってもかまいません。Vimに慣れている人はVimを使ってもよいです。\nメモ帳、サクラエディタ、TeraPadは非推奨です。")])])])]),t._v(" "),s("h3",{attrs:{id:"_0-4-この資料のお約束"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_0-4-この資料のお約束"}},[t._v("#")]),t._v(" 0.4. この資料のお約束")]),t._v(" "),s("p",[t._v("💻 は自分で操作する箇所を示しています。")]),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("git clone <リモートリポジトリのアドレス>\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),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("git clone git@github.com:iij/bootcamp.git\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("p",[t._v("というように置き換えてください。")]),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("継続的インテグレーション(Continuous Integration、以下CI)とは、\nアプリケーションのリリースサイクルにおいてビルドやテストなどを自動化し、\n継続的に実行することで品質改善や納期短縮を実現するための方法です。")]),t._v(" "),s("p",[t._v("もしかしたら自分のプロジェクトがそうかもれませんが、プログラミングは意外と手作業の多い分野でした。\nしかしながら手作業でビルド、テストをしていると不具合が含まれていても発覚するのが遅くなり、手戻りが大きくなってしまいます。\nそこでビルドやテストを自動化し、コードがpushされたらすぐに実行することで早期に不具合を見つけようというのがCIです。\nまた世の中にはビルドが難しいプロダクトというのも多数存在しますので、自動化されているということは開発メンバーを追加するのも楽になります。")]),t._v(" "),s("p",[t._v("継続的デリバリ(Continuous Delivery、以下CD)とはCIをさらに進めてユーザーに製品を届けるまでのリリースプロセス全体を自動化し、\n"),s("strong",[t._v("継続的に顧客に価値を届ける")]),t._v("ことを目的とした手法です。")]),t._v(" "),s("p",[t._v("CI/CDを導入した場合、コードをコミットするとビルドが走り、ユニットテストを通して、コードがテスト環境にデプロイされます。その後結合テスト、\nシステムテストを行い、必要であれば承認後、本番環境にデプロイされます。")]),t._v(" "),s("h2",{attrs:{id:"_2-droneとは"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-droneとは"}},[t._v("#")]),t._v(" 2. droneとは")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone"),s("OutboundLink")],1),t._v(" とはdockerをベースとしたCI/CDのためのプラットフォームです。\nIIJ社内ではdrone v1.0を提供しています。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("社内のプライベートな環境で使えるdroneが用意されていることがあります。\n講師の指示がある場合はそちらの環境を利用しましょう。")])]),t._v(" "),s("ul",[s("li",[t._v("サイト: "),s("a",{attrs:{href:"https://cloud.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://cloud.drone.io/"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("ドキュメント: "),s("a",{attrs:{href:"https://docs.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.drone.io/"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("GitHubと連携して簡単に設定を行うことができ、設定もyamlに記載するシンプルなもので、dockerベースのため環境構築も簡単に行うことができます。")]),t._v(" "),s("h3",{attrs:{id:"_2-1-とりあえず初めてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-とりあえず初めてみる"}},[t._v("#")]),t._v(" 2.1. とりあえず初めてみる")]),t._v(" "),s("h4",{attrs:{id:"_2-1-1-droneの設定を有効化する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-1-droneの設定を有効化する"}},[t._v("#")]),t._v(" 2.1.1. droneの設定を有効化する")]),t._v(" "),s("p",[t._v("このハンズオンのためにCI/CDを行うためのサンプルリポジトリを "),s("a",{attrs:{href:"https://github.com/iij/drone-exercise",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/iij/drone-exercise"),s("OutboundLink")],1),t._v(" に用意しています。")]),t._v(" "),s("p",[t._v("💻 GitHub上で操作し、作業用リポジトリを作成してください。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(256),alt:"droneで最初のテスト"}})]),t._v(" "),s("p",[t._v("上記リポジトリを開いて「Use this template」を押してください。"),s("br"),t._v("\nリポジトリ名は「"),s("code",[t._v("drone-exercise-")]),t._v("」にしましょう。"),s("br"),t._v("\nほかの設定値はデフォルトで良いです。")]),t._v(" "),s("p",[t._v("ここで作成したリポジトリに対して操作をしていきます。")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("Use this template が表示されていない場合は正しくログインできているか確認してください。"),s("br"),t._v("\n講師から repository を作成する organization が指示されていれば、それに従ってください。")])]),t._v(" "),s("p",[t._v("droneはGitHub上のコミットやpushといったイベントが発生するとそれに応じて自動的に処理が走るようになっています。\nこれはWebhookというしくみを用いて実現されていますが、droneを使う前にこの設定が必要です。")]),t._v(" "),s("p",[t._v("💻 droneにリポジトリを登録する。")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://cloud.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://cloud.drone.io/"),s("OutboundLink")],1),t._v(" では 新規のユーザー登録を行っていません。"),s("br"),t._v("\nそのため、以下の手順を実施するためには "),s("a",{attrs:{href:"http://drone.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone.io"),s("OutboundLink")],1),t._v(" の 構築が必要です。"),s("br"),t._v("\n講師は接続先を案内してください。")])]),t._v(" "),s("p",[s("a",{attrs:{href:"http://xn--drone-f83dqcwesos420avc3aymzhv8g.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("講師に案内されたdrone.io"),s("OutboundLink")],1),t._v(" にログインしてください。")]),t._v(" "),s("p",[t._v("初回のログインでは OAuth の連携の許可が必要になるかもしれません。")]),t._v(" "),s("p",[t._v("Repositories の リストから「自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v("」を探してリポジトリ名をクリックし詳細ページを開きましょう。")]),t._v(" "),s("p",[t._v("見つからない場合は「SYNC」ボタンを押してから探してください。")]),t._v(" "),s("p",[t._v("「SETTINGS」タブから「ACTIVATE REPOSITORY」をクリックすると自動で設定が行われ、設定画面が表示されます。")]),t._v(" "),s("p",[t._v("これでdroneを利用する準備が整いました。")]),t._v(" "),s("h4",{attrs:{id:"_2-1-2-droneでテストを実行する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-2-droneでテストを実行する"}},[t._v("#")]),t._v(" 2.1.2. droneでテストを実行する")]),t._v(" "),s("p",[t._v("💻 作成した作業用リポジトリ(自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v(")をローカルにgit cloneしてください。")]),t._v(" "),s("p",[t._v("このリポジトリにはすでにdroneの設定ファイルが置かれています。\n適当に"),s("code",[t._v("README.md")]),t._v("を編集してコミット、pushしてみましょう。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("おさらいです。 編集したあとは git add でステージング したのち commit && push となります。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('date >> README.md\ngit add README.md\ngit commit -m "update README.md"\ngit push origin master\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")])])]),t._v(" "),s("p",[t._v("GitHub に push してしばらくすると drone で テストが実行されます。")]),t._v(" "),s("p",[t._v("先程開いた droneの「自分のアカウント名/"),s("code",[t._v("drone-exercise-")]),t._v("」の 詳細ページから「ACTIVITY FEED」タブを開くとテストの実行ログが表示されます。")]),t._v(" "),s("p",[t._v("クリックして 実行ログを読むとどのようにテストが実行されているかが分かります。")]),t._v(" "),s("p",[t._v("このリポジトリにはRubyで書かれたプログラムと、Ruby用のテストフレームワークである"),s("a",{attrs:{href:"https://rspec.info/",target:"_blank",rel:"noopener noreferrer"}},[t._v("RSpec"),s("OutboundLink")],1),t._v("で\n書かれたテストが置かれています。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(300),alt:"droneで最初のテスト"}})]),t._v(" "),s("p",[t._v("図を見ると、 clone と test の step からなるとわかります。")]),t._v(" "),s("p",[t._v("それぞれクリックすると各 step の詳細が表示されます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント2 🏁")]),t._v(" "),s("p",[t._v("「test」ではどういうメッセージが出力されたでしょうか?")])]),t._v(" "),s("h4",{attrs:{id:"_2-1-3-webhookの設定確認"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-3-webhookの設定確認"}},[t._v("#")]),t._v(" 2.1.3 Webhookの設定確認")]),t._v(" "),s("p",[t._v("drone と GitHub の連携には Webhook を利用しています。")]),t._v(" "),s("p",[t._v("デフォルトでは"),s("code",[t._v("pr")]),t._v("と"),s("code",[t._v("push")]),t._v("の2つが登録されています。")]),t._v(" "),s("p",[s("code",[t._v("pr")]),t._v("を指定すると、Pull Requestをオープンしたとき、または既存のPRへpushしたときにテストが実行されます。\n"),s("code",[t._v("push")]),t._v(" を指定すると、"),s("code",[t._v("git push")]),t._v(" したときにテストが実行されます。")]),t._v(" "),s("p",[t._v("この設定は 「Settings」->「Hooks」->「Webhooks」-> droneのエントリ -> Edit で確認でき、")]),t._v(" "),s("p",[t._v("設定画面最下部の「Recent Deliveries」では実際に発行されたWebhookを確認 & 再送信できます。")]),t._v(" "),s("h2",{attrs:{id:"_3-droneの基本的な設定"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-droneの基本的な設定"}},[t._v("#")]),t._v(" 3. droneの基本的な設定")]),t._v(" "),s("p",[t._v("drone設定の基本は、どういった環境で、どのようなコマンドを実行するかということを記述することです。")]),t._v(" "),s("p",[t._v("設定はKubernetesライクな書き方になっていますので、Kubernetesの知識があれば読みやすいです。")]),t._v(" "),s("p",[t._v("droneはバージョンによって設定ファイルの書き方が異なりますので、\n既存のプロジェクトを編集するときは気を付けてください。")]),t._v(" "),s("p",[t._v("droneの設定はデフォルトでリポジトリの一番上の階層に"),s("code",[t._v(".drone.yml")]),t._v("という名前で置きます。")]),t._v(" "),s("p",[t._v("2.1.2 でcloneしたリポジトリの"),s("code",[t._v(".drone.yml")]),t._v("を見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\n\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle install\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" rspec\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("p",[t._v("各項目について解説していきます。")]),t._v(" "),s("h3",{attrs:{id:"_3-1-pipeline"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-1-pipeline"}},[t._v("#")]),t._v(" 3.1. Pipeline")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\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("Pipeline")]),t._v("と呼びます。")]),t._v(" "),s("p",[t._v("まずyamlの先頭で"),s("code",[t._v("kind: pipeline")]),t._v("と記載し、この下に書かれる設定値がPipelineのものであることを宣言します。\nただIIJの環境では"),s("code",[t._v("Pipeline")]),t._v("以外の設定は利用できませんのでこの"),s("code",[t._v("kind")]),t._v("と"),s("code",[t._v("name")]),t._v("は固定になります。")]),t._v(" "),s("p",[s("code",[t._v("Pipeline")]),t._v("は複数の"),s("code",[t._v("Step")]),t._v("で構成されます。")]),t._v(" "),s("h3",{attrs:{id:"_3-2-steps"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3-2-steps"}},[t._v("#")]),t._v(" 3.2. Steps")]),t._v(" "),s("p",[t._v("ひとつのPipelineで複数のテスト("),s("code",[t._v("Step")]),t._v(")を実行できます。")]),t._v(" "),s("p",[t._v("各"),s("code",[t._v("Step")]),t._v("は別々のdockerコンテナで実行され、各テストは独立した環境でテストできます。")]),t._v(" "),s("p",[t._v("UI上では各"),s("code",[t._v("Step")]),t._v("ごとに結果が分けて表示され、"),s("code",[t._v("name")]),t._v("で名前を付けることができ、これはUI上に表示されます。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(301),alt:"Stepの表示"}})]),t._v(" "),s("p",[s("code",[t._v("image")]),t._v(" はテストに利用するdockerイメージを指定します。")]),t._v(" "),s("p",[s("code",[t._v("image: ruby")]),t._v(" と指定した場合はDocker Hubの"),s("a",{attrs:{href:"https://hub.docker.com/_/ruby",target:"_blank",rel:"noopener noreferrer"}},[t._v("Ruby Official Image"),s("OutboundLink")],1),t._v("が利用されます。\n素性のわからないイメージを利用することはやめましょう。")]),t._v(" "),s("p",[t._v("また、"),s("code",[t._v("image: ruby:3.1.2")]),t._v(" のようにタグを指定して、特定のバージョンを利用できるイメージもありますが意図せず更新される場合があります。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("プライベートなDockerイメージ置き場を自分で作成することもできます。")])]),t._v(" "),s("p",[s("code",[t._v("commands")]),t._v(" にはコンテナ内で実行するコマンドを記述します。")]),t._v(" "),s("p",[s("code",[t._v("bundle install")]),t._v(" ではテスト実行に必要なライブラリをインストールしていて、"),s("code",[t._v("rspec")]),t._v("でテストを実行しています。")]),t._v(" "),s("p",[t._v("各テストが成功したかどうかは各コマンドのExit Codeを見ていて、Code 0以外ではテストは中断され失敗となります。\nテストが失敗すると以下のような表示になります。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(302),alt:"テスト失敗時の表示"}})]),t._v(" "),s("h2",{attrs:{id:"_4-pull-requestと組み合わせる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4-pull-requestと組み合わせる"}},[t._v("#")]),t._v(" 4. Pull Requestと組み合わせる")]),t._v(" "),s("p",[t._v("droneを設定した状態でPull Requestを作成するとどうなるでしょうか。")]),t._v(" "),s("p",[t._v("💻 意図的にテストが失敗するようにコードを修正し、Pull Requestを作成してみましょう。")]),t._v(" "),s("ol",[s("li",[t._v("まず、別の branch へ checkout します")])]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("git checkout -b feature/text-error\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("ol",{attrs:{start:"2"}},[s("li",[t._v("実装である"),s("code",[t._v("hello_world.rb")]),t._v("を以下のように書き換えてみます。")])]),t._v(" "),s("div",{staticClass:"language-diff line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" def world\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" 'Hello World'\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" 'Goodby World'\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" end\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("ol",{attrs:{start:"3"}},[s("li",[t._v("編集した内容を commit し push します。")])]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add hello_world.rb\ngit commit -m "goodby"\ngit push origin feature/text-error\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("💻 GitHub を開いてPull Requestを作成しましょう。")]),t._v(" "),s("p",[t._v("別のbranch に push した内容を develop branch などへ取り込んでもらうためのリクエストを Pull Request(PR) と呼びます。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(303),alt:"Pull Requestを作成しましょう"}})]),t._v(" "),s("p",[t._v("もし、作業リポジトリを fork して作成した場合 PR の送り先が fork 元 repository になっています。")]),t._v(" "),s("p",[t._v("その時は 自分のrepository に PR を送るように base repository (左側) の 表記を見直してください。")]),t._v(" "),s("p",[t._v("無事PRを作成できた場合 PRのページへ遷移します。")]),t._v(" "),s("p",[t._v("ページ下部にdroneのテスト結果が表示されています。")]),t._v(" "),s("p",[t._v("一目でテストが失敗していることがわかるでしょう。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(304),alt:"Pull Requestに表示されたdroneの結果"}})]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント3 🏁")]),t._v(" "),s("p",[t._v("Pull Request は作成できましたか?"),s("br"),t._v(" "),s("a",{attrs:{href:"http://drone.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone.io"),s("OutboundLink")],1),t._v(" は動作しましたか?"),s("br"),t._v("\nテストが失敗しましたか?")])]),t._v(" "),s("h3",{attrs:{id:"_4-1-テストが失敗したらマージできないようにしたい"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-テストが失敗したらマージできないようにしたい"}},[t._v("#")]),t._v(" 4.1. テストが失敗したらマージできないようにしたい")]),t._v(" "),s("p",[t._v("さて、 先程のPRではテストに失敗してしまいました。")]),t._v(" "),s("p",[t._v("この状態でもMergeボタンを押すことは可能ですが、普通は押されたくないはずです。\nこの挙動はGitHubの設定画面から変更できます。")]),t._v(" "),s("p",[t._v("💻 テストが通ったときだけマージできるように設定する")]),t._v(" "),s("ol",[s("li",[t._v("「Settings」->「Branches」->「Branch protection rules」->「Add rule」を押し、")]),t._v(" "),s("li",[t._v("「Branch name pattern」に「master」と記入し、")]),t._v(" "),s("li",[t._v("「Include administrators」にチェックを入れます。")]),t._v(" "),s("li",[t._v("「Require status checks to pass before merging」にチェックを入れて")]),t._v(" "),s("li",[t._v("「Status checks found in the last week for this repository」に出ている\n「continuous-integration/drone/pr」と\n「continuous-integration/drone/push」にチェックを入れます。")]),t._v(" "),s("li",[t._v("「Create」します。")])]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("もし、continuous-integration/drone/pr が見つからない場合"),s("br"),t._v("\n先ほど作成したPRによる drone のテストがうまく動いていないかもしれません。"),s("br"),t._v("\nfork した場合は PR の作成先を確認する必要があります。"),s("br"),t._v("\nPR の作成先が間違っているかもしれません。見直してください。")])]),t._v(" "),s("p",[s("img",{attrs:{src:e(305),alt:"Branch protection rules"}})]),t._v(" "),s("p",[t._v("先程作成したPull Requestのページに戻るとマージボタンが押せなくなっています。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(306),alt:"マージできない状態"}})]),t._v(" "),s("p",[t._v("これは複数人で開発するときには便利な機能です。")]),t._v(" "),s("p",[t._v("このあとmasterブランチを利用しますのでBranch protection rulesは削除しておきましょう。")]),t._v(" "),s("p",[t._v("💻 Branch protection rules の一覧ページで"),s("code",[t._v("master")]),t._v("と名前がついたルールの"),s("code",[t._v("delete")]),t._v("ボタンを押す")]),t._v(" "),s("p",[t._v("💻 この後項目のためにmasterブランチに戻っておきましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ git checkout master\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント4 🏁")]),t._v(" "),s("p",[t._v("マージボタンが押せなくなったのはなぜですか?")])]),t._v(" "),s("h2",{attrs:{id:"_5-さまざまなプラグイン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-さまざまなプラグイン"}},[t._v("#")]),t._v(" 5. さまざまなプラグイン")]),t._v(" "),s("p",[s("a",{attrs:{href:"http://plugins.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("droneには様々なプラグインが用意されています。"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("主に外部と連携する機能が用意されています。\n後述する利用のしかたからも分かるとおり plugin は 単なる docker コンテナであるため、自分で開発することもできます。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("Plugins are just Docker containers which means you can write plugins in any programming language that runs inside a container. You can even create plugins using simple bash scripting.\n")])]),t._v(" "),s("div",{staticClass:"line-numbers-wrapper"},[s("span",{staticClass:"line-number"},[t._v("1")]),s("br")])]),s("blockquote",[s("p",[s("a",{attrs:{href:"https://docs.drone.io/plugins/overview/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.drone.io/plugins/overview/"),s("OutboundLink")],1),t._v(" より引用")])]),t._v(" "),s("h3",{attrs:{id:"_5-1-キャッシュプラグイン"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-キャッシュプラグイン"}},[t._v("#")]),t._v(" 5.1. キャッシュプラグイン")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("この項目はS3互換のオブジェクトストレージが必要です。\n講師は接続先を案内してください。")])]),t._v(" "),s("p",[t._v("これまでの例ではテストを実行するときに必要なライブラリを"),s("code",[t._v("bundle install")]),t._v("で事前にダウンロードしてから実行していました。\nしかし毎回ダウンロードしていたのではテスト実行に時間がかかりますし、ネットワークの無駄です。")]),t._v(" "),s("p",[s("a",{attrs:{href:"http://plugins.drone.io/drone-plugins/drone-s3-cache/",target:"_blank",rel:"noopener noreferrer"}},[t._v("s3-cache"),s("OutboundLink")],1),t._v("プラグインを使うと特定のディレクトリを\n"),s("a",{attrs:{href:"https://aws.amazon.com/jp/s3/",target:"_blank",rel:"noopener noreferrer"}},[t._v("S3"),s("OutboundLink")],1),t._v("に保存し、テストのたびに復元してくれる機能を提供します。")]),t._v(" "),s("h4",{attrs:{id:"_5-1-1-ライブラリの保存場所を変更する"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-1-ライブラリの保存場所を変更する"}},[t._v("#")]),t._v(" 5.1.1. ライブラリの保存場所を変更する")]),t._v(" "),s("p",[t._v("パッケージマネージャーによってはリポジトリの中ではなく、システムの特別な場所に配置するものがあります。\ns3-cacheプラグインはリポジトリ内にあるファイルしかキャッシュできません。")]),t._v(" "),s("p",[t._v("nodejsのパッケージマネージャーであるnpmはデフォルトでリポジトリ直下にライブラリを配置しますが、\nRubyのパッケージマネージャーであるbundlerはシステム領域にライブラリを保存するため、\nそのままではキャッシュさせることができません。\nほとんどの場合、パッケージマネージャーのオプションで保存場所を変更できますので、\nRubyを例に設定してみましょう。")]),t._v(" "),s("p",[t._v("Rubyのパッケージマネージャーであるbundlerは"),s("code",[t._v("--path")]),t._v("オプションで保存場所を指定できます。")]),t._v(" "),s("p",[s("code",[t._v("drone.yaml")]),t._v(" で 実行している "),s("code",[t._v("bundle install")]),t._v(" にオプションを渡し 一般的によく使われる"),s("code",[t._v("vendor/bundle")]),t._v("に保存するように変更してください。")]),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(" - name: test\n image: ruby:2.6.2\n commands:\n - bundle install --path vendor/bundle\n - bundle exec rspec\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("h4",{attrs:{id:"_5-1-2-ライブラリをキャッシュさせてみる"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-2-ライブラリをキャッシュさせてみる"}},[t._v("#")]),t._v(" 5.1.2. ライブラリをキャッシュさせてみる")]),t._v(" "),s("p",[t._v("さっそくキャッシュさせてみましょう。")]),t._v(" "),s("p",[t._v("このプラグインはキャッシュしたデータを戻す"),s("code",[t._v("restore")]),t._v("と、キャッシュを行う"),s("code",[t._v("rebuild")]),t._v("の機能があります。")]),t._v(" "),s("p",[s("code",[t._v(".drone.yml")]),t._v("を編集してキャッシュを組み込んでみましょう。\n"),s("code",[t._v("rebuild")]),t._v("ステップで"),s("code",[t._v("vendor/bundle")]),t._v("ディレクトリをキャッシュし、\n"),s("code",[t._v("restore")]),t._v("ステップでキャッシュされたものを戻します。")]),t._v(" "),s("p",[s("code",[t._v("test")]),t._v("Stepの前後に、"),s("code",[t._v("restore")]),t._v("と"),s("code",[t._v("rebuild")]),t._v("を追加します。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("以下の設定は各環境で用意された接続先へ値を変更してください。")]),t._v(" "),s("p",[t._v("<変数名>で書かれた場所を適切な値で置き換えてください。")])]),t._v(" "),s("p",[t._v("💻 パッケージをキャッシュするように変更する")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" restore\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" plugins/s3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("cache\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("settings")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("pull")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("endpoint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("access_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("secret_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("restore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle install "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("path vendor/bundle\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rspec\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rebuild\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" plugins/s3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("cache\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("settings")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("pull")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("endpoint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("access_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("secret_key")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("rebuild")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("mount")]),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(" vendor/bundle\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("p",[t._v("💻 コミットして実行してみましょう。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add .drone.yml\ngit commit -m "Cache導入"\ngit push origin master\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("droneのUI上で最新の実行ログを開いて見ましょう。\n"),s("code",[t._v("restore")]),t._v("と"),s("code",[t._v("rebuild")]),t._v("のステップが増えていることを確認してください。")]),t._v(" "),s("p",[t._v("1回目はキャッシュされてないので何も起きませんが、2回目からはキャッシュが使われるので実行が早くなります。\n1回目の実行時間を確認してから、以下のように2回目のテストを実行してみてください。")]),t._v(" "),s("p",[s("code",[t._v("--allow-empty")]),t._v(" はファイルを変更していなくてもコミットを作ることができるオプションです。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git commit --allow-empty -m "Cacheの効果を確認する"\ngit push origin master\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("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),s("p",[t._v("このキャッシュを利用する操作は drone で完結しない処理です。"),s("br"),t._v("\nもしオブジェクトストレージ側にトラブルがあれば 処理が長時間に及ぶ可能性があります。")]),t._v(" "),s("p",[t._v("また、drone 上の ジョブの同時実行数には限りがある場合があります。"),s("br"),t._v("\n処理が長時間に渡る場合は drone 上のジョブ実行結果の確認画面から キャンセルができます")])]),t._v(" "),s("p",[t._v("💻 テスト実行が早くなっていることを確認しましょう。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント5 🏁")]),t._v(" "),s("p",[t._v("テストが速くなったのはなぜですか?")])]),t._v(" "),s("h2",{attrs:{id:"_6-さまざまな応用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-さまざまな応用"}},[t._v("#")]),t._v(" 6. さまざまな応用")]),t._v(" "),s("h3",{attrs:{id:"_6-1-text-lintを使った日本語チェックの例"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-1-text-lintを使った日本語チェックの例"}},[t._v("#")]),t._v(" 6.1. Text Lintを使った日本語チェックの例")]),t._v(" "),s("p",[t._v("droneはプログラムにしか使えないものでしょうか。そうではありません。\ndroneは何でも動かすことができますから、テストをすることだけが仕事ではありません。")]),t._v(" "),s("p",[t._v("ここでは自然言語のチェックを行う "),s("a",{attrs:{href:"https://textlint.github.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("textlint"),s("OutboundLink")],1),t._v(" を使った例を紹介します。")]),t._v(" "),s("p",[t._v("💻 stepsに以下の項目を追加してみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" textlint\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" node\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" npm install textlint\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" npm install textlint"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rule"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("preset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("technical"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("writing\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" $(npm bin)/textlint "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("format pretty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("error "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("preset ja"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("technical"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("writing README.md\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("p",[t._v("textlintはnodejsで動作しますので、イメージにnodeを指定します。\ntextlintはフレームワークのみでこれ単体で動かすことはできません。どういったものをチェックするかというルールは別に定義しなければいけません。\nここでは日本語の技術文書を書くうえで必要ないくつかのルールをまとめた\n"),s("a",{attrs:{href:"https://github.com/textlint-ja/textlint-rule-preset-ja-technical-writing",target:"_blank",rel:"noopener noreferrer"}},[t._v("textlint-rule-preset-ja-technical-writing"),s("OutboundLink")],1),t._v("\nを利用します。試しに "),s("code",[t._v("README.md")]),t._v("をチェックしてみましょう。")]),t._v(" "),s("p",[t._v("💻 README.mdをTextlintでチェックする。")]),t._v(" "),s("div",{staticClass:"language- line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('git add .drone.yml\ngit commit -m "textlintによるチェック"\ngit push origin master\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",[s("img",{attrs:{src:e(307),alt:"textlintのエラー"}})]),t._v(" "),s("p",[t._v("このようにチェックする対象はプログラムに限りませんので、マニュアルなどのドキュメントのチェックなどにも活用できます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント6 🏁")]),t._v(" "),s("p",[t._v("droneが利用できる事例としてふさわしいものはどれですか?")])]),t._v(" "),s("h3",{attrs:{id:"_6-2-services"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_6-2-services"}},[t._v("#")]),t._v(" 6.2 Services")]),t._v(" "),s("p",[t._v("単純なスクリプトやライブラリのテストはこれだけでも十分にdroneでテストできます。")]),t._v(" "),s("p",[t._v("ではデータベース使ったテストはできるでしょうか。")]),t._v(" "),s("p",[t._v("データベースのテストをするならデータベースのプロセスが上がっている必要があります。"),s("br"),t._v("\nでも1つのコンテナでアプリケーションと一緒にデータベースも一緒に立ち上げたくないですよね。")]),t._v(" "),s("p",[t._v("そのために"),s("code",[t._v("Service")]),t._v("というしくみがあります。"),s("br"),t._v("\n同時に複数のコンテナを立ち上げて待機させておき、テスト中利用できます。")]),t._v(" "),s("p",[t._v("サンプルとしてRuby on Rails+MySQLで構成されたアプリケーションを用意しました。")]),t._v(" "),s("p",[t._v("💻 "),s("a",{attrs:{href:"https://github.com/iij/drone-exercise-rails",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/iij/drone-exercise-rails"),s("OutboundLink")],1),t._v(" から 作業用リポジトリを作成し、git cloneしてください。")]),t._v(" "),s("p",[s("img",{attrs:{src:e(256),alt:"rails テスト"}})]),t._v(" "),s("p",[t._v("上記リポジトリを開いて「Use this template」を押してください。")]),t._v(" "),s("p",[t._v("リポジトリ名は「"),s("code",[t._v("drone-exercise-rails-")]),t._v("」にしましょう。"),s("br"),t._v("\nほかの設定値はデフォルトで良いです。")]),t._v(" "),s("p",[t._v("ここで作成したリポジトリに対して操作をしていきます。")]),t._v(" "),s("p",[t._v("Ruby on RailsはWebアプリケーションを作るためのRuby製フレームワークです。\nデータの保存にMySQLなどのデータベースを利用できます。")]),t._v(" "),s("p",[t._v("標準的なWebアプリケーションではデータベースなどの外部サービスにデータを保存し、それを読み取って加工して表示するという動作が多く、\nテストするときもデータベースが動いている必要があります。")]),t._v(" "),s("p",[t._v("上記リポジトリにはテストを行うだけの "),s("code",[t._v(".drone.yml")]),t._v(" が含まれています。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pipeline\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" default\n\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("steps")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ruby"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("2.6.2\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("RAILS_ENV")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("commands")]),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(" bundle "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 実行に必要なライブラリをインストールする")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rails db"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("reset "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# テスト用のデータベース、テーブルを作成する")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" bundle exec rails test "),s("span",{pre:!0,attrs:{class:"token comment"}},[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("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("新しいrepository への drone の 有効のしかた"),s("br"),t._v("\n内容の変更を伴わないcommit をする方法を思い出してください。")])]),t._v(" "),s("p",[t._v("ちなみにテストは "),s("code",[t._v("test/models/user_test.rb")]),t._v(" に書いてあります。")]),t._v(" "),s("p",[t._v("この状態ではデータベースが動いていないのでテストが成功しません。")]),t._v(" "),s("p",[t._v("テストしている間横で何か動かしておきたいという場合には "),s("code",[t._v("Service")]),t._v(" を使います。\n今回はMySQLを使いますが、"),s("a",{attrs:{href:"https://hub.docker.com/_/mysql",target:"_blank",rel:"noopener noreferrer"}},[t._v("MySQLは公式でdockerイメージが提供されています"),s("OutboundLink")],1),t._v("のでこれを利用します。")]),t._v(" "),s("p",[t._v("💻 以下の項目を"),s("code",[t._v(".drone.yml")]),t._v("に追加してください。")]),t._v(" "),s("p",[t._v("kind, name, steps と同じ高さでよいです(services の左側にスペースはいりません)")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),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(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" db\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mysql"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("8.0.16\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--default-authentication-plugin=mysql_native_password"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# MySQL8.0のデフォルト認証方式にRailsが対応していないため変更")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MYSQL_ALLOW_EMPTY_PASSWORD")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'yes'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[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("p",[s("code",[t._v("name")]),t._v(" で名前をつけて "),s("code",[t._v("image")]),t._v(" で使用するdockerイメージを指定します。")]),t._v(" "),s("p",[t._v("Railsではデータベースの設定を"),s("code",[t._v("config/database.yml")]),t._v("から読み込みます。"),s("br"),t._v("\ndroneで設定したデータベースへ接続するための設定値を見てみましょう。")]),t._v(" "),s("div",{staticClass:"language-yaml line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("test")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("<<")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token important"}},[t._v("*default")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("host")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" db\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3306")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("socket")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token null important"}},[t._v("null")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("database")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" drone"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("exercise"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rails_test\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("p",[t._v("このファイルで接続先MySQLサーバのホストを指定しているのですが、"),s("br"),t._v("\nここでは"),s("code",[t._v(".drone.yml")]),t._v("で指定した"),s("code",[t._v("db")]),t._v("がホスト名になっていて、この名前で接続できます。")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("チェックポイント7 🏁")]),t._v(" "),s("p",[t._v("テスト実行中にデータベースはどこで実行されているでしょうか?")])]),t._v(" "),s("h2",{attrs:{id:"_8-参考情報"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_8-参考情報"}},[t._v("#")]),t._v(" 8. 参考情報")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://docs.drone.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("drone Documentation"),s("OutboundLink")],1)])]),t._v(" "),s("h2",{attrs:{id:"続き"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#続き"}},[t._v("#")]),t._v(" 続き")]),t._v(" "),s("ul",[s("li",[s("RouterLink",{attrs:{to:"/cicd_infra/github_actions/"}},[t._v("GitHub Actions でCIテスト・デプロイを回す")])],1)])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/14.33573406.js b/assets/js/14.433600fc.js similarity index 99% rename from assets/js/14.33573406.js rename to assets/js/14.433600fc.js index f64895d0..c0effdf1 100644 --- a/assets/js/14.33573406.js +++ b/assets/js/14.433600fc.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{416:function(s,t,e){s.exports=e.p+"assets/img/components-of-kubernetes.51120ad2.svg"},417:function(s,t,e){s.exports=e.p+"assets/img/module_03_pods.ccc5ba54.svg"},418:function(s,t,e){s.exports=e.p+"assets/img/image_clusterip.8223e0b9.svg"},419:function(s,t,e){s.exports=e.p+"assets/img/image_nodeport.5357aa05.svg"},420:function(s,t,e){s.exports=e.p+"assets/img/image_loadbalancer.7df1efb3.svg"},526: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(416),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(417),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(418),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(419),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(420),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([[14],{395:function(s,t,e){s.exports=e.p+"assets/img/components-of-kubernetes.51120ad2.svg"},396:function(s,t,e){s.exports=e.p+"assets/img/module_03_pods.ccc5ba54.svg"},397:function(s,t,e){s.exports=e.p+"assets/img/image_clusterip.8223e0b9.svg"},398:function(s,t,e){s.exports=e.p+"assets/img/image_nodeport.5357aa05.svg"},399:function(s,t,e){s.exports=e.p+"assets/img/image_loadbalancer.7df1efb3.svg"},525: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(395),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(396),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(397),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(398),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(399),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/15.b528c6e2.js b/assets/js/15.fbc41b18.js similarity index 99% rename from assets/js/15.b528c6e2.js rename to assets/js/15.fbc41b18.js index 3f1f5461..ba3ea6a1 100644 --- a/assets/js/15.b528c6e2.js +++ b/assets/js/15.fbc41b18.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{474:function(s,a,t){s.exports=t.p+"assets/img/apache-start.c57e81f7.png"},475:function(s,a,t){s.exports=t.p+"assets/img/site-80.1afd6993.png"},476:function(s,a,t){s.exports=t.p+"assets/img/site-82.9465a06d.png"},477:function(s,a,t){s.exports=t.p+"assets/img/nginx_html.ae5a2fc7.png"},478:function(s,a,t){s.exports=t.p+"assets/img/nginx-proxy.drawio.50845ff4.png"},545:function(s,a,t){"use strict";t.r(a);var n=t(10),e=Object(n.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h1",{attrs:{id:"apache-nginx-を触ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-nginx-を触ってみよう"}},[s._v("#")]),s._v(" Apache + Nginx を触ってみよう")]),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.17-bookworm")])]),s._v("\n"),a("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")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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("8082")]),s._v(":82 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8088")]),s._v(":88 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8089")]),s._v(":89 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8443")]),s._v(":443 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8444")]),s._v(":444 python:3.8.17-bookworm /bin/bash")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("a0da070e286fd52ebb323e5faff9c960014bfcd8eb1e509cb5a12daa9fb9a85e\n")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("root@a0da070e286f:/#\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("p",[s._v("Apacheとnginxをインストールします。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update")])]),s._v("\n"),a("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")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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 nginx "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")])])]),s._v("\n"),a("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 libsub-override-perl libsys-hostname-long-perl libuchardet0 libxtables12 man-db nginx-common po-debconf ssl-cert vim-common vim-runtime xxd\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(" "),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("p",[s._v("以下のコマンドでバージョンが表示されれば成功です。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("apache2 -v")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("Server version: Apache/2.4.57 (Debian)\nServer built: 2023-04-13T03:26:51\n")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("nginx -v")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("nginx version: nginx/1.22.1\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("h2",{attrs:{id:"webサーバー"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#webサーバー"}},[s._v("#")]),s._v(" Webサーバー")]),s._v(" "),a("p",[s._v("いわゆる「Webサーバー」とは、HTTP(Hypertext Transfer Protocol)でリクエストを受け、HTTPでレスポンスを返すソフトウェアの通称です。\n僕らがブラウザなどにURLを入力したりリンクをクリックした時、Webページが表示されるのはWebサーバーが要求したURLに対するレスポンスを返しているからです。またスマホアプリの裏で行われるサーバーとのやりとりには多くの場合HTTPが使われており、ここでもWebサーバーがゲームのデータなどをレスポンスとして返しています。")]),s._v(" "),a("p",[s._v("Webサーバのシンプルな機能は前述の通りですが、実際にはユースケースに合わせてさまざまな役割を持ちます。")]),s._v(" "),a("ul",[a("li",[s._v("HTMLやテキストファイルの配信")]),s._v(" "),a("li",[s._v("動的アプリケーションのホスティング\n"),a("ul",[a("li",[s._v("JavaやPythonやPHPなど、プログラムで生成されたレスポンスを返す")])])]),s._v(" "),a("li",[s._v("HTTP通信を別のサーバーに中継するプロキシ")]),s._v(" "),a("li",[s._v("Basic認証などによる認証処理")]),s._v(" "),a("li",[s._v("ACL(Access Control List)によるアクセス制御・不正な通信への防御")])]),s._v(" "),a("p",[s._v("簡単なWebサーバーであればどのプログラミング言語でも比較的簡単に作ることができます。\nしかし実用上はさまざまな機能をもち、セキュリティやパフォーマンスについても長年改善されてきた専用のソフトウェアが必要になり、それがいわゆる「Webサーバーソフトウェア」と呼ばれるツールです。")]),s._v(" "),a("p",[s._v("有名どころを挙げてみると")]),s._v(" "),a("ul",[a("li",[s._v("Apache HTTP Server")]),s._v(" "),a("li",[s._v("IIS")]),s._v(" "),a("li",[s._v("lighttpd")]),s._v(" "),a("li",[s._v("nginx")])]),s._v(" "),a("p",[s._v("あたりでしょうか。Linuxサーバー上で動かすのであればほぼApacheとnginxの2択になると思います。")]),s._v(" "),a("p",[s._v("また最近ではenvoyやtraefikなど、クラウドやKubernetesという文脈ではプロキシ機能に特化したソフトウェアが使われることも多くなりました。")]),s._v(" "),a("h2",{attrs:{id:"apache-と-nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-と-nginx"}},[s._v("#")]),s._v(" Apache と Nginx")]),s._v(" "),a("h3",{attrs:{id:"apache-http-server"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-http-server"}},[s._v("#")]),s._v(" Apache HTTP Server")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」はnginxと並んで2大勢力を誇っているWebサーバソフトウェアのひとつです。 CentOSではhttpdという名前になっていたり、単にApacheと呼ばれます。")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」は「Apacheソフトウェア財団」によって管理されるOSSで、20年以上の歴史を持ちます。 世界的にもっとも普及したWebサーバで、LAMP(Linux, Apache, MySQL, PHP)環境のひとつにも挙げられ、nginxと並んで2大勢力を誇ります。\n(参考: "),a("a",{attrs:{href:"https://www.netcraft.com/blog/june-2023-web-server-survey/",target:"_blank",rel:"noopener noreferrer"}},[s._v("June 2023 Web Server Survey"),a("OutboundLink")],1),s._v(")")]),s._v(" "),a("p",[s._v("正式名称は「Apache HTTP Server」ですが、歴史的経緯などからCentOSではhttpdという名前になっていたり、単にApacheと呼ばれたりします。")]),s._v(" "),a("p",[s._v("以前は大量のリクエストを受けた際にプロセスをforkできず、リクエストを捌き切れなくなる(いわゆるC10K問題)ことが問題視されました。 その際nginxをはじめとして新しいWebサーバーソフトウェアが登場しましたが、Apache自体もworkerやevent MPMといった新しい仕組みを導入し、動作も安定していることからいまだに高いシェアを占めています。")]),s._v(" "),a("h3",{attrs:{id:"nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginx"}},[s._v("#")]),s._v(" nginx")]),s._v(" "),a("p",[s._v("nginxは2004年頃、当時のWebサーバーが抱えていたパフォーマンス問題(C10K問題)の解決を背景に開発が進められました。\n当時からApache 2.2は高機能で信頼性が高く、ある種成熟したソフトウェアでしたが、それに対してnginxは軽量さと高パフォーマンスに焦点をあてて開発されており、Apacheのカバーしきれないユースケースに対して力を発揮しました。")]),s._v(" "),a("p",[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("h3",{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("まずはApacheを起動しましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 start")])]),s._v("\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:"http://localhost:8080",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8080"),a("OutboundLink")],1),s._v("にアクセスしてみてください。以下のような画面が表示されれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(474),alt:"apache-start"}})]),s._v(" "),a("p",[s._v("表示されたページはデフォルトのHTMLファイルです。これを自分で作成したページに置き換えてみましょう。 デフォルトではDocument Rootは/var/www/html/に設定されています。")]),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mv")]),s._v(" /var/www/html/index.html /var/www/html/_index.html")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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(" /var/www/html/index.html")])]),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("再び"),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/hoge")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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")])]),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",[a("code",[s._v("http://localhost:8080/hoge/huga.txt")]),s._v(" にアクセスすると追加したファイルが表示されます。")]),s._v(" "),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("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/apache2/access.log")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h3",{attrs:{id:"virtualhost-の設定-check2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#virtualhost-の設定-check2"}},[s._v("#")]),s._v(" VirtualHost の設定(check2)")]),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("82")]),s._v("に分けて別々のWebサイトを設定してみます。\n(docker起動時にport forwardしているため、手元からは"),a("code",[s._v("8080")]),s._v("と"),a("code",[s._v("8082")]),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",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /var/www/html/site-80")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /var/www/html/site-82")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'This is site 80!' > /var/www/html/site-80/index.html")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'This is site 82!' > /var/www/html/site-82/index.html")]),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("ul",[a("li",[s._v("listen portに82を追加")]),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 82")]),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 82\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-82.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("82")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-82\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:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2dissite 000-default")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2ensite site-80")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2ensite site-82")]),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("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("以下のファイルのsymlinkを"),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("p",[s._v("そしてApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service apache2 reload")]),s._v("\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:8082")]),s._v("にアクセスしてみてください。意図通りの挙動になっているでしょうか。")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(475),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(476),alt:"site-82"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("h2",{attrs:{id:"nginx-ハンズオン"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginx-ハンズオン"}},[s._v("#")]),s._v(" nginx ハンズオン")]),s._v(" "),a("h3",{attrs:{id:"htmlファイルの配信-check3"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#htmlファイルの配信-check3"}},[s._v("#")]),s._v(" HTMLファイルの配信(check3)")]),s._v(" "),a("p",[s._v("次はnginxを使って同じことをしてみましょう。")]),s._v(" "),a("p",[s._v("80 portはすでにApacheが使っているため、nginxのサイトは88 portでリクエストを受け付けるようにします。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vim /etc/nginx/sites-enabled/default")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("div",{staticClass:"language-nginx line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-nginx"}},[a("code",[a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("88")]),s._v(" default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:88 default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("root")]),s._v(" /var/www/html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("try_files")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ =404")]),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"),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")])]),a("p",[s._v("変更したらnginxを起動しましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx start")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Starting nginx: nginx.\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("a",{attrs:{href:"http://localhost:8088",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8088"),a("OutboundLink")],1),s._v(" にアクセスしてみてください。さっき作った"),a("code",[s._v("Hello Bootcamp!!")]),s._v("のHTMLが見えていれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(477),alt:"nginx_html"}})]),s._v(" "),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("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/nginx/access.log")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h3",{attrs:{id:"ロードバランス-check4"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ロードバランス-check4"}},[s._v("#")]),s._v(" ロードバランス(check4)")]),s._v(" "),a("p",[s._v("nginxのプロキシ・ロードバランス機能を使ってみましょう。以下のような構成を作ってみます。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(478),alt:"nginx_proxy"}})]),s._v(" "),a("p",[a("code",[s._v("localhost:8089")]),s._v("にアクセスすると、先ほどApacheで作ったsite-80とsite-89のどちらかにランダムでリクエストをプロキシするようにします。")]),s._v(" "),a("p",[s._v("そのための設定を"),a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v("に書いていきます。")]),s._v(" "),a("div",{staticClass:"language-nginx line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-nginx"}},[a("code",[a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("upstream")]),s._v(" backend")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")]),s._v(" localhost:80 weight=1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")]),s._v(" localhost:82 weight=1")]),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\n"),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("89")]),s._v(" default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:89 default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("proxy_pass")]),s._v(" http://backend")]),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"),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"),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("p",[a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v("を作成したらnginxをリスタートしましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx restart")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Restarting nginx: nginx.\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("a",{attrs:{href:"http://localhost:8089/",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost:8089/"),a("OutboundLink")],1),s._v(" にアクセスしてみてください。\nsite-80とsite-82がランダムで表示されたでしょうか。")]),s._v(" "),a("h3",{attrs:{id:"https-対応-check5"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#https-対応-check5"}},[s._v("#")]),s._v(" https 対応(check5)")]),s._v(" "),a("p",[s._v("HTTP は基本的に平文でデータをやりとりします。")]),s._v(" "),a("p",[s._v("ということは、途中でパケットキャプチャをすると、やり取りの内容を読み取ることができます。")]),s._v(" "),a("p",[s._v("もしそこにパスワード情報など見られてはいけない情報が含まれていたら...怖いですね。")]),s._v(" "),a("p",[s._v("そこで、SSL/TLS (Secure Socket Layer/Transport Layer Securityの技術)を用いて通信路の暗号化を行うHTTP over SSL いわゆるHTTPS を重要な情報のやりとりを行う際には用いるのが一般的です。")]),s._v(" "),a("p",[s._v("各種Web サーバはこのHTTPS もサポートしており、証明書とそれに対応する秘密鍵さえあれば、簡単に設定することができます。")]),s._v(" "),a("h4",{attrs:{id:"証明書と秘密鍵の用意"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#証明書と秘密鍵の用意"}},[s._v("#")]),s._v(" 証明書と秘密鍵の用意")]),s._v(" "),a("p",[s._v("HTTPS で用いる証明書は、権威ある証明局から、これは正当な証明書である、とお墨付きをもらうことで正当性が担保されています。")]),s._v(" "),a("p",[s._v("通常、証明書は以下の手順で入手します。")]),s._v(" "),a("ol",[a("li",[s._v("秘密鍵を生成する")]),s._v(" "),a("li",[s._v("秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),a("li",[s._v("CSR を証明書に提出し、審査を受け、証明局の持つ秘密鍵で署名された証明書を発行してもらう")])]),s._v(" "),a("p",[s._v("ここでは、3を簡略化して1 で生成した鍵で署名する、自己署名証明書(いわゆるオレオレ証明書)を作ります。\nこのdocker image に既にインストールされている、openssl ツールで一通りの操作を行うことができます。")]),s._v(" "),a("h5",{attrs:{id:"_1-秘密鍵を生成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-秘密鍵を生成する"}},[s._v("#")]),s._v(" 1. 秘密鍵を生成する")]),s._v(" "),a("p",[s._v("ここではRSA の2048 bit の秘密鍵を生成します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるgenrsa はRSA 暗号の秘密鍵を生成するものとなります。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /etc/nginx/ssl")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl genrsa 2048 > /etc/nginx/ssl/private.key")]),s._v("\nGenerating RSA private key, "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2048")]),s._v(" bit long modulus "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" primes"),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("..")]),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("..")]),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("..")]),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 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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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(".+++++\ne is "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("65537")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x010001"),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("h5",{attrs:{id:"_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[s._v("#")]),s._v(" 2. 秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),a("p",[s._v("1 で作った秘密鍵から、CSR を生成します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるreq はCSR を扱うためのものとなります。")])]),s._v(" "),a("p",[s._v("証明書で表示する情報をここで入力することになります。\n実際に発行する際は、正当性を担保したい対象であるCommon Name は特に間違わないようにしましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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 "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'.'")]),s._v(", the field will be left blank.\n-----\nCountry Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" letter code"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("AU"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":JP\nState or Province Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("full name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Some-State"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Tokyo\nLocality Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, city"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Chiyoda\nOrganization Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, company"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Internet Widgits Pty Ltd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":IIJ\nOrganizational Unit Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, section"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":TU\nCommon Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("e.g. server FQDN or YOUR name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":localhost\nEmail Address "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n\nPlease enter the following "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'extra'")]),s._v(" attributes\nto be sent with your certificate request\nA challenge password "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\nAn optional company name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),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"),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("h5",{attrs:{id:"_3-署名された証明書を発行する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3-署名された証明書を発行する"}},[s._v("#")]),s._v(" 3. 署名された証明書を発行する")]),s._v(" "),a("p",[s._v("1 で作った秘密鍵、2 で作ったCSR から証明書を発行します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるx509 は、証明書の標準規格を指しています。\n-req でinput がCSR であることを示し、signkey に1 で作った秘密鍵を指定することでこれで署名します。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("subject")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("C "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\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("出来上がったら、証明書の中を覗いてみましょう。text オプションでテキスト出力をすることができます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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: "),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("0x0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Serial Number:\n "),a("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 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Validity\n Not Before: Aug "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2022")]),s._v(" GMT\n Not After "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" Aug "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(" GMT\n Subject: C "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Subject Public Key Info:\n"),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("."),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")])]),a("p",[s._v("実際に発行されたものを確認する際は、期間(Not BeforeとNot After)とSubject (CN が正しいか)に特に注意しましょう。")]),s._v(" "),a("p",[s._v("秘密鍵と証明書のペアが正しいかを確認するには、RSA のものならmodulus を比較するのが簡単です。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl rsa -in /etc/nginx/ssl/private.key -modulus -noout")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -modulus -noout")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\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("h4",{attrs:{id:"https-の設定"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#https-の設定"}},[s._v("#")]),s._v(" https の設定")]),s._v(" "),a("p",[s._v("check4 で作ったhttp で受けていたproxy をhttps でも受けられるようにしてみます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v(" の一番下に以下を追記していきます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/server.crt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/private.key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n server_name _"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n location / "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n proxy_pass http://backend"),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"),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"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br")])]),a("p",[s._v("追記したら、nginx をリスタートしましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@dea1ac0e1edb:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v(" ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" Restarting nginx: nginx.\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("443 は8443 にポートフォワードの設定が入っているため、8443 ポートにアクセスしてみましょう。\nhttps での通信となるため、URL の先頭がhttp ではなくhttps となっています。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://localhost:8443/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443/"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("今回は自己署名証明書であるため、ほとんどのブラウザは正当な証明書ではないと判断し、注意喚起の画面が表示されます。\n危険性を承知で閲覧すると、Check4 の時と同様のものが表示されます。")]),s._v(" "),a("p",[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:"apache-でもhttpsを設定してみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-でもhttpsを設定してみよう"}},[s._v("#")]),s._v(" apache でもhttpsを設定してみよう")]),s._v(" "),a("ul",[a("li",[s._v("Apache でもhttps を受けられるようにしてみましょう。")]),s._v(" "),a("li",[s._v("8444 を444 にポートフォワードする設定も予め入れてあるので、444 で受ける設定を入れれば、外から8444 でアクセスできます。証明書は同じものを使い回しで構いません。")])]),s._v(" "),a("h3",{attrs:{id:"basic認証を追加してみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#basic認証を追加してみよう"}},[s._v("#")]),s._v(" Basic認証を追加してみよう")]),s._v(" "),a("ul",[a("li",[s._v("ApacheとnginxそれぞれにBasic認証を導入し、アクセスした時にユーザー名とパスワードの入力を求められるようにしてください。")]),s._v(" "),a("li",[s._v("ブラウザで動作確認ができたら、次は"),a("code",[s._v("curl")]),s._v("コマンドでアクセスしてBasic認証がどのように動作するか確認してください。")])]),s._v(" "),a("h3",{attrs:{id:"pythonアプリを動かしてみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#pythonアプリを動かしてみよう"}},[s._v("#")]),s._v(" Pythonアプリを動かしてみよう")]),s._v(" "),a("p",[s._v("Pythonで書かれたWebアプリをApache経由で動かす設定を作ってみます。\nこの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.17")]),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\nCollecting mod-wsgi\n Downloading mod_wsgi-4.9.4.tar.gz "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("497")]),s._v(" kB"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("497.5")]),s._v("/497.5 kB "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("6.1")]),s._v(" MB/s eta "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(":00:00\n Preparing metadata "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("setup.py"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("done")]),s._v("\nBuilding wheels "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" collected packages: mod-wsgi\n Building wheel "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" mod-wsgi "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("setup.py"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("done")]),s._v("\n Created wheel "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" mod-wsgi: "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("filename")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("mod_wsgi-4.9.4-cp38-cp38-linux_x86_64.whl "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("size")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("734287")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("sha256")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("b643e88dbd9659e671e2e014621153066c1061f7c385385b4ccb48b3cc453ee1\n Stored "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" directory: /root/.cache/pip/wheels/a7/96/89/a6231ee168c52f30f56065d1431e08ee24443e96b402595c85\nSuccessfully built mod-wsgi\nInstalling collected packages: mod-wsgi\nSuccessfully installed mod-wsgi-4.9.4\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("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("p",[s._v("またnginxから同様のアプリケーションを動かせるようにしてみてください。")]),s._v(" "),a("h3",{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=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{477:function(s,a,t){s.exports=t.p+"assets/img/apache-start.c57e81f7.png"},478:function(s,a,t){s.exports=t.p+"assets/img/site-80.1afd6993.png"},479:function(s,a,t){s.exports=t.p+"assets/img/site-82.9465a06d.png"},480:function(s,a,t){s.exports=t.p+"assets/img/nginx_html.ae5a2fc7.png"},481:function(s,a,t){s.exports=t.p+"assets/img/nginx-proxy.drawio.50845ff4.png"},546:function(s,a,t){"use strict";t.r(a);var n=t(10),e=Object(n.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h1",{attrs:{id:"apache-nginx-を触ってみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-nginx-を触ってみよう"}},[s._v("#")]),s._v(" Apache + Nginx を触ってみよう")]),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" pull python:3.8.17-bookworm")])]),s._v("\n"),a("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")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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("8082")]),s._v(":82 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8088")]),s._v(":88 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8089")]),s._v(":89 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8443")]),s._v(":443 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8444")]),s._v(":444 python:3.8.17-bookworm /bin/bash")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("a0da070e286fd52ebb323e5faff9c960014bfcd8eb1e509cb5a12daa9fb9a85e\n")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("$")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("root@a0da070e286f:/#\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("p",[s._v("Apacheとnginxをインストールします。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("apt")]),s._v(" update")])]),s._v("\n"),a("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")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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 nginx "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("vim")])])]),s._v("\n"),a("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 libsub-override-perl libsys-hostname-long-perl libuchardet0 libxtables12 man-db nginx-common po-debconf ssl-cert vim-common vim-runtime xxd\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(" "),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("p",[s._v("以下のコマンドでバージョンが表示されれば成功です。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("apache2 -v")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("Server version: Apache/2.4.57 (Debian)\nServer built: 2023-04-13T03:26:51\n")]),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[s._v("nginx -v")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("nginx version: nginx/1.22.1\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("h2",{attrs:{id:"webサーバー"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#webサーバー"}},[s._v("#")]),s._v(" Webサーバー")]),s._v(" "),a("p",[s._v("いわゆる「Webサーバー」とは、HTTP(Hypertext Transfer Protocol)でリクエストを受け、HTTPでレスポンスを返すソフトウェアの通称です。\n僕らがブラウザなどにURLを入力したりリンクをクリックした時、Webページが表示されるのはWebサーバーが要求したURLに対するレスポンスを返しているからです。またスマホアプリの裏で行われるサーバーとのやりとりには多くの場合HTTPが使われており、ここでもWebサーバーがゲームのデータなどをレスポンスとして返しています。")]),s._v(" "),a("p",[s._v("Webサーバのシンプルな機能は前述の通りですが、実際にはユースケースに合わせてさまざまな役割を持ちます。")]),s._v(" "),a("ul",[a("li",[s._v("HTMLやテキストファイルの配信")]),s._v(" "),a("li",[s._v("動的アプリケーションのホスティング\n"),a("ul",[a("li",[s._v("JavaやPythonやPHPなど、プログラムで生成されたレスポンスを返す")])])]),s._v(" "),a("li",[s._v("HTTP通信を別のサーバーに中継するプロキシ")]),s._v(" "),a("li",[s._v("Basic認証などによる認証処理")]),s._v(" "),a("li",[s._v("ACL(Access Control List)によるアクセス制御・不正な通信への防御")])]),s._v(" "),a("p",[s._v("簡単なWebサーバーであればどのプログラミング言語でも比較的簡単に作ることができます。\nしかし実用上はさまざまな機能をもち、セキュリティやパフォーマンスについても長年改善されてきた専用のソフトウェアが必要になり、それがいわゆる「Webサーバーソフトウェア」と呼ばれるツールです。")]),s._v(" "),a("p",[s._v("有名どころを挙げてみると")]),s._v(" "),a("ul",[a("li",[s._v("Apache HTTP Server")]),s._v(" "),a("li",[s._v("IIS")]),s._v(" "),a("li",[s._v("lighttpd")]),s._v(" "),a("li",[s._v("nginx")])]),s._v(" "),a("p",[s._v("あたりでしょうか。Linuxサーバー上で動かすのであればほぼApacheとnginxの2択になると思います。")]),s._v(" "),a("p",[s._v("また最近ではenvoyやtraefikなど、クラウドやKubernetesという文脈ではプロキシ機能に特化したソフトウェアが使われることも多くなりました。")]),s._v(" "),a("h2",{attrs:{id:"apache-と-nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-と-nginx"}},[s._v("#")]),s._v(" Apache と Nginx")]),s._v(" "),a("h3",{attrs:{id:"apache-http-server"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-http-server"}},[s._v("#")]),s._v(" Apache HTTP Server")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」はnginxと並んで2大勢力を誇っているWebサーバソフトウェアのひとつです。 CentOSではhttpdという名前になっていたり、単にApacheと呼ばれます。")]),s._v(" "),a("p",[s._v("「Apache HTTP Server」は「Apacheソフトウェア財団」によって管理されるOSSで、20年以上の歴史を持ちます。 世界的にもっとも普及したWebサーバで、LAMP(Linux, Apache, MySQL, PHP)環境のひとつにも挙げられ、nginxと並んで2大勢力を誇ります。\n(参考: "),a("a",{attrs:{href:"https://www.netcraft.com/blog/june-2023-web-server-survey/",target:"_blank",rel:"noopener noreferrer"}},[s._v("June 2023 Web Server Survey"),a("OutboundLink")],1),s._v(")")]),s._v(" "),a("p",[s._v("正式名称は「Apache HTTP Server」ですが、歴史的経緯などからCentOSではhttpdという名前になっていたり、単にApacheと呼ばれたりします。")]),s._v(" "),a("p",[s._v("以前は大量のリクエストを受けた際にプロセスをforkできず、リクエストを捌き切れなくなる(いわゆるC10K問題)ことが問題視されました。 その際nginxをはじめとして新しいWebサーバーソフトウェアが登場しましたが、Apache自体もworkerやevent MPMといった新しい仕組みを導入し、動作も安定していることからいまだに高いシェアを占めています。")]),s._v(" "),a("h3",{attrs:{id:"nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginx"}},[s._v("#")]),s._v(" nginx")]),s._v(" "),a("p",[s._v("nginxは2004年頃、当時のWebサーバーが抱えていたパフォーマンス問題(C10K問題)の解決を背景に開発が進められました。\n当時からApache 2.2は高機能で信頼性が高く、ある種成熟したソフトウェアでしたが、それに対してnginxは軽量さと高パフォーマンスに焦点をあてて開発されており、Apacheのカバーしきれないユースケースに対して力を発揮しました。")]),s._v(" "),a("p",[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("h3",{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("まずはApacheを起動しましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" apache2 start")])]),s._v("\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:"http://localhost:8080",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8080"),a("OutboundLink")],1),s._v("にアクセスしてみてください。以下のような画面が表示されれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(477),alt:"apache-start"}})]),s._v(" "),a("p",[s._v("表示されたページはデフォルトのHTMLファイルです。これを自分で作成したページに置き換えてみましょう。 デフォルトではDocument Rootは/var/www/html/に設定されています。")]),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mv")]),s._v(" /var/www/html/index.html /var/www/html/_index.html")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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(" /var/www/html/index.html")])]),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("再び"),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-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("mkdir")]),s._v(" /var/www/html/hoge")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[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")])]),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",[a("code",[s._v("http://localhost:8080/hoge/huga.txt")]),s._v(" にアクセスすると追加したファイルが表示されます。")]),s._v(" "),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("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/apache2/access.log")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h3",{attrs:{id:"virtualhost-の設定-check2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#virtualhost-の設定-check2"}},[s._v("#")]),s._v(" VirtualHost の設定(check2)")]),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("82")]),s._v("に分けて別々のWebサイトを設定してみます。\n(docker起動時にport forwardしているため、手元からは"),a("code",[s._v("8080")]),s._v("と"),a("code",[s._v("8082")]),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",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /var/www/html/site-80")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /var/www/html/site-82")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'This is site 80!' > /var/www/html/site-80/index.html")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo 'This is site 82!' > /var/www/html/site-82/index.html")]),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("ul",[a("li",[s._v("listen portに82を追加")]),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 82")]),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 82\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-82.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("82")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(">")])]),s._v("\n DocumentRoot /var/www/html/site-82\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:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2dissite 000-default")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2ensite site-80")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# a2ensite site-82")]),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("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("以下のファイルのsymlinkを"),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("p",[s._v("そしてApacheをリスタートします。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service apache2 reload")]),s._v("\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:8082")]),s._v("にアクセスしてみてください。意図通りの挙動になっているでしょうか。")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(478),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(479),alt:"site-82"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("h2",{attrs:{id:"nginx-ハンズオン"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginx-ハンズオン"}},[s._v("#")]),s._v(" nginx ハンズオン")]),s._v(" "),a("h3",{attrs:{id:"htmlファイルの配信-check3"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#htmlファイルの配信-check3"}},[s._v("#")]),s._v(" HTMLファイルの配信(check3)")]),s._v(" "),a("p",[s._v("次はnginxを使って同じことをしてみましょう。")]),s._v(" "),a("p",[s._v("80 portはすでにApacheが使っているため、nginxのサイトは88 portでリクエストを受け付けるようにします。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vim /etc/nginx/sites-enabled/default")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("div",{staticClass:"language-nginx line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-nginx"}},[a("code",[a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("88")]),s._v(" default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:88 default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 80 => 88 に変更")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("root")]),s._v(" /var/www/html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("try_files")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$uri")]),s._v("/ =404")]),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"),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")])]),a("p",[s._v("変更したらnginxを起動しましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx start")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Starting nginx: nginx.\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("a",{attrs:{href:"http://localhost:8088",target:"_blank",rel:"noopener noreferrer"}},[s._v("localhost:8088"),a("OutboundLink")],1),s._v(" にアクセスしてみてください。さっき作った"),a("code",[s._v("Hello Bootcamp!!")]),s._v("のHTMLが見えていれば成功です。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(480),alt:"nginx_html"}})]),s._v(" "),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("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# tail /var/log/nginx/access.log")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("h3",{attrs:{id:"ロードバランス-check4"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ロードバランス-check4"}},[s._v("#")]),s._v(" ロードバランス(check4)")]),s._v(" "),a("p",[s._v("nginxのプロキシ・ロードバランス機能を使ってみましょう。以下のような構成を作ってみます。")]),s._v(" "),a("p",[a("img",{attrs:{src:t(481),alt:"nginx_proxy"}})]),s._v(" "),a("p",[a("code",[s._v("localhost:8089")]),s._v("にアクセスすると、先ほどApacheで作ったsite-80とsite-89のどちらかにランダムでリクエストをプロキシするようにします。")]),s._v(" "),a("p",[s._v("そのための設定を"),a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v("に書いていきます。")]),s._v(" "),a("div",{staticClass:"language-nginx line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-nginx"}},[a("code",[a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("upstream")]),s._v(" backend")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")]),s._v(" localhost:80 weight=1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")]),s._v(" localhost:82 weight=1")]),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\n"),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server")])]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("89")]),s._v(" default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("listen")]),s._v(" [::]:89 default_server")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("index")]),s._v(" index.html index.htm index.nginx-debian.html")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("server_name")]),s._v(" _")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("location")]),s._v(" /")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token directive"}},[a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("proxy_pass")]),s._v(" http://backend")]),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"),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"),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("p",[a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v("を作成したらnginxをリスタートしましょう。")]),s._v(" "),a("div",{staticClass:"language-shell-session line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-shell-session"}},[a("code",[a("span",{pre:!0,attrs:{class:"token command"}},[a("span",{pre:!0,attrs:{class:"token info punctuation"}},[a("span",{pre:!0,attrs:{class:"token user"}},[s._v("root@a0da070e286f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),a("span",{pre:!0,attrs:{class:"token path"}},[s._v("/")])]),a("span",{pre:!0,attrs:{class:"token shell-symbol important"}},[s._v("#")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token bash language-bash"}},[a("span",{pre:!0,attrs:{class:"token function"}},[s._v("service")]),s._v(" nginx restart")])]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token output"}},[s._v("[ ok ] Restarting nginx: nginx.\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("a",{attrs:{href:"http://localhost:8089/",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://localhost:8089/"),a("OutboundLink")],1),s._v(" にアクセスしてみてください。\nsite-80とsite-82がランダムで表示されたでしょうか。")]),s._v(" "),a("h3",{attrs:{id:"https-対応-check5"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#https-対応-check5"}},[s._v("#")]),s._v(" https 対応(check5)")]),s._v(" "),a("p",[s._v("HTTP は基本的に平文でデータをやりとりします。")]),s._v(" "),a("p",[s._v("ということは、途中でパケットキャプチャをすると、やり取りの内容を読み取ることができます。")]),s._v(" "),a("p",[s._v("もしそこにパスワード情報など見られてはいけない情報が含まれていたら...怖いですね。")]),s._v(" "),a("p",[s._v("そこで、SSL/TLS (Secure Socket Layer/Transport Layer Securityの技術)を用いて通信路の暗号化を行うHTTP over SSL いわゆるHTTPS を重要な情報のやりとりを行う際には用いるのが一般的です。")]),s._v(" "),a("p",[s._v("各種Web サーバはこのHTTPS もサポートしており、証明書とそれに対応する秘密鍵さえあれば、簡単に設定することができます。")]),s._v(" "),a("h4",{attrs:{id:"証明書と秘密鍵の用意"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#証明書と秘密鍵の用意"}},[s._v("#")]),s._v(" 証明書と秘密鍵の用意")]),s._v(" "),a("p",[s._v("HTTPS で用いる証明書は、権威ある証明局から、これは正当な証明書である、とお墨付きをもらうことで正当性が担保されています。")]),s._v(" "),a("p",[s._v("通常、証明書は以下の手順で入手します。")]),s._v(" "),a("ol",[a("li",[s._v("秘密鍵を生成する")]),s._v(" "),a("li",[s._v("秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),a("li",[s._v("CSR を証明書に提出し、審査を受け、証明局の持つ秘密鍵で署名された証明書を発行してもらう")])]),s._v(" "),a("p",[s._v("ここでは、3を簡略化して1 で生成した鍵で署名する、自己署名証明書(いわゆるオレオレ証明書)を作ります。\nこのdocker image に既にインストールされている、openssl ツールで一通りの操作を行うことができます。")]),s._v(" "),a("h5",{attrs:{id:"_1-秘密鍵を生成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-秘密鍵を生成する"}},[s._v("#")]),s._v(" 1. 秘密鍵を生成する")]),s._v(" "),a("p",[s._v("ここではRSA の2048 bit の秘密鍵を生成します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるgenrsa はRSA 暗号の秘密鍵を生成するものとなります。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir /etc/nginx/ssl")]),s._v("\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl genrsa 2048 > /etc/nginx/ssl/private.key")]),s._v("\nGenerating RSA private key, "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2048")]),s._v(" bit long modulus "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" primes"),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("..")]),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("..")]),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("..")]),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 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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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("..")]),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(".+++++\ne is "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("65537")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("0x010001"),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("h5",{attrs:{id:"_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2-秘密鍵からcsr-certificate-signing-request-を生成する"}},[s._v("#")]),s._v(" 2. 秘密鍵からCSR (Certificate Signing Request) を生成する")]),s._v(" "),a("p",[s._v("1 で作った秘密鍵から、CSR を生成します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるreq はCSR を扱うためのものとなります。")])]),s._v(" "),a("p",[s._v("証明書で表示する情報をここで入力することになります。\n実際に発行する際は、正当性を担保したい対象であるCommon Name は特に間違わないようにしましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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 "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'.'")]),s._v(", the field will be left blank.\n-----\nCountry Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" letter code"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("AU"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":JP\nState or Province Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("full name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Some-State"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Tokyo\nLocality Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, city"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":Chiyoda\nOrganization Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, company"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Internet Widgits Pty Ltd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":IIJ\nOrganizational Unit Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("eg, section"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":TU\nCommon Name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("e.g. server FQDN or YOUR name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":localhost\nEmail Address "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\n\nPlease enter the following "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'extra'")]),s._v(" attributes\nto be sent with your certificate request\nA challenge password "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":\nAn optional company name "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),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"),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("h5",{attrs:{id:"_3-署名された証明書を発行する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3-署名された証明書を発行する"}},[s._v("#")]),s._v(" 3. 署名された証明書を発行する")]),s._v(" "),a("p",[s._v("1 で作った秘密鍵、2 で作ったCSR から証明書を発行します。")]),s._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[s._v("TIP")]),s._v(" "),a("p",[s._v("サブコマンドであるx509 は、証明書の標準規格を指しています。\n-req でinput がCSR であることを示し、signkey に1 で作った秘密鍵を指定することでこれで署名します。")])]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("subject")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("C "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\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("出来上がったら、証明書の中を覗いてみましょう。text オプションでテキスト出力をすることができます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("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: "),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("0x0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n Serial Number:\n "),a("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 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Validity\n Not Before: Aug "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2022")]),s._v(" GMT\n Not After "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" Aug "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("16")]),s._v(":29:36 "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2023")]),s._v(" GMT\n Subject: C "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" JP, ST "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Tokyo, L "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" Chiyoda, O "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" IIJ, OU "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" TU, CN "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" localhost\n Subject Public Key Info:\n"),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("."),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")])]),a("p",[s._v("実際に発行されたものを確認する際は、期間(Not BeforeとNot After)とSubject (CN が正しいか)に特に注意しましょう。")]),s._v(" "),a("p",[s._v("秘密鍵と証明書のペアが正しいかを確認するには、RSA のものならmodulus を比較するのが簡単です。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl rsa -in /etc/nginx/ssl/private.key -modulus -noout")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\n\nroot@a0da070e286f:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# openssl x509 -in /etc/nginx/ssl/server.crt -modulus -noout")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("Modulus")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B\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("h4",{attrs:{id:"https-の設定"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#https-の設定"}},[s._v("#")]),s._v(" https の設定")]),s._v(" "),a("p",[s._v("check4 で作ったhttp で受けていたproxy をhttps でも受けられるようにしてみます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/nginx/sites-enabled/proxy")]),s._v(" の一番下に以下を追記していきます。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n listen "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("443")]),s._v(" default_server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n listen "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("::"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(":443 default_server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n ssl on"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate /etc/nginx/ssl/server.crt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n ssl_certificate_key /etc/nginx/ssl/private.key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n index index.html index.htm index.nginx-debian.html"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n server_name _"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n\n location / "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n proxy_pass http://backend"),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"),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"),a("span",{staticClass:"line-number"},[s._v("15")]),a("br"),a("span",{staticClass:"line-number"},[s._v("16")]),a("br")])]),a("p",[s._v("追記したら、nginx をリスタートしましょう。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("root@dea1ac0e1edb:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# service nginx restart")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v(" ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" Restarting nginx: nginx.\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("443 は8443 にポートフォワードの設定が入っているため、8443 ポートにアクセスしてみましょう。\nhttps での通信となるため、URL の先頭がhttp ではなくhttps となっています。")]),s._v(" "),a("p",[a("a",{attrs:{href:"https://localhost:8443/",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://localhost:8443/"),a("OutboundLink")],1)]),s._v(" "),a("p",[s._v("今回は自己署名証明書であるため、ほとんどのブラウザは正当な証明書ではないと判断し、注意喚起の画面が表示されます。\n危険性を承知で閲覧すると、Check4 の時と同様のものが表示されます。")]),s._v(" "),a("p",[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:"apache-でもhttpsを設定してみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#apache-でもhttpsを設定してみよう"}},[s._v("#")]),s._v(" apache でもhttpsを設定してみよう")]),s._v(" "),a("ul",[a("li",[s._v("Apache でもhttps を受けられるようにしてみましょう。")]),s._v(" "),a("li",[s._v("8444 を444 にポートフォワードする設定も予め入れてあるので、444 で受ける設定を入れれば、外から8444 でアクセスできます。証明書は同じものを使い回しで構いません。")])]),s._v(" "),a("h3",{attrs:{id:"basic認証を追加してみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#basic認証を追加してみよう"}},[s._v("#")]),s._v(" Basic認証を追加してみよう")]),s._v(" "),a("ul",[a("li",[s._v("ApacheとnginxそれぞれにBasic認証を導入し、アクセスした時にユーザー名とパスワードの入力を求められるようにしてください。")]),s._v(" "),a("li",[s._v("ブラウザで動作確認ができたら、次は"),a("code",[s._v("curl")]),s._v("コマンドでアクセスしてBasic認証がどのように動作するか確認してください。")])]),s._v(" "),a("h3",{attrs:{id:"pythonアプリを動かしてみよう"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#pythonアプリを動かしてみよう"}},[s._v("#")]),s._v(" Pythonアプリを動かしてみよう")]),s._v(" "),a("p",[s._v("Pythonで書かれたWebアプリをApache経由で動かす設定を作ってみます。\nこの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.17")]),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\nCollecting mod-wsgi\n Downloading mod_wsgi-4.9.4.tar.gz "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("497")]),s._v(" kB"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("497.5")]),s._v("/497.5 kB "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("6.1")]),s._v(" MB/s eta "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(":00:00\n Preparing metadata "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("setup.py"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("done")]),s._v("\nBuilding wheels "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" collected packages: mod-wsgi\n Building wheel "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" mod-wsgi "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("setup.py"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(". "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("done")]),s._v("\n Created wheel "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" mod-wsgi: "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("filename")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("mod_wsgi-4.9.4-cp38-cp38-linux_x86_64.whl "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("size")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("734287")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("sha256")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("b643e88dbd9659e671e2e014621153066c1061f7c385385b4ccb48b3cc453ee1\n Stored "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" directory: /root/.cache/pip/wheels/a7/96/89/a6231ee168c52f30f56065d1431e08ee24443e96b402595c85\nSuccessfully built mod-wsgi\nInstalling collected packages: mod-wsgi\nSuccessfully installed mod-wsgi-4.9.4\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("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("p",[s._v("またnginxから同様のアプリケーションを動かせるようにしてみてください。")]),s._v(" "),a("h3",{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=e.exports}}]); \ No newline at end of file diff --git a/assets/js/19.86a73a9d.js b/assets/js/19.21875a6e.js similarity index 99% rename from assets/js/19.86a73a9d.js rename to assets/js/19.21875a6e.js index 8ca61b40..bd57ec88 100644 --- a/assets/js/19.86a73a9d.js +++ b/assets/js/19.21875a6e.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{479:function(s,a,t){s.exports=t.p+"assets/img/apache-start.82b13324.png"},480:function(s,a,t){s.exports=t.p+"assets/img/site-80.7a41e96d.png"},481:function(s,a,t){s.exports=t.p+"assets/img/site-81.19ee5e97.png"},546: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(479),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(480),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(481),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([[19],{474:function(s,a,t){s.exports=t.p+"assets/img/apache-start.82b13324.png"},475:function(s,a,t){s.exports=t.p+"assets/img/site-80.7a41e96d.png"},476:function(s,a,t){s.exports=t.p+"assets/img/site-81.19ee5e97.png"},545: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(474),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(475),alt:"site-80"}})])])]),s._v(" "),a("tbody")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[a("img",{attrs:{src:t(476),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/25.59e28d48.js b/assets/js/25.1dc895d7.js similarity index 99% rename from assets/js/25.59e28d48.js rename to assets/js/25.1dc895d7.js index 18ceb6f3..77bed7aa 100644 --- a/assets/js/25.59e28d48.js +++ b/assets/js/25.1dc895d7.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{459:function(s,t,a){s.exports=a.p+"assets/img/white-label-error.c9ee4513.png"},460:function(s,t,a){s.exports=a.p+"assets/img/http-handler.0b72f6d9.png"},541: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(459),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(460),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([[25],{458:function(s,t,a){s.exports=a.p+"assets/img/white-label-error.c9ee4513.png"},459:function(s,t,a){s.exports=a.p+"assets/img/http-handler.0b72f6d9.png"},540: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(458),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(459),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/26.addb59f2.js b/assets/js/26.b037d27f.js similarity index 99% rename from assets/js/26.addb59f2.js rename to assets/js/26.b037d27f.js index fb4c06af..fa91f081 100644 --- a/assets/js/26.addb59f2.js +++ b/assets/js/26.b037d27f.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{482:function(s,a){s.exports="data:image/png;base64,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bqp+swlcmt6m9mK34uMiCUDw4osvypIlS8yxzLGkRYsWctlll5l6BBX7EFCnYJ9OQ0ek+rVbv26PTu3FbUT91V7cJIFuv/DCCybtwLcbIp19QIqBg5dIMyhB8Jei3eyNOgU30fRfW6pf/+nEzz1Se/GzdpLvW0Ikgcd99dVX0rdvX1mxYoXUqFFD/vrrL8mdO7f5/3nnnScDBgzQFEPyevF1C+oUfK2epDun+k0awrRqQO3FbnUnTBIcODh2GWLAgUkcvQxh0F0MdhuLMzp1CnbrWfVrt37dHp3ai9uI+qu9bJMEnTD8pUgve6NOwUu0vX+W6td7zIP8RLWXIGsvdt+VJMTGSK8IQ0Cdgt0mofq1W79ujy4I9sKpwf/8848ce+yxbg/f+vaUJFivYvcHGASn4P6o06dF1W/66NqNkQbBXubMmWO28W/ZssWNIadVG4YkZGRkHM7IyPB84DyzX79+nj9XH5gcAkFwCsmNML3vVv2mt/4THX0Q7EVJQqJa/d/1GknIPnZpe2cQnELaKseFgat+XQAxjZpIhb1s375dunbtKvPnz5eqVavKoEGDpHr16gb1SZMmycMPPyx79uyRW265RZ588kn59NNP5fbbbzdfJR4/frwULlzYbOc///zzTRriiSeeMP9ntx7XsUsvb968mbT47rvvyrBhw2T58uXSuHFjGTFihJx00kkSrT8cQEhbtWrVktdee02KFSsmzZo1k+bNm5u2+dnMmTPl9ddfl88++0x69uwpX3/9tVxzzTUyePBgKVSokPkK8/fffy8//vijSZfQTy9FSYKXaFvyrFQ4BUugC8QwVL+BUJNvOum1vVBfwKR78skny/333y8fffSRDBw4UHbv3i3suuO0XybyMmXKGCLRp08fKVKkiFx11VVyww03SOvWrc0E/O+//8q8efNk5MiRcu+99xqiwL2QhJYtW0r//v2PwvjXX3+V0047TSZOnCinnHKKuefOO++U7t27R+3PqlWrzMGCZcuWlfvuu0/WrFlj/nz44Yem7csvv9z8adeunenjPffcIzfddJMhNnv37pWPP/7YkIzevXsb4vDAAw+Y670UJQleom3Js7x2CpbAFphhqH4DoypfdNRre2EVTwSA1XWJEiUE0sAE+/zzz8uyZctk4cKFZnJFSDPwkcLixYsbkrB//37Jly+fWb0TZWAivuCC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"},483:function(s,a,e){s.exports=e.p+"assets/img/ruby-nginx.eb55bb1c.png"},548:function(s,a,e){"use strict";e.r(a);var n=e(10),t=Object(n.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("ul",[a("li",[s._v("dockerのインストール")])]),s._v(" "),a("h2",{attrs:{id:"前置き"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前置き"}},[s._v("#")]),s._v(" 前置き")]),s._v(" "),a("p",[s._v("現在では様々な言語、フレームワークを使ってWebサーバーアプリケーションの構築を行います。")]),s._v(" "),a("ul",[a("li",[s._v("ruby\n"),a("ul",[a("li",[s._v("rails")])])]),s._v(" "),a("li",[s._v("python\n"),a("ul",[a("li",[s._v("Django")])])]),s._v(" "),a("li",[s._v("go")]),s._v(" "),a("li",[s._v("java")]),s._v(" "),a("li",[s._v("PHP")])]),s._v(" "),a("p",[s._v("上はほんの一例ですが、どの言語で作っても「HTTPリクエストを受け取って何らかのレスポンスを返す」というHTTPサーバーとしての機能を持っていることには変わりありません。")]),s._v(" "),a("p",[s._v("しかし実際の本番環境においては、通常これらのアプリケーションがユーザーからのリクエストを直接受けることはしません。必ずApacheやNginxなど専用のWebサーバーを中継してリクエストを処理します。")]),s._v(" "),a("p",[s._v("なぜこんな二度手間のようなことをするかというと理由は色々ありますが、大雑把に以下のようなものです。")]),s._v(" "),a("ul",[a("li",[s._v("各言語で実装されたHTTPサーバーの機能が貧弱")]),s._v(" "),a("li",[s._v("TLSの終端などをアプリケーション側に実装したくない\n"),a("ul",[a("li",[s._v("証明書の管理などをアプリケーションでやらないといけない")])])]),s._v(" "),a("li",[s._v("HTTPサーバーとして信頼性の高いものを使いたい\n"),a("ul",[a("li",[s._v("脆弱性対策などが一通り行われている")]),s._v(" "),a("li",[s._v("不具合が少ない\n"),a("ul",[a("li",[s._v("HTTPサーバーとしての機能に特化している")]),s._v(" "),a("li",[s._v("世界中で多く使われているため修正が活発")])])])])])]),s._v(" "),a("p",[s._v("このハンズオンではフロントとしてApacheまたはNginxを利用し、裏側のRailsやGo製アプリケーションと連携するような構成を手元で作ってみたいと思います。")]),s._v(" "),a("h2",{attrs:{id:"go-nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#go-nginx"}},[s._v("#")]),s._v(" go + nginx")]),s._v(" "),a("h3",{attrs:{id:"echo-serverの起動"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#echo-serverの起動"}},[s._v("#")]),s._v(" echo-serverの起動")]),s._v(" "),a("p",[s._v("まずは比較的シンプルな例としてGo言語で作られたアプリケーションとNginxを連携させてみましょう。なぜシンプルなのかはあとでお話しします。")]),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",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":8080 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 -it --rm regunorf/bootcamp-go-nginx /bin/bash\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("このコンテナの中にはgo言語で書かれたリクエストを返すだけのシンプルなechoサーバーと、nginxがインストールされています。ソースコードとDockerfileは "),a("a",{attrs:{href:"https://github.com/ryoya-fujimoto/go-echo-server",target:"_blank",rel:"noopener noreferrer"}},[s._v("ここ"),a("OutboundLink")],1),s._v(" にあります。")]),s._v(" "),a("p",[s._v("dockerにログインしたら試しにecho-serverを起動してみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@4dd714ba5f7b:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo-server --bind 0.0.0.0:8080")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("8080 portで動き始めるので、別のターミナルを開いてcurlを叩くかブラウザからアクセスしてみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Accept-Encoding:gzip,deflate'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost:8080\nUser-Agent: curl/7.54.0\nAccept: */*\nContent-Type: application/json\nAccept-Encoding: gzip,deflate\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("bootcamp:true"),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("送信したリクエストのheaderとbodyが出力されます。ブラウザでアクセスするとどのようなヘッダーが送信されているか面白いので試してみてください。")]),s._v(" "),a("h3",{attrs:{id:"nginxと連携させる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginxと連携させる"}},[s._v("#")]),s._v(" nginxと連携させる")]),s._v(" "),a("p",[s._v("echo-serverを立ち上げて外からアクセスできるようにはできましたが、echo-serverにはTLSの終端機能もHTMLなどの静的ファイル配信機能もありません。頑張って実装してもいいですが、ここではnginxにその辺りをやってもらいましょう。\n(TLSは今回やりませんが・・・)")]),s._v(" "),a("p",[a("img",{attrs:{src:e(482),alt:"go-nginx"}})]),s._v(" "),a("p",[s._v("まずはecho-serverの起動方法を変更します。今は外からecho-serverに直接アクセスできてしまいますが、それでは都合が悪いためサーバー内部からしかアクセスできないようにします。")]),s._v(" "),a("p",[s._v("ついでにecho-server起動中も他の作業ができるようにバックグラウンドで実行させておきましょう。echo-serverが起動しているかどうかは"),a("code",[s._v("ps")]),s._v("コマンドなどで確認できます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ echo-server --bind "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:8080 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("&")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 起動できているか確認")]),s._v("\n$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v(" -aux "),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(" echo-server\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("バックグラウンドで起動している他に、先ほどのecho-server起動と何が違うでしょうか。試しに先ほどと同じようにブラウザや手元のマシンからcurlなどでアクセスみてもecho-serverにアクセスできません。")]),s._v(" "),a("p",[s._v("しかしecho-serverを起動したdockerの中で"),a("code",[s._v("localhost:8080")]),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 comment"}},[s._v("# 手元のwindowsマシンでcurlやブラウザを開くとアクセスできない")]),s._v("\n$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080\ncurl: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("52")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" Empty reply from server\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# dockerの中からはアクセスできる")]),s._v("\nroot@4dd714ba5f7b:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl localhost:8080")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:40468 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" GET / "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" time: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2019")]),s._v("-07-21 02:02:09.250992846 +0000 UTC\nUser-Agent: curl/7.58.0\nAccept: */*\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("p",[s._v("最初の例では"),a("code",[s._v("bind")]),s._v("オプションに"),a("code",[s._v("0.0.0.0:8080")]),s._v("を指定していました。"),a("code",[s._v("0.0.0.0")]),s._v("はざっくり言うと「全てのアドレス」と言う意味で、この場合は「全てのアドレスからのリクエストを受け付ける」と言う設定になります。")]),s._v(" "),a("p",[s._v("反対に2回目の"),a("code",[s._v("127.0.0.1:8080")]),s._v("は"),a("code",[s._v("127.0.0.1 = localhost")]),s._v("からのリクエストからしか受け付けないため、上のような挙動になっています。")]),s._v(" "),a("p",[s._v("ではこの"),a("code",[s._v("localhost")]),s._v("で公開されているecho-serverに、nginxを通してアクセスできるようにしてみましょう。\nnginxの設定ファイルは"),a("code",[s._v("/etc/nginx/nginx.conf")]),s._v("にありますが、設定を見やすくするため"),a("code",[s._v("/etc/nginx/conf.d/")]),s._v("の下に新しく設定ファイルを作っていきます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/nginx/conf.d/bootcamp.conf")]),s._v("というファイルを以下のように作成してください。")]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("server {\n listen 80 default_server;\n server_name _;\n\n root /var/www/html;\n location / {\n try_files $uri $uri/ =404;\n }\n location /echo-server {\n proxy_pass http://127.0.0.1:8080;\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("p",[s._v("次にdefaultで作られている設定を削除しておきます(上の内容をこちらに追記しても構いません)。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("rm")]),s._v(" /etc/nginx/sites-enabled/default\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("nginxを起動します")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ nginx\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")]),s._v("にアクセスしてみてください。するとnginxのwelcomeページが表示されるはずです。そして"),a("code",[s._v("localhost/echo-server")]),s._v("というパスにアクセスするとecho-serverからリクエストが返ってきます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost/echo-server\nConnection: close\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v("\nUser-Agent: curl/7.54.0\nAccept: */*\nContent-Type: application/json\nAccept-Encoding: gzip,deflate\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("bootcamp:true"),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("無事にgoアプリ+nginxな構成を作ることができました。今回はecho-serverとの連携に"),a("code",[s._v("localhost")]),s._v("を使いましたが、unixドメインソケットを介して連携することもよく行われます。")]),s._v(" "),a("h2",{attrs:{id:"ruby-apache"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ruby-apache"}},[s._v("#")]),s._v(" ruby + apache")]),s._v(" "),a("h3",{attrs:{id:"前書き"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前書き"}},[s._v("#")]),s._v(" 前書き")]),s._v(" "),a("p",[s._v("Go言語やNode.jsで作られたアプリケーションは、大抵の場合複数のリクエストを並行して捌くことが可能です。これはGoの場合はgoroutine、Node.jsは非同期IOという形で並行処理をデフォルトで扱えるような設計になっているためです。")]),s._v(" "),a("p",[s._v("そのため上の例ではnginxからのリクエストを単にそれぞれのアプリケーションに投げつければ、複数のユーザーからアクセスが同時にあっても問題はありませんでした。")]),s._v(" "),a("p",[s._v("それに対してjavaやruby、pythonなどで作られたアプリケーションは通常1度に1つの処理しか行えないため、「マルチプロセス」や「マルチスレッド」という形で並行処理を実現します。そのためプロセスやスレッドを管理するためのソフトウェアが追加で必要になります。")]),s._v(" "),a("p",[a("img",{attrs:{src:e(483),alt:"go-nginx"}})]),s._v(" "),a("p",[s._v("このようなソフトウェアの例としては以下のようなものがあります")]),s._v(" "),a("ul",[a("li",[s._v("ruby\n"),a("ul",[a("li",[s._v("puma")]),s._v(" "),a("li",[s._v("unicorn")]),s._v(" "),a("li",[s._v("passenger")])])]),s._v(" "),a("li",[s._v("java\n"),a("ul",[a("li",[s._v("tomcat")])])]),s._v(" "),a("li",[s._v("python\n"),a("ul",[a("li",[s._v("uWSGI?")])])])]),s._v(" "),a("p",[s._v("pumaやunicornはアプリケーション側に組み込んで並行処理機能を追加するもの、passengerやtomcatはwebサーバー側(Apache)に組み込んでアプリケーションを動かすもの、といったようにツールによって動かし方は様々です。")]),s._v(" "),a("h3",{attrs:{id:"動かしてみる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#動かしてみる"}},[s._v("#")]),s._v(" 動かしてみる")]),s._v(" "),a("p",[s._v("ここではrubyのアプリケーションをwebサーバーと連携させてみましょう。最近だとpumaやunicornが使われることが多いですが、その場合は先ほどのGoの例と同じようにpumaやunicornをwebアプリとして起動させて、リバースプロキシで連携させます。")]),s._v(" "),a("p",[s._v("同じことをしてもつまらないので、passengerを使ってモジュール組み込みな例を動かしてみましょう。このようにモジュールとして動作させる場合はnginxよりも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",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 --rm -it regunorf/bootcamp-passenger /bin/bash\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://github.com/ryoya-fujimoto/ruby-echo-server",target:"_blank",rel:"noopener noreferrer"}},[s._v("ここ"),a("OutboundLink")],1),s._v(" にあります。アプリケーションは"),a("code",[s._v("/app")]),s._v("ディレクトリにあります。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ls -l")]),s._v("\ntotal "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("20")]),s._v("\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("66")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:15 Dockerfile\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("45")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 04:59 Gemfile\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("325")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:00 Gemfile.lock\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("64")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 04:57 README.md\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("319")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:12 app.rb\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("p",[s._v("コンテナには既にrubyの環境が作られているので、"),a("code",[s._v("ruby main.rb")]),s._v("するとアプリケーションが起動します。")]),s._v(" "),a("p",[s._v("ではpassengerの環境を作っていきましょう。まずはgemコマンドでpassengerをインストールします。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# gem install passenger")]),s._v("\nFetching passenger-6.0.2.gem\nBuilding native extensions. This could take a while"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\nSuccessfully installed passenger-6.0.2\n"),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" gem installed\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",[s._v("そしてapache用のモジュール(プラグイン)をビルドするコマンドを実行します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# passenger-install-apache2-module")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("TUIが起動するので、今回はrubyだけを選択してエンターを押します。\nすると足りてないライブラリが表示されるので、言われるがままインストールしましょう。")]),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-get")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2-dev libapr1-dev libaprutil1-dev\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),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("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# passenger-install-apache2-module")]),s._v("\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("Please edit your Apache configuration file, and add these lines:")]),s._v("というメッセージが表示されます。そこに書いてある内容をapacheの設定ファイルに追記せよとのこと。")]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("LoadModule passenger_module /usr/local/bundle/gems/passenger-6.0.2/buildout/apache2/mod_passenger.so\n\n PassengerRoot /usr/local/bundle/gems/passenger-6.0.2\n PassengerDefaultRuby /usr/local/bin/ruby\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("p",[s._v("一旦enterを押して抜けましょう。apacheの設定ファイルに以下の内容を追加します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@0111e4e14e6d:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vi /etc/apache2/conf-enabled/bootcamp.conf")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('LoadModule passenger_module /usr/local/bundle/gems/passenger-6.0.2/buildout/apache2/mod_passenger.so\n\n PassengerRoot /usr/local/bundle/gems/passenger-6.0.2\n PassengerDefaultRuby /usr/local/bin/ruby\n\n\n\n ServerName bootcamp\n DocumentRoot "/app/public"\n RackEnv production\n\n\n\n Options FollowSymlinks Includes\n AllowOverride All\n AddType text/html .html\n\n Require all granted\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("p",[s._v("apacheを起動します。ServerNameについて警告が出ますがひとまず問題ありません。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@0111e4e14e6d:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# apachectl restart")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("無事起動できたらホストのブラウザなどからアクセスしてみましょう。sinatraアプリが動いていれば成功です。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("1つ目の例と違うのは、sinatraアプリケーションが単体で起動してlocalhostなどをlistenしているのではなく、apacheの一部として起動している点です。この場合はechoアプリケーションを再起動するためにはApacheごと再起動しなければいけません。")]),s._v(" "),a("h2",{attrs:{id:"追加"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#追加"}},[s._v("#")]),s._v(" 追加")]),s._v(" "),a("ul",[a("li",[s._v("apacheが起動している状態で"),a("code",[s._v("/app")]),s._v("以下の"),a("code",[s._v("app.rb")]),s._v("を変更してみましょう。")]),s._v(" "),a("li",[s._v("pumaまたはunicornを使ってapacheまたはnginxとrubyアプリケーションを連携させてみましょう。")])]),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([[26],{482:function(s,a){s.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgkAAABRCAYAAABYIDVLAAADznpUWHRteEdyYXBoTW9kZWwAAE1Ux7KrOBD9Gle9t7ALEBizJNpgMNmEzS1MNjkbvn7E+E7VLJDU6aj70K0DYKtPkpfxAUOqJsqTPI4OgDtgGIag1BEhjxhqIdgB0MS+oNQJ4JgPzdA/a4bx67ssyynqg+WUN19TkMb1r01ptrwsgwMmECcEmv4oQZjXYzNkB8BAWazHuIQ7VMNVNeHiwg9FflD8h/gLj3TblrETv+75uKMA8gTOO9D9ZinyAWPhucyLvYJrHBbNHsJmfVNBjUDCS0+AJJETiu+JmUES9Pn/YHZlPAbpN9k73x5NlxlhLUdR6TXzMWlfnznuh7ypv24oClHRr2Fc2/irjeI5D+NdC/gDYKM8SPuggi75L6Xe8XEUi+nGPpX1qs78Dz3jX5A6qH5BNEjdEf2CfJ6+HBLDwms4usnmLeJoW8Pxq3rAGCSr7brXCckyGu/RMfZQeg7kU5jbuzFJGf+ex0lEtHpEtQn0Q9LCIJ7Q/ZbyKkvTKpa3OHkItHsIycUv62VKPq7+QGj7QrWp9LY6ka9uLzabEZYbC0++DsLEs6leBRzB08tnXWmbxj/Le7IXkq798OGEH45mH7mQeTARUzVyqUXkYoM0C5ymqxrfc6svt51RSlma+FKk5+IQEyw5jist4v3V7Aa6undRZ09WBKMAG8jDh2tL3PLuVIJm9epoFOXeVvlVyUnrY9MLmCAqrOeUcg42u1U/O2FXPbIAeWhb6sHs5u3BKcwdwiH2dcanN07a/njXc13uUFCIA+8pF9m5xe0my6KI3vzx7NqfB54TDBvChhRogkWlpmwZumlss7BEijXCJ7UBQrcBWEY57ihlubWbr0p1RgEQnZ2wH5H0HK/OrLYLiXrvCzIqKpsBNme9l5spYWoNBiE7U4GljFVokxEbi8e7+l43nRizCA+vScQDZOQG+PdotbJ4X1mde/AMFIwjRk8qLVrK49uFSNYsSzFLuUguz6BTs709YuKzKHAnh0Uc4He1kUrSGyurPpcFrD/Pd7NQhf6NdW+eXC1UqGWCoPbbYclM4cbz2JnTbDtnYyV8z7qsfCkU8fhKzTbEz2fgkDm51OmbznRSZLVrviUpuvpG2DmNTU/seeJ11X2FiIotV+JjLK5gif5g65ni64xFb7iUXMwbkSbiUzqHm4vsjQJZlJnq8QxIWrygmWi8H+I1TSxtWPXZUs8jR9WbjBglRisK32NeRQVDYK86wB3MFda6ceFQCQOYLztpdQeFrI3x9ewGyS0F2wS1F7B3F/KCYwr3/yb137GF8u+LCPh/ALXeLTEAABp/SURBVHhe7Z0J2E3V98eXX1GSFCUpMmSWqSRNpPEhUkoqCmWWKU0yvCVFk7GRDGmQJJShyFCZCpU5pYhEZQgVpfyez/7/j99133vfe+97z3vuOfuu9TwevO85++z9Xeus/d1rrb1PrsOHDx+WBGXz5s3y5ZdfyqZNm6REiRJSvXp1KV68eIKt6OVBRCBXrlxB7Lb2OQEEsuESEmhdL7UJAfUHNmkz8lhyJUISli9fLv369ZPVq1dLtWrVpG7dujJ//nz54osvpEqVKtK/f3/zcxV7EcAp6CSi+rUXAR1ZIgioP0gEreBdi37jJgkjRoyQsWPHyvvvvy9FihTJNNpt27ZJgwYNpF27dtK+ffvgoaE9jgsBdQpxwRTYi1S/gVVdSjqu9pIS2D17aNwkAYJAFGHMmDExO9eyZUupVauWdOjQIea1ekHwEFCnEDydJdJj1W8iaOm1ai9220BcJAFyQHRg2bJlcaNBymHcuHFStWrVuO/RC4OBgDqFYOgpu71U/WYXufS8T+3Fbr3HRRKuu+46GTVqVMQUQzR4tm7dKh07dpRp06bZjWAajk6dgt1KV/3arV+3R6f24jai/movJklgF0OdOnXMLoZEpVixYrJ48WI566yzEr1Vr/cxAuoUfKwcF7qm+nUBxDRqQu3FbmXHJAlTp041dQhTpkxJGInrr79e7rrrLmnUqFHC9+oN/kVAnYJ/deNGz1S/bqCYPm2ovdit65gkYejQoWa7W7du3RJGIpl7E36Y3uAZAuoUPIM6JQ9S/aYE9sA+VO0lsKqLq+MxSUIykQQiCG3atJGGDRvG1Rm9KBgIqFMIhp6y20vVb3aRS8/71F7s1ntMkvDDDz/IpZdeKtQmJCrUIixdulTOPPPMRG/V632MgDoFHyvHha6pfl0AMY2aUHuxW9kxSQLDJxLw0ksvSdGiReNGA1LRpUsXIRKhYhcC6hTs0mf4aFS/duvX7dGpvbiNqL/ai4sk8I2GVq1amaOX45Vzzz1X3nzzTalcuXK8t+h1AUFAnUJAFJXNbqp+swlcmt6m9mK34uMiCUDw4osvypIlS8yxzLGkRYsWctlll5l6BBX7EFCnYJ9OQ0ek+rVbv26PTu3FbUT91V7cJIFuv/DCCybtwLcbIp19QIqBg5dIMyhB8Jei3eyNOgU30fRfW6pf/+nEzz1Se/GzdpLvW0Ikgcd99dVX0rdvX1mxYoXUqFFD/vrrL8mdO7f5/3nnnScDBgzQFEPyevF1C+oUfK2epDun+k0awrRqQO3FbnUnTBIcODh2GWLAgUkcvQxh0F0MdhuLMzp1CnbrWfVrt37dHp3ai9uI+qu9bJMEnTD8pUgve6NOwUu0vX+W6td7zIP8RLWXIGsvdt+VJMTGSK8IQ0Cdgt0mofq1W79ujy4I9sKpwf/8848ce+yxbg/f+vaUJFivYvcHGASn4P6o06dF1W/66NqNkQbBXubMmWO28W/ZssWNIadVG4YkZGRkHM7IyPB84DyzX79+nj9XH5gcAkFwCsmNML3vVv2mt/4THX0Q7EVJQqJa/d/1GknIPnZpe2cQnELaKseFgat+XQAxjZpIhb1s375dunbtKvPnz5eqVavKoEGDpHr16gb1SZMmycMPPyx79uyRW265RZ588kn59NNP5fbbbzdfJR4/frwULlzYbOc///zzTRriiSeeMP9ntx7XsUsvb968mbT47rvvyrBhw2T58uXSuHFjGTFihJx00kkSrT8cQEhbtWrVktdee02KFSsmzZo1k+bNm5u2+dnMmTPl9ddfl88++0x69uwpX3/9tVxzzTUyePBgKVSokPkK8/fffy8//vijSZfQTy9FSYKXaFvyrFQ4BUugC8QwVL+BUJNvOum1vVBfwKR78skny/333y8fffSRDBw4UHbv3i3suuO0XybyMmXKGCLRp08fKVKkiFx11VVyww03SOvWrc0E/O+//8q8efNk5MiRcu+99xqiwL2QhJYtW0r//v2PwvjXX3+V0047TSZOnCinnHKKuefOO++U7t27R+3PqlWrzMGCZcuWlfvuu0/WrFlj/nz44Yem7csvv9z8adeunenjPffcIzfddJMhNnv37pWPP/7YkIzevXsb4vDAAw+Y670UJQleom3Js7x2CpbAFphhqH4DoypfdNRre2EVTwSA1XWJEiUE0sAE+/zzz8uyZctk4cKFZnJFSDPwkcLixYsbkrB//37Jly+fWb0TZWAivuCC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"},483:function(s,a,e){s.exports=e.p+"assets/img/ruby-nginx.eb55bb1c.png"},547:function(s,a,e){"use strict";e.r(a);var n=e(10),t=Object(n.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("ul",[a("li",[s._v("dockerのインストール")])]),s._v(" "),a("h2",{attrs:{id:"前置き"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前置き"}},[s._v("#")]),s._v(" 前置き")]),s._v(" "),a("p",[s._v("現在では様々な言語、フレームワークを使ってWebサーバーアプリケーションの構築を行います。")]),s._v(" "),a("ul",[a("li",[s._v("ruby\n"),a("ul",[a("li",[s._v("rails")])])]),s._v(" "),a("li",[s._v("python\n"),a("ul",[a("li",[s._v("Django")])])]),s._v(" "),a("li",[s._v("go")]),s._v(" "),a("li",[s._v("java")]),s._v(" "),a("li",[s._v("PHP")])]),s._v(" "),a("p",[s._v("上はほんの一例ですが、どの言語で作っても「HTTPリクエストを受け取って何らかのレスポンスを返す」というHTTPサーバーとしての機能を持っていることには変わりありません。")]),s._v(" "),a("p",[s._v("しかし実際の本番環境においては、通常これらのアプリケーションがユーザーからのリクエストを直接受けることはしません。必ずApacheやNginxなど専用のWebサーバーを中継してリクエストを処理します。")]),s._v(" "),a("p",[s._v("なぜこんな二度手間のようなことをするかというと理由は色々ありますが、大雑把に以下のようなものです。")]),s._v(" "),a("ul",[a("li",[s._v("各言語で実装されたHTTPサーバーの機能が貧弱")]),s._v(" "),a("li",[s._v("TLSの終端などをアプリケーション側に実装したくない\n"),a("ul",[a("li",[s._v("証明書の管理などをアプリケーションでやらないといけない")])])]),s._v(" "),a("li",[s._v("HTTPサーバーとして信頼性の高いものを使いたい\n"),a("ul",[a("li",[s._v("脆弱性対策などが一通り行われている")]),s._v(" "),a("li",[s._v("不具合が少ない\n"),a("ul",[a("li",[s._v("HTTPサーバーとしての機能に特化している")]),s._v(" "),a("li",[s._v("世界中で多く使われているため修正が活発")])])])])])]),s._v(" "),a("p",[s._v("このハンズオンではフロントとしてApacheまたはNginxを利用し、裏側のRailsやGo製アプリケーションと連携するような構成を手元で作ってみたいと思います。")]),s._v(" "),a("h2",{attrs:{id:"go-nginx"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#go-nginx"}},[s._v("#")]),s._v(" go + nginx")]),s._v(" "),a("h3",{attrs:{id:"echo-serverの起動"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#echo-serverの起動"}},[s._v("#")]),s._v(" echo-serverの起動")]),s._v(" "),a("p",[s._v("まずは比較的シンプルな例としてGo言語で作られたアプリケーションとNginxを連携させてみましょう。なぜシンプルなのかはあとでお話しします。")]),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",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("8080")]),s._v(":8080 -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 -it --rm regunorf/bootcamp-go-nginx /bin/bash\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("このコンテナの中にはgo言語で書かれたリクエストを返すだけのシンプルなechoサーバーと、nginxがインストールされています。ソースコードとDockerfileは "),a("a",{attrs:{href:"https://github.com/ryoya-fujimoto/go-echo-server",target:"_blank",rel:"noopener noreferrer"}},[s._v("ここ"),a("OutboundLink")],1),s._v(" にあります。")]),s._v(" "),a("p",[s._v("dockerにログインしたら試しにecho-serverを起動してみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@4dd714ba5f7b:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# echo-server --bind 0.0.0.0:8080")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("8080 portで動き始めるので、別のターミナルを開いてcurlを叩くかブラウザからアクセスしてみましょう。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Accept-Encoding:gzip,deflate'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost:8080\nUser-Agent: curl/7.54.0\nAccept: */*\nContent-Type: application/json\nAccept-Encoding: gzip,deflate\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("bootcamp:true"),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("送信したリクエストのheaderとbodyが出力されます。ブラウザでアクセスするとどのようなヘッダーが送信されているか面白いので試してみてください。")]),s._v(" "),a("h3",{attrs:{id:"nginxと連携させる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#nginxと連携させる"}},[s._v("#")]),s._v(" nginxと連携させる")]),s._v(" "),a("p",[s._v("echo-serverを立ち上げて外からアクセスできるようにはできましたが、echo-serverにはTLSの終端機能もHTMLなどの静的ファイル配信機能もありません。頑張って実装してもいいですが、ここではnginxにその辺りをやってもらいましょう。\n(TLSは今回やりませんが・・・)")]),s._v(" "),a("p",[a("img",{attrs:{src:e(482),alt:"go-nginx"}})]),s._v(" "),a("p",[s._v("まずはecho-serverの起動方法を変更します。今は外からecho-serverに直接アクセスできてしまいますが、それでは都合が悪いためサーバー内部からしかアクセスできないようにします。")]),s._v(" "),a("p",[s._v("ついでにecho-server起動中も他の作業ができるようにバックグラウンドで実行させておきましょう。echo-serverが起動しているかどうかは"),a("code",[s._v("ps")]),s._v("コマンドなどで確認できます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ echo-server --bind "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:8080 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("&")]),s._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 起動できているか確認")]),s._v("\n$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("ps")]),s._v(" -aux "),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(" echo-server\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("バックグラウンドで起動している他に、先ほどのecho-server起動と何が違うでしょうか。試しに先ほどと同じようにブラウザや手元のマシンからcurlなどでアクセスみてもecho-serverにアクセスできません。")]),s._v(" "),a("p",[s._v("しかしecho-serverを起動したdockerの中で"),a("code",[s._v("localhost:8080")]),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 comment"}},[s._v("# 手元のwindowsマシンでcurlやブラウザを開くとアクセスできない")]),s._v("\n$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" localhost:8080\ncurl: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("52")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" Empty reply from server\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# dockerの中からはアクセスできる")]),s._v("\nroot@4dd714ba5f7b:/"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# curl localhost:8080")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("127.0")]),s._v(".0.1:40468 "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" GET / "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("|")]),s._v(" time: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2019")]),s._v("-07-21 02:02:09.250992846 +0000 UTC\nUser-Agent: curl/7.58.0\nAccept: */*\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("p",[s._v("最初の例では"),a("code",[s._v("bind")]),s._v("オプションに"),a("code",[s._v("0.0.0.0:8080")]),s._v("を指定していました。"),a("code",[s._v("0.0.0.0")]),s._v("はざっくり言うと「全てのアドレス」と言う意味で、この場合は「全てのアドレスからのリクエストを受け付ける」と言う設定になります。")]),s._v(" "),a("p",[s._v("反対に2回目の"),a("code",[s._v("127.0.0.1:8080")]),s._v("は"),a("code",[s._v("127.0.0.1 = localhost")]),s._v("からのリクエストからしか受け付けないため、上のような挙動になっています。")]),s._v(" "),a("p",[s._v("ではこの"),a("code",[s._v("localhost")]),s._v("で公開されているecho-serverに、nginxを通してアクセスできるようにしてみましょう。\nnginxの設定ファイルは"),a("code",[s._v("/etc/nginx/nginx.conf")]),s._v("にありますが、設定を見やすくするため"),a("code",[s._v("/etc/nginx/conf.d/")]),s._v("の下に新しく設定ファイルを作っていきます。")]),s._v(" "),a("p",[a("code",[s._v("/etc/nginx/conf.d/bootcamp.conf")]),s._v("というファイルを以下のように作成してください。")]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("server {\n listen 80 default_server;\n server_name _;\n\n root /var/www/html;\n location / {\n try_files $uri $uri/ =404;\n }\n location /echo-server {\n proxy_pass http://127.0.0.1:8080;\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("p",[s._v("次にdefaultで作られている設定を削除しておきます(上の内容をこちらに追記しても構いません)。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("rm")]),s._v(" /etc/nginx/sites-enabled/default\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("nginxを起動します")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ nginx\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")]),s._v("にアクセスしてみてください。するとnginxのwelcomeページが表示されるはずです。そして"),a("code",[s._v("localhost/echo-server")]),s._v("というパスにアクセスするとecho-serverからリクエストが返ってきます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost/echo-server\nConnection: close\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("15")]),s._v("\nUser-Agent: curl/7.54.0\nAccept: */*\nContent-Type: application/json\nAccept-Encoding: gzip,deflate\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("bootcamp:true"),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("無事にgoアプリ+nginxな構成を作ることができました。今回はecho-serverとの連携に"),a("code",[s._v("localhost")]),s._v("を使いましたが、unixドメインソケットを介して連携することもよく行われます。")]),s._v(" "),a("h2",{attrs:{id:"ruby-apache"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ruby-apache"}},[s._v("#")]),s._v(" ruby + apache")]),s._v(" "),a("h3",{attrs:{id:"前書き"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前書き"}},[s._v("#")]),s._v(" 前書き")]),s._v(" "),a("p",[s._v("Go言語やNode.jsで作られたアプリケーションは、大抵の場合複数のリクエストを並行して捌くことが可能です。これはGoの場合はgoroutine、Node.jsは非同期IOという形で並行処理をデフォルトで扱えるような設計になっているためです。")]),s._v(" "),a("p",[s._v("そのため上の例ではnginxからのリクエストを単にそれぞれのアプリケーションに投げつければ、複数のユーザーからアクセスが同時にあっても問題はありませんでした。")]),s._v(" "),a("p",[s._v("それに対してjavaやruby、pythonなどで作られたアプリケーションは通常1度に1つの処理しか行えないため、「マルチプロセス」や「マルチスレッド」という形で並行処理を実現します。そのためプロセスやスレッドを管理するためのソフトウェアが追加で必要になります。")]),s._v(" "),a("p",[a("img",{attrs:{src:e(483),alt:"go-nginx"}})]),s._v(" "),a("p",[s._v("このようなソフトウェアの例としては以下のようなものがあります")]),s._v(" "),a("ul",[a("li",[s._v("ruby\n"),a("ul",[a("li",[s._v("puma")]),s._v(" "),a("li",[s._v("unicorn")]),s._v(" "),a("li",[s._v("passenger")])])]),s._v(" "),a("li",[s._v("java\n"),a("ul",[a("li",[s._v("tomcat")])])]),s._v(" "),a("li",[s._v("python\n"),a("ul",[a("li",[s._v("uWSGI?")])])])]),s._v(" "),a("p",[s._v("pumaやunicornはアプリケーション側に組み込んで並行処理機能を追加するもの、passengerやtomcatはwebサーバー側(Apache)に組み込んでアプリケーションを動かすもの、といったようにツールによって動かし方は様々です。")]),s._v(" "),a("h3",{attrs:{id:"動かしてみる"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#動かしてみる"}},[s._v("#")]),s._v(" 動かしてみる")]),s._v(" "),a("p",[s._v("ここではrubyのアプリケーションをwebサーバーと連携させてみましょう。最近だとpumaやunicornが使われることが多いですが、その場合は先ほどのGoの例と同じようにpumaやunicornをwebアプリとして起動させて、リバースプロキシで連携させます。")]),s._v(" "),a("p",[s._v("同じことをしてもつまらないので、passengerを使ってモジュール組み込みな例を動かしてみましょう。このようにモジュールとして動作させる場合はnginxよりも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",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" run -p "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("80")]),s._v(":80 --rm -it regunorf/bootcamp-passenger /bin/bash\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://github.com/ryoya-fujimoto/ruby-echo-server",target:"_blank",rel:"noopener noreferrer"}},[s._v("ここ"),a("OutboundLink")],1),s._v(" にあります。アプリケーションは"),a("code",[s._v("/app")]),s._v("ディレクトリにあります。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# ls -l")]),s._v("\ntotal "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("20")]),s._v("\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("66")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:15 Dockerfile\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("45")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 04:59 Gemfile\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("325")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:00 Gemfile.lock\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("64")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 04:57 README.md\n-rw-r--r-- "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" root root "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("319")]),s._v(" Jul "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("21")]),s._v(" 05:12 app.rb\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("p",[s._v("コンテナには既にrubyの環境が作られているので、"),a("code",[s._v("ruby main.rb")]),s._v("するとアプリケーションが起動します。")]),s._v(" "),a("p",[s._v("ではpassengerの環境を作っていきましょう。まずはgemコマンドでpassengerをインストールします。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# gem install passenger")]),s._v("\nFetching passenger-6.0.2.gem\nBuilding native extensions. This could take a while"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v(".\nSuccessfully installed passenger-6.0.2\n"),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" gem installed\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",[s._v("そしてapache用のモジュール(プラグイン)をビルドするコマンドを実行します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# passenger-install-apache2-module")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("TUIが起動するので、今回はrubyだけを選択してエンターを押します。\nすると足りてないライブラリが表示されるので、言われるがままインストールしましょう。")]),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-get")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" -y apache2-dev libapr1-dev libaprutil1-dev\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),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("root@f8fb220a22d0:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# passenger-install-apache2-module")]),s._v("\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("Please edit your Apache configuration file, and add these lines:")]),s._v("というメッセージが表示されます。そこに書いてある内容をapacheの設定ファイルに追記せよとのこと。")]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("LoadModule passenger_module /usr/local/bundle/gems/passenger-6.0.2/buildout/apache2/mod_passenger.so\n\n PassengerRoot /usr/local/bundle/gems/passenger-6.0.2\n PassengerDefaultRuby /usr/local/bin/ruby\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("p",[s._v("一旦enterを押して抜けましょう。apacheの設定ファイルに以下の内容を追加します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@0111e4e14e6d:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# vi /etc/apache2/conf-enabled/bootcamp.conf")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v('LoadModule passenger_module /usr/local/bundle/gems/passenger-6.0.2/buildout/apache2/mod_passenger.so\n\n PassengerRoot /usr/local/bundle/gems/passenger-6.0.2\n PassengerDefaultRuby /usr/local/bin/ruby\n\n\n\n ServerName bootcamp\n DocumentRoot "/app/public"\n RackEnv production\n\n\n\n Options FollowSymlinks Includes\n AllowOverride All\n AddType text/html .html\n\n Require all granted\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("p",[s._v("apacheを起動します。ServerNameについて警告が出ますがひとまず問題ありません。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("root@0111e4e14e6d:/app"),a("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# apachectl restart")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("無事起動できたらホストのブラウザなどからアクセスしてみましょう。sinatraアプリが動いていれば成功です。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("$ "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("curl")]),s._v(" -H "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("'Content-Type:application/json'")]),s._v(" -d "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"{"')]),s._v("bootcamp"),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('":"')]),a("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("true")]),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"}"')]),s._v(" localhost\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("1つ目の例と違うのは、sinatraアプリケーションが単体で起動してlocalhostなどをlistenしているのではなく、apacheの一部として起動している点です。この場合はechoアプリケーションを再起動するためにはApacheごと再起動しなければいけません。")]),s._v(" "),a("h2",{attrs:{id:"追加"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#追加"}},[s._v("#")]),s._v(" 追加")]),s._v(" "),a("ul",[a("li",[s._v("apacheが起動している状態で"),a("code",[s._v("/app")]),s._v("以下の"),a("code",[s._v("app.rb")]),s._v("を変更してみましょう。")]),s._v(" "),a("li",[s._v("pumaまたはunicornを使ってapacheまたはnginxとrubyアプリケーションを連携させてみましょう。")])]),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/28.67fdfc00.js b/assets/js/28.76e1a8ff.js similarity index 99% rename from assets/js/28.67fdfc00.js rename to assets/js/28.76e1a8ff.js index 6021e347..76562a54 100644 --- a/assets/js/28.67fdfc00.js +++ b/assets/js/28.76e1a8ff.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{237:function(s,e,a){s.exports=a.p+"assets/img/getting-started.a1278e29.png"},517: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([[28],{237:function(s,e,a){s.exports=a.p+"assets/img/getting-started.a1278e29.png"},518: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/29.cee133e1.js b/assets/js/29.d0f11907.js similarity index 99% rename from assets/js/29.cee133e1.js rename to assets/js/29.d0f11907.js index 2c725e16..c484de72 100644 --- a/assets/js/29.cee133e1.js +++ b/assets/js/29.d0f11907.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{237:function(s,e,t){s.exports=t.p+"assets/img/getting-started.a1278e29.png"},521:function(s,e,t){"use strict";t.r(e);var n=t(10),a=Object(n.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("前回ではコンテナの起動に作成済みのイメージを使ってコンテナを起動していました。\n今回は作成済みのコンテナイメージを使ってコンテナを起動するのではなく、コンテナイメージの作成から行ってみましょう。")]),s._v(" "),e("h3",{attrs:{id:"演習2-dockerコンテナイメージを作成する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習2-dockerコンテナイメージを作成する"}},[s._v("#")]),s._v(" 演習2 Dockerコンテナイメージを作成する")]),s._v(" "),e("p",[s._v("Dockerコンテナイメージの作成には予め作成済みである"),e("code",[s._v("DockerFile")]),s._v("を使用します")]),s._v(" "),e("ul",[e("li",[s._v("DockerFileの取得"),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("git")]),s._v(" clone https://github.com/docker/getting-started.git\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",[e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" getting-started\n"),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" build -t iijbootcamp_docker01 "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\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("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 --name iijbootcamp_docker01-tutorial iijbootcamp_docker01\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('/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/06/17 04:10:18 [notice] 1#1: using the "epoll" event method\n2022/06/17 04:10:18 [notice] 1#1: nginx/1.21.6\n2022/06/17 04:10:18 [notice] 1#1: built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027)\n2022/06/17 04:10:18 [notice] 1#1: OS: Linux 4.18.0-348.2.1.el8_5.x86_64\n2022/06/17 04:10:18 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/06/17 04:10:18 [notice] 1#1: start worker processes\n2022/06/17 04:10:18 [notice] 1#1: start worker process 32\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("ここまでできたらDockerコンテナによる仮想環境プラットフォームの構築は完了です。\n"),e("ul",[e("li",[s._v("とりあえずここでは "),e("strong",[s._v("Ctrl+C")]),s._v(" で停止してください。")])])])]),s._v(" "),e("h3",{attrs:{id:"発展課題2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#発展課題2"}},[s._v("#")]),s._v(" 発展課題2")]),s._v(" "),e("p",[s._v("前回同様、演習. 2の通り実施するとターミナルが占有されていまいます。\n従って、今回もバックグラウンドで起動し、ターミナルが占有されないように実行してみましょう")]),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 -d --name iijbootcamp_docker01-tutorial iijbootcamp_docker01\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:t(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("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考"}},[s._v("#")]),s._v(" 参考")]),s._v(" "),e("h3",{attrs:{id:"docker-イメージのビルド"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#docker-イメージのビルド"}},[s._v("#")]),s._v(" Docker イメージのビルド")]),s._v(" "),e("p",[s._v("Dockerコンテナを使って仮想環境プラットフォームを作成するためには、Dockerイメージが必要となります。")]),s._v(" "),e("p",[s._v("通常であれば、"),e("code",[s._v("Dockerfile")]),s._v("を使用して自分のアプリケーションのDockerイメージを作成します。\n"),e("code",[s._v("Dockerfile")]),s._v("は、アプリケーションの依存関係や設定、実行コマンドなどを指定するためのテキストファイルです。\n"),e("code",[s._v("DockerFile")]),s._v("が作成できたらDockerイメージをビルドして作成します。その際に使うコマンドは"),e("code",[s._v("docker build")]),s._v("になります。")]),s._v(" "),e("p",[s._v("通常であればテキストエディタを開いて"),e("code",[s._v("DockerFile")]),s._v("を作成しますが、完全にゼロの状態から"),e("code",[s._v("DockerFile")]),s._v("を作成するのは難しい為、先ずはチュートリアル用に公開されている物を使用すると良いでしょう")]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{237:function(s,e,t){s.exports=t.p+"assets/img/getting-started.a1278e29.png"},522:function(s,e,t){"use strict";t.r(e);var n=t(10),a=Object(n.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("前回ではコンテナの起動に作成済みのイメージを使ってコンテナを起動していました。\n今回は作成済みのコンテナイメージを使ってコンテナを起動するのではなく、コンテナイメージの作成から行ってみましょう。")]),s._v(" "),e("h3",{attrs:{id:"演習2-dockerコンテナイメージを作成する"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演習2-dockerコンテナイメージを作成する"}},[s._v("#")]),s._v(" 演習2 Dockerコンテナイメージを作成する")]),s._v(" "),e("p",[s._v("Dockerコンテナイメージの作成には予め作成済みである"),e("code",[s._v("DockerFile")]),s._v("を使用します")]),s._v(" "),e("ul",[e("li",[s._v("DockerFileの取得"),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("git")]),s._v(" clone https://github.com/docker/getting-started.git\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",[e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("cd")]),s._v(" getting-started\n"),e("span",{pre:!0,attrs:{class:"token function"}},[s._v("docker")]),s._v(" build -t iijbootcamp_docker01 "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(".")]),s._v("\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("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 --name iijbootcamp_docker01-tutorial iijbootcamp_docker01\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('/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/06/17 04:10:18 [notice] 1#1: using the "epoll" event method\n2022/06/17 04:10:18 [notice] 1#1: nginx/1.21.6\n2022/06/17 04:10:18 [notice] 1#1: built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027)\n2022/06/17 04:10:18 [notice] 1#1: OS: Linux 4.18.0-348.2.1.el8_5.x86_64\n2022/06/17 04:10:18 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/06/17 04:10:18 [notice] 1#1: start worker processes\n2022/06/17 04:10:18 [notice] 1#1: start worker process 32\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("ここまでできたらDockerコンテナによる仮想環境プラットフォームの構築は完了です。\n"),e("ul",[e("li",[s._v("とりあえずここでは "),e("strong",[s._v("Ctrl+C")]),s._v(" で停止してください。")])])])]),s._v(" "),e("h3",{attrs:{id:"発展課題2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#発展課題2"}},[s._v("#")]),s._v(" 発展課題2")]),s._v(" "),e("p",[s._v("前回同様、演習. 2の通り実施するとターミナルが占有されていまいます。\n従って、今回もバックグラウンドで起動し、ターミナルが占有されないように実行してみましょう")]),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 -d --name iijbootcamp_docker01-tutorial iijbootcamp_docker01\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:t(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("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考"}},[s._v("#")]),s._v(" 参考")]),s._v(" "),e("h3",{attrs:{id:"docker-イメージのビルド"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#docker-イメージのビルド"}},[s._v("#")]),s._v(" Docker イメージのビルド")]),s._v(" "),e("p",[s._v("Dockerコンテナを使って仮想環境プラットフォームを作成するためには、Dockerイメージが必要となります。")]),s._v(" "),e("p",[s._v("通常であれば、"),e("code",[s._v("Dockerfile")]),s._v("を使用して自分のアプリケーションのDockerイメージを作成します。\n"),e("code",[s._v("Dockerfile")]),s._v("は、アプリケーションの依存関係や設定、実行コマンドなどを指定するためのテキストファイルです。\n"),e("code",[s._v("DockerFile")]),s._v("が作成できたらDockerイメージをビルドして作成します。その際に使うコマンドは"),e("code",[s._v("docker build")]),s._v("になります。")]),s._v(" "),e("p",[s._v("通常であればテキストエディタを開いて"),e("code",[s._v("DockerFile")]),s._v("を作成しますが、完全にゼロの状態から"),e("code",[s._v("DockerFile")]),s._v("を作成するのは難しい為、先ずはチュートリアル用に公開されている物を使用すると良いでしょう")]),s._v(" "),e("credit-footer")],1)}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/31.933bab7f.js b/assets/js/31.9ffadeca.js similarity index 99% rename from assets/js/31.933bab7f.js rename to assets/js/31.9ffadeca.js index 24b0f5ae..c8bbf18d 100644 --- a/assets/js/31.933bab7f.js +++ b/assets/js/31.9ffadeca.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{458:function(s,t,a){s.exports=a.p+"assets/img/event-loop.08b77931.png"},540: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(458),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([[31],{460:function(s,t,a){s.exports=a.p+"assets/img/event-loop.08b77931.png"},541: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(460),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/36.53033b52.js b/assets/js/36.0934d1ed.js similarity index 96% rename from assets/js/36.53033b52.js rename to assets/js/36.0934d1ed.js index 8d69607d..cb12461c 100644 --- a/assets/js/36.53033b52.js +++ b/assets/js/36.0934d1ed.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[36],{501: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([[36],{497: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/37.3f9bf71e.js b/assets/js/37.d1dd3600.js similarity index 99% rename from assets/js/37.3f9bf71e.js rename to assets/js/37.d1dd3600.js index 3b0ca3a7..a23e8f73 100644 --- a/assets/js/37.3f9bf71e.js +++ b/assets/js/37.d1dd3600.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{497: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([[37],{498: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/38.0654fab9.js b/assets/js/38.88f91ac8.js similarity index 99% rename from assets/js/38.0654fab9.js rename to assets/js/38.88f91ac8.js index 425a1e88..3c6b111c 100644 --- a/assets/js/38.0654fab9.js +++ b/assets/js/38.88f91ac8.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{498: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("h2",{attrs:{id:"_4-ansible-playbook-の作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-ansible-playbook-の作成"}},[s._v("#")]),s._v(" 4. Ansible playbook の作成")]),s._v(" "),a("p",[s._v("皆さんはここまでで既にansible を使ってホストの管理を行っていました。\n先ほどの演習で使った"),a("code",[s._v("ansible")]),s._v("コマンドがそれであり、ほかにも様々なモジュールを用いることで\n例えばアプリケーションのインストールなども行うことができます。")]),s._v(" "),a("p",[s._v("しかし、Ansible のアドホックコマンドは単純なオペレーションには便利ですが、\n複雑な構成管理や作業の定型化などには適していません。\nAnsible の真価を発揮するためには、Playbook の使用方法を学習し、一連のターゲットホストに対して複数の複雑なタスクを簡単に反復可能な方法で実行できるようにする必要があります。")]),s._v(" "),a("h3",{attrs:{id:"playbook-について"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook-について"}},[s._v("#")]),s._v(" Playbook について")]),s._v(" "),a("p",[s._v("Playbook(プレイブック)は、管理対象に対してこうなってほしいという構成や手順を記述したファイルです。\nplaybook は先ほど実行していたアドホックコマンドを複数取り込み、複数の task のセットとして利用することができるようになります。")]),s._v(" "),a("p",[s._v("タスクは、特定の作業単位を実行するモジュールのアプリケーションです。\nつまり、Playbook とは、特定の順序で実行される 1 つ以上の task を含むテキストファイルです。")]),s._v(" "),a("p",[s._v("では、これまでアドホックに行っていた ansible ping を行う playbook を作成してみましょう。\nPlaybookはYAMLと呼ばれる書式によって書く必要があります。")]),s._v(" "),a("h2",{attrs:{id:"演習-playbookの作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-playbookの作成"}},[s._v("#")]),s._v(" [演習] Playbookの作成")]),s._v(" "),a("ul",[a("li",[s._v("Ansibleのplaybookを作成します。今回は"),a("code",[s._v("playbook.yml")]),s._v("として作成してみます\n"),a("ul",[a("li",[s._v("docker 上で作成する場合は以下の通りvi 等でテキストファイルを作成します")]),s._v(" "),a("li",[s._v("演習環境に沿って実施している人は ansible フォルダがそのままconsoleホストにマウントされているため、ansibleフォルダ配下にファイルを作成しvscodeで編集することが可能です")])])]),s._v(" "),a("li",[s._v("playbook.ymlの作成\n"),a("ul",[a("li",[s._v("以下のように記載します"),a("div",{staticClass:"language-yml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-yml"}},[a("code",[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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" exercise\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tasks")]),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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ping")]),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")])])])])]),s._v(" "),a("li",[s._v("playbookの実行\n"),a("ul",[a("li",[s._v("Playbookの実行には"),a("code",[s._v("ansible-plyabook")]),s._v("というコマンドを使って実行します"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook playbooks.yml\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",[s._v("SSH password:\n\nPLAY "),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(" ***********************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Gathering Facts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ****************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ping"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP ****************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\nhost01 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),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")])])])])])]),s._v(" "),a("p",[s._v("上記の通り exercise グループに属している host00, host01, の 2 台に対し、ping モジュールが実行され"),a("code",[s._v("OK")]),s._v("が表示されれば成功です。")]),s._v(" "),a("h3",{attrs:{id:"解説"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),a("ul",[a("li",[s._v("1 行目: "),a("code",[s._v("---")]),s._v(" "),a("ul",[a("li",[s._v("Playbook の始まりを意味します")]),s._v(" "),a("li",[s._v("YAML における形式の区切りの意味も持つため Playbook を書く際には必ず入れましょう")])])]),s._v(" "),a("li",[s._v("2 行目 "),a("code",[s._v("hosts: exercise")]),s._v(" "),a("ul",[a("li",[s._v("この Playbook(Play)は、Inventory の中の"),a("code",[s._v("exercise")]),s._v("グループに対して実行すると示します")])])]),s._v(" "),a("li",[s._v("4 行目 "),a("code",[s._v("tasks:")]),s._v(" "),a("ul",[a("li",[s._v("この行以下は、この Playbook(Play)で実行される task を定義します")]),s._v(" "),a("li",[s._v("tasks 後の行は、インデント(行頭の空白による字下げ)が入ります\n"),a("ul",[a("li",[s._v("このインデントは、YAML の書式同様、以降の要素が tasks の子要素や孫要素となっていることを意味します")])])])])]),s._v(" "),a("li",[s._v("5 行目 "),a("code",[s._v("ping")]),s._v(" "),a("ul",[a("li",[s._v("ここで"),a("code",[s._v("ping")]),s._v("モジュールを用いて操作する事(task)を宣言します\n"),a("ul",[a("li",[s._v("モジュールによって様々なオプションを追加することがあります")])])])])])]),s._v(" "),a("h2",{attrs:{id:"発展-gather-の停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#発展-gather-の停止"}},[s._v("#")]),s._v(" [発展] gather の停止")]),s._v(" "),a("p",[s._v("先ほどの実行結果で "),a("code",[s._v("TASK[ping]")]),s._v(" の前に "),a("code",[s._v("TASK[Gathering Facts]")]),s._v("というものがあったことに気づいたでしょうか。\nAnsible は通常、実行する際に実行対象となるホストから様々な情報の収集を行っています。\nこれらはansible_factsと呼ばれる特殊な変数に格納され、続くtaskで活用したり、収集結果をファイルに出力するなどに活用することができます。")]),s._v(" "),a("p",[s._v("しかし一方でそういった情報を収集する必要が無い場合は、収集の分だけ実行時間が長引くことになってしまいます。\n従って収集する必要がない場合は明示的に情報収集を停止したり、設定ファイルを編集し、デフォルトの動作を切り替えることで収集を停止し、即座に記載したtaskを実行させることができます。")]),s._v(" "),a("p",[s._v("以下のいずれかを実施することで下記の通り直ぐにpingモジュールの実行に移ることができます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook playbooks.yml\nSSH password:\n\nPLAY "),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(" ***********************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ping"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP ****************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\nhost01 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),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("h3",{attrs:{id:"playbook単位での停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook単位での停止"}},[s._v("#")]),s._v(" playbook単位での停止")]),s._v(" "),a("ul",[a("li",[s._v("playbookに以下を宣言することで停止することができます\n"),a("ul",[a("li",[s._v("記載する場所どこでも構いませんが"),a("code",[s._v("hosts")]),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("gather_facts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])]),s._v(" "),a("h3",{attrs:{id:"設定ファイルでの停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#設定ファイルでの停止"}},[s._v("#")]),s._v(" 設定ファイルでの停止")]),s._v(" "),a("ul",[a("li",[s._v("ansible.cfg に以下の通り記載することでこのplaybookに宣言しなくともgatherを停止することができます\n"),a("ul",[a("li",[s._v("宣言する箇所は"),a("code",[s._v("[defaults]")]),s._v("セクションに宣言してください")])]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("gathering = explicit\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=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{499: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("h2",{attrs:{id:"_4-ansible-playbook-の作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-ansible-playbook-の作成"}},[s._v("#")]),s._v(" 4. Ansible playbook の作成")]),s._v(" "),a("p",[s._v("皆さんはここまでで既にansible を使ってホストの管理を行っていました。\n先ほどの演習で使った"),a("code",[s._v("ansible")]),s._v("コマンドがそれであり、ほかにも様々なモジュールを用いることで\n例えばアプリケーションのインストールなども行うことができます。")]),s._v(" "),a("p",[s._v("しかし、Ansible のアドホックコマンドは単純なオペレーションには便利ですが、\n複雑な構成管理や作業の定型化などには適していません。\nAnsible の真価を発揮するためには、Playbook の使用方法を学習し、一連のターゲットホストに対して複数の複雑なタスクを簡単に反復可能な方法で実行できるようにする必要があります。")]),s._v(" "),a("h3",{attrs:{id:"playbook-について"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook-について"}},[s._v("#")]),s._v(" Playbook について")]),s._v(" "),a("p",[s._v("Playbook(プレイブック)は、管理対象に対してこうなってほしいという構成や手順を記述したファイルです。\nplaybook は先ほど実行していたアドホックコマンドを複数取り込み、複数の task のセットとして利用することができるようになります。")]),s._v(" "),a("p",[s._v("タスクは、特定の作業単位を実行するモジュールのアプリケーションです。\nつまり、Playbook とは、特定の順序で実行される 1 つ以上の task を含むテキストファイルです。")]),s._v(" "),a("p",[s._v("では、これまでアドホックに行っていた ansible ping を行う playbook を作成してみましょう。\nPlaybookはYAMLと呼ばれる書式によって書く必要があります。")]),s._v(" "),a("h2",{attrs:{id:"演習-playbookの作成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#演習-playbookの作成"}},[s._v("#")]),s._v(" [演習] Playbookの作成")]),s._v(" "),a("ul",[a("li",[s._v("Ansibleのplaybookを作成します。今回は"),a("code",[s._v("playbook.yml")]),s._v("として作成してみます\n"),a("ul",[a("li",[s._v("docker 上で作成する場合は以下の通りvi 等でテキストファイルを作成します")]),s._v(" "),a("li",[s._v("演習環境に沿って実施している人は ansible フォルダがそのままconsoleホストにマウントされているため、ansibleフォルダ配下にファイルを作成しvscodeで編集することが可能です")])])]),s._v(" "),a("li",[s._v("playbook.ymlの作成\n"),a("ul",[a("li",[s._v("以下のように記載します"),a("div",{staticClass:"language-yml line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-yml"}},[a("code",[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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("hosts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" exercise\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tasks")]),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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ping")]),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")])])])])]),s._v(" "),a("li",[s._v("playbookの実行\n"),a("ul",[a("li",[s._v("Playbookの実行には"),a("code",[s._v("ansible-plyabook")]),s._v("というコマンドを使って実行します"),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook playbooks.yml\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",[s._v("SSH password:\n\nPLAY "),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(" ***********************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("Gathering Facts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ****************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ping"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP ****************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\nhost01 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),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")])])])])])]),s._v(" "),a("p",[s._v("上記の通り exercise グループに属している host00, host01, の 2 台に対し、ping モジュールが実行され"),a("code",[s._v("OK")]),s._v("が表示されれば成功です。")]),s._v(" "),a("h3",{attrs:{id:"解説"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#解説"}},[s._v("#")]),s._v(" 解説")]),s._v(" "),a("ul",[a("li",[s._v("1 行目: "),a("code",[s._v("---")]),s._v(" "),a("ul",[a("li",[s._v("Playbook の始まりを意味します")]),s._v(" "),a("li",[s._v("YAML における形式の区切りの意味も持つため Playbook を書く際には必ず入れましょう")])])]),s._v(" "),a("li",[s._v("2 行目 "),a("code",[s._v("hosts: exercise")]),s._v(" "),a("ul",[a("li",[s._v("この Playbook(Play)は、Inventory の中の"),a("code",[s._v("exercise")]),s._v("グループに対して実行すると示します")])])]),s._v(" "),a("li",[s._v("4 行目 "),a("code",[s._v("tasks:")]),s._v(" "),a("ul",[a("li",[s._v("この行以下は、この Playbook(Play)で実行される task を定義します")]),s._v(" "),a("li",[s._v("tasks 後の行は、インデント(行頭の空白による字下げ)が入ります\n"),a("ul",[a("li",[s._v("このインデントは、YAML の書式同様、以降の要素が tasks の子要素や孫要素となっていることを意味します")])])])])]),s._v(" "),a("li",[s._v("5 行目 "),a("code",[s._v("ping")]),s._v(" "),a("ul",[a("li",[s._v("ここで"),a("code",[s._v("ping")]),s._v("モジュールを用いて操作する事(task)を宣言します\n"),a("ul",[a("li",[s._v("モジュールによって様々なオプションを追加することがあります")])])])])])]),s._v(" "),a("h2",{attrs:{id:"発展-gather-の停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#発展-gather-の停止"}},[s._v("#")]),s._v(" [発展] gather の停止")]),s._v(" "),a("p",[s._v("先ほどの実行結果で "),a("code",[s._v("TASK[ping]")]),s._v(" の前に "),a("code",[s._v("TASK[Gathering Facts]")]),s._v("というものがあったことに気づいたでしょうか。\nAnsible は通常、実行する際に実行対象となるホストから様々な情報の収集を行っています。\nこれらはansible_factsと呼ばれる特殊な変数に格納され、続くtaskで活用したり、収集結果をファイルに出力するなどに活用することができます。")]),s._v(" "),a("p",[s._v("しかし一方でそういった情報を収集する必要が無い場合は、収集の分だけ実行時間が長引くことになってしまいます。\n従って収集する必要がない場合は明示的に情報収集を停止したり、設定ファイルを編集し、デフォルトの動作を切り替えることで収集を停止し、即座に記載したtaskを実行させることができます。")]),s._v(" "),a("p",[s._v("以下のいずれかを実施することで下記の通り直ぐにpingモジュールの実行に移ることができます。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook playbooks.yml\nSSH password:\n\nPLAY "),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(" ***********************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ping"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP ****************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\nhost01 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),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("h3",{attrs:{id:"playbook単位での停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#playbook単位での停止"}},[s._v("#")]),s._v(" playbook単位での停止")]),s._v(" "),a("ul",[a("li",[s._v("playbookに以下を宣言することで停止することができます\n"),a("ul",[a("li",[s._v("記載する場所どこでも構いませんが"),a("code",[s._v("hosts")]),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("gather_facts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])])])]),s._v(" "),a("h3",{attrs:{id:"設定ファイルでの停止"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#設定ファイルでの停止"}},[s._v("#")]),s._v(" 設定ファイルでの停止")]),s._v(" "),a("ul",[a("li",[s._v("ansible.cfg に以下の通り記載することでこのplaybookに宣言しなくともgatherを停止することができます\n"),a("ul",[a("li",[s._v("宣言する箇所は"),a("code",[s._v("[defaults]")]),s._v("セクションに宣言してください")])]),s._v(" "),a("div",{staticClass:"language- line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[s._v("gathering = explicit\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=n.exports}}]); \ No newline at end of file diff --git a/assets/js/39.fd14a75e.js b/assets/js/39.03c1bce8.js similarity index 99% rename from assets/js/39.fd14a75e.js rename to assets/js/39.03c1bce8.js index 9439b35b..4621aba0 100644 --- a/assets/js/39.fd14a75e.js +++ b/assets/js/39.03c1bce8.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{499:function(s,a,t){"use strict";t.r(a);var n=t(10),e=Object(n.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_5-ansible-によるサーバセットアップ"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_5-ansible-によるサーバセットアップ"}},[s._v("#")]),s._v(" 5. Ansible によるサーバセットアップ")]),s._v(" "),a("p",[s._v("ここまでで一通り、Ansible playbook の作り方を学びました。\nでは、ここからは実際にサーバのセットアップを行っていきます。")]),s._v(" "),a("h3",{attrs:{id:"サーバ構築用のplaybook-を作成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#サーバ構築用のplaybook-を作成する"}},[s._v("#")]),s._v(" サーバ構築用のplaybook を作成する")]),s._v(" "),a("p",[s._v("前回、Playbook は"),a("strong",[s._v("一連のターゲットホストに対して複数の複雑なタスクを簡単に反復可能な方法で実行できるようにする")]),s._v("と記載しました。")]),s._v(" "),a("p",[s._v("今回は前回の「対象ホストに対して ping 応答を確認する」とは異なり、\n実際にサーバに対して何らかの操作を施すものを作ります。")]),s._v(" "),a("h2",{attrs:{id:"実践演習-ユーザ・グループを作成するplaybookを作る"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#実践演習-ユーザ・グループを作成するplaybookを作る"}},[s._v("#")]),s._v(" [実践演習] ユーザ・グループを作成するplaybookを作る")]),s._v(" "),a("p",[s._v("以下の要件を満たす playbook を作成してください")]),s._v(" "),a("h3",{attrs:{id:"要求仕様"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#要求仕様"}},[s._v("#")]),s._v(" 要求仕様")]),s._v(" "),a("ul",[a("li",[s._v("作成する playbook 名\n"),a("ul",[a("li",[a("strong",[s._v("create_group.yml")])])])]),s._v(" "),a("li",[s._v("操作対象のターゲットグループ\n"),a("ul",[a("li",[s._v("グループ名\n"),a("ul",[a("li",[a("strong",[s._v("exercise")])])])]),s._v(" "),a("li",[s._v("対象ホスト\n"),a("ul",[a("li",[a("strong",[s._v("host00, host01")])])])])])]),s._v(" "),a("li",[s._v("実行タスク\n"),a("ul",[a("li",[s._v("タスク1\n"),a("ul",[a("li",[s._v("Linux ユーザグループの作成\n"),a("ul",[a("li",[s._v("グループ名, GID\n"),a("ul",[a("li",[a("strong",[s._v("bootcamp")]),s._v(", "),a("strong",[s._v("1750")])])])])])])])]),s._v(" "),a("li",[s._v("タスク2\n"),a("ul",[a("li",[s._v("Linux ユーザ の作成\n"),a("ul",[a("li",[s._v("ユーザ名, UID\n"),a("ul",[a("li",[a("strong",[s._v("bootcampuser")]),s._v(", "),a("strong",[s._v("4000")])])])])])])])])])])]),s._v(" "),a("h4",{attrs:{id:"tasks作成のヒント"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#tasks作成のヒント"}},[s._v("#")]),s._v(" tasks作成のヒント")]),s._v(" "),a("ul",[a("li",[s._v("linux グループの作成には"),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/group_module.html#ansible-collections-ansible-builtin-group-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("ansible-builtin-group-module"),a("OutboundLink")],1),s._v("を使います")]),s._v(" "),a("li",[s._v("linux ユーザの作成には"),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html#ansible-collections-ansible-builtin-user-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("ansible-builtin-user-module"),a("OutboundLink")],1),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("playbookが作成できたならば以下の通り実行します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook create_group.yml\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),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("SSH password:\n\nPLAY "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("演習.5 ユーザ・グループを作成するplaybookを作る"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***********************************************************************************************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ユーザグループを作成する"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" **********************************************************************************************************************************************************************************************************\nchanged: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nchanged: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ユーザを作成する"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ******************************************************************************************************************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP *******************************************************************************************************************************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("host")]),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("h3",{attrs:{id:"作成されたことの確認"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#作成されたことの確認"}},[s._v("#")]),s._v(" 作成されたことの確認")]),s._v(" "),a("p",[s._v("ansibleコマンドが無事に実行できたならば、\nansible-exerciseのリポジトリにあるチェック用のplaybookを使ってグループとユーザの確認を行います。")]),s._v(" "),a("p",[s._v("分からなければ以下をそのままコピーして確認用のplaybookを作成します")]),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 punctuation"}},[s._v("---")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"演習.5 ユーザ・グループを作成するplaybookを作る"')]),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(" exercise\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("gather_facts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tasks")]),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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" グループ 'bootcamp' の情報を取得\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.command")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" getent group bootcamp\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp_group\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("changed_when")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ユーザ 'bootcampuser' の情報を取得\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.command")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" getent passwd bootcampuser\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcampuser_user\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("changed_when")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" グループ 'bootcamp' が存在し、GID 1750 を持っているか確認\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.assert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("that")]),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(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bootcamp_group.rc == 0"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"bootcamp_group.stdout.split(':')[2] == '1750'\"")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("fail_msg")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"'bootcamp' グループが GID 1750 を持っていません。\"")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ユーザ 'bootcampuser' が存在し、UID 4000 を持っているか確認\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.assert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("that")]),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(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bootcampuser_user.rc == 0"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"bootcampuser_user.stdout.split(':')[2] == '4000'\"")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("fail_msg")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"'bootcampuser' ユーザが UID 4000 を持っていません。\"")]),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("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("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook assert_group.yml\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[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:"syntax-check・dryrunの活用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#syntax-check・dryrunの活用"}},[s._v("#")]),s._v(" syntax-check・dryrunの活用")]),s._v(" "),a("p",[s._v("Ansible を使う上で、playbook が正しく作る事ができたか事前に確認したくなることはないでしょうか。\n幸い、Ansible には事前に書式をチェックする(Syntax check)や実行チェック(dryrun)機能が備わっています。")]),s._v(" "),a("p",[s._v("Playbook の書式チェックには "),a("code",[s._v("--syntax-check")]),s._v("オプションを使います。\n先ほどのコマンドの例で言えば以下のように実行します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ansible-playbook -i inventories/hosts create_group.yml --syntax-check\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これは playbook が正しく記述されているかどうかをチェックするコマンドになります。\n"),a("code",[s._v("--syntax-check")]),s._v("は簡単に実行できる反面、あくまで書式のチェックを行うだけなので\nタスクの実行チェックはなされません。")]),s._v(" "),a("p",[s._v("例えば、 http サーバをインストールし、起動する。といったタスクを作ろうとしたときに\n"),a("code",[s._v("httpd の起動")]),s._v(" -> "),a("code",[s._v("httpd のインストール")]),s._v(" と書いていてもそれぞれの書式が正しければ\nsyntax-check は OK となってしまいます。")]),s._v(" "),a("p",[s._v("従って、Playbook が正しく実行できるか?と言うことを事前に調べるにはチェックモード(dryrun)を用います。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ansible-playbook -i inventories/hosts create_group.yml -C\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("こちらのコマンドであれば、実際に Playbook を順番に試行し、実行できるか否かを\nチェックするため、より深く知ることが可能です。\nでは Ansible はこのドライランの時はどのように動作をしているのでしょうか?\nそれはモジュール毎に "),a("code",[s._v("-C")]),s._v("オプション付きで実行された時の動作が記載されており\n条件分岐を行っています。")]),s._v(" "),a("p",[s._v("しかし、dryrunもsyntax-check同様万能ではありません。\nAnsibleにおけるdryrunの実装はモジュールに委ねられており、Ansible全体としての動作を保証していません。\n例えば、コマンドモジュール(command)のようなモジュールはチェックモードの分岐がなく、処理そのものがスキップされてしまいます。\nこういったタスクが含まれている場合、コマンドの結果を次のタスクに受け渡す、\nといったタスクが失敗してしまうので注意して使う必要があります。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{500:function(s,a,t){"use strict";t.r(a);var n=t(10),e=Object(n.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"_5-ansible-によるサーバセットアップ"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_5-ansible-によるサーバセットアップ"}},[s._v("#")]),s._v(" 5. Ansible によるサーバセットアップ")]),s._v(" "),a("p",[s._v("ここまでで一通り、Ansible playbook の作り方を学びました。\nでは、ここからは実際にサーバのセットアップを行っていきます。")]),s._v(" "),a("h3",{attrs:{id:"サーバ構築用のplaybook-を作成する"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#サーバ構築用のplaybook-を作成する"}},[s._v("#")]),s._v(" サーバ構築用のplaybook を作成する")]),s._v(" "),a("p",[s._v("前回、Playbook は"),a("strong",[s._v("一連のターゲットホストに対して複数の複雑なタスクを簡単に反復可能な方法で実行できるようにする")]),s._v("と記載しました。")]),s._v(" "),a("p",[s._v("今回は前回の「対象ホストに対して ping 応答を確認する」とは異なり、\n実際にサーバに対して何らかの操作を施すものを作ります。")]),s._v(" "),a("h2",{attrs:{id:"実践演習-ユーザ・グループを作成するplaybookを作る"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#実践演習-ユーザ・グループを作成するplaybookを作る"}},[s._v("#")]),s._v(" [実践演習] ユーザ・グループを作成するplaybookを作る")]),s._v(" "),a("p",[s._v("以下の要件を満たす playbook を作成してください")]),s._v(" "),a("h3",{attrs:{id:"要求仕様"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#要求仕様"}},[s._v("#")]),s._v(" 要求仕様")]),s._v(" "),a("ul",[a("li",[s._v("作成する playbook 名\n"),a("ul",[a("li",[a("strong",[s._v("create_group.yml")])])])]),s._v(" "),a("li",[s._v("操作対象のターゲットグループ\n"),a("ul",[a("li",[s._v("グループ名\n"),a("ul",[a("li",[a("strong",[s._v("exercise")])])])]),s._v(" "),a("li",[s._v("対象ホスト\n"),a("ul",[a("li",[a("strong",[s._v("host00, host01")])])])])])]),s._v(" "),a("li",[s._v("実行タスク\n"),a("ul",[a("li",[s._v("タスク1\n"),a("ul",[a("li",[s._v("Linux ユーザグループの作成\n"),a("ul",[a("li",[s._v("グループ名, GID\n"),a("ul",[a("li",[a("strong",[s._v("bootcamp")]),s._v(", "),a("strong",[s._v("1750")])])])])])])])]),s._v(" "),a("li",[s._v("タスク2\n"),a("ul",[a("li",[s._v("Linux ユーザ の作成\n"),a("ul",[a("li",[s._v("ユーザ名, UID\n"),a("ul",[a("li",[a("strong",[s._v("bootcampuser")]),s._v(", "),a("strong",[s._v("4000")])])])])])])])])])])]),s._v(" "),a("h4",{attrs:{id:"tasks作成のヒント"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#tasks作成のヒント"}},[s._v("#")]),s._v(" tasks作成のヒント")]),s._v(" "),a("ul",[a("li",[s._v("linux グループの作成には"),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/group_module.html#ansible-collections-ansible-builtin-group-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("ansible-builtin-group-module"),a("OutboundLink")],1),s._v("を使います")]),s._v(" "),a("li",[s._v("linux ユーザの作成には"),a("a",{attrs:{href:"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html#ansible-collections-ansible-builtin-user-module",target:"_blank",rel:"noopener noreferrer"}},[s._v("ansible-builtin-user-module"),a("OutboundLink")],1),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("playbookが作成できたならば以下の通り実行します。")]),s._v(" "),a("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook create_group.yml\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),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("SSH password:\n\nPLAY "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("演習.5 ユーザ・グループを作成するplaybookを作る"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ***********************************************************************************************************************************************************************************\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ユーザグループを作成する"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" **********************************************************************************************************************************************************************************************************\nchanged: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nchanged: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nTASK "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("ユーザを作成する"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" ******************************************************************************************************************************************************************************************************************\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host01"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\nok: "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("host00"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n\nPLAY RECAP *******************************************************************************************************************************************************************************************************************************\nhost00 "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ok")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("changed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("unreachable")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("failed")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("skipped")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("rescued")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("ignored")]),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("0")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("host")]),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("h3",{attrs:{id:"作成されたことの確認"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#作成されたことの確認"}},[s._v("#")]),s._v(" 作成されたことの確認")]),s._v(" "),a("p",[s._v("ansibleコマンドが無事に実行できたならば、\nansible-exerciseのリポジトリにあるチェック用のplaybookを使ってグループとユーザの確認を行います。")]),s._v(" "),a("p",[s._v("分からなければ以下をそのままコピーして確認用のplaybookを作成します")]),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 punctuation"}},[s._v("---")]),s._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"演習.5 ユーザ・グループを作成するplaybookを作る"')]),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(" exercise\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("gather_facts")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("tasks")]),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(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" グループ 'bootcamp' の情報を取得\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.command")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" getent group bootcamp\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcamp_group\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("changed_when")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ユーザ 'bootcampuser' の情報を取得\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.command")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" getent passwd bootcampuser\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" bootcampuser_user\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ignore_errors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("true")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("changed_when")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[s._v("false")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" グループ 'bootcamp' が存在し、GID 1750 を持っているか確認\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.assert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("that")]),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(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bootcamp_group.rc == 0"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"bootcamp_group.stdout.split(':')[2] == '1750'\"")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("fail_msg")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"'bootcamp' グループが GID 1750 を持っていません。\"")]),s._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" ユーザ 'bootcampuser' が存在し、UID 4000 を持っているか確認\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("ansible.builtin.assert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("that")]),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(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v('"bootcampuser_user.rc == 0"')]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("-")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"bootcampuser_user.stdout.split(':')[2] == '4000'\"")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[s._v("fail_msg")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[s._v("\"'bootcampuser' ユーザが UID 4000 を持っていません。\"")]),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("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("div",{staticClass:"language-bash line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[s._v("ansible-playbook assert_group.yml\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[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:"syntax-check・dryrunの活用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#syntax-check・dryrunの活用"}},[s._v("#")]),s._v(" syntax-check・dryrunの活用")]),s._v(" "),a("p",[s._v("Ansible を使う上で、playbook が正しく作る事ができたか事前に確認したくなることはないでしょうか。\n幸い、Ansible には事前に書式をチェックする(Syntax check)や実行チェック(dryrun)機能が備わっています。")]),s._v(" "),a("p",[s._v("Playbook の書式チェックには "),a("code",[s._v("--syntax-check")]),s._v("オプションを使います。\n先ほどのコマンドの例で言えば以下のように実行します。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ansible-playbook -i inventories/hosts create_group.yml --syntax-check\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("これは playbook が正しく記述されているかどうかをチェックするコマンドになります。\n"),a("code",[s._v("--syntax-check")]),s._v("は簡単に実行できる反面、あくまで書式のチェックを行うだけなので\nタスクの実行チェックはなされません。")]),s._v(" "),a("p",[s._v("例えば、 http サーバをインストールし、起動する。といったタスクを作ろうとしたときに\n"),a("code",[s._v("httpd の起動")]),s._v(" -> "),a("code",[s._v("httpd のインストール")]),s._v(" と書いていてもそれぞれの書式が正しければ\nsyntax-check は OK となってしまいます。")]),s._v(" "),a("p",[s._v("従って、Playbook が正しく実行できるか?と言うことを事前に調べるにはチェックモード(dryrun)を用います。")]),s._v(" "),a("div",{staticClass:"language-sh line-numbers-mode"},[a("pre",{pre:!0,attrs:{class:"language-sh"}},[a("code",[s._v("ansible-playbook -i inventories/hosts create_group.yml -C\n")])]),s._v(" "),a("div",{staticClass:"line-numbers-wrapper"},[a("span",{staticClass:"line-number"},[s._v("1")]),a("br")])]),a("p",[s._v("こちらのコマンドであれば、実際に Playbook を順番に試行し、実行できるか否かを\nチェックするため、より深く知ることが可能です。\nでは Ansible はこのドライランの時はどのように動作をしているのでしょうか?\nそれはモジュール毎に "),a("code",[s._v("-C")]),s._v("オプション付きで実行された時の動作が記載されており\n条件分岐を行っています。")]),s._v(" "),a("p",[s._v("しかし、dryrunもsyntax-check同様万能ではありません。\nAnsibleにおけるdryrunの実装はモジュールに委ねられており、Ansible全体としての動作を保証していません。\n例えば、コマンドモジュール(command)のようなモジュールはチェックモードの分岐がなく、処理そのものがスキップされてしまいます。\nこういったタスクが含まれている場合、コマンドの結果を次のタスクに受け渡す、\nといったタスクが失敗してしまうので注意して使う必要があります。")]),s._v(" "),a("credit-footer")],1)}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/4.20506e07.js b/assets/js/4.0c1e113d.js similarity index 99% rename from assets/js/4.20506e07.js rename to assets/js/4.0c1e113d.js index 6c06e2ce..3e718936 100644 --- a/assets/js/4.20506e07.js +++ b/assets/js/4.0c1e113d.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{257:function(t,s,a){t.exports=a.p+"assets/img/2-tree.a737f3fb.svg"},395:function(t,s,a){t.exports=a.p+"assets/img/1.f70c70b4.png"},396:function(t,s,a){t.exports=a.p+"assets/img/1-console.d801891b.png"},397:function(t,s,a){t.exports=a.p+"assets/img/2.c84e5dd6.png"},398:function(t,s,a){t.exports=a.p+"assets/img/2-console.ef2ec19d.png"},399:function(t,s,a){t.exports=a.p+"assets/img/2-elements.23f81857.png"},400:function(t,s,a){t.exports=a.p+"assets/img/2.1-document.09510311.png"},401:function(t,s){t.exports="data:image/png;base64,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"},402:function(t,s){t.exports="data:image/png;base64,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"},403:function(t,s){t.exports="data:image/png;base64,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3KW6fvCa6rpjLQM5EREREVESUKJyYvBQdWz4JerYRder48NGKNW9RKkuLcORE6pL/TZ1yqqnGMiJiIiIiPyUlZWpiooKVVl5ppkSXGP/Iepwt3L13sDJaufQM5Qqda6n0hLKu779BgM5EREREZGXCRMmqG9+8xuqR48eZko4W07to+qbStVTjcPV7j4t60DPuO34CdXlgy287CERERERkQu94t/61l2Rw3gYoXvIu/Xoo/qPPEd98sc3zJT2+o+qUkcO7VcH92w3U4iIiIiICstdd92lJk6coO/v2bNHvfbaa/q+68wzz1SVlZVqyZIlZkobKVmpG1Abb8nK5f/yn2pc7fXqg5efVCt/+o9makqvASPUjf/1vjpyuFH9z71XqL1b68wcIiIiIqLCMX/+w2rAgAH6/h133KlDuZeZM2eoGTNmqNmzrzdT2tgndR6/5EZ1bMgw56TOreqUVYvCl6xs+8MyfTv28htU7b/+h74v0Cu+d+t7qnuPPurvf/Si7i0nIiIiIio0EsYBYRz15E8//VTr8JOf/MTM9Ycf/sF1xrvuWKO6vP6EOufoZ2rogcNq6P5DumccYVzPM8sHtrUlkEvPuFcoX3rvVIZyIiIiIupUNmzYoMtS7CGILvVbVEnTAXXKR6+ra/p+rK7p/ZEeur3+cz0N8yKd1GmXq7ih/OjhRoZyIiIiIupUmpqa1OLFS1qHdevWmTlpHP+r6rL1A1Xy8XbVv+sRVVHWrIeS/Z/qaZgX+SorDOVEREREVCyilKzYShob1PCjh9ToPzfqQYf0lmkQOZADQzkRERERFYOoJStBZBTIwQ3lE677jr4PXqEcl00kIiIiIiokkUtWAsg4kAeTurJiSYnz60RERERERAUEpSpRSlY6knEgt0tVNq18Sq17pm3n0Bt+lSlVOdp0QC39/lTda05EREREVIjyrmTFDeMv/+yb+j54hXH+UBARERERFTKUquRNyQrDOBERERFR5iIFcoZxIiIiIqJ4hA7kDONERERERPEJHchHVU/Tt24Yh94DKxjGiYiIiKjgHT582NxTasCAAeZedPY69uzZY+6llPTs2St1TcKA0As+9KxL1NY/LDNT2hty9iXq4K56dXDPdjOFiIiIiKiw3HXXXWrixAn6PgL0q6++qu+7Kisr9bB48WIzxdull17aGsrffHOdevDBB/V9CB3IiYiIiIg6u4qKCnXfffFda9x2zz3fUfX19WZMqa7dunX/oblPREREREQtGhsbdWgeP75KdevWzUzNDMpgfvrTn6mNGzeaKSnsISciIiIi8lFWVqZGjhypKivPNFOi2bBho9q2bZv+CX4XAzkRERERUQ5l9EudRERERESUGQZyIiIiIqIcYiAnIiIiIsohBnIiIiIiopxR6v8BeJnDOYHBdTEAAAAASUVORK5CYII="},404:function(t,s){t.exports="data:image/png;base64,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Pd2NwbOaO3eemjZv9vfWHMA55ny0RURERK1HRGHbiFWvq2nH1Gs7a5oRyIuKdum12EIvOkT6Hutt23aoufOLB74gmN5408NtSmhMGQ0CNZbNPRjo1ca5sezVRn026rRN4D7nnLNqlo/KV199FXaK1Xu3cZ3ly/+iXo3ox/r161X5SJ8+P4jpwMWGINgj4CPoI/B79cknn6rp0KFDeot3OMecT0RERK2L77CNUGlKIpxMTbaXEhO3dkzPr32+WXbrFTbBNVJoG4HXS9A21zK11AbOQzDG4EcDx9hvHUHduV0+gwmfG+dh2a5LN73aXt9Y4ocduFG/jQGT8X7tHK719NPLaj5r5L+UaA9cRPCOF9M+roegT0RERBSthAULHnP91RGUeKCHGYEUwdEWLpwiKCJAo9bYWfvspx07QCOQhhskaY5DYLUhyIf7NUZzb+Ze3TivabdnnosTeqdN2Qg0FuBRWoIeb+dnM73c4T5zLCBgjx9/mRooefToUXnppbUxezPJqad2V20DfrAIpSPfffe9Wo8WyjsGDOhfW9rh9guSeAvJyF8uCfmVSLytxE1V6Ve1rwVEjzaei5eykYaMHz+u9kdt8C7tcD+Bj4GQ+EEbWLs2WLZDRERErY/6uXa9TG2IM3A/99zqmJSNmF+QREnQypXPy4kTJ/SetgG/IDlt2rV6zZslS56SjRs36TUiIiJqTZICgYzZepnaEPS4oue1T58+qqQEAyHD/by8H1VV36q23nvv/bj+VHtzhWeK10GaXuvGYHAke7WJiIhaL/Zst3FpaWnSoUMHNSCSYgdhu3//fpKaGnxvdl5enpqbt5VUVlapAZH2z7YTERFR68OwTdQEZsy4U83xU+1ERETUdkT16j8iIiIiIgqPPdtETcDUcLNshIiIqG1h2CYiIiIiihOWkRARERERxQnDNhERERFRnDBsExERERHFCcM2EREREVGcJAwffgEHSBIRERERxUFChw5pDNtERERERHHAMhIiIiIiojhh2CYiIiIiihOWkRARERERRaG6U0CqA5kiKSnBDVVVklBaIgnlpQzbREREREQRSUySE1l9Vciubp8u1R26qs0JRw5IwtGyYOhm2CYiIiIi8u9EzgCpTu0ox3vmyfEBI6W6Uze1PaF8vyTtXC9JezYzbBMRERER+YWykerup8n3vS6Q7384VU707C3SPkEksWY6Wi2JxUXSbsNSDpAkIiIiIvJL1Wm3T5fjAy4IBu2UmliNoA01oftEVrbq7WbPNhERERG1SampqZKVlSW5uYP0Fu8OnZotZSd1lw9Ou0SOnJpZF7SNE9WSuPMzhm0iIiIianuGDh0qt912q5xyyil6iz+fndxJiitTZGl5LznQqaaNMGE7pIwk+ZRO0uP08/VafRk5gyUts7deIyIiIiJqedCbfdddMyMO2tD+xHEJnPSddEs8qmq06/lOJKFsX2jP9uif/1EGjpkqO9c9LQUL/l1vDULIvvaJrXL0m3J5YdZlcnBXod5DRERERNRyzJw5U4YNG6qWS0pKZP369WrZadCgQZKbmysrV67UW+p8e0pH+aZjQL5M7ie7c0bLwdQOjgGSu6TdhmWhYTvnvPHyb/csVctugXvSw29LRs6ZDNxERERE1GLNnz9PMjMz1fIdd9ypArebq66aKBMnTpQpU6bqLaHMq/9OHnCR9Lz4Wimtbi/Hjx+Xkk8KJWnnm5K0Z1NoGcmud9bUBuwBo6+RMf/x32rZWD1rXE3A/kjan9JJJsx5SZWVEBERERG1JCZoA4I26refeWZp7fTAAw/ovQ1LLP5MEior5OiOv8mkwJcyqeMXkt95ryS/+bi0++JNta/eq//sHm1n4D72TTkDNxERERG1Ktu2bVOlIvbkyYnjkrhrpyR8uVsyko5KVmqVmhLKvlLbsM/1PdsM3ERERETUVlRWVsqKFStrpy1btug93iSUl0qvY0ek77flalIBvGYbuIZtYOAmIiIiorYg0jISL8KGbXAG7qGTf6WWwS1w49WBREREREQtScRlJB40GLa9Cb7MJCHB8SJvIiIiIqIWINoykoY0GLbt8pEdBUtly/K6LnX0Yl+hy0eOVVbI6vvGqd5uIiIiIqKWCOUjTVZG4gza6/5wm1oGt6DNd24TERERUUvWZGUkDNpERERE1NagfCTuZSQM2kREREREsRESthm0iYiIiIhiJyRs5+SNV3Nn0IaOXbMYtImIiIiIfAgJ2wULbpO/PTi1XtAGhOvVsy6TZ2f8C4M2EREREbVY33zzjV4SyczM1EuRs9soKSnRS0EJHTqkBV+UTURERETUBsycOVOGDRuqlhGO33jjDbXslJubq6YVK1boLe5GjRpVG7jffXeLPPLII2oZGLaJiIiIqE3JysqSBx+M3bu0bffc8yspLi7WayJJycntZ+tlIiIiIqJWr7y8XAXiIUMGS3Jyst4aHZSmLFjwB9m+fbveEsSebSIiIiJqk1JTUyU7O1tycwfpLZHZtm27FBUVqZ99d2LYJiIiIiKKE9dfkCQiIiIiomiJ/B+fuvch3Kgi/QAAAABJRU5ErkJggg=="},405:function(t,s){t.exports="data:image/png;base64,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"},406:function(t,s){t.exports="data:image/png;base64,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"},407:function(t,s){t.exports="data:image/png;base64,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"},408:function(t,s,a){t.exports=a.p+"assets/img/2.1-object_Text.f32fd356.png"},409:function(t,s){t.exports="data:image/png;base64,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L0XLHRdCZl6hXiIiIiIiop4mWyluuWVF0kmFNPjUSQz/7N8wasAJ4IRLO8TfgLS2ZvWEKPl0KMlXi8XMf34YEyu+hfc2P4WaB/5JrzXIhOLq3+zAiWPt2HDnZWjdV6+3EBERERFRT1mxYgUuuKBUzbe0tGDr1q1q3qm4uBiTJk3C888/r9dE/HVoFo5lDcdfBk1A47iZaE3PUGMrVMuFSDQGHNiHz9SuxWea/oi0vzSqd1r4SizGfXkO/n7l02reLbn45q/+B7njpjK5ICIiIiLqJatX34+8PGNA9bJl31PJhZuFCxdgwYIFuOqqb+k1dubjZj933iyMueRqfHJ6ME6ePImW9+sx8L0/YuCH22yPm/XVFWrfn6rDycR5M5eg4ub/UPOml+68VCQT72Lw0GzM+8krqmsUERERERH1HDOpkGRSIcdbPPPM0+Hp5z//ud4an3z5nUwcTuz+Pb45/C/4ZlYTFg87iEF/fMhoqZBJhXxBnub7cbPWlgpncvHpsXYmF0REREREfciuXbtUdyfr5Mmpk6o1QnZ1yh14AmPTj6spre0jtU61VOi3bktJvceCyQURERERUf/Q1dWFdeueD091dXV6izdy/ETBp0cx/q/talLJhljnlFRiITG5ICIiIiLq+5LtCuVX0omF5EwuShf9UM1LbsmFfFwtERERERH1nKS7QvmUUmLhjfHQqbQ0x0s1iIiIiIio26XaFcqrlBILaxeo3TVPo+7ZSLOKbJ34hu4C9WlXB16641LVikFERERERD1PdoHqk12hnEnF5l/fqOYlt6SC77QgIiIiIuo9fbIrFJMKIiIiIqL+RXaB6lNdoZhUEBERERGRk6/EgkkFERERERG58ZVYjLtwjvrTmVRIWSPHMqkgIiIiIjpL+Uosah64Eb9f9a2opEKSicRLd16G55aXM6kgIiIiIuolx44d03NAXl6enkue9RgtLS16LlpaRkam8aIJIiIiIiLq91asWIELLihV8zIR2LJli5p3mjRpkprWrVun17ibPn16OLl466063HvvvWreiYkFEREREdEZZOzYsVi1qnveVbFy5Q9x4MABvWQ3cNCgwT/W80RERERE1M+1t7er4L+kJIRBgwbptamR3aseeODXaGho0GuiscWCiIiIiOgMlJ6ejqKiIkyaVKzXJGfXrgbs378fXV1deo07JhZERERERJSypN68TUREREREZMXEgoiIiIiIUsbEgoiIiIiIUgT8P3Zg1mlYsCrRAAAAAElFTkSuQmCC"},410:function(t,s){t.exports="data:image/png;base64,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"},411:function(t,s,a){t.exports=a.p+"assets/img/3-click.154a0bfa.gif"},412:function(t,s,a){t.exports=a.p+"assets/img/4-input.65808a47.gif"},413:function(t,s,a){t.exports=a.p+"assets/img/5-post.439678c0.gif"},414:function(t,s,a){t.exports=a.p+"assets/img/6-buttons.a17f2e36.png"},415:function(t,s,a){t.exports=a.p+"assets/img/6-delete-button.7f767117.gif"},525: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("h1",{attrs:{id:"素のjavascript-dom入門"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#素のjavascript-dom入門"}},[t._v("#")]),t._v(" 素のJavaScript + DOM入門")]),t._v(" "),s("p",[t._v("JavaScriptは、ウェブアプリケーションのフロントエンドに欠かせない、重要なプログラミング言語です。この講義では、ReactやVue.js、Svelteなど現代的なJavaScript用フレームワークを一切使わずに簡単なアプリケーションを作ってみることで、そうしたフレームワークが根本的な処理で何をしているのか、はたまたそうしたフレームワークがどのような処理を代替してくれているのか、理解する手がかりにすることを目指します。")]),t._v(" "),s("h2",{attrs:{id:"前提知識"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#前提知識"}},[t._v("#")]),t._v(" 前提知識")]),t._v(" "),s("p",[t._v("他のプログラミング言語によるプログラミング経験。JavaScriptの構文については軽く触れる程度にとどめますので、これまで学習したプログラミング言語について、その言語から類推できるほどの理解が備わっていると好ましいです。")]),t._v(" "),s("h2",{attrs:{id:"事前準備・注意事項"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#事前準備・注意事項"}},[t._v("#")]),t._v(" 事前準備・注意事項")]),t._v(" "),s("ul",[s("li",[t._v("適当なテキストエディターとブラウザーが動くパソコン\n"),s("ul",[s("li",[t._v("特にこだわりがなければ"),s("a",{attrs:{href:"https://azure.microsoft.com/ja-jp/products/visual-studio-code",target:"_blank",rel:"noopener noreferrer"}},[t._v("Visual Studio Codeをこちらからダウンロードしてインストール"),s("OutboundLink")],1),t._v("するのを推奨します")]),t._v(" "),s("li",[t._v("ブラウザーについては、次のいずれかのブラウザーの、比較的新しいバージョンの利用のみを想定します\n"),s("ul",[s("li",[t._v("Microsoft Edge (以下「Edge」)")]),t._v(" "),s("li",[t._v("Google Chrome")]),t._v(" "),s("li",[t._v("Safari")]),t._v(" "),s("li",[t._v("Mozilla Firefox (以下「Firefox」)")]),t._v(" "),s("li",[t._v("⚠️特に断りがない限り、ブラウザーのスクリーンショットを掲載する際はFirefoxで撮影したものを使用します。他のブラウザーを使用した場合やブラウザーなどの設定により、"),s("strong",[t._v("表示される内容が多かれ少なかれ異なります")]),t._v("のでご了承ください")])])])])])]),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("div",{staticClass:"language-bash line-numbers-mode"},[s("pre",{pre:!0,attrs:{class:"language-bash"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# デスクトップにiij-bootcamp-domディレクトリーを作る")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" ~/Desktop\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mkdir")]),t._v(" iij-bootcamp-dom\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("iij-bootcamp-dom")]),t._v("ディレクトリーと呼びます。")]),t._v(" "),s("h2",{attrs:{id:"最初のブラウザーアプリケーション"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#最初のブラウザーアプリケーション"}},[t._v("#")]),t._v(" 最初のブラウザーアプリケーション")]),t._v(" "),s("p",[t._v("とりあえず、最も単純なJavaScriptのアプリケーションを作ってみましょう。")]),t._v(" "),s("p",[t._v("前の節で作った"),s("code",[t._v("iij-bootcamp-dom")]),t._v("ディレクトリーに、"),s("code",[t._v("1.html")]),t._v("というファイルを作ります。「JavaScriptの勉強なのにHTMLファイル?」と疑問に持った方もいらっしゃるかも知れませんのでお答えしますと、次のような事情があります:")]),t._v(" "),s("ul",[s("li",[t._v("今回勉強するDOMを操作するJavaScriptは、原則HTMLファイルを経由して読み込まれるため。"),s("code",[t._v(".js")]),t._v("ファイルだけをブラウザーで開いてプログラムを起動することはできない")]),t._v(" "),s("li",[s("code",[t._v(".html")]),t._v("ファイルと"),s("code",[t._v(".js")]),t._v("ファイルを分割して読み込ませることもできるものの、1つのファイルに完結させた方が扱いが楽だから\n"),s("ul",[s("li",[t._v("細かい話: 特に今回紹介するモダンな"),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 344a4718..913f6c4e 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 d09a1af1..e072386e 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 d46b6c77..00ba33f6 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 d7771bc4..cf1b1476 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 743e2354..833b2160 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 24c6922b..8e8c13a0 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 708fec2f..414d4760 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 49ca831d..b13faa06 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 8a58a600..ceb57df9 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 9b31160c..43127083 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 970fd9ed..86b70de3 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 7e16eb6b..da2dc278 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 226324c0..5723500a 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 d50d27b1..b93403c0 100644 --- a/database/mongodb/index.html +++ b/database/mongodb/index.html @@ -8,7 +8,7 @@ - + @@ -196,7 +196,7 @@ mongo-set [direct: primary] bootcamp-db> mongo-set [direct: primary] bootcamp-db> mongo-set [direct: primary] bootcamp-db> -
1
2
3
4

rs.status()をもう一度Secondaryで叩いてみてください。先ほどとどう変わったでしょうか。


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

rs.status()をもう一度Secondaryで叩いてみてください。先ほどとどう変わったでしょうか。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.
+ diff --git a/database/mysql/index.html b/database/mysql/index.html index 57b1a4e5..eb4af2a0 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 ecf5fc12..7afacddb 100644 --- a/database/overview/index.html +++ b/database/overview/index.html @@ -8,7 +8,7 @@ - + @@ -36,7 +36,7 @@
  • 実行された処理結果は失われない、永続的に保存される

ACID特性は、上記の4つの特性の英単語の頭文字から作られた単語(参考: Wikipedia (opens new window))

一般的に、リレーショナルデータベースは、ACID特性が保証されているが、非リレーショナルデータベース(NoSQL)ではACID特性が緩められていることがある。

  • リレーショナルデータベースは、一般的にACID特性が保証されているため、データを安全に扱うことができるが、ACID特性を保証するためには様々な処理を実行する必要があるため、NoSQLに比べると相対的に速度が遅いと言われる。

  • 非リレーショナルデータベース(NoSQL)は、リレーショナルデータベースに比べて、大規模データを高速に処理することができると言われるが、NoSQLがACID特性の一部を緩めることで、大規模データの処理性能を効率的に実施している部分がある。アプリケーション開発者は、アプリケーションで扱うデータを守る上で、ACID特性の一部が緩められていても問題がないか検討し、必要であれば保証されていない特性をアプリケーション側で補う必要があるが、考慮する必要性が無ければ、ACID特性を緩めることで、高速性等の恩恵を受けることができる。

# 原子性(Atomicity)とは

  • 例: AさんからBさんに10万円送金する場合

    1. Aさんの口座を10万円減額する
    2. Bさんの口座を10万円増額する

仮に、1の処理を実行した後、2の処理が実行できなかった場合、Aさんの口座だけが減額されるトラブルが発生することになる

原子性は、こうしたトラブルが発生しないよう、例えば2の処理が失敗した場合、1の処理も無かったことになることによって、意図した処理が実行されるか(コミット)、処理が全く実行されないか(ロールバック)、どちらかになるように保証する特性

  • コミット(commit)

    トランザクション内で実行してきた処理を確定すること

  • ロールバック(rollback)

    トランザクション内で実行してきた処理を全て破棄し、トランザクション開始時点の状態に戻すこと

# 一貫性・整合性(Consistency)とは

  • 例: 口座の金額は0円以上である

仮に、残高が5千円の口座から、1万円の引落を実行した場合、口座残高はマイナス5千円となり、「口座の金額は0円以上である」というルールに違反することになるため、このケースでは1万円の引落の処理は、データベース側でブロックされ実行されない

一貫性・整合性は、データベースに保存される情報が、設計者の想定していない状態にならないよう、あらかじめ設定されたルール(≒業務仕様)に適合するように保証することができる特性

  • 制約の例
    • NOT NULL制約

      NULLデータを許容しない(データが必ずあることを保証する)

    • UNIQUE制約

      UNIQUE制約を設定した列のデータは、必ず一意になる(その列のデータは列内で重複しないことを保証する)

# 独立性・分離性(Isolation)とは

データベースは、組織内などで情報共有のために使われるため、同時に複数のアクセス(トランザクション)を処理する必要がある。独立性・分離性は、複数トランザクションを同時実行しても、複数トランザクションを1つずつ逐次実行した時と同じ結果になるように保証する特性。

# ロック(lock)とは

複数のトランザクションを同時実行する際に、処理対象データが他のトランザクションの影響を受けないよう、処理に必要になる期間データを占有する機能。独立性・分離性を保証する上で必要な機能の1つ。

  • 例: AさんからBさんに10万円送金する処理(トランザクション1)と、BさんからCさんに5万円送金する処理(トランザクション2)を同時実行する場合
    1. トランザクション1がAさんの口座をロックし10万円減額し20万円にする
    2. トランザクション2がBさんの口座をロックし5万円減額し15万円にする
    3. トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機
    4. トランザクション2がCさんの口座をロックし5万円増額し10万円にする
    5. トランザクション2が処理結果を保存し、BさんとCさんの口座ロックを解除する
    6. トランザクション1がBさんの口座をロックし10万増額し25万円にする
    7. トランザクション1が処理結果を保存し、AさんとBさんの口座ロックを解除する

上記例の場合、Bさんの口座は、5万円の減額処理と10万円の増額処理が競合したが、トランザクション2がBさんの口座を2の時点でロックするため、トランザクション2 → トランザクション1の順番で処理が実施される。この結果は、トランザクション1を先に全て実行し、その後トランザクション2を実行した場合と同じになるので、独立性・分離性が担保できている。

# デッドロック(deadlock)とは

デッドロックとは、お互いのロック解除を待つことでお互いの処理が永久に進まなくなる状態のこと。例えば、トランザクション1がトランザクション2のロック解除を待ち、トランザクション2がトランザクション1のロック解除を待ってしまう様な状態になると、トランザクション1もトランザクション2も処理が完了せずロック解除に至らないため、ロックが永久に解除されず処理が永久に終了しなくなってしまう。

  • 例: AさんがBさんに10万円送金する処理(トランザクション1)と、BさんがAさんに5万円送金する処理(トランザクション2)を同時実行する場合 -
    1. トランザクション1がAさんの口座をロックし10万円減額し20万円にする
    2. トランザクション2がBさんの口座をロックし5万円減額し15万円にする
    3. トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機
    4. トランザクション2がAさんの口座を5万増額しようとするが、トランザクション1が口座をロック中のため待機

上記例は、トランザクション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))。

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

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

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

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

- +
  1. トランザクション1がAさんの口座をロックし10万円減額し20万円にする
  2. トランザクション2がBさんの口座をロックし5万円減額し15万円にする
  3. トランザクション1がBさんの口座を10万増額しようとするが、トランザクション2が口座をロック中のため待機
  4. トランザクション2がAさんの口座を5万増額しようとするが、トランザクション1が口座をロック中のため待機

上記例は、トランザクション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 5a693b91..cef3dc3d 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 b3ca4e16..b7b85bd8 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 a3d855af..f7d0b918 100644 --- a/development/docker/docker-compose/index.html +++ b/development/docker/docker-compose/index.html @@ -8,7 +8,7 @@ - + @@ -38,8 +38,8 @@ ├── Dockerfile └── requirements.txt
1
2
3
4
5

# 1-1. サンプルアプリケーションの作成

今回は Docker composeの項であるため Pythonアプリケーションについては言及しません。 -アプリケーションはそれぞれ以下から取得してください。

 curl https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/app.py -O app.py
-
1
 curl https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/requirements.txt -O requirements.txt
+アプリケーションはそれぞれ以下から取得してください。

 curl https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/app.py -o app.py
+
1
 curl https://raw.githubusercontent.com/iij/bootcamp/master/src/development/docker/docker-compose/solution/requirements.txt -o requirements.txt
 
1

# 1-2. Dockerfile の作成

この節では、前述したコンテナのDockerfile を作成します。 Dockerfile の作成については、前講義「Docker を触ってみよう」で行いましたので、各命令などの詳細な説明は割愛します。

以下の内容をファイル名「Dockerfile」で作成してください。

FROM python:3.11-slim
 WORKDIR /code
@@ -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 25ec7474..db7a265d 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 17fe884f..3b59ccf8 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 a39053fa..cba7a0ea 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 ba1d1bf0..686c1ab4 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 9e148c1a..20436ebf 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 79902f43..285baa02 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 78ca4d91..bb930129 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 092cb70a..5d60e05e 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 56e14417..d57877e7 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 0ed356e6..d5a58569 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 0c02b337..eedb961c 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 9951edad..834e050d 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 78b08300..ede02e38 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 b360494d..bdc2d0ff 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 902c1c3c..da9a4566 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 ae40aec4..04e55454 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 72df7083..fa2aa87b 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 2efbeb81..64216218 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 4e50fe6f..04d2e899 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 8c2ca72b..be6dd659 100644 --- a/server-app/fastapi/index.html +++ b/server-app/fastapi/index.html @@ -8,7 +8,7 @@ - + @@ -265,7 +265,7 @@ お時間があればやってみてください。

    • import/設定例
    from logging_context import LoggingContextRoute
     
     app.router.route_class = LoggingContextRoute
    -
    1
    2
    3

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

    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 6c6d5519..f8cc41b0 100644 --- a/server-app/go/index.html +++ b/server-app/go/index.html @@ -8,7 +8,7 @@ - + @@ -871,7 +871,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 44a1607c..e3deb282 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 1858265a..925ed19e 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 bf76ef5f..0a4c27d2 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 af7225a7..cf1ef919 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 874ec081..8636ab13 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 7902cffa..1a9790da 100644 --- a/server-app/overview/index.html +++ b/server-app/overview/index.html @@ -8,7 +8,7 @@ - + @@ -315,7 +315,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 1e8afb68..268d937f 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 bec2ed23..73d3d313 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 c6711047..aed2b843 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 61df5504..362e035d 100644 --- a/web-server/apache_nginx/index.html +++ b/web-server/apache_nginx/index.html @@ -8,7 +8,7 @@ - + @@ -285,7 +285,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 35b5c5b1..f0a61291 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 82d85944..62b3bbfe 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 d0036352..690ec216 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.
    +