Criar seu primeiro aplicativo de contêiner do Service Fabric no Linux

A execução de um aplicativo existente em um contêiner do Linux em um cluster do Service Fabric não requer alterações no seu aplicativo. Este artigo mostra como criar uma imagem do Docker que contém um aplicativo de web Python Flask e a implantá-lo em um cluster do Service Fabric. Você também compartilhará seu aplicativo em contêineres pelo Registro de Contêiner do Azure. Este artigo pressupõe uma compreensão básica sobre o Docker. Saiba mais sobre o Docker lendo a Visão geral de Docker.

Observação

Este artigo aplica-se a um ambiente de desenvolvimento do Linux. O runtime do cluster do Service Fabric e o runtime do Docker devem estar em execução no mesmo sistema operacional. Não é possível executar contêineres do Linux em um cluster do Windows.

Pré-requisitos

Defina o contêiner Docker

Crie uma imagem com base na imagem do Python localizada no Hub do Docker.

Especifique o contêiner do Docker em um Dockerfile. O Dockerfile é composto por instruções para configurar o ambiente do seu contêiner, carregar o aplicativo que você deseja executar e mapear portas. O Dockerfile é a entrada para o comando docker build, que cria a imagem.

Crie um diretório vazio e crie o arquivo Dockerfile (sem extensão de arquivo). Adicione o seguinte ao Dockerfile e salve as alterações:

# Use an official Python runtime as a base image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt

# Make port 80 available outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

Leia a referência do Dockerfile para saber mais informações.

Criar um aplicativo Web básico

Crie um aplicativo Web Flask que escuta a porta 80 retornar "Olá, Mundo!". No mesmo diretório, crie o arquivo requirements.txt. Adicione o seguinte e salve as alterações:

Flask

Crie também o arquivo app.py e adicione o seguinte snippet:

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():

    return 'Hello World!'


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

Fazer logon no Docker e criar a imagem

Em seguida, criaremos a imagem que executa seu aplicativo Web. Ao efetuar pull de imagens públicas do Docker (como python:2.7-slim em nosso Dockerfile), a melhor prática é autenticar com sua conta do Docker Hub em vez de fazer uma solicitação de pull anônima.

Observação

Ao fazer solicitações de pull anônimas frequentes, você poderá ver erros de Docker semelhantes a ERROR: toomanyrequests: Too Many Requests. ou You have reached your pull rate limit.. Faça a autenticação no Docker Hub para evitar esses erros. Consulte Gerenciar conteúdo público com o Registro de Contêiner do Azure para obter mais informações.

Abra uma janela do PowerShell e acesse o diretório que contém o Dockerfile. Em seguida, execute os comandos a seguir:

docker login
docker build -t helloworldapp .

Esse comando cria a nova imagem usando as instruções no seu Dockerfile de nomeando (marcação -t) a imagem helloworldapp. Para criar uma imagem de contêiner, primeiro a imagem base é baixada do Hub do Docker ao qual o aplicativo foi adicionado.

Depois de concluir o comando de compilação, execute o comando docker images para ver informações sobre a nova imagem:

$ docker images
    
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
helloworldapp                 latest              86838648aab6        2 minutes ago       194 MB

Executar o aplicativo localmente

Verifique se seu aplicativo em contêineres está sendo executado localmente antes de enviar a ele o registro de contêiner.

Execute o aplicativo, mapeando a porta 4000 do computador para a porta 80 exposta do contêiner:

docker run -d -p 4000:80 --name my-web-site helloworldapp

name fornece um nome para o contêiner em execução (em vez da ID do contêiner).

Conectar-se ao contêiner em execução. Abra um navegador da Web apontando para o endereço IP retornado na porta 4000, por exemplo "http://localhost:4000"". Você deve ver o título "Olá, Mundo!" exibido no navegador.

Hello World!

Para interromper o contêiner, execute:

docker stop my-web-site

Exclua o contêiner do seu computador de desenvolvimento:

docker rm my-web-site

Enviar a imagem para o registro de contêiner

Depois de verificar que o aplicativo é executado no Docker, envie a imagem por push para o registro no Registro de Contêiner do Azure.

