Triển khai ứng dụng cho nhiều cụm Kubernetes bằng Helm

Cách Dailymotion sử dụng Kubernetes: Triển khai ứng dụng

Tại Dailymotion, chúng tôi đã bắt đầu sử dụng Kubernetes trong sản xuất từ ​​3 năm trước. Tuy nhiên, việc triển khai ứng dụng trên nhiều cụm rất thú vị, vì vậy trong vài năm qua, chúng tôi đã cố gắng cải thiện các công cụ và quy trình làm việc của mình.

Nó bắt đầu từ đâu

Ở đây chúng tôi sẽ đề cập đến cách chúng tôi triển khai các ứng dụng của mình trên nhiều cụm Kubernetes trên khắp thế giới.

Để triển khai nhiều đối tượng Kubernetes cùng một lúc, chúng tôi sử dụng Quản lývà tất cả các biểu đồ của chúng tôi được lưu trữ trong một kho lưu trữ git. Để triển khai một ngăn xếp ứng dụng đầy đủ từ một số dịch vụ, chúng tôi sử dụng cái gọi là biểu đồ tóm tắt. Về cơ bản, đây là biểu đồ khai báo các phần phụ thuộc và cho phép bạn khởi tạo API cũng như các dịch vụ của nó bằng một lệnh.

Chúng tôi cũng đã viết một tập lệnh Python nhỏ trên Helm để kiểm tra, tạo biểu đồ, thêm bí mật và triển khai ứng dụng. Tất cả các tác vụ này được thực hiện trên nền tảng CI trung tâm bằng hình ảnh docker.

Hãy đi vào vấn đề.

Ghi chú. Khi bạn đọc nội dung này, ứng cử viên phát hành đầu tiên cho Helm 3 đã được công bố. Phiên bản chính chứa rất nhiều cải tiến nhằm giải quyết một số vấn đề mà chúng tôi gặp phải trong quá khứ.

Quy trình phát triển biểu đồ

Chúng tôi sử dụng tính năng phân nhánh cho các ứng dụng và quyết định áp dụng cách tiếp cận tương tự cho biểu đồ.

  • Chi nhánh dev được sử dụng để tạo biểu đồ sẽ được thử nghiệm trên các cụm phát triển.
  • Khi một yêu cầu kéo được gửi tới chủ, chúng được kiểm tra trong quá trình dàn dựng.
  • Cuối cùng, chúng tôi tạo một yêu cầu kéo để thực hiện các thay đổi đối với nhánh sản phẩm và ứng dụng chúng vào sản xuất.

Mỗi môi trường có kho lưu trữ riêng để lưu trữ biểu đồ của chúng tôi và chúng tôi sử dụng Bảo tàng biểu đồ với các API rất hữu ích. Bằng cách này, chúng tôi đảm bảo sự cách ly nghiêm ngặt giữa các môi trường và thử nghiệm biểu đồ trong thế giới thực trước khi sử dụng chúng trong sản xuất.

Kho lưu trữ biểu đồ trong các môi trường khác nhau

Cần lưu ý rằng khi các nhà phát triển đẩy một nhánh phát triển, một phiên bản biểu đồ của họ sẽ tự động được đẩy tới Bảo tàng biểu đồ dành cho nhà phát triển. Do đó, tất cả các nhà phát triển đều sử dụng cùng một kho lưu trữ dành cho nhà phát triển và bạn cần chỉ định cẩn thận phiên bản biểu đồ của mình để không vô tình sử dụng các thay đổi của người khác.

Hơn nữa, tập lệnh Python nhỏ của chúng tôi xác thực các đối tượng Kubernetes dựa trên các thông số Kubernetes OpenAPI bằng cách sử dụng Kubeval, trước khi xuất bản chúng trên Chartmusem.

