Creare la prima applicazione contenitore di Service Fabric in Linux

Per eseguire un'applicazione esistente in un contenitore Linux in un cluster di Service Fabric non è necessario apportare modifiche all'applicazione. Questo articolo illustra come creare un'immagine Docker contenente un'applicazione Web Python Flask e come distribuirla in un cluster di Service Fabric. Si condividerà anche l'applicazione in contenitore tramite Registro Azure Container. L'articolo presuppone una conoscenza di base di Docker. Per informazioni su Docker, vedere Docker overview (Panoramica su Docker).

Nota

Questo articolo si applica a un ambiente di sviluppo Linux. Il runtime del cluster di Service Fabric e il runtime di Docker devono essere in esecuzione nello stesso sistema operativo. Non è possibile eseguire contenitori Linux su un cluster Windows.

Prerequisiti

Definire il contenitore Docker

Compilare un'immagine in base all'immagine Python disponibile nell'hub Docker.

Specificare il contenitore Docker in un Dockerfile. Il Dockerfile è costituito da istruzioni per la configurazione dell'ambiente all'interno del contenitore, il caricamento dell'applicazione da eseguire e il mapping delle porte. Il file Dockerfile rappresenta l'input per il comando docker build che crea l'immagine.

Creare una directory vuota e il file Dockerfile (senza estensione file). Aggiungere quanto segue a Dockerfile e salvare le modifiche:

# 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"]

Per altre informazioni, vedere Dockerfile reference (Informazioni di riferimento su Dockerfile).

Creare un'applicazione Web di base

Creare un'applicazione Web Flask in ascolto sulla porta 80 che restituisce "Hello World!". Nella stessa directory creare il file requirements.txt. Aggiungere quanto segue e salvare le modifiche:

Flask

Creare anche il file app.py e aggiungere il frammento di codice seguente:

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)

Accedere a Docker e creare l'immagine

Quindi si creerà l'immagine che esegue l'applicazione Web. Quando si esegue il pull delle immagini da Docker (come python:2.7-slim in Dockerfile), la procedura consigliata consiste nell'eseguire l'autenticazione con l'account Docker Hub invece di effettuare richieste pull anonime.

Nota

Quando si effettuano richieste pull anonime frequenti, si potrebbero vedere errori Docker simili a ERROR: toomanyrequests: Too Many Requests. o You have reached your pull rate limit.. Esegui l'autenticazione con Docker Hub per evitare questi errori. Per informazioni dettagliate, vedere Gestire il contenuto pubblico con Registro Azure Container.

Aprire una finestra di PowerShell e passare alla directory contenente il Dockerfile. Eseguire quindi i comandi seguenti:

docker login
docker build -t helloworldapp .

Questo comando crea la nuova immagine usando le istruzioni contenute nel Dockerfile e denomina l'immagine helloworldapp, ovvero le assegna un tag -t. Per creare un'immagine del contenitore, l'immagine di base viene prima scaricata dall'hub Docker nel quale viene aggiunta l'applicazione.

Al termine dell'esecuzione del comando di compilazione, eseguire il comando docker images per visualizzare le informazioni sulla nuova immagine:

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

Eseguire l'applicazione in locale

Verificare l'esecuzione dell'applicazione in contenitore in locale prima di eseguirne il push nel registro contenitori.

Eseguire l'applicazione, effettuando il mapping della porta 4000 del computer alla porta 80 esposta dal contenitore:

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

name assegna un nome al contenitore in esecuzione, anziché l'ID contenitore.

Connettersi al contenitore in esecuzione. Aprire un Web browser puntando all'indirizzo IP restituito sulla porta 4000, ad esempio "http://localhost:4000"". Verrà visualizzata nel browser l'intestazione "Hello World!".

Hello World!

Per arrestare il contenitore, eseguire:

docker stop my-web-site

Per eliminare il contenitore dal computer di sviluppo, eseguire:

docker rm my-web-site

Effettuare il push dell'immagine nel registro contenitori

Dopo aver verificato l'esecuzione dell'applicazione in Docker, eseguire il push dell'immagine nel registro all'interno di Registro Azure Container.

