- Trong các môi trường có số lượng đối tượng cấu hình ngày càng tăng như Kubernetes, cách trực tiếp tạo thêm file YAML sẽ sớm chạm giới hạn, và cách tiếp cận tạo dữ liệu cấu hình trở nên phù hợp hơn template YAML
- Helm chart chèn giá trị bằng
values.yaml và Go template, nhưng ngay khi xuất hiện trường tùy chọn, mảng và map, gánh nặng về câu điều kiện và thụt lề tăng lên
- YAML có quy tắc khoảng trắng nghiêm ngặt, trong khi trình phân tích Helm template không hiểu cấu trúc YAML, nên việc kết hợp
toYaml và indent rất dễ dẫn đến tạo cấu hình mong manh
- YAML là siêu tập của JSON nên việc chuyển đổi qua lại đơn giản; Jsonnet xử lý việc tạo đối tượng cấu hình như viết code bằng biến bên ngoài, trường có điều kiện, kết hợp map và gộp đối tượng
- kr8 dùng luồng dựa trên Jsonnet để tạo và thao tác cấu hình cho nhiều cụm Kubernetes, chọn cách trực tiếp tạo và biến đổi đối tượng thay vì lắp ghép chuỗi YAML phức tạp
Độ phức tạp cấu hình bắt đầu khi số lượng file YAML tăng lên
- Khi ứng dụng và hạ tầng vượt qua một quy mô nhất định, độ phức tạp cấu hình tăng rất nhanh
- Nếu chỉ có 1–2 mục tiêu triển khai, viết trực tiếp các file cấu hình YAML là đủ, nhưng khi số lượng tăng hơn nữa thì cần quản lý cấu hình một cách có hệ thống
- Lý do cần nhiều file cấu hình thường là vì ngay cả cùng một đối tượng cũng có một số giá trị khác nhau
- Triển khai theo môi trường như
dev, stg, prod
- Triển khai theo khu vực như Europe, North America
- Không phải mọi cấu hình đều khác nhau, nhưng nếu khác biệt đủ lớn thì cần tách phần chung và phần khác nhau để quản lý
- Lĩnh vực quản lý cấu hình đã xử lý vấn đề này từ lâu, và nhiều công cụ đã tận dụng YAML theo cách riêng của mình
- hiera trong Puppet cho phép tra cứu biến theo phân cấp, mạnh mẽ và linh hoạt, đồng thời giảm đáng kể nhu cầu template hóa chính YAML
Vấn đề của template YAML bộc lộ trong Helm chart
- Khi cloud computing và Kubernetes mở rộng đối tượng cấu hình lên các tầng phía trên hệ điều hành, các công cụ như CloudFormation và Helm đã xuất hiện
- Helm chart có thể nhận các tham số bên ngoài được định nghĩa trong
values.yaml để render
- Giá trị chuỗi đơn giản thì tương đối dễ xử lý
image: "{{ .Values.image }}"
- Nếu chỉ định giá trị
image trong values.yaml, giá trị đó sẽ được đưa vào template
- Vấn đề trở nên lớn hơn khi bắt đầu xử lý cấu hình phức tạp hơn, chẳng hạn trường tùy chọn
{{- with .resourceGroup }}
resourceGroup: {{ . }}
{{- end }}
- Vì giá trị tùy chọn không thể để trống, cần câu điều kiện và vòng lặp, khiến template dễ trở nên lộn xộn
- Khi đưa mảng hoặc map vào, cần kết hợp
toYaml và indent
{{- with .Values.podAnnotations }}
annotations:
{{ toYaml . | indent 8 }}
{{- end }}
- Việc gọi hàm dùng
toYaml để chuyển YAML lại thành YAML đã khá gượng gạo, nhưng vấn đề lớn hơn là xử lý khoảng trắng
Xung đột giữa quy tắc khoảng trắng của YAML và template engine
- YAML có quy tắc nghiêm ngặt về thụt lề và khoảng trắng
- Ví dụ sau không phải YAML hợp lệ hoặc hoàn chỉnh
something: nothing
hello: goodbye
- Nếu con người viết trực tiếp, có thể nhấn backspace vài lần để sửa, nhưng khi tạo YAML bằng hệ thống template thì không đơn giản như vậy
- Nếu số lượng file cấu hình vượt quá mức 5–10 file, cần tạo cấu hình thay vì viết trực tiếp
- Để đưa giá trị
.Values.podAnnotations vào dưới annotations vốn đã được thụt lề, bản thân giá trị đó cũng phải được thụt lề chính xác ở đúng mức
- Vì trình phân tích Go template không hiểu YAML, cố thụt lề cú pháp template cho dễ nhìn cũng có thể gây vấn đề
{{- with .Values.podAnnotations }}
annotations:
{{ toYaml . | indent 6 }}
{{- end }}
- Khi hệ thống template không biết cấu trúc YAML mà vẫn phải xử lý khoảng trắng cùng câu điều kiện, việc tạo cấu hình phức tạp ngày càng khó hơn
- Cách viết trực tiếp JSON cũng không phù hợp vì thiếu chú thích và dễ thiếu dấu phẩy; chính những bất tiện này khiến YAML được sử dụng
Jsonnet là ngôn ngữ template dữ liệu để tạo cấu hình JSON
- YAML là siêu tập của JSON, nên việc chuyển đổi giữa JSON và YAML rất đơn giản
- Nhiều ứng dụng và ngôn ngữ lập trình có thể phân tích hoặc chuyển đổi JSON và YAML một cách mặc định
- Trong Python cũng có thể đọc YAML và xuất ra JSON
python -c 'import json, sys, yaml ; y=yaml.safe_load(sys.stdin.read()) ; print(json.dumps(y))'
- Jsonnet tự gọi mình là ngôn ngữ template dữ liệu, với mục đích cốt lõi là tạo cấu hình JSON
- Có thể xem bối cảnh thiết kế của Jsonnet trong design rationale
Xử lý biến bên ngoài và trường tùy chọn
- Jsonnet có thể dùng biến bên ngoài để chèn giá trị cấu hình
{
image: std.extVar('image'),
}
- Khi truyền biến bên ngoài từ CLI, kết quả JSON sẽ được tạo ra
jsonnet image.jsonnet -V image="my-image"
{
"image": "my-image"
}
- Trường tùy chọn có thể được biểu diễn bằng biểu thức điều kiện trong code, thay vì nhét câu điều kiện template vào trong chuỗi
// define a variable - yes, jsonnet also has comments
local rg = null;
{
image: std.extVar('image'),
// if the variable is null, this will be blank
[if rg != null then 'resourceGroup']: rg,
}
- Nếu
rg là null, trường resourceGroup sẽ không được đưa vào kết quả
- Nếu chỉ định giá trị, trường đó sẽ được xuất ra
Thao tác với map và đối tượng đơn giản hơn thụt lề YAML
- Khi đưa map vào cấu hình, như annotation của Kubernetes pod, trong Jsonnet có thể định nghĩa giá trị dưới dạng biến rồi đặt vào đối tượng
local annotations = {
'nginx.ingress.kubernetes.io/app-root': '/',
'nginx.ingress.kubernetes.io/enable-cors': true,
};
{
metadata: { // annotations are nested under the metadata of a pod
annotations: annotations,
},
}
- Cách này đơn giản hơn nhiều so với căn thụt lề trong template YAML
- Kết quả được tạo là một đối tượng JSON có map annotation nằm dưới
metadata.annotations
{
"metadata": {
"annotations": {
"nginx.ingress.kubernetes.io/app-root": "/",
"nginx.ingress.kubernetes.io/enable-cors": true
}
}
}
- Việc thêm annotation vào đối tượng hiện có cũng có thể xử lý bằng toán tử
+ trong Jsonnet
local annotations = {
'nginx.ingress.kubernetes.io/app-root': '/',
'nginx.ingress.kubernetes.io/enable-cors': true,
};
{
metadata: {
annotations: annotations,
},
} + { // this adds another JSON object
metadata+: { // I'm using the + operator, so we'll append to the existing metadata
annotations+: { // same as above
something: 'nothing',
},
},
}
- Trong đối tượng kết quả,
something: "nothing" được thêm vào các annotation hiện có
{
"metadata": {
"annotations": {
"nginx.ingress.kubernetes.io/app-root": "/",
"nginx.ingress.kubernetes.io/enable-cors": true,
"something": "nothing"
}
}
}
- Trong ví dụ đơn giản, code có thể trông dài hơn, nhưng khi cấu hình trở nên phức tạp, khả năng thao tác đối tượng theo cách này sẽ rất hữu ích
kr8 xử lý cấu hình Kubernetes theo cách của Jsonnet
- kr8 sử dụng các phương pháp này để dễ dàng và đơn giản tạo cũng như thao tác cấu hình của nhiều cụm Kubernetes
- Luồng cốt lõi là tạo đối tượng cấu hình JSON rồi biến đổi theo cách cần thiết, thay vì lắp ghép template YAML bằng khoảng trắng và câu điều kiện
1 bình luận
Ý kiến trên Hacker News
Tôi giờ đã chán ngấy cấu hình viết bằng YAML. Đây là phần tôi ghét nhất ở GitHub Actions, thậm chí còn tệ hơn cả vấn đề độ ổn định
Mỗi khi thấy một công cụ hay nào đó yêu cầu file YAML cho cấu hình là tôi lập tức thấy bất an. Các ngôn ngữ cấu hình độc quyền như HCL của Terraform hay ASL của AWS Step Functions cũng vậy
Muốn có API khai báo thì không sao, nhưng tôi ước họ cho phép tạo phần khai báo đó bằng chương trình. Trải nghiệm cấu hình được khai báo và sinh ra từ mã tốt hơn rất nhiều, và AWS CDK làm điều này cực kỳ tốt
Bạn có thể viết định nghĩa hạ tầng đám mây bằng ngôn ngữ an toàn kiểu dữ liệu với hỗ trợ IDE tốt, mà không phải phụ thuộc vào plugin đã 2 năm rồi không được cập nhật
deno fmtcó bộ định dạng cho JSON nhưng không có cho YAMLTrình định dạng JSON là một binary duy nhất chạy trong vài mili giây, còn tự động định dạng YAML thì thực tế gần như phải dùng Prettier, mà Prettier lại phụ thuộc vào nửa hệ sinh thái NPM và mất khoảng 2 giây để khởi động và chạy
Vì thế, trong kho mã của công ty, mọi file YAML có thể đổi sang JSON đều đã được tôi chuyển hết sang JSON, và ít nhất với tôi thì hài lòng hơn nhiều. Không ai phàn nàn cả
Nhiều trình soạn thảo cũng hỗ trợ thẻ
$schemacủa JSON. Tôi đã thêm tính năng này vào sản phẩm, và thật tuyệt khi có thể chỉ cần bấm tab là tạo được file cấu hình mà không cần đọc tài liệuYAML cũng làm được với YAML language server, nhưng vì phím tab còn cần cho thụt lề nên tính tiện dụng không tốt lắm. JSON cũng không hoàn hảo, nhưng ít nhất thì văn bản
"no"không bị hiểu là giá trị trueChỉ cần thêm một bộ lọc để xóa chú thích trước khi đọc cấu hình là được mà? Chắc chắn không thể khó hơn việc đổi sang YAML
Tôi cũng không thật sự hiểu luận điểm rằng YAML dễ đọc hơn. Nỗi khổ khi xử lý cấu hình không đến từ việc đỡ phải phân tích vài dấu ngoặc nhọn hay ngoặc vuông trong vài giây, mà đến từ chuyện rất khó biết có gì sai trong hàng trăm dòng cấu hình chỉ vì thiếu khoảng trắng hoặc tab
Ansible cũng mắc cùng sai lầm đó, và vô số công cụ khác cũng vậy
Tôi không rõ CDK có hoạt động theo kiểu đó không. Khi thử dùng một chút thì nó khá khác với trải nghiệm “sinh CloudFormation” mà tôi đã tạo, nên tôi không thật sự cảm nhận được ưu điểm của CDK
Nó giống như biến vấn đề YAML/template thành vấn đề kế thừa/phép thuật. Tôi muốn nghe thêm trải nghiệm từ những người đã dùng AWS CDK, Terraform CDK và Pulumi
https://github.com/actions/runner/issues/1182
Tôi đồng ý rằng việc tạo template cho YAML khá là điên rồ, nhưng tôi vẫn luôn không hiểu vì sao không ngừng dùng các ngôn ngữ giả và chuyển sang ngôn ngữ lập trình thực thụ
Nếu cần logic phức tạp thì cứ dùng ngôn ngữ lập trình để sinh ra YAML/JSON/bất cứ thứ gì. Ruby, Python, hay bất kỳ ngôn ngữ nào khác đều cung cấp thứ bạn cần mà không cần đến các ngôn ngữ na ná kỳ quặc như Jsonnet hay Go template
Chỉ cần viết code thì sẽ ít mắc vào những vấn đề kỳ quái, khó nhìn thấu của engine template hơn rất nhiều. Dùng bất kỳ ngôn ngữ thực thụ nào cũng tốt hơn hẳn
Tôi cũng từng dùng Chef cho công việc tương tự, và vì đó là Ruby nên rất tiện để định nghĩa logic mong muốn cũng như dùng vòng lặp và biến đúng nghĩa
Tôi hiểu Ansible được thiết kế cho người không phải lập trình viên, nhưng với những ai đã quen lập trình cơ bản thì chẳng có địa ngục nào tệ hơn việc nhốt các playbook Ansible đầy thao tác điều kiện và lặp vào cú pháp dài dòng của template Jinja
Nhưng giờ thì cách làm phổ biến là mang parser JSON/TOML/YAML vào rồi tạo một hàm
readConfig, kể cả ở những chỗ mà một interpreter nhúng sẽ phù hợp hơnTừ góc nhìn nhà phát triển, việc tăng độ phức tạp cho format cấu hình dễ hơn là cung cấp khả năng nhúng ngôn ngữ hoàn chỉnh cùng binding cho ứng dụng. Vì thế có vẻ như người ta либо đã quên mất cách làm đó, либо thậm chí không nghĩ rằng nó khả thi
Từ thời Chef/Puppet, nhiều nơi đã bắt đầu nhét logic vào IaC rồi cuối cùng biến thành những bãi hỗn độn khổng lồ không thể nâng cấp cũng không thể bảo trì. Cách tiếp cận Chef/Pulumi cũng làm được, nhưng cần những người cực kỳ nghiêm khắc về phong cách và bảo trì
Với đội ngũ lớn và bảo trì lâu dài, tôi cho rằng mô hình Terraform/Puppet tốt hơn. HCL có thể khó chịu và việc dùng Python/TypeScript v.v. có thể đem lại cảm giác giải phóng, nhưng code thuần khai báo giúp ngăn được rất nhiều mớ spaghetti
Họ muốn nhét vào các yếu tố thiết kế ngôn ngữ đang thịnh hành, muốn tự hosting, lại còn muốn nó có thể dùng để viết web server đa luồng tốc độ cao, nên mọi thứ trở nên phức tạp về mặt khái niệm
Điều cần thiết là một ngôn ngữ đồ chơi đơn giản kiểu Logo dành cho kỹ sư hệ thống/DevOps. Lý tưởng nhất là phải có thể giải thích bằng một cuốn sách chỉ dày cỡ sách K&R C
Nó nên có kiểu động, các cấu trúc điều khiển có thể học trong một cuối tuần, không có threading hay concurrency, không có hướng đối tượng hay kế thừa, có thiết kế hàm/mô-đun, và có mô hình FFI để dễ gọi sang cũng như được gọi từ ngôn ngữ và framework khác
Vấn đề là những người mê ngôn ngữ không tự kiềm chế được nên cứ tiếp tục thêm tính năng, rồi những thứ đó đi vào thư viện cốt lõi và style guide, khiến cả người mới bắt đầu cũng phải học hết
Chính tôi cũng sẽ muốn thêm các hàm kiểu
each/mapvào mảng/hash map và đưa vào hàm hạng nhất cùng closure, nhưng đó có thể là một sai lầm. Đã có các ngôn ngữ hàm thuần bất biến cho cấu hình, nhưng hơn 95% số người dùng YAML template không muốn học lập trình theo kiểu đó, nên chúng khó phổ biến rộng rãiCấu hình sẽ đơn giản hơn, dễ tiêu thụ hơn và cũng dễ tài liệu hóa hơn. Nhưng khi viết file cấu hình thì nên dùng ngôn ngữ lập trình, và nếu có thể thì nên là ngôn ngữ kiểu tĩnh có kiểm tra lỗi, tự động hoàn thành và tài liệu inline
AWS CDK là ví dụ điển hình. Việc viết CloudFormation thuần túy rất đau đớn, nhưng CDK không phải là chồng thêm khả năng lập trình lên CloudFormation mà là sinh ra CloudFormation. Đầu vào mà AWS tiêu thụ vẫn là CloudFormation tương đối đơn giản và ổn định
Vừa nhìn tiêu đề là tôi tưởng sẽ nói về Kubernetes.
Kubernetes API khá trực quan và có lược đồ JSON được định nghĩa tốt. Phần lớn thời gian học k8s lẽ ra nên dành để hiểu cách dùng API, nhưng trên thực tế lại bị dùng để mò cách sử dụng Helm chart.
Tôi không nghĩ Jsonnet, Ksonnet, Nu hay CUE đã đạt được mức độ phổ biến lớn đến vậy. Có vẻ đa số dùng Kustomize hơn, vì nó tương đối trực quan và được tích hợp sẵn trong
kubectl.Công cụ mong muốn là thứ có thể cung cấp kiểm tra kiểu theo lược đồ k8s, kiểm định và cảnh báo ngừng hỗ trợ phiên bản cho người viết định nghĩa, đưa cho người dùng một đầu ra duy nhất dễ kiểm tra, thất bại theo kiểu nguyên tử nếu cụm không hỗ trợ bất kỳ đối tượng/phiên bản nào, và phải được tích hợp vào chuỗi công cụ mặc định.
Các script TypeScript chạy bằng Bun hay Deno, xuất ra một hàm nhận đối số và trả về danh sách định nghĩa, có vẻ sẽ khá hợp khi đi cùng
deno compilevà tương tự, nhưng lại vi phạm điều kiện phải tích hợp vào chuỗi công cụ mặc định.Bạn được che chắn khỏi các chi tiết cấp thấp, nhưng khi có sự cố thì phải đối mặt với một chồng trừu tượng khổng lồ khiến việc chẩn đoán và gỡ lỗi khó hơn nhiều.
Việc tìm ra chính xác điều gì đang diễn ra trở nên khó hơn rất nhiều, và bạn bị phụ thuộc vào các lớp trừu tượng, đồng thời phải gánh cả những bản cập nhật do nhà cung cấp tung ra lẫn các vấn đề khác trong đồ thị phụ thuộc.
Nó gần như làm được điều chúng tôi cần mà không gặp vấn đề gì, chạy đa nền tảng và có thể dùng xuyên qua nhiều ngôn ngữ. Tôi đã nhúng nó vào các file thực thi C++, .NET và JVM.
Cấu hình JSON đầu ra có thể đi cùng một hệ công cụ rất phong phú mà các lựa chọn thay thế như toml/yaml/hocon/ini v.v. khó mà có được. Tôi từng thử dùng HOCON ở các ngôn ngữ không phải JVM, nhưng luôn vướng phải một trường hợp biên nào đó.
Nhưng khi thực sự phải quản lý một hệ thống lớn, cuối cùng bạn cũng không thể tránh được lợi ích của template.
Nhìn cách các lập trình viên ít suy nghĩ đến mức nào về việc xử lý cấu hình cho đúng thật buồn cười.
Nó trông như chỉ là một tập hợp khóa và giá trị được lưu trong file hoặc được sinh ra bằng mã, nhưng thật ra đó là tất cả mọi thứ. Đó chính là lập trình.
Mọi thứ đều là cấu hình, và mọi đối số hàm cũng là một dạng cấu hình. Mọi cấu hình trong file bên ngoài rồi cũng sẽ trở thành đối số hàm theo cách nào đó.
Vấn đề nằm ở cách biểu diễn bằng văn bản thuần của mã. File cấu hình khai báo trông có vẻ tốt vì bạn có thể nhìn mọi thứ ở một chỗ, nhưng khi biến cấu hình thành chương trình thì rất khó tìm ra cần thay đổi ở đâu.
Nếu mã có thể chạy trực tiếp để hiển thị biểu diễn của cấu hình cuối cùng, và có thể lần vết mỗi giá trị cấu hình cuối cùng được tạo ra như thế nào, thì sẽ không thành vấn đề. Nhưng dù tính năng này khá đơn giản, lại không có hệ thống nào được thiết kế theo cách đó. Cấu hình lúc nào cũng là thứ được nghĩ đến sau cùng.
Nếu mở rộng ý tưởng này ra toàn bộ lập trình, bạn sẽ phải có khả năng nhìn thấy toàn bộ mã phụ thuộc vào một giá trị cấu hình, cùng với các phép biến đổi của nó.
Ngoài ra, phần lớn cấu hình mang tính quan hệ/đồ thị, nên có lẽ tốt hơn là đặt chúng trong một cơ sở dữ liệu trung tâm. Các giá trị cấu hình khác nhau có liên hệ với nhau. Vì vậy, cấu hình nên được xem trong một trình chỉnh sửa cơ sở dữ liệu/đồ thị.
Một khi rời khỏi văn bản thuần, mọi thứ bắt đầu đơn giản hơn rất nhiều, nhưng các tính năng ngôn ngữ đã nói ở trên vẫn cần thiết.
Các cấu hình đang cố dùng quy ước đặt tên để gom các biến có liên quan lại với nhau.
Họ muốn chuyển sang cấu trúc dữ liệu lồng nhau thực sự, có lẽ là JSON, nhưng các kỹ sư thì tuyệt đối không muốn viết code, nên cấu hình dưới dạng mã là điều không thể, mà nó cũng có các nhược điểm đã nói ở trên.
Ý tưởng tiếp theo là cần có cách hiển thị và chỉnh sửa cấu hình tốt hơn. Tôi đã nghĩ đến một UI trực quan cho phép khám phá biểu diễn của sản phẩm cuối cùng, chọn các bộ phận và chỉnh sửa tham số theo cách đó.
Tôi muốn biết liệu đây có phải hướng đi đúng không. Nếu không, mong được giải thích thêm một chút. Trọng tâm của ứng dụng này là cấu hình.
Tệ hơn nữa là ở những nơi như CI/CD, YAML gần như trở thành một ngôn ngữ lập trình. Mà lại là một thứ rất dài dòng, không trực quan, đặc tả kém và còn khác nhau tùy nhà cung cấp.
Dù có DTD và kiểm định XML, nó vẫn có đặc tính quen thuộc là chỉ nổ muộn và đưa ra các thông báo lỗi khó hiểu.
Khi đó rất nhiều sự thất vọng đổ dồn lên XML, nhưng nhìn vào địa ngục YAML giữa thập niên 2020 thì vấn đề không nằm ở bản thân ngôn ngữ đánh dấu.
Tôi thực sự ghét việc chôn logic ở đâu đó bên trong template YAML.
[0] https://tanzu.vmware.com/developer/guides/ytt-gs/
Việc Helm thắng thế thật sự rất đáng buồn. Tôi làm công việc liên quan đến k8s mã nguồn mở ở công ty, và 100% người dùng đều yêu cầu tạo Helm chart, nên cuối cùng vẫn phải làm
Làm việc với nó rất khổ sở. Tên file là kiểu
foo.yamlnhưng thực ra không phải YAML, nên editor không thể hỗ trợ. Phải đẩy toàn bộ dữ liệu quaindent 4để canh đúng thụt lề YAMLĐiều u ám nhất là phải phơi bày lại toàn bộ tính năng của Kubernetes theo cách riêng của nó. Nếu ai đó muốn thêm
deployment.spec.template.spec.fooBarsthì phải thêmdeploymentFooBarsvàovalues.yamlrồi nối lại. Tính năng nào cũng lặp lại như vậyĐây đúng là một ví dụ đi sai hướng của kiểu “cái tệ lại thành cái tốt”. Tôi cũng từng làm trò kinh khủng kiểu
sed -e s/$FOO/foo/gđể tự triển khai template, và có lẽ Helm cũng bắt đầu như thế. Kết quả là một mớ hỗn độnCá nhân tôi đã dùng Kustomize từ trước khi nó được đưa vào
kubectl, và luôn khá hài lòng. Nó có nhiều điểm kỳ quặc, nhưng ít nhất nó hiểu ý nghĩa của các đối tượng được tạo ra nên tiết kiệm thời gianJsonnet tốt hơn nhiều. Chúng tôi phân phối kèm triển khai Envoy cho routing traffic phức tạp như một phần của ứng dụng k8s, và cấu hình Envoy thì dài dòng nhưng lại rất dễ xử lý bằng Jsonnet: https://github.com/pachyderm/pachyderm/blob/master/etc/gener...
Tôi đang nghiêm túc cân nhắc transpile jsonnet sang ngôn ngữ template Go rồi triển khai mọi thứ bằng Jsonnet. Ít nhất cũng sẽ dễ bảo trì hơn phần nào, và
helm installvẫn sẽ chạy bình thường nên chẳng ai nhận raNhưng có vẻ Helm sẽ là dấu chấm hết của Kubernetes. Nếu có công cụ cạnh tranh nào cho việc cấp phát tài nguyên tính toán/chạy container xuất hiện cùng một ngôn ngữ cấu hình tử tế, mọi người sẽ chuyển sang chỉ sau một đêm
sed -e s/$FOO/foo/g, có lẽ nên xem qua envsubst như một giải pháp chuẩn hơn và tốt hơn một chútNếu đang nói đến chuyện template hóa hoặc chỉnh sửa Helm chart bằng jsonnet, Tanka cũng có thể hữu ích: https://tanka.dev/helm
Nhưng những nơi tôi từng làm vẫn sợ thay đổi nên cứ tiếp tục dùng tf/hcl và helm. Ít nhất trong dự án cá nhân thì còn thấy dễ thở hơn đôi chút
Tôi nghĩ ở đây có vấn đề. Nhưng tôi không chắc kiểu người chọn YAML làm ngôn ngữ cấu hình có xem đó là vấn đề hay không
Có một xung đột trực tiếp giữa cách biểu diễn dữ liệu hướng con người và cách biểu diễn dữ liệu hướng máy tính. Máy tính thích thứ gì đó giống Lisp, còn con người thích thứ gì đó giống Python
Nếu bạn là người muốn thao tác cấu hình Kubernetes bằng máy tính, việc Kubernetes dùng YAML hẳn sẽ khiến bạn khó chịu âm ỉ. Nhưng cộng đồng Kubernetes dường như chủ yếu là kiểu người thiên về YAML, nên họ cần gì phải quan tâm đến việc file cấu hình trở thành cơn ác mộng khi đưa logic lập trình vào
Đây chính là nhược điểm của YAML trong tình huống này, và tôi nghĩ những người làm k8s nhìn chung đủ thông minh để lường trước điều đó
Câu “YAML là tập cha của JSON” theo tôi không đúng trong thực tế, bất kể tác giả đặc tả có viết gì trong tài liệu. Nếu bạn đổi toàn bộ cấu hình YAML sang JSON thì đội DevOps sẽ nổi giận
Hai định dạng dữ liệu có thể biểu diễn cùng một ngữ nghĩa, nhưng mọi ngôn ngữ cùng biên dịch xuống một kiến trúc CPU cũng vậy. JSON và YAML trong thực tế là hai thứ riêng biệt, và trộn chúng với nhau không phải ý hay
Tất nhiên rồi mọi người vẫn viết bằng tay, và khi không chịu nổi nữa thì bắt đầu gắn thêm template. Có vẻ mọi chuyện lúc nào cũng diễn ra như vậy
Văn bản viết tay không bị thay thế bởi văn bản tuần tự hóa cấu hình do máy sinh ra, mà thay vào đó bị thay bằng chính văn bản viết tay ban đầu nhưng được gắn thêm template
Nguyên tắc cá nhân của tôi là không nên dùng nội suy chuỗi để sinh ra mã cho máy đọc. Ngôn ngữ template rốt cuộc cũng chỉ là nội suy chuỗi được làm cho đẹp hơn
Tôi đã thấy hậu quả của cả SQL injection lẫn cross-site scripting. Chừng nào còn tiếp tục ném văn bản tùy ý vào trình thông dịch thì những chuyện này vẫn sẽ tiếp diễn
Vì vậy tôi cũng cho rằng không nên dùng file template để tạo HTML
Các lựa chọn thay thế cho ngôn ngữ template HTML có Ruby Haml và JavaScript Pug. Những ngôn ngữ này cung cấp một cách xác định rõ ràng toàn bộ cây gồm thẻ, thuộc tính và nút văn bản
Nếu bạn không thích kiểu thụt lề có ý nghĩa của Python thì JavaScript có JSX. Phần trông như HTML trong JSX được biên dịch thành các biểu thức
createElementđể tạo cây tài liệu web, và cây đó có thể được xuất ra HTML nếu cầnHaml, Pug và JSX không phải là ngôn ngữ template, dù chúng có thể xuất ra HTML. Tương tự,
JSON.stringify(myObj)cũng không phải là ngôn ngữ template cho JSONMã cho máy đọc, nếu có thể, nên được sinh ra bằng công cụ hiểu và tận dụng cấu trúc đã biết của ngôn ngữ đích
Haml là một hệ thống template để tránh mã nội tuyến trong tài liệu web và làm HTML gọn gàng hơn, còn Pug là một template engine giàu tính năng cho Node.js
Tôi có thể đồng ý rằng xét nghiêm ngặt thì JSX không phải ngôn ngữ template
Cuối cùng thì tất cả chúng đều được biên dịch thành HTML. Chỉ là thay vì nội suy chuỗi, chúng được parse thành cây cú pháp rồi render thành HTML dựa trên hiểu biết nội bộ về cấu trúc hợp lệ
Template YAML là nội suy chuỗi được làm cho đẹp hơn, và không phải ngôn ngữ template, hoặc ít nhất là một ngôn ngữ template được hiện thực rất tệ
Quy tắc cá nhân của tôi là mỗi khi một giá trị đi vào chuỗi thì nó phải luôn được mã hóa cho đúng
Trước đây tôi đã viết về chủ đề này: https://kevincox.ca/2022/02/08/escape-everything/
Tóm lại, mọi chuỗi đều có một định dạng cần tuân theo, như HTML, SQL, hay đầu ra terminal cho con người đọc. Mỗi khi đưa một giá trị vào chuỗi, bạn phải mã hóa nó đúng theo định dạng đó, nhưng gần như chúng ta không bao giờ làm vậy
Chúng tôi đang chuyển sang cuelang [1]. Cá nhân tôi thấy nó có thiết kế tốt hơn Jsonette
Kubernetes đã có sẵn cơ chế điều phối trạng thái, nên thứ còn thiếu trong cấu hình này chỉ là xóa, và giờ có thể giải quyết bằng tính năng prune [2]
[1] https://cuelang.org/docs/integrations/k8s/
[2] https://kubernetes.io/blog/2023/05/09/introducing-kubectl-ap...
Một vài thông báo lỗi hơi khó diễn giải, nhưng bù lại nó bắt được rất nhiều lỗi từ trước nên hoàn toàn chấp nhận được. Những lần hiếm hoi giờ đây phải tự viết yaml thì so ra thấy chán kinh khủng
Trong những lúc thế này tôi thường chen vào bằng câu “bạn đã nghe về vị cứu tinh CUELang của chúng ta chưa?”: https://cuelang.org/
Nó vẫn chưa Turing-complete, nhưng đủ biểu đạt để khử trùng lặp, và bạn có thể định nghĩa schema lẫn dữ liệu bằng cùng một ngôn ngữ trong cùng file hoặc file riêng, đồng thời còn có union type
Nó có thể sinh YAML hoặc JSON, và có thể validate chính nó hoặc các file YAML/JSON
Nhược điểm lớn nhất là hiện tại chỉ có implementation bằng Go, nên có thể cần subprocess hoặc FFI
Sau đó nó tạo file JSON cho từng ứng dụng, rồi một công cụ nào đó tạo ra định nghĩa XML, định nghĩa đó được áp vào một file XLS do các kiến trúc sư sở hữu, và từ đó nó nhả ra YAML để áp vào Helm chart
Các chart triển khai client k8s, và client đó tương tác với cluster chính bằng JSON thông qua API
Mất kha khá thời gian, nhưng chúng tôi đang dùng công cụ tốt nhất cho từng công việc