【Docker入門】公式Docsの「Get Started」を日本語で要約しながらやってみる（Part2）

[【Docker入門】公式Docsの「Get Started」を日本語で要約しながらやってみる（Part1）](https://qiita.com/jnaiFy04EJ0cP2L/items/f97d0bac305767519c50)の続きです。

今回は、コンテナについて解説する、Docker公式docsの「Get Started」のPart2を日本で要約しながら進めていきます。

https://docs.docker.com/get-started/part2/

## イントロダクション

Docker流でアプリケーションを開発していく。

アプリケーションは以下の３層から成る。今回は最下層の「コンテナ」を見ていく。

- スタック（stack）
- サービス（services）
- コンテナ（container）　←今ここ


## 新しい開発環境

Pythonのアプリケーションを作る場合を例に取る。

Dockerなしで作っていくときに、まず必要となったのはPythonのランタイムをインストールすること。

このやり方だと、環境構築が結構大変。

pythonアプリケーションが期待通りに作動し、なおかつ、production環境にも合うローカル開発環境を整備しないといけない。

Dockerを使う場合、こうした手間はかからない。

PythonのランタイムをDockerのイメージとして使える。自分のマシンにインストールする必要はない。

アプリケーションのコードと一緒に、Pythonのイメージを組み込むことができる。ゆえに、アプリケーション、依存関係、ランタイムの全てがセットになっていることを保証できる。

こうしたポータブルなイメージは、`Dockerfile`と呼ばれるファイルに定義される。


## Dockerfileでコンテナを定義

まず、空のディレクトリを作って、

```terminal
mkdir sample-container
```

cdコマンドでこの新しいディレクトリに移動

```terminal
cd sample-container
```

`Dockerfile`という名前のファイルを作成

```terminal
touch Dockerfile
```


以下のコードをコピペする。

```Dockerfile
# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]
```

ランタイム、依存関係（pip installでrequirements.txtから読み込んでる）、アプリケーションのコードの全てが入っていることが見受けられる。

`app.py`や`requirements.txt`は未作成なので、これから作っていく。


## アプリケーションのコードを書く

以下２ファイルを作成

```terminal
touch app.py
touch requrements.txt
```

Flaskを知らなくても、コピペすれば動くので、とりあえずやってみると良いと思う。

```requrements.txt
Flask
Redis
```

```app.py
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

```

`Redis`は未稼働なので、エラー文を出してくるだろうなあ〜と想定しておくべき。（「cannot connect to Redis, counter disabled」が出てくるはず。）

`requirements.txt`でPythonのライブラリとしては読み込んだが、Redisそのものが入っているわけではない、というわけ。

以上で、コード書くのはおわり。

Pythonやrequirementsに書いたライブラリを一つも自分のマシンにインストールしていないが、これで環境構築完了してしまった！！

素晴らしい！！


## アプリケーションをビルドする

準備が整ったので、早速アプリケーションをビルドする。

今、ディレクトリの配下はこうなっているはず。

```
ls
Dockerfile		app.py			requirements.txt
```

buildコマンドを使おう。この時、`-t`オプションを使って、分かりやすいタグをつける。

```
docker build -t friendlyhello .
```

ビルドしたイメージを確認。

```
docker image ls

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398
```


## アプリケーションを稼働させる

`-p`コマンドを使って、自分のマシンのポート4000をコンテナの80ポートにマッピングする。

```
docker run -p 4000:80 friendlyhello
```

ターミナルには`Running on http://0.0.0.0:80/`と表示されていると思うが、これはコンテナ内部からのメッセージで、ポート4000に繋げたことを認識していないため、こうなっている。

実際はポート4000からアクセスできる。

ブラウザで`http://localhost:4000/`にアクセスしてみよう。

こんなメッセージが表示できてればオッケー。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158916/1238d3f6-5be0-caeb-5958-d5a2110c7bf9.png)

curlコマンドを使ってもいける。

```
curl http://localhost:4000

<h3>Hello World!</h3><b>Hostname:</b> 681ec613534b<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
```

止めるときは`Ctrl+C`。

detatchedモードを使えば、バックグラウンドで実行させることもできる。

```
docker run -d -p 4000:80 friendlyhello
```

running中のコンテナを確認。

```
docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
681ec613534b        friendlyhello       "python app.py"     15 minutes ago      Up 14 minutes       0.0.0.0:4000->80/tcp   affectionate_johnson
```

CONTAINER IDを指定して、ストップさせることができる。

```
docker container stop 681ec613534b
```

## イメージをシェアする

ポータビリティを示すため、これまで作ってきたimageをアップロードし、別の場所で実行してみよう。

コンテナをproduction環境にデプロイしたいときに、レジストリをプッシュする方法さえ知っていれば良い。

レジストリというのは、リポジトリの集まりのこと。

リポジトリというのは、イメージの集まりのことを言う。Githubのリポジトリと似ているが、コードがビルトされている点が異なる。


## Docker Idでログイン

dockerのパブリックレジストリにログインする。

```
docker login
```

## イメージをタグ付けする

ローカルのイメージをレジストリ内のリポジトリに結びつけるには、

`username/repository:tag`

このように書く必要がある。

tagはオプションではあるが、つけておくことを勧める。

Dockerイメージにバージョンをつける時にも同じやり方を取るから。

ここでは、リポジトリ名に`get-started`、タグ名に`part2`を付けることにする。

`docker tag image`コマンドとユーザー名を使って、

```
docker tag image username/get-started:part2
```

このように実行。

新たにタグ付けしたイメージがあるか確認しておく。

```
docker image ls

REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
friendlyhello            latest              d9e555c53008        3 minutes ago       195MB
username/get-started         part2               d9e555c53008        3 minutes ago       195MB
python                   2.7-slim            1c7128a655f6        5 days ago          183MB
...
```


## イメージをアップロード

それでは、イメージをリポジトリにプッシュしよう。

```
docker push username/get-started:part2
```

プッシュが完了すると、イメージはどこからでも利用可能になる。

[Docker Hub](https://hub.docker.com/)にログインした状態でアクセスすると、イメージが確認できる。


## イメージをリモートリポジトリからプルして実行してみる

これで、`docker run`コマンドが使え、どのマシンからでも、アプリケーションを稼働させることができるようになった。

```
docker run -p 4000:80 username/get-started:part2
```

このコマンドを実行すれば、どのマシンからでもイメージをプルして利用することができる。

このイメージには、Pythonとrequirements.txtに書いた依存関係が一緒に詰まっている。

Pythonのランタイムや依存関係、アプリケーションのコードが小さな、でも整ったパッケージに入って、どこにでも行ける。

ホストマシン側は、このDockerのイメージを実行するために何かインストールする必要は全くない。

## まとめ

以上でこのページは終了。

Part3では、コンテナをサービスの中で動かすことによって、アプリケーションをスケールさせる方法を学ぶ。

Part3はこちら。

[【Docker入門】公式Docsの「Get Started」を日本語で要約しながらやってみる（Part3）](https://qiita.com/jnaiFy04EJ0cP2L/items/6bab57d4bf254101e9c9)