Khi một sản phẩm đã chạy ổn trên Helm, các dự án Laravel khác cũng muốn lên Kubernetes. Chép chart sang rồi sửa tay là cách nhanh nhất, nhưng sẽ sinh ra nhiều bản gần giống nhau và lệch dần. Giải pháp là một bộ chart dùng chung: chart Laravel, MySQL, Redis (và các dịch vụ nền khác như MongoDB, Elasticsearch) được tham số hoá để mỗi dự án chỉ cần viết một file values. Tài liệu này giải thích vì sao phải tách, cách thiết kế values để một chart phục vụ nhiều dự án, lựa chọn giữa library chart với application chart, và các lỗi cần tránh.
1. Chart riêng của một sản phẩm chứa quá nhiều giả định
Chart website viết cho một sản phẩm (như ở bài Helm chart là gì: đóng gói website Laravel để deploy lặp lại) làm tốt việc của nó, nhưng mọi thứ đặc thù thường được viết thẳng vào template:
- initContainer chạy cứng một chuỗi lệnh như
cp -rf /var/code/plugins ./ && php artisan migrate --force && php artisan <lệnh-seed-riêng>. Dự án khác không có thư mục đó, cũng không có lệnh đó. - Danh sách volumeMount (
public,themes,plugins, session, cache) viết sẵn trong template. - Volume luôn là NFS khai báo trực tiếp trong Deployment, không có lựa chọn PVC.
- nodeAffinity chỉ nhận một giá trị ID node pool.
Mỗi giả định là một chỗ phải sửa tay khi đem chart sang dự án khác. Một chart dùng chung phải đảo ngược cách làm: template chỉ giữ khung, mọi chi tiết đi vào values.
2. Khi giả định nằm sai chỗ: ví dụ về quyền file
Một tình huống điển hình cho thấy cái giá của việc gắn chặt giả định vào template. Image ứng dụng được nâng lên base image mới và thêm dòng USER nginx, tức container không còn chạy bằng root. Trong khi đó, hook postStart của chart vẫn chạy chown -R nginx:nginx và chmod -R 755 trên thư mục NFS. Không còn root, các lệnh này thất bại, site lỗi quyền file và phải lùi image.
Với chart dùng chung, cách sửa đúng là chuyển việc phân quyền về nơi có quyền:
- Pipeline tạo site
chownthư mục ngay khi tạo trên máy NFS, đúng UID/GID mà container dùng. - Bỏ
chown/chmodkhỏipostStart. Với volume hỗ trợ, có thể dùngsecurityContext.fsGroupcủa pod thay cho chown thủ công. - Nếu cần hai biến thể image cho hai loại site, đưa tên image vào values thay vì sửa template.
Bài học cho chart dùng chung: lệnh khởi tạo, hook và quyền file là chuyện của từng dự án, không được viết cứng trong template.
3. Application chart hay library chart cho chart dùng chung
Helm có hai loại chart, khai báo bằng trường type trong Chart.yaml:
- Application chart (mặc định): render ra manifest và cài được thành release.
- Library chart: chỉ chứa các named template (
define) để chart khác gọi bằnginclude. Library chart không tự sinh manifest và không cài trực tiếp được; chart dùng nó khai báo trong mụcdependencies. Chi tiết xem tài liệu Helm về library chart.
Một lựa chọn thực dụng cho bộ chart dùng chung là nhiều application chart độc lập, mỗi chart có _helpers.tpl riêng sinh từ helm create. Helpers của các chart gần như giống nhau, nên lặp code là có thật, nhưng người mới chỉ cần đọc một thư mục là hiểu cả chart.
| Nhiều application chart | Một library chart + chart mỏng | |
|---|---|---|
| Đọc hiểu | Mở một thư mục là đủ | Phải nhảy giữa chart chính và library |
| Sửa helper (label, tên) | Sửa ở nhiều chỗ | Sửa một chỗ, nâng version library |
| Phát hành | Cài thẳng từ thư mục repo | Cần helm dependency update hoặc một chart repository |
| Rủi ro | Các bản helpers lệch nhau dần | Sửa library có thể làm vỡ mọi chart cùng lúc |
4. Thiết kế values cho chart dùng chung Laravel
Chart Laravel là chart dùng chung quan trọng nhất: giữ nguyên khung Deployment, Service, Ingress, Certificate, HPA, nhưng mọi chỗ đặc thù chuyển thành values có công tắc bật/tắt.
Biến môi trường: một map, không cần sửa template
Thay vì liệt kê từng key trong template ConfigMap, chart duyệt cả map configMap và tự đổi tên key sang kiểu biến môi trường Laravel:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "laravel.fullname" . }}-configmap
data:
{{- range $k, $val := .Values.configMap }}
{{ $k | snakecase | upper }}: {{ $val | quote }}
{{- end }}Values ghi dbHost: mysql thì ConfigMap có DB_HOST. Dự án cần thêm biến chỉ việc thêm một dòng trong values, không phải sửa chart dùng chung. Secret dùng cùng cách. Cách nạp biến vào pod bằng envFrom có ở bài ConfigMap và Secret: biến môi trường Laravel trên Kubernetes.
Lệnh khởi tạo, hook và probe đều có công tắc
initContainers:
enabled: true
command: "php artisan migrate --force && php artisan optimize:clear"
lifecycle:
enabled: false
postStart:
command: "php artisan optimize:clear;"
startupProbe:
enabled: false
path: /startup
failureThreshold: 30
periodSeconds: 5
livenessProbe:
enabled: false
path: /liveness
timeoutSeconds: 5
readinessProbe:
enabled: false
path: /readinessDự án chưa có endpoint health thì để probe tắt; dự án có rồi thì bật và khai đường dẫn trong values của chart dùng chung. Thời gian chờ và ngưỡng lỗi cũng nên là tham số, vì mỗi ứng dụng khởi động nhanh chậm khác nhau.
Volume: danh sách mount và lựa chọn NFS hoặc PVC
volumes:
enabled: false
volumeMounts:
- path: /var/www/html/public
alias: public
- path: /var/www/html/storage/app
alias: storage-app
nfs:
enabled: false # false thì dùng PVC
ip: 10.0.0.30
sharedFolder: /data
storage: 10GiTemplate duyệt danh sách volumeMounts, mỗi phần tử thành một mount với subPath bằng alias. Nếu nfs.enabled là false, chart tạo PVC ReadWriteOnce trên StorageClass linode-block-storage-retain. PVC RWO chỉ gắn được vào một node: Deployment nhiều replica trên nhiều node phải dùng NFS hoặc storage hỗ trợ RWX.
nodeAffinity nhận nguyên khối YAML
Thay vì chỉ nhận một ID node pool, chart dùng chung nhận nguyên khối affinity rồi render bằng toYaml, nên dự án có thể ghim pool, tránh node, hay đặt anti-affinity tuỳ ý mà không phải sửa template.
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}5. Chart dùng chung cho dịch vụ nền
Chart dùng chung cho dịch vụ nền theo cùng một khuôn đơn giản:
- MySQL: StatefulSet một replica, mật khẩu root lấy từ Secret,
volumeClaimTemplatestrên Block Storage, dung lượng là tham số. - Redis: Deployment chạy
redis-server --requirepass $(REDIS_PASSWORD), mật khẩu nạp từ Secret. - MongoDB và Elasticsearch: StatefulSet theo cùng khuôn với MySQL.
Với Redis cần lưu ý: Redis không persistence, mỗi replica là một kho dữ liệu riêng. Nếu bật HPA, Service chia request cho nhiều Redis không biết gì về nhau, cache và session trở nên chập chờn. Với Redis dùng làm cache hoặc queue, giữ replicaCount: 1 và để HPA tắt; cần sẵn sàng cao thì dùng Sentinel hoặc Redis Cluster.
6. Lỗi thường gặp khi viết chart dùng chung
- Lỗi chỉ lộ khi bật công tắc: khối
podSecurityContextviết{{- toYaml | nindent 8 }}, thiếu dấu chấm sautoYaml. Vì nằm trongwithvà giá trị mặc định rỗng, lỗi không hiện ra cho tới ngày có dự án khai báo nó. Chạyhelm lintvàhelm templatevới một file values bật mọi công tắc trong CI của repo chart. - Tag image trôi:
tag: latestcho Redis, hay một bản MySQL innovation, khiến mỗi lần cài ra một thứ khác nhau. Ghim phiên bản cụ thể; với MySQL nên chọn nhánh LTS như 8.4. - Giá trị mẫu trông như bí mật thật: values mặc định có sẵn
appKeyvà mật khẩu mẫu thì ai quên ghi đè sẽ chạy production với khoá mẫu. Để trống và dùng hàmrequiredđể Helm báo lỗi khi thiếu. - Không quản lý version: giữ nguyên
version: 0.1.0,appVersion: "1.16.0"củahelm create. Khi nhiều dự án dùng chung, nâng version mỗi lần đổi template để biết release nào chạy bản nào. - Đổi ConfigMap không restart pod: thêm annotation checksum vào pod template để Deployment rollout khi cấu hình đổi:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}Mỗi chart dùng chung cần được kiểm thử như code thư viện, vì mỗi lỗi sẽ lan sang mọi dự án dùng nó. Cách viết named template và gọi lại bằng include được hướng dẫn trong phần named template của Helm.
7. Đánh đổi khi chuyển sang chart dùng chung
| Được | Mất |
|---|---|
| Dự án Laravel mới chỉ cần viết một file values cho chart dùng chung | Values dài hơn, người dùng phải hiểu từng công tắc |
| Sửa lỗi template một lần, mọi dự án hưởng lợi | Sửa sai một lần, mọi dự án cùng vỡ nếu không có kiểm thử |
| MySQL, Redis, MongoDB, Elasticsearch có sẵn khuôn | Khuôn đơn giản: một replica, chưa có tinh chỉnh hay backup đi kèm |
Không cần ép sản phẩm đang chạy chuyển ngay sang chart dùng chung: các site hiện có và pipeline tạo site có thể giữ chart riêng, còn dự án mới bắt đầu từ chart dùng chung. Bài Dựng hạ tầng bằng Linode API: cluster, NFS và MySQL qua pipeline cho thấy chart MySQL được cài tự động thế nào trong pipeline dựng hạ tầng.
Checklist
- Template của chart dùng chung không chứa lệnh, đường dẫn hay ID riêng của một dự án.
- Mọi tính năng tuỳ chọn có công tắc
enabled, kèm file values bật tất cả để kiểm thử. - Bí mật dùng
required, không có giá trị mẫu. - Image và mỗi chart dùng chung đều ghim version.
- Có checksum annotation để rollout khi cấu hình đổi.
Bài tiếp theo
MySQL trong Kubernetes hay ngoài cluster: vì sao đưa database ra VPS. StatefulSet và Block Storage trong cluster so với một VPS MySQL riêng: các tiêu chí để chọn và cách chuyển.
