Deploy to Amazon ECS #5485
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |