2020年7月15日 星期三

AWS CloudWatch Synthetics

原本想用 DataDog 的 Sythetics API Test 做 e2e monitoring & alarm 但是遇到 multi-part form-data 沒辦法在 Request Body 使用 \r\n 的問題

curl -i -X  --trace-ascii - -F 'key=key1' -F 'key2=key2' https://whatever.totest.com/auth

你可以看到送出的 Content-Length 和 Request Body 是

Content-Length: YYY
Content-Type: multipart/form-data; boundary=xxxx
--xxxx
Content-Disposition: form-data; name="key1"
key1
--xxxx
Content-Disposition: form-data; name="key2"
key2
--xxxx--

但是使用 DataDog 送出去的話只會用 \n 和 curl 送出的一比較,就發現 Content-Length 比較小,但是 DataDog 似乎沒辦法改..

試了一下 AWS CloudWatch Synthetics Canary 發現他其實就是 deploy 一個 nodejs lambda layer 裡面執行 Puppeteer, Chromium 和 AWS SDK 但是又整合了 cloudwatch dashoboard, cloudwatch logs 和 cloudwatch alarm,所以不只可以跑 script 還可以透過 chromium 去抓網頁的 screenshot 彈性超大!

另外一個好處是可以編輯要送出去的 postData,一開始我看他預設也是送 \n 會造成 invalid request 但是只要改成 \r\n 就好囉!



2020年7月12日 星期日

Publish lambda layer to regions from s3 bucket?

boto3 有個 publish_layer_version API,你可以使用 S3 bucket + key or zipfile 來發布 lambda layer.

使用 zipfile 的話就是從命令執行端上傳,所以假設你的 lambda packet 是 20MB 然後你要發佈到 16 個 AWS region 的話,就看你命令執行端的網路上傳速度了。

使用 S3 bucket 來放要發佈的 zipfile 檔似乎比較合理,因為這樣子檔案的複製就是在 AWS 的網路內發生。

But... 很機車的是,如果你的 zipfile 是放在 us-west-1 的 S3 bucket 那麼你就只能發佈到 us-west-1 .... 當你要發佈到其他 region 會出現 error
botocore.errorfactory.InvalidParameterValueException: 
An error occurred (InvalidParameterValueException) when calling 
the PublishLayerVersion operation: Error occurred while GetObject. 
S3 Error Code: PermanentRedirect. 
S3 Error Message: The bucket is in this region: us-east-1. 
Please use this region to retry the request

S3 bucket name 是 global unique 但其實 bucket 位置是有 region 的...
目前還沒有看到什麼比較好的方式來發佈 lambda layer 到全部的 region.... replicate 所有的 zipfile 到所有的 region, 然後每個都有自己的 bucket name 似乎蠻蠢的?....

Application Load Balancer lambda endpoint healthy check will be charged

AWS 還是有蠻多坑的...

如果 ALB 的 TargetGroup 使用 lambda endpoint
那麼為了避免 code start issue 可能會使用 provisioned concurrency

另外 lambda endpoint 在 update stack 的時候會出現 Load Balancer not able to stabilizied 的問題。AWS support 目前給的work around就是開啟 ALB healthy check
預設是每 35 秒會做一次 healthy check 然後如果 ALB 跨3個 AZ 的話,healthy check count 就 x3 ....
然後每次的 lambda execution 都是照 lambda function usage 來收費

另外ALB的 healthy check 不會有完整的 request header
如果你的 framework 不預期有這種不正常的 header 沒有去 handle 的話 可能會一直狂噴500...
如果你還沒有把預設的 lambda error retry 關掉的話......
這樣子用 lambda endpoint 真的比較省嗎?....


2020年7月10日 星期五

Access private API gateway from another account

最近在做一個 PoC 驗證說怎麼在現有的架構下,把一些 public API 移到 VPC 裡面,只讓特定網路的人可以存取。

當然最直覺的想法就是,建一個新的 VPC ,加上 execute-api VPC endpoint 然後這個 VPC 再跟特定網路做 Peering,建一個 Private API Gateway 在新的 VPC 裡面。

