mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 22:17:14 +00:00
Refactor kubelet, standalone k8s and integration test to all use the same code.
This commit is contained in:
parent
ff1e9f4c19
commit
d47b510104
@ -23,7 +23,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -40,12 +39,11 @@ import (
|
|||||||
minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller"
|
minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller"
|
||||||
replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
|
replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/health"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/health"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/standalone"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
|
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
|
||||||
@ -56,6 +54,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const testRootDir = "/tmp/kubelet"
|
const testRootDir = "/tmp/kubelet"
|
||||||
|
const testRootDir2 = "/tmp/kubelet2"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fakeDocker1, fakeDocker2 dockertools.FakeDockerClient
|
fakeDocker1, fakeDocker2 dockertools.FakeDockerClient
|
||||||
@ -187,26 +186,11 @@ func startComponents(manifestURL string) (apiServerURL string) {
|
|||||||
minionController.Run(10 * time.Second)
|
minionController.Run(10 * time.Second)
|
||||||
|
|
||||||
// Kubelet (localhost)
|
// Kubelet (localhost)
|
||||||
os.MkdirAll(testRootDir, 0750)
|
standalone.SimpleRunKubelet(etcdClient, &fakeDocker1, machineList[0], testRootDir, manifestURL, "127.0.0.1", 10250)
|
||||||
cfg1 := config.NewPodConfig(config.PodConfigNotificationSnapshotAndUpdates)
|
|
||||||
config.NewSourceEtcd(config.EtcdKeyForHost(machineList[0]), etcdClient, cfg1.Channel("etcd"))
|
|
||||||
config.NewSourceURL(manifestURL, 5*time.Second, cfg1.Channel("url"))
|
|
||||||
myKubelet := kubelet.NewIntegrationTestKubelet(machineList[0], testRootDir, &fakeDocker1)
|
|
||||||
go util.Forever(func() { myKubelet.Run(cfg1.Updates()) }, 0)
|
|
||||||
go util.Forever(func() {
|
|
||||||
kubelet.ListenAndServeKubeletServer(myKubelet, cfg1.Channel("http"), net.ParseIP("127.0.0.1"), 10250, true)
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
// Kubelet (machine)
|
// Kubelet (machine)
|
||||||
// Create a second kubelet so that the guestbook example's two redis slaves both
|
// Create a second kubelet so that the guestbook example's two redis slaves both
|
||||||
// have a place they can schedule.
|
// have a place they can schedule.
|
||||||
cfg2 := config.NewPodConfig(config.PodConfigNotificationSnapshotAndUpdates)
|
standalone.SimpleRunKubelet(etcdClient, &fakeDocker2, machineList[1], testRootDir2, "", "127.0.0.1", 10251)
|
||||||
config.NewSourceEtcd(config.EtcdKeyForHost(machineList[1]), etcdClient, cfg2.Channel("etcd"))
|
|
||||||
otherKubelet := kubelet.NewIntegrationTestKubelet(machineList[1], testRootDir, &fakeDocker2)
|
|
||||||
go util.Forever(func() { otherKubelet.Run(cfg2.Updates()) }, 0)
|
|
||||||
go util.Forever(func() {
|
|
||||||
kubelet.ListenAndServeKubeletServer(otherKubelet, cfg2.Channel("http"), net.ParseIP("127.0.0.1"), 10251, true)
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
return apiServer.URL
|
return apiServer.URL
|
||||||
}
|
}
|
||||||
|
@ -24,28 +24,15 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/health"
|
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/standalone"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version/verflag"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/version/verflag"
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"github.com/fsouza/go-dockerclient"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
cadvisor "github.com/google/cadvisor/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultRootDir = "/var/lib/kubelet"
|
const defaultRootDir = "/var/lib/kubelet"
|
||||||
@ -82,52 +69,15 @@ func init() {
|
|||||||
flag.Var(&apiServerList, "api_servers", "List of Kubernetes API servers to publish events to. (ip:port), comma separated.")
|
flag.Var(&apiServerList, "api_servers", "List of Kubernetes API servers to publish events to. (ip:port), comma separated.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDockerEndpoint() string {
|
func setupRunOnce() {
|
||||||
var endpoint string
|
if *runonce {
|
||||||
if len(*dockerEndpoint) > 0 {
|
if len(etcdServerList) > 0 {
|
||||||
endpoint = *dockerEndpoint
|
glog.Fatalf("invalid option: --runonce and --etcd_servers are mutually exclusive")
|
||||||
} else if len(os.Getenv("DOCKER_HOST")) > 0 {
|
|
||||||
endpoint = os.Getenv("DOCKER_HOST")
|
|
||||||
} else {
|
|
||||||
endpoint = "unix:///var/run/docker.sock"
|
|
||||||
}
|
}
|
||||||
glog.Infof("Connecting to docker on %s", endpoint)
|
if *enableServer {
|
||||||
|
glog.Infof("--runonce is set, disabling server")
|
||||||
return endpoint
|
*enableServer = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHostname() string {
|
|
||||||
hostname := []byte(*hostnameOverride)
|
|
||||||
if string(hostname) == "" {
|
|
||||||
// Note: We use exec here instead of os.Hostname() because we
|
|
||||||
// want the FQDN, and this is the easiest way to get it.
|
|
||||||
fqdn, err := exec.Command("hostname", "-f").Output()
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalf("Couldn't determine hostname: %v", err)
|
|
||||||
}
|
|
||||||
hostname = fqdn
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(hostname))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getApiserverClient() (*client.Client, error) {
|
|
||||||
authInfo, err := clientauth.LoadFromFile(*authPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
clientConfig, err := authInfo.MergeWithConfig(client.Config{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// TODO: adapt Kube client to support LB over several servers
|
|
||||||
if len(apiServerList) > 1 {
|
|
||||||
glog.Infof("Mulitple api servers specified. Picking first one")
|
|
||||||
}
|
|
||||||
clientConfig.Host = apiServerList[0]
|
|
||||||
if c, err := client.New(&clientConfig); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
return c, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,147 +89,34 @@ func main() {
|
|||||||
|
|
||||||
verflag.PrintAndExitIfRequested()
|
verflag.PrintAndExitIfRequested()
|
||||||
|
|
||||||
if *runonce {
|
setupRunOnce()
|
||||||
exclusiveFlag := "invalid option: --runonce and %s are mutually exclusive"
|
|
||||||
if len(etcdServerList) > 0 {
|
|
||||||
glog.Fatalf(exclusiveFlag, "--etcd_servers")
|
|
||||||
}
|
|
||||||
if *enableServer {
|
|
||||||
glog.Infof("--runonce is set, disabling server")
|
|
||||||
*enableServer = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
etcd.SetLogger(util.NewLogger("etcd "))
|
kcfg := standalone.KubeletConfig{
|
||||||
|
Address: address,
|
||||||
// Make an API client if possible.
|
AuthPath: *authPath,
|
||||||
if len(apiServerList) < 1 {
|
ApiServerList: apiServerList,
|
||||||
glog.Info("No api servers specified.")
|
|
||||||
} else {
|
|
||||||
if apiClient, err := getApiserverClient(); err != nil {
|
|
||||||
glog.Errorf("Unable to make apiserver client: %v", err)
|
|
||||||
} else {
|
|
||||||
// Send events to APIserver if there is a client.
|
|
||||||
glog.Infof("Sending events to APIserver.")
|
|
||||||
record.StartRecording(apiClient.Events(""), "kubelet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the events locally too.
|
|
||||||
record.StartLogging(glog.Infof)
|
|
||||||
|
|
||||||
capabilities.Initialize(capabilities.Capabilities{
|
|
||||||
AllowPrivileged: *allowPrivileged,
|
AllowPrivileged: *allowPrivileged,
|
||||||
})
|
HostnameOverride: *hostnameOverride,
|
||||||
|
RootDirectory: *rootDirectory,
|
||||||
dockerClient, err := docker.NewClient(getDockerEndpoint())
|
ConfigFile: *config,
|
||||||
if err != nil {
|
ManifestURL: *manifestURL,
|
||||||
glog.Fatal("Couldn't connect to docker.")
|
FileCheckFrequency: *fileCheckFrequency,
|
||||||
}
|
HttpCheckFrequency: *httpCheckFrequency,
|
||||||
|
NetworkContainerImage: *networkContainerImage,
|
||||||
hostname := getHostname()
|
SyncFrequency: *syncFrequency,
|
||||||
|
RegistryPullQPS: *registryPullQPS,
|
||||||
if *rootDirectory == "" {
|
RegistryBurst: *registryBurst,
|
||||||
glog.Fatal("Invalid root directory path.")
|
MinimumGCAge: *minimumGCAge,
|
||||||
}
|
MaxContainerCount: *maxContainerCount,
|
||||||
*rootDirectory = path.Clean(*rootDirectory)
|
Runonce: *runonce,
|
||||||
if err := os.MkdirAll(*rootDirectory, 0750); err != nil {
|
Port: *port,
|
||||||
glog.Fatalf("Error creating root directory: %v", err)
|
EnableServer: *enableServer,
|
||||||
}
|
EnableDebuggingHandlers: *enableDebuggingHandlers,
|
||||||
|
DockerClient: kubelet.ConnectToDockerOrDie(*dockerEndpoint),
|
||||||
// source of all configuration
|
EtcdClient: kubelet.EtcdClientOrDie(etcdServerList, *etcdConfigFile),
|
||||||
cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates)
|
|
||||||
|
|
||||||
// define file config source
|
|
||||||
if *config != "" {
|
|
||||||
kconfig.NewSourceFile(*config, *fileCheckFrequency, cfg.Channel("file"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// define url config source
|
|
||||||
if *manifestURL != "" {
|
|
||||||
kconfig.NewSourceURL(*manifestURL, *httpCheckFrequency, cfg.Channel("http"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// define etcd config source and initialize etcd client
|
|
||||||
var etcdClient *etcd.Client
|
|
||||||
if len(etcdServerList) > 0 {
|
|
||||||
etcdClient = etcd.NewClient(etcdServerList)
|
|
||||||
} else if *etcdConfigFile != "" {
|
|
||||||
var err error
|
|
||||||
etcdClient, err = etcd.NewClientFromFile(*etcdConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalf("Error with etcd config file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if etcdClient != nil {
|
|
||||||
glog.Infof("Watching for etcd configs at %v", etcdClient.GetCluster())
|
|
||||||
kconfig.NewSourceEtcd(kconfig.EtcdKeyForHost(hostname), etcdClient, cfg.Channel("etcd"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: block until all sources have delivered at least one update to the channel, or break the sync loop
|
|
||||||
// up into "per source" synchronizations
|
|
||||||
|
|
||||||
k := kubelet.NewMainKubelet(
|
|
||||||
getHostname(),
|
|
||||||
dockerClient,
|
|
||||||
etcdClient,
|
|
||||||
*rootDirectory,
|
|
||||||
*networkContainerImage,
|
|
||||||
*syncFrequency,
|
|
||||||
float32(*registryPullQPS),
|
|
||||||
*registryBurst,
|
|
||||||
*minimumGCAge,
|
|
||||||
*maxContainerCount)
|
|
||||||
|
|
||||||
k.BirthCry()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
util.Forever(func() {
|
|
||||||
err := k.GarbageCollectContainers()
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("Garbage collect failed: %v", err)
|
|
||||||
}
|
|
||||||
}, time.Minute*1)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer util.HandleCrash()
|
|
||||||
// TODO: Monitor this connection, reconnect if needed?
|
|
||||||
glog.V(1).Infof("Trying to create cadvisor client.")
|
|
||||||
cadvisorClient, err := cadvisor.NewClient("http://127.0.0.1:4194")
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("Error on creating cadvisor client: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
glog.V(1).Infof("Successfully created cadvisor client.")
|
|
||||||
k.SetCadvisorClient(cadvisorClient)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// TODO: These should probably become more plugin-ish: register a factory func
|
|
||||||
// in each checker's init(), iterate those here.
|
|
||||||
health.AddHealthChecker(health.NewExecHealthChecker(k))
|
|
||||||
health.AddHealthChecker(health.NewHTTPHealthChecker(&http.Client{}))
|
|
||||||
health.AddHealthChecker(&health.TCPHealthChecker{})
|
|
||||||
|
|
||||||
// process pods and exit.
|
|
||||||
if *runonce {
|
|
||||||
if _, err := k.RunOnce(cfg.Updates()); err != nil {
|
|
||||||
glog.Fatalf("--runonce failed: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the kubelet
|
|
||||||
go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
|
|
||||||
|
|
||||||
// start the kubelet server
|
|
||||||
if *enableServer {
|
|
||||||
go util.Forever(func() {
|
|
||||||
kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), net.IP(address), *port, *enableDebuggingHandlers)
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
standalone.RunKubelet(&kcfg)
|
||||||
// runs forever
|
// runs forever
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/standalone"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/standalone"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
@ -50,7 +51,9 @@ func startComponents(etcdClient tools.EtcdClient, cl *client.Client, addr string
|
|||||||
standalone.RunApiServer(cl, etcdClient, addr, port)
|
standalone.RunApiServer(cl, etcdClient, addr, port)
|
||||||
standalone.RunScheduler(cl)
|
standalone.RunScheduler(cl)
|
||||||
standalone.RunControllerManager(machineList, cl, *nodeMilliCPU, *nodeMemory)
|
standalone.RunControllerManager(machineList, cl, *nodeMilliCPU, *nodeMemory)
|
||||||
standalone.RunKubelet(etcdClient, machineList[0], *dockerEndpoint)
|
|
||||||
|
dockerClient := kubelet.ConnectToDockerOrDie(*dockerEndpoint)
|
||||||
|
standalone.SimpleRunKubelet(etcdClient, dockerClient, machineList[0], "/tmp/kubernetes", "", "127.0.0.1", 10250)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newApiClient(addr string, port int) *client.Client {
|
func newApiClient(addr string, port int) *client.Client {
|
||||||
|
187
pkg/kubelet/util.go
Normal file
187
pkg/kubelet/util.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 kubelet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/health"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
"github.com/coreos/go-etcd/etcd"
|
||||||
|
"github.com/fsouza/go-dockerclient"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
cadvisor "github.com/google/cadvisor/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: move this into a pkg/util
|
||||||
|
func GetHostname(hostnameOverride string) string {
|
||||||
|
hostname := []byte(hostnameOverride)
|
||||||
|
if string(hostname) == "" {
|
||||||
|
// Note: We use exec here instead of os.Hostname() because we
|
||||||
|
// want the FQDN, and this is the easiest way to get it.
|
||||||
|
fqdn, err := exec.Command("hostname", "-f").Output()
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Couldn't determine hostname: %v", err)
|
||||||
|
}
|
||||||
|
hostname = fqdn
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(hostname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into a pkg/util
|
||||||
|
func GetDockerEndpoint(dockerEndpoint string) string {
|
||||||
|
var endpoint string
|
||||||
|
if len(dockerEndpoint) > 0 {
|
||||||
|
endpoint = dockerEndpoint
|
||||||
|
} else if len(os.Getenv("DOCKER_HOST")) > 0 {
|
||||||
|
endpoint = os.Getenv("DOCKER_HOST")
|
||||||
|
} else {
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
}
|
||||||
|
glog.Infof("Connecting to docker on %s", endpoint)
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into pkg/util
|
||||||
|
func ConnectToDockerOrDie(dockerEndpoint string) *docker.Client {
|
||||||
|
client, err := docker.NewClient(GetDockerEndpoint(dockerEndpoint))
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatal("Couldn't connect to docker.")
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into the kubelet itself
|
||||||
|
func GarbageCollectLoop(k *Kubelet) {
|
||||||
|
func() {
|
||||||
|
util.Forever(func() {
|
||||||
|
err := k.GarbageCollectContainers()
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Garbage collect failed: %v", err)
|
||||||
|
}
|
||||||
|
}, time.Minute*1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into the kubelet itself
|
||||||
|
func MonitorCAdvisor(k *Kubelet) {
|
||||||
|
defer util.HandleCrash()
|
||||||
|
// TODO: Monitor this connection, reconnect if needed?
|
||||||
|
glog.V(1).Infof("Trying to create cadvisor client.")
|
||||||
|
cadvisorClient, err := cadvisor.NewClient("http://127.0.0.1:4194")
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error on creating cadvisor client: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(1).Infof("Successfully created cadvisor client.")
|
||||||
|
k.SetCadvisorClient(cadvisorClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into the kubelet itself
|
||||||
|
func InitHealthChecking(k *Kubelet) {
|
||||||
|
// TODO: These should probably become more plugin-ish: register a factory func
|
||||||
|
// in each checker's init(), iterate those here.
|
||||||
|
health.AddHealthChecker(health.NewExecHealthChecker(k))
|
||||||
|
health.AddHealthChecker(health.NewHTTPHealthChecker(&http.Client{}))
|
||||||
|
health.AddHealthChecker(&health.TCPHealthChecker{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into a pkg/tools/etcd_tools
|
||||||
|
func EtcdClientOrDie(etcdServerList util.StringList, etcdConfigFile string) *etcd.Client {
|
||||||
|
if len(etcdServerList) > 0 {
|
||||||
|
return etcd.NewClient(etcdServerList)
|
||||||
|
} else if etcdConfigFile != "" {
|
||||||
|
etcdClient, err := etcd.NewClientFromFile(etcdConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Error with etcd config file: %v", err)
|
||||||
|
}
|
||||||
|
return etcdClient
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into pkg/util
|
||||||
|
func SetupRootDirectoryOrDie(rootDirectory string) {
|
||||||
|
if rootDirectory == "" {
|
||||||
|
glog.Fatal("Invalid root directory path.")
|
||||||
|
}
|
||||||
|
rootDirectory = path.Clean(rootDirectory)
|
||||||
|
if err := os.MkdirAll(rootDirectory, 0750); err != nil {
|
||||||
|
glog.Fatalf("Error creating root directory: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into pkg/capabilities
|
||||||
|
func SetupCapabilities(allowPrivileged bool) {
|
||||||
|
capabilities.Initialize(capabilities.Capabilities{
|
||||||
|
AllowPrivileged: allowPrivileged,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Split this up?
|
||||||
|
func SetupLogging() {
|
||||||
|
etcd.SetLogger(util.NewLogger("etcd "))
|
||||||
|
// Log the events locally too.
|
||||||
|
record.StartLogging(glog.Infof)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this into pkg/client
|
||||||
|
func getApiserverClient(authPath string, apiServerList util.StringList) (*client.Client, error) {
|
||||||
|
authInfo, err := clientauth.LoadFromFile(authPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clientConfig, err := authInfo.MergeWithConfig(client.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// TODO: adapt Kube client to support LB over several servers
|
||||||
|
if len(apiServerList) > 1 {
|
||||||
|
glog.Infof("Mulitple api servers specified. Picking first one")
|
||||||
|
}
|
||||||
|
clientConfig.Host = apiServerList[0]
|
||||||
|
if c, err := client.New(&clientConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupEventSending(authPath string, apiServerList util.StringList) {
|
||||||
|
// Make an API client if possible.
|
||||||
|
if len(apiServerList) < 1 {
|
||||||
|
glog.Info("No api servers specified.")
|
||||||
|
} else {
|
||||||
|
if apiClient, err := getApiserverClient(authPath, apiServerList); err != nil {
|
||||||
|
glog.Errorf("Unable to make apiserver client: %v", err)
|
||||||
|
} else {
|
||||||
|
// Send events to APIserver if there is a client.
|
||||||
|
glog.Infof("Sending events to APIserver.")
|
||||||
|
record.StartRecording(apiClient.Events(""), "kubelet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/resources"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/resources"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
|
||||||
@ -39,12 +40,9 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
|
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
|
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
|
||||||
|
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testRootDir = "/tmp/kubelet"
|
|
||||||
|
|
||||||
type delegateHandler struct {
|
type delegateHandler struct {
|
||||||
delegate http.Handler
|
delegate http.Handler
|
||||||
}
|
}
|
||||||
@ -141,20 +139,131 @@ func RunControllerManager(machineList []string, cl *client.Client, nodeMilliCPU,
|
|||||||
controllerManager.Run(10 * time.Second)
|
controllerManager.Run(10 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunKubelet starts a Kubelet talking to dockerEndpoint
|
// SimpleRunKubelet is a simple way to start a Kubelet talking to dockerEndpoint, using an etcdClient.
|
||||||
func RunKubelet(etcdClient tools.EtcdClient, hostname, dockerEndpoint string) {
|
// Under the hood it calls RunKubelet (below)
|
||||||
dockerClient, err := docker.NewClient(GetDockerEndpoint(dockerEndpoint))
|
func SimpleRunKubelet(etcdClient tools.EtcdClient, dockerClient dockertools.DockerInterface, hostname, rootDir, manifestURL, address string, port uint) {
|
||||||
if err != nil {
|
kcfg := KubeletConfig{
|
||||||
glog.Fatal("Couldn't connect to docker.")
|
EtcdClient: etcdClient,
|
||||||
|
DockerClient: dockerClient,
|
||||||
|
HostnameOverride: hostname,
|
||||||
|
RootDirectory: rootDir,
|
||||||
|
ManifestURL: manifestURL,
|
||||||
|
NetworkContainerImage: kubelet.NetworkContainerImage,
|
||||||
|
Port: port,
|
||||||
|
Address: util.IP(net.ParseIP(address)),
|
||||||
|
EnableServer: true,
|
||||||
|
EnableDebuggingHandlers: true,
|
||||||
|
}
|
||||||
|
RunKubelet(&kcfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kubelet (localhost)
|
// RunKubelet is responsible for setting up and running a kubelet. It is used in three different applications:
|
||||||
os.MkdirAll(testRootDir, 0750)
|
// 1 Integration tests
|
||||||
cfg1 := config.NewPodConfig(config.PodConfigNotificationSnapshotAndUpdates)
|
// 2 Kubelet binary
|
||||||
config.NewSourceEtcd(config.EtcdKeyForHost(hostname), etcdClient, cfg1.Channel("etcd"))
|
// 3 Standalone 'kubernetes' binary
|
||||||
myKubelet := kubelet.NewIntegrationTestKubelet(hostname, testRootDir, dockerClient)
|
// Eventually, #2 will be replaced with instances of #3
|
||||||
go util.Forever(func() { myKubelet.Run(cfg1.Updates()) }, 0)
|
func RunKubelet(kcfg *KubeletConfig) {
|
||||||
|
kubelet.SetupEventSending(kcfg.AuthPath, kcfg.ApiServerList)
|
||||||
|
kubelet.SetupLogging()
|
||||||
|
kubelet.SetupCapabilities(kcfg.AllowPrivileged)
|
||||||
|
|
||||||
|
kcfg.Hostname = kubelet.GetHostname(kcfg.HostnameOverride)
|
||||||
|
if len(kcfg.RootDirectory) > 0 {
|
||||||
|
kubelet.SetupRootDirectoryOrDie(kcfg.RootDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := makePodSourceConfig(kcfg)
|
||||||
|
k := createAndInitKubelet(kcfg)
|
||||||
|
// process pods and exit.
|
||||||
|
if kcfg.Runonce {
|
||||||
|
if _, err := k.RunOnce(cfg.Updates()); err != nil {
|
||||||
|
glog.Errorf("--runonce failed: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startKubelet(k, cfg, kcfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startKubelet(k *kubelet.Kubelet, cfg *config.PodConfig, kc *KubeletConfig) {
|
||||||
|
// start the kubelet
|
||||||
|
go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
|
||||||
|
|
||||||
|
// start the kubelet server
|
||||||
|
if kc.EnableServer {
|
||||||
go util.Forever(func() {
|
go util.Forever(func() {
|
||||||
kubelet.ListenAndServeKubeletServer(myKubelet, cfg1.Channel("http"), net.ParseIP("127.0.0.1"), 10250, true)
|
kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), net.IP(kc.Address), kc.Port, kc.EnableDebuggingHandlers)
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePodSourceConfig(kc *KubeletConfig) *config.PodConfig {
|
||||||
|
// source of all configuration
|
||||||
|
cfg := config.NewPodConfig(config.PodConfigNotificationSnapshotAndUpdates)
|
||||||
|
|
||||||
|
// define file config source
|
||||||
|
if kc.ConfigFile != "" {
|
||||||
|
config.NewSourceFile(kc.ConfigFile, kc.FileCheckFrequency, cfg.Channel("file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// define url config source
|
||||||
|
if kc.ManifestURL != "" {
|
||||||
|
config.NewSourceURL(kc.ManifestURL, kc.HttpCheckFrequency, cfg.Channel("http"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if kc.EtcdClient != nil {
|
||||||
|
glog.Infof("Watching for etcd configs at %v", kc.EtcdClient.GetCluster())
|
||||||
|
config.NewSourceEtcd(config.EtcdKeyForHost(kc.Hostname), kc.EtcdClient, cfg.Channel("etcd"))
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubeletConfig struct {
|
||||||
|
EtcdClient tools.EtcdClient
|
||||||
|
DockerClient dockertools.DockerInterface
|
||||||
|
Address util.IP
|
||||||
|
AuthPath string
|
||||||
|
ApiServerList util.StringList
|
||||||
|
AllowPrivileged bool
|
||||||
|
HostnameOverride string
|
||||||
|
RootDirectory string
|
||||||
|
ConfigFile string
|
||||||
|
ManifestURL string
|
||||||
|
FileCheckFrequency time.Duration
|
||||||
|
HttpCheckFrequency time.Duration
|
||||||
|
Hostname string
|
||||||
|
NetworkContainerImage string
|
||||||
|
SyncFrequency time.Duration
|
||||||
|
RegistryPullQPS float64
|
||||||
|
RegistryBurst int
|
||||||
|
MinimumGCAge time.Duration
|
||||||
|
MaxContainerCount int
|
||||||
|
EnableServer bool
|
||||||
|
EnableDebuggingHandlers bool
|
||||||
|
Port uint
|
||||||
|
Runonce bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAndInitKubelet(kc *KubeletConfig) *kubelet.Kubelet {
|
||||||
|
// TODO: block until all sources have delivered at least one update to the channel, or break the sync loop
|
||||||
|
// up into "per source" synchronizations
|
||||||
|
|
||||||
|
k := kubelet.NewMainKubelet(
|
||||||
|
kc.Hostname,
|
||||||
|
kc.DockerClient,
|
||||||
|
kc.EtcdClient,
|
||||||
|
kc.RootDirectory,
|
||||||
|
kc.NetworkContainerImage,
|
||||||
|
kc.SyncFrequency,
|
||||||
|
float32(kc.RegistryPullQPS),
|
||||||
|
kc.RegistryBurst,
|
||||||
|
kc.MinimumGCAge,
|
||||||
|
kc.MaxContainerCount)
|
||||||
|
|
||||||
|
k.BirthCry()
|
||||||
|
|
||||||
|
go kubelet.GarbageCollectLoop(k)
|
||||||
|
go kubelet.MonitorCAdvisor(k)
|
||||||
|
kubelet.InitHealthChecking(k)
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user