创建高级前端扩展

要开始创建您的扩展程序,您首先需要一个包含相关文件的目录,这些文件范围从扩展程序的源代码到必需的扩展程序专用文件。本页提供有关如何设置具有更高级前端的扩展程序的信息。

开始之前,请确保您已安装最新版本的 Docker Desktop

扩展文件夹结构

创建新扩展的最快方式是运行 docker extension init my-extension,详见 快速入门。这将创建一个名为 my-extension 的新目录,其中包含一个功能完整的扩展。

提示

docker extension init 会生成一个基于 React 的扩展。但您仍可将其作为自己扩展的起点,并使用任何其他前端框架,如 Vue、Angular、Svelte 等,甚至可以继续使用原生 JavaScript。

尽管您可以从空目录或 react-extension 示例文件夹开始, 但强烈建议您从 docker extension init 命令开始,并根据您的需求进行修改。

.
├── Dockerfile # (1)
├── ui # (2)
│   ├── public # (3)
│   │   └── index.html
│   ├── src # (4)
│   │   ├── App.tsx
│   │   ├── index.tsx
│   ├── package.json
│   └── package-lock.lock
│   ├── tsconfig.json
├── docker.svg # (5)
└── metadata.json # (6)
  1. 包含构建扩展并使其在 Docker Desktop 中运行所需的一切。
  2. 包含前端应用程序源代码的顶层文件夹。
  3. 未编译或动态生成的资源将存储在此处。这些资源可以是静态资源,如 logo 图标或 robots.txt 文件。
  4. src(源文件夹)包含所有 React 组件、外部 CSS 文件以及动态资源,这些资源会被引入到组件文件中。
  5. Docker Desktop 仪表板左侧菜单中显示的图标。
  6. 提供有关扩展(例如名称、描述和版本)信息的文件。

调整 Dockerfile

注意

使用 docker extension init 时,将创建一个已包含 React 插件所需内容的 Dockerfile

一旦扩展创建完成,您需要配置 Dockerfile 以构建该扩展,并设置用于在 Marketplace 中填充扩展卡片的标签。以下是一个 React 扩展的 Dockerfile 示例:


# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM node:18.9-alpine3.15 AS client-builder
WORKDIR /ui
# cache packages in layer
COPY ui/package.json /ui/package.json
COPY ui/package-lock.json /ui/package-lock.json
RUN --mount=type=cache,target=/usr/src/app/.npm \
    npm set cache /usr/src/app/.npm && \
    npm ci
# install
COPY ui /ui
RUN npm run build

FROM alpine
LABEL org.opencontainers.image.title="My extension" \
    org.opencontainers.image.description="Your Desktop Extension Description" \
    org.opencontainers.image.vendor="Awesome Inc." \
    com.docker.desktop.extension.api.version="0.3.3" \
    com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png" \
    com.docker.extension.screenshots="" \
    com.docker.extension.detailed-description="" \
    com.docker.extension.publisher-url="" \
    com.docker.extension.additional-urls="" \
    com.docker.extension.changelog=""

COPY metadata.json .
COPY docker.svg .
COPY --from=client-builder /ui/build ui

注意

在示例 Dockerfile 中,您可以看到镜像标签 com.docker.desktop.extension.icon 被设置为一个图标 URL。扩展市场在未安装扩展的情况下显示此图标。Dockerfile 还包含 COPY docker.svg . 以将图标文件复制到镜像内部。安装扩展后,第二个图标文件用于在仪表板中显示扩展 UI。

重要

我们还没有针对 Vue 的可用 Dockerfile。 填写表单 并告知我们您是否需要针对 Vue 的 Dockerfile。

重要

我们还没有可用的 Angular Dockerfile。 填写表单 并告诉我们您是否需要 Angular 的 Dockerfile。

重要

我们还没有适用于 Svelte 的可用 Dockerfile。 填写表单 并告诉我们您是否需要 Svelte 的 Dockerfile。


配置元数据文件

为了在 Docker Desktop 中为您的扩展添加选项卡,您必须在扩展目录根目录下的 metadata.json 文件中对其进行配置。

{
  "icon": "docker.svg",
  "ui": {
    "dashboard-tab": {
      "title": "UI Extension",
      "root": "/ui",
      "src": "index.html"
    }
  }
}

title 属性是在 Docker Desktop Dashboard 左侧菜单中显示的扩展名称。 root 属性是扩展容器文件系统中前端应用程序的路径,系统使用该路径将其部署在主机上。 src 属性是 root 文件夹中前端应用程序 HTML 入口点的路径。

有关 ui 部分的 metadata.json 的更多信息,请参见 元数据

构建扩展并安装它

既然您已经配置了扩展,就需要构建 Docker Desktop 将用来安装它的扩展镜像。

docker build --tag=awesome-inc/my-extension:latest .

这构建了一个标签为 awesome-inc/my-extension:latest 的镜像,你可以运行 docker inspect awesome-inc/my-extension:latest 来查看更多关于它的详细信息。

最后,您可以安装该扩展,并查看它出现在 Docker Desktop 仪表板中。

docker extension install awesome-inc/my-extension:latest

使用扩展 API 客户端

要使用扩展 API 并通过 Docker Desktop 执行操作,扩展必须首先导入 @docker/extension-api-client 库。要安装它,请运行以下命令:

npm install @docker/extension-api-client

然后调用 createDockerDesktopClient 函数创建客户端对象以调用扩展 API。

import { createDockerDesktopClient } from '@docker/extension-api-client';

const ddClient = createDockerDesktopClient();

在使用 Typescript 时,您还可以将 @docker/extension-api-client-types 作为开发依赖项安装。这将为您提供扩展 API 的类型定义以及 IDE 中的自动补全功能。

npm install @docker/extension-api-client-types --save-dev
Auto completion in an IDE

例如,您可以使用 docker.cli.exec 函数通过 docker ps --all 命令获取所有容器的列表,并将结果显示在表格中。


用以下代码替换 ui/src/App.tsx 文件:


// ui/src/App.tsx
import React, { useEffect } from 'react';
import {
  Paper,
  Stack,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Typography
} from "@mui/material";
import { createDockerDesktopClient } from "@docker/extension-api-client";

//obtain docker desktop extension client
const ddClient = createDockerDesktopClient();

export function App() {
  const [containers, setContainers] = React.useState<any[]>([]);

  useEffect(() => {
    // List all containers
    ddClient.docker.cli.exec('ps', ['--all', '--format', '"{{json .}}"']).then((result) => {
      // result.parseJsonLines() parses the output of the command into an array of objects
      setContainers(result.parseJsonLines());
    });
  }, []);

  return (
    <Stack>
      <Typography data-testid="heading" variant="h3" role="title">
        Container list
      </Typography>
      <Typography
      data-testid="subheading"
      variant="body1"
      color="text.secondary"
      sx={{ mt: 2 }}
    >
      Simple list of containers using Docker Extensions SDK.
      </Typography>
      <TableContainer sx={{mt:2}}>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>Container id</TableCell>
              <TableCell>Image</TableCell>
              <TableCell>Command</TableCell>
              <TableCell>Created</TableCell>
              <TableCell>Status</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {containers.map((container) => (
              <TableRow
                key={container.ID}
                sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
              >
                <TableCell>{container.ID}</TableCell>
                <TableCell>{container.Image}</TableCell>
                <TableCell>{container.Command}</TableCell>
                <TableCell>{container.CreatedAt}</TableCell>
                <TableCell>{container.Status}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
    </Stack>
  );
}
Screenshot of the container list.

重要

我们目前还没有 Vue 的示例。 填写表单 并告诉我们您是否希望我们提供一个 Vue 示例。

重要

我们暂无 Angular 的示例。 填写表单 并告知我们您是否希望提供 Angular 示例。

重要

我们尚未提供 Svelte 的示例。 填写表单 并告知我们您是否需要 Svelte 的示例。


前端代码实施的策略

扩展 UI 代码在单独的 electron 会话中渲染,没有初始化 node.js 环境,也无法直接访问 electron API。

这是为了限制对 Docker 仪表盘整体可能产生的意外副作用。

扩展 UI 代码无法执行特权任务,例如更改系统或生成子进程,除非使用扩展框架提供的 SDK API。 扩展 UI 代码还可以与 Docker Desktop 进行交互,例如导航到仪表板中的各个位置,但只能通过扩展 SDK API 进行。

扩展 UI 部分相互隔离,扩展 UI 代码在每个扩展的独立会话中运行。扩展无法访问其他扩展的会话数据。

localStorage 是浏览器 Web 存储的机制之一。它允许用户在浏览器中以键值对的形式保存数据,以供后续使用。localStorage 不会在浏览器(扩展窗格)关闭时清除数据。这使得它非常适合在从扩展导航到 Docker Desktop 的其他部分时持久化数据。

如果您的扩展使用 localStorage 存储数据,则在 Docker Desktop 中运行的其他扩展无法访问您扩展的本地存储。即使 Docker Desktop 停止或重新启动,扩展的本地存储也会保留。当扩展升级时,其本地存储会被保留;而当扩展被卸载时,其本地存储将被完全删除。

重新构建扩展并更新它

由于您修改了扩展的代码,因此必须重新构建扩展。

$ docker build --tag=awesome-inc/my-extension:latest .

构建完成后,您需要更新它。

$ docker extension update awesome-inc/my-extension:latest

现在,您可以在 Docker Desktop Dashboard 的容器选项卡中看到后端服务正在运行,并在需要调试时查看日志。

提示

您可以开启 热重载以避免每次更改后都需要 重新构建扩展。

接下来是什么?