這樣新的 VPC 可以透過 VPC endpoint 去存取 API Gateway,然後再加一個 ALB ,裡面的 target group 指向 VPC endpoint 的 IP和 443 port,ALB 設定好 Domain Listener,API Gateway 也加上相同的 Custom Domain Name,這樣子 ALB 就可以當成特定網路 access 的 entry point,一但 Peering 完成後,從特定網路來的 request 就可以經由 ALB -> API GW 跨 VPC 存取原本環境的 backend resource。

但我一直在想,Peering 是必要的嗎? 沒想到隔天就看到這個教學 How can I access an API Gateway private REST API in another account using an interface VPC endpoint? 裡面的做法突破了原本我直覺上的盲點! 就是即使是 Internal API Gateway 似乎只要設定好了 resource policy 就可以允許其他 account 的 VPC endpoint 跨帳號存取!

所以例子裡面的 private API Gateway 是建立在 B 帳號裡面,A 帳號的 VPC 只是啟用了 VPC endpoint,一但 B 帳號的 private API Gateway 在 resource policy 設定好允許 A 帳號的 VPC endpoint 存取,即使兩個帳號之間沒有 peering 也是可以互通的!

雖然說這樣子要帶 hostname header or api-gw-id 但是 原本架構上的 ALB 也是為了 VPC endpoint 可以知道要 forward request 給哪個 API Gateway 才加上的... 

但至少是一個不想要建 VPC Peering 的選項之一.... :p

Reference:


2020年7月2日 星期四

CloudFomration and serverless lambda deployment

幸運的有 AWS::Serverless::Function 可以使用,不然想到要用 Custom Resource 頭就痛。原生的 AWS::Lambda::Function 如果要使用 Provisioned Concurrency 就必須使用 Version/Alias,但是 Version 的 property 全部都是不能更新的!所以接下來 lambda function 的更新,都不會讓 Alias 指向新的 Version....
還好透過 Transform 的宣告後,啟用AutoPublishAlias: live 再加上小部分 property 的修改,CloudFormation 就可以自動地幫我們管理 Version 和 Alias 啦。

不過還踩到另外一個小雷是,預設 Application Load Balancer 的 Target Group 如果是指向 lambda 的話,healthy check 是關閉的。但是在這種情況下,你 update stack 就會遇到 "ElasticLoadBalancerV2 TargetGroup did not stabilize" ,很厲害的 AWS Support 就給了一個 workaround ,把 healthy check 打開就好了 .... lol

Ref:

2020年6月27日 星期六

Safety Razor Murker 38C and Edwin Jagger DE89

繼入手第一隻 Murker 38C 傳統式刮鬍刀後,現在每天都很期待刮鬍子。還收集了很多刀片 Astra / Derby Extra / Derby Premium,然後又手滑買了第二隻,也是幾乎都會推薦給新手使用的 DE89 ,比較常聽到的名字可能是 Muhle R89 根據我看到的說法是,Muhle 和 Edwin Jagger 共同開發這個刀頭,所以只有兩家各自出的手柄握把不一樣,刀頭都是相同的。那為什麼選 Edwin Jagger 呢?因為 Amazon CA 只有賣 Edwin Jagger ... Orz...

Edwin Jagger 的刀頭設計和 38C 一樣雖然也是 close combat 但他的刀片露出就比 Murker 38C 多一些。刮起來也是很順暢,但是第3刮的 Against The Grain 就噴血了..lol...不知道是刀片太利了還是 DE89 攻擊性比較強.... 但也有可能是我技術太差...

所以又換回 38C 刮刮看,確認 Murker 38C 真的比較溫和,但似乎不像 DE89 可以刮得很乾淨。現在都會乖乖用 pre shave cream 這樣子容錯率比較高...

