Как мы пришли к Continuous Delivery


шишки, грабли, планы на будущее

Андрей Ермаков, Тинькофф.ру

  • Андрей Ермаков
    Ведущий разработчик, Tinkoff.ru
  • Telegram: dreef3
  1. Опыт
  2. Построение pipeline
  3. Тесты
  4. Люди
  5. Резюме

Опыт

  • Не успевали протестировать — размерджили фичу
  • 5 человек 3 дня делают регресс-тестирование
  • Ночные релизы
  • Команда не отвечает за доставку
  • Спринт: 14 дней
  • Cycle time задачи (от постановки до релиза):
    25 дней

Новый продукт: CRM

  • фронтенд + бэкенд
  • 3 разработчика, тестировщик, автоматизатор, аналитик

3 продукта в production

  • ~20 сервисов
  • 14 разработчиков, 3 тестировщика, 3 аналитика
  • 2 разработчика инфраструктуры

Cycle time:
4 дня
× 6 раз

1. Построение pipeline

  1. Build
  2. Deploy
  3. Test

Build

Deploy

Test

build-step.sh

      npm install
      npm run build
      tar czvf app-1.1.tar.gz dist/    
      
Dockerfile

      FROM nginx:stable
      COPY . /usr/share/nginx/www/html/
      

build-step.sh

      npm install
      npm run build
      docker build -t app-1.1 ./dist/
      
Dockerfile

      FROM node:8 AS build-env
      COPY . .
      RUN npm ci && npm run build
      FROM nginx:stable
      COPY --from=build-env /dist \
           /usr/share/nginx/www/html/
      
build-step.sh

      docker build -t app-1.1. ./dist/
      
node-build.Dockerfile

      FROM private.registry/node-base
      ONBUILD COPY package.json .
      ONBUILD RUN npm ci
      ONBUILD COPY . .
      ONBUILD RUN npm run build
      
Dockerfile

      FROM private.registry/node-build AS build-env
      FROM private.registry/static
      

Build

Deploy

Test

Сколько нужно тестовых окружений?

  • QA1=PRODLIKE
  • QA2
  • QA3
docker-compose.yml

      version: '2'
      services:
        file-api-qa3:
          env:
            DB_NAME: file-api-qa3
          ports:
          - 8191:8080
      
docker-compose.yml

      version: '2'
      services:
        file-api:
          env:
            SERVICE_NAME: file-api-$VARIANT
            DB_NAME: file-api-$VARIANT
          ports:
          - 8080
      
  • file-api-qa3.rancher.internal
  • file-api-qa3.consul
  • file-api-qa3.qa-ns
upstream.ctmpl

      {{range services}}
      {{$name := .Name}}
      {{$service := service .Name}}
      upstream {{$name}} {
          {{range $service}}
          server {{.Address}}:{{.Port}};
          {{else}}server 127.0.0.1:65535;
          {{end}}
      } {{end}}
      
  • Кушает ресурсы
  • Что делать с stateful?
  • Внешние зависимости
  • 188 Gb, 58 vCPU
  • 9 nodes
  • 50+ окружений
cleanup_feature_stacks.py

statuses = ','.join(['trashed', 'released'])
params = {
'jql': "status in ({1})".format(statuses),
'fields': 'id,key'
}
rq.get('https://jira/rest/api/2/search',
      params=params)
      

docker run \
  --name db-file-api-$VARIANT \
  -p 5432 \
  postgres:9.6
export PORT=$(docker port db-file-api-$VARIANT 5432 |
  sed 's/.*:\([0-9]\+\)$/\1/g')

docker exec -t -i \
  db-file-api-$VARIANT \
  psql -c test-dumb.sql

      createdb db-file-api-$VARIANT \
          -t file-api-prod-db-template
      dropdb db-file-api-$VARIANT
      
git clone git:server/internal-services-new2
docker build -t fileStorageApi .
docker run -e DB_NAME=testdb1
curl https://vm-tst-app-1:8191/

git clone git:server/file-api
docker build -t file-api .
docker run -e DB_NAME=file-api-$VARIANT
curl https://file-api-$VARIANT.test/
      

Pipeline == Code

Click, click, click

CRM.kt

          backend {
            name = "file-api"
    
            resources {
              swagger {
                name = "file-api-swagger"
                dockerFile = "swagger.Dockerfile"
              }
            }
          }
          

2. Тесты

Сколько нужно тестов и каких?

  • Е2Е — Selenium
  • API
  • Unit
  • Все на одном стеке технологий
  • Запускать все
  • Не запускать все
  • На языке сервиса
  • Проверяем то, что изменилось

      service {
          name = "file-api"
      }
          

      export TEST_TAGS="file-api"
          

      it('@file-api загружает файл', () => {
           /* ... */ 
      });
          
Компонент 🐛 🔥
Админка
Логин
Заявки
Клиенты
Компонент 🐛 🔥
Админка 6
Логин 10
Заявки 9
Клиенты 8

2 Blocker * 10

+ 1 Critical * 5

+ 8 Major * 3

+ 6 Normal * 1

Компонент 🐛 🔥
Админка 6 13 78
Логин 10 10 100
Заявки 9 24 216
Клиенты 8 3 24
  • SLA качества продукта
    0 Blocker-багов
  • Архитектура

3. Люди

Кто должен писать тесты?

  • Автоматизатор тестирования
  • Тестировщик
  • Разработчик

Функциональный тестировщик?

Ответственный за качество

Наставник по тестированию

Тестировщики
1 : 5
Разработчики

  • Как будет тестироваться моя фича?
  • Как она попадет на production?

Сами используем свой продукт

Эксперименты

Резюме

  • Все в Docker
  • Масштабировать окружения
  • Унификация шагов пайплайна, настроек
  • Тестирование на основе метрик
  • Ответственность всей команды

Андрей Ермаков, Тинькофф.ру
Как мы пришли к Continuous Delivery