# 基础镜像 (oakapp-base)

基础镜像是一个最小的 Docker 镜像，为运行复杂的 OAK4 应用提供必要的环境。它包含 Python 3.12 和其他必需的库，允许开发者在 OAK4 相机上构建和部署应用。基础镜像的关键组件包括：

 * nginx —— 用于提供静态文件或处理应用前端请求的代理。它同时暴露 DAI WebSocket 和前端在同一端口上。这对于通过 IP 地址访问尤其重要。编码流仅可通过 HTTPS 获取，而为设备通过 IP 访问启用 HTTPS
   的唯一方式是使用自签名证书。浏览器每个 IP 只允许一个自签名证书，因此 nginx 为前端和 DAI WebSocket 创建反向代理，它们内部运行在不同端口，但必须通过单个端口公开暴露并附带一个证书。
 * oak_webrtc —— 用于 WebRTC 通信的工具，通过 *.luxonis.app 域名提供对应用的远程访问。它使用 WebRTC
   在应用与查看器之间建立连接，实现视频和其他数据的实时流传输。它可以在本地网络中直接连接，当网络容量允许时，应能处理高达 400 Mbps 的吞吐量。通过 WebSocket 的通信受浏览器性能和网络带宽限制，但可以无问题地处理 1 Gbps+。
 * python 3.12 —— Python 用于应用的后端，使用 DAI v3。请注意，DAI
   本身安装在应用中；需求文件的示例如下：[requirements.txt](https://github.com/luxonis/oak-examples/blob/main/custom-frontend/open-vocabulary-object-detection/backend/src/requirements.txt)。

# 示例

## 默认应用

你可以在以下定义中查看：[oakapp.toml](https://github.com/luxonis/oak-examples/blob/main/apps/default-app/oakapp.toml)。关键部分如下：

```toml
identifier = "com.example.default"
entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"]
app_version = "1.0.0"
assign_frontend_port = true
```

 * identifier —— 应用的名称，采用 Java 类似的格式（以点作为分隔符）。请注意，标识符在 Hub 中必须唯一，因此建议使用你的团队名称作为前缀。
 * entrypoint —— 字符串数组（argv 格式的命令）。这是应用启动时执行的命令。在此例中，它运行服务管理器（runit 监控器），负责运行所有默认服务以及使用基础镜像的应用中用户自定义的服务。它首先启动内部 nginx，处理 HTTPS
   和端口暴露。然后运行 ak_webrtc 工具，通过 WebRTC 管理远程访问。最后，执行 /etc/service/ 文件夹中定义的任何脚本。在我们的案例中，即文件 /etc/service/backend/run，其中包括 exec
   python3.12 /app/main.py 以启动应用的后端。你可以根据需要为应用添加更多服务。
 * app_version —— 应用的版本。建议使用语义化版本。
 * assign_frontend_port —— 如果为 true，应用将被分配一个前端端口，内部 nginx 服务器将使用该端口提供前端文件。如果为 false，应用不会获得前端端口分配，也无法在标准端口上提供任何前端文件。运行在 OAK4
   上的代理负责端口分配，每个应用被分配一个唯一端口，因此无需在 oakapp.toml 文件中指定端口。

```toml
prepare_container = [
    { type = "COPY", source = "./requirements.txt", target = "./requirements.txt" },
    { type = "RUN", command = "python3.12 -m pip install -r /app/requirements.txt --break-system-packages"}
]
```

此部分定义准备容器的步骤，用于应用。它包括将 requirements.txt 文件复制到容器中，并使用 pip 安装所需的 Python 包。这确保了应用启动时所有必要的依赖都可用。此外，它保证依赖只安装一次，或者在执行 oakctl app run
命令时按需安装。

```toml
build_steps = [
    "mkdir -p /etc/service/backend",
    "cp /app/backend-run.sh /etc/service/backend/run",
    "chmod +x /etc/service/backend/run",
]
```

此部分注册了应用启动时将运行的后端服务。它创建服务的目录，将 backend-run.sh 脚本复制到适当位置，并设置必要的权限使其可执行。该脚本负责启动应用的后端。你可以根据需要为应用添加更多服务。

```toml
[base_image]
api_url = "https://registry-1.docker.io"
service = "registry.docker.io"
oauth_url = "https://auth.docker.io/token"
auth_type = "repository"
auth_name = "luxonis/oakapp-base"
image_name = "luxonis/oakapp-base"
image_tag = "1.2.5"
```

你可以使用任何 Docker 镜像（如果要在 OAK4 上运行应用，需支持 ARM64 架构）作为应用的基础镜像，但推荐使用 luxonis/oakapp-base 镜像，因为它专为 OAK4 Apps 设计，并包含了所有必要的依赖项和配置。

另一个重要的部分是文件 [backend-run.sh](https://github.com/luxonis/oak-examples/blob/main/apps/default-app/backend-run.sh)，其中包含一个简单的命令用于启动
Python 后端。它使用 Python 3.12，这是基础镜像的一部分，因此无需再次安装。

## 开放词汇目标检测（Open Vocabulary Object Detection）

这是一个更复杂的示例，使用了所有功能。应用定义位于：[oakapp.toml](https://github.com/luxonis/oak-examples/blob/main/custom-frontend/open-vocabulary-object-detection/oakapp.toml)。与上一个示例的主要区别如下：

```toml
prepare_build_container = [{type = "RUN", command = "bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash && . \"$HOME/.nvm/nvm.sh\" && nvm install 24'"}]
build_steps = [
    "mkdir -p /etc/service/backend",
    "cp /app/backend-run.sh /etc/service/backend/run",
    "chmod +x /etc/service/backend/run",
]

[static_frontend]
dist_path = "./frontend/dist"

[static_frontend.build]
source_path = "./frontend"
steps = ["bash -c 'cd /app/frontend/src && export NODE_OPTIONS=--max-old-space-size=4096 && . $HOME/.nvm/nvm.sh && npm install && npm run build'"]
```

本示例并未使用 DAI 中集成的可视化器（Visualizer），而是使用了基于 React.js 的自定义前端。它使用 nvm 安装 Node.js 版本 24，然后构建前端应用。前端构建在 static_frontend.build 部分执行，使用 npm
安装依赖并构建应用。该构建过程也在设备上执行，因此只需运行 oakctl app run . 即可构建整个应用，包括前端。

 * dist_path – 构建后的前端应用路径；构建过程完成后，构建好的文件位于此处。建议遵循本示例的结构，即一个文件夹存放后端，另一个存放前端。这不是必须的，但有助于保持结构清晰有序。
 * source_path – 前端应用的源代码路径；构建过程开始前，源代码位于此处。
 * steps – 字符串数组，每个字符串代表一个在容器中执行的命令，用于构建前端应用。在本例中，使用 npm 安装依赖并构建应用。设置 NODE_OPTIONS 环境变量以增加 Node.js 的内存限制，有助于避免构建过程中出现内存不足的错误。前端使用
   React.js 库 @luxonis/depthai-viewer-common，该库提供了用于渲染流和注释的高级组件，并通过 WebSocket 实现直接访问、通过 WebRTC 实现通过 *.luxonis.app
   域名的远程访问，从而实现与后端的通信。
