Nâng cao
6 phút đọc5 tháng 6, 20261

Bootleg: Deploy ứng dụng Elixir không còn là cơn ác mộng

Bootleg giúp bạn build, deploy và quản lý Elixir releases chỉ với vài lệnh mix. Đơn giản, extensible, và không cần viết script bash lằng nhằng.

N

Nguyễn Nhật Long

@nguyennhatlong1303

Bootleg: Deploy ứng dụng Elixir không còn là cơn ác mộng

Nếu bạn đã từng deploy một ứng dụng Elixir lên production, bạn biết cái cảm giác đó ngồi viết shell script, SSH vào từng server, copy file release, restart service, rồi cầu trời nó không crash. Mình đã trải qua giai đoạn đó và thật sự không muốn lặp lại.

Bootleg ra đời để giải quyết đúng cái pain point này. Đây là một Elixir library cho phép bạn build và deploy ứng dụng Elixir/Phoenix theo cách có cấu trúc, reproducible, và quan trọng nhất viết config bằng Elixir luôn chứ không phải YAML hay TOML gì cả.

Tại sao không dùng Ansible, Capistrano hay mấy cái kia?

Mình cũng đã thử. Ansible thì okay nhưng bạn phải học thêm một layer YAML nữa, và khi cần customize logic phức tạp thì nó khá cồng kềnh. Capistrano thì gắn chặt với Ruby ecosystem. Còn viết tay shell script thì... mình không muốn nhắc lại.

Cái hay của Bootleg là nó nằm ngay trong Elixir project của bạn, config bằng Elixir DSL, và tích hợp trực tiếp với Distillery để build release. Không cần học thêm tool nào ngoài hệ sinh thái bạn đang dùng.

Setup từ đầu đến khi chạy được

Đầu tiên, thêm dependencies vào mix.exs:

Elixir
1def deps do
2 [
3 {:distillery, "~> 2.1.0", runtime: false},
4 {:bootleg, "~> 0.13.0", runtime: false}
5 ]
6end

Cả hai đều có runtime: false vì đây là dev/deploy tooling, không cần chạy trong production app.

Sau đó init Distillery trước:

Terminal
1mix distillery.init

Lệnh này tạo ra thư mục rel/ với config cơ bản. Nếu bạn chưa dùng Distillery bao giờ thì đây là bước quan trọng Bootleg phụ thuộc vào Distillery để tạo release artifact.

Tiếp theo init Bootleg:

Terminal
1mix bootleg.init

Nó sẽ tạo ra config/deploy.exsconfig/deploy/production.exs. Đây là hai file config chính bạn cần quan tâm.

Cấu trúc config phần quan trọng nhất

config/deploy.exs là nơi bạn define build server tức là server dùng để compile và tạo release:

Elixir
1use Bootleg.DSL
2
3role :build, "build.example.com",
4 workspace: "/home/deploy/build",
5 user: "deploy",
6 identity: "~/.ssh/id_rsa",
7 silently_accept_hosts: true

Còn config/deploy/production.exs define app servers nơi release sẽ được copy đến và chạy:

Elixir
1use Bootleg.DSL
2
3role :app, ["app1.example.com", "app2.example.com"],
4 workspace: "/opt/myapp",
5 user: "deploy",
6 identity: "~/.ssh/id_rsa",
7 silently_accept_hosts: true

Mình thấy cái này hay ở chỗ bạn có thể define nhiều server trong role :app cùng một lúc Bootleg sẽ deploy song song lên tất cả. Không cần viết loop hay gì cả.

Build, deploy, và mọi thứ còn lại

Sau khi config xong, workflow hàng ngày của bạn sẽ trông như thế này:

Terminal
1# Build release trên build server
2mix bootleg.build
3
4# Copy release xuống app servers và chuẩn bị
5mix bootleg.deploy
6
7# Start application
8mix bootleg.start

Hoặc nếu cần restart:

Terminal
1mix bootleg.stop
2mix bootleg.start
3
4# Hoặc gộp lại
5mix bootleg.restart

Và cái mình hay dùng nhất khi có sự cố:

Terminal
1mix bootleg.rollback

Rollback là tính năng mình đánh giá cao. Bootleg giữ lại các release cũ trên server, nên khi deploy mới bị lỗi, một lệnh là bạn quay về version trước. Không cần SSH vào tay, không cần panic.

Các deployment strategy Bootleg hỗ trợ

Theo kinh nghiệm của mình, setup remote build server riêng là cách tốt nhất cho production. Lý do là build Elixir release cần đúng OS và architecture với production server nếu bạn build trên Mac rồi deploy lên Linux thì sẽ gặp vấn đề với native dependencies. Build server riêng đảm bảo môi trường nhất quán.

