Skip to content

Commit 2f139b5

Browse files
committed
Add docker compose deploy command
Implements a production-oriented deploy command that builds, pushes, and applies a Compose project to a Docker server with force-recreate semantics and optional health-check-based wait for zero-downtime deployments. Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent b043368 commit 2f139b5

10 files changed

Lines changed: 463 additions & 0 deletions

File tree

cmd/compose/compose.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
615615
statsCommand(&opts, dockerCli),
616616
watchCommand(&opts, dockerCli, backendOptions),
617617
publishCommand(&opts, dockerCli, backendOptions),
618+
deployCommand(&opts, dockerCli, backendOptions),
618619
alphaCommand(&opts, dockerCli, backendOptions),
619620
bridgeCommand(&opts, dockerCli),
620621
volumesCommand(&opts, dockerCli, backendOptions),

cmd/compose/deploy.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"time"
23+
24+
"github.com/docker/cli/cli/command"
25+
"github.com/spf13/cobra"
26+
27+
"github.com/docker/compose/v5/pkg/api"
28+
"github.com/docker/compose/v5/pkg/compose"
29+
)
30+
31+
type deployOptions struct {
32+
*ProjectOptions
33+
composeOptions
34+
build bool
35+
noBuild bool
36+
push bool
37+
quiet bool
38+
removeOrphans bool
39+
wait bool
40+
waitTimeout int
41+
}
42+
43+
func deployCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *BackendOptions) *cobra.Command {
44+
opts := deployOptions{
45+
ProjectOptions: p,
46+
}
47+
cmd := &cobra.Command{
48+
Use: "deploy [OPTIONS] [SERVICE...]",
49+
Short: "Deploy a Compose application to a Docker server",
50+
Long: `Deploy a Compose application to a Docker server.
51+
52+
This command applies the Compose project to the target Docker server,
53+
recreating containers with updated configuration and images. Images are
54+
pulled from the registry unless --build is specified.
55+
56+
Use health checks defined in the Compose file to ensure zero-downtime
57+
deployments by passing --wait.`,
58+
PreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
59+
if opts.waitTimeout < 0 {
60+
return fmt.Errorf("--wait-timeout must be a non-negative integer")
61+
}
62+
if opts.build && opts.noBuild {
63+
return fmt.Errorf("--build and --no-build are incompatible")
64+
}
65+
return nil
66+
}),
67+
RunE: Adapt(func(ctx context.Context, args []string) error {
68+
return runDeploy(ctx, dockerCli, backendOptions, opts, args)
69+
}),
70+
ValidArgsFunction: completeServiceNames(dockerCli, p),
71+
}
72+
flags := cmd.Flags()
73+
flags.BoolVar(&opts.build, "build", false, "Build images before deploying")
74+
flags.BoolVar(&opts.noBuild, "no-build", false, "Do not build images even if build configuration is defined")
75+
flags.BoolVar(&opts.push, "push", false, "Push images to registry before deploying")
76+
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress pull/push progress output")
77+
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
78+
flags.BoolVar(&opts.wait, "wait", false, "Wait for services to be healthy before returning")
79+
flags.IntVar(&opts.waitTimeout, "wait-timeout", 0, "Maximum duration in seconds to wait for services to be healthy (0 = no timeout)")
80+
return cmd
81+
}
82+
83+
func runDeploy(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts deployOptions, services []string) error {
84+
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
85+
if err != nil {
86+
return err
87+
}
88+
89+
project, _, err := opts.ToProject(ctx, dockerCli, backend, services)
90+
if err != nil {
91+
return err
92+
}
93+
94+
deployOpts := api.DeployOptions{
95+
Push: opts.push,
96+
Quiet: opts.quiet,
97+
RemoveOrphans: opts.removeOrphans,
98+
Wait: opts.wait,
99+
Services: services,
100+
}
101+
102+
if opts.waitTimeout > 0 {
103+
deployOpts.WaitTimeout = time.Duration(opts.waitTimeout) * time.Second
104+
}
105+
106+
if opts.build && !opts.noBuild {
107+
deployOpts.Build = &api.BuildOptions{
108+
Services: services,
109+
}
110+
}
111+
112+
return backend.Deploy(ctx, project, deployOpts)
113+
}

docs/reference/compose.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Define and run multi-container applications with Docker
1919
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
2020
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
2121
| [`create`](compose_create.md) | Creates containers for a service |
22+
| [`deploy`](compose_deploy.md) | Deploy a Compose application to a Docker server |
2223
| [`down`](compose_down.md) | Stop and remove containers, networks |
2324
| [`events`](compose_events.md) | Receive real time events from containers |
2425
| [`exec`](compose_exec.md) | Execute a command in a running container |

docs/reference/compose_deploy.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# docker compose deploy
2+
3+
<!---MARKER_GEN_START-->
4+
Deploy a Compose application to a Docker server.
5+
6+
This command applies the Compose project to the target Docker server,
7+
recreating containers with updated configuration and images. Images are
8+
pulled from the registry unless --build is specified.
9+
10+
Use health checks defined in the Compose file to ensure zero-downtime
11+
deployments by passing --wait.
12+
13+
### Options
14+
15+
| Name | Type | Default | Description |
16+
|:-------------------|:-------|:--------|:--------------------------------------------------------------------------------|
17+
| `--build` | `bool` | | Build images before deploying |
18+
| `--dry-run` | `bool` | | Execute command in dry run mode |
19+
| `--no-build` | `bool` | | Do not build images even if build configuration is defined |
20+
| `--push` | `bool` | | Push images to registry before deploying |
21+
| `-q`, `--quiet` | `bool` | | Suppress pull/push progress output |
22+
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
23+
| `--wait` | `bool` | | Wait for services to be healthy before returning |
24+
| `--wait-timeout` | `int` | `0` | Maximum duration in seconds to wait for services to be healthy (0 = no timeout) |
25+
26+
27+
<!---MARKER_GEN_END-->
28+

docs/reference/docker_compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ cname:
1212
- docker compose config
1313
- docker compose cp
1414
- docker compose create
15+
- docker compose deploy
1516
- docker compose down
1617
- docker compose events
1718
- docker compose exec
@@ -48,6 +49,7 @@ clink:
4849
- docker_compose_config.yaml
4950
- docker_compose_cp.yaml
5051
- docker_compose_create.yaml
52+
- docker_compose_deploy.yaml
5153
- docker_compose_down.yaml
5254
- docker_compose_events.yaml
5355
- docker_compose_exec.yaml
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
command: docker compose deploy
2+
short: Deploy a Compose application to a Docker server
3+
long: |-
4+
Deploy a Compose application to a Docker server.
5+
6+
This command applies the Compose project to the target Docker server,
7+
recreating containers with updated configuration and images. Images are
8+
pulled from the registry unless --build is specified.
9+
10+
Use health checks defined in the Compose file to ensure zero-downtime
11+
deployments by passing --wait.
12+
usage: docker compose deploy [OPTIONS] [SERVICE...]
13+
pname: docker compose
14+
plink: docker_compose.yaml
15+
options:
16+
- option: build
17+
value_type: bool
18+
default_value: "false"
19+
description: Build images before deploying
20+
deprecated: false
21+
hidden: false
22+
experimental: false
23+
experimentalcli: false
24+
kubernetes: false
25+
swarm: false
26+
- option: no-build
27+
value_type: bool
28+
default_value: "false"
29+
description: Do not build images even if build configuration is defined
30+
deprecated: false
31+
hidden: false
32+
experimental: false
33+
experimentalcli: false
34+
kubernetes: false
35+
swarm: false
36+
- option: push
37+
value_type: bool
38+
default_value: "false"
39+
description: Push images to registry before deploying
40+
deprecated: false
41+
hidden: false
42+
experimental: false
43+
experimentalcli: false
44+
kubernetes: false
45+
swarm: false
46+
- option: quiet
47+
shorthand: q
48+
value_type: bool
49+
default_value: "false"
50+
description: Suppress pull/push progress output
51+
deprecated: false
52+
hidden: false
53+
experimental: false
54+
experimentalcli: false
55+
kubernetes: false
56+
swarm: false
57+
- option: remove-orphans
58+
value_type: bool
59+
default_value: "false"
60+
description: Remove containers for services not defined in the Compose file
61+
deprecated: false
62+
hidden: false
63+
experimental: false
64+
experimentalcli: false
65+
kubernetes: false
66+
swarm: false
67+
- option: wait
68+
value_type: bool
69+
default_value: "false"
70+
description: Wait for services to be healthy before returning
71+
deprecated: false
72+
hidden: false
73+
experimental: false
74+
experimentalcli: false
75+
kubernetes: false
76+
swarm: false
77+
- option: wait-timeout
78+
value_type: int
79+
default_value: "0"
80+
description: |
81+
Maximum duration in seconds to wait for services to be healthy (0 = no timeout)
82+
deprecated: false
83+
hidden: false
84+
experimental: false
85+
experimentalcli: false
86+
kubernetes: false
87+
swarm: false
88+
inherited_options:
89+
- option: dry-run
90+
value_type: bool
91+
default_value: "false"
92+
description: Execute command in dry run mode
93+
deprecated: false
94+
hidden: false
95+
experimental: false
96+
experimentalcli: false
97+
kubernetes: false
98+
swarm: false
99+
deprecated: false
100+
hidden: false
101+
experimental: false
102+
experimentalcli: false
103+
kubernetes: false
104+
swarm: false
105+

