docker override port, host, backend
reuse loadbalancer
This commit is contained in:
parent
0af872e661
commit
730fbfe9c5
5 changed files with 76 additions and 45 deletions
46
docker.go
46
docker.go
|
@ -5,8 +5,31 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"log"
|
"log"
|
||||||
|
"text/template"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
var DockerFuncMap = template.FuncMap{
|
||||||
|
"getBackend": func(container docker.Container) string {
|
||||||
|
for key, value := range container.Config.Labels {
|
||||||
|
if (key == "træfik.backend") {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return container.Config.Hostname
|
||||||
|
},
|
||||||
|
"getPort": func(container docker.Container) string {
|
||||||
|
for key, value := range container.Config.Labels {
|
||||||
|
if (key == "træfik.port") {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key, _ := range container.NetworkSettings.Ports {
|
||||||
|
return key.Port()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
"getHost": getHost,
|
||||||
|
}
|
||||||
type DockerProvider struct {
|
type DockerProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
Endpoint string
|
Endpoint string
|
||||||
|
@ -38,16 +61,22 @@ func (provider *DockerProvider) loadDockerConfig() *Configuration {
|
||||||
configuration := new(Configuration)
|
configuration := new(Configuration)
|
||||||
containerList, _ := provider.dockerClient.ListContainers(docker.ListContainersOptions{})
|
containerList, _ := provider.dockerClient.ListContainers(docker.ListContainersOptions{})
|
||||||
containersInspected := []docker.Container{}
|
containersInspected := []docker.Container{}
|
||||||
|
hosts := map[string][]docker.Container{}
|
||||||
for _, container := range containerList {
|
for _, container := range containerList {
|
||||||
containerInspected, _ := provider.dockerClient.InspectContainer(container.ID)
|
containerInspected, _ := provider.dockerClient.InspectContainer(container.ID)
|
||||||
containersInspected = append(containersInspected, *containerInspected)
|
containersInspected = append(containersInspected, *containerInspected)
|
||||||
|
hosts[getHost(*containerInspected)] = append(hosts[getHost(*containerInspected)], *containerInspected)
|
||||||
}
|
}
|
||||||
containers := struct {
|
|
||||||
|
templateObjects := struct {
|
||||||
Containers []docker.Container
|
Containers []docker.Container
|
||||||
|
Hosts map[string][]docker.Container
|
||||||
}{
|
}{
|
||||||
containersInspected,
|
containersInspected,
|
||||||
|
hosts,
|
||||||
}
|
}
|
||||||
tmpl, err := gtf.New("docker.tmpl").ParseFiles("docker.tmpl")
|
gtf.Inject(DockerFuncMap)
|
||||||
|
tmpl, err := template.New("docker.tmpl").Funcs(DockerFuncMap).ParseFiles("docker.tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error reading file:", err)
|
log.Println("Error reading file:", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -55,7 +84,7 @@ func (provider *DockerProvider) loadDockerConfig() *Configuration {
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
err = tmpl.Execute(&buffer, containers)
|
err = tmpl.Execute(&buffer, templateObjects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error with docker template:", err)
|
log.Println("Error with docker template:", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -66,4 +95,13 @@ func (provider *DockerProvider) loadDockerConfig() *Configuration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return configuration
|
return configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHost(container docker.Container) string {
|
||||||
|
for key, value := range container.Config.Labels {
|
||||||
|
if (key == "træfik.host") {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(container.Name, "/")
|
||||||
}
|
}
|
29
docker.tmpl
29
docker.tmpl
|
@ -1,25 +1,12 @@
|
||||||
[backends]{{range .Containers}}
|
[backends]{{range .Containers}}
|
||||||
[backends.backend-{{.Config.Hostname}}]
|
[backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/"}}]
|
||||||
[backends.backend-{{.Config.Hostname}}.servers.server-{{.Config.Hostname}}]
|
url = "http://{{.NetworkSettings.IPAddress}}:{{getPort .}}"
|
||||||
|
|
||||||
{{/* Only one exposed port! */}}
|
|
||||||
{{if eq (len .Config.ExposedPorts) 1}}
|
|
||||||
{{$ip := .NetworkSettings.IPAddress}}
|
|
||||||
{{range $keyPort, $valuePort := .Config.ExposedPorts}}
|
|
||||||
url = "http://{{$ip}}:{{$keyPort.Port}}"
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
[routes]{{range .Containers}}
|
[routes]{{range $host, $containers := .Hosts}}
|
||||||
[routes.route-{{.Config.Hostname}}]
|
[routes.route-{{$host}}]
|
||||||
backends = ["backend-{{.Config.Hostname}}"]
|
backends = [{{range $container := $containers}}"backend-{{getBackend $container}}",{{end}}]
|
||||||
[routes.route-{{.Config.Hostname}}.rules.rule-{{.Config.Hostname}}-hostname]
|
[routes.route-{{$host}}.rules.rule-host-{{$host}}]
|
||||||
category = "Host"
|
category = "Host"
|
||||||
value = "{{.Config.Hostname}}.zenika.fr"
|
value = "{{$host}}.zenika.fr"
|
||||||
[routes.route-{{.Config.Hostname}}.rules.rule-{{.Config.Hostname}}-name]
|
{{end}}
|
||||||
category = "Host"
|
|
||||||
value = "{{.Name | replace "/"}}.zenika.fr"
|
|
||||||
{{end}}
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Ramify</title>
|
<title>træfik</title>
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-default" role="navigation">
|
<nav class="navbar navbar-default" role="navigation">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<a class="navbar-brand" href="/html/">Ramify</a>
|
<a class="navbar-brand" href="/html/">træfik</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
|
|
|
@ -20,8 +20,8 @@ import (
|
||||||
|
|
||||||
type FileConfiguration struct {
|
type FileConfiguration struct {
|
||||||
Docker *DockerProvider
|
Docker *DockerProvider
|
||||||
File *FileProvider
|
File *FileProvider
|
||||||
Web *WebProvider
|
Web *WebProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
var srv *graceful.Server
|
var srv *graceful.Server
|
||||||
|
@ -40,9 +40,9 @@ func main() {
|
||||||
log.Println("Configuration receveived", configuration)
|
log.Println("Configuration receveived", configuration)
|
||||||
if configuration == nil {
|
if configuration == nil {
|
||||||
log.Println("Skipping empty configuration")
|
log.Println("Skipping empty configuration")
|
||||||
} else if(reflect.DeepEqual(currentConfiguration, configuration)){
|
} else if (reflect.DeepEqual(currentConfiguration, configuration)) {
|
||||||
log.Println("Skipping same configuration")
|
log.Println("Skipping same configuration")
|
||||||
} else{
|
} else {
|
||||||
currentConfiguration = configuration
|
currentConfiguration = configuration
|
||||||
configurationRouter = LoadConfig(configuration)
|
configurationRouter = LoadConfig(configuration)
|
||||||
srv.Stop(10 * time.Second)
|
srv.Stop(10 * time.Second)
|
||||||
|
@ -52,15 +52,15 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
configuration := LoadFileConfig()
|
configuration := LoadFileConfig()
|
||||||
if(configuration.Docker != nil){
|
if (configuration.Docker != nil) {
|
||||||
providers = append(providers, configuration.Docker)
|
providers = append(providers, configuration.Docker)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(configuration.File != nil){
|
if (configuration.File != nil) {
|
||||||
providers = append(providers, configuration.File)
|
providers = append(providers, configuration.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(configuration.Web != nil){
|
if (configuration.Web != nil) {
|
||||||
providers = append(providers, configuration.Web)
|
providers = append(providers, configuration.Web)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ func main() {
|
||||||
|
|
||||||
func LoadConfig(configuration *Configuration) *mux.Router {
|
func LoadConfig(configuration *Configuration) *mux.Router {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
|
backends := map[string]http.Handler{}
|
||||||
for routeName, route := range configuration.Routes {
|
for routeName, route := range configuration.Routes {
|
||||||
log.Println("Creating route", routeName)
|
log.Println("Creating route", routeName)
|
||||||
fwd, _ := forward.New()
|
fwd, _ := forward.New()
|
||||||
|
@ -118,16 +119,21 @@ func LoadConfig(configuration *Configuration) *mux.Router {
|
||||||
newRoutes = append(newRoutes, newRoute)
|
newRoutes = append(newRoutes, newRoute)
|
||||||
}
|
}
|
||||||
for _, backendName := range route.Backends {
|
for _, backendName := range route.Backends {
|
||||||
log.Println("Creating backend", backendName)
|
if (backends[backendName] ==nil) {
|
||||||
lb, _ := roundrobin.New(fwd)
|
log.Println("Creating backend", backendName)
|
||||||
rb, _ := roundrobin.NewRebalancer(lb)
|
lb, _ := roundrobin.New(fwd)
|
||||||
for serverName, server := range configuration.Backends[backendName].Servers {
|
rb, _ := roundrobin.NewRebalancer(lb)
|
||||||
log.Println("Creating server", serverName)
|
for serverName, server := range configuration.Backends[backendName].Servers {
|
||||||
url, _ := url.Parse(server.Url)
|
log.Println("Creating server", serverName)
|
||||||
rb.UpsertServer(url)
|
url, _ := url.Parse(server.Url)
|
||||||
|
rb.UpsertServer(url)
|
||||||
|
}
|
||||||
|
backends[backendName]=lb
|
||||||
|
}else {
|
||||||
|
log.Println("Reusing backend", backendName)
|
||||||
}
|
}
|
||||||
for _, route := range newRoutes {
|
for _, route := range newRoutes {
|
||||||
route.Handler(handlers.CombinedLoggingHandler(os.Stdout, lb))
|
route.Handler(handlers.CombinedLoggingHandler(os.Stdout, backends[backendName]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,9 +148,9 @@ func Invoke(any interface{}, name string, args ...interface{}) []reflect.Value {
|
||||||
return reflect.ValueOf(any).MethodByName(name).Call(inputs)
|
return reflect.ValueOf(any).MethodByName(name).Call(inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadFileConfig() *FileConfiguration {
|
func LoadFileConfig() *FileConfiguration {
|
||||||
configuration := new(FileConfiguration)
|
configuration := new(FileConfiguration)
|
||||||
if _, err := toml.DecodeFile("ramify.toml", configuration); err != nil {
|
if _, err := toml.DecodeFile("træfik.toml", configuration); err != nil {
|
||||||
log.Fatal("Error reading file:", err)
|
log.Fatal("Error reading file:", err)
|
||||||
}
|
}
|
||||||
return configuration
|
return configuration
|
|
@ -6,7 +6,7 @@ watch = true
|
||||||
address = ":8010"
|
address = ":8010"
|
||||||
|
|
||||||
#[file]
|
#[file]
|
||||||
#filename = "ramify.toml"
|
#filename = "træfik.toml"
|
||||||
#watch = true
|
#watch = true
|
||||||
|
|
||||||
[backends]
|
[backends]
|
Loading…
Reference in a new issue