在AWS EC2上執行Express專案


需求描述:原本的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即可
  • 建立使用者,user01pass1234分別為帳號跟密碼,可設定為自己要用的
    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;
    

    輸入exitMySQL

使用 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.

#docker #linux #ec2 #nodejs #nginx #Certbot






你可能感興趣的文章

Reactive Programming 簡介與教學(以 RxJS 為例)

Reactive Programming 簡介與教學(以 RxJS 為例)

容器化的概念

容器化的概念

SQL-injection lab(3)

SQL-injection lab(3)






留言討論