Как мы настроили Continuous Delivery в Kubernetes с помощью TFS

  • Tutorial

Мы продолжаем наш путь к Continuous Delivery (CD) и High Availability (HA), основанной на избыточности. В предыдущей серии мы перевели API для мобильного приложения на .NET Core. Следующий логичный шаг для достижения CD — настроить сборку в Docker-контейнер.


Сегодня поделимся нашим getting-started гайдом по настройке сборки docker-образов и деплоя в Kubernetes в TFS для разработчиков .NET.


(Предполагается, что к этому моменту вы уже мигрировали ваше ASP.NET приложение на ASP.NET Core, а если нет, читайте нашу прошлую статью).



Настройка ASP.NET приложения на работу с Docker


  1. В Visual Studio 2017 правой кнопкой по web проекту -> Add -> Docker Support;
  2. Для VS2015 нужно дополнительно поставить расширение.
  3. В папку с проектом добавится файл Dockerfile – это конфиг для создания образа нашего приложения.
  4. Подробнее о Docker можно почитать здесь.
  5. Добавится новый проект docker-compose.
  6. Сама по себе docker-compose – это утилита для управления мульти-контейнерными приложениями. Например, вы запускаете приложение и СУБД к нему.
  7. Файлы в проекте:
    a. docker-compose.yml – описание ваших сервисов/зависимостей.
    Вот пример ASP.NET приложения в связке с SQL Server:

version: '3'

services:
  agentrequests.webapp:
    image: agentrequests.webapp
    build:
      context: .
      dockerfile: AgentRequests.WebApp/Dockerfile
    depends_on:
      - agentrequests-db
  agentrequests-db: 
    image: microsoft/mssql-server-linux
    environment:
      SA_PASSWORD: "<YourStrong!Passw0rd>1"
      ACCEPT_EULA: "Y"
    ports:
      - "1401:1433"
    volumes:
      - agent-requests-db-data:/var/opt/mssql
volumes:
  agent-requests-db-data:

Имена сервисов (в примере БД — agentrequests-db) можно использовать напрямую в вашем приложении, этакий Server Side Service Discovery.


К примеру, строка соединения до базы – "Server=agentrequests-db;Database=AgentRequests;User=sa;Password=<YourStrong!Passw0rd>1;"


b. docker-compose.override.yml – этот файл используется при разработке. Visual Studio мержит эти файлы, когда жмёте F5.


HINT: При каждом изменении docker-compose.yml приложению будет назначаться новый локальный порт, что быстро надоедает при дебаге. Поэтому в docker-compose.override.yml полезно зафиксировать порт вашего приложения:


version: '3' 

services: 
  agentrequests.webapp: 
    environment: 
    - ASPNETCORE_ENVIRONMENT=Development 
    ports: 
    - "40005:80" 

c. docker-compose.ci.build.yml – с этим конфигом можно сбилдить ваше приложение на CI, и результат будет аналогичным локальному билду. Этот файл нужен, если вы просто деплоите готовые файлы, например, в IIS. Мы же собираемся поставлять готовые docker-образы и будем использовать Dockerfile напрямую.


Промежуточный итог: делаем проект docker-compose стартовым, жмём F5 и радуемся.
NOTE: Первый запуск может оказаться долгим, поскольку докеру нужно скачать образы SQL/ASP.Net Core.


Также при дебаге Visual Studio не создаёт новый докер-образ при каждом изменении кода приложения – на самом деле, создаётся только один контейнер из вашего Dockerfile, к которому монтируется папка c вашими исходниками на хост-машине. Таким образом, каждое изменение, например, js-файла, мгновенно отразится даже на запущенном контейнере.


Сборка и деплой Docker-образов в TFS


CI предполагает, что у нас есть build-машина для выполнения автоматизированных сборок независимо от разработчика. Разработчики заливают свои изменения в систему контроля версиями, build-машина берет последние изменения и пересобирает проект. Таким образом, на build-машине должны быть все необходимые инструменты для сборки проекта. В нашем случае она должна иметь доступ к Docker, чтобы собирать Docker-образы.