Mô tả chung về quy trình phát triển biểu đồ

  1. Thiết lập các nhiệm vụ đường ống theo đặc điểm kỹ thuật gazr.io để kiểm soát chất lượng (lint, unit-test).
  2. Đẩy hình ảnh docker bằng các công cụ Python để triển khai các ứng dụng của chúng tôi.
  3. Thiết lập môi trường theo tên chi nhánh.
  4. Xác thực các tệp yaml Kubernetes bằng Kubeval.
  5. Tự động tăng phiên bản của biểu đồ và biểu đồ gốc của nó (biểu đồ phụ thuộc vào biểu đồ được thay đổi).
  6. Gửi biểu đồ tới Bảo tàng biểu đồ phù hợp với môi trường của nó

Quản lý sự khác biệt giữa các cụm

Liên đoàn các cụm

Đã có lúc chúng ta sử dụng liên kết các cụm Kubernetes, trong đó các đối tượng Kubernetes có thể được khai báo từ một điểm cuối API duy nhất. Nhưng vấn đề nảy sinh. Ví dụ: không thể tạo một số đối tượng Kubernetes ở điểm cuối liên kết, gây khó khăn cho việc duy trì các đối tượng liên kết và các đối tượng khác cho các cụm riêng lẻ.

Để giải quyết vấn đề, chúng tôi bắt đầu quản lý các cụm một cách độc lập, điều này giúp đơn giản hóa đáng kể quy trình (chúng tôi đã sử dụng phiên bản liên kết đầu tiên; có thể có điều gì đó đã thay đổi trong phiên bản thứ hai).

Nền tảng phân phối theo địa lý

Nền tảng của chúng tôi hiện được phân phối trên 6 khu vực - 3 khu vực cục bộ và 3 trên đám mây.


Triển khai phân tán

Giá trị Helm toàn cầu

4 giá trị Helm toàn cầu cho phép bạn xác định sự khác biệt giữa các cụm. Tất cả các biểu đồ của chúng tôi đều có giá trị tối thiểu mặc định.

global:
  cloud: True
  env: staging
  region: us-central1
  clusterName: staging-us-central1

Giá trị toàn cầu

Các giá trị này giúp xác định bối cảnh cho các ứng dụng của chúng tôi và được sử dụng cho nhiều mục đích khác nhau: giám sát, truy tìm, ghi nhật ký, thực hiện cuộc gọi bên ngoài, mở rộng quy mô, v.v.

  • "đám mây": Chúng tôi có nền tảng Kubernetes lai. Ví dụ: API của chúng tôi được triển khai trong vùng GCP và trong trung tâm dữ liệu của chúng tôi.
  • "env": Một số giá trị có thể thay đổi đối với môi trường phi sản xuất. Ví dụ: định nghĩa tài nguyên và cấu hình tự động chia tỷ lệ.
  • "khu vực": Thông tin này giúp xác định vị trí của cụm và có thể được sử dụng để xác định các điểm cuối lân cận cho các dịch vụ bên ngoài.
  • "clusterName": nếu và khi chúng tôi muốn xác định giá trị cho một cụm riêng lẻ.

Đây là một ví dụ cụ thể:

{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}

Ví dụ về mẫu mũ bảo hiểm

Logic này được xác định trong mẫu trợ giúp để tránh làm lộn xộn Kubernetes YAML.

Thông báo ứng dụng

Các công cụ triển khai của chúng tôi dựa trên nhiều tệp YAML. Dưới đây là ví dụ về cách chúng tôi khai báo một dịch vụ và cấu trúc liên kết mở rộng quy mô của nó (số lượng bản sao) trong một cụm.

releases:
  - foo.world

foo.world:                # Release name
  services:               # List of dailymotion's apps/projects
    foobar:
      chart_name: foo-foobar
      repo: [email protected]:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3

Định nghĩa dịch vụ

Đây là bản phác thảo tất cả các bước xác định quy trình triển khai của chúng tôi. Bước cuối cùng triển khai ứng dụng tới nhiều cụm công nhân cùng một lúc.


Các bước triển khai Jenkins

Còn bí mật thì sao?

Về bảo mật, chúng tôi theo dõi tất cả bí mật từ những nơi khác nhau và lưu trữ chúng trong một kho lưu trữ duy nhất Vault ở Paris.

Các công cụ triển khai của chúng tôi trích xuất các giá trị bí mật từ Vault và khi đến thời điểm triển khai, hãy chèn chúng vào Helm.

Để thực hiện việc này, chúng tôi đã xác định ánh xạ giữa các bí mật trong Vault và các bí mật mà ứng dụng của chúng tôi cần:

secrets:                                                                                                                                                                                                        
     - secret_id: "stack1-app1-password"                                                                                                                                                                                  
       contexts:                                                                                                                                                                                                   
         - name: "default"                                                                                                                                                                                         
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"                                                                                                                                                                                    
         - name: "cluster1"                                                                                                                                                                           
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"

  • Chúng tôi đã xác định các quy tắc chung cần tuân thủ khi ghi lại bí mật trong Vault.
  • Nếu bí mật được áp dụng đến một bối cảnh hoặc cụm cụ thể, bạn cần thêm một mục cụ thể. (Ở đây, bối cảnh cluster1 có giá trị riêng cho mật khẩu stack-app1 bí mật).
  • Nếu không thì giá trị sẽ được sử dụng theo mặc định.
  • Đối với mỗi mục trong danh sách này trong Bí mật Kubernetes một cặp khóa-giá trị được chèn vào. Do đó, mẫu bí mật trong biểu đồ của chúng tôi rất đơn giản.

apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
  {{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
  name: "{{ .Chart.Name }}"
  labels:
    chartVersion: "{{ .Chart.Version }}"
    tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque

Các vấn đề và hạn chế

Làm việc với nhiều kho lưu trữ

Bây giờ chúng tôi tách biệt việc phát triển biểu đồ và ứng dụng. Điều này có nghĩa là các nhà phát triển phải làm việc trong hai kho git: một cho ứng dụng và một để xác định việc triển khai nó cho Kubernetes. 2 kho git có nghĩa là 2 quy trình công việc và người mới sử dụng rất dễ nhầm lẫn.

Quản lý biểu đồ tổng quát là một rắc rối

Như chúng tôi đã nói, biểu đồ chung rất hữu ích để xác định các phần phụ thuộc và nhanh chóng triển khai nhiều ứng dụng. Nhưng chúng tôi sử dụng --reuse-valuesđể tránh chuyển tất cả các giá trị mỗi khi chúng tôi triển khai một ứng dụng nằm trong biểu đồ tổng quát này.

Trong quy trình phân phối liên tục, chúng tôi chỉ có hai giá trị thay đổi thường xuyên: số lượng bản sao và thẻ hình ảnh (phiên bản). Các giá trị khác ổn định hơn được thay đổi thủ công và điều này khá khó khăn. Hơn nữa, một sai lầm khi triển khai biểu đồ tổng quát có thể dẫn đến những thất bại nghiêm trọng, như chúng ta đã thấy từ kinh nghiệm của chính mình.

Cập nhật nhiều tập tin cấu hình

Khi nhà phát triển thêm một ứng dụng mới, anh ta phải thay đổi một số tệp: khai báo ứng dụng, danh sách bí mật, thêm ứng dụng làm phần phụ thuộc nếu nó được đưa vào biểu đồ tổng quát.

Quyền của Jenkins được mở rộng quá mức trong Vault

Bây giờ chúng tôi có một AppRole, đọc tất cả bí mật từ Vault.

Quá trình khôi phục không được tự động

Để khôi phục, bạn cần chạy lệnh trên một số cụm và điều này có nhiều lỗi. Chúng tôi thực hiện thao tác này theo cách thủ công để đảm bảo rằng ID phiên bản chính xác được chỉ định.

Chúng tôi đang hướng tới GitOps

Mục tiêu của chúng tôi

Chúng tôi muốn trả biểu đồ về kho lưu trữ của ứng dụng mà nó triển khai.

Quy trình làm việc sẽ giống như quá trình phát triển. Ví dụ: khi một nhánh được đẩy lên mức chính, quá trình triển khai sẽ được kích hoạt tự động. Sự khác biệt chính giữa phương pháp này và quy trình làm việc hiện tại là mọi thứ sẽ được quản lý trong git (bản thân ứng dụng và cách nó được triển khai trong Kubernetes).

Có một số lợi thế:

  • Nhiều rõ ràng hơn cho nhà phát triển. Việc học cách áp dụng các thay đổi trong biểu đồ cục bộ sẽ dễ dàng hơn.
  • Định nghĩa triển khai dịch vụ có thể được chỉ định cùng một nơi với mã Dịch vụ.
  • Quản lý việc loại bỏ các biểu đồ tổng quát. Dịch vụ này sẽ có bản phát hành Helm riêng. Điều này sẽ cho phép bạn quản lý vòng đời ứng dụng (khôi phục, nâng cấp) ở mức nhỏ nhất để không ảnh hưởng đến các dịch vụ khác.
  • Lợi ích của git để quản lý biểu đồ: hoàn tác các thay đổi, nhật ký kiểm tra, v.v. Nếu bạn cần hoàn tác một thay đổi đối với biểu đồ, bạn có thể thực hiện việc này bằng git. Việc triển khai bắt đầu tự động.
  • Bạn có thể xem xét cải thiện quy trình phát triển của mình bằng các công cụ như giàn giáo, nhờ đó các nhà phát triển có thể thử nghiệm các thay đổi trong bối cảnh gần với quá trình sản xuất.

Di chuyển hai bước

Các nhà phát triển của chúng tôi đã sử dụng quy trình làm việc này được 2 năm rồi, vì vậy chúng tôi muốn quá trình di chuyển diễn ra suôn sẻ nhất có thể. Vì vậy, chúng tôi quyết định thêm một bước trung gian trên con đường đạt đến mục tiêu.
Giai đoạn đầu tiên rất đơn giản:

  • Chúng tôi giữ cấu trúc tương tự để thiết lập triển khai ứng dụng, nhưng trong một đối tượng duy nhất có tên DailymotionRelease.

apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
  name: "app1.ns1"
  environment: "dev"
  branch: "mybranch"
spec:
  slack_channel: "#admin"
  chart_name: "app1"
  scaling:
    - context: "dev-us-central1-0"
      replicas:
        - name: "hermes"
          count: 2
    - context: "dev-europe-west1-0"
      replicas:
        - name: "app1-deploy"
          count: 2
  secrets:
    - secret_id: "app1"
      contexts:
        - name: "default"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"
        - name: "dev-europe-west1-0"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"

  • 1 bản phát hành cho mỗi ứng dụng (không có biểu đồ tổng quát).
  • Biểu đồ trong kho git của ứng dụng.

Chúng tôi đã nói chuyện với tất cả các nhà phát triển nên quá trình di chuyển đã bắt đầu. Giai đoạn đầu tiên vẫn được điều khiển bằng nền tảng CI. Tôi sẽ sớm viết một bài đăng khác về giai đoạn hai: cách chúng tôi chuyển sang quy trình làm việc GitOps với Phun ra. Tôi sẽ cho bạn biết cách chúng tôi thiết lập mọi thứ và những khó khăn chúng tôi gặp phải (nhiều kho lưu trữ, bí mật, v.v.). Theo dõi tin tức.

Ở đây, chúng tôi đã cố gắng mô tả tiến trình của chúng tôi trong quy trình triển khai ứng dụng trong những năm qua, điều này dẫn đến những suy nghĩ về phương pháp GitOps. Chúng tôi vẫn chưa đạt được mục tiêu và sẽ báo cáo kết quả, nhưng bây giờ chúng tôi tin chắc rằng mình đã làm đúng khi quyết định đơn giản hóa mọi thứ và đưa nó đến gần hơn với thói quen của các nhà phát triển.

Nguồn: www.habr.com

Thêm một lời nhận xét