I have an Azure Function, with custom imported python modules, that runs correctly when I use func start in the directory. But, the next step is to get it running locally in a Docker container. After I build and run (following the Azure instructions), nothing happens, and I get the following output:
info: Host.Triggers.Warmup[0]
Initializing Warmup Extension.
info: Host.Startup[503]
Initializing Host. OperationId: '9711d195-2ab7-4b93-8c53-dd577b6b70d5'.
info: Host.Startup[504]
Host initialization: ConsecutiveErrors=0, StartupCount=1, OperationId=9711d195-2ab7-4b93-8c53-dd577b6b70d5
info: Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService[0]
LoggerFilterOptions
{
"MinLevel": "None",
"Rules": [
{
"ProviderName": null,
"CategoryName": null,
"LogLevel": null,
"Filter": "<AddFilter>b__0"
},
{
"ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
"CategoryName": null,
"LogLevel": "None",
"Filter": null
},
{
"ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
"CategoryName": null,
"LogLevel": null,
"Filter": "<AddFilter>b__0"
}
]
}
info: Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService[0]
FunctionResultAggregatorOptions
{
"BatchSize": 1000,
"FlushTimeout": "00:00:30",
"IsEnabled": true
}
info: Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService[0]
SingletonOptions
{
"LockPeriod": "00:00:15",
"ListenerLockPeriod": "00:01:00",
"LockAcquisitionTimeout": "10675199.02:48:05.4775807",
"LockAcquisitionPollingInterval": "00:00:05",
"ListenerLockRecoveryPollingInterval": "00:01:00"
}
info: Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService[0]
HttpOptions
{
"DynamicThrottlesEnabled": false,
"MaxConcurrentRequests": -1,
"MaxOutstandingRequests": -1,
"RoutePrefix": "api"
}
info: Microsoft.Azure.WebJobs.Hosting.JobHostService[0]
Starting JobHost
info: Host.Startup[401]
Starting Host (HostId=c8cdf3a0c1a6-2137340777, InstanceId=c64a8e95-4ded-4a90-ac41-461d9223ed24, Version=3.0.15417.0, ProcessId=1, AppDomainId=1, InDebugMode=False, InDiagnosticMode=False, FunctionsExtensionVersion=(null))
info: Host.Startup[314]
Loading functions metadata
info: Host.Startup[315]
1 functions loaded
info: Host.Startup[0]
Generating 1 job function(s)
info: Host.Startup[0]
Found the following functions:
Host.Functions.BadFunc
info: Microsoft.Azure.WebJobs.Script.WebHost.WebScriptHostHttpRoutesManager[0]
Initializing function HTTP routes
No HTTP routes mapped
info: Host.Startup[412]
Host initialized (107ms)
fail: Host.Startup[0]
The listener for function 'Functions.BadFunc' was unable to start.
Microsoft.Azure.WebJobs.Host.Listeners.FunctionListenerException: The listener for function 'Functions.BadFunc' was unable to start.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
at Microsoft.Azure.Storage.CloudStorageAccount.Parse(String connectionString)
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.get_TimerStatusDirectory() in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 77
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusBlobReference(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 144
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusAsync(String timerName)
at Microsoft.Azure.WebJobs.Extensions.Timers.Listeners.TimerListener.StartAsync(CancellationToken cancellationToken) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Listener\TimerListener.cs:line 99
at Microsoft.Azure.WebJobs.Host.Listeners.SingletonListener.StartAsync(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Singleton\SingletonListener.cs:line 72
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.StartAsync(CancellationToken cancellationToken, Boolean allowRetry) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 69
--- End of inner exception stack trace ---
info: Host.Startup[413]
Host started (154ms)
info: Host.Startup[0]
Job host started
Hosting environment: Production
Content root path: /
Now listening on: http://[::]:80
Application started. Press Ctrl+C to shut down.
info: Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcFunctionInvocationDispatcher[0]
Worker process started and initialized.
info: Host.Startup[0]
Retrying to start listener for function 'Functions.BadFunc' (Attempt 1)
info: Host.Startup[0]
Listener successfully started for function 'Functions.BadFunc' after 1 retries.
info: Host.General[316]
Host lock lease acquired by instance ID '000000000000000000000000F28FAECC'.
So it looks like the container is running after one failed start-up. Great. But why is nothing happening? I'm supposed to get Slack notifications if it runs correctly or fails. The Dockerfile is as follows:
# FROM mcr.microsoft.com/azure-functions/python:3.0-python3.6-appservice
FROM mcr.microsoft.com/azure-functions/python:3.0-python3.6
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
# install python dependencies
RUN apt-get update \
&& apt-get install -y \
build-essential \
cmake \
git \
wget \
unzip \
&& rm -rf /var/lib/apt/lists/*
# chrome install
#ARG CHROME_VERSION="google-chrome-stable"
RUN 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-chrome.list \
&& apt-get update -qqy \
&& apt-get -qqy install \
# ${CHROME_VERSION:-google-chrome-stable} \
&& rm /etc/apt/sources.list.d/google-chrome.list \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# install chromedriver used by Selenium
RUN LATEST=$(wget -q -O - http://chromedriver.storage.googleapis.com/LATEST_RELEASE) && \
wget http://chromedriver.storage.googleapis.com/$LATEST/chromedriver_linux64.zip && \
unzip chromedriver_linux64.zip && ln -s $PWD/chromedriver /usr/local/bin/chromedriver
ENV PATH="/usr/local/bin/chromedriver:${PATH}"
COPY requirements.txt /
RUN pip install -r /requirements.txt
COPY . /home/site/wwwroot
I use docker build --tag <DOCKER_ID>/azurefunctionsimage:v1.0.0 . to build the image, and I run it from Docker Desktop clicking "run."
I've closely followed this tutorial on getting selenium working in a container. I have pointed my scraping function to the chromedriver, but I don't think that's the issue. Here is my function.json file:
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "mytimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 * * * * *"
}
]
}
And local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureWebJobsStorage": "UseDevelopmentStorage=true;"
}
}
File structure in VSCode:
FunctionDirectory
|_ BadFunc
| __init__.py
| custom_mod1.py
| custom_mod2.py
...
| function.json
|_ local.settings.json
Dockerfile
requirements.txt
etc..
Am I missing something obvious on why I'm getting no results, when running the container locally?
Or would this have something to do with the actual __init__.py code, having a return type of None vs. something else?
def main(mytimer: func.TimerRequest) -> None: #should be something else?
utc_timestamp = datetime.datetime.utcnow().replace(
tzinfo=datetime.timezone.utc).isoformat()
if mytimer.past_due:
logging.info('The timer is past due!')
logging.info('Python timer trigger function ran at %s', utc_timestamp)
# Class instantiation for the handling stuff ----
name_handle_instance = dc.NameHandle()
#calls other functions below...