Removes old blog

This commit is contained in:
2024-09-30 20:19:23 -04:00
parent a724935eb9
commit 6dc9859a94
45 changed files with 0 additions and 1170 deletions

View File

@@ -1,6 +0,0 @@
+++
paginate_by = 7
path = "posts"
title = "Posts"
sort_by = "date"
+++

View File

@@ -1,54 +0,0 @@
+++
title = "Structed and passively collected metrics via AWS CloudWatch"
date = "2022-11-12"
+++
AWS is a vast and sprawling set of services. It can be hard to find the hidden
gems like this one so I wanted to point this one out.
Structured metrics are very helpful to monitoring the health and function of an
software system.
- Do you want to know how long a particular transaction typically takes?
- How fast your database queries are?
- How long external APIs take to respond?
- Fire an alert when a particular function on the site happens too many times? Or too few times?
...plus a million other things specific to whatever system you're working on.
There are a lot of great tools for doing this and one that you might not know
about is AWS CloudWatch Metric Filters. If you're already on AWS then you
should consider these because it requires only that your application logs to CloudWatch.
If you're on ECS then the
[awslogs](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html)
log driver for Docker gets you that nearly for free. By "free" I mean that your
application itself can have *zero* dependencies on AWS services and not require
any AWS credentials or libraries to start pumping out metrics that you can
visualize, alert on and record over time.
The [AWS
docs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/MonitoringLogData.html)
themselves offer the canonical reference for configuring these so I won't go
into detail here.
However, the gist is that for a log filter you define the following properties
- A filter pattern for extracting a discrete metric value out of a log entry
- A metric name to store the value in
- An optional dimension for sub-classifying the value
- And finally a log group to extract the metric values from
After that you just run the application and as the logs roll in the metric
values get pumped out. Then you can [define alarms for
alerting](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Create-alarm-on-metric-math-expression.html)
on them, [graph
them](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html),
[define autoscaling
rules](https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html#policy-creating-alarm-console)
from them and more.
To conclude - AWS is big and hairy. While there are benefits to staying
platform agnostic, some AWS services don't require much or any coupling of your
application code to take advantage of. Cloudwatch Metrics is one of those
services and you can get a lot of value out of it with not much effort.

View File

@@ -1,25 +0,0 @@
+++
title = "Syntax test post"
date = "2022-11-08"
+++
## Code Block
```haskell
main :: IO ()
main = do
putStrLn "Nice"
}
```
{% mermaid() %}
journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me
{% end %}

View File

@@ -1,30 +0,0 @@
+++
title = "My multiroom audio setup"
date = "2022-11-08"
+++
I've put my home audio solution together out of the following components.
- [Snapcast](https://github.com/badaix/snapcast)
- [MPD](https://www.musicpd.org/)
- [Librespot](https://github.com/librespot-org/librespot)
- [Shairport-sync](https://github.com/mikebrady/shairport-sync)
- A mini-PC in my closet running the above software
- Two Raspberry Pi 4s
- Four Raspberry Pi Zero Ws
- Some desktop speakers and some Bluetooth speakers (wired to the Pis)
Each of the Raspberry Pis is in a room or porch attached to a speaker.
Snapcast lets me take an audio source and synchronize it across multiple
clients. Each of the Raspberry Pis are running a `snapclient` instance and play
whatever the `snapserver` instance tells them to.
Snapcast is setup to send whichever of the streams (MPD, Spotify, Shairport-sync/AirPlay) is playing audio to each of the clients that are connected to it.
This lets me or anyone else on my WiFi network play directly on one or more of the speakers - each named for the room that they're in using either Spotify, AirPlay, picking from my own music collection or by pointing at a URL (like to a podcast episode).
This works out great and we've used it at home for the past year.
I'd like to get the podcast experience to a more seamless place but it's pretty OK right now using AirMusic on my phone to play audio to the speakers over AirPlay.

View File

@@ -1,43 +0,0 @@
+++
title = "HTTPS @ Home"
date = "2022-11-08"
+++
I run a lot of services at home.
This includes, but isn't limited to
- [ArchiveBox](https://archivebox.io/)
- [VaultWarden](https://github.com/dani-garcia/vaultwarden)
- [Navidrome](https://github.com/navidrome/navidrome)
- [Plex](https://plex.tv)
- [LibrePhotos](https://github.com/LibrePhotos/librephotos)
- This blog
and a lot more.
Pretty much anything that's served up over HTTP is always nice if not necessary to have behind TLS.
[LetsEncrypt](https://letsencrypt.org/) long ago brought free certs to the masses and there are a lot of tools for automating that nowadays.
My preferred approach for getting all the unnecessary nonsense I self-host at home behind TLS is [Caddy](https://caddyserver.com).
I have a super straight forward setup, generally:
- Run Caddy in a docker container
- Create a wildcard CNAME record in my DNS pointing at my home's (effectively) static IP
- Add an entry in my Caddyfile for each services I'm running at home on its own subdomain
- If it's a service then I add it with a `reverse_proxy` block
- If it's a static site (like this) then there's a block for
- If it's something I want only accessible on my home network then I put a block like
```txt
@local_network {
path *
remote_ip <home subnet here>
}
```
in the directive. And voila.
Then tell Caddy to reload the config and I'm done.

View File

@@ -1,32 +0,0 @@
+++
title = "vi-mode editing in most places"
date = "2022-11-13"
+++
# Vi/Vim bindings
For my sake, I prefer to have Vim bindings in as many places as possible.
Most shells can be configured to use Vim bindings by putting `set -o vi` somewhere in your shell startup script.
If you're using ZSH then you'll probably want an additional binding to restore CTRL-R reverse history search.
`bindkey '^R' history-incremental-search-backward`
For CLI tools that use the `readline` library then you can configure its input mode using a `.inputrc` file in your `$HOME` directory.
This affects REPLs like `ghci` and tools like `psql`.
```txt
set editing-mode vi
$if mode=vi
set keymap vi-command
# these are for vi-command mode
Control-l: clear-screen
set keymap vi-insert
# these are for vi-insert mode
Control-l: clear-screen
$endif
```

View File

@@ -1,109 +0,0 @@
+++
title = "The allure of cloud services - AWS Transfer Server"
date = "2023-01-05"
+++
## Build vs buy
I like cloud services as much as the next person. Definitely not _too_ much but
definitely, umm, much?
Anyway, I think cloud specific services can _sometimes_ be worth the sort of
vendor lock-in that they facilitate.
Vendor lock in sucks, but so does re-inventing the wheel. As mentioned in an
[earlier post](/posts/cloudwatch-metric-filters/) it can be pretty nice to
create alerts and application metrics passively from your log files.
Knowing when to use vendor-specific services and APIs versus rolling your own
is, in some ways, more art than science. Reasonable teams won't change their
infrastructure provider very many times and the difference between the two
choices is often less about lock-in and more about whether or not the problem
you're solving is really the special unicorn that you want it to be or not.
Sometimes, the cloud vendor solution is just bad, though.
## Case in point - AWS Transfer Server
On my team, we need to accept SFTP file transfers from about 20-30 vendors.
Sadly we can't avoid SFTP for this. It's an industry standard for the kind of
data we're receiving and we aren't in a position to dictate otherwise.
AWS offers a suite of products which include a hosted SFTP solution, [AWS Transfer](https://aws.amazon.com/aws-transfer-family/).
SFTP is simple enough and I'd ultimately like the uploaded files in an S3
bucket so seems like a great fit, right?
Wrong.
You have to jump through [a hundred](https://aws.amazon.com/secrets-manager/)
[different](https://aws.amazon.com/lambda/)
[hoops](https://aws.amazon.com/iam/) to event attempt to have password-based
authentication working on this service.
The services involved to make password based authentication work here aren't in
and of themselves a problem. The problem is the multiple points of failure they
represent. Each service can fail on its own, the permissions between the
services can fail, or the code in the Lambda itself can fail.
Debugging it was a nightmare. It ultimately worked but I wasn't sure _why_. I
had a strange permissions error about the IAM Role being used to either launch
the Lambda or the role used to access the S3 bucket for uploads. I don't
remember at this point. But it was really clear that if it ever broke again
then myself or whatever poor soul had to work on it was going to going to
regret the choice of technologies to make this work.
You know what is easy and simple and well understood? OpenSSH and Linux user
accounts.
In the end, the AWS Transfer option proved to be just complicated and enciting
enough for me to waste about a day and a half on it before I realize, in a
drunken stupor, that I'd be better off creating a snow-flaked EC2 instance and
calling it a day.
## Resisting temptation
I do think I could have identified that this was a bad idea before I wasted
over a day. One doesn't have to be doomed to repeat this mistake to learn the
lesson here.
The way to avoid this is to interrogate the path before you start.
Empathy is a good tool, here.
Picture the thing working as intended. Then imagine someone else (or future you
a year from now) having to search for how to change something or track down an
error.
- Are they likely to find many other people using these services? (In this case, no)
- Are the services involved purpose build for the task at hand? (In this case, no none of them)
- Does your team have pre-existing expertise around the services involved? (Again, no)
- What benefits does this approach yield over a traditional solution? (Upload to S3 is nice)
- Are those benefits important to your use case? (Not for us)
I didn't ask myself of those questions. I _really_ should have. Because the
answers are easy to get and they clearly indicate AWS Transfer Server is not
the best answer.
# Timeboxes
Another tool that could have helped here is a timebox.
A timebox is when you decided to give yourself a fixed, and often pretty short,
amount of time to accomplish something or to at least get meaningful insight
into a potential solution.
Sometimes I will demand that a working solution can be done inside a given
timebox or then the approach is abandoned. But other times I might just say
that I'm going to spend X amount of time on an approach and then make a point
to re-evaluate how long the complete solution will take.
It's important to remember how valuable timeboxes can be. The more time we
waste on things like this then the more attached we become to it. [Sunk
cost](https://en.wikipedia.org/wiki/Escalation_of_commitment) can get even the
best of us.
For me the simplest tool for avoiding sunk cost fallacy is to timebox risky
endeavors like this one. The time inside a timebox starts out as a write off. I
never feel bad about discarding failed results inside a timebox. I should use
them more aggressively.

View File

@@ -1,15 +0,0 @@
+++
title = "Rhetorical techniques - The uncharitably extrapolated strawman"
date = "2022-12-06"
+++
Ever find yourself arguing with someone? It's usually an idiot, isn't it?
When arguing an idiot will often "take a position" or "make a claim".
They'll say something like "you shouldn't kill people" to defend their stupid little point about "the law" or "murder" or some other nonsense.
--- draft below
- "here's what you do"
- "I'm really surprised to hear you'd just let Nazis kill your friends and family, but OK"

View File

@@ -1,24 +0,0 @@
+++
title = "Twitter's ultimate demise"
date = "2022-11-18"
+++
I _think_ Twitter will die but I don't exactly hope that it will.
The best thing that could come out of this is that Mastodon gets critical mass
such that it can survive on its own. We don't have any popular services besides
email which are decentralized anymore.
Mastodon is more complicated than Twitter because you have to "pick a server"
and that's true but at the end of the day all that matters is where the people
are.
If enough content generating posters will just make the shift at once then this
thing can be self-sustaining...even if it is more complicated.
Twitter was more complicated than not using Twitter and people still started
using it. This is perenially true for all new things.
Anyway, it's late and I'm tired.
Long live microblogging.

View File

@@ -1,59 +0,0 @@
+++
title = "Setting up Woodpecker CI at home"
date = "2022-11-10"
+++
[Woodpecker CI](https://woodpecker-ci.org/) is very straight forward and seems to basically have what I want from a CI tool without much beyond that.
- In-repo pipeline definition
- Docker job execution
- Docker image for deploying server and agents
- Parallel execution on a single agent (looking at you GitHub)
But of course I still had to fight with it for a couple hours to make it do something super simple. Because everything is awful and I'm an idiot.
I just wanted to have it build (`zola build`) this site and deploy it (`rsync`).
Building was easy enough - I needed a docker image with Zola on it. I couldn't use the "official" zola one, though, because it lacks even `/bin/sh` and Woodpecker needs some way to execute your scripts on the container you want a job to run in.
So I made a small alpine based image to do that - no problem.
Then I needed to rsync. And SSH auth and secrets.
There are a million ways for things to go wrong here that are painful and just take time to figure out.
Here are the issues I ran into - many were my fault and others were simply not-great UX from Woodpecker.
- Woodpecker supports per-repo secrets - great - but they have to be specified
by name in the YAML of each pipeline step that wants to use them. This is
clearly stated in the docs and I followed the instructions clearly but at one
point I removed them from the YAML and didn't realize it.
Unfortunately Woodpecker doesn't have a great way of letting you know you're
referencing secrets which you haven't asked for because it exposes secrets as
just plain environment variables.
I don't really know what I would want from Woodpecker here - this was my
fault and it was hard to notice once I'd mistakenly removed the secrets.
- Putting "-" in your secret names breaks your YAML and Woodpecker fails
without much information when you give it broken pipeline YAML.
- Copying environment variables into files and using them as ssh keys is super
annoying and error prone. Between key file permissions and the whole process
failing because the file contents were empty because the secret being echo'ed
into the file was actually not present. Ugh.
- SSH [TOFU](https://en.wikipedia.org/wiki/Trust_on_first_use) is great for users but annoying for automation.
You have to pick from one of several annoying options.
- use ssh-keyscan during the automation to trust the host key
- Muck with ssh command line arguments to turn StrictHostKeyChecking off
- Pre-emptively load an `authorized_hosts` file onto the machine you're deploying from.
The last option is probably the best because it's the most secure but it's
also annoying because it means you can't use a generic image for that
deployment step.
Ultimately it all worked out - I ended up using some Alpine docker images, sshpass and rsync.
But it's always a fight to get these things working. Even when I'm using software that seems like such a nice fit for my personal approach like Woodpecker.