[軟體概念入門系列][Workshop] Linux與後端服務 #2

Brett Yu
Brett’s dev log
Published in
11 min readNov 13, 2023

前置準備

  1. 依據官網文件安裝docker並可以成功執行hello-world container
sudo docker run hello-world

若在wsl中起docker service遇到問題可參考此篇

2. 在 https://hub.docker.com/ 用公司信箱註冊自己的帳號

Workshop目標

將上次我們建立的python flask API, nginx改為使用docker建立

你會學習到

  • 如何撰寫docker file並透過docker build建立python image
  • 如何用同樣的docker image建立多個container
  • 如何對執行中的container進行操作及變更

建立python flask api docker image

  1. 建立稍後要放進docker image中的python檔

用vim建立app.py並填入以下程式碼

import socket
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "this message comes from " + socket.gethostname()

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

這個python script會建立一個回傳server name的API
執行以下指令讓api啟動

python3 -m venv .venv
source .venv/bin/activate
python app.py

接著瀏覽 http://localhost:5566/ 確認API正常運行即可以中斷python執行

2. 建立python API docker file

在app.py的同一個目錄下, 用vim建立dockerfile

FROM python:3.9

WORKDIR /app

RUN pip install Flask

COPY app.py /app/

CMD ["python", "app.py"]

3. 建立docker image

執行以下指令, 以剛剛撰寫的dockerfile來建立docker image, 並將image命名為flask-api, 版號為1.0

docker build -t flask-api:1.0 .

接著執行以下指令確認docker image已經存在於本機

docker image ls

4. 將docker image執行為docker container

docker run --rm -p 3000:5566 --name my-api-1 flask-api:1.0

瀏覽 http://localhost:3000/ 確認 API 可被存取到
接著再開另一個cmd/powershell/windows terminal tab進入wsl
並執行以下指令確認執行中的container資訊

docker ps -a
docker inspect my-api-1
docker stats my-api-1
docker logs my-api-1

確認完回到container執行的畫面, 按下ctrl+c結束container執行
並改執行以下指定讓container在背景執行

docker run --rm -p 3000:5566 --name my-api-1 -d flask-api:1.0

執行第二個使用5000 port的api

docker run --rm -p 5000:5566 --name my-api-2 -d flask-api:1.0

瀏覽 http://localhost:3000/http://localhost:5000/ 確認兩個container都正常執行

5. 將 docker image 推上 docker hub

在 docker hub上建立一個叫做flask-api的repository
https://hub.docker.com/repository/create

接著將剛剛建立的flask-api docker image重新tag為你的docker hub namespace下的docker image
使用docker login登入docker hub並將image推上docker hub

docker tag flask-api:1.0 {dockerhub_namespace}/flask-api:1.0
docker login
docker push {dockerhub_namespace}/flask-api:1.0

重新整理你的docker hub repository, 應可以看到image被成功推上去了

建立nginx container並設定config

  1. 停止原有wsl中的nginx

執行以下指令停止workshop #1安裝的nginx

sudo service nginx stop
sudo service nginx status

這時候瀏覽 http://workshop-load-balance.com/ 應會無法存取

2. 啟用nginx container

執行以下指令使用官方的nginx image, 並把原本nginx config的folder設定成volumn指定到container內

docker run --name my-nginx -v /etc/nginx/:/etc/nginx/ -p 80:80 -d nginx

這時候瀏覽 http://workshop-load-balance.com/ 應該會是502 Bad Gateway
因為nginx找不到upstream的python flask api, 因此要改成正確的python flask api才能正確地存取到

3. 調整nginx config將相關的ip改為my-api-1及my-api-2的container ip

先用以下指令查詢兩個container的ip

docker inspect my-api-1 | grep IPAddress
docker inspect my-api-2 | grep IPAddress

將nginx config改為以下

    upstream loadbalance_backend {
server {my-api-1的IP}:5566;
server {my-api-2的IP}:5566;
}

server {
listen 80;
server_name workshop-load-balance.com;

location / {
proxy_pass http://loadbalance_backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

接著執行以下指令讓nginx container reload config

docker exec my-nginx nginx -s reload

現在瀏覽 http://workshop-load-balance.com/
應可以正常做到load balance存取到兩個python flask api了

使用docker compose一次管理多個container

  1. 停止所有的container
docker ps
docker stop {$container name 1} {$container name 2} {$container name 3}

2. 撰寫docker compose yaml

使用vim用以下內容建立docker-compose.yaml

version: '3'

services:
nginx:
image: nginx:latest
container_name: my-nginx
ports:
- "80:80"
volumes:
- /etc/nginx/:/etc/nginx/


api-1:
image: flask-api:1.0
container_name: my-api-1
ports:
- "3000:5566"

api-2:
image: flask-api:1.0
container_name: my-api-2
ports:
- "5000:5566"

3. 使用docker compose建立多個container

執行以下指令以docker-compose.yaml的內容建立多個container

docker compose -f docker-compose.yaml up

# 如果想要背景執行則用下面這個
docker compose -f docker-compose.yaml up -d
# 停止則要使用
docker compose -f docker-compose.yaml down

4. 查詢my-api-1及my-api-2的IP並調整nginx config

同前面操作過的步驟, 查詢目前兩個container的IP並更新到nginx config

docker inspect my-api-1 | grep IPAddress
docker inspect my-api-2 | grep IPAddress

#調整nginx config後reload
docker exec my-nginx nginx -s reload

瀏覽 http://workshop-load-balance.com/ 檢查結果是否如預期

Recap

回顧我們這次做過的內容

  1. 撰寫docker file, 使用python官方base image再加上自行開發的python api file建立新的docker file
  2. 將docker image執行為container, 並可以執行多個不同的container, 使用相關指令查看container的設定及執行狀況
  3. 將docker image推上docker hub讓別人可以使用
  4. 使用docker volumn從container外部編輯nginx config, 並使用docker exec讓container執行nginx reload指令
  5. 使用docker compose一次管理多個container

延伸思考

  1. 同樣的image要上到不同環境執行成container時通常會連到不同環境的資料庫或其他共用資源, 要怎麼樣做比較方便控制?
  2. 如果要管理大量container的執行及停止, 是否有更簡便的方式?
  3. 如果container有異常或效能不足時, 是否有機制可以繼續維持服務正常?
  4. 每次調整container造成IP變動的時候, 都必須要去查詢並更新nginx config, 是否有更簡便的方式?

--

--