From 8178240bbe9e719c6ff67c394346e422cd8be8c4 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Wed, 11 Jun 2014 16:02:08 -0700 Subject: [PATCH] Add a binary and scripts for running a local kubernetes cluster. --- cmd/apiserver/apiserver.go | 5 +- cmd/controller-manager/controller-manager.go | 1 + cmd/localkube/localkube.go | 138 +++++++++++++++++++ hack/build-go.sh | 2 +- hack/localcfg.sh | 27 ++++ hack/localkube.sh | 50 +++++++ pkg/util/util.go | 2 +- 7 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 cmd/localkube/localkube.go create mode 100755 hack/localcfg.sh create mode 100755 hack/localkube.sh diff --git a/cmd/apiserver/apiserver.go b/cmd/apiserver/apiserver.go index 40ae6cf2df..392bcbb663 100644 --- a/cmd/apiserver/apiserver.go +++ b/cmd/apiserver/apiserver.go @@ -13,6 +13,7 @@ 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. */ + // apiserver is the main api server and master for the cluster. // it is responsible for serving the cluster management API. package main @@ -53,7 +54,7 @@ func main() { } var ( - podRegistry registry.PodRegistry + podRegistry registry.PodRegistry controllerRegistry registry.ControllerRegistry serviceRegistry registry.ServiceRegistry ) @@ -77,7 +78,7 @@ func main() { random := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) storage := map[string]apiserver.RESTStorage{ - "pods": registry.MakePodRegistryStorage(podRegistry, containerInfo, registry.MakeFirstFitScheduler(machineList, podRegistry, random)), + "pods": registry.MakePodRegistryStorage(podRegistry, containerInfo, registry.MakeFirstFitScheduler(machineList, podRegistry, random)), "replicationControllers": registry.MakeControllerRegistryStorage(controllerRegistry), "services": registry.MakeServiceRegistryStorage(serviceRegistry), } diff --git a/cmd/controller-manager/controller-manager.go b/cmd/controller-manager/controller-manager.go index d832873edb..f09d7192ca 100644 --- a/cmd/controller-manager/controller-manager.go +++ b/cmd/controller-manager/controller-manager.go @@ -13,6 +13,7 @@ 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. */ + // The controller manager is responsible for monitoring replication controllers, and creating corresponding // pods to achieve the desired state. It listens for new controllers in etcd, and it sends requests to the // master to create/delete pods. diff --git a/cmd/localkube/localkube.go b/cmd/localkube/localkube.go new file mode 100644 index 0000000000..6eb3c96cb2 --- /dev/null +++ b/cmd/localkube/localkube.go @@ -0,0 +1,138 @@ +/* +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. +*/ + +// An all-in-one binary for standing up a fake Kubernetes cluster on your +// local machine. +// Assumes that there is a pre-existing etcd server running on localhost. +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "os" + "time" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/coreos/go-etcd/etcd" + "github.com/fsouza/go-dockerclient" +) + +// kubelet flags +var ( + file = flag.String("config", "", "Path to the config file") + syncFrequency = flag.Duration("sync_frequency", 10*time.Second, "Max period between synchronizing running containers and config") + fileCheckFrequency = flag.Duration("file_check_frequency", 20*time.Second, "Duration between checking file for new data") + httpCheckFrequency = flag.Duration("http_check_frequency", 20*time.Second, "Duration between checking http for new data") + manifest_url = flag.String("manifest_url", "", "URL for accessing the container manifest") + kubelet_address = flag.String("kubelet_address", "127.0.0.1", "The address for the kubelet info server to serve on") + kubelet_port = flag.Uint("kubelet_port", 10250, "The port for the kubelete info server to serve on") +) + +// master flags +var ( + master_port = flag.Uint("master_port", 8080, "The port to listen on. Default 8080.") + master_address = flag.String("master_address", "127.0.0.1", "The address on the local server to listen to. Default 127.0.0.1") + apiPrefix = flag.String("api_prefix", "/api/v1beta1", "The prefix for API requests on the server. Default '/api/v1beta1'") +) + +// flags that affect both +var ( + etcd_server = flag.String("etcd_servers", "http://localhost:4001", "Url of local etcd server") +) + +// Starts kubelet services. Never returns. +func fake_kubelet() { + endpoint := "unix:///var/run/docker.sock" + dockerClient, err := docker.NewClient(endpoint) + if err != nil { + log.Fatal("Couldn't connnect to docker.") + } + + my_kubelet := kubelet.Kubelet{ + DockerClient: dockerClient, + FileCheckFrequency: *fileCheckFrequency, + SyncFrequency: *syncFrequency, + HTTPCheckFrequency: *httpCheckFrequency, + } + my_kubelet.RunKubelet(*file, *manifest_url, *etcd_server, *kubelet_address, *kubelet_port) +} + +// Starts api services (the master). Never returns. +func api_server() { + //machineList := util.StringList{fmt.Sprintf("%s:%v", *kubelet_address, *kubelet_port)} + machineList := util.StringList{*kubelet_address} + + etcdClient := etcd.NewClient([]string{*etcd_server}) + podRegistry := registry.MakeEtcdRegistry(etcdClient, machineList) + controllerRegistry := registry.MakeEtcdRegistry(etcdClient, machineList) + serviceRegistry := registry.MakeEtcdRegistry(etcdClient, machineList) + + containerInfo := &client.HTTPContainerInfo{ + Client: http.DefaultClient, + Port: *kubelet_port, + } + storage := map[string]apiserver.RESTStorage{ + "pods": registry.MakePodRegistryStorage(podRegistry, containerInfo, registry.MakeFirstFitScheduler(machineList, podRegistry)), + "replicationControllers": registry.MakeControllerRegistryStorage(controllerRegistry), + "services": registry.MakeServiceRegistryStorage(serviceRegistry), + } + + endpoints := registry.MakeEndpointController(serviceRegistry, podRegistry) + go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10) + + s := &http.Server{ + Addr: fmt.Sprintf("%s:%d", *master_address, *master_port), + Handler: apiserver.New(storage, *apiPrefix), + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + log.Fatal(s.ListenAndServe()) +} + +// Starts up a controller manager. Never returns. +func controller_manager() { + controllerManager := registry.MakeReplicationManager(etcd.NewClient([]string{*etcd_server}), + client.Client{ + Host: fmt.Sprintf("http://%s:%d", *master_address, *master_port), + }) + + go util.Forever(func() { controllerManager.Synchronize() }, 20*time.Second) + go util.Forever(func() { controllerManager.WatchControllers() }, 20*time.Second) + select {} +} + +func main() { + flag.Parse() + + // Set up logger for etcd client + etcd.SetLogger(log.New(os.Stderr, "etcd ", log.LstdFlags)) + + go api_server() + go fake_kubelet() + go controller_manager() + + log.Printf("All components started.\nMaster running at: http://%s:%d\nKubelet running at: http://%s:%d\n", + *master_address, *master_port, + *kubelet_address, *kubelet_port) + select {} +} diff --git a/hack/build-go.sh b/hack/build-go.sh index 1493bc5a2b..7a63059757 100755 --- a/hack/build-go.sh +++ b/hack/build-go.sh @@ -22,7 +22,7 @@ source $(dirname $0)/config-go.sh cd "${KUBE_TARGET}" -BINARIES="proxy integration apiserver controller-manager kubelet cloudcfg" +BINARIES="proxy integration apiserver controller-manager kubelet cloudcfg localkube" for b in $BINARIES; do echo "+++ Building ${b}" diff --git a/hack/localcfg.sh b/hack/localcfg.sh new file mode 100755 index 0000000000..3ed16afbb3 --- /dev/null +++ b/hack/localcfg.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 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. + +# This file is exactly like cloudcfg.sh, but it talks to a local master +# (which you're assumed to be running with localkube.sh). + +CLOUDCFG=$(dirname $0)/../output/go/cloudcfg +if [ ! -x $CLOUDCFG ]; then + echo "Could not find cloudcfg binary. Run hack/build-go.sh to build it." + exit 1 +fi + +# 8080 is the default port for the master +$CLOUDCFG -h https://localhost:8080 $@ diff --git a/hack/localkube.sh b/hack/localkube.sh new file mode 100755 index 0000000000..a279b68c2d --- /dev/null +++ b/hack/localkube.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# 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. + +# This command builds and runs a local kubernetes cluster. + +if [ "$(which etcd)" == "" ]; then + echo "etcd must be in your PATH" + exit 1 +fi + +# Stop right away if the build fails +set -e + +# Only build what we need +( + source $(dirname $0)/config-go.sh + cd "${KUBE_TARGET}" + BINARIES="cloudcfg localkube" + for b in $BINARIES; do + echo "+++ Building ${b}" + go build "${KUBE_GO_PACKAGE}"/cmd/${b} + done +) + +echo "Starting etcd" + +ETCD_DIR=$(mktemp -d -t kube-integration.XXXXXX) +trap "rm -rf ${ETCD_DIR}" EXIT + +etcd -name test -data-dir ${ETCD_DIR} > /tmp/etcd.log & +ETCD_PID=$! + +sleep 5 + +$(dirname $0)/../output/go/localkube + +kill $ETCD_PID diff --git a/pkg/util/util.go b/pkg/util/util.go index 8d71eb91cc..4e981f4d37 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -26,7 +26,7 @@ import ( func HandleCrash() { r := recover() if r != nil { - log.Printf("Recovered from panic: %#v", r) + log.Printf("Recovered from panic: %v", r) } }