---
# System prepended metadata

title: Deploy Flask App to EC2 by Docker
tags: [AWS]

---

# 簡介
使用Docker部署一個Flask App with Nginx and uWSGI
到AWS EC2 Instance上
使外部能透過public ip連線到該雲端server訪問Flask App

# Flask Nginx uWSGI  架構
![](https://hackmd.io/_uploads/BJZQ-ysla.png)

* Nginx：非同步Web Server，可做反向代理、負載平衡器和 HTTP 快取等
* WSGI (Web Server Gateway Interface)：負責溝通代理伺服器跟Flask，可以當成一個協議或規範
    uWSGI是WSGI的一種協議


# Build
先在開發端安裝Docker以建立應用程式與環境的Docker Image
此例使用DockerHub上
將Nginx與uWSGI包裝在一起的Docker Image做為Base Image
### Install Docker

Add Docker's official GPG key:
```shell=
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
```


Add the repository to Apt sources:
```shell=
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  
  
sudo apt-get update
```

Install:
```shell
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

Test:
```shell
sudo docker run hello-world
```
若安裝成功會顯示以下畫面
![](https://hackmd.io/_uploads/H1YRkFYla.png)


### Build Docker Image
建立一簡單Flask App作為範例:
先建立一目錄app
並在目錄中新增 main.py:
```python=
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World from Flask"

if __name__ == "__main__":
    # Only for debugging while developing
    app.run(host='0.0.0.0', debug=True, port=80)
```

在與app同目錄的地方新增Dockerfile:
```dockerfile
FROM tiangolo/uwsgi-nginx-flask:python3.11
COPY ./app /app
```
FORM 指令為使用的Base Image 
COPY 代表複製檔案到Image裡面

此例的Base Image:
https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask/?source=post_page-----b8bdc60d1dc7--------------------------------

Dockerfile建立完成後檔案結構為:
```
.
├── app
│   └── main.py
└── Dockerfile
```

接下來在與Dockerfile同目錄下執行Build指令:
```shell
sudo docker build -t myimage .
```
完成後即可使用docker images指令查看目前所有Image
確認建立完成
![](https://hackmd.io/_uploads/B1wPd6Yxp.png)

### 上傳至Docker Hub
為了讓部署的主機下載Docker Image
需上傳至Docker Hub提供Docker pull

Image建立完成後
先將Docker Image加上tag:
```shell
sudo docker tag <Image Name>  <DockerHub Id>/<Image Name>
```

登入Docker Hub:
```shell
sudo docker login
```

上傳至Docker Hub:
```shell
sudo docker push <DockerHub Id>/<Image Name>
```

即可到Docker Hub確認上傳完成
![](https://hackmd.io/_uploads/ryS1QgieT.png)



# Deploy
在被部署的主機上安裝完Docker
即可Pull開發端Build好的Docker Image下來
Run成Container即完成部署

### Pull Docker Image
登入Docker Hub到剛剛上傳的Repository公開頁面
即可複製該Image的Pull指令
![](https://hackmd.io/_uploads/Hy-QVejxp.png)

Pull指令格式:
```shell
sudo docker pull <DockerHub Id>/<ImageName>
```

執行sudo docker images確認有pull下來
![](https://hackmd.io/_uploads/ByoF4gie6.png)

### Run Container
將Image Run成Container :
```shell
sudo docker run -d --name mycontainer -p 8000:80 leefunyu/myimage
```
--name 後面為建立的Container名稱
-p 8000:80 
8000為主機對外的port 
80是container的port
leefunyu/myimage為Image名稱

Container run起來後可用指令確認目前執行中的Container:
```
sudo docker ps
```
![](https://hackmd.io/_uploads/BkjF8xola.png)

打開該EC2 Instance public ip加上指定port(該port需加入安全群組)
即可連到Flask App
完成部署
![](https://hackmd.io/_uploads/H1Ihc6txT.png)

若想停止該服務
把Container關掉即可:
```shell
sudo docker stop <containerID>
```

# 過程遇到的問題
不同AWS EC2 Instance若共用安全群組會導致port發生問題無法存取

# Reference
https://medium.com/%E5%B7%A5%E7%A8%8B%E9%9A%A8%E5%AF%AB%E7%AD%86%E8%A8%98/flask-app-%E5%8A%A0%E4%B8%8A-wsgi-%E5%8F%8A-nginx-%E6%9C%8D%E5%8B%99-b8bdc60d1dc7

https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask/?source=post_page-----b8bdc60d1dc7--------------------------------
