-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: finalizers #136
Merged
Merged
Feature: finalizers #136
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
35d9c4e
Adding finalizer support
ewoutp c11ec3f
Prevent stopping Pods to early to avoid dataloss
ewoutp 29db9bd
Set (increase) default termination grace period for pods
ewoutp 264c05e
Set termination grace period also on arangosync
ewoutp e2bdb9f
Prevent deleting PVCs while their member still exists
ewoutp 0c2b903
Remove finalizers upon delete of deployment to prevent orphans
ewoutp ddafad4
Merge branch 'master' into feature/pod-finalizers
ewoutp b7e07d5
Prevent multiple-agent delete
ewoutp 191c4c3
Documented finalizers
ewoutp e2f2c60
Always remove cleaned out server
ewoutp cb587c7
Fixed resilience PVC test wrt finalizers
ewoutp 2184890
Merge branch 'master' into feature/pod-finalizers
ewoutp 5dad616
Merged in master
ewoutp db36758
Merge branch 'master' into feature/pod-finalizers
ewoutp bd90071
Typo
ewoutp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Lifecycle hooks & Finalizers | ||
|
||
The ArangoDB operator expects full control of the `Pods` and `PersistentVolumeClaims` it creates. | ||
Therefore it takes measures to prevent the removal of those resources | ||
until it is safe to do so. | ||
|
||
To achieve this, the server containers in the `Pods` have | ||
a `preStop` hook configured and finalizers are added to the `Pods` | ||
and `PersistentVolumeClaims`. | ||
|
||
The `preStop` hook executes a binary that waits until all finalizers of | ||
the current pod have been removed. | ||
Until this `preStop` hook terminates, Kubernetes will not send a `TERM` signal | ||
to the processes inside the container, which ensures that the server remains running | ||
until it is safe to stop them. | ||
|
||
The operator performs all actions needed when a delete of a `Pod` or | ||
`PersistentVolumeClaims` has been triggered. | ||
E.g. for a dbserver it cleans out the server if the `Pod` and `PersistentVolumeClaim` are being deleted. | ||
|
||
## Lifecycle init-container | ||
|
||
Because the binary that is called in the `preStop` hook is not part of a standard | ||
ArangoDB docker image, it has to be brought into the filesystem of a `Pod`. | ||
This is done by an initial container that copies the binary to an `emptyDir` volume that | ||
is shared between the init-container and the server container. | ||
|
||
## Finalizers | ||
|
||
The ArangoDB operators adds the following finalizers to `Pods`. | ||
|
||
- `dbserver.database.arangodb.com/drain`: Added to DBServers, removed only when the dbserver can be restarted or is completely drained | ||
- `agent.database.arangodb.com/agency-serving`: Added to Agents, removed only when enough agents are left to keep the agency serving | ||
|
||
The ArangoDB operators adds the following finalizers to `PersistentVolumeClaims`. | ||
|
||
- `pvc.database.arangodb.com/member-exists`: removed only when its member exists no longer exists or can be safely rebuild |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// | ||
// DISCLAIMER | ||
// | ||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany | ||
// | ||
// 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. | ||
// | ||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Author Ewout Prangsma | ||
// | ||
|
||
package main | ||
|
||
import ( | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
"github.com/arangodb/kube-arangodb/pkg/util/constants" | ||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" | ||
) | ||
|
||
var ( | ||
cmdLifecycle = &cobra.Command{ | ||
Use: "lifecycle", | ||
Run: cmdUsage, | ||
Hidden: true, | ||
} | ||
|
||
cmdLifecyclePreStop = &cobra.Command{ | ||
Use: "preStop", | ||
Run: cmdLifecyclePreStopRun, | ||
Hidden: true, | ||
} | ||
cmdLifecycleCopy = &cobra.Command{ | ||
Use: "copy", | ||
Run: cmdLifecycleCopyRun, | ||
Hidden: true, | ||
} | ||
|
||
lifecycleCopyOptions struct { | ||
TargetDir string | ||
} | ||
) | ||
|
||
func init() { | ||
cmdMain.AddCommand(cmdLifecycle) | ||
cmdLifecycle.AddCommand(cmdLifecyclePreStop) | ||
cmdLifecycle.AddCommand(cmdLifecycleCopy) | ||
|
||
cmdLifecycleCopy.Flags().StringVar(&lifecycleCopyOptions.TargetDir, "target", "", "Target directory to copy the executable to") | ||
} | ||
|
||
// Wait until all finalizers of the current pod have been removed. | ||
func cmdLifecyclePreStopRun(cmd *cobra.Command, args []string) { | ||
cliLog.Info().Msgf("Starting arangodb-operator, lifecycle preStop, version %s build %s", projectVersion, projectBuild) | ||
|
||
// Get environment | ||
namespace := os.Getenv(constants.EnvOperatorPodNamespace) | ||
if len(namespace) == 0 { | ||
cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodNamespace) | ||
} | ||
name := os.Getenv(constants.EnvOperatorPodName) | ||
if len(name) == 0 { | ||
cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodName) | ||
} | ||
|
||
// Create kubernetes client | ||
kubecli, err := k8sutil.NewKubeClient() | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to create Kubernetes client") | ||
} | ||
|
||
pods := kubecli.CoreV1().Pods(namespace) | ||
recentErrors := 0 | ||
for { | ||
p, err := pods.Get(name, metav1.GetOptions{}) | ||
if k8sutil.IsNotFound(err) { | ||
cliLog.Warn().Msg("Pod not found") | ||
return | ||
} else if err != nil { | ||
recentErrors++ | ||
cliLog.Error().Err(err).Msg("Failed to get pod") | ||
if recentErrors > 20 { | ||
cliLog.Fatal().Err(err).Msg("Too many recent errors") | ||
return | ||
} | ||
} else { | ||
// We got our pod | ||
finalizerCount := len(p.GetFinalizers()) | ||
if finalizerCount == 0 { | ||
// No more finalizers, we're done | ||
cliLog.Info().Msg("All finalizers gone, we can stop now") | ||
return | ||
} | ||
cliLog.Info().Msgf("Waiting for %d more finalizers to be removed", finalizerCount) | ||
} | ||
// Wait a bit | ||
time.Sleep(time.Second) | ||
} | ||
} | ||
|
||
// Copy the executable to a given place. | ||
func cmdLifecycleCopyRun(cmd *cobra.Command, args []string) { | ||
cliLog.Info().Msgf("Starting arangodb-operator, lifecycle copy, version %s build %s", projectVersion, projectBuild) | ||
|
||
exePath, err := os.Executable() | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to get executable path") | ||
} | ||
|
||
// Open source | ||
rd, err := os.Open(exePath) | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to open executable file") | ||
} | ||
defer rd.Close() | ||
|
||
// Open target | ||
targetPath := filepath.Join(lifecycleCopyOptions.TargetDir, filepath.Base(exePath)) | ||
wr, err := os.Create(targetPath) | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to create target file") | ||
} | ||
defer wr.Close() | ||
|
||
if _, err := io.Copy(wr, rd); err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to copy") | ||
} | ||
|
||
// Set file mode | ||
if err := os.Chmod(targetPath, 0755); err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to chmod") | ||
} | ||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// DISCLAIMER | ||
// | ||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany | ||
// | ||
// 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. | ||
// | ||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Author Ewout Prangsma | ||
// | ||
|
||
package deployment | ||
|
||
import ( | ||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" | ||
) | ||
|
||
// removePodFinalizers removes all finalizers from all pods owned by us. | ||
func (d *Deployment) removePodFinalizers() error { | ||
log := d.deps.Log | ||
kubecli := d.GetKubeCli() | ||
pods, err := d.GetOwnedPods() | ||
if err != nil { | ||
return maskAny(err) | ||
} | ||
for _, p := range pods { | ||
if err := k8sutil.RemovePodFinalizers(log, kubecli, &p, p.GetFinalizers()); err != nil { | ||
log.Warn().Err(err).Msg("Failed to remove pod finalizers") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// removePVCFinalizers removes all finalizers from all PVCs owned by us. | ||
func (d *Deployment) removePVCFinalizers() error { | ||
log := d.deps.Log | ||
kubecli := d.GetKubeCli() | ||
pvcs, err := d.GetOwnedPVCs() | ||
if err != nil { | ||
return maskAny(err) | ||
} | ||
for _, p := range pvcs { | ||
if err := k8sutil.RemovePVCFinalizers(log, kubecli, &p, p.GetFinalizers()); err != nil { | ||
log.Warn().Err(err).Msg("Failed to remove PVC finalizers") | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for the record (one more time): LOL about this trick.