---
# System prepended metadata

title: 雲端技術實務期末
tags: [雲端技術實務]

---

# 雲端技術實務期末
:::info
**無法使用到學校電腦，使用ubuntu22.04 desktop進行測試**

參考資料
https://hackmd.io/@saID5sq_QhCsFgtxP02KkA/rJBkb3pU3

https://ithelp.ithome.com.tw/articles/10191016

https://ithelp.ithome.com.tw/articles/10191139

https://hackmd.io/@A9aTZ-55SDW754ufW1ctXw/HyAmVctPh
:::
:::danger
**評語：**
1. 主機的hostname不是學號??
2. dokcer pull出現Warning??

**解決方法：**
1. 直接抓hostname會抓到container的id ，解決方式只查到可以用sudo docker run -e HOSTNAME=$(hostname) -p 8080:8080 {image_name:tag}
2. 可能是run flask 出現Warning，https://blog.csdn.net/qq_42991509/article/details/127101393

:::
## 1.安裝Docker
:::info
https://www.51cto.com/article/715086.html

https://hackmd.io/@whYPD8MBSHWRZV6y-ymFwQ/rynv0a7CT
:::
## 2.安裝web server
```cmd
sudo apt-get -y update
sudo apt-get -y install nginx
```
> 輸入虛擬機IP查看網站是否有正確運作
![image](https://hackmd.io/_uploads/B1EWS6cSC.png)


## 3.建立一個web server應用程式製作成docker image放置雲端網站(docker hub)
### 建立DockerFile
> 建立寫 Dockerfile 會用到的資料夾(docker-test)，指令如下
```cmd
mkdir docker-test
cd docker-test
```
> 撰寫Dockerfile
```
vi Dockerfile
```
:::success
Dockerfile 的內容如下
```
# 使用官方的 Python 執行環境作為基本的 Docker 影像
FROM python:3.9-slim

# 設置工作目錄
WORKDIR /app

# 複製當前目錄下的所有文件到工作目錄
COPY . .

# 安裝 requirements.txt 中所列的必要套件
RUN pip install -r requirements.txt

# 讓 80 連接埠可以從 Docker 容器外部存取
EXPOSE 80

# 當 Docker 容器啟動時，自動執行 app.py
CMD ["python", "app.py"]
```
:::

> 在相同的目錄中創建名為 app.py 及 requirements.txt 的文件

:::info
Dockerfile 需要用到另外兩個檔案，分別為requirements.txt 與 app.py，app.py 就是我們自己撰寫的 Python 應用程式，而 requirements.txt 則是相依套件的列表。

> 創建一個Flask應用程式。
app.py
```python=
from flask import Flask, render_template
import socket
import os

app = Flask(__name__)

@app.route('/')
def index():
    # 獲取主機名
    hostname = socket.gethostname()
    VM_hostname = os.getenv('HOSTNAME')
    return render_template('index.html', hostname=hostname, VM_hostname=VM_hostname)

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


> 在 requirements.txt 寫入所需套件
requirements.txt
```tex=
Flask
```

> **在docker-test/templates目錄內，創建index.html文件，內容如下：**
``` 
mkdir templates
cd templates
```

> index.html
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>1092960 BMI計算器</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <style>
        @media screen and (min-width: 600px) {
            table {
                width: 80%;
            }
        }
    </style>
</head>
<body>
    <script>
        function calculateBMI() {
            var flag = true;
            const weight = document.getElementById('weight').value;
            const height = document.getElementById('height').value;
            
            //還原起始顏色
            restoreColor();

            if (isNaN(Number(weight,10))) {
                alert("您輸入的體重格式錯誤");
                flag = false;
            }else if (weight === null || weight.trim() === '') {
                alert("請輸入您的體重");
                flag = false;
            }

            if (isNaN(Number(height,10))) {
                alert("您輸入的身高格式錯誤");
                flag = false;
            }else if (height === null || height.trim() === '') {
                alert("請輸入您的身高");
                flag = false;
            }

            if (weight && height && flag) {
                const heightInMeters = height / 100;
                const bmi = weight / (heightInMeters * heightInMeters);
                const bmiRounded = bmi.toFixed(2);
                if (bmiRounded < 18.5) {
                    changeColor('underweight');
                }else if (bmiRounded < 24) {
                    changeColor('normal');
                }else {
                    changeColor('overweight');
                }
                document.getElementById('result').innerText = `你的 BMI 值為 ${bmiRounded}`;
            } else {
                document.getElementById('result').innerText = `你的 BMI 值為 ???`;
            }
        }
        function changeColor(weightRange){
            var allHighlight = document.getElementsByClassName(weightRange);
            for (var i=0;i<allHighlight.length;i++){
                allHighlight[i].style.backgroundColor = '#FFFF6F';
            }
        }
        function restoreColor(){
            var allHighlight = document.getElementsByClassName('ori');
            for (var i=0;i<allHighlight.length;i++){
                allHighlight[i].style.backgroundColor = '#FFFFFF';
            }
        }
        document.addEventListener('DOMContentLoaded', function() {
            var vmHostname = document.getElementById('VM_hostname');
            var vmHint = document.getElementById('VM_hint');

            if (vmHostname.innerText !== 'VM_Hostname not available') {
                vmHint.style.display = 'none';
            }else {
                vmHostname.style.display = 'none';
            }
        });
    </script>
    <div align="center">
        <h1>1092960 BMI計算器</h1>
        <h3 id="hostname">你的hostname為 {{ hostname }}</h3>
        {% if VM_hostname != hostname %}
            <h4 id="VM_hostname">{{ VM_ostname }}</h4>
        {% else %}
            <h4 id="VM_hostname">VM_Hostname not available</h4>
        {% endif %}
        <div id="VM_hint">若是想取得VM的hostname，而非container請輸入<br>sudo docker run -e HOSTNAME=$(hostname) -p 8080:8080 {image_name:tag}</div>
        <div>
            <span>身高：</span> 
            <input id="height" type="text" size="11" placeholder="請輸入身高" style="border: 1px solid #0B6060; text-align:right;">
            <font face="Arial"> cm</font>
        </div>
        <br>
        <div>
            <span>體重：</span> 
            <input id="weight" type="text" size="11" placeholder="請輸入體重" style="border: 1px solid #0B6060; text-align:right;">
            <font face="Arial"> kg</font><font color="#FFFFFF">.</font>
        </div>
        <br>
        <button onclick="calculateBMI()">計算 BMI</button>
        <div id="result">你的 BMI 值為 ???</div>
        <br>
        <table align="center" border="1" cellspacing="0" bordercolordark="black" bordercolorlight="black" height="159">
            <tbody>
                <tr bordercolor="#cccccc" bgcolor="#66B3FF">
                    <td width="100" height="35"></td>
                    <td width="190" height="35">
                        <div align="center"><font color="#000000"><span>身體質量指數(BMI)<br>(kg/m²)</span><br></font></div>
                    </td>
                    <td width="150" height="35">
                        <div align="center"><font color="#000000"><span>腰圍<br>(cm)</span><br></font></div>
                    </td>
                </tr>
                <tr bordercolor="#ACD6FF">
                    <td width="100" bgcolor="#C4E1FF">
                        <div align="center">體重過輕</div>
                    </td>
                    <td width="180" class="underweight ori">
                        <div align="center">BMI < 18.5</div>
                    </td>
                    <td width="150" class="underweight ori">
                        <p align="center">---</p>
                    </td>
                </tr>
                <tr bordercolor="#cccccc">
                    <td width="100" bgcolor="#C4E1FF">
                        <div align="center">正常範圍</div>
                    </td>
                    <td width="180" class="normal ori">
                        <div align="center">18.5 ≤ BMI < 24</div>
                    </td>
                    <td width="150" class="normal ori">
                        <p align="center">---</p>
                    </td>
                </tr>
                <tr bordercolor="#cccccc">
                    <td width="100" bgcolor="#C4E1FF">
                        <div align="center">異常範圍</div>
                    </td>
                    <td valign="top" width="180" class="overweight ori">
                        <div align="center">
                            <span>過重：24 ≤ BMI < 27<br>輕度肥胖：27 ≤ BMI < 30<br>中度肥胖：30 ≤ BMI < 35<br>重度肥胖：BMI ≥ 35</span>
                        </div>
                    </td>
                    <td valign="top" width="150" class="overweight ori">
                        <div align="center">
                            <p>男性： ≥ 90公分<br>女性： ≥ 80公分<br></p>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
```
:::


:::success
資料夾的結構如下圖：
flask_app/
├── app.py
├── Dockerfile
├── requirements.txt
├── templates/
│   └── index.html
![image](https://hackmd.io/_uploads/H14DFT5S0.png)
:::
### 建立Docker image
> **返回到包含Dockerfile的目錄中**，運行以下命令來建置Docker image
```
docker build -t s1092960 --network=host .
```
:::warning
如果出現**permission denied**最簡單就是改成
```
sudo docker build -t s1092960 --network=host .

sudo docker images
```
:::
![image](https://hackmd.io/_uploads/B1BoY69H0.png)

> Build 完 Docker Image 之後，使用 docker images 指令查看是否有 build 成功
```
docker images
```
![image](https://hackmd.io/_uploads/H1Hrq6qHR.png)

### 執行 Docker Container
:::info
docker run -p 8080:8080 {image_name:tag}

輸入http://localhost:8080查看網頁
若是使用伺服器請輸入http://{ip}:8080
:::
```cmd
docker run -p 8080:8080 s1092960:version3
```
![image](https://hackmd.io/_uploads/r1E2hC9BR.png)

![image](https://hackmd.io/_uploads/SJ5R20qSA.png)

:::success
若是想取得VM的hostname，而非container請輸入
```
sudo docker run -e HOSTNAME=$(hostname) -p 8080:8080 {image_name:tag}
```
![image](https://hackmd.io/_uploads/BJK7a0cS0.png)

![image](https://hackmd.io/_uploads/H1wEpR5BC.png)

:::

## 4.最終打包，把 Docker Image Push 到 Docker Hub 上
:::success

> 到dockerhub註冊帳號
https://hub.docker.com/

> 確認執行沒問題後建立Docker image並將其命名為web_dl_1092960
```
docker build -t web_dl_1092960 --network=host .
```
![image](https://hackmd.io/_uploads/H1IBy1oBA.png)

:::

### 1. 打開 terminal 輸入 docker images，畫面如下
![image](https://hackmd.io/_uploads/HyONbJiHC.png)

### 2. 要把 Docker Image Push 到 Docker Hub 上，需要把 Docker Image 加上 tag，如下指令
:::info
> 使用 docker tag 命令為本地的 Docker image打上標籤。

使用的Docker tag格式如下：
docker tag < Image ID or Image Name > < DockerHub帳號 >/< Image Name >:< Tag >
:::
    
```
docker tag web_dl_1092960 whitefir473/web_dl_1092960:0001
```
![image](https://hackmd.io/_uploads/rJiBf1jrC.png)

### 3. 輸入 docker login 指令登入到 Docker Hub，畫面如下
```
docker login
```
![image](https://hackmd.io/_uploads/rJSO71orC.png)

### 4.使用 docker push 指令把 Docker Image Push 到 Docker Hub 裡，指令如下
```
docker push whitefir473/web_dl_1092960:0001
```
![image](https://hackmd.io/_uploads/HJl1LLyirR.png)

![image](https://hackmd.io/_uploads/BkWu8yjSC.png)

## 5.測試是否成功的把 Docker Image Push 到 Docker Hub 上
:::success
使用另一台機器或將 local 的 Image 刪除掉
:::
### 1. 在 Docker Hub 網站裡，登入帳號進去可以看到 Repositories 已經有 whitefir473/web_dl_1092960 的 Docker Image
![image](https://hackmd.io/_uploads/BkWu8yjSC.png)

### 2. 使用 docker rm 指令把 local 的 Image 刪除掉，測試從 Docker Hub pull Docker Image 下來，指令如下
```
docker rmi -f whitefir473/web_dl_1092960:0001
docker rmi -f web_dl_1092960
```
![image](https://hackmd.io/_uploads/S12CkloHC.png)

### 3. 從 Docker Hub Pull Docker Image 下來，指令如下
```
docker pull whitefir473/web_dl_1092960:0001
```

![image](https://hackmd.io/_uploads/B1R2xeiBC.png)

### 4. 啟動 Docker Container，指令如下
```cmd
docker run -p 8080:8080 whitefir473/web_dl_1092960:0001
```
![image](https://hackmd.io/_uploads/rkb1ZxoHA.png)

:::success
> 若是想取得VM的hostname，而非container請輸入
> sudo docker run -e HOSTNAME=$(hostname) -p 8080:8080 {image_name:tag}
```
sudo docker run -e HOSTNAME=$(hostname) -p 8080:8080 whitefir473/web_dl_1092960:0001
```
![image](https://hackmd.io/_uploads/ByW7-xsH0.png)
:::

### 5. 啟動完成之後就可以使用 Browser 查看結果，輸入 URL 位址為 http://localhost:8080 可以看到如下畫面
:::info
輸入http://localhost:8080查看網頁
若是使用伺服器請輸入http://{ip}:8080
:::
![image](https://hackmd.io/_uploads/rySW-xiSA.png)

![image](https://hackmd.io/_uploads/SJtVbgsS0.png)

## 6.OpenStack的VM也啟動

![image](https://hackmd.io/_uploads/rkM0CGaB0.png)

![image](https://hackmd.io/_uploads/SyWEJmaH0.png)

![image](https://hackmd.io/_uploads/ByinZQTSR.png)

![image](https://hackmd.io/_uploads/rkirMQTrR.png)

![image](https://hackmd.io/_uploads/By8A7QpHC.png)

![image](https://hackmd.io/_uploads/SJgW4XTSA.png)
