This is a simple Rails 7.1 application built based on Rails "blog app" guide. We will be using this app to learn how to deploy a Rails app to VPS with Kamal.
Sources:
Pre-requisites:
- Ruby 3.4.1
- installed and configured git
- installed and configured ssh
- ssh-key without passphrase
- docker installed and updated
-
Find or create an SSH key without a passphrase
https://linuxize.com/post/how-to-setup-passwordless-ssh-login/#setup-ssh-passwordless-login
-
Go to
kamal.cklos.foo
-
Click on
Sign up
button -
Enter signup code form slides, username, password, PUBLIC part of SSH key
-
Test your SSH connection to assigned servers
-
Generate a new application
gem update rails rails _8.0.1_ new kamal-workshops-new
-
Add Posts scaffold (optional)
bin/rails generate scaffold Posts title:string bin/rails db:migrate
set
root "posts#index"
inconfig/routes.rb
-
Edit
config/deploy.yml
image: <docker_username- e.g. kamal_gh_1>/kamal_workshops_new servers: web: - <IP address - e.g. 255.255.255.100> proxy: ssl: true host: <Host - e.g pluto.cklos.foo> registry: username: <docker_username - e.g. kamal_gh_1>
-
Set KAMAL_REGISTRY_PASSWORD environment variable in terminal
export KAMAL_REGISTRY_PASSWORD=<docker_token>
-
Commit changes
-
Deploy with
kamal setup
-
Go to
pluto.cklos.foo
You have deployed your application.
git clone [email protected]:visualitypl/kamal-workshops-app.git
git clone https://github.com/visualitypl/kamal-workshops-app.git
Example files for this task:
gem "kamal", require: false
bundle install
kamal init
We will need to edit:
- service (name of the app: blog-space)
- image (#{Docker Hub username from the companion app}/#{name of the app})
- servers (IP from the companion app)
- proxy (with hostname)
- registry (username: #{Docker Hub username from the companion app})
- env (clear, secret)
- accessories
We will need to edit:
- KAMAL_REGISTRY_PASSWORD (Docker Hub token from the companion app)
- SECRET_KEY_BASE (generate secret key base with
rails secret
) - POSTGRES_PASSWORD (pick any password)
- REDIS_PASSWORD (pick any password)
- REDIS_URL (substitute password in string: "redis://:$REDIS_PASSWORD@blog-space-redis:6379/0")
To install docker, start postgres and redis containers, kamal-proxy and deploy the app we need to run:
kamal server bootstrap
kamal env push
kamal accessory boot postgres && kamal accessory boot redis
# or kamal accessory boot all
kamal deploy
Alternatively, you can run a command that calls all the above:
kamal setup
To check the result, run:
kamal audit
kamal details
Visit the app at hostname you set in the proxy.
Example files for this task:
Create config/deploy.staging.yml
.
We only need to override values from config/deploy.yml
that are different for staging.
servers:
web:
hosts:
- [HOST IP] ## CHANGE ME
worker:
hosts:
- [HOST IP] ## CHANGE ME
env:
clear:
RAILS_ENV: "staging"
POSTGRES_DB: "blog_space_staging"
accessories:
postgres:
files:
- "config/init.staging.sql:/docker-entrypoint-initdb.d/init.sql"
redis:
cmd: "redis-server --requirepass <%= File.read('.env.staging')[/REDIS_PASSWORD="(.*?)"/, 1] %>"
Create .env.staging
and add:
- SECRET_KEY_BASE (generate secret key base with
rails secret
) - POSTGRES_PASSWORD (pick a password)
- REDIS_PASSWORD (pick a password)
- REDIS_URL (
"redis://:<REDIS_PASSWORD>@blog-space-redis:6379/0"
, substitute the password with above)
Notice that we are not setting KAMAL_REGISTRY_PASSWORD
env var, because we are using the same one as in production.
To use kamal with staging destination we need to pass -d staging
flag to all commands.
Like before we need to setup docker on hosts, push env, deploy postgres accessory and deploy the app.
kamal setup -d staging
kamal server bootstrap -d staging
kamal env push -d staging
kamal accessory boot all -d staging
kamal deploy -d staging
To check the result, run:
kamal audit -d staging
kamal details -d staging
Visit the app at http://#{STAGING-IP}
We will break the app by generating a migration that will fail.
bundle exec rails generate migration addAuthorToArticle
class AddAuthorToArticle < ActiveRecord::Migration[7.1]
def change
add_column :articles, :author, :string, null: false
end
end
Commit the changes and push them to the server.
git add -A
git commit -m "Add author to article"
kamal deploy
Inspect the output of the deploy command and notice that the app have not been deployed to the server.
Fix the migration by adding a default value to the author column. BUT at the same time break something else.
class AddAuthorToArticle < ActiveRecord::Migration[7.1]
def change
add_column :articles, :author, :string, null: false, default: ""
end
end
Comment the "delete_barons_comments" route in config/routes.rb
# post "delete_barons_comments", on: :member
Commit the changes and push them to the server.
This time the migration were applied to the database. But "the core functionality" of the app is lost.
Rollback to previous version of the app:
kamal rollback <VERSION>
# to find the version run:
kamal audit
Remove the migration that we added previously.
This task has no solution here ;)