mirror of
https://github.com/zix99/traefik-lazyload.git
synced 2025-12-21 21:33:09 +01:00
Some docs and cleanup
This commit is contained in:
96
README.md
96
README.md
@@ -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
|
||||||
|
|||||||
21
config.yaml
21
config.yaml
@@ -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
|
||||||
2
main.go
2
main.go
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user