Browse code

Add force option to service update

Currently, there's no way to restart the tasks of a service without
making an actual change to the service. This leads to us giving awkward
workarounds as in
https://github.com/docker/docker.github.io/pull/178/files, where we tell
people to scale a service up and down to restore balance, or make
unnecessary changes to trigger a restart.

This change adds a --force option to "docker service update", which
forces the service to be updated even if no changes require that.

Since rolling update parameters are respected, the user can use
"docker service --force" to do a rolling restart. For example, the
following is supported:

docker service update --force --update-parallelism 2 \
--update-delay 5s myservice

Since the default value of --update-parallelism is 1, the default
behavior is to restart the service one task at a time.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2016/10/21 04:04:01
Showing 8 changed files
... ...
@@ -61,6 +61,10 @@ type TaskSpec struct {
61 61
 	// spec. If not present, the one on cluster default on swarm.Spec will be
62 62
 	// used, finally falling back to the engine default if not specified.
63 63
 	LogDriver *Driver `json:",omitempty"`
64
+
65
+	// ForceUpdate is a counter that triggers an update even if no relevant
66
+	// parameters have been changed.
67
+	ForceUpdate uint64
64 68
 }
65 69
 
66 70
 // Resources represents resources (CPU/Memory).
... ...
@@ -37,6 +37,7 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
37 37
 	flags.String("image", "", "Service image tag")
38 38
 	flags.String("args", "", "Service command args")
39 39
 	flags.Bool("rollback", false, "Rollback to previous specification")
40
+	flags.Bool("force", false, "Force update even if no changes require it")
40 41
 	addServiceFlags(cmd, opts)
41 42
 
42 43
 	flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
... ...
@@ -257,6 +258,15 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
257 257
 		return err
258 258
 	}
259 259
 
260
+	force, err := flags.GetBool("force")
261
+	if err != nil {
262
+		return err
263
+	}
264
+
265
+	if force {
266
+		spec.TaskTemplate.ForceUpdate++
267
+	}
268
+
260 269
 	return nil
261 270
 }
262 271
 
... ...
@@ -2473,6 +2473,7 @@ _docker_service_update() {
2473 2473
 		--constraint
2474 2474
 		--endpoint-mode
2475 2475
 		--env -e
2476
+		--force
2476 2477
 		--group-add
2477 2478
 		--label -l
2478 2479
 		--limit-cpu
... ...
@@ -1185,6 +1185,7 @@ __docker_service_subcommand() {
1185 1185
                 "($help)--arg=[Service command args]:arguments: _normal" \
1186 1186
                 "($help)*--container-label-add=[Add or update container labels]:label: " \
1187 1187
                 "($help)*--container-label-rm=[Remove a container label by its key]:label: " \
1188
+                "($help)--force[Force update]" \
1188 1189
                 "($help)*--group-rm=[Remove previously added user groups from the container]:group:_groups" \
1189 1190
                 "($help)--image=[Service image tag]:image:__docker_repositories" \
1190 1191
                 "($help)--rollback[Rollback to previous specification]" \
... ...
@@ -74,6 +74,7 @@ func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) *types.ServiceSpec {
74 74
 			Placement:     placementFromGRPC(spec.Task.Placement),
75 75
 			LogDriver:     driverFromGRPC(spec.Task.LogDriver),
76 76
 			Networks:      taskNetworks,
77
+			ForceUpdate:   spec.Task.ForceUpdate,
77 78
 		},
78 79
 
79 80
 		Networks:     serviceNetworks,
... ...
@@ -136,9 +137,10 @@ func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) {
136 136
 			Labels: s.Labels,
137 137
 		},
138 138
 		Task: swarmapi.TaskSpec{
139
-			Resources: resourcesToGRPC(s.TaskTemplate.Resources),
140
-			LogDriver: driverToGRPC(s.TaskTemplate.LogDriver),
141
-			Networks:  taskNetworks,
139
+			Resources:   resourcesToGRPC(s.TaskTemplate.Resources),
140
+			LogDriver:   driverToGRPC(s.TaskTemplate.LogDriver),
141
+			Networks:    taskNetworks,
142
+			ForceUpdate: s.TaskTemplate.ForceUpdate,
142 143
 		},
143 144
 		Networks: serviceNetworks,
144 145
 	}
... ...
@@ -139,6 +139,7 @@ This section lists each version from latest to oldest.  Each listing includes a
139 139
   containers that are tasks (part of a service in swarm mode).
140 140
 * `POST /containers/create` now takes `StopTimeout` field.
141 141
 * `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
142
+* `POST /services/(id or name)/update` now accepts a `ForceUpdate` parameter inside the `TaskTemplate`, which causes the service to be updated even if there are no changes which would ordinarily trigger an update.
142 143
 * `GET /networks/(name)` now returns field `Created` in response to show network created time.
143 144
 * `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
