Skip to content

Commit

Permalink
Update project directory structure to match templates.
Browse files Browse the repository at this point in the history
Change deployment to docker compose removing use of AWS ECS.
  • Loading branch information
Layoric committed Aug 2, 2023
1 parent bfdd6b5 commit 74673f8
Show file tree
Hide file tree
Showing 54 changed files with 180 additions and 251 deletions.
25 changes: 25 additions & 0 deletions .deploy/docker-compose-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: "3.9"
services:
${APP_NAME}:
image: ghcr.io/${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:
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ services:
network_mode: bridge

letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
image: jrcs/letsencrypt-nginx-proxy-companion:2.0
container_name: nginx-proxy-le
restart: always
environment:
Expand All @@ -38,8 +38,3 @@ volumes:
dhparam:
certs:
acme:

networks:
default:
external:
name: webproxy
138 changes: 20 additions & 118 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,24 @@
# ServiceStack mix GitHub Actions
`release.yml` generated from `x mix release-ecr-aws`, this template in designed to help with automating CI deployments to AWS ECS and dedicated AWS ECS cluster.
This is a cheap way to start without an AWS Application Load Balancer (ALB) and also be in a situation that will easier to add one once the web service needs additional scale or high availability.
The `release.yml` in designed to help with CI deployment to a dedicated server with SSH access, Docker and Docker Compose.

## Overview
`release.yml` is designed to work with a ServiceStack app templates deploying directly to a single server in a dedicated ECS cluster via templated GitHub Actions.
A docker image is built and stored on GitHub's `ghcr.io` docker registry when a GitHub Release is created.

## Setup
### Create unique ECS cluster
For this setup, it is best to create a separate cluster as cluster will only have the single instance in it running.
This pattern is to start from a good base with AWS ECS and automated CI deployment while avoiding the higher costs of needing to run an application load balancer (ALB).
GitHub Actions specified in `release.yml` then copy files remotely via scp and use `docker-compose` to run the app remotely via SSH.

If/when you can justify the cost of an ALB for easier scaling and zero downtime deployment, the GitHub Action `release.yml` can be slightly modified to be used with a re-created or different ECS Service that is configured to be used with an Application Load Balancer and Target Group.
## Deployment server setup
To get this working, a server needs to be setup with the following:

### Elastic IP (optional)
The reason you might want to register this first is because we are only running one EC2 instance and hosting our own `nginx-proxy` on the same instance as the applications.
Since an `A` record will be pointing there, one advantage of not using an auto-assigned IP is that we can reassign the elastic IP if for what ever reason the instance goes down or is lost.
- SSH access
- docker
- docker-compose
- ports 443 and 80 for web access of your hosted application

## Launch to EC2 Instance
When launching the EC2 instance, you'll need to select an 'ECS optimized' AMI as the image used for your instance.
### Choose AMI
The easiest way to find the latest Amazon Linux 2 image for this is to go to the [AWS documentation for ECS-optimized AMIs and look up your region here](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux).
This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.

Using the AMI ID (starts with `ami-`) at the bottom, search in the 'Community AMIs' tab on the first step of the `Launch EC2 Instance` wizard.
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.

### Choose Instance Type
A t2.micro or larger will work fine, this pattern can be used to host multiple applications on the 1 server so if the number of applications gets larger, you might need a larger instance type.
> Note this pattern is suitable for testing prototypes or low traffic applications as it is cost effective and makes it easy to bundle multiple apps onto 1 EC2 instance.
### Configure Instance
Under `IAM role`, use the `ecsInstanceRole`, if this is not available, see [AWS documentation for the process of checking if it exists and creating it if needed](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html).

You will also want to add the following Userdata script (in the `Configure` step of the launch wizard) with your own `ECS_CLUSTER` value. This tells the ecs-agent running on the instance which ECS cluster the instance should join.

```bash
#!/bin/bash
cat <<EOS >/etc/ecs/ecs.config
ECS_CLUSTER=northwinds
ECS_AVAILABLE_LOGGING_DRIVERS=["awslogs", "syslog"]
ECS_ENABLE_CONTAINER_METADATA=true
EOS
```

Note down your cluster name as it will need to be used to create the cluster in ECS before it is visible.
See [`ECS Container Agent Configuration`](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-agent-config.html) for more information.

### Add Storage
The default of 30gb is fine but take into account how large/how many applications you'll have running.

### Configure Security Groups
You'll want to expose at least ports 80 and 443.

### Setup Docker-compose and nginx-proxy
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.
> This is done via docker-compose rather than via ECS for simplicity.

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

Expand All @@ -63,82 +29,18 @@ 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.

## GitHub Repository setup
The `release.yml` assumes 6 secrets have been setup.
The `release.yml` uses the following secrets.

- 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.
- AWS_ECS_CLUSTER - Cluster name in ECS, this should match the value in your Userdata.
- HOST_DOMAIN - Domain/submain of your application, eg `northwind.example.com` .
- 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 are used to populate variables within GitHub Actions and other configuration files.
These secrets can use the [GitHub CLI](https://cli.github.com/manual/gh_secret_set) for ease of creation.

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`
- `AmazonECS_FullAccess`

Once the application is successfully deployed the first time, reduced access for both ECR and ECS can be used instead. For application updates, the GitHub Action can use the following policy.

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ecr:GetRegistryPolicy",
"ecr:PutImageTagMutability",
"ecr:GetDownloadUrlForLayer",
"ecr:DescribeRegistry",
"ecr:GetAuthorizationToken",
"ecr:ListTagsForResource",
"ecr:UploadLayerPart",
"ecr:ListImages",
"ecr:PutImage",
"ecr:UntagResource",
"ecr:BatchGetImage",
"ecr:CompleteLayerUpload",
"ecr:DescribeImages",
"ecr:TagResource",
"ecr:DescribeRepositories",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:ReplicateImage",
"ecr:GetRepositoryPolicy",
"ecs:SubmitTaskStateChange",
"ecs:UpdateContainerInstancesState",
"ecs:RegisterContainerInstance",
"ecs:DescribeTaskDefinition",
"ecs:DescribeClusters",
"ecs:ListServices",
"ecs:UpdateService",
"ecs:ListTasks",
"ecs:ListTaskDefinitionFamilies",
"ecs:RegisterTaskDefinition",
"ecs:SubmitContainerStateChange",
"ecs:StopTask",
"ecs:DescribeServices",
"ecs:ListContainerInstances",
"ecs:DescribeContainerInstances",
"ecs:DeregisterContainerInstance",
"ecs:TagResource",
"ecs:DescribeTasks",
"ecs:UntagResource",
"ecs:ListTaskDefinitions",
"ecs:ListClusters"
],
"Resource": "*"
}
]
}
```
> Further permission reduction can be done by reducing what resources can be accessed.
> Application permissions can be controlled via `taskRoleArn`, see [AWS docs for details](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html).
These secrets are used to populate variables within GitHub Actions and other configuration files.

## What's the process of the `release.yml`?
## What's the process of `release.yml`?

![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ecr-aws-diagram.png)
![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)
24 changes: 18 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@ on:

jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: checkout
uses: actions/checkout@v3.0.0
uses: actions/checkout@v2.0.0

- name: setup .net core
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: '6.0'
dotnet-version: 6.0.100

- name: build
run: dotnet build
working-directory: ./src/Northwind/
working-directory: .

- name: test
run: |
dotnet test
if [ $? -eq 0 ]; then
echo TESTS PASSED
else
echo TESTS FAILED
exit 1
fi
working-directory: ./Northwind.Tests

Loading

0 comments on commit 74673f8

Please sign in to comment.