QuickJS 嵌入式 JavaScript 引擎完全教程 / 09 - Docker 使用
Docker 使用
9.1 Docker 基础环境
基于 Debian 的 QuickJS 镜像
# Dockerfile — 基础 QuickJS 开发镜像
FROM debian:bookworm-slim AS builder
# 安装编译依赖
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gcc \
make \
git \
ca-certificates \
xz-utils \
&& rm -rf /var/lib/apt/lists/*
# 获取 QuickJS 源码
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git
# 编译
WORKDIR /build/quickjs
RUN make -j$(nproc) && \
strip qjs qjsc
# === 最终镜像 ===
FROM debian:bookworm-slim
# 仅复制需要的二进制文件
COPY --from=builder /build/quickjs/qjs /usr/local/bin/qjs
COPY --from=builder /build/quickjs/qjsc /usr/local/bin/qjsc
# 设置工作目录
WORKDIR /app
# 默认运行 REPL
CMD ["qjs"]
构建与运行
# 构建镜像
docker build -t quickjs .
# 运行 REPL
docker run -it --rm quickjs
# 执行脚本(挂载当前目录)
docker run -it --rm -v $(pwd):/app quickjs qjs script.js
# 执行内联代码
docker run --rm quickjs qjs -e 'console.log("Hello from Docker!")'
9.2 多阶段构建
最小化生产镜像
# Dockerfile.prod — 最小化生产镜像
FROM alpine:3.19 AS builder
RUN apk add --no-cache \
gcc \
musl-dev \
make \
git
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git
WORKDIR /build/quickjs
RUN make -j$(nproc) qjs
# === 最终镜像(Alpine 最小化) ===
FROM alpine:3.19
RUN apk add --no-cache libstdc++
COPY --from=builder /build/quickjs/qjs /usr/local/bin/qjs
# 复制应用脚本
COPY scripts/ /app/scripts/
WORKDIR /app
ENTRYPOINT ["qjs"]
CMD ["/app/scripts/main.js"]
镜像大小对比:
| 基础镜像 | 大小 | 说明 |
|---|
debian:bookworm-slim | ~85MB | 标准选择 |
alpine:3.19 | ~12MB | 最小化 |
scratch + 静态编译 | ~2MB | 极致最小 |
Scratch 镜像(静态编译)
# Dockerfile.scratch — 纯静态编译的最小镜像
FROM gcc:13 AS builder
WORKDIR /build
COPY . .
# 静态编译 QuickJS
RUN make clean && \
CFLAGS="-static -O2" LDFLAGS="-static" make -j$(nproc) qjs && \
strip qjs
# === 最终镜像(scratch,约 2MB) ===
FROM scratch
COPY --from=builder /build/qjs /qjs
# scratch 没有 shell,必须用 exec 格式
ENTRYPOINT ["/qjs"]
9.3 嵌入式开发工作流
交叉编译
# Dockerfile.arm64 — ARM64 交叉编译
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y \
gcc-aarch64-linux-gnu \
make \
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git
WORKDIR /build/quickjs
# 交叉编译
ENV CC=aarch64-linux-gnu-gcc
RUN make clean && \
CFLAGS="-static -O2" LDFLAGS="-static" make -j$(nproc) qjs && \
aarch64-linux-gnu-strip qjs
# 验证文件架构
RUN file qjs
# 输出: qjs: ELF 64-bit LSB executable, ARM aarch64
多架构构建脚本
#!/bin/bash
# build-multiarch.sh — 构建多架构 QuickJS 镜像
ARCHS=("amd64" "arm64" "arm/v7")
for arch in "${ARCHS[@]}"; do
echo "Building for $arch..."
docker buildx build \
--platform "linux/$arch" \
-t "quickjs:latest-$arch" \
--load \
-f Dockerfile.multiarch .
done
# 创建 manifest
docker manifest create quickjs:latest \
quickjs:latest-amd64 \
quickjs:latest-arm64 \
quickjs:latest-arm-v7
docker manifest push quickjs:latest
9.4 测试环境
CI/CD 测试镜像
# Dockerfile.ci — CI 测试环境
FROM debian:bookworm-slim
RUN apt-get update && \
apt-get install -y \
gcc \
make \
git \
nodejs \
npm \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git
WORKDIR /build/quickjs
RUN make -j$(nproc)
# 运行 QuickJS 内置测试
RUN make test
# 可选:运行 Test262(需要较长时间)
# RUN make test262
GitHub Actions 配置
# .github/workflows/quickjs-test.yml
name: QuickJS CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build QuickJS
run: |
git clone --depth 1 https://github.com/nicoretti/quickjs.git
cd quickjs
make -j$(nproc)
- name: Run unit tests
working-directory: quickjs
run: make test
- name: Run custom scripts
run: |
./quickjs/qjs test_script.js
Docker Compose 测试环境
# docker-compose.test.yml
version: '3.8'
services:
# QuickJS 应用
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./scripts:/app/scripts
command: qjs /app/scripts/main.js
# 测试运行器
tests:
build:
context: .
dockerfile: Dockerfile.test
volumes:
- ./tests:/app/tests
command: qjs /app/tests/run_tests.js
depends_on:
- app
9.5 脚本运行器
通用脚本运行器镜像
# Dockerfile.runner — 通用 JavaScript 脚本运行器
FROM alpine:3.19 AS builder
RUN apk add --no-cache gcc musl-dev make git
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git && \
cd quickjs && make -j$(nproc) qjs
FROM alpine:3.19
COPY --from=builder /build/quickjs/qjs /usr/local/bin/qjs
# 设置入口点为脚本运行器
ENTRYPOINT ["qjs"]
# 默认显示帮助
CMD ["--help"]
# 使用方式
# 运行脚本文件
docker run --rm -v $(pwd):/app -w /app quickjs-runner qjs script.js
# 运行内联代码
docker run --rm quickjs-runner qjs -e 'console.log(42)'
# 管道处理
echo 'console.log("piped")' | docker run --rm -i quickjs-runner qjs
# 交互式 REPL
docker run -it --rm quickjs-runner qjs
npm 风格的脚本执行
{
"scripts": {
"start": "docker run --rm -v $(pwd):/app quickjs-runner qjs /app/src/main.js",
"test": "docker run --rm -v $(pwd):/app quickjs-runner qjs /app/tests/all.js",
"build": "docker run --rm -v $(pwd):/app quickjs-runner qjsc -o /app/dist/app.qjsc /app/src/main.js",
"lint": "echo 'No linter available for QuickJS yet'"
}
}
9.6 微服务部署
QuickJS 微服务容器
# Dockerfile.microservice — QuickJS 微服务
FROM alpine:3.19 AS builder
RUN apk add --no-cache gcc musl-dev make git
WORKDIR /build
RUN git clone --depth 1 https://github.com/nicoretti/quickjs.git
# 添加自定义 HTTP 服务扩展
COPY native/ /build/quickjs/native/
WORKDIR /build/quickjs
RUN make -j$(nproc) qjs
FROM alpine:3.19
RUN apk add --no-cache libstdc++
COPY --from=builder /build/quickjs/qjs /usr/local/bin/qjs
COPY service/ /app/service/
WORKDIR /app
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:8080/health || exit 1
EXPOSE 8080
CMD ["qjs", "/app/service/server.js"]
9.7 Docker 网络与安全
安全容器配置
# docker-compose.secure.yml
version: '3.8'
services:
quickjs-app:
image: quickjs:latest
# 安全配置
read_only: true
tmpfs:
- /tmp:size=10M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
resources:
limits:
memory: 128M
cpus: '0.5'
networks:
- internal
networks:
internal:
driver: bridge
网络隔离
# 不同服务间的网络隔离
version: '3.8'
services:
# 公开 API
api-gateway:
image: quickjs-api:latest
ports:
- "8080:8080"
networks:
- frontend
- backend
# 内部脚本处理器(无外部网络访问)
script-worker:
image: quickjs-worker:latest
networks:
- backend # 仅内部网络
# 不暴露端口
networks:
frontend:
backend:
internal: true # 无外部访问
9.8 日志与监控
结构化日志
// logger.js — Docker 环境的结构化日志
const LOG_LEVELS = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
class Logger {
#level;
#service;
constructor(service, level = "INFO") {
this.#service = service;
this.#level = LOG_LEVELS[level] || 1;
}
#log(level, message, data = null) {
if (LOG_LEVELS[level] < this.#level) return;
const entry = {
timestamp: new Date().toISOString(),
level,
service: this.#service,
message,
...(data && { data })
};
// Docker 日志收集器通常读取 stdout/stderr
if (level === "ERROR") {
console.error(JSON.stringify(entry));
} else {
console.log(JSON.stringify(entry));
}
}
debug(msg, data) { this.#log("DEBUG", msg, data); }
info(msg, data) { this.#log("INFO", msg, data); }
warn(msg, data) { this.#log("WARN", msg, data); }
error(msg, data) { this.#log("ERROR", msg, data); }
}
export default Logger;
9.9 本章小结
| 要点 | 说明 |
|---|
| 基础镜像 | 使用多阶段构建最小化镜像(Alpine ~12MB) |
| 静态编译 | scratch 镜像 + 静态编译可至 ~2MB |
| 交叉编译 | 通过 Docker 实现 ARM/MIPS 等架构编译 |
| CI/CD | Docker 化的 QuickJS 测试环境 |
| 安全配置 | read_only、no-new-privileges、资源限制 |
| 日志 | 结构化 JSON 日志,便于 Docker 日志收集 |
扩展阅读