pkg/api/api.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,26 @@ type Compose interface {
149149
Volumes(ctx context.Context, project string, options VolumesOptions) ([]VolumesSummary, error)
150150
// LoadProject loads and validates a Compose project from configuration files.
151151
LoadProject(ctx context.Context, options ProjectLoadOptions) (*types.Project, error)
152+
// Deploy executes the equivalent to a `compose deploy`
153+
Deploy(ctx context.Context, project *types.Project, options DeployOptions) error
154+
}
155+
156+
// DeployOptions group options of the Deploy API
157+
type DeployOptions struct {
158+
// Build rebuilds service images before deploying
159+
Build *BuildOptions
160+
// Push pushes images to the registry before deploying
161+
Push bool
162+
// Quiet suppresses pull/push progress output
163+
Quiet bool
164+
// RemoveOrphans removes containers for services not defined in the project
165+
RemoveOrphans bool
166+
// Services is the list of services to deploy (defaults to all)
167+
Services []string
168+
// Wait waits for services to be healthy after deploy
169+
Wait bool
170+
// WaitTimeout is the maximum time to wait for services to become healthy
171+
WaitTimeout time.Duration
152172
}
153173

154174
type VolumesOptions struct {

pkg/compose/deploy.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
22+
"github.com/compose-spec/compose-go/v2/types"
23+
24+
"github.com/docker/compose/v5/pkg/api"
25+
)
26+
27+
func (s *composeService) Deploy(ctx context.Context, project *types.Project, options api.DeployOptions) error {
28+
if options.Build != nil {
29+
if err := s.Build(ctx, project, *options.Build); err != nil {
30+
return err
31+
}
32+
}
33+
34+
if options.Push {
35+
if err := s.Push(ctx, project, api.PushOptions{
36+
Quiet: options.Quiet,
37+
}); err != nil {
38+
return err
39+
}
40+
}
41+
42+
return s.Up(ctx, project, api.UpOptions{
43+
Create: api.CreateOptions{
44+
Services: options.Services,
45+
Recreate: api.RecreateForce,
46+
RecreateDependencies: api.RecreateForce,
47+
RemoveOrphans: options.RemoveOrphans,
48+
Inherit: true,
49+
QuietPull: options.Quiet,
50+
},
51+
Start: api.StartOptions{
52+
Project: project,
53+
Services: options.Services,
54+
Wait: options.Wait,
55+
WaitTimeout: options.WaitTimeout,
56+
},
57+
})
58+
}

0 commit comments

Comments
 (0)