k3s/pkg/daemons/control/server.go

422 lines
13 KiB
Go
Raw Normal View History

2019-01-01 08:23:01 +00:00
package control
import (
"context"
"math/rand"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
2019-12-16 18:44:13 +00:00
"github.com/pkg/errors"
"github.com/rancher/k3s/pkg/cluster"
2019-01-09 16:54:15 +00:00
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/k3s/pkg/daemons/control/deps"
"github.com/rancher/k3s/pkg/daemons/executor"
util2 "github.com/rancher/k3s/pkg/util"
"github.com/rancher/k3s/pkg/version"
2019-10-15 21:17:26 +00:00
"github.com/rancher/wrangler-api/pkg/generated/controllers/rbac"
2019-01-01 08:23:01 +00:00
"github.com/sirupsen/logrus"
apierrors "k8s.io/apimachinery/pkg/api/errors"
2019-10-15 21:17:26 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
2019-01-01 08:23:01 +00:00
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/client-go/kubernetes"
2019-10-15 21:17:26 +00:00
"k8s.io/client-go/tools/clientcmd"
app2 "k8s.io/controller-manager/app"
2019-01-01 08:23:01 +00:00
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
2019-02-08 04:14:02 +00:00
"k8s.io/kubernetes/pkg/proxy/util"
// for client metric registration
_ "k8s.io/component-base/metrics/prometheus/restclient"
2019-01-01 08:23:01 +00:00
)
var localhostIP = net.ParseIP("127.0.0.1")
2019-09-03 23:41:54 +00:00
2019-01-01 08:23:01 +00:00
func Server(ctx context.Context, cfg *config.Control) error {
rand.Seed(time.Now().UTC().UnixNano())
runtime := &config.ControlRuntime{}
cfg.Runtime = runtime
if err := prepare(ctx, cfg, runtime); err != nil {
2019-12-16 18:44:13 +00:00
return errors.Wrap(err, "preparing server")
2019-01-01 08:23:01 +00:00
}
cfg.Runtime.Tunnel = setupTunnel()
2019-02-08 04:14:02 +00:00
util.DisableProxyHostnameCheck = true
2019-01-01 08:23:01 +00:00
var auth authenticator.Request
var handler http.Handler
var err error
2019-01-01 08:23:01 +00:00
if !cfg.DisableAPIServer {
auth, handler, err = apiServer(ctx, cfg, runtime)
if err != nil {
return err
}
if err := waitForAPIServerInBackground(ctx, runtime); err != nil {
return err
}
}
2020-08-26 19:47:25 +00:00
basicAuth, err := basicAuthenticator(runtime.PasswdFile)
if err != nil {
return err
}
runtime.Authenticator = combineAuthenticators(basicAuth, auth)
2019-01-01 08:23:01 +00:00
runtime.Handler = handler
if !cfg.DisableScheduler {
if err := scheduler(cfg, runtime); err != nil {
return err
}
2019-01-01 08:23:01 +00:00
}
if !cfg.DisableControllerManager {
if err := controllerManager(cfg, runtime); err != nil {
return err
}
}
2019-01-01 08:23:01 +00:00
2019-10-15 21:17:26 +00:00
if !cfg.DisableCCM {
if err := cloudControllerManager(ctx, cfg, runtime); err != nil {
return err
}
2019-10-15 21:17:26 +00:00
}
2019-01-01 08:23:01 +00:00
return nil
}
func controllerManager(cfg *config.Control, runtime *config.ControlRuntime) error {
argsMap := map[string]string{
2019-05-29 18:53:51 +00:00
"kubeconfig": runtime.KubeConfigController,
"service-account-private-key-file": runtime.ServiceKey,
"allocate-node-cidrs": "true",
"cluster-cidr": util2.JoinIPNets(cfg.ClusterIPRanges),
2019-05-29 18:53:51 +00:00
"root-ca-file": runtime.ServerCA,
"port": "10252",
"profiling": "false",
"address": localhostIP.String(),
2019-05-29 18:53:51 +00:00
"bind-address": localhostIP.String(),
"secure-port": "0",
2019-05-29 18:53:51 +00:00
"use-service-account-credentials": "true",
"cluster-signing-kube-apiserver-client-cert-file": runtime.ClientCA,
"cluster-signing-kube-apiserver-client-key-file": runtime.ClientCAKey,
"cluster-signing-kubelet-client-cert-file": runtime.ClientCA,
"cluster-signing-kubelet-client-key-file": runtime.ClientCAKey,
"cluster-signing-kubelet-serving-cert-file": runtime.ServerCA,
"cluster-signing-kubelet-serving-key-file": runtime.ServerCAKey,
"cluster-signing-legacy-unknown-cert-file": runtime.ClientCA,
"cluster-signing-legacy-unknown-key-file": runtime.ClientCAKey,
2019-01-01 08:23:01 +00:00
}
2019-01-22 21:14:58 +00:00
if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false"
2019-01-22 21:14:58 +00:00
}
if !cfg.DisableCCM {
argsMap["configure-cloud-routes"] = "false"
argsMap["controllers"] = "*,-service,-route,-cloud-node-lifecycle"
}
args := config.GetArgsList(argsMap, cfg.ExtraControllerArgs)
logrus.Infof("Running kube-controller-manager %s", config.ArgString(args))
return executor.ControllerManager(runtime.APIServerReady, args)
2019-01-01 08:23:01 +00:00
}
func scheduler(cfg *config.Control, runtime *config.ControlRuntime) error {
argsMap := map[string]string{
2019-05-29 18:53:51 +00:00
"kubeconfig": runtime.KubeConfigScheduler,
"port": "10251",
"address": "127.0.0.1",
"bind-address": "127.0.0.1",
"secure-port": "0",
"profiling": "false",
2019-01-01 08:23:01 +00:00
}
2019-01-22 21:14:58 +00:00
if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false"
2019-01-22 21:14:58 +00:00
}
args := config.GetArgsList(argsMap, cfg.ExtraSchedulerAPIArgs)
logrus.Infof("Running kube-scheduler %s", config.ArgString(args))
return executor.Scheduler(runtime.APIServerReady, args)
2019-01-01 08:23:01 +00:00
}
2019-01-09 16:54:15 +00:00
func apiServer(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) (authenticator.Request, http.Handler, error) {
argsMap := make(map[string]string)
2019-06-11 22:48:47 +00:00
setupStorageBackend(argsMap, cfg)
2019-01-01 08:23:01 +00:00
2020-04-25 07:22:48 +00:00
certDir := filepath.Join(cfg.DataDir, "tls", "temporary-certs")
os.MkdirAll(certDir, 0700)
argsMap["cert-dir"] = certDir
argsMap["allow-privileged"] = "true"
argsMap["authorization-mode"] = strings.Join([]string{modes.ModeNode, modes.ModeRBAC}, ",")
argsMap["service-account-signing-key-file"] = runtime.ServiceKey
argsMap["service-cluster-ip-range"] = util2.JoinIPNets(cfg.ServiceIPRanges)
argsMap["service-node-port-range"] = cfg.ServiceNodePortRange.String()
argsMap["advertise-port"] = strconv.Itoa(cfg.AdvertisePort)
2019-05-29 18:53:51 +00:00
if cfg.AdvertiseIP != "" {
argsMap["advertise-address"] = cfg.AdvertiseIP
}
argsMap["insecure-port"] = "0"
argsMap["secure-port"] = strconv.Itoa(cfg.APIServerPort)
if cfg.APIServerBindAddress == "" {
argsMap["bind-address"] = localhostIP.String()
} else {
argsMap["bind-address"] = cfg.APIServerBindAddress
}
2019-05-29 18:53:51 +00:00
argsMap["tls-cert-file"] = runtime.ServingKubeAPICert
argsMap["tls-private-key-file"] = runtime.ServingKubeAPIKey
argsMap["service-account-key-file"] = runtime.ServiceKey
argsMap["service-account-issuer"] = "https://kubernetes.default.svc." + cfg.ClusterDomain
argsMap["api-audiences"] = "https://kubernetes.default.svc." + cfg.ClusterDomain + "," + version.Program
argsMap["kubelet-certificate-authority"] = runtime.ServerCA
2019-05-29 18:53:51 +00:00
argsMap["kubelet-client-certificate"] = runtime.ClientKubeAPICert
argsMap["kubelet-client-key"] = runtime.ClientKubeAPIKey
argsMap["requestheader-client-ca-file"] = runtime.RequestHeaderCA
argsMap["requestheader-allowed-names"] = deps.RequestHeaderCN
argsMap["proxy-client-cert-file"] = runtime.ClientAuthProxyCert
argsMap["proxy-client-key-file"] = runtime.ClientAuthProxyKey
argsMap["requestheader-extra-headers-prefix"] = "X-Remote-Extra-"
argsMap["requestheader-group-headers"] = "X-Remote-Group"
argsMap["requestheader-username-headers"] = "X-Remote-User"
2019-05-29 18:53:51 +00:00
argsMap["client-ca-file"] = runtime.ClientCA
argsMap["enable-admission-plugins"] = "NodeRestriction"
argsMap["anonymous-auth"] = "false"
argsMap["profiling"] = "false"
2019-12-12 22:41:10 +00:00
if cfg.EncryptSecrets {
argsMap["encryption-provider-config"] = runtime.EncryptionConfig
}
args := config.GetArgsList(argsMap, cfg.ExtraAPIArgs)
logrus.Infof("Running kube-apiserver %s", config.ArgString(args))
return executor.APIServer(ctx, runtime.ETCDReady, args)
2019-01-01 08:23:01 +00:00
}
func defaults(config *config.Control) {
if config.ClusterIPRange == nil {
_, clusterIPNet, _ := net.ParseCIDR("10.42.0.0/16")
config.ClusterIPRange = clusterIPNet
}
if config.ServiceIPRange == nil {
_, serviceIPNet, _ := net.ParseCIDR("10.43.0.0/16")
config.ServiceIPRange = serviceIPNet
}
2019-01-09 16:54:15 +00:00
if len(config.ClusterDNS) == 0 {
config.ClusterDNS = net.ParseIP("10.43.0.10")
}
2019-01-01 08:23:01 +00:00
if config.AdvertisePort == 0 {
2019-05-29 18:53:51 +00:00
config.AdvertisePort = config.HTTPSPort
2019-01-01 08:23:01 +00:00
}
if config.APIServerPort == 0 {
if config.HTTPSPort != 0 {
config.APIServerPort = config.HTTPSPort + 1
} else {
config.APIServerPort = 6444
}
2019-01-01 08:23:01 +00:00
}
if config.DataDir == "" {
config.DataDir = "./management-state"
}
}
func prepare(ctx context.Context, config *config.Control, runtime *config.ControlRuntime) error {
2019-01-01 08:23:01 +00:00
var err error
defaults(config)
if err := os.MkdirAll(config.DataDir, 0700); err != nil {
2019-01-01 08:23:01 +00:00
return err
}
config.DataDir, err = filepath.Abs(config.DataDir)
if err != nil {
return err
}
2020-04-25 07:22:48 +00:00
os.MkdirAll(filepath.Join(config.DataDir, "tls"), 0700)
os.MkdirAll(filepath.Join(config.DataDir, "cred"), 0700)
deps.FillRuntimeCerts(config, runtime)
2019-12-12 22:41:10 +00:00
cluster := cluster.New(config)
2020-05-05 21:59:15 +00:00
if err := cluster.Bootstrap(ctx); err != nil {
2019-06-27 19:00:43 +00:00
return err
}
if err := deps.GenServerDeps(config, runtime); err != nil {
return err
}
2020-05-05 21:59:15 +00:00
ready, err := cluster.Start(ctx)
if err != nil {
return err
}
runtime.ETCDReady = ready
return nil
2019-05-29 18:53:51 +00:00
}
2019-01-01 08:23:01 +00:00
2019-06-11 22:48:47 +00:00
func setupStorageBackend(argsMap map[string]string, cfg *config.Control) {
argsMap["storage-backend"] = "etcd3"
2019-06-11 22:48:47 +00:00
// specify the endpoints
2019-11-16 00:12:27 +00:00
if len(cfg.Datastore.Endpoint) > 0 {
argsMap["etcd-servers"] = cfg.Datastore.Endpoint
2019-06-11 22:48:47 +00:00
}
// storage backend tls configuration
2019-11-16 00:12:27 +00:00
if len(cfg.Datastore.CAFile) > 0 {
argsMap["etcd-cafile"] = cfg.Datastore.CAFile
2019-06-11 22:48:47 +00:00
}
2019-11-16 00:12:27 +00:00
if len(cfg.Datastore.CertFile) > 0 {
argsMap["etcd-certfile"] = cfg.Datastore.CertFile
2019-06-11 22:48:47 +00:00
}
2019-11-16 00:12:27 +00:00
if len(cfg.Datastore.KeyFile) > 0 {
argsMap["etcd-keyfile"] = cfg.Datastore.KeyFile
2019-06-11 22:48:47 +00:00
}
}
2019-09-26 22:38:04 +00:00
func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) error {
2019-10-15 21:17:26 +00:00
argsMap := map[string]string{
"profiling": "false",
"allocate-node-cidrs": "true", // ccmOptions.KubeCloudShared.AllocateNodeCIDRs = true
"cloud-provider": version.Program, // ccmOptions.KubeCloudShared.CloudProvider.Name = version.Program
"cluster-cidr": util2.JoinIPNets(cfg.ClusterIPRanges), // ccmOptions.KubeCloudShared.ClusterCIDR = util2.JoinIPNets(cfg.ClusterIPRanges)
"configure-cloud-routes": "false", // ccmOptions.KubeCloudShared.ConfigureCloudRoutes = false
"kubeconfig": runtime.KubeConfigCloudController, // ccmOptions.Kubeconfig = runtime.KubeConfigCloudController
"node-status-update-frequency": "1m0s", // ccmOptions.NodeStatusUpdateFrequency = metav1.Duration{Duration: 1 * time.Minute}
"bind-address": "127.0.0.1", // ccmOptions.SecureServing.BindAddress = localhostIP
"port": "0", // ccmOptions.SecureServing.BindPort = 0
2019-10-15 21:17:26 +00:00
}
if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false"
2019-10-15 21:17:26 +00:00
}
args := config.GetArgsList(argsMap, cfg.ExtraCloudControllerArgs)
2019-10-15 21:17:26 +00:00
logrus.Infof("Running cloud-controller-manager %s", config.ArgString(args))
ccmRBACReady := make(chan struct{})
go func() {
defer close(ccmRBACReady)
2019-10-15 21:17:26 +00:00
apiReadyLoop:
for {
select {
case <-ctx.Done():
return
case <-runtime.APIServerReady:
break apiReadyLoop
case <-time.After(30 * time.Second):
logrus.Infof("Waiting for API server to become available")
}
}
2019-10-15 21:17:26 +00:00
logrus.Infof("Waiting for cloud-controller-manager privileges to become available")
2019-10-15 21:17:26 +00:00
for {
select {
case <-ctx.Done():
return
case err := <-promise(func() error { return checkForCloudControllerPrivileges(runtime, 5*time.Second) }):
if err != nil {
logrus.Infof("Waiting for cloud-controller-manager privileges to become available")
continue
}
return
2019-10-15 21:17:26 +00:00
}
}
}()
return executor.CloudControllerManager(ccmRBACReady, args)
2019-10-15 21:17:26 +00:00
}
func checkForCloudControllerPrivileges(runtime *config.ControlRuntime, timeout time.Duration) error {
2019-10-15 21:17:26 +00:00
restConfig, err := clientcmd.BuildConfigFromFlags("", runtime.KubeConfigAdmin)
if err != nil {
return err
}
err = wait.PollImmediate(time.Second, timeout, func() (bool, error) {
crb := rbac.NewFactoryFromConfigOrDie(restConfig).Rbac().V1().ClusterRoleBinding()
_, err = crb.Get(version.Program+"-cloud-controller-manager", metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
return false, err
}
return true, nil
})
2019-10-15 21:17:26 +00:00
if err != nil {
logrus.Errorf("error encountered waitng for cloud-controller-manager privileges: %v", err)
2019-10-15 21:17:26 +00:00
}
return nil
}
func waitForAPIServerInBackground(ctx context.Context, runtime *config.ControlRuntime) error {
restConfig, err := clientcmd.BuildConfigFromFlags("", runtime.KubeConfigAdmin)
if err != nil {
return err
}
k8sClient, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return err
}
done := make(chan struct{})
runtime.APIServerReady = done
go func() {
defer close(done)
etcdLoop:
for {
select {
case <-ctx.Done():
return
case <-runtime.ETCDReady:
break etcdLoop
case <-time.After(30 * time.Second):
logrus.Infof("Waiting for etcd server to become available")
}
}
logrus.Infof("Waiting for API server to become available")
for {
select {
case <-ctx.Done():
return
case err := <-promise(func() error { return app2.WaitForAPIServer(k8sClient, 30*time.Second) }):
if err != nil {
logrus.Infof("Waiting for API server to become available")
continue
}
return
}
}
}()
return nil
}
func promise(f func() error) <-chan error {
c := make(chan error, 1)
go func() {
c <- f()
close(c)
}()
return c
}