Partilhar via


Trazer dependências ou biblioteca de terceiros para Funções do Azure

Neste artigo, vai aprender a incluir dependências de terceiros nas suas aplicações de funções. Exemplos de dependências de terceiros são ficheiros json, ficheiros binários e modelos de machine learning.

Neste artigo, vai aprender a:

  • Trazer dependências através do projeto Código das Funções
  • Trazer dependências através da montagem do Azure Fileshare

Trazer dependências do diretório do projeto

Uma das formas mais simples de introduzir dependências é colocar os ficheiros/artefactos em conjunto com o código da aplicação de funções na estrutura de diretório do projeto de Funções. Eis um exemplo dos exemplos de diretórios num projeto de funções do Python:

<project_root>/
 | - my_first_function/
 | | - __init__.py
 | | - function.json
 | | - example.py
 | - dependencies/
 | | - dependency1
 | - .funcignore
 | - host.json
 | - local.settings.json

Ao colocar as dependências numa pasta dentro do diretório do projeto de aplicação de funções, a pasta de dependências será implementada juntamente com o código. Como resultado, o código de função pode aceder às dependências na cloud através da API do sistema de ficheiros.

Aceder às dependências no código

Eis um exemplo para aceder e executar ffmpeg a dependência que é colocada no <project_root>/ffmpeg_lib diretório.

import logging

import azure.functions as func
import subprocess

FFMPEG_RELATIVE_PATH = "../ffmpeg_lib/ffmpeg"

def main(req: func.HttpRequest,
         context: func.Context) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    command = req.params.get('command')
    # If no command specified, set the command to help
    if not command:
        command = "-h"

    # context.function_directory returns the current directory in which functions is executed 
    ffmpeg_path = "/".join([str(context.function_directory), FFMPEG_RELATIVE_PATH])

    try:
        byte_output  = subprocess.check_output([ffmpeg_path, command])
        return func.HttpResponse(byte_output.decode('UTF-8').rstrip(),status_code=200)
    except Exception as e:
        return func.HttpResponse("Unexpected exception happened when executing ffmpeg. Error message:" + str(e),status_code=200)

Nota

Poderá ter de utilizar chmod para fornecer Execute direitos ao binário ffmpeg num ambiente linux

Uma das formas mais simples de introduzir dependências é colocar os ficheiros/artefactos em conjunto com o código da aplicação de funções na estrutura de diretório do projeto de funções. Eis um exemplo dos exemplos de diretórios num projeto de funções Java:

<project_root>/
 | - src/
 | | - main/java/com/function
 | | | - Function.java
 | | - test/java/com/function
 | - artifacts/
 | | - dependency1
 | - host.json
 | - local.settings.json
 | - pom.xml

Especificamente para Java, tem de incluir especificamente os artefactos na pasta build/target ao copiar recursos. Eis um exemplo sobre como fazê-lo no Maven:

