[Перевод] Подготовка приложения для Istio

qyywv1qmzphh1svbblasrxjc6ym.jpeg

Istio — это удобный инструмент для соединения, защиты и мониторинга распределенных приложений. В Istio используются разные технологии для масштабного запуска ПО и управления им, включая контейнеры для упаковки кода приложения и зависимостей для развертывания и Kubernetes — для управления этими контейнерами. Поэтому для работы с Istio вы должны знать, как приложение с несколькими сервисами на основе этих технологий работает без Istio. Если эти инструменты и понятия вам уже знакомы, смело пропускайте это руководство и переходите прямо к разделу Установка Istio на Google Kubernetes Engine (GKE) или установке расширения Istio on GKE.

Это пошаговое руководство, где мы рассмотрим весь процесс от исходного кода до контейнера на GKE, чтобы вы получили базовое представление об этих технологиях на примере. Также вы увидите, как Istio использует возможности этих технологий. Предполагается, что вы не знаете ничего о контейнерах, Kubernetes, service mesh или Istio.


Задачи

В этом руководстве вы выполните следующие задачи:


  1. Изучение простого приложения hello world с несколькими службами.
  2. Запуск приложение из исходного кода.
  3. Упаковка приложения в контейнеры.
  4. Создание кластера Kubernetes.
  5. Развертывание контейнеров в кластер.


Прежде чем начать

Следуйте инструкциям, чтобы включить Kubernetes Engine API:


  1. Зайдите на страницу Kubernetes Engine в консоли Google Cloud Platform.
  2. Создайте или выберите проект.
  3. Подождите, пока включится API и связанные службы. Это может занять несколько минут.
  4. Убедитесь, что для проекта Google Cloud Platform настроено выставление счетов. Узнайте, как включить выставление счетов.

В этом руководстве можно использовать Cloud Shell, который подготавливает виртуальную машину g1-small в Google Compute Engine с Linux на основе Debian, или компьютер на Linux или macOS.


Вариант А: использование Cloud Shell

Преимущества использования Cloud Shell:


  • Среды разработки Python 2 и Python 3 (включая virtualenv) полностью настроены.
  • Инструменты командной строки gcloud, docker, git и kubectl, которые мы будем использовать, уже установлены.
  • У вас на выбор несколько текстовых редакторов:
    1. Редактор кода, который открывается значком редактирования в верхней части окна Cloud Shell.
    2. Emacs, Vim или Nano, которые открываются из командной строки в Cloud Shell.

Чтобы использовать Cloud Shell:


  1. Перейдите в консоль GCP.
  2. Нажмите кнопку Activate Cloud Shell (Активировать Cloud Shell) в верхней части окна консоли GCP.

yush9xie9_peummad02lgu2n5x8.png

В нижней части консоли GCP в новом окне откроется сеанс Cloud Shell с командной строкой.

yino-qj5bwaluwxlo3xtf0yr7ty.png


Вариант Б: использование инструментов командной строки локально

Если вы будете работать на компьютере с Linux или macOS, нужно настроить и установить следующие компоненты:


  1. Настройте среду разработки Python 3 и Python 2.


  2. Установите Cloud SDK с инструментом командной строки gcloud.


  3. Установите kubectl — инструмент командной строки для работы с Kubernetes.

    gcloud components install kubectl

  4. Установите Docker Community Edition (CE). Вы будете использовать инструмент командной строки docker, чтобы создавать образы контейнеров для примера приложения.


  5. Установите инструмент контроля версий Git, чтобы получить пример приложения с GitHub.



Загрузка примера кода


  1. Загрузите исходный код helloserver:

    git clone https://github.com/GoogleCloudPlatform/istio-samples

  2. Перейдите в каталог примера кода:

    cd istio-samples/sample-apps/helloserver


Изучение приложения с несколькими сервисами