Есть несколько вариантов:


  1. Мы можем поставить Docker непосредственно на build-машину либо удаленно подключаться к Docker на другой машине через Docker-клиент. Изначально у нас была стандартная разработка .Net на Windows, поэтому все build-машины представляли собой виртуальные машины Windows с одним или несколькими build-агентами. Чтобы Docker мог собирать Linux-контейнеры на Windows-машине, докер устанавливает виртуальную машину с Linux. Получается, что у нас будет несколько вложенных друг в друга виртуальных машин. Но что-то не хочется начинать городить огороды, и Docker официально не поддерживает такой режим.


  2. Чтобы подключиться к Docker на другой машине, нужно настраивать удаленный доступ, по умолчанию он выключен. Также рекомендуется обеспечить безопасность TLS сертификатам. Еще есть инструкция от Microsoft, в которой предлагается упрощенный вариант настройки с помощью windows-контейнера с предустановленной LibreSSL.


  3. Наиболее простой способ: build-агент можно запустить прямо в Docker-контейнере, и он будет иметь доступ к Docker, на котором запущен. Достаточно выбрать нужный контейнер из репозитория microsoft/vsts-agent.

Настройка билд-агента


  1. Скачиваем билд-агент.
  2. Про различия версий образов и параметры можно прочитать тут.
  3. Нужна версия c docker'ом на борту, к примеру:
    docker pull microsoft/vsts-agent:ubuntu-16.04-tfs-2017-u1-docker-17.12.0-ce
  4. Генерим Personal Access Token (PAT) в на странице Security в TFS:

  5. Можно добавить новый Agent Pool для сборок докера. Делается это здесь:

  6. Запускаем контейнер:
    docker run \ 
    -e TFS_URL=<YOUR_TFS_URL> \ 
    -e VSTS_TOKEN=<PAT> \ 
    -e VSTS_POOL=<POOL> \
    -e VSTS_AGENT=$(hostname)-agent \ 
    -v /var/run/docker.sock:/var/run/docker.sock \ 
    --restart=always \
    -it microsoft/vsts-agent:ubuntu-16.04-tfs-2017-u1-docker-17.12.0-ce 

Настройка CI


  1. В проекте в TFS добавляем новый Build Definition с темплейтом – Container (PREVIEW)
  2. Таска build image:

    a. Container Registry Type – Container Registry;
    b. Docker Registry Connection – здесь настраиваем путь до вашего реестра образов. Можно использовать и Docker Hub, но мы в компании используем Nexus Registry;
    c. Docker File – путь до докер файла. Лучше указать путь явно, без маски;
    d. Use Default Build Context – снимаем галочку;
    e. Build Context – путь до папки, в которой лежит ваш .sln файл;
    f. Image-name – лучше задать явно, все символы в нижнем регистре. Пример: groups/agent-requests:$(Build.BuildId);
    g. Можно поставить include latest tag – будет обновляться latest тег в реестре.
  3. Таска push an image — аналогично второму пункту. Главное, не забыть поменять image name.
  4. Добавить таску с темплейтом Publish Build Artifacts. Поскольку мы планируем деплоить в kubernetes, нашим артефактом будет конфиг для kubectl:

    a. Path to Publish – путь до вашего yaml файла с конфигом kubernetes. Можно указать папку, если конфигов несколько;
    b. Artifact Name – на ваш вкус. К примеру, kubernetes;
    c. Artifact Type – Server.

Настройка CD и Kubernetes


Вначале небольшое отступление. Грубо говоря, docker-compose (swarm) – это конкурент kubernetes. Но поскольку в VS нет удобного тулинга для билда и дебага в kubernetes, то используем оба варианта: compose при разработке, kubernetes на бою.


Приятная новость в том, что есть утилита Kompose – она умеет конвертить Kubernetes конфиги в/из docker-compose.yaml файлы.


Впрочем, не обязательно для девелопа вообще использовать docker/compose – можно настроить всё по старинке и руками менять урлы/конфиги или хранить по десять web.config для разных окружений.


Пример service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  type: LoadBalancer
  selector:
    app: webapp
  ports:
  - port: 80

Пример deployment.yaml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: webapp
    spec:
      imagePullSecrets:
      - name: <название соединения с Docker Registry>
      containers:
      - image: webapp
        name: webapp
        env:
          - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: database-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: database-secret
              key: password
        - name: SQLCONNSTR_<Название сроки подключения> 
#например SQLCONNSTR_DefaultConnection
          value: "Server=<адрес SQL сервера>;Initial Catalog=<название БД>;Persist Security Info=False;User ID=$(DB_USER);Password=$(DB_PASSWORD);"
        ports:
        - containerPort: 80

Для настройки CD мы использовали Kubernetes extension для нашего TFS сервера, так как стандартная таска Deploy to Kubernetes, которая идет из коробки, в нашей версии TFS оказалась нерабочей.


  1. Добавьте в Kubernetes настройки подключения к нашему Docker Registry

    kubectl create secret docker-registry\
     <название соединения с Docker Registry>\
    --docker-server=<адрес сервера>\
    --docker-username=<логин>\
    --docker-password=<пароль>\
    --docker-email=<почта>
  2. Добавьте логин и пароль для подключения к базе


    kubectl create secret generic database-secret\
    --from-literal=username=<логин>\
    --from-literal=password=<пароль>

    Примечание: шаги 1 и 2 так же можно автоматизировать через TFS.


  3. Добавьте Kubernetes endpoint в TFS:



  4. Создайте новый Release Defenition:

    a. Выберите проект и Build definition
    b. Поставьте галочку Continuous deployment


  5. Добавьте таску kubernetes downloader:

    a. В поле “kubernetes end point” выберите ваш kubernetes endpoint
    b. В поле “kubectl download version” укажите необходимую версию или оставьте поле пустым, в этом случае будет установлена последняя версия клиента.


  6. Добавьте таску kubectl exec:

    a. В поле “Sub Command” пишем: apply
    b. В поле “Arguments” пишем: -f $(System.DefaultWorkingDirectory)/<путь к папке с конфигами>/deployment.yaml


  7. Добавьте таску kubectl exec:

    a. В поле “Sub Command” пишем: apply
    b. В поле “Arguments” пишем: image -f $(System.DefaultWorkingDirectory)/<путь к папке с конфигами>/service.yaml


  8. Добавьте таску kubectl exec:

    a. В поле “Sub Command” пишем: set
    b. В поле “Arguments” пишем: image -f $(System.DefaultWorkingDirectory)/<путь к папке с конфигами>/deployment.yaml webapp=webapp:$(Build.BuildID)

Итоги


За сим всё. Используйте docker, автоматизируйте развёртывание и наслаждайтесь лёгким релизом хоть в вечер пятницы, даже перед вашим отпуском.


  • +17
  • 4,7k
  • 3
EastBanc Technologies 124,22
Специалисты по цифровой трансформации бизнеса
Поделиться публикацией
Комментарии 3
    0
    месье знает толк в извращениях %)
      0
      Зачем вам докер? Я уже лет 6 автоматически выкладываю asp проекты и иные используя от простого паблиша через msbuild, заканчивая release management + новинками в 2017 tfs. Что я упустил, если ваше апи это один или несколько сервисов или сайтов в iis?
        0

        Проблема в том, что окружение на test/stage/production серверах может сильно отличаться, что ведет к непредсказуемым ошибкам. С контейнерами можно быть уверенным, что если это работает локально, то также будет и на продакшене.


        А вообще Хансельман написал отдельную статью, отвечающую на этот вопрос.

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

      Самое читаемое