戰(zhàn)指南:從EC2到Lambda的云架構(gòu)落地與避坑)
簡介這是一部系統(tǒng)講解Amazon Web Services的實(shí)戰(zhàn)指南第三版面向云計(jì)算工程師、架構(gòu)師及開發(fā)者適合希望從零搭建云架構(gòu)、快速上手AWS核心服務(wù)的讀者。書中從計(jì)算、網(wǎng)絡(luò)、存儲(chǔ)到部署與安全管理覆蓋EC2、Lambda、VPC、CloudFormation、IAM等常用服務(wù)并給出了服務(wù)速查表與章節(jié)索引方便按業(yè)務(wù)場景快速定位同時(shí)配有可運(yùn)行的練習(xí)代碼和架構(gòu)示意圖幫助讀者在接近真實(shí)的環(huán)境中理解無服務(wù)器、容器編排、基礎(chǔ)設(shè)施即代碼等重點(diǎn)技術(shù)。書中還演示了如何組合這些服務(wù)構(gòu)建高可用、可擴(kuò)展的應(yīng)用架構(gòu)并給出成本估算與安全合規(guī)方面的實(shí)用建議。資源為單個(gè)PDF文件大小約35.27MB包含完整目錄與英文正文目錄層級(jí)分明適合按需精讀或系統(tǒng)學(xué)習(xí)。目前已有73人學(xué)習(xí)瀏覽內(nèi)容兼顧概念講解與動(dòng)手實(shí)踐既可作為AWS入門路線圖也能為中級(jí)開發(fā)者補(bǔ)齊自動(dòng)化部署、網(wǎng)絡(luò)安全組配置、成本控制等實(shí)踐細(xì)節(jié)是一份高信息密度的英文原版參考手冊(cè)。1. Amazon Web Services 在 2023 年這本英文實(shí)戰(zhàn)書到底教了什么拿到《Amazon Web Services in Action 3rd Edition》這個(gè)標(biāo)題大部分人的第一反應(yīng)是又一本 AWS 官方手冊(cè)我剛開始也這么以為直到照著目錄走了一遍才發(fā)現(xiàn)它跟你想象中的“文檔合集”完全是兩碼事。第三版更新了 2023 年前后 AWS 主推的服務(wù)形態(tài)從容器編排到無服務(wù)器再到基礎(chǔ)設(shè)施即代碼幾乎覆蓋了你在生產(chǎn)環(huán)境里真正會(huì)用到的那套東西。它不是給你背服務(wù)名而是帶著你從零把架構(gòu)搭起來——EC2、VPC、負(fù)載均衡、Lambda每一步都有可執(zhí)行的命令和配置。適合誰適合已經(jīng)厭倦了“看文檔都會(huì)、一上手就廢”的開發(fā)者也適合準(zhǔn)備從傳統(tǒng)機(jī)房遷移到云上、但不知道第一腳該往哪兒踩的運(yùn)維。讀這本書的正確姿勢不是翻是照著敲。你能獲得的不是知識(shí)點(diǎn)是一條完整的、踩過坑的落地路徑。2. AWS 動(dòng)手前的三件大事賬號(hào)體系、預(yù)算邊界和區(qū)域選型2.1 root 用戶和 IAM 的坑別用根賬號(hào)跑日常操作很多人注冊(cè)完 AWS 就拿著 root 賬號(hào)一路點(diǎn)下去圖省事。這在個(gè)人實(shí)驗(yàn)環(huán)境里問題不大一旦進(jìn)入團(tuán)隊(duì)協(xié)作或生產(chǎn)環(huán)境root 賬號(hào)的權(quán)限大到?jīng)]有后悔藥——API key 泄露、誤刪資源、賬單爆炸全都跟它有關(guān)。第三版里強(qiáng)調(diào)的第一條安全實(shí)踐就是用 root 賬號(hào)只做一件事——?jiǎng)?chuàng)建 IAM 用戶然后鎖起來。# 用 AWS CLI 創(chuàng)建 IAM 用戶需要先配置好 root 的 access key aws iam create-user --user-name deployer # 給用戶附加托管策略這里是管理員權(quán)限實(shí)際生產(chǎn)建議按需給最小權(quán)限 aws iam attach-user-policy \ --user-name deployer \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess # 創(chuàng)建訪問密鑰輸出里的 AccessKeyId 和 SecretAccessKey 要保存好只顯示這一次 aws iam create-access-key --user-name deployer # 配置本地 CLI 使用該用戶 aws configure --profile deployer這段命令的邏輯是先建人再授權(quán)再發(fā)鑰匙。attach-user-policy掛的是 AWS 托管策略省去自己寫 JSON 權(quán)限文檔的麻煩但 AdministratorAccess 這種權(quán)限在生產(chǎn)環(huán)境要謹(jǐn)慎它意味著這個(gè)用戶能刪掉整個(gè)賬號(hào)下所有資源。實(shí)際項(xiàng)目中我一般會(huì)給不同的角色拆細(xì)粒度策略比如給 CI/CD 系統(tǒng)只掛AmazonEC2FullAccess和AmazonS3FullAccess不給全量權(quán)限。還有一個(gè)容易踩的細(xì)節(jié)IAM 用戶創(chuàng)建的 access key 不會(huì)二次顯示關(guān)閉終端就等于永久丟失只能刪了重建。所以保存密鑰這件事值得你用密碼管理器而不是記事本。2.2 預(yù)算告警讓 AWS 在你破產(chǎn)之前先通知你AWS 計(jì)費(fèi)最大的特點(diǎn)就是后付費(fèi)資源開著就計(jì)費(fèi)關(guān)掉才停。新手最容易在 EC2 實(shí)例上翻車開了一臺(tái)m5.xlarge忘了關(guān)一個(gè)月下來幾百美金沒了。第三版里給的方案是 Billing Conductor 和 Cost Explorer其實(shí)最實(shí)用的還是 Budgets——它能做到接近實(shí)時(shí)的告警。# 創(chuàng)建月度預(yù)算金額設(shè)為 100 美元 aws budgets create-budget \ --account-id 123456789012 \ --budget { BudgetName: monthly-100, BudgetLimit: {Amount: 100, Unit: USD}, TimeUnit: MONTHLY, BudgetType: COST } # 配置告警閾值超過預(yù)算的 80% 就發(fā)郵件到你的郵箱 aws budgets create-notification \ --account-id 123456789012 \ --budget-name monthly-100 \ --notification {NotificationType: ACTUAL, ComparisonOperator: GREATER_THAN, Threshold: 80, ThresholdType: PERCENTAGE} \ --subscribers [{SubscriptionType: EMAIL, Address: youexample.com}]這段命令值錢的地方在于Threshold: 80這個(gè)參數(shù)——不要等 100% 才告警那時(shí)候你已經(jīng)超支了。我一般的做法是設(shè)兩道線80% 警告一次100% 再警告一次。另外NotificationType用ACTUAL表示實(shí)際產(chǎn)生費(fèi)用時(shí)觸發(fā)還有一種是FORECASTED基于預(yù)測觸發(fā)適合對(duì)成本敏感的場景。預(yù)算告警不是可有可無的配置它是你所有實(shí)驗(yàn)室操作的安全網(wǎng)。2.3 區(qū)域選型延遲、價(jià)格和功能三重博弈區(qū)域選擇是 AWS 實(shí)操里第一個(gè)真正影響架構(gòu)的決策點(diǎn)。us-east-1便宜功能全新服務(wù)基本首發(fā)就是它ap-southeast-1新加坡離國內(nèi)近延遲低但部分服務(wù)價(jià)格貴 10%-20%eu-central-1法蘭克福適合歐洲業(yè)務(wù)合規(guī)要求多。第三版里的建議是把區(qū)域當(dāng)成架構(gòu)參數(shù)而不是隨意選項(xiàng)。# 查看當(dāng)前區(qū)域的可用區(qū) aws ec2 describe-availability-zones --region us-east-1 # 查看某區(qū)域的具體價(jià)格以 t3.micro 為例 aws pricing get-products \ --service-code AmazonEC2 \ --filters [{Type: TERM_MATCH, Field: instanceType, Value: t3.micro}]describe-availability-zones告訴你有幾個(gè)可用區(qū)這在設(shè)計(jì)高可用架構(gòu)時(shí)直接決定你能跨幾個(gè)故障域。get-products接口查價(jià)格但注意它返回的是列表價(jià)格實(shí)際賬單還要考慮 Savings Plans 和 Spot 折扣。選區(qū)域沒有銀彈我的習(xí)慣是面向全球用戶選us-east-1面向亞太選ap-southeast-1如果業(yè)務(wù)有合規(guī)要求直接查 AWS 的區(qū)域合規(guī)白皮書再定。別在這個(gè)問題上花太多時(shí)間選錯(cuò)了后面可以通過多區(qū)域架構(gòu)遷移但成本是實(shí)打?qū)嵉摹?. 用 EC2 和 VPC 搭最小可用架構(gòu)命令行完整走一遍3.1 安全組設(shè)計(jì)出站入站規(guī)則里的大學(xué)問EC2 實(shí)例本身是個(gè)虛擬機(jī)但它的安全邊界完全由安全組Security Group決定。安全組是有狀態(tài)的——你允許了入站 80 端口那么從實(shí)例發(fā)出的響應(yīng)流量會(huì)自動(dòng)允許不用額外配出站規(guī)則。這是新手最容易忽略的點(diǎn)也是防火墻和它最大的區(qū)別。# 創(chuàng)建一個(gè)安全組指定 VPC 和描述 aws ec2 create-security-group \ --group-name web-sg \ --description Security group for web servers \ --vpc-id vpc-0a1b2c3d4e5f67890 # 允許 80 端口入站來源限定為本安全組即 SG 內(nèi)互相訪問 aws ec2 authorize-security-group-ingress \ --group-name web-sg \ --protocol tcp \ --port 80 \ --source-group sg-0a1b2c3d4e5f67890 # 允許 22 端口入站來源限定特定 IP——生產(chǎn)環(huán)境千萬別設(shè) 0.0.0.0/0 aws ec2 authorize-security-group-ingress \ --group-name web-sg \ --protocol tcp \ --port 22 \ --cidr 203.0.113.0/32這里有兩個(gè)關(guān)鍵設(shè)計(jì)一個(gè)是 80 端口的安全組嵌套——來源不是 IP 而是另一個(gè)安全組這樣后面新增實(shí)例只要掛同一個(gè)安全組就能互相訪問不用改規(guī)則另一個(gè)是 22 端口的 IP 白名單——用/32精確到單個(gè) IP而不是/0開放所有來源。安全組規(guī)則是即時(shí)生效的改完不需要重啟實(shí)例這在排查問題時(shí)幫了大忙。要注意的是規(guī)則數(shù)量上限——單個(gè)安全組默認(rèn)最多 60 條入站加 60 條出站超過就要拆分多個(gè)安全組。3.2 從 AMI 到實(shí)例User Data 和 Tag 的妙用選 AMIAmazon Machine Image是啟動(dòng)實(shí)例最關(guān)鍵的一步。第三版里的建議相當(dāng)樸素除非有特殊的內(nèi)核需求否則優(yōu)先選 Amazon Linux 2023因?yàn)樗?AWS 的集成度最高SSM 代理、CloudWatch 代理都是預(yù)裝的省去了手工安裝的環(huán)節(jié)。# 找到最新的 Amazon Linux 2023 AMI ID aws ec2 describe-images \ --owners amazon \ --filters Namename,Valuesal2023-ami-2023.*-x86_64 \ --query sort_by(Images, CreationDate)[-1].ImageId \ --output text # 啟動(dòng)實(shí)例掛載安全組注入 User Data aws ec2 run-instances \ --image-id ami-0abcdef1234567890 \ --instance-type t3.micro \ --key-name my-keypair \ --security-group-ids sg-0a1b2c3d4e5f67890 \ --subnet-id subnet-0a1b2c3d4e5f67890 \ --user-data file://user-data.sh \ --tag-specifications ResourceTypeinstance,Tags[{KeyName,Valueweb-server-1},{KeyEnvironment,Valueprod}]User Data 是很多新手看不懂的部分。它的作用是在實(shí)例第一次啟動(dòng)時(shí)執(zhí)行一段 shell 腳本——安裝軟件、拉代碼、啟動(dòng)服務(wù)全部自動(dòng)完成。配合 AMI你能做到服務(wù)器一開機(jī)就處于可用狀態(tài)而不是手動(dòng) SSH 上去敲一串命令。Tag 是 AWS 的資源管理核心Name和Environment是約定俗成的必備鍵后續(xù)你用aws ec2 describe-instances --filters Nametag:Environment,Valuesprod就能把所有生產(chǎn)實(shí)例篩出來批量操作。沒有 Tag 的資源在 AWS 里就是黑匣子——賬單查不清楚資源找不著運(yùn)維全靠猜。3.3 固定 IP 的三種方案Public IP、EIP 和負(fù)載均衡EC2 實(shí)例默認(rèn)拿到的是動(dòng)態(tài)公網(wǎng) IP——重啟就變這在測試環(huán)境還能忍生產(chǎn)環(huán)境絕對(duì)不行。第三版講了三種固定訪問的方式適用場景完全不同。第一種是 Elastic IPEIP一個(gè)靜態(tài)公網(wǎng) IP 綁到實(shí)例上。注意 EIP 是收費(fèi)的——綁著運(yùn)行的實(shí)例免費(fèi)但綁著停機(jī)的實(shí)例或空置的 EIP 每小時(shí)收費(fèi)。第二種是負(fù)載均衡器由 AWS 托管入口 IP后面掛多臺(tái)實(shí)例這個(gè)最貼近生產(chǎn)。第三種最容易被忽略——NAT Gateway 私有子網(wǎng)實(shí)例本身不暴露公網(wǎng) IP只通過 NAT 出站訪問互聯(lián)網(wǎng)。# 分配一個(gè) Elastic IP aws ec2 allocate-address --domain vpc # 把它綁到實(shí)例上這里拿到的是 AllocationId aws ec2 associate-address \ --allocation-id eipalloc-0a1b2c3d4e5f67890 \ --instance-id i-0a1b2c3d4e5f67890 # 創(chuàng)建 Application Load Balancer綁定兩個(gè)子網(wǎng) aws elbv2 create-load-balancer \ --name prod-alb \ --subnets subnet-0a1b2c3d4e5f67891 subnet-0a1b2c3d4e5f67892 \ --security-groups sg-0a1b2c3d4e5f67890我的建議很簡單單機(jī)實(shí)驗(yàn)用 EIP生產(chǎn)環(huán)境直接上 ALB。EIP 的坑在于它跟實(shí)例強(qiáng)綁定實(shí)例掛了 IP 也救不回來雖然可以重新關(guān)聯(lián)到新實(shí)例但中間有短暫空窗。ALB 是托管服務(wù)自動(dòng)做健康檢查、流量分發(fā)、證書卸載你用它的理由不是省事而是高可用——掛一臺(tái)實(shí)例在 ALB 后面實(shí)例 3 分鐘內(nèi)被替換訪問不受影響。3.4 存儲(chǔ)選型EBS 的 io2 和 gp3 到底差在哪EBS 是 EC2 的塊存儲(chǔ)類似虛擬機(jī)磁盤。第三版給了三種主流卷類型的定位gp3是默認(rèn)選擇性價(jià)比高io2是高性能場景主打極低延遲st1是冷數(shù)據(jù)存儲(chǔ)吞吐優(yōu)先。新手最常犯的錯(cuò)誤是盲目選io1/io2以為 IOPS 越高越好結(jié)果賬單翻了幾倍性能卻沒什么差別。# 創(chuàng)建 100GB 的 gp3 卷默認(rèn) 3000 IOPS 和 125 MB/s 吞吐 aws ec2 create-volume \ --volume-type gp3 \ --size 100 \ --availability-zone us-east-1a # 創(chuàng)建 100GB 的 io2 卷預(yù)置 10000 IOPS aws ec2 create-volume \ --volume-type io2 \ --size 100 \ --iops 10000 \ --availability-zone us-east-1agp3 最大的優(yōu)勢是 IOPS 和吞吐可以獨(dú)立調(diào)整——你把 IOPS 從 3000 調(diào)到 10000價(jià)格漲幅遠(yuǎn)小于換類型。而 io2 的 IOPS 是預(yù)置的不管你用不用都計(jì)費(fèi)。選型邏輯挺直接跑數(shù)據(jù)庫或延遲敏感應(yīng)用選 io2普通 Web 服務(wù)選 gp3日志歸檔選 st1。另有個(gè)參數(shù)容易忽視——AvailabilityZone必須跟實(shí)例在同一個(gè)可用區(qū)否則掛載失敗。這不是 AWS 的限制所有云平臺(tái)的塊存儲(chǔ)都這樣跨可用區(qū)得用其他方案。4. 從服務(wù)器到無服務(wù)器Lambda 和 API Gateway 的真實(shí)落地路徑4.1 Lambda 函數(shù)的基本結(jié)構(gòu)handler、事件和返回值Lambda 把服務(wù)器抽象掉了你不用管操作系統(tǒng)、補(bǔ)丁、擴(kuò)容寫的代碼直接跑在 AWS 托管的運(yùn)行時(shí)里。第三版花了相當(dāng)篇幅講這個(gè)——不是因?yàn)?Lambda 是新東西而是因?yàn)樗_實(shí)改變了應(yīng)用的部署方式。用 Lambda 寫接口你的思維要從「啟動(dòng)一個(gè)進(jìn)程監(jiān)聽端口」切換到「一個(gè)函數(shù)被事件觸發(fā)然后結(jié)束」。// index.js — 一個(gè)最簡單的 Lambda 函數(shù) exports.handler async (event, context) { // 從 API Gateway 傳來的事件里取出查詢參數(shù) const name event.queryStringParameters?event.queryStringParameters.name : World; // 返回值就是 HTTP 響應(yīng)的樣子 return { statusCode: 200, headers: {Content-Type: application/json}, body: JSON.stringify({ message: Hello, ${name}! }) }; };這個(gè) handler 的結(jié)構(gòu)是 AWS Lambda 的固定約定event是輸入數(shù)據(jù)context是運(yùn)行時(shí)信息返回值即響應(yīng)。你不需要監(jiān)聽任何端口也不需要處理 TCP 連接——這些全部由 Lambda 運(yùn)行時(shí)搞定。函數(shù)執(zhí)行完環(huán)境會(huì)凍結(jié)下次調(diào)用再解凍。理解了這個(gè)生命周期你就明白了為什么 Lambda 不適合跑長連接——你的代碼超過執(zhí)行超時(shí)就被殺掉默認(rèn) 3 秒最長可以調(diào)到 15 分鐘。也明白了為什么冷啟動(dòng)會(huì)成為性能瓶頸——第一次調(diào)用時(shí)環(huán)境要先初始化延遲會(huì)明顯變高。4.2 用 SAM 模板把 Lambda 部署到線上一條命令的事寫 Lambda 只是開始要把它變成一個(gè)真正的 HTTP 接口需要 API Gateway、IAM 角色、日志權(quán)限——手動(dòng)在控制臺(tái)點(diǎn)要 20 分鐘用 AWS SAMServerless Application Model可以壓縮到一條命令。# template.yaml — SAM 模板定義 Lambda 和 API Gateway AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Resources: HelloFunction: Type: AWS::Serverless::Function Properties: CodeUri: ./ Handler: index.handler Runtime: nodejs20.x Events: HelloApi: Type: Api Properties: Path: /hello Method: get# 用 SAM 構(gòu)建并部署--guided 會(huì)讓你交互式確認(rèn)參數(shù) sam build sam deploy --guided這段模板聲明了一個(gè) Lambda 函數(shù)然后聲明了一個(gè) API 事件——Path: /hello和Method: get意味著 API Gateway 會(huì)自動(dòng)創(chuàng)建/hello的 GET 接口把請(qǐng)求轉(zhuǎn)給 Lambda。sam build負(fù)責(zé)把本地代碼打包成部署產(chǎn)物sam deploy負(fù)責(zé)創(chuàng)建所有云資源。這套模式最大的價(jià)值不是省那 20 分鐘而是可重復(fù)——你的基礎(chǔ)設(shè)施變成了一堆代碼環(huán)境的每次變更都有跡可循刪掉整套環(huán)境也是一條命令的事。還有一個(gè)細(xì)節(jié)sam deploy --guided會(huì)讓你設(shè)置堆棧名和確認(rèn) IAM 權(quán)限如果沒有加--capabilities CAPABILITY_IAM部署會(huì)失敗——這是 SAM 最常見的報(bào)錯(cuò)之一新手遇到會(huì)以為是代碼問題其實(shí)只是缺了一個(gè)權(quán)限參數(shù)。4.3 API Gateway 的四個(gè)必調(diào)參數(shù)超時(shí)、限流、CORS 和二進(jìn)制API Gateway 在無服務(wù)器架構(gòu)里扮演入口角色但默認(rèn)配置在生產(chǎn)環(huán)境基本不可用。第三版里點(diǎn)到幾個(gè)必調(diào)的參數(shù)我做 Lambda 接口時(shí)的經(jīng)驗(yàn)也是圍繞這幾個(gè)坑展開的。第一個(gè)是超時(shí)。API Gateway 默認(rèn)的集成超時(shí)是 29 秒如果你后面掛的 Lambda 運(yùn)行超過這個(gè)時(shí)間返回 504。第二個(gè)是限流。不配限流意味著你的接口暴露在公網(wǎng)上被腳本刷一下就可能導(dǎo)致賬單飆升。第三個(gè)是 CORS跨域資源共享。前后端分離的項(xiàng)目里前端域名和 API 域名不一樣瀏覽器會(huì)攔截跨域請(qǐng)求。第四個(gè)是二進(jìn)制負(fù)載。API Gateway 默認(rèn)按文本處理請(qǐng)求上傳圖片或文件時(shí)要用binaryMediaTypes聲明。# 創(chuàng)建限流用的 usage plan每秒 10 個(gè)請(qǐng)求突發(fā) 20 個(gè) aws apigateway create-usage-plan \ --name basic-plan \ --throttle {burstLimit: 20, rateLimit: 10} # 開啟 CORS允許所有來源允許常用方法 aws apigateway update-stage \ --rest-api-id a1b2c3d4e5 \ --stage-name prod \ --patch-operations \ opreplace,path/settings/propagationEnabled,valuetrue限流的rateLimit是每秒的穩(wěn)定速率burstLimit是瞬時(shí)爆發(fā)容量——這兩個(gè)值要按你的業(yè)務(wù)量評(píng)估設(shè)小了會(huì)誤殺正常用戶設(shè)大了等于沒設(shè)。CORS 配置在第三版里反復(fù)強(qiáng)調(diào)因?yàn)樗膱?bào)錯(cuò)信息相當(dāng)迷惑——瀏覽器報(bào)「CORS policy: No Access-Control-Allow-Origin header」第一反應(yīng)是后端代碼的問題實(shí)際上需要在 API Gateway 這邊配置響應(yīng)頭。5. 避坑AWS 實(shí)戰(zhàn)里最容易翻車的 6 個(gè)細(xì)節(jié)5.1 數(shù)據(jù)持久化陷阱實(shí)例停機(jī)被終止數(shù)據(jù)全沒了現(xiàn)象一臺(tái) EC2 實(shí)例關(guān)機(jī)再開機(jī)SSH 登錄上去發(fā)現(xiàn)/home下新裝的文件全沒了——不是被重置是整個(gè)實(shí)例被終止了。原因啟動(dòng)實(shí)例時(shí)勾選了「Delete on Termination」屬性默認(rèn)勾選意味著實(shí)例終止時(shí)根卷跟著銷毀。如果你用 Spot 實(shí)例這個(gè)風(fēng)險(xiǎn)還要再放大——Spot 實(shí)例被回收時(shí)會(huì)直接終止實(shí)例沒有「先通知后停機(jī)」的緩沖。解決把數(shù)據(jù)放在獨(dú)立 EBS 卷實(shí)例和卷分開管理或 S3 里。EBS 卷默認(rèn)不隨實(shí)例刪除但需要專門把DeleteOnTermination設(shè)為 false。# 查看根卷的刪除屬性 aws ec2 describe-instances \ --instance-id i-0a1b2c3d4e5f67890 \ --query Reservations[0].Instances[0].BlockDeviceMappings # 修改根卷的 DeleteOnTermination 為 false先分離再修改最后重新掛載 aws ec2 modify-instance-attribute \ --instance-id i-0a1b2c3d4e5f67890 \ --block-device-mappings [{DeviceName: /dev/xvda, Ebs: {DeleteOnTermination: false}}]這是一個(gè)老生常談但永遠(yuǎn)有人踩的坑。根卷的DeleteOnTermination默認(rèn) true 是有設(shè)計(jì)考量的——讓臨時(shí)服務(wù)器銷毀時(shí)不留殘留。但所有重要的數(shù)據(jù)、配置、日志放在根卷都是不安全的。第三版的建議是根卷只放操作系統(tǒng)應(yīng)用和數(shù)據(jù)全部放在獨(dú)立卷或?qū)ο蟠鎯?chǔ)里。5.2 跨區(qū)域折騰數(shù)據(jù)帶寬費(fèi)比存儲(chǔ)費(fèi)貴得多現(xiàn)象開了一個(gè)新區(qū)域想遷移數(shù)據(jù)把原來區(qū)域里的 S3 文件用aws s3 cp --recursive直接拷到新區(qū)域月底賬單驚掉下巴——傳輸費(fèi)比存儲(chǔ)費(fèi)貴了 3 倍。原因S3 的跨區(qū)域復(fù)制或手動(dòng)傳輸會(huì)收取數(shù)據(jù)傳輸費(fèi)大約 $0.02/GB出口到互聯(lián)網(wǎng)則更貴$0.09/GB。你沒做任何操作光從us-east-1傳到ap-southeast-1100GB就要花 2 美元——聽起來不多但 TB 級(jí)數(shù)據(jù)就是 20 美元的差距加上請(qǐng)求費(fèi)用和 S3 自身的讀寫費(fèi)用累計(jì)起來相當(dāng)可觀。解決用 S3 的SameRegionReplication做同區(qū)域復(fù)制用 S3 Batch Operations 做批量遷移而不是腳本直傳。真正大規(guī)??鐓^(qū)域遷移用 AWS DataSync 或 Snowball 設(shè)備。5.3 權(quán)限追蹤靠猜AWS 里最貴的一句話是「剛才誰動(dòng)的」現(xiàn)象團(tuán)隊(duì)里有人誤刪了生產(chǎn)數(shù)據(jù)庫問起來所有人都說「不是我」。原因沒開 CloudTrail 或開了但不看日志。CloudTrail 默認(rèn)記錄過去 90 天的管理事件但如果你在控制臺(tái)手動(dòng)關(guān)閉過這段歷史就沒了。解決立刻開啟 CloudTrail 并配置日志投遞到 S3用 Athena 查日志。-- 用 Athena 查過去 24 小時(shí)的 DeleteDBInstance 操作 SELECT eventTime, userIdentity.userName, eventName, errorMessage FROM cloudtrail_logs WHERE eventName DeleteDBInstance AND eventTime date_add(day, -1, now()) ORDER BY eventTime DESC;這一招在「事故復(fù)盤」時(shí)價(jià)值千金。CloudTrail 日志默認(rèn)存在 S3 里直接查沒法查——因?yàn)樗?JSON 格式得先用 Athena 建表。Athena 是按掃描量計(jì)費(fèi)的建議加上分區(qū)按年/月/日分區(qū)再建表不然全表掃描的費(fèi)用也會(huì)成為新的賬單事故。5.4 IAM 策略太長導(dǎo)致「策略大小超出限制」現(xiàn)象寫了一個(gè)復(fù)雜的 IAM 策略應(yīng)用時(shí)aws iam put-role-policy報(bào)錯(cuò)PolicySizeExceededException。原因單個(gè) IAM 策略的最大長度是 6144 字節(jié)托管策略最長 10240 字節(jié)。列了一堆資源 ARN 和條件關(guān)鍵字很容易超限。解決拆成多個(gè)策略。IAM 角色支持掛多個(gè)策略內(nèi)聯(lián)策略的大小限制比托管策略更嚴(yán)格所以優(yōu)先用托管策略。如果同一個(gè)角色要訪問多個(gè)服務(wù)的不同資源策略拆分是標(biāo)準(zhǔn)做法。# 創(chuàng)建一個(gè)精簡的 S3 訪問策略避免把所有桶都寫進(jìn)一個(gè)策略 aws iam create-policy \ --policy-name app-s3-access \ --policy-document { Version: 2012-10-17, Statement: [ {Effect: Allow, Action: s3:GetObject, Resource: arn:aws:s3:::app-assets/*}, {Effect: Allow, Action: s3:ListBucket, Resource: arn:aws:s3:::app-assets} ] }5.5 一鍵刪除的幻覺CloudFormation 刪除堆棧時(shí)把數(shù)據(jù)也帶走了現(xiàn)象用 CloudFormation 部署了一套環(huán)境測試完覺得沒用了執(zhí)行刪除堆棧然后發(fā)現(xiàn) S3 桶里的歷史數(shù)據(jù)也全沒了——堆棧刪除默認(rèn)會(huì)刪除桶里的所有對(duì)象而不是只刪桶本身。原因CloudFormation 刪除堆棧時(shí)對(duì) S3 桶的默認(rèn)行為是強(qiáng)制刪除——不管桶里有沒有數(shù)據(jù)直接清除。你以為是刪了個(gè)空桶實(shí)際上是連帶數(shù)據(jù)一起刪。解決給 S3 桶加DeletionPolicy: Retain或者設(shè)置DeletionPolicy: Snapshot適用于數(shù)據(jù)庫這樣堆棧刪了數(shù)據(jù)還在只是變成了孤兒資源需要手動(dòng)清理。是麻煩但比數(shù)據(jù)永久消失強(qiáng)一萬倍。# template.yaml 片段保留 S3 桶數(shù)據(jù)不讓刪除堆棧時(shí)連帶刪掉 Resources: DataBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain5.6 EC2 實(shí)例啟動(dòng)慢其實(shí)是元數(shù)據(jù)服務(wù)在拖后腿現(xiàn)象同一套 AMI在us-east-1啟動(dòng)只要 30 秒在某個(gè)區(qū)域要 3 分鐘網(wǎng)絡(luò)也時(shí)好時(shí)差。原因EC2 實(shí)例啟動(dòng)時(shí)需要通過 Instance Metadata ServiceIMDS獲取密鑰、網(wǎng)絡(luò)配置等信息。某些區(qū)域 IMDS 的響應(yīng)慢導(dǎo)致啟動(dòng)流程卡住。解決改用 IMDSv2強(qiáng)制版本并給實(shí)例設(shè)置較長的恢復(fù)等待時(shí)間。如果是 T 系列實(shí)例t3/t4g查看是否觸發(fā)了 CPU 積分耗盡——積分用完性能直接掉到基準(zhǔn)以下。# 查看實(shí)例的 CPU 積分余額 aws ec2 describe-instances \ --instance-id i-0a1b2c3d4e5f67890 \ --query Reservations[0].Instances[0].CpuOptions6. 把基礎(chǔ)設(shè)施寫成代碼用 CDK 管理這套環(huán)境的實(shí)戰(zhàn)技巧6.1 為什么用 CDK 而不是 CloudFormation YAML第三版從 CloudFormation 講到 CDK這個(gè)演進(jìn)很多人還沒來得及接受。CloudFormation 用 YAML/JSON 描述資源工作了但體驗(yàn)一般——YAML 沒有類型檢查、沒有自動(dòng)補(bǔ)全、復(fù)用邏輯得靠嵌套模板或宏改一個(gè)參數(shù)要在多個(gè)文件里找。CDKCloud Development Kit允許你用 TypeScript/JavaScript/Python以及 Java、C# 等寫基礎(chǔ)設(shè)施本質(zhì)上是把 CloudFormation 模板變成代碼生成器。有 IDE 補(bǔ)全和類型系統(tǒng)寫錯(cuò)屬性名在編譯期就報(bào)錯(cuò)不用等部署失敗可以用變量、循環(huán)、函數(shù)組合資源應(yīng)付復(fù)雜的重復(fù)性資源提供構(gòu)造庫Construct Library封裝高層模式比如「一個(gè)負(fù)載均衡 兩個(gè) EC2」這種代碼寫成一行// cdk-app.ts — 用 TypeScript 定義一個(gè)包含 VPC、EC2、安全組的應(yīng)用 import * as cdk from aws-cdk-lib; import * as ec2 from aws-cdk-lib/aws-ec2; export class MyStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc new ec2.Vpc(this, MyVpc, { maxAzs: 2, natGateways: 1 }); const securityGroup new ec2.SecurityGroup(this, WebSG, { vpc, description: Allow web traffic, allowAllOutbound: true }); securityGroup.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(80), Allow HTTP from anywhere ); const instance new ec2.Instance(this, WebServer, { vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), machineImage: ec2.MachineImage.latestAmazonLinux2023(), securityGroup }); } } const app new cdk.App(); new MyStack(app, MyCdkStack);# 部署 CDK 應(yīng)用 cdk bootstrap cdk deployCDK 代碼里最重要的幾個(gè)對(duì)象Vpc會(huì)默認(rèn)創(chuàng)建包含兩個(gè)可用區(qū)的完整網(wǎng)絡(luò)環(huán)境——公有子網(wǎng)、私有子網(wǎng)、NAT 網(wǎng)關(guān)、路由表全部自動(dòng)化。SecurityGroup的addIngressRule在 YAML 里對(duì)應(yīng)好幾行配置這里一行就完成了。ec2.Instance則自動(dòng)幫你創(chuàng)建實(shí)例、掛載安全組、分配存儲(chǔ)。6.2 本地模擬與快速驗(yàn)證CDK 的 Test 功能不是擺設(shè)CDK 有一個(gè)被低估的能力——單元測試。它能把基礎(chǔ)設(shè)施的「預(yù)期狀態(tài)」寫進(jìn)斷言在部署之前就驗(yàn)證。第三版雖然沒有把這個(gè)當(dāng)重點(diǎn)講但我實(shí)際操作下來這是防止生產(chǎn)事故最有效的工具。// test/my-stack.test.ts — 用 CDK 斷言驗(yàn)證資源屬性 import { Template } from aws-cdk-lib/assertions; test(Security group allows inbound HTTP, () { const app new cdk.App(); const stack new MyStack(app, TestStack); const template Template.fromStack(stack); template.hasResourceProperties(AWS::EC2::SecurityGroup, { SecurityGroupIngress: [ { IpProtocol: tcp, FromPort: 80, ToPort: 80, CidrIp: 0.0.0.0/0 } ] }); });# 跑測試 npm test這段測試的意義在于安全組規(guī)則、VPC 配置、實(shí)例類型——所有基礎(chǔ)設(shè)施的「關(guān)鍵參數(shù)」都變成了可斷言的代碼。當(dāng)團(tuán)隊(duì)里有人改了一個(gè)安全組規(guī)則或換了一個(gè)實(shí)例類型測試會(huì)立刻告訴你這違反了預(yù)期。我跟人協(xié)作 AWS 項(xiàng)目的習(xí)慣是所有資源變更必須帶著測試提交不然不管其他代碼測得多好上線大概率出問題。6.3 環(huán)境隔離用上下文參數(shù)切換 dev、test、prodCDK 在實(shí)踐中最容易翻車的場景是「測試環(huán)境跟生產(chǎn)環(huán)境混在一起」。有人圖省事所有環(huán)境都部署到同一個(gè)賬號(hào)沒有做隔離結(jié)果測試環(huán)境的告警和實(shí)驗(yàn)數(shù)據(jù)污染了生產(chǎn)監(jiān)控甚至誤刪了生產(chǎn)資源。我的方案是用 CDK Context 區(qū)分環(huán)境不同環(huán)境用不同賬號(hào) 不同 VPC。// 在 cdk.json 里定義環(huán)境差異 { app: node bin/app.js, context: { dev: { instanceType: t3.micro, natGateways: 0 }, prod: { instanceType: m5.large, natGateways: 2 } } }// bin/app.js — 根據(jù)環(huán)境讀取不同配置 const env process.env.ENV || dev; const config app.node.tryGetContext(env); new MyStack(app, MyStack-${env}, { instanceType: config.instanceType, natGateways: config.natGateways, env: { account: config.account, region: config.region } });# 分別部署不同環(huán)境 ENVdev cdk deploy ENVprod cdk deploytryGetContext從cdk.json或命令行參數(shù)里讀取配置這樣同樣的代碼在不同的環(huán)境得到不同的基礎(chǔ)設(shè)施。dev 環(huán)境不需要 NAT 網(wǎng)關(guān)因?yàn)椴恍枰L問外部網(wǎng)絡(luò)用natGateways: 0能省掉一大筆費(fèi)用prod 環(huán)境必須雙 NAT 保證高可用。這個(gè)模式下環(huán)境之間的差異被代碼顯式管理而不是靠「誰記得改了什么參數(shù)」——這是我做過太多環(huán)境混亂項(xiàng)目之后的血淚經(jīng)驗(yàn)?;A(chǔ)設(shè)施沒有后悔藥可言但 CDK 的代碼化至少讓你知道「現(xiàn)在到底有什么、從哪來的、怎么拆掉它」。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取