關於 web service, unity, blogger 等軟體工程筆記

AWS Beanstalk Go WebApp 部署筆記

Edit icon 沒有留言
Go and AWS

記錄前陣子將使用 Go 所寫的網路服務 (Restful API server),部署到 AWS Beanstalk 的筆記記錄,讓未來部署除了官方文件外,還有一個參考筆記。

為什麼採用 AWS Beanstalk 佈署

這得先提伺服器部署模式,如下圖所示,最左邊是偏 Infrastructure as a service (IaaS),從最基礎的 VM 開始架設服務,彈性最高,但得自行設定環境 (Environment)。最右邊是偏 Platform as a service (PaaS),部署只關心服務程式,環境設定托給系統服務商處理。或是中間使用容器技術 (Container) 技術來發布服務。關於更多 IaaS 以及 PaaS 的描述,可參考筆記末的 Reference。

原先我們的服務環境也是自己管理設定,租用 EC2,安裝服務執行環境,設定 LoadBalancer,建立 Amazon image 以設定 Autoscaling group……。後來發現我們沒有能力以及人力,去管理日漸龐大的系統。因此開始研究 Beanstalk,只需要關心服務程式碼,剩下的都交給 AWS 協助管理。

Amazon IaaS & PaaS 架構所使用的服務

Amazon IaaS & PaaS 架構所使用的服務

建立 Source bundle 部署到 AWS Beanstalk

從 AWS 官方文件中,了解要佈署到 Beanstalk 有幾種方式,根據需求將服務資源包成 Source bundle 後上傳,接著用其 Beanstalk 佈署到環境即可。主要根據這兩條需求而有四種包法:

  • 是否為複雜的服務?(需要執行額外的程序,或者需要調整 Beanstalk 環境)
  • 使用 Binary 佈署嗎?

可以畫成以下的流程圖來決定:

AWS Beanstalk 包裝應用服務考慮流程

AWS Beanstalk 包裝應用服務考慮流程

紀錄其中複雜環境上傳程式碼的打包佈署方式,需要打包 Beanstalk 環境設定檔 (/.ebextensions)、程式碼、程式碼編譯命令 (BuildProc)、以及服務啟動命令 (Profile)。整理打包成 app.zip source bundle 的資料結構如下:

- .ebextensions/
  - applogs.config
- src/
  - app/
    - main.go
    - vendor/
- Procfile
- Buildfile
- build.sh
  • .ebextensions 資料夾內放置所有的環境設定檔,Beanstalk 環境會根據其 yaml 設定,建立或是修改指定檔案的內容
  • .ebextensions/applogs.config,建立 Log 設定檔,進行 S3 Log rotate,將本機端的 Log 複製到 S3 空間上,要注意的是寫入 os.Stdout 以及 os.Stderr 的內容,會分別存放在 /var/log/web-1.log 以及 /var/log/web-1.error.log 這兩個檔案中
    files:
       "/opt/elasticbeanstalk/tasks/publishlogs.d/applogs.conf" :
       mode: "000755"
       owner: root
       group: root
       content: |
          /var/log/web-1.log
          /var/log/web-1.error.log
    
  • src/app/,放置建置網路服務程式的程式碼
  • src/app/vendor/,放置所有使用到的相依 package 程式碼,可以以下使用工具進行管理 govendor, dep, godep, or glide
  • ProcFile,當環境準備好後的啟動服務命令
    web: ./app
    
  • Buildfile,環境準備的建置命令
    make: sh build.sh
    
  • build.sh,主要的 Go 程式碼建置指令,$APP_STAGING_DIR 是佈署服務的暫存位置
    export GOPATH=$APP_STAGING_DIR
    go build -o app -v app
    

當這份 source bundle 上傳到 AWS Beanstalk 環境並執行佈署時,會依序執行步驟:

  • .ebextensions
  • Buildfile
  • Procfile

關於這份 Go 佈署 template 已上傳 Github [twsiyuan/aws-beanstalk-go-deploy-template] 上。

關於網路 Port 監聽選擇

Beanstalk 環境會有設定,應該監聽哪個連接埠 (port) 才能服務正常運作,從環境參數 Environment 取得:

addr := ":" + os.Getenv("PORT")
http.ListenAndServe(addr, handler)

若是開發本機端測試,可以考慮加入從 command-line flag 或是 config file 讀取:

port := flag.String("PORT", "8080", "listening port")

flag.Parsed()

if sysPort := os.Getenv("PORT"); len(sysPort) > 0 {
   port = sysPort
} else if cfgPort := fromCfgPort(); len(cfgPort) > 0 {  // TODO: fromCfgPort
   port = cfgPort
}

addr := ":" + os.Getenv("PORT")
http.ListenAndServe(addr, handler)

關於網路架構以及 Remote Addr

若佈署的是 Web Go application,要特別注意的是 Remote Addr 取得,不再能以下那樣的方式來取得遠端用戶的 IP 位置,這將取得 Nginx 的伺服器在本機端的位置 127.0.0.1 :

func h(w http.ResponseWriter, req *http.Request) {
   println(req.RemoteAddr) 
}

這是因為改用 AWS Beanstalk 後,Web app 不再直接面對客戶端的連結,網路服務架構被調整如下圖:

網路服務架構示意圖

網路服務架構示意圖

Ngnix 會負責轉傳 Load Balancer 來的要求給 WebApp,因此從 headers X-Forwarded-For 拿到其要求的原始來源位置:

req.Header.Get("X-Forwarded-For")

它會是一長串的 IP address,例如以下示意:

cleint-ip-address, load-balancer-address

如果用戶端本身使用 proxy 代理連線,LoadBalancer 也會轉傳 proxy 所帶的 X-Forwarded-For 資訊,那麼 ip address 可能以下,其中 ip-address-3 為最後的代理伺服器位置,ip-address-1 是可能是用戶端的實體位置:

ip-address-1, ip-address-2, ip-address-3, load-balancer-address

Reference

沒有留言: