创建一个高级前端扩展

要开始创建扩展,首先需要一个包含从扩展源代码到所需扩展特定文件的目录。本页提供有关如何设置具有更高级前端的扩展的信息。

在开始之前,请确保您已安装最新版本的 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. 未编译或动态生成的资产存储在此处。这些可以是静态资产,如徽标或 robots.txt 文件。
  4. src 或 source 文件夹包含所有 React 组件、外部 CSS 文件以及引入组件文件中的动态资产。
  5. 显示在 Docker Desktop 仪表板左侧菜单中的图标。
  6. 提供有关扩展信息的文件,例如名称、描述和版本。

修改 Dockerfile

注意

使用 docker extension init 命令时,它会创建一个 Dockerfile,其中已包含 React 扩展所需的内容。

创建扩展后,您需要配置 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://docker.net.cn/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。Extensions Marketplace 无需安装扩展即可显示此图标。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 仪表板左侧菜单中的扩展名称。root 属性是前端应用在扩展容器文件系统中的路径,系统会使用此路径将其部署到主机上。src 属性是前端应用 HTML 入口文件在 root 文件夹内的路径。

有关 metadata.json 文件中 ui 部分的更多信息,请参阅元数据

构建扩展并安装

现在您已经配置好了扩展,需要构建 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

使用 Extension API 客户端

要使用 Extension API 并与 Docker Desktop 交互,扩展必须首先导入 @docker/extension-api-client 库。要安装它,运行以下命令:

npm install @docker/extension-api-client

然后调用 createDockerDesktopClient 函数创建一个客户端对象来调用 Extension API。

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

const ddClient = createDockerDesktopClient();

使用 Typescript 时,您还可以将 @docker/extension-api-client-types 作为开发依赖 (dev dependency) 安装。这将为您提供 Extension 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 Dashboard 造成意外的副作用。

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

扩展的 UI 部分彼此隔离,并且每个扩展的 UI 代码都在其自己的会话中运行。扩展无法访问其他扩展的会话数据。

localStorage 是浏览器 Web Storage 的机制之一。它允许用户在浏览器中以键值对的形式保存数据以供以后使用。当浏览器(扩展面板)关闭时,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 的容器选项卡中看到后端服务正在运行,并在需要调试时查看日志。

提示

您可以开启热重载,以免每次进行更改时都需要重新构建扩展。

下一步?

页面选项