Amazon EKS 上有狀態服務啟用儲存加密

亞馬遜雲開發者發表於2023-05-20

1.背景

使用者透過 Deployment, Replication Controller 可以方便地在 Kubernetes 中部署一套高可用、可擴充套件的分散式無狀態服務。這類應用不在本地儲存資料,透過簡單的負載均衡策略可實現請求分發。

Deployment 以及 Replication Controller 是為無狀態服務而設計的,它們中 Pod 的名稱、主機名、儲存都是不穩定的,且 Pod 的啟動、銷燬順序隨機,並不適合資料庫這樣的有狀態應用。

亞馬遜雲科技開發者社群為開發者們提供全球的開發技術資源。這裡有技術文件、開發案例、技術專欄、培訓影片、活動與競賽等。幫助中國開發者對接世界最前沿技術,觀點,和專案,並將中國優秀開發者或技術推薦給全球雲社群。如果你還沒有關注/收藏,看到這裡請一定不要匆匆劃過,點這裡讓它成為你的技術寶庫!

為此,Kubernetes 推出了面向有狀態服務的工作負載 StatefulSet,管理的 Pod 具有如下特點:

  • 唯一性 – 對於包含 N 個副本的 StatefulSet,每個 Pod 會被分配一個 [0,N] 範圍內的唯一序號。
  • 順序性 – StatefulSet 中 Pod 的啟動、更新、銷燬預設都是按順序進行的。
  • 穩定的網路身份標識 – Pod 的主機名、DNS 地址不會隨著 Pod 被重新排程而發生變化。
  • 穩定的持久化儲存 – 當 Pod 被重新排程後,仍然能掛載原有的 PersistentVolume,保證了資料的完整性和一致性。

本文針對有狀態服務業務場景的資料保護安全需求,旨在從 Amazon EKS 內部結合採用 StatefulSet, Snapshot Controller 的方式實現有狀態服務的儲存加密啟用,並在測試環境下驗證了本方案的可行性。

2.場景案例

2.1 場景架構

業務場景:

該業務平臺構建在亞馬遜雲上,服務客群主要為全國各醫院,核心業務價值在於簡化醫院採購結算環節,幫助醫院方降本增效。該平臺透過結合醫院,醫院供應商提供的訂單、結算單、發票資訊,進行三單驗證,並整合銀行側銀企直連,進而實現醫院的快速透明支付結算、有效提升採購效率。

部署架構:

之前客戶在本地 IDC 的 Kubernetes 叢集中除了部署了無狀態的微服務應用外,還部署了有狀態的中介軟體服務,包括 Redis, Kafka, MySQL,儲存使用的是本地磁碟。由於自建 Kubernetes 叢集的升級、管理、安全等各方面的運維工作複雜,需要耗費大量的運維資源;並且在 Kubernetes 叢集中執行有狀態的應用,特別是業務資料庫,存在很大的潛在風險,因而在遷移至亞馬遜雲上的過程中,接受我方建議做了相應的架構最佳化及調整:

自建 Kubernetes 調整為託管的 EKS 服務;

  • 從 Kubernetes 叢集中移除關聯式資料庫 MySQL,替換為託管的 RDS Aurora 服務;
  • EKS 中各業務元件採用無狀態部署;
  • EKS 中保留 Redis 和 Kafka 等中介軟體,並採用有狀態部署,持久化採用 EBS;
  • EKS 中採用 EFS 作為共享儲存,實現儲存的安全高可用。

架構示意圖:

2.2 問題及解決方案

問題描述:

由於涉及到訂單、結算單、發票等敏感資料的傳輸及儲存,在業務上線後不久,客戶希望加強雲上資料安全保護,結合 KMS 對持久化資料進行自動儲存加密。之前有狀態服務 Redis, Kafka 等在部署上線時所掛載的 EBS 並未啟用加密,由於 EBS 在初始未啟用加密的情況下無法直接開啟加密(見下面技術約束示意圖),那麼在對業務的影響降至最低的前提下如何實現 EBS 儲存加密的開啟及業務的平滑遷移呢?

技術約束示意圖:

初始未加密EBS無法直接啟用加密,需對未加密 EBS 做快照,再將未加密快照還原成加密 EBS,如下圖所示:

