Add post about Forgejo Actions
All checks were successful
/ blog-pim (push) Successful in 11m49s

This commit is contained in:
Pim Kunis 2024-05-03 19:12:08 +02:00
parent 4abcb61db4
commit 8666240a9e
2 changed files with 144 additions and 1 deletions

View file

@ -0,0 +1,143 @@
---
layout: post
title: "Notes on Forgejo Actions"
date: 2024-05-03 19:12:00 Europe/Amsterdam
categories: forgejo actions ci act nix nixos kubernetes
---
I recently revived this blog, which is now running as a pod in my Kubernetes cluster.
In order to deploy a new version, I had to (manualy) perform the following steps:
1. Build static website from the Jekyll source
2. Build container image using the static website
3. Push container image to my container registry
4. Update Kubernetes deployment manifest to use the new container image
5. Apply the new manifest to the running blog deployment
This quickly gets annoying and I started looking for a Continuous Integration (CI) system to automate this process.
I am using [Forgejo](https://forgejo.org/) as my git server, which recently had its own CI system, Forgejo Actions, [graduated](https://forgejo.org/2023-07-release-v1201-0/) to alpha status.
Even though this is still alpha software, I decided to try it out for myself.
# Setting up a Forgejo Actions runner
[Forgejo Actions](https://forgejo.org/docs/v1.20/user/actions/) is forked from [Gitea Actions](https://docs.gitea.com/usage/actions/overview) which in turn is forked from [act](https://github.com/nektos/act).
Act is project to run GitHub Actions locally, which is the reason why Forgejo Actions are quite similar to Github Actions.
In fact, there are quite a few references to GitHub still.
Forgejo Actions work roughly as follows:
1. You install a runner, which is a server that accepts workloads from Forgejo Actions.
2. You register this runner with Forgejo.
3. You define workflows on a git repository that execute steps.
4. These workflows are submitted to a runner.
5. Either a Docker container or an LXC container is spinned up to run the workflow to completion.
6. The result is communicated back to Forgejo.
All my workloads run on Kubernetes, and wanted the Forgejo runner to run on Kubernetes as well.
Unfortunately, there is no Kubernetes-native way of running this runner, like for example [GitLab](https://docs.gitlab.com/runner/install/operator.html) does.
However, there is [an example deployment](https://code.forgejo.org/forgejo/runner/src/branch/main/examples/kubernetes) using a Docker-in-Docker setup.
This does however require to run the image as a privileged container, and (spoiler) this setup seems to be very unreliable.
# Using Forgejo Actions to build Nix derivations
Both the static website and container image for my blog are built using Nix (which I wrote about [here]({% post_url nix-jekyll-derivation/2024-05-03-nix-jekyll-derivation.md %})).
Therefore, it made sense to me to use the [official Docker image](https://hub.docker.com/r/nixos/nix).
However, it seems Forgeo Actions has an undocumented dependency on `/bin/sleep` being present inside the container image.
It seems sleep is being used to check whether the image is working correctly.
Nix being very unconventional, does not have `/bin/sleep` by default in its Docker image.
Therefore, I extended the offical Docker image using Nix below.
The `coreutils` package contains the `sleep` binary which is linked inside the image using `pathsToLink`.
```nix
let
nixFromDockerHub = pkgs.dockerTools.pullImage {
imageName = "nixos/nix";
imageDigest = "sha256:b3dc72ab3216606d52357ee46f0830a0cc32f3e50e00bd490efa1a8304e9f99d";
sha256 = "sha256-FvDlbSnCmPtWTn4eG3hu8WVK1Wm3RSi2T+CdmIDLkG4=";
finalImageTag = "2.22.0";
finalImageName = "nix";
};
};
in
{
packages.forgejo-nix-action = pkgs.dockerTools.buildImage {
name = "forgejo-nix-action";
tag = "latest";
fromImage = nixFromDockerHub;
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = with pkgs; [ coreutils ];
pathsToLink = [ "/bin" ];
};
};
}
```
This is sufficient to be able to run `nix build` commands inside Forgejo Action, for example:
```yaml
on: [ push ]
jobs:
blog-pim:
runs-on: docker
container:
image: git.kun.is/home/forgejo-nix-action:687d16c49ea7936068bac64ec68c480a9d681962
steps:
- name: Clone repository
run: git clone ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git src
- name: Build image
run: nix build --out-link image ./src#packages.x86_64-linux.container-image
```
This workflows clones the source code of my blog, then builds a container image for it using Nix.
# Pushing the image to a container registry
Being able to automatically build container images is useless if we don't publish them somewhere.
[Skopeo](https://github.com/containers/skopeo) is a real life-saver here, and I would strongly recommend this tool when interacting with container images.
Before settling with Skopeo, I first tried using just Docker.
However, for many operations, Docker needs a running Docker daemon and this is annoying to setup inside an already annoying Docker-in-Docker setup.
Another option I explored was Podman but it was not even able to read the container images built by Nix.
These are the steps I ended up with to push a container image to a registry using Skopeo:
```yaml
- name: Log into container registry
run: /bin/skopeo login --tls-verify --username ${{ vars.RUNNER_USER }} --password ${{ secrets.RUNNER_TOKEN }} ${GITHUB_SERVER_URL}
- name: Push image to container registry
run: |
/bin/skopeo --insecure-policy copy docker-archive:image docker://${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY_OWNER}/blog-pim:latest
```
# Failing to deploy to Kubernetes
Having pushed a new container image to a registry, we just have to updated the Kubernetes deployment to use the new image.
This is unfortunately something I have not been able to get working.
For some reason, anytime I run `nix run` inside a step, the k3s systemd service crashes.
It seems during the execution of `nix run`, `etcd` is continuously timing out.
After a few seconds of this, k3s deems it unhealthy and restarts itself.
I have a hunch running `nix run` puts so much strain on the disk that `etcd` is not able to quickly write to disk.
It is known that `etcd` needs very quick disk times to function correctly.
I have however not been able to reproduce this issue outside of the container.
This leads me to believe that perhaps the Docker-in-Docker setup is the cause of this issue.
# Conclusions
I think Forgejo Actions really needs a first-class Kubernetes treatment.
It seems to me having Action runners on Kubernetes gives free improvements in terms of scalability and management.
Going further, I will not be using Forgejo Actions for CI, but will explore some systems that run on Kubernetes natively.
In particular, I'm interested in [Argo Workflows](https://argo-workflows.readthedocs.io/en/latest/), and perhaps I will give [GitLab](https://docs.gitlab.com/runner/install/operator.html) a try.
However in general, and this is not a critique on Forgejo Actions in particular, I don't fully agree with the way these "Actions" systems work.
It seems to leading way to use these actions is to use a `runs-on` directive and specify some container image, perhaps the latest Ubuntu version (who thought it was a good idea to turn OSes into images?).
Then, people install some tools using the image's package manager after which their actual jobs runs.
Unless they are very well maintained, these actions will probably stop working after a while when the underlying container image is discontinued from support.
Or perhaps a package is updated, which is incompatable with the action.
Or there is some hidden dependency that changes in the underlying container image.
Or ...
I believe Nix could be an amazing tool to improve these "Actions" systems.
Using Nix, you wouldn't need a bloated container image with lots of tools you won't need.
You could take advantage of Nixpkgs and install only the tools you need to run your job.
Everything inside the image would be version-pinned and reproducible and will continue to work in 50 years.
I'm not entirely sure how such a system would even look, but my gut tells me it should be possible.
I'm unfortunately not aware whether such a system currently exists though.

View file

@ -1,7 +1,7 @@
---
layout: post
title: "Building a Jekyll Static Website using Nix"
date: 2024-05-02 23:51:00 Europe/Amsterdam
date: 2024-05-03 15:44:00 Europe/Amsterdam
categories: jekyll nix blog ruby
---