playwright-chrome

关于在docker中使用playwright,以及独立运行chrome的记录

使用chromium

创建dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
FROM mcr.microsoft.com/playwright:v1.55.0-noble

# Install system packages (cached layer - rarely changes)
RUN apt-get update && apt-get install -y \
xvfb \
x11vnc \
fluxbox \
supervisor \
python3-pip \
git \
net-tools \
socat \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*

# Install Python dependencies (separate layer for better caching)
RUN pip3 install --break-system-packages websockify

# Download external resources (separate layer)
RUN git clone https://github.com/novnc/noVNC.git /opt/noVNC

# Create user and directories (last - avoid cache invalidation)
RUN groupadd -r playwright && useradd -r -g playwright -G audio,video playwright \
&& mkdir -p /home/playwright/.vnc /app/chrome-user-data /var/log/supervisor \
&& chown -R playwright:playwright /home/playwright /app /var/log/supervisor

RUN echo '[supervisord]\n\
nodaemon=true\n\
user=playwright\n\
logfile=/var/log/supervisor/supervisord.log\n\
logfile_maxbytes=10MB\n\
logfile_backups=3\n\
\n\
[program:xvfb]\n\
command=Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset\n\
priority=10\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/xvfb.log\n\
stderr_logfile=/var/log/supervisor/xvfb.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:socat]\n\
command=socat TCP-LISTEN:9222,fork,reuseaddr TCP6:[::1]:9222\n\
priority=15\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/socat.log\n\
stderr_logfile=/var/log/supervisor/socat.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:fluxbox]\n\
command=bash -c "sleep 2 && fluxbox"\n\
priority=20\n\
environment=DISPLAY=:99\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/fluxbox.log\n\
stderr_logfile=/var/log/supervisor/fluxbox.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:x11vnc]\n\
command=bash -c "sleep 3 && x11vnc -display :99 -forever -noxdamage -shared -rfbport 5901 -passwd wqf12345"\n\
priority=20\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/x11vnc.log\n\
stderr_logfile=/var/log/supervisor/x11vnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:chromium]\n\
command=bash -c "sleep 8 && rm -rf /app/chrome-user-data/SingletonLock /app/chrome-user-data/Singleton* && /ms-playwright/chromium-1187/chrome-linux/chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-gpu --no-sandbox --user-data-dir=/app/chrome-user-data --display=:99 ${TARGET_URL:-about:blank}"\n\
priority=25\n\
autorestart=true\n\
environment=DISPLAY=:99\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/chromium.log\n\
stderr_logfile=/var/log/supervisor/chromium.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:novnc]\n\
command=bash -c "sleep 5 && /opt/noVNC/utils/novnc_proxy --vnc localhost:5901 --listen 8080 --web /opt/noVNC"\n\
priority=30\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/novnc.log\n\
stderr_logfile=/var/log/supervisor/novnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3' > /etc/supervisor/conf.d/supervisord.conf

WORKDIR /app
ENV DISPLAY=:99
ENV TARGET_URL=about:blank
EXPOSE 8080
EXPOSE 9222

USER playwright
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

几个关键点说明:

  • 通过supervisor来管理多个进程
  • 使用xvfb来创建虚拟显示器
  • 使用x11vnc来创建vnc服务
  • 使用novnc来创建web端的vnc访问
  • 这其中因为chrome已无法监听0.0.0.0地址,所以使用socat做了一个地址转发
  • chrome的用户数据目录挂载在/app/chrome-user-data,方便持久化,由于每次启动时chrome会创建SingletonLock文件,所以每次启动前需要删除

构建并运行容器

创建一个脚本run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/zsh

# 构建 Docker 镜像
echo "构建 Docker 镜像..."
docker build -t playwright-env .

# 运行 Docker 容器
echo "启动 Docker 容器..."
docker run -d \
--name playwright-env \
-p 8080:8080 \
-p 9222:9222 \
-e DISPLAY=:99 \
-e VNC_PASSWORD=playwright \
-v ./workspace:/app \
--shm-size=2g \
-it \
--restart=unless-stopped \
playwright-env

echo "容器已启动,名称: playwright-env"
echo "端口映射: 8080:8080, 9222:9222"

还有个使用tigervnc替代xvfb+x11vnc的方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
FROM mcr.microsoft.com/playwright:v1.55.0-noble

# Install system packages (cached layer - rarely changes)
RUN apt-get update && apt-get install -y \
tigervnc-standalone-server \
tigervnc-common \
fluxbox \
supervisor \
python3-pip \
git \
net-tools \
socat \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*

# Install Python dependencies (separate layer for better caching)
RUN pip3 install --break-system-packages websockify

# Download external resources (separate layer)
RUN git clone https://github.com/novnc/noVNC.git /opt/noVNC \
&& ln -sf /opt/noVNC/vnc.html /opt/noVNC/index.html

# Create user and directories (last - avoid cache invalidation)
RUN groupadd -r playwright && useradd -r -g playwright -G audio,video playwright \
&& mkdir -p /home/playwright/.vnc /app/chrome-user-data /var/log/supervisor \
&& chown -R playwright:playwright /home/playwright /app /var/log/supervisor

