Aplicações modernas no OpenShift, parte 2: construções encadeadas

Olá a todos! Estamos de volta com o segundo post da nossa série, onde mostramos como implantar aplicações web modernas no Red Hat OpenShift.

Aplicações modernas no OpenShift, parte 2: construções encadeadas

No post anterior, introduzimos as capacidades da nova imagem builder S2I (source-to-image), destinada à construção e implantação de aplicações web modernas na plataforma OpenShift. Naquela ocasião, focamos no tema da rápida implantação de aplicações, e hoje veremos como usar a imagem S2I como uma imagem builder "pura" e combiná-la com construções encadeadas do OpenShift.

Imagem builder pura

Como mencionamos na primeira parte, a maioria das aplicações web modernas possui uma chamada fase de construção, onde normalmente são realizadas operações como transpiração de código, concatenação de arquivos e minificação. Os arquivos resultantes dessas operações – incluindo HTML estático, JavaScript e CSS – são armazenados na pasta de saída. O local dessa pasta depende das ferramentas de construção utilizadas, e para o React, será a pasta ./build (voltaremos a esse ponto mais adiante).

Source-to-Image (S2I)

Neste post, não abordaremos o tema "o que é S2I e como usá-lo" (pode-se ler mais sobre isso aqui), mas é importante ter uma visão clara das duas etapas desse processo para entender o que a imagem Web App Builder realmente faz.

Fase de montagem (assemble phase)

A fase de montagem é muito semelhante ao que acontece quando você executa um docker build e, como resultado, obtém uma nova imagem Docker. Portanto, essa fase ocorre ao iniciar uma construção na plataforma OpenShift.

No caso da imagem Web App Builder, a instalação das dependências da sua aplicação e o início da construção é responsabilidade do script de montagem. Por padrão, a imagem builder utiliza o comando npm run build, mas isso pode ser sobrescrito através da variável de ambiente NPM_BUILD.

Como mencionamos anteriormente, a localização do aplicativo final, já montado, depende das ferramentas utilizadas. Por exemplo, no caso do React, será a pasta ./build, enquanto para aplicativos Angular, será a pasta project_name/dist. E, como já mostrado na postagem anterior, a localização do diretório de saída, que por padrão é definido como build, pode ser redefinida através da variável de ambiente OUTPUT_DIR. E como a localização da pasta de saída varia de framework para framework, você simplesmente copia a saída gerada para a pasta padrão na imagem, ou seja, para /opt/apt-root/output. Isso é importante para entender a próxima parte deste artigo, mas agora vamos rapidamente revisar a próxima etapa – a fase de execução.

Fase de execução

Esta fase ocorre quando uma nova imagem, criada na fase de montagem, é chamada usando docker run. Ela também acontece ao realizar a implantação na plataforma OpenShift. Por padrão script run usa módulo serve para servir conteúdo estático localizado no diretório de saída padrão mencionado acima.

Esse método é bom para implantações rápidas de aplicativos, mas, na verdade, não é recomendado servir conteúdo estático dessa forma. Como na verdade estamos servindo apenas conteúdo estático, o Node.js instalado dentro da nossa imagem não é necessário – um servidor web é suficiente.

Ou seja, ao construir precisamos de uma coisa, ao executar – outra. Nessa situação, construções encadeadas serão úteis.

Construções encadeadas

Veja o que dizem sobre construções encadeadas na documentação do OpenShift:

«Duas construções podem ser vinculadas, onde uma gera a entidade compilada e a outra coloca essa entidade em uma imagem separada, que é usada para executar essa entidade».

Em outras palavras, podemos usar a imagem do Web App Builder para executar nossa construção e, em seguida, usar a imagem do servidor web, como o NGINX, para servir nosso conteúdo.

Assim, podemos usar a imagem do Web App Builder como um construtor "limpo" e ter uma imagem de runtime pequena.

Agora vamos analisar isso com um exemplo específico.

Para a prática, usaremos um aplicativo React simples, criado com a ferramenta de linha de comando create-react-app.

Para juntar tudo, usaremos o arquivo de modelo do OpenShift.

Vamos analisar este arquivo em detalhes, começando pela seção de parâmetros.

parameters:
  - name: SOURCE_REPOSITORY_URL
    description: A URL de origem para o aplicativo
    displayName: URL de Origem
    required: true
  - name: SOURCE_REPOSITORY_REF
    description: O nome do branch para o aplicativo
    displayName: Branch de Origem
    value: master
    required: true
  - name: SOURCE_REPOSITORY_DIR
    description: O local dentro do repositório de origem do aplicativo
    displayName: Diretório de Origem
    value: .
    required: true
  - name: OUTPUT_DIR
    description: O local dos arquivos estáticos compilados do seu construtor de aplicativos web
    displayName: Diretório de Saída
    value: build
    required: false

Aqui tudo é bastante claro, mas vale a pena prestar atenção no parâmetro OUTPUT_DIR. Para o aplicativo React do nosso exemplo, não há com o que se preocupar, pois o React usa como pasta de saída o valor padrão. No entanto, para aplicações Angular ou algo semelhante, este parâmetro precisará ser alterado adequadamente.

Agora vamos dar uma olhada na seção de ImageStreams.

- 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'

Observe as terceira e quarta imagens. Ambas estão definidas como imagens Docker, e fica claro de onde elas vêm.

A terceira imagem é o web-app-builder, e ela é originada de nodeshift/ubi8-s2i-web-app com a tag 10.x no Docker hub.

A quarta imagem é a imagem NGINX (versão 1.12) com a tag latest no Docker hub.

Agora vamos olhar as duas primeiras imagens. Ambas estão vazias no início e são criadas apenas na fase de construção (build phase). A primeira imagem, react-web-app-builder, será o resultado da fase de montagem, que combinará a imagem web-app-builder-runtime e nosso código fonte. É por isso que incluímos no nome dessa imagem "-builder".

