简体   繁体   中英

Azure Function in Docker Container runs in Visual Studio, but does nothing from command line

My goal is to have a timer azure function (leveraging Selenium Chromedriver) run inside a docker container. it works in Visual Studio when i click this VS Docker Button ; however, when i execute the same docker command from VS in the command line, nothing happens. here's the command VS executes

docker run -dt -v "C:\Users\rlin\vsdbg\vs2017u5:/remote_debugger:rw" -e "ASPNETCORE_ENVIRONMENT=Development" -p 34480:80 --name Retriever --entrypoint tail retriever -f /dev/null

here's my dockerfile for your reference

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/azure-functions/dotnet:3.0 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /home/site/wwwroot
COPY . Retriever/
WORKDIR /home/site/wwwroot/Retriever
RUN dotnet build -c Release -o /home/site/wwwroot

FROM build AS publish
RUN dotnet publish -c Release -o /home/site/wwwroot

FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /home/site/wwwroot .
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true \
    AzureWebJobsStorage="StorageConnectionString"
    URL="url" \
    USERNAME="username" \
    PASSWORD="password" \
    SFTP="sftp" \
    CONTAINER="container"

WORKDIR /home/site/wwwroot
RUN apt-get update && \
    apt-get install -y gnupg wget curl unzip --no-install-recommends && \
    wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
    echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list && \
    apt-get update -y && \
    apt-get install -y google-chrome-stable && \
    CHROMEVER=$(google-chrome --product-version | grep -o "[^\.]*\.[^\.]*\.[^\.]*") && \
    DRIVERVER=$(curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROMEVER") && \
    wget -q --continue -P /chromedriver "http://chromedriver.storage.googleapis.com/$DRIVERVER/chromedriver_linux64.zip" && \
    unzip /chromedriver/chromedriver* -d /chromedriver

WORKDIR /
CMD ["/home/site/wwwroot/bin/Retriever.dll"]

been stuck on this for days, so any help is much appreciated.

EDIT: here's the code for ref

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OpenQA.Selenium.Chrome;
using Microsoft.Extensions.Configuration;
using OpenQA.Selenium;
using System.Collections.Generic;
using Azure.Storage.Blobs;
using System.IO.Compression;

namespace Retriever
{
    public class Retriever
    {
        private IConfigurationRoot config;

        [FunctionName("Retriever")]
        public void Run(
            [TimerTrigger("0 */1 * * * *", RunOnStartup = true)] TimerInfo myTimer,
            //[HttpTrigger(AuthorizationLevel.System, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            config = new ConfigurationBuilder()
                .SetBasePath(Environment.CurrentDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            Retrieve(log);
            log.LogInformation("Uploaded attachments");
            //return new OkResult();
        }
        public void Retrieve(ILogger log)
        {
            log.LogInformation("Retrieving attachments");

            ChromeOptions options = new ChromeOptions();
            options.AddArgument("--headless");
            options.AddArgument("--no-sandbox"); // Bypass OS security model
            options.AddArgument("start-maximized"); // open Browser in maximized mode
            options.AddArgument("disable-infobars"); // disabling infobars
            options.AddArgument("--disable-extensions"); // disabling extensions
            options.AddArgument("--disable-gpu"); // applicable to windows os only
            options.AddArgument("--disable-dev-shm-usage"); // overcome limited resource problems
            options.AddArgument("--whitelisted-ips");
            options.AddArgument("--proxy-server=direct://");
            options.AddArgument("--proxy-bypass-list=*");
            options.BinaryLocation = "/opt/google/chrome/google-chrome"; //Google Chrome path
            options.AddUserProfilePreference("download.prompt_for_download", false); // DO NOT prompt for download
            options.AddUserProfilePreference("download.default_directory", Path.GetTempPath()); // saves to Temp directory

            ChromeDriverService service = ChromeDriverService.CreateDefaultService("/chromedriver/", "chromedriver"); // use "/usr/bin/" and "chromedriver" in deployment

            ChromeDriver driver = new ChromeDriver(service, options, TimeSpan.FromMinutes(3));
            driver.Manage().Timeouts().ImplicitWait = new TimeSpan(5, 0, 0);

            // Login and Download Steps //
            driver.Navigate().GoToUrl(config.GetValue<string>("URL"));

            IWebElement username = driver.FindElement(By.XPath("//*[@id=\"username\"]"));
            username.SendKeys(config.GetValue<string>("USERNAME"));

            IWebElement next = driver.FindElement(By.CssSelector("body > mc-login > div > div.container > div > div > div.panel.panel-default.panel-shadow.login-panel > div > div > div > form > button"));
            next.Click();

            IWebElement password = driver.FindElement(By.CssSelector("#password"));
            password.SendKeys(config.GetValue<string>("PASSWORD"));

            IWebElement login = driver.FindElement(By.CssSelector("body > mc-login > div > div.container > div > div > div.panel.panel-default.panel-shadow.login-panel > div > div > div > form > button"));
            login.Click();

            IWebElement inbox = driver.FindElement(By.LinkText("Inbox"));
            inbox.Click();
            
            ICollection<IWebElement> emails = driver.FindElements(By.CssSelector("tr"));
            System.Threading.Thread.Sleep(new TimeSpan(0, 0, 2));
            foreach(IWebElement email in emails)
            {
                if (email.Displayed)
                {
                    email.Click();

                    IWebElement view = driver.FindElement(By.LinkText("View"));
                    view.Click();

                    string filename = driver.FindElement(By.CssSelector("body > div.page-container.with-sidebar.full-height.snap-content > div.main-content.full-height > div.ng-isolate-scope > div > div > div.container-fluid.full-height.fill-container.mainarea.ng-scope.mc-tab-unique-main > mc-list-detail > div > div.col-xs-12.col-sm-7.col-md-8.col-lg-8.hidden-xs.full-height.animate-active.no-padding > div > div.dynamic-full-height.horizontal-scroll.vertical-scroll.panel-half-margin-top-negative.ng-scope > div.pull-left.full-width.ng-scope > detail > div:nth-child(4) > div > mc-thumbnail > div > div:nth-child(2) > ul > li > span > span:nth-child(2)")).GetAttribute("innerHTML");
                    string filepath = Path.Combine(Path.GetTempPath(), filename);

                    IWebElement download = driver.FindElement(By.LinkText("Download"));
                    download.Click();
                    System.Threading.Thread.Sleep(new TimeSpan(0, 0, 2));
                    UploadToBlobStorage(log, filepath, filename);
                }
            }
            driver.Close();
            driver.Quit();
            driver.Dispose();
            service.Dispose();
        }
        public void UploadToBlobStorage(ILogger log, string filepath, string filename)
        {
            BlobServiceClient serviceClient = new BlobServiceClient(config.GetValue<string>("SFTP"));
            BlobContainerClient containerClient = serviceClient.GetBlobContainerClient(config.GetValue<string>("CONTAINER"));
            BlobClient client = containerClient.GetBlobClient("uploads/" + filename + ".txt");

            ZipArchive z = ZipFile.Open(filepath, ZipArchiveMode.Update);
            foreach(ZipArchiveEntry e in z.Entries)
            {
                client.Upload(e.Open(), overwrite: true);
            }
            z.Dispose();
            if(File.Exists(filepath)) { File.Delete(filepath); }
        }
    }
}

The command you posted is only used for Visual Studios debugging functionality. It overwites the entrypoint with tail -f /dev/null which is basically "loop forever".

You will need to run docker build <path to dir where Dockerfile is located> -t retriever to build the image and then docker run -p 80:80 retriever to run it. This should make the app available on port 80 of your system. If you want the app on another port, you can use -p <another port>:80

The last line ( CMD ["/home/site/wwwroot/bin/Retriever.dll"] ) in your Dockerfile overwrites the preset CMD of azure-functions/dotnet ( /azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost ) and will lead to the error "exec: \"dotnet\": executable file not found in $PATH" . Your function is supposed to be run by WebHost , not by invoking the dll directly. Remove this line . The one above it is also unnecessary.

Your Dockerfile also has another issue (it will possibly still work without adressing them): You are copying your sources, building and publishing all to the same directory /home/site/wwwroot . I would suggest changing it like this for the build stage:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY . .
RUN dotnet build -c Release -o /app/build

For the publish stage:

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

And for final:

FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /app/publish .
[...]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM