2014-06-06 23:40:48 +00:00
|
|
|
/*
|
|
|
|
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 (
|
2014-07-01 21:05:10 +00:00
|
|
|
"errors"
|
2014-06-06 23:40:48 +00:00
|
|
|
"fmt"
|
2014-09-24 21:27:10 +00:00
|
|
|
"io"
|
2014-06-06 23:40:48 +00:00
|
|
|
"net/http"
|
2014-07-29 17:20:50 +00:00
|
|
|
"path"
|
2014-06-06 23:40:48 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-09-01 05:10:49 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
2014-09-16 14:04:12 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
2014-07-15 18:39:19 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/health"
|
2014-09-09 04:33:17 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
2014-06-30 19:00:14 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
2014-06-06 23:40:48 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
2014-07-15 01:39:30 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
2014-06-06 23:40:48 +00:00
|
|
|
"github.com/fsouza/go-dockerclient"
|
2014-06-25 03:51:57 +00:00
|
|
|
"github.com/golang/glog"
|
2014-06-19 00:31:18 +00:00
|
|
|
"github.com/google/cadvisor/info"
|
2014-06-06 23:40:48 +00:00
|
|
|
)
|
|
|
|
|
2014-07-14 20:12:44 +00:00
|
|
|
const defaultChanSize = 1024
|
|
|
|
|
2014-07-15 23:49:34 +00:00
|
|
|
// taken from lmctfy https://github.com/google/lmctfy/blob/master/lmctfy/controllers/cpu_controller.cc
|
|
|
|
const minShares = 2
|
2014-07-16 09:46:22 +00:00
|
|
|
const sharesPerCPU = 1024
|
|
|
|
const milliCPUToCPU = 1000
|
2014-06-24 23:31:33 +00:00
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// CadvisorInterface is an abstract interface for testability. It abstracts the interface of "github.com/google/cadvisor/client".Client.
|
2014-06-19 00:31:18 +00:00
|
|
|
type CadvisorInterface interface {
|
2014-07-14 21:48:51 +00:00
|
|
|
ContainerInfo(name string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
|
2014-06-19 00:31:18 +00:00
|
|
|
MachineInfo() (*info.MachineInfo, error)
|
|
|
|
}
|
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
// SyncHandler is an interface implemented by Kubelet, for testability
|
|
|
|
type SyncHandler interface {
|
2014-10-08 19:56:02 +00:00
|
|
|
SyncPods([]api.BoundPod) error
|
2014-07-15 20:24:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type volumeMap map[string]volume.Interface
|
|
|
|
|
2014-07-22 21:40:59 +00:00
|
|
|
// New creates a new Kubelet for use in main
|
|
|
|
func NewMainKubelet(
|
|
|
|
hn string,
|
2014-09-09 04:33:17 +00:00
|
|
|
dc dockertools.DockerInterface,
|
2014-07-19 00:13:34 +00:00
|
|
|
ec tools.EtcdClient,
|
2014-08-06 20:12:19 +00:00
|
|
|
rd string,
|
2014-10-02 18:58:58 +00:00
|
|
|
ni string,
|
2014-09-26 04:24:44 +00:00
|
|
|
ri time.Duration,
|
|
|
|
pullQPS float32,
|
|
|
|
pullBurst int) *Kubelet {
|
2014-07-22 21:40:59 +00:00
|
|
|
return &Kubelet{
|
2014-10-02 18:58:58 +00:00
|
|
|
hostname: hn,
|
|
|
|
dockerClient: dc,
|
|
|
|
etcdClient: ec,
|
|
|
|
rootDirectory: rd,
|
|
|
|
resyncInterval: ri,
|
|
|
|
networkContainerImage: ni,
|
|
|
|
podWorkers: newPodWorkers(),
|
|
|
|
runner: dockertools.NewDockerContainerCommandRunner(),
|
|
|
|
httpClient: &http.Client{},
|
|
|
|
pullQPS: pullQPS,
|
|
|
|
pullBurst: pullBurst,
|
2014-07-22 21:40:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewIntegrationTestKubelet creates a new Kubelet for use in integration tests.
|
|
|
|
// TODO: add more integration tests, and expand parameter list as needed.
|
2014-10-03 23:50:53 +00:00
|
|
|
func NewIntegrationTestKubelet(hn string, rd string, dc dockertools.DockerInterface) *Kubelet {
|
2014-07-22 21:40:59 +00:00
|
|
|
return &Kubelet{
|
2014-10-02 18:58:58 +00:00
|
|
|
hostname: hn,
|
|
|
|
dockerClient: dc,
|
2014-10-03 23:50:53 +00:00
|
|
|
rootDirectory: rd,
|
2014-10-02 18:58:58 +00:00
|
|
|
dockerPuller: &dockertools.FakeDockerPuller{},
|
|
|
|
networkContainerImage: NetworkContainerImage,
|
|
|
|
resyncInterval: 3 * time.Second,
|
|
|
|
podWorkers: newPodWorkers(),
|
2014-07-22 21:40:59 +00:00
|
|
|
}
|
2014-07-01 16:15:49 +00:00
|
|
|
}
|
|
|
|
|
2014-09-03 20:39:56 +00:00
|
|
|
type httpGetInterface interface {
|
|
|
|
Get(url string) (*http.Response, error)
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// Kubelet is the main kubelet implementation.
|
2014-06-06 23:40:48 +00:00
|
|
|
type Kubelet struct {
|
2014-10-02 18:58:58 +00:00
|
|
|
hostname string
|
|
|
|
dockerClient dockertools.DockerInterface
|
|
|
|
rootDirectory string
|
|
|
|
networkContainerImage string
|
2014-10-10 00:16:21 +00:00
|
|
|
podWorkers *podWorkers
|
2014-10-02 18:58:58 +00:00
|
|
|
resyncInterval time.Duration
|
2014-10-08 19:56:02 +00:00
|
|
|
pods []api.BoundPod
|
2014-07-15 20:24:41 +00:00
|
|
|
|
|
|
|
// Optional, no events will be sent without it
|
2014-07-22 21:40:59 +00:00
|
|
|
etcdClient tools.EtcdClient
|
2014-07-15 20:24:41 +00:00
|
|
|
// Optional, defaults to simple implementaiton
|
2014-07-22 21:40:59 +00:00
|
|
|
healthChecker health.HealthChecker
|
2014-07-15 20:24:41 +00:00
|
|
|
// Optional, defaults to simple Docker implementation
|
2014-09-09 04:33:17 +00:00
|
|
|
dockerPuller dockertools.DockerPuller
|
2014-07-15 20:24:41 +00:00
|
|
|
// Optional, defaults to /logs/ from /var/log
|
2014-07-22 21:40:59 +00:00
|
|
|
logServer http.Handler
|
2014-08-07 18:15:11 +00:00
|
|
|
// Optional, defaults to simple Docker implementation
|
2014-09-09 04:33:17 +00:00
|
|
|
runner dockertools.ContainerCommandRunner
|
2014-09-03 20:39:56 +00:00
|
|
|
// Optional, client for http requests, defaults to empty client
|
|
|
|
httpClient httpGetInterface
|
2014-09-26 04:24:44 +00:00
|
|
|
// Optional, maximum pull QPS from the docker registry, 0.0 means unlimited.
|
|
|
|
pullQPS float32
|
|
|
|
// Optional, maximum burst QPS from the docker registry, must be positive if QPS is > 0.0
|
|
|
|
pullBurst int
|
2014-10-09 00:05:04 +00:00
|
|
|
|
|
|
|
// Optional, no statistics will be available if omitted
|
|
|
|
cadvisorClient CadvisorInterface
|
|
|
|
cadvisorLock sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetCadvisorClient sets the cadvisor client in a thread-safe way.
|
|
|
|
func (kl *Kubelet) SetCadvisorClient(c CadvisorInterface) {
|
|
|
|
kl.cadvisorLock.Lock()
|
|
|
|
defer kl.cadvisorLock.Unlock()
|
|
|
|
kl.cadvisorClient = c
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCadvisorClient gets the cadvisor client.
|
|
|
|
func (kl *Kubelet) GetCadvisorClient() CadvisorInterface {
|
|
|
|
kl.cadvisorLock.RLock()
|
|
|
|
defer kl.cadvisorLock.RUnlock()
|
|
|
|
return kl.cadvisorClient
|
2014-07-15 20:24:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run starts the kubelet reacting to config updates
|
|
|
|
func (kl *Kubelet) Run(updates <-chan PodUpdate) {
|
2014-07-22 21:40:59 +00:00
|
|
|
if kl.logServer == nil {
|
|
|
|
kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))
|
2014-07-15 07:04:30 +00:00
|
|
|
}
|
2014-07-22 21:40:59 +00:00
|
|
|
if kl.dockerPuller == nil {
|
2014-09-26 04:24:44 +00:00
|
|
|
kl.dockerPuller = dockertools.NewDockerPuller(kl.dockerClient, kl.pullQPS, kl.pullBurst)
|
2014-06-24 23:31:33 +00:00
|
|
|
}
|
2014-07-22 21:40:59 +00:00
|
|
|
if kl.healthChecker == nil {
|
|
|
|
kl.healthChecker = health.NewHealthChecker()
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
kl.syncLoop(updates, kl)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 18:42:47 +00:00
|
|
|
// Per-pod workers.
|
|
|
|
type podWorkers struct {
|
|
|
|
lock sync.Mutex
|
|
|
|
|
|
|
|
// Set of pods with existing workers.
|
|
|
|
workers util.StringSet
|
|
|
|
}
|
|
|
|
|
2014-10-10 00:16:21 +00:00
|
|
|
func newPodWorkers() *podWorkers {
|
|
|
|
return &podWorkers{
|
2014-07-18 18:42:47 +00:00
|
|
|
workers: util.NewStringSet(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs a worker for "podFullName" asynchronously with the specified "action".
|
|
|
|
// If the worker for the "podFullName" is already running, functions as a no-op.
|
|
|
|
func (self *podWorkers) Run(podFullName string, action func()) {
|
|
|
|
self.lock.Lock()
|
|
|
|
defer self.lock.Unlock()
|
|
|
|
|
|
|
|
// This worker is already running, let it finish.
|
|
|
|
if self.workers.Has(podFullName) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
self.workers.Insert(podFullName)
|
|
|
|
|
|
|
|
// Run worker async.
|
|
|
|
go func() {
|
|
|
|
defer util.HandleCrash()
|
|
|
|
action()
|
|
|
|
|
|
|
|
self.lock.Lock()
|
|
|
|
defer self.lock.Unlock()
|
|
|
|
self.workers.Delete(podFullName)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2014-09-24 21:35:34 +00:00
|
|
|
// LogEvent reports an event.
|
2014-06-09 03:35:07 +00:00
|
|
|
func (kl *Kubelet) LogEvent(event *api.Event) error {
|
2014-09-24 21:35:34 +00:00
|
|
|
return nil
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-06-09 20:47:25 +00:00
|
|
|
func makeEnvironmentVariables(container *api.Container) []string {
|
|
|
|
var result []string
|
2014-06-06 23:40:48 +00:00
|
|
|
for _, value := range container.Env {
|
2014-06-09 20:47:25 +00:00
|
|
|
result = append(result, fmt.Sprintf("%s=%s", value.Name, value.Value))
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-06-09 20:47:25 +00:00
|
|
|
return result
|
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
func makeBinds(pod *api.BoundPod, container *api.Container, podVolumes volumeMap) []string {
|
2014-06-06 23:40:48 +00:00
|
|
|
binds := []string{}
|
2014-08-27 05:08:06 +00:00
|
|
|
for _, mount := range container.VolumeMounts {
|
|
|
|
vol, ok := podVolumes[mount.Name]
|
|
|
|
if !ok {
|
|
|
|
continue
|
2014-06-19 23:59:48 +00:00
|
|
|
}
|
2014-08-27 05:08:06 +00:00
|
|
|
b := fmt.Sprintf("%s:%s", vol.GetPath(), mount.MountPath)
|
|
|
|
if mount.ReadOnly {
|
|
|
|
b += ":ro"
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-08-27 05:08:06 +00:00
|
|
|
binds = append(binds, b)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-08-27 05:08:06 +00:00
|
|
|
return binds
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
|
2014-06-09 20:47:25 +00:00
|
|
|
func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, map[docker.Port][]docker.PortBinding) {
|
2014-06-06 23:40:48 +00:00
|
|
|
exposedPorts := map[docker.Port]struct{}{}
|
|
|
|
portBindings := map[docker.Port][]docker.PortBinding{}
|
|
|
|
for _, port := range container.Ports {
|
|
|
|
exteriorPort := port.HostPort
|
2014-08-19 22:18:49 +00:00
|
|
|
if exteriorPort == 0 {
|
|
|
|
// No need to do port binding when HostPort is not specified
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
interiorPort := port.ContainerPort
|
2014-06-06 23:40:48 +00:00
|
|
|
// Some of this port stuff is under-documented voodoo.
|
|
|
|
// See http://stackoverflow.com/questions/20428302/binding-a-port-to-a-host-interface-using-the-rest-api
|
2014-06-16 04:19:35 +00:00
|
|
|
var protocol string
|
2014-09-28 03:31:37 +00:00
|
|
|
switch strings.ToUpper(string(port.Protocol)) {
|
2014-07-08 04:32:56 +00:00
|
|
|
case "UDP":
|
2014-06-16 04:19:35 +00:00
|
|
|
protocol = "/udp"
|
2014-07-08 04:32:56 +00:00
|
|
|
case "TCP":
|
2014-06-16 04:19:35 +00:00
|
|
|
protocol = "/tcp"
|
|
|
|
default:
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.Warningf("Unknown protocol '%s': defaulting to TCP", port.Protocol)
|
2014-06-16 04:19:35 +00:00
|
|
|
protocol = "/tcp"
|
|
|
|
}
|
|
|
|
dockerPort := docker.Port(strconv.Itoa(interiorPort) + protocol)
|
2014-06-06 23:40:48 +00:00
|
|
|
exposedPorts[dockerPort] = struct{}{}
|
|
|
|
portBindings[dockerPort] = []docker.PortBinding{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-06 23:40:48 +00:00
|
|
|
HostPort: strconv.Itoa(exteriorPort),
|
2014-07-09 05:44:15 +00:00
|
|
|
HostIp: port.HostIP,
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2014-06-09 20:47:25 +00:00
|
|
|
return exposedPorts, portBindings
|
|
|
|
}
|
|
|
|
|
2014-07-16 09:46:22 +00:00
|
|
|
func milliCPUToShares(milliCPU int) int {
|
2014-07-29 18:34:16 +00:00
|
|
|
if milliCPU == 0 {
|
|
|
|
// zero milliCPU means unset. Use kernel default.
|
|
|
|
return 0
|
|
|
|
}
|
2014-07-16 09:46:22 +00:00
|
|
|
// Conceptually (milliCPU / milliCPUToCPU) * sharesPerCPU, but factored to improve rounding.
|
|
|
|
shares := (milliCPU * sharesPerCPU) / milliCPUToCPU
|
2014-07-15 23:49:34 +00:00
|
|
|
if shares < minShares {
|
|
|
|
return minShares
|
2014-06-19 12:29:42 +00:00
|
|
|
}
|
2014-07-15 23:49:34 +00:00
|
|
|
return shares
|
2014-06-19 12:29:42 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) mountExternalVolumes(pod *api.BoundPod) (volumeMap, error) {
|
2014-07-15 01:39:30 +00:00
|
|
|
podVolumes := make(volumeMap)
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, vol := range pod.Spec.Volumes {
|
|
|
|
extVolume, err := volume.CreateVolumeBuilder(&vol, pod.ID, kl.rootDirectory)
|
2014-07-15 01:39:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-07-16 19:32:59 +00:00
|
|
|
// TODO(jonesdl) When the default volume behavior is no longer supported, this case
|
|
|
|
// should never occur and an error should be thrown instead.
|
|
|
|
if extVolume == nil {
|
|
|
|
continue
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
2014-07-16 19:32:59 +00:00
|
|
|
podVolumes[vol.Name] = extVolume
|
2014-07-24 20:45:55 +00:00
|
|
|
err = extVolume.SetUp()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
|
|
|
return podVolumes, nil
|
|
|
|
}
|
|
|
|
|
2014-09-03 20:39:56 +00:00
|
|
|
// A basic interface that knows how to execute handlers
|
|
|
|
type actionHandler interface {
|
2014-09-05 09:49:11 +00:00
|
|
|
Run(podFullName, uuid string, container *api.Container, handler *api.Handler) error
|
2014-09-03 20:39:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (kl *Kubelet) newActionHandler(handler *api.Handler) actionHandler {
|
|
|
|
switch {
|
|
|
|
case handler.Exec != nil:
|
|
|
|
return &execActionHandler{kubelet: kl}
|
|
|
|
case handler.HTTPGet != nil:
|
|
|
|
return &httpActionHandler{client: kl.httpClient, kubelet: kl}
|
|
|
|
default:
|
2014-10-07 20:53:25 +00:00
|
|
|
glog.Errorf("Invalid handler: %v", handler)
|
2014-09-03 20:39:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-05 09:49:11 +00:00
|
|
|
func (kl *Kubelet) runHandler(podFullName, uuid string, container *api.Container, handler *api.Handler) error {
|
2014-09-03 20:39:56 +00:00
|
|
|
actionHandler := kl.newActionHandler(handler)
|
|
|
|
if actionHandler == nil {
|
|
|
|
return fmt.Errorf("invalid handler")
|
|
|
|
}
|
2014-09-05 09:49:11 +00:00
|
|
|
return actionHandler.Run(podFullName, uuid, container, handler)
|
2014-09-03 20:39:56 +00:00
|
|
|
}
|
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
// Run a single container from a pod. Returns the docker container ID
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) runContainer(pod *api.BoundPod, container *api.Container, podVolumes volumeMap, netMode string) (id dockertools.DockerID, err error) {
|
2014-06-09 20:47:25 +00:00
|
|
|
envVariables := makeEnvironmentVariables(container)
|
2014-08-27 05:08:06 +00:00
|
|
|
binds := makeBinds(pod, container, podVolumes)
|
2014-06-09 20:47:25 +00:00
|
|
|
exposedPorts, portBindings := makePortsAndBindings(container)
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
opts := docker.CreateContainerOptions{
|
2014-10-08 19:56:02 +00:00
|
|
|
Name: dockertools.BuildDockerName(pod.UID, GetPodFullName(pod), container),
|
2014-06-06 23:40:48 +00:00
|
|
|
Config: &docker.Config{
|
2014-07-15 03:56:18 +00:00
|
|
|
Cmd: container.Command,
|
|
|
|
Env: envVariables,
|
|
|
|
ExposedPorts: exposedPorts,
|
2014-10-08 19:56:02 +00:00
|
|
|
Hostname: pod.ID,
|
2014-06-06 23:40:48 +00:00
|
|
|
Image: container.Image,
|
2014-07-30 23:56:50 +00:00
|
|
|
Memory: int64(container.Memory),
|
2014-07-16 09:46:22 +00:00
|
|
|
CpuShares: int64(milliCPUToShares(container.CPU)),
|
2014-06-06 23:40:48 +00:00
|
|
|
WorkingDir: container.WorkingDir,
|
|
|
|
},
|
|
|
|
}
|
2014-07-22 21:40:59 +00:00
|
|
|
dockerContainer, err := kl.dockerClient.CreateContainer(opts)
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2014-09-11 23:34:24 +00:00
|
|
|
privileged := false
|
2014-09-16 22:18:33 +00:00
|
|
|
if capabilities.Get().AllowPrivileged {
|
2014-09-11 23:34:24 +00:00
|
|
|
privileged = container.Privileged
|
|
|
|
} else if container.Privileged {
|
|
|
|
return "", fmt.Errorf("Container requested privileged mode, but it is disallowed globally.")
|
|
|
|
}
|
2014-07-22 21:40:59 +00:00
|
|
|
err = kl.dockerClient.StartContainer(dockerContainer.ID, &docker.HostConfig{
|
2014-06-06 23:40:48 +00:00
|
|
|
PortBindings: portBindings,
|
|
|
|
Binds: binds,
|
2014-06-20 03:30:42 +00:00
|
|
|
NetworkMode: netMode,
|
2014-09-11 23:34:24 +00:00
|
|
|
Privileged: privileged,
|
2014-06-06 23:40:48 +00:00
|
|
|
})
|
2014-09-03 20:39:56 +00:00
|
|
|
if err == nil && container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
|
2014-10-08 19:56:02 +00:00
|
|
|
handlerErr := kl.runHandler(GetPodFullName(pod), pod.UID, container, container.Lifecycle.PostStart)
|
2014-09-03 20:39:56 +00:00
|
|
|
if handlerErr != nil {
|
|
|
|
kl.killContainerByID(dockerContainer.ID, "")
|
2014-09-09 04:33:17 +00:00
|
|
|
return dockertools.DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr)
|
2014-09-03 20:39:56 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-09 04:33:17 +00:00
|
|
|
return dockertools.DockerID(dockerContainer.ID), err
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 23:24:20 +00:00
|
|
|
// Kill a docker container
|
2014-08-08 04:49:17 +00:00
|
|
|
func (kl *Kubelet) killContainer(dockerContainer *docker.APIContainers) error {
|
2014-09-03 20:39:56 +00:00
|
|
|
return kl.killContainerByID(dockerContainer.ID, dockerContainer.Names[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kl *Kubelet) killContainerByID(ID, name string) error {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(2).Infof("Killing: %s", ID)
|
2014-09-03 20:39:56 +00:00
|
|
|
err := kl.dockerClient.StopContainer(ID, 10)
|
|
|
|
if len(name) == 0 {
|
|
|
|
return err
|
|
|
|
}
|
2014-09-24 21:35:34 +00:00
|
|
|
|
|
|
|
// TODO(lavalamp): restore event logging:
|
|
|
|
// podFullName, uuid, containerName, _ := dockertools.ParseDockerName(name)
|
|
|
|
// kl.LogEvent(&api.Event{})
|
2014-06-06 23:40:48 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-07-19 07:09:43 +00:00
|
|
|
const (
|
|
|
|
networkContainerName = "net"
|
2014-10-02 18:58:58 +00:00
|
|
|
NetworkContainerImage = "kubernetes/pause:latest"
|
2014-07-19 07:09:43 +00:00
|
|
|
)
|
2014-06-20 03:30:42 +00:00
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
// createNetworkContainer starts the network container for a pod. Returns the docker container ID of the newly created container.
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) createNetworkContainer(pod *api.BoundPod) (dockertools.DockerID, error) {
|
2014-06-20 03:30:42 +00:00
|
|
|
var ports []api.Port
|
|
|
|
// Docker only exports ports from the network container. Let's
|
2014-06-20 15:55:02 +00:00
|
|
|
// collect all of the relevant ports and export them.
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, container := range pod.Spec.Containers {
|
2014-06-20 03:30:42 +00:00
|
|
|
ports = append(ports, container.Ports...)
|
|
|
|
}
|
|
|
|
container := &api.Container{
|
2014-07-19 07:09:43 +00:00
|
|
|
Name: networkContainerName,
|
2014-10-02 18:58:58 +00:00
|
|
|
Image: kl.networkContainerImage,
|
2014-07-19 07:09:43 +00:00
|
|
|
Ports: ports,
|
2014-06-20 03:30:42 +00:00
|
|
|
}
|
2014-10-02 18:58:58 +00:00
|
|
|
// TODO: make this a TTL based pull (if image older than X policy, pull)
|
|
|
|
ok, err := kl.dockerPuller.IsImagePresent(container.Image)
|
|
|
|
if err != nil {
|
2014-09-26 04:24:44 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2014-10-02 18:58:58 +00:00
|
|
|
if !ok {
|
|
|
|
if err := kl.dockerPuller.Pull(container.Image); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
return kl.runContainer(pod, container, nil, "")
|
2014-06-20 03:30:42 +00:00
|
|
|
}
|
|
|
|
|
2014-08-08 04:49:17 +00:00
|
|
|
// Delete all containers in a pod (except the network container) returns the number of containers deleted
|
|
|
|
// and an error if one occurs.
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) deleteAllContainers(pod *api.BoundPod, podFullName string, dockerContainers dockertools.DockerContainers) (int, error) {
|
2014-08-08 04:49:17 +00:00
|
|
|
count := 0
|
2014-10-08 19:56:02 +00:00
|
|
|
errs := make(chan error, len(pod.Spec.Containers))
|
2014-08-08 04:49:17 +00:00
|
|
|
wg := sync.WaitGroup{}
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, container := range pod.Spec.Containers {
|
|
|
|
if dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, pod.UID, container.Name); found {
|
2014-08-08 04:49:17 +00:00
|
|
|
count++
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
err := kl.killContainer(dockerContainer)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Failed to delete container. (%v) Skipping pod %s", err, podFullName)
|
|
|
|
errs <- err
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
close(errs)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
errList := []error{}
|
|
|
|
for err := range errs {
|
|
|
|
errList = append(errList, err)
|
|
|
|
}
|
|
|
|
return -1, fmt.Errorf("failed to delete containers (%v)", errList)
|
|
|
|
}
|
|
|
|
return count, nil
|
|
|
|
}
|
|
|
|
|
2014-07-18 18:42:47 +00:00
|
|
|
type empty struct{}
|
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) syncPod(pod *api.BoundPod, dockerContainers dockertools.DockerContainers) error {
|
2014-07-15 20:24:41 +00:00
|
|
|
podFullName := GetPodFullName(pod)
|
2014-10-08 19:56:02 +00:00
|
|
|
uuid := pod.UID
|
2014-09-09 04:33:17 +00:00
|
|
|
containersToKeep := make(map[dockertools.DockerID]empty)
|
|
|
|
killedContainers := make(map[dockertools.DockerID]empty)
|
2014-07-15 20:24:41 +00:00
|
|
|
|
2014-07-18 18:42:47 +00:00
|
|
|
// Make sure we have a network container
|
2014-09-09 04:33:17 +00:00
|
|
|
var netID dockertools.DockerID
|
2014-09-05 09:49:11 +00:00
|
|
|
if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, networkContainerName); found {
|
2014-09-09 04:33:17 +00:00
|
|
|
netID = dockertools.DockerID(networkDockerContainer.ID)
|
2014-07-15 17:26:56 +00:00
|
|
|
} else {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("Network container doesn't exist, creating")
|
2014-08-08 04:49:17 +00:00
|
|
|
count, err := kl.deleteAllContainers(pod, podFullName, dockerContainers)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
dockerNetworkID, err := kl.createNetworkContainer(pod)
|
2014-06-20 03:30:42 +00:00
|
|
|
if err != nil {
|
2014-07-15 20:24:41 +00:00
|
|
|
glog.Errorf("Failed to introspect network container. (%v) Skipping pod %s", err, podFullName)
|
2014-07-01 05:27:56 +00:00
|
|
|
return err
|
|
|
|
}
|
2014-07-15 17:26:56 +00:00
|
|
|
netID = dockerNetworkID
|
2014-08-08 04:49:17 +00:00
|
|
|
if count > 0 {
|
|
|
|
// relist everything, otherwise we'll think we're ok
|
2014-09-29 21:38:31 +00:00
|
|
|
dockerContainers, err = dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
|
2014-08-08 04:49:17 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Error listing containers %#v", dockerContainers)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2014-07-01 05:27:56 +00:00
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
containersToKeep[netID] = empty{}
|
2014-07-15 20:24:41 +00:00
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
podVolumes, err := kl.mountExternalVolumes(pod)
|
2014-07-16 19:32:59 +00:00
|
|
|
if err != nil {
|
2014-07-24 20:45:55 +00:00
|
|
|
glog.Errorf("Unable to mount volumes for pod %s: (%v) Skipping pod.", podFullName, err)
|
|
|
|
return err
|
2014-07-16 19:32:59 +00:00
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
|
2014-10-08 15:19:46 +00:00
|
|
|
podState := api.PodState{}
|
2014-09-05 09:49:11 +00:00
|
|
|
info, err := kl.GetPodInfo(podFullName, uuid)
|
2014-08-01 00:35:54 +00:00
|
|
|
if err != nil {
|
2014-09-05 09:49:11 +00:00
|
|
|
glog.Errorf("Unable to get pod with name %s and uuid %s info, health checks may be invalid.",
|
|
|
|
podFullName, uuid)
|
2014-08-01 00:35:54 +00:00
|
|
|
}
|
|
|
|
netInfo, found := info[networkContainerName]
|
2014-10-03 06:39:02 +00:00
|
|
|
if found {
|
|
|
|
podState.PodIP = netInfo.PodIP
|
2014-08-01 00:35:54 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, container := range pod.Spec.Containers {
|
2014-09-09 04:33:17 +00:00
|
|
|
expectedHash := dockertools.HashContainer(&container)
|
2014-09-05 09:49:11 +00:00
|
|
|
if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found {
|
2014-09-09 04:33:17 +00:00
|
|
|
containerID := dockertools.DockerID(dockerContainer.ID)
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID)
|
2014-07-15 17:26:56 +00:00
|
|
|
|
2014-08-07 23:59:18 +00:00
|
|
|
// look for changes in the container.
|
|
|
|
if hash == 0 || hash == expectedHash {
|
|
|
|
// TODO: This should probably be separated out into a separate goroutine.
|
2014-10-08 15:19:46 +00:00
|
|
|
healthy, err := kl.healthy(podFullName, uuid, podState, container, dockerContainer)
|
2014-08-07 23:59:18 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.V(1).Infof("health check errored: %v", err)
|
2014-08-13 04:33:56 +00:00
|
|
|
containersToKeep[containerID] = empty{}
|
2014-08-07 23:59:18 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if healthy == health.Healthy {
|
|
|
|
containersToKeep[containerID] = empty{}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
glog.V(1).Infof("pod %s container %s is unhealthy.", podFullName, container.Name, healthy)
|
|
|
|
} else {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("container hash changed %d vs %d.", hash, expectedHash)
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
2014-08-08 04:49:17 +00:00
|
|
|
if err := kl.killContainer(dockerContainer); err != nil {
|
2014-07-15 20:24:41 +00:00
|
|
|
glog.V(1).Infof("Failed to kill container %s: %v", dockerContainer.ID, err)
|
2014-07-15 17:26:56 +00:00
|
|
|
continue
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
killedContainers[containerID] = empty{}
|
2014-07-01 05:27:56 +00:00
|
|
|
}
|
2014-07-15 17:26:56 +00:00
|
|
|
|
2014-08-26 18:25:17 +00:00
|
|
|
// Check RestartPolicy for container
|
2014-09-09 04:33:17 +00:00
|
|
|
recentContainers, err := dockertools.GetRecentDockerContainersWithNameAndUUID(kl.dockerClient, podFullName, uuid, container.Name)
|
2014-08-26 18:25:17 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Error listing recent containers with name and uuid:%s--%s--%s", podFullName, uuid, container.Name)
|
|
|
|
// TODO(dawnchen): error handling here?
|
|
|
|
}
|
|
|
|
|
2014-10-08 19:56:02 +00:00
|
|
|
if len(recentContainers) > 0 && pod.Spec.RestartPolicy.Always == nil {
|
|
|
|
if pod.Spec.RestartPolicy.Never != nil {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("Already ran container with name %s--%s--%s, do nothing",
|
2014-08-26 18:25:17 +00:00
|
|
|
podFullName, uuid, container.Name)
|
|
|
|
continue
|
|
|
|
}
|
2014-10-08 19:56:02 +00:00
|
|
|
if pod.Spec.RestartPolicy.OnFailure != nil {
|
2014-08-26 18:25:17 +00:00
|
|
|
// Check the exit code of last run
|
|
|
|
if recentContainers[0].State.ExitCode == 0 {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("Already successfully ran container with name %s--%s--%s, do nothing",
|
2014-08-26 18:25:17 +00:00
|
|
|
podFullName, uuid, container.Name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("Container with name %s--%s--%s doesn't exist, creating %#v", podFullName, uuid, container.Name, container)
|
2014-09-26 04:53:17 +00:00
|
|
|
if !api.IsPullNever(container.ImagePullPolicy) {
|
|
|
|
present, err := kl.dockerPuller.IsImagePresent(container.Image)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Failed to inspect image: %s: %#v skipping pod %s container %s", container.Image, err, podFullName, container.Name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if api.IsPullAlways(container.ImagePullPolicy) || !present {
|
|
|
|
if err := kl.dockerPuller.Pull(container.Image); err != nil {
|
|
|
|
glog.Errorf("Failed to pull image %s: %v skipping pod %s container %s.", container.Image, err, podFullName, container.Name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
2014-08-26 18:25:17 +00:00
|
|
|
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
|
2014-07-15 20:24:41 +00:00
|
|
|
containerID, err := kl.runContainer(pod, &container, podVolumes, "container:"+string(netID))
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
// TODO(bburns) : Perhaps blacklist a container after N failures?
|
2014-07-15 20:24:41 +00:00
|
|
|
glog.Errorf("Error running pod %s container %s: %v", podFullName, container.Name, err)
|
2014-07-15 17:26:56 +00:00
|
|
|
continue
|
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
containersToKeep[containerID] = empty{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Kill any containers in this pod which were not identified above (guards against duplicates).
|
|
|
|
for id, container := range dockerContainers {
|
2014-09-09 04:33:17 +00:00
|
|
|
curPodFullName, curUUID, _, _ := dockertools.ParseDockerName(container.Names[0])
|
2014-09-05 09:49:11 +00:00
|
|
|
if curPodFullName == podFullName && curUUID == uuid {
|
2014-07-18 18:42:47 +00:00
|
|
|
// Don't kill containers we want to keep or those we already killed.
|
|
|
|
_, keep := containersToKeep[id]
|
|
|
|
_, killed := killedContainers[id]
|
|
|
|
if !keep && !killed {
|
2014-08-08 04:49:17 +00:00
|
|
|
err = kl.killContainer(container)
|
2014-07-18 18:42:47 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Error killing container: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
|
2014-07-01 05:27:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-18 18:42:47 +00:00
|
|
|
type podContainer struct {
|
|
|
|
podFullName string
|
2014-09-05 09:49:11 +00:00
|
|
|
uuid string
|
2014-07-18 18:42:47 +00:00
|
|
|
containerName string
|
|
|
|
}
|
2014-07-03 01:06:54 +00:00
|
|
|
|
2014-07-30 21:04:19 +00:00
|
|
|
// Stores all volumes defined by the set of pods into a map.
|
|
|
|
// Keys for each entry are in the format (POD_ID)/(VOLUME_NAME)
|
2014-10-08 19:56:02 +00:00
|
|
|
func getDesiredVolumes(pods []api.BoundPod) map[string]api.Volume {
|
2014-07-30 21:04:19 +00:00
|
|
|
desiredVolumes := make(map[string]api.Volume)
|
2014-07-25 20:16:59 +00:00
|
|
|
for _, pod := range pods {
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, volume := range pod.Spec.Volumes {
|
|
|
|
identifier := path.Join(pod.ID, volume.Name)
|
2014-07-30 21:04:19 +00:00
|
|
|
desiredVolumes[identifier] = volume
|
2014-07-25 20:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-30 21:04:19 +00:00
|
|
|
return desiredVolumes
|
2014-07-25 20:16:59 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 21:04:19 +00:00
|
|
|
// Compares the map of current volumes to the map of desired volumes.
|
|
|
|
// If an active volume does not have a respective desired volume, clean it up.
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) reconcileVolumes(pods []api.BoundPod) error {
|
2014-07-30 21:04:19 +00:00
|
|
|
desiredVolumes := getDesiredVolumes(pods)
|
|
|
|
currentVolumes := volume.GetCurrentVolumes(kl.rootDirectory)
|
|
|
|
for name, vol := range currentVolumes {
|
|
|
|
if _, ok := desiredVolumes[name]; !ok {
|
|
|
|
//TODO (jonesdl) We should somehow differentiate between volumes that are supposed
|
|
|
|
//to be deleted and volumes that are leftover after a crash.
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.Warningf("Orphaned volume %s found, tearing down volume", name)
|
2014-07-30 21:04:19 +00:00
|
|
|
//TODO (jonesdl) This should not block other kubelet synchronization procedures
|
|
|
|
err := vol.TearDown()
|
2014-07-29 17:20:50 +00:00
|
|
|
if err != nil {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.Errorf("Could not tear down volume %s (%s)", name, err)
|
2014-07-29 17:20:50 +00:00
|
|
|
}
|
2014-07-25 20:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
// SyncPods synchronizes the configured list of pods (desired state) with the host current state.
|
2014-10-08 19:56:02 +00:00
|
|
|
func (kl *Kubelet) SyncPods(pods []api.BoundPod) error {
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(4).Infof("Desired [%s]: %+v", kl.hostname, pods)
|
2014-07-15 20:24:41 +00:00
|
|
|
var err error
|
2014-07-18 18:42:47 +00:00
|
|
|
desiredContainers := make(map[podContainer]empty)
|
2014-07-01 05:27:56 +00:00
|
|
|
|
2014-09-29 21:38:31 +00:00
|
|
|
dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Error listing containers %#v", dockerContainers)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-07-01 05:27:56 +00:00
|
|
|
// Check for any containers that need starting
|
2014-10-07 04:20:00 +00:00
|
|
|
for ix := range pods {
|
|
|
|
pod := &pods[ix]
|
|
|
|
podFullName := GetPodFullName(pod)
|
2014-10-08 19:56:02 +00:00
|
|
|
uuid := pod.UID
|
2014-07-18 18:42:47 +00:00
|
|
|
|
|
|
|
// Add all containers (including net) to the map.
|
2014-09-05 09:49:11 +00:00
|
|
|
desiredContainers[podContainer{podFullName, uuid, networkContainerName}] = empty{}
|
2014-10-08 19:56:02 +00:00
|
|
|
for _, cont := range pod.Spec.Containers {
|
2014-09-05 09:49:11 +00:00
|
|
|
desiredContainers[podContainer{podFullName, uuid, cont.Name}] = empty{}
|
2014-07-18 18:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the sync in an async manifest worker.
|
|
|
|
kl.podWorkers.Run(podFullName, func() {
|
2014-10-07 04:20:00 +00:00
|
|
|
err := kl.syncPod(pod, dockerContainers)
|
2014-07-01 05:27:56 +00:00
|
|
|
if err != nil {
|
2014-07-15 20:24:41 +00:00
|
|
|
glog.Errorf("Error syncing pod: %v skipping.", err)
|
2014-07-01 05:27:56 +00:00
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
})
|
2014-07-01 16:37:45 +00:00
|
|
|
}
|
2014-07-25 21:17:02 +00:00
|
|
|
|
2014-06-25 23:24:20 +00:00
|
|
|
// Kill any containers we don't need
|
2014-09-29 21:38:31 +00:00
|
|
|
existingContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
|
2014-06-25 23:24:20 +00:00
|
|
|
if err != nil {
|
2014-06-29 05:16:26 +00:00
|
|
|
glog.Errorf("Error listing containers: %v", err)
|
2014-06-25 23:24:20 +00:00
|
|
|
return err
|
|
|
|
}
|
2014-07-18 18:42:47 +00:00
|
|
|
for _, container := range existingContainers {
|
|
|
|
// Don't kill containers that are in the desired pods.
|
2014-09-09 04:33:17 +00:00
|
|
|
podFullName, uuid, containerName, _ := dockertools.ParseDockerName(container.Names[0])
|
2014-09-05 09:49:11 +00:00
|
|
|
if _, ok := desiredContainers[podContainer{podFullName, uuid, containerName}]; !ok {
|
2014-08-08 04:49:17 +00:00
|
|
|
err = kl.killContainer(container)
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
2014-06-29 05:16:26 +00:00
|
|
|
glog.Errorf("Error killing container: %v", err)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-30 21:04:19 +00:00
|
|
|
|
|
|
|
// Remove any orphaned volumes.
|
|
|
|
kl.reconcileVolumes(pods)
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
// filterHostPortConflicts removes pods that conflict on Port.HostPort values
|
2014-10-08 19:56:02 +00:00
|
|
|
func filterHostPortConflicts(pods []api.BoundPod) []api.BoundPod {
|
|
|
|
filtered := []api.BoundPod{}
|
2014-07-15 20:24:41 +00:00
|
|
|
ports := map[int]bool{}
|
2014-07-08 04:48:47 +00:00
|
|
|
extract := func(p *api.Port) int { return p.HostPort }
|
2014-07-15 20:24:41 +00:00
|
|
|
for i := range pods {
|
|
|
|
pod := &pods[i]
|
2014-10-08 19:56:02 +00:00
|
|
|
if errs := validation.AccumulateUniquePorts(pod.Spec.Containers, ports, extract); len(errs) != 0 {
|
2014-07-15 20:24:41 +00:00
|
|
|
glog.Warningf("Pod %s has conflicting ports, ignoring: %v", GetPodFullName(pod), errs)
|
|
|
|
continue
|
2014-07-08 04:48:47 +00:00
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
filtered = append(filtered, *pod)
|
2014-07-08 04:48:47 +00:00
|
|
|
}
|
2014-07-15 20:24:41 +00:00
|
|
|
|
|
|
|
return filtered
|
2014-07-08 04:48:47 +00:00
|
|
|
}
|
|
|
|
|
2014-07-01 20:01:39 +00:00
|
|
|
// syncLoop is the main loop for processing changes. It watches for changes from
|
2014-06-20 16:31:18 +00:00
|
|
|
// four channels (file, etcd, server, and http) and creates a union of them. For
|
2014-06-06 23:40:48 +00:00
|
|
|
// any new change seen, will run a sync against desired state and running state. If
|
|
|
|
// no changes are seen to the configuration, will synchronize the last known desired
|
2014-07-15 20:24:41 +00:00
|
|
|
// state every sync_frequency seconds. Never returns.
|
|
|
|
func (kl *Kubelet) syncLoop(updates <-chan PodUpdate, handler SyncHandler) {
|
2014-06-06 23:40:48 +00:00
|
|
|
for {
|
|
|
|
select {
|
2014-07-15 20:24:41 +00:00
|
|
|
case u := <-updates:
|
|
|
|
switch u.Op {
|
|
|
|
case SET:
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.V(3).Infof("Containers changed [%s]", kl.hostname)
|
2014-10-03 06:39:02 +00:00
|
|
|
kl.pods = u.Pods
|
|
|
|
kl.pods = filterHostPortConflicts(kl.pods)
|
2014-07-15 20:24:41 +00:00
|
|
|
|
|
|
|
case UPDATE:
|
|
|
|
//TODO: implement updates of containers
|
2014-09-18 10:46:14 +00:00
|
|
|
glog.Warningf("Containers updated, not implemented [%s]", kl.hostname)
|
2014-07-15 20:24:41 +00:00
|
|
|
continue
|
2014-06-06 23:40:48 +00:00
|
|
|
|
2014-07-15 20:24:41 +00:00
|
|
|
default:
|
|
|
|
panic("syncLoop does not support incremental changes")
|
2014-07-01 20:01:39 +00:00
|
|
|
}
|
2014-08-06 20:12:19 +00:00
|
|
|
case <-time.After(kl.resyncInterval):
|
2014-10-03 06:39:02 +00:00
|
|
|
if kl.pods == nil {
|
2014-08-06 20:12:19 +00:00
|
|
|
continue
|
|
|
|
}
|
2014-06-21 21:20:35 +00:00
|
|
|
}
|
2014-06-20 16:31:18 +00:00
|
|
|
|
2014-10-03 06:39:02 +00:00
|
|
|
err := handler.SyncPods(kl.pods)
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
2014-06-29 05:16:26 +00:00
|
|
|
glog.Errorf("Couldn't sync containers : %v", err)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-14 21:48:51 +00:00
|
|
|
func getCadvisorContainerInfoRequest(req *info.ContainerInfoRequest) *info.ContainerInfoRequest {
|
|
|
|
ret := &info.ContainerInfoRequest{
|
2014-09-22 18:22:40 +00:00
|
|
|
NumStats: req.NumStats,
|
2014-07-14 21:48:51 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2014-07-01 21:05:10 +00:00
|
|
|
// This method takes a container's absolute path and returns the stats for the
|
|
|
|
// container. The container's absolute path refers to its hierarchy in the
|
|
|
|
// cgroup file system. e.g. The root container, which represents the whole
|
|
|
|
// machine, has path "/"; all docker containers have path "/docker/<docker id>"
|
2014-10-09 00:05:04 +00:00
|
|
|
func (kl *Kubelet) statsFromContainerPath(cc CadvisorInterface, containerPath string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
|
|
|
cinfo, err := cc.ContainerInfo(containerPath, getCadvisorContainerInfoRequest(req))
|
2014-06-19 01:26:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-07-14 21:48:51 +00:00
|
|
|
return cinfo, nil
|
2014-06-19 01:26:23 +00:00
|
|
|
}
|
2014-07-01 21:05:10 +00:00
|
|
|
|
2014-08-27 19:41:32 +00:00
|
|
|
// GetKubeletContainerLogs returns logs from the container
|
2014-09-24 23:51:54 +00:00
|
|
|
// The second parameter of GetPodInfo and FindPodContainer methods represents pod UUID, which is allowed to be blank
|
2014-09-22 20:14:23 +00:00
|
|
|
func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error {
|
2014-09-24 23:51:54 +00:00
|
|
|
_, err := kl.GetPodInfo(podFullName, "")
|
|
|
|
if err == dockertools.ErrNoContainersInPod {
|
|
|
|
return fmt.Errorf("Pod not found (%s)\n", podFullName)
|
|
|
|
}
|
2014-09-29 21:38:31 +00:00
|
|
|
dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, true)
|
2014-09-17 19:00:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-09-24 23:51:54 +00:00
|
|
|
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, "", containerName)
|
2014-09-17 19:00:09 +00:00
|
|
|
if !found {
|
2014-09-24 23:51:54 +00:00
|
|
|
return fmt.Errorf("Container not found (%s)\n", containerName)
|
2014-09-17 19:00:09 +00:00
|
|
|
}
|
2014-09-24 21:27:10 +00:00
|
|
|
return dockertools.GetKubeletDockerContainerLogs(kl.dockerClient, dockerContainer.ID, tail, follow, stdout, stderr)
|
2014-08-27 19:41:32 +00:00
|
|
|
}
|
|
|
|
|
2014-07-15 17:26:56 +00:00
|
|
|
// GetPodInfo returns information from Docker about the containers in a pod
|
2014-09-05 09:49:11 +00:00
|
|
|
func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
|
2014-10-08 19:56:02 +00:00
|
|
|
var manifest api.PodSpec
|
2014-10-03 06:39:02 +00:00
|
|
|
for _, pod := range kl.pods {
|
2014-10-06 18:54:51 +00:00
|
|
|
if GetPodFullName(&pod) == podFullName {
|
2014-10-08 19:56:02 +00:00
|
|
|
manifest = pod.Spec
|
2014-10-03 06:39:02 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dockertools.GetDockerPodInfo(kl.dockerClient, manifest, podFullName, uuid)
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
|
|
|
|
2014-07-16 09:46:22 +00:00
|
|
|
// GetContainerInfo returns stats (from Cadvisor) for a container.
|
2014-09-05 09:49:11 +00:00
|
|
|
func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
2014-10-09 00:05:04 +00:00
|
|
|
cc := kl.GetCadvisorClient()
|
|
|
|
if cc == nil {
|
2014-07-01 21:05:10 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
2014-09-29 21:38:31 +00:00
|
|
|
dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
2014-07-01 21:05:10 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-05 09:49:11 +00:00
|
|
|
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, containerName)
|
2014-07-15 17:26:56 +00:00
|
|
|
if !found {
|
|
|
|
return nil, errors.New("couldn't find container")
|
|
|
|
}
|
2014-10-09 00:05:04 +00:00
|
|
|
return kl.statsFromContainerPath(cc, fmt.Sprintf("/docker/%s", dockerContainer.ID), req)
|
2014-07-01 21:05:10 +00:00
|
|
|
}
|
|
|
|
|
2014-07-15 22:40:02 +00:00
|
|
|
// GetRootInfo returns stats (from Cadvisor) of current machine (root container).
|
|
|
|
func (kl *Kubelet) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
2014-10-09 00:05:04 +00:00
|
|
|
cc := kl.GetCadvisorClient()
|
|
|
|
if cc == nil {
|
|
|
|
return nil, fmt.Errorf("no cadvisor connection")
|
|
|
|
}
|
|
|
|
return kl.statsFromContainerPath(cc, "/", req)
|
2014-07-01 21:05:10 +00:00
|
|
|
}
|
2014-07-03 05:35:50 +00:00
|
|
|
|
2014-07-15 22:40:02 +00:00
|
|
|
func (kl *Kubelet) GetMachineInfo() (*info.MachineInfo, error) {
|
2014-10-09 00:05:04 +00:00
|
|
|
cc := kl.GetCadvisorClient()
|
|
|
|
if cc == nil {
|
|
|
|
return nil, fmt.Errorf("no cadvisor connection")
|
|
|
|
}
|
|
|
|
return cc.MachineInfo()
|
2014-07-15 22:40:02 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 15:19:46 +00:00
|
|
|
func (kl *Kubelet) healthy(podFullName, podUUID string, currentState api.PodState, container api.Container, dockerContainer *docker.APIContainers) (health.Status, error) {
|
2014-07-03 05:35:50 +00:00
|
|
|
// Give the container 60 seconds to start up.
|
2014-07-09 23:53:31 +00:00
|
|
|
if container.LivenessProbe == nil {
|
2014-07-15 18:39:19 +00:00
|
|
|
return health.Healthy, nil
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
|
|
|
if time.Now().Unix()-dockerContainer.Created < container.LivenessProbe.InitialDelaySeconds {
|
2014-07-15 18:39:19 +00:00
|
|
|
return health.Healthy, nil
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2014-07-22 21:40:59 +00:00
|
|
|
if kl.healthChecker == nil {
|
2014-07-15 18:39:19 +00:00
|
|
|
return health.Healthy, nil
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2014-10-08 15:19:46 +00:00
|
|
|
return kl.healthChecker.HealthCheck(podFullName, podUUID, currentState, container)
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2014-07-15 07:04:30 +00:00
|
|
|
|
|
|
|
// Returns logs of current machine.
|
|
|
|
func (kl *Kubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
|
|
|
|
// TODO: whitelist logs we are willing to serve
|
2014-07-22 21:40:59 +00:00
|
|
|
kl.logServer.ServeHTTP(w, req)
|
2014-07-15 07:04:30 +00:00
|
|
|
}
|
2014-08-07 18:15:11 +00:00
|
|
|
|
|
|
|
// Run a command in a container, returns the combined stdout, stderr as an array of bytes
|
2014-09-05 09:49:11 +00:00
|
|
|
func (kl *Kubelet) RunInContainer(podFullName, uuid, container string, cmd []string) ([]byte, error) {
|
2014-08-07 18:15:11 +00:00
|
|
|
if kl.runner == nil {
|
|
|
|
return nil, fmt.Errorf("no runner specified.")
|
|
|
|
}
|
2014-09-29 21:38:31 +00:00
|
|
|
dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
|
2014-08-07 18:15:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-05 09:49:11 +00:00
|
|
|
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, container)
|
2014-08-07 18:15:11 +00:00
|
|
|
if !found {
|
|
|
|
return nil, fmt.Errorf("container not found (%s)", container)
|
|
|
|
}
|
|
|
|
return kl.runner.RunInContainer(dockerContainer.ID, cmd)
|
|
|
|
}
|