A segunda imagem, react-web-app-runtime, será o resultado da combinação de nginx-image-runtime e alguns arquivos da imagem react-web-app-builder. Esta imagem também será utilizada durante o deploy e conterá apenas o servidor web e o HTML, JavaScript e CSS estático do nosso aplicativo.

Confuso? Vamos dar uma olhada nas configurações de construção e ficará um pouco mais claro.

No nosso template, há duas configurações de construção. Aqui está a primeira delas, que é bastante padrão:

  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

Como podemos ver, a linha com a etiqueta 1 nos diz que o resultado desta construção será colocado na imagem react-web-app-builder, que vimos anteriormente na seção de ImageStreams.

A linha com a etiqueta 2 indica de onde pegar o código. Neste caso, é um repositório git, e o local, ref e diretório de contexto são definidos pelas variáveis que já vimos acima.

A linha com a etiqueta 3 é algo que já vimos na seção de parâmetros. Ela adiciona a variável de ambiente OUTPUT_DIR, que em nosso exemplo é igual a build.
A linha com a etiqueta 4 indica que devemos usar a imagem web-app-builder-runtime, que já vimos na seção de ImageStreams.

A linha com a etiqueta 5 diz que queremos usar a construção incremental, se a imagem S2I a suportar, e a imagem Web App Builder – suporta. Na primeira execução, após a conclusão da fase de montagem, a imagem salvará a pasta node_modules em um arquivo compactado. Então, nas execuções seguintes, a imagem apenas descompactará essa pasta para reduzir a duração da construção.

E, finalmente, a linha com a etiqueta 6 – são apenas alguns gatilhos para que a construção seja iniciada automaticamente, sem intervenção manual, quando algo muda.

Em geral, esta é uma configuração de construção bastante padrão.

Agora, vamos olhar para a segunda configuração de construção. Ela é muito parecida com a primeira, mas há uma diferença importante.

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\/.
              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

Então, a segunda configuração do build é react-web-app-runtime, e ela começa de maneira bastante padrão.

Na linha com o rótulo 1, não há nada de novo — ela apenas indica que o resultado do build é colocado na imagem react-web-app-runtime.

A linha com o rótulo 2, assim como na configuração anterior, indica de onde obter o código-fonte. Mas note que aqui estamos dizendo que ele vem da imagem. E, especificamente, da imagem que acabamos de criar — a react-web-app-builder (indicada na linha com o rótulo 3). Os arquivos que queremos usar estão dentro da imagem, e sua localização é definida na linha com o rótulo 4, que neste caso é /opt/app-root/output/. Se você se lembra, é exatamente para lá que são colocados os arquivos gerados como resultado da construção do nosso aplicativo.

A pasta de destino, especificada na linha com o rótulo 5, é simplesmente o diretório atual (todo esse processo, lembre-se, acontece dentro de uma coisa mágica chamada OpenShift, e não em seu computador local).

A seção strategy — a linha com o rótulo 6 — também é semelhante à primeira configuração do build. Mas desta vez, vamos usar o nginx-image-runtime, que já vimos na seção ImageStream.

Finalmente, a linha com o rótulo 7 é a seção de gatilhos que ativa esse build toda vez que a imagem react-web-app-builder é alterada.

No restante, este template contém uma configuração de implantação bastante padrão, além de coisas relacionadas a serviços e rotas, mas não vamos nos aprofundar nisso. Observe que a imagem que será implantada é a imagem react-web-app-runtime.

Implantação do aplicativo

Então, depois de olharmos para o template, vejamos como usá-lo para implantar um aplicativo.

Podemos usar a ferramenta de linha de comando OpenShift chamada oc, para implantar nosso template:

$ 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

O primeiro comando na tela acima é uma maneira intencionalmente técnica de encontrar o template /openshiftio/application.yaml.

O segundo comando simplesmente cria um novo aplicativo com base nesse template.

Depois que esses comandos forem executados, veremos que tivemos duas builds:

Aplicações modernas no OpenShift, parte 2: construções encadeadas

E voltando à tela de Visão Geral, veremos um pod em execução:

Aplicações modernas no OpenShift, parte 2: construções encadeadas

Um clique no link nos levará ao nosso aplicativo, que se apresenta como a página padrão do React App:

Aplicações modernas no OpenShift, parte 2: construções encadeadas

Complemento 1

Para os fãs do Angular, também temos exemplo de aplicação.

O padrão aqui é o mesmo, exceto pela variável OUTPUT_DIR.

Complemento 2

Neste artigo, usamos o NGINX como servidor web, mas é bastante fácil substituí-lo pelo Apache, basta alterar no arquivo de modelo imagem do NGINX para imagem do Apache.

Conclusão

Na primeira parte desta série, mostramos como implantar rapidamente aplicações web modernas na plataforma OpenShift. Hoje, exploramos o que faz a imagem da Web App e como ela pode ser combinada com um servidor web puro como o NGINX usando builds encadeados (chained builds) para organizar uma construção mais adequada para condições de produção. No próximo e último artigo desta série, mostraremos como executar um servidor de desenvolvimento no OpenShift para sua aplicação e garantir a sincronização de arquivos locais e remotos.

Conteúdo desta série de artigos

  • Parte 1: como implantar aplicações web modernas em apenas alguns passos;
  • Parte 2: como aplicar a nova imagem S2I junto com uma imagem de servidor HTTP existente, como o NGINX, usando builds encadeados do OpenShift para organizar uma implantação de produção;
  • Parte 3: como executar um servidor de desenvolvimento na plataforma OpenShift para sua aplicação e sincronizá-lo com o sistema de arquivos local.

Recursos adicionais

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster