Allow performing boot & start operations in groups

Adds top-level configuration options for `group_limit` and `group_wait`.
When a `group_limit` is present, we'll perform app boot & start
operations on no more than `group_limit` hosts at a time, optionally
sleeping for `group_wait` seconds after each batch.

We currently only do this batching on boot & start operations (including
when they are part of a deployment). Other commands, like `app stop` or
`app details` still work on all hosts in parallel.
This commit is contained in:
Kevin McConnell
2023-04-13 12:43:19 +01:00
parent bc8875e020
commit f530009a6e
5 changed files with 64 additions and 4 deletions

View File

@@ -831,6 +831,23 @@ mrsk lock acquire -m "Doing maintanence"
mrsk lock release mrsk lock release
``` ```
## Gradual restarts
When deploying to large numbers of hosts, you might prefer not to restart your services on every host at the same time.
MRSK's default is to start new containers on all hosts in parallel. But you can control this by configuring `group_limit` and `group_wait`.
```yaml
service: myservice
group_limit: 10
group_wait: 30
```
When `group_limit` is specified, containers will be started on, at most, `group_limit` hosts at once. MRSK will pause for `group_wait` seconds between batches.
These settings only apply when starting containers (using `mrsk deploy`, `mrsk app boot` or `mrsk app start`). For other commands, MRSK continues to run commands in parallel across all hosts.
## Stage of development ## Stage of development
This is beta software. Commands may still move around. But we're live in production at [37signals](https://37signals.com). This is beta software. Commands may still move around. But we're live in production at [37signals](https://37signals.com).

View File

@@ -13,7 +13,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
execute *MRSK.app.tag_current_as_latest execute *MRSK.app.tag_current_as_latest
end end
on(MRSK.hosts) do |host| on(MRSK.hosts, **MRSK.config.group_strategy) do |host|
roles = MRSK.roles_on(host) roles = MRSK.roles_on(host)
roles.each do |role| roles.each do |role|
@@ -39,7 +39,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "start", "Start existing app container on servers" desc "start", "Start existing app container on servers"
def start def start
with_lock do with_lock do
on(MRSK.hosts) do |host| on(MRSK.hosts, **MRSK.config.group_strategy) do |host|
roles = MRSK.roles_on(host) roles = MRSK.roles_on(host)
roles.each do |role| roles.each do |role|

View File

@@ -153,6 +153,15 @@ class Mrsk::Configuration
end end
def group_strategy
if group_limit.present?
{ in: :groups, limit: group_limit, wait: group_wait }
else
{}
end
end
def audit_broadcast_cmd def audit_broadcast_cmd
raw_config.audit_broadcast_cmd raw_config.audit_broadcast_cmd
end end
@@ -237,4 +246,12 @@ class Mrsk::Configuration
raise "Can't use commit hash as version, no git repository found in #{Dir.pwd}" raise "Can't use commit hash as version, no git repository found in #{Dir.pwd}"
end end
end end
def group_limit
raw_config.group_limit&.to_i
end
def group_wait
raw_config.group_wait&.to_i
end
end end

View File

@@ -33,6 +33,16 @@ class CliAppTest < CliTestCase
Thread.report_on_exception = true Thread.report_on_exception = true
end end
test "boot uses group strategy when specified" do
Mrsk::Cli::App.any_instance.stubs(:on).with("1.1.1.1").twice # acquire & release lock
Mrsk::Cli::App.any_instance.stubs(:on).with([ "1.1.1.1" ]) # tag container
# Strategy is used when booting the containers
Mrsk::Cli::App.any_instance.expects(:on).with([ "1.1.1.1" ], in: :groups, limit: 3, wait: 30).with_block_given
run_command("boot", config: :with_group_strategy)
end
test "start" do test "start" do
run_command("start").tap do |output| run_command("start").tap do |output|
assert_match "docker start app-web-999", output assert_match "docker start app-web-999", output
@@ -151,7 +161,7 @@ class CliAppTest < CliTestCase
end end
private private
def run_command(*command) def run_command(*command, config: :with_accessories)
stdouted { Mrsk::Cli::App.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1"]) } stdouted { Mrsk::Cli::App.start([*command, "-c", "test/fixtures/deploy_#{config}.yml", "--hosts", "1.1.1.1"]) }
end end
end end

View File

@@ -0,0 +1,16 @@
service: app
image: dhh/app
servers:
web:
- "1.1.1.1"
- "1.1.1.2"
workers:
- "1.1.1.3"
- "1.1.1.4"
registry:
username: user
password: pw
group_limit: 3
group_wait: 30