關於 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

AWS Beanstalk 系列文章

沒有留言: