【超初心者】Docker を使用して自作 Web サイトを表示してみた

# 1. はじめに
[Webページが表示される仕組み](https://qiita.com/sugurutakahashi12345/items/3cc26f23b82f344fa188)の続編になります。（別に読まなくても大丈夫なように記事は執筆しました。）

本記事では、「Web サイトが表示される原理」、「Docker での Web サーバーの構築」、「Web サイトの作成」の順に説明して、自ら作成した Web サイトが表示されるところまでを体験することが目標になります。

かなり初心者向けの記事になります。
(結果的に、自作の Web サイトを表示することが目的なのに、Docker の使い方の記事みたいになってしまいました、、、)

## 1-1. Why Docker？
Web サイトを作成するためには Web サーバーが必要になりますが、今回の目標を達成するには凝った機能は必要なく、ただ URL でアクセスしたときにコンテンツを返却してくれるだけで十分であるため、手軽に Web サーバーを構築できるを利用できる Docker を使ってみようと思いました。

（なお、筆者は Docker は初めて触ります。）

本記事を投稿する上で、以下の記事がとても参考になりました。
[Docker 公式 httpd イメージを利用して Docker を体験してみよう](https://weblabo.oscasierra.net/docker-httpd-usage/)

結局、本記事は、ほぼその記事の丸写しになってしまいましたが、備忘録もかねて投稿いたします。

# 2. 事前に知っておきたい知識
ご存知の方は飛ばしていただいて結構です。
## 2-1. Web ページが表示される仕組み
細かい説明は省きますが、図で表すと以下のようになります。（[参照元ページ](https://daeuwordpress.com/homepage-system/)）
![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_259125_f56984d6-5c04-531f-ac96-fbb00b5a8470.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/0785812b-8034-d252-8660-bffcb9a62381.png)
本記事では図の右側のWebサーバーをローカル PC 上の Docker のコンテナ上で稼働させます。

なお、今回はローカル PC 上のコンテナに直接アクセスするため、DNS サーバーによる名前解決は行いません。
（つまり、今回は ① と ② は扱いません。いきなり ③ → ④ となります。）

## 2-2. Webサーバー
Web サーバーとはクライアントからの要求に対し、Web ページを閲覧するために必要なデータを転送するサーバーのことです。

「クライアントからの要求」とは、**HTTP リクエスト** のことで、ブラウザでアクセスする URL をみると「https://〜」となっていると思いますが、その頭文字の http とは **HTTP リクエスト** であることを意味しております。

つまり、Web サーバーに対して、**HTTPリクエスト** をすると Web ページを閲覧するために必要なデータ（HTML、CSS、JavaScript、動画、写真など）を転送してくれます。

## 2-3. ドキュメントルート
ドキュメントルートとは、Web サイトで外部から公開される HTML ファイルや画像データなどが設置される場所のことを指します。

もし、仮にドメインが「http\://example.com」である場合、ドキュメントルートに指定したディレクトリに「hogehoge.html」というHTMLファイルを設置していると「http\://example.com/hogehoge.html」とブラウザからアクセスすると、Webサーバーはhogehoge.htmlを返却して、ブラウザにhogehoge.htmlの内容が表示されます。

また、「http\://example.com」とアクセスした場合、デフォルトでその階層の**「index.html」**を返却するようになっています。
（これを知らないと 「index.html」って何だろう？ となってしまうため結構大事かも。。。）

## 2-4. Web サーバーの種類
Web サーバーにも種類があります。
世界的にシェアが高いのは**「Apache」「Nginx」「IIS」**の3種類です。

まず、IIS は Windows サーバー縛りという環境でなければ使うことがないでしょう。

そうなると「Apache」と「Nginx」の2択になりますが、Nginx の方が処理が軽く大量のリクエストを処理するのに向いていおり、さらにリバーシプロキシ機能までついているおまけ付きであり、現在では Nginx の方が人気らしいです。

どちらを使うかは case-by-case であるので状況に応じて使い分けてください。
比較する記事がネットには溢れていました。（個人的には[こちら](https://qiita.com/kamihork/items/49e2a363da7d840a4149)がわかりやすかったです。）

今回は単に手元で Web サーバーとして動けば何でもよかったので、Apache でも Nginx でもどちらでもよかったのですが、参考にした記事が Apache の httpd の Docker イメージを使用していたので、そちらを採用しました。

「httpd」って何？って思う方もいらっしゃると思いますが、「Apache で Web サーバーとしての機能をする常駐のプログラムのこと」だと思っていただければ大丈夫です。（[参考記事](https://wa3.i-3-i.info/word13579.html)）

## 2-5. Docker

Docker についてここでは詳しく説明しませんが、Docker とはコンテナ型の仮想化環境を提供するプラットフォームのことです。
とりあえず、Docker を使えば簡単に開発環境を構築することができるということだけ抑えてもらえれば大丈夫です。

今回は Docker を使用して自身の環境に Web サーバーを構築します。

また、Docker を使用するには自身の環境にインストールする必要があるので、以下の記事などを参考にインストールしてください。
[Docker を Mac にインストールする（更新: 2019/7/13）](https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81)

そもそも Docker について知りたいという方は以下の記事がとても勉強になりましたのでご参考まで。
[Docker 入門の前に知っておきたい基礎知識 - 週末勉強会レポート](https://www.tech-training.jp/blog/entries/28)

# 3. Docker での Web サーバーの構築
Webサーバーとして有名な Apache httpd だけを含んでいる httpd という Docker イメージが公式の Docker Hub リポジトリで公開されています。
今回は[こちら](https://hub.docker.com/_/httpd/)を使用します。

<img width="782" alt="スクリーンショット 2020-07-25 22.00.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/eaf5314d-03bd-dc51-3652-fcb996a5a5a0.png">

また今から使用する Docker のコマンドは大きく以下のようになります。（[参照元](https://morizyun.github.io/docker/about-docker-command.html)）

![docker-command-outline.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/681f17ee-667b-6a40-03f8-b2427cb64da1.jpeg)


## 3-1. httpd イメージのダウンロード・確認（docker image pull、docker image ls）
まずはローカル環境に Docker イメージを持っておく必要があります。 現在ローカルで持っているイメージを確認するには`docker image ls` コマンドを実行します。 
初期状態では一つも Docker イメージがないはずなので以下のように表示されます。

```shell:ダウンロードされているDockerイメージの確認
$ docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```
Docker Hub からイメージを取得するには、`docker image pull [サービス名]:[タグ名]`とコマンド入力します。
httpd のイメージを pull する際に指定できるタグ名は、Docker Hub の[タグ一覧のページ](https://hub.docker.com/_/httpd?tab=tags)から確認することができます。
<img width="711" alt="スクリーンショット 2020-07-25 22.11.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/8e859494-2b0e-fde7-0cd4-4442d177fa44.png">

`docker image pull [サービス名]:[タグ名]`と入力する際に、タグ名を省略した場合はデフォルトで **latest** を指定したことになります。
今回は Apache httpd の最新版をインストールできればよいのでタグ名を指定せずに実行します。

```shell:httpdイメージのダウンロード
$ docker image pull httpd

Using default tag: latest
latest: Pulling from library/httpd
6ec8c9369e08: Pull complete
819d6e0b29e7: Pull complete
6a237d0d4aa4: Pull complete
cd9a987eec32: Pull complete
fdec8f3f8485: Pull complete
Digest: sha256:2a9ae199b5efc3e818cdb41c790638fc043ffe1aba6bc61ada28ab6356d044c6
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest
```
`docker image ls`コマンドでダウントー度されていることを確認します。

```shell:httpdイメージがダウンロードされたことの確認
$ docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
httpd               latest              9d2a0c6e5b57        2 days ago          166MB
```

httpd のサービスがあることが確認できるかとおもいます。

## 3-2. httpd イメージからコンテナを作成・起動する（docker container run）

Docker イメージの取得ができたら、次は実行させます。 
イメージからコンテナを作成して起動するには、`docker container run (オプション) [イメージ名]`とコマンドを実行します。 

```shell:コンテナの作成・起動
$ docker container run -d -p 8080:80 httpd

d2e80c20cfbdcf7aee76772a790821e529bbbdfe07329e9179ea32939cb85797
```

今回`docker container run`コマンドで指定したオプションは`-d` と `-p 8080:80` のオプションです。
これらについて解説します。

`-d` オプションは、コンテナの実行をバックグラウンドで行うオプションです。 このオプションを指定しなかった場合は、ターミナルのコマンド操作が Ctrl+C を実行するまでコンテナに奪われてしまいます。

 `-p 8080:80` オプションは、コンテナのポート番号とローカルのコンピュータのポート番号を紐づけるオプションです。

今回実行したコンテナは httpd の機能を持っておりコンテナの80番ポートで httpd サービスが開始されるので、ローカルマシンの8080番ポートをコンテナの80番ポートに紐づけたことになります。


一旦、これで Web サーバーである httpd コンテナの実行が完了しましたので、ブラウザで http://localhost:8080 にアクセスしてみてください。
「It work!」の表示がされるはずです。

<img width="417" alt="スクリーンショット 2020-07-25 18.45.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/82bcbd14-00a3-aafb-f803-3f0b28df0459.png">


これは、Web サーバーのドキュメントルートにデフォルトでおいてある index.html （あとで確認します）が返却されてその内容がブラウザに表示されているということです。

たったこれだけで Web サーバーを立てることができるとは、Docker はほんとに素晴らしいですね。　

### ポート番号についてちょっとした解説
Webサーバーは一般的に80番ポートで http 通信を待ち受けています。
また、ポート8080番は代替 HTTP ポートと呼ばれ、大抵のプロキシサーバは8080番でサービスを待ち受けています。

以下の図で表すと、クライアントAがブラウザ、プロキシサーバーBが自身の Mac の PC、Web サーバーCが Docker コンテナ上で動いている httpd というイメージです。（[参照元](https://www.fe-siken.com/kakomon/01_aki/q34.html)）

![34.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/4d8a994a-4298-a79a-f82e-43852a4dd293.gif)

## 3-3. 実行中のコンテナを確認する（docker container ls）

現在実行中の docker のコンテナの確認は`docker container ls`のコマンド実行によって確認ができます。

```shell:実行中のコンテナの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
d2e80c20cfbd        httpd               "httpd-foreground"   4 minutes ago       Up 4 minutes        0.0.0.0:8080->80/tcp   clever_chatelet
```
httpdのコンテナプロセスが起動していることが確認できます。

## 3-4. コンテナの停止・起動（docker container stop、docker container start）
実行中のコンテナを一時的に停止したい場合、`docker container stop`コマンドを利用します。 コマンドの引数として、コンテナのIDを指定する必要があります。
コンテナのIDは上記で説明した`docker container ls`のコマンドで確認できます。

```shell:コンテナの停止
docker container stop d2e80c20cfbd

d2e80c20cfbd
```
`docker container ls` のコマンドで先ほど `docker container stop`コマンドで指定したプロセスが起動していないことを確認します。

```shell:実行中のコンテナの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```
停止させたコンテナも表示させたい場合は、`docker container ls` コマンドに `-a` オプションをつけて実行します。 表示された結果の STATUS の欄に Exited と表示されている点に着目してください。

```shell:停止中のコンテナの確認
$ docker container ls -a

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                          PORTS               NAMES
d2e80c20cfbd        httpd               "httpd-foreground"   7 minutes ago       Exited (0) About a minute ago                       clever_chatelet
```
この停止したコンテナを再度起動したい場合は、`docker container start` コマンドにコンテナIDを指定して実行します。ポイントは一度`docker container run`したコンテナは、再び`docker container run`するのではなく`docker container start` コマンドによって再起動させます。
そうしないと`docker container run`する度に別のコンテナが生成されて、httpd のコンテナが複数作られることになります。

```shell:停止中のコンテナの起動
$ docker container start d2e80c20cfbd

d2e80c20cfbd
```

起動後にまた `docker container ls` コマンドを実行すれば、再度実行中であることがわかります。

```shell:起動中のコンテナの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
d2e80c20cfbd        httpd               "httpd-foreground"   9 minutes ago       Up About a minute   0.0.0.0:8080->80/tcp   clever_chatelet
```

## 3-5. コンテナへのログイン
実際にコンテナにログインしてみて、httpd のドキュメントルートの状況を確認してみたいと思います。

コンテナには `docker exec -it [コンテナID] /bin/bash` とコマンド実行すると、そのコンテナにログインすることができ、コマンド操作をすることができます。

```shell:コンテナへのログイン
$ docker exec -it d2e80c20cfbd /bin/bash

root@d2e80c20cfbd:/usr/local/apache2# (指定したコンテナ内のコンソール)
```

httpd イメージでは、Apache httpd のドキュメントルート(DocumentRoot)は、`/usr/local/apache2/htdocs/`となっていますので、そこを確認してみます。

```shell:ドキュメントルートの確認
root@d2e80c20cfbd:/usr/local/apache2# cd /usr/local/apache2/htdocs/
root@d2e80c20cfbd:/usr/local/apache2/htdocs# ls
index.html
root@d2e80c20cfbd:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works!</h1></body></html>
```

ドキュメントルートには「It works!」と書かれた index.html という HTML ファイルがあることが確認できますね。
これがさきほど、ブラウザで http://localhost:8080 にアクセスしたときに表示されたものになります。


ちなみに、ログインしたコンテナからは `exit` コマンドでログアウトできます。

```shell:ログインしたコンテナからのログアウト
root@d2e80c20cfbd:/usr/local/apache2/htdocs# exit
exit

$ (Macのコンソール)
```

# 4. 自作の Web サイトを作成する
さて、ここまでで Docker を利用して Apache httpd を実行できることはお分かり頂けたでしょう。 しかし一つ問題は、表示できた WEB サイトは Apache httpd デフォルトの「It work!」という画面だということです。 

ここでは、もうひと頑張りして、自作の HTML を作成して、それを表示してみようということになります。

## 自作する HTML ファイルの保存場所についての考察
まず、自作する HTML ファイルの保存場所について考察します。それには Docker コンテナの生存期間について考える必要があります。

そもそも Docker コンテナのような技術はポータビリティの考え方が重要で、「必要な時にコピーして持ってきて起動し、必要なくなったら破棄する」という考え方が根底にあります。「破棄する」とはコンテナやサーバーごと削除してしまうことを指し、その破棄をする時にファイルのバックアップなどの煩わしい事を考える必要がないことも重要です。

よって、コンテナやサーバー内にバックアップしたいようなコンテンツを保存してしまうと面倒となってしまいます。

このような考え方から、いつ破棄されてしまうか分からないコンテナにはコンテンツやログなどの動的なファイルを保存せず、常時起動されていることが確約されているマシンにコンテンツや動的なファイルを保存するのが良いでしょう。

裏を返すと httpd の Docker コンテナのドキュメントルートに直接保存していくということはしないということになります。

今回は起動した Docker コンテナに HTML を保存するのではなく、Docker を起動しているローカルマシン側に HTML を作成して、起動した Docker コンテナからそれを参照するようにします。 そうすることで、Docker コンテナは破棄されても、ローカルマシンにある HTML ファイルには影響がありません。

## 4-1. Web ページ作成用のディレクトリの作成
ローカル環境に適当なディレクトリ（今回の場合は`/tmp/httpdHtdocs`）にHTMLファイルを配置します。

```bash:
$ cd /tmp
$ mkdir httpdHtdocs
$ cd httpdHtdocs
$ echo "This is test page." > index.html
$ ls
index.html
$ cat index.html
This is test page.
```
今回、index.html の中身は適当でよいのでとりあえず、「This is test page.」 としました。

## 4-2. httpd コンテナのドキュメントルートの指定（docker container run -v）

`docker run` コマンドでコンテナを起動する際に、`-v "[ホストディレクトリの絶対パス]:[コンテナの絶対パス]"` のオプションを指定することでコンテナにローカルマシンのディレクトリをマウントすることができます。

先ほど確認したように、httpd イメージでは、Apache httpd のドキュメントルート(DocumentRoot)は、`/usr/local/apache2/htdocs/`となっています。

なので、ホストディレクトリの絶対パスに`/tmp/httpdHtdocs`、コンテナの絶対パスに `/usr/local/apache2/htdocs/`を指定して、`docker container run` コマンドを実行します。

```shell:ホストのディレクトリをマウントしながらコンテナの作成・起動
$ docker container run -d -p 8080:80 -v "/tmp/httpdHtdocs:/usr/local/apache2/htdocs/" httpd

c789395b6476f694ac991f7b207d4d1007229f6c5a41f4af8d2dd2bf14ee88f2
```
`docker container ls` コマンドによってコンテナが起動していることの確認します。

```shell:コンテナが起動していることの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
ec4edd39d7ff        httpd               "httpd-foreground"   16 minutes ago      Up 16 minutes       0.0.0.0:8080->80/tcp   happy_hoover
```

この状態でブラウザで http://localhost:8080 にアクセスしてみてください。
マウントする前では、「It work!」とデフォルトのページが表示されていましたが、マウント先の`/tmp/httpdHtdocs`がドキュメントルートになっているため、さきほど index.html に記述した「This is test page.」の表示がされるはずです。

<img width="418" alt="スクリーンショット 2020-07-25 19.21.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/9495575d-8346-597b-ba95-e3a268d31e5f.png">

これにて自作 Web サイトの完成です。
おめでとうございます。

# 5. Web サイトの拡張

これからどうやってWebサイトを拡張していくかはおまかせします。

試しに HTML、CSS、JavaScript のそれぞれのファイルを作成して、HTMLからCSSとJavaScriptのファイルを読み込ませたサンプルを GitHub に投稿してみたので参考にしてみてください。
（[こちら]((https://github.com/suguruTakahashi-1234/sample_web_page
))になります。）

もっとこだわっていくと[ルーティング](https://kore1server.com/323/%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%A8URL%E7%94%9F%E6%88%90)や、今後ファイルがどんどん増えていくと思うのでディレクトリ構成なども考えていく必要があります。

実際はサンプルのような形でWebサイトを書いていくということなく、フレームワークの React / Vue / Angular を用いてもっといい感じにやっていると思います。

# 6. Docrker の後片付け
コンテナを無意味に起動しておく必要はないため、必要に応じて以下のコマンドでコンテナの停止・削除、イメージの削除を行ってください。
Docker は簡単に同じ環境を構築することができるので、コマンドの練習も兼ねてためらわずに消してしまいましょう。

## 6-1. コンテナの停止

`docker container stop`コマンドによってコンテナを停止します。

```shell:起動中のコンテナの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
c789395b6476        httpd               "httpd-foreground"   43 minutes ago      Up 43 minutes       0.0.0.0:8080->80/tcp   focused_ptolemy
```

```shell:コンテナの停止
$ docker container stop c789395b6476

c789395b6476
```

```shell:起動中のコンテナがないことの確認
$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

## 6-2. コンテナの削除

`docker container rm`コマンドによってコンテナを停止します。

```shell:すべてのコンテナの確認
$ docker container ls -a

CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                         PORTS               NAMES
c789395b6476        httpd               "httpd-foreground"   2 hours ago         Exited (0) About an hour ago                       focused_ptolemy
```

```shell:コンテナの停止
$ docker container rm c789395b6476

c789395b6476
```

```shell:指定したコンテナが削除されていることの確認
$ docker container ls -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

## 6-3. イメージの削除
`docker image rm`コマンドによってダウンロードしたDockerイメージは削除できます。

```shell:ダウンロードしたimageの確認
$ docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
httpd               latest              9d2a0c6e5b57        2 days ago          166MB
```

```shell:rmコマンドによるDockerイメージの削除
$ docker image rm httpd

Untagged: httpd:latest
Untagged: httpd@sha256:2a9ae199b5efc3e818cdb41c790638fc043ffe1aba6bc61ada28ab6356d044c6
Deleted: sha256:9d2a0c6e5b5714303c7b72793311d155b1652d270a785c25b88197069ba78734
Deleted: sha256:d501b5ad5ac59deebf3d002fda60014429d851340ee8f80dfe745e525aaba281
Deleted: sha256:c0ffe105e5d8c201ddc0fa3aa0d75d1f77c88632a5dabe196b1fb837ac83dd73
Deleted: sha256:58da7575a150b2378511246c3d1091d24319d25db05319a07bcda674d089e3a1
Deleted: sha256:5e73f7688a03b2696608629be23d77a75183cc9ac2a5b622b513a8df5ee04573
Deleted: sha256:95ef25a3204339de1edf47feaa00f60b5ac157a498964790c58c921494ce7ffd
```

```shell:削除されたことの確認
$ docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```

## 6-4. Mac 上での Docker のサービスの停止
デスクトップ上部のタスクバーの Docker のアイコンから停止することが可能です。
<img width="299" alt="スクリーンショット 2020-07-25 21.52.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/890fad75-d016-6747-fbb5-544a8b7713dd.png">
また、『起動しててもよいがメモリをリソースは最小限に抑えたい!』ということであれば、タスクバーの Docker のアイコンから Preferences -> Resources でリソースを調整することができます。
<img width="1042" alt="スクリーンショット 2020-07-25 21.52.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/8a186f16-c977-62f5-7b90-68e1487f940a.png">

# 7. 最後に
意外と知ってたようで、知らないことが多く記事を書いていてとても勉強になりました。
知識不足を痛感しました。。。

# 参考
Docker 公式 httpd イメージを利用して Docker を体験してみよう
https://weblabo.oscasierra.net/docker-httpd-usage/

httpdとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
https://wa3.i-3-i.info/word13579.html

ルーティングとURL生成
https://kore1server.com/323/%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%A8URL%E7%94%9F%E6%88%90

Webページ表示の仕組みと表示までの流れ
https://daeuwordpress.com/homepage-system/

Webサーバはどのように動いているのだろう？～HTTP・URL・クッキー編～
https://www.jtp.co.jp/techport/2017-02-08-002/

ApacheとNginxについて比較
https://qiita.com/kamihork/items/49e2a363da7d840a4149

Docker入門の前に知っておきたい基礎知識 - 週末勉強会レポート
https://www.tech-training.jp/blog/entries/28