解決方案:

  • 方案一:需應用層做資料遷移,EKS 中原 Redis 等有狀態服務保持不動,採用 StatefulSet 方式新建 Redis 等有狀態服務並啟用 EBS 儲存加密,在應用層做資料遷移(將敏感資料從原服務未加密 EBS 遷移至新建服務已加密 EBS)後將中介軟體服務切換至新建 Redis 等有狀態服務並下線原服務以實現有狀態服務的儲存加密;
  • 方案二:不涉及應用層資料遷移,從亞馬遜雲科技控制檯直接對 EBS 進行相關操作,將未加密EBS製作快照後還原成加密 EBS,並將加密 EBS 靜態掛載回正確的 Redis 等有狀態服務;
  • 方案三:不涉及應用層資料遷移,從 EKS 內部進行相關操作,首先啟用 Snapshot Controller,之後透過該 Controller 對未加密 pvc 製作快照後還原成加密pvc,新建 Redis StatefulSet 並掛載加密 pvc,引流至新建 Redis 測試一切正常後做服務遷移;

由於客戶不希望應用層做資料遷移,以及從亞馬遜雲科技控制檯操作後如何正確靜態掛載回對應的 Pod 存在較大風險,最終採用了方案三進行了相應的測試及部署,併成功啟用了 EKS 下 EBS 的儲存加密。

3.部署與測試

接下來我們會按照上述方案三進行部署測試,整體的過程如下:

3.1 環境準備

安裝 Amazon CLI, eksctl, kubectl 和 helm; 安裝並配置 Amazon CLI: https://docs.AWS.amazon.com/cli/latest/userguide/getting-started-install.html 安裝eksctl: https://docs.AWS.amazon.com/eks/latest/userguide/eksctl.html 安裝kubectl: https://docs.AWS.amazon.com/eks/latest/userguide/install-kubectl.html Helm 安裝: https://docs.AWS.amazon.com/eks/latest/userguide/helm.html 執行以下命令建立 EKS 叢集,本實驗環境中的叢集版本為 V1.21

eksctl create cluster --name test-cluster

建立完成後,檢查叢集連線性,應該會看到兩個 Ready 狀態的節點;

kubectl get node

安裝 EBS CSI Driver 外掛,將 arn 中的 111122223333 替換為您的賬戶 ID;

eksctl utils associate-iam-oidc-provider \
  --cluster test-cluster \
  --approve
eksctl create iamserviceaccount \
  --name ebs-csi-Controller-sa \
  --namespace kube-system \
  --cluster test-cluster \
  --attach-policy-arn arn:AWS:iam::AWS:policy/service-role/AmazonEBSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EBS_CSI_DriverRole
eksctl create addon --name AWS-ebs-csi-driver --cluster test-cluster --service-account-role-arn arn:AWS:iam::111122223333:role/AmazonEKS_EBS_CSI_DriverRole --force

作為生產環境 EBS 卷加密前的模擬,接下來建立一個 StorageClass,和一個 Redis 的 StatefulSet:

curl -LJO https://raw.githubusercontent.com/henghengha/hello-world/master/sc.yml
curl -LJO https://raw.githubusercontent.com/henghengha/hello-world/master/sts.yml
curl -LJO https://raw.githubusercontent.com/henghengha/hello-world/master/rbac.yml
curl -LJO https://raw.githubusercontent.com/henghengha/hello-world/master/cm.yml
kubectl apply -f sc.yml
kubectl apply -f rbac.yml
kubectl apply -f cm.yml
kubectl apply -f sts.yml

檢查部署狀態

檢視 EBS 卷均未加密

3.2 方案實現

EBS 卷是否加密的屬性是在 StorageClass 物件中宣告的,透過重建 StorageClass 可以保證重新建立出來的 pv 即 EBS 卷是加密的,但是不會作用到之前已經存在的 EBS 卷。本部分將重點展示針對有狀態物件在啟用儲存加密後,如何將原有 Pod 的資料備份,從而確保所有的 EBS 卷都已加密。操作步驟如下:

安裝 Snapshot Controller,預設安裝在 kube-system 名稱空間,確保執行正常,參考:

https://aws.amazon.com/cn/blogs/containers/using-ebs-snapshots-for-persistent-storage-with-your-eks-cluster/

注意:配置檔案中 Snapshot-Controller 使用的映象地址為 gcr.io/Kubernetes-staging-sig-storage/Snapshot-Controller:v5.0.1,如果在中國區部署,請將該映象下載下來,可以上傳至 ECR,然後修改配置檔案中的地址為中國區 ECR 的地址。

生產環境中為了儘可能的減少業務受影響的時間,我們後面將使用相同的配置建立一個新的 StatefulSet 物件,命名為 redis1,根據命名規則(該示例 StatefulSet 中的 volumeClaimTemplates 的名字為 data),redis1 生成的 pvc 名字將是 data-redis1-0 和 data-redis1-1。注意這裡先不做真正的 StatefulSet 的部署;

刪除原有 StorageClass,並使用如下配置重建:

apiVersion: storage.Kubernetes.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.AWS.com
  parameters:
    encrypted: 'true'
volumeBindingMode: WaitForFirstConsumer

