项目地址
https://github.com/JessicaWin/aws-fargate-example
项目架构图
1.构建nodejs项目
$ sudo npm i -g @nestjs/cli
$ nest new aws-fargate-example
$ cd aws-fargate-example
$ npm install
$ npm run start
项目启动成功后,浏览器输入http://localhost:3000/,可以看到Hello World!
2.构建docker image并启动
创建Dockerfile:
#use alpine version to decrease docker image size, https://hub.docker.com/_/node?tab=description&page=1&ordering=last_updated
FROM node:18-alpine3.14 As development
WORKDIR /app
COPY package*.json ./
RUN npm install
#copies files from a local source location to a destination in the Docker container
COPY . .
RUN npm run build
CMD ["npm", "run", "start"]
# Multi Staging Build
FROM node:18-alpine3.14 As production
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /app
COPY package*.json ./
# Create node_modules that is necessary only for production
RUN npm install production
#copies files from a local source location to a destination in the Docker container
COPY . .
# Copy dist generated in development stage
COPY --from=development /app/dist ./dist
EXPOSE 3000
CMD ["npm", "run", "start:prod"]
指令含义:
FROM
:指定 基础镜像,因此一个Dockerfile
中FROM
是必备的指令,并且必须是第一条指令WORKDIR:制定后续RUN
,CMD
,ENTRYPOINT
,COPY
和ADD指令的工作目录
COPY
:将从构建上下文目录中<源路径>
的文件/目录复制到新的一层的镜像内的<目标路径>
位置。RUN
:执行命令行命令CMD:
容器启动命令ARG:
构建参数- ENV:设置环境变量
EXPOSE:
声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务
构建和启动docker
$ docker build -t aws-fargate-example:latest --target=development ./
$ docker run -it -d -p 3001:3000 aws-fargate-example:latest
docker启动成功后,浏览器输入http://localhost:3001/,可以看到Hello World!
3.使用docker compose管理容器
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
Compose 使用的三个步骤:
-
使用 Dockerfile 定义应用程序的环境。
-
使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
-
最后,执行 docker-compose up 命令来启动并运行整个应用程序。
# set based on docker version, refer to https://docs.docker.com/compose/compose-file/compose-file-v3/
version: '3.6'
services:
dev:
container_name: aws-fargate-example-dev
image: aws-fargate-example-dev:${IMAGE_TAG:-latest}
build:
context: .
target: development
dockerfile: ./Dockerfile
command: npm run start
ports:
- 3000:3000
networks:
- aws-fargate-example-network
restart: unless-stopped
prod:
container_name: aws-fargate-example-prod
image: aws-fargate-example-prod:${IMAGE_TAG:-latest}
build:
context: .
target: production
dockerfile: ./Dockerfile
command: npm run start:prod
ports:
- 3000:3000
networks:
- aws-fargate-example-network
restart: unless-stopped
networks:
aws-fargate-example-network:
构建和启动docker
# use ocker-compose to build image for all services/stages
$ docker-compose build
# use ocker-compose to start specific service
$ docker-compose up dev
$ docker-compose up prod
4.部署deploy bucket
在部署ecr之前,我们首先部署一个bucket用作以后各个aws资源的deploy bucket
deploy bucket cloudformation template
AWSTemplateFormatVersion: '2010-09-09'
Description: Cloudformation template for creating deployment related S3 buckets
Parameters:
Stage:
Type: String
Default: develop
Description: the stage of the environment like develop, production
Resources:
DeploymentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub com.jessica.${Stage}-deploy-bucket
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: [GET, PUT, HEAD, POST, DELETE]
AllowedOrigins: ['*']
Outputs:
DeploymentBucket:
Value: !Ref DeploymentBucket
Export:
Name: !Sub ${Stage}-deploy-bucket
deploy bucket 部署脚本
#!/bin/bash
echo "Deploying deploy bucket ..."
DELETE=$1
STAGE_LIST=( develop production )
for i in "${STAGE_LIST[@]}"
do
STAGE=$i
REGION="ap-northeast-1"
if [ "$STAGE" = "develop" ];then
REGION="ap-southeast-1"
fi
echo "Deploying deploy-bucket.yml to $STAGE in $REGION ..."
if [ "$DELETE" = "remove" ];then
aws cloudformation delete-stack --stack-name $STAGE-deploy-bucket
else
stackOutput=`aws cloudformation create-stack --stack-name $STAGE-deploy-bucket --template-body file://./deploy-bucket.yml --parameters ParameterKey=Stage,ParameterValue=$STAGE --region $REGION 2>&1`
if [[ "$stackOutput" =~ "AlreadyExistsException" ]]; then
noUpdate=`aws cloudformation update-stack --stack-name $STAGE-deploy-bucket --template-body file://./deploy-bucket.yml --parameters ParameterKey=Stage,ParameterValue=$STAGE --region $REGION 2>&1`
if [[ "$noUpdate" =~ "No updates are to be performed" ]]; then
echo "No updates are to be performed"
else
echo $noUpdate
fi
else
echo $stackOutput
fi
fi
done
5.部署ECR repository
部署ecr repository用来存储docker image
ecr cloudformation template
service: aws-fargate-example-ecr
provider:
name: aws
region: ${opt:region, 'ap-southeast-1'}
stage: ${opt:stage, 'develop'}
stackName: ${self:provider.stage}-${self:service}
deploymentBucket:
name: com.jessica.${self:provider.stage}-deploy-bucket
serverSideEncryption: AES256
resources:
Resources:
NestjsstarterEcr:
Type: AWS::ECR::Repository
Properties:
RepositoryName: aws-fargate-example
RepositoryPolicyText:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- 'ecr:ListImages'
- 'ecr:BatchGetImage'
- 'ecr:GetDownloadUrlForLayer'
- 'ecr:PutImage'
ecr部署命令:
# deploy to develop stage
$ sls deploy --stage develop --region ap-southeast-1
# deploy to production stage
$ sls deploy --stage production --region ap-norheast-1
6.部署IAM Role
部署ECS Fargate Service至少需要两个role:
- Task Role:ECS task执行时使用的role,需要的权限根据task
- ExecutionRole:ECS service启动task使用的role,需要的权限
此外,因为本项目中的fargate service用到了auto scale,所以额外需要一个auto scale role
service: aws-fargate-example-iam
provider:
name: aws
region: ${opt:region, 'ap-southeast-1'}
stage: ${opt:stage, 'develop'}
stackName: ${self:provider.stage}-${self:service}
deploymentBucket:
name: name: com.jessica.${self:provider.stage}-deploy-bucket
serverSideEncryption: AES256
resources:
Resources:
ECSPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub '${self:provider.stage}_ECSPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
- ecr:CompleteLayerUpload
- ecr:GetDownloadUrlForLayer
- ecr:InitiateLayerUpload
- ecr:PutImage
- ecr:UploadLayerPart
- ecr:GetAuthorizationToken
Resource:
- '*'
Effect: Allow
- Action:
- ecs:*
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeListeners
- elasticloadbalancing:ModifyListener
- elasticloadbalancing:DescribeRules
- elasticloadbalancing:ModifyRule
- lambda:InvokeFunction
- cloudwatch:DescribeAlarms
- sns:Publish