docker runコマンドをマッスルメモリーする

## 当ドキュメントの目的  
docker便利です。好きです。  
とはいえ、とっかかりはなかなか苦労しました。  
自分が覚える際に躓いた部分や、こうすればスムーズに身につくのではないかと思う方法を記述します。  
当ドキュメントでは`docker run`コマンドを主軸に、コンテナを作成したり、破棄したりを繰り返して理屈よりも先に体にしみこむことを目的にしています。  
dockerを覚えようとされている方の助けになればと思います。  

当方がコンテナを作成する際に実行するコマンドは以下のようなものです。  
```bash
docker run --name example-project \
-it -v /data/example-project:/app \
-w /app -p 3000:3000 -p 4200:4200 \
my-node:1.0 /bin/bash
```
※上記はあくまで例です。この段階で実行してもmy-node:1.0というイメージがないためエラーになります。  
長いコマンドで当初は面食らってしまうと思いますが、当ドキュメントを読み終えたときには理解できるようになることを目標にしています。  

## 環境  
当ドキュメントに記載してあるコマンドは、Windows10 WSL2 Ubuntu20.04で実行し、動作確認しました。  
ターミナルソフトはWindows Terminalを使用しました。  

## イメージの取得からコンテナの破棄まで  
dockerイメージの取得、コンテナの作成、起動、終了、破棄をコマンドで実行します。  
理屈は考えず、とりあえず例の通り実行してみてください。  

### イメージを取得  
以下、特別な記載がなければ、wslのUbuntuのターミナルで実行します。  
※Windows Power Shellでも実行可能です。  
`ubuntu:22.04`イメージを取得します。  
`docker pull`コマンドを実行します。  
クラウド上のレジストリからイメージがダウンロードされます。  
```bash
docker pull ubuntu:22.04
```
完了後、`docker images`コマンドを実行すると、ダウンロードされたイメージを確認することができます。  
```bash
docker images

# 結果
REPOSITORY                  TAG                                        IMAGE ID       CREATED         SIZE
ubuntu                      22.04                                      1c5c8d0b973a   3 weeks ago     72.8MB
...
```

### コンテナを作成  
`docker run`コマンドを実行してコンテナを作成します。  
下記の例、いくつかオプション設定有りますが、現時点ではあまり気にしないでください。各々後述します。  

```bash
docker run --name my-ubuntu -it ubuntu:22.04 /bin/bash
```
実行すると、"my-ubuntu"というコンテナ名のコンテナが作成されます。  
またターミナルのプロンプト(下記の例の"root@hogehoge:/home/user#"の部分)の表記が変わったはずです。  
当方の環境だと以下のようになりました。  
```
# docker run コマンド実行
root@hogehoge:/user# docker run --name my-ubuntu -it ubuntu:22.04 /bin/bash

# 実行後
root@479e7ac97829:/#
```
これは、コンテナの中に入った状態です。  
**docker runを実行したターミナルとは別にもう一つターミナルを開いて**`docker ps`コマンドを実行してください。  
実行中のコンテナが一覧されます。  
```bash
docker ps

# 結果
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS         PORTS     NAMES
479e7ac97829   ubuntu:22.04   "/bin/bash"   7 minutes ago   Up 7 minutes             my-ubuntu
```
ちなみに`-a`オプションをつけて`docker ps`コマンドを実行すると、停止しているコマンドも一覧します。
```bash
docker ps -a

# 結果例
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS                      PORTS                                            NAMES
479e7ac97829   ubuntu:22.04     "/bin/bash"              9 minutes ago   Up 9 minutes                                                                 my-ubuntu
d58395d67ee6   my-node:1.0      "docker-entrypoint.s…"   44 hours ago    Exited (137) 43 hours ago                                                    hoge-container
64ffb5b39354   my-node:1.0      "docker-entrypoint.s…"   44 hours ago    Exited (137) 43 hours ago                                                    
...
```

#### `docker run`後のコンテナ内で`exit`
先の例で`docker run`コマンドを実行後のターミナルで実行します。
```bash
exit
```
コンテナを抜けてホストPCに戻ります。  
この時コンテナも終了します。`docker ps`コマンドを実行すると、先ほど表示されていた`my-ubuntu`コンテナがリストにないはずです。  

### コンテナを破棄  
作成したコンテナ`my-ubuntu`を破棄します。  
```bash
docker rm my-ubuntu
```
`docker ps`コマンドで、停止中のコンテナもリストアップする`-p`オプションを付与して実行しても`my-ubuntu`はリストアップされなくなります。  
```bash
docker ps -p

# 結果例
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS                      PORTS                                            NAMES
```

### コマンド解剖  
先ほど実行したコマンドを詳しく見ていこうと思います。  
```bash
docker run --name my-ubuntu -it ubuntu:22.04 /bin/bash
```
- `--name my-ubuntu`  
    作成するコンテの名称を指定しています。  
- `-it`  
    当方はコンテナに入って入力が受け付けられる状態にするためのオプションと理解しています。  
    理屈は考えずにとりあえず、付与するオプションだと思っています。  
    が、何とか自分なりに解説します。  
    `-it`は`--intereractive`オプションと`--tty`オプションを同時に指定しています。  
    以下のコマンド例は全て同じ実行結果になります。  
    ```bash
    # 各々のオプション名を省略せずに記述
    docker run --name my-ubuntu --interactive --tty ubuntu:22.04 /bin/bash

    # 省略値を各々記述
    docker exec -i -t my-ubuntu /bin/bash
    docker run --name my-ubuntu -i -t ubuntu:22.04 /bin/bash

    # オプション指定に順番はない
    docker run --name my-ubuntu -t -i ubuntu:22.04 /bin/bash
    docker run --name my-ubuntu -ti ubuntu:22.04 /bin/bash
    ```
    後でこのオプションの有無による実験を行います。
- `ubuntu:22.04`  
    dockerイメージを指定しています。 
- `/bin/bash`  
    コンテナに入って`/bin/bash`コマンドを実行することを指定しています。  
    `/bin/bash`はターミナルソフトです。当方はWindowsにおけるPower Shellやコマンドプロンプトと理解しています。  
    コンテナ内に入ってターミナルソフトを起動することを指定しています。  

### `-it`オプション実験  
`-it`オプション無しで`docker run`コマンドを実行してみます。  
```bash
docker run --name my-ubuntu ubuntu:22.04 /bin/bash
```
何も起こりません。`docker ps -a`を実行すると、`my-ubuntu`がリストに上がるのでコンテナは作成されています。  
```bash
docker ps -a

# 結果例  
CONTAINER ID   IMAGE            COMMAND                   CREATED         STATUS                          PORTS                                            NAMES
f451fa19024d   ubuntu:22.04     "/bin/bash"               2 minutes ago   Exited (0) About a minute ago                                                    my-ubuntu
```
`docker start`コマンド(後述します)でコンテナを起動後、起動中のコンテナを確認するため`docker ps`コマンドを実行しても`my-ubuntu`コンテナはリストに上がりません。  
```bash
# コンテナ起動
docker start my-ubuntu

# 起動中のコンテナを確認
docker ps

# 結果例
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES  
# my-ubuntuがない...
```
当方は、コンテナを起動後、コンテナ内で`/bin/bash`を起動した直後にコンテナが終了してしまうものと理解しています。  
これでは困るので、コンテナ起動後、コンテナ内のターミナルで入力を受け付ける状態にするために、`--interactive`オプションを付与して実行してみます。  
```bash
# コンテナ削除
docker rm my-ubuntu

# --interactiveオプションを付与してコンテナ作成  
docker run --name my-ubuntu --interactive ubuntu:22.04 /bin/bash

# 以下は--interactiveの省略形-iで実行
docker run --name my-ubuntu -i ubuntu:22.04 /bin/bash
```
パッと見たところ、何も起こっていないのかと思ったのですが、コンテナが作成され、コンテナの中に入っています。  
ただしプロンプトが表示されていません。  
試しにディレクトリを一覧する`ls -l`コマンドを実行してみました。
```bash
# コンテナを作成
root@hogehoge:/home/user# docker run --name my-ubuntu -i ubuntu:22.04 /bin/bash

# 何も表示されていないがコンテナに入っている。
# lsコマンドを実行してみる。
ls -l
# lsコマンドの結果
total 48
lrwxrwxrwx   1 root root    7 Apr 25 14:03 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Apr 18  2022 boot
drwxr-xr-x   5 root root  340 May 15 08:12 dev
drwxr-xr-x   1 root root 4096 May 15 08:12 etc
...
```
コマンドは受け付けられていますが、プロンプトの表示がないと不便です。`-tty`オプションを付与するとプロンプトが表示されるようになります。  

以上のことから、`-it`オプションを付与することが必要と考えています。  

### コンテナの開始、停止  
作成したコンテナの開始、終了コマンドについて示します。  
#### 開始  
`docker start`コマンドを実行します。  
``` bash
docker start my-ubuntu
```
**注意**  
先の例の`docker run`コマンド実行後、そのままコンテナ内に入りましたが、`docker start`実行後はコンテナ内には入りません。  

#### コンテナ内に入る  
`docker exec`コマンドを実行します。  
```bash
docker exec -it my-ubuntu /bin/bash
```
プロンプトの表記が変わったことを確認してください。コンテナに入ったことを示しています。  
上記のコマンドについて解説します。  
- `/bin/bash`  
    コンテナに入って`/bin/bash`コマンドを実行することを指定しています。  
    `/bin/bash`はターミナルソフトです。当方はWindowsにおけるPower Shellやコマンドプロンプトと理解しています。  
    コンテナ内に入ってターミナルソフトを起動することを指定しています。  
- `-it`    
    `docker run`コマンド時の`-it`オプションと同等と理解しています。  

コンテナから抜けるには`exit`コマンドを実行します。  
```bash
exit
```
`docker run`実行後の`exit`と異なり、コンテナを抜けた後も、コンテナは停止せずに稼働中です。  
`docker ps`コマンドを実行して、稼働していることが確認できます。  

### コンテナを停止  
`docker stop`コマンドを実行します。  
```bash
docker stop my-ubuntu
```
`docker ps`コマンドを実行して、コンテナが停止していることを確認してください。  

### コンテナを破棄  
`docker rm`コマンドでコンテナを削除します。  
```bash
docker rm my-ubuntu
```
`docker ps -a`コマンドで、`my-ubuntu`コンテナが亡くなったことを確認してください。  

## コンテナ内でいろいろ操作  
コンテナ内でいくつかLinuxコマンドを実行します。  
先の例を参考にして、Ubuntuコンテナを作成しコンテナ内に入ります。  
```bash
# コンテナを作成する。
docker run --name my-ubuntu -it ubuntu:22.04 /bin/bash
# コンテナ内に入る。
docker exec -it my-ubuntu /bin/bash
```

