Wdrażaj aplikacje w wielu klastrach Kubernetes za pomocą Helm

Jak Dailymotion wykorzystuje Kubernetes: wdrażanie aplikacji

W Dailymotion zaczęliśmy używać Kubernetes w środowisku produkcyjnym 3 lata temu. Jednak wdrażanie aplikacji w wielu klastrach sprawia przyjemność, dlatego przez ostatnie kilka lat próbowaliśmy ulepszyć nasze narzędzia i przepływy pracy.

Gdzie to się zaczęło

W tym miejscu omówimy sposób wdrażania naszych aplikacji w wielu klastrach Kubernetes na całym świecie.

Aby wdrożyć wiele obiektów Kubernetes jednocześnie, używamy Ster, a wszystkie nasze wykresy są przechowywane w jednym repozytorium git. Aby wdrożyć pełny stos aplikacji z kilku usług, korzystamy z tzw. wykresu zbiorczego. Zasadniczo jest to wykres, który deklaruje zależności i pozwala zainicjować interfejs API i jego usługi za pomocą jednego polecenia.

Na platformie Helm napisaliśmy także mały skrypt w języku Python, który umożliwia sprawdzanie, tworzenie wykresów, dodawanie wpisów tajnych i wdrażanie aplikacji. Wszystkie te zadania wykonywane są na centralnej platformie CI z wykorzystaniem obrazu dokera.

Przejdźmy do rzeczy.

Notatka. Kiedy to czytasz, ogłoszono już pierwszego kandydata do wydania Helma 3. Wersja główna zawiera cały szereg ulepszeń rozwiązujących niektóre problemy, które napotkaliśmy w przeszłości.

Przepływ pracy przy opracowywaniu wykresów

W aplikacjach używamy rozgałęzień i postanowiliśmy zastosować to samo podejście do wykresów.

  • Oddział dev służy do tworzenia wykresów, które będą testowane na klastrach deweloperskich.
  • Kiedy zostanie przesłane żądanie ściągnięcia mistrz, są sprawdzane w inscenizacji.
  • Na koniec tworzymy żądanie ściągnięcia, aby zatwierdzić zmiany w gałęzi szturchać i zastosować je w produkcji.

Każde środowisko ma swoje prywatne repozytorium, w którym przechowujemy nasze wykresy i z których korzystamy Muzeum Chart z bardzo przydatnymi API. W ten sposób zapewniamy ścisłą izolację pomiędzy środowiskami i testowanie wykresów w świecie rzeczywistym przed wykorzystaniem ich w produkcji.

Repozytoria wykresów w różnych środowiskach

Warto zauważyć, że gdy programiści przesyłają gałąź deweloperską, wersja ich wykresu jest automatycznie przesyłana do deweloperskiego Chartmuseum. Dlatego wszyscy programiści korzystają z tego samego repozytorium deweloperów i musisz dokładnie określić swoją wersję wykresu, aby przypadkowo nie skorzystać z cudzych zmian.

Co więcej, nasz mały skrypt w Pythonie sprawdza poprawność obiektów Kubernetes pod kątem specyfikacji Kubernetes OpenAPI przy użyciu Kubeval, przed opublikowaniem ich w Chartmusem.

Ogólny opis procesu tworzenia wykresu

  1. Konfigurowanie zadań rurociągowych zgodnie ze specyfikacją gazr.io do kontroli jakości (lint, test jednostkowy).
  2. Przesyłanie obrazu dokera za pomocą narzędzi Pythona, które wdrażają nasze aplikacje.
  3. Konfigurowanie środowiska według nazwy oddziału.
  4. Sprawdzanie poprawności plików yaml Kubernetes przy użyciu Kubeval.
  5. Automatycznie zwiększaj wersję wykresu i jego wykresów nadrzędnych (wykresy zależne od zmienianego wykresu).
  6. Przesyłanie wykresu do Chartmuseum pasującego do jego otoczenia

Zarządzanie różnicami pomiędzy klastrami

Federacja Klastrów

Był czas, kiedy używaliśmy federacja klastrów Kubernetes, gdzie obiekty Kubernetes można deklarować z jednego punktu końcowego interfejsu API. Ale pojawiły się problemy. Na przykład nie można było utworzyć niektórych obiektów Kubernetes w punkcie końcowym federacji, co utrudniało obsługę obiektów stowarzyszonych i innych obiektów dla poszczególnych klastrów.

Aby rozwiązać problem, zaczęliśmy samodzielnie zarządzać klastrami, co znacznie uprościło proces (użyliśmy pierwszej wersji federacji, w drugiej mogło się coś zmienić).

Platforma rozproszona geograficznie

Nasza platforma jest obecnie dystrybuowana w 6 regionach – 3 lokalnie i 3 w chmurze.


Wdrożenie rozproszone

Globalne wartości Helma

4 globalne wartości Helm pozwalają zidentyfikować różnice pomiędzy klastrami. Wszystkie nasze wykresy mają domyślne wartości minimalne.

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

Globalne wartości

Wartości te pomagają zdefiniować kontekst dla naszych aplikacji i służą do różnych celów: monitorowania, śledzenia, rejestrowania, wykonywania połączeń zewnętrznych, skalowania itp.

  • „cloud”: Posiadamy hybrydową platformę Kubernetes. Na przykład nasze API jest wdrażane w strefach GCP i w naszych centrach danych.
  • „env”: Niektóre wartości mogą ulec zmianie w przypadku środowisk nieprodukcyjnych. Na przykład definicje zasobów i konfiguracje automatycznego skalowania.
  • „region”: te informacje pomagają określić lokalizację klastra i mogą służyć do określania pobliskich punktów końcowych dla usług zewnętrznych.
  • „clusterName”: czy i kiedy chcemy zdefiniować wartość dla pojedynczego klastra.

Oto konkretny przykład:

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

Przykład szablonu steru

Ta logika jest zdefiniowana w szablonie pomocniczym, aby uniknąć zaśmiecania Kubernetes YAML.

Ogłoszenie o aplikacji

Nasze narzędzia wdrożeniowe opierają się na wielu plikach YAML. Poniżej znajduje się przykład, jak deklarujemy usługę i jej topologię skalowania (liczbę replik) w klastrze.

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

Definicja usługi

To jest zarys wszystkich kroków, które definiują nasz przepływ pracy wdrożeniowy. Ostatni krok polega na jednoczesnym wdrożeniu aplikacji w wielu klastrach procesów roboczych.


Kroki wdrażania Jenkinsa

A co z tajemnicami?

Jeśli chodzi o bezpieczeństwo, śledzimy wszystkie sekrety z różnych miejsc i przechowujemy je w unikalnym skarbcu Sklepienie w Paryżu.

Nasze narzędzia do wdrażania wyodrębniają tajne wartości z Vault i, gdy nadejdzie czas wdrożenia, wstawiają je do Helma.

W tym celu zdefiniowaliśmy mapowanie sekretów w Vault i sekretów potrzebnych naszym aplikacjom:

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"

  • Określiliśmy ogólne zasady, których należy przestrzegać podczas zapisywania sekretów w Krypcie.
  • Jeśli tajemnica ma zastosowanie do określonego kontekstu lub klastra, musisz dodać konkretny wpis. (Tutaj klaster kontekstu1 ma swoją własną wartość dla tajnego hasła stosu-aplikacji1).
  • W przeciwnym razie używana jest wartość domyślnie.
  • Dla każdej pozycji na tej liście w Sekret Kubernetesa wstawiana jest para klucz-wartość. Dlatego tajny szablon na naszych wykresach jest bardzo prosty.

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

Problemy i ograniczenia

Praca z wieloma repozytoriami

Teraz oddzielamy tworzenie wykresów i aplikacji. Oznacza to, że programiści muszą pracować w dwóch repozytoriach git: jednym dla aplikacji i drugim służącym do definiowania jej wdrożenia w Kubernetesie. 2 repozytoria git oznaczają 2 przepływy pracy i nowicjuszowi łatwo jest się pomylić.

Zarządzanie uogólnionymi wykresami jest kłopotliwe

Jak już powiedzieliśmy, wykresy ogólne są bardzo przydatne do identyfikowania zależności i szybkiego wdrażania wielu aplikacji. Ale używamy --reuse-valuesaby uniknąć przekazywania wszystkich wartości za każdym razem, gdy wdrażamy aplikację będącą częścią tego uogólnionego wykresu.

