Some docs and cleanup

This commit is contained in:
Christopher LaPointe
2023-05-24 21:46:55 -04:00
parent 97f3805899
commit 574e90f73f
5 changed files with 127 additions and 15 deletions

View File

@@ -1,12 +1,104 @@
# traefik-lazyloader # traefik-lazyloader
Takes advantage of traefik's router priority to proxy to this small application that will manage This small app will automatically start and stop docker containers based on access, for instance,
starting/stopping containers to save resources. via traefik.
## How it Works
It works by acting as the fallback-route for the containers. For instance, if you have
`example.com` as a container you want to lazy-load, you would add the container, as well
as this lazyloader that would act as a lower-priority router for the same domain. If the
host is accessed, the lazyloader will work to boot up the container and redirect the user
as soon as it's up.
It then monitors the container's network interface. If the network is idle for X minutes, it
will stop the container.
## Quick-Start ## Quick-Start
### docker-compose
```yaml
version: '3.5'
services:
reverse-proxy:
image: traefik:v2.4
command:
- --api.insecure
- --providers.docker
- --providers.docker.defaultRule=Host(`{{.Name}}.example.com`)
- --entryPoints.web.address=:80
- --entryPoints.web.forwardedHeaders.insecure
- --providers.docker.exposedByDefault=false
restart: always
ports:
- "80:80" # The HTTP port
- "8080:8080" # The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
lazyloader:
build: .
labels:
- traefik.enable=true
- "traefik.http.routers.lazyload.priority=-100" # Lower router priority. Would only be hit if the app isn't running
- "traefik.http.routers.lazyload.rule=Host(`whoami.example.com`, `lazyloader.example.com`)"
environment:
TLL_STOPATBOOT: true # Stop all lazyloaded containers at boot (great for an example)
TLL_STATUSHOST: lazyloader.example.com # This hostname will display a status page. Disabled by default
networks:
- traefik-bridge
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Must access docker
whoami:
image: containous/whoami
networks:
- traefik-bridge
labels:
- traefik.enable=true
- "traefik.http.routers.lazywhoami.rule=Host(`whoami.example.com`)"
- lazyloader=true
- lazyloader.stopdelay=30s # Overrides the default
networks:
traefik-bridge:
external: true
name: traefik-bridge
```
You can run `docker-compose up` on the above for a quick-start. You will need to alter the domains as needed.
## Config ## Config
Configuration uses [viper]() and can be specified by either overwriting the `config.yaml` file or
via environment variables with the `TLL_` prefix (Traefik lazy loader)
```yaml
# What port to listen on
listen: :8080
# If set, when access via this hostname, will display status page
statushost: ""
# Enable debug logging
verbose: false
# if true, will stop all running tagged containers when the lazyloader starts
stopatboot: false
# which splash-page asset to use
splash: splash.html
# Container defaults
stopdelay: 5m # How long to wait before stopping container
pollfreq: 10s # How often to check
# This will be the label-prefix to look at settings on a container
# usually won't need to change (only if running multiple instances)
labelprefix: lazyloader
```
## Labels ## Labels
* `lazyloader=true` -- (Required) Add to containers that should be managed * `lazyloader=true` -- (Required) Add to containers that should be managed

View File

@@ -1,11 +1,22 @@
# What port to listen on
listen: :8080 listen: :8080
stopatboot: false # If set, when access via this hostname, will display status page
splash: splash.html
stopdelay: 5m
pollfreq: 10s
statushost: "" statushost: ""
# Enable debug logging
verbose: false verbose: false
# if true, will stop all running tagged containers when the lazyloader starts
stopatboot: false
# which splash-page asset to use
splash: splash.html
# Container defaults
stopdelay: 5m # How long to wait before stopping container
pollfreq: 10s # How often to check
# This will be the label-prefix to look at settings on a container
# usually won't need to change (only if running multiple instances)
labelprefix: lazyloader labelprefix: lazyloader

View File

@@ -94,7 +94,7 @@ func StatusHandler(w http.ResponseWriter, r *http.Request) {
runtime.ReadMemStats(&stats) runtime.ReadMemStats(&stats)
statusPageTemplate.Execute(w, StatusPageModel{ statusPageTemplate.Execute(w, StatusPageModel{
Active: core.ActiveContainers(), Active: core.ActiveContainers(),
Qualifying: core.QualifyingContainers(), Qualifying: core.QualifyingContainers(r.Context()),
RuntimeMetrics: fmt.Sprintf("Heap=%d, InUse=%d, Total=%d, Sys=%d, NumGC=%d", stats.HeapAlloc, stats.HeapInuse, stats.TotalAlloc, stats.Sys, stats.NumGC), RuntimeMetrics: fmt.Sprintf("Heap=%d, InUse=%d, Total=%d, Sys=%d, NumGC=%d", stats.HeapAlloc, stats.HeapInuse, stats.TotalAlloc, stats.Sys, stats.NumGC),
}) })
default: default:

View File

@@ -74,7 +74,6 @@ func (s *Core) StartHost(hostname string) (*StartResult, error) {
} }
if ets, exists := s.active[ct.ID]; exists { if ets, exists := s.active[ct.ID]; exists {
// TODO: Handle case we think it's active, but not? (eg. crash? slow boot?)
logrus.Debugf("Asked to start host, but we already think it's started: %s", ets.name) logrus.Debugf("Asked to start host, but we already think it's started: %s", ets.name)
return &StartResult{ return &StartResult{
WaitForCode: ets.waitForCode, WaitForCode: ets.waitForCode,
@@ -265,6 +264,7 @@ func (s *Core) findAllLazyloadContainers(ctx context.Context, includeStopped boo
}) })
} }
// Returns all actively managed containers
func (s *Core) ActiveContainers() []*ContainerState { func (s *Core) ActiveContainers() []*ContainerState {
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
@@ -279,8 +279,9 @@ func (s *Core) ActiveContainers() []*ContainerState {
return ret return ret
} }
func (s *Core) QualifyingContainers() []string { // Return all containers that qualify to be load-managed (eg. have the tag)
ct, err := s.findAllLazyloadContainers(context.Background(), true) func (s *Core) QualifyingContainers(ctx context.Context) []string {
ct, err := s.findAllLazyloadContainers(ctx, true)
if err != nil { if err != nil {
return nil return nil
} }

View File

@@ -2,6 +2,7 @@ package service
import ( import (
"fmt" "fmt"
"strings"
"traefik-lazyload/pkg/config" "traefik-lazyload/pkg/config"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@@ -22,7 +23,7 @@ func labelOrDefault(ct *types.Container, sublabel, dflt string) (string, bool) {
return dflt, false return dflt, false
} }
func short(id string) string { func shortId(id string) string {
const SLEN = 8 const SLEN = 8
if len(id) <= SLEN { if len(id) <= SLEN {
return id return id
@@ -33,11 +34,18 @@ func short(id string) string {
func containerShort(c *types.Container) string { func containerShort(c *types.Container) string {
var name string var name string
if len(c.Names) > 0 { if len(c.Names) > 0 {
name = c.Names[0] name = trimRootPath(c.Names[0])
} else { } else {
name = c.Image name = c.Image
} }
return fmt.Sprintf("%s(%s)", name, short(c.ID)) return fmt.Sprintf("%s(%s)", name, shortId(c.ID))
}
func trimRootPath(s string) string {
if strings.HasPrefix(s, "/") {
return s[1:]
}
return s
} }
func isRunning(c *types.Container) bool { func isRunning(c *types.Container) bool {