mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
Volume manager verify containers terminated before deleting
This commit is contained in:
parent
a59ec45e2a
commit
c723d9e5c4
@ -504,7 +504,8 @@ func NewMainKubelet(
|
|||||||
hostname,
|
hostname,
|
||||||
klet.podManager,
|
klet.podManager,
|
||||||
klet.kubeClient,
|
klet.kubeClient,
|
||||||
klet.volumePluginMgr)
|
klet.volumePluginMgr,
|
||||||
|
klet.containerRuntime)
|
||||||
|
|
||||||
runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
|
runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -299,7 +299,8 @@ func newTestKubeletWithImageList(
|
|||||||
kubelet.hostname,
|
kubelet.hostname,
|
||||||
kubelet.podManager,
|
kubelet.podManager,
|
||||||
fakeKubeClient,
|
fakeKubeClient,
|
||||||
kubelet.volumePluginMgr)
|
kubelet.volumePluginMgr,
|
||||||
|
fakeRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to initialize volume manager: %v", err)
|
t.Fatalf("failed to initialize volume manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,8 @@ func TestRunOnce(t *testing.T) {
|
|||||||
kb.hostname,
|
kb.hostname,
|
||||||
kb.podManager,
|
kb.podManager,
|
||||||
kb.kubeClient,
|
kb.kubeClient,
|
||||||
kb.volumePluginMgr)
|
kb.volumePluginMgr,
|
||||||
|
fakeRuntime)
|
||||||
|
|
||||||
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR)
|
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR)
|
||||||
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
||||||
|
@ -29,7 +29,9 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/pod"
|
"k8s.io/kubernetes/pkg/kubelet/pod"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/volume/cache"
|
"k8s.io/kubernetes/pkg/kubelet/volume/cache"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
@ -64,24 +66,31 @@ type DesiredStateOfWorldPopulator interface {
|
|||||||
func NewDesiredStateOfWorldPopulator(
|
func NewDesiredStateOfWorldPopulator(
|
||||||
kubeClient internalclientset.Interface,
|
kubeClient internalclientset.Interface,
|
||||||
loopSleepDuration time.Duration,
|
loopSleepDuration time.Duration,
|
||||||
|
getPodStatusRetryDuration time.Duration,
|
||||||
podManager pod.Manager,
|
podManager pod.Manager,
|
||||||
desiredStateOfWorld cache.DesiredStateOfWorld) DesiredStateOfWorldPopulator {
|
desiredStateOfWorld cache.DesiredStateOfWorld,
|
||||||
|
kubeContainerRuntime kubecontainer.Runtime) DesiredStateOfWorldPopulator {
|
||||||
return &desiredStateOfWorldPopulator{
|
return &desiredStateOfWorldPopulator{
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
loopSleepDuration: loopSleepDuration,
|
loopSleepDuration: loopSleepDuration,
|
||||||
podManager: podManager,
|
getPodStatusRetryDuration: getPodStatusRetryDuration,
|
||||||
desiredStateOfWorld: desiredStateOfWorld,
|
podManager: podManager,
|
||||||
|
desiredStateOfWorld: desiredStateOfWorld,
|
||||||
pods: processedPods{
|
pods: processedPods{
|
||||||
processedPods: make(map[volumetypes.UniquePodName]bool)},
|
processedPods: make(map[volumetypes.UniquePodName]bool)},
|
||||||
|
kubeContainerRuntime: kubeContainerRuntime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type desiredStateOfWorldPopulator struct {
|
type desiredStateOfWorldPopulator struct {
|
||||||
kubeClient internalclientset.Interface
|
kubeClient internalclientset.Interface
|
||||||
loopSleepDuration time.Duration
|
loopSleepDuration time.Duration
|
||||||
podManager pod.Manager
|
getPodStatusRetryDuration time.Duration
|
||||||
desiredStateOfWorld cache.DesiredStateOfWorld
|
podManager pod.Manager
|
||||||
pods processedPods
|
desiredStateOfWorld cache.DesiredStateOfWorld
|
||||||
|
pods processedPods
|
||||||
|
kubeContainerRuntime kubecontainer.Runtime
|
||||||
|
timeOfLastGetPodStatus time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type processedPods struct {
|
type processedPods struct {
|
||||||
@ -102,6 +111,20 @@ func (dswp *desiredStateOfWorldPopulator) populatorLoopFunc() func() {
|
|||||||
return func() {
|
return func() {
|
||||||
dswp.findAndAddNewPods()
|
dswp.findAndAddNewPods()
|
||||||
|
|
||||||
|
// findAndRemoveDeletedPods() calls out to the container runtime to
|
||||||
|
// determine if the containers for a given pod are terminated. This is
|
||||||
|
// an expensive operation, therefore we limit the rate that
|
||||||
|
// findAndRemoveDeletedPods() is called independently of the main
|
||||||
|
// populator loop.
|
||||||
|
if time.Since(dswp.timeOfLastGetPodStatus) < dswp.getPodStatusRetryDuration {
|
||||||
|
glog.V(5).Infof(
|
||||||
|
"Skipping findAndRemoveDeletedPods(). Not permitted until %v (getPodStatusRetryDuration %v).",
|
||||||
|
dswp.timeOfLastGetPodStatus.Add(dswp.getPodStatusRetryDuration),
|
||||||
|
dswp.getPodStatusRetryDuration)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dswp.findAndRemoveDeletedPods()
|
dswp.findAndRemoveDeletedPods()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,19 +140,60 @@ func (dswp *desiredStateOfWorldPopulator) findAndAddNewPods() {
|
|||||||
// Iterate through all pods in desired state of world, and remove if they no
|
// Iterate through all pods in desired state of world, and remove if they no
|
||||||
// longer exist
|
// longer exist
|
||||||
func (dswp *desiredStateOfWorldPopulator) findAndRemoveDeletedPods() {
|
func (dswp *desiredStateOfWorldPopulator) findAndRemoveDeletedPods() {
|
||||||
|
var runningPods []*kubecontainer.Pod
|
||||||
|
|
||||||
|
runningPodsFetched := false
|
||||||
for _, volumeToMount := range dswp.desiredStateOfWorld.GetVolumesToMount() {
|
for _, volumeToMount := range dswp.desiredStateOfWorld.GetVolumesToMount() {
|
||||||
if _, podExists :=
|
if _, podExists :=
|
||||||
dswp.podManager.GetPodByUID(volumeToMount.Pod.UID); !podExists {
|
dswp.podManager.GetPodByUID(volumeToMount.Pod.UID); podExists {
|
||||||
glog.V(10).Infof(
|
continue
|
||||||
"Removing volume %q (volSpec=%q) for pod %q from desired state.",
|
|
||||||
volumeToMount.VolumeName,
|
|
||||||
volumeToMount.VolumeSpec.Name(),
|
|
||||||
volumeToMount.PodName)
|
|
||||||
|
|
||||||
dswp.desiredStateOfWorld.DeletePodFromVolume(
|
|
||||||
volumeToMount.PodName, volumeToMount.VolumeName)
|
|
||||||
dswp.deleteProcessedPod(volumeToMount.PodName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Once a pod has been deleted from kubelet pod manager, do not delete
|
||||||
|
// it immediately from volume manager. Instead, check the kubelet
|
||||||
|
// containerRuntime to verify that all containers in the pod have been
|
||||||
|
// terminated.
|
||||||
|
if !runningPodsFetched {
|
||||||
|
var getPodsErr error
|
||||||
|
runningPods, getPodsErr = dswp.kubeContainerRuntime.GetPods(false)
|
||||||
|
if getPodsErr != nil {
|
||||||
|
glog.Errorf(
|
||||||
|
"kubeContainerRuntime.findAndRemoveDeletedPods returned error %v.",
|
||||||
|
getPodsErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
runningPodsFetched = true
|
||||||
|
dswp.timeOfLastGetPodStatus = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
runningContainers := false
|
||||||
|
for _, runningPod := range runningPods {
|
||||||
|
if runningPod.ID == volumeToMount.Pod.UID {
|
||||||
|
if len(runningPod.Containers) > 0 {
|
||||||
|
runningContainers = true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runningContainers {
|
||||||
|
glog.V(5).Infof(
|
||||||
|
"Pod %q has been removed from pod manager. However, it still has one or more containers in the non-exited state. Therefore it will not be removed from volume manager.",
|
||||||
|
format.Pod(volumeToMount.Pod))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(5).Infof(
|
||||||
|
"Removing volume %q (volSpec=%q) for pod %q from desired state.",
|
||||||
|
volumeToMount.VolumeName,
|
||||||
|
volumeToMount.VolumeSpec.Name(),
|
||||||
|
format.Pod(volumeToMount.Pod))
|
||||||
|
|
||||||
|
dswp.desiredStateOfWorld.DeletePodFromVolume(
|
||||||
|
volumeToMount.PodName, volumeToMount.VolumeName)
|
||||||
|
dswp.deleteProcessedPod(volumeToMount.PodName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,10 +215,9 @@ func (dswp *desiredStateOfWorldPopulator) processPodVolumes(pod *api.Pod) {
|
|||||||
dswp.createVolumeSpec(podVolume, pod.Namespace)
|
dswp.createVolumeSpec(podVolume, pod.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf(
|
glog.Errorf(
|
||||||
"Error processing volume %q for pod %q/%q: %v",
|
"Error processing volume %q for pod %q: %v",
|
||||||
podVolume.Name,
|
podVolume.Name,
|
||||||
pod.Namespace,
|
format.Pod(pod),
|
||||||
pod.Name,
|
|
||||||
err)
|
err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/pod"
|
"k8s.io/kubernetes/pkg/kubelet/pod"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/volume/cache"
|
"k8s.io/kubernetes/pkg/kubelet/volume/cache"
|
||||||
@ -48,6 +49,12 @@ const (
|
|||||||
// DesiredStateOfWorldPopulator loop waits between successive executions
|
// DesiredStateOfWorldPopulator loop waits between successive executions
|
||||||
desiredStateOfWorldPopulatorLoopSleepPeriod time.Duration = 100 * time.Millisecond
|
desiredStateOfWorldPopulatorLoopSleepPeriod time.Duration = 100 * time.Millisecond
|
||||||
|
|
||||||
|
// desiredStateOfWorldPopulatorGetPodStatusRetryDuration is the amount of
|
||||||
|
// time the DesiredStateOfWorldPopulator loop waits between successive pod
|
||||||
|
// cleanup calls (to prevent calling containerruntime.GetPodStatus too
|
||||||
|
// frequently).
|
||||||
|
desiredStateOfWorldPopulatorGetPodStatusRetryDuration time.Duration = 2 * time.Second
|
||||||
|
|
||||||
// podAttachAndMountTimeout is the maximum amount of time the
|
// podAttachAndMountTimeout is the maximum amount of time the
|
||||||
// WaitForAttachAndMount call will wait for all volumes in the specified pod
|
// WaitForAttachAndMount call will wait for all volumes in the specified pod
|
||||||
// to be attached and mounted. Even though cloud operations can take several
|
// to be attached and mounted. Even though cloud operations can take several
|
||||||
@ -120,7 +127,8 @@ func NewVolumeManager(
|
|||||||
hostName string,
|
hostName string,
|
||||||
podManager pod.Manager,
|
podManager pod.Manager,
|
||||||
kubeClient internalclientset.Interface,
|
kubeClient internalclientset.Interface,
|
||||||
volumePluginMgr *volume.VolumePluginMgr) (VolumeManager, error) {
|
volumePluginMgr *volume.VolumePluginMgr,
|
||||||
|
kubeContainerRuntime kubecontainer.Runtime) (VolumeManager, error) {
|
||||||
vm := &volumeManager{
|
vm := &volumeManager{
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
volumePluginMgr: volumePluginMgr,
|
volumePluginMgr: volumePluginMgr,
|
||||||
@ -143,8 +151,10 @@ func NewVolumeManager(
|
|||||||
vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
||||||
|
desiredStateOfWorldPopulatorGetPodStatusRetryDuration,
|
||||||
podManager,
|
podManager,
|
||||||
vm.desiredStateOfWorld)
|
vm.desiredStateOfWorld,
|
||||||
|
kubeContainerRuntime)
|
||||||
|
|
||||||
return vm, nil
|
return vm, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user