Use protobuf instead of rest to connect to apiserver host
This commit is contained in:
parent
b896215613
commit
e5b02b609f
2 changed files with 356 additions and 14 deletions
|
@ -15,10 +15,10 @@ import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/client/restclient"
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/healthz"
|
"k8s.io/kubernetes/pkg/healthz"
|
||||||
kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
|
||||||
|
|
||||||
"k8s.io/ingress/core/pkg/ingress"
|
"k8s.io/ingress/core/pkg/ingress"
|
||||||
"k8s.io/ingress/core/pkg/k8s"
|
"k8s.io/ingress/core/pkg/k8s"
|
||||||
|
@ -29,6 +29,12 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
||||||
var (
|
var (
|
||||||
flags = pflag.NewFlagSet("", pflag.ExitOnError)
|
flags = pflag.NewFlagSet("", pflag.ExitOnError)
|
||||||
|
|
||||||
|
apiserverHost = flags.String("apiserver-host", "", "The address of the Kubernetes Apiserver "+
|
||||||
|
"to connect to in the format of protocol://address:port, e.g., "+
|
||||||
|
"http://localhost:8080. If not specified, the assumption is that the binary runs inside a "+
|
||||||
|
"Kubernetes cluster and local discovery is attempted.")
|
||||||
|
kubeConfigFile = flags.String("kubeconfig", "", "Path to kubeconfig file with authorization and master location information.")
|
||||||
|
|
||||||
defaultSvc = flags.String("default-backend-service", "",
|
defaultSvc = flags.String("default-backend-service", "",
|
||||||
`Service used to serve a 404 page for the default backend. Takes the form
|
`Service used to serve a 404 page for the default backend. Takes the form
|
||||||
namespace/name. The controller uses the first node port of this Service for
|
namespace/name. The controller uses the first node port of this Service for
|
||||||
|
@ -77,7 +83,6 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
||||||
|
|
||||||
flags.AddGoFlagSet(flag.CommandLine)
|
flags.AddGoFlagSet(flag.CommandLine)
|
||||||
flags.Parse(os.Args)
|
flags.Parse(os.Args)
|
||||||
clientConfig := kubectl_util.DefaultClientConfig(flags)
|
|
||||||
|
|
||||||
flag.Set("logtostderr", "true")
|
flag.Set("logtostderr", "true")
|
||||||
|
|
||||||
|
@ -91,17 +96,9 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
||||||
glog.Fatalf("Please specify --default-backend-service")
|
glog.Fatalf("Please specify --default-backend-service")
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeconfig, err := restclient.InClusterConfig()
|
kubeClient, err := createApiserverClient(*apiserverHost, *kubeConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
kubeconfig, err = clientConfig.ClientConfig()
|
handleFatalInitError(err)
|
||||||
if err != nil {
|
|
||||||
glog.Fatalf("error configuring the client: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kubeClient, err := clientset.NewForConfig(kubeconfig)
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalf("failed to create client: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = k8s.IsValidService(kubeClient, *defaultSvc)
|
_, err = k8s.IsValidService(kubeClient, *defaultSvc)
|
||||||
|
@ -186,3 +183,55 @@ func registerHandlers(enableProfiling bool, port int, ic *GenericController) {
|
||||||
}
|
}
|
||||||
glog.Fatal(server.ListenAndServe())
|
glog.Fatal(server.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// High enough QPS to fit all expected use cases. QPS=0 is not set here, because
|
||||||
|
// client code is overriding it.
|
||||||
|
defaultQPS = 1e6
|
||||||
|
// High enough Burst to fit all expected use cases. Burst=0 is not set here, because
|
||||||
|
// client code is overriding it.
|
||||||
|
defaultBurst = 1e6
|
||||||
|
)
|
||||||
|
|
||||||
|
// createApiserverClient creates new Kubernetes Apiserver client. When kubeconfig or apiserverHost param is empty
|
||||||
|
// the function assumes that it is running inside a Kubernetes cluster and attempts to
|
||||||
|
// discover the Apiserver. Otherwise, it connects to the Apiserver specified.
|
||||||
|
//
|
||||||
|
// apiserverHost param is in the format of protocol://address:port/pathPrefix, e.g.http://localhost:8001.
|
||||||
|
// kubeConfig location of kubeconfig file
|
||||||
|
func createApiserverClient(apiserverHost string, kubeConfig string) (*client.Clientset, error) {
|
||||||
|
|
||||||
|
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||||
|
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfig},
|
||||||
|
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: apiserverHost}})
|
||||||
|
|
||||||
|
cfg, err := clientConfig.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.QPS = defaultQPS
|
||||||
|
cfg.Burst = defaultBurst
|
||||||
|
cfg.ContentType = "application/vnd.kubernetes.protobuf"
|
||||||
|
|
||||||
|
glog.Infof("Creating API server client for %s", cfg.Host)
|
||||||
|
|
||||||
|
client, err := client.NewForConfig(cfg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles fatal init error that prevents server from doing any work. Prints verbose error
|
||||||
|
* message and quits the server.
|
||||||
|
*/
|
||||||
|
func handleFatalInitError(err error) {
|
||||||
|
glog.Fatalf("Error while initializing connection to Kubernetes apiserver. "+
|
||||||
|
"This most likely means that the cluster is misconfigured (e.g., it has "+
|
||||||
|
"invalid apiserver certificates or service accounts configuration). Reason: %s\n"+
|
||||||
|
"Refer to the troubleshooting guide for more information: "+
|
||||||
|
"https://github.com/kubernetes/ingress/blob/master/docs/troubleshooting.md", err)
|
||||||
|
}
|
||||||
|
|
293
docs/troubleshooting.md
Normal file
293
docs/troubleshooting.md
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
<!--
|
||||||
|
-----------------NOTICE------------------------
|
||||||
|
This file is referenced in code as
|
||||||
|
https://github.com/kubernetes/ingress/blob/master/docs/troubleshooting.md
|
||||||
|
Do not move it without providing redirects.
|
||||||
|
-----------------------------------------------
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
|
||||||
|
## Authentication to the Kubernetes API Server
|
||||||
|
|
||||||
|
|
||||||
|
A number of components are involved in the authentication process and the first step is to narrow
|
||||||
|
down the source of the problem, namely whether it is a problem with service authentication or with the kubeconfig file.
|
||||||
|
Both authentications must work:
|
||||||
|
|
||||||
|
```
|
||||||
|
+-------------+ service +------------+
|
||||||
|
| | authentication | |
|
||||||
|
+ apiserver +<-------------------+ ingress |
|
||||||
|
| | | controller |
|
||||||
|
+-------------+ +------------+
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
__Service authentication__
|
||||||
|
|
||||||
|
The Ingress controller needs information from apiserver. Therefore, authentication is required, which can be achieved in two different ways:
|
||||||
|
|
||||||
|
1. _Service Account:_ This is recommended, because nothing has to be configured. The Ingress controller will use information provided by the system to communicate with the API server. See 'Service Account' section for details.
|
||||||
|
|
||||||
|
2. _Kubeconfig file:_ In some Kubernetes environments service accounts are not available. In this case a manual configuration is required. The Ingress controller binary can be started with the `--kubeconfig` flag. The value of the flag is a path to a file specifying how to connect to the API server.
|
||||||
|
The format of the file is identical to `~/.kube/config` which is used by kubectl to connect to the API server. See 'kubeconfig' section for details.
|
||||||
|
|
||||||
|
In the diagram below you can see the full authentication flow with all options, starting with the browser
|
||||||
|
on the lower left hand side.
|
||||||
|
```
|
||||||
|
|
||||||
|
Kubernetes Workstation
|
||||||
|
+---------------------------------------------------+ +------------------+
|
||||||
|
| | | |
|
||||||
|
| +-----------+ apiserver +------------+ | | +------------+ |
|
||||||
|
| | | proxy | | | | | | |
|
||||||
|
| | apiserver | | ingress | | | | ingress | |
|
||||||
|
| | | | controller | | | | controller | |
|
||||||
|
| | | | | | | | | |
|
||||||
|
| | | | | | | | | |
|
||||||
|
| | | service account/ | | | | | | |
|
||||||
|
| | | kubeconfig | | | | | | |
|
||||||
|
| | +<-------------------+ | | | | | |
|
||||||
|
| | | | | | | | | |
|
||||||
|
| +------+----+ kubeconfig +------+-----+ | | +------+-----+ |
|
||||||
|
| |<--------------------------------------------------------| |
|
||||||
|
| | | |
|
||||||
|
+---------------------------------------------------+ +------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Service Account
|
||||||
|
If using a service account to connect to the API server, Dashboard expects the file
|
||||||
|
`/var/run/secrets/kubernetes.io/serviceaccount/token` to be present. It provides a secret
|
||||||
|
token that is required to authenticate with the API server.
|
||||||
|
|
||||||
|
Verify with the following commands:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# start a container that contains curl
|
||||||
|
$ kubectl run test --image=tutum/curl -- sleep 10000
|
||||||
|
|
||||||
|
# check that container is running
|
||||||
|
$ kubectl get pods
|
||||||
|
NAME READY STATUS RESTARTS AGE
|
||||||
|
test-701078429-s5kca 1/1 Running 0 16s
|
||||||
|
|
||||||
|
# check if secret exists
|
||||||
|
$ kubectl exec test-701078429-s5kca ls /var/run/secrets/kubernetes.io/serviceaccount/
|
||||||
|
ca.crt
|
||||||
|
namespace
|
||||||
|
token
|
||||||
|
|
||||||
|
# get service IP of master
|
||||||
|
$ kubectl get services
|
||||||
|
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
kubernetes 10.0.0.1 <none> 443/TCP 1d
|
||||||
|
|
||||||
|
# check base connectivity from cluster inside
|
||||||
|
$ kubectl exec test-701078429-s5kca -- curl -k https://10.0.0.1
|
||||||
|
Unauthorized
|
||||||
|
|
||||||
|
# connect using tokens
|
||||||
|
$ TOKEN_VALUE=$(kubectl exec test-701078429-s5kca -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
|
||||||
|
$ echo $TOKEN_VALUE
|
||||||
|
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3Mi....9A
|
||||||
|
$ kubectl exec test-701078429-s5kca -- curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $TOKEN_VALUE" https://10.0.0.1
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/api",
|
||||||
|
"/api/v1",
|
||||||
|
"/apis",
|
||||||
|
"/apis/apps",
|
||||||
|
"/apis/apps/v1alpha1",
|
||||||
|
"/apis/authentication.k8s.io",
|
||||||
|
"/apis/authentication.k8s.io/v1beta1",
|
||||||
|
"/apis/authorization.k8s.io",
|
||||||
|
"/apis/authorization.k8s.io/v1beta1",
|
||||||
|
"/apis/autoscaling",
|
||||||
|
"/apis/autoscaling/v1",
|
||||||
|
"/apis/batch",
|
||||||
|
"/apis/batch/v1",
|
||||||
|
"/apis/batch/v2alpha1",
|
||||||
|
"/apis/certificates.k8s.io",
|
||||||
|
"/apis/certificates.k8s.io/v1alpha1",
|
||||||
|
"/apis/extensions",
|
||||||
|
"/apis/extensions/v1beta1",
|
||||||
|
"/apis/policy",
|
||||||
|
"/apis/policy/v1alpha1",
|
||||||
|
"/apis/rbac.authorization.k8s.io",
|
||||||
|
"/apis/rbac.authorization.k8s.io/v1alpha1",
|
||||||
|
"/apis/storage.k8s.io",
|
||||||
|
"/apis/storage.k8s.io/v1beta1",
|
||||||
|
"/healthz",
|
||||||
|
"/healthz/ping",
|
||||||
|
"/logs",
|
||||||
|
"/metrics",
|
||||||
|
"/swaggerapi/",
|
||||||
|
"/ui/",
|
||||||
|
"/version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If it is not working, there are two possible reasons:
|
||||||
|
|
||||||
|
1. The contents of the tokens is invalid. Find the secret name with `kubectl get secrets | grep service-account` and
|
||||||
|
delete it with `kubectl delete secret <name>`. It will automatically be recreated.
|
||||||
|
|
||||||
|
2. You have a non-standard Kubernetes installation and the file containing the token
|
||||||
|
may not be present. The API server will mount a volume containing this file, but
|
||||||
|
only if the API server is configured to use the ServiceAccount admission controller.
|
||||||
|
If you experience this error, verify that your API server is using the ServiceAccount
|
||||||
|
admission controller. If you are configuring the API server by hand, you can set
|
||||||
|
this with the `--admission-control` parameter. Please note that you should use
|
||||||
|
other admission controllers as well. Before configuring this option, you should
|
||||||
|
read about admission controllers.
|
||||||
|
|
||||||
|
More information:
|
||||||
|
|
||||||
|
* [User Guide: Service Accounts](http://kubernetes.io/docs/user-guide/service-accounts/)
|
||||||
|
* [Cluster Administrator Guide: Managing Service Accounts](http://kubernetes.io/docs/admin/service-accounts-admin/)
|
||||||
|
|
||||||
|
## kubeconfig
|
||||||
|
If you want to use a kubeconfig file for authentication, create a deployment file similar to the one below:
|
||||||
|
|
||||||
|
*Note:* the important part is the flag `--kubeconfig=/etc/kubernetes/kubeconfig.yaml`.
|
||||||
|
|
||||||
|
```
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: nginx-default-backend
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: http
|
||||||
|
selector:
|
||||||
|
app: nginx-default-backend
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: nginx-default-backend
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
app: nginx-default-backend
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- name: default-http-backend
|
||||||
|
image: gcr.io/google_containers/defaultbackend:1.0
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: ingress-nginx
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: ingress-nginx
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
selector:
|
||||||
|
app: ingress-nginx
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: http
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
targetPort: https
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ingress-nginx
|
||||||
|
labels:
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ingress-nginx
|
||||||
|
k8s-addon: ingress-nginx.addons.k8s.io
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0
|
||||||
|
name: ingress-nginx
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
- name: https
|
||||||
|
containerPort: 443
|
||||||
|
protocol: TCP
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
args:
|
||||||
|
- /nginx-ingress-controller
|
||||||
|
- --default-backend-service=$(POD_NAMESPACE)/nginx-default-backend
|
||||||
|
- --configmap=$(POD_NAMESPACE)/ingress-nginx
|
||||||
|
- --kubeconfig=/etc/kubernetes/kubeconfig.yaml
|
||||||
|
volumes:
|
||||||
|
- name: "kubeconfig"
|
||||||
|
hostPath:
|
||||||
|
path: "/etc/kubernetes/"
|
||||||
|
```
|
Loading…
Reference in a new issue