# 创建VNC初始化脚本
# 这个脚本会在容器启动时执行,根据环境变量动态生成密码文件
RUN echo '#!/bin/bash\n\
set -e\n\
\n\
# 从环境变量获取密码,如果未设置则使用默认密码\n\
VNC_PASSWORD=${VNC_PASSWORD:-wqf12345}\n\
\n\
echo "Initializing VNC password..."\n\
\n\
# 生成VNC密码文件\n\
# 使用vncpasswd -f命令将明文密码转换为VNC加密格式\n\
echo "${VNC_PASSWORD}" | vncpasswd -f > /home/playwright/.vnc/passwd\n\
\n\
# 设置正确的权限和所有者\n\
# 600权限确保只有所有者可以读写,提高安全性\n\
chmod 600 /home/playwright/.vnc/passwd\n\
\n\
echo "VNC password initialized successfully"\n\
\n\
# 启动supervisord\n\
# exec确保supervisord成为PID 1,可以正确接收信号\n\
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf\n\
' > /docker-entrypoint.sh \
&& chmod +x /docker-entrypoint.sh

# Supervisor配置
# 注意:Xvnc启动时密码文件已经由entrypoint脚本创建好了
RUN echo '[supervisord]\n\
nodaemon=true\n\
user=playwright\n\
logfile=/var/log/supervisor/supervisord.log\n\
logfile_maxbytes=10MB\n\
logfile_backups=3\n\
\n\
[program:xvnc]\n\
command=Xvnc :99 -geometry 1920x1080 -depth 24 -rfbport 5901 -SecurityTypes VncAuth -PasswordFile /home/playwright/.vnc/passwd -AlwaysShared -localhost no\n\
priority=10\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/xvnc.log\n\
stderr_logfile=/var/log/supervisor/xvnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:socat]\n\
command=socat TCP-LISTEN:9222,fork,reuseaddr TCP6:[::1]:9222\n\
priority=15\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/socat.log\n\
stderr_logfile=/var/log/supervisor/socat.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:fluxbox]\n\
command=bash -c "sleep 2 && fluxbox"\n\
priority=20\n\
environment=DISPLAY=:99\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/fluxbox.log\n\
stderr_logfile=/var/log/supervisor/fluxbox.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:chromium]\n\
command=bash -c "sleep 5 && rm -rf /app/chrome-user-data/SingletonLock /app/chrome-user-data/Singleton* && /ms-playwright/chromium-1187/chrome-linux/chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-gpu --no-sandbox --user-data-dir=/app/chrome-user-data --display=:99 ${TARGET_URL:-about:blank}"\n\
priority=25\n\
autorestart=true\n\
environment=DISPLAY=:99\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/chromium.log\n\
stderr_logfile=/var/log/supervisor/chromium.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:novnc]\n\
command=bash -c "sleep 3 && /opt/noVNC/utils/novnc_proxy --vnc localhost:5901 --listen 8080 --web /opt/noVNC"\n\
priority=30\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/novnc.log\n\
stderr_logfile=/var/log/supervisor/novnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3' > /etc/supervisor/conf.d/supervisord.conf

WORKDIR /app
ENV DISPLAY=:99
ENV TARGET_URL=about:blank
EXPOSE 8080
EXPOSE 9222

USER playwright
# 使用自定义的entrypoint脚本替代直接启动supervisord
# 这样可以在启动前执行密码初始化
CMD ["/docker-entrypoint.sh"]

构建并运行容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/zsh

# 构建 Docker 镜像
echo "构建 Docker 镜像..."
docker build -t playwright-tigervnc .

# 若存在同名容器则先强制删除
if [ -n "$(docker ps -aq -f name=^playwright-tigervnc$)" ]; then
echo "检测到已有同名容器,正在删除..."
docker rm -f playwright-tigervnc
fi

# 运行 Docker 容器
echo "启动 Docker 容器..."
docker run -d \
--name playwright-tigervnc \
-p 8080:8080 \
-p 9222:9222 \
-e VNC_PASSWORD=password \
-v ./workspace:/app \
--shm-size=2g \
-it \
--restart=unless-stopped \
playwright-tigervnc

echo "容器已启动,名称: playwright-tigervnc"
echo "端口映射: 8080:8080, 9222:9222"

使用chrome

创建dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# =========================================================
# Base: Playwright (Ubuntu 24.04 Noble) + Google Chrome
# 中文字体 + Xvfb + VNC/noVNC + fluxbox + supervisord
# =========================================================
FROM mcr.microsoft.com/playwright:v1.55.0-noble

ENV DEBIAN_FRONTEND=noninteractive

