mirror of https://github.com/kubernetes/kops.git
				
				
				
			
		
			
				
	
	
		
			247 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			247 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2019 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package model
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"k8s.io/kops/nodeup/pkg/distros"
 | |
| 	"k8s.io/kops/pkg/flagbuilder"
 | |
| 	"k8s.io/kops/pkg/k8scodecs"
 | |
| 	"k8s.io/kops/pkg/kubemanifest"
 | |
| 	"k8s.io/kops/upup/pkg/fi"
 | |
| 	"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
 | |
| 	"k8s.io/kops/util/pkg/exec"
 | |
| 	"k8s.io/kops/util/pkg/proxy"
 | |
| 
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	"k8s.io/apimachinery/pkg/api/resource"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/util/intstr"
 | |
| )
 | |
| 
 | |
| // KubeControllerManagerBuilder install kube-controller-manager (just the manifest at the moment)
 | |
| type KubeControllerManagerBuilder struct {
 | |
| 	*NodeupModelContext
 | |
| }
 | |
| 
 | |
| var _ fi.ModelBuilder = &KubeControllerManagerBuilder{}
 | |
| 
 | |
| // Build is responsible for configuring the kube-controller-manager
 | |
| func (b *KubeControllerManagerBuilder) Build(c *fi.ModelBuilderContext) error {
 | |
| 	if !b.IsMaster {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// If we're using the CertificateSigner, include the CA Key
 | |
| 	// @TODO: use a per-machine key?  use KMS?
 | |
| 	if b.useCertificateSigner() {
 | |
| 		if err := b.BuildPrivateKeyTask(c, fi.CertificateId_CA, "ca.key"); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		pod, err := b.buildPod()
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("error building kube-controller-manager pod: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		manifest, err := k8scodecs.ToVersionedYaml(pod)
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("error marshaling pod to yaml: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		c.AddTask(&nodetasks.File{
 | |
| 			Path:     "/etc/kubernetes/manifests/kube-controller-manager.manifest",
 | |
| 			Contents: fi.NewBytesResource(manifest),
 | |
| 			Type:     nodetasks.FileType_File,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		c.AddTask(&nodetasks.File{
 | |
| 			Path:        "/var/log/kube-controller-manager.log",
 | |
| 			Contents:    fi.NewStringResource(""),
 | |
| 			Type:        nodetasks.FileType_File,
 | |
| 			Mode:        s("0400"),
 | |
| 			IfNotExists: true,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	// Add kubeconfig
 | |
| 	{
 | |
| 		// @TODO: Change kubeconfig to be https
 | |
| 		kubeconfig, err := b.BuildPKIKubeconfig("kube-controller-manager")
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		c.AddTask(&nodetasks.File{
 | |
| 			Path:     "/var/lib/kube-controller-manager/kubeconfig",
 | |
| 			Contents: fi.NewStringResource(kubeconfig),
 | |
| 			Type:     nodetasks.FileType_File,
 | |
| 			Mode:     s("0400"),
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // useCertificateSigner checks to see if we need to use the certificate signer for the controller manager
 | |
| func (b *KubeControllerManagerBuilder) useCertificateSigner() bool {
 | |
| 	// For now, we enable this on 1.6 and later
 | |
| 	return b.IsKubernetesGTE("1.6")
 | |
| }
 | |
| 
 | |
| // buildPod is responsible for building the kubernetes manifest for the controller-manager
 | |
| func (b *KubeControllerManagerBuilder) buildPod() (*v1.Pod, error) {
 | |
| 
 | |
| 	kcm := b.Cluster.Spec.KubeControllerManager
 | |
| 	kcm.RootCAFile = filepath.Join(b.PathSrvKubernetes(), "ca.crt")
 | |
| 	kcm.ServiceAccountPrivateKeyFile = filepath.Join(b.PathSrvKubernetes(), "server.key")
 | |
| 
 | |
| 	flags, err := flagbuilder.BuildFlagsList(kcm)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error building kube-controller-manager flags: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Add cloud config file if needed
 | |
| 	if b.Cluster.Spec.CloudConfig != nil {
 | |
| 		flags = append(flags, "--cloud-config="+CloudConfigFilePath)
 | |
| 	}
 | |
| 
 | |
| 	// Add kubeconfig flag
 | |
| 	flags = append(flags, "--kubeconfig="+"/var/lib/kube-controller-manager/kubeconfig")
 | |
| 
 | |
| 	// Configure CA certificate to be used to sign keys, if we are using CSRs
 | |
| 	if b.useCertificateSigner() {
 | |
| 		flags = append(flags, []string{
 | |
| 			"--cluster-signing-cert-file=" + filepath.Join(b.PathSrvKubernetes(), "ca.crt"),
 | |
| 			"--cluster-signing-key-file=" + filepath.Join(b.PathSrvKubernetes(), "ca.key")}...)
 | |
| 	}
 | |
| 
 | |
| 	pod := &v1.Pod{
 | |
| 		TypeMeta: metav1.TypeMeta{
 | |
| 			APIVersion: "v1",
 | |
| 			Kind:       "Pod",
 | |
| 		},
 | |
| 		ObjectMeta: metav1.ObjectMeta{
 | |
| 			Name:      "kube-controller-manager",
 | |
| 			Namespace: "kube-system",
 | |
| 			Labels: map[string]string{
 | |
| 				"k8s-app": "kube-controller-manager",
 | |
| 			},
 | |
| 		},
 | |
| 		Spec: v1.PodSpec{
 | |
| 			HostNetwork: true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	volumePluginDir := b.Cluster.Spec.Kubelet.VolumePluginDirectory
 | |
| 
 | |
| 	// Ensure the Volume Plugin dir is mounted on the same path as the host machine so DaemonSet deployment is possible
 | |
| 	if volumePluginDir == "" {
 | |
| 		switch b.Distribution {
 | |
| 		case distros.DistributionContainerOS:
 | |
| 			// Default is different on ContainerOS, see https://github.com/kubernetes/kubernetes/pull/58171
 | |
| 			volumePluginDir = "/home/kubernetes/flexvolume/"
 | |
| 
 | |
| 		case distros.DistributionCoreOS:
 | |
| 			// The /usr directory is read-only for CoreOS
 | |
| 			volumePluginDir = "/var/lib/kubelet/volumeplugins/"
 | |
| 
 | |
| 		case distros.DistributionFlatcar:
 | |
| 			// The /usr directory is read-only for Flatcar
 | |
| 			volumePluginDir = "/var/lib/kubelet/volumeplugins/"
 | |
| 
 | |
| 		default:
 | |
| 			volumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Add the volumePluginDir flag if provided in the kubelet spec, or set above based on the OS
 | |
| 	flags = append(flags, "--flex-volume-plugin-dir="+volumePluginDir)
 | |
| 
 | |
| 	container := &v1.Container{
 | |
| 		Name:  "kube-controller-manager",
 | |
| 		Image: b.Cluster.Spec.KubeControllerManager.Image,
 | |
| 		Env:   proxy.GetProxyEnvVars(b.Cluster.Spec.EgressProxy),
 | |
| 		LivenessProbe: &v1.Probe{
 | |
| 			Handler: v1.Handler{
 | |
| 				HTTPGet: &v1.HTTPGetAction{
 | |
| 					Host: "127.0.0.1",
 | |
| 					Path: "/healthz",
 | |
| 					Port: intstr.FromInt(10252),
 | |
| 				},
 | |
| 			},
 | |
| 			InitialDelaySeconds: 15,
 | |
| 			TimeoutSeconds:      15,
 | |
| 		},
 | |
| 		Resources: v1.ResourceRequirements{
 | |
| 			Requests: v1.ResourceList{
 | |
| 				v1.ResourceCPU: resource.MustParse("100m"),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Log both to docker and to the logfile
 | |
| 	addHostPathMapping(pod, container, "logfile", "/var/log/kube-controller-manager.log").ReadOnly = false
 | |
| 	if b.IsKubernetesGTE("1.15") {
 | |
| 		// From k8s 1.15, we use lighter containers that don't include shells
 | |
| 		// But they have richer logging support via klog
 | |
| 		container.Command = []string{"/usr/local/bin/kube-controller-manager"}
 | |
| 		container.Args = append(
 | |
| 			sortedStrings(flags),
 | |
| 			"--logtostderr=false", //https://github.com/kubernetes/klog/issues/60
 | |
| 			"--alsologtostderr",
 | |
| 			"--log-file=/var/log/kube-controller-manager.log")
 | |
| 	} else {
 | |
| 		container.Command = exec.WithTee(
 | |
| 			"/usr/local/bin/kube-controller-manager",
 | |
| 			sortedStrings(flags),
 | |
| 			"/var/log/kube-controller-manager.log")
 | |
| 	}
 | |
| 
 | |
| 	for _, path := range b.SSLHostPaths() {
 | |
| 		name := strings.Replace(path, "/", "", -1)
 | |
| 		addHostPathMapping(pod, container, name, path)
 | |
| 	}
 | |
| 
 | |
| 	// Add cloud config file if needed
 | |
| 	if b.Cluster.Spec.CloudConfig != nil {
 | |
| 		addHostPathMapping(pod, container, "cloudconfig", CloudConfigFilePath)
 | |
| 	}
 | |
| 
 | |
| 	pathSrvKubernetes := b.PathSrvKubernetes()
 | |
| 	if pathSrvKubernetes != "" {
 | |
| 		addHostPathMapping(pod, container, "srvkube", pathSrvKubernetes)
 | |
| 	}
 | |
| 
 | |
| 	addHostPathMapping(pod, container, "varlibkcm", "/var/lib/kube-controller-manager")
 | |
| 
 | |
| 	addHostPathMapping(pod, container, "volplugins", volumePluginDir).ReadOnly = false
 | |
| 
 | |
| 	pod.Spec.Containers = append(pod.Spec.Containers, *container)
 | |
| 
 | |
| 	kubemanifest.MarkPodAsCritical(pod)
 | |
| 	kubemanifest.MarkPodAsClusterCritical(pod)
 | |
| 
 | |
| 	return pod, nil
 | |
| }
 |