【Docker】公式チュートリアルをやってみた 【Part1/Part2】

# **はじめに**
- ConohaのVPSを借りたのでDockerを入れようと思い、[公式のチュートリアル](https://docs.docker.com/get-started/)をやってみようと思ったのでございます

# **環境**
- VirtualBox 5.1.22 r115126
  - ポートフォワーディング設定でアクセスできるようにしています
- Ubuntu16.04.2 LTS
- Docker version 17.03.1-ce, build c6d412e
  - Dockerのインストールについては[公式手順](https://docs.docker.com/engine/installation/linux/ubuntu/)に沿って実施しております



# **チュートリアル**
- [**Part 1: Orientation and Setup**](https://docs.docker.com/get-started/)
- [**Part 2: Containers**](https://docs.docker.com/get-started/part2/)
    - [【Docker】公式チュートリアルをやってみた 【Part1/Part2】](https://qiita.com/Tocyuki/items/420c3afa25bd28e58159)
- [Part 3: Services](https://docs.docker.com/get-started/part3/)
    - [【Docker】公式チュートリアルをやってみた 【Part3】](https://qiita.com/Tocyuki/items/2b04a0fcf25c07c1b367)
- [Part 4: Swarms](https://docs.docker.com/get-started/part4/)
    - [【Docker】公式チュートリアルをやってみた 【Part4】](https://qiita.com/Tocyuki/items/dade81596400b80b7ef3)
- [Part 5: Stacks](https://docs.docker.com/get-started/part5/)
- [Part 6: Deploy your app](https://docs.docker.com/get-started/part6/)



# **Part 1: Orientation and Setup**
```shell-session
tocyuki@vm01:~$ docker run hello-world
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.27/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.
```

怒られたのでdockerグループにユーザーを追加し、シェルを入り直す

```shell-session
tocyuki@vm01:~$ sudo gpasswd -a $USER docker
Adding user tocyuki to group docker
```

再度実行

```shell-session
tocyuki@vm01:~$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/
```

# **Part 2: Containers**
## Dockerfileの作成
```py:Dockerfile
# Use an official Python runtime as a base 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 -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"]
```
## Dockerfileで定義している`requirements.txt`の作成
```text:requirements.txt
Flask
Redis
```

## app.pyの作成
```py: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)
```

## ディレクトリ内の確認
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ ls
app.py  Dockerfile  requirements.txt
```

## アプリのビルド
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker build -t friendlyhello .
Sending build context to Docker daemon 4.608 kB
Step 1/7 : FROM python:2.7-slim
2.7-slim: Pulling from library/python
10a267c67f42: Pull complete
f68a39a6a5e4: Pull complete
9beaffc0cf19: Pull complete
3c1fe835fb6b: Pull complete
Digest: sha256:fdee3139831a8e66543effffdc5addf73c7b18b66c575bd030f9f6f3a7052a6b
Status: Downloaded newer image for python:2.7-slim
 ---> 1c7128a655f6
Step 2/7 : WORKDIR /app
 ---> 58fb2cb39ce7
Removing intermediate container 62ac3b33d5b2
Step 3/7 : ADD . /app
 ---> b622ab3a4d89
Removing intermediate container c08462f54caa
Step 4/7 : RUN pip install -r requirements.txt
 ---> Running in e9f759585f93
Collecting Flask (from -r requirements.txt (line 1))
  Downloading Flask-0.12.2-py2.py3-none-any.whl (83kB)
Collecting Redis (from -r requirements.txt (line 2))
  Downloading redis-2.10.5-py2.py3-none-any.whl (60kB)
Collecting itsdangerous>=0.21 (from Flask->-r requirements.txt (line 1))
  Downloading itsdangerous-0.24.tar.gz (46kB)
Collecting Jinja2>=2.4 (from Flask->-r requirements.txt (line 1))
  Downloading Jinja2-2.9.6-py2.py3-none-any.whl (340kB)
Collecting Werkzeug>=0.7 (from Flask->-r requirements.txt (line 1))
  Downloading Werkzeug-0.12.2-py2.py3-none-any.whl (312kB)
Collecting click>=2.0 (from Flask->-r requirements.txt (line 1))
  Downloading click-6.7-py2.py3-none-any.whl (71kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask->-r requirements.txt (line 1))
  Downloading MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129a
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/88/a7/30/e39a54a87bcbe25308fa3ca64e8ddc75d9b3e5afa21ee32d57
Successfully built itsdangerous MarkupSafe
Installing collected packages: itsdangerous, MarkupSafe, Jinja2, Werkzeug, click, Flask, Redis
Successfully installed Flask-0.12.2 Jinja2-2.9.6 MarkupSafe-1.0 Redis-2.10.5 Werkzeug-0.12.2 click-6.7 itsdangerous-0.24
 ---> 8253de36c294
Removing intermediate container e9f759585f93
Step 5/7 : EXPOSE 80
 ---> Running in 1a5aee83fd16
 ---> a19b6d05e75a
Removing intermediate container 1a5aee83fd16
Step 6/7 : ENV NAME World
 ---> Running in f40fb19d6e66
 ---> 03ce499faeb7
Removing intermediate container f40fb19d6e66
Step 7/7 : CMD python app.py
 ---> Running in ee9f921a2a28
 ---> 7f1578ee5e75
Removing intermediate container ee9f921a2a28
Successfully built 7f1578ee5e75
```

## 作成されたイメージの確認
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              7f1578ee5e75        2 minutes ago       195 MB
```

## アプリの起動
- 4000番ポートをコンテナの80番へフォワードさせる

```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker run -p 4000:80 friendlyhello
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
```

- バックグラウンドで起動させる場合

```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker run -d -p 4000:80 friendlyhello
32a00753256cf419c262e28bbf5bf456fe66895b491d044fef1b802c18132101
```

## アプリの起動確認
### コマンドでの確認
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
32a00753256c        friendlyhello       "python app.py"     28 seconds ago      Up 27 seconds       0.0.0.0:4000->80/tcp   jovial_newton
```

### ブラウザから`http://127.0.0.1:4000`へアクセス
![WS000514.png](https://qiita-image-store.s3.amazonaws.com/0/79134/5787e9ea-52ae-f878-3a68-d4287a3a91dd.png)

## コンテナの停止
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker stop 32a00753256c
32a00753256c
```

## Docker Cloudへイメージのアップロード
### `docker login`の実施
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: tocyuki
Password:
Login Succeeded
```

### ローカルにあるイメージをリポジトリに関連付け
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker tag friendlyhello tocyuki/repository:tag
```

### イメージのアップロード
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker push tocyuki/repository:tag
The push refers to a repository [docker.io/tocyuki/repository]
cb79c3ca3b0e: Pushed
a7c2dcf5a1b0: Pushed
50e365e5527d: Pushed
7b7f69d6236f: Mounted from library/python
667e68ed0db3: Mounted from library/python
5eac2de68a97: Mounted from library/python
8d4d1ab5ff74: Mounted from library/python
tag: digest: sha256:1ae82dcb5528f752c9c1fc57a55ec0dde097ee2b119011ed2d99b3b9e81228d8 size: 1787
```

[Docker Cloud](https://cloud.docker.com)にアクセスしてみるとこんな感じでアップロードされていることが確認できる

![WS000515.png](https://qiita-image-store.s3.amazonaws.com/0/79134/2163ac81-6443-8fb5-b710-5b5451a1afd1.png)

## Docker Cloud上のイメージからコンテナを起動させる
```shell-session
tocyuki@vm01:~/docker_tutorial/part2$ docker run -d -p 4000:80 tocyuki/repository:tag
4b463ae3362b945f19249c82325928068046770cf13faf2b8e855e23b3305e20
```

WEBブラウザからアクセスしてみると起動させたコンテナIDが表示されている

![WS000516.png](https://qiita-image-store.s3.amazonaws.com/0/79134/29eb3bc0-1eae-ff1f-549b-ff6b554c36f0.png)

## Part2で学習したコマンドのチートシート
```bash
# Create image using this directory's Dockerfile
docker build -t friendlyname .  

# Run "friendlyname" mapping port 4000 to 80
docker run -p 4000:80 friendlyname  

# Same thing, but in detached mode
docker run -d -p 4000:80 friendlyname  

# See a list of all running containers       
docker ps        

# Gracefully stop the specified container                         
docker stop <hash>  

# See a list of all containers, even the ones not running                   
docker ps -a    

# Force shutdown of the specified container       
docker kill <hash> 

# Remove the specified container from this machine                  
docker rm <hash>     

# Remove all containers from this machine         
docker rm $(docker ps -a -q)    

# Show all images on this machine       
docker images -a               

# Remove the specified image from this machine                
docker rmi <imagename>            

# Remove all images from this machine
docker rmi $(docker images -q)         

# Log in this CLI session using your Docker credentials    
docker login             

# Tag <image> for upload to registry
docker tag <image> username/repository:tag 

# Upload tagged image to registry 
docker push username/repository:tag     

# Run image from a registry       
docker run username/repository:tag                   
```