强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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/CDDocker 化的 QuickJS 测试环境
安全配置read_onlyno-new-privileges、资源限制
日志结构化 JSON 日志,便于 Docker 日志收集

扩展阅读