Replace Traefik with mproxy

[mproxy](https://github.com/kevinmcconnell/mproxy) is a custom minimal
proxy designed specifically for Kamal.

It has two big advantages over Traefik:
1. Imperative deployments - we tell it to switch from container A to
   container B, and it waits for container B to start then switches. No
   need to poll for health checks ourselves or mess around with forcing
   health checks to fail.
2. Support for multiple apps - as much as possible, configuration is
   supplied at runtime by the deploy command, allowing us to have
   multiple apps share an instance of mproxy without conflicting config.
This commit is contained in:
Donal McBreen
2024-03-08 08:19:48 +00:00
parent 8bb596e216
commit 9c4747ec0c
52 changed files with 648 additions and 912 deletions

114
test/commands/proxy_test.rb Normal file
View File

@@ -0,0 +1,114 @@
require "test_helper"
class CommandsProxyTest < ActiveSupport::TestCase
setup do
@config = {
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ],
}
ENV["EXAMPLE_API_KEY"] = "456"
end
teardown do
ENV.delete("EXAMPLE_API_KEY")
end
test "run" do
assert_equal \
"docker run --name mproxy --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Kamal::Commands::Proxy::DEFAULT_IMAGE}",
new_command.run.join(" ")
end
test "run with ports configured" do
assert_equal \
"docker run --name mproxy --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Kamal::Commands::Proxy::DEFAULT_IMAGE}",
new_command.run.join(" ")
end
test "run without configuration" do
@config.delete(:proxy)
assert_equal \
"docker run --name mproxy --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-opt max-size=\"10m\" #{Kamal::Commands::Proxy::DEFAULT_IMAGE}",
new_command.run.join(" ")
end
test "run with logging config" do
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
assert_equal \
"docker run --name mproxy --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{Kamal::Commands::Proxy::DEFAULT_IMAGE}",
new_command.run.join(" ")
end
test "proxy start" do
assert_equal \
"docker container start mproxy",
new_command.start.join(" ")
end
test "proxy stop" do
assert_equal \
"docker container stop mproxy",
new_command.stop.join(" ")
end
test "proxy info" do
assert_equal \
"docker ps --filter name=^mproxy$",
new_command.info.join(" ")
end
test "proxy logs" do
assert_equal \
"docker logs mproxy --timestamps 2>&1",
new_command.logs.join(" ")
end
test "proxy logs since 2h" do
assert_equal \
"docker logs mproxy --since 2h --timestamps 2>&1",
new_command.logs(since: '2h').join(" ")
end
test "proxy logs last 10 lines" do
assert_equal \
"docker logs mproxy --tail 10 --timestamps 2>&1",
new_command.logs(lines: 10).join(" ")
end
test "proxy logs with grep hello!" do
assert_equal \
"docker logs mproxy --timestamps 2>&1 | grep 'hello!'",
new_command.logs(grep: 'hello!').join(" ")
end
test "proxy remove container" do
assert_equal \
"docker container prune --force --filter label=org.opencontainers.image.title=mproxy",
new_command.remove_container.join(" ")
end
test "proxy remove image" do
assert_equal \
"docker image prune --all --force --filter label=org.opencontainers.image.title=mproxy",
new_command.remove_image.join(" ")
end
test "proxy follow logs" do
assert_equal \
"ssh -t root@1.1.1.1 -p 22 'docker logs mproxy --timestamps --tail 10 --follow 2>&1'",
new_command.follow_logs(host: @config[:servers].first)
end
test "proxy follow logs with grep hello!" do
assert_equal \
"ssh -t root@1.1.1.1 -p 22 'docker logs mproxy --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'",
new_command.follow_logs(host: @config[:servers].first, grep: 'hello!')
end
private
def new_command
Kamal::Commands::Proxy.new(Kamal::Configuration.new(@config, version: "123"))
end
end