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

Kate

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

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

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

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

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

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

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

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

oyot7myao-bgagpi_gyrwr9unje.png

vsvtuuwbwdpnb2bqa7wuoecrqag.png

w9mhv86bmql8vizyjsf6fwhlobk.png

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

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

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

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


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

gjxqyius-psz8bvdhjoaf7wclry.png


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

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


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

tags: "murr_runner"

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

xlkwkvzi31eu7mijls82bfj3ufm.png


gitlabUrl: https://gitlab.com/

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

runnerRegistrationToken: "___"

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

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

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

qcqgsc1oupuynjdbmyav5ngejfq.png


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

5q987vnrxxgvyvyrzevkvfwiplw.png


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

uaqqk2debplyfffxbyfrmrwsmau.png


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

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


i3zxelvp1fm_yw2fjp5ykzhnfvg.png


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

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


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

0hgeuuuzbhqajn2vdnxlzkwbs-u.png


Создам 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/ доступно приложение. Откроем его в браузере.

ymqxecrevz10tsbevqi8hx4gkwy.png


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

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


ftcaxkgmfy7tziaj939hn9_xb6s.png


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

35cpgnffxyemh88qnz2nsexd5uu.png


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

r4m6fmr3wtogcxrxdigxw6uyer8.png


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

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

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


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

uyxvofaqjkau8gbiolel2-aljpg.png

h-gnpoj49l5rimmrjxnvaibrkny.png


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

q7-_dmfxypdcf3bh9vbmnsylpgg.png


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

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. Теперь, указав его в браузере, мы получим доступ к нашему приложению из любой точки мира.

hbwzi-joatrnuz-3ctypgjvqvj8.png


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

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

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

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

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


 
Сверху