Skip to content

Commit

Permalink
Add procs config field, add gitea act_runner example
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasBak committed Sep 30, 2023
1 parent 5e67fe2 commit 51968cf
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 35 deletions.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Dockerfile
.git
autoscaler-proxy
cloud-init.yml
dev.env
config.yml
variables.yml
TODO
43 changes: 43 additions & 0 deletions .github/workflows/act_runner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build act_runner image

on:
push:
tags:
- '*'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/[email protected]
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/[email protected]
with:
context: .
file: example/act_runner/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
autoscaler-proxy
cloud-init.yml
dev.env
config.yml
/config.yml
variables.yml
TODO
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The main motivation was to replace my drone.io setup that uses a [Hetzner autosc
Test it with:

```
HCLOUD_TOKEN=your_token go run .
HCLOUD_TOKEN=your_token go run . example/docker/config.yml
# New terminal window:
DOCKER_HOST=tcp://127.0.0.1:8081 docker ps
Expand Down Expand Up @@ -44,21 +44,19 @@ autoscaler:
name: autoscaler
ssh_authorized_keys:
- ${AUTOSCALER_AUTHORIZED_KEY}
listen_addr:
127.0.0.1:8081:
net: unix
addr: /var/run/docker.sock
```
If you haven't provided some of the fields, it will default to the values here.
The configuration file supports some basic templating for the following variables:
| Field | Description |
| --------------------------- | ----------------------------------------------------------- |
| `SERVER_RSA_PRIVATE` | Private ssh key for the server, generated by the autoscaler |
| `SERVER_RSA_PUBLIC` | Public ssh key for the server, generated by the autoscaler |
| `AUTOSCALER_AUTHORIZED_KEY` | Public key of the autoscaler |
| Field | Description |
| --------------------------- | ----------------------------------------------------------------------------------------- |
| `SERVER_RSA_PRIVATE` | Private ssh key for the server, generated by the autoscaler |
| `SERVER_RSA_PUBLIC` | Public ssh key for the server, generated by the autoscaler |
| `AUTOSCALER_AUTHORIZED_KEY` | Public key of the autoscaler |
| `AUTOSCALER_AUTHORIZED_KEY` | Public key of the autoscaler |
| `env.[VARIABLE]` | The value of the environment variable specified (from the process running the autoscaler) |

You can also put key-value pairs in `autoscaler.cloud_init_variables` and reference them the same way. If you want to add some secrets you can create a yaml file with [sops](https://github.com/mozilla/sops) and put the file name in `autoscaler.cloud_init_variables_from`.

Expand Down Expand Up @@ -102,3 +100,9 @@ autoscaler:
```

This would install tailscale and run `tailscale up` with an authkey from an encrypted file (secrets.yml). The autoscaler would wait until it was able to connect to `some_tailscale_ip:22` from the server before starting to proxy connections, so we know the server is fully configured and working as intended.

There is also the option to configure `procs`, which lets you run other processes when starting this program. The processes are started when the server is ready to receive connections, and is stopped before shutting down the autoscaler. See example in `example/act_runner/config.yml`.

## Autoscaling gitea runner

There is a working example of an autoscaling gitea runner in `example/act_runner/`, it uses a "slightly patched" version of the runner and scales a server up when doing ci jobs, and down when idle.
2 changes: 1 addition & 1 deletion autoscaler/cloud-init.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func CreateCloudInitFile(template map[string]interface{}, opts AutoscalerOpts, s
variables["SERVER_RSA_PUBLIC"] = string(pubkeyBytes)
variables["AUTOSCALER_AUTHORIZED_KEY"] = string(authorizedKeyBytes)

config := utils.BuildTemplate(utils.TemplateMap(variables), template)
config := utils.BuildTemplate(utils.WithEnvMap(utils.TemplateMap(variables)), template)

d, err := yaml.Marshal(&config)

Expand Down
15 changes: 3 additions & 12 deletions autoscaler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"io"
"net"
"sync"
"time"

"github.com/JonasBak/autoscaler-proxy/utils"
Expand All @@ -15,8 +14,9 @@ import (
var log = utils.Logger().WithField("pkg", "autoscaler")

type UpstreamOpts struct {
Net string `yaml:"net"`
Addr string `yaml:"addr"`
Net string `yaml:"net"`
Addr string `yaml:"addr"`
Name *string `yaml:"name"`
}

type AutoscalerOpts struct {
Expand Down Expand Up @@ -73,7 +73,6 @@ func serverOptions(client *hcloud.Client, opts AutoscalerOpts, cloudInit string)

type Autoscaler struct {
client *hcloud.Client
mx *sync.Mutex
server *hcloud.Server
serverOpts hcloud.ServerCreateOpts

Expand Down Expand Up @@ -111,7 +110,6 @@ func New(opts AutoscalerOpts) Autoscaler {
serverOpts := serverOptions(client, opts, cloudInit)

as := Autoscaler{
mx: new(sync.Mutex),
client: client,
serverOpts: serverOpts,
sshClient: sshClient,
Expand Down Expand Up @@ -183,9 +181,6 @@ func (as *Autoscaler) evaluateScaledown(ctx context.Context) error {
return nil
}

as.mx.Lock()
defer as.mx.Unlock()

timeSinceLastInteraction := time.Now().Sub(as.lastInteraction)

log.WithField("time_since_last_interaction", timeSinceLastInteraction.String()).Debug("Evaluating scaledown")
Expand All @@ -203,8 +198,6 @@ func (as *Autoscaler) evaluateScaledown(ctx context.Context) error {
// other should use EnsureOnline.
func (as *Autoscaler) ensureOnline(ctx context.Context) error {
log.Debug("Making sure server is online")
as.mx.Lock()
defer as.mx.Unlock()

as.lastInteraction = time.Now()

Expand Down Expand Up @@ -297,8 +290,6 @@ LOOP:
}
break
case c := <-as.cShutdown:
as.mx.Lock()
defer as.mx.Unlock()
c <- as.deleteServer()
break LOOP
}
Expand Down
25 changes: 16 additions & 9 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"fmt"
"os"
"time"

as "github.com/JonasBak/autoscaler-proxy/autoscaler"
"github.com/JonasBak/autoscaler-proxy/proxy"
"github.com/JonasBak/autoscaler-proxy/utils"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -38,13 +40,20 @@ func DefaultConfig() proxy.ProxyOpts {
},
CloudInitVariables: map[string]string{},
},
ListenAddr: map[string]as.UpstreamOpts{
"127.0.0.1:8081": {
Net: "unix",
Addr: "/var/run/docker.sock",
},
},
ListenAddr: map[string]as.UpstreamOpts{},
}
}

func patchProcsOpts(opts proxy.ProxyOpts) proxy.ProxyOpts {
variables := make(map[string]string)
for addr, upstream := range opts.ListenAddr {
if upstream.Name != nil {
variables[fmt.Sprintf("autoscaler.listen.%s", *upstream.Name)] = addr
}
}
env := utils.BuildTemplate(utils.TemplateMap(variables), opts.Procs.Env).(map[string]string)
opts.Procs.Env = env
return opts
}

func ParseConfigFile(path string) (proxy.ProxyOpts, error) {
Expand All @@ -59,9 +68,7 @@ func ParseConfigFile(path string) (proxy.ProxyOpts, error) {
return opts, err
}

if opts.Autoscaler.HCloudToken == "" {
opts.Autoscaler.HCloudToken = os.Getenv("HCLOUD_TOKEN")
}
opts = patchProcsOpts(opts)

return opts, nil
}
36 changes: 36 additions & 0 deletions example/act_runner/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM golang:1.21-alpine as runner

WORKDIR /build

RUN apk add make git

RUN git clone https://gitea.com/gitea/act_runner.git . && git reset --hard d07fbfc8c3

COPY ./example/act_runner/docker-client-ping.patch .

RUN git apply docker-client-ping.patch

RUN make build

FROM golang:1.21-alpine as proxy

WORKDIR /build

COPY . .

RUN go build .

FROM alpine

RUN mkdir /app /data && chown 10000:10000 /app /data

USER 10000

WORKDIR /data

COPY --from=runner /build/act_runner /usr/bin/act_runner
COPY --from=proxy /build/autoscaler-proxy /usr/bin/autoscaler-proxy

COPY ./example/act_runner/config.yml /app/config.yml

ENTRYPOINT ["autoscaler-proxy", "/app/config.yml"]
12 changes: 12 additions & 0 deletions example/act_runner/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
listen_addr:
127.0.0.1:8081:
net: unix
addr: /var/run/docker.sock
name: docker
procs:
env:
GITEA_INSTANCE: "${env.GITEA_INSTANCE}"
GITEA_TOKEN: "${env.GITEA_TOKEN}"
DOCKER_HOST: "tcp://${autoscaler.listen.docker}"
run:
- test -f .runner || act_runner register --instance $GITEA_INSTANCE --token $GITEA_TOKEN --no-interactive; act_runner daemon
55 changes: 55 additions & 0 deletions example/act_runner/docker-client-ping.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
diff --git a/internal/pkg/envcheck/docker.go b/internal/pkg/envcheck/docker.go
index 0a634ad..173b53a 100644
--- a/internal/pkg/envcheck/docker.go
+++ b/internal/pkg/envcheck/docker.go
@@ -5,30 +5,30 @@ package envcheck

import (
"context"
- "fmt"
+ // "fmt"

- "github.com/docker/docker/client"
+ // "github.com/docker/docker/client"
)

func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error {
- opts := []client.Opt{
- client.FromEnv,
- }
-
- if configDockerHost != "" {
- opts = append(opts, client.WithHost(configDockerHost))
- }
-
- cli, err := client.NewClientWithOpts(opts...)
- if err != nil {
- return err
- }
- defer cli.Close()
-
- _, err = cli.Ping(ctx)
- if err != nil {
- return fmt.Errorf("cannot ping the docker daemon, does it running? %w", err)
- }
+ // opts := []client.Opt{
+ // client.FromEnv,
+ // }
+
+ // if configDockerHost != "" {
+ // opts = append(opts, client.WithHost(configDockerHost))
+ // }
+
+ // cli, err := client.NewClientWithOpts(opts...)
+ // if err != nil {
+ // return err
+ // }
+ // defer cli.Close()
+
+ // _, err = cli.Ping(ctx)
+ // if err != nil {
+ // return fmt.Errorf("cannot ping the docker daemon, does it running? %w", err)
+ // }

return nil
}
4 changes: 4 additions & 0 deletions example/docker/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
listen_addr:
127.0.0.1:8081:
net: unix
addr: /var/run/docker.sock
6 changes: 5 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func main() {
}
config = c
}
if config.Autoscaler.HCloudToken == "" {
config.Autoscaler.HCloudToken = os.Getenv("HCLOUD_TOKEN")
}

p := proxy.New(config)

ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -29,7 +33,7 @@ func main() {

go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Kill, syscall.SIGTERM)
signal.Notify(c, syscall.SIGTERM)
<-c
log.Warn("Killing...")
p.Kill()
Expand Down
Loading

0 comments on commit 51968cf

Please sign in to comment.