Removes old blog
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
paginate_by = 7
|
||||
path = "posts"
|
||||
title = "Posts"
|
||||
sort_by = "date"
|
||||
+++
|
||||
@@ -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.
|
||||
@@ -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 %}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user