Skip to content

Commit 45e58d6

Browse files
Implement support for building local Dockerfiles for python build native deps (#4611)
* Adding python313 dockerfile locally * Refactor approach for local docker file build * Update release notes --------- Co-authored-by: Lilian Kasem <[email protected]>
1 parent 8c1ead2 commit 45e58d6

File tree

7 files changed

+105
-17
lines changed

7 files changed

+105
-17
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
#### Changes
99

1010
- <entry>
11+
- Add Dockerfile for python 3.13 local build environment (#4611)

src/Cli/func/Azure.Functions.Cli.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@
9898
<EmbeddedResource Include="StaticResources\Dockerfile.python3.13">
9999
<LogicalName>$(AssemblyName).Dockerfile.python3.13</LogicalName>
100100
</EmbeddedResource>
101+
<EmbeddedResource Include="StaticResources\Dockerfile.python3.13-buildenv">
102+
<LogicalName>$(AssemblyName).Dockerfile.python3.13-buildenv</LogicalName>
103+
</EmbeddedResource>
101104
<EmbeddedResource Include="StaticResources\Dockerfile.javascript">
102105
<LogicalName>$(AssemblyName).Dockerfile.javascript</LogicalName>
103106
</EmbeddedResource>

src/Cli/func/Common/Constants.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ public static class DockerImages
189189
public const string LinuxPython310ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.10-buildenv";
190190
public const string LinuxPython311ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.11-buildenv";
191191
public const string LinuxPython312ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.12-buildenv";
192-
public const string LinuxPython313ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.13-buildenv";
193192
}
194193

195194
public static class StaticResourcesNames
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace Azure.Functions.Cli.Helpers;
5+
6+
public class DockerImageInfo
7+
{
8+
public string ImageName { get; set; }
9+
10+
public bool CanPull { get; set; }
11+
}

src/Cli/func/Helpers/PythonHelpers.cs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,8 @@ public static async Task<Stream> ZipToSquashfsStream(Stream stream)
362362
string containerId = null;
363363
try
364364
{
365-
string dockerImage = await ChoosePythonBuildEnvImage();
365+
DockerImageInfo result = await ChoosePythonBuildEnvImage();
366+
string dockerImage = result.ImageName;
366367
containerId = await DockerHelpers.DockerRun(dockerImage, command: "sleep infinity");
367368

368369
await DockerHelpers.CopyToContainer(containerId, tmpFile, $"/file.zip");
@@ -494,14 +495,19 @@ private static async Task RestorePythonRequirementsDocker(string functionAppRoot
494495
var dockerSkipPullFlagSetting = Environment.GetEnvironmentVariable(Constants.PythonDockerImageSkipPull);
495496
var dockerRunSetting = Environment.GetEnvironmentVariable(Constants.PythonDockerRunCommand);
496497

498+
bool canPull = true;
497499
string dockerImage = pythonDockerImageSetting;
498500
if (string.IsNullOrEmpty(dockerImage))
499501
{
500-
dockerImage = await ChoosePythonBuildEnvImage();
502+
DockerImageInfo result = await ChoosePythonBuildEnvImage();
503+
dockerImage = result.ImageName;
504+
canPull = result.CanPull;
501505
}
502506

503-
if (string.IsNullOrEmpty(dockerSkipPullFlagSetting) ||
504-
!(dockerSkipPullFlagSetting.Equals("true", StringComparison.OrdinalIgnoreCase) || dockerSkipPullFlagSetting == "1"))
507+
if (canPull &&
508+
(string.IsNullOrEmpty(dockerSkipPullFlagSetting) ||
509+
!(dockerSkipPullFlagSetting.Equals("true", StringComparison.OrdinalIgnoreCase) ||
510+
dockerSkipPullFlagSetting == "1")))
505511
{
506512
await DockerHelpers.DockerPull(dockerImage);
507513
}
@@ -547,10 +553,30 @@ private static async Task RestorePythonRequirementsDocker(string functionAppRoot
547553
}
548554
}
549555

550-
private static async Task<string> ChoosePythonBuildEnvImage()
556+
private static async Task<DockerImageInfo> ChoosePythonBuildEnvImage()
551557
{
552558
WorkerLanguageVersionInfo workerInfo = await GetEnvironmentPythonVersion();
553-
return GetBuildNativeDepsEnvironmentImage(workerInfo);
559+
var (image, isLocal) = await GetBuildNativeDepsEnvironmentImage(workerInfo);
560+
561+
if (isLocal)
562+
{
563+
// Setup image tag and content
564+
string imageContent = image;
565+
image = $"azure-functions/python:4-python{workerInfo.Major}.{workerInfo.Minor}-buildenv";
566+
567+
// Prepare temporary directory for docker build context
568+
string tempDockerfileDirecotry = Path.Combine(Path.GetTempPath(), $"{image}-docker");
569+
FileSystemHelpers.EnsureDirectory(tempDockerfileDirecotry);
570+
string tempDockerfile = Path.Combine(tempDockerfileDirecotry, "Dockerfile");
571+
572+
// Write Dockerfile content to temporary file
573+
await FileSystemHelpers.WriteAllTextToFileAsync(tempDockerfile, imageContent);
574+
575+
// Build the image
576+
await DockerHelpers.DockerBuild(image, tempDockerfileDirecotry);
577+
}
578+
579+
return new DockerImageInfo { ImageName = image, CanPull = !isLocal };
554580
}
555581

556582
private static string CopyToTemp(IEnumerable<string> files, string rootPath)
@@ -596,32 +622,35 @@ public static Task<string> GetDockerInitFileContent(WorkerLanguageVersionInfo in
596622
return StaticResources.DockerfilePython37;
597623
}
598624

599-
private static string GetBuildNativeDepsEnvironmentImage(WorkerLanguageVersionInfo info)
625+
// Build environment images for building native dependencies for python function apps
626+
private static async Task<(string Image, bool IsLocal)> GetBuildNativeDepsEnvironmentImage(WorkerLanguageVersionInfo info)
600627
{
601628
if (info?.Major == 3)
602629
{
603630
switch (info?.Minor)
604631
{
605632
case 6:
606-
return Constants.DockerImages.LinuxPython36ImageAmd64;
633+
return (DockerImages.LinuxPython36ImageAmd64, false);
607634
case 7:
608-
return Constants.DockerImages.LinuxPython37ImageAmd64;
635+
return (DockerImages.LinuxPython37ImageAmd64, false);
609636
case 8:
610-
return Constants.DockerImages.LinuxPython38ImageAmd64;
637+
return (DockerImages.LinuxPython38ImageAmd64, false);
611638
case 9:
612-
return Constants.DockerImages.LinuxPython39ImageAmd64;
639+
return (DockerImages.LinuxPython39ImageAmd64, false);
613640
case 10:
614-
return Constants.DockerImages.LinuxPython310ImageAmd64;
641+
return (DockerImages.LinuxPython310ImageAmd64, false);
615642
case 11:
616-
return Constants.DockerImages.LinuxPython311ImageAmd64;
643+
return (DockerImages.LinuxPython311ImageAmd64, false);
617644
case 12:
618-
return Constants.DockerImages.LinuxPython312ImageAmd64;
645+
return (DockerImages.LinuxPython312ImageAmd64, false);
646+
647+
// From Python 3.13 onwards, we use a Dockerfile to build the image locally
619648
case 13:
620-
return Constants.DockerImages.LinuxPython313ImageAmd64;
649+
return (await StaticResources.DockerfilePython313BuildEnv, true);
621650
}
622651
}
623652

624-
return Constants.DockerImages.LinuxPython312ImageAmd64;
653+
return (DockerImages.LinuxPython312ImageAmd64, false);
625654
}
626655

627656
private static bool IsVersionSupported(WorkerLanguageVersionInfo info)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
FROM mcr.microsoft.com/oryx/python:3.13-debian-bookworm-20250724.1
2+
3+
ENV LANG=C.UTF-8 \
4+
ACCEPT_EULA=Y \
5+
AzureWebJobsScriptRoot=/home/site/wwwroot \
6+
HOME=/home \
7+
FUNCTIONS_WORKER_RUNTIME=python \
8+
ASPNETCORE_URLS=http://+:80 \
9+
DOTNET_RUNNING_IN_CONTAINER=true \
10+
DOTNET_USE_POLLING_FILE_WATCHER=true
11+
12+
# Install Python dependencies
13+
RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor --batch --yes -o /usr/share/keyrings/microsoft-prod.gpg && \
14+
apt-get update && \
15+
apt-get install -y wget apt-transport-https curl gnupg2 locales && \
16+
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \
17+
echo "deb [arch=amd64] https://packages.microsoft.com/debian/12/prod bookworm main" | tee /etc/apt/sources.list.d/mssql-release.list && \
18+
# Needed for libss3 and in turn MS SQL
19+
echo 'deb http://security.debian.org/debian-security bookworm-security main' >> /etc/apt/sources.list && \
20+
curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list && \
21+
# install MS SQL related packages.pinned version in PR # 1012.
22+
echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && \
23+
locale-gen && \
24+
apt-get update && \
25+
# MS SQL related packages: unixodbc msodbcsql18 mssql-tools
26+
ACCEPT_EULA=Y apt-get install -y unixodbc msodbcsql18 mssql-tools18 && \
27+
# OpenCV dependencies:libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb
28+
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb && \
29+
# .NET Core dependencies: ca-certificates libc6 libgcc1 libgssapi-krb5-2 libicu72 libssl3 libstdc++6 zlib1g
30+
# Azure ML dependencies: liblttng-ust0
31+
# OpenMP dependencies: libgomp1
32+
# binutils: binutils
33+
apt-get install -y --no-install-recommends ca-certificates \
34+
libc6 libgcc1 libgssapi-krb5-2 libicu72 libssl3 libstdc++6 zlib1g && \
35+
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb binutils \
36+
libgomp1 liblttng-ust1 && \
37+
rm -rf /var/lib/apt/lists/*
38+
39+
RUN apt-get update && \
40+
apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev \
41+
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
42+
xz-utils tk-dev libffi-dev liblzma-dev python3-openssl git unixodbc-dev dh-autoreconf \
43+
libcurl4-openssl-dev libssl-dev python3-dev libevent-dev python3-openssl squashfs-tools unzip

src/Cli/func/StaticResources/StaticResources.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public static class StaticResources
4848

4949
public static Task<string> DockerfilePython313 => GetValue("Dockerfile.python3.13");
5050

51+
public static Task<string> DockerfilePython313BuildEnv => GetValue("Dockerfile.python3.13-buildenv");
52+
5153
public static Task<string> DockerfilePowershell7 => GetValue("Dockerfile.powershell7");
5254

5355
public static Task<string> DockerfilePowershell72 => GetValue("Dockerfile.powershell7.2");

0 commit comments

Comments
 (0)