需求描述:原本的demo專案是用授課方提供的Heroku平台與MySQL資料庫,後因這兩者皆已到期,所以決定自己上線demo網站.
實作流程:
於AWS EC2 上建立VM
在VM上安裝MySQL資料庫
在VM上安裝Docker相關套件
把GIT上的Express專案建立成container
在VM上安裝NGINX
在VM上安裝Certbot來獲取SSL(HTTPS)
預期架構:
於AWS EC2 上建立VM
註:本篇使用AWS EC2免費方案,但操作過程可能產生額外收費的情形(資料的傳輸等)。
前置作業
- 已註冊AWS帳號
建立VM
1.登入至AWS,進入EC2控制台,點擊右上登入主控台,搜尋"EC2"點擊第一個
進入https://aws.amazon.com/tw/
2.建立VM
- 點擊"啟動執行個體"
- 設置VM名稱
- 選擇作業系統,本文操作皆使用"Red Hat Enterprise Linux 9"
- 執行個體類型,可視為該VM的硬體配置(CPU,記憶體),免費方案只有"t2.micro"可選擇,跑個人專案還是夠的
- 建立金鑰對,為後續連線VM的驗證金鑰,點選"建立新的金鑰對",輸入金鑰對名稱後即可建立,此時會自動下載一個pem檔案,先保留著
- 網路設定,勾選 "允許來自網際網路的 HTTP(S) 流量",這樣在VM建立後,會自動在安全性群組的輸入規則加入80/tcp,443/tcp這兩條
- 創建VM,拉至最底下,點擊"啟動執行個體"
登入至VM
我們會採用SSH的方式連線至VM,所以要在你的電腦安裝相關SSH連線程式,本文則是使用"Xshell 7"這套軟體來連線
- 開啟Xshell 7並使用金鑰對來連線,在AWS頁面左側選取"執行個體"即可看到已建立的VM清單,點擊"執行個體ID"以查看該VM的詳細資訊
- 點擊"連線",此時應會跳至"SSH 用戶端"這一頁,直接點擊最下面的複製按鈕,此為SSH的連線指令
註:可看到第三點建議修改金鑰的權限,Windows系統的用戶可安裝"Cygwin",這樣就可在Windows下執行Linux指令了,另注意Windows與Linux檔案路徑的斜線"
\","/"是相反的
- 回到Xshell上,直接右鍵貼上剛剛的指令,然後enter,點選"接受及存檔"
註:在Xshell上預設是無法用ctrl+V的方式,是預防該指令會執行在登入中的系統,所以用右鍵貼上的方式即可
- 選擇使用者金鑰,點擊"匯入",在下方選"安全性"並進入該VM的安全行群組匯入之前下載的pem檔案,確定即可,之後連線就選該金鑰就可以了
成功登入
在VM上安裝MySQL資料庫
設定VM的開放port
- 首先回到該VM的詳細資料頁面,在下方選"安全性"並進入該VM的安全行群組
- 因此VM只有SQL資料庫運行,所以在創建時的網路設定不需勾選"允許http(s)",在下方的傳入規則就會看到只有一條SSH,接著進入"編輯傳入規則",並"新增規則"
類型:自訂TCP
連接埠範圍:3306
來源:0.0.0.0/0
然後"儲存規則"
安裝MySQL
登入至VM,開始輸入下列指令
- 更新
sudo yum update -y
- 安裝MySQL
sudo yum install -y mysql-server
- 修改設定檔,於最底部加入
bind-address=0.0.0.0
sudo vi /etc/my.cnf.d/mysql-server.cnf
- 啟動MySQL服務
sudo systemctl start mysqld.service
- 設定開機自動啟動
sudo systemctl enable mysqld.service
- 查看服務狀態
sudo systemctl status mysqld.service
ctrl+C後enter可跳出 - 查看port狀態
netstat -anltp|grep :3306
註:此作業系統應為最小化安裝,許多工具套件是沒有先安裝的,所以接下來遇到"command not found"的部分就需安裝對應的套件
- 安裝netstat套件
sudo yum install -y net-tools
建立資料庫使用者
登入至VM,開始輸入下列指令
- 使用 root 帳號登入到 MySQL
mysql -u root -p
會要求輸入password,直接按Enter即可
- 建立使用者,
user01
與pass1234
分別為帳號跟密碼,可設定為自己要用的CREATE USER 'user01'@'localhost' IDENTIFIED BY 'pass1234';
CREATE USER 'user01'@'%' IDENTIFIED BY 'pass1234';
GRANT ALL ON *.* TO 'user01'@'localhost';
GRANT ALL ON *.* TO 'user01'@'%';
FLUSH PRIVILEGES;
輸入exit
MySQL
使用 MySQL Workbench 進行連線測試
設定資料庫的連線IP(使用彈性IP)
- 於該VM的資料頁面中,可看到"公有 IPv4 地址"跟"彈性IP地址","彈性IP地址"可當作是靜態IP,當VM重新啟動時,公有IPv4地址可能會變動,但彈性IP地址不會,所以建議使用彈性IP
- 設置彈性IP,在左側欄位"網路和安全"點擊"彈性IP",點擊"配置彈性IP地址",點擊"配置"
- 進入該IP的摘要,點擊"與彈性IP地址建立關聯"
- 在"執行個體"選擇你的VM,會有下拉選單讓你選,選好後就"建立關聯",回到VM資訊就可以看到彈性IP綁訂了,SSH連線的指令也會變為這個IP
使用MySQL Workbench
- 直接至網站下載該軟體
- 開啟後點擊"+"來新增連線,
Connetcion Name:自訂名稱
Hostname:彈性IP地址
Username:上面所設定的帳戶名稱
Password:點擊"Store in Vault"來輸入上面設定的密碼 - 點擊"Test Connection"來驗證連線,出現"Success"後"OK"
- 在主畫面進入該SQL連線,在一個新的 Query 頁面輸入以下的 SQL 指令去建立資料庫
create database twitter_sequelize; use twitter_sequelize;
- 按下黃色閃電執行,在下方可看到執行成功
這樣SQL Server設置好了,接下來就到另一台VM來部署網站
在VM上安裝Docker相關套件
- 依先前步驟再創建一次VM並綁定彈性IP,然後SSH連線
- 輸入下列指令來安裝docker
- 更新
sudo yum update -y
- 安裝相關dependencies
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
- 添加Docker Repo
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- 安裝Docker
sudo yum install -y docker-ce
- 啟動和設定開機自動執行Docker
sudo systemctl start docker
sudo systemctl enable docker
- 確認安裝成功,使用檢查版本的指令
sudo docker --version
- 修改Docker執行權限,之後要執行docker相關指令就不需sudo了
sudo usermod -aG docker <your_username>
- 重啟VM以確保所有更動生效
sudo reboot
- 登入VM測試指令
docker -v
把GIT上的Express專案建立成container
安裝git並clone專案至VM
- 安裝GIT
sudo yum install -y git
- 下載專案,這邊使用個人的專案,該專案使用 nodejs/express 開發
git clone https://github.com/Antarctic-penguin/twitter-api-2020.git
- 使用 cd 指令進入專案資料夾,使用 ls 查看檔案狀態
創建container
- 這邊簡述一下流程:
1.可執行的專案(補齊.env , 設定資料庫連線)
2.製作成docker image(創建Dockerfile)
3.從docker image創建container
1.可執行的專案
- 在專案資料夾的路徑下,補上該有的.env檔
vi .env
貼上你所需的資訊 - 修改資料庫的連線資訊
帳號,密碼,資料庫名稱 改為上一部分所設定的值,"host"就是資料庫VM的彈性IP地址
vi config/config.json
2.製作成docker image
- 創建Dockerfile,一樣在專案路徑下,輸入以下
vi Dockerfile
>vi Dockerfile
# Use an official Node.js runtime as the base image
FROM node:14
# Set the working directory in the container to /app
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install any needed packages specified in package.json
RUN npm install
# Bundle app source inside Docker image (by copying from your host to your image filesystem)
COPY . .
# Make port 3000 available to the world outside this container
EXPOSE 3000
# Run the app when the container launches
CMD ["node", "app.js"]
因為此專案是用node 14版本開發的,所以FROM node:14
- 建立image file
docker build -t twitter_api .
-t 後接標籤名稱,協助我們確認該image是什麼,最後面的"."為在當前目錄下的dockerfile
建立完成
- 確認image狀態
docker images
3.從docker image創建container
- 創建container,3000是專案所使用的port,--name後面接container名稱,最後面則是image的ID,
docker run -d -p 3000:3000 --name api01 419d112ba624
- 檢查容器運行狀態,可看到該容器且STATUS為UP,若容器沒有啟動,使用docker ps -a可以看到
docker ps
- 檢查專案運行狀態,最後面是接Container ID,可以從上圖的資訊獲得
docker logs 12985ae4c388
當時在專案內有設置提示訊息,所以看到成功提示且沒有其他錯誤訊息,就代表專案成功運行
註:用docker-compose建立container:
- 在專案路徑下,創建
docker-compose.yml
vi docker-compose.yml
>vi docker-compose.yml
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/app
ports:
- "3000:3000"
- 建立image
docker compose -f docker-compose.yml build
- 從image執行container
docker compose -f docker-compose.yml up -d
檢查container狀態,沒有成功跑起來,於是檢查LOG,發現是"Cannot find module "
把"Cannot find module "跟"docker compose"拿去google,從這篇大概得知是磁碟區路徑不同的問題,拿去問AI就得到了答案
這樣安裝的module就可被找到了,在volumes區塊加入- /app/node_modules
- 刪除原本的volumes
docker compose down -v
- 重建image跟container,可看到container成功執行
docker compose -f docker-compose.yml build
docker compose -f docker-compose.yml up -d
測試API功能
- 於安全性群組的輸入規則,新增port 3000
- 建立資料庫的種子資料,首先登入至container裡
docker exec -it 12985ae4c388 /bin/bash
- 使用ls可看到專案的檔案都複製到容器裡了
- 此時就可以把專案裡的 migration 設定檔同步到資料庫
npx sequelize db:migrate
- 於MySQL Workbench可看到資料表都建立了
- 建立種子資料
npx sequelize db:seed:all
輸入exit
離開容器 - 於MySQL Workbench可看到用戶資料都建立了
- 使用Postman發請求,位址是 http:// 彈性IP地址 + port + 路徑,可看到成功回應
這樣API專案就是成功上線了,但為了跟當初Heroku的效果一樣,所以要把IP地址改成網域名稱,並加入https
在VN上安裝NGINX
1.申請網域
原本是想用google domain,但發現已不提供服務,所以就用google建議的Squarespace,這邊購買滿單純的,一年就是20美含安全防護,不像GoDaddy有一堆加購方案,然後第一年有折扣是12美
- 購買好之後進入你的網域總覽,進入左側的"DNS"
- 點擊"ADD RECORD"
- "Host":
@
,"Type"選擇"A","DATA":彈性IP地址,然後"SAVE"
- 使用Postman測試,位址是 http:// 網域名稱 + port + 路徑,可看到成功回應
接下來要把port 3000移掉,就需要Nginx的幫忙
2.安裝NGINX
- 安裝NGINX
sudo yum install -y nginx
- 安裝firewalld
sudo yum install -y firewalld
- 啟用和設定開機自動啟動firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo systemctl status firewalld
- 開啟http(s) port以供NGINX使用,於防火牆新增規則
sudo firewall-cmd --permanent --add-port={80/tcp,443/tcp}
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
- 啟用和設定開機自動啟動Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
註: 後續若有重啟NGINX服務,相關的container也要重啟,不然NGINX的設定會套用不到container上
3.修改NGINX設定檔
- 修改設定檔
sudo vi /etc/nginx/nginx.conf
於綠框處( 在server{...}之後 )加入:
server_name
後替換成你的網域名稱
>加入以下
server {
server_name blade8128.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
- 重啟服務
sudo systemctl restart nginx
- 重啟容器
docker restart 12985ae4c388
4.API測試
Postman測試,位址是 http:// 網域名稱 + 路徑
得到 "502 Bad Gateway",看起來是NGINX的問題,所以我們來查看LOG查看NGINX LOG,至NGINX的LOG路徑
cd /var/log/nginx/
sudo ls -ll
查看"error.log"
sudo tail error.log
>2024/03/18 11:02:40 [crit] 4661#4661: *1 connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: 111.82.65.173, server: blade8128.com, request: "POST /api/signin HTTP/1.1", upstream: "http://127.0.0.1:3000/api/signin", host: "blade8128.com
直接把"(13: Permission denied) while connecting to upstream, client"拿去google 從stackoverflow得知是SELinux的問題,輸入以下:
sudo setsebool -P httpd_can_network_connect 1
再次Postman測試,成功
在VM上安裝Certbot來獲取SSL(HTTPS)
本文使用Certbot來獲得免費SSL憑證
1.安裝Certbot
- 使用"yum install certbot-nginx"無法成功安裝Certbot,要使用這篇的方法來安裝
- 輸入以下
sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip
sudo /opt/certbot/bin/pip install certbot certbot-nginx
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
執行 Certbot 來設定https server,執行後會有幾個問題要答覆
sudo certbot --nginx
問題答覆依序是:
1.輸入信箱
2.Y
3.Y
4.按enter
可看到認證金鑰的檔案都建好了
sudo cat /etc/nginx/nginx.conf
可看到Certbot幫我們改好了NGINX設定檔SSL部分
- 重啟服務
sudo systemctl restart nginx
- 重啟容器
docker restart 12985ae4c388
2.Postman測試
- 位址是 https:// 網域名稱 + 路徑,請求成功
而原本的http就無法請求了
以上內容若有錯誤,歡迎來信blade8128ch@gmail.com
參考資料
【 Cloud 】於 AWS Ubuntu VM 安裝 MySQL
(20) A Step-by-Step Guide to Installing Docker on RHEL 9 Locally. | LinkedIn
Install certbot with nginx on Amazon Linux 2023 | by Eika Chiu | Medium
AWS EC2 上部署 Docker、Nginx、反向代理設定(https)使用紀錄; Deploy Docker, Nginx with https reverse proxy on AWS EC2 step by step.