Как стать автором
Обновить
45
0
Timofey Kirillov @tkir

System utils development

Отправить сообщение

Цель: изоляция кеша стадий и итоговых образов собранных для разных веток в git. Кеш, собранный для какой-либо ветки от master не будет доступен в master до тех пор, пока эта ветка не будет смержена в master.

Почему? Они друг другу не должны мешать.

Это простой шаблонизатор yaml, встроенный в gitlab.


.job_template: &job_definition  # Hidden key that defines an anchor named 'job_definition'
  image: ruby:2.1
  services:
    - postgres
    - redis

test1:
  <<: *job_definition           # Merge the contents of the 'job_definition' alias
  script:
    - test1 project

test2:
  <<: *job_definition           # Merge the contents of the 'job_definition' alias
  script:
    - test2 project

https://docs.gitlab.com/ee/ci/yaml/#anchors

Решили. Была проблема с использованием RUN --mount.


https://github.com/flant/werf/pull/1769


Проверить можно в бета-канале <(multiwerf use 1.0 beta).

Понял, так тоже можно. Использовать только werf build-and-publish и werf cleanup. Вот по этому гайду https://werf.io/documentation/guides/gitlab_ci_cd_integration.html#pipeline просто пропустить стадию деплоя.

Пока нет, не смотрели, выглядит как немного не то.


Но интеграция с самим gitlab у нас есть и достаточно плотная:



Например werf автоматом использует токены, которые выдает gitlab для логина в docker-registry, который также задает gitlab. Werf автоматом использует gitlab-environment если он определен.


Так-то по возможности стараемся лишнего не делать, если это можно переложить на внешнюю CI-систему.


Какой в gitlab есть ci конкретно для деплоя в кубы, где верф будет избыточен, можно подробнее на примере? Везде где используется kubectl apply или helm upgrade можно использовать и werf deploy — тут никаких ограничений не вносится, даже наоборот werf deploy проще использовать из-за наличия интеграции с гитлабом.

Target можно:


configVersion: 1
project: myproj
---
image: backend
dockerfile: ./Dockerfile
target: backend
---
image: frontend
dockerfile: ./Dockerfile
target: frontend

Поддерживается любой синтаксис стандартного Dockerfile, т.к. для билда используется docker server (который может использовать buildkit).


После того как образы описаны в werf.yaml, собраны и опубликованы (через werf build-and-publish), их можно использовать для деплоя в кубы. Для этого надо описать chart. В описанном чарте можно ссылаться на имена образов из werf.yaml, в нашем случае например так:


apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: backend
spec:
  template:
    metadata:
      labels:
        service: backend
    spec:
      containers:
      - name: main
        command: [ ... ]
{{ tuple "backend" . | include "werf_container_image" | indent 8 }}
        env:
{{ tuple "backend" . | include "werf_container_env" | indent 8 }}

Werf ориентирован на kubernetes. И чтобы деплоить приложения туда. И в дальнейшем чтобы собирать образы используя runner-ы работающие в самом kubernetes.

Планируем плотно развивать тему локальной разработки с использованием werf. Это возможно включает и поддержку docker-compose. Сейчас работаем над стабилизацией версии 1.0. Где-то в конце этого года можно ждать подвижек.

Не оставим, но в любом случае, когда выйдет стабильный helm 3 и будет способ мигрировать старые инсталляции на новый хельм — мы перейдем на кодовую базу helm 3 и будем использовать и развивать kubedog для слежения за ресурсами в werf.


Короче советую переходить на werf ;)

Так "верфь" и есть транслитерация слова werf, а не наоборот ;)

Это только в теории. Есть проблемы с аутентификацией в api например. Ниже ответил развернуто.


В k8s еще есть куча оберток над этим api. Например, shared-informer-ы для слежения за состоянием ресурсов. Эта обертка решает низкоуровневые проблемы и является стандартной для написания kubernetes-операторов. В других языках такого нет, пока сам не напишешь.

Инфраструктура — главный аргумент. На Rust не может быть богаче инфраструктура именно для Docker и Kubernetes.


Например, по опыту с Ruby: отсуствие нормальных клиентов для Kubernetes. У нас был самописный, чтобы достучатся до некоторых фич API типа слежения за ресурсами. Самописный клиент привел к тому, что наш dapp не мог коннектится к Google Kubernetes Engine, из-за использования там кастомной схемы аутентфикации. А времени реализовать эту схему в нашем клиенте не было. Вот такого рода проблемы.


Когда перешли на Golang — мы просто включили стандартный клиент kubernetes и все, коннект к GKE из коробки. Если появится что-то новое — сразу будет из коробки.

Хороший вопрос. Так не сделали, потому что подход выработался не заранее перед началом работы, а уже во время переписывания.


Во время переписывания было удобно распределить работу параллельно по людям и каждому дать задачу типа: вот тебе компонент, ожидается бинарник с таким-то интерфейсом. При этом каких-то договоренностей о том, как устроен бинарь не было, главное чтобы делал то что надо и соответствовал интерфейсу. Так было удобнее делать работу именно параллельно с точки зрения редактирования кода. В случае когда один бинарь и над ним работает несколько человек — надо сразу определиться с "правильной" организацией кода, чтобы несколько человек не редактировало один файл. Нам же был больше нужен быстрый результат, чем экономия места.


Сейчас можно сказать, что ваш вариант правильнее и решает проблему с тем сколько места занимают бинарные зависимости. Но когда прога будет переписана полностью это уже не имеет значения, т.к. это временный код.

Kubectl wait можно использовать в CI/CD, но есть "нюансы".


1) Зафейлить ci/cd pipeline как можно раньше.


Например, есть deployment с ошибкой, он никогда не перейдет в состояние READY. Если запустить kubectl wait --for condition=Available,
то команда завершится с ошибкой только по timeout. Kubedog увидит ошибку и может сразу зафейлить ci/cd pipeline. Для пользователя это дает быстрый фидбек.


2) Показывать "что происходит" во время ожидания.


Kubedog следит не только за указанным ресурсом до достижения какого-то condition, но и за всеми связанными. И получает события и логи этих связанных ресурсов. Вся инфа объединяется в единый поток и выдается наверх. Грубо говоря это аггрегатор "новостей" по указанному ресурсу.


У нас изначально стояла задача: сделать штуку для выката, которая может сказать что "все хорошо", может сказать что "все плохо" и покажет "что происходит сейчас" во время ожидания выката.


В случае с kubectl wait надо придумывать отдельный поток/процесс для показа логов. А чтобы показать логи надо еще узнать имена связанных ресурсов. И как это все в CI/CD по-простому сделать неясно.


Но и логи это не все, еще есть event'ы. С ними отдельная история. В event'ах может содержаться важная пользователю информация для того, чтобы исправить ошибку.


Мы держали в голове такую мысль: следилка должна показать достаточно информации пользователю для дебага проблем. Не надо вызывать kubectl и копатся в консоли. В идеале достаточно посмотреть на вывод CI/CD pipeline и понять где ошибка.


3) Использовать как библиотеку.


Kubectl wait на данный момент это pkg связанный с cli. А хочется подключить этот wait в свое приложение. Будь то helm или dapp. Понятно почему они так делают: не обобщают раньше времени. Но мы сделали библиотеку, потому что было видение сразу.


4) Детали реализации.


Kubedog использует informer-ы из библиотеки того же куба. Это примитив, который позволяет следить за ресурсами сутками, обрабатывает всякие низкоуровневые косяки. Например, натравил сейчас kubectl wait на древний ресурс, который у меня создан месяц назад, вылезла сразу такая ошибка:


error: An error occurred while waiting for the condition to be satisfied: too old resource version: 847449 (1360240)

Вот informer-ы эту ситуацию обрабатывают. Их обычно используют для написания всяких controller-ов.


В общем это надежная такая обертка над низкоуровневым watch-ем. И за счет ее использования в kubedog нет вышеуказанной проблемы. Но это исправимо в kubectl, надо патч им предложить :).




Обобщая. Если сделать kubectl apply + kubedog rollout track, то сразу из коробки получаем и логи, и event-ы, и ожидание до готовности, и прерывание по ошибкам.


Честно, была идея попробовать в kubectl такие функции добавить. Это в идеале. Потому что тогда работа точно зря не пропадет. Но возможно чуть позже.

Кеш пакетов apt можно расшарить между билдами с помощью mount("/cached-dir") {from :build_dir}.
Тут подробнее: https://flant.github.io/dapp/mount_for_advanced_build.html#%D0%BA%D1%8D%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D0%B1%D0%BE%D1%80%D0%BE%D1%87%D0%BD%D1%8B%D1%85-%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2-%D0%BC%D0%B5%D0%B6%D0%B4%D1%83-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B0%D0%BC%D0%B8


Указывать произвольную директорию нельзя, выбор только между временной директорией на каждую сборку и build-директорией, которая сохраняется между сборками. Build-директория создается в ~/.dapp/builds/<project-name> и содержит помимо mount'ов всякие кеши git'а, chef-cookbook'ов и т.п.

Sensitive data кодируется симметричным ключом и попадает в repo. При выкате, если указана переменная окружения с ключом, то секретные данные становятся доступны. Эту переменную окружения можно указывать, например, как gitlab-secret-variable. Секретные значения — это либо файлы, либо helm values. Соответственно в yaml-шаблонах либо читается уже расшифрованный на момент запуска файл либо подставляется значение из values.
Подробнее здесь: https://flant.github.io/dapp/secrets_for_deploy.html.

В плане самой фичи mount подойдет. Т.е. mount из host в minikube + mount из minikube в docker позволят пробросить директорию в контейнер из хост-системы. Но главная проблема не в этом, а в том, чтобы этот запущенный контейнер соответствовал тем инструкциям по сборке образа приложения, которые описываются в Dappfile. Например, при изменении Gemfile.lock надо сделать bundle install. Для этого в dapp предусмотрены стадии и для пересборки стадии можно поставить триггер на изменение файла Gemfile.lock. А в случае, если изменяется не Gemfile.lock, то при сборке новой версии происходит простое и быстрое наложение патча. Так вот, если поменялись какие-то файлы, от которых зависят инструкции по сборке каких-то стадий, то образ придется пересобрать и перезапустить контейнер приложения. Но если поменялись другие файлы приложения, то новый образ собирать не надо, если исходники уже примонтированы в запущенный контейнер.


Однако сам minikube mount мы пробовали использовать для хранения файлов docker-registry на хост-системе, а не внутри minikube на виртуалке. Но производительность этой штуки для docker-registry неприемлема.

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Работает в
Дата рождения
Зарегистрирован
Активность