Chắc chăn là anh em đã nghe nói nhiều spinnaker được sử dụng để deploy lên K8s.
Bắt đâu làm theo Nim thôi
1) Preparing.
1.1) Create helm package
Đầu tiền bạn cần có 1 helm chart.
Tạo 1 helm chart
helm create nimtechnology
Lúc này nó sẽ tạo ra 1 folder nimtechnology.
Giờ thược hiện package lại helm chart
helm package ./nimtechnology/
Bạn tạo repo git và push mọi thứ lên đó.
https://github.com/mrnim94/kubernetes-manifest
1.2) Configure the GitHub token on spinnaker.
Bạn cần tạo 1 personal access token.
Sau đó run các command bên dưới trên spinnaker_halyard
hal config features edit --artifacts true hal config artifact github enable export GITHUB_ACCOUNT_NAME=<user_name_github> hal config artifact github account add ${GITHUB_ACCOUNT_NAME} \ --token <personal_access_token>
2) Create a pipeline of spinnaker so manually.
Giơ mình tạo 1 template bằng tay trước.
Tạo 1 application trước
Giờ tạo pipeline
giờ tạo các stage trong 1 pipeline
Stage 1: Find Artifacts From Resource
Mục đích của case này là lấy được số replicate
Ingnore the failure: chỗ này nên tích vì nếu đây là lần đầu tiên chạy pipeline thì chắc chắn spinnaker không get được thông tin gì(có workload đâu mà get)
2.1) Deploy according to the current replication of pod.
stage 2 là: Bake (Manifest)
ở phần url của github bạn cần lưu ý chút
ví dụ file package của mình ở link này:
https://github.com/mrnim94/kubernetes-manifest/blob/master/nimtechnology-0.1.0.tgz
thì bạn sẽ input vào spinnaker link sau:
https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology-0.1.0.tgz
Giờ bạn config để spinnaker thấy được file value của helm
Phần url này cũng cần lưu ý như bên trên:
https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology/values.yaml
Bạn còn nhớ ở stage1 chúng ta đã có thể lấy được thông tin của workload(deployment)
Giờ chúng ta action đưa thông tin replicate từ stage 1 to stage 2
Mục tiêu là khi deploy không làm thay đổi số replicas của workload.
replicaCount
${ #stage("Get Manifest").context["manifest"]["spec"]["replicas"] }
${#stage("Get Manifest")["status"].toString() == "SUCCEEDED"}
Cấu hình ở trên nghĩa là nếu stage “Get Manifest” thành công thì mới chạy stage “Generate K8s Manifest 1“
==> vì ở Generate K8s Manifest 1 chúng ta đang lấy số replicas từ Get Manifest ,nếu Get Manifest thì Generate K8s Manifest 1 sẽ jenkins ra file manifest sai -> deploy sẽ bị lỗi.
Lăn xuống dưới chúng ta sẽ tạo file manifest cho bước sau để deploy thực hiện đặt tên lại cho đẹp
Giờ đến bước deploy lên k8s
${#stage("Generate K8s Manifest 1")["status"].toString() == "SUCCEEDED"}
2.2) Deploy according to values.yaml
Nếu stage ở mục 2.1 bị fail thì chúng ta sẽ deploy và lấy số replicacount theo như file values.
Sau đây bước gen manifest nếu Get Manifest bị failed
Giờ config deploy theo Manifest 2
${#stage("Generate K8s Manifest 2")["status"].toString() == "SUCCEEDED"}
Xong khi hoàn tất bạn ần save
2.3) Check the result
Nếu mà lần đầu tiên tạo deployment thì như sau:
Lần thứ 2 deploy:
Giờ mình sẽ export template cho các bạn dễ kiểm tra
{ "keepWaitingPipelines": false, "lastModifiedBy": "anonymous", "limitConcurrent": true, "spelEvaluator": "v4", "stages": [ { "account": "default", "completeOtherBranchesThenFail": false, "continuePipeline": true, "failOnFailedExpressions": false, "failPipeline": false, "location": "default", "manifestName": "deployment default-nimtechnology", "mode": "static", "name": "Get Manifest", "refId": "1", "requisiteStageRefIds": [], "restrictExecutionDuringTimeWindow": false, "type": "findArtifactsFromResource" }, { "completeOtherBranchesThenFail": false, "continuePipeline": false, "evaluateOverrideExpressions": false, "expectedArtifacts": [ { "defaultArtifact": { "customKind": true, "id": "98c0e91b-8b99-4d02-9fcf-c52d066f22ee" }, "displayName": "Manifest 1", "id": "5f18c089-9347-4e3b-9d1e-43c57347a984", "matchArtifact": { "artifactAccount": "embedded-artifact", "customKind": false, "id": "b505ac48-b63b-438a-8cab-96955c11a985", "name": "Manifest 1", "type": "embedded/base64" }, "useDefaultArtifact": false, "usePriorArtifact": false } ], "failOnFailedExpressions": false, "failPipeline": true, "inputArtifacts": [ { "account": "mrnim94", "artifact": { "artifactAccount": "mrnim94", "id": "445e4ef5-bf73-4a8c-a2c3-6af72b0211f7", "name": "nimtechnology-0.1.0.tgz", "reference": "https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology-0.1.0.tgz", "type": "github/file" } }, { "account": "mrnim94", "artifact": { "artifactAccount": "mrnim94", "id": "3cea7c6f-e6e2-47e2-9601-f3117f03f5ba", "name": "nimtechnology/values.yaml", "reference": "https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology/values.yaml", "type": "github/file" } } ], "name": "Generate K8s Manifest 1", "namespace": "default", "outputName": "default", "overrides": { "replicaCount": "${ #stage(\"Get Manifest\").context[\"manifest\"][\"spec\"][\"replicas\"] }" }, "rawOverrides": false, "refId": "2", "requisiteStageRefIds": [ "1" ], "stageEnabled": { "expression": "${#stage(\"Get Manifest\")[\"status\"].toString() == \"SUCCEEDED\"}", "type": "expression" }, "templateRenderer": "HELM3", "type": "bakeManifest" }, { "account": "cluster1", "cloudProvider": "kubernetes", "manifestArtifactId": "5f18c089-9347-4e3b-9d1e-43c57347a984", "moniker": { "app": "manual-create-app" }, "name": "Deploy (Current ReplicaSet)", "namespaceOverride": "default", "refId": "3", "requisiteStageRefIds": [ "2" ], "skipExpressionEvaluation": false, "source": "artifact", "stageEnabled": { "expression": "${#stage(\"Generate K8s Manifest 1\")[\"status\"].toString() == \"SUCCEEDED\"}", "type": "expression" }, "trafficManagement": { "enabled": false, "options": { "enableTraffic": false } }, "type": "deployManifest" }, { "completeOtherBranchesThenFail": false, "continuePipeline": false, "expectedArtifacts": [ { "defaultArtifact": { "customKind": true, "id": "2240fe9d-52c5-49ce-9fe2-5b5133f72a8e" }, "displayName": "Manifest 2", "id": "c64b9cea-495f-43c0-b391-7fbd0e96c014", "matchArtifact": { "artifactAccount": "embedded-artifact", "customKind": false, "id": "784649d9-02a5-4f19-9b4d-ed87b6b8df96", "name": "Manifest 2", "type": "embedded/base64" }, "useDefaultArtifact": false, "usePriorArtifact": false } ], "failPipeline": true, "inputArtifacts": [ { "account": "mrnim94", "artifact": { "artifactAccount": "mrnim94", "id": "94e81b5f-ee7d-49a6-a059-5cbf85d0823e", "name": "nimtechnology-0.1.0.tgz", "reference": "https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology-0.1.0.tgz", "type": "github/file" } }, { "account": "mrnim94", "artifact": { "artifactAccount": "mrnim94", "id": "996f2c8b-178d-4060-a70a-5a06c8e63a27", "name": "nimtechnology/values.yaml", "reference": "https://api.github.com/repos/mrnim94/kubernetes-manifest/contents/nimtechnology/values.yaml", "type": "github/file" } } ], "name": "Generate K8s Manifest 2", "namespace": "default", "outputName": "default", "overrides": {}, "rawOverrides": true, "refId": "4", "requisiteStageRefIds": [ "1" ], "stageEnabled": { "expression": "${#stage(\"Get Manifest\")[\"status\"].toString() == \"FAILED_CONTINUE\"}", "type": "expression" }, "templateRenderer": "HELM3", "type": "bakeManifest" }, { "account": "cluster1", "cloudProvider": "kubernetes", "manifestArtifactId": "c64b9cea-495f-43c0-b391-7fbd0e96c014", "moniker": { "app": "manual-create-app" }, "name": "Deploy (Value ReplicaSet)", "namespaceOverride": "default", "refId": "5", "requisiteStageRefIds": [ "4" ], "skipExpressionEvaluation": false, "source": "artifact", "stageEnabled": { "expression": "${#stage(\"Generate K8s Manifest 2\")[\"status\"].toString() == \"SUCCEEDED\"}", "type": "expression" }, "trafficManagement": { "enabled": false, "options": { "enableTraffic": false } }, "type": "deployManifest" } ], "triggers": [], "updateTs": "1652515439000" }
Q&A
Tên của workload(deployment) được tạo ntn?
Mình cũng đã thắc mắc sao workload lại có tên như thế
name của workload từ 2 thứ
Đầu tiên là từ config trong helm chart.
thứ 2 là config trong spinnaker.
Bạn sẽ hỏi cách trên manual như thế vậy với 1000 deployment thì tạo gãy tay à?
Có cách đầu tiên bạn export template như mình và chỉ cần sửa 1 vài thông tin rồi lại import vào spinnaker bằng spin (command line).
Hoặc ở bài tiếp theo mình sẽ chỉ 1 cách khác
Spinnaker Pipeline Expressions
One of the nice features in Spinnaker is pipeline expressions – a way to do a little dynamic calculation based on the current state of the pipeline.
Bạn thấy mình cũng có sử dụng trong stage “Get Manifest”
replicaCount
${ #stage("Get Manifest").context["manifest"]["spec"]["replicas"] }
Mình xin giải thích 1 chút chỗ này!
Nếu mà stage Get Manifest thì bước tiếp theo Generate K8s Manifest 1 sẽ lỗi mà manifest của chúng ta sẽ như sau:
Ngoài cách trên là mình tạo thêm 1 stage nữa thì chúng ta có thể sửa trong helm chart.
... ... {{- $replicaCount := regexFind "\\d+\\.\\d+" .Values.replicaCount }} ... ... progressDeadlineSeconds: 1200 {{- end }} {{- if .Values.ingress.canary.enabled }} {{- if $replicaCountCanary }} replicas: {{ $replicaCountCanary }} {{- end }} {{- else if $replicaCount }} replicas: {{ $replicaCount }} {{- else }} replicas: 1 {{- end }} selector: matchLabels
Nếu có lỗi j thì replicas sẽ bằng 1
Refence links:
https://www.paraesthesia.com/archive/2018/08/07/figuring-out-spinnaker-pipeline-expressions/
https://spinnaker.io/docs/guides/user/pipeline/expressions/