mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-21 21:30:48 +02:00
Rolling restart (#619)
* implement rolling restart functionality bouncing each image individually can ensure that a group of docker containers launched with docker-compose can stay 100% up during deploy. * move rolling restart into a function * honor params.Cleanup Co-authored-by: Simon Aronsson <simme@arcticbit.se>
This commit is contained in:
parent
6a18ee911e
commit
c56e0a95a7
5 changed files with 53 additions and 9 deletions
|
@ -30,7 +30,8 @@ var (
|
||||||
notifier *notifications.Notifier
|
notifier *notifications.Notifier
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
lifecycleHooks bool
|
lifecycleHooks bool
|
||||||
scope string
|
rollingRestart bool
|
||||||
|
scope string
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
|
@ -91,6 +92,7 @@ func PreRun(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
enableLabel, _ = f.GetBool("label-enable")
|
enableLabel, _ = f.GetBool("label-enable")
|
||||||
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
|
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
|
||||||
|
rollingRestart, _ = f.GetBool("rolling-restart")
|
||||||
scope, _ = f.GetString("scope")
|
scope, _ = f.GetString("scope")
|
||||||
|
|
||||||
log.Debug(scope)
|
log.Debug(scope)
|
||||||
|
@ -211,6 +213,7 @@ func runUpdatesWithNotifications(filter t.Filter) {
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
MonitorOnly: monitorOnly,
|
MonitorOnly: monitorOnly,
|
||||||
LifecycleHooks: lifecycleHooks,
|
LifecycleHooks: lifecycleHooks,
|
||||||
|
RollingRestart: rollingRestart,
|
||||||
}
|
}
|
||||||
err := actions.Update(client, updateParams)
|
err := actions.Update(client, updateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -247,7 +247,18 @@ can be defined, but not both. An example: `--schedule "0 0 4 * * *"`
|
||||||
Environment Variable: WATCHTOWER_SCHEDULE
|
Environment Variable: WATCHTOWER_SCHEDULE
|
||||||
Type: String
|
Type: String
|
||||||
Default: -
|
Default: -
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Rolling restart
|
||||||
|
Restart one image at time instead of stopping and starting all at once. Useful in conjunction with lifecycle hooks
|
||||||
|
to implement zero-downtime deploy.
|
||||||
|
|
||||||
|
```
|
||||||
|
Argument: --rolling-restart
|
||||||
|
Environment Variable: WATCHTOWER_ROLLING_RESTART
|
||||||
|
Type: Boolean
|
||||||
|
Default: false
|
||||||
|
```
|
||||||
|
|
||||||
## Wait until timeout
|
## Wait until timeout
|
||||||
Timeout before the container is forcefully stopped. When set, this option will change the default (`10s`) wait time to the given value. An example: `--stop-timeout 30s` will set the timeout to 30 seconds.
|
Timeout before the container is forcefully stopped. When set, this option will change the default (`10s`) wait time to the given value. An example: `--stop-timeout 30s` will set the timeout to 30 seconds.
|
||||||
|
|
|
@ -52,15 +52,33 @@ func Update(client container.Client, params types.UpdateParams) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stopContainersInReversedOrder(containers, client, params)
|
if params.RollingRestart {
|
||||||
restartContainersInSortedOrder(containers, client, params)
|
performRollingRestart(containers, client, params)
|
||||||
|
} else {
|
||||||
|
stopContainersInReversedOrder(containers, client, params)
|
||||||
|
restartContainersInSortedOrder(containers, client, params)
|
||||||
|
}
|
||||||
if params.LifecycleHooks {
|
if params.LifecycleHooks {
|
||||||
lifecycle.ExecutePostChecks(client, params)
|
lifecycle.ExecutePostChecks(client, params)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func performRollingRestart(containers []container.Container, client container.Client, params types.UpdateParams) {
|
||||||
|
cleanupImageIDs := make(map[string]bool)
|
||||||
|
|
||||||
|
for i := len(containers) - 1; i >= 0; i-- {
|
||||||
|
if containers[i].Stale {
|
||||||
|
stopStaleContainer(containers[i], client, params)
|
||||||
|
restartStaleContainer(containers[i], client, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Cleanup {
|
||||||
|
cleanupImages(client, cleanupImageIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) {
|
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) {
|
||||||
for i := len(containers) - 1; i >= 0; i-- {
|
for i := len(containers) - 1; i >= 0; i-- {
|
||||||
stopStaleContainer(containers[i], client, params)
|
stopStaleContainer(containers[i], client, params)
|
||||||
|
@ -99,11 +117,16 @@ func restartContainersInSortedOrder(containers []container.Container, client con
|
||||||
restartStaleContainer(staleContainer, client, params)
|
restartStaleContainer(staleContainer, client, params)
|
||||||
imageIDs[staleContainer.ImageID()] = true
|
imageIDs[staleContainer.ImageID()] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.Cleanup {
|
if params.Cleanup {
|
||||||
for imageID := range imageIDs {
|
cleanupImages(client, imageIDs)
|
||||||
if err := client.RemoveImageByID(imageID); err != nil {
|
}
|
||||||
log.Error(err)
|
}
|
||||||
}
|
|
||||||
|
func cleanupImages(client container.Client, imageIDs map[string]bool) {
|
||||||
|
for imageID := range imageIDs {
|
||||||
|
if err := client.RemoveImageByID(imageID); err != nil {
|
||||||
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,12 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
|
||||||
viper.GetBool("WATCHTOWER_LIFECYCLE_HOOKS"),
|
viper.GetBool("WATCHTOWER_LIFECYCLE_HOOKS"),
|
||||||
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks")
|
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks")
|
||||||
|
|
||||||
|
flags.BoolP(
|
||||||
|
"rolling-restart",
|
||||||
|
"",
|
||||||
|
viper.GetBool("WATCHTOWER_ROLLING_RESTART"),
|
||||||
|
"Restart containers one at a time")
|
||||||
|
|
||||||
flags.BoolP(
|
flags.BoolP(
|
||||||
"http-api",
|
"http-api",
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -12,4 +12,5 @@ type UpdateParams struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
MonitorOnly bool
|
MonitorOnly bool
|
||||||
LifecycleHooks bool
|
LifecycleHooks bool
|
||||||
|
RollingRestart bool
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue