Juju và Charmed Operators trên Ubuntu: Tự động hóa triển khai và quản lý vòng đời ứng dụng theo mô hình Model-driven

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

Vấn đề mình từng đau đầu khi quản lý nhiều service cùng lúc

Khi hệ thống production có khoảng 5–10 service chạy song song — MySQL, Redis, Nginx, ứng dụng Flask, Celery workers — việc quản lý thủ công bắt đầu trở thành bài toán không có lời giải sạch. Mình đã từng maintain một đống bash script deploy.sh đủ kiểu, mỗi khi thêm service mới lại phải viết thêm script, cập nhật tài liệu (mà thường không ai đọc), rồi còn phải nhớ thứ tự khởi động đúng: “MySQL phải lên trước, Redis trước, app sau…”.

Rồi đến lúc cần scale: khách hàng cần thêm 2 node web để xử lý traffic peak. Cài thủ công từng cái, copy config, test connection… Mất nửa ngày. Chưa kể mỗi lần update lại lo không biết service nào sẽ break. Người mới vào team thì phải đọc cả chục trang wiki để hiểu hệ thống — mà wiki thì luôn lạc hậu hơn thực tế ít nhất vài tháng.

Phân tích nguyên nhân: công cụ truyền thống thiếu gì?

Ansible, Chef, Puppet — đều là công cụ tốt cho configuration management. Nhưng chúng có cùng một điểm mù: chỉ nói được “WHAT” (cấu hình máy như thế nào) chứ không hiểu “HOW” (các service liên kết với nhau theo cách nào).

Với Ansible, bạn viết playbook để cài MySQL trên node A, cài WordPress trên node B. Nhưng mối quan hệ giữa chúng — WordPress kết nối MySQL bằng user nào, database nào, password gì — phải hardcode hoặc manage riêng bằng variable. Khi thay đổi topology (thêm replica MySQL, đổi IP), lại phải cập nhật thủ công ở 3–4 chỗ khác nhau rồi mới chạy lại playbook.

Kubernetes + Helm giải quyết tốt bài toán container orchestration. Nhưng nếu bạn đang chạy bare metal, VM cũ, hoặc có service không thể container hóa dễ dàng — legacy database, workload đặc thù phần cứng — Helm không phải lựa chọn phù hợp.

Thứ mà cả ba đều bỏ qua: không công cụ nào hiểu được ngữ nghĩa của quan hệ giữa các service — “WordPress cần database”, “Kafka cần ZooKeeper”, “Grafana cần Prometheus làm datasource”. Topology thay đổi? Con người vẫn phải vào tay can thiệp.

Các cách giải quyết bài toán orchestration phức tạp

Cách 1: Script thủ công có version control

Viết bash/Python script cẩn thận, đưa vào Git, document đầy đủ. Đây là cách phổ biến nhất nhưng tốn công bảo trì và khó scale — mỗi thay đổi topology là một lần can thiệp thủ công.

Cách 2: Ansible với dynamic inventory

Dùng Ansible dynamic inventory kết hợp với group_vars để manage quan hệ giữa service. Tốt hơn script thuần, nhưng vẫn phải tự viết logic xử lý khi service thêm/bớt.

Cách 3: Kubernetes Operators

Nếu đã dùng Kubernetes, Operators pattern (như những gì Prometheus Operator hay MongoDB Operator làm) giải quyết được bài toán lifecycle management trong môi trường container. Tuy nhiên yêu cầu K8s cluster và kiến thức sâu về Go/Python để tự viết operator — thường mất vài tuần để đủ tự tin đưa lên production-ready.

Cách 4: Juju với Charmed Operators

Juju mang operator pattern lên cả bare metal và VM, không chỉ giới hạn trong Kubernetes. Charmed Operators (Charms) là các package Python đóng gói toàn bộ logic deploy, configure, scale và upgrade — kể cả quan hệ với service khác.

Cách tốt nhất: Juju Model-driven Orchestration

Khái niệm cốt lõi cần nắm

  • Controller: “Brain” của Juju, quản lý toàn bộ hệ thống, chạy trên máy riêng hoặc container
  • Model: Nhóm logic gồm các ứng dụng và quan hệ giữa chúng (tương tự namespace)
  • Charm: Package Python chứa logic deploy/configure/scale/upgrade cho một ứng dụng cụ thể
  • Relation: Định nghĩa cách hai service trao đổi thông tin — credentials, endpoints, config
  • Unit: Một instance của ứng dụng (tương tự Pod trong K8s)

