Как мы настроили Docker и Kubernetes в TFS

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


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


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


31sbg4hscjrlzb9tv-lxoaighn0.jpeg


Настройка 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: "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=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:

    zssrzfhqc6agl55asetshvtqc2o.png

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

    8sqvt8gq8u5vnkxtgtwa8uzo9ys.png

  6. Запускаем контейнер:
    docker run \ 
    -e TFS_URL= \ 
    -e VSTS_TOKEN= \ 
    -e VSTS_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:

    b_g8ob6ifjo2ynohpozqb4ym-mg.png
    eqpzqcqein5sxc5ueynjh9hqw1i.png


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

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


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

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

    kfvv7ov4l_nsykmz-0g74hje75a.png


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

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

    n5l-xuhvm3vcluwklo8kdd3sdhm.png


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


Напоследок несколько ссылок, которые мы нашли весьма полезными
  • https://store.docker.com/editions/community/docker-ce-desktop-windows
  • https://habrahabr.ru/company/microsoft/blog/337626/
  • https://blogs.msdn.microsoft.com/wasimbloch/2017/01/23/setting-up-kubernetes-on-windows10-laptop-with-minikube/
  • https://www.youtube.com/watch? v=ngcigr_8oxw

© Habrahabr.ru