Skip to content

Commit e61ce04

Browse files
committed
feat: use multi stage docker image
1 parent 1da7591 commit e61ce04

File tree

2 files changed

+80
-35
lines changed

2 files changed

+80
-35
lines changed

.generator/Dockerfile

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
# --- Builder Stage ---
16+
# This stage installs all build dependencies and compiles all Python versions.
1517
FROM marketplace.gcr.io/google/ubuntu2404 AS builder
1618

1719
# TODO(https://github.com/googleapis/librarian/issues/901): Install the necssary dependencies and build tools.
@@ -38,54 +40,85 @@ RUN apt-get update && \
3840
# Set up environment variables for tool versions to make updates easier.
3941
ENV BAZELISK_VERSION=v1.26.0
4042

43+
# Install multiple Python versions from source. `make altinstall` is used to
44+
# prevent replacing the system's default python binary.
4145
RUN for PYTHON_VERSION in 3.9.23 3.10.18 3.11.13 3.12.11 3.13.5; do \
42-
# Install Python from source
4346
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz && \
4447
tar -xvf Python-${PYTHON_VERSION}.tgz && \
4548
cd Python-${PYTHON_VERSION} && \
46-
./configure --enable-optimizations && \
49+
./configure --enable-optimizations --prefix=/usr/local && \
50+
make -j$(nproc) && \
4751
make altinstall && \
4852
cd / && \
4953
rm -rf Python-${PYTHON_VERSION}* \
5054
; done
5155

52-
# Get the pip installation script using the instructions below
53-
# https://pip.pypa.io/en/stable/installation/#get-pip-py
54-
# Attempts to use `ensurepip` instead of `get-pip.py` resulted in errors like `python3.13.5 not found`
55-
RUN wget --no-check-certificate -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py'
56-
5756
# Install pip for each python version
58-
RUN for PYTHON_VERSION in 3.9 3.10 3.11 3.12 3.13; do \
59-
python${PYTHON_VERSION} /tmp/get-pip.py \
60-
; done
61-
62-
# Remove the pip installation script
63-
RUN rm /tmp/get-pip.py
64-
65-
# Test Pip
66-
RUN for PYTHON_VERSION in 3.9 3.10 3.11 3.12 3.13; do \
67-
python${PYTHON_VERSION} -m pip \
68-
; done
69-
70-
71-
# TODO(https://github.com/googleapis/librarian/issues/904): Install protoc for gencode.
57+
RUN wget --no-check-certificate -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' && \
58+
for PYTHON_VERSION in 3.9 3.10 3.11 3.12 3.13; do \
59+
python${PYTHON_VERSION} /tmp/get-pip.py; \
60+
done && \
61+
rm /tmp/get-pip.py
7262

7363
# Install Bazelisk
7464
RUN wget https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-amd64 -O /usr/local/bin/bazelisk && \
7565
chmod +x /usr/local/bin/bazelisk
7666

77-
# TODO(https://github.com/googleapis/librarian/issues/902): Create a dedicate non-root user and
78-
# switch to the non-root user to run subsequent commands.
79-
80-
# Set the working directory in the container.
67+
# Set the working directory for build-related tasks.
8168
WORKDIR /app
8269

70+
# TODO(https://github.com/googleapis/librarian/issues/904): Install protoc for gencode.
8371
# TODO(https://github.com/googleapis/librarian/issues/907): Install Python dependencies from requirements.in.
8472
# TODO(https://github.com/googleapis/librarian/issues/905): Install Synthtool by cloning its repo.
8573
# TODO(https://github.com/googleapis/librarian/issues/906): Clone googleapis and run bazelisk build.
8674

