mrsk 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -6
- data/lib/mrsk/cli/accessory.rb +9 -12
- data/lib/mrsk/cli/app.rb +9 -9
- data/lib/mrsk/cli/base.rb +5 -0
- data/lib/mrsk/cli/build.rb +1 -1
- data/lib/mrsk/cli/healthcheck.rb +29 -0
- data/lib/mrsk/cli/main.rb +32 -7
- data/lib/mrsk/cli/prune.rb +3 -3
- data/lib/mrsk/cli/traefik.rb +4 -4
- data/lib/mrsk/commander.rb +4 -0
- data/lib/mrsk/commands/app.rb +10 -4
- data/lib/mrsk/commands/auditor.rb +13 -3
- data/lib/mrsk/commands/base.rb +4 -0
- data/lib/mrsk/commands/healthcheck.rb +46 -0
- data/lib/mrsk/commands/prune.rb +4 -7
- data/lib/mrsk/configuration/role.rb +1 -1
- data/lib/mrsk/configuration.rb +12 -1
- data/lib/mrsk/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de97b438ef507257a74ec7c5143028946072ac152ff631e979f71788786bc2ba
|
4
|
+
data.tar.gz: 35112851498a8a99452850077ad1845f403d198d49b39fb769a746690c48909b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f35616494c2fcf90fb00f2cfd108492147e76d5eb12b91356a520dd0c5b7974bb6336feb55fa8239df407566144b1b1e069d1981e6aad8c159068e4bb4a91d5d
|
7
|
+
data.tar.gz: 4e9e55f95b32399c465384351d209bc12709158f39ce05f4ea1b569a3e9f1bd7cb725ce8bd795aa18548230812c0309e0d277392bf942adc62e3a6f436d626d4
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ env:
|
|
20
20
|
- RAILS_MASTER_KEY
|
21
21
|
```
|
22
22
|
|
23
|
-
Then edit your `.env` file to add your registry password as `MRSK_REGISTRY_PASSWORD` (and your `RAILS_MASTER_KEY` for production with a Rails app).
|
23
|
+
Then edit your `.env` file to add your registry password as `MRSK_REGISTRY_PASSWORD` (and your `RAILS_MASTER_KEY` for production with a Rails app).
|
24
24
|
|
25
25
|
Now you're ready to deploy to the servers:
|
26
26
|
|
@@ -37,9 +37,10 @@ This will:
|
|
37
37
|
5. Push the image to the registry.
|
38
38
|
6. Pull the image from the registry on the servers.
|
39
39
|
7. Ensure Traefik is running and accepting traffic on port 80.
|
40
|
-
8.
|
41
|
-
9.
|
42
|
-
10.
|
40
|
+
8. Ensure your app responds with `200 OK` to `GET /up`.
|
41
|
+
9. Stop any containers running a previous versions of the app.
|
42
|
+
10. Start a new container with the version of the app that matches the current git version hash.
|
43
|
+
11. Prune unused images and stopped containers to ensure servers don't fill up.
|
43
44
|
|
44
45
|
Voila! All the servers are now serving the app on port 80. If you're just running a single server, you're ready to go. If you're running multiple servers, you need to put a load balancer in front of them.
|
45
46
|
|
@@ -183,10 +184,10 @@ You can specialize the default Traefik rules by setting labels on the containers
|
|
183
184
|
|
184
185
|
```
|
185
186
|
labels:
|
186
|
-
traefik.http.routers.hey.rule:
|
187
|
+
traefik.http.routers.hey.rule: Host(\`app.hey.com\`)
|
187
188
|
```
|
188
189
|
|
189
|
-
Note: The
|
190
|
+
Note: The escaped backticks are needed to ensure the rule is passed in correctly and not treated as command substitution by Bash!
|
190
191
|
|
191
192
|
This allows you to run multiple applications on the same server sharing the same Traefik instance and port.
|
192
193
|
See https://doc.traefik.io/traefik/routing/routers/#rule for a full list of available routing rules.
|
@@ -345,6 +346,41 @@ This template can safely be checked into git. Then everyone deploying the app ca
|
|
345
346
|
|
346
347
|
If you need separate env variables for different destinations, you can set them with `.env.destination.erb` for the template, which will generate `.env.staging` when run with `mrsk envify -d staging`.
|
347
348
|
|
349
|
+
### Using audit broadcasts
|
350
|
+
|
351
|
+
If you'd like to broadcast audits of deploys, rollbacks, etc to a chatroom or elsewhere, you can configure the `audit_broadcast_cmd` setting with the path to a bin file that reads the audit line from STDIN, and then does whatever with it:
|
352
|
+
|
353
|
+
```yaml
|
354
|
+
audit_broadcast_cmd:
|
355
|
+
bin/audit_broadcast
|
356
|
+
```
|
357
|
+
|
358
|
+
The broadcast command could look something like:
|
359
|
+
|
360
|
+
```bash
|
361
|
+
#!/usr/bin/env bash
|
362
|
+
read
|
363
|
+
curl -q -d content="[My app] ${REPLY}" https://3.basecamp.com/XXXXX/integrations/XXXXX/buckets/XXXXX/chats/XXXXX/lines
|
364
|
+
```
|
365
|
+
|
366
|
+
That'll post a line like follows to a preconfigured chatbot in Basecamp:
|
367
|
+
|
368
|
+
```
|
369
|
+
[My App] [2023-02-18 11:29:52] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
370
|
+
```
|
371
|
+
|
372
|
+
### Using custom healthcheck path or port
|
373
|
+
|
374
|
+
MRSK defaults to checking the health of your application again `/up` on port 3000. You can tailor both with the `healthcheck` setting:
|
375
|
+
|
376
|
+
```yaml
|
377
|
+
healthcheck:
|
378
|
+
path: /healthz
|
379
|
+
port: 4000
|
380
|
+
```
|
381
|
+
|
382
|
+
This will ensure your application is configured with a traefik label for the healthcheck against `/healthz` and that the pre-deploy healthcheck that MRSK performs is done against the same path on port 4000.
|
383
|
+
|
348
384
|
## Commands
|
349
385
|
|
350
386
|
### Running commands on servers
|
data/lib/mrsk/cli/accessory.rb
CHANGED
@@ -9,9 +9,11 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
9
9
|
upload(name)
|
10
10
|
|
11
11
|
on(accessory.host) do
|
12
|
-
execute *MRSK.auditor.record("
|
12
|
+
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
13
13
|
execute *accessory.run
|
14
14
|
end
|
15
|
+
|
16
|
+
audit_broadcast "Booted accessory #{name}"
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -20,8 +22,6 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
20
22
|
def upload(name)
|
21
23
|
with_accessory(name) do |accessory|
|
22
24
|
on(accessory.host) do
|
23
|
-
execute *MRSK.auditor.record("accessory #{name} upload files"), verbosity: :debug
|
24
|
-
|
25
25
|
accessory.files.each do |(local, remote)|
|
26
26
|
accessory.ensure_local_file_present(local)
|
27
27
|
|
@@ -37,8 +37,6 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
37
37
|
def directories(name)
|
38
38
|
with_accessory(name) do |accessory|
|
39
39
|
on(accessory.host) do
|
40
|
-
execute *MRSK.auditor.record("accessory #{name} create directories"), verbosity: :debug
|
41
|
-
|
42
40
|
accessory.directories.keys.each do |host_path|
|
43
41
|
execute *accessory.make_directory(host_path)
|
44
42
|
end
|
@@ -59,7 +57,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
59
57
|
def start(name)
|
60
58
|
with_accessory(name) do |accessory|
|
61
59
|
on(accessory.host) do
|
62
|
-
execute *MRSK.auditor.record("
|
60
|
+
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
63
61
|
execute *accessory.start
|
64
62
|
end
|
65
63
|
end
|
@@ -69,7 +67,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
69
67
|
def stop(name)
|
70
68
|
with_accessory(name) do |accessory|
|
71
69
|
on(accessory.host) do
|
72
|
-
execute *MRSK.auditor.record("
|
70
|
+
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
73
71
|
execute *accessory.stop, raise_on_non_zero_exit: false
|
74
72
|
end
|
75
73
|
end
|
@@ -111,14 +109,14 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
111
109
|
when options[:reuse]
|
112
110
|
say "Launching command from existing container...", :magenta
|
113
111
|
on(accessory.host) do
|
114
|
-
execute *MRSK.auditor.record("
|
112
|
+
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
115
113
|
capture_with_info(*accessory.execute_in_existing_container(cmd))
|
116
114
|
end
|
117
115
|
|
118
116
|
else
|
119
117
|
say "Launching command from new container...", :magenta
|
120
118
|
on(accessory.host) do
|
121
|
-
execute *MRSK.auditor.record("
|
119
|
+
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
122
120
|
capture_with_info(*accessory.execute_in_new_container(cmd))
|
123
121
|
end
|
124
122
|
end
|
@@ -169,7 +167,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
169
167
|
def remove_container(name)
|
170
168
|
with_accessory(name) do |accessory|
|
171
169
|
on(accessory.host) do
|
172
|
-
execute *MRSK.auditor.record("
|
170
|
+
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
173
171
|
execute *accessory.remove_container
|
174
172
|
end
|
175
173
|
end
|
@@ -179,7 +177,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
179
177
|
def remove_image(name)
|
180
178
|
with_accessory(name) do |accessory|
|
181
179
|
on(accessory.host) do
|
182
|
-
execute *MRSK.auditor.record("
|
180
|
+
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
183
181
|
execute *accessory.remove_image
|
184
182
|
end
|
185
183
|
end
|
@@ -189,7 +187,6 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
189
187
|
def remove_service_directory(name)
|
190
188
|
with_accessory(name) do |accessory|
|
191
189
|
on(accessory.host) do
|
192
|
-
execute *MRSK.auditor.record("accessory #{name} remove service directory"), verbosity: :debug
|
193
190
|
execute *accessory.remove_service_directory
|
194
191
|
end
|
195
192
|
end
|
data/lib/mrsk/cli/app.rb
CHANGED
@@ -7,7 +7,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
7
7
|
|
8
8
|
MRSK.config.roles.each do |role|
|
9
9
|
on(role.hosts) do |host|
|
10
|
-
execute *MRSK.auditor.record("app
|
10
|
+
execute *MRSK.auditor.record("Booted app version #{version}"), verbosity: :debug
|
11
11
|
|
12
12
|
begin
|
13
13
|
execute *MRSK.app.stop, raise_on_non_zero_exit: false
|
@@ -15,7 +15,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
15
15
|
rescue SSHKit::Command::Failed => e
|
16
16
|
if e.message =~ /already in use/
|
17
17
|
error "Rebooting container with same version already deployed on #{host}"
|
18
|
-
execute *MRSK.auditor.record("app
|
18
|
+
execute *MRSK.auditor.record("Rebooted app version #{version}"), verbosity: :debug
|
19
19
|
|
20
20
|
execute *MRSK.app.remove_container(version: version)
|
21
21
|
execute *MRSK.app.run(role: role.name)
|
@@ -31,7 +31,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
31
31
|
desc "start", "Start existing app on servers (use --version=<git-hash> to designate specific version)"
|
32
32
|
def start
|
33
33
|
on(MRSK.hosts) do
|
34
|
-
execute *MRSK.auditor.record("app
|
34
|
+
execute *MRSK.auditor.record("Started app version #{MRSK.version}"), verbosity: :debug
|
35
35
|
execute *MRSK.app.start, raise_on_non_zero_exit: false
|
36
36
|
end
|
37
37
|
end
|
@@ -39,7 +39,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
39
39
|
desc "stop", "Stop app on servers"
|
40
40
|
def stop
|
41
41
|
on(MRSK.hosts) do
|
42
|
-
execute *MRSK.auditor.record("app
|
42
|
+
execute *MRSK.auditor.record("Stopped app"), verbosity: :debug
|
43
43
|
execute *MRSK.app.stop, raise_on_non_zero_exit: false
|
44
44
|
end
|
45
45
|
end
|
@@ -74,7 +74,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
74
74
|
say "Launching command with version #{version} from existing container...", :magenta
|
75
75
|
|
76
76
|
on(MRSK.hosts) do |host|
|
77
|
-
execute *MRSK.auditor.record("
|
77
|
+
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
78
78
|
puts_by_host host, capture_with_info(*MRSK.app.execute_in_existing_container(cmd))
|
79
79
|
end
|
80
80
|
end
|
@@ -84,7 +84,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
84
84
|
using_version(options[:version] || most_recent_version_available) do |version|
|
85
85
|
say "Launching command with version #{version} from new container...", :magenta
|
86
86
|
on(MRSK.hosts) do |host|
|
87
|
-
execute *MRSK.auditor.record("
|
87
|
+
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
88
88
|
puts_by_host host, capture_with_info(*MRSK.app.execute_in_new_container(cmd))
|
89
89
|
end
|
90
90
|
end
|
@@ -140,7 +140,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
140
140
|
desc "remove_container [VERSION]", "Remove app container with given version from servers"
|
141
141
|
def remove_container(version)
|
142
142
|
on(MRSK.hosts) do
|
143
|
-
execute *MRSK.auditor.record("app
|
143
|
+
execute *MRSK.auditor.record("Removed app container with version #{version}"), verbosity: :debug
|
144
144
|
execute *MRSK.app.remove_container(version: version)
|
145
145
|
end
|
146
146
|
end
|
@@ -148,7 +148,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
148
148
|
desc "remove_containers", "Remove all app containers from servers"
|
149
149
|
def remove_containers
|
150
150
|
on(MRSK.hosts) do
|
151
|
-
execute *MRSK.auditor.record("app
|
151
|
+
execute *MRSK.auditor.record("Removed all app containers"), verbosity: :debug
|
152
152
|
execute *MRSK.app.remove_containers
|
153
153
|
end
|
154
154
|
end
|
@@ -156,7 +156,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
156
156
|
desc "remove_images", "Remove all app images from servers"
|
157
157
|
def remove_images
|
158
158
|
on(MRSK.hosts) do
|
159
|
-
execute *MRSK.auditor.record("app
|
159
|
+
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
160
160
|
execute *MRSK.app.remove_images
|
161
161
|
end
|
162
162
|
end
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -59,9 +59,14 @@ module Mrsk::Cli
|
|
59
59
|
def print_runtime
|
60
60
|
started_at = Time.now
|
61
61
|
yield
|
62
|
+
return Time.now - started_at
|
62
63
|
ensure
|
63
64
|
runtime = Time.now - started_at
|
64
65
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
65
66
|
end
|
67
|
+
|
68
|
+
def audit_broadcast(line)
|
69
|
+
run_locally { execute *MRSK.auditor.broadcast(line), verbosity: :debug }
|
70
|
+
end
|
66
71
|
end
|
67
72
|
end
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -29,7 +29,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
29
29
|
desc "pull", "Pull app image from the registry onto servers"
|
30
30
|
def pull
|
31
31
|
on(MRSK.hosts) do
|
32
|
-
execute *MRSK.auditor.record("
|
32
|
+
execute *MRSK.auditor.record("Pulled image with version #{MRSK.version}"), verbosity: :debug
|
33
33
|
execute *MRSK.builder.pull
|
34
34
|
end
|
35
35
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
2
|
+
desc "perform", "Health check the current version of the app"
|
3
|
+
def perform
|
4
|
+
on(MRSK.primary_host) do
|
5
|
+
begin
|
6
|
+
execute *MRSK.healthcheck.run
|
7
|
+
|
8
|
+
target = "Health check against #{MRSK.config.healthcheck["path"]}"
|
9
|
+
|
10
|
+
if capture_with_info(*MRSK.healthcheck.curl) == "200"
|
11
|
+
info "#{target} succeeded with 200 OK!"
|
12
|
+
else
|
13
|
+
# Catches 1xx, 2xx, 3xx
|
14
|
+
raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
|
15
|
+
end
|
16
|
+
rescue SSHKit::Command::Failed => e
|
17
|
+
if e.message =~ /curl/
|
18
|
+
# Catches 4xx, 5xx
|
19
|
+
raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
|
20
|
+
else
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
ensure
|
24
|
+
execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
|
25
|
+
execute *MRSK.healthcheck.remove, raise_on_non_zero_exit: false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -10,7 +10,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
10
10
|
|
11
11
|
desc "deploy", "Deploy the app to servers"
|
12
12
|
def deploy
|
13
|
-
print_runtime do
|
13
|
+
runtime = print_runtime do
|
14
14
|
say "Ensure Docker is installed...", :magenta
|
15
15
|
invoke "mrsk:cli:server:bootstrap"
|
16
16
|
|
@@ -23,33 +23,48 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
23
23
|
say "Ensure Traefik is running...", :magenta
|
24
24
|
invoke "mrsk:cli:traefik:boot"
|
25
25
|
|
26
|
+
say "Ensure app can pass healthcheck...", :magenta
|
27
|
+
invoke "mrsk:cli:healthcheck:perform"
|
28
|
+
|
26
29
|
invoke "mrsk:cli:app:boot"
|
27
30
|
|
28
31
|
say "Prune old containers and images...", :magenta
|
29
32
|
invoke "mrsk:cli:prune:all"
|
30
33
|
end
|
34
|
+
|
35
|
+
audit_broadcast "Deployed app in #{runtime.to_i} seconds"
|
31
36
|
end
|
32
37
|
|
33
38
|
desc "redeploy", "Deploy new version of the app to servers (without bootstrapping servers, starting Traefik, pruning, and registry login)"
|
34
39
|
def redeploy
|
35
|
-
print_runtime do
|
40
|
+
runtime = print_runtime do
|
36
41
|
say "Build and push app image...", :magenta
|
37
42
|
invoke "mrsk:cli:build:deliver"
|
38
43
|
|
44
|
+
say "Ensure app can pass healthcheck...", :magenta
|
45
|
+
invoke "mrsk:cli:healthcheck:perform"
|
46
|
+
|
39
47
|
invoke "mrsk:cli:app:boot"
|
40
48
|
end
|
49
|
+
|
50
|
+
audit_broadcast "Redeployed app in #{runtime.to_i} seconds"
|
41
51
|
end
|
42
52
|
|
43
53
|
desc "rollback [VERSION]", "Rollback the app to VERSION"
|
44
54
|
def rollback(version)
|
45
55
|
MRSK.version = version
|
46
56
|
|
47
|
-
|
57
|
+
if container_name_available?(MRSK.config.service_with_version)
|
58
|
+
say "Stop current version, then start version #{version}...", :magenta
|
48
59
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
60
|
+
on(MRSK.hosts) do |host|
|
61
|
+
execute *MRSK.app.stop, raise_on_non_zero_exit: false
|
62
|
+
execute *MRSK.app.start
|
63
|
+
end
|
64
|
+
|
65
|
+
audit_broadcast "Rolled back app to version #{version}"
|
66
|
+
else
|
67
|
+
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
|
53
68
|
end
|
54
69
|
end
|
55
70
|
|
@@ -138,6 +153,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
138
153
|
desc "build", "Build the application image"
|
139
154
|
subcommand "build", Mrsk::Cli::Build
|
140
155
|
|
156
|
+
desc "healthcheck", "Healthcheck the application"
|
157
|
+
subcommand "healthcheck", Mrsk::Cli::Healthcheck
|
158
|
+
|
141
159
|
desc "prune", "Prune old application images and containers"
|
142
160
|
subcommand "prune", Mrsk::Cli::Prune
|
143
161
|
|
@@ -149,4 +167,11 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
149
167
|
|
150
168
|
desc "traefik", "Manage the Traefik load balancer"
|
151
169
|
subcommand "traefik", Mrsk::Cli::Traefik
|
170
|
+
|
171
|
+
private
|
172
|
+
def container_name_available?(container_name, host: MRSK.primary_host)
|
173
|
+
container_names = nil
|
174
|
+
on(host) { container_names = capture_with_info(*MRSK.app.list_container_names).split("\n") }
|
175
|
+
Array(container_names).include?(container_name)
|
176
|
+
end
|
152
177
|
end
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -5,10 +5,10 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
5
5
|
invoke :images
|
6
6
|
end
|
7
7
|
|
8
|
-
desc "images", "Prune unused images older than
|
8
|
+
desc "images", "Prune unused images older than 7 days"
|
9
9
|
def images
|
10
10
|
on(MRSK.hosts) do
|
11
|
-
execute *MRSK.auditor.record("
|
11
|
+
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
12
12
|
execute *MRSK.prune.images
|
13
13
|
end
|
14
14
|
end
|
@@ -16,7 +16,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
16
16
|
desc "containers", "Prune stopped containers for the service older than 3 days"
|
17
17
|
def containers
|
18
18
|
on(MRSK.hosts) do
|
19
|
-
execute *MRSK.auditor.record("
|
19
|
+
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
20
20
|
execute *MRSK.prune.containers
|
21
21
|
end
|
22
22
|
end
|
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -14,7 +14,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
14
14
|
desc "start", "Start existing Traefik on servers"
|
15
15
|
def start
|
16
16
|
on(MRSK.traefik_hosts) do
|
17
|
-
execute *MRSK.auditor.record("traefik
|
17
|
+
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
18
18
|
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
19
19
|
end
|
20
20
|
end
|
@@ -22,7 +22,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
22
22
|
desc "stop", "Stop Traefik on servers"
|
23
23
|
def stop
|
24
24
|
on(MRSK.traefik_hosts) do
|
25
|
-
execute *MRSK.auditor.record("traefik
|
25
|
+
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
26
26
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
27
27
|
end
|
28
28
|
end
|
@@ -72,7 +72,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
72
72
|
desc "remove_container", "Remove Traefik container from servers"
|
73
73
|
def remove_container
|
74
74
|
on(MRSK.traefik_hosts) do
|
75
|
-
execute *MRSK.auditor.record("traefik
|
75
|
+
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
76
76
|
execute *MRSK.traefik.remove_container
|
77
77
|
end
|
78
78
|
end
|
@@ -80,7 +80,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
80
80
|
desc "remove_container", "Remove Traefik image from servers"
|
81
81
|
def remove_image
|
82
82
|
on(MRSK.traefik_hosts) do
|
83
|
-
execute *MRSK.auditor.record("traefik
|
83
|
+
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
84
84
|
execute *MRSK.traefik.remove_image
|
85
85
|
end
|
86
86
|
end
|
data/lib/mrsk/commander.rb
CHANGED
data/lib/mrsk/commands/app.rb
CHANGED
@@ -75,10 +75,6 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
75
75
|
docker :ps, "-q", *service_filter
|
76
76
|
end
|
77
77
|
|
78
|
-
def container_id_for(container_name:)
|
79
|
-
docker :container, :ls, "-a", "-f", "name=#{container_name}", "-q"
|
80
|
-
end
|
81
|
-
|
82
78
|
def current_running_version
|
83
79
|
# FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail!
|
84
80
|
pipe \
|
@@ -93,11 +89,21 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
93
89
|
"head -n 1"
|
94
90
|
end
|
95
91
|
|
92
|
+
def all_versions_from_available_containers
|
93
|
+
pipe \
|
94
|
+
docker(:image, :ls, "--format", '"{{.Tag}}"', config.repository),
|
95
|
+
"head -n 1"
|
96
|
+
end
|
97
|
+
|
96
98
|
|
97
99
|
def list_containers
|
98
100
|
docker :container, :ls, "-a", *service_filter
|
99
101
|
end
|
100
102
|
|
103
|
+
def list_container_names
|
104
|
+
[ *list_containers, "--format", "'{{ .Names }}'" ]
|
105
|
+
end
|
106
|
+
|
101
107
|
def remove_container(version:)
|
102
108
|
pipe \
|
103
109
|
container_id_for(container_name: service_with_version(version)),
|
@@ -1,12 +1,22 @@
|
|
1
1
|
require "active_support/core_ext/time/conversions"
|
2
2
|
|
3
3
|
class Mrsk::Commands::Auditor < Mrsk::Commands::Base
|
4
|
+
# Runs remotely
|
4
5
|
def record(line)
|
5
6
|
append \
|
6
7
|
[ :echo, tagged_line(line) ],
|
7
8
|
audit_log_file
|
8
9
|
end
|
9
10
|
|
11
|
+
# Runs locally
|
12
|
+
def broadcast(line)
|
13
|
+
if broadcast_cmd = config.audit_broadcast_cmd
|
14
|
+
pipe \
|
15
|
+
[ :echo, tagged_line(line) ],
|
16
|
+
broadcast_cmd
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
10
20
|
def reveal
|
11
21
|
[ :tail, "-n", 50, audit_log_file ]
|
12
22
|
end
|
@@ -21,14 +31,14 @@ class Mrsk::Commands::Auditor < Mrsk::Commands::Base
|
|
21
31
|
end
|
22
32
|
|
23
33
|
def tags
|
24
|
-
"[#{
|
34
|
+
"[#{recorded_at}] [#{performer}]"
|
25
35
|
end
|
26
36
|
|
27
37
|
def performer
|
28
|
-
`whoami`.strip
|
38
|
+
@performer ||= `whoami`.strip
|
29
39
|
end
|
30
40
|
|
31
|
-
def
|
41
|
+
def recorded_at
|
32
42
|
Time.now.to_fs(:db)
|
33
43
|
end
|
34
44
|
end
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
|
2
|
+
EXPOSED_PORT = 3999
|
3
|
+
|
4
|
+
def run
|
5
|
+
web = config.role(:web)
|
6
|
+
|
7
|
+
docker :run,
|
8
|
+
"-d",
|
9
|
+
"--name", container_name_with_version,
|
10
|
+
"-p", "#{EXPOSED_PORT}:#{config.healthcheck["port"]}",
|
11
|
+
"--label", "service=#{container_name}",
|
12
|
+
*web.env_args,
|
13
|
+
*config.volume_args,
|
14
|
+
config.absolute_image,
|
15
|
+
web.cmd
|
16
|
+
end
|
17
|
+
|
18
|
+
def curl
|
19
|
+
[ :curl, "--silent", "--output", "/dev/null", "--write-out", "'%{http_code}'", health_url ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
pipe \
|
24
|
+
container_id_for(container_name: container_name),
|
25
|
+
xargs(docker(:stop))
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove
|
29
|
+
pipe \
|
30
|
+
container_id_for(container_name: container_name),
|
31
|
+
xargs(docker(:container, :rm))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def container_name
|
36
|
+
"healthcheck-#{config.service}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def container_name_with_version
|
40
|
+
"healthcheck-#{config.service_with_version}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def health_url
|
44
|
+
"http://localhost:#{EXPOSED_PORT}#{config.healthcheck["path"]}"
|
45
|
+
end
|
46
|
+
end
|
data/lib/mrsk/commands/prune.rb
CHANGED
@@ -2,14 +2,11 @@ require "active_support/duration"
|
|
2
2
|
require "active_support/core_ext/numeric/time"
|
3
3
|
|
4
4
|
class Mrsk::Commands::Prune < Mrsk::Commands::Base
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def images
|
9
|
-
docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{PRUNE_IMAGES_AFTER}h"
|
5
|
+
def images(until_hours: 7.days.in_hours.to_i)
|
6
|
+
docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h"
|
10
7
|
end
|
11
8
|
|
12
|
-
def containers
|
13
|
-
docker :container, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{
|
9
|
+
def containers(until_hours: 3.days.in_hours.to_i)
|
10
|
+
docker :container, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h"
|
14
11
|
end
|
15
12
|
end
|
@@ -59,7 +59,7 @@ class Mrsk::Configuration::Role
|
|
59
59
|
if running_traefik?
|
60
60
|
{
|
61
61
|
"traefik.http.routers.#{config.service}.rule" => "'PathPrefix(`/`)'",
|
62
|
-
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.path" => "
|
62
|
+
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.path" => config.healthcheck["path"],
|
63
63
|
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.interval" => "1s",
|
64
64
|
"traefik.http.middlewares.#{config.service}.retry.attempts" => "3",
|
65
65
|
"traefik.http.middlewares.#{config.service}.retry.initialinterval" => "500ms"
|
data/lib/mrsk/configuration.rb
CHANGED
@@ -107,6 +107,7 @@ class Mrsk::Configuration
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
+
|
110
111
|
def ssh_user
|
111
112
|
if raw_config.ssh.present?
|
112
113
|
raw_config.ssh["user"] || "root"
|
@@ -127,6 +128,15 @@ class Mrsk::Configuration
|
|
127
128
|
end
|
128
129
|
|
129
130
|
|
131
|
+
def audit_broadcast_cmd
|
132
|
+
raw_config.audit_broadcast_cmd
|
133
|
+
end
|
134
|
+
|
135
|
+
def healthcheck
|
136
|
+
{ "path" => "/up", "port" => 3000 }.merge(raw_config.healthcheck || {})
|
137
|
+
end
|
138
|
+
|
139
|
+
|
130
140
|
def valid?
|
131
141
|
ensure_required_keys_present && ensure_env_available
|
132
142
|
end
|
@@ -145,7 +155,8 @@ class Mrsk::Configuration
|
|
145
155
|
volume_args: volume_args,
|
146
156
|
ssh_options: ssh_options,
|
147
157
|
builder: raw_config.builder,
|
148
|
-
accessories: raw_config.accessories
|
158
|
+
accessories: raw_config.accessories,
|
159
|
+
healthcheck: healthcheck
|
149
160
|
}.compact
|
150
161
|
end
|
151
162
|
|
data/lib/mrsk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mrsk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- lib/mrsk/cli/app.rb
|
97
97
|
- lib/mrsk/cli/base.rb
|
98
98
|
- lib/mrsk/cli/build.rb
|
99
|
+
- lib/mrsk/cli/healthcheck.rb
|
99
100
|
- lib/mrsk/cli/main.rb
|
100
101
|
- lib/mrsk/cli/prune.rb
|
101
102
|
- lib/mrsk/cli/registry.rb
|
@@ -115,6 +116,7 @@ files:
|
|
115
116
|
- lib/mrsk/commands/builder/multiarch/remote.rb
|
116
117
|
- lib/mrsk/commands/builder/native.rb
|
117
118
|
- lib/mrsk/commands/builder/native/remote.rb
|
119
|
+
- lib/mrsk/commands/healthcheck.rb
|
118
120
|
- lib/mrsk/commands/prune.rb
|
119
121
|
- lib/mrsk/commands/registry.rb
|
120
122
|
- lib/mrsk/commands/traefik.rb
|