I dislike ops, but oddly I enjoy running my own Linux server. Last year I got interested in deploying a container-based application using an automated Github CI/CD workflow to a $5/mo Linode VPS server.
This workflow builds the project for every branch and PR. The build is based on a
docker-compose.test.yml file with an
If the tests pass and it’s on the
main branch, this workflow compiles a production-ready image (this could use further optimization), pushes it to the Github Docker Repo, and redeploys it via SSH.
My biggest issues with this process is that Github Actions takes around 12 minutes to complete the workflow for a production deploy.
# .github/workflows/cicd.yml name: CICD # Kick off this pipeline on either a pull request or push to a branch on: push: pull_request: jobs: # Run this application's tests or simply build the image. test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run tests run: | if [ -f docker-compose.test.yml ]; then docker-compose --file docker-compose.test.yml build docker-compose --file docker-compose.test.yml run sut else docker build . --file Dockerfile fi # Build a production image and push to GitHub Packages. push: # Ensure test job passes before pushing image. needs: test runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v2 - name: Build image run: docker build . --file Dockerfile --tag docker.pkg.github.com/my-profile/my-repo/my-image:latest - name: Log into registry run: echo "$" | docker login docker.pkg.github.com -u $ --password-stdin - name: Push image run: docker push docker.pkg.github.com/my-profile/my-repo/my-image:latest # Run the deploy.sh script on a server deploy: needs: push runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v2 - name: Deploy run: | echo "$" > ssh_key chmod 700 ssh_key ssh -o StrictHostKeyChecking=no -i ssh_key $@$ "sh -s" < deploy.sh rm ssh_key
In the final step of the CICD pipeline above, Github SSHs onto my Linux server and redeploys the application using a custom
deploy.sh script. This is a generic example of what that script does. Things could definitely be easier with a
docker-compose.prod.yml file, but Compose can also be too helpful by abstracting so much complexity.
#!/bin/sh # This script runs on the docker server to deploy the application. It can be kicked off locally via: # # ``` # ssh my-server < deploy.sh # ``` set -e echo 'Removing dangling images' yes | docker image prune echo 'Pulling latest' docker pull docker.pkg.github.com/my-profile/my-repo/my-image:latest echo 'Stopping the container' docker container stop my-app || true until [ "`docker ps --filter 'name=my-app' --format ''`" == "" ]; do sleep 0.1; done; echo 'Starting the container' docker run --rm --name my-app -d -p 3000:3000 docker.pkg.github.com/my-profile/my-repo/my-image:latest startup.sh