### コンテナ内でファイル作成  
以下よりいくつかLinuxコマンドを実行します。  
当方はLinuxコマンドについて以下の書籍を参考にしました。  
[図解！　Linuxコマンドのツボとコツがゼッタイにわかる本 著者:高橋 隆雄 出版社:秀和システム](https://www.amazon.co.jp/%E5%9B%B3%E8%A7%A3%EF%BC%81-Linux%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E3%83%84%E3%83%9C%E3%81%A8%E3%82%B3%E3%83%84%E3%81%8C%E3%82%BC%E3%83%83%E3%82%BF%E3%82%A4%E3%81%AB%E3%82%8F%E3%81%8B%E3%82%8B%E6%9C%AC-%E9%AB%98%E6%A9%8B%E9%9A%86%E9%9B%84/dp/479806811X/ref=asc_df_479806811X/?tag=jpgo-22&linkCode=df0&hvadid=589020067580&hvpos=&hvnetw=g&hvrand=2973467621209463676&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009461&hvtargid=pla-1720402038891&psc=1&th=1&psc=1)  
いくつかコマンド出てきますが、覚えようとしなくてよいです。  
- ファイルを作成する  
    `touch`コマンドでファイルを`hoge.txt`というテキストファイルを作成します。  
    ```bash
    touch hoge.txt
    ```
    `ls`コマンドを実行すると、ファイルが作成されたことが確認できます。  
    ```bash
    ls -la

    # 結果
    ...
    -rw-r--r--   1 root root    0 Mar 30 05:36 hoge.txt
    ...
    ```
- ファイルを編集する  

    Ubuntu標準のテキストエディタ`nano`を使って`hoge.txt`を編集します。  
    ```bash
    nano hoge.txt

    # 結果
    bash: nano: command not found
    ```
    `nano`が未インストールのためエラーが表示されました。  
    `apt-get`コマンドを実行して`nano`をインストールします。  
    ※`nano`既定でインストールされているものと思っていましたが、`ubuntu:22.04`dockerイメージから作成するubuntuにはインストールされていない模様です。当ドキュメント作成中に知りました。しかし、ついでに`apt-get`コマンド紹介出来て良かったです。  
    ```bash
    echo 'nameserver 8.8.8.8' > /etc/resolv.conf
    apt-get update
    apt-get upgrade 

    apt-get install nano
    ```
    nanoのインストールが完了したところで改めてhoge.txtを編集します。  
    ```bash
    nano hoge.txt
    ```
    テキストエディタが起動するので何か入力します。日本語を入力しようとすると文字化けします。日本語対応させる設定有るのですが後述します。  
    ```text:hoge.txt
    abcdefghijolmnopqrstuvwxyz
    ```
    `ctrl`+`x`キーでテキストエディタを終了します。保存するかどうか聞かれるので`y`キーを押して保存します。  
    編集したテキストの内容を確認します。`nano`を起動してもよいですし、`cat`コマンドでも確認することができます。  
    ```bash
    cat hoge.txt

    # 結果 先ほど編集した内容が表示される。
    abcdefghijolmnopqrstuvwxyz　
    ```
- 日本語対応  
    Linuxの環境変数を設定します。  
    以下のコマンドを実行します。  
    ```bash
    export LANG=C.UTF-8
    export LANGUAGE=en_US: #末尾は";"セミコロンではなく":"コロンであることに注意
    ```
    nanoで日本語が入力できるようになったことを確認してください。  
    ただしこの設定は永続化されません。コンテナを抜けて再度、コンテナに入ると先ほどの設定がクリアされてしまいます。  
    コンテナ内のLinuxの設定ファイルを編集することで、設定値を永続化することができます。  
    ※2023/03/30 当ドキュメント作成時、以下の設定期待した挙動になりませんでした。下記設定方法は主題からは逸れるので読み飛ばしてください。  
    ```bash
    # 設定ファイルを編集する。
    nano /etc/profile

    # 末尾に以下を追記します。
    export LANG=C.UTF-8
    export LANGUAGE=en_US:
    ```
### コンテナを終了、破棄  
```bash
# コンテナを抜ける
exit

# コンテナを終了
docker stop my-ubuntu

# コンテナを破棄
docker rm my-ubuntu
```
コンテナ内でテキストファイルを作成し、編集、保存しました。  
その後コンテナを破棄しました。編集したテキストファイルはどうなったかというと、一緒に破棄されています。  
先ほどは簡単なテキストファイルだったのでなんということはありませんが、これが手塩にかけたプログラムコードであったならえらいことです。  
コンテナを破棄しても、ホストPCに成果物を残す方法があります。`-v`オプションです。  

### `VOLUME`オプション  
`-v`オプションで、コンテナ内のディレクトをホストPCのディレクトリにマウントします。  
※"マウント"当方は紐づけるという理解です。  

ホストPC側に`/data/example-project`というディレクトリを作成し、コンテナ内の`/app`ディレクトリをマウントします。コンテナ内の`/app`ディレクトリは、ホストPCで実行する`docker-run`コマンドで作成されます。ついでに`-w`オプションで、ワーキングディレクトリが`/app`であることを指定します。これにより、コンテナに入った時のカレントディレクトリが`/app`になります。  
では以下のコマンドを順に実行します。  
```bash
# ディレクトリを作成する。
mkdir /data
mkdir /data/example-project

# コンテナを作成する。  
docker run --name my-project -it -v /data/example-project:/app -w /app ubuntu:22.04 /bin/bash
```
コンテナ作成後にコンテナ内に入った時に、プロンプトの表示を見てください。  
`/app`ディレクトリになっているはずです。これは先に実行した`docker run`コマンドの`-w`オプションによるものです。  

コンテナ内でファイルを作成し、編集、保存します。  
```bash
# テキストエディタをインストールする準備。
echo 'nameserver 8.8.8.8' > /etc/resolv.conf
apt-get update && apt-get upgrade -y
# テキストエディタをインストールする。
apt-get install nano

# "hoge.txt"を作成する。
touch hoge.txt

# 内容を編集する。
nano hoge.txt

# コンテナを抜ける
exit
```
テキストファイルを保存後、コンテナを停止します。  
※`docker run`実行後そのままコンテナ内で作業していた場合、以下は不要です。
```bash
docker stop my-project
```
コンテナ終了後、ホストPCの`/data/example-project`ディレクトリを確認してみてください。  
```bash
# ホストPCでの操作です。

# ディレクトリを移動する。
cd /data/example-project

# カレントディレクトリの内容を一覧する。  
ls -l

# 結果
-rw-r--r-- 1 root root 15  3月 30 15:39 hoge.txt
```
コンテナ内で作成したファイルがリストアップされているはずです。  
ファイルの内容を確認してみます。  
```bash
cat hoge.txt

# 結果
ここにコンテナ内で編集、保存した内容が表示されます。
```
以上により、`-v`オプション指定により、コンテナでの編集内容がホストPCに保存できることが確認できました。  
コンテナを破棄しても、コンテナ内での編集が残っていることを確認してみてください。  

## Dockerfile  
先ほどの例で`ubuntu:22.04`イメージを元にコンテナを作成しました。  
その後、テキストエディタのインストール、日本語対応設定を行いました。  
以降、ubuntuコンテナを作成して作業するとき、毎回同じようにテキストエディタのインストール、日本語対応設定を行うのでしょうか?  
同じ手順を省く方法はないのでしょうか?  
あります。`Dockerfile`を作成して独自のdockerイメージを作成することができます。  
以下に手順を示します。  
### Dockerfileの作成\-`docker build`コマンド  
以下はホストPCで実行します。  
`/data/my-ubuntu-image`というディレクトリを作成して、そこに`Dockerfile`(拡張子なし)を作成します。
```bash
# ディレクトリを作成
mkdir /data/my-ubuntu-image
# ディレクトリに移動
cd /data/my-ubuntu-image
# ファイル作成
touch Dockerfile
# テキストエディタで開く
nano Dockerfile
```
以下のように編集します。  
```:Dockerfile
# 元となるdockerイメージ
FROM ubuntu:22.04

# 2バイト文字対応設定
ENV LANG=C.UTF-8
ENV LANGUAGE=en_US:

# テキストエディタnanoをインストールする
RUN apt-get update && apt-get install nano

# ワーキングディレクトリの設定
RUN mkdir /app
WORKDIR /app
```
`docker build`コマンドでイメージを作成します。  
```bash
docker build -t my-ubuntu:1.0 .
```
末尾の"."を忘れないでください。これは`Dockerfile`へのパスを指定しています。  
コマンドを実行しているディレクトリに`Dockerfile`があるのでカレントディレクトリを示す"."を指定しています。  
`-t`オプションは、タグを指定するオプションです。作成するイメージの名称を指定します。`my-ubuntu:1.0`というのはイメージ名`my-ubuntu`でバージョンが`1.0`であることを示しています。  
完了後`docker images`コマンドを実行すると、`my-ubuntu:1.0`というイメージが作成されたことが確認できます。  
```bash
docker images

# 結果
REPOSITORY                  TAG                                        IMAGE ID       CREATED         SIZE
my-ubuntu                   1.0                                        5e52b0214d5c   3 minutes ago   929MB
```

### 作成したイメージからコンテナを作成し動作確認  
`my-ubuntu:1.0`イメージからコンテナを作成します。  
動作確認だけなので、コンテナ終了後、コンテナが破棄される`--rm`オプションを付与します。  
```bash
docker run --rm -it my-ubuntu:1.0 /bin/bash
```
テキストエディタで日本語入力ができることを確認してください。  
```bash
nano hoge.txt
```
`ubuntu:22.04`イメージからコンテナを作成したときと異なり、最初から、テキストエディタがインストールされており2バイト文字に対応していることに注目してください。  

## `node`イメージ  
`node`イメージを用いてNode.jsのコンテナを作成し、ローカルにWebサーバーを建ててブラウザでアクセスするまでを説明します。  
### nodeイメージについて  
当方が最初疑問に思ったところを記述します。  
Node.jsはOSではなく処理系です。何故OSではないNode.jsをコンテナに展開してlinuxコマンドが使えるのか?と思ったことがあります。  
nodeイメージはNode.jsで開発するためのコンテナを展開するためのイメージであり、LinuxディストリビューションであるDebian上にNode.jsをインストールした状態の環境が展開されます。  
つまりnodeイメージを実行して作成されるコンテナのOSはDebianです。  
少し話がそれますが、**DockerコンテナはLinuxカーネル(WSLの場合、Linuxカーネルをエミュレートしたもの)上で実行されます。Dockerイメージに含まれるOSはLinuxベースである必要があります。**  
太字の部分、ChatGPTとの対話で情報を得ました。  
### nodeイメージをベースにdockerイメージを作成  
(参考)[docker hubのnode:18.15.0のページ](https://hub.docker.com/layers/library/node/18.15.0/images/sha256-33f306d574d22a441f6473d09c851763ff0d44459af682a2ff23b6ec8a06b03e?context=explore)
`18.15.0`タグの選定理由はNode.jsの公式サイトを確認したとき2023/03/31現在、LTS版のバージョンが18.15.0だったからです。  
作業用ディレクトリを`/data/node`とします。  
```bash
mkdir /data/node
cd /data/node
```
`Dockerfile`を作成します。  
下の例では`touch`コマンドでファイル作成せずに`nano`コマンドでファイル名を`Dockerfile`を指定して実行ます。  
こうすると、`nano`を閉じるときに保存すれば、編集した内容でファイルが作成されます。
```bash
nano Dockerfile
```
`Dockerfile`の内容は以下のようにします。  
```:Dockerfile
FROM node:18.15.0

# 2バイト文字に対応させる
ENV LANG=C.UTF-8
ENV LANGUAGE=en_US:

# テキストエディタをインストールする。
RUN apt-get update && apt-get install nano

# /appディレクトリを作成し、それを作業ディレクトリとする。
RUN mkdir /app
WORKDIR /app
```
`docker build`コマンドで`my-node`というdockerイメージをタグ`1.0`で作成します。  
```bash
docker build -t my-node:1.0 .
```
`docker ps`コマンドで作成したイメージがリストに表示されることを確認してください。  

### コンテナを作成  
作成した`my-node:1.0`イメージを元にコンテナを作成します。  
ホストPC側に`/data/node/my-node-project`ディレクトリを作成しこれをコンテナのボリュームに指定します。
```bash
mkdir /data/node/my-node-project
```

```bash
docker run --name my-node-project -it -v /data/node/my-node-project:/app -w /app my-node:1.0 /bin/bash
```
ここからは作成したコンテナ内での作業です。Node.jsでWEBサーバーを建てます。  
元となる`app.js`というファイルを作成します。  
VSCodeでコンテナにアタッチしてコーディングしてもよいですが、当例ではターミナル上でテキストエディタを用いてコーディングします。この節においてコードの内容は重要ではないのでコードについては考えなくてよいです。ポート3000を使ってローカルにWEBサーバを建てているとだけ理解してください。  
```bash
nano app.js
```
`app.js`の内容は以下です。  
ポート3000でWEBサーバーを建てます。ブラウザ等で`http://localhost:3000`にアクセスすると、`Hello world!`と表示されることを期待しています。  
```javascript:app.js
const http = require('http');

const server = http.createServer((req, res) => {
  res.end('Hello world!');
});

server.listen(3000);
console.log(`Server listen on port 3000.`);
```
以下のコマンドで作成した`app.js`を実行します。  
```bash
node app.js

# 正常な場合、ターミナルに以下のように表示されます。
Server listen on port 3000.
```
ブラウザで`http://localhost:3000`にアクセスしてみてください。`Hellor world!`が表示され...ません。  
これはコンテナ内のport3000がホストPCと繋がっていないためです。  
以下より、ブラウザで`Hello world!`が表示されるようにしていきます。  
その前に、まず実行中の`app.js`を停止させ、コンテナも破棄します。  
コンテナ作成時に`-v`オプションでホストPCのディレクトリにコンテナのワーキングディレクトリをマウントしたので、コンテナを削除しても、ホストPCにコードは残っています。  
プログラムの停止は`ctrl`+`c`キーを押します。  
その後、`exit`でコンテナを抜けて`docker stop`コマンドでコンテナを停止させ、`docker rm`コマンドでコンテナを破棄します。  
```bash
# コンテナ内で実行します。
exit

# 以下よりホストPCで実行します。
docker stop my-node-project
docker rm my-node-project
```

### `-p`オプション  
`docker run`コマンドでコンテナを作成する際、`-p`オプションを使用してホストPCとコンテナ内のポートを紐づけます。  
```bash
docker run --name my-node-project -it -v /data/node/my-node-project:/app -w /app -p 3000:3000 my-node:1.0 /bin/bash
```
上記コマンドの`-p 3000:3000`の部分に注目してください。  
ポートの指定の記述方法は`-p <ホストのポート>:<コンテナのポート>`です。  
ですので上記の例はホストのポート3000をコンテナのポート3000に紐づけています。  
また`-v`オプションの部分にも注目してください。先ほど破棄したコンテナと同じディレクトリを指定しています。  
これにより先ほど破棄したコンテナ内で作成したコード、これが残っているので、これを再利用しています。 
コンテナ内で`ls -l` を実行すると、`app.js`があることを確認できます。  
```bash
# コンテナ内で実行します。

ls -l

# 結果
-rw-r--r-- 1 root root 177 Mar 31 06:28 app.js
```
では`app.js`を実行してブラウザで`http://localhost:3000`にアクセスしてみてください。
```bash
node app.js
```
`Hello world!`が表示されたはずです。  

## まとめ  
いかがでしたでしょうか。当方最初はさっぱり分かりませんでしたが、何度かコマンド実行していくうちに何とか理解できるようになりました。何度もコマンドを入力して見るのが良いと思います。  
最後に登場したコマンドやファイルについて説明し、当ドキュメントを閉じます。  
- `docker pull`コマンド  
    dockerイメージを取得するコマンドです。指定したイメージをダウンロードします。  
- `docker run`コマンド  
    dockerイメージを元にコンテナを作成するコマンドです。  
- `docker start`コマンド  
    指定したコンテナを開始するコマンドです。
- `docker stop`コマンド  
    指定したコンテナを停止させるコマンドです。  
- `docker ps`コマンド  
    コンテナを一覧表示するコマンドです。`-a`オプション無しで実行すると実行中のコンテナをリストアップし、`-a`オプション有りで実行すると、停止中のコンテナもリストアップします。  
- `Dockerfile`  
    dockerイメージを作成するための元となるファイルです。  
- `docker build`コマンド  
    作成した`Dockerfile`を元にdockerイメージを作成します。  

以上です。