Современные приложения на OpenShift, часть 2: связанные сборки chained builds

Всем привет! С вами второй пост из нашей серии, в которой мы показываем, как развертывать на Red Hat OpenShift современные веб-приложения.

Современные приложения на OpenShift, часть 2: связанные сборки chained builds

В предыдущем посте мы слегка затронули возможности нового builder-образа S2I (source-to-image), который предназначен для сборки и развертывания современных веб-приложений на платформе OpenShift. Тогда нас интересовала тема быстрого развертывание приложения, а сегодня мы рассмотрим, как использовать S2I-образ в качестве «чистого» builder-образа и совмещать его со связанными сборками OpenShift.

Чистый builder-образ

Как мы упомянули в первой части, у большинства современных веб-приложений есть так называемый этап сборки, на котором обычно выполняются такие операции, как транспиляция кода, конкатенация нескольких файлов и минификация. Полученные по итогу этих операций файлы – а это статический HTML, JavaScript и CSS – складываются в output-папку. Местоположение этой папки обычно зависит от того, какие средства сборки используются, и для React это будет папка ./build (мы подробнее вернемся к этому вопросу ниже).

Source-to-Image (S2I)

В этом посте мы совсем не касаемся темы «что такое S2I и как его использовать» (подробнее об этом можно почитать здесь), но при этом важно четко представлять себе два этапа этого процесса, чтобы понять, что же делает образ Web App Builder.

Этап ассемблирования (assemble phase)

Этап ассемблирования по своей сути очень похож на то, что происходит, когда вы запускаете docker build и в результате получаете новый Docker-образ. Соответственно, этот этап возникает при запуске сборки на платформе OpenShift.

В случае образа Web App Builder за установку зависимостей вашего приложения и запуск сборки отвечает assemble script. По умолчанию builder-образ использует конструкцию npm run build, но ее можно переопределить через переменную окружения NPM_BUILD.

Как мы говорили ранее, местоположение готового, уже собранного приложения зависит от того, какие инструменты использовать. Например, в случае React это будет папка./build, а для приложений Angular – папка project_name/dist. И, как уже было показано в прошлом посте, местоположение output-каталога, который по умолчанию задан как build, можно переопределить через переменную окружения OUTPUT_DIR. Ну и поскольку местоположение output-папки отличается от фреймворка к фреймворку, вы просто копируете сгенерированный output в стандартную папку в образе, а именно в /opt/apt-root/output. Это важно для понимания дальнейшей части этой статьи, а пока что давайте быстренько рассмотрим следующий этап – запуск (run phase).

Этап запуска (run phase)

Этот этап возникает, когда для нового образа, созданного на этапе ассемблирования, делается вызов docker run. Он же происходит и при выполнении развертывания на платформе OpenShift. По умолчанию run script использует serve module для обслуживания статического контента, лежащего в указанном выше стандартном output-каталоге.

Этот метод хорош для быстрого развертывания приложений, но вообще-то обслуживать статический контент таким образом не рекомендуется. Ну поскольку реально мы обслуживаем только статический контент, то установленный внутри нашего образа Node.js не нужно – хватит и веб-сервера.

Иначе говоря, при сборке нам нужно одно, при выполнении – другое. В такой ситуации пригодятся связанные сборки (chained builds).

Связанные сборки (chained builds)

Вот что пишут про chained builds в документации OpenShift:

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

Другими словами, мы можем использовать образ Web App Builder для запуска нашей сборки, а затем использовать образ веб-сервера, того же NGINX, чтобы обслуживать наш контент.

Таким образом, мы можем применять образ Web App Builder в качестве «чистого» builder-а и иметь при этом небольшой по размеру runtime-образ.

Теперь разберем это на конкретном примере.

Для тренировки будем использовать простое приложение React, созданное с помощью инструмента командной строки create-react-app.

Собрать все воедино нам поможет файл шаблона OpenShift.

Разберем этот файл подробнее, и начнем с раздела параметров.

