Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/workflows/benchmark_docker_pull.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: "benchmark: docker pull time"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert


on:
workflow_dispatch:

env:
ELECTRON_SKIP_BINARY_DOWNLOAD: 1

jobs:
benchmark:
name: "pull-time comparison"
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: npm ci
- run: npm run build
- name: Build monolithic image (main branch Dockerfile)
run: |
node utils/pack_package.js playwright-core utils/docker/playwright-core.tar.gz
git show main:utils/docker/Dockerfile.jammy > /tmp/Dockerfile.monolithic
docker buildx build \
--platform linux/amd64 \
--push \
-f /tmp/Dockerfile.monolithic \
-t ghcr.io/${{ github.repository }}:bench-monolithic \
utils/docker/
- name: Build split-layer image (this branch Dockerfile)
run: |
docker buildx build \
--platform linux/amd64 \
--push \
-f utils/docker/Dockerfile.jammy \
-t ghcr.io/${{ github.repository }}:bench-split \
utils/docker/
- name: Benchmark cold pulls
run: |
echo "## Docker Pull Benchmark" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Runner: $(uname -m), $(nproc) cores" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Image | Run 1 | Run 2 | Run 3 | Avg |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|-------|-------|-----|" >> $GITHUB_STEP_SUMMARY

for variant in monolithic split; do
IMAGE="ghcr.io/${{ github.repository }}:bench-${variant}"
TIMES=()
for i in 1 2 3; do
docker rmi "$IMAGE" 2>/dev/null
docker image prune -a -f >/dev/null 2>&1
START=$(date +%s%N)
docker pull "$IMAGE" >/dev/null 2>&1
END=$(date +%s%N)
ELAPSED=$(python3 -c "print(f'{($END - $START) / 1e9:.1f}')")
TIMES+=("$ELAPSED")
done
AVG=$(python3 -c "t=[${TIMES[0]},${TIMES[1]},${TIMES[2]}]; print(f'{sum(t)/len(t):.1f}')")
echo "| ${variant} | ${TIMES[0]}s | ${TIMES[1]}s | ${TIMES[2]}s | ${AVG}s |" >> $GITHUB_STEP_SUMMARY
done