Eseguire docker login per accedere al registro contenitori con le credenziali del registro.

L'esempio seguente passa l'ID e la password di un'entità servizio di Microsoft Entra. Ad esempio, è possibile che sia stata assegnata un'entità servizio al registro per uno scenario di automazione. In alternativa, è possibile eseguire l'accesso usando il nome utente e la password del registro.

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

Il comando seguente crea un tag, o alias, dell'immagine, con un percorso completo del registro. Questo esempio inserisce l'immagine dello spazio dei nomi samples per evitare confusione nella radice del registro.

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

Effettuare il push dell'immagine nel registro contenitori:

docker push myregistry.azurecr.io/samples/helloworldapp

Creare il pacchetto dell'immagine Docker con Yeoman

Service Fabric SDK per Linux include un generatore Yeoman che semplifica la creazione dell'applicazione e l'aggiunta di un'immagine contenitore. È possibile usare Yeoman per creare un'applicazione con un singolo contenitore Docker denominato SimpleContainerApp.

Per creare un'applicazione contenitore di Service Fabric, aprire una finestra del terminale ed eseguire yo azuresfcontainer.

Assegnare un nome all'applicazione (ad esempio mycontainer) e al servizio dell'applicazione (ad esempio myservice).

Per il nome dell'immagine, specificare l'URL dell'immagine del contenitore in un registro contenitori, ad esempio "myregistry.azurecr.io/samples/helloworldapp".

Dato che per l'immagine è stato definito un punto di ingresso del carico di lavoro, non è necessario specificare in modo esplicito i comandi di input, che vengono eseguiti all'interno del contenitore in modo che la relativa esecuzione continui dopo l'avvio.

Specificare "1" come numero di istanze.

Specificare il mapping della porta nel formato appropriato. Per questo articolo, fornire 80:4000 come mapping della porta. Questa configurazione consente di reindirizzare tutte le richieste in ingresso che arrivano alla porta 4000 del computer host alla porta 80 del contenitore.

Service Fabric Yeoman generator for containers

Configurare l'autenticazione del repository di contenitori

Vedere Autenticazione repository contenitoreper scoprire come configurare tipi diversi di autenticazione per il download dell'immagine del contenitore.

Configurare la modalità di isolamento

Con la versione del runtime 6.3, l'isolamento delle macchine virtuali è supportato per i contenitori Linux, consentendo in tal modo il supporto di due modalità di isolamento per i contenitori, ovvero processo e Hyper-V. Nella modalità di isolamento Hyper-V, i kernel sono isolati tra ogni contenitore e l'host dei contenitori. L'isolamento Hyper-V viene implementato tramite Clear Containers. La modalità di isolamento viene specificata per i cluster Linux nell'elemento ServicePackageContainerPolicy nel file manifesto dell'applicazione. Le modalità di isolamento specificabili sono process, hyperv e default. Il valore predefinito è la modalità di isolamento processo. Il frammento seguente indica come è specificata la modalità di isolamento nel file manifesto dell'applicazione.

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

Configurare la governance delle risorse

La governance delle risorse limita le risorse che possono essere usate dal contenitore nell'host. L'elemento ResourceGovernancePolicy, specificato nel manifesto dell'applicazione, viene usato per dichiarare limiti di risorse per il pacchetto di codice di un servizio. È possibile impostare limiti per le risorse seguenti: Memory, MemorySwap, CpuShares (peso relativo CPU), MemoryReservationInMB, BlkioWeight (peso relativo BlockIO). In questo esempio, il pacchetto del servizio Guest1Pkg ottiene un core nei nodi del cluster in cui è posizionato. I limiti di memoria sono assoluti, quindi i pacchetti di codice sono limitati a 1024 MB di memoria, con prenotazione a garanzia flessibile. I pacchetti di codice (contenitori o pacchetti) non sono in grado di allocare una quantità di memoria superiore a questo limite. Un'operazione di questo tipo genererebbe un'eccezione di memoria esaurita. Perché l'imposizione di un limite di risorse funzioni, è necessario che tutti i pacchetti di codice inclusi in un pacchetto del servizio abbiano limiti di memoria specificati.

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

