
Deploying a Rails App to Kubernetes π’
- Shem
- February 2, 2021
Deploying a Rails App to Kubernetes π’
Hey there! πββοΈ Ever wondered about the best way to deploy your Ruby on Rails app? Well, one popular method is using Docker containers and Kubernetes for orchestration. This guide is here to show you why Kubernetes might just be your new best friend compared to other deployment strategies. π
We’re all about keeping things real and practical, focusing on getting your app up and running in production using containers. π¦ No fluff, just the essentials covering deployment, load balancers, environment secrets, asset compilation, database migrations, and more. Whether it’s keeping those servers humming with background tasks or ensuring your app’s uptime with monitoring and updates, we’ve got you covered.
What’s on the Menu? π½
- A bit of history and why Kubernetes could be better than other options
- The essentials: Git, Docker, and that Kubernetes magic
- All the tech goodies you need to know: Domain setup, SSL, environment secrets
- The how-to’s: Logging, background jobs, console commands, and those inevitable database migrations
- Keeping things smooth with continuous delivery and monitoring
- Staying secure with regular updates
And because we like to keep things straightforward, we’ll also introduce you to Cuber - your shortcut to deploying apps on Kubernetes with ease. π
Why Not Just Heroku or DigitalOcean? π€
Heroku is super easy for deployment and scaling but can get pricey as you grow. Plus, you’re kinda at the mercy of their platform. On the flip side, managing your own servers on IaaS like DigitalOcean gives you more control but requires more effort as you scale. Enter Kubernetes: the best of both worlds, offering PaaS-like simplicity with IaaS-level control, all while being open source and widely supported. π
Stick around as we dive deep into deploying your Rails application in production with Kubernetes. It’s going to be a fun ride! π’
Alright, let’s keep the vibe going with the next section in the same casual and emoji-filled style! Here’s your Markdown:
A Stroll Down Memory Lane: Kubernetes and Its Alternatives π€
When it comes to deploying Rails apps, the path of least resistance often leads us to PaaS solutions like Heroku. It’s like being on cruise control: easy deployment, scaling at the push of a button, and not a server worry in sight. π But here’s the rub:
- As your app grows, so does the price tag. πΈ It might just start feeling a bit too rich for your blood.
- Ever felt like you’re not in the driver’s seat of your app? With PaaS, you’re handing over the reins, which might give you pause about uptime and control.
- PaaS can feel a bit like a golden cage: comfy but with limits on how you can stretch your legs.
- And then there’s the “all your eggs in one basket” scenario. If your app’s tied to one platform, moving it can be a real headache.
Enter IaaS, like DigitalOcean, the DIY kit of the internet world. Start with one server and scale up as needed. Sounds great, right? Until you realize you’re the one juggling servers, load balancers, databases, and those night-shift workers (aka background jobs). π€ΉββοΈ Sure, you’ve got more control, but also more plates spinning:
- Getting off the ground takes some serious know-how and time.
- Updating and managing an army of servers? Ouch.
- Mistyped a command? Good luck rolling that one back across your fleet.
- Consistency is key, but also a massive headache when you’re manually managing configs across multiple servers.
- And as for scaling, well, that’s a manual treadmill you’re running on.
So, Why Kubernetes? π€
Kubernetes steps into this tangle with a swagger, offering the ease of PaaS while letting you keep the control you love about IaaS. It’s like having your cake and eating it too, but without the calorie guilt. It’s open-source, backed by cloud giants, and basically everywhere. Whether you’re in Camp “Roll Your Own Server” or Camp “Let Someone Else Handle It,” Kubernetes is waving at you with a friendly, “I got this.”
Getting Ready for the Big League π
Before we dive into the Kubernetes pool, let’s make sure you’re geared up:
- Web Development Savvy: Check! You’ve got the basics down.
- The Right Tools: Ruby, Rails, Git, Docker… all installed and ready to rock.
- Cloud Accounts: Docker Hub for your images, and DigitalOcean (or your preferred cloud service) for Kubernetes action.
- Your Rails App: Got an app? Great. Need one?
rails new kubernetes-rails-examplewill kickstart a new project. Add a simple homepage to say “Hello, world!” to the Kubernetes world.
Git to It! π±βπ»
Version control is non-negotiable, folks. Save your progress with Git, push it to GitHub, and bask in the glory of CI/CD tools automagically building your Docker images whenever you unleash new code into the wild.
Cooking Up Your Docker Image π³
First things first, let’s package your app into a Docker image. Think of a Docker image as a box that contains everything your app needs to run - from the code itself to all the dependencies and environment settings. π¦
Whip Up a Dockerfile
In your Rails app’s root, start with a Dockerfile. This magic file is like a recipe that tells Docker how to build your app’s environment. Here’s a simple one to get you started:
FROM ruby:2.5
RUN apt-get update && apt-get install -y nodejs yarn postgresql-client
RUN mkdir /app
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install
COPY . .
RUN rake assets:precompile
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
This Dockerfile does a few things:
- Grabs a Ruby image to use as a base.
- Installs Node.js and Yarn because, let’s face it, we need some JavaScript in our lives.
- Sets up the app directory and copies your code into the image.
- Installs your gems, precompiles your assets, and gets ready to launch your app on port 3000.
Build and Run π
Once your Dockerfile is ready, build the image with a tag so you can easily find it later:
docker build -t username/kubernetes-rails-example:latest .
Then, run your image as a container to make sure everything’s looking good:
docker run -p 3000:3000 username/kubernetes-rails-example:latest
Visit http://localhost:3000 and you should see your app waving back at you. π
Share Your Creation π
Before we move on to Kubernetes, let’s push your image to Docker Hub or another registry of your choice:
docker push username/kubernetes-rails-example:latest
Setting Up Your Kubernetes Orchestra π»
Kubernetes is where the real magic happens. It’s like the conductor to your Docker image orchestra, ensuring every container plays its part in harmony.
Cluster Setup
Head over to your favorite cloud provider and spin up a Kubernetes cluster. For this guide, we’re going DigitalOcean style.
Once your cluster is ready, grab the kubeconfig file to connect kubectl to it:
export KUBECONFIG=~/.kube/kubernetes-rails-example-kubeconfig.yaml
Checking In With Your Cluster
Make sure kubectl is installed and say hello to your cluster:
kubectl version
Take a look at the nodes in the cluster:
kubectl get nodes
Deploying Your App
Now, let’s get your app running on Kubernetes. You’ll need to create a deployment configuration and a load balancer to expose your app to the world.
Deployment Config
In your Rails app, create a config/kube/deployment.yml that describes your app’s deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-rails-example-deployment
spec:
replicas: 4
selector:
matchLabels:
app: rails-app
template:
metadata:
labels:
app: rails-app
spec:
containers:
- name: rails-app
image: username/kubernetes-rails-example:latest
ports:
- containerPort: 3000
This config tells Kubernetes to run 4 instances of your app, using the Docker image you created.
Load Balancer
Next, create a config/kube/load_balancer.yml to direct traffic to your app:
apiVersion: v1
kind: Service
metadata:
name: kubernetes-rails-example-load-balancer
spec:
type: LoadBalancer
selector:
app: rails-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
name: http
This setup listens on port 80 and forwards traffic to your app’s containers.
Apply Your Configs
Time to deploy:
kubectl apply -f config/kube
Check on your pods:
kubectl get pods
And find your app’s external IP:
kubectl get services
Your app should now be live! π
Making Your App Official with Domain Names and SSL π
Your app’s ready to meet the world, but let’s be real, nobody wants to type in an IP address. It’s time to hook up a domain name and get that shiny SSL certificate for secure connections. Here’s the lowdown:
Setting Up Your Domain
- Head over to your DNS provider and add an A record:
example.com. A 192.0.2.0
Replace example.com with your domain and 192.0.2.0 with your load balancer’s external IP (grab it using kubectl get services).
- SSL Certificates: The easiest route on platforms like DigitalOcean? Use their dashboard to configure SSL for your load balancer. π No fuss, no muss.
Environment Variables & Secrets: Keeping Things Tidy π€«
Managing environment variables and secrets correctly is super important for keeping your app’s configuration flexible and secure.
Environment Variables
With Kubernetes, you’ve got options:
- Set them in your Rails config.
- Include them in your Dockerfile.
- Or, my recommendation, use Kubernetes for production. It lets you update variables without a full rebuild and even inject some dynamically based on your environment.
Here’s how to set an environment variable in Kubernetes:
env:
- name: EXAMPLE
value: "This env variable is defined by Kubernetes."
Add that snippet to your deployment.yml under the container specs. Then apply your changes with kubectl apply -f config/kube.
Secrets
When it comes to secrets, think passwords or API keys; you want them encrypted and away from prying eyes. Use Rails credentials for your app secrets and Kubernetes secrets for sensitive keys like the Rails master key.
To set a secret in Kubernetes:
kubectl create secret generic rails-secrets --from-literal=rails_master_key='your_master_key_here'
And reference it in your deployment like so:
env:
- name: RAILS_MASTER_KEY
valueFrom:
secretKeyRef:
name: rails-secrets
key: rails_master_key
Logging: Keeping an Eye on Things π
There are two paths you can take for logging:
- Direct logging: Send your Rails app logs straight to a service like Logz.io. Simple and straightforward.
- Container logging: Let Kubernetes handle it by logging to stdout. Then, use something like Fluentd to forward these logs to a centralized logging service.
For the latter, Kubernetes makes it easy to collect and query logs with tools like Fluentd, a CNCF project that can funnel your logs to places like ELK for easy searching and monitoring.
To set up Fluentd:
kubectl create secret generic logzio-logs-secret --from-literal=logzio-log-shipping-token='MY_LOGZIO_TOKEN' --from-literal=logzio-log-listener='MY_LOGZIO_URL' -n kube-system
kubectl apply -f https://raw.githubusercontent.com/logzio/logzio-k8s/master/logzio-daemonset-rbac.yaml
Check your logs after setup to ensure everything’s running smoothly. If you’re curious about what’s happening under the hood, kubectl logs -l app=rails-app will show you the latest from your application.
Powering Through with Background Jobs and Sidekiq π
Background jobs are the unsung heroes of Rails applications, quietly working behind the scenes. When it comes to Kubernetes, running Sidekiq to handle these jobs is a breeze. Let’s dive in!
Integrating Sidekiq
First, add Sidekiq to your Gemfile:
gem 'sidekiq'
Then, install it by running bundle install. Next, craft a worker that does the heavy lifting:
# app/jobs/hard_worker.rb
class HardWorker
include Sidekiq::Worker
def perform(*args)
# Do something amazing
Rails.logger.info 'It works!'
end
end
Kick off a job in your controller like so:
HardWorker.perform_async
Deploying Sidekiq on Kubernetes
Now, for the Kubernetes magic. We’ll set up a dedicated deployment for Sidekiq:
# config/kube/sidekiq.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sidekiq
spec:
replicas: 2
selector:
matchLabels:
app: sidekiq
template:
metadata:
labels:
app: sidekiq
spec:
containers:
- name: sidekiq
image: username/kubernetes-rails-example:latest
command: ["sidekiq"]
env:
- name: REDIS_URL
value: redis://redis.default.svc.cluster.local:6379/
This config tells Kubernetes to run two Sidekiq workers, ensuring your jobs are processed smoothly. Don’t forget to apply your config:
kubectl apply -f config/kube
Setting Up Cron Jobs
Cron jobs in Kubernetes can either be handled natively or through Ruby. Hereβs how to keep your tasks scheduled on time:
- Kubernetes CronJobs: Great for containerized tasks but requires a separate config for each job.
- Ruby Process: Use gems like
sidekiq-cronto schedule jobs directly in your app.
To use sidekiq-cron, add it to your Gemfile and create a job schedule:
# Gemfile
gem 'sidekiq-cron'
# config/initializers/sidekiq.rb
Sidekiq::Cron::Job.load_from_hash YAML.load_file('config/schedule.yml') if Sidekiq.server?
Define your cron jobs in config/schedule.yml:
my_first_job:
cron: "* * * * *"
class: "HardWorker"
Now, your jobs will run like clockwork. β°
Kubernetes Console Access π₯
Sometimes, you just need to get hands-on with your running containers. Here’s how:
kubectl exec -it my-pod-name -- /bin/bash
This command drops you into a bash session inside your pod. For tasks and Rails consoles, this is invaluable.
Maintenance Pods
For regular maintenance, consider a dedicated pod:
# kube/config
apiVersion: v1
kind: Pod
metadata:
name: terminal
spec:
containers:
- name: terminal
image: username/kubernetes-rails-example:latest
command: ['sleep']
args: ['infinity']
Apply it, and then run commands or Rails consoles as needed:
kubectl exec -it terminal -- rails console
Running Rake Tasks
For rake tasks, you have two options:
- Kubernetes Jobs: Perfect for one-off tasks.
- Direct exec: Jump into a pod and run your task.
Here’s how to execute a rake task directly:
kubectl exec my-pod-name -- rake task-name
Smooth Database Migrations Without the Drama π οΈ
Migrating your database when rolling out new features can be a bit of a tightrope walk. You want zero downtime, but there’s always that awkward phase where the new code and old database schema need to play nice together. Here’s the scoop on doing this gracefully:
Downtime? Nah, Let’s Avoid It
While you could take the easy route and just pause everything during migrations, where’s the fun in that? The goal is to keep your app live while the gears turn behind the scenes.
Strategies for the Brave
- Deploy First, Migrate Later: The ol’ switcheroo. Update your pods with the new code, then run your migrations. Just make sure your code can handle the old and new schema alike to avoid any surprises.
- Migrate First, Then Deploy: Roll out the red carpet for your database schema changes before the new code steps in. It’s all about making those transitions seamless.
Kubernetes to the Rescue
Using a Kubernetes Job for migrations feels like having a magic wand:
# config/kube/migrate.yml
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: username/kubernetes-rails-example:latest
command: ['rails']
args: ['db:migrate']
Fire off that migration with kubectl apply -f config/kube/migrate.yml, and you’re golden. π
Effortless Continuous Delivery ππ¨
Who has time for manual deployments? Automate that goodness with a simple script:
# deploy.sh
#!/bin/sh -ex
export KUBECONFIG=~/.kube/kubernetes-rails-example-kubeconfig.yaml
docker build -t username/kubernetes-rails-example:latest .
docker push username/kubernetes-rails-example:latest
kubectl apply -f config/kube/migrate.yml
kubectl wait --for=condition=complete job/migrate --timeout=600s
kubectl delete job migrate
kubectl delete pods -l app=rails-app
kubectl delete pods -l app=sidekiq
Run ./deploy.sh and kick back. πΉ Your app updates are now rolling out smoothly.
Keeping an Eye on Your Cluster π΅οΈββοΈ
Monitoring is key to understanding what’s happening in your Kubernetes universe. Whether it’s checking on resource usage or ensuring your pods are healthy, keep tabs on everything:
- Kubernetes Dashboard: Get a bird’s-eye view of your cluster’s health and performance.
- Prometheus or Datadog: For the detail-oriented, dive deeper into metrics and get alerts before things go sideways.
Stay Sharp with Security Updates π
In the container world, staying updated is your best defense. Don’t let those vulnerabilities sneak up on you:
- Update Kubernetes and Node: Your cloud provider might handle this, but always double-check.
- Keep Your Containers Fresh: Regularly update your Docker images to include the latest security patches.
- Rails Dependencies: Don’t sleep on updating gems and packages. Outdated dependencies are like open doors for troublemakers.
Remember, a well-maintained app is a secure app. Stay vigilant, update often, and your Kubernetes adventure will be smooth sailing. π
Wrapping It Up π
And there you have it, folks! We’ve journeyed through the nuts and bolts of deploying a Ruby on Rails application in the vast universe of Kubernetes. π From containerizing your app to ensuring it thrives in a Kubernetes cluster, we’ve covered the A to Z to get you production-ready. π
The Takeaway π½
Deploying and scaling your app, or tweaking its configuration across an army of nodes, has never been easier. Thanks to Kubernetes’ love from the tech community, you’re looking at sweeter pricing and flexibility that PaaS platforms can only dream of. π
Achieving High Availability & Scalability π
To reach the summit of availability and global-scale scalability, remember to sidestep those pesky bottlenecks and eliminate single points of failure:
- Load Balancers: Don’t let them become the choke point. Upgrading hardware or using Round-Robin DNS can help spread the load more evenly. And if you’re getting cozy with services like CloudFlare, enjoy the added perks of health checks, DDoS protection, and cached content. π‘οΈ
- Databases: Single-server setups are so last season. Consider diving into the world of sharding for distributing data across multiple servers, ensuring smooth sailing even as you scale. With solutions like MongoDB and Redis Cluster, and a plethora of managed options, you’re well-equipped to handle growth without breaking a sweat. πΎ
Final Thoughts π
Deploying with Kubernetes might seem like gearing up for a space mission at first, but once you get the hang of it, you’ll see your Rails app soaring high with reliability, performance, and scalability. π
So, go forth and deploy with confidence! And remember, the Kubernetes community is vast and supportive, ready to help you navigate any challenges that come your way. Happy deploying! π’