shop_platform - 部署到 GCP (2)

監聽 80 port(後來發現其實不需要)

試著在 80 port 上開啟伺服器,但是失敗了

flora@*******:~/shop_platform$ pipenv run gunicorn "app:create_app()"
Loading .env environment variables...
[2021-10-28 13:38:45 +0000] [22652] [INFO] Starting gunicorn 20.1.0
[2021-10-28 13:38:45 +0000] [22652] [ERROR] Retrying in 1 second.
[2021-10-28 13:38:46 +0000] [22652] [ERROR] Retrying in 1 second.
[2021-10-28 13:38:47 +0000] [22652] [ERROR] Retrying in 1 second.
[2021-10-28 13:38:48 +0000] [22652] [ERROR] Retrying in 1 second.
[2021-10-28 13:38:49 +0000] [22652] [ERROR] Retrying in 1 second.
[2021-10-28 13:38:50 +0000] [22652] [ERROR] Can't connect to ('0.0.0.0', 80)

換成別的 port 就沒問題

flora@*******:~/shop_platform$ pipenv run gunicorn --bind 0.0.0.0:5000 "app:create_app()"
Loading .env environment variables...
[2021-10-28 14:00:11 +0000] [22879] [INFO] Starting gunicorn 20.1.0
[2021-10-28 14:00:11 +0000] [22879] [INFO] Listening at: http://0.0.0.0:5000 (22879)
[2021-10-28 14:00:11 +0000] [22879] [INFO] Using worker: sync
[2021-10-28 14:00:11 +0000] [22888] [INFO] Booting worker with pid: 22888

參考 Run a flask app on port 80 without sudo

sudo apt install authbind
sudo touch /etc/authbind/byport/80
sudo chmod 777 /etc/authbind/byport/80

然後就成功了

flora@*******:~/shop_platform$ authbind --deep pipenv run gunicorn --bind 0.0.0.0:80 "app:create_app()"
Loading .env environment variables...
[2021-10-28 14:08:57 +0000] [23160] [INFO] Starting gunicorn 20.1.0
[2021-10-28 14:08:57 +0000] [23160] [INFO] Listening at: http://0.0.0.0:80 (23160)
[2021-10-28 14:08:57 +0000] [23160] [INFO] Using worker: sync
[2021-10-28 14:08:57 +0000] [23170] [INFO] Booting worker with pid: 23170

使用 nginx

參考 Deploy a Flask Application to Ubuntu 18.04 Server,發現使用 nginx 的話,app 就不一定要監聽 80 port 了,nginx 會幫忙把 request 轉過去。

ModernWeb 2021 年會,10 月 22 日的議程,Vic Huang 講者的講題就是 NGINX misconfiguration in Real World。今天也讓我摸到一點 nginx 的邊邊了。

GCP 上 VM 的 External IP 是 34.80.108.187。設定好 nginx 後,連線到 34.80.108.187 會看到:

接下來,只要在 nginx 的設定中指定的 proxy_pass http://127.0.0.1:8000; 這個地方,啟動 app 去監聽就大功告成了。不過要先建立種子資料,不然網站會空空的。


建立資料表

pipenv run flask db upgrade

檢查是否建立成功

flora@*******:~/shop_platform$ mysql -u root -p
mysql> SHOW DATABASES;
mysql> USE shop_platform;
mysql> SHOW TABLES;
+-------------------------+
| Tables_in_shop_platform |
+-------------------------+
| alembic_version         |
| cart                    |
| cart_item               |
| category                |
| order                   |
| order_item              |
| order_status            |
| payment                 |
| payment_status          |
| product                 |
| question                |
| rating                  |
| reply                   |
| shipping_status         |
| user                    |
+-------------------------+
15 rows in set (0.00 sec)

看樣子沒問題


建立種子資料

pipenv run flask seed run

