[英]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.我的目标是让计时器 azure function(利用 Selenium Chromedriver)在 Z040AF6ZFC3 容器内运行。 it works in Visual Studio when i click this VS Docker Button ;
当我单击此VS Docker 按钮时,它在 Visual Studio 中工作; however, when i execute the same docker command from VS in the command line, nothing happens.
但是,当我在命令行中从 VS 执行相同的 docker 命令时,什么也没有发生。 here's the command VS executes
这是VS执行的命令
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这是我的 dockerfile 供您参考
#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编辑:这是 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.您发布的命令仅用于 Visual Studios 调试功能。 It overwites the entrypoint with
tail -f /dev/null
which is basically "loop forever".它用
tail -f /dev/null
覆盖入口点,这基本上是“永远循环”。
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.您将需要运行
docker build <path to dir where Dockerfile is located> -t retriever
来构建图像,然后docker run -p 80:80 retriever
This should make the app available on port 80 of your system.这应该使应用程序在系统的端口 80 上可用。 If you want the app on another port, you can use
-p <another port>:80
如果您希望应用程序在另一个端口上,您可以使用
-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"
. 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
) 并且会导致错误"exec: \"dotnet\": executable file not found in $PATH"
。 Your function is supposed to be run by WebHost
, not by invoking the dll directly.您的 function 应该由
WebHost
运行,而不是通过直接调用 dll 来运行。 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
.您的
Dockerfile
还存在另一个问题(它可能仍然可以在不解决它们的情况下工作):您正在复制源代码,构建并发布所有内容到同一目录/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 .
[...]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.