parameters:
  - name: SOURCE_REPOSITORY_URL
    description: The source URL for the application
    displayName: Source URL
    required: true
  - name: SOURCE_REPOSITORY_REF
    description: The branch name for the application
    displayName: Source Branch
    value: master
    required: true
  - name: SOURCE_REPOSITORY_DIR
    description: The location within the source repo of the application
    displayName: Source Directory
    value: .
    required: true
  - name: OUTPUT_DIR
    description: The location of the compiled static files from your web apps builder
    displayName: Output Directory
    value: build
    required: false

Тут все довольно понятно, но стоит обратить внимание на параметр OUTPUT_DIR. Для React-приложения из нашего примера беспокоиться не о чем, поскольку React использует в качестве output-папки значение по умолчанию, а вот в случае Angular или чего-то еще, этот параметр надо будет поменять нужным образом.

Теперь давайте взглянем на раздел ImageStream-ов.

- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-builder  // 1 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-runtime  // 2 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: web-app-builder-runtime // 3
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: nodeshift/ubi8-s2i-web-app:10.x
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: nginx-image-runtime // 4
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: 'centos/nginx-112-centos7:latest'

Взгляните на третий и четвертый образы. Они оба определены как Docker-образы, и тут отчетливо видно, откуда они берутся.

Третий образ – это web-app-builder, и он берется из nodeshift/ubi8-s2i-web-app с тегом 10.x на Docker hub.

Четвертый – это образ NGINX (версии 1.12) с тегом latest на Docker hub.

Теперь взглянем на два первых образа. Они оба пусты на старте и создаются только на этапе сборки (build phase). Первый образ – react-web-app-builder – станет результатом этапа ассемблирования, который объединит образ web-app-builder-runtime и наш исходный код. Именно поэтому мы и вписали в имя этого образа «-builder».

Второй образ – react-web-app-runtime – станет результатом объединения nginx-image-runtime и неких файлов из образа react-web-app-builder. Этот образ также будет использоваться при развертывании и будет содержать только веб-сервер и статический HTML, JavaScript, CSS нашего приложения.

Запутанно? Сейчас взглянем на конфигурации сборки и станет немного понятнее.

В нашем шаблоне есть две конфигурации сборки. Вот первая из них, и она довольно стандартная:

  apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-builder
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-builder:latest // 1
    source:   // 2 
      git:
        uri: ${SOURCE_REPOSITORY_URL}
        ref: ${SOURCE_REPOSITORY_REF}
      contextDir: ${SOURCE_REPOSITORY_DIR}
      type: Git
    strategy:
      sourceStrategy:
        env:
          - name: OUTPUT_DIR // 3 
            value: ${OUTPUT_DIR}
        from:
          kind: ImageStreamTag
          name: web-app-builder-runtime:latest // 4
        incremental: true // 5
      type: Source
    triggers: // 6
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - imageChange: {}
      type: ImageChange

Как видим, строка с меткой 1 говорит, что результат этой сборки будет помещен в тот самый образ react-web-app-builder, который мы чуть раньше видели в разделе ImageStream-ов.

Строка с меткой 2 говорит, откуда брать код. В нашем случае это репозиторий git, а местоположение, ref и папка контекста определены параметрами, которые мы уже видели выше.

Строка с меткой 3 – это мы уже видели в разделе parameters. Она добавляет переменную окружения OUTPUT_DIR, которая в нашем примере равна build.
Строка с меткой 4 говорит использовать образ web-app-builder-runtime, который мы уже видели в разделе ImageStream.

Строка с меткой 5 говорит, что мы хотим использовать инкрементный билд, если S2I-образ его поддерживает, а образ Web App Builder – поддерживает. При первом запуске, после завершения этапа ассемблирования, образ сохранит папку node_modules в архивный файл. Затем при последующих запусках образ будет просто разархивировать эту папку, чтобы сократить продолжительность сборки.

И, наконец, строка с меткой 6 – это всего лишь несколько триггеров, чтобы сборка запускалась автоматически, без ручного вмешательства, когда что-то меняется.

В общем, это довольно стандартная конфигурация сборки.

Теперь взглянем на вторую конфигурацию сборки. Она очень похожа на первую, но есть одно важное отличие.

apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-runtime
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-runtime:latest // 1
    source: // 2
      type: Image
      images:                              
        - from:
            kind: ImageStreamTag
            name: react-web-app-builder:latest // 3
          paths:
            - sourcePath: /opt/app-root/output/.  // 4
              destinationDir: .  // 5
             
    strategy: // 6
      sourceStrategy:
        from:
          kind: ImageStreamTag
          name: nginx-image-runtime:latest
        incremental: true
      type: Source
    triggers:
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - type: ImageChange
      imageChange: {}
    - type: ImageChange
      imageChange:
        from:
          kind: ImageStreamTag
          name: react-web-app-builder:latest // 7

Итак, вторая конфигурация сборки – react-web-app-runtime, и она начинается вполне стандартно.

В строке с меткой 1 нет ничего нового – она просто говорит, что результат сборки кладется в образ react-web-app-runtime.

Строка с меткой 2, как и в предыдущей конфигурации, указывает, откуда брать исходный код. Но обратите внимание, что здесь мы говорим, что он берется из образа. Причем из того образа, который мы только что создали – из react-web-app-builder (указан в строке с меткой 3). Файлы, которые мы хотим использовать, находятся внутри образа и их местоположение там задается в строке с меткой 4, в нашем случае это /opt/app-root/output/. Если помните, именно туда складываются файлы, сгенерированные по результатам сборки нашего приложения.

Папка назначения, заданная в сроке с меткой 5 – это просто текущий каталог (это всё, напомним, крутится внутри некой волшебной вещи под названием OpenShift, а не на вашем локальном компьютере).

Секция strategy – строка с меткой 6 – тоже похожа первую конфигурацию сборки. Только в это раз мы собираемся использовать nginx-image-runtime, который уже видели в разделе ImageStream.

Наконец, строка с меткой 7 – это раздел триггеров, который активируют эту сборку каждый раз, когда меняется образ react-web-app-builder.

В остальном этот шаблон содержит вполне стандартную конфигурацию развертывания, а также вещи, которые относятся к сервисам и маршрутам, но мы не будем в это углубляться. Обратите внимание, что образ, который будет развертываться– это образ react-web-app-runtime.

Развертывание приложения

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

Мы можем использовать клиентский инструмент OpenShift под названием oc, чтобы развернуть наш шаблон:

$ find . | grep openshiftio | grep application | xargs -n 1 oc apply -f

$ oc new-app --template react-web-app -p SOURCE_REPOSITORY_URL=https://github.com/lholmquist/react-web-app

Первая команда на скрине выше – это нарочито инженерный способ найти шаблон./openshiftio/application.yaml.

Вторая команда просто создает новое приложение на основе этого шаблона.

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

Современные приложения на OpenShift, часть 2: связанные сборки chained builds

А вернувшись на экран Overview, увидим запустившийся pod:

Современные приложения на OpenShift, часть 2: связанные сборки chained builds

Щелчок по ссылке – и мы перейдем к нашему приложения, которое представляет собой страницу приложения React App по умолчанию:

Современные приложения на OpenShift, часть 2: связанные сборки chained builds

Дополнение 1

Для любителей Angular у нас тоже есть пример приложения.

Шаблон здесь такой же, за исключением переменной OUTPUT_DIR.

Дополнение 2

В этом статье мы использовали в качестве веб-сервера NGINX, но его довольно легко заменить на Apache, просто поменяйте в файле шаблон образ NGINX на образ Apache.

Заключение

В первой части этой серии мы показали, как быстро развертывать современные веб-приложения на платформе OpenShift. Сегодня мы рассмотрели, что делает образ Web App и как его можно сочетать с чистым веб-сервером типа NGINX с помощью связанных сборок (chained build) при организации более подходящей для продакшн-условий сборки приложений. В следующей, заключительной статье этой серии, мы покажем, как запускать на OpenShift сервер разработки для своего приложения и обеспечивать синхронизацию локальных и удаленных файлов.

Содержание этой серии статей

  • Часть 1: как развертывать современные веб-приложения всего за несколько шагов;
  • Часть 2: как применять новый образ S2I вместе с уже имеющимся образом HTTP-сервера, например, NGINX, используя связанные сборки OpenShift, для организации продакшн-развертывания;
  • Часть 3: как запустить на платформе OpenShift сервер разработки для своего приложения и синхронизировать его с локальной файловой системой.

Дополнительные ресурсы

Источник: habr.com

Добавить комментарий