Execute docker login para entrar em seu registro de contêiner com as credenciais de registro.

O exemplo a seguir passa a ID e a senha de uma entidade de serviço do Microsoft Entra. Por exemplo, você pode atribuir uma entidade de serviço ao registro para um cenário de automação. Ou, você pode entrar usando o nome de usuário e a senha do registro.

docker login myregistry.azurecr.io -u xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -p myPassword

O comando a seguir cria uma marca ou alias imagem, com um caminho totalmente qualificado para o registro. Este exemplo coloca a imagem no namespace samples para evitar confusão na raiz do registro.

docker tag helloworldapp myregistry.azurecr.io/samples/helloworldapp

Enviar a imagem para o eu registro de contêiner:

docker push myregistry.azurecr.io/samples/helloworldapp

Empacotar a imagem do Docker com o Yeoman

O SDK do Service Fabric para Linux inclui um gerador Yeoman que facilita a criação de seu aplicativo e a adição de uma imagem de contêiner. Vamos usar o Yeoman para criar um aplicativo, com um único contêiner do Docker, chamado de SimpleContainerApp.

Para criar um aplicativo de contêiner do Service Fabric, abra uma janela do terminal e execute yo azuresfcontainer.

Nomeie seu aplicativo (por exemplo, mycontainer) e nomeie o serviço de aplicativo (por exemplo, myservice).

Para o nome da imagem, forneça a URL da imagem de contêiner em um registro de contêiner (por exemplo, "myregistry.azurecr.io/samples/helloworldapp").

Como essa imagem tem um ponto de entrada de carga de trabalho-ponto definido, não é necessário especificar explicitamente os comandos de entrada (comandos executados dentro do contêiner, o que manterá o contêiner em execução depois da inicialização).

Especifique a contagem de instâncias como "1".

Especifique o mapeamento de porta no formato apropriado. Para este artigo, você precisa fornecer 80:4000 como o mapeamento de porta. Ao fazer isso, você configurou que quaisquer solicitações recebidas chegando à porta 4000 na máquina host serão redirecionadas para a porta 80 no contêiner.

Service Fabric Yeoman generator for containers

Configurar a autenticação do repositório de contêiner

Consulte Autenticação de repositório de contêiner para saber como configurar diferentes tipos de autenticação para download de imagem de contêiner.

Configurar o modo de isolamento

Com a versão de runtime 6.3, o isolamento de VM tem suporte para contêineres do Linux, dando suporte, portanto, a dois modos de isolamento para contêineres: processo e Hyper-V. Com o modo de isolamento Hyper-V, os kernels são isolados entre cada contêiner e o host do contêiner. O isolamento Hyper-V é implementado usando Contêineres Não Criptografados. O modo de isolamento é especificado para clusters do Linux no elemento ServicePackageContainerPolicy no arquivo de manifesto do aplicativo. Os modos de isolamento que podem ser especificados são process, hyperv e default. O padrão é o modo de isolamento do processo. O snippet a seguir mostra como o modo de isolamento é especificado no arquivo de manifesto do aplicativo.

<ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="MyServicePkg" ServiceManifestVersion="1.0.0"/>
      <Policies>
        <ServicePackageContainerPolicy Hostname="votefront" Isolation="hyperv">
          <PortBinding ContainerPort="80" EndpointRef="myServiceTypeEndpoint"/>
        </ServicePackageContainerPolicy>
    </Policies>
  </ServiceManifestImport>

Configurar governança de recursos

A governança de recursos restringe os recursos que o contêiner pode usar no host. O elemento ResourceGovernancePolicy, especificado no manifesto do aplicativo, é usado para declarar os limites de recurso para um pacote de códigos de serviço. Os limites de recursos podem ser definidos para os seguintes recursos: Memory, MemorySwap, CpuShares (CPU relative weight), MemoryReservationInMB, BlkioWeight (BlockIO relative weight). Neste exemplo, o pacote de serviço Guest1Pkg obtém um núcleo nos nós de cluster em que ele é colocado. Os limites de memória são absolutos e, portanto, o pacote de códigos é limitado a 1024 MB de memória (e a uma reserva de garantia reversível da mesma). Os pacotes de códigos (contêineres ou processos) não podem alocar mais memória do que esse limite. A tentativa de fazer isso resultará em uma exceção de memória insuficiente. Para que a imposição do limite de recursos funcione, todos os pacotes de códigos em um pacote de serviço devem ter limites de memória especificados.

