Skip to content

Instantly share code, notes, and snippets.

@gistlyn
Last active August 31, 2022 04:59
Show Gist options
  • Save gistlyn/2106a97cf9cdf3a8b0ce91145a507144 to your computer and use it in GitHub Desktop.
Save gistlyn/2106a97cf9cdf3a8b0ce91145a507144 to your computer and use it in GitHub Desktop.
release-ecr-vanilla
version: "3.9"
services:
MyApp:
image: ${IMAGE_REPO}:${RELEASE_VERSION}
restart: always
network_mode: bridge
ports:
- "80"
environment:
VIRTUAL_HOST: ${HOST_DOMAIN}
LETSENCRYPT_HOST: ${HOST_DOMAIN}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes:
- ${APP_NAME}-mydb:/app/App_Data
${APP_NAME}-migration:
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
restart: "no"
profiles:
- migration
command: --AppTasks=migrate
volumes:
- ${APP_NAME}-mydb:/app/App_Data
volumes:
${APP_NAME}-mydb:
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- dhparam:/etc/nginx/dhparam
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
network_mode: bridge
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion:2.0
container_name: nginx-proxy-le
restart: always
environment:
- DEFAULT_EMAIL=you@example.com
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
network_mode: bridge
volumes:
conf:
vhost:
html:
dhparam:
certs:
acme:

ServiceStack mix GitHub Actions

release.yml generated from x mix release-ecr-vanilla, this template in designed to help with automating CI deployment to a dedicated server with SSH access while using AWS ECR as container image registry.

Overview

release.yml is designed to work with a ServiceStack app deploying directly to a single server via SSH. A docker image is built and stored on AWS ECR container registry when a GitHub Release is created. GitHub Actions specified in release.yml then copy files remotely via scp and use docker-compose to run the app remotely via SSH.

Deployment server setup

To get this working, a server needs to be setup with the following:

  • SSH access
  • docker
  • docker-compose
  • ports 443 and 80 for web access of your hosted application

This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.

When setting up your server, you'll want to use a dedicated SSH key for access to be used by GitHub Actions. GitHub Actions will need the private SSH key within a GitHub Secret to authenticate. This can be done via ssh-keygen and copying the public key to the authorized clients on the server.

To let your server handle multiple ServiceStack applications and automate the generation and management of TLS certificates, an additional docker-compose file is provided via the x mix template, nginx-proxy-compose.yml. This docker-compose file is ready to run and can be copied to the deployment server.

For example, once copied to remote ~/nginx-proxy-compose.yml, the following command can be run on the remote server.

docker-compose -f ~/nginx-proxy-compose.yml up -d

This will run an nginx reverse proxy along with a companion container that will watch for additional containers in the same docker network and attempt to initialize them with valid TLS certificates.

If the container doesn't have the environment variable VIRTUAL_HOST set, it will be ignored.

GitHub Repository setup

The release.yml assumes 8 secrets have been setup.

  • AWS_ACCESS_KEY_ID - AWS access key for programmatic access to AWS APIs.
  • AWS_SECRET_ACCESS_KEY - AWS access secrets for programmatic access to AWS APIs.
  • AWS_REGION - default region for AWS API calls.
  • DEPLOY_HOST - hostname used to SSH to, this can either be an IP address or subdomain with A record pointing to the server.
  • DEPLOY_PORT - SSH port, usually 22.
  • DEPLOY_USERNAME - the username being logged into via SSH. Eg, ubuntu, ec2-user, root etc.
  • DEPLOY_KEY - SSH private key used to remotely access deploy server/app host.
  • LETSENCRYPT_EMAIL - Email address, required for Let's Encrypt automated TLS certificates.

These secrets can use the GitHub CLI for ease of creation. Eg, using the GitHub CLI the following can be set.

gh secret set AWS_ACCESS_KEY_ID -b"<AWS_ACCESS_KEY_ID>"
gh secret set AWS_SECRET_ACCESS_KEY -b"<AWS_SECRET_ACCESS_KEY>"
gh secret set AWS_REGION -b"<AWS_REGION, eg us-east-1>"
gh secret set DEPLOY_HOST -b"<DEPLOY_HOST, domain or subdomain for your application and server host.>"
gh secret set DEPLOY_PORT -b"<DEPLOY_PORT, eg SSH port, usually 22>"
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME, the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.>"
gh secret set DEPLOY_KEY -b"<DEPLOY_KEY, SSH private key used to remotely access deploy server/app host.>"
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL, Email address for your TLS certificate generation, eg me@example.com>"

These secrets are used to populate variables within GitHub Actions and other configuration files.

For the AWS access, a separate user specifically for deploying via GitHub Actions should be used.

The policies required for the complete initial setup will be:

  • AmazonEC2ContainerRegistryFullAccess
name: Release
on:
release:
types: [published]
jobs:
push_to_registry:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: repository name fix
run: echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: login_ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Create ECR repo if not exists.
env:
ECR_REPOSITORY: ${{ env.image_repository_name }}
run: aws ecr describe-repositories --repository-names ${ECR_REPOSITORY} || aws ecr create-repository --repository-name ${ECR_REPOSITORY}
- name: Build and push to ECR
id: push_image_to_ecr
uses: docker/build-push-action@v2.2.2
with:
file: Dockerfile
context: .
push: true
tags: ${{ steps.login_ecr.outputs.registry }}/${{ env.image_repository_name }}:${{ github.event.release.tag_name }}
deploy_via_ssh:
needs: push_to_registry
runs-on: ubuntu-20.04
steps:
- name: checkout
uses: actions/checkout@v2
- name: repository name fix and env
run: |
echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
echo "domain=${{ secrets.DEPLOY_HOST }}" >> $GITHUB_ENV
echo "letsencrypt_email=${{ secrets.LETSENCRYPT_EMAIL }}" >> $GITHUB_ENV
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: login_ecr
uses: aws-actions/amazon-ecr-login@v1
- name: docker-compose file prep
uses: danielr1996/envsubst-action@1.0.0
env:
RELEASE_VERSION: ${{ github.event.release.tag_name }}
IMAGE_REPO: ${{ steps.login_ecr.outputs.registry }}/${{ env.image_repository_name }}
HOST_DOMAIN: ${{ env.domain }}
LETSENCRYPT_EMAIL: ${{ env.letsencrypt_email }}
with:
input: .deploy/docker-compose-template.yml
output: .deploy/MyApp-docker-compose.yml
- name: copy compose file via scp
uses: appleboy/scp-action@v0.1.1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
port: ${{ secrets.DEPLOY_PORT }}
key: ${{ secrets.DEPLOY_KEY }}
source: ".deploy/MyApp-docker-compose.yml"
target: "~/"
- name: Set the value
run: |
echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}" >> $GITHUB_ENV
echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> $GITHUB_ENV
echo "AWS_REGION=${{ secrets.AWS_REGION }}" >> $GITHUB_ENV
echo "USERNAME=${{ secrets.DEPLOY_USERNAME }}" >> $GITHUB_ENV
- name: remote docker-compose up via ssh
uses: appleboy/ssh-action@v0.1.4
env:
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ env.AWS_REGION }}
ECR_ID: ${{ steps.login_ecr.outputs.registry }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_KEY }}
port: ${{ secrets.DEPLOY_PORT }}
envs: AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION,ECR_ID
script: |
aws ecr get-login-password --region $AWS_REGION | docker login -u AWS --password-stdin $ECR_ID
docker-compose -f ~/.deploy/MyApp-docker-compose.yml up -d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment