Разгортванне прыкладанняў на некалькіх кластарах Kubernetes з Helm

Як Dailymotion выкарыстоўвае Kubernetes: разгортванне прыкладанняў

Мы ў Dailymotion пачалі выкарыстоўваць Kubernetes у прадакшэне 3 гады таму. Але разгортваць прыкладанні на некалькіх кластарах тое яшчэ задавальненне, таму ў апошнія некалькі гадоў мы імкнуліся палепшыць нашы інструменты і працоўныя працэсы.

З чаго пачалося

Тут мы раскажам, як мы разгортваем нашы прыкладанні на некалькіх кластарах Kubernetes па ўсім свеце.

Каб разгарнуць некалькі аб'ектаў Kubernetes зараз, мы выкарыстоўваем шлем, і ўсе нашы чарты захоўваюцца ў адным рэпазітары git. Каб разгарнуць поўны стэк прыкладанні з некалькіх сэрвісаў, мы выкарыстоўваем так званы абагульняючы чарт. Па сутнасці, гэта чарт, які аб'яўляе залежнасці і дазваляе ініцыялізаваць API і яго сэрвісы адной камандай.

Яшчэ мы напісалі невялікі скрыпт Python па-над Helm, каб рабіць праверкі, ствараць чарты, дадаваць сакрэты і разгортваць прыкладанні. Усе гэтыя задачы выконваюцца на цэнтральнай платформе CI з дапамогай выявы docker.

Пяройдзем да сутнасці.

Заўвага. Калі вы гэта праглядаеце, першы рэліз-кандыдат Helm 3 ужо быў абвешчаны. Асноўная версія змяшчае цэлы набор паляпшэнняў, закліканых вырашыць некаторыя праблемы, з якімі мы сутыкаліся ў мінулым.

Рабочы працэс распрацоўкі чартаў

Для прыкладанняў мы выкарыстоўваем галінаванне, і гэты ж падыход вырашылі прымяніць да чартам.

  • Галіна DEV выкарыстоўваецца для стварэння чартаў, якія будуць тэсціравацца на кластарах распрацоўкі.
  • Калі пул-рэквест перадаецца ў майстар, яны правяраюцца ў стэйджынгу.
  • Нарэшце, мы ствараем пул-рэквест, каб перадаць змены ў галіну prod і прымяніць іх у прадакшэне.

У кожнага асяроддзя ёсць свой прыватны рэпазітар, які захоўвае нашы чарты, і мы выкарыстоўваем Chartmuseum з вельмі карыснымі API. Такім чынам мы гарантуем строгую ізаляцыю паміж асяроддзямі і праверку чартаў у рэальных умовах, перш чым выкарыстоўваць іх у прадакшэне.

Рэпазітары чартаў у розных асяроддзях

Варта адзначыць, што калі распрацоўшчыкі адпраўляюць галіну dev, версія іх чарта аўтаматычна адпраўляецца ў dev Chartmuseum. Такім чынам усе распрацоўнікі выкарыстоўваюць адзін рэпазітар dev, і трэба ўважліва паказваць сваю версію чарта, каб выпадкова не выкарыстоўваць чые-небудзь змены.

Больш за тое, наш невялікі скрыпт Python правярае аб'екты Kubernetes па спецыфікацыях Kubernetes OpenAPI з дапамогай Kubeval, перш чым апублікаваць іх у Chartmusem.

Агульнае апісанне працоўнага працэсу распрацоўкі чарта

  1. Настройка задач пайплайна па спецыфікацыі gazr.io для кантролю якасці (lint, unit-test).
  2. Адпраўка выявы docker з інструментамі Python, якія разгортваюць нашы прыкладання.
  3. Настройка асяроддзя па імені галіны.
  4. Праверка файлаў yaml Kubernetes з дапамогай Kubeval.
  5. Аўтаматычнае павелічэнне версіі чарта і яго бацькоўскіх чартаў (чартаў, якія залежаць ад змянянага чарта).
  6. Адпраўка чарта ў Chartmuseum, які адпавядае яго асяроддзі

Упраўленне адрозненнямі ў кластарах

Федэрацыя кластараў

Быў час, калі мы выкарыстоўвалі федэрацыю кластараў Kubernetes, дзе можна было аб'яўляць аб'екты Kubernetes з адной канчатковай кропкі API. Але ўзніклі праблемы. Напрыклад, некаторыя аб'екты Kubernetes нельга было стварыць у канчатковым пункце федэрацыі, таму было складана абслугоўваць аб'яднаныя аб'екты і іншыя аб'екты для асобных кластараў.

Каб вырашыць праблему, мы сталі кіраваць кластарамі незалежна, што значна спрасціла працэс (выкарыстоўвалі першую версію federation; у другой нешта магло памяняцца).

Геаразмеркаваная платформа

Цяпер наша платформа размеркавана па 6 рэгіёнах - 3 лакальна і 3 у воблаку.


Размеркаванае разгортванне

Глабальныя значэнні Helm

4 глабальных значэння Helm дазваляюць вызначаць адрозненні паміж кластарамі. Для ўсіх нашых чартаў ёсць мінімальныя значэнні па змаўчанні.

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

Глабальныя значэнні

Гэтыя значэнні дапамагаюць вызначыць кантэкст для нашых прыкладанняў і выкарыстоўваюцца для розных задач: маніторынг, трасіроўка, лагіраванне, здзяйсненне знешніх выклікаў, маштабаванне і г.д.

  • cloud: у нас ёсць гібрыдная платформа Kubernetes. Напрыклад, наш API разгортваецца ў зонах GCP і ў нашых датацэнтрах.
  • "env": некаторыя значэнні могуць змяняцца для непрацоўных асяроддзяў. Напрыклад, вызначэнні рэсурсаў і канфігурацыі аўтамаштабавання.
  • "region": гэтая інфармацыя дапамагае вызначаць размяшчэнне кластара і можа выкарыстоўвацца для вызначэння бліжэйшых канчатковых кропак для знешніх сэрвісаў.
  • "clusterName": калі і калі мы хочам вызначыць значэнне для асобнага кластара.

Вось канкрэтны прыклад:

{{/* 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 -}}

Прыклад шаблону Helm

Гэтая логіка вызначана ў дапаможным шаблоне, каб не засмечваць Kubernetes YAML.

Аб'ява прыкладання

Нашы інструменты разгортвання заснаваны на некалькіх файлах YAML. Ніжэй прыведзены прыклад таго, як мы аб'яўляем сэрвіс і яго тапалогію маштабавання (колькасць рэплік) у кластары.

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

Вызначэнне сэрвісу

Гэта схема ўсіх крокаў, якія вызначаюць наш працоўны працэс разгортвання. Апошні крок разгортвае дадатак адначасова на некалькіх працоўных кластарах.


Крокі разгортвання ў Jenkins

А сакрэты?

Што да бяспекі, мы адсочваем усе сакрэты з розных месцаў і захоўваем іх ва ўнікальным сховішчы склеп ў Парыжы.

Нашы прылады разгортвання здабываюць значэнні сакрэтаў з Vault і, калі прыходзіць час разгортвання, устаўляюць іх у Helm.

Для гэтага мы вызначылі супастаўленне паміж сакрэтамі ў Vault і сакрэтамі, якія патрэбныя нашым прыкладанням:

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"

  • Мы вызначылі агульныя правілы, якім неабходна прытрымлівацца пры запісе сакрэтаў у Vault.
  • Калі сакрэт адносіцца да пэўнага кантэксту або кластара, трэба дадаць канкрэтны запіс. (Тут у кантэксту cluster1 ёсць уласнае значэнне для сакрэту stack-app1-password).
  • У адваротным выпадку выкарыстоўваецца значэнне па змаўчанні.
  • Для кожнага пункта ў гэтым спісе ў сакрэт Kubernetes устаўляецца пара ключ-значэнне. Таму шаблон сакрэту ў нашых чартах вельмі просты.

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

Праблемы і абмежаванні

Праца з некалькімі рэпазітарамі

Цяпер мы падзяляем распрацоўку чартаў і прыкладанняў. Гэта значыць, што распрацоўнікам даводзіцца працаваць у двух рэпазітарах git: адзін для прыкладання, а другі - для вызначэння яго разгортвання ў Kubernetes. 2 рэпазітара git - гэта 2 працоўных працэсу, і пачаткоўцу лёгка заблытацца.

Кіраваць абагульненымі чартамі клапотна

Як мы ўжо казалі, абагульненыя чарты вельмі зручныя для вызначэння залежнасцяў і хуткага разгортвання некалькіх прыкладанняў. Але мы выкарыстоўваем --reuse-values, Каб пазбегнуць перадачы ўсіх значэнняў кожны раз, калі мы разгортваем прыкладанне, якое ўваходзіць у гэты абагульнены чарт.

У працоўным працэсе бесперапыннай пастаўкі ў нас усяго два значэнні, якія мяняюцца рэгулярна: колькасць рэплік і тэг выявы (версія). Іншыя, больш стабільныя значэнні, мяняюцца ўручную, і гэта даволі складана. Больш за тое, адна памылка ў разгортванні абагульненага чарта можа прывесці да сур'ёзных збояў, як мы пераканаліся на ўласным досведзе.

Абнаўленне некалькіх файлаў канфігурацыі

Калі распрацоўшчык дадае новае прыкладанне, яму даводзіцца змяняць некалькі файлаў: аб'ява прыкладання, спіс сакрэтаў, даданне прыкладання ў залежнасці, калі яно ўваходзіць у абагульнены чарт.

Дазволы Jenkins занадта пашыраны ў Vault

Цяпер у нас ёсць адзін AppRole, які чытае ўсе сакрэты з Vault.

Працэс адкату не аўтаматызаваны

Для адкату трэба выканаць каманду на некалькіх кластарах, а гэта багата памылкамі. Мы выконваем гэтую аперацыю ўручную, каб гарантавана пазначыць правільны ідэнтыфікатар версіі.

Мы рухаемся ў бок GitOps

Наша мэта

Мы хочам вярнуць чарт у рэпазітар прыкладання, якое ён разгортвае.

Рабочы працэс будзе такім жа, як для распрацоўкі. Напрыклад, калі галіна адпраўляецца ў майстар, разгортванне будзе запускацца аўтаматычна. Асноўная розніца паміж такім падыходам і бягучым працоўным працэсам будзе ў тым, што усё будзе кіравацца ў git (само прыкладанне і спосаб яго разгортвання ў Kubernetes).

Пераваг некалькі:

  • Значна больш зразумела для распрацоўшчыка. Прасцей навучыцца прымяняць змены ў лакальным чарце.
  • Вызначэнне разгортвання службы можна ўказаць там жа, дзе код службы.
  • Кіраванне выдаленнем абагульненых чартаў. У сервісу будзе свой выпуск Helm. Гэта дазволіць кіраваць жыццёвым цыклам прыкладання (адкат, апгрэйд) на драбнюткім узроўні, каб не закранаць іншыя сэрвісы.
  • Перавагі git для кіравання чартамі: адмена змен, часопіс аўдыту і т. д. Калі трэба адмяніць змену чарта, гэта можна зрабіць з дапамогай git. Разгортванне запускаецца аўтаматычна.
  • Можна падумаць аб удасканаленні працоўнага працэсу распрацоўкі з дапамогай такіх прылад, як Скафот, з якім распрацоўшчыкі могуць тэсціраваць змены ў кантэксце, набліжаным да прадакшэну.

Двухэтапная міграцыя

Нашы распрацоўшчыкі выкарыстоўваюць гэты працоўны працэс ужо 2 гады, так што нам патрэбна максімальна бязбольная міграцыя. Таму мы вырашылі дадаць прамежкавы этап на шляху да мэты.
Першы этап просты:

  • Мы захоўваем падобную структуру для налады разгортвання прыкладанняў, але ў адным аб'екце з імем 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 рэліз на дадатак (без абагульненых чартаў).
  • Чарты ў рэпазітары git прыкладання.

Мы пагаварылі з усімі распрацоўшчыкамі, так што працэс міграцыі ўжо пачаўся. Першы этап па-ранейшаму кантралюецца з выкарыстаннем платформы CI. Хутка я напішу яшчэ адзін пост аб другім этапе: як мы перайшлі на працоўны працэс GitOps з Паток. Я раскажу, як мы ўсе настроілі і з якімі цяжкасцямі сутыкнуліся (некалькі рэпазітараў, сакрэты і г. д.). Сачыце за навінамі.

Тут мы паспрабавалі апісаць наш прагрэс у працоўным працэсе разгортвання прыкладанняў за апошнія гады, які прывёў да думак аб падыходзе GitOps. Мы яшчэ не дасягнулі мэты і будзем паведамляць аб выніках, але зараз перакананыя, што правільна зрабілі, калі вырашылі ўсё спрасціць і наблізіць да звычак распрацоўшчыкаў.

Крыніца: habr.com

Дадаць каментар