# 構建鏡像及容器編排
本篇文章參考了[示例](https://yeasy.gitbooks.io/docker_practice/content/compose/usage.html)。
> 2018-04-04 更新:
>
> 改用基于alpine的鏡像,在容量上較默認鏡像更小。
## 使用Dockerfile創建鏡像
### 編寫WEB應用
之前,我們直接用官方的MySQL鏡像創建了一個MySQL實例,但若想把自己的應用通過Docker部署,那還是需要通過Dockerfile構建自己的鏡像。
首先,我們利用python的flask框架建立一個hello world的web應用。
```python
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
# 默認端口為5000
app.run(host='0.0.0.0', debug=True)
```
在當前目錄下,運行`python app.py`即可啟動應用。

然后,我們在瀏覽器中輸入[http://localhost:5000](http://localhost:5000)就能看到熟悉的hello world。

### 編寫Dockfile
在當前目錄下,我們先建立一個**Dockerfile**文件,然后鍵入以下內容。
```Dockerfile
# 基于alpine的python3.6鏡像開始構建
FROM python:alpine3.6
# 切換工作目錄至/code(可以理解為cd ./code)
WORKDIR /code
# 將代碼文件添加到鏡像的/code目錄下
ADD ./app.py .
# 安裝flask庫
# 這里使用了清華的鏡像源,為了安裝的快一些
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask
# 運行
CMD [ "python", "app.py" ]
```
隨后,啟動一個PowerShell窗口,并切換到當前目錄下。
```PowerShell
# 構建鏡像
# -t 表示將該鏡像命名為demo,并打上flask的標簽
docker build -t demo:flask .
```
這邊需要注意的是最后還有一個`.`,代表在當前目錄下查找**Dockerfile**。
在成功構建之后,我們來啟動這個鏡像的一個實例。
```PowerShell
# 啟動容器
# --name 參數將該容器命名為demo.flask,方便后續操作
# -d 參加將容器切入后臺運行,網上的教程也稱為守護進程
# -p 參數將容器內的5000端口,映射到本地的5000端口,使得我們能在本地訪問
docker run --name demo.flask -d -p 5000:5000 demo:flask
```
至此,一個使用Docker部署的WEB應用就完成了,我們可以通過瀏覽器訪問[http://localhost:5000](http://localhost:5000)看到和之前一樣的Hello World界面。
### Docker鏡像中有什么?
其實,我也不是特別清楚Docker鏡像中到底包含了什么。但我們還是看到在基于python 3.6的鏡像上,我們添加了什么。
通過`docker exec -it demo.flask bash`,我們進入容器內部,鍵入`ls`查看當前目錄下的文件。

我們可以看到的是,進入后的默認目錄就是我們在**Dockerfile**中設置的/code,同時,里面有我們添加的app.py文件。
另外,如果有興趣,還可以再看一下`pip list`里面除了最基礎的pip和wheel,其余的就是我們在**Dockerfile**中使用pip安裝的庫。
## 使用docker-compose編排容器
很多時候,我們的應用往往不能完全塞入同一個容器中,比如需要連接數據庫的情況,這時候就需要多個容器之間的協調。Docker對此也有一個利器,docker-compose。在默認情況下,docker-compose會隨docker一同安裝好。這里,我根據網上的教程,簡單的說下docker-compose的使用。
### 編寫一個基于redis的web應用
redis是一個內存數據庫,可以用來作為應用緩存,這里我們通過redis和flask實現一個統計訪問次數的應用。
首先,修改之前的WEB應用,加入redis支持。
```python
from flask import Flask
from redis import Redis
app = Flask(__name__)
# 注意,此處的host需和docker-compose中redis的service名相同
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
count = redis.incr('hits')
return 'Hello World! 此頁面已被訪問%d次。\n' % count
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
```
在**Dockerfile**中,使用pip安裝redis庫。
```Dockerfile
FROM python:alpine3.6
WORKDIR /code
ADD ./app.py .
# 修改這里,添加redis庫
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask redis
CMD [ "python", "app.py" ]
```
自此,一個具有訪問次數統計的WEB應用就完成了。。。?
顯然,還缺少一步。
### 編寫docker-compose.yml
既然提到了docker-compose,那總是要用的。在同一目錄下,我們建立一個`docker-compose.yml`,鍵入以下內容。
```yml
# 由于一些未知的編碼原因,若要復制此段配置文件,請手動去掉注釋
# 聲明使用version 3的docker-compose.yml格式
# 詳情可以參考:https://docs.docker.com/compose/compose-file/
version: '3'
# 定義有關服務
services:
# 使用官方的alpine版redis鏡像創建redis實例
# 注意這里的redis,指的是服務名,需和之前host中的對應
# 至于原因,之后再解釋
redis:
image: "redis:alpine"
# 我們的WEB應用,這里構建為demo:flask-redis鏡像
web:
image: "demo:flask-redis"
build: .
# 添加對redis服務的依賴
depends_on:
- redis
# 映射5000端口
ports:
- "5000:5000"
```
保存后,我們在當前目錄下打開PowerShell窗口,執行`docker-compose build`來完成構建。
然后,使用`docker-compose up -d`來啟動應用。
訪問[http://localhost:5000](http://localhost:5000)可以看到如下界面。

使用`docker ps`,我們也可以看到兩個被啟動的容器。

至此,應該算是完成了吧。。。
另外,補充一條,如果不再需要這兩個容器,可以使用`docker-compose down -v`來刪除。注意~記得加-v參數,因為redis容器會創建一個數據卷,如果多次使用不刪除的話。。emmm。。硬盤大就當我沒說。