Cài đặt Juju trên Ubuntu

sudo snap install juju --classic

Bootstrap controller với LXD (môi trường local)

# Cài LXD nếu chưa có
sudo snap install lxd
lxd init --auto

# Bootstrap controller — Juju tạo LXD container riêng làm controller
juju bootstrap localhost lxd-controller

Trên môi trường staging của công ty chạy Ubuntu 22.04, mình đã test config này trước khi đưa lên production. Bootstrap mất khoảng 3–5 phút tùy tốc độ mạng.

Deploy ứng dụng và kết nối service

# Tạo model mới
juju add-model mywebapp

# Deploy MySQL và WordPress từ Charmhub
juju deploy mysql
juju deploy wordpress

# Theo dõi trạng thái — chờ "active"
juju status

Output điển hình của juju status:

App        Version  Status   Scale  Charm
mysql      8.0.36   active       1  mysql
wordpress  6.4.2    waiting      1  wordpress   waiting: ready

Unit          Workload  Agent  Public address
mysql/0*      active    idle   10.0.0.10
wordpress/0*  waiting   idle   10.0.0.11

Bước quan trọng nhất — kết nối hai service:

juju integrate wordpress:db mysql:db

Sau lệnh này, Juju tự động:

  1. MySQL charm tạo database và user mới với random credentials
  2. Truyền host, port, dbname, user, password sang WordPress charm qua relation data
  3. WordPress charm ghi wp-config.php với thông tin đó
  4. Restart PHP-FPM nếu cần — không cần bạn SSH vào

Quản lý vòng đời: scale, config, upgrade

# Scale WordPress lên 3 unit — Juju tự tạo container, deploy, kết nối MySQL
juju scale-application wordpress 3

# Xem và set config — charm tự apply, không cần SSH
juju config wordpress
juju config wordpress blog-title="IT From Zero"

# Upgrade charm lên version mới
juju refresh wordpress --channel latest/stable

# Chạy action có sẵn trong charm
juju actions mysql          # Xem danh sách actions
juju run mysql/0 backup     # Chạy backup

Deploy stack phức tạp: Kafka + monitoring trong vài phút

juju add-model kafka-stack

# Deploy Kafka và ZooKeeper
juju deploy kafka --channel 3/stable
juju deploy zookeeper --channel 3/stable
juju integrate kafka:zookeeper zookeeper:zookeeper

# Thêm monitoring — Prometheus + Grafana tự cấu hình nhau
juju deploy prometheus2
juju deploy grafana
juju integrate kafka:metrics prometheus2:scrape
juju integrate prometheus2:grafana-source grafana:grafana-source

# Xem toàn bộ hệ thống
juju status --color

Kết quả: Kafka cluster với Prometheus metrics và Grafana dashboard hoàn chỉnh, không viết một dòng config thủ công.

Một vài lưu ý thực tế khi áp dụng

  • Juju không thay thế Docker/Kubernetes — nó bổ sung. Juju có thể deploy charm lên K8s cluster (Juju + K8s mode), charm khi đó quản lý Kubernetes resources thay vì máy ảo.
  • Chọn charm chất lượng: không phải mọi charm trên Charmhub đều production-ready. Ưu tiên charm có badge Canonical hoặc nhiều downloads, đọc kỹ README trước khi dùng thật.
  • Đầu tư học concepts: dành 1–2 ngày để hiểu model, controller, charm, relation, unit trước khi deploy production.
  • Phù hợp nhất cho stack phức tạp: Charmed Kubernetes, MLOps với Kubeflow charm, data platform với Spark + HDFS. Với 1–2 service đơn giản, Ansible vẫn là lựa chọn nhanh hơn.

Tóm lại: Juju không giải quyết bài toán “cài phần mềm lên máy” — nó giải quyết bài toán “quản lý toàn bộ vòng đời ứng dụng phức tạp theo cách có thể reproduce và chuẩn hóa cho cả team”. Đó là thứ mà Ansible, Chef hay Helm chưa bao giờ định làm.

Share: