Skip to content

Commit 7638229

Browse files
authored
Merge pull request #1355 from npezza93/local-docker-registry
Use local docker registry to push and pull app images
2 parents 5bac3e6 + b660fd0 commit 7638229

File tree

24 files changed

+352
-61
lines changed

24 files changed

+352
-61
lines changed

lib/kamal/cli/build.rb

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,18 @@ def push
6868

6969
desc "pull", "Pull app image from registry onto servers"
7070
def pull
71-
login_to_registry_remotely
72-
73-
if (first_hosts = mirror_hosts).any?
74-
#  Pull on a single host per mirror first to seed them
75-
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
76-
pull_on_hosts(first_hosts)
77-
say "Pulling image on remaining hosts...", :magenta
78-
pull_on_hosts(KAMAL.app_hosts - first_hosts)
79-
else
80-
pull_on_hosts(KAMAL.app_hosts)
71+
login_to_registry_remotely unless KAMAL.registry.local?
72+
73+
forward_local_registry_port do
74+
if (first_hosts = mirror_hosts).any?
75+
#  Pull on a single host per mirror first to seed them
76+
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
77+
pull_on_hosts(first_hosts)
78+
say "Pulling image on remaining hosts...", :magenta
79+
pull_on_hosts(KAMAL.app_hosts - first_hosts)
80+
else
81+
pull_on_hosts(KAMAL.app_hosts)
82+
end
8183
end
8284
end
8385

@@ -194,7 +196,11 @@ def pull_on_hosts(hosts)
194196

195197
def login_to_registry_locally
196198
run_locally do
197-
execute *KAMAL.registry.login
199+
if KAMAL.registry.local?
200+
execute *KAMAL.registry.setup
201+
else
202+
execute *KAMAL.registry.login
203+
end
198204
end
199205
end
200206

@@ -203,4 +209,14 @@ def login_to_registry_remotely
203209
execute *KAMAL.registry.login
204210
end
205211
end
212+
213+
def forward_local_registry_port(&block)
214+
if KAMAL.config.registry.local?
215+
Kamal::Cli::PortForwarding.
216+
new(KAMAL.hosts, KAMAL.config.registry.local_port).
217+
forward(&block)
218+
else
219+
yield
220+
end
221+
end
206222
end

lib/kamal/cli/main.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def remove
185185
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
186186
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
187187
invoke "kamal:cli:accessory:remove", [ "all" ], options
188-
invoke "kamal:cli:registry:logout", [], options.without(:confirmed).merge(skip_local: true)
188+
invoke "kamal:cli:registry:remove", [], options.without(:confirmed).merge(skip_local: true)
189189
end
190190
end
191191
end

lib/kamal/cli/port_forwarding.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
class Kamal::Cli::PortForwarding
2+
attr_reader :hosts, :port
3+
4+
def initialize(hosts, port)
5+
@hosts = hosts
6+
@port = port
7+
end
8+
9+
def forward
10+
@done = false
11+
forward_ports
12+
13+
yield
14+
ensure
15+
stop
16+
end
17+
18+
private
19+
20+
def stop
21+
@done = true
22+
@threads.to_a.each(&:join)
23+
end
24+
25+
def forward_ports
26+
@threads = hosts.map do |host|
27+
Thread.new do
28+
Net::SSH.start(host, KAMAL.config.ssh.user, **{ proxy: KAMAL.config.ssh.proxy }.compact) do |ssh|
29+
ssh.forward.remote(port, "localhost", port, "localhost")
30+
ssh.loop(0.1) do
31+
if @done
32+
ssh.forward.cancel_remote(port, "localhost")
33+
break
34+
else
35+
true
36+
end
37+
end
38+
end
39+
end
40+
end
41+
end
42+
end

lib/kamal/cli/registry.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
class Kamal::Cli::Registry < Kamal::Cli::Base
2-
desc "login", "Log in to registry locally and remotely"
2+
desc "setup", "Setup local registry or log in to remote registry locally and remotely"
33
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
44
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
5-
def login
5+
def setup
66
ensure_docker_installed unless options[:skip_local]
77

8-
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
9-
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
8+
if KAMAL.registry.local?
9+
run_locally { execute *KAMAL.registry.setup } unless options[:skip_local]
10+
else
11+
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
12+
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
13+
end
1014
end
1115

