272 lines
8.3 KiB
Go
272 lines
8.3 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors 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 minion
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
exservice "k8s.io/kubernetes/contrib/mesos/pkg/executor/service"
|
|
"k8s.io/kubernetes/contrib/mesos/pkg/hyperkube"
|
|
"k8s.io/kubernetes/contrib/mesos/pkg/minion/config"
|
|
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
|
"k8s.io/kubernetes/pkg/api/resource"
|
|
"k8s.io/kubernetes/pkg/client"
|
|
|
|
log "github.com/golang/glog"
|
|
"github.com/kardianos/osext"
|
|
"github.com/spf13/pflag"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
type MinionServer struct {
|
|
// embed the executor server to be able to use its flags
|
|
// TODO(sttts): get rid of this mixing of the minion and the executor server with a multiflags implementation for km
|
|
KubeletExecutorServer *exservice.KubeletExecutorServer
|
|
|
|
privateMountNS bool
|
|
hks hyperkube.Interface
|
|
clientConfig *client.Config
|
|
kmBinary string
|
|
done chan struct{} // closed when shutting down
|
|
exit chan error // to signal fatal errors
|
|
|
|
logMaxSize resource.Quantity
|
|
logMaxBackups int
|
|
logMaxAgeInDays int
|
|
|
|
runProxy bool
|
|
proxyLogV int
|
|
proxyBindall bool
|
|
}
|
|
|
|
// NewMinionServer creates the MinionServer struct with default values to be used by hyperkube
|
|
func NewMinionServer() *MinionServer {
|
|
s := &MinionServer{
|
|
KubeletExecutorServer: exservice.NewKubeletExecutorServer(),
|
|
privateMountNS: false, // disabled until Docker supports customization of the parent mount namespace
|
|
done: make(chan struct{}),
|
|
exit: make(chan error),
|
|
|
|
logMaxSize: config.DefaultLogMaxSize(),
|
|
logMaxBackups: config.DefaultLogMaxBackups,
|
|
logMaxAgeInDays: config.DefaultLogMaxAgeInDays,
|
|
|
|
runProxy: true,
|
|
}
|
|
|
|
// cache this for later use
|
|
binary, err := osext.Executable()
|
|
if err != nil {
|
|
log.Fatalf("failed to determine currently running executable: %v", err)
|
|
}
|
|
s.kmBinary = binary
|
|
|
|
return s
|
|
}
|
|
|
|
// filterArgsByFlagSet returns a list of args which are parsed by the given flag set
|
|
// and another list with those which do not match
|
|
func filterArgsByFlagSet(args []string, flags *pflag.FlagSet) ([]string, []string) {
|
|
matched := []string{}
|
|
notMatched := []string{}
|
|
for _, arg := range args {
|
|
err := flags.Parse([]string{arg})
|
|
if err != nil {
|
|
notMatched = append(notMatched, arg)
|
|
} else {
|
|
matched = append(matched, arg)
|
|
}
|
|
}
|
|
return matched, notMatched
|
|
}
|
|
|
|
func (ms *MinionServer) launchProxyServer() {
|
|
bindAddress := "0.0.0.0"
|
|
if !ms.proxyBindall {
|
|
bindAddress = ms.KubeletExecutorServer.Address.String()
|
|
}
|
|
args := []string{
|
|
fmt.Sprintf("--bind-address=%s", bindAddress),
|
|
fmt.Sprintf("--v=%d", ms.proxyLogV),
|
|
"--logtostderr=true",
|
|
}
|
|
|
|
if ms.clientConfig.Host != "" {
|
|
args = append(args, fmt.Sprintf("--master=%s", ms.clientConfig.Host))
|
|
}
|
|
|
|
ms.launchHyperkubeServer(hyperkube.CommandProxy, &args, "proxy.log")
|
|
}
|
|
|
|
func (ms *MinionServer) launchExecutorServer() {
|
|
allArgs := os.Args[1:]
|
|
|
|
// filter out minion flags, leaving those for the executor
|
|
executorFlags := pflag.NewFlagSet("executor", pflag.ContinueOnError)
|
|
executorFlags.SetOutput(ioutil.Discard)
|
|
ms.AddExecutorFlags(executorFlags)
|
|
executorArgs, _ := filterArgsByFlagSet(allArgs, executorFlags)
|
|
|
|
// run executor and quit minion server when this exits cleanly
|
|
err := ms.launchHyperkubeServer(hyperkube.CommandExecutor, &executorArgs, "executor.log")
|
|
if err != nil {
|
|
// just return, executor will be restarted on error
|
|
log.Error(err)
|
|
return
|
|
}
|
|
|
|
log.Info("Executor exited cleanly, stopping the minion")
|
|
ms.exit <- nil
|
|
}
|
|
|
|
func (ms *MinionServer) launchHyperkubeServer(server string, args *[]string, logFileName string) error {
|
|
log.V(2).Infof("Spawning hyperkube %v with args '%+v'", server, args)
|
|
|
|
// prepare parameters
|
|
kmArgs := []string{server}
|
|
for _, arg := range *args {
|
|
kmArgs = append(kmArgs, arg)
|
|
}
|
|
|
|
// create command
|
|
cmd := exec.Command(ms.kmBinary, kmArgs...)
|
|
if _, err := cmd.StdoutPipe(); err != nil {
|
|
// fatal error => terminate minion
|
|
err = fmt.Errorf("error getting stdout of %v: %v", server, err)
|
|
ms.exit <- err
|
|
return err
|
|
}
|
|
stderrLogs, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
// fatal error => terminate minion
|
|
err = fmt.Errorf("error getting stderr of %v: %v", server, err)
|
|
ms.exit <- err
|
|
return err
|
|
}
|
|
|
|
ch := make(chan struct{})
|
|
go func() {
|
|
defer func() {
|
|
select {
|
|
case <-ch:
|
|
log.Infof("killing %v process...", server)
|
|
if err = cmd.Process.Kill(); err != nil {
|
|
log.Errorf("failed to kill %v process: %v", server, err)
|
|
}
|
|
default:
|
|
}
|
|
}()
|
|
|
|
maxSize := ms.logMaxSize.Value()
|
|
if maxSize > 0 {
|
|
// convert to MB
|
|
maxSize = maxSize / 1024 / 1024
|
|
if maxSize == 0 {
|
|
log.Warning("maximal log file size is rounded to 1 MB")
|
|
maxSize = 1
|
|
}
|
|
}
|
|
writer := &lumberjack.Logger{
|
|
Filename: logFileName,
|
|
MaxSize: int(maxSize),
|
|
MaxBackups: ms.logMaxBackups,
|
|
MaxAge: ms.logMaxAgeInDays,
|
|
}
|
|
defer writer.Close()
|
|
|
|
log.V(2).Infof("Starting logging for %v: max log file size %d MB, keeping %d backups, for %d days", server, maxSize, ms.logMaxBackups, ms.logMaxAgeInDays)
|
|
|
|
<-ch
|
|
written, err := io.Copy(writer, stderrLogs)
|
|
if err != nil {
|
|
log.Errorf("error writing data to %v: %v", logFileName, err)
|
|
}
|
|
|
|
log.Infof("wrote %d bytes to %v", written, logFileName)
|
|
}()
|
|
|
|
// if the server fails to start then we exit the executor, otherwise
|
|
// wait for the proxy process to end (and release resources after).
|
|
if err := cmd.Start(); err != nil {
|
|
// fatal error => terminate minion
|
|
err = fmt.Errorf("error starting %v: %v", server, err)
|
|
ms.exit <- err
|
|
return err
|
|
}
|
|
close(ch)
|
|
if err := cmd.Wait(); err != nil {
|
|
log.Error("%v exited with error: %v", server, err)
|
|
err = fmt.Errorf("%v exited with error: %v", server, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// runs the main kubelet loop, closing the kubeletFinished chan when the loop exits.
|
|
// never returns.
|
|
func (ms *MinionServer) Run(hks hyperkube.Interface, _ []string) error {
|
|
if ms.privateMountNS {
|
|
// only the Linux version will do anything
|
|
enterPrivateMountNamespace()
|
|
}
|
|
|
|
// create apiserver client
|
|
clientConfig, err := ms.KubeletExecutorServer.CreateAPIServerClientConfig()
|
|
if err != nil {
|
|
// required for k8sm since we need to send api.Binding information
|
|
// back to the apiserver
|
|
log.Fatalf("No API client: %v", err)
|
|
}
|
|
ms.clientConfig = clientConfig
|
|
|
|
// run subprocesses until ms.done is closed on return of this function
|
|
defer close(ms.done)
|
|
if ms.runProxy {
|
|
go runtime.Until(ms.launchProxyServer, 5*time.Second, ms.done)
|
|
}
|
|
go runtime.Until(ms.launchExecutorServer, 5*time.Second, ms.done)
|
|
|
|
// wait until minion exit is requested
|
|
// don't close ms.exit here to avoid panics of go routines writing an error to it
|
|
return <-ms.exit
|
|
}
|
|
|
|
func (ms *MinionServer) AddExecutorFlags(fs *pflag.FlagSet) {
|
|
ms.KubeletExecutorServer.AddFlags(fs)
|
|
}
|
|
|
|
func (ms *MinionServer) AddMinionFlags(fs *pflag.FlagSet) {
|
|
// general minion flags
|
|
fs.BoolVar(&ms.privateMountNS, "private-mountns", ms.privateMountNS, "Enter a private mount NS before spawning procs (linux only). Experimental, not yet compatible with k8s volumes.")
|
|
|
|
// log file flags
|
|
fs.Var(resource.NewQuantityFlagValue(&ms.logMaxSize), "max-log-size", "Maximum log file size for the executor and proxy before rotation")
|
|
fs.IntVar(&ms.logMaxAgeInDays, "max-log-age", ms.logMaxAgeInDays, "Maximum log file age of the executor and proxy in days")
|
|
fs.IntVar(&ms.logMaxBackups, "max-log-backups", ms.logMaxBackups, "Maximum log file backups of the executor and proxy to keep after rotation")
|
|
|
|
// proxy flags
|
|
fs.BoolVar(&ms.runProxy, "run-proxy", ms.runProxy, "Maintain a running kube-proxy instance as a child proc of this kubelet-executor.")
|
|
fs.IntVar(&ms.proxyLogV, "proxy-logv", ms.proxyLogV, "Log verbosity of the child kube-proxy.")
|
|
fs.BoolVar(&ms.proxyBindall, "proxy-bindall", ms.proxyBindall, "When true will cause kube-proxy to bind to 0.0.0.0.")
|
|
}
|