Инструкция: Как построить процесс доставки приложения в Kubernetes, используя gitlab ci и gitlab runner

от автора

Привет, Хабр! Меня зовут Егор Комаров, я тестировщик в команде #CloudMTS.

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

Когда в приложении появляется новый функционал (например, изменился ответ от сервера), запускается ряд стандартных действий:

  • получить фичу от разработчика;
  • сбилдить контейнер с новым приложением;
  • загрузить контейнер в репозиторий;
  • изменить и применить манифест кубера.

Эти рутинные действия можно автоматизировать через функционал gitlab ci.


Архитектура приложения

Рассмотрим по шагам процесс доставки приложения в Kubernetes.

Я создал репозиторий на гитлаб и взял токен для гитлаб-раннера:




Токен вставлю чуть дальше

Хелм — это пакетный менеджер для кубернетиса (как pip для питона).

Для юниксов установка стандартная, для винды ситуация интереснее: скачайте Experimental Windows AMD64 по ссылке.

helm repo add gitlab https://charts.gitlab.io helm repo update 

Чтобы соединить гитлаб ui и гитлаб-раннер в кластере кубера,

я прописываю registration token из пункта выше в конфиг гитлаб-раннера из хелма:

# выведу содержимое конфига в .yml файл helm show values gitlab/gitlab-runner > murr-gitlab-runner.yml 

В murr-gitlab-runner.yml меня интересует три поля:

tags: "murr_runner"

Прописываем свой тег, чтобы идентифицировать гитлаб-раннер. Именно по тегу gitlab поймет, на какой под слать запросы:

gitlabUrl: https://gitlab.com/

Проверяем путь к гитлабу (гитлаб можно установить свой).

runnerRegistrationToken: "___"

Прописываем токен из шага выше.

Создам кластер кубернетиса в облачным сервисе.

Этот сервис #CloudMTS предоставляет набор готовых решений, в частности поднимет мне ноду в кубере, выделит стабильный IP-адрес и предоставит бесплатный плагин в виде ingress-nginx (пустит трафик из интернета в кластер).

На выходе я скачаю кубконфиг.

Переименую в config и закину в C:\Users\Admin\.kube

Проверю доступность кластера.

kubectl get nodes NAME                         STATUS   ROLES    AGE   VERSION liberal-dove-dcddd8-115d1b   Ready    <none>   84m   v1.21.11 

Устанавливаю раннер в отдельный неймспейс кубера.
Неймспейс — это как виртуальное окружение в питоне, то есть изолированная среда, в которой можно систематизировать сервисы:

kubectl create ns gitlab-runner helm install --namespace gitlab-runner gitlab-runner -f murr-gitlab-runner.yml gitlab/gitlab-runner 

Выдам полные права раннеру:

kubectl create clusterrolebinding --clusterrole=cluster-admin -n gitlab-runner --serviceaccount=gitlab-runner:default our-murr-runner 

Подожду пару минут и проверю, что раннер запущен:

kubectl get po -n gitlab-runner -w NAME                                          READY   STATUS    RESTARTS   AGE gitlab-runner-gitlab-runner-994b96676-bjftj   1/1     Running   0          3m33s 

Также увижу раннер в настройках гитлаба:

Создам go сервер:

package main  import (   "fmt"   "github.com/rs/cors"   "net/http" )  func main() {   fmt.Println("murr_server запущен")   mux := http.NewServeMux()   mux.HandleFunc("/murrengan/", func(w http.ResponseWriter, r *http.Request) {      w.Header().Set("Content-Type", "application/json")      w.Write([]byte("{\"message\": \"Привет, муррен!\"}"))      fmt.Println("Вызвана функция по роуту murrengan")   })   handler := cors.Default().Handler(mux)   err := http.ListenAndServe(":1991", handler)   if err != nil {      fmt.Println("murr_server упал:", err)   } } 

Он возвращает json {“message”: “Привет, муррен!”} при гет-запросе с любого IP на :1991/murrengan/

Проверяем локально:

go run main.go murr_serve запущен 

Теперь по адресу 127.0.0.1:1991/murrengan/ доступно приложение. Откроем его в браузере.

Или проверим в терминале:

curl http://127.0.0.1:1991/murrengan/ {"message": "Привет, муррен!"} 

Завернем наше приложение в Dockerfile и добавим возможность запуска в контейнере:

FROM golang:1.17-alpine  WORKDIR /  COPY go.mod ./ COPY go.sum ./ RUN go mod download  COPY *.go ./  RUN go build -o /murr_server  EXPOSE 1991   ENTRYPOINT ["/murr_server"] 