echo "" >> $GITHUB_STEP_SUMMARY
echo "### Layer structure" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "Monolithic:" >> $GITHUB_STEP_SUMMARY
docker manifest inspect ghcr.io/${{ github.repository }}:bench-monolithic 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
for m in data.get('manifests', []):
if m['platform']['architecture'] == 'amd64':
print(f\" digest: {m['digest'][:32]}...\")
" || true
docker buildx imagetools inspect ghcr.io/${{ github.repository }}:bench-monolithic --raw 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
layers = data.get('layers', [])
for i,l in enumerate(layers):
print(f' Layer {i}: {l[\"size\"]/1e6:.1f} MB')
print(f' Total: {sum(l[\"size\"] for l in layers)/1e6:.1f} MB')
" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY
echo "Split:" >> $GITHUB_STEP_SUMMARY
docker buildx imagetools inspect ghcr.io/${{ github.repository }}:bench-split --raw 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
layers = data.get('layers', [])
for i,l in enumerate(layers):
print(f' Layer {i}: {l[\"size\"]/1e6:.1f} MB')
print(f' Total: {sum(l[\"size\"] for l in layers)/1e6:.1f} MB')
" >> $GITHUB_STEP_SUMMARY || true
echo '```' >> $GITHUB_STEP_SUMMARY
2 changes: 2 additions & 0 deletions .github/workflows/publish_release_docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
uses: docker/setup-qemu-action@v4
with:
platforms: arm64
- name: Set up Docker Buildx
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

uses: docker/setup-buildx-action@v3
- run: npm ci
- run: npm run build
- name: Azure Login
Expand Down
30 changes: 23 additions & 7 deletions utils/docker/Dockerfile.jammy
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

FROM ubuntu:jammy

ARG DEBIAN_FRONTEND=noninteractive
Expand All @@ -11,7 +12,6 @@ ENV LC_ALL=C.UTF-8
# === INSTALL Node.js ===

RUN apt-get update && \
# Install Node.js
apt-get install -y curl wget gpg ca-certificates && \
mkdir -p /etc/apt/keyrings && \
curl -sL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
Expand All @@ -27,23 +27,21 @@ RUN apt-get update && \
adduser pwuser

# === BAKE BROWSERS INTO IMAGE ===
# Browsers are split into separate layers to enable parallel pulls.

ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright

# 1. Add tip-of-tree Playwright package to install its browsers.
# The package should be built beforehand from tip-of-tree Playwright.
COPY ./playwright-core.tar.gz /tmp/playwright-core.tar.gz

# 2. Bake in browsers & deps.
# Browsers will be downloaded in `/ms-playwright`.
# Note: make sure to set 777 to the registry so that any user can access
# registry.
# 2. Set up playwright-core and install all system dependencies in one layer.
RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npm exec --no -- playwright-core mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npm exec --no -- playwright-core install --with-deps && rm -rf /var/lib/apt/lists/* && \
npm exec --no -- playwright-core install-deps chromium chromium-headless-shell firefox webkit && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not list browsers, install all deps.

# Workaround for https://github.com/microsoft/playwright/issues/27313
# While the gstreamer plugin load process can be in-process, it ended up throwing
# an error that it can't have libsoup2 and libsoup3 in the same process because
Expand All @@ -53,7 +51,25 @@ RUN mkdir /ms-playwright && \
else \
rm /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstwebrtc.so; \
fi && \
rm -rf /var/lib/apt/lists/*

# 3. Install each browser binary in its own layer for parallel pulling.
RUN cd /ms-playwright-agent && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you could benefit from splitting these too? chromium is your largest layer

npm exec --no -- playwright-core install chromium chromium-headless-shell && \
chmod -R 777 /ms-playwright/chromium-* /ms-playwright/chromium_headless_shell-*

RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install firefox && \
chmod -R 777 /ms-playwright/firefox-*

RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install webkit && \
chmod -R 777 /ms-playwright/webkit-*

# 4. Install ffmpeg and clean up.
RUN cd /ms-playwright-agent && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First installed browser will do it, this layer will be empty.

npm exec --no -- playwright-core install ffmpeg && \
rm /tmp/playwright-core.tar.gz && \
rm -rf /ms-playwright-agent && \
rm -rf ~/.npm/ && \
chmod -R 777 /ms-playwright
chmod -R 777 /ms-playwright/ffmpeg-*
30 changes: 23 additions & 7 deletions utils/docker/Dockerfile.noble
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtop

FROM ubuntu:noble

ARG DEBIAN_FRONTEND=noninteractive
Expand All @@ -11,7 +12,6 @@ ENV LC_ALL=C.UTF-8
# === INSTALL Node.js ===

RUN apt-get update && \
# Install Node.js
apt-get install -y curl wget gpg ca-certificates && \
mkdir -p /etc/apt/keyrings && \
curl -sL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
Expand All @@ -27,24 +27,40 @@ RUN apt-get update && \
adduser pwuser

# === BAKE BROWSERS INTO IMAGE ===
# Browsers are split into separate layers to enable parallel pulls.

ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright

# 1. Add tip-of-tree Playwright package to install its browsers.
# The package should be built beforehand from tip-of-tree Playwright.
COPY ./playwright-core.tar.gz /tmp/playwright-core.tar.gz

# 2. Bake in browsers & deps.
# Browsers will be downloaded in `/ms-playwright`.
# Note: make sure to set 777 to the registry so that any user can access
# registry.
# 2. Set up playwright-core and install all system dependencies in one layer.
RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npm exec --no -- playwright-core mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npm exec --no -- playwright-core install --with-deps && rm -rf /var/lib/apt/lists/* && \
npm exec --no -- playwright-core install-deps chromium chromium-headless-shell firefox webkit && \
rm -rf /var/lib/apt/lists/*

# 3. Install each browser binary in its own layer for parallel pulling.
RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install chromium chromium-headless-shell && \
chmod -R 777 /ms-playwright/chromium-* /ms-playwright/chromium_headless_shell-*

RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install firefox && \
chmod -R 777 /ms-playwright/firefox-*

RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install webkit && \
chmod -R 777 /ms-playwright/webkit-*

# 4. Install ffmpeg and clean up.
RUN cd /ms-playwright-agent && \
npm exec --no -- playwright-core install ffmpeg && \
rm /tmp/playwright-core.tar.gz && \
rm -rf /ms-playwright-agent && \
rm -rf ~/.npm/ && \
chmod -R 777 /ms-playwright
chmod -R 777 /ms-playwright/ffmpeg-*
24 changes: 18 additions & 6 deletions utils/docker/publish_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ tag_and_push() {
local source="$1"
local target="$2"
echo "-- tagging: $target"
docker tag $source $target
docker push $target
attach_eol_manifest $target
docker buildx imagetools create --tag "$target" "$source"
attach_eol_manifest "$target"
}

attach_eol_manifest() {
Expand Down Expand Up @@ -79,11 +78,24 @@ publish_docker_images_with_arch_suffix() {
fi
# Prune docker images to avoid platform conflicts
docker system prune -fa
./build.sh "--${ARCH}" "${FLAVOR}" playwright:localbuild

for ((i = 0; i < ${#TAGS[@]}; i++)) do
local CANONICAL_TAG="playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAGS[0]}-${ARCH}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert


# Build and push the canonical tag.
node ../../utils/pack_package.js playwright-core ./playwright-core.tar.gz
docker buildx build \
--platform "linux/${ARCH}" \
--push \
-f "Dockerfile.${FLAVOR}" \
-t "${CANONICAL_TAG}" \
.
rm -f playwright-core.tar.gz
attach_eol_manifest "${CANONICAL_TAG}"

# Create additional tags via registry-side copy (no re-upload).
for ((i = 1; i < ${#TAGS[@]}; i++)) do
local TAG="${TAGS[$i]}"
tag_and_push playwright:localbuild "playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}-${ARCH}"
tag_and_push "${CANONICAL_TAG}" "playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}-${ARCH}"
done
}

Expand Down