@povio/ecs-deploy-cli
Version:
Use this tool to deploy a Docker image to ECR and ECS with CI or manually.
286 lines (210 loc) • 7.61 kB
Markdown
# ECS Deploy CLI
Use this tool to deploy a Docker image to ECR and ECS with CI or manually.
Features:
- Environment and SSM credentials storage conventions
- GitHub Actions pipeline example
- Cross-platform (made with TypeScript/Javascript, external requirements: `git`, `docker`)
Examples:
- [NestJs](./examples/nestjs) Docker and Pipeline
# Usage
```bash
yarn add @povio/ecs-deploy-cli
```
## Configure
### .config/${STAGE}.ecs-deploy.yaml
```yaml
accountId: "000000000000"
region: us-east-1
taskFamily: myapp-dev-backend
serviceName: myapp-dev-backend
clusterName: myapp-dev
# build and upload to ecr with `ecs-deploy build backend --stage dev`
build:
- name: backend
repoName: myapp-backend
#context: ./test
#dockerfile: Dockerfile
platform: linux/amd64
# Docker Buildx Bake support
#bakeFile: "./docker-bake.hcl"
#bakeTarget: "backend" # optional: specific target
environmentValues:
# resolved at build time
- name: RELEASE
valueFrom: "func:release"
- name: BUILD_TIMESTAMP
valueFrom: "func:timestamp"
- name: BUILD_ENV_VAR_1
value: "static value"
# deploy to ecs with `ecs-deploy deploy --stage dev`
taskDefinition:
- name: default
# resolved at deploy time, requires SSM access
template: arn:aws:ssm:::parameter/myapp-dev/backend/task-definition
containerDefinitions:
- name: backend
# name of build above or any other docker path
image: backend
# inserted into task definition and resolved at deploy time
environmentValues:
- name: DEPLOY_TIMESTAMP
valueFrom: "func:timestamp"
- name: TASK_ENV_VAR_1
value: "static value"
# inserted into task definition and resolved at task init
secrets:
STAGE2: arn:aws:ssm:::parameter/myapp-dev/backend/task-definition
# resolved at runtime using `ecs-deploy config backend --stage dev`
configs:
- name: backend
destination: ./.config/myapp-dev.backend.yml
# optional template, to diff the resolved data from
template:
values:
# load config from ./.config/${stage}.backend.template.yml
# and interpolate ${arn:aws:ssm..} and ${env:ENV_VALUE} values
# load them onto the root
- name: "@"
configFrom: backend.template
- name: "@"
configFrom: backend.override
optional: true
# simple value mapping
- name: database__password
valueFrom: arn:aws:ssm:::parameter/myapp-dev/database/password
# JSON object mapping
- name: database
valueFrom: arn:aws:ssm:::parameter/myapp-dev/database
- name: database__host
valueFrom: env:DATABASE_HOST
```
### Example
Where `configFrom: backend.template` and the config file is `.config/${stage}.backend.template.yml`:
```yaml
stage: ${func:stage}
release: ${func:release}
database:
username: myapp2
password: ${arn:aws:ssm:::parameter/myapp-dev/database/password}
debug: ${env:DEBUG}
```
the output will be at the set destination, for example `./.config/myapp-dev.backend.yml`:
```yaml
database:
username: myapp2
password: the-password-from-ssm
debug: the-value-from-the-environment
```
## Run
```bash
yarn ecs-deploy --help
# Build a new image from the current git commit and push to ECR
yarn ecs-deploy build <name> --stage my-stage
# Push an existing image to ECR (tag of image needs to be the same as RELEASE or the git commit hash )
# yarn ecs-deploy push <name> --stage my-stage
# Deploy the task definition to ECS
yarn ecs-deploy deploy [name] --stage my-stage
# Deploy and clean up old unused image tags older than 30 days
yarn ecs-deploy deploy [name] --stage my-stage --untagUnused --days 30
# Generate a config script
yarn ecs-deploy bootstrap [name] --stage my-stage
```
## Run Options
Descriptions for useful flags. Use `--help` for a comprehensive list.
#### --ignoreGitChanges
Use this flag while debugging the build. This might have unintended consequences - never deploy a build made using this flag. (build only)
#### --skipEcrExistsCheck
Speed up builds if you know the ECR image does not exist. (build only)
#### --skipPush
Only build the image. Useful for testing.
#### --buildx
Use [docker buildx](https://docs.docker.com/buildx/working-with-buildx/) to build on ARM / Apple M1.
#### Docker Buildx Bake Support
Enable Docker Buildx Bake by adding `bake-file` to your build configuration:
```yaml
build:
- name: backend
repoName: myapp-backend
bake-file: "./docker-bake.hcl"
bake-target: "api-final" # optional: specific target, otherwise builds all
```
When `bake-file` is present, the CLI automatically:
- Uses `docker buildx bake` instead of standard Docker build
- Sets `--set "*.tags=<generated-tag>"` to override all image tags
- Supports HCL, JSON, and YAML bake files
- Optionally targets specific bake targets
#### --watch
In CI, wait for ecs-deploy to complete. This could take a while so set a timeout on the CI.
#### --untagUnused
Clean up old unused image tags from ECR repositories after deployment. This will remove tags that are older than the specified number of days (default: 30) while preserving the currently deployed image and the newly deployed image.
#### --days
Specify the number of days to keep image tags when using `--untagUnused`. Tags older than this number of days will be removed (default: 30).
#### --untagPrefix
Only untag images whose tags start with this prefix. Providing this argument overrides `build.prefix` property in config.
#### untag
Standalone command to untag images.
## Required AWS IAM Permissions
To use all features of this CLI (build, push, deploy, untag, describe, etc.), your IAM user/role needs the following permissions:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage",
"ecr:BatchDeleteImage",
"ecs:DescribeServices",
"ecs:UpdateService",
"ecs:DescribeTaskDefinition",
"ecs:RegisterTaskDefinition",
"ecs:ListTasks",
"ecs:DescribeTasks"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Resource": "*"
}
]
}
```
**Notes:**
- You may scope `Resource` to specific ARNs for tighter security.
- If you use SSM for secrets or task definitions, SSM permissions are required.
## How it works
The build script builds and pushes a Docker image to ECR.
The deploy script generates a ECS task definition using a template stored on SSM and deploys it to ECS.
The bootstrap script generates a config script with resolved values from SSM and environment variables.
<img src="./docs/arch-overview.svg">
## Development
### Test locally
Set up `./test/.config/myapp-dev.ecs-deploy.yml` with credentials to do a E2E test.
```bash
# alias for `ecs-deploy` while developing
yarn start build backend --cwd ./test --stage myapp-dev
yarn start bootstrap --stage myapp-dev --verbose --pwd ./test
yarn test:watch
```
### Release
Set new version in `package.json`.
```bash
yarn build
```