Интро
Всем привет! Это мой первый пост на Хабре. Хотел написать сюда давно, первый блин комом — не бейте.
Сегодня хочу рассказать о связке GitLab + K8S + Werf и как с помощью него быстро собрать и задеплоить свое приложение в одну команду. Этот пост будет иметь формат мини-туториала.
Думаю большинство набредших на эту статью знают, что такое Gitlab и Kubernetes. Не знаете — гугл в помощь. В этой статье это out of scope.
Что такое Werf? Werf — это утилита, объединяющая CI/CD системы (типа Gitlab, Github Actions), docker и helm в одном флаконе и позволяющая одной командой собрать образ контейнера, запушить его в репозиторий контейнеров и задеплоить с помощью helm.
Итак, поехали.
Пост будет коротким и максимально сухим. Поделим его на две части:
-
Настройка окружения
-
Пример деплоя приложения
Приступим к первой части.
Настройка окружения
Надеюсь у вас уже есть Gitlab. Если нет, то разверните. У меня гитлаб развернут с помощью docker-compose.
O подключении Kubernetes к Gitlab
? Gitlab начиная с 15 версии объявил метод подключения K8S через сертификаты как deprecated и предлагает теперь единственный способ подключения кластеров через gitlab-agent и kubernetes agent server (он же kas). Подробнее тут.
Если у вас еще на подключен kubernetes agent server — подключайте так
environment: GITLAB_OMNIBUS_CONFIG: | ... gitlab_kas['enable'] = true
То есть в gitlab.rb это будет просто gitlab_kas['enable'] = true. Не забываем делать gitlab-ctl reconfigure.
Kubernetes кластер нам тоже понадобится. Надеюсь, что он у вас тоже есть. Если нет — советую попробовать Managed Service For Kubernetes от Yandex.Cloud. Можно выбрать компактные, недорогие и к тому же вымещаемые (самые бюджетные) инстансы.
Теперь необходимо запустить gitlab-agent для kubernetes. Для этого создаем репозиторий infra и теперь добавляем файл по пути:
# .gitlab/agents/mks-agent ci_access: groups: - id: <group_id> projects: - id: <project_id>
Вместо group_id или project_id проставляем пути к проектам или группам, где этот kubernetes кластер будет доступен. Например для группы infra и проекта my-group/my-app это будет выглядеть так:
# .gitlab/agents/mks-agent ci_access: groups: - id: infra projects: - id: my-group/my-app
После этого идем в Infrastructure > Kubernetes Clusters > Connect a cluster, выбираем из выпадающего списка нужного агента. Gitlab покажет для установки gitlab-agent через helm. Выглядеть будет так:
helm repo add gitlab <https://charts.gitlab.io> helm repo update helm upgrade --install mks-agent gitlab/gitlab-agent \\ --namespace gitlab-agent \\ --create-namespace \\ --set image.tag=v15.2.0 \\ --set config.token=<your_token> \\ --set config.kasAddress=wss://<gitlab_domain>/-/kubernetes-agent/
Готово. Можете проверить gitlab-agent в Infrastructure > Kubernetes Clusters списке. Должно быть состоянии connected .
Теперь установим в кластер kubernetes необходимые компоненты для работы Werf. В репозитории на Github я выложил манифесты для деплоя этих компонент (плюс там все для раскатки окружения). Эти манифесты я взял с официального сайта из этого раздела.
Если вы скачали репозиторий, то просто выполните
cd werf kubectl -n kube-system apply -f werf-fuse-device-plugin-ds.yaml kubectl create namespace gitlab-ci kubectl apply -f enable-fuse-pod-limit-range.yaml kubectl apply -f sa-runner.yaml cd ..
Дело за малым, осталось установить gitlab-runner.
В этом же репозитории values.yaml файл для официального gitlab-runner. Чтобы добавить shared runner зайдите в Gitlab > Admin > Shared Runners > Register an instance runner. Скопируем registration token. Теперь в скопированном репозитории делаем
cd gitlab-runner helm repo add gitlab <https://charts.gitlab.io> vim values.yaml # set your domain and registry token helm install --namespace gitlab-ci gitlab-runner -f values.yaml gitlab/gitlab-runner
Проверяем, что раннер появился. Я специально поставил тег werf, чтобы вы потом не потерялись.
Окружение готово. На данном этапе у нас получается такая схема:

Деплой приложения
Установим werf локально к себе на компьютер. Это поможет в будущем быстрее разрабатывать приложения, смотеть рендер манифестов и работать с секретами helm.
Используем эту ссылку на официальный сайт.
Проверим версию
werf version v1.2.122+fix2
Создаем проект в Gitlab, называем my-app-werf (к примеру). Добавляем в проект файл werf.yaml
# werf.yaml configVersion: 1 project: my-app-werf deploy: namespace: my-app-werf --- image: my_app_werf dockerfile: Dockerfile
Если у вас монорепо для микросервисов, то werf без проблем с этим справится. werf.yaml будет тогда выглядеть как-то так:
configVersion: 1 project: my-microservice-app-werf deploy: namespace: my-microservice-app-werf --- image: my_app_werf_backend dockerfile: Dockerfile context: backend # папка сервиса --- image: my_app_werf_frontend dockerfile: Dockerfile context: frontend # папка сервиса
Для наглядности сделаем простой веб сервер на Go:
// main.go package main import ( "fmt" "html" "log" "net/http" "os" ) func main() { http.HandleFunc("/", MainHandler) log.Print("Starting server...") log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil)) } func MainHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }
Напишем Dockerfile
# Dockerfile FROM golang:alpine as builder WORKDIR /app COPY . . RUN go build -o app FROM alpine as prod WORKDIR /app COPY --from=builder /app/app /app/app ENV PORT=8080 ENTRYPOINT [ "/app/app" ]
Создаем шаблоны helm для деплоя по пути .helm/templates :
# .helm/templates/regcred.yaml {{- if .Values.dockerconfigjson -}} apiVersion: v1 kind: Secret metadata: name: regcred type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: {{ .Values.dockerconfigjson }} {{- end -}}
# .helm/templates/app.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: my-app-werf name: my-app-werf spec: selector: matchLabels: app: my-app-werf replicas: 1 template: metadata: labels: app: my-app-werf spec: imagePullSecrets: - name: regcred containers: - name: my-app-werf image: "{{.Values.werf.image.my_app_werf}}" imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: my-app-werf labels: app: my-app-werf spec: ports: - port: 8080 name: my-app-werf-service selector: app: my-app-werf
# .helm/templates/ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-werf annotations: cert-manager.io/cluster-issuer: letsencrypt-cluster spec: ingressClassName: nginx tls: - hosts: - "app-werf.apps.<your_domain>" secretName: app-werf-mks-tls rules: - host: "app-werf.apps.<your_domain>" http: paths: - path: / pathType: ImplementationSpecific backend: service: name: my-app-werf port: number: 8080
Лично я использую nginx ingress с cert-manager поэтому в аннотациях указан cluster issuer.
Добавьте .dockerignore чтобы не копировать лишнее и кешировать только нужное.
.helm .gitlab-ci.yml werf.yaml
Остается вишенка на торте: gitlab CI/CD. Werf собирает все переменные окружения Gitlab и использует их при сборке и рендере манифестов. Плюс используется особый вид сборки, подробнее тут. Это опять же out of scope.
Пишем .gitlab-ci.yml и удивляемся как компактно он выглядит
# .gitlab-ci.yml stages: - build-and-deploy build_and_deploy: stage: build-and-deploy image: registry.werf.io/werf/werf variables: WERF_SYNCHRONIZATION: kubernetes://gitlab-ci script: - source $(werf ci-env gitlab --as-file) - werf converge tags: ["werf"]
Одна команда werf converge делает все разом: собирает docker образ, пушит в registry и деплоит приложение из собранного образа.
Предлагаю сверится по структуре проекта
. ├── .dockerignore ├── .gitlab-ci.yml ├── .helm │ └── templates │ ├── app.yaml │ ├── ingress.yaml │ └── regcred.yaml ├── Dockerfile ├── go.mod ├── main.go └── werf.yaml
Перед пушем можно проверить как будут выглядеть манифесты локально следующей командой.
werf render --dev
Коммитим изменения, пушим. Вуаля! Так выглядит пайплайн в Gitlab CI/CD:

У нас получилась такая схема:

Плюсы и минусы
Плюсы
-
Быстрое развертывание и простая интеграция.
Минусы
-
Нет поддержания конфигурации в актуальном состоянии как у ArgoCD
Откровенно говоря, ArgoCD и Werf могут друг друга дополнять. Подробности тут.
ссылка на оригинал статьи https://habr.com/ru/articles/679826/
Добавить комментарий