# 1) 系统依赖(较稳定,单独层缓存)
RUN apt-get update && apt-get install -y \
xvfb x11vnc socat fluxbox supervisor \
python3-pip git net-tools \
locales wget gpg \
--no-install-recommends && \
rm -rf /var/lib/apt/lists/*

# 2) Python 依赖(websockify 给 noVNC 用)
RUN pip3 install --break-system-packages websockify

# 3) 安装 Google Chrome(正确导入 keyring:先 dearmor 再引用)
RUN set -eux; \
install -d -m 0755 /etc/apt/keyrings; \
curl -fsSL https://dl.google.com/linux/linux_signing_key.pub \
| gpg --dearmor -o /etc/apt/keyrings/google-linux-signing.gpg; \
chmod 0644 /etc/apt/keyrings/google-linux-signing.gpg; \
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-linux-signing.gpg] http://dl.google.com/linux/chrome/deb/ stable main" \
> /etc/apt/sources.list.d/google-chrome.list; \
apt-get update; \
apt-get install -y --no-install-recommends google-chrome-stable; \
rm -rf /var/lib/apt/lists/*

# 4) 中文字体 + 生成中文/英文 UTF-8 本地化环境
RUN apt-get update && apt-get install -y \
fonts-wqy-zenhei fonts-wqy-microhei fonts-noto-cjk \
&& sed -i 's/^# *\(zh_CN.UTF-8 UTF-8\)/\1/' /etc/locale.gen \
&& sed -i 's/^# *\(en_US.UTF-8 UTF-8\)/\1/' /etc/locale.gen \
&& locale-gen \
&& update-locale LANG=zh_CN.UTF-8 \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=zh_CN.UTF-8 \
LC_ALL=zh_CN.UTF-8

# 5) 下载 noVNC
RUN git clone https://github.com/novnc/noVNC.git /opt/noVNC

# 6) 创建运行用户与目录
RUN groupadd -r playwright && useradd -r -g playwright -G audio,video playwright && \
mkdir -p /home/playwright/.vnc /app/chrome-user-data /var/log/supervisor && \
chown -R playwright:playwright /home/playwright /app /var/log/supervisor

# 8) supervisord 配置
# - Xvfb: :99 虚拟显示
# - fluxbox: 轻量窗口管理器
# - x11vnc: 使用 -rfbauth,运行前若检测到环境变量 VNC_PASSWORD 则动态更新密码文件
# - noVNC: 监听 8080,代理至 localhost:5901
# - Google Chrome: 开启远程调试端口 9222,绑定 0.0.0.0 方便容器外访问

RUN echo '[supervisord]\n\
nodaemon=true\n\
user=playwright\n\
logfile=/var/log/supervisor/supervisord.log\n\
logfile_maxbytes=10MB\n\
logfile_backups=3\n\
\n\
[program:xvfb]\n\
command=Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset\n\
priority=10\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/xvfb.log\n\
stderr_logfile=/var/log/supervisor/xvfb.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:socat]\n\
command=socat TCP-LISTEN:9222,fork,reuseaddr TCP6:[::1]:9222\n\
priority=15\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/socat.log\n\
stderr_logfile=/var/log/supervisor/socat.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:fluxbox]\n\
command=bash -c "sleep 2 && fluxbox"\n\
priority=20\n\
environment=DISPLAY=:99\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/fluxbox.log\n\
stderr_logfile=/var/log/supervisor/fluxbox.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:x11vnc]\n\
command=bash -c "sleep 3 && x11vnc -display :99 -forever -noxdamage -shared -rfbport 5901 -passwd wqf12345"\n\
priority=20\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/x11vnc.log\n\
stderr_logfile=/var/log/supervisor/x11vnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:chromium]\n\
command=bash -c "sleep 8 && rm -rf /app/chrome-user-data/SingletonLock /app/chrome-user-data/Singleton* && google-chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-gpu --no-sandbox --user-data-dir=/app/chrome-user-data --display=:99 ${TARGET_URL:-about:blank}"\n\
priority=25\n\
autorestart=true\n\
environment=DISPLAY=:99\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/chromium.log\n\
stderr_logfile=/var/log/supervisor/chromium.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3\n\
\n\
[program:novnc]\n\
command=bash -c "sleep 5 && /opt/noVNC/utils/novnc_proxy --vnc localhost:5901 --listen 8080 --web /opt/noVNC"\n\
priority=30\n\
autorestart=true\n\
user=playwright\n\
stdout_logfile=/var/log/supervisor/novnc.log\n\
stderr_logfile=/var/log/supervisor/novnc.log\n\
stdout_logfile_maxbytes=10MB\n\
stdout_logfile_backups=3' > /etc/supervisor/conf.d/supervisord.conf

WORKDIR /app
ENV DISPLAY=:99
ENV TARGET_URL=about:blank

EXPOSE 8080
EXPOSE 9222

USER playwright
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

构建并运行容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

# 构建 Docker 镜像
echo "构建 Docker 镜像..."
docker build -t pw-chrome-cn .

mkdir -p ./workspace
chmod 777 ./workspace

# 运行 Docker 容器
echo "启动 Docker 容器..."
docker run -d --rm \
--name pw-chrome-cn \
-p 8080:8080 \
-p 9222:9222 \
-e DISPLAY=:99 \
-v ./workspace:/app \
--shm-size=2g \
-it \
pw-chrome-cn

echo "容器已启动,名称: pw-chrome-cn"
echo "端口映射: 8080:8080, 9222:9222"