本示例使用託管的 Amazon KMS Key aws/ebs 來進行加密,因此在 StorageClass 的配置中未指定具體的 Key ID。如果需要使用自定義 KMS 金鑰,可以在 StorageClass 中新增 KMS Key ID 配置。與此同時,遵循最小許可權原則,還需要給 EBS CSI Driver 授權 KMS 的對應許可權。建立 IAM policy 並關聯至 AmazonEKS_EBS_CSI_DriverRole,策略內容可參考官方文件進行配置: https://docs.aws.amazon.com/eks/latest/userguide/csi-iam-role.html

為原有的未加密 EBS 卷建立快照,並進行快照恢復到加密卷。首先,為 pvc data-redis-0 和data-redis-1 建立 Snapshot:

a.建立 Volume Snapshot Class;

cat <<"EOF" > Snapshot-class.yaml
apiVersion: Snapshot.storage.Kubernetes.io/v1
kind: VolumeSnapshotClass
metadata:
  name: test-snapclass
driver: ebs.csi.AWS.com
deletionPolicy: Delete
EOF
kubectl apply -f Snapshot-class.yaml

b.建立 YAML 檔案,volume-Snapshot-redis.yaml 內容如下;

注意:VolumeSnapshot 中的 persistentVolumeClaimName 要保證與 Pod 對應的 pvc 名稱相同,此處分別為 data-redis-0 和 data-redis-1。部署該檔案來建立快照;

cat <<"EOF" > volume-Snapshot-redis.yaml
apiVersion: Snapshot.storage.Kubernetes.io/v1
kind: VolumeSnapshot
metadata:
  name: redis-0-Snapshot
spec:
  volumeSnapshotClassName: test-snapclass
  source:
    persistentVolumeClaimName: data-redis-0
---
apiVersion: Snapshot.storage.Kubernetes.io/v1
kind: VolumeSnapshot
metadata:
  name: redis-1-Snapshot
spec:
  volumeSnapshotClassName: test-snapclass
  source:
    persistentVolumeClaimName: data-redis-1
EOF

kubectl apply -f volume-Snapshot-redis.yaml

c.檢查快照資訊;

使用快照作為資料來源建立 pvc,給下一步新建立的 StatefulSet 中的 Pod 使用:

cat <<"EOF" > volume-from-Snapshot.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-redis1-0
spec:
  storageClassName: ebs-sc
  dataSource:
    name: redis-0-Snapshot
    kind: VolumeSnapshot
    apiGroup: Snapshot.storage.Kubernetes.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-redis1-1
spec:
  storageClassName: ebs-sc
  dataSource:
    name: redis-1-Snapshot
    kind: VolumeSnapshot
    apiGroup: Snapshot.storage.Kubernetes.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
EOF

kubectl apply -f volume-from-Snapshot.yaml

部署新的 StatefulSet redis1,並檢查狀態:

確認 pv 對應的 EBS 卷已加密,並驗證資料備份無誤之後可以根據具體的配置情況將連線資訊切到新的物件上。

如果確認所有流量都已轉移到新的 Pod 上,即可刪除原有的 StatefulSet 資源。

3.3 環境清除

刪除叢集中部署的資源,並刪除 pvc 和 pv 物件:

kubectl delete -f sts.yml
kubectl delete -f rbac.yml
kubectl delete -f cm.yml

刪除叢集:

eksctl delete iamserviceaccount --cluster test-cluster --name ebs-csi-Controller-sa
eksctl delete nodegroup {your-node-group-name} --cluster test-cluster
eksctl delete cluster --name test-cluster

4.參考

https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/ebs-csi.html

https://aws.amazon.com/blogs/containers/using-ebs-snapshots-for-persistent-storage-with-your-eks-cluster/

https://eksctl.io/

5.小結

本文我們針對 Amazon EKS 上有狀態服務的資料保護安全需求,介紹了結合 KMS 服務,在 Amazon EKS 中如何使用 StatefulSet, Snapshot Controller 的方式實現有狀態服務的儲存加密啟用,該方式透過從底層儲存解決資料加密儲存及遷移,無需應用層介入資料遷移,從而簡單有效解決了 Amazon EKS 上有狀態服務的儲存加密需求。

本篇作者

李銳亞馬遜雲科技金融行業資深解決方案架構師,負責基於亞馬遜的雲端計算方案架構諮詢和設計,擅長安全領域。曾任職網際網路公司,擁有多年金融雲產品及架構設計經驗。

孫彥巧 亞馬遜雲科技金融行業解決方案架構師,負責雲端計算解決方案的架構設計和諮詢。有多年亞馬遜雲科技從業經驗,熱衷於容器、微服務架構及 Devops 方面的研究和應用。

閱讀原文

文章來源:https://dev.amazoncloud.cn/column/article/631478a1323d8e18f89...

相關文章