有了兩隻比較以後,已經可以分辨出,刀頭設計,二件式,三件式,握把長短,握把重量等等的差異。像 38C 就屬於長握把約 10cm,重量約 100g 。 DE89 就是一般的短握把,約 8cm,重量也較輕。但這兩款的確都很適合新手,所謂的噴血也只是有小血珠冒出,不是割痕,用鬚後水塗完就沒感覺了。

現在慢慢體會到傳統濕式刮鬍的魅力有一部分是刮鬍刀本身設計很漂亮,刀片有很多選擇,不會卡鬍渣,刮鬍手感很棒。另一部份就是塗 pre shave cream,從刮鬍皂用刮鬍刷打出刮鬍泡塗在臉上,並沒有想像中的麻煩,現在反而有點期待這整個過程。

這是還沒用過傳統刮鬍刀前我完全沒想到的! 繼續收集研究刮鬍刀和刀片,還有各種不同的刮鬍皂/膏…刮鬍子現在變的很有趣 :)

Lambda version with provisioned concurrency in CloudFormation update stack fail

如果在 CloudFormation 裡面直接使用 "AWS::Lambda::Version" 加上 ProvisionedConcurrencyConfig 接著 update stack 更新 ConcurrentExecutions 就會出現 update error 因為 version 不能夠 update

但是如果把 ProvisionedConcurrency 放在 "AWS::Lambda::Alias" 裡面,update stack 卻又可以了... lol...

不過一個 provisioned concurrent lambda 一個月要額外多 $5 USD 的費用還不包含 lambda execution 的費用,一個 1 vCPU + 512MB 的 fargate container 也不過大概是 $31 USD ....

Update:
再仔細看了一下,update stack 失敗的原因有兩個一個是因為 Application Load Balancer Target Group 指向的 lambda 無法 stabilized 所以 update 失敗導致 rollback。其中一個原因是因為 ALB 沒有權限 invoke lambda function。LambdaPermission resource 還沒執行時,就先執行了 ALB TargetGroup 設定,所以在 TargetGroup resource 加了 DependsOn LambdaPermission 後,就少了 一個錯誤,但剩下的 ALB 無法 stabilized 的錯誤目前還是無解.... 只能把 stack 刪掉重新 deploy,接下來的 update 好像就都沒事。只是說在刪除 stack 的時候,會出現等待 lambda ENI 刪除的 waiting... 要等 8 ~ 20 min? 據說是後來 aggregate ENI 給 VPC 內的 lambda 後導致的 side effect....
CloudFormation 印象中也是越來越聰明,當 Resource A 有 !Ref Resource B 的時候,就會自動做 DependsOn 調整執行順序,但是像今天這個 LambdaPermission 和 ALB TargetGroup 似乎就漏掉了...

ALB + lambda + provisioned concurrency 很多 bug 一次踩好踩滿 Q_Q...


2020年6月13日 星期六

傳統式刮鬍刀

一直以來都是用吉列三刀片/五刀片刮鬍刀

想不透有時候買整組刀架附刀片可能還比單買刀片便宜
因為覺得刀片蠻貴的,所以都拖蠻久才換一次
多刀片的設計,刀片和刀片之間也特別容易卡鬍渣

後來看到傳統式刮鬍刀,很多人都推薦 Merkur 34C
我在 Amazon 剛好看到有 Merkur 38C $69 CAD 所以就選他了
ASTRA 100 片 雙面刀片  $16 CAD

刮了幾天,目前很喜歡刀片抓住鬍鬚那種喀喀喀的聲音,
而且刀片用了三四天後還可以翻面再用,因為我鬍子長的範圍不多,
目前一片刀片大概可以用一個星期左右,還是分不太出來鈍了的感覺是什麼,可能要再多試試看

原本都是用噴罐式刮鬍泡,現在才知道原來還有刮鬍皂
用起覺得還不錯,推薦可以試試看,沒有想像中可怕,
感覺和用吉列時差不多安全,但多了刀片抓住鬍鬚的爽感
還有可以自由的換刀片!!

後來又買了 Derby Extra 和 Derby Premium Double Edge 要來試試
刀片便宜到爆阿阿阿!!




AWS Autos Scaling Group termination policy

今天遇到了一個有趣的問題,是有個 build job 說他的 build node 會被隨機 terminate 掉。

大致去看了一下 Auto Scaling Group 的設定,當 idle node > 1 的時候, ASG 就會開始 scale in ,當有 build queue 不為空的時候,就 step scaling out
研究一下他的做法是在 jenkins cronjob 每分鐘去 query jenkins api 回報 build queue 和目前狀態是 idle or offline 的 build node number 去 CloudWatch metrics

然後 CloudWatch metrics 再去觸發 ASG 的 scale in or out

這時候我想到的問題是,那當 scale in 被觸發時,是哪台 instance 要被 terminate?
當初的設計很聰明,他利用了 lifecycle hook,用意是說當 build node 被選上要被 terminate 時,會進入 Terminate Wait 的狀態,直到 ASG 收到 complete-lifecycle-action 的指令時,才會真的 terminate 掉。

但這個 lifecycle hook 是有預設 timeout 時間 3600 sec !!  也就是說一但 instance 進入 Terminate Wait 即使你沒有用 cron job 去 complete-lifecycle-action 過了一小時 waiting 時間它還是會被關掉....

原本我以為 ASG 應該會挑 CPU 閒置的機器下手吧?後來再去翻一下文件發現, default termination policy 的策略依序是盡量讓機器分散在不同 AZ >  Allocation Policy > Oldest Launch Template or Config 最後是開機時間最接近整數小時的機器。

其他可以選的 termination policy 也都不是根據 instance usage 來判定。

原本因為不想花太多時間,只是暫時先把 timeout 時間設長,讓他有機會即使不幸被選上,還是可以把 build 跑完。

但再翻一下文件,或許可以把 busy build node 透過 cron job 送 record-lifecycle-action-heartbeat 或者是怕 timeout 太長,就等 build 跑完,再補送 complete-lifecycle-action 就好了...


Ref:

https://docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html

https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-instance-termination.html

2020年6月10日 星期三

Mount sequence in fstab systemd with aufs

接手了一台 Jenkins server 他的Infrastructure as Code設計概念很棒
他用 aufs 來掛載 EBS data volume,AMI 的 rebuild 是透過 ansible 和 yaml 來設定,再透過 CloudFormation 自動 deploy

所以要更新的時候只要 commit 更新後的版本號碼進去 repo 就會有一個 fully patched AMI,然後再把 EBS volume 用 overlay fs 掛載到新的 /var/lib/jenkins 目錄

/etc/fstab 是這樣寫的:
LABEL=cloudimg-rootfs / ext4 defaults,discard 0 0
LABEL=jenkins-ebs-fs /mnt/jenkins ext4 defaults,nofail 0 2
none /var/lib/jenkins aufs br=/mnt/jenkins=rw:/var/lib/jenkins=ro,udba=reval 0 0

但長久以來一直有個問題是,每次重新開機後,/var/lib/jenkins 的目錄並沒有正確的疊上 EBS data volume 裡的資料

所以 workaround 都是 umount /var/lib/jenkins 再 mount -a 卻又好了

最近又要更新 Jenkins ,實在是很討厭每次都要手動進去把 fs 重新 mount 起來

原本以為是 jenkins service 啟動時間和 overlay fs 是不是有 in-use lock 之類,或者是 aufs.ko 載入的時候和 fstab 先後的問題

無意間看到有人說 /etc/fstab 的載入順序不保證是按照設定檔裡寫的順序的!!

df -h 一看,發現第一次開機有問題的 mount /var/lib/jenkins 比 /mnt/jenkins 還前面

mount -a 正常的版本是 /mnt/jenkins 會比 /var/lib/jenkins 早
(其實以前也一直沒注意 df -h 的顯示順序)

最後只要加上 x-systemd.requires=/mnt/jenkins 就好囉!


Ref:
https://github.com/systemd/systemd/commit/3519d230c8bafe834b2dac26ace49fcfba139823