sqlalchemy.exc.DataError: (pymysql.err.DataError) (1366, "Incorrect string value: '\\xE5\\x88\\x86\\xE9\\xA1\\x9E...' for column 'name' at row 1")
[SQL: INSERT INTO category (id, name, insert_time, update_time) VALUES (%(id)s, %(name)s, %(insert_time)s, %(update_time)s)]
[parameters: ({'id': 1, 'name': '分類 1', 'insert_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530780), 'update_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530798)}, {'id': 2, 'name': '分類 2', 'insert_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530800), 'update_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530802)}, {'id': 3, 'name': '分類 3', 'insert_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530804), 'update_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530805)}, {'id': 4, 'name': '分類 4', 'insert_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530807), 'update_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530808)}, {'id': 5, 'name': '分類 5', 'insert_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530810), 'update_time': datetime.datetime(2021, 10, 28, 18, 1, 42, 530811)})]
(Background on this error at: https://sqlalche.me/e/14/9h9h)

失敗了。我本地的電腦建立種子資料都沒出問題,我試著檢查,是否是編碼問題。首先,查看我本地 MySQL (版本 8.0.23)的編碼設定:

再對照 VM 上 MySQL (版本 5.7.36)的編碼設定:

mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

參考資料:[MySQL] 為什麼 MySQL 要設定用 UTF8MB4 編碼 UTF8MB4_UNICODE_CI

試著把編碼改成 utb8mb4,查過一些文章後,底下這些指令,mysql 都跟我說 ERROR:

  • mysql> ALTER DATABASE shop_platform CHARACTER SET utf8 COLLATE utf8mb4;

  • mysql> ALTER DATABASE shop_platform CHARACTER SET utf8 COLLATE 'utf8mb4';

  • mysql> ALTER DATABASE shop_platform CHARACTER SET utf8 COLLATE utf8mb4_unicode_ci;

  • mysql> ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

  • mysql> ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8mb4;

後來根據 How to set utf8mb4 on MySQL 5.7 (windows),使用 mysql> SET NAMES utf8mb4;,有效果了,再次查詢編碼的結果是:

mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

character_set_database 和 character_set_server 的編碼設定還是和我本地的 MySQL 不一樣。再次嘗試產生種子資料,依然是 Incorrect string value。

上述操作,我猜應該是在未用 use database-name 指令指定資料庫時做的。下述操作,則是有下 use shop_platform 這個指令

再根據關於MySQL如何修改編碼問題(utf8轉utf8mb4)--解決表情存庫,繼續設定:

  • mysql> SET character_set_server = utf8mb4;

  • mysql> SET character_set_database = utf8mb4;

mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8mb4                    |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

看起來是有用,可是後來我發現,我一離開 mysql 操作介面,再重新登入,上次改好的設定都不見了⋯⋯

看著 character_sets_dir 那一行,我決定到 /usr/share/mysql/charsets/ 資料夾看看是怎麼回事。根據 README,可以設定的編碼都列在 Index.xml 檔案裡。而 Index.xml 裡面沒有 utf8mb4 也沒有 utf8mb4_unicode_ci,只有 utf8、utf8_general_ci、utf8_bin。難怪 mysql> ALTER DATABASE shop_platform CHARACTER SET utf8 COLLATE utf8mb4; 會跟我說是 invalid 的設定。

但是根據 PHP PDOException: SQLSTATE[HY000] [2019] Can't initialize character set utf8mb4 執行了 mysql> show character set like 'utf%';

+---------+------------------+--------------------+--------+
| Charset | Description      | Default collation  | Maxlen |
+---------+------------------+--------------------+--------+
| utf8    | UTF-8 Unicode    | utf8_general_ci    |      3 |
| utf8mb4 | UTF-8 Unicode    | utf8mb4_general_ci |      4 |
| utf16   | UTF-16 Unicode   | utf16_general_ci   |      4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci |      4 |
| utf32   | UTF-32 Unicode   | utf32_general_ci   |      4 |
+---------+------------------+--------------------+--------+

裡面倒是出現了 utf8mb4。

嘗試 mysql> ALTER DATABASE shop_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 就沒有報錯了。看來是我對語法不熟悉惹得禍。接著再嘗試新增種子資料,依然是 Incorrect string value

話說,我在找資料的過程中才知道,就算修改了預設編碼的設定,也不會自動套用到之前已經建立的資料庫上,所以還需要再對已經建立的資料庫設定一次。改預設編碼的話,可以到 /etc/my.cnf 或 /etc/mysql/my.cnf 去改

我在 /etc/mysql/my.cnf 檔案最下方新增了

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4


[mysqld]
collation-server = utf8mb4_unicode_ci
init-connect='SET NAMES utf8mb4'
character-set-server = utf8mb4

重新啟動 mysql 後,再次登入 mysql(這次是用 sudo -u root -p 登入,之前登入時,開頭都沒有加上 sudo)

mysql> SHOW variables LIKE 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4                    |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

看來預設值 OK 了。然後再設定一次已經建立的資料庫的編碼設定:mysql> ALTER DATABASE shop_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。不過種子資料還是建立失敗了。所以失敗的原因,大概跟是不是用 sudo 登入無關。

參考這篇文章 Mysql 插入中文错误:Incorrect string value: '\xE7\xA8\x8B\xE5\xBA\x8F...' for column 'course' at row 1,用另一個語法查看資料表的編碼

mysql> show create table category;
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                     |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| category | CREATE TABLE `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

原來編碼還是 latin1。難道說,即使我改了 shop_platform 這個 database 的編碼設定,但 shop_platform 中已經建立的資料表,並不會自動套用新的編碼設定?因為有滿多張資料表的,我決定 drop shop_platform,再重新建立一次。這樣新建的 database 和 table 都能套用新的 utf8mb4 預設值。然後我終於成功了!

執行 flask

flora@*******:~/shop_platform$ pipenv run gunicorn --bind 127.0.0.0:8000 "app:create_app()"
Loading .env environment variables...
[2021-10-29 07:51:02 +0000] [3552] [INFO] Starting gunicorn 20.1.0
[2021-10-29 07:51:02 +0000] [3552] [INFO] Listening at: http://127.0.0.0:8000 (3552)
[2021-10-29 07:51:02 +0000] [3552] [INFO] Using worker: sync
[2021-10-29 07:51:02 +0000] [3561] [INFO] Booting worker with pid: 3561

就能連上網站了

我本來是想把伺服器開在 127.0.0.1:8000 的,因為 nginx 的設定是 proxy_pass http://127.0.0.1:8000;,我以為一定要開在那個位置,app 才收得到 request。但是開失敗了,可能是有什麼程式已經在那個位置執行了。結果改成在 127.0.0.0:8000 開,居然也能連得上。看來我對 IP、PORT 或是 nginx 的運作方式還不夠瞭解。

目前是我自己手動開啟伺服器,應該有什麼辦法可以讓電腦自己開伺服器。

update: ssh 連線斷掉以後,http://34.80.108.187/products/ 這個網址依然沒有壞掉。看樣子,有沒有保持 ssh 連線,和遠端 VM 上的 app 服務有沒有在運作,是兩回事。所以,為什麼沒辦法把伺服器開在 127.0.0.1:8000,是因為,在前一次的 ssh 連線時,我已經在那個位置開啟伺服器,ssh 連線斷掉後,伺服器還是在那個位置運作。

根據 How to kill a process on a port on ubuntu,刪除在某個 port 上運行的 prcoess,語法是:

flora@*******:~/shop_platform$ sudo netstat -lpn |grep :8000
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      3359/python         
flora@*******:~/shop_platform$ sudo kill -9 3359
flora@*******:~/shop_platform$ sudo netstat -lpn |grep :8000
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      3563/python         
flora@*******:~/shop_platform$ kill -9 3563
-bash: kill: (3563) - No such process

先查詢在某個 port 上運行的 prcoess 的 PIN,然後指定該 PIN 予以刪除。不過,我操做起來,刪完第一次後, process 還在,但是換了個 PIN,要再針對新的 PIN 重刪一次。

Comments

Popular posts from this blog

Alpha Camp 全端開發課程學習心得

在 javascript 用 regular expression 為金額加上千位數分隔符號

shop_platform - sqlalchemy.exc.TimeoutError