Skip to content

Unable to start Chrome inside almalinux-minimal image #298

@baptouboullax

Description

@baptouboullax

Context

I am trying to make Plotly + Kaleido/Choreographer work inside a Docker image based on AlmaLinux 9.6 minimal, in order to export Plotly figures as PNG images.

Installing Chromium through dnf/EPEL was problematic because of proxy issues, so I switched to the recommended Kaleido/Choreographer installation command:

kaleido_get_chrome

This installs Chrome at:

/app/.local/share/choreographer/deps/chrome-linux64/chrome

Runtime dependencies installed

The following Chrome runtime dependencies were installed:

RUN microdnf install -y \
    nss \
    nspr \
    dbus-libs \
    at-spi2-atk \
    cups-libs \
    libXcomposite \
    libXdamage \
    libXfixes \
    libXrandr \
    mesa-libgbm \
    libdrm \
    libxkbcommon \
    pango \
    cairo \
    alsa-lib \
    gtk3 \
    xdg-utils \
    liberation-fonts \
    && microdnf clean all

Then this command:

ldd /app/.local/share/choreographer/deps/chrome-linux64/chrome | grep "not found"

returns nothing.


Sandbox issue identified

Chrome only works with --no-sandbox.

This command works:

/app/.local/share/choreographer/deps/chrome-linux64/chrome \
  --headless \
  --disable-gpu \
  --disable-dev-shm-usage \
  --no-sandbox \
  --dump-dom https://example.com

This command fails without --no-sandbox:

/app/.local/share/choreographer/deps/chrome-linux64/chrome \
  --headless \
  --disable-gpu \
  --disable-dev-shm-usage \
  --dump-dom https://example.com

Error:

FATAL:content/browser/zygote_host/zygote_host_impl_linux.cc:128] No usable sandbox!

Wrapper added around Chrome

Since Choreographer was directly launching:

/app/.local/share/choreographer/deps/chrome-linux64/chrome

I renamed the actual Chrome binary to chrome.real and replaced chrome with a wrapper.

Wrapper:

#!/bin/sh
exec /app/.local/share/choreographer/deps/chrome-linux64/chrome.real --no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage "$@"

Dockerfile section:

RUN mv /app/.local/share/choreographer/deps/chrome-linux64/chrome \
       /app/.local/share/choreographer/deps/chrome-linux64/chrome.real \
    && printf '%s\n' \
       '#!/bin/sh' \
       'exec /app/.local/share/choreographer/deps/chrome-linux64/chrome.real --no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage "$@"' \
       > /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chmod +x /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chmod +x /app/.local/share/choreographer/deps/chrome-linux64/chrome.real \
    && chown app:app /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chown app:app /app/.local/share/choreographer/deps/chrome-linux64/chrome.real

A Chrome smoke test through the wrapper works:

/app/.local/share/choreographer/deps/chrome-linux64/chrome \
  --headless \
  --disable-gpu \
  --dump-dom file:///tmp/chrome-test.html

It returns the expected HTML content, although Chrome logs some DBus/OOM warnings, which do not seem to be blocking.


The point where image export works

When opening a shell inside the container as user app, the following minimal Plotly/Kaleido export works:

python - <<'PY'
import os
import plotly.graph_objects as go

print("BROWSER_PATH =", os.environ.get("BROWSER_PATH"))

fig = go.Figure(data=[go.Bar(x=["A", "B"], y=[1, 2])])
fig.write_image("/tmp/test-kaleido.png")

print("Kaleido OK")
PY

Output:

BROWSER_PATH = /app/.local/share/choreographer/deps/chrome-linux64/chrome
Kaleido OK

So, when run manually in an interactive shell as user app, PNG export works correctly.


Remaining issue inside the Streamlit application

Inside the Streamlit application, the same export still fails with:

choreographer.browsers._errors.BrowserFailedError:
('The browser seemed to close immediately after starting.',
 ...
 'The browser we tried to start is located at /app/.local/share/choreographer/deps/chrome-linux64/chrome.')

The Chrome path used by Choreographer is the same as the one used in the successful manual test.


Additional debug inside the application

I added a direct subprocess.run() call before the Plotly export inside the application:

subprocess.run(
    [
        "/app/.local/share/choreographer/deps/chrome-linux64/chrome",
        "--headless",
        "--disable-gpu",
        "--dump-dom",
        "file:///tmp/chrome-test.html",
    ],
    check=True,
)

Inside the application, this fails with:

chrome_crashpad_handler: --database is required
Try 'chrome_crashpad_handler --help' for more information.
...
subprocess.CalledProcessError:
Command '['/app/.local/share/choreographer/deps/chrome-linux64/chrome', ...]'
died with <Signals.SIGTRAP: 5>.

After trying to add Crashpad-related flags, I still get:

chrome_crashpad_handler: --database is required
[ERROR:third_party/crashpad/crashpad/util/linux/socket.cc:120] recvmsg: Connection reset by peer (104)
...
died with <Signals.SIGTRAP: 5>.

Relevant Dockerfile section

USER root

RUN microdnf install -y \
    nss \
    nspr \
    dbus-libs \
    at-spi2-atk \
    cups-libs \
    libXcomposite \
    libXdamage \
    libXfixes \
    libXrandr \
    mesa-libgbm \
    libdrm \
    libxkbcommon \
    pango \
    cairo \
    alsa-lib \
    gtk3 \
    xdg-utils \
    liberation-fonts \
    && microdnf clean all

USER app

RUN kaleido_get_chrome

USER root

RUN mv /app/.local/share/choreographer/deps/chrome-linux64/chrome \
       /app/.local/share/choreographer/deps/chrome-linux64/chrome.real \
    && printf '%s\n' \
       '#!/bin/sh' \
       'exec /app/.local/share/choreographer/deps/chrome-linux64/chrome.real --no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage "$@"' \
       > /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chmod +x /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chmod +x /app/.local/share/choreographer/deps/chrome-linux64/chrome.real \
    && chown app:app /app/.local/share/choreographer/deps/chrome-linux64/chrome \
    && chown app:app /app/.local/share/choreographer/deps/chrome-linux64/chrome.real

USER app

ENV BROWSER_PATH=/app/.local/share/choreographer/deps/chrome-linux64/chrome

Summary

A minimal Plotly/Kaleido export works when run manually inside the container as user app.

However, the same Chrome binary fails when invoked from the Streamlit application process.
The Chrome binary path is the same in both cases:

/app/.local/share/choreographer/deps/chrome-linux64/chrome

At this point I don't know what to do, any ideas ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions