00:00:10.040
Hello everyone, let's start the presentation. Thank you for coming.
00:00:15.360
Welcome to this engineered sponsored talk. I'm Allan Espinosa, and I’m a Support Engineer at Engineered. Today, I'll be discussing various aspects of deploying Rails applications with Docker.
00:00:23.269
My background is primarily in operations engineering. For the past few years, I've mostly worked with Ruby in my day-to-day work. I used Ruby back in the day when it was more popular for building systems before Go became prevalent. However, Ruby still remains my go-to language.
00:00:39.780
Though I’m not strictly a Rails developer, my last experience with Rails was six or eight years ago. I do a lot of Ruby development outside of web applications, and I'm also the author of the 'Docker High-Performance' book from Packt Publishing.
00:01:07.650
Given that Docker changes frequently, much of the book's content may become obsolete in the coming months. While writing the book, I attempted to identify concepts that would endure through Docker updates, and I’ll share some of those in this session.
00:01:28.200
Specifically, I'll be addressing optimization techniques for Docker images and tailoring it for Rails deployment. It's essential to consider how to optimize the rollout of Rails applications in Docker.
00:01:49.829
When we talk about optimization, many of us think primarily about achieving faster response times or ensuring that our controllers respond quickly. While asynchronous workers help with speed, it’s important to remember that performance ultimately comes down to improving the overall customer experience.
00:02:41.380
By tracing the value stream from our customers, we can begin refactoring controllers and business logic based on feedback from direct production traffic.
00:03:07.080
Additionally, we can optimize the middleware and fine-tune our server settings, such as configuring Unicorn workers or Puma threads, to better utilize memory allocation.
00:03:42.760
Likewise, optimizing SQL queries can lead to faster database responses. All these tuning techniques should not be performed in isolation; rather, they should be informed by instrumentation data gathered from your application and infrastructure.
00:04:12.580
Integrating effective logging mechanisms and application metrics helps gauge user interactions. Even tools like Google Analytics can provide valuable insights that can be correlated with system metrics, subsequently informing scaling decisions.
00:04:24.940
By scaling the application to meet customer demand and enhancing the overall architecture with caching and increased capacity, it becomes crucial to understand when it's appropriate to spin up new instances of your Rails application.
00:04:51.390
Establishing a well-defined operational strategy is vital for optimizing deployment techniques. Therefore, while refining your application for improved performance is essential, it’s equally important to ensure that your deployment processes are efficient.
00:05:15.199
A slow deployment process can render any optimization efforts moot by the time they are implemented. Thus, in this talk, I'll focus more on expediting the software delivery process to production.
00:05:45.110
Despite concentrating on the delivery aspect, the underlying concepts I discuss will be applicable across various paradigms. Many early adopters have embraced the hype surrounding containers, and we are now starting to refine our practices.
00:06:17.210
Ultimately, while it's beneficial to package applications in Docker containers, we must always focus on the application's inherent value. Docker is merely a tool that enhances how we deliver our products.
00:06:49.030
Earlier, I spoke to several attendees at our booth at Engineered about their usage of Docker. Many users shared that they were just beginning their journey with Docker, often facing resistance when trying to implement it in production environments.
00:07:12.470
As an operations engineer, I can understand the hesitancy surrounding changes to the technology stack. However, knowing what changing to a container-based architecture means could help alleviate these concerns. Ultimately, it all comes down to how effectively you can deliver your application.
00:07:34.360
When discussing application delivery, we typically consider the deployment phase, but it’s essential to recognize the build phase as well. It's common to perceive the build phase as compiling code into binaries, such as those created with C or Go.
00:08:14.690
This perspective might seem counterintuitive for Rails and Ruby developers since Ruby is interpreted. However, in Rails, there is an equivalent to binaries, which includes everything necessary for the application to run in a production environment.
00:08:36.590
Understanding what components will be deployed is critical. For instance, when I am paged at 3:00 a.m., I need to know where to look for issues. In Rails applications, your gem packages play a crucial role in defining these components.
00:09:09.380
When you perform a 'gem install' in production, it's important to remember that there may be native bindings and shared object files involved. Essentially, the code in your Rails app forms a part of the binary alongside dependencies resulting from your 'gem install' or 'bundle install' commands.
00:09:40.480
Furthermore, Rails assets also comprise components of what it takes to run your application. Consequently, Docker provides an effective way to bundle these elements together.
00:09:58.360
The notion of a Docker image encapsulates everything required for your application.
00:10:00.550
During the Docker build process, you specify in the Dockerfile how the image will be constructed. For those just entering this field, I'll outline a basic Dockerfile for defining your Rails image. You typically start from an official Ruby image, such as Ruby 2.2.
00:10:42.900
Next, include your application's current directory and all files it requires, execute a 'bundle install' to resolve dependencies, and compile any necessary assets. Lastly, define how to run your application, which in a production setting should use Unicorn, Passenger, or Puma instead of WEBrick.
00:11:04.400
For example, when you run the Docker build command while naming your image, you can observe the process of compiling the Docker image. Initially, the Docker build command adds necessary directories as well as dependencies by executing 'bundle install'.
00:11:47.770
If you encounter native bindings during this process, the build could take a bit longer due to compilation. The initial build could take about a minute and a half to complete, but a crucial feature within Docker is the build cache.
00:12:56.120
When you run the same build without changing any code, Docker utilizes caching, allowing the process to finish in just one second. If adjustments are made to the application’s code, however, the build takes longer to finish as Docker creates new image layers.
00:13:15.590
This may not be a concern early on, but as your teams grow or if significant refactoring takes place, running 'bundle install' frequently can become a dragging task.
00:13:48.070
One effective solution is to optimize your Docker builds by organizing your application into separate sections based on how often they need updates. For example, you can separate your Gemfile from other application files to exploit caching more effectively.
00:14:15.200
After all, the strategy should be similar to dividing your unit and integration tests. Unit tests are generally quicker, while integration tests might require additional resources.
00:14:57.720
The initial build might take the same time, around a minute and a half, but if subsequent changes are limited to the application code, the build can complete as if nothing had happened, significantly improving efficiency.
00:15:31.080
The main takeaway when it comes to building your Rails application is to be as prompt as possible in receiving feedback regarding the readiness of the artifacts or binaries for deployment.
00:16:09.950
After constructing the Docker image, it's important to run it through a series of tests, such as unit tests and integration tests, in your delivery pipeline before marking it as good to go.
00:16:32.990
Upon confirming that the image is suitable for production, the next stage involves deployment. I've observed a phenomenon where many teams associate compiling with deploying, which can lead to underestimating how long deployments might take.
00:17:07.750
Customers have reached out for support, expressing frustration over deployment durations of around 30 minutes. If responses are slow during this process, the feedback loop is delayed, preventing teams from understanding whether their changes benefit users.
00:17:27.360
In my talk, I will outline several factors that can lead to deployment delays and suggest ways to improve efficiency, particularly as an operations engineer.
00:17:43.950
I particularly dislike manual deployment processes where one would log into the server, pull the latest code, and run 'bundle install.' While some might argue for parallel execution across the fleet, issues arise when large changes are introduced.
00:18:07.600
Implementing a full redeployment aspect limits how much can be updated at once, thereby hindering the process. In contrast, deploying Docker images simplifies the task.
00:18:33.050
By executing a 'docker pull' command, one simply downloads the latest image from the repository. The deployment workflow becomes more straightforward, where the focus narrows to the speed of downloading from the Docker registry.
00:19:05.360
It’s important to note that in this scenario, relying on other sources such as RubyGems can be slower compared to Docker Hub.
00:19:29.960
Furthermore, while many packages make our applications function, we ultimately hold responsibility for the availability of our services. An insightful website that became famous in the tech community is WhoOwnsMyAvailability.com. It provides random articles about availability and reliability.
00:20:04.520
This site succinctly captures the essence of being accountable for your application's uptime and emphasizes the importance of understanding various dependencies.
00:20:39.500
For instance, customers rely on our services to maintain uptime, and we in turn depend on external services to provide our functionality. If we utilize RubyGems or depend on proprietary Debian mirrors, we expose ourselves to potential inconsistencies.
00:21:13.610
Integrating Docker introduces another layer of dependency by relying on Docker Hub for images. This situation raises concern among operations teams as it can complicate deployment processes that might already be fragile.
00:21:43.840
To mitigate this, it's beneficial to vendor dependencies whenever feasible, ensuring that our deployment process remains resilient regardless of any external service failures.
00:22:06.910
Incorporating proxies into your environment may also aid significantly. Developers often dislike configuring proxy settings, which creates friction.
00:22:34.490
Recently at the conference, Jamie detailed the importance of understanding operational challenges and empathizing with different teams.
00:22:50.989
In my personal setup, I have multiple proxy servers running to streamline development. This includes a mirror for my application, one for Docker registry, and another for RubyGems, allowing for efficient software development without the constant need to download images.
00:23:34.620
By utilizing proxy services like Nexus or Artifactory for Docker images, I can alleviate dependency on external sources. As a result, I can more easily manage installations, thereby speeding up development as opposed to repeatedly pulling external resources.
00:24:32.140
Such provisions apply equally to databases. If your primary database is down, your Rails application should ideally maintain some level of functionality by reading from a secondary, perhaps read-only, source.
00:25:05.200
Thus, graceful degradation is crucial. Measures should be in place to allow your application to provide service in a reduced capacity during failures.
00:25:34.010
In conclusion, while Rails and containers clarify much of the complexity away, having a good and reliable infrastructure is fundamental. Building applications upon a shaky foundation will lead to issues. Although Ruby and Rails inspired and attracted many into the field, understanding the underlying principles of what we use can prepare us to adapt when changes occur, especially during deployments.
00:26:48.030
Developing the ability to troubleshoot effectively during crises is essential. Knowing when problems arise and how to resolve them will ultimately lead to more operable, reliable application environments.
00:27:25.180
By understanding these concepts, we can focus on serving the actual needs of our users without compromise. Thank you for listening to my talk, and if you have any questions, please feel free to ask.
00:27:50.880
Also, if you visit our Engineered booth, we have limited copies of my Docker book available. I would love to chat about your experiences using Docker or encouraging management to adopt it.