Skip to content

Deploy to Amazon ECS #4667

Deploy to Amazon ECS

Deploy to Amazon ECS #4667

Workflow file for this run

name: Deploy to Amazon ECS
env:
aws_region: eu-west-2
webservers_cluster_name: webservers
sidekiq_cluster_name: sidekiq
website_task_definition_name: website
website_service_name: website
api_task_definition_name: api
api_service_name: api
anycable_task_definition_name: anycable
anycable_service_name: anycable
sidekiq_task_definition_name: sidekiq
sidekiq_service_name: sidekiq
ecr_repository_rails: "webserver-rails"
dockerfile_rails: "docker/rails.Dockerfile"
ecr_repository_nginx: "webserver-nginx"
dockerfile_nginx: "docker/nginx.Dockerfile"
ecr_repository_anycable_go: "anycable-go"
dockerfile_anycable_go: "docker/anycable-go.Dockerfile"
# TODO: (Required) Move this to be a secret
s3_bucket: "exercism-v3-assets"
on:
push:
branches: [main]
workflow_dispatch:
schedule:
- cron: 0 */2 * * *
jobs:
deploy:
if: github.repository == 'exercism/website-deployer'
name: Upload images to ECR, assets to S3, and definition to ECS
runs-on: self-hosted
steps:
###############
###############
# Setup Steps #
###############
###############
- name: Checkout
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef
with:
aws-access-key-id: ${{ secrets.AWS_DEPLOY_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_DEPLOY_SECRET_ACCESS_KEY }}
aws-region: ${{ env.aws_region }}
- name: Login to Amazon ECR
id: login_to_ecr
uses: aws-actions/amazon-ecr-login@2f9f10ea3fa2eed41ac443fee8bfbd059af2d0a4
#########################
#########################
# Build the Rails image #
#########################
#########################
- name: Build and Push Rails Docker
id: build_rails
env:
ECR_REGISTRY: ${{ steps.login_to_ecr.outputs.registry }}
ECR_REPOSITORY: ${{ env.ecr_repository_rails }}
IMAGE_NAME: "${{ steps.login_to_ecr.outputs.registry}}/${{ env.ecr_repository_rails }}:${{ github.sha }}"
DOCKERFILE: ${{ env.dockerfile_rails }}
GEOIP_LICENSE_KEY: ${{ secrets.GEOIP_LICENSE_KEY }}
GEOIP_ACCOUNT_ID: ${{ secrets.GEOIP_ACCOUNT_ID }}
run: |
# Build a docker container and push it to ECR
docker build -t $IMAGE_NAME -f $DOCKERFILE \
--build-arg GEOIP_LICENSE_KEY=$GEOIP_LICENSE_KEY \
--build-arg GEOIP_ACCOUNT_ID=$GEOIP_ACCOUNT_ID \
--build-arg GEOIP_CACHE_BUSTER=$(date -d "`date +%Y%m01`" +%Y-%m-%d) \
--build-arg BUNDLER_VERSION=$(awk '/BUNDLED WITH/ {getline;$1=$1;print}' Gemfile.lock) \
.
docker push $IMAGE_NAME
# Retag this as the latest
docker tag $IMAGE_NAME $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
# Retag it as website below because the `:` hurts us otherwise
docker tag $IMAGE_NAME website
# Save the output for writing to the task definiton
echo "image=${IMAGE_NAME}" >> $GITHUB_OUTPUT
##################################
##################################
# Extact and upload assets to s3 #
##################################
##################################
- name: Copy assets from Dockerfile
run: |
container_id=$(docker create website)
docker cp ${container_id}:/opt/exercism/website/public/assets assets
docker rm ${container_id}
- name: Upload assets to s3
run: |
aws s3 sync assets s3://${{ env.s3_bucket }}/assets --acl public-read --no-progress --cache-control max-age=31536000
#########################
#########################
# Build the Nginx image #
#########################
#########################
- name: Build and Push Nginx Docker
id: build_nginx
env:
ECR_REGISTRY: ${{ steps.login_to_ecr.outputs.registry }}
ECR_REPOSITORY: ${{ env.ecr_repository_nginx }}
IMAGE_NAME: "${{ steps.login_to_ecr.outputs.registry}}/${{ env.ecr_repository_nginx }}:${{ github.sha }}"
DOCKERFILE: ${{ env.dockerfile_nginx }}
run: |
# Build a docker container and push it to ECR
docker build -t $IMAGE_NAME -f $DOCKERFILE .
docker push $IMAGE_NAME
# Retag this as the latest
docker tag $IMAGE_NAME $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
# Save the output for writing to the task definiton
echo "image=${IMAGE_NAME}" >> $GITHUB_OUTPUT
##################################
##################################
# Update website Task definition #
##################################
##################################
# We get the existing task definition to update
# Note that this is passed into the first update action
# but then chained from action to action for the others.
- name: Download website task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.website_task_definition_name }} --query taskDefinition > website-task-definition.json
- name: Update website | puma container
id: update_definition_for_website_puma
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: website-task-definition.json
container-name: puma
image: "${{ steps.build_rails.outputs.image }}"
- name: Update website | nginx container
id: update_definition_for_website_nginx
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: ${{ steps.update_definition_for_website_puma.outputs.task-definition }}
container-name: nginx
image: "${{ steps.build_nginx.outputs.image }}"
# Push the new definition up to ECS and deploy the new service from it
- name: Deploy Website
uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
with:
task-definition: ${{ steps.update_definition_for_website_nginx.outputs.task-definition }}
service: ${{ env.website_service_name }}
cluster: ${{ env.webservers_cluster_name }}
wait-for-service-stability: false
##############################
##############################
# Update api Task definition #
##############################
##############################
# We get the existing task definition to update
# Note that this is passed into the first update action
# but then chained from action to action for the others.
- name: Download api task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.api_task_definition_name }} --query taskDefinition > api-task-definition.json
- name: Update api | puma container
id: update_definition_for_api_puma
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: api-task-definition.json
container-name: puma
image: "${{ steps.build_rails.outputs.image }}"
- name: Update api | nginx container
id: update_definition_for_api_nginx
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: ${{ steps.update_definition_for_api_puma.outputs.task-definition }}
container-name: nginx
image: "${{ steps.build_nginx.outputs.image }}"
# Push the new definition up to ECS and deploy the new service from it
- name: Deploy API
uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
with:
task-definition: ${{ steps.update_definition_for_api_nginx.outputs.task-definition }}
service: ${{ env.api_service_name }}
cluster: ${{ env.webservers_cluster_name }}
wait-for-service-stability: false
##################################
##################################
# Update anycable Task definition #
##################################
##################################
# We get the existing task definition to update
# Note that this is passed into the first update action
# but then chained from action to action for the others.
- name: Download anycable task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.anycable_task_definition_name }} --query taskDefinition > anycable-task-definition.json
- name: Update anycable | anycable_ruby container
id: update_definition_for_anycable_ruby
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: anycable-task-definition.json
container-name: anycable_ruby
image: "${{ steps.build_rails.outputs.image }}"
- name: Update anycable | anycable_go container
id: update_definition_for_anycable_go
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: ${{ steps.update_definition_for_anycable_ruby.outputs.task-definition }}
container-name: anycable_go
image: "${{ steps.login_to_ecr.outputs.registry}}/anycable-go-pro:latest"
- name: Update anycable | nginx container
id: update_definition_for_anycable_nginx
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: ${{ steps.update_definition_for_anycable_go.outputs.task-definition }}
container-name: nginx
image: "${{ steps.build_nginx.outputs.image }}"
# Push the new definition up to ECS and deploy the new service from it
- name: Deploy Anycable
uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
with:
task-definition: ${{ steps.update_definition_for_anycable_nginx.outputs.task-definition }}
service: ${{ env.anycable_service_name }}
cluster: ${{ env.webservers_cluster_name }}
wait-for-service-stability: false
##################################
##################################
# Update Sidekiq Task definition #
##################################
##################################
# We get the existing task definition to update
# Note that this is passed into the first update action
# but then chained from action to action for the others.
- name: Download sidekiq task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.sidekiq_task_definition_name }} --query taskDefinition > sidekiq-task-definition.json
# Set puma to rails
- name: Update the sidekiq definition
id: update_definition_for_sidekiq
uses: aws-actions/amazon-ecs-render-task-definition@61b0c00c3743b70987a73a1faf577f0d167d1574
with:
task-definition: sidekiq-task-definition.json
container-name: sidekiq
image: "${{ steps.build_rails.outputs.image }}"
# Push the new definition up to ECS and deploy a new service from it.
- name: Deploy Sidekiq
uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
with:
# Ensure this is the final step in the daisy-chain above
task-definition: ${{ steps.update_definition_for_sidekiq.outputs.task-definition }}
service: ${{ env.sidekiq_service_name }}
cluster: ${{ env.sidekiq_cluster_name }}
wait-for-service-stability: true