...
<execution>
    <id>copy-resources</id>
    <phase>package</phase>
    <goals>
        <goal>copy-resources</goal>
    </goals>
    <configuration>
        <overwrite>true</overwrite>
        <outputDirectory>${stagingDirectory}</outputDirectory>
        <resources>
            <resource>
                <directory>${project.basedir}</directory>
                <includes>
                    <include>host.json</include>
                    <include>local.settings.json</include>
                    <include>artifacts/**</include>
                </includes>
            </resource>
        </resources>
    </configuration>
</execution>
...

Ao colocar as dependências numa pasta dentro do diretório do projeto de aplicação de funções, a pasta de dependências será implementada juntamente com o código. Como resultado, o código de função pode aceder às dependências na cloud através da API do sistema de ficheiros.

Aceder às dependências no código

Eis um exemplo para aceder e executar ffmpeg a dependência que é colocada no <project_root>/ffmpeg_lib diretório.

public class Function {
    final static String BASE_PATH = "BASE_PATH";
    final static String FFMPEG_PATH = "/artifacts/ffmpeg/ffmpeg.exe";
    final static String HELP_FLAG = "-h";
    final static String COMMAND_QUERY = "command";

    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) throws IOException{
        context.getLogger().info("Java HTTP trigger processed a request.");

        // Parse query parameter
        String flags = request.getQueryParameters().get(COMMAND_QUERY);

        if (flags == null || flags.isBlank()) {
            flags = HELP_FLAG;
        }

        Runtime rt = Runtime.getRuntime();
        String[] commands = { System.getenv(BASE_PATH) + FFMPEG_PATH, flags};
        Process proc = rt.exec(commands);

        BufferedReader stdInput = new BufferedReader(new 
        InputStreamReader(proc.getInputStream()));

        String out = stdInput.lines().collect(Collectors.joining("\n"));
        if(out.isEmpty()) {
            BufferedReader stdError = new BufferedReader(new 
                InputStreamReader(proc.getErrorStream()));
            out = stdError.lines().collect(Collectors.joining("\n"));
        }
        return request.createResponseBuilder(HttpStatus.OK).body(out).build();

    }

Nota

Para que este fragmento de código funcione no Azure, tem de especificar uma definição de aplicação personalizada de "BASE_PATH" com o valor "/home/site/wwwroot"

Traga dependências ao montar uma partilha de ficheiros

Ao executar a aplicação de funções no Linux, existe outra forma de introduzir dependências de terceiros. As funções permitem-lhe montar uma partilha de ficheiros alojada no Ficheiros do Azure. Considere esta abordagem quando quiser desassociar dependências ou artefactos do código da aplicação.

Primeiro, tem de criar uma Conta de Armazenamento do Azure. Na conta, também tem de criar uma partilha de ficheiros nos ficheiros do Azure. Para criar estes recursos, siga este guia

Depois de criar a conta de armazenamento e a partilha de ficheiros, utilize o comando az webapp config storage-account add para anexar a partilha de ficheiros à sua aplicação de funções, conforme mostrado no exemplo seguinte.

az webapp config storage-account add \
  --name < Function-App-Name > \
  --resource-group < Resource-Group > \
  --subscription < Subscription-Id > \
  --custom-id < Unique-Custom-Id > \
  --storage-type AzureFiles \
  --account-name < Storage-Account-Name > \
  --share-name < File-Share-Name >  \
  --access-key < Storage-Account-AccessKey > \
  --mount-path </path/to/mount>
Sinalizador Valor
custom-id Qualquer cadeia exclusiva
tipo de armazenamento Atualmente, apenas o AzureFiles é suportado
share-name Partilha pré-existente
mount-path Caminho no qual a partilha estará acessível dentro do contentor. O valor tem de ser do formato /dir-name e não pode começar com /home

Pode encontrar mais comandos para modificar/eliminar a configuração da partilha de ficheiros aqui

Carregar as dependências para Ficheiros do Azure

Uma opção para carregar a dependência para Ficheiros do Azure é através de portal do Azure. Veja este guia para obter instruções para carregar dependências com o portal. Outras opções para carregar as suas dependências para Ficheiros do Azure são através da CLI do Azure e do PowerShell.

Aceder às dependências no código

Depois de as dependências serem carregadas na partilha de ficheiros, pode aceder às dependências a partir do seu código. A partilha montada está disponível no caminho de montagem especificado, como /path/to/mount. Pode aceder ao diretório de destino através de APIs do sistema de ficheiros.

O exemplo seguinte mostra o código do acionador HTTP que acede à ffmpeg biblioteca, que é armazenada numa partilha de ficheiros montada.

import logging

import azure.functions as func
import subprocess 

FILE_SHARE_MOUNT_PATH = os.environ['FILE_SHARE_MOUNT_PATH']
FFMPEG = "ffmpeg"

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    command = req.params.get('command')
    # If no command specified, set the command to help
    if not command:
        command = "-h"

    try:
        byte_output  = subprocess.check_output(["/".join(FILE_SHARE_MOUNT_PATH, FFMPEG), command])
        return func.HttpResponse(byte_output.decode('UTF-8').rstrip(),status_code=200)
    except Exception as e:
        return func.HttpResponse("Unexpected exception happened when executing ffmpeg. Error message:" + str(e),status_code=200)

Quando implementa este código numa aplicação de funções no Azure, tem de criar uma definição de aplicação com um nome de FILE_SHARE_MOUNT_PATH chave e valor do caminho de partilha de ficheiros montado, que, para este exemplo, é /azure-files-share. Para efetuar a depuração local, tem de preencher o com o FILE_SHARE_MOUNT_PATH caminho do ficheiro onde as dependências estão armazenadas no seu computador local. Eis um exemplo a definir FILE_SHARE_MOUNT_PATH com local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "FILE_SHARE_MOUNT_PATH" : "PATH_TO_LOCAL_FFMPEG_DIR"
  }
}

Passos seguintes