87-
# Copy the CLI script into the container and set ownership.
75+
# --- Final Stage ---
76+
# This stage creates the lightweight final image, copying only the
77+
# necessary artifacts from the builder stage.
78+
FROM marketplace.gcr.io/google/ubuntu2404
79+
80+
# Install only the essential runtime libraries for Python.
81+
# These are the non "-dev" versions of the libraries used in the builder.
82+
RUN apt-get update && \
83+
apt-get install -y --no-install-recommends \
84+
ca-certificates \
85+
libssl3 \
86+
zlib1g \
87+
libbz2-1.0 \
88+
libffi8 \
89+
libsqlite3-0 \
90+
libreadline8 \
91+
&& apt-get clean && \
92+
rm -rf /var/lib/apt/lists/*
93+
94+
# TODO(https://github.com/googleapis/librarian/issues/902): Create a dedicate non-root user and
95+
# switch to the non-root user to run subsequent commands.
96+
# Example:
97+
# RUN groupadd --system --gid 1000 appgroup && \
98+
# useradd --system --uid 1000 --gid appgroup appuser
99+
# USER appuser
100+
101+
# Copy all Python interpreters, their pip executables, and their standard libraries from the builder.
102+
COPY --from=builder /usr/local/bin/python3.9 /usr/local/bin/
103+
COPY --from=builder /usr/local/lib/python3.9 /usr/local/lib/python3.9
104+
105+
COPY --from=builder /usr/local/bin/python3.10 /usr/local/bin/
106+
COPY --from=builder /usr/local/lib/python3.10 /usr/local/lib/python3.10
107+
108+
COPY --from=builder /usr/local/bin/python3.11 /usr/local/bin/
109+
COPY --from=builder /usr/local/lib/python3.11 /usr/local/lib/python3.11
110+
111+
COPY --from=builder /usr/local/bin/python3.12 /usr/local/bin/
112+
COPY --from=builder /usr/local/lib/python3.12 /usr/local/lib/python3.12
113+
114+
COPY --from=builder /usr/local/bin/python3.13 /usr/local/bin/
115+
COPY --from=builder /usr/local/lib/python3.13 /usr/local/lib/python3.13
116+
117+
# Set the working directory in the container.
118+
WORKDIR /app
119+
120+
# Copy the CLI script into the container.
88121
COPY .generator/cli.py .
89122

90123
# Set the entrypoint for the container to run the script.
91-
ENTRYPOINT ["python3.11", "./cli.py"]
124+
ENTRYPOINT ["python3.11", "./cli.py"]

cloudbuild.yaml

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,30 @@
1414

1515
# TODO(https://github.com/googleapis/google-cloud-python/issues/14142):
1616
# Reduce this timeout by moving the installation of Python runtimes to a separate base image
17-
timeout: 7200s # 2 hours
17+
timeout: 7200s # 2 hours for the first uncached run, can be lowered later.
1818
steps:
19-
# This step builds the Docker image.
19+
# STEP 1: Pull the latest version of the image to use as a cache.
20+
# The '|| exit 0' part ensures the build doesn't fail if the image
21+
# doesn't exist yet (e.g., on the very first run).
22+
- name: 'gcr.io/cloud-builders/docker'
23+
entrypoint: 'bash'
24+
args: ['-c', 'docker pull gcr.io/$PROJECT_ID/python-librarian-generator:latest || exit 0']
25+
26+
# STEP 2: Build the new image, using the pulled image as a cache source.
2027
- name: 'gcr.io/cloud-builders/docker'
2128
args:
2229
- 'build'
23-
- '-t'
24-
- 'gcr.io/$PROJECT_ID/python-librarian-generator:latest'
25-
- '-f'
26-
- '.generator/Dockerfile'
30+
- '--tag=gcr.io/$PROJECT_ID/python-librarian-generator:latest'
31+
- '--cache-from=gcr.io/$PROJECT_ID/python-librarian-generator:latest'
32+
- '--file=.generator/Dockerfile'
2733
- '.'
2834

29-
# This section automatically create a storage bucket for storing docker build logs.
35+
# STEP 3: Push the newly built image to the registry so it can be used
36+
# as the cache for the next run. Cloud Build does this automatically if
37+
# the image is listed in the 'images' section.
38+
images:
39+
- 'gcr.io/$PROJECT_ID/python-librarian-generator:latest'
40+
41+
# This section automatically creates a storage bucket for storing docker build logs.
3042
options:
31-
default_logs_bucket_behavior: REGIONAL_USER_OWNED_BUCKET
43+
default_logs_bucket_behavior: REGIONAL_USER_OWNED_BUCKET

0 commit comments

Comments
 (0)