以下是关于 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: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: <account-id>.dkr.ecr.<region>.amazonaws.com/my-app:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
GitHub Actions 工作流(.github/workflows/deploy.yml)
name: Deploy to EKS
on:
push:
branches:
- main
jobs:
deploy:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
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 ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
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_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Update kubeconfig
run: |
echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 -d > kubeconfig
export KUBECONFIG=kubeconfig
- name: Deploy to EKS
uses: kodermax/kubectl-aws-eks@main
with:
args: set image deployment/my-app my-app=${{ steps.build-image.outputs.image }} --record
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
- name: Verify deployment
uses: kodermax/kubectl-aws-eks@main
with:
args: rollout status deployment/my-app
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
- name: Cleanup
if: 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
bash
name: Deploy to EKS
on:
push:
branches:
- main
- dev
jobs:
deploy:
name: Build and Deploy
runs-on: ubuntu-latest
env:
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 code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
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 ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
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_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Update kubeconfig
run: |
echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 -d > kubeconfig
export KUBECONFIG=kubeconfig
- name: Deploy to EKS
uses: kodermax/kubectl-aws-eks@main
with:
args: set image deployment/my-app my-app=${{ steps.build-image.outputs.image }} --record
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
- name: Verify deployment
run: |
kubectl rollout status deployment/my-app --timeout=5m
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
timeout-minutes: 5
- name: Rollback on failure
if: failure()
run: |
kubectl rollout undo deployment/my-app
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
- name: Cleanup
if: always()
run: |
rm -f kubeconfig
- name: Notify deployment status
if: 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