Derp
The What
I want to make a chatbot for fun and profit. But also to do reminders, tell me the weather, remind me of calendar items... personal-assistent-type stuff.
Discord demands that its chatbots maintain a WebSocket gateway with an always-on connection back to their servers to receive realtime events (reactions, mentions, and the like). No serverless platform I have available to me provides for such usage. Every serverless provider I can find expects a shorter-lived request/response model well suited for HTTP traffic or API calls. That means I need to run a server for the WebSocket gateway.
Because it's the future I want the main "body" of the chatbot to run in a Docker container. Any other piece of functionality for the chatbot can run on a serverless platform. Thus, I really only need to host a single Docker container in a production environment.
Finding a way to easily and reliably run a single Docker container in a production environment is a funny kind of problem to my programmer brain used to dozens of microservices that all run basically independently.
- Kubernetes is probably overkill. It's a single service with no available HTTP endpoint that should restart if it crashes. Most k8s solutions require at least two nodes, which is immediately too expensive given the resources I actually need.
- Run it in my home network. I could probably put the gateway on a cheapo computer somewhere in my house and script updates that way. So far I've been able to not have my network accept incoming requests; if I use something like GitHub Actions as CI that would have to change. I could also poll GitHub to see if the bot's repo changed, but that's yucko city (ask me about being #1 at GitHub sometime).
- Run a tiny, single production server. This seems the likeliest route. Keeps running if my home network goes down, reachable from wherever, easy to manage ops-wise.
Sounds like I'm running a $5/month server somewhere and pushing new Docker images to it when the main gateway repo builds successfully.
The How
Probably build the Docker container via this: https://github.com/docker/build-push-action and push to Docker Hub (in a GitHub action, natch).
But how to get it to the server?
Current solution is a messy combo detailed here: https://github.com/derp-bot/bot/commit/033fd0b596fcb50c3265272507a82196f625b665
- First, build the thing: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/.github/workflows/deploy-the-bot.yml#L13
- Then, deploy the thing: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/.github/workflows/deploy-the-bot.yml#L38
- That involves fun things like finding the might-have-changed-IP of the server: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/.github/workflows/deploy-the-bot.yml#L53
- ...and then writing an SSH config: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/.github/workflows/deploy-the-bot.yml#L61
- ...and then call my deploy script: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/.github/workflows/deploy-the-bot.yml#L76
- ...which is here: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/bin/deploy.sh
- It mostly SSH's into my box with some env: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/bin/deploy.sh#L21
- The script running on my prod box is here: https://github.com/derp-bot/bot/blob/033fd0b596fcb50c3265272507a82196f625b665/bin/run-latest-derp.sh
- It logs into Docker Hub.
- Then pulls the latest derp container.
- Kills the running derp container.
- Stands up another derp instance.
I don't like this. I think this is cursed and Rube-Goldberg-esque.
The Possible Future How
build-and-deploy-action: https://github.com/docker/build-push-action
Don't write the SSH config, use key trust: https://serverfault.com/questions/856194/securely-add-a-host-e-g-github-to-the-ssh-known-hosts-file
Using Docker Context and Docker Compose: https://www.docker.com/blog/how-to-deploy-on-remote-docker-hosts-with-docker-compose/
Docker Context: https://docs.docker.com/engine/context/working-with-contexts/
Environment Variables in Compose: https://docs.docker.com/compose/environment-variables/
Not using Docker Machine: https://stackoverflow.com/questions/43505196/how-to-deploy-a-docker-container-on-a-remote-ubuntu-server