Configurare docker HEALTHCHECK

A partire dalla versione 6.1, Service Fabric integra automaticamente gli eventi di docker HEALTHCHECK nel report relativo all'integrità del sistema. Se HEALTHCHECK è stato abilitato nel contenitore, Service Fabric fornirà informazioni sull'integrità ogni volta che lo stato dell'integrità del contenitore subisce modifiche, in base a quanto segnalato da Docker. Un report sull'integrità di tipo OK verrà visualizzato in Service Fabric Explorer quando il valore health_status è healthy e un report di tipo AVVISO verrà visualizzato quando il valore health_status è unhealthy.

A partire dall'aggiornamento più recente della versione v6.4, è possibile specificare che le valutazioni HEALTHCHECK docker dovrebbero essere segnalate come un errore. Se questa opzione è abilitata, verrà visualizzato un report sull'integrità OK quando health_status è integro, mentre verrà visualizzato ERRORE quando health_status è non integro.

L'istruzione HEALTHCHECK che fa riferimento alla verifica effettiva eseguita per il monitoraggio dell'integrità dei contenitori deve essere presente nel Dockerfile usato durante la generazione dell'immagine del contenitore.

Screenshot shows details of the Deployed Service Package NodeServicePackage.

HealthCheckUnhealthyApp

HealthCheckUnhealthyDsp

È possibile configurare il comportamento di HEALTHCHECK per ogni contenitore specificando le opzioni di HealthConfig come parte di ContainerHostPolicies in ApplicationManifest.

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

Per impostazione predefinita, l'opzione IncludeDockerHealthStatusInSystemHealthReport è impostata su true, l'opzione RestartContainerOnUnhealthyDockerHealthStatus è impostata su false e l'opzione TreatContainerUnhealthyStatusAsError è impostata su false.

Se l'opzione RestartContainerOnUnhealthyDockerHealthStatus è impostata su true, un contenitore che segnala ripetutamente uno stato non integro viene riavviato, possibilmente su altri nodi.

Se TreatContainerUnhealthyStatusAsError è impostato su true, i report sull'integrità ERRORE verranno visualizzati quando health_status del contenitore è non integro.

Se si vuole disabilitare l'integrazione di HEALTHCHECK per l'intero cluster di Service Fabric, sarà necessario impostare EnableDockerHealthCheckIntegration su false.

Distribuire l'applicazione

Dopo aver compilato l'applicazione, è possibile distribuirla nel cluster locale tramite l'interfaccia della riga di comando di Service Fabric.

Connettersi al cluster locale di Service Fabric.

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

Usare lo script di installazione messo a disposizione nei modelli in https://github.com/Azure-Samples/service-fabric-containers/ per copiare il pacchetto dell'applicazione nell'archivio immagini del cluster, registrare il tipo di applicazione e creare un'istanza dell'applicazione.

./install.sh

Aprire un browser e passare a Service Fabric Explorer all'indirizzo http://localhost:19080/Explorer. Sostituire localhost con l'indirizzo IP privato della macchina virtuale se si usa Vagrant in Mac OS X. Espandere il nodo delle applicazioni, nel quale sarà ora presente una voce per il tipo di applicazione e un'altra per la prima istanza del tipo.

Connettersi al contenitore in esecuzione. Aprire un Web browser puntando all'indirizzo IP restituito sulla porta 4000, ad esempio "http://localhost:4000"". Verrà visualizzata nel browser l'intestazione "Hello World!".

Hello World!

Eseguire la pulizia

Usare lo script di disinstallazione incluso nel modello per eliminare l'istanza dell'applicazione dal cluster di sviluppo locale e annullare la registrazione del tipo di applicazione.

./uninstall.sh

Dopo aver effettuato il push dell'immagine nel registro contenitori, è possibile eliminare l'immagine locale dal computer di sviluppo:

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

Manifesti di esempio completi del servizio e dell'applicazione di Service Fabric

Di seguito sono riportati i manifesti completi del servizio e dell'applicazione usati in questo articolo.

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>

Aggiunta di altri servizi a un'applicazione esistente

Per aggiungere un altro servizio contenitore a un'applicazione già creata usando yeoman, seguire questa procedura:

  1. Modificare la directory impostandola sulla radice dell'applicazione esistente. Ad esempio, cd ~/YeomanSamples/MyApplication, se MyApplication è l'applicazione creata da Yeoman.
  2. Eseguire yo azuresfcontainer:AddService

Configurare l'intervallo di tempo prima della terminazione forzata del contenitore

È possibile configurare un intervallo di tempo di attesa del runtime prima che il contenitore venga rimosso dopo l'avvio dell'eliminazione del servizio (o di un passaggio a un altro nodo). Configurando l'intervallo di tempo, viene inviato il comando docker stop <time in seconds> al contenitore. Per altri dettagli, vedere docker stop. L'intervallo di tempo per l'attesa viene specificato nella sezione Hosting. Il frammento di manifesto del cluster seguente illustra come impostare l'intervallo di attesa:

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

L'intervallo di tempo predefinito è impostato su 10 secondi. Poiché questa configurazione è dinamica, un aggiornamento della sola configurazione nel cluster aggiorna il timeout.

Configurare il runtime per rimuovere le immagini del contenitore non usate

È possibile configurate il cluster di Service Fabric per rimuovere le immagini del contenitore non usate dal nodo. Questa configurazione consente di riacquisire spazio su disco se nel nodo sono presenti troppe immagini del contenitore. Per abilitare questa funzionalità, aggiornare la sezione Hosting nel manifesto del cluster, come illustrato nel frammento seguente:

{
        "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|..."
          }
          ...
          }
        ]
} 

Le immagini che non devono essere eliminate possono essere specificate nel parametro ContainerImagesToSkip.

Configurare il tempo di download delle immagini del contenitore

Il runtime di Service Fabric alloca 20 minuti per il download e l'estrazione delle immagini del contenitore, perché tale limite è appropriato per la maggior parte delle immagini del contenitore. Per immagini di grandi dimensioni o in caso di connessione di rete lenta potrebbe essere necessario aumentare il tempo di attesa prima dell'interruzione del download e dell'estrazione dell'immagine. Questo timeout viene configurato tramite l'attributo ContainerImageDownloadTimeout nella sezione Hosting del manifesto del cluster, come mostrato nel frammento di codice seguente:

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

Configurare i criteri di conservazione dei contenitori

Per semplificare la diagnosi degli errori di avvio dei contenitori, Service Fabric (versione 6.1 o successive) supporta la conservazione di contenitori terminati o il cui avvio non è riuscito. Questo criterio può essere configurato nel file ApplicationManifest.xml, come mostrato nel frammento di codice seguente:

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

L'impostazione ContainersRetentionCount specifica il numero di contenitori da conservare in caso di errore. Se viene specificato un valore negativo, verranno conservati tutti i contenitori con errori. Quando l'attributo ContainersRetentionCount non viene specificato, non verrà conservato alcun contenitore. L'attributo ContainersRetentionCount supporta anche i parametri dell'applicazione, quindi gli utenti possono specificare valori diversi per cluster di test e di produzione. Usare vincoli di posizionamento per specificare come destinazione un nodo specifico per il servizio contenitore quando si usa questa funzionalità, per evitare che il servizio contenitore passi ad altri nodi. Eventuali contenitori conservati tramite questa funzionalità devono essere rimossi manualmente.

Avviare il daemon Docker con argomenti personalizzati

A partire dalla versione 6.2 del runtime di Service Fabric è possibile avviare il daemon Docker con argomenti personalizzati. Quando vengono specificati argomenti personalizzati, Service Fabric non passa altri argomenti al motore Docker, ad eccezione dell'argomento --pidfile. Di conseguenza, --pidfile non deve essere passato come argomento. L'argomento deve continuare ad avere il daemon Docker in ascolto sulla named pipe predefinita in Windows (o sul socket di dominio Unix in Linux) perché Service Fabric possa comunicare con il daemon. Gli argomenti personalizzati vengono specificati nel manifesto del cluster nella sezione Hosting in ContainerServiceArguments. Un esempio è mostrato nel frammento di codice seguente:

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

Passaggi successivi