Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz authored Mar 13, 2024
0 parents commit e550089
Show file tree
Hide file tree
Showing 205 changed files with 49,348 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .deploy/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: "3.9"
services:
app:
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
restart: always
ports:
- "8080"
container_name: ${APP_NAME}_app
environment:
VIRTUAL_HOST: ${HOST_DOMAIN}
VIRTUAL_PORT: 8080 # New default ASP.NET port -> https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/8.0/aspnet-port
LETSENCRYPT_HOST: ${HOST_DOMAIN}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes:
- app-mydb:/app/App_Data

app-migration:
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
restart: "no"
container_name: ${APP_NAME}_app_migration
profiles:
- migration
command: --AppTasks=migrate
volumes:
- app-mydb:/app/App_Data

networks:
default:
external: true
name: nginx

volumes:
app-mydb:
46 changes: 46 additions & 0 deletions .deploy/nginx-proxy-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
version: "3.9"

services:
nginx-proxy:
image: nginxproxy/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
labels:
- "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy"

letsencrypt:
image: nginxproxy/acme-companion:2.2
container_name: nginx-proxy-le
restart: always
depends_on:
- "nginx-proxy"
environment:
- [email protected]
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- /var/run/docker.sock:/var/run/docker.sock:ro

networks:
default:
name: nginx

volumes:
conf:
vhost:
html:
dhparam:
certs:
acme:
99 changes: 99 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
## Overview

This template uses the deployment configurations for a ServiceStack .NET 8 application. The application is containerized using Docker and is set up to be automatically built and deployed via GitHub Actions. The recommended deployment target is a stand-alone Linux server running Ubuntu, with an NGINX reverse proxy also containerized using Docker, which a Docker Compose file is included in the template under the `.deploy` directory.

### Highlights
- 🌐 **NGINX Reverse Proxy**: Utilizes an NGINX reverse proxy to handle web traffic and SSL termination.
- 🚀 **GitHub Actions**: Leverages GitHub Actions for CI/CD, pushing Docker images to GitHub Container Registry and deploying them on a remote server.
- 🐳 **Dockerized ServiceStack App**: The application is containerized, with the image built using `.NET 8`.
- 🔄 **Automated Migrations**: Includes a separate service for running database migrations.

### Technology Stack
- **Web Framework**: ServiceStack
- **Language**: C# (.NET 8)
- **Containerization**: Docker
- **Reverse Proxy**: NGINX
- **CI/CD**: GitHub Actions
- **OS**: Ubuntu 22.04 (Deployment Server)



## Deployment Server Setup

To successfully host your ServiceStack applications, there are several components you need to set up on your deployment server. This guide assumes you're working on a standalone Linux server (Ubuntu is recommended) with SSH access enabled.

### Prerequisites

1. **SSH Access**: Required for GitHub Actions to communicate with your server.
2. **Docker**: To containerize your application.
3. **Docker-Compose**: For orchestrating multiple containers.
4. **Ports**: 80 and 443 should be open for web access.
5. **nginx-reverse-proxy**: For routing traffic to multiple ServiceStack applications and managing TLS certificates.

You can use any cloud-hosted or on-premises server like Digital Ocean, AWS, Azure, etc., for this setup.

### Step-by-Step Guide

#### 1. Install Docker and Docker-Compose

