2016-07-22 21:24:05 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package dockershim
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
dockertypes "github.com/docker/engine-api/types"
|
|
|
|
dockercontainer "github.com/docker/engine-api/types/container"
|
|
|
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
2016-08-27 02:04:09 +00:00
|
|
|
"github.com/golang/glog"
|
2016-07-25 23:25:36 +00:00
|
|
|
|
2016-07-22 21:24:05 +00:00
|
|
|
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
2016-09-25 02:07:43 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubelet/qos"
|
2016-07-22 21:24:05 +00:00
|
|
|
)
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
const (
|
|
|
|
defaultSandboxImage = "gcr.io/google_containers/pause-amd64:3.0"
|
|
|
|
|
|
|
|
// Various default sandbox resources requests/limits.
|
|
|
|
defaultSandboxCPUshares int64 = 2
|
|
|
|
|
|
|
|
// Termination grace period
|
|
|
|
defaultSandboxGracePeriod int = 10
|
|
|
|
)
|
|
|
|
|
2016-09-07 13:38:56 +00:00
|
|
|
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
|
|
|
|
// the sandbox is in ready state.
|
2016-07-25 23:25:36 +00:00
|
|
|
// For docker, PodSandbox is implemented by a container holding the network
|
|
|
|
// namespace for the pod.
|
|
|
|
// Note: docker doesn't use LogDirectory (yet).
|
2016-09-07 13:38:56 +00:00
|
|
|
func (ds *dockerService) RunPodSandbox(config *runtimeApi.PodSandboxConfig) (string, error) {
|
2016-07-25 23:25:36 +00:00
|
|
|
// Step 1: Pull the image for the sandbox.
|
|
|
|
// TODO: How should we handle pulling custom pod infra container image
|
|
|
|
// (with credentials)?
|
|
|
|
image := defaultSandboxImage
|
|
|
|
if err := ds.client.PullImage(image, dockertypes.AuthConfig{}, dockertypes.ImagePullOptions{}); err != nil {
|
|
|
|
return "", fmt.Errorf("unable to pull image for the sandbox container: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 2: Create the sandbox container.
|
|
|
|
createConfig := makeSandboxDockerConfig(config, image)
|
|
|
|
createResp, err := ds.client.CreateContainer(*createConfig)
|
|
|
|
if err != nil || createResp == nil {
|
2016-08-17 08:04:25 +00:00
|
|
|
return "", fmt.Errorf("failed to create a sandbox for pod %q: %v", config.Metadata.GetName(), err)
|
2016-07-25 23:25:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3: Start the sandbox container.
|
|
|
|
// Assume kubelet's garbage collector would remove the sandbox later, if
|
|
|
|
// startContainer failed.
|
|
|
|
err = ds.StartContainer(createResp.ID)
|
|
|
|
return createResp.ID, err
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
|
|
|
// sandbox, they should be force terminated.
|
|
|
|
func (ds *dockerService) StopPodSandbox(podSandboxID string) error {
|
2016-07-25 23:25:36 +00:00
|
|
|
return ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod)
|
|
|
|
// TODO: Stop all running containers in the sandbox.
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-03 22:08:10 +00:00
|
|
|
// RemovePodSandbox removes the sandbox. If there are running containers in the
|
|
|
|
// sandbox, they should be forcibly removed.
|
|
|
|
func (ds *dockerService) RemovePodSandbox(podSandboxID string) error {
|
2016-07-25 23:25:36 +00:00
|
|
|
return ds.client.RemoveContainer(podSandboxID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
|
|
|
// TODO: remove all containers in the sandbox.
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
// PodSandboxStatus returns the status of the PodSandbox.
|
2016-07-22 21:24:05 +00:00
|
|
|
func (ds *dockerService) PodSandboxStatus(podSandboxID string) (*runtimeApi.PodSandboxStatus, error) {
|
2016-07-25 23:25:36 +00:00
|
|
|
// Inspect the container.
|
|
|
|
r, err := ds.client.InspectContainer(podSandboxID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the timstamps.
|
|
|
|
createdAt, _, _, err := getContainerTimestamps(r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parse timestamp for container %q: %v", podSandboxID, err)
|
|
|
|
}
|
|
|
|
ct := createdAt.Unix()
|
|
|
|
|
|
|
|
// Translate container to sandbox state.
|
|
|
|
state := runtimeApi.PodSandBoxState_NOTREADY
|
|
|
|
if r.State.Running {
|
|
|
|
state = runtimeApi.PodSandBoxState_READY
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: We can't really get the IP address from the network plugin, which
|
|
|
|
// is handled by kubelet as of now. Should we amend the interface? How is
|
|
|
|
// this handled in the new remote runtime integration?
|
|
|
|
// See DockerManager.determineContainerIP() for more details.
|
|
|
|
// For now, just assume that there is no network plugin.
|
|
|
|
// Related issue: https://github.com/kubernetes/kubernetes/issues/28667
|
|
|
|
var IP string
|
|
|
|
if r.NetworkSettings != nil {
|
|
|
|
IP = r.NetworkSettings.IPAddress
|
|
|
|
// Fall back to IPv6 address if no IPv4 address is present
|
|
|
|
if IP == "" {
|
|
|
|
IP = r.NetworkSettings.GlobalIPv6Address
|
|
|
|
}
|
|
|
|
}
|
|
|
|
network := &runtimeApi.PodSandboxNetworkStatus{Ip: &IP}
|
|
|
|
netNS := getNetworkNamespace(r)
|
|
|
|
|
2016-08-27 02:04:09 +00:00
|
|
|
metadata, err := parseSandboxName(r.Name)
|
2016-08-17 08:04:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-09-15 20:08:51 +00:00
|
|
|
labels, annotations := extractLabels(r.Config.Labels)
|
2016-07-25 23:25:36 +00:00
|
|
|
return &runtimeApi.PodSandboxStatus{
|
2016-09-15 20:08:51 +00:00
|
|
|
Id: &r.ID,
|
|
|
|
State: &state,
|
|
|
|
CreatedAt: &ct,
|
|
|
|
Metadata: metadata,
|
|
|
|
Labels: labels,
|
|
|
|
Annotations: annotations,
|
|
|
|
Network: network,
|
|
|
|
Linux: &runtimeApi.LinuxPodSandboxStatus{Namespaces: &runtimeApi.Namespace{Network: &netNS}},
|
2016-07-25 23:25:36 +00:00
|
|
|
}, nil
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListPodSandbox returns a list of Sandbox.
|
|
|
|
func (ds *dockerService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]*runtimeApi.PodSandbox, error) {
|
2016-07-25 23:25:36 +00:00
|
|
|
// By default, list all containers whether they are running or not.
|
|
|
|
opts := dockertypes.ContainerListOptions{All: true}
|
|
|
|
filterOutReadySandboxes := false
|
|
|
|
|
|
|
|
opts.Filter = dockerfilters.NewArgs()
|
|
|
|
f := newDockerFilter(&opts.Filter)
|
2016-09-15 19:05:54 +00:00
|
|
|
// Add filter to select only sandbox containers.
|
|
|
|
f.AddLabel(containerTypeLabelKey, containerTypeLabelSandbox)
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
if filter != nil {
|
|
|
|
if filter.Id != nil {
|
|
|
|
f.Add("id", filter.GetId())
|
|
|
|
}
|
|
|
|
if filter.State != nil {
|
|
|
|
if filter.GetState() == runtimeApi.PodSandBoxState_READY {
|
|
|
|
// Only list running containers.
|
|
|
|
opts.All = false
|
|
|
|
} else {
|
|
|
|
// runtimeApi.PodSandBoxState_NOTREADY can mean the
|
|
|
|
// container is in any of the non-running state (e.g., created,
|
|
|
|
// exited). We can't tell docker to filter out running
|
|
|
|
// containers directly, so we'll need to filter them out
|
|
|
|
// ourselves after getting the results.
|
|
|
|
filterOutReadySandboxes = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.LabelSelector != nil {
|
|
|
|
for k, v := range filter.LabelSelector {
|
|
|
|
f.AddLabel(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
containers, err := ds.client.ListContainers(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert docker containers to runtime api sandboxes.
|
|
|
|
result := []*runtimeApi.PodSandbox{}
|
2016-08-26 23:15:06 +00:00
|
|
|
for i := range containers {
|
|
|
|
c := containers[i]
|
2016-08-27 02:04:09 +00:00
|
|
|
converted, err := toRuntimeAPISandbox(&c)
|
|
|
|
if err != nil {
|
|
|
|
glog.V(5).Infof("Unable to convert docker to runtime API sandbox: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if filterOutReadySandboxes && converted.GetState() == runtimeApi.PodSandBoxState_READY {
|
2016-07-25 23:25:36 +00:00
|
|
|
continue
|
|
|
|
}
|
2016-08-27 02:04:09 +00:00
|
|
|
|
|
|
|
result = append(result, converted)
|
2016-07-25 23:25:36 +00:00
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig, image string) *dockertypes.ContainerCreateConfig {
|
|
|
|
// Merge annotations and labels because docker supports only labels.
|
|
|
|
labels := makeLabels(c.GetLabels(), c.GetAnnotations())
|
|
|
|
// Apply a label to distinguish sandboxes from regular containers.
|
|
|
|
labels[containerTypeLabelKey] = containerTypeLabelSandbox
|
|
|
|
|
|
|
|
hc := &dockercontainer.HostConfig{}
|
|
|
|
createConfig := &dockertypes.ContainerCreateConfig{
|
2016-08-27 02:04:09 +00:00
|
|
|
Name: makeSandboxName(c),
|
2016-07-25 23:25:36 +00:00
|
|
|
Config: &dockercontainer.Config{
|
|
|
|
Hostname: c.GetHostname(),
|
|
|
|
// TODO: Handle environment variables.
|
|
|
|
Image: image,
|
|
|
|
Labels: labels,
|
|
|
|
},
|
|
|
|
HostConfig: hc,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply linux-specific options.
|
|
|
|
if lc := c.GetLinux(); lc != nil {
|
|
|
|
// Apply Cgroup options.
|
|
|
|
// TODO: Check if this works with per-pod cgroups.
|
|
|
|
hc.CgroupParent = lc.GetCgroupParent()
|
|
|
|
|
|
|
|
// Apply namespace options.
|
|
|
|
hc.NetworkMode, hc.UTSMode, hc.PidMode = "", "", ""
|
|
|
|
nsOpts := lc.GetNamespaceOptions()
|
|
|
|
if nsOpts != nil {
|
|
|
|
if nsOpts.GetHostNetwork() {
|
|
|
|
hc.NetworkMode = namespaceModeHost
|
|
|
|
} else {
|
|
|
|
// Assume kubelet uses either the cni or the kubenet plugin.
|
|
|
|
// TODO: support docker networking.
|
|
|
|
hc.NetworkMode = "none"
|
|
|
|
}
|
|
|
|
if nsOpts.GetHostIpc() {
|
|
|
|
hc.IpcMode = namespaceModeHost
|
|
|
|
}
|
|
|
|
if nsOpts.GetHostPid() {
|
|
|
|
hc.PidMode = namespaceModeHost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Set port mappings.
|
|
|
|
exposedPorts, portBindings := makePortsAndBindings(c.GetPortMappings())
|
|
|
|
createConfig.Config.ExposedPorts = exposedPorts
|
|
|
|
hc.PortBindings = portBindings
|
|
|
|
|
|
|
|
// Set DNS options.
|
|
|
|
if dnsOpts := c.GetDnsOptions(); dnsOpts != nil {
|
|
|
|
hc.DNS = dnsOpts.GetServers()
|
|
|
|
hc.DNSSearch = dnsOpts.GetSearches()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply resource options.
|
2016-08-12 18:17:35 +00:00
|
|
|
setSandboxResources(hc)
|
2016-07-25 23:25:36 +00:00
|
|
|
|
|
|
|
// Set security options.
|
|
|
|
hc.SecurityOpt = []string{getSeccompOpts()}
|
|
|
|
|
|
|
|
return createConfig
|
|
|
|
}
|
|
|
|
|
2016-08-12 18:17:35 +00:00
|
|
|
func setSandboxResources(hc *dockercontainer.HostConfig) {
|
2016-07-25 23:25:36 +00:00
|
|
|
hc.Resources = dockercontainer.Resources{
|
|
|
|
MemorySwap: -1, // Always disable memory swap.
|
|
|
|
CPUShares: defaultSandboxCPUshares,
|
|
|
|
// Use docker's default cpu quota/period.
|
|
|
|
}
|
2016-09-25 02:07:43 +00:00
|
|
|
// TODO: Get rid of the dependency on kubelet internal package.
|
|
|
|
hc.OomScoreAdj = qos.PodInfraOOMAdj
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|