Skip to content

Commit

Permalink
add Github Actions workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
ships committed Feb 1, 2024
1 parent cd49d0d commit b19fd08
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 8 deletions.
117 changes: 117 additions & 0 deletions .github/workflows/awsdeploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Based on https://docs.github.com/en/actions/deployment/deploying-to-your-cloud-provider/deploying-to-amazon-elastic-container-service

name: aws ecs deploy

on:
push:
branches:
- main

env:
AWS_REGION: us-east-1 # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: pubpub-v7 # set this to your Amazon ECR repository name
ECS_SERVICE: blake-core # set this to your Amazon ECS service name
ECS_CLUSTER: blake-ecs-cluster-staging # set this to your Amazon ECS cluster name
ECS_TASK_DEFINITION_TEMPLATE: blake-core # set this to the FAMILY of your task definition
CONTAINER_NAME: core # set this to the name of the container in the
# containerDefinitions section of your task definition

jobs:
build:
name: Build
runs-on: ubuntu-latest
environment: production

steps:
- name: Checkout
uses: actions/checkout@v4

- 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: ${{ env.AWS_REGION }}

- name: Get short sha
id: shortsha
run: echo "sha_short=$(git describe --always --abbrev=40 --dirty)" >> $GITHUB_OUTPUT

- 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 }}
IMAGE_TAG: ${{ steps.shortsha.outputs.sha_short }}
run: |
# Build a docker container and
# push it to ECR so that it can
# be deployed to ECS.
docker build \
--platform linux/amd64 \
--build-arg PACKAGE=core \
--build-arg PACKAGE_DIR=core \
-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
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
needs:
- build

steps:
- name: Checkout
uses: actions/checkout@v4

- 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: ${{ env.AWS_REGION }}

- name: Get short sha
id: shortsha
run: echo "sha_short=$(git describe --always --abbrev=40 --dirty)" >> $GITHUB_OUTPUT

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Retrieve Task Definition contents from template
id: get-taskdef
run: |
aws ecs describe-task-definition \
--task-definition $ECS_TASK_DEFINITION_TEMPLATE \
--query taskDefinition >> template_task_def.json
- name: Interpolate image tag
id: write-tag
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ steps.shortsha.outputs.sha_short }}
run: |
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@c804dfbdd57f713b6c079302a4c01db7017a36fc
with:
task-definition: template_task_def.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.write-tag.outputs.image }}

- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,74 @@ We currently have a race condition where dev will sometimes fail because we can'
- https://github.com/vercel/turbo/issues/460

`core` depends on `ui` which depends on `utils`. `utils` often takes longer to build than it does for `ui` to start building, which causes an error to be thrown because `utils` d.ts file has been cleared out during its build and hasn't been replaced yet. This generates an error, but is quick to resolve, so doesn't break actual dev work from beginning. It does make the console output messier though.


## Building and deploying for AWS environments

All change management to Knowledge Futures' production environment is done through github actions.
This environment runs on AWS ECS and leverages terraform to allow reproducible, parametric environments.

Services running in AWS ECS are scheduled using "Task Definitions", which are CRUDdy resources
including all details for a container. We don't want to tie code releases to terraform "infrastructure" changes,
but the service "declaration" relies on this Task Definition to exist.

Therefore based on community patterns we have seen, the flow is roughly this:
1. The infrastructure code in terraform declares a "template" Task Definition.
2. Terraform is told not to change the "service" based on changes to the Task Definition.
3. Any changes to the template will be picked up by the next deploy, which is done outside of Terraform.
4. Github Actions builds new containers on merge, and will use AWS-provided Actions to template the literal correct Task Definition and update the Service.

### Updating deployment topology and/or environment variables/container settings

To change "infrastructure settings", which include anything from networking to env vars,
make changes to `./infrastructure/terraform/aws`. Use `terraform apply` there to update
the infrastructure and/or Task Definition Template. See that directory for more info.

Then you must perform a Github Actions Deploy, either by pushing your changes to main or
with local `act` CLI.

### Updating container versions with github actions

The core automation workflow can be examined in [`awsdeploy.yml`](./.github/workflows/awsdeploy.yml)

There is a Dockerfile in this repository that builds a container for one package. You can use it like:

```
docker build \
--platform linux/amd64 \
--build-arg PACKAGE=core \
--build-arg PACKAGE_DIR=core \
-t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
.
```

Note how this matches the invocation used in the Actions workflow file.

We automatically build and push a container to AWS ECR with the github SHA as a tag.

### Using `act` to run the deploy events locally

If you need to build or validate a change and deploy to production, you can use the [`act` cli](https://github.com/nektos/act):

```
act \
-W .github/workflows/awsdeploy.yml \
--container-architecture linux/amd64 \
--secret-file ~/.aws/pubpub.secrets \
-j deploy
```

**Secrets:** Though you will have an `~/.aws/credentials` file, this is not the format for secrets access that
`act` requires, so I copy the key-value pairs in that file into a file that matches the Github
secrets called `~/.aws/pubpub.secrets`:

```
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY...
```

**Dirty worktree:** If you run `act` like this, the image will be conveniently tagged with the latest SHA plus `-dirty`.
Images tagged with a SHA alone should be idempotently built, but `-dirty` can be changed/overwritten.

**TODO:**
- [ ] allow deploying without a rebuild, so that a rollback is convenient
2 changes: 1 addition & 1 deletion infrastructure/terraform/aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module "service_core" {
{name = "API_KEY", value = "undefined"},
{name = "JWT_SECRET", value = "undefined"},
{name = "MAILGUN_SMTP_USERNAME", value = "undefined"},
{name = "NEXT_PUBLIC_PUBPUB_URL", value = "undefined"},
{name = "NEXT_PUBLIC_PUBPUB_URL", value = "https://v7.pubpub.org"},
{name = "NEXT_PUBLIC_SUPABASE_URL", value = "undefined"},
{name = "SENTRY_AUTH_TOKEN", value = "undefined"},
{name = "SUPABASE_SERVICE_ROLE_KEY", value = "undefined"},
Expand Down
11 changes: 4 additions & 7 deletions infrastructure/terraform/aws/modules/ecs-service/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ module "ecs_service" {
cluster_arn = var.cluster_info.cluster_arn
enable_execute_command = true

# allow github actions to update the service without confusing TF
ignore_task_definition_changes = true

cpu = var.resources.cpu
memory = var.resources.memory
desired_count = var.resources.desired_count
# execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
# task_role_arn = aws_iam_role.ecs_task_role.arn

# Container definition(s)
# TEMPLATE Container definition(s).
container_definitions = {

"${var.service_name}" = {
Expand Down Expand Up @@ -56,10 +59,4 @@ module "ecs_service" {
Environment = "${var.cluster_info.name}-${var.cluster_info.environment}"
Project = "Pubpub-v7"
}

# this lifecycle property allows us to update the version of the container image without terraform clobbering it later
# changing the container image creates a "revision" of the task definition
# lifecycle {
# ignore_changes = [services.core.container_definitions.core.image]
# }
}

0 comments on commit b19fd08

Please sign in to comment.