StrategyMô tảKhi nào dùng
Remote build + Remote deployBuild trên build server riêng, deploy lên app serversProduction với team lớn
Local build + Remote deployBuild trên máy dev, push lên serverSolo dev hoặc project nhỏ
Remote build + Local deployÍt phổ biến hơnMột số edge case đặc biệt

Customize với Bootleg DSL

Đây là phần mà Bootleg thực sự tỏa sáng so với các tool khác. Bạn có thể hook vào các lifecycle events và chạy custom logic:

Elixir
1use Bootleg.DSL
2
3# Chạy migration sau khi deploy
4after_deploy do
5 remote :app do
6 "bin/myapp rpc 'MyApp.Release.migrate()'"
7 end
8end
9
10# Notify Slack sau khi start thành công
11after_start do
12 IO.puts "Deploy thành công! Version: #{version()}"
13end

Bạn có thể dùng remote/2 để chạy lệnh trực tiếp trên server từ trong Elixir code. Mình thấy cái này cực kỳ mạnh bạn có thể viết bất kỳ logic nào bằng Elixir, không bị giới hạn bởi YAML hay template engine.

Ngoài ra còn có các hooks khác:

Elixir
1before_build do ... end
2after_build do ... end
3before_deploy do ... end
4after_deploy do ... end
5before_start do ... end
6after_start do ... end

Một số lưu ý thực tế khi dùng

Anh em lưu ý mấy điểm này để tránh mất thời gian debug:

SSH key setup: Bootleg dùng SSHKit.ex bên dưới để connect. Đảm bảo SSH key bạn chỉ định trong identity đã được add vào authorized_keys trên cả build server lẫn app servers. Nghe đơn giản nhưng mình đã thấy nhiều người quên bước này.

Elixir version phải match: Build server và app server cần cùng Elixir/OTP version. Nếu khác nhau, release có thể không chạy được. Dùng asdf hoặc Docker để đảm bảo consistency.

Workspace permissions: User bạn dùng để deploy cần có write permission vào workspace directory. Nếu dùng /opt/myapp thì nhớ chown cho đúng.

silently_accept_hosts: true: Trong môi trường CI/CD, bạn cần option này để không bị prompt xác nhận SSH fingerprint. Trong production thực tế thì nên manage known_hosts properly.

So sánh nhanh với một số alternative

Nếu bạn đang dùng Elixir và self-host infrastructure, Bootleg là lựa chọn tự nhiên nhất. Không phải vì nó perfect, mà vì nó nằm trong cùng ecosystem và bạn không cần context switch sang tool khác.

ToolLanguageElixir-nativeExtensibilityLearning curve
BootlegElixirCao (Elixir DSL)Thấp với Elixir devs
AnsibleYAML/PythonCao (modules)Trung bình
CapistranoRubyCaoTrung bình
Shell scriptsBashThấpThấp nhưng dễ sai
Gigalixir CLIElixirThấp (platform lock-in)Rất thấp

Tích hợp vào CI/CD pipeline

Bootleg hoạt động rất tốt trong CI pipeline. Ví dụ với GitHub Actions:

YAML
1- name: Deploy to production
2 run: |
3 mix bootleg.build
4 mix bootleg.deploy
5 mix bootleg.start
6 env:
7 MIX_ENV: prod

Bạn cần đảm bảo SSH private key được inject vào CI environment (dùng secrets) và agent forwarding được setup đúng. Mình thường dùng ssh-agent action để handle phần này.

Tình trạng hiện tại của project

Một điều mình cần nói thẳng: Bootleg hiện có ~400 stars trên GitHub và không còn được update thường xuyên. Repo vẫn còn mở nhưng activity khá thấp. Nếu bạn cần một tool đang được maintain tích cực, đây là điều cần cân nhắc.

Tuy nhiên, với những project đang dùng Distillery và cần một deployment workflow đơn giản, Bootleg vẫn hoàn toàn làm được việc. Codebase khá nhỏ gọn và nếu cần fix gì bạn có thể fork và tự patch.

Cộng đồng Elixir hiện cũng có các hướng khác như dùng Fly.io hoặc Render để tránh hẳn việc manage deployment infrastructure. Nhưng nếu bạn cần self-hosted và muốn control hoàn toàn, Bootleg vẫn là một lựa chọn đáng xem xét.

Cho những ai muốn explore thêm, Bootleg có channel #bootleg trên Elixir Slack cộng đồng ở đó khá friendly và responsive với câu hỏi kỹ thuật.

NN

Nguyễn Nhật Long

@nguyennhatlong1303

Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.

Thấy hay? Chia sẻ cho bạn bè!