144 145
 * `GET /volumes`, `GET /volumes/(name)`, and `POST /volumes/create` now return the `Options` field which holds the driver specific options to use for when creating the volume.
... ...
@@ -4915,7 +4915,8 @@ List services
4915 4915
               "Condition": "any",
4916 4916
               "MaxAttempts": 0
4917 4917
             },
4918
-            "Placement": {}
4918
+            "Placement": {},
4919
+            "ForceUpdate": 0
4919 4920
           },
4920 4921
           "Mode": {
4921 4922
             "Replicated": {
... ...
@@ -5038,7 +5039,8 @@ image](#create-an-image) section for more details.
5038 5038
           "Condition": "on-failure",
5039 5039
           "Delay": 10000000000.0,
5040 5040
           "MaxAttempts": 10
5041
-        }
5041
+        },
5042
+        "ForceUpdate": 0
5042 5043
       },
5043 5044
       "Mode": {
5044 5045
         "Replicated": {
... ...
@@ -5132,6 +5134,7 @@ image](#create-an-image) section for more details.
5132 5132
         - **Window** – Windows is the time window used to evaluate the restart policy (default value is
5133 5133
           0, which is unbounded).
5134 5134
     - **Placement** – An array of constraints.
5135
+    - **ForceUpdate**: A counter that triggers an update even if no relevant parameters have been changed.
5135 5136
 - **Mode** – Scheduling mode for the service (`replicated` or `global`, defaults to `replicated`).
5136 5137
 - **UpdateConfig** – Specification for the update strategy of the service.
5137 5138
     - **Parallelism** – Maximum number of tasks to be updated in one iteration (0 means unlimited
... ...
@@ -5303,7 +5306,8 @@ image](#create-an-image) section for more details.
5303 5303
           "Condition": "any",
5304 5304
           "MaxAttempts": 0
5305 5305
         },
5306
-        "Placement": {}
5306
+        "Placement": {},
5307
+        "ForceUpdate": 0
5307 5308
       },
5308 5309
       "Mode": {
5309 5310
         "Replicated": {
... ...
@@ -5374,6 +5378,7 @@ image](#create-an-image) section for more details.
5374 5374
         - **Window** – Windows is the time window used to evaluate the restart policy (default value is
5375 5375
           0, which is unbounded).
5376 5376
     - **Placement** – An array of constraints.
5377
+    - **ForceUpdate**: A counter that triggers an update even if no relevant parameters have been changed.
5377 5378
 - **Mode** – Scheduling mode for the service (`replicated` or `global`, defaults to `replicated`).
5378 5379
 - **UpdateConfig** – Specification for the update strategy of the service.
5379 5380
     - **Parallelism** – Maximum number of tasks to be updated in one iteration (0 means unlimited
... ...
@@ -29,6 +29,7 @@ Options:
29 29
       --endpoint-mode string             Endpoint mode (vip or dnsrr)
30 30
       --env-add value                    Add or update environment variables (default [])
31 31
       --env-rm value                     Remove an environment variable (default [])
32
+      --force                            Force update even if no changes require it
32 33
       --group-add value                  Add additional user groups to the container (default [])
33 34
       --group-rm value                   Remove previously added user groups from the container (default [])
34 35
       --help                             Print usage
... ...
@@ -67,6 +68,12 @@ Updates a service as described by the specified parameters. This command has to
67 67
 The parameters are the same as [`docker service create`](service_create.md). Please look at the description there
68 68
 for further information.
69 69
 
70
+Normally, updating a service will only cause the service's tasks to be replaced with new ones if a change to the
71
+service requires recreating the tasks for it to take effect. For example, only changing the
72
+`--update-parallelism` setting will not recreate the tasks, because the individual tasks are not affected by this
73
+setting. However, the `--force` flag will cause the tasks to be recreated anyway. This can be used to perform a
74
+rolling restart without any changes to the service parameters.
75
+
70 76
 ## Examples
71 77
 
72 78
 ### Update a service
... ...
@@ -75,6 +82,19 @@ for further information.
75 75
 $ docker service update --limit-cpu 2 redis
76 76
 ```
77 77
 
78
+### Perform a rolling restart with no parameter changes
79
+
80
+```bash
81
+$ docker service update --force --update-parallelism 1 --update-delay 30s redis
82
+```
83
+
84
+In this example, the `--force` flag causes the service's tasks to be shut down
85
+and replaced with new ones even though none of the other parameters would
86
+normally cause that to happen. The `--update-parallelism 1` setting ensures
87
+that only one task is replaced at a time (this is the default behavior). The
88
+`--update-delay 30s` setting introduces a 30 second delay between tasks, so
89
+that the rolling restart happens gradually.
90
+
78 91
 ### Adding and removing mounts
79 92
 
80 93
 Use the `--mount-add` or `--mount-rm` options add or remove a service's bind-mounts