Số 03. Dev gà - sops - k8s - swagger (& OpenAPI)
Hello! I'm Zu.Doan
Series này mình muốn tạo ra để note lại những kiến thức, đoạn code, issues, solutions mà mình gặp phải trong quá trình làm việc với Project thực tế.
1. Sử dụng sops để encrypt/decrypt secrets value (k8s, aws)
Trong K8s khi cần lưu những dữ liệu nhạy cảm (api key, username, password của một dịch vụ bên thứ 3, database,...), chúng ta sẽ lưu các thông tin đó vào file secret (kind: Secret). Tuy nhiên chúng ta không thể lưu trực tiếp các giá trị đó một cách open (nhìn rõ mồn một) được. Ở đây mình ví dụ cách sử dụng cho trường hợp service đang được triển khai bằng k8s trên AWS:
- Cần sử dụng đúng credentials của tài khoản AWS đang triển khai k8s cluster.
- Cần cài đặt ứng dụng sops (tham khảo tại docs )
- Cần tạo file secrets.yaml với nội dung như sau
apiVersion: v1 kind: Secret metadata: name: secret-sa-sample annotations: kubernetes.io/service-account.name: "sa-name" type: kubernetes.io/service-account-token data: # You can include additional key value pairs as you do with Opaque Secrets secret_key_1: {secret_value_1_base64} secret_key_2: {secret_value_2_base64}
Lưu ý những giá trị {secret_value_1_base64}, {secret_value_2_base64} đó là giá trị đã được mã hóa base64.
Để mã hóa base64 một giá trị ta sử dụng câu lệnh sau:
echo "{value}" | base64 -w 0
- Sử dụng sops để encrypt file secret
Giả sử ta có 1 file secrets.yaml với nội dung như sau:
apiVersion: v1
kind: Secret
metadata:
name: secret-sa-sample
annotations:
kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
# example: secret_key: secret_value
secret_key: c2VjcmV0IHZhbHVlCg==
Run lệnh sau để thực hiện mã hóa
sops --encrypt secrets.yaml > secrets.yaml.enc
Lúc này thay ta sẽ nhận được 1 thông báo hết sức khó hiểu =)))