<ServiceManifestImport>
  <ServiceManifestRef ServiceManifestName="MyServicePKg" ServiceManifestVersion="1.0.0" />
  <Policies>
    <ServicePackageResourceGovernancePolicy CpuCores="1"/>
    <ResourceGovernancePolicy CodePackageRef="Code" MemoryInMB="1024"  />
  </Policies>
</ServiceManifestImport>

Configurar o HEALTHCHECK do Docker

Iniciando a versão 6.1, o Service Fabric integra automaticamente os eventos do HEALTHCHECK do Docker em seu relatório de integridade do sistema. Isso significa que, se o contêiner tiver o HEALTHCHECK habilitado, o Service Fabric relatará a integridade sempre que o status de integridade do contêiner for alterado conforme relatado pelo Docker. Um relatório de integridade OK será exibido no Service Fabric Explorer quando o health_status for íntegro e um AVISO aparecerá quando o health_status for não íntegro.

Com a atualização mais recente da v6.4, você tem a opção de especificar que as avaliações de HEALTHCHECK do Docker devem ser relatadas como um erro. Se essa opção estiver habilitada, um relatório de integridade OK será exibido quando health_status for healthy e ERROR será exibido quando health_status for unhealthy.

A instrução do HEALTHCHECK apontando para a verificação real que é executada para monitorar a integridade do contêiner deve estar presente no Dockerfile usado ao gerar a imagem de contêiner.

Screenshot shows details of the Deployed Service Package NodeServicePackage.

HealthCheckUnhealthyApp

HealthCheckUnhealthyDsp

Você pode configurar o comportamento do HEALTHCHECK para cada contêiner especificando as opções do HealthConfig como parte do ContainerHostPolicies no ApplicationManifest.

<ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="ContainerServicePkg" ServiceManifestVersion="2.0.0" />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <HealthConfig IncludeDockerHealthStatusInSystemHealthReport="true"
		      RestartContainerOnUnhealthyDockerHealthStatus="false" 
		      TreatContainerUnhealthyStatusAsError="false" />
      </ContainerHostPolicies>
    </Policies>
</ServiceManifestImport>

Por padrão, IncludeDockerHealthStatusInSystemHealthReport é definido como true, RestartContainerOnUnhealthyDockerHealthStatus é definido como false e TreatContainerUnhealthyStatusAsError é definido como false.

Se o RestartContainerOnUnhealthyDockerHealthStatus for definido como true, um contêiner relatando repetidamente um estado não íntegro será reiniciado (possivelmente em outros nós).

Se TreatContainerUnhealthyStatusAsError for definido como true, os relatórios de integridade com ERROR serão exibidos quando o health_status do contêiner for unhealthy.

Se você deseja desabilitar a integração do HEALTHCHECK para todo o cluster do Service Fabric, precisará definir o EnableDockerHealthCheckIntegration como false.

Implantar o aplicativo

Após a compilação do aplicativo, você pode implantá-lo no cluster local usando a CLI do Service Fabric.

Conectar-se ao cluster local do Service Fabric.

sfctl cluster select --endpoint http://localhost:19080

Use o script de instalação fornecido nos modelos em https://github.com/Azure-Samples/service-fabric-containers/ para copiar o pacote de aplicativo no repositório de imagens do cluster, registrar o tipo de aplicativo e criar uma instância do aplicativo.

./install.sh

Abra um navegador e navegue até o Service Fabric Explorer em http://localhost:19080/Explorer(substitua localhost pelo IP privado da VM se estiver usando Vagrant no Mac OS X). Expanda o nó Aplicativos e observe que agora há uma entrada para o seu tipo de aplicativo e outra para a primeira instância desse tipo.

Conectar-se ao contêiner em execução. Abra um navegador da Web apontando para o endereço IP retornado na porta 4000, por exemplo "http://localhost:4000"". Você deve ver o título "Olá, Mundo!" exibido no navegador.