W przepływie pracy ciągłego dostarczania mamy tylko dwie wartości, które zmieniają się regularnie: liczbę replik i znacznik obrazu (wersję). Inne, bardziej stabilne wartości zmienia się ręcznie, a to jest dość trudne. Co więcej, jeden błąd we wdrażaniu uogólnionego wykresu może prowadzić do poważnych niepowodzeń, jak widzieliśmy z własnego doświadczenia.

Aktualizowanie wielu plików konfiguracyjnych

Kiedy programista dodaje nową aplikację, musi zmienić kilka plików: deklarację aplikacji, listę sekretów, dodać aplikację jako zależność, jeśli jest uwzględniona w uogólnionym schemacie.

Uprawnienia Jenkinsa w Vault są zbyt rozszerzone

Teraz mamy jeden Rola aplikacji, który odczytuje wszystkie sekrety ze Krypty.

Proces wycofywania zmian nie jest zautomatyzowany

Aby wycofać zmiany, należy uruchomić polecenie w kilku klastrach, co jest obarczone błędami. Operację tę wykonujemy ręcznie, aby mieć pewność, że podano poprawny identyfikator wersji.

Zmierzamy w stronę GitOps

Nasz cel

Chcemy zwrócić wykres do repozytorium aplikacji, którą wdraża.

Przepływ pracy będzie taki sam jak w przypadku programowania. Na przykład, gdy gałąź zostanie przekazana do mastera, wdrożenie zostanie uruchomione automatycznie. Główna różnica między tym podejściem a obecnym przepływem pracy polega na tym wszystko będzie zarządzane w git (sama aplikacja i sposób jej wdrożenia w Kubernetesie).

Istnieje kilka zalet:

  • Dużo jaśniejszy dla dewelopera. Łatwiej jest nauczyć się stosowania zmian na wykresie lokalnym.
  • Można określić definicję wdrożenia usługi w tym samym miejscu co kod usługa.
  • Zarządzanie usuwaniem uogólnionych wykresów. Usługa będzie miała własną wersję Helma. Dzięki temu będziesz mógł zarządzać cyklem życia aplikacji (rollback, upgrade) na najmniejszym poziomie, tak aby nie mieć wpływu na inne usługi.
  • Korzyści z gita do zarządzania wykresami: cofanie zmian, dziennik audytu itp. Jeśli chcesz cofnąć zmianę na wykresie, możesz to zrobić za pomocą git. Wdrożenie rozpoczyna się automatycznie.
  • Możesz rozważyć ulepszenie przepływu pracy programistycznej za pomocą narzędzi takich jak Rusztowanie, za pomocą którego programiści mogą testować zmiany w kontekście zbliżonym do produkcyjnego.

Migracja dwuetapowa

Nasi programiści korzystają z tego przepływu pracy już od 2 lat, dlatego chcemy, aby migracja była jak najbardziej bezbolesna. Dlatego postanowiliśmy dodać etap pośredni na drodze do celu.
Pierwszy etap jest prosty:

  • Utrzymujemy podobną strukturę konfiguracji wdrażania aplikacji, ale w jednym obiekcie o nazwie 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 wydanie na aplikację (bez uogólnionych wykresów).
  • Wykresy w repozytorium git aplikacji.

Rozmawialiśmy ze wszystkimi programistami, więc proces migracji już się rozpoczął. Sterowanie pierwszym etapem odbywa się nadal za pomocą platformy CI. Wkrótce napiszę kolejny post o fazie drugiej: o tym, jak przeszliśmy do przepływu pracy GitOps Topnik. Opowiem Ci jak wszystko skonfigurowaliśmy i jakie napotkaliśmy trudności (wiele repozytoriów, sekrety itp.). Śledź wiadomości.

Tutaj staraliśmy się opisać nasz postęp w procesie wdrażania aplikacji na przestrzeni ostatnich lat, co skłoniło nas do przemyśleń na temat podejścia GitOps. Nie osiągnęliśmy jeszcze celu i będziemy informować o wynikach, ale teraz jesteśmy przekonani, że dobrze postąpiliśmy, decydując się na wszystko i przybliżenie nawyków programistów.

Źródło: www.habr.com

Dodaj komentarz