Support cluster-external Kubernetes client. (#1159)
Detect whether in-cluster or cluster-external Kubernetes client should be used based on the KUBERNETES_SERVICE_{HOST,PORT} environment variables. Adds bearer token and CA certificate file path parameters.
This commit is contained in:
parent
3611818eda
commit
920b5bb15d
5 changed files with 122 additions and 34 deletions
39
docs/toml.md
39
docs/toml.md
|
@ -1042,17 +1042,46 @@ Træfɪk can be configured to use Kubernetes Ingress as a backend configuration:
|
||||||
|
|
||||||
# Kubernetes server endpoint
|
# Kubernetes server endpoint
|
||||||
#
|
#
|
||||||
# When deployed as a replication controller in Kubernetes,
|
# When deployed as a replication controller in Kubernetes, Traefik will use
|
||||||
# Traefik will use env variable KUBERNETES_SERVICE_HOST
|
# the environment variables KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT
|
||||||
# and KUBERNETES_SERVICE_PORT_HTTPS as endpoint
|
# to construct the endpoint.
|
||||||
# Secure token will be found in /var/run/secrets/kubernetes.io/serviceaccount/token
|
# Secure token will be found in /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
# and SSL CA cert in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
# and SSL CA cert in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||||
#
|
#
|
||||||
# Optional
|
# The endpoint may be given to override the environment variable values.
|
||||||
|
#
|
||||||
|
# When the environment variables are not found, Traefik will try to connect to
|
||||||
|
# the Kubernetes API server with an external-cluster client. In this case, the
|
||||||
|
# endpoint is required. Specifically, it may be set to the URL used by
|
||||||
|
# `kubectl proxy` to connect to a Kubernetes cluster from localhost.
|
||||||
|
#
|
||||||
|
# Optional for in-cluster configuration, required otherwise
|
||||||
|
# Default: empty
|
||||||
#
|
#
|
||||||
# endpoint = "http://localhost:8080"
|
# endpoint = "http://localhost:8080"
|
||||||
# namespaces = ["default","production"]
|
|
||||||
|
# Bearer token used for the Kubernetes client configuration.
|
||||||
#
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# token = "my token"
|
||||||
|
|
||||||
|
# Path to the certificate authority file used for the Kubernetes client
|
||||||
|
# configuration.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# certAuthFilePath = "/my/ca.crt"
|
||||||
|
|
||||||
|
# Array of namespaces to watch.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ["default"].
|
||||||
|
#
|
||||||
|
# namespaces = ["default", "production"]
|
||||||
|
|
||||||
# See: http://kubernetes.io/docs/user-guide/labels/#list-and-watch-filtering
|
# See: http://kubernetes.io/docs/user-guide/labels/#list-and-watch-filtering
|
||||||
# labelselector = "A and not B"
|
# labelselector = "A and not B"
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package k8s
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/client-go/1.5/kubernetes"
|
"k8s.io/client-go/1.5/kubernetes"
|
||||||
|
@ -39,32 +42,48 @@ type clientImpl struct {
|
||||||
clientset *kubernetes.Clientset
|
clientset *kubernetes.Clientset
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInClusterClient returns a new Kubernetes client that expect to run inside the cluster
|
// NewInClusterClient returns a new Kubernetes client that is expected to run
|
||||||
func NewInClusterClient() (Client, error) {
|
// inside the cluster.
|
||||||
|
func NewInClusterClient(endpoint string) (Client, error) {
|
||||||
config, err := rest.InClusterConfig()
|
config, err := rest.InClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to create in-cluster configuration: %s", err)
|
||||||
}
|
|
||||||
clientset, err := kubernetes.NewForConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &clientImpl{
|
if endpoint != "" {
|
||||||
clientset: clientset,
|
config.Host = endpoint
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return createClientFromConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL
|
// NewExternalClusterClient returns a new Kubernetes client that may run outside
|
||||||
func NewInClusterClientWithEndpoint(endpoint string) (Client, error) {
|
// of the cluster.
|
||||||
config, err := rest.InClusterConfig()
|
// The endpoint parameter must not be empty.
|
||||||
if err != nil {
|
func NewExternalClusterClient(endpoint, token, caFilePath string) (Client, error) {
|
||||||
return nil, err
|
if endpoint == "" {
|
||||||
|
return nil, errors.New("endpoint missing for external cluster client")
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Host = endpoint
|
config := &rest.Config{
|
||||||
|
Host: endpoint,
|
||||||
|
BearerToken: token,
|
||||||
|
}
|
||||||
|
|
||||||
clientset, err := kubernetes.NewForConfig(config)
|
if caFilePath != "" {
|
||||||
|
caData, err := ioutil.ReadFile(caFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read CA file %s: %s", caFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.TLSClientConfig = rest.TLSClientConfig{CAData: caData}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createClientFromConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClientFromConfig(c *rest.Config) (Client, error) {
|
||||||
|
clientset, err := kubernetes.NewForConfig(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -30,7 +32,9 @@ const (
|
||||||
// Kubernetes holds configurations of the Kubernetes provider.
|
// Kubernetes holds configurations of the Kubernetes provider.
|
||||||
type Kubernetes struct {
|
type Kubernetes struct {
|
||||||
BaseProvider `mapstructure:",squash"`
|
BaseProvider `mapstructure:",squash"`
|
||||||
Endpoint string `description:"Kubernetes server endpoint"`
|
Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)"`
|
||||||
|
Token string `description:"Kubernetes bearer token (not needed for in-cluster client)"`
|
||||||
|
CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)"`
|
||||||
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
||||||
Namespaces k8s.Namespaces `description:"Kubernetes namespaces"`
|
Namespaces k8s.Namespaces `description:"Kubernetes namespaces"`
|
||||||
LabelSelector string `description:"Kubernetes api label selector to use"`
|
LabelSelector string `description:"Kubernetes api label selector to use"`
|
||||||
|
@ -38,12 +42,18 @@ type Kubernetes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Kubernetes) newK8sClient() (k8s.Client, error) {
|
func (provider *Kubernetes) newK8sClient() (k8s.Client, error) {
|
||||||
|
withEndpoint := ""
|
||||||
if provider.Endpoint != "" {
|
if provider.Endpoint != "" {
|
||||||
log.Infof("Creating in cluster Kubernetes client with endpoint %v", provider.Endpoint)
|
withEndpoint = fmt.Sprintf(" with endpoint %v", provider.Endpoint)
|
||||||
return k8s.NewInClusterClientWithEndpoint(provider.Endpoint)
|
|
||||||
}
|
}
|
||||||
log.Info("Creating in cluster Kubernetes client")
|
|
||||||
return k8s.NewInClusterClient()
|
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "" {
|
||||||
|
log.Infof("Creating in-cluster Kubernetes client%s\n", withEndpoint)
|
||||||
|
return k8s.NewInClusterClient(provider.Endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Creating cluster-external Kubernetes client%s\n", withEndpoint)
|
||||||
|
return k8s.NewExternalClusterClient(provider.Endpoint, provider.Token, provider.CertAuthFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide allows the provider to provide configurations to traefik
|
// Provide allows the provider to provide configurations to traefik
|
||||||
|
|
|
@ -385,13 +385,14 @@ func (server *Server) configureProviders() {
|
||||||
func (server *Server) startProviders() {
|
func (server *Server) startProviders() {
|
||||||
// start providers
|
// start providers
|
||||||
for _, provider := range server.providers {
|
for _, provider := range server.providers {
|
||||||
|
providerType := reflect.TypeOf(provider)
|
||||||
jsonConf, _ := json.Marshal(provider)
|
jsonConf, _ := json.Marshal(provider)
|
||||||
log.Infof("Starting provider %v %s", reflect.TypeOf(provider), jsonConf)
|
log.Infof("Starting provider %v %s", providerType, jsonConf)
|
||||||
currentProvider := provider
|
currentProvider := provider
|
||||||
safe.Go(func() {
|
safe.Go(func() {
|
||||||
err := currentProvider.Provide(server.configurationChan, server.routinesPool, server.globalConfiguration.Constraints)
|
err := currentProvider.Provide(server.configurationChan, server.routinesPool, server.globalConfiguration.Constraints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error starting provider %s", err)
|
log.Errorf("Error starting provider %v: %s", providerType, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -665,17 +665,46 @@
|
||||||
|
|
||||||
# Kubernetes server endpoint
|
# Kubernetes server endpoint
|
||||||
#
|
#
|
||||||
# When deployed as a replication controller in Kubernetes,
|
# When deployed as a replication controller in Kubernetes, Traefik will use
|
||||||
# Traefik will use env variable KUBERNETES_SERVICE_HOST
|
# the environment variables KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT
|
||||||
# and KUBERNETES_SERVICE_PORT_HTTPS as endpoint
|
# to construct the endpoint.
|
||||||
# Secure token will be found in /var/run/secrets/kubernetes.io/serviceaccount/token
|
# Secure token will be found in /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
# and SSL CA cert in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
# and SSL CA cert in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||||
#
|
#
|
||||||
|
# The endpoint may be given to override the environment variable values.
|
||||||
|
#
|
||||||
|
# When the environment variables are not found, Traefik will try to connect to
|
||||||
|
# the Kubernetes API server with an external-cluster client. In this case, the
|
||||||
|
# endpoint is required. Specifically, it may be set to the URL used by
|
||||||
|
# `kubectl proxy` to connect to a Kubernetes cluster from localhost.
|
||||||
|
#
|
||||||
|
# Optional for in-cluster configuration, required otherwise
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# endpoint = "http://127.0.0.1:8001"
|
||||||
|
|
||||||
|
# Bearer token used for the Kubernetes client configuration.
|
||||||
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# token = "my token"
|
||||||
|
|
||||||
|
# Path to the certificate authority file used for the Kubernetes client
|
||||||
|
# configuration.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# certAuthFilePath = "/my/ca.crt"
|
||||||
|
|
||||||
|
# Array of namespaces to watch.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ["default"].
|
||||||
#
|
#
|
||||||
# endpoint = "http://localhost:8080"
|
|
||||||
# namespaces = ["default"]
|
# namespaces = ["default"]
|
||||||
#
|
|
||||||
# See: http://kubernetes.io/docs/user-guide/labels/#list-and-watch-filtering
|
# See: http://kubernetes.io/docs/user-guide/labels/#list-and-watch-filtering
|
||||||
# labelselector = "A and not B"
|
# labelselector = "A and not B"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue