Commit 4b3498d2 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #34287 from Random-Liu/add-sandbox-gc-minage

Automatic merge from submit-queue Add sandbox gc minage Fixes https://github.com/kubernetes/kubernetes/issues/34272. Fixes https://github.com/kubernetes/kubernetes/issues/33984. This PR: 1) Change the `GetPodStatus` to get statuses of all containers in a pod instead of only containers belonging to existing sandboxes. This is because sandbox may be removed by GC or by users, kubelet should be able to deal with this case. 2) Change the CRI comment to clarify the timestamp unit (nanosecond). 2) Add MinAge for sandbox GC Policy. @yujuhong @feiskyer @yifan-gu /cc @kubernetes/sig-node
parents d631d8e7 08aedca1
...@@ -812,7 +812,7 @@ type PodSandboxStatus struct { ...@@ -812,7 +812,7 @@ type PodSandboxStatus struct {
Metadata *PodSandboxMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"` Metadata *PodSandboxMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"`
// State of the sandbox. // State of the sandbox.
State *PodSandBoxState `protobuf:"varint,3,opt,name=state,enum=runtime.PodSandBoxState" json:"state,omitempty"` State *PodSandBoxState `protobuf:"varint,3,opt,name=state,enum=runtime.PodSandBoxState" json:"state,omitempty"`
// Creation timestamp of the sandbox // Creation timestamp of the sandbox in nanoseconds.
CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"`
// Network contains network status if network is handled by the runtime. // Network contains network status if network is handled by the runtime.
Network *PodSandboxNetworkStatus `protobuf:"bytes,5,opt,name=network" json:"network,omitempty"` Network *PodSandboxNetworkStatus `protobuf:"bytes,5,opt,name=network" json:"network,omitempty"`
...@@ -971,7 +971,7 @@ type PodSandbox struct { ...@@ -971,7 +971,7 @@ type PodSandbox struct {
Metadata *PodSandboxMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"` Metadata *PodSandboxMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"`
// The state of the PodSandbox // The state of the PodSandbox
State *PodSandBoxState `protobuf:"varint,3,opt,name=state,enum=runtime.PodSandBoxState" json:"state,omitempty"` State *PodSandBoxState `protobuf:"varint,3,opt,name=state,enum=runtime.PodSandBoxState" json:"state,omitempty"`
// Creation timestamps of the sandbox // Creation timestamps of the sandbox in nanoseconds
CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"`
// The labels of the PodSandbox // The labels of the PodSandbox
Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
...@@ -1740,7 +1740,7 @@ type Container struct { ...@@ -1740,7 +1740,7 @@ type Container struct {
ImageRef *string `protobuf:"bytes,5,opt,name=image_ref,json=imageRef" json:"image_ref,omitempty"` ImageRef *string `protobuf:"bytes,5,opt,name=image_ref,json=imageRef" json:"image_ref,omitempty"`
// State is the state of the container. // State is the state of the container.
State *ContainerState `protobuf:"varint,6,opt,name=state,enum=runtime.ContainerState" json:"state,omitempty"` State *ContainerState `protobuf:"varint,6,opt,name=state,enum=runtime.ContainerState" json:"state,omitempty"`
// Creation time of the container. // Creation time of the container in nanoseconds.
CreatedAt *int64 `protobuf:"varint,7,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` CreatedAt *int64 `protobuf:"varint,7,opt,name=created_at,json=createdAt" json:"created_at,omitempty"`
// Labels are key value pairs that may be used to scope and select individual resources. // Labels are key value pairs that may be used to scope and select individual resources.
Labels map[string]string `protobuf:"bytes,8,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Labels map[string]string `protobuf:"bytes,8,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
...@@ -1862,11 +1862,11 @@ type ContainerStatus struct { ...@@ -1862,11 +1862,11 @@ type ContainerStatus struct {
Metadata *ContainerMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"` Metadata *ContainerMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata,omitempty"`
// Status of the container. // Status of the container.
State *ContainerState `protobuf:"varint,3,opt,name=state,enum=runtime.ContainerState" json:"state,omitempty"` State *ContainerState `protobuf:"varint,3,opt,name=state,enum=runtime.ContainerState" json:"state,omitempty"`
// Creation time of the container. // Creation time of the container in nanoseconds.
CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` CreatedAt *int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt" json:"created_at,omitempty"`
// Start time of the container. // Start time of the container in nanoseconds.
StartedAt *int64 `protobuf:"varint,5,opt,name=started_at,json=startedAt" json:"started_at,omitempty"` StartedAt *int64 `protobuf:"varint,5,opt,name=started_at,json=startedAt" json:"started_at,omitempty"`
// Finish time of the container. // Finish time of the container in nanoseconds.
FinishedAt *int64 `protobuf:"varint,6,opt,name=finished_at,json=finishedAt" json:"finished_at,omitempty"` FinishedAt *int64 `protobuf:"varint,6,opt,name=finished_at,json=finishedAt" json:"finished_at,omitempty"`
// Exit code of the container. // Exit code of the container.
ExitCode *int32 `protobuf:"varint,7,opt,name=exit_code,json=exitCode" json:"exit_code,omitempty"` ExitCode *int32 `protobuf:"varint,7,opt,name=exit_code,json=exitCode" json:"exit_code,omitempty"`
......
...@@ -251,7 +251,7 @@ message PodSandboxStatus { ...@@ -251,7 +251,7 @@ message PodSandboxStatus {
optional PodSandboxMetadata metadata = 2; optional PodSandboxMetadata metadata = 2;
// State of the sandbox. // State of the sandbox.
optional PodSandBoxState state = 3; optional PodSandBoxState state = 3;
// Creation timestamp of the sandbox // Creation timestamp of the sandbox in nanoseconds.
optional int64 created_at = 4; optional int64 created_at = 4;
// Network contains network status if network is handled by the runtime. // Network contains network status if network is handled by the runtime.
optional PodSandboxNetworkStatus network = 5; optional PodSandboxNetworkStatus network = 5;
...@@ -296,7 +296,7 @@ message PodSandbox { ...@@ -296,7 +296,7 @@ message PodSandbox {
optional PodSandboxMetadata metadata = 2; optional PodSandboxMetadata metadata = 2;
// The state of the PodSandbox // The state of the PodSandbox
optional PodSandBoxState state = 3; optional PodSandBoxState state = 3;
// Creation timestamps of the sandbox // Creation timestamps of the sandbox in nanoseconds
optional int64 created_at = 4; optional int64 created_at = 4;
// The labels of the PodSandbox // The labels of the PodSandbox
map<string, string> labels = 5; map<string, string> labels = 5;
...@@ -533,7 +533,7 @@ message Container { ...@@ -533,7 +533,7 @@ message Container {
optional string image_ref = 5; optional string image_ref = 5;
// State is the state of the container. // State is the state of the container.
optional ContainerState state = 6; optional ContainerState state = 6;
// Creation time of the container. // Creation time of the container in nanoseconds.
optional int64 created_at = 7; optional int64 created_at = 7;
// Labels are key value pairs that may be used to scope and select individual resources. // Labels are key value pairs that may be used to scope and select individual resources.
map<string, string> labels = 8; map<string, string> labels = 8;
...@@ -560,11 +560,11 @@ message ContainerStatus { ...@@ -560,11 +560,11 @@ message ContainerStatus {
optional ContainerMetadata metadata = 2; optional ContainerMetadata metadata = 2;
// Status of the container. // Status of the container.
optional ContainerState state = 3; optional ContainerState state = 3;
// Creation time of the container. // Creation time of the container in nanoseconds.
optional int64 created_at = 4; optional int64 created_at = 4;
// Start time of the container. // Start time of the container in nanoseconds.
optional int64 started_at = 5; optional int64 started_at = 5;
// Finish time of the container. // Finish time of the container in nanoseconds.
optional int64 finished_at = 6; optional int64 finished_at = 6;
// Exit code of the container. // Exit code of the container.
optional int32 exit_code = 7; optional int32 exit_code = 7;
......
...@@ -19,6 +19,7 @@ package dockershim ...@@ -19,6 +19,7 @@ package dockershim
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
...@@ -57,6 +58,8 @@ func toRuntimeAPIContainer(c *dockertypes.Container) (*runtimeApi.Container, err ...@@ -57,6 +58,8 @@ func toRuntimeAPIContainer(c *dockertypes.Container) (*runtimeApi.Container, err
} }
labels, annotations := extractLabels(c.Labels) labels, annotations := extractLabels(c.Labels)
sandboxID := c.Labels[sandboxIDLabelKey] sandboxID := c.Labels[sandboxIDLabelKey]
// The timestamp in dockertypes.Container is in seconds.
createdAt := c.Created * int64(time.Second)
return &runtimeApi.Container{ return &runtimeApi.Container{
Id: &c.ID, Id: &c.ID,
PodSandboxId: &sandboxID, PodSandboxId: &sandboxID,
...@@ -64,6 +67,7 @@ func toRuntimeAPIContainer(c *dockertypes.Container) (*runtimeApi.Container, err ...@@ -64,6 +67,7 @@ func toRuntimeAPIContainer(c *dockertypes.Container) (*runtimeApi.Container, err
Image: &runtimeApi.ImageSpec{Image: &c.Image}, Image: &runtimeApi.ImageSpec{Image: &c.Image},
ImageRef: &c.ImageID, ImageRef: &c.ImageID,
State: &state, State: &state,
CreatedAt: &createdAt,
Labels: labels, Labels: labels,
Annotations: annotations, Annotations: annotations,
}, nil }, nil
...@@ -117,11 +121,13 @@ func toRuntimeAPISandbox(c *dockertypes.Container) (*runtimeApi.PodSandbox, erro ...@@ -117,11 +121,13 @@ func toRuntimeAPISandbox(c *dockertypes.Container) (*runtimeApi.PodSandbox, erro
return nil, err return nil, err
} }
labels, annotations := extractLabels(c.Labels) labels, annotations := extractLabels(c.Labels)
// The timestamp in dockertypes.Container is in seconds.
createdAt := c.Created * int64(time.Second)
return &runtimeApi.PodSandbox{ return &runtimeApi.PodSandbox{
Id: &c.ID, Id: &c.ID,
Metadata: metadata, Metadata: metadata,
State: &state, State: &state,
CreatedAt: &c.Created, CreatedAt: &createdAt,
Labels: labels, Labels: labels,
Annotations: annotations, Annotations: annotations,
}, nil }, nil
......
...@@ -61,6 +61,7 @@ func TestListContainers(t *testing.T) { ...@@ -61,6 +61,7 @@ func TestListContainers(t *testing.T) {
expected := []*runtimeApi.Container{} expected := []*runtimeApi.Container{}
state := runtimeApi.ContainerState_RUNNING state := runtimeApi.ContainerState_RUNNING
var createdAt int64 = 0
for i := range configs { for i := range configs {
// We don't care about the sandbox id; pass a bogus one. // We don't care about the sandbox id; pass a bogus one.
sandboxID := fmt.Sprintf("sandboxid%d", i) sandboxID := fmt.Sprintf("sandboxid%d", i)
...@@ -77,6 +78,7 @@ func TestListContainers(t *testing.T) { ...@@ -77,6 +78,7 @@ func TestListContainers(t *testing.T) {
Id: &id, Id: &id,
PodSandboxId: &sandboxID, PodSandboxId: &sandboxID,
State: &state, State: &state,
CreatedAt: &createdAt,
Image: configs[i].Image, Image: configs[i].Image,
ImageRef: &imageRef, ImageRef: &imageRef,
Labels: configs[i].Labels, Labels: configs[i].Labels,
......
...@@ -35,8 +35,9 @@ import ( ...@@ -35,8 +35,9 @@ import (
"k8s.io/kubernetes/pkg/kubelet/dockershim" "k8s.io/kubernetes/pkg/kubelet/dockershim"
"k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/qos" "k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/format" "k8s.io/kubernetes/pkg/kubelet/util/format"
"k8s.io/kubernetes/pkg/types" kubetypes "k8s.io/kubernetes/pkg/types"
utilruntime "k8s.io/kubernetes/pkg/util/runtime" utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/term" "k8s.io/kubernetes/pkg/util/term"
...@@ -115,7 +116,7 @@ func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandb ...@@ -115,7 +116,7 @@ func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandb
} }
// getContainerLogsPath gets log path for container. // getContainerLogsPath gets log path for container.
func getContainerLogsPath(containerName string, podUID types.UID) string { func getContainerLogsPath(containerName string, podUID kubetypes.UID) string {
return path.Join(podLogsRootDirectory, string(podUID), fmt.Sprintf("%s.log", containerName)) return path.Join(podLogsRootDirectory, string(podUID), fmt.Sprintf("%s.log", containerName))
} }
...@@ -345,10 +346,11 @@ func getTerminationMessage(status *runtimeApi.ContainerStatus, kubeStatus *kubec ...@@ -345,10 +346,11 @@ func getTerminationMessage(status *runtimeApi.ContainerStatus, kubeStatus *kubec
return message return message
} }
// getKubeletContainerStatuses gets all containers' status for the pod sandbox. // getPodContainerStatuses gets all containers' statuses for the pod.
func (m *kubeGenericRuntimeManager) getKubeletContainerStatuses(podSandboxID string) ([]*kubecontainer.ContainerStatus, error) { func (m *kubeGenericRuntimeManager) getPodContainerStatuses(uid kubetypes.UID, name, namespace string) ([]*kubecontainer.ContainerStatus, error) {
// Select all containers of the given pod.
containers, err := m.runtimeService.ListContainers(&runtimeApi.ContainerFilter{ containers, err := m.runtimeService.ListContainers(&runtimeApi.ContainerFilter{
PodSandboxId: &podSandboxID, LabelSelector: map[string]string{types.KubernetesPodUIDLabel: string(uid)},
}) })
if err != nil { if err != nil {
glog.Errorf("ListContainers error: %v", err) glog.Errorf("ListContainers error: %v", err)
...@@ -377,16 +379,16 @@ func (m *kubeGenericRuntimeManager) getKubeletContainerStatuses(podSandboxID str ...@@ -377,16 +379,16 @@ func (m *kubeGenericRuntimeManager) getKubeletContainerStatuses(podSandboxID str
Hash: annotatedInfo.Hash, Hash: annotatedInfo.Hash,
RestartCount: annotatedInfo.RestartCount, RestartCount: annotatedInfo.RestartCount,
State: toKubeContainerState(c.GetState()), State: toKubeContainerState(c.GetState()),
CreatedAt: time.Unix(status.GetCreatedAt(), 0), CreatedAt: time.Unix(0, status.GetCreatedAt()),
} }
if c.GetState() == runtimeApi.ContainerState_RUNNING { if c.GetState() == runtimeApi.ContainerState_RUNNING {
cStatus.StartedAt = time.Unix(status.GetStartedAt(), 0) cStatus.StartedAt = time.Unix(0, status.GetStartedAt())
} else { } else {
cStatus.Reason = status.GetReason() cStatus.Reason = status.GetReason()
cStatus.Message = status.GetMessage() cStatus.Message = status.GetMessage()
cStatus.ExitCode = int(status.GetExitCode()) cStatus.ExitCode = int(status.GetExitCode())
cStatus.FinishedAt = time.Unix(status.GetFinishedAt(), 0) cStatus.FinishedAt = time.Unix(0, status.GetFinishedAt())
} }
tMessage := getTerminationMessage(status, cStatus, annotatedInfo.TerminationMessagePath) tMessage := getTerminationMessage(status, cStatus, annotatedInfo.TerminationMessagePath)
......
...@@ -27,6 +27,20 @@ import ( ...@@ -27,6 +27,20 @@ import (
"k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/types"
) )
// sandboxMinGCAge is the minimum age for an empty sandbox before it is garbage collected.
// This is introduced to avoid a sandbox being garbage collected before its containers are
// created.
// Notice that if the first container of a sandbox is created too late (exceeds sandboxMinGCAge),
// the sandbox could still be garbaged collected. In that case, SyncPod will recreate the
// sandbox and make sure old containers are all stopped.
// In the following figure, 'o' is a stopped sandbox, 'x' is a removed sandbox. It shows
// that, approximately if a sandbox keeps crashing and MinAge = 1/n GC Period, there will
// be 1/n more sandboxes not garbage collected.
// oooooo|xxxxxx|xxxxxx| <--- MinAge = 0
// gc gc gc gc
// oooooo|oooxxx|xxxxxx| <--- MinAge = 1/2 GC Perod
const sandboxMinGCAge time.Duration = 30 * time.Second
// containerGC is the manager of garbage collection. // containerGC is the manager of garbage collection.
type containerGC struct { type containerGC struct {
client internalApi.RuntimeService client internalApi.RuntimeService
...@@ -141,7 +155,7 @@ func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByE ...@@ -141,7 +155,7 @@ func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByE
continue continue
} }
createdAt := time.Unix(container.GetCreatedAt(), 0) createdAt := time.Unix(0, container.GetCreatedAt())
if newestGCTime.Before(createdAt) { if newestGCTime.Before(createdAt) {
continue continue
} }
...@@ -182,6 +196,7 @@ func (cgc *containerGC) evictableSandboxes() ([]string, error) { ...@@ -182,6 +196,7 @@ func (cgc *containerGC) evictableSandboxes() ([]string, error) {
} }
evictSandboxes := make([]string, 0) evictSandboxes := make([]string, 0)
newestGCTime := time.Now().Add(-sandboxMinGCAge)
for _, sandbox := range sandboxes { for _, sandbox := range sandboxes {
// Prune out ready sandboxes. // Prune out ready sandboxes.
if sandbox.GetState() == runtimeApi.PodSandBoxState_READY { if sandbox.GetState() == runtimeApi.PodSandBoxState_READY {
...@@ -201,6 +216,12 @@ func (cgc *containerGC) evictableSandboxes() ([]string, error) { ...@@ -201,6 +216,12 @@ func (cgc *containerGC) evictableSandboxes() ([]string, error) {
continue continue
} }
// Only garbage collect sandboxes older than sandboxMinGCAge.
createdAt := time.Unix(0, sandbox.GetCreatedAt())
if createdAt.After(newestGCTime) {
continue
}
evictSandboxes = append(evictSandboxes, sandboxID) evictSandboxes = append(evictSandboxes, sandboxID)
} }
......
...@@ -876,15 +876,14 @@ func (m *kubeGenericRuntimeManager) GetPodStatus(uid kubetypes.UID, name, namesp ...@@ -876,15 +876,14 @@ func (m *kubeGenericRuntimeManager) GetPodStatus(uid kubetypes.UID, name, namesp
UID: uid, UID: uid,
}, },
}) })
glog.V(4).Infof("getSandboxIDByPodUID got sandbox IDs %q for pod %q(UID:%q)", podSandboxIDs, podFullName, string(uid)) glog.V(4).Infof("getSandboxIDByPodUID got sandbox IDs %q for pod %q", podSandboxIDs, podFullName)
sandboxStatuses := make([]*runtimeApi.PodSandboxStatus, len(podSandboxIDs)) sandboxStatuses := make([]*runtimeApi.PodSandboxStatus, len(podSandboxIDs))
containerStatuses := []*kubecontainer.ContainerStatus{}
podIP := "" podIP := ""
for idx, podSandboxID := range podSandboxIDs { for idx, podSandboxID := range podSandboxIDs {
podSandboxStatus, err := m.runtimeService.PodSandboxStatus(podSandboxID) podSandboxStatus, err := m.runtimeService.PodSandboxStatus(podSandboxID)
if err != nil { if err != nil {
glog.Errorf("PodSandboxStatus for pod (uid:%v, name:%s, namespace:%s) error: %v", uid, name, namespace, err) glog.Errorf("PodSandboxStatus of sandbox %q for pod %q error: %v", podSandboxID, podFullName, err)
return nil, err return nil, err
} }
sandboxStatuses[idx] = podSandboxStatus sandboxStatuses[idx] = podSandboxStatus
...@@ -893,13 +892,13 @@ func (m *kubeGenericRuntimeManager) GetPodStatus(uid kubetypes.UID, name, namesp ...@@ -893,13 +892,13 @@ func (m *kubeGenericRuntimeManager) GetPodStatus(uid kubetypes.UID, name, namesp
if idx == 0 && podSandboxStatus.GetState() == runtimeApi.PodSandBoxState_READY { if idx == 0 && podSandboxStatus.GetState() == runtimeApi.PodSandBoxState_READY {
podIP = m.determinePodSandboxIP(namespace, name, podSandboxStatus) podIP = m.determinePodSandboxIP(namespace, name, podSandboxStatus)
} }
}
statuses, err := m.getKubeletContainerStatuses(podSandboxID) // Get statuses of all containers visible in the pod.
if err != nil { containerStatuses, err := m.getPodContainerStatuses(uid, name, namespace)
glog.Errorf("getKubeletContainerStatuses for sandbox %s failed: %v", podSandboxID, err) if err != nil {
return nil, err glog.Errorf("getPodContainerStatuses for pod %q failed: %v", podFullName, err)
} return nil, err
containerStatuses = append(containerStatuses, statuses...)
} }
return &kubecontainer.PodStatus{ return &kubecontainer.PodStatus{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment