Ruby on Rails

Summarized using AI

Introducing Kamal Proxy

Kevin McConnell • September 26, 2024 • Toronto, Canada

Introducing Kamal Proxy

In this presentation, Kevin McConnell introduces Kamal Proxy, a new HTTP proxy service developed to enhance the capabilities of Kamal 2.0. This service is aimed at simplifying zero-downtime deployments for Rails applications while improving their speed and usability. McConnell outlines the essential functionalities of Kamal Proxy, explaining its purpose and benefits through a structured talk at Rails World 2024.

Key Points Discussed:

  • Overview of Kamal Proxy: Kamal Proxy is a tool that aids in deploying apps, particularly those built with Rails, using a Docker-based architecture. It aims to manage traffic seamlessly between different versions of an app, ensuring no downtime during updates.

  • Gapless Deployments: The proxy supports gapless deployments by allowing traffic to migrate from the old to the new version of an app effortlessly through its proxy layer, providing a stable endpoint for user requests.

  • Reasons for Developing Kamal Proxy:

    • Iterative Improvement: The new proxy simplifies the previously complex mechanics of deploying apps with gapless transitions and enhances overall performance.
    • User Experience Focus: Aimed to provide an easy-to-use experience for users deploying their apps, Kamal Proxy integrates features that would often be found in hosted solutions, thus making self-hosting more appealing.
  • Features of Kamal Proxy:

    1. Host-based Routing: Enables deploying multiple apps on a single server by distinguishing between app traffic based on hostnames.
    2. Automatic HTTPS: Simplifies HTTPS setup using Let's Encrypt.
    3. Request and Response Buffering: Mitigates issues from slow clients, allowing for smoother app performance.
    4. HTTP/2 Support: Offers improved performance for apps with many assets through faster file downloads.
    5. Caching Mechanisms: Enhances server efficiency by caching static files and responses, significantly increasing request handling capacity.
    6. X-Accel-Redirect Support: Facilitates efficient file downloads by serving files directly, bypassing Rails processes for larger files.
    7. Maintenance Page Management: Simplifies putting apps in maintenance mode and customizing maintenance messages.
    8. Request Pausing: Allows temporary holding of incoming requests during brief maintenance periods without erroring out users.
    9. Rollout Features: Facilitates gradual deployments by controlling traffic distribution between versions for testing purposes.

Conclusion

The presentation effectively highlights how Kamal Proxy is designed not only to simplify but also to enhance the deployment experience for Rails apps. By addressing both functional performance and user-friendly deployment features, Kamal Proxy is positioned as a robust solution for developers looking to manage their applications efficiently in production environments.

In essence, Kamal Proxy represents a significant advancement for Rails developers, promoting effective self-hosted solutions by incorporating desirable features typically found in managed hosting environments.

Introducing Kamal Proxy
Kevin McConnell • September 26, 2024 • Toronto, Canada

Kamal Proxy is a new, purpose-built HTTP proxy service that powers Kamal 2.0. It is designed to make zero-downtime deployments simpler, and comes with additional features to make your Rails applications faster and easier to operate. Kevin McConnell explains what Kamal Proxy does, why they built it, and how it works in his talk at #RailsWorld.

#Kamalproxy #kamal2 #rails #rubyonrails #rails8

Thank you Shopify for sponsoring the editing and post-production of these videos. Check out insights from the Engineering team at: https://shopify.engineering/

Stay tuned: all 2024 Rails World videos will be subtitled in Japanese and Brazilian Portuguese soon thanks to our sponsor Happy Scribe, a transcription service built on Rails. https://www.happyscribe.com/

Rails World 2024

00:00:08.519 uh so good afternoon welcome to
00:00:10.320 introducing Cal proxy um my name's Kevin
00:00:13.559 I'm a programmer at 307 signals and Cal
00:00:15.719 proxy is one of the things I've been
00:00:16.800 working on there
00:00:18.359 recently um so before I get started I
00:00:21.640 did want to say if you've just watched
00:00:23.320 donal's talk which is also about Kamal
00:00:25.000 don't worry um I'll be talking more
00:00:26.720 about the proxy later not just about Cal
00:00:28.640 so there might be a wee bit overlap but
00:00:30.519 hopefully it's not going to be like a
00:00:31.439 ground talk day kind of situation for
00:00:33.399 you um but at the same time if you
00:00:36.360 didn't watch donal's talk also don't
00:00:38.120 worry um because I'm not going to expect
00:00:40.680 you to have a lot of Cal knowledge going
00:00:42.640 into this um in fact all the things that
00:00:45.399 you'll need to know about Cal for this
00:00:46.719 to make sense we can fit on one slide
00:00:48.440 which we'll do in a second and then
00:00:49.920 we'll all kind of be on the same page
00:00:51.840 from
00:00:52.600 there okay so just a a quick look at
00:00:55.039 what we'll cover in this talk pardon me
00:00:57.840 we'll just uh start by going through
00:00:59.760 what Cal proxy is um talk about why we
00:01:02.320 decided to build it how it works and
00:01:04.080 what it's for um and then the bulk of
00:01:06.320 the talk will be to take a we tour
00:01:08.479 through some of the new features that
00:01:09.840 we've added in the proxy so there's nine
00:01:11.920 features that are kind of
00:01:13.479 arranged roughly into a few different
00:01:15.640 categories there's some things there to
00:01:18.439 um make it easier to set up your your
00:01:21.720 deployments in a way that'll feel kind
00:01:23.560 of production ready I know that's a bit
00:01:25.280 of a vague term I think it'll get a bit
00:01:26.600 clearer when we get into the details
00:01:28.000 there uh but some of the features are
00:01:29.600 for that that uh there's some features
00:01:31.799 there to uh improve the performance of
00:01:34.159 your apps so they'll perform better in
00:01:36.280 production and um and then lastly some
00:01:39.439 features that are there to help uh
00:01:41.600 manage your apps once they're in
00:01:42.840 production make it easier to do the
00:01:44.159 sorts of tasks that sometimes come up
00:01:45.719 when you've got when you've got an app
00:01:47.680 running uh and then at the end we'll
00:01:49.600 just take a quick look at some of the
00:01:50.960 things that might be coming next to Cal
00:01:52.520 proxy after
00:01:54.040 that so as promised the one one slide or
00:01:58.000 one pager guides to what Cal is
00:02:01.000 um the main thing is it's a tool for
00:02:03.159 deploying apps so if you built an app
00:02:05.200 probably in rails and you want to deploy
00:02:06.880 it to production run it on some servers
00:02:08.599 so that people can use it Cal's there to
00:02:11.080 help you do that uh it's Docker based so
00:02:14.360 it works by building a container image
00:02:16.120 for your app um shipping that out to
00:02:18.840 your servers and then running the
00:02:19.959 container image on your
00:02:21.360 servers um and then importantly for the
00:02:23.959 purposes of this talk it supports what
00:02:25.400 we call gapless deployments don't know
00:02:27.840 may have mentioned this a we a but um
00:02:29.480 what we mean by gapless deployments is
00:02:31.879 if you have a version of your app
00:02:33.040 running in production and uh you deploy
00:02:36.319 an updated version of it then as we're
00:02:38.840 transitioning from the old version to
00:02:40.519 the new version there should be no gaps
00:02:42.879 in service there should be no points
00:02:44.519 where your apps unavailable because the
00:02:46.640 new version is booting or no requests
00:02:48.599 that were errored or dropped uh as a
00:02:50.560 result of the deploy happening it has to
00:02:51.879 be kind of a seamless transition from
00:02:53.440 one to the other um and as we'll see in
00:02:56.200 a minute that's where the proxy layer
00:02:57.440 comes in um and then lastly I want want
00:02:59.959 to acknowledge that despite me squeezing
00:03:01.920 this at one slide Cal does a whole bunch
00:03:03.480 of other things too I'm totally under
00:03:04.840 selling it by trying to put it on one
00:03:06.200 slide but it's just that those other
00:03:08.080 parts aren't things you need to know in
00:03:09.599 order to talk about a proxy which is
00:03:11.159 what I want to talk
00:03:13.200 about okay so the gapless deployments as
00:03:16.400 I mentioned the way they work in Camal
00:03:18.959 is that uh when you have a version of
00:03:21.720 your app to run and you've got the app
00:03:24.000 container built we don't run it on its
00:03:25.959 own on a server we run it alongside or
00:03:28.760 behind a little proxy layer and then all
00:03:31.120 the traffic that's going to come into
00:03:32.560 your app come through the proxy before
00:03:34.360 it gets to the app and that proxy gives
00:03:36.599 us a stable end point so that we can
00:03:39.000 then change things behind behind that to
00:03:41.720 get onto new versions and things without
00:03:43.640 affecting where people access your app
00:03:46.239 if you see what I mean so the process of
00:03:48.599 a deploy is basically boot up a new
00:03:50.680 container with a new version of your app
00:03:53.360 um ask the proxy to switch traffic from
00:03:55.400 one to the other and then you can remove
00:03:57.239 the old version and then it's done and
00:03:59.879 so this proxy here that's what Cal proxy
00:04:02.840 is um in Camal 1 it was a third party
00:04:05.640 proxy called traffic um that we used to
00:04:08.480 use and soal proxy is replacing that
00:04:11.400 which leads to probably the obvious
00:04:13.519 question of why build a new proxy if we
00:04:15.360 had one that was already working um and
00:04:19.280 so there's really two reasons for this
00:04:20.919 the the first one is just an iterative
00:04:23.520 improvement it's just about um trying to
00:04:26.440 do a better job of making these gapless
00:04:28.360 deployments work and so although they
00:04:30.320 work in Cal 1 it was quite complicated
00:04:33.000 to get them to work well um the Cal code
00:04:36.320 has quite a lot of has to handle quite a
00:04:38.360 lot of complicated interactions between
00:04:40.199 itself uh and the proxy to make sure
00:04:42.600 that this this the transition from one
00:04:45.120 version to another happens sort of
00:04:46.960 seamlessly and doesn't do anything wrong
00:04:49.479 um we also found that there are some
00:04:51.600 kind of edge cases um and sharp edges
00:04:54.280 with it where you could occasionally a
00:04:57.320 bit of misconfiguration might lead to
00:04:58.919 traffic not rout where it should or
00:05:00.560 something like that and so as we worked
00:05:02.720 with that setup uh we started to realize
00:05:06.000 that if we were just built our own proxy
00:05:08.639 that's purpose designed to handle this
00:05:10.560 kind of um traffic switch that we need
00:05:12.600 from which is really the only thing we
00:05:14.000 were asking it to do um we could really
00:05:16.440 simplify how this works we could make it
00:05:18.319 a lot more robust um and we could also
00:05:20.639 make it a little bit faster in the
00:05:21.919 process because the proxy would have
00:05:23.280 more knowledge of like the detail of the
00:05:26.919 the requests that were going on at any
00:05:28.479 one point in time we can use that to
00:05:29.880 speed the process up so that was our
00:05:31.680 first reason for building a new proxy um
00:05:35.080 and on its own it seemed like a fairly
00:05:36.479 compelling reason but there's a second
00:05:38.840 reason for it too and the second reason
00:05:41.800 came up actually while working on a
00:05:43.280 different project so if kind of rewind
00:05:46.440 about a year or so ago um I was working
00:05:49.639 on the team that at 37 signals that was
00:05:51.960 looking at building a new line of
00:05:53.720 products called once uh the first of
00:05:55.440 which was once campfire which you
00:05:56.880 probably heard of if you've used the
00:05:58.520 rails word campfire it's one of these um
00:06:02.319 and the idea behind the ones products is
00:06:04.479 that rather than as as an alternative to
00:06:07.639 SAS software um we could instead offer
00:06:11.680 products that we would build and sell
00:06:13.840 you as like a packaged piece of software
00:06:15.800 that you can run yourself on your own
00:06:18.039 server not pay any ongoing um software
00:06:21.560 costs for it and you can kind of run it
00:06:23.080 wherever and and however you want um so
00:06:25.520 it's a bit of a different model to
00:06:27.400 try um but for that idea to be sort of
00:06:30.840 viable and appealing it has to be really
00:06:33.479 easy like you you can't get a piece of
00:06:35.639 software and then have a big long list
00:06:37.599 of things you're going to need to do in
00:06:38.840 order to run in production it has to be
00:06:40.759 more like you buy it you boot it you put
00:06:43.000 it on the internet and everything's
00:06:44.160 working great um so that meant we had to
00:06:47.400 figure out how to get enough performance
00:06:50.160 out of kind of modest Hardware so you
00:06:52.520 didn't have to spend a lot on hosting um
00:06:54.800 but also how to build in answers to some
00:06:57.160 of the things you might normally have
00:06:58.680 done outside of
00:07:00.440 um outside of the app itself so things
00:07:02.479 like setting up SSL certificates or
00:07:05.319 figuring out how to make sure your
00:07:06.759 static files were served quickly and
00:07:08.400 that kind of thing those would all have
00:07:09.919 to be built in um so the way we ended up
00:07:14.639 solving that for campfire was through
00:07:16.199 this Library Thruster which is really
00:07:17.960 just a a little proxy it runs inside the
00:07:21.120 container um with your app next to the
00:07:23.560 rails process and it provides some of
00:07:26.319 those parts that would otherwise have
00:07:27.599 been missing like if you just take a
00:07:28.840 rails app running puma and stick that on
00:07:31.879 the internet um it'll work reasonably
00:07:34.160 well but you'll be missing some things
00:07:35.560 that you would want for it to be kind of
00:07:37.199 to feel fully sort of production ready
00:07:39.639 um so Thruster fills in those little
00:07:41.840 middle parts uh or missing parts
00:07:44.560 sorry so that turned out to work pretty
00:07:46.800 well for for the on product so campfire
00:07:49.319 works like this you can just boot the
00:07:50.680 container stick it on the internet and
00:07:52.879 um get good performance and so on and so
00:07:56.280 this kind of started making us think
00:07:58.080 about the second reason for writing own
00:08:00.479 replacement proxy which was if this is
00:08:03.000 working well for apps like campfire we
00:08:05.280 should make sure and share this in a way
00:08:06.879 that will work well for any rails app
00:08:09.199 that people build so if as soon as you
00:08:11.440 build your app and deploy it it should
00:08:12.879 you should get that same experience of
00:08:14.759 you just boot your app on the internet
00:08:16.360 and everything works the way it should
00:08:18.520 so that led to this the second reason
00:08:21.720 which is as I say was really about
00:08:23.039 making it easier not just to have your
00:08:26.039 app run but to have your app run really
00:08:27.879 well um and so that in rails a by
00:08:32.959 default this is the kind of setup you
00:08:34.519 get now where uh if you make a new app
00:08:37.599 deploy it with Camal you'll have Cal
00:08:40.080 proxy running um kind of in front of
00:08:43.039 your app you'll have Thruster running in
00:08:44.920 the container next to rails and Cal
00:08:47.519 proxy and Thruster will work together to
00:08:49.760 take care of all those sort of missing
00:08:51.279 parts that I mentioned earlier and Sor I
00:08:53.720 realized I'm still being a bit vague
00:08:54.880 about what the missing parts are but
00:08:56.440 we'll get to that in a second
00:09:00.839 um and I yeah I just wanted to also
00:09:02.920 mention that the more we talked about
00:09:05.519 this reason too the more we realized
00:09:07.839 that there's like there's a further
00:09:09.200 version of this that's more of a sort of
00:09:10.560 lofty goal which was not only can we
00:09:13.959 make it easier to to get your apps
00:09:16.320 working with the things that they need
00:09:18.160 but we can also look around to find out
00:09:19.920 what were the other convenient parts
00:09:21.440 that you might have been getting if you
00:09:22.560 hosted your app somewhere else if you
00:09:24.279 used like a Heroku or some kind of
00:09:26.079 platform as a service managed
00:09:27.640 environment kind of thing um if we keep
00:09:31.640 spotting the convenient things in these
00:09:33.839 other environments and bring those into
00:09:35.320 the proxy proxy as well um we should be
00:09:39.600 able to make it so that self-hosting
00:09:41.320 your apps becomes like a better
00:09:43.959 alternative it should be easier to set
00:09:46.000 up or it could be could be more
00:09:48.120 convenient to run with it could be
00:09:49.519 cheaper because you don't have to pay
00:09:51.399 anybody else there's no overhead um you
00:09:54.079 wouldn't have the same kind of lock into
00:09:55.519 a particular platform because it's just
00:09:58.160 everything that you're running just run
00:09:59.519 on a basic Linux server and all of the
00:10:01.600 conveniencies are software that's open
00:10:03.519 sourced that are in your project and you
00:10:05.160 can take wherever um so we have this
00:10:08.120 lofty goal too um where we've reached
00:10:11.079 today I think is we have the answers for
00:10:13.680 reasons one and two today uh the lofty
00:10:16.040 goal is kind of a thing that we're
00:10:17.240 working towards but there's more to do
00:10:19.680 there so in terms of specifically how
00:10:23.000 Cal proxy uh improves gapless deployment
00:10:26.000 so how to how we solved the reason one
00:10:28.320 there um um really there's a few things
00:10:31.399 that are going on but it really boils
00:10:32.680 down to the way that we made it so that
00:10:35.560 Cal can interact with Cal proxy and so
00:10:38.639 with traffic we were working with it
00:10:40.959 with a bit more of a kind of lower level
00:10:42.800 and there was a lot of coordination that
00:10:44.560 had to happen between Cal and Cal proxy
00:10:47.200 proxy to make things work um the way
00:10:50.680 we've redesigned that with Cal proxy is
00:10:54.160 by giving it commands that you can use
00:10:55.880 to instruct it to do things so Cal proxy
00:10:58.639 runs it sort of has two parts it runs as
00:11:01.880 um as a proxy service there kind of a
00:11:04.880 long live process that Roots traffic as
00:11:06.680 it needs to but then there's also a set
00:11:08.800 of CLI commands that come with it that
00:11:11.000 you can run to tell it to do things so
00:11:14.880 um one of the like one of the main ones
00:11:17.800 is the Cal proxy deploy command so when
00:11:20.200 you Cal needs to initiate one of these
00:11:22.519 traffic switches that we saw in the
00:11:23.839 diagram before instead of having to do a
00:11:25.959 lot of back and forth interaction it can
00:11:27.600 just run this one command
00:11:30.320 uh specify the name of the app and this
00:11:33.040 target flag is the um host name of the
00:11:35.639 container that we want to ret trffic to
00:11:38.279 um and when it does that Cal proxy can
00:11:40.200 go through the steps necessary to do the
00:11:41.800 whole switch so it'll wait for that
00:11:43.600 container to be healthy by doing health
00:11:45.560 checks on it um when it's healthy it can
00:11:48.600 start rooting any new requests that come
00:11:50.760 into the container and but while that
00:11:53.279 happens there's probably some inflight
00:11:54.800 requests on the old one so it can watch
00:11:56.839 those and wait for them to complete and
00:11:58.920 then it's in the instant that the last
00:12:00.839 one of those is finished it can return
00:12:03.279 success back to Kamal so Kamal knows
00:12:05.279 that it can at that point destroy the
00:12:07.680 old container and kind of move on with
00:12:09.440 its other tasks um there's a lot more we
00:12:13.880 um kind of flexibility in this deploy
00:12:15.519 commanders other flags and so on that
00:12:17.040 you can pass but really what it boils
00:12:18.760 down to is having this model where we
00:12:21.560 can just instruct the proxy to do
00:12:23.199 something and let it do it and there's
00:12:24.519 not any kind of back and forth trying to
00:12:26.160 coordinate with
00:12:27.839 it so that
00:12:29.680 that was the the reason one for building
00:12:31.279 the proxy uh reason two is really about
00:12:34.279 the features that I wanted to go through
00:12:35.600 and quickly show you and so these are
00:12:37.639 the things that we've tried to add um to
00:12:40.399 just kind of make deployments work a bit
00:12:42.360 better um arranged in the categories
00:12:45.480 that I mentioned at the start um there's
00:12:47.839 nine things that we'll look at today
00:12:49.279 some of them we'll go through pretty
00:12:50.440 fast there's a couple of end that we'll
00:12:52.040 look at in a we bit more detail so I'll
00:12:54.480 just get started going through these um
00:12:58.040 in terms of easy production deployments
00:13:00.120 um you have seen some of this with uh
00:13:02.000 David's keynote this morning uh but
00:13:03.839 we've made it really easy to set up um
00:13:06.000 or to deploy multiple apps onto one
00:13:07.920 server this was kind of surprisingly
00:13:10.079 fiddly to do um with with Cal 1 and with
00:13:13.360 some other deployment tools cuz all of
00:13:15.160 your apps will want to like fight for
00:13:16.680 the same ports um so in Cal proxy we've
00:13:20.600 given it the ability to do host-based
00:13:22.000 routing and so when you deploy an app
00:13:23.760 you could just tell it the host name
00:13:25.360 that your app wants to serve traffic on
00:13:27.880 and then a single instance of the proxy
00:13:29.560 can serve multiple apps and rout the
00:13:31.600 request as necessary to the right one
00:13:33.560 based on their host name um we added
00:13:36.800 automatic https because that's something
00:13:39.480 that almost everybody's going to want um
00:13:42.000 and that's kind of easy to do
00:13:43.560 automatically with light en Crypt so if
00:13:45.440 your app wants https you just have to
00:13:47.320 select that you want it and that will
00:13:49.199 get taken care of
00:13:51.079 automatically um and we also added um
00:13:55.040 buffering of requests and responses by
00:13:57.320 default so having this on in builing by
00:13:59.800 default means
00:14:01.240 that uh the rails process is kind of
00:14:03.880 isolated to some degree from clients
00:14:05.920 that might misbehave in some way so
00:14:07.920 clients that are really slow to send a
00:14:10.199 request or to read a response could tie
00:14:12.519 up a a rails worker for a long time or a
00:14:15.160 client that sends a an oversized request
00:14:18.440 that your app isn't capable of handling
00:14:21.120 uh could cause problems so we have this
00:14:22.440 buffering layer that's on by default
00:14:24.079 that will uh fully buffer the requests
00:14:26.720 on the way in and then the responses on
00:14:28.440 the way out and you can set a size limit
00:14:30.800 if you want to cap the sizes of your
00:14:32.720 requests um so those are kind of just
00:14:35.519 there and kind of automatic to make
00:14:37.079 things a bit
00:14:38.959 easier the uh the next category are
00:14:41.320 things that are there for perform for
00:14:43.360 performance reasons um http2 is is just
00:14:47.959 kind of the way that it's on by default
00:14:49.880 the proxy will Ser all its traffic over
00:14:52.199 http2 as opposed to 1.1 which is what
00:14:55.320 you would get running Puma on its own um
00:14:58.720 that makes a huge difference if you have
00:15:00.360 an app that has a lot of files to
00:15:02.320 download like a an app that has many
00:15:04.440 assets or something http2 is like a lot
00:15:07.440 faster for
00:15:08.959 that um and then the other the other
00:15:11.240 couple of performance improvements we
00:15:12.519 added are really about um making it
00:15:15.199 faster to um to send content from your
00:15:18.320 app um back to back to clients and so
00:15:21.120 we'll look at those in just a wee bit
00:15:22.279 more detail they're the the HTTP caching
00:15:24.279 and this xend file
00:15:26.120 support uh so for HTT caching it's
00:15:29.120 useful because most apps have a ton of
00:15:31.319 things that cach really well so all of
00:15:33.399 your static files like your assets and
00:15:35.399 CSS images and everything um those tend
00:15:38.800 to cach really well you might have also
00:15:40.920 some public pages that don't change much
00:15:42.800 that you could cache um and so the goal
00:15:45.839 here is to offload serving any of that
00:15:47.399 from rails and have it served instead by
00:15:49.680 something that's a bit more uh dedicated
00:15:51.920 for that and can do it in a more
00:15:53.120 lightweight way and do it
00:15:55.040 faster so in rails a by all all asset
00:16:00.079 files that it serves will have cash
00:16:01.560 controls headers set already to say that
00:16:03.720 they're cashable um and Thruster running
00:16:06.560 in your in your container next to the
00:16:09.079 app maintains an in-memory cache of all
00:16:11.360 of the cachable responses that it sees
00:16:14.120 and so without having to do anything to
00:16:15.480 set it up um out of the box asset
00:16:18.160 serving will just be fast in this
00:16:19.880 environment because whenever you deploy
00:16:21.959 a version of your app after the first
00:16:24.160 couple of requests assets will have been
00:16:26.319 sort of requested and pulled into the
00:16:27.920 cach and then served Direct directly
00:16:29.199 from the cach from that point on um but
00:16:32.720 obviously it's not just for assets um if
00:16:34.800 you want to use it on other responses
00:16:36.360 it's again it's just a case of setting
00:16:37.839 the cache control headers that you want
00:16:39.759 and you can use the normal rails helpers
00:16:41.440 for that so if you set something to say
00:16:43.120 expires in seven days then Thruster can
00:16:45.600 cash that for seven days and your app
00:16:47.040 doesn't have to keep serving it so it's
00:16:48.360 useful for things like a login page that
00:16:50.560 isn't changing per
00:16:52.399 user um in terms of how much difference
00:16:55.279 this makes um I did a just to get a data
00:16:58.360 point I I took some measurements running
00:17:00.759 the campfire container on kind of a
00:17:02.160 low-end digital ocean droplet um one of
00:17:05.199 the sort of Cheaper sizes and on that
00:17:08.559 Dynamic Pages served by rails we're
00:17:10.079 going at about 160 requests a second but
00:17:12.959 if I take the same page and Mark it
00:17:14.360 cachable then Thruster can do that at
00:17:16.120 80200 requests a second so it's like a
00:17:18.799 50 times speed up to get once you get
00:17:20.640 something in the cache and obviously
00:17:23.199 that's not a very fair comparison it's
00:17:24.480 not to say one's faster than the other
00:17:26.000 they're just different things and
00:17:27.360 there's a reason that we use Dynamic um
00:17:30.600 that we want to have Dynamic responses
00:17:32.000 that's why we're building rails apps and
00:17:33.280 not static websites um but again I think
00:17:35.960 the point here is just if we identify
00:17:38.320 the parts that don't need to be dynamic
00:17:40.240 and get let the cash serve them then the
00:17:43.360 capacity that you do have for dynamic
00:17:45.120 request on that particular instance goes
00:17:47.520 a lot
00:17:49.760 further xn file is somewhat related in
00:17:52.960 that it's also about being able to send
00:17:54.720 content back more quickly um xnf is a
00:17:58.159 convention where where if you have a if
00:18:01.240 you have a controller action that needs
00:18:02.559 to respond with the contents of a file
00:18:04.600 so if you maybe you have an app that
00:18:06.159 serves up big video files or something
00:18:08.200 that that people can download um rather
00:18:10.760 than rails have to send back all of the
00:18:13.400 content for the file it can just respond
00:18:15.960 with a path name the location on the dis
00:18:17.960 where the file is and then Thruster can
00:18:20.320 pick up on that and take over the work
00:18:21.840 of actually serving the file um this is
00:18:24.760 something you don't really need to think
00:18:26.000 about or there's nothing to do to set it
00:18:27.720 up it's just it's supported and it'll
00:18:29.960 kind of kick in in the situations where
00:18:31.799 it can uh usefully one of those
00:18:34.159 situations is active storage so if
00:18:36.000 you're using active storage um to store
00:18:38.559 content on a file system that the
00:18:40.400 container can access your app container
00:18:43.159 um then the xn file support means that
00:18:46.080 serving those files will be handled
00:18:47.600 outside of rail for you and so they'll
00:18:49.880 the files will be uh served faster but
00:18:52.200 also it'll free up more time from your
00:18:54.720 uh from your rails
00:18:57.520 workers okay um and so then the last
00:19:00.520 category of things I want to look at
00:19:02.039 were features that are there to make it
00:19:04.600 easier to manage apps once they're in
00:19:06.760 production or to do things that would be
00:19:08.559 useful um these are the parts that I
00:19:10.799 think are more interesting and it's
00:19:12.200 where we can I think start to see how
00:19:15.000 having um a custom proxy lets us build
00:19:17.240 some more conveniencies in that that we
00:19:19.520 wouldn't normally
00:19:20.919 have so the the first of these is is
00:19:23.600 having an easy way to put up a
00:19:24.760 maintenance page uh it's pretty common
00:19:27.240 if you're running an app in production
00:19:28.440 that from time to time you you'll have
00:19:30.640 to take it take it down for a little
00:19:33.000 while to do something like to do some
00:19:34.520 kind of upgrade or um change some
00:19:36.520 infrastructure or something um and
00:19:38.880 you'll want to put up a maintenance page
00:19:40.280 to let people know what you're doing so
00:19:42.559 we made a proxy command for it you can
00:19:44.760 call Cal proxy stop with the name of
00:19:46.840 your app and the proxy will immediately
00:19:48.960 just start serving a temporarily
00:19:50.400 unavailable page for you um to so that
00:19:53.480 to show that your app's down it's kind
00:19:55.520 of a generic page because obviously we
00:19:57.159 don't know anything about your app or
00:19:58.840 the reason that you're stopping it um
00:20:00.799 but you do have the option to pass in a
00:20:02.360 message when you call this and wherever
00:20:04.520 you passes the message will be displayed
00:20:06.159 on that page so like in this example you
00:20:08.640 could put a note to say when your
00:20:10.240 schedule maintenance is going to be done
00:20:12.600 um if you want to change these mors
00:20:15.080 change these more um you can actually
00:20:17.679 Supply your own HTML templates for the
00:20:19.760 proxy to use instead so you can brand
00:20:21.880 these to suit your app um and that's on
00:20:24.080 a Pera basis so if you're using the
00:20:25.679 feature to deploy multiple apps on the
00:20:27.280 same server you can give them all their
00:20:28.960 own sets of pages and have them look
00:20:30.760 like your
00:20:32.400 app uh and then when you're done with
00:20:34.280 your maintenance there's a resume
00:20:35.400 command which is the opposite of stop it
00:20:36.880 just brings your app back into
00:20:39.799 Service uh the second feature in this
00:20:42.000 category is request pausing this is kind
00:20:43.720 of related because it's also about times
00:20:45.520 when you need to do a bit of Maintenance
00:20:47.600 but it's for the cases where the
00:20:49.520 maintenance is um is not going to take a
00:20:52.280 long time um so an example of this might
00:20:54.480 be that you might have to reboot a
00:20:55.679 database maybe you've applied a security
00:20:57.360 patch or something but you have to Res
00:20:58.640 start like a postgrad server or
00:21:00.280 something to pick up those changes um
00:21:03.000 you can't just do that live I mean you
00:21:04.720 could do that live but people will see
00:21:06.240 errors from your app where for the time
00:21:08.360 where rails can't connect to the
00:21:09.679 database because it's restarting um so
00:21:12.960 an alternative to having a downtime for
00:21:14.640 this is if you don't think it's going to
00:21:16.559 take very long you can ask the proxy to
00:21:18.320 just hold on to all the requests that
00:21:20.520 are coming in and sort of let them queue
00:21:22.080 up until your app's ready for them and
00:21:23.960 then it kind of finishes playing them
00:21:25.520 through um so the the sequence of
00:21:27.760 commands would be something something
00:21:29.039 along lines of this so if I was going to
00:21:31.400 restart a post dat database I might use
00:21:33.720 something like this pgct restart command
00:21:37.159 so if I do a proxy pause first then my
00:21:39.480 restart and then a resume then in the
00:21:41.720 time between the pause and the resume
00:21:44.000 there will be no request actually
00:21:45.360 flowing into my app they'll just be sort
00:21:46.960 of kind of queed up for a moment and
00:21:49.080 then played through um this one I find a
00:21:52.360 we bit hard to to describe how it would
00:21:54.760 look so I've got a little video to show
00:21:56.440 what it looks like from someone using an
00:21:57.880 app um so this window on the right is
00:22:00.720 just a a campfire instance running which
00:22:02.559 is deployed behind Cal proxy it's also
00:22:06.120 an excuse from be able to show a picture
00:22:07.480 of my cat in the talk um but you can see
00:22:10.799 it's working normally there but if I hop
00:22:12.760 over to this terminal and pause the
00:22:14.960 service at the proxy level then the next
00:22:17.159 request is you'll see the loading bar
00:22:19.440 because it sort of it hasn't been able
00:22:20.760 to go through yet but as soon as I then
00:22:22.880 resume the service the request finishes
00:22:25.679 so no one saw an error but we had a few
00:22:27.600 seconds there where we could do stuff
00:22:28.919 behind the scenes where the app was kind
00:22:30.200 of
00:22:32.600 down um and then the last feature that I
00:22:35.400 wanted to talk about is roll out so roll
00:22:38.679 out the idea behind roll out is you can
00:22:41.120 deploy a an alternate version of your
00:22:43.760 app alongside the main version and then
00:22:46.159 you can use some kind of traffic split
00:22:47.840 to send just a certain portion of your
00:22:49.960 traffic into this alternate version this
00:22:52.200 rollout version we call it um so it's
00:22:54.679 useful for things like uh a canary
00:22:57.360 deployment or if you want to try out
00:22:59.760 some kind of uh like a library upgrade
00:23:02.080 or something and you want to see what
00:23:03.200 effect it's going to have you could
00:23:04.159 deploy it to a small portion of your
00:23:05.640 users or maybe just to your own account
00:23:07.360 or something like that uh before you
00:23:09.159 roll it out more
00:23:10.520 widely uh it works by having the proxy
00:23:14.120 um identify the requests that come in to
00:23:16.360 know who they belong to and then to use
00:23:18.760 whatever um traffic split rule you've
00:23:21.159 told it to decide where to send those
00:23:23.559 requests on um and so for this to work
00:23:27.000 it does need a way to know which request
00:23:29.120 like it know sorry it needs a way to
00:23:31.640 know who each request belongs to um so
00:23:35.120 the way you do that is just have your
00:23:36.240 app set a cookie with a value that makes
00:23:38.440 sense for your app whatever you put in
00:23:40.480 there that's sort of the the unit that
00:23:42.960 your roll out can be controlled by if
00:23:45.039 you see what I mean so if your app has a
00:23:47.919 notion of accounts then account ID is
00:23:49.799 probably a good um a good thing to put
00:23:52.600 in there because then you can say Which
00:23:55.279 percentage of accounts should go to roll
00:23:56.760 out or which specific accounts
00:23:59.000 um other values that would make sense in
00:24:01.039 there might be things like uh user IDs
00:24:04.039 or geographical regions or um or
00:24:07.559 something like that um and so as I
00:24:10.440 mentioned once it's set you can control
00:24:12.320 the split either a percentage in which
00:24:14.240 case they're sort of um randomly
00:24:16.720 allocated if you say 5% of accounts then
00:24:19.120 it's going to be a consistent random 5%
00:24:21.880 of all the accounts um or you can use
00:24:24.799 specific ones um we use this approach a
00:24:28.039 lot uh at37 signals actually with a
00:24:30.200 different mechanism we use it a lot on
00:24:31.399 base camp which doesn't use Cal proxy it
00:24:33.760 has its own roll out implementation that
00:24:35.919 predates this and is kind of where the
00:24:37.760 inspiration for this feature came from
00:24:40.520 uh but we use it a lot to do things like
00:24:42.279 if we want to um change the Ruby version
00:24:45.760 that we're running against you don't
00:24:47.000 want to just kind of change that and
00:24:48.320 deploy it to everybody without having a
00:24:51.120 better look at the effect it's going to
00:24:52.760 have and so a roll out feature lets us
00:24:54.520 make these kind of changes um in a more
00:24:56.720 controlled manner before we do a full a
00:24:58.679 full roll
00:25:00.360 out and uh so like the other features
00:25:03.520 this is also driven by a set of proxy
00:25:05.360 commands there's there's a subcommand
00:25:07.159 called roll out um so you can call
00:25:09.520 things like Cal proxy roll out deploy
00:25:11.559 rather than Cal proxy deploy um the
00:25:14.159 rollout deploy version is a way to
00:25:15.960 register the container that should get
00:25:18.279 roll out traffic but it doesn't yet send
00:25:20.399 any traffic to it um for that you've got
00:25:23.360 Cal proxy roll out set which is where
00:25:25.360 you can give it something like in this
00:25:26.880 example perent 15 just means send 15% of
00:25:30.039 the accounts if I'm using account as my
00:25:32.320 cookie value it we send 15% of accounts
00:25:34.640 into to roll out and the other 85 um go
00:25:37.480 to the regular container um if you want
00:25:40.440 to isolate specific accounts you could
00:25:42.320 use a list form so you could say that
00:25:44.159 account IDs 729 and 30 are the only ones
00:25:46.679 getting roll out and nobody else
00:25:49.159 is and then when you're done with your
00:25:51.039 roll out experiment you can just call
00:25:52.480 roll out stop and that resets everything
00:25:54.320 all your traffic goes back to your main
00:25:55.640 container and you're kind of back where
00:25:57.320 you started
00:26:00.640 so that's all the features that I wanted
00:26:02.399 to go through today um these all exist
00:26:05.799 now at the at the proxy level and some
00:26:09.240 of them are things that are built in and
00:26:11.120 just work so things like xend file or
00:26:13.640 the request response buffering they're
00:26:15.320 just features that happen you don't have
00:26:16.799 to think about them um some of them are
00:26:19.720 things that you access through Camal so
00:26:22.159 things like Mel plaps per server are
00:26:24.399 there because they're present in the Cal
00:26:27.039 config that's who use those
00:26:29.000 and then a couple of the ones at the end
00:26:30.919 at the moment only exist in the proxy
00:26:32.919 we've not yet surfaced them in Cal so if
00:26:35.159 you wanted to use them now you can you
00:26:36.960 would just be sending the Cal proxy
00:26:38.480 commands directly um but there are
00:26:41.279 things that soon we will have them at
00:26:42.600 the Cal level So Cal will let you to
00:26:44.520 kind of do all the things in one
00:26:47.440 place uh so those are all the features I
00:26:49.480 want to look at um and then as I say the
00:26:52.039 one thing I want to mention at the end
00:26:53.840 was what other things we're starting to
00:26:55.960 look at to add next to it um I think the
00:26:59.919 the goal here is mainly will be to
00:27:02.320 continue to look for things that would
00:27:04.520 be super convenient to have especially
00:27:06.120 if there were things you would have
00:27:07.039 gotten in another environment and would
00:27:09.200 make you feel like self-hosting would be
00:27:10.760 a step backwards I think spotting those
00:27:13.320 types of things in C if we can build
00:27:14.640 them in is kind of the mission for the
00:27:16.960 for the next we
00:27:18.279 while um specifically two things that
00:27:20.960 we've started to look at now that may
00:27:22.240 come next I think one is load balancing
00:27:25.480 so right now if you have an app that
00:27:27.399 runs across many hosts um Cal does a
00:27:30.840 great job of deploying across those
00:27:32.399 hosts but it doesn't have an answer for
00:27:34.240 how you would load balance your traffic
00:27:35.679 amongst them you sort of bring your own
00:27:37.240 load balancer in that situation um but I
00:27:40.399 think we can we can do this probably
00:27:42.000 quite effectively at the proxy level so
00:27:43.840 the proxy can serve as a load balancer
00:27:45.640 between hosts um that would give us the
00:27:48.519 ability to like we can make it very easy
00:27:50.880 for you to have a new commale deployment
00:27:52.840 where you have multiple hosts in there
00:27:54.640 you deploy it and you're provided with
00:27:57.200 everything from the
00:27:58.760 ingestion point with its SSL
00:28:00.440 certificates and the load balancing and
00:28:02.120 then all the deployments and all the
00:28:03.080 host like we can do kind of the whole
00:28:04.240 picture there which I think would be
00:28:05.720 kind of neat um so that that's
00:28:08.039 definitely something we're looking at
00:28:09.159 and then the other one is um websocket
00:28:11.880 acceleration um got some ideas there
00:28:14.159 about since the traffic is already
00:28:16.480 flowing through the through axy before
00:28:18.720 it gets to your app I think there's some
00:28:20.799 cool things we could do by intercepting
00:28:22.559 that for websocket traffic so if you
00:28:24.360 have an app that does um a lot of real
00:28:27.279 time act real time activity a lot of
00:28:28.960 action cable type stuff we can probably
00:28:30.640 make that faster as well in the same way
00:28:32.320 that we've been looking for ways to make
00:28:34.120 the uh regular HTTP content
00:28:37.799 faster cool so that's everything I want
00:28:39.960 to go through today thanks for listening
00:28:42.159 um if you want to talk about proxies
00:28:44.200 please send me an email thanks
Explore all talks recorded at Rails World 2024
+31