Day eight: deploying
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 1m57s

This commit is contained in:
Lewis Dale 2024-05-05 20:47:23 +01:00
parent 1b1c3e6636
commit 2c4187a0df
2 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,145 @@
---
title: "Learning Go: Day Eight"
date: 2024-05-08T08:00:00.0Z
tags:
- learning
- go
excerpt: "Getting the project deployed via Gitea actions"
---
So that I can do the whole build-in-public thing properly, I always want my code to automatically deploy. I've got [Gitea Actions](https://docs.gitea.com/usage/actions/overview) on my Gitea server, so I can use those to build, deploy, and start a Go binary.
## Building and copying a binary
This is the simplest part. I had a decent template from my Eleventy action that I was able to take and turn into the following workflow:
```yaml
name: Build and copy to prod
on:
push:
jobs:
build-and-copy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22
- name: Build binary
run: go build -o dist/oopsie
- name: Install SSH Key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_KEY }}
known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
- name: Copy to prod
run: scp -rp dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:oopsie/
```
This installs Go on the action runner, builds the binary, and then uses SCP to copy the compiled binary onto my server. To facilitate this, I created a user on my VPS, created a new SSH key, and added the public key to the `.ssh/authorized_keys` file. Then I added the private key to the Gitea action secrets, along with the `known_hosts` entry for my git server, the name of the user I created, and the hostname for my VPS.
## Running the software
So now I need to run my compiled software. I can create a systemd service to do this, and then run it as the user I've created. First of all I create a new file, `/etc/systemd/user/oopsie.service`:
```systemd
[Unit]
Description=Daemon for the Oopsie service
[Service]
Type=simple
#User=
#Group=
ExecStart=/home/<user>/oopsie/oopsie
Restart=on-failure
StandardOutput=file:%h/log_file
[Install]
WantedBy=default.target
```
Then, as the user I've created I run:
```bash
systemctl --user daemon-reload
systemctl --user start oopsie.service
```
And can confirm my service is running locally:
```bash
curl -X POST http://localhost:8000
> This was a POST request!
```
## Nginx proxy
Next up I need to use Nginx's `proxy_pass` directive to direct any requests to https://oopsie.lewisdale.dev to my running service. Again, this was mostly lifted from an existing template I already had:
```nginx
server {
listen 80;
listen [::]:80;
server_name oopsie.lewisdale.dev;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
# SSL configuration
#
listen 443 ssl;
listen [::]:443 ssl;
server_name oopsie.lewisdale.dev;
# Include certificate params
include snippets/certs/lewisdale.dev;
ssl_certificate /etc/letsencrypt/live/lewisdale.dev/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/lewisdale.dev/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Woo, now I can actually access my service over the internet!
![Screenshot from Mozilla Firefox, showing oopsie.lewisdale.dev responding with "This was a GET request!"](./src/images/oopsie-working.png)
## Restarting the service
Finally, I can use the `-o` argument with the `RemoteCommand` [SSH config option](https://linux.die.net/man/5/ssh_config) to execute a command. I can use that to run `systemctl restart`:
```yaml
- name: Restart the service
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} -o RemoteCommand="systemctl --user restart oopsie.service"
```
## Nope
Ah, that's not quite correct. My first build failed:
```bash
scp: oopsie//oopsie: Text file busy
```
I can't overwrite a file while it's in use. Instead, I have to stop the service, copy the file, and then start the service again:
```yaml
- name: Stop the service
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} -o RemoteCommand="systemctl --user stop oopsie.service"
- name: Copy to prod
run: scp -rp dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:oopsie/
- name: Restart the service
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} -o RemoteCommand="systemctl --user start oopsie.service"
```
And that works! It deploys successfully. Ironically, there's a minor bit of downtime while it does, but for now that's really not an issue. You can see the project in progress on [its deployed home](https://oopsie.lewisdale.dev) or [on the Git repo](https://git.lewisdale.dev/lewis/oopsie).

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB