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 contenitori di Azure. L'articolo presuppone una conoscenza di base di Docker. Per informazioni su Docker, vedere Docker overview (Panoramica su Docker).

Prerequisiti

Definire il contenitore Docker

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

Definire il contenitore Docker in un Dockerfile. Il Dockerfile contiene 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 semplice

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 quanto segue:

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)

Compilare l'immagine

Eseguire il comando docker build per creare l'immagine che esegue l'applicazione Web. Aprire una finestra di PowerShell e passare a c:\temp\helloworldapp. Eseguire il comando seguente:

docker build -t helloworldapp .

Questo comando compila la nuova immagine usando le istruzioni contenute nel file Dockerfile e denomina l'immagine "helloworldapp" , ovvero le assegna un tag -t. La compilazione di un'immagine determina il pull dell'immagine di base dall'hub Docker e la creazione di una nuova immagine che aggiunge l'applicazione sopra l'immagine di base.

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 l'intestazione "Hello World!" nel browser.

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 contenitori di Azure.

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

L'esempio seguente passa l'ID e la password di un'entità servizio di Azure Active Directory. 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".

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, è 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.

Generatore Yeoman di Service Fabric per i contenitori

Configurare il mapping delle porte e l'autenticazione per il repository del contenitore

Il servizio in contenitore richiede un endpoint per la comunicazione. Aggiungere ora il protocollo, la porta e il tipo a un Endpoint nel file ServiceManifest.xml sotto il tag 'Resources'. Per questo articolo, il servizio in contenitore è in ascolto sulla porta 4000:


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

Se si specifica UriScheme, l'endpoint del contenitore viene registrato automaticamente con Service Fabric Naming Service per l'individuazione. Alla fine di questo articolo viene fornito un file di esempio completo di ServiceManifest.xml.

Configurare il mapping tra la porta del contenitore e la porta dell'host specificando i criteri PortBinding in ContainerHostPolicies nel file ApplicationManifest.xml. Per questo articolo, il valore di ContainerPort è 80, dato che il contenitore espone la porta 80, come specificato nel Dockerfile, e il valore di EndpointRef è "myServiceTypeEndpoint", ossia l'endpoint definito nel manifesto del servizio. Per le richieste in ingresso per il servizio sulla porta 4000 viene eseguito il mapping alla porta 80 del contenitore. Se il contenitore deve eseguire l'autenticazione con un repository privato, aggiungere RepositoryCredentials. Per questo articolo, aggiungere il nome e la password dell'account per il registro contenitori myregistry.azurecr.io. Verificare che il criterio venga aggiunto sotto il tag 'ServiceManifestImport' corrispondente al pacchetto del servizio corretto.

   <ServiceManifestImport>
      <ServiceManifestRef ServiceManifestName="MyServicePkg" ServiceManifestVersion="1.0.0" />
    <Policies>
        <ContainerHostPolicies CodePackageRef="Code">
        <RepositoryCredentials AccountName="myregistry" Password="=P==/==/=8=/=+u4lyOB=+=nWzEeRfF=" PasswordEncrypted="false"/>
        <PortBinding ContainerPort="80" EndpointRef="myServiceTypeEndpoint"/>
        </ContainerHostPolicies>
    </Policies>
   </ServiceManifestImport>

Compilare l'applicazione di Service Fabric e creare il pacchetto

I modelli Yeoman di Service Fabric includono uno script di compilazione per Gradle, che può essere usato per compilare l'applicazione dal terminale. Per compilare l'applicazione e creare il pacchetto, eseguire questo comando:

cd mycontainer
gradle

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 nel modello 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 l'intestazione "Hello World!" nel browser.

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="http://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="http://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 Windows containers 
      to Service Fabric: https://aka.ms/sfguestcontainers -->
      <ContainerHost>
        <ImageName>myregistry.azurecr.io/samples/helloworldapp</ImageName>
        <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="http://www.w3.org/2001/XMLSchema"
                     xmlns:xsi="http://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": [
          {
            "ContainerDeactivationTimeout": "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": [
          {
            "PruneContainerImages": “True”,
            "ContainerImagesToSkip": "microsoft/windowsservercore|microsoft/nanoserver|…",
          ...
          }
        ]
} 

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

Passaggi successivi