Deploy Rails 8.1 with Kamal on a VM (Experimental)
หลังจากที่ rails 8.1 ปล่อยออกมา มีอย่างหนึ่งที่เห็นแล้วเป็นอะไรที่กำลังรออยู่เลยก็คือ Registry-Free Kamal Deployments ที่จะทำให้เราสามารถ deploy rails application ได้โดยไม่ต้องใช้ registry ที่เป็น online อย่างพวก docker hub อีกแล้ว เพราะมันได้ free private registry แค่ 1 ตัวเท่านั้น
ในการทดลองนี้จะลอง deploy บน vm ก่อน ซึ่งน่าจะเป็นแนวทางในการ deploy บน server จริง ๆ หรือบน cloud service ที่มีการจัดการ server ให้เรา อย่างเช่น DigitalOcean
Getting Started
- ติดตั้ง ubuntu server บน vm (ผมเลือกใช้ VirtualBox)
เหมือนเดิม เราจะสร้าง user ใหม่เพื่อใช้ในการ deploy
1
2
3
sudo adduser deploy
sudo adduser deploy sudo
exit
- แน่ใจว่า ssh จาก local machine เข้าไปที่ vm ที่ต้องการ deploy ได้ (ใช้
ssh-copy-idเพื่อจะได้ไม่ต้องใส่ password ทุกครั้งตอน deploy) - แน่ใจว่า local machine ของคุณมี Docker Desktop ติดตั้งแล้ว (หรือจะใช้ OrbStack ก็ได้)
- สร้าง rails application
1
2
3
rails new blog
cd blog
rails generate controller home index
ตั้ง root ใช้เรียบร้อย
1
root 'home#index'
- ที่ server ของเราจะเพิ่ม group docker อันนี้เป็นส่วนที่ kamal จะใช้ในการ deploy
1
2
sudo addgroup docker
sudo usermod -aG docker $USER
จากนั้น logout แล้ว login ใหม่ (เพื่อให้สิทธิ์กลุ่ม docker มีผล)
แล้วสร้าง directory สำหรับ storage สำหรับ rails application
1
mkdir blog_storage
- กลับมาที่ local machine ไปตรวจดู
config/deploy.ymlแก้ไขตามนี้
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# Name of your application. Used to uniquely configure containers.
service: blog
# Name of the container image (use your-user/app-name on external registries).
image: blog
# Deploy to these servers.
servers:
web:
- - 192.168.0.1
+ - [ip ของ server ของเรา]
# job:
# hosts:
# - 192.168.0.1
# cmd: bin/jobs
# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
# If used with Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
#
# Using an SSL proxy like this requires turning on config.assume_ssl and config.force_ssl in production.rb!
#
# Don't use this when deploying to multiple web servers (then you have to terminate SSL at your load balancer).
#
# proxy:
# ssl: true
# host: app.example.com
# Where you keep your container images.
registry:
# Alternatives: hub.docker.com / registry.digitalocean.com / ghcr.io / ...
server: localhost:5555
# Needed for authenticated registries.
# username: your-user
# Always use an access token rather than real password when possible.
# password:
# - KAMAL_REGISTRY_PASSWORD
# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
secret:
- RAILS_MASTER_KEY
clear:
# Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
# When you start using multiple servers, you should split out job processing to a dedicated machine.
SOLID_QUEUE_IN_PUMA: true
# Set number of processes dedicated to Solid Queue (default: 1)
# JOB_CONCURRENCY: 3
# Set number of cores available to the application on each server (default: 1).
# WEB_CONCURRENCY: 2
# Match this to any external database server to configure Active Record correctly
# Use blog-db for a db accessory server on same machine via local kamal docker network.
# DB_HOST: 192.168.0.2
# Log everything from Rails
# RAILS_LOG_LEVEL: debug
# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
aliases:
console: app exec --interactive --reuse "bin/rails console"
shell: app exec --interactive --reuse "bash"
logs: app logs -f
dbc: app exec --interactive --reuse "bin/rails dbconsole --include-password"
# Use a persistent storage volume for sqlite database files and local Active Storage files.
# Recommended to change this to a mounted volume path that is backed up off server.
volumes:
- - "blog_storage:/rails/storage"
+ - "./blog_storage:/rails/storage"
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
asset_path: /rails/public/assets
# Configure the image builder.
builder:
arch: amd64
# # Build image via remote server (useful for faster amd64 builds on arm64 computers)
# remote: ssh://docker@docker-builder-server
#
# # Pass arguments and secrets to the Docker build process
# args:
# RUBY_VERSION: ruby-3.4.7
# secrets:
# - GITHUB_TOKEN
# - RAILS_MASTER_KEY
# Use a different ssh user than root
-# ssh:
-# user: app
+ssh:
+ user: deploy
# Use accessory services (secrets come from .kamal/secrets).
# accessories:
# db:
# image: mysql:8.0
# host: 192.168.0.2
# # Change to 3306 to expose port to the world instead of just local network.
# port: "127.0.0.1:3306:3306"
# env:
# clear:
# MYSQL_ROOT_HOST: '%'
# secret:
# - MYSQL_ROOT_PASSWORD
# files:
# - config/mysql/production.cnf:/etc/mysql/my.cnf
# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
# directories:
# - data:/var/lib/mysql
# redis:
# image: valkey/valkey:8
# host: 192.168.0.2
# port: 6379
# directories:
# - data:/data
จากนั้น commit ให้เรียบร้อยก่อนจะเริ่ม deploy กัน
- รันคำสั่ง
kamal setup
ในขั้นตอนนี้อาจจะมีการขอสิทธิ์ sudo ในตอนรันคำสั่ง ซึ่งจะมี error ประมาณนี้:
1 2 3 sh stderr: + sudo -E sh -c apt-get -qq update >/dev/null sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper sudo: a password is requiredให้เรา login เข้า deploy user แล้วทำตามนี้:
1sudo visudoจากนั้นเพิ่ม
deploy ALL=(ALL) NOPASSWD:ALLหลังบรรทัด%sudo ALL=(ALL:ALL) ALL
1 2 3 4 # Allow members of group sudo to execute any command %sudo ALL=(ALL:ALL) ALL deploy ALL=(ALL) NOPASSWD:ALL
หลังจาก setup เสร็จก็เปิดเว็บเบราว์เซอร์ไปที่ ip address ของ server ที่เรา deploy ได้เลย
และครั้งต่อไปเราจะใช้ kamal deploy เพื่อ deploy ใหม่
Conclusion
ในบทความนี้เราได้ทำการ deploy rails 8.1 บน server ด้วย kamal ได้สำเร็จแบบ minimal setup แล้ว