Lỗi trên xuất hiện do ta thiếu cấu hình KMS (AWS) cho secret file. Để fix lỗi trên ta cần tạo 1 file với tên .sops.yaml (đứng ngang hàng với secret file) với nội dung như sau:
creation_rules:
- kms: {aws KMS key - bạn có thể tạo KMS key tại link: https://ap-southeast-1.console.aws.amazon.com/kms/home?region=ap-southeast-1#/kms/keys }
Sau khi setup thành công KMS, chúng ta sẽ thử lại lệnh mã hóa
sops --encrypt secrets.yaml > secrets.yaml.enc
Kết quả ta nhận được là file secrets.yaml.enc
apiVersion: ENC[AES256_GCM,data:ths=,iv:eIN8R9quqMyRLayuFjPt5RJIr0X9V5q4QydeKyv8K68=,tag:YcxVygpEF7Z4wflRJtAPmQ==,type:str]
kind: ENC[AES256_GCM,data:dTkGnq8t,iv:tYcyqmuhPeQkHVVazbfyTYl/WO45a/+IbU2JS5/BmaA=,tag:M9OkxET66DU2k/EcsqtLeQ==,type:str]
metadata:
name: ENC[AES256_GCM,data:d1xEa8G3EZ9skBJDTaU9pA==,iv:Rab3HB6xcIBoqc3EWvpOK8HOScYnEuwg6V8d+Q1oczg=,tag:IriA5KTSRJMVo74hb+DKlQ==,type:str]
annotations:
kubernetes.io/service-account.name: ENC[AES256_GCM,data:XJ1ra23Aeg==,iv:bbTynlCxSCSkCFSpUQRMBhI9hFYkOM3YoFAFD0+vN9w=,tag:fwTC3+N6WEgMh+f/XHSlXQ==,type:str]
type: ENC[AES256_GCM,data:oDxnjmGmMJJvCuHfyXb6t4+aT5dQcN4Xqf3ZAnY80TIHtkA=,iv:5UtEBljH62gAvvlni6Q7Jx6qWA317HfaEdzE2qn1MVE=,tag:9vFBnh3HFE3v1fTVEiI3NQ==,type:str]
data:
#ENC[AES256_GCM,data:wMi3JjnDghPYCmwaHM1t3YBXL8svEW8nBCAXMAeDYuhS+w==,iv:/I331jqrENp66wYQVUXRdg43fGb8TetOge2xoXE98jA=,tag:IhsrMrx91of3Ol68YFkHZg==,type:comment]
secret_key: ENC[AES256_GCM,data:sxIun5Gk9cWzUutPdykwsmM0tp4=,iv:pgnQd04LWhXqtmVTSWRSYk55k2ozn2W0REWVmBf4lU0=,tag:+phXxEtZgFcuT3w0gzfVMQ==,type:str]
sops:
kms:
- arn: {aws KMS key của bạn}
created_at: "2021-07-17T15:19:04Z"
enc: {enc data}
aws_profile: ""
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2021-07-17T15:19:05Z"
mac: ENC[AES256_GCM,data:ZqjjhVY1sDPMQF1Xl/QihWc+KDpHzUH93R0rluAr9OT33lDjEwkocEaqUPqKy+Sc+UGgLGNoaAUyDeYVMdiQuEg7xuASOFyGjoEw8aFEu9ovjTR560IJlcdepLo8hSuCp+nNm3C+8NOfcVIKsodmoTEhP8++ukDiQsdhqCHRLs8=,iv:iyIrePPb/oW88yjeSd13398KJKlhLBTujM58ep9WXO8=,tag:tcSGZTAgGXphX6zUZLhvvQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.1
Hehe, good job!
* Tham khảo:
- https://github.com/mozilla/sops#encrypt-or-decrypt-a-file-in-place
- https://github.com/mozilla/sops/issues/367
2. Xóa job k8s
Bối cảnh ở phần này là trong khi mình tạo 1 job trên k8s thì mình đã chỉ định sai tên file để thực thi job đó, tất nhiên sau đó mình chỉnh sửa lại file job đó và deploy lại lên cluster.
File bị lỗi tên file thực thi
apiVersion: batch/v1
kind: Job
metadata:
name: my-first-job
spec:
template:
spec:
containers:
- name: api-xxx
image: {registry-image:tag}
command: ["node", "./dist/scripts/myJob.js"] <= ở đây mình đặt sai tên file thực thi, tên đúng là **myFirstJob.js**
restartPolicy: Never
Tuy nhiên vì tên job không thay đổi, dẫn đến việc k8s check các thông tin liên quan đến job đó và phát hiện sự thay đổi trong file job và báo lỗi
The Job "my-first-job" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"",...{còn rất nhiều thông tin khác}
Đại ý nội dung của lỗi trên muốn nói đến lỗi là khi job template của mình đã được tạo thì mình không được sửa đổi template đó (như đã nói ở trên, mình đã chỉnh sửa tên file thực thi job)
Do đó mình cần xóa các job đã tạo trước đó đi để việc kiểm tra job created ở trên bị loại bỏ
kubectl delete jobs/{job name} -n {name space}
Sau khi xóa các job created, mình thực hiện deploy lại service và job đã run success.
* Tham khảo:
- https://portal2portal.blogspot.com/2021/04/penny-dropped-why-is-my-job-immutable.html
3. Swagger & OpenAPI có rule require true cho params nằm trên path
Trong lúc mình code NestJs & Swagger thì gặp trường hợp như sau:
- Có 2 controller cùng trỏ tới cùng 1 service với sự khác nhau ở 2 controller đó là 1 controller có param (
/category/:id) và controller còn lại thì không có param (/category) - Trong service dùng chung của 2 controller kể trên thì có check điều kiện là nếu có id thì sẽ làm nhiệm vụ A ngược lại sẽ làm nhiệm vụ B
- Mình bắt đầu nghĩ là sao lại ko gộp vào 1 controller và đưa param id trở thành optional (
/category/:id?), thế là mình bắt đầu refactor lại code 2 controller thành 1. - Bất ngờ xảy ra khi mình test controller đã gộp đó trên swagger, kết quả trả về của id khi mình không truyền vào thì nó sẽ bị gán bằng "," (dấu phẩy - comma). Thật lạ!!!
- Sau một hồi đi search thì mình tìm được bài viết này . Đại ý nói đến việc swagger sử dụng OpenAPI làm đặc tả về API do vậy nó đang tuân theo OpenAPI-specification nên đối với params nằm trên path, nó sẽ bắt buộc require = true cho những params đó. ( refer doc )
