Use Docker API version auto negotiation (#486)
This commit is contained in:
@@ -23,14 +23,14 @@ Some other features included:
|
|||||||
```text
|
```text
|
||||||
-api-binding host:port
|
-api-binding host:port
|
||||||
The host:port bound for servicing API requests (env API_BINDING)
|
The host:port bound for servicing API requests (env API_BINDING)
|
||||||
|
-auto-scale-allow-deny string
|
||||||
|
Path to config for server allowlists and denylists. If a global/server entry is specified, only players allowed to connect to the server will be able to trigger a scale up when -auto-scale-up is enabled or cancel active down scalers when -auto-scale-down is enabled (env AUTO_SCALE_ALLOW_DENY)
|
||||||
|
-auto-scale-down
|
||||||
|
Decrease Kubernetes StatefulSet Replicas (only) from 1 to 0 on respective backend servers after there are no connections (env AUTO_SCALE_DOWN)
|
||||||
|
-auto-scale-down-after string
|
||||||
|
Server scale down delay after there are no connections (env AUTO_SCALE_DOWN_AFTER) (default "10m")
|
||||||
-auto-scale-up
|
-auto-scale-up
|
||||||
Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed (env AUTO_SCALE_UP)
|
Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed (env AUTO_SCALE_UP)
|
||||||
-auto-scale-down
|
|
||||||
Decrease Kubernetes StatefulSet Replicas (only) from 1 to 0 after all backend connections have stopped and a configurable amount of delay has passed (env AUTO_SCALE_DOWN)
|
|
||||||
-auto-scale-down-after
|
|
||||||
String indicating how long an auto scale down should wait before scaling down a backend server. If a player rejoins the server during this delay, the scale down will be canceled (env AUTO_SCALE_DOWN_AFTER)
|
|
||||||
-auto-scale-allow-deny string
|
|
||||||
Path to config for server allowlists and denylists. If a global/server entry is specified, only players allowed to connect to the server will be able to trigger a scale up when -auto-scale-up is enabled or cancel active down scalers when -auto-scale-down is enabled (env AUTO_SCALE_ALLOW_DENY)
|
|
||||||
-clients-to-allow value
|
-clients-to-allow value
|
||||||
Zero or more client IP addresses or CIDRs to allow. Takes precedence over deny. (env CLIENTS_TO_ALLOW)
|
Zero or more client IP addresses or CIDRs to allow. Takes precedence over deny. (env CLIENTS_TO_ALLOW)
|
||||||
-clients-to-deny value
|
-clients-to-deny value
|
||||||
@@ -43,6 +43,8 @@ Some other features included:
|
|||||||
Enable debug logs (env DEBUG)
|
Enable debug logs (env DEBUG)
|
||||||
-default string
|
-default string
|
||||||
host:port of a default Minecraft server to use when mapping not found (env DEFAULT)
|
host:port of a default Minecraft server to use when mapping not found (env DEFAULT)
|
||||||
|
-docker-api-version string
|
||||||
|
Instead of auto-negotiating, use specific Docker API version (env DOCKER_API_VERSION)
|
||||||
-docker-refresh-interval int
|
-docker-refresh-interval int
|
||||||
Refresh interval in seconds for the Docker integrations (env DOCKER_REFRESH_INTERVAL) (default 15)
|
Refresh interval in seconds for the Docker integrations (env DOCKER_REFRESH_INTERVAL) (default 15)
|
||||||
-docker-socket string
|
-docker-socket string
|
||||||
@@ -85,12 +87,16 @@ Some other features included:
|
|||||||
The port bound to listen for Minecraft client connections (env PORT) (default 25565)
|
The port bound to listen for Minecraft client connections (env PORT) (default 25565)
|
||||||
-receive-proxy-protocol
|
-receive-proxy-protocol
|
||||||
Receive PROXY protocol from backend servers, by default trusts every proxy header that it receives, combine with -trusted-proxies to specify a list of trusted proxies (env RECEIVE_PROXY_PROTOCOL)
|
Receive PROXY protocol from backend servers, by default trusts every proxy header that it receives, combine with -trusted-proxies to specify a list of trusted proxies (env RECEIVE_PROXY_PROTOCOL)
|
||||||
|
-record-logins
|
||||||
|
Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend (env RECORD_LOGINS)
|
||||||
-routes-config path
|
-routes-config path
|
||||||
Name or full path to routes config file (env ROUTES_CONFIG)
|
Name or full path to routes config file (env ROUTES_CONFIG)
|
||||||
-routes-config-watch
|
-routes-config-watch
|
||||||
Watch for config file changes (env ROUTES_CONFIG_WATCH)
|
Watch for config file changes (env ROUTES_CONFIG_WATCH)
|
||||||
-simplify-srv
|
-simplify-srv
|
||||||
Simplify fully qualified SRV records for mapping (env SIMPLIFY_SRV)
|
Simplify fully qualified SRV records for mapping (env SIMPLIFY_SRV)
|
||||||
|
-trace
|
||||||
|
Enable trace logs (env TRACE)
|
||||||
-trusted-proxies value
|
-trusted-proxies value
|
||||||
Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol (env TRUSTED_PROXIES)
|
Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol (env TRUSTED_PROXIES)
|
||||||
-use-proxy-protocol
|
-use-proxy-protocol
|
||||||
@@ -101,8 +107,6 @@ Some other features included:
|
|||||||
Indicates if the webhook will only be called if a user is connecting rather than just server list/ping (env WEBHOOK_REQUIRE_USER)
|
Indicates if the webhook will only be called if a user is connecting rather than just server list/ping (env WEBHOOK_REQUIRE_USER)
|
||||||
-webhook-url string
|
-webhook-url string
|
||||||
If set, a POST request that contains connection status notifications will be sent to this HTTP address (env WEBHOOK_URL)
|
If set, a POST request that contains connection status notifications will be sent to this HTTP address (env WEBHOOK_URL)
|
||||||
-record-logins
|
|
||||||
Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend (env RECORD_LOGINS)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Multi-Architecture Image
|
## Docker Multi-Architecture Image
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type Config struct {
|
|||||||
DockerSocket string `default:"unix:///var/run/docker.sock" usage:"Path to Docker socket to use"`
|
DockerSocket string `default:"unix:///var/run/docker.sock" usage:"Path to Docker socket to use"`
|
||||||
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker integrations"`
|
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker integrations"`
|
||||||
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker integrations"`
|
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker integrations"`
|
||||||
|
DockerApiVersion string `usage:"Instead of auto-negotiating, use specific Docker API version"`
|
||||||
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb,prometheus"`
|
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb,prometheus"`
|
||||||
MetricsBackendConfig MetricsBackendConfig
|
MetricsBackendConfig MetricsBackendConfig
|
||||||
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
|
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
|
||||||
|
|||||||
+41
-16
@@ -15,28 +15,56 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type IDockerWatcher interface {
|
type IDockerWatcher interface {
|
||||||
Start(ctx context.Context, socket string, timeoutSeconds int, refreshIntervalSeconds int, autoScaleUp bool, autoScaleDown bool) error
|
Start(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DockerAPIVersion = "1.24"
|
|
||||||
DockerRouterLabelHost = "mc-router.host"
|
DockerRouterLabelHost = "mc-router.host"
|
||||||
DockerRouterLabelPort = "mc-router.port"
|
DockerRouterLabelPort = "mc-router.port"
|
||||||
DockerRouterLabelDefault = "mc-router.default"
|
DockerRouterLabelDefault = "mc-router.default"
|
||||||
DockerRouterLabelNetwork = "mc-router.network"
|
DockerRouterLabelNetwork = "mc-router.network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DockerWatcher IDockerWatcher = &dockerWatcherImpl{}
|
type dockerWatcherConfig struct {
|
||||||
|
autoScaleUp bool
|
||||||
|
autoScaleDown bool
|
||||||
|
socket string
|
||||||
|
timeoutSeconds int
|
||||||
|
refreshIntervalSeconds int
|
||||||
|
apiVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dockerWatcherConfig) apiVersionOpt() client.Opt {
|
||||||
|
if c.apiVersion != "" {
|
||||||
|
logrus.WithField("apiVersion", c.apiVersion).Debug("Using specific Docker API version")
|
||||||
|
return client.WithVersion(c.apiVersion)
|
||||||
|
} else {
|
||||||
|
logrus.Debug("Using Docker API version negotiation")
|
||||||
|
return client.WithAPIVersionNegotiation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDockerWatcher(socket string, timeoutSeconds int, refreshIntervalSeconds int, autoScaleUp bool, autoScaleDown bool, dockerApiVersion string) IDockerWatcher {
|
||||||
|
return &dockerWatcherImpl{
|
||||||
|
config: dockerWatcherConfig{
|
||||||
|
socket: socket,
|
||||||
|
timeoutSeconds: timeoutSeconds,
|
||||||
|
refreshIntervalSeconds: refreshIntervalSeconds,
|
||||||
|
autoScaleUp: autoScaleUp,
|
||||||
|
autoScaleDown: autoScaleDown,
|
||||||
|
apiVersion: dockerApiVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type dockerWatcherImpl struct {
|
type dockerWatcherImpl struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
autoScaleUp bool
|
config dockerWatcherConfig
|
||||||
autoScaleDown bool
|
client *client.Client
|
||||||
client *client.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerWatcherImpl) makeWakerFunc(_ *routableContainer) ScalerFunc {
|
func (w *dockerWatcherImpl) makeWakerFunc(_ *routableContainer) ScalerFunc {
|
||||||
if !w.autoScaleUp {
|
if !w.config.autoScaleUp {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
@@ -46,7 +74,7 @@ func (w *dockerWatcherImpl) makeWakerFunc(_ *routableContainer) ScalerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerWatcherImpl) makeSleeperFunc(_ *routableContainer) ScalerFunc {
|
func (w *dockerWatcherImpl) makeSleeperFunc(_ *routableContainer) ScalerFunc {
|
||||||
if !w.autoScaleDown {
|
if !w.config.autoScaleDown {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
@@ -55,22 +83,19 @@ func (w *dockerWatcherImpl) makeSleeperFunc(_ *routableContainer) ScalerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerWatcherImpl) Start(ctx context.Context, socket string, timeoutSeconds int, refreshIntervalSeconds int, autoScaleUp bool, autoScaleDown bool) error {
|
func (w *dockerWatcherImpl) Start(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
w.autoScaleUp = autoScaleUp
|
timeout := time.Duration(w.config.timeoutSeconds) * time.Second
|
||||||
w.autoScaleDown = autoScaleDown
|
refreshInterval := time.Duration(w.config.refreshIntervalSeconds) * time.Second
|
||||||
|
|
||||||
timeout := time.Duration(timeoutSeconds) * time.Second
|
|
||||||
refreshInterval := time.Duration(refreshIntervalSeconds) * time.Second
|
|
||||||
|
|
||||||
opts := []client.Opt{
|
opts := []client.Opt{
|
||||||
client.WithHost(socket),
|
client.WithHost(w.config.socket),
|
||||||
client.WithTimeout(timeout),
|
client.WithTimeout(timeout),
|
||||||
client.WithHTTPHeaders(map[string]string{
|
client.WithHTTPHeaders(map[string]string{
|
||||||
"User-Agent": "mc-router ",
|
"User-Agent": "mc-router ",
|
||||||
}),
|
}),
|
||||||
client.WithVersion(DockerAPIVersion),
|
w.config.apiVersionOpt(),
|
||||||
}
|
}
|
||||||
|
|
||||||
w.client, err = client.NewClientWithOpts(opts...)
|
w.client, err = client.NewClientWithOpts(opts...)
|
||||||
|
|||||||
+21
-14
@@ -19,17 +19,27 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DockerSwarmWatcher IDockerWatcher = &dockerSwarmWatcherImpl{}
|
func NewDockerSwarmWatcher(socket string, timeoutSeconds int, refreshIntervalSeconds int, autoScaleUp bool, autoScaleDown bool, dockerApiVersion string) IDockerWatcher {
|
||||||
|
return &dockerSwarmWatcherImpl{
|
||||||
|
config: dockerWatcherConfig{
|
||||||
|
socket: socket,
|
||||||
|
timeoutSeconds: timeoutSeconds,
|
||||||
|
refreshIntervalSeconds: refreshIntervalSeconds,
|
||||||
|
autoScaleUp: autoScaleUp,
|
||||||
|
autoScaleDown: autoScaleDown,
|
||||||
|
apiVersion: dockerApiVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type dockerSwarmWatcherImpl struct {
|
type dockerSwarmWatcherImpl struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
autoScaleUp bool
|
config dockerWatcherConfig
|
||||||
autoScaleDown bool
|
client *client.Client
|
||||||
client *client.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerSwarmWatcherImpl) makeWakerFunc(_ *routableService) ScalerFunc {
|
func (w *dockerSwarmWatcherImpl) makeWakerFunc(_ *routableService) ScalerFunc {
|
||||||
if !w.autoScaleUp {
|
if !w.config.autoScaleUp {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
@@ -39,7 +49,7 @@ func (w *dockerSwarmWatcherImpl) makeWakerFunc(_ *routableService) ScalerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerSwarmWatcherImpl) makeSleeperFunc(_ *routableService) ScalerFunc {
|
func (w *dockerSwarmWatcherImpl) makeSleeperFunc(_ *routableService) ScalerFunc {
|
||||||
if !w.autoScaleDown {
|
if !w.config.autoScaleDown {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
@@ -48,22 +58,19 @@ func (w *dockerSwarmWatcherImpl) makeSleeperFunc(_ *routableService) ScalerFunc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dockerSwarmWatcherImpl) Start(ctx context.Context, socket string, timeoutSeconds int, refreshIntervalSeconds int, autoScaleUp bool, autoScaleDown bool) error {
|
func (w *dockerSwarmWatcherImpl) Start(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
w.autoScaleUp = autoScaleUp
|
timeout := time.Duration(w.config.timeoutSeconds) * time.Second
|
||||||
w.autoScaleDown = autoScaleDown
|
refreshInterval := time.Duration(w.config.refreshIntervalSeconds) * time.Second
|
||||||
|
|
||||||
timeout := time.Duration(timeoutSeconds) * time.Second
|
|
||||||
refreshInterval := time.Duration(refreshIntervalSeconds) * time.Second
|
|
||||||
|
|
||||||
opts := []client.Opt{
|
opts := []client.Opt{
|
||||||
client.WithHost(socket),
|
client.WithHost(w.config.socket),
|
||||||
client.WithTimeout(timeout),
|
client.WithTimeout(timeout),
|
||||||
client.WithHTTPHeaders(map[string]string{
|
client.WithHTTPHeaders(map[string]string{
|
||||||
"User-Agent": "mc-router ",
|
"User-Agent": "mc-router ",
|
||||||
}),
|
}),
|
||||||
client.WithVersion(DockerAPIVersion),
|
client.WithAPIVersionNegotiation(),
|
||||||
}
|
}
|
||||||
|
|
||||||
w.client, err = client.NewClientWithOpts(opts...)
|
w.client, err = client.NewClientWithOpts(opts...)
|
||||||
|
|||||||
+4
-2
@@ -143,7 +143,8 @@ func NewServer(ctx context.Context, config *Config) (*Server, error) {
|
|||||||
|
|
||||||
// TODO convert to RouteFinder
|
// TODO convert to RouteFinder
|
||||||
if config.InDocker {
|
if config.InDocker {
|
||||||
err = DockerWatcher.Start(ctx, config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down)
|
watcher := NewDockerWatcher(config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down, config.DockerApiVersion)
|
||||||
|
err = watcher.Start(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start docker integration: %w", err)
|
return nil, fmt.Errorf("could not start docker integration: %w", err)
|
||||||
}
|
}
|
||||||
@@ -151,7 +152,8 @@ func NewServer(ctx context.Context, config *Config) (*Server, error) {
|
|||||||
|
|
||||||
// TODO convert to RouteFinder
|
// TODO convert to RouteFinder
|
||||||
if config.InDockerSwarm {
|
if config.InDockerSwarm {
|
||||||
err = DockerSwarmWatcher.Start(ctx, config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down)
|
watcher := NewDockerSwarmWatcher(config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down, config.DockerApiVersion)
|
||||||
|
err = watcher.Start(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not start docker swarm integration: %w", err)
|
return nil, fmt.Errorf("could not start docker swarm integration: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user