Hello World!

Limpar

Use o script de desinstalação fornecido com o modelo para excluir a instância do aplicativo no cluster de desenvolvimento local e cancelar o registro do tipo de aplicativo.

./uninstall.sh

Depois que você enviar a imagem para o registro de contêiner, você pode excluir a imagem local do seu computador de desenvolvimento:

docker rmi helloworldapp
docker rmi myregistry.azurecr.io/samples/helloworldapp

Exemplo completo de manifestos de serviço e aplicativo do Service Fabric

Aqui estão os manifestos de aplicativo e serviço completos usados neste artigo.

ServiceManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="myservicePkg"
                 Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric"
                 xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <ServiceTypes>
    <!-- This is the name of your ServiceType.
         The UseImplicitHost attribute indicates this is a guest service. -->
    <StatelessServiceType ServiceTypeName="myserviceType" UseImplicitHost="true" />
  </ServiceTypes>

  <!-- Code package is your service executable. -->
  <CodePackage Name="Code" Version="1.0.0">
    <EntryPoint>
      <!-- Follow this link for more information about deploying containers 
      to Service Fabric: https://aka.ms/sfguestcontainers -->
      <ContainerHost>
        <ImageName>myregistry.azurecr.io/samples/helloworldapp</ImageName>
        <!-- Pass comma delimited commands to your container: dotnet, myproc.dll, 5" -->
        <!--Commands> dotnet, myproc.dll, 5 </Commands-->
        <Commands></Commands>
      </ContainerHost>
    </EntryPoint>
    <!-- Pass environment variables to your container: -->
    
    <EnvironmentVariables>
      <!--
      <EnvironmentVariable Name="VariableName" Value="VariableValue"/>
      -->
    </EnvironmentVariables>
    
  </CodePackage>

  <Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Name="myServiceTypeEndpoint" UriScheme="http" Port="4000" Protocol="http"/>
    </Endpoints>
  </Resources>
</ServiceManifest>

ApplicationManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="mycontainerType"
                     ApplicationTypeVersion="1.0.0"
                     xmlns="http://schemas.microsoft.com/2011/01/fabric"
                     xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                     xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion 
       should match the Name and Version attributes of the ServiceManifest element defined in the 
       ServiceManifest.xml file. -->
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="myservicePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <RepositoryCredentials AccountName="myregistry" Password="=P==/==/=8=/=+u4lyOB=+=nWzEeRfF=" PasswordEncrypted="false"/>
        <PortBinding ContainerPort="80" EndpointRef="myServiceTypeEndpoint"/>
      </ContainerHostPolicies>
    </Policies>
  </ServiceManifestImport>
  <DefaultServices>
    <!-- The section below creates instances of service types, when an instance of this 
         application type is created. You can also create one or more instances of service type using the 
         ServiceFabric PowerShell module.
         
         The attribute ServiceTypeName below must match the name defined in the imported ServiceManifest.xml file. -->
    <Service Name="myservice">
      <!-- On a local development cluster, set InstanceCount to 1. On a multi-node production 
      cluster, set InstanceCount to -1 for the container service to run on every node in 
      the cluster.
      -->
      <StatelessService ServiceTypeName="myserviceType" InstanceCount="1">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

Adicionando mais serviços a um aplicativo existente

Para adicionar outro serviço de contêiner a um aplicativo já criado usando o yeoman, execute as seguintes etapas:

  1. Altere o diretório para a raiz do aplicativo existente. Por exemplo, cd ~/YeomanSamples/MyApplication, se MyApplication é o aplicativo criado pelo Yeoman.
  2. Execute yo azuresfcontainer:AddService

Configurar o intervalo de tempo antes do contêiner ser forçado a terminar

Você pode configurar um intervalo de tempo para a execução aguardar antes do contêiner ser removido após a exclusão do serviço (ou um movimento para outro nó) ter iniciado. Configurar o intervalo de tempo envia o comando docker stop <time in seconds> para o contêiner. Para obter mais detalhes, consulte parar docker. O intervalo de tempo de espera é especificado na seção Hosting. O snippet de manifesto do cluster a seguir mostra como definir o intervalo de espera:

{
        "name": "Hosting",
        "parameters": [
          {
                "name": "ContainerDeactivationTimeout",
                "value" : "10"
          },
	      ...
        ]
}

O intervalo de tempo padrão é definido para 10 segundos. Como essa configuração é dinâmica, uma configuração somente atualiza no cluster que atualiza no tempo limite.

Configurar a execução para remover as imagens de contêiner não utilizadas

Você pode configurar o cluster do Service Fabric para remover as imagens de contêiner não utilizadas do nó. Essa configuração permite que o espaço em disco seja recapturado se houver imagens de contêiner demais no nó. Para habilitar esse recurso, atualize a Hosting seção no manifesto do cluster, conforme mostrado no snippet a seguir:

{
        "name": "Hosting",
        "parameters": [
          {
                "name": "PruneContainerImages",
                "value": "True"
          },
          {
                "name": "ContainerImagesToSkip",
                "value": "mcr.microsoft.com/windows/servercore|mcr.microsoft.com/windows/nanoserver|mcr.microsoft.com/dotnet/framework/aspnet|..."
          }
          ...
          }
        ]
} 

Para as imagens que não devem ser excluídas, você pode especificá-las no parâmetro ContainerImagesToSkip.

Configurar o tempo de download de imagem de contêiner

O runtime do Service Fabric aloca 20 minutos para baixar e extrair as imagens de contêiner, o que funciona para a maioria das imagens de contêiner. Para imagens grandes, ou quando a conexão de rede estiver lenta, talvez seja necessário aumentar o tempo de espera antes de cancelar o download e a extração da imagem. Esse tempo limite é definido usando o atributo ContainerImageDownloadTimeout na seção Hospedagem do manifesto do cluster, conforme mostrado no snippet de código a seguir:

{
        "name": "Hosting",
        "parameters": [
          {
              "name": "ContainerImageDownloadTimeout",
              "value": "1200"
          }
        ]
}

Definir política de retenção de contêiner

Para ajudar a diagnosticar as falhas de inicialização do contêiner, o Service Fabric (versão 6.1 ou superiores) oferece suporte à retenção de contêineres que encerraram ou falharam na inicialização. Essa política pode ser definida no arquivo ApplicationManifest.xml conforme mostrado no snippet de código a seguir:

 <ContainerHostPolicies CodePackageRef="NodeService.Code" Isolation="process" ContainersRetentionCount="2"  RunInteractive="true"> 

A configuração ContainersRetentionCount especifica o número de contêineres mantidos quando ocorre uma falha. Se um valor negativo for especificado, todos os contêineres com falha serão mantidos. Quando o atributo ContainersRetentionCount não for especificado, nenhum contêiner será retido. O atributo ContainersRetentionCount também oferece suporte a Parâmetros de Aplicativo para que os usuários possam especificar valores diferentes para clusters de teste e produção. Use restrições de posicionamento para direcionar o serviço de contêiner para um determinado nó quando esses recursos forem usados com a finalidade de impedir que o serviço de contêiner se mova para outros nós. Todos os contêineres retidos usando esse recurso devem ser removidos manualmente.

Iniciar o daemon do Docker com argumentos personalizados

Com a versão 6.2, e superiores, do runtime do Service Fabric, você pode iniciar o daemon do Docker com argumentos personalizados. Ao especificar argumentos personalizados, o Service Fabric não passa outro argumento para o mecanismo do docker, com exceção do argumento --pidfile. Portanto, não passe --pidfile como um argumento. Além disso, o argumento deve manter a escuta do daemon do docker no pipe de nome padrão no Windows (ou um soquete de domínio do unix no Linux) para o Service Fabric se comunicar com o daemon. Os argumentos personalizados são especificados no manifesto do cluster na seção Hospedagem em ContainerServiceArguments. Um exemplo é mostrado no snippet a seguir:

{ 
        "name": "Hosting", 
        "parameters": [ 
          { 
            "name": "ContainerServiceArguments", 
            "value": "-H localhost:1234 -H unix:///var/run/docker.sock" 
          } 
        ] 
} 

Próximas etapas