Пример приложения написан на Python и состоит из двух компонентов, которые взаимодействуют с помощью REST:


  • server: простой сервер с одной конечной точкой GET, /, который выводит «hello world» на консоли.
  • loadgen: скрипт, который посылает трафик на server, с настраиваемым числом запросов в секунду.

b1kyljxdkjdci12qtle2w3dmtjw.png


Запуск приложения из исходного кода

Чтобы изучить пример приложения, запустите его в Cloud Shell или на компьютере.
1) В каталоге istio-samples/sample-apps/helloserver запустите server:

python3 server/server.py

При запуске server отображается следующее:

INFO:root:Starting server...

2) Откройте другое окно терминала, чтобы отправлять запросы к server. Если вы используете Cloud Shell, нажмите значок добавления, чтобы открыть другой сеанс.
3) Отправьте запрос к server:

curl http://localhost:8080

server отвечает:

Hello World!

4) Из каталога, куда вы загрузили примера кода, перейдите в каталог, который содержит loadgen:

cd YOUR_WORKING_DIRECTORY/istio-samples/sample-apps/helloserver/loadgen

5) Создайте следующие переменные среды:

export SERVER_ADDR=http://localhost:8080
export REQUESTS_PER_SECOND=5

6) Запустите virtualenv:

virtualenv --python python3 env

7) Активируйте виртуальную среду:

source env/bin/activate

8) Установите требования для loadgen:

pip3 install -r requirements.txt

9) Запустите loadgen:

python3 loadgen.py

При запуске loadgen выводит примерно следующее сообщение:

Starting loadgen: 2019-05-20 10:44:12.448415
5 request(s) complete to http://localhost:8080

В другом окне терминала server выводит на консоль примерно следующие сообщения:

127.0.0.1 - - [21/Jun/2019 14:22:01] "GET / HTTP/1.1" 200 -
INFO:root:GET request,
Path: /
Headers:
Host: localhost:8080
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*

С точки зрения сети, все приложение работает на одном хосте (локальном компьютере или виртуальной машине Cloud Shell). Поэтому можно использовать localhost, чтобы отправлять запросы к server.
10) Чтобы остановить loadgen и server, введите Ctrl-c в каждом окне терминала.
11) В окне терминала loadgen деактивируйте виртуальную среду:

deactivate


Упаковка приложения в контейнеры

Чтобы запустить приложение на GKE, нужно упаковать пример приложения — server и loadgen — в контейнеры. Контейнер — это способ упаковать приложение, чтобы изолировать его от среды.

Чтобы упаковать приложение в контейнер, нужен Dockerfile. Dockerfile — это текстовый файл, где определяются команды для сборки исходного кода приложения и его зависимостей в образ Docker. После сборки вы загружаете образ в реестр контейнеров, например Docker Hub или Container Registry.

В примере уже есть Dockerfile для server и loadgen со всеми нужными командами, чтобы собрать образы. Ниже — Dockerfile для server:

FROM python:3-slim as base
FROM base as builder
RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        g++ \
    && rm -rf /var/lib/apt/lists/*

# Enable unbuffered logging
FROM base as final
ENV PYTHONUNBUFFERED=1

RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        wget

WORKDIR /helloserver

# Grab packages from builder
COPY --from=builder /usr/local/lib/python3.7/ /usr/local/lib/python3.7/

# Add the application
COPY . .

EXPOSE 8080
ENTRYPOINT [ "python", "server.py" ]


  • Команда FROM python:3-slim as base велит Docker использовать последний образ Python 3 в качестве базового.
  • Команда COPY. . копирует исходные файлы в текущий рабочий каталог (в нашем случае только server.py) в файловую систему контейнера.
  • ENTRYPOINT определяет команду, которая используется для запуска контейнера. В нашем случае эта команда почти совпадает с той, которую вы использовали для запуска server.py из исходного кода.
  • Команда EXPOSE указывает, что server ожидает данные через порт 8080. Эта команда не предоставляет порты. Это что-то вроде документации, которая нужна, чтобы открыть порт 8080 при запуске контейнера.


Подготовка к контейнеризации приложения

1) Задайте следующие переменные среды. Замените PROJECT_ID на идентификатор своего проекта GCP.

export PROJECT_ID="PROJECT_ID"
export GCR_REPO="preparing-istio"

С помощью значений PROJECT_ID и GCR_REPO вы помечаете образ Docker, когда собираете и отправляете его в частный Container Registry.

2) Задайте проект GCP по умолчанию для инструмента командной строки gcloud.

gcloud config set project $PROJECT_ID

3) Задайте зону по умолчанию для инструмента командной строки gcloud.

gcloud config set compute/zone us-central1-b

4) Убедитесь, что сервис Container Registry включен в проекте GCP.

gcloud services enable containerregistry.googleapis.com


Контейнеризация server


  1. Перейдите в каталог, где находится пример server:

    cd YOUR_WORKING_DIRECTORY/istio-samples/sample-apps/helloserver/server/

  2. Соберите образ с помощью Dockerfile и переменных среды, которые вы определили раньше:

    docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1 .

Параметр -t представляет тег Docker. Это имя образа, который вы используете при развертывании контейнера.


  1. Отправьте образ в Container Registry:
    docker push gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1


Контейнеризация loadgen

1) Перейдите в каталог, где находится пример loadgen:

cd ../loadgen

2) Соберите образ:

docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1 .

3) Отправьте образ в Container Registry:

docker push gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1


Просмотр списка образов

Просмотрите список образов в репозитории и убедитесь, что образы отправлены:

gcloud container images list --repository gcr.io/$PROJECT_ID/preparing-istio

Команда выдает имена только что отправленных образов:

NAME
gcr.io/PROJECT_ID/preparing-istio/helloserver
gcr.io/PROJECT_ID/preparing-istio/loadgen


Создание кластера GKE.

Эти контейнеры можно было бы запустить на виртуальной машине Cloud Shell или на компьютере командой docker run. Но в производственной среде нужен способ централизованно оркестрировать контейнеры. Например, нужна система, которая следит, чтобы контейнеры всегда работали, и нужен способ увеличивать масштаб и запускать дополнительные экземпляры контейнеров, если трафик возрастет.

Для запуска контейнерных приложений можно использовать GKE. GKE — это платформа оркестрации контейнеров, которая объединяет виртуальные машины в кластер. Каждая виртуальная машина называется узлом. Кластеры GKE основаны на опенсорс-системе управления кластерами Kubernetes. Kubernetes предоставляет механизмы взаимодействия с кластером.

Создание кластера GKE:

1) Создайте кластер:

gcloud container clusters create istioready \
  --cluster-version latest \
  --machine-type=n1-standard-2 \
  --num-nodes 4

Команда gcloud создает кластер istioready в проекте GCP и зоне по умолчанию, которые вы указали. Чтобы запустить Istio, рекомендуем иметь хотя бы 4 узла и виртуальную машину n1-standard-2.

Команда создает кластер несколько минут. Когда кластер будет готов, команда выдает подобное сообщение.

2) Укажите учетные данные в инструменте командной строки kubectl, чтобы с ее помощью управлять кластером:

gcloud container clusters get-credentials istioready

3) Теперь можно общаться с Kubernetes через kubectl. Например, следующей командой можно узнать статус узлов:

kubectl get nodes

Команда выдает список узлов:

NAME                                       STATUS   ROLES    AGE    VERSION
gke-istoready-default-pool-dbeb23dc-1vg0   Ready       99s    v1.13.6-gke.13
gke-istoready-default-pool-dbeb23dc-36z5   Ready       100s   v1.13.6-gke.13
gke-istoready-default-pool-dbeb23dc-fj7s   Ready       99s    v1.13.6-gke.13
gke-istoready-default-pool-dbeb23dc-wbjw   Ready       99s    v1.13.6-gke.13


Ключевые понятия Kubernetes

На схеме показано приложение на GKE:

-sx2ujtlifvzdtixzdqpnr9jzva.png

Прежде чем развернуть контейнеры в GKE, изучите ключевые понятия Kubernetes. В самом конце есть ссылки, если вы хотите узнать больше.


  • Узлы и кластеры. В GKE узел — это виртуальная машина. На других платформах Kubernetes узлом может быть компьютер или виртуальная машина. Кластер — это набор узлов, которые можно считать единым целым и где вы развертываете контейнеризированное приложение.
  • Pod«ы. В Kubernetes контейнеры запускаются в pod«ах. Pod в Kubernetes — это неделимая единица. Pod вмещает один или несколько контейнеров. Вы разворачиваете контейнеры server и loadgen в отдельных pod«ах. Когда в pod«е несколько контейнеров (например, сервер приложения и прокси-сервер), контейнеры управляются как единый объект и совместно используют ресурсы pod«а.
  • Развертывания. В Kubernetes развертывание — это объект, представляющий собой набор идентичных pod«ов. Развертывание запускает несколько реплик pod«ов, распределенных по узлам кластера. Развертывание автоматически заменяет pod«ы, которые отказали или не отвечают.
  • Сервис Kubernetes. При запуске кода приложения в GKE меняется соединение между loadgen и server. Когда вы запустили сервисы на виртуальной машине Cloud Shell или на компьютере, вы отправляли запросы к server по адресу localhost:8080. После развертывания в GKE pod«ы выполняются на доступных узлах. По умолчанию вы никак не можете управлять тем, на каком узле запущен pod, так что у pod«ов нет постоянных IP-адресов.
    Чтобы получить IP-адрес для server, нужно определить абстракцию сети поверх pod«ов. Это и есть сервис Kubernetes. Сервис Kubernetes предоставляет постоянную конечную точку для набора pod«ов. Есть несколько типов сервисов. server использует LoadBalancer, который предоставляет внешний IP-адрес, чтобы связаться с server из-за пределов кластера.
    Еще в Kubernetes есть встроенная система DNS, которая назначает имена DNS (например, helloserver.default.cluster.local) сервисам. Благодаря этому pod«ы внутри кластера связываются с другими pod«ами в кластере по постоянному адресу. Имя DNS нельзя использовать за пределами кластера, например в Cloud Shell или на компьютере.


Манифесты Kubernetes

Когда вы запускали приложение из исходного кода, вы использовали императивную команду python3

server.py

Императивность подразумевает глагол: «сделай это».

Kubernetes использует декларативную модель. Это значит, что мы не говорим Kubernetes, что именно нужно делать, а описываем желаемое состояние. Например, Kubernetes запускает и останавливает pod«ы по мере необходимости, чтобы фактическое состояние системы соответствовало желаемому.

Желаемое состояние вы указываете в манифестах, или файлах YAML. Файл YAML содержит спецификации для одного или нескольких объектов Kubernetes.

В примере содержится файл YAML для server и loadgen. Каждый файл YAML указывает желаемое состояние объекта развертывания и сервиса Kubernetes.

server.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloserver
spec:
  selector:
    matchLabels:
      app: helloserver
  replicas: 1
  template:
    metadata:
      labels:
        app: helloserver
    spec:
      terminationGracePeriodSeconds: 5
      restartPolicy: Always
      containers:
      - name: main
        image: gcr.io/google-samples/istio/helloserver:v0.0.1
        imagePullPolicy: Always


  • kind указывает тип объекта.
  • metadata.name указывает имя развертывания.
  • Первое поле spec содержит описание желаемого состояния.
  • spec.replicas указывает желаемое число pod«ов.
  • Раздел spec.template определяет шаблон pod«а. В спецификации pod«ов есть поле image, где указывается имя образа, который нужно извлечь из Container Registry.

Сервис определяется следующим образом:

apiVersion: v1
kind: Service
metadata:
  name: hellosvc
spec:
  type: LoadBalancer
  selector:
    app: helloserver
  ports:
  - name: http
    port: 80
    targetPort: 8080


  • LoadBalancer: клиенты отправляют запросы на IP-адрес балансировщика нагрузки, у которого есть постоянный IP-адрес и который доступен из-за пределов кластера.
  • targetPort: как вы помните, команда EXPOSE 8080 в Dockerfile не предоставляла порты. Вы предоставляете порт 8080, чтобы можно было связаться с контейнером server снаружи кластера. В нашем случае hellosvc.default.cluster.local:80 (короткое имя: hellosvc) соответствует порту 8080 IP-адреса пода helloserver.
  • port: это номер порта, куда остальные сервисы в кластере будут отправлять запросы.

loadgen.yaml

Объект развертывания в loadgen.yaml похож на server.yaml. Разница в том, что объект развертывания содержит раздел env. Он определяет переменные среды, которые нужны loadgen и которые вы установили при запуске приложения из исходного кода.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: loadgenerator
spec:
  selector:
    matchLabels:
      app: loadgenerator
  replicas: 1
  template:
    metadata:
      labels:
        app: loadgenerator
    spec:
      terminationGracePeriodSeconds: 5
      restartPolicy: Always
      containers:
      - name: main
        image: gcr.io/google-samples/istio/loadgen:v0.0.1
        imagePullPolicy: Always
        env:
        - name: SERVER_ADDR
          value: "http://hellosvc:80/"
        - name: REQUESTS_PER_SECOND
          value: "10"
        resources:
          requests:
            cpu: 300m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi

Раз loadgen не принимает входящие запросы, для поля type указано ClusterIP. Этот тип предоставляет постоянный IP-адрес, который могут использовать сервисы в кластере, но этот IP-адрес не предоставляется внешним клиентам.

apiVersion: v1
kind: Service
metadata:
  name: loadgensvc
spec:
  type: ClusterIP
  selector:
    app: loadgenerator
  ports:
  - name: http
    port: 80
    targetPort: 8080


Развертывание контейнеров в GKE

1) Перейдите в каталог, где находится пример server:

cd YOUR_WORKING_DIRECTORY/istio-samples/sample-apps/helloserver/server/

2) Откройте server.yaml в текстовом редакторе.
3) Замените имя в поле image на имя вашего образа Docker.

image: gcr.io/PROJECT_ID/preparing-istio/helloserver:v0.0.1

Замените PROJECT_ID на идентификатор вашего проекта GCP.
4) Сохраните и закройте server.yaml.
5) Разверните файл YAML в Kubernetes:

kubectl apply -f server.yaml

После успешного завершения команда выдает следующий код:

deployment.apps/helloserver created
service/hellosvc created

6) Перейдите в каталог, где находится loadgen:

cd ../loadgen

7) Откройте loadgen.yaml в текстовом редакторе.
8) Замените имя в поле image на имя вашего образа Docker.

image: gcr.io/PROJECT_ID/preparing-istio/loadgenv0.0.1

Замените PROJECT_ID на идентификатор вашего проекта GCP.
9) Сохраните и закройте loadgen.yaml, закройте текстовый редактор.
10) Разверните файл YAML в Kubernetes:

kubectl apply -f loadgen.yaml

После успешного завершения команда выдает следующий код:

deployment.apps/loadgenerator created
service/loadgensvc created

11) Проверьте статус подов:

kubectl get pods

Команда показывает статус:

NAME                             READY   STATUS    RESTARTS   AGE
helloserver-69b9576d96-mwtcj     1/1     Running   0          58s
loadgenerator-774dbc46fb-gpbrz   1/1     Running   0          57s

12) Извлеките логи приложения из пода loadgen. Замените POD_ID на идентификатор из предыдущего ответа.

kubectl logs loadgenerator-POD_ID

13) Получите внешние IP-адреса hellosvc:

kubectl get service

Ответ команды выглядит примерно так:

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
hellosvc     LoadBalancer   10.81.15.158   192.0.2.1       80:31127/TCP   33m
kubernetes   ClusterIP      10.81.0.1                443/TCP        93m
loadgensvc   ClusterIP      10.81.15.155             80/TCP         4m52s

14) Отправьте запрос к hellosvc: замените EXTERNAL_IP на внешний IP-адрес hellosvc.

curl http://EXTERNAL_IP


Беремся за Istio

У вас уже есть приложение, развернутое в GKE. loadgen может использовать Kubernetes DNS (hellosvc:80), чтобы отправлять запросы к server, и вы можете отправлять запросы к server по внешнему IP-адресу. Хотя у Kubernetes много возможностей, кое-какой информации о сервисах не хватает:


  • Как взаимодействуют сервисы? Какие отношения между сервисами? Как проходит трафик между сервисами? Вы в курсе, что loadgen отправляет запросы к server, но представьте, что вы ничего не знаете о приложении. Чтобы ответить на эти вопросы, смотрим на список запущенных подов в GKE.
  • Метрики. Как долго server отвечает на входящий запрос? Сколько запросов в секунду поступает на server? Он выдает сообщения об ошибках?
  • Сведения о безопасности. Трафик между loadgen и server проходит просто по HTTP или по mTLS?

На все эти вопросы отвечает Istio. Для этого Istio помещает sidecar-прокси Envoy в каждый pod. Прокси Envoy перехватывает весь входящий и исходящий трафик к контейнерам приложения. Это означает, что server и loadgen получают по sidecar-прокси Envoy, и весь трафик от loadgen к server проходит через прокси Envoy.

Соединения между прокси Envoy образуют service mesh. Архитектура service mesh предоставляет уровень контроля поверх Kubernetes.

7nskz-5os2onyoaaf5kz5x38ve4.png

Раз прокси Envoy выполняются в своих контейнерах, Istio можно установить поверх кластера GKE, почти не меняя код приложения. Но вы проделали кое-какую работу, чтобы подготовить приложение к управлению с помощью Istio:


  • Сервисы для всех контейнеров. К развертываниям server и loadgen привязано по сервису Kubernetes. Даже у loadgen, к которому не поступают входящие запросы, есть сервис.
  • У портов в сервисах должны быть имена. Хотя в GKE порты сервисов можно оставлять без имени, Istio требует указать имя порта в соответствии с его протоколом. В файле YAML порт для server называется http, потому что server использует протокол HTTP. Если бы service использовал gRPC, вы бы назвали порт grpc.
  • Развертывания помечаются. Поэтому вы можете использовать функции управления трафиком Istio, например разделять трафик между версиями одного сервиса.


Установка Istio

Установить Istio можно двумя способами. Можно включить расширение Istio on GKE или установить опенсорс-версию Istio на кластере. С Istio on GKE можно легко управлять установкой и апгрейдом Istio в рамках жизненного цикла кластера GKE. Если вам нужна самая новая версия Istio или больше контроля над конфигурацией панели управления Istio, установите опенсорс-версию вместо расширения Istio on GKE. Чтобы определиться с подходом, читайте статью Нужен ли мне Istio on GKE?.

Выберите вариант, изучите соответствующее руководство и следуйте инструкциям, чтобы установить Istio на кластере. Если вы хотите использовать Istio с только что развернутым приложением, включите внедрение sidecar«ов для пространства имен default.


Очистка

Чтобы с аккаунта Google Cloud Platform не списывалась плата за ресурсы, которые вы использовали в этом руководстве, удалите кластер контейнера, когда установите Istio и наиграетесь с примером приложения. При этом будут удалены все ресурсы кластера, например вычислительные экземпляры, диски и сетевые ресурсы.


Что дальше?


© Habrahabr.ru