以下是關于 EKS 直接拉取 ECR 鏡像的解答,以及如何通過 GitHub Actions 將項目打包為容器、推送至 AWS ECR 并使 EKS 自動拉取以完成發版部署的詳細步驟。當前時間為 2025 年 7 月 23 日下午 12:27 HKT,基于最新技術實踐提供方案。
1. EKS 直接拉取 ECR 鏡像嗎?
-
答案:是的,EKS 可以直接拉取 ECR 鏡像,但需要滿足以下條件:
- IAM 權限:EKS 集群的節點(Worker Nodes)需關聯一個 IAM 角色,擁有
AmazonEC2ContainerRegistryReadOnly
權限,以便從 ECR 拉取鏡像。 - 配置正確:在 Kubernetes Pod 的
spec.containers.image
字段中指定 ECR 鏡像路徑(例如account-id.dkr.ecr.region.amazonaws.com/repository:tag
)。 - 網絡訪問:EKS 集群需能訪問 ECR(通常通過 VPC 配置或公網訪問)。
- 默認行為:EKS 的 kubelet 會自動嘗試拉取指定的鏡像,無需額外工具,但拉取過程依賴節點的網絡和權限設置。
- IAM 權限:EKS 集群的節點(Worker Nodes)需關聯一個 IAM 角色,擁有
-
驗證方法:
- 使用
kubectl describe pod <pod-name>
檢查事件日志,確認拉取成功或失敗(例如 “ImagePullBackOff” 表示權限或網絡問題)。
- 使用
2. 通過 GitHub Actions 打包容器、推送至 ECR 并部署到 EKS 的步驟
準備工作
- AWS 配置:
- 創建 ECR 倉庫(例如
my-app
),記錄倉庫 URL(account-id.dkr.ecr.region.amazonaws.com/my-app
)。 - 設置 EKS 集群(例如
my-eks-cluster
),并通過aws eks update-kubeconfig --region region --name my-eks-cluster
獲取kubeconfig
。 - 創建 IAM 角色并附加到 EKS 節點組,包含
AmazonEC2ContainerRegistryReadOnly
權限。 - 在 GitHub 倉庫的
Settings > Secrets and variables > Actions
中添加:AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
(例如us-east-1
)ECR_REPOSITORY
(例如my-app
)KUBE_CONFIG_DATA
(將kubeconfig
轉為 Base64 編碼,例如cat ~/.kube/config | base64
)。
- 創建 ECR 倉庫(例如
- 項目配置:
- 根目錄下創建
Dockerfile
(示例見下)。 - 創建 Kubernetes 部署文件
deployment.yaml
(示例見下)。
- 根目錄下創建
Dockerfile 示例
# 使用官方 Node.js 鏡像作為基礎
FROM node:18-alpine# 設置工作目錄
WORKDIR /app# 復制 package.json 和 package-lock.json
COPY package*.json ./# 安裝依賴
RUN npm install# 復制項目文件
COPY . .# 構建應用
RUN npm run build# 暴露端口
EXPOSE 3000# 啟動命令
CMD ["node", "dist/index.js"]
Kubernetes 部署文件(deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:name: my-app
spec:replicas: 2selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appspec:containers:- name: my-appimage: <account-id>.dkr.ecr.<region>.amazonaws.com/my-app:latestports:- containerPort: 3000resources:requests:memory: "256Mi"cpu: "200m"limits:memory: "512Mi"cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:name: my-app-service
spec:selector:app: my-appports:- protocol: TCPport: 80targetPort: 3000type: LoadBalancer
GitHub Actions 工作流(.github/workflows/deploy.yml)
name: Deploy to EKSon:push:branches:- mainjobs:deploy:name: Build and Deployruns-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v4- name: Set up Docker Buildxuses: docker/setup-buildx-action@v3- name: Configure AWS credentialsuses: aws-actions/configure-aws-credentials@v4with:aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}aws-region: ${{ secrets.AWS_REGION }}- name: Login to Amazon ECRid: login-ecruses: aws-actions/amazon-ecr-login@v2- name: Build, tag, and push image to Amazon ECRid: build-imageenv:ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }}IMAGE_TAG: ${{ github.sha }}run: |docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAGecho "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT- name: Update kubeconfigrun: |echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 -d > kubeconfigexport KUBECONFIG=kubeconfig- name: Deploy to EKSuses: kodermax/kubectl-aws-eks@mainwith:args: set image deployment/my-app my-app=${{ steps.build-image.outputs.image }} --recordenv:KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}- name: Verify deploymentuses: kodermax/kubectl-aws-eks@mainwith:args: rollout status deployment/my-appenv:KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}- name: Cleanupif: always()run: |rm -f kubeconfig
實現流程
- 觸發工作流:
- 推送到
main
分支時,GitHub Actions 自動運行。
- 推送到
- 構建與推送:
- 配置 AWS 憑證,登錄 ECR。
- 使用 Docker Buildx 構建鏡像,打上 Git 提交哈希標簽(
${{ github.sha }}
),推送到 ECR。
- 部署到 EKS:
- 解碼
kubeconfig
,更新 Deployment 中的鏡像標簽。 - 使用
kubectl
應用更改,EKS 節點自動拉取新鏡像。
- 解碼
- 驗證與清理:
- 檢查部署狀態,確保 Pod 運行。
- 清理臨時文件。
EKS 自動拉取鏡像
- EKS 的 kubelet 會在 Pod 啟動時自動拉取
deployment.yaml
中指定的鏡像(例如account-id.dkr.ecr.us-east-1.amazonaws.com/my-app:sha256...
)。 - 如果鏡像未找到或權限不足,Pod 狀態將顯示
ImagePullBackOff
,需檢查 IAM 角色和網絡配置。
注意事項
- IAM 角色:確保 EKS 節點組的 IAM 角色有 ECR 讀取權限。
- 鏡像標簽:使用動態標簽(
${{ github.sha }}
)避免緩存問題,生產環境可加環境標識(如prod-${{ github.sha }}
)。 - 網絡:若 EKS 在私有 VPC,配置 VPC Endpoint 或 NAT Gateway 訪問 ECR。
- 安全:使用 GitHub OIDC 替代長期憑證,提升安全性。
- 測試:初次部署后,使用
kubectl get pods
和kubectl logs
驗證。
驗證部署
- 推代碼后,檢查 GitHub Actions 日志。
- 使用
kubectl get deployments
確認my-app
更新。 - 通過 Service 的外部 IP(
kubectl get svc my-app-service
)訪問應用。
此方案實現了從代碼提交到 EKS 自動部署的全流程,適合中小型項目。如需多環境或回滾支持,可擴展工作流邏輯。
優化改進版action.yml
name: Deploy to EKSon:push:branches:- main- devjobs:deploy:name: Build and Deployruns-on: ubuntu-latestenv:ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}IMAGE_TAG: ${{ github.ref == 'refs/heads/main' && format('prod-{0}', github.sha) || format('dev-{0}', github.sha) }}steps:- name: Checkout codeuses: actions/checkout@v4- name: Set up Docker Buildxuses: docker/setup-buildx-action@v3- name: Configure AWS credentialsuses: aws-actions/configure-aws-credentials@v4with:aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}aws-region: ${{ secrets.AWS_REGION }}- name: Login to Amazon ECRid: login-ecruses: aws-actions/amazon-ecr-login@v2- name: Build, tag, and push image to Amazon ECRid: build-imageenv:ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }}run: |docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAGecho "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT- name: Update kubeconfigrun: |echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 -d > kubeconfigexport KUBECONFIG=kubeconfig- name: Deploy to EKSuses: kodermax/kubectl-aws-eks@mainwith:args: set image deployment/my-app my-app=${{ steps.build-image.outputs.image }} --recordenv:KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}- name: Verify deploymentrun: |kubectl rollout status deployment/my-app --timeout=5menv:KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}timeout-minutes: 5- name: Rollback on failureif: failure()run: |kubectl rollout undo deployment/my-appenv:KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}- name: Cleanupif: always()run: |rm -f kubeconfig- name: Notify deployment statusif: always()run: |echo "Deployment to ${ENVIRONMENT} completed with status ${{ job.status }}"# 可選:集成 Slack 或郵件通知# curl -X POST -H 'Content-type: application/json' --data '{"text":"Deployment to ${ENVIRONMENT} ${{ job.status }}"}' https://hooks.slack.com/services/xxx