Сбилдим имидж:

docker build -t murr_server_in_docker:0.3.0 .

Проверим готовый имидж:

docker images REPOSITORY                  TAG        IMAGE ID       CREATED              SIZE murr_server_in_docker       0.3.0      18965967f809   About a minute ago   308MB 

Можно запустить и протестировать локально:

docker run -it -p 1991:1991 murr_server_in_docker:0.3.0

Описываем инструкцию работы gitlab runner в .gitlab-ci.yml

# В работе 2 стадии stages:  - build  - deploy  # https://github.com/GoogleContainerTools/kaniko # В этой функции мы даем право канико работать с нашим гитлабом. # Канико позволяет билдить контейнеры в контейнерах. .docker-login.: &docker-login  before_script:    - mkdir -p /kaniko/.docker    - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json  Build container:  image: gcr.io/kaniko-project/executor:debug  stage: build  <<: *docker-login  # тег указывали в murr-gitlab-runner.yml  tags:    - murr_runner  only:    - new_prod  script:    # тут канико билдит контейнер и через --destination пушит образ в реджистери    # каждый пуш уникальный из-за $CI_COMMIT_SHORT_SHA (глобальная переменная гитлаб-раннера)    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA  Deploy container:  image:    # этот образ позволяет запускать kubectl в script    name: lachlanevenson/k8s-kubectl:latest    entrypoint: ["/bin/sh", "-c"]  stage: deploy  tags:    - murr_runner  only:    - new_prod  script:    # в manifest.yaml я указал шаблон image: registry.gitlab.com/murrengan/murr_server:change_thist_tag_on_gitlab_ci    # и теперь через утилиту sed меняю change_thist_tag_on_gitlab_ci на уникальный коммит    - sed -i "s|change_thist_tag_on_gitlab_ci|${CI_COMMIT_SHORT_SHA}|" manifest.yaml    # применяю новый деплоймент    - kubectl apply -n default -f manifest.yaml 

Отдельно можно отметить manifest.yaml. Манифест — это описание состояния кластера кубернетис. Ты пишешь, как хочешь, чтобы было, а кубер старается так сделать.

apiVersion: apps/v1 kind: Deployment metadata:  name: murr-server-deployment spec:  selector:    matchLabels:      app: murr-server  replicas: 2  template:    metadata:      labels:        app: murr-server    spec:      containers:        - name: murr-server          image: registry.gitlab.com/murrengan/murr_server:change_thist_tag_on_gitlab_ci          imagePullPolicy: Always          ports:            - containerPort: 1991 --- kind: Service apiVersion: v1 metadata:  name: murr-server-service spec:  selector:    app: murr-server  ports:    - port: 1991 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata:  name: murr-server-ingress  annotations:    kubernetes.io/ingress.class: "nginx"    nginx.ingress.kubernetes.io/rewrite-target: /$2  spec:  rules:    - http:        paths:          - path: /murr_server(/|$)(.*)            pathType: ImplementationSpecific            backend:              service:                name: murr-server-service                port:                  number: 1991 

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

Сервис указывает, какое приложение на каком порту ждет трафик.

И как раз в ингрессе я через предустановленный плагин получаю трафик и проксирую его на url /murr_server — получается, чтобы обратиться к приложению, url будет выглядеть так:http://EXTERNAL-IP_от_провайдера_/murr_server/murrengan/

Пушим наши изменения в продакшен-ветку — new_prod:

git add . git commit git push --set-upstream origin new_prod 

Идем в пайплайны и видим запуск:


Ждем окончания второй джобы:

Ждем запуск сервиса для доступа к приложению:

kubectl get po -w NAME                                      READY   STATUS    RESTARTS   AGE murr-server-deployment-748f76bbb8-2hxxn   1/1     Running   0          60s murr-server-deployment-748f76bbb8-77ldv   1/1     Running   0          60s 

Нам надо получить EXTERNAL-IP для murr-server-load-balancer. Теперь, указав его в браузере, мы получим доступ к нашему приложению из любой точки мира.

В данном примере url выглядит так: http://91.185.95.26/murr_server/murrengan/

Теперь приложение доступно во всем мире 24/7.

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

Гитлаб сиай подхватит измения, сбилдит новый имидж в раннере у нас в кубере, зальет его в реджестери, обновит и применит новый деплоймент.

Впереди много задач: линтер, тестирование, фронтенд, https и murr_game…

Спасибо за ваш интерес к теме. Пишите в комментариях, если у вас есть вопросы.


ссылка на оригинал статьи https://habr.com/ru/company/cloud_mts/blog/658427/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *