# 佈署完整應用程式至AWS上 ###### tags: `AWS` `NodeJs` `Github` `WebApp` `Deploy` [TOC] {%hackmd 2q2OKXhpSca0en6d0b1GEg %} ## 前言 最近剛好手上有一個專案要佈建Production版了,有鑑於之前佈署Dev環境的時候沒有仔細記錄,導致那時候雖然拼拼湊湊的把環境架好了,卻還是一知半解,所以這次希望可以好好寫一篇,讓未來的自己能夠有取回記憶的方法。 首先簡單描述一下完整的應用程式包含哪些部分,可以參考下圖 ```mermaid flowchart LR; db[("Database<br />(MySQL)")]; server(("API Server<br/>(nest js)")); web["Web<br/>(Next js)"]; admin["Admin<br/>(react static)"]; s3("Cloud Storage<br/>(AWS S3)"); classDef frontEnd fill: #7fbfff classDef strokeColor stroke: #000 style db fill: #98c379 style server fill:#ff7a7a style s3 fill: #eda35e class web,admin frontEnd; class db,server,web,admin,s3 strokeColor; server <---> db; web -->server; admin--upload-->s3; admin-->server; s3--notify-->server; web--get-->s3; ``` 可以看到前端會有兩個網頁,分別是給一般用戶看得Web,用Next js實作,可以支援SSR(Server Side Rendering),與給管理者看的Admin,用一般的React去做靜態網頁,他們都連到同一個API Server,再統一由API Server去跟Database溝通存取資料,有一個額外的功能是Admin要支援可以將依些靜態資源存到雲端上,並從Web那邊可以存取,所以額外添加了一個存貯空間。另外還有一些塗上沒有畫出來的,像是掛網域、SSL憑證...等等。 所以統整起來,要佈署這個應用上AWS,主要會需要以下幾個東西: - MySQL資料庫 - Host Nest js的主機 - Host Next js的主機 - Host React App的主機 - 雲端存貯空間 以下就來一項一項把他們建立起來吧! ## AWS EB(Elastic Beanstalk) 首先建一個虛擬機給我們的API Server,這裡要跑Node 14環境,我們會用到AWS的Elastic Beanstalk,登入到AWS中控台,再搜尋框找`EB`,就可以進到Elastic Beanstalk的主控版。 EB其實不是虛擬基本體(?),真正要跑我們NodeJs的是EC2(Elastic Compute Cloud),EB根據官方的說法,他可以自動地幫我們scale up,像是做load balance,根據當前不同的負載程度,多開不同數量的EC2,並同時管理App的健康程度,也可以將一組環境變數,同時套用到多台EC2,總之就是很方便(X) ![](https://i.imgur.com/GBE1Cab.png) <center class="description"> <small> Elastic Beanstalk主控台 </small> </center> 點右上角的`Create a new environment`來新增一個環境 ![](https://i.imgur.com/5bohK4A.png) 這邊因為要建API Server,所以選Web Server Environment,下一步就是設定一些名字,基本環境,平台這邊就選Node14,Code的話,等等會用別的方式從Github上匯進來,所以先選Sample就可以了 ![](https://i.imgur.com/QR1CiZz.png) 接下來我們要做更多設定,點`Configure more options`,進來後我們要設定兩個地方,Software和Capacity ![](https://i.imgur.com/2EOMImN.png) 再Software的部分,主要是先將environment variable打進去,這邊就要一筆一筆的從dev環境複製過來...好累...怎麼沒有匯出匯入功能。 ![](https://i.imgur.com/W8KnYaI.png) 再來是Capacity的部分,這邊我們要打開Load balanced,但網站上線初期應該不會有太多流量,所以Min跟Max都設1就好了,雖然說這樣其實就失去了Load balanced的功能,不過開這個還有另外一個目的,就是之後要<a id="eb-ssl" href="#掛載SSL憑證到Elastic-Beanstalk">掛SSL</a>,都選好了之後就可以按開始建立環境,之後大概等個5分鐘,環境就建好了 ## AWS RDS(Relational Database Service) ### 建立新DB 再來建Database,在搜尋框找`RDS`,進到頁面後點左側 `Database`就可以進到管理DB的Dashboard,這邊可以看到已經建立的DB的一些相關資訊,按右上的`Create database`開始建立新的DB ![](https://i.imgur.com/W1PwENi.png) 前面是一些基本設定,像是名字、引擎、登入帳密...等 ![](https://i.imgur.com/t8bjFtD.png) 在Instance這邊,一樣由於剛上線不會有很高的Data吞吐量,所以選擇了最小的DB instance,class要選Burstable,意思是一開始只有基本的性能,但可以依據當前的需求短暫進入爆發模式(?) ![](https://i.imgur.com/fHgwEi9.png) 而storage的部分,有3種type可以選,分別是 - General Purpose (gp2) - Provisioned IOPS(io1) - Magnetic 根據官方描述,io1主要是用在有大量I/O需求的應用上,而Magnetic則是已經過時(?)的方案,所以我們就選最一般性的gp2,空間大小也先配置為最小的20GB,並把autoscaling勾起來,日後如果資料變多,可以讓AWS幫我們自動增加大小 ![](https://i.imgur.com/4xCHQu8.png) 再來是連線的設定,這邊我們要設定讓剛剛建起來的API Server可以連到這個DB,由於安全性的關係,AWS中大部分的功能都會被放在不同的VPC(virtual private cloud)中,不同的VPC間彼此是不能互聯的,所以為了要讓EB可以access到DB(單押),這裡需要設定RDS跟剛剛建立的EB在同一個VPC底下 ![](https://i.imgur.com/WwSQW0R.png) 繼續設定connectivity,Public access預設是不能選yes的,這邊要等到建立好之後再進去設定,但根據Info裡面的說明,即便開啟了Public access,還是要通過所在VPC的防火牆設定,所以不是隨隨便便就能從外部連進來DB的(好安全),可是一開始我們會需要連進來Insert一些初始資料,所以為了要能夠從外部連線,我們要在Additional VPC security group這邊assign給他一個`publicAccessTODB`的group,這個group不是預設的,是之前為了dev環境連進DB所新增的,這邊我們可以先跳離開一下設定畫面,去建一個給production用的VPC security group ### 建立Public access的VPC security group 一樣在新分頁登入AWS,搜尋`VPC`進到VPC的主控版,底下會有一大堆可以設定的東東,找到Security Groups,點進去,從右上角新增security group ![](https://i.imgur.com/mVLVaU4.png) <center class="description"> <small>VPC security group的主控台</small> </center> ![](https://i.imgur.com/k9KUBQy.png) 這邊設定好group的名稱、描述、與上層VPC後,底下要新增一個Inbound rule,點Add rule,設定type為custom TCP,監聽的port是MySQL的3306,source可以設為Anywhere IPv4,這邊IP會被設定為0.0.0.0,即任何人都可以連線進來,當然還是要輸入帳號密碼,但這樣其實有點不夠安全,比較好的設定是選custom,然後輸入可以連線的IP,不過前提是要有固定IP,不然就是每次要連線的時候,再登入修改IP ### 完成RDS建立設定 建立好了group就可以回來繼續我們的設定,可以點connectivity區塊右上角的重整按紐,在不會把剛剛輸入清空的情況下,重新載入列表,就可以選取剛剛建立好的`database-public-access`這個group,接者設定一些Additional configuration,這邊可以設定初始的database名稱,還有開啟備份。 ![](https://i.imgur.com/gBqUI6Z.png) 按下create database後,他會跳回dashboard,但這時DB還沒建立起來,status顯示為creating ![](https://i.imgur.com/XdRW0Kn.png) 等個差不多10分鐘,就可以看到他的status轉成Available了,這時候點進去database-prod裡面,可以在底下看到它的endpoint ![](https://i.imgur.com/c6JiOWN.png) <center class="description"> <small>database的詳細設定資訊</small> </center> 但這時候我們還沒開public access,所以是沒辦法從外部連進去的,於是選起來我們剛剛建立好的DB,點下Modify,進去將public access打開,就可以快快樂樂的從外部連線進去了...嗎? ### 設定Subnet route table 當然沒有這麼容易!明明public access也開了,security group也加了,怎麼沒辦法連進DB呢?仔細看了官方的[故障診斷](https://docs.aws.amazon.com/zh_tw/AmazonRDS/latest/UserGuide/CHAP_Troubleshooting.html#CHAP_Troubleshooting.Connecting),原來是剛剛我們設定connect to EC2的時候,會同時幫我們建立一個subnet,這是屬於RDS與EB間的小圈圈,外人是沒辦法連進去的,為了成為第三者(X),我們還需要做一些額外設定。 先記住詳細資訊圖中的subnets有哪些,再到VPC的主控台底下進入Subnets的Dashboard,可以看到剛剛建RDS的時候一併加入的`RDS-Pvt-subnet-*` ![](https://i.imgur.com/7N3Ytq6.png) 點進去設定底下的route table,按Edit route table association ![](https://i.imgur.com/oiENNwp.png) 在上面的route table ID裡面應該會看到有一個主要的route table,點選它就會將0.0.0.0/0綁訂到我們的getway上了 ![](https://i.imgur.com/i0KtQrQ.png) 同樣把另一個subnet也設定好route table就可以從外部連進來DB了。 ## AWS Amplify Amplify是AWS一個比較新的服務(至少界面上看起來比較新XD),他主打簡單快速的建造、佈署、hosting、管理一個無伺服器(serverless)APP,但同時也支援單純的Host App,由於他的設定非常的簡單,也同時支援SSR或是單純靜態網頁的hosting,所以就選用這個服務來作為我們Nextjs與React App的平台。此外Amplify也可以直接連結到Github,比起以前需要自己寫Github Action,雖然這需要讓AWS在你的Github帳號上裝一些連動的功能,但簡化了CD(continuous deployment)很多。 自動佈署的部分等一下會設定要連動到哪一個branch,每當該branch有push新的commit時,就會自動觸發CD,更新APP的版本。為此,我們會需要在Github上面開一個新的branch。 ### 建立production branch (Github) 這裡先說個題外話,為了要分離開發環境,確保production的運作是沒有問題的,我們在開發的時候會有幾個主要的branch: - main(dev) - staging - production 在local開發完功能或修好bug的時候會提交PR到main,經過code review後就可以merge進main裡面了,這時候會主動更新dev環境的APP。一段時間就會將dev環境的code merge到staging中,這裡其實算是進production前的一個緩衝區,將這段時間內所開發的功能進行一次總測試,確認沒有任何bug之後就會將stagin的code推到production,在正式環境中開始提供這些功能。但多開一個環境就要多花錢(X),為了省錢省事(X),我們把dev拿掉,改為定期更新staging,當要發布新版本前,可以減緩開發新功能的branch,等確定staging都沒有問題後,再將它推到production branch,可以參考下圖。 ```mermaid %%{init: { 'logLevel': 'debug', 'theme': 'base' } }%% gitGraph commit branch featureA order: 1 branch featureB order: 2 commit checkout featureA commit checkout featureB commit checkout featureA commit checkout main merge featureA checkout featureB commit checkout main merge featureB commit tag:"v0.1.0" branch staging order: 4 commit checkout main branch featureC order: 3 commit checkout staging branch hot-fix order: 5 commit checkout staging merge hot-fix checkout featureC commit checkout main merge staging merge featureC commit tag:"v0.1.1" checkout staging merge main branch production order: 6 commit ``` 為了做到上述的功能,我們需要建立兩個新的branch,`release-staging`和`release-production`,建立branch本身沒什麼問題,在repo的branch管理頁面中按New branch就可以了。但為了防止我們不小心(?),傳了壞掉的code上去把它們用壞,所以必須要加上branch protection rule,來阻止直接推上production或staging的branch。 ![](https://i.imgur.com/IlT7fsK.png) 進到Settings這個tab,在左側導航列找到branches,可以看到如上的設定畫面,在branch protection rules那邊點選Add rule,開始設定保護規則。首先設定要保護branch的名稱,這邊可以用regex,我們就設為`release-*`,這樣就可以一次把rule設定到兩個branch上面,而底下的設定都不需要打開,這是確保這些branches不能被push。 ### 利用Github Action Deploy branches建好了,也保護起來了,但我們要怎麼把main上面的code推進去呢?在上述的保護規則下,是沒有辦法在本地端merge完在堆上去的,加上前面有提到我們要定時把main的code merge進staging,這裡就可以用到github action來幫我們完成這些事。 ![](https://i.imgur.com/SMEm9aV.png) <center class="description"> <small>Github Actions管理頁面</small> </center> 在Actions這個tab底下,可以看到我們建立的workflow以及每次執行的結果,要建立workflow有兩種方法,一個是直接在這個頁面點New workflow,然後用線上編輯器編輯,另外也可以在local端將workflow檔案放進 `_WORKING_DIR_/.github/workflows` 資料夾底下,再推上去,這邊只是簡單的建立一個merge的workflow就採用線上編輯器了。 #### Staging環境的CD 進到New workflow之後,會先有一個頁面讓你選擇模板,這邊就先按Skip this and set up a workflow yourself 跳過設定進入編輯器,這裡我們先來做CD-staging ```yaml # This is a basic workflow to help you get started with Actions name: CD-staging # Controls when the workflow will run on: schedule: - cron: "0 8 * * 2" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: merge-branch: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Merge main to staging uses: devmasx/merge-branch@master with: type: now from_branch: main target_branch: release-staging github_token: ${{ github.token }} ``` Github Action的設定檔都是yaml的格式,一開始可以定義這個action的名字,在`on`的部分,是設定這個action在什麼情況下會觸發,schedule是排程,以`POSIX cron syntax`來設定,從左到右每位數字分別代表: 1. 每小時的第幾分鐘 [0,59] 2. 每天的第幾個小時 [0,23] 3. 每月的第幾天 [1,31] 4. 每年的第幾個月 [1,12] 5. 每周的第幾天 ([0,6] 週日=0) 可以用萬用字元`*`代表該時段的所有值,這裡我們設定`0 8 * * 2`即是指每周二的早上8點(UTC)會執行這個action。這邊為了保留手動執行的彈性,也有另外設定了`workflow_dispatch`這個條件,這可以讓我們直接從actions page手動執行底下的jobs。 而在jobs設定的部分,可以指定工作名稱、要執行的環境(`runs-on`)、與該工作的每一步驟(`steps`),第一步驟我們用github官方寫好的package來checkout到main,第二步驟則是用其他人寫好的package將main這個branch merge到release-staging這個branch,其中的`github_token`是你repo的token,這個package需要用這個token才能執行merge的動作,不過它本來就存在於環境變數`github.token`中了,所以無需做額外設定。 ![](https://i.imgur.com/5fWY1zN.png) 編輯完成後,按右上角的Start commit,然後輸入commit訊息,選Commit directly to the main branch.就可以直接把這個workflow寫入到`.github/workflows/CD-staging.yml`底下了。完成後回到Actions page,選擇我們剛剛建立的workflow,因為有設定`workflow_dispatch`這個條件,所以會看到有Run workflow的按鈕選單出現,點開按Run workflow,就可以手動觸發我們上面設定的jobs了。 等一小段時間(真的很快),就可以看到workflow順利跑完,這其實也不會出錯,畢竟只是把一個branch merge到另一個而已,這時候到release-staging這個branch看他的commit,可以看到一個由 @github-actions github-actions\[bot\] 所推的commit,到這邊我們staging的CD在Github上的部分已經完成了,剩下的要去AWS設定。 ![](https://i.imgur.com/80UdzAC.png) #### Production環境的CD 與建立CDstaging的流程一樣,我們幫production也建一個merge的workflow ```yaml name: CD-production # Controls when the action will run. Workflow runs when manually triggered using the UI or API. on: workflow_dispatch # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: merge-branch: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Merge staging to production uses: devmasx/merge-branch@master with: type: now from_branch: release-staging target_branch: release-production github_token: ${{ github.token }} # Bump tag with minor version unless any commit message contains #major or #patch bump-tag: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: '0' - name: Bump version and push tag uses: anothrNick/github-tag-action@1.36.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} WITH_V: true ``` 這邊我們只有開啟手動執行的選項,另外在jobs的部分,除了merge-branch,還額外裝了別人寫的用來自動推版本號tag的package,他在每次執行的時候都會將minor的版號推進1,如過這次有任何的commit裡面含有`#major`則會推進主版號,若有`#patch`則是推進最後一位。 寫入main之後一樣可以在Actions page看到這個workflow,手動執行後,進到tag頁面,就可以看到他幫我們推進了副版號到v0.1.0 ![](https://i.imgur.com/R64Fs5e.png) 接下來就要回到Amplify那邊去設定要同步更新的branch。 ### 建立Next js App 一樣前往AWS console,搜尋Amplify來到他的主控台,點選New App,在選單中找到host web app,開始設定。一進來首先要設定這個APP要跑的code從哪裡來,我們在From your existing code這裡選github然後按continue ![](https://i.imgur.com/Xkf1i8M.png) 這時候如果還沒有再Github裝AWS的APP的話,他會跳轉去安裝授權,如果已經有AWS APP,他就會主動列處擬在Github那邊授權可以操作的Repo,選好Repo之後,底下的Branch選擇我們剛剛建立的release-production,接者下一步。 ![](https://i.imgur.com/xlfE3Nh.png) 這邊則是設定一些初始化的細節,前面有提到Amplify主要是hosting靜態網頁,但他可以透過lambda function在需要的時候去執行server的部分,這裡因為我們有用到SSR,所以必須要給他一個role去跑server的部分,底下的build and test setting是在Amplify抓完最新的code之後要怎麼build的指令,通常他都會從我們的package.json自動判斷,不太需要額外設定。 ![](https://i.imgur.com/KyVahYU.png) 底下打開Advanced settings,這邊我們可以預先將environment variable輸入,另外我們也可以指定Next.js跟Node.js的版本,下一步就是review所有設定,確定沒問題之後就可以save and deploy了。接下來Ampliyfy就會主動去我們githun上抓release-production這個branch的code下來,然後build,在deploy出去。 ### 設定Domain name feat. AWS Route 53 在App settings的Domain management管立頁面可以看到,現在我們的URL是一串Amplify隨機生成的亂碼,當然不能讓客戶用這奇奇怪怪記不起來的網址連入我們的APP,所以必須給他一個好聽(?)的網址。按右上角Add domain進入設定 ![](https://i.imgur.com/XQjkWmV.png) 如果之前有在Route 53設定過你的Domain,當點下Domain的欄位的時候,就會跳出可選的Domain讓你設定,輸入完Domain後按Configure domain,底下的sub-domain欄位就會跑出來,預設是會有一個root的domain,另一個是www開頭的domain,這邊我把www的subdomain拿掉,只留root的domain,每一列後面則是可以選要連結到哪一個branch,這個只有在該APP有連結到多個branch的時候才有意義,通常是在dev環境中想要先看看某個featurebranch實際上會跑怎樣才去設定的。 ![](https://i.imgur.com/ze7Hlcp.png) 按下Save後,可以看到AWS會主動幫我們去申請SSL憑證,然後回來設定SSL相關的config,可以說是真 • 一鍵安裝。而如果一開始沒有在Route 53設定過Domain的話可以參考下面一段,先去Route 53設定Domain。 #### 在Route 53設定Domain 搜尋Route 53到他的主控台,點選左邊的host zone,再按Create hosted zone,輸入你的Domain name然後下一步。 ![](https://i.imgur.com/iN7uO9d.png) 建立好host zone之後點進去在設定record的地方,這邊可以看到很多的records,那些route到cloudfront.net的record就是Amplify幫我們建立的,可以把那些URL導到Amplify放靜態網頁的地方。而第2列的NS Record是用來將DNS要求導向AWS的DNS Server。這邊可以提一下,當初這個Domain name是在Go Daddy買的,他有隨附(?)他們的DNS server,所以AWS這邊生成之後要再將這些DNS server的網址填到Go Daddy那邊。其他的CNAME大部分是用來驗證你擁有這個Domain。 ![](https://i.imgur.com/QyIMZs6.png) 基本上Route 53設定好的話在Amplify那邊就可以直接選取Domain了 ### 建立React App 其實這部分跟前面完全一樣,就是在Github開新的Branch,然後回到Amplify,建立新的APP,連結到repo下的release-production branch,完成後設定Domain就可以了。 ## AWS CodePipeline 到這邊,我們前端的網頁都已經成功佈署了,也都會根據github上release的branch去自動更新(CD),但API Server的部分還沒有辦法主動從github那邊抓到code來更新。這邊就可以用到AWS的另一個功能 --- CodePipeline,他可以連結到Github,然後主動抓某個branch上面的code下來,然後Deploy到EB上,其實就很像前面Amplify幫我們做的事,雖然也可以用github action做到鑄件事,但既然AWS有提供的話就還是用吧XD 一樣搜尋code pipeline到他的主控台,在左側選單找到pipelines然後點create pipeline開始設定 ![](https://i.imgur.com/sXZQlGX.png) 第一步輸入完名字後,底下需要指派一個role給pipeline去執行fetch和deploy的動作,這邊因為之前已經有一個role給dev了,所以就直接用Existing service role,如果還沒有任何相關的role,也可以選New service role讓他幫你新增。 ![](https://i.imgur.com/wf8KZkB.png) 第二步是連接source,這邊選Github(version 2),接著選以連接的github 帳號,如果還沒連接,也可以點旁邊的connect to github,讓AWS在你的github帳號上裝一些APP已取得授權。然後選API的repo和release-production這個branch。 第三步的 build stage可以跳過,因為我們code clone過來,EB就會幫我們build好啟動了。 ![](https://i.imgur.com/eRm2iwS.png) 第四部選擇deploy的目的地,這邊就選一開始我們在EB中開的production環境與APP,下一步review完設定後就可以順利create pipeline。 建立後稍微等一下就可以看到pipeline成功把source的code deploy到EB上了,這時候回去EB看,可以發現中間的Running Version已經變成`code-pipeline-...`的版本,代表我們成功把release-production上的code放進EB中了,之後branch如果有任何變動也會觸發pipeline deploy。 ## 掛載SSL憑證到Elastic Beanstalk 接下來就是要把我們的Domain與SSL憑證掛到EB上的API server,在Amplify那邊,可以直接在domain設定那邊設定好domain,他會直接幫我們建立route 53上的record與掛上SSL憑證,但是在EB這裡沒有這麼方便,需要手動設定。 首先來到route 53這邊,在我們domain name的host zone底下新增一筆record。設定api server的URL,Record type選A,底下的traffic原本只能輸入要導向的ip位置,但把alias勾起來之後,就可以直接設定導向到EB底下的環境。 ![](https://i.imgur.com/iX3EwBs.png) 再來設定SSL憑證的部分,正如[前面](#eb-ssl)講的,我們現在開load balancer主要是為了掛SSL上去,所以進到我們production EB的環境中,在左邊Configuration的設定頁面中,找到Load balancer,案edit開始編輯。 ![](https://i.imgur.com/5WEtL8D.png) 在最上面設定listeners的部分,增加一個監聽443port,且protocal為https的listener,並把流量導向default process,也就是在port 80上的API server,新增完成後記得按底下的Apply套用設定。 ## AWS S3(Simple Storage Service) 接下我們要開一個可以上傳靜態資源的雲端存貯空間,搜尋S3到他的主控台,按create bucket,輸入名字與選擇地區後,需要把底下的public access打開,因為這些上傳的靜態資源(e.g.: 圖片)需要可以從外部存取到,記得底下的warning要勾起來。 ![](https://i.imgur.com/nTblPOA.png) 建立好後點進去到permission的標籤底下,先編輯Bucket policy,這是用來設定bucket中那些資源可以被public access,設定如下: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPublicRead", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "${arn}/public/*" } ] } ``` 其中`${arn}`的部分要換成你的bucket的arn,這讓所有在`/public`資料夾底下的檔案都可以被public read,接者設定底下CORS的部分,因為S3沒有設置網域,到時候的存取不管是從前端還是admin,都是跨網域的,所以要把CORS給開起來,設定如下: ```json [ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] ``` ## AWS Lambda > Lambda 是一項運算服務,可讓您執行程式碼,而無需佈建或管理伺服器。 以上是來自官方的說明,那我們這邊要用到lambda的部分主要是希望當有檔案上傳到S3的時候,可以主動通知API server去更新DB的資料。一樣到Lambda的主控台然後Create function。基本設定如下 ![](https://i.imgur.com/TXXw8Y6.png) 建立好function之後,點Add triger,添加觸發function執行的條件,source選S3,bucket選剛剛建的assets bucket,底下的event type選put,設定好後按Add,這樣有任何檔案上傳到S3的時候就會觸發lambda function執行了。再同樣設定一遍All object delete的trigger。 ![](https://i.imgur.com/yvAAo1Z.png) 接者點到Code的tab底下,輸入以下程式碼: ```javascript const https = require('https'); const postData = (url, type, key) => new Promise((resolve, reject) => { const data = JSON.stringify({ type, key }); const options = { hostname: url, path: '/aws/notify/s3', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': data.length } }; const req = https.request(options, res => { console.log(`statusCode: ${res.statusCode}`); resolve(); }); req.on('error', error => { console.error(error); reject(); }); req.write(data); req.end(); }); exports.handler = async (event) => { // Get the object from the event and show its content type const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const type = decodeURIComponent(event.Records[0].eventName); await postData(process.env.API_URL, type, key); return key; }; ``` 其中`export.handler`所assign的function即是trigger觸發時所執行的function,trigger會傳一個event給它,裡面包含了event type(put, delete),與key(即檔案路徑),這邊我們額外寫了一個function去post這些資料給API server。 存檔後,記得按Deploy,再去Configuration底下的Environment variable中設定API_URL就可以了。這邊小小的紀錄一下踩到的雷,再用https(或http)這個module的時候,他hostname那邊是不可以有協定名稱的,即,不能是https開頭,結尾也不可以是`/`,這邊弄了好久想說怎麼server一直沒有收到通知,去跑了lambda內建的test才發現有error。