It is best to follow the [latest installation instructions on the Docker website](https://docs.docker.com/engine/install/ubuntu/) to ensure to have the correct setup with the latest patches.

#### 2. Configure SSH for GitHub Actions

Generate a dedicated SSH key pair to be used by GitHub Actions:

```bash
ssh-keygen -t rsa -b 4096 -f ~/.ssh/github_actions
```

Add the public key to the `authorized_keys` file on your server:

```bash
cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
```

Then, add the *private* key to your GitHub Secrets as `DEPLOY_KEY` to enable GitHub Actions to SSH into the server securely.

#### 3. Set Up nginx-reverse-proxy

You should have a `docker-compose` file similar to the `nginx-proxy-compose.yml` in your repository. Upload this file to your server:

```bash
scp nginx-proxy-compose.yml user@your_server:~/
```

To bring up the nginx reverse proxy and its companion container for handling TLS certificates, run:

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

This will start an nginx reverse proxy along with a companion container. They will automatically watch for additional Docker containers on the same network and initialize them with valid TLS certificates.



## GitHub Repository Setup

Configuring your GitHub repository is an essential step for automating deployments via GitHub Actions. This guide assumes you have a `release.yml` workflow file in your repository's `.github/workflows/` directory, and your deployment server has been set up according to the [Deployment Server Setup](#Deployment-Server-Setup) guidelines.

### Secrets Configuration

Your GitHub Actions workflow requires the following secrets to be set in your GitHub repository:

1. **`DEPLOY_HOST`**: The hostname for SSH access. This can be either an IP address or a domain with an A-record pointing to your server.
2. **`DEPLOY_USERNAME`**: The username for SSH login. Common examples include `ubuntu`, `ec2-user`, or `root`.
3. **`DEPLOY_KEY`**: The SSH private key to securely access the deployment server. This should be the same key you've set up on your server for GitHub Actions.
4. **`LETSENCRYPT_EMAIL`**: Your email address, required for Let's Encrypt automated TLS certificates.

#### Using GitHub CLI for Secret Management

You can conveniently set these secrets using the [GitHub CLI](https://cli.github.com/manual/gh_secret_set) like this:

```bash
gh secret set DEPLOY_HOST --body="your-host-or-ip"
gh secret set DEPLOY_USERNAME --body="your-username"
gh secret set DEPLOY_KEY --bodyFile="path/to/your/ssh-private-key"
gh secret set LETSENCRYPT_EMAIL --body="[email protected]"
```

These secrets will populate environment variables within your GitHub Actions workflow and other configuration files, enabling secure and automated deployment of your ServiceStack applications.
35 changes: 35 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Build

on:
pull_request: {}
push:
branches:
- '**' # matches every branch

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

- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.*'

- name: build
run: dotnet build
working-directory: .

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

155 changes: 155 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
name: Release
permissions:
packages: write
contents: write
on:
# Triggered on new GitHub Release
release:
types: [published]
# Triggered on every successful Build action
workflow_run:
workflows: ["Build"]
branches: [main,master]
types:
- completed
# Manual trigger for rollback to specific release or redeploy latest
workflow_dispatch:
inputs:
version:
default: latest
description: Tag you want to release.
required: true

jobs:
push_to_registry:
runs-on: ubuntu-22.04
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
steps:
# Checkout latest or specific tag
- name: checkout
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
uses: actions/checkout@v3
- name: checkout tag
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
uses: actions/checkout@v3
with:
ref: refs/tags/${{ github.event.inputs.version }}

# Assign environment variables used in subsequent steps
- name: Env variable assignment
run: echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
# TAG_NAME defaults to 'latest' if not a release or manual deployment
- name: Assign version
run: |
echo "TAG_NAME=latest" >> $GITHUB_ENV
if [ "${{ github.event.release.tag_name }}" != "" ]; then
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
fi;
if [ "${{ github.event.inputs.version }}" != "" ]; then
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
fi;
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}


- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.*'

# Build and push new docker image, skip for manual redeploy other than 'latest'
- name: Build and push Docker image
run: |
dotnet publish --os linux --arch x64 -c Release -p:ContainerRepository=${{ env.image_repository_name }} -p:ContainerRegistry=ghcr.io -p:ContainerImageTags=${{ env.TAG_NAME }} -p:ContainerPort=80
deploy_via_ssh:
needs: push_to_registry
runs-on: ubuntu-22.04
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
steps:
# Checkout latest or specific tag
- name: checkout
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
uses: actions/checkout@v3
- name: checkout tag
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
uses: actions/checkout@v3
with:
ref: refs/tags/${{ github.event.inputs.version }}

- name: repository name fix and env
run: |
echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
echo "TAG_NAME=latest" >> $GITHUB_ENV
if [ "${{ github.event.release.tag_name }}" != "" ]; then
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
fi;
if [ "${{ github.event.inputs.version }}" != "" ]; then
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
fi;
- name: Create .env file
run: |
echo "Generating .env file"
echo "# Autogenerated .env file" > .deploy/.env
echo "HOST_DOMAIN=${{ secrets.DEPLOY_HOST }}" >> .deploy/.env
echo "LETSENCRYPT_EMAIL=${{ secrets.LETSENCRYPT_EMAIL }}" >> .deploy/.env
echo "APP_NAME=${{ github.event.repository.name }}" >> .deploy/.env
echo "IMAGE_REPO=${{ env.image_repository_name }}" >> .deploy/.env
echo "RELEASE_VERSION=${{ env.TAG_NAME }}" >> .deploy/.env
# Copy only the docker-compose.yml to remote server home folder
- name: copy files to target server via scp
uses: appleboy/[email protected]
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
port: 22
key: ${{ secrets.DEPLOY_KEY }}
strip_components: 2
source: "./.deploy/docker-compose.yml,./.deploy/.env"
target: "~/.deploy/${{ github.event.repository.name }}/"

- name: Run remote db migrations
uses: appleboy/[email protected]
env:
APPTOKEN: ${{ secrets.GITHUB_TOKEN }}
USERNAME: ${{ secrets.DEPLOY_USERNAME }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_KEY }}
port: 22
envs: APPTOKEN,USERNAME
script: |
set -e
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
cd ~/.deploy/${{ github.event.repository.name }}
docker compose pull
export APP_ID=$(docker compose run --entrypoint "id -u" --rm app)
docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/App_Data" --user root --rm app
docker compose up app-migration --exit-code-from app-migration
# Deploy Docker image with your application using `docker compose up` remotely
- name: remote docker-compose up via ssh
uses: appleboy/[email protected]
env:
APPTOKEN: ${{ secrets.GITHUB_TOKEN }}
USERNAME: ${{ secrets.DEPLOY_USERNAME }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_KEY }}
port: 22
envs: APPTOKEN,USERNAME
script: |
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
cd ~/.deploy/${{ github.event.repository.name }}
docker compose pull
docker compose up app -d
Loading

0 comments on commit e550089

Please sign in to comment.