---
# System prepended metadata

title: Lab1

---

# Lab1

## Proxmox

> - Install Proxmox on your external disk, and ensure that you can access Proxmox web console from another device in the
same network
> - Create VMs for your web server and database respectively

### USB ([reference](https://pve.proxmox.com/wiki/Prepare_Installation_Media))

- 使用 windows 的軟體 rufus 將 [iso](https://www.proxmox.com/en/downloads/category/iso-images-pve) 燒進 USB，記得要選 DD 模式

### Installation

- 進 BIOS
    - 關閉 Secure Boot
    - 調整開機順序，將 USB 調到最優先
- 以 USB 開機（請有線連接 AP）
    - 安裝空間選外接硬碟
    - 安裝軟體會自動接上有線內網
- 重新開機，再進 BIOS
    - 將外接硬碟的開機順序調到最優先

---

## Config

:::warning

### Database

**/etc/hosts**
```
192.168.1.X webserver
192.168.1.Y database-out
```
`sudo systemctl restart mariadb`

### Webserver
**/etc/hosts**
```
192.168.1.Y database
```
`uwsgi --ini flask_uwsgi.ini`
:::

:::info
### Database
- hostname: database
- 服務: MariaDB (MySQL)
    - user
        - root (password: proxmox1234)
        - flask (password: proxmox1234), request from webserver

### Web Server
- 服務: nginx, uwsgi, flask
:::

## Database

### [Installation](https://linuxize.com/post/how-to-install-mariadb-on-ubuntu-18-04/)

```bash
sudo apt update
sudo apt install mariadb-server
sudo systemctl status mariadb # 測試是否有裝好
mysql -V # 測試是否有裝好

sudo mysql_secure_installation # 設定 mysql root 的密碼
```


### [Create user and database](https://blog.gtwang.org/linux/mysql-create-database-add-user-table-tutorial/)

```shell
sudo mysql -u root -p
```

```sql
CREATE DATABASE `flask`;

--新增使用者，設定密碼
CREATE USER 'flask'@'localhost' IDENTIFIED BY 'proxmox1234';

--設定使用者權限
GRANT ALL PRIVILEGES ON flask.* TO 'flask'@'localhost';
--GRANT ALL PRIVILEGES ON my_db.* TO 'flask'@'192.168.1.107';
--GRANT ALL PRIVILEGES ON flask.* TO 'flask'@'webserver' IDENTIFIED BY 'proxmox1234';

--允許使用者從所有 ip 登入
GRANT ALL PRIVILEGES ON flask.* TO 'flask'@'%' IDENTIFIED BY 'proxmox1234';
```

### Bind Address

讓資料庫可以從非 localhost 連到
讓資料庫可以從 database-out (192.168.1.Y) 連到

在 `/etc/mysql/mariadb.cnf` 最下面寫
```ini
[mysqld]
bind-address = database-out
```

測試：
在 webserver 中下 `telnet database 3306`
不能跑出 connection refused


## Web Server

App File Structure
```
.
├── app
│   ├── app.py
│   ├── flask_uwsgi.ini
│   └── wsgi.py
├── docker-compose.yml
├── Dockerfile
└── requirements.txt
```

```graphviz
digraph{
    node []
    Nginx,uWSGI,Flask [color=lightblue]
    Nginx -> uWSGI
    uWSGI -> Nginx
    uWSGI -> Flask
    Flask -> uWSGI   
}
```

### 設定環境

```bash
apt-get update
# pip packages
apt-get install -y python3-pip
pip3 install -r requirements.txt
# utils
apt-get install -y git
apt-get install -y make
apt-get install -y gcc
apt-get install -y --no-install-recommends vim
apt-get install -y --no-install-recommends python3-pymysql
```
設定環境變數

`/etc/profile`
```bash
export FLASK_APP=app.py
export FLASK_RUN_HOST=0.0.0.0
export FLASK_ENV=development
export DB_HOST=database
export DB_NAME=flask
export DB_USER=flask
export DB_PASSWORD=proxmox1234
```

### Flask

Flask App (`app.py`)
```python
import os
from os.path import join

from flask import Flask, jsonify, request, make_response
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
api = Api(app)

# 連接 database
# 要使用 flask@webserver 這個 user 到 database 上登入 mysql 使用名叫 flask 的 database
# getenv 就是直接從環境變數取設定值
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}/{}'.format(
    os.getenv('DB_USER', 'flask'),
    os.getenv('DB_PASSWORD', ''),
    os.getenv('DB_HOST', 'database'),
    os.getenv('DB_NAME', 'flask')
)
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username

@app.before_first_request
def creat_tables():
    db.create_all()

@app.route('/')
def hello_world():
    return 'Hello, World!'

@app.route('/data')
def users():
    ret = []
    res = User.query.all()
    for user in res:
        ret.append(
            {
                'username': user.username,
                'email': user.email
            }
        )
    return jsonify(ret), 200
```

手動新增資料

```python
>>> from app import db
>>> db.create_all()

>>> from app import User
>>> admin = User(username='admin', email='admin@example.com')
>>> guest = User(username='guest', email='guest@example.com')

>>> db.session.add(admin)
>>> db.session.add(guest)
>>> db.session.commit()

>>> User.query.all()
[<User u'admin'>, <User u'guest'>]
>>> User.query.filter_by(username='admin').first()
<User u'admin'>
```

測試（在有 app.py 的目錄下）
```
flask run
```

然後看 localhost:5000

### nginx

新增檔案 `/etc/nginx/conf.d/flask_nginx.conf`

```
server {
	listen	80;
	server_name 192.168.1.*;
	location / {
		uwsgi_pass	127.0.0.1:3031;
		include uwsgi_params;
	}
}
```
nginx 會將 80 port listen 到的 request 傳到 127.0.0.1:3031
-> nginx 跟 flask 會藉由 uwsgi 的幫助在 127.0.0.1:3031 溝通

### uwsgi

新增 `wsgi.py`
```python
from app import app

if __name__ == '__main__':
    app.run()
```

新增 `flask_uwsgi.ini`
```ini
[uwsgi]

socket = 127.0.0.1:3031
wsgi-file = wsgi.py
callable = app
chmod-socket = 660

processes = 4
threads = 2
```

測試 uwsgi 有沒有接好

```
uwsgi --socket 127.0.0.1:3031 --wsgi-file wsgi.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191 --chmod-socket=660
```

啟動指令

```
uwsgi --ini flask_uwsgi.ini
```

查看
http://webserver/data

## GitLab

手動從 github import