mrsk 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|