12-
desc "logout", "Log out of registry locally and remotely"
16+
desc "remove", "Remove local registry or log out of remote registry locally and remotely"
1317
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
1418
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
15-
def logout
16-
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
17-
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
19+
def remove
20+
if KAMAL.registry.local?
21+
run_locally { execute *KAMAL.registry.remove, raise_on_non_zero_exit: false } unless options[:skip_local]
22+
else
23+
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
24+
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
25+
end
1826
end
1927
end

lib/kamal/cli/templates/deploy.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ proxy:
2525

2626
# Credentials for your image host.
2727
registry:
28+
server: localhost:5555
2829
# Specify the registry server, if you're not using Docker Hub
2930
# server: registry.digitalocean.com / ghcr.io / ...
30-
username: my-user
31+
# username: my-user
3132

3233
# Always use an access token rather than real password (pulled from .kamal/secrets).
33-
password:
34-
- KAMAL_REGISTRY_PASSWORD
34+
# password:
35+
# - KAMAL_REGISTRY_PASSWORD
3536

3637
# Configure builder setup.
3738
builder:

lib/kamal/cli/templates/secrets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
44

55
# Option 1: Read secrets from the environment
6-
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
6+
# KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
77

88
# Option 2: Read secrets via a command
99
# RAILS_MASTER_KEY=$(cat config/master.key)

lib/kamal/commander.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def reset
2121
end
2222

2323
def config
24-
@config ||= Kamal::Configuration.create_from(**@config_kwargs).tap do |config|
24+
@config ||= Kamal::Configuration.create_from(**@config_kwargs.to_h).tap do |config|
2525
@config_kwargs = nil
2626
configure_sshkit_with(config)
2727
end
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
22
def create
3-
docker :buildx, :create, "--name", builder_name, "--driver=#{driver}" unless docker_driver?
3+
return if docker_driver?
4+
5+
options =
6+
if KAMAL.registry.local?
7+
"--driver=#{driver} --driver-opt network=host"
8+
else
9+
"--driver=#{driver}"
10+
end
11+
12+
docker :buildx, :create, "--name", builder_name, options
413
end
514

615
def remove
@@ -9,6 +18,10 @@ def remove
918

1019
private
1120
def builder_name
12-
"kamal-local-#{driver}"
21+
if KAMAL.registry.local?
22+
"kamal-local-registry-#{driver}"
23+
else
24+
"kamal-local-#{driver}"
25+
end
1326
end
1427
end

lib/kamal/commands/registry.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
22
def login(registry_config: nil)
33
registry_config ||= config.registry
44

5+
return if registry_config.local?
6+
57
docker :login,
68
registry_config.server,
79
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
@@ -13,4 +15,24 @@ def logout(registry_config: nil)
1315

1416
docker :logout, registry_config.server
1517
end
18+
19+
def setup(registry_config: nil)
20+
registry_config ||= config.registry
21+
22+
combine \
23+
docker(:start, "kamal-docker-registry"),
24+
docker(:run, "--detach", "-p", "127.0.0.1:#{registry_config.local_port}:5000", "--name", "kamal-docker-registry", "registry:3"),
25+
by: "||"
26+
end
27+
28+
def remove
29+
combine \
30+
docker(:stop, "kamal-docker-registry"),
31+
docker(:rm, "kamal-docker-registry"),
32+
by: "&&"
33+
end
34+
35+
def local?
36+
config.registry.local?
37+
end
1638
end

lib/kamal/configuration.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
require "net/ssh/proxy/jump"
77

88
class Kamal::Configuration
9-
delegate :service, :image, :labels, :hooks_path, to: :raw_config, allow_nil: true
9+
delegate :service, :labels, :hooks_path, to: :raw_config, allow_nil: true
1010
delegate :argumentize, :optionize, to: Kamal::Utils
1111

1212
attr_reader :destination, :raw_config, :secrets
@@ -157,6 +157,13 @@ def proxy_hosts
157157
(proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
158158
end
159159

160+
def image
161+
name = raw_config&.image.presence
162+
name ||= raw_config&.service if registry.local?
163+
164+
name
165+
end
166+
160167
def repository
161168
[ registry.server, image ].compact.join("/")
162169
end
@@ -282,10 +289,12 @@ def ensure_destination_if_required
282289
end
283290

284291
def ensure_required_keys_present
285-
%i[ service image registry ].each do |key|
292+
%i[ service registry ].each do |key|
286293
raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
287294
end
288295

296+
raise Kamal::ConfigurationError, "Missing required configuration for image" if image.blank?
297+
289298
if raw_config.servers.nil?
290299
raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
291300
else

0 commit comments

Comments
 (0)