摘要

随着互联网的快速发展，云计算成为一大趋势，而云计算除了带来诸多好处之外，也带来了诸多问题。其中一个显著的问题是应用复杂化，而应用复杂化导致的一个直接问题便是开发环境配置困难，也正因如此，容器化技术兴起，Docker脱颖而出。众多开发人员开始大量使用Docker。然而，Docker官方主要提供的管理接口命令行方式具有学习成本高，使用不便等缺点。因此，做一个具有图形化界面的Docker管理系统是必要的。本文的目标就在于设计并实现一个基于Portainer的具有图形化界面的Docker管理系统。本文首先使用用例图进行了需求分析，然后使用功能层次结构图进行了概要设计，之后根据概要设计进行了详细设计与实现，最后对整个系统进行了必要的测试。

关键词：Docker Portainer 图形用户界面 管理系统

ABSTRACT

With the rapid development of the Internet, cloud computing has become a major trend, but cloud computing has brought many problems in addition to many benefits. One of the obvious problems is the complexity of applications, and a direct problem caused by the complexity is the difficulty of configuring the development environment. It is also because of this, containerization technology rises, Docker stands out. Many developers began to use Docker in large quantities. However, the command interface mainly provided by Docker has the disadvantages of high learning cost and inconvenience. Therefore, it is necessary to make a Docker management system with a graphical interface. The goal of this article is to design and implement a Docker management system with a graphical interface based on Portainer. This article first uses a use case diagram to perform a requirement analysis, then uses a functional hierarchy diagram to perform a summary design, then performs detailed design and implementation based on the summary design, and finally performs the necessary tests on the entire system.

Keywords: docker portainer GUI management system

目录

摘要	1

ABSTRACT	2

第一章 绪论	1

1.1 课题背景及研究意义	1

1.2 国内外研究现状	2

1.3 主要研究内容	3

1.4 论文结构	4

第二章 相关技术	5

2.1 容器化技术	5

2.2 Docker	6

2.2.1 Docker简介	6

2.2.2 Docker API	7

2.3 Portainer	8

2.3.1 Portainer简介	8

2.3.2 Portainer API	8

2.4 JavaFX	9

2.5 本章小结	9

第三章 需求分析	10

3.1 功能需求	10

3.2 非功能需求	12

3.3 本章小结	13

第四章 概要设计	14

4.1 系统架构	14

4.2 功能模块	14

4.3 本章小结	16

第五章 详细设计与实现	17

5.1 详细设计	17

5.1.1 初始化与登录	17

5.1.2 主面板	18

5.1.3 应用模板	20

5.1.4 Registries	21

5.1.5 设置	22

5.1.6 主机组	22

5.1.7 主机标签	23

5.1.8 用户	23

5.1.9 团队	23

5.1.10 主机	24

5.2 实现	32

5.2.1 组件的选择	32

5.2.2 开发环境	33

5.2.3 代码结构	34

5.2.4 Modules依赖	35

5.3 运行效果	36

5.3.1 Home(Endpoints)	36

5.3.2 Host(EndpointInfo)	37

5.3.3 Containers	38

5.3.4 Images	38

5.3.5 Networks	39

5.3.6 Volumes	39

5.4 本章小结	40

第六章 测试	41

6.1 功能测试	41

6.2 非功能测试	42

6.3 本章小结	43

第七章 结论	44

致谢	45

参考文献	46

绪论

本课题源于毕业设计题目。本章重点介绍了Docker管理系统的研究背景和研究意义，分析了Docker管理系统在国内外的研究现状，解释了本课题研究的主要内容，最后说明了本文的行文结构。

课题背景及研究意义

随着互联网的快速发展，容器化技术兴起。在虚拟化技术方面，传统的虚拟机把底层设备资源进行虚拟化，而容器化技术则直接建立在 Linux 内核之上，因此容器技术的资源消耗量远远少于传统的虚拟机。容器（以Docker为例）与传统虚拟机的区别如图 1.1 所示。

图1.1 虚拟机与容器的区别

Docker作为使用容器化技术中的佼佼者，除了具有上述轻量级的特点之外，还具有如下优势：

灵活：即使最复杂的应用程序也可以容器化。

可移植：您可以在本地构建，部署到云并在任何地方运行。

松散耦合：容器是高度自给自足并封装的容器，使您可以在不破坏其他容器的情况下更换或升级它们。

可扩展：您可以在数据中心内增加并自动分发容器副本。

安全：容器将积极的约束和隔离应用于流程，而无需用户方面的任何配置。

因此大量公司及个人开发人员将使用Docker作为应用开发的部署方式，从而在开发、测试、运维三个团队建立起有效的协作。运维人员可在短时间内部署出标准的、满足需求的、稳定的开发测试环境供开发测试人员使用，从而提高公司开发及测试产品的效率，加快产品整体上线速度。

然而，Docker官方主要提供的管理接口为命令行方式。而命令行方式的Docker管理有许多不便：其命令数量庞大、命令功能复杂、需要在每个装有Docker主机上执行、学习成本较高、使用效率较低。总之，CLI较复杂、不直观、多主机管理不便。

而图形化管理系统则可以解决上述问题。除此之外，图形化管理系统还可以和用户角色管理等关联起来，让不同的用户具有不同的管理能力，从而提高安全性和开发效率

因此，设计一个方便可靠、界面统一的Docker管理系统是必要的，它可以简化维护成本，降低学习成本，提高生产效率

国内外研究现状

如今国内外均已有大量的Docker管理系统，其中比较突出的有DockerUI、Shipyard、Rancher、Portainer。下面简要分析下上述各个管理系统的特点。

DockerUI以最小化依赖和一致性为其开发目标。最小化依赖表示尽可能让该项目为纯HTML/JS应用；一致性则强调Web UI应与docker CLI上的命令一致。因此，它支持Docker管理系统应有的基本功能，包括镜像仓库管理、容器管理、镜像管理等。除此之外，它由于自身也是个容器，所以部署方便。它还能动态显示容器之前的关系图。但是，它没有登录验证机制，无法分配某个容器给某个用户，不支持控制台命令，不支持多主机，不支持集群Swarm等。其中没有登录体系是其最大的缺点，这大大限制了它在公司中的应用。此外，它的官方repo已经标记为deprecated，并说明了开发工作已转移到Portainer [1]

Shipyard基于Docker Swarm，它使你能够管理包括容器、镜像、私有注册表在内的Docker资源。Shipyard与其他Docker管理系统的不同之处在于，它提高了可组合性，并且与Docker 远程API 100％兼容。Shipyard在整个集群范围内管理容器，镜像，节点，私有注册表，并提供身份验证和基于角色的访问控制。缺点在于它启动容器较多，占用每个节点的一部分资源。最关键的是，它已于2017-12-05停止开发 [2]

Portainer是一个轻量级的管理UI，它可以让你轻松管理不同的Docker环境（Docker主机或Swarm集群）。Portainer的目标是易于部署和使用。它由一个可以在任何Docker引擎上运行的容器组成（可以部署为Linux容器或Windows本机容器，也支持其他平台）。 Portainer允许您管理所有Docker资源（容器、镜像、卷、网络等）。它与独立的Docker引擎和Docker Swarm模式兼容。此外，它还支持权限分配 [3]

Rancher是供采用容器的团队使用的完整软件堆栈。它解决了管理多个Kubernetes集群的运营和安全挑战，同时为DevOps团队提供了用于运行容器化工作负载的集成工具。Rancher的最大特点在于它是针对Kubernetes的企业级Docker管理系统，因此功能强大。但是也正因如此，它比较复杂，不如Portainer轻量。 [4]

总之，随着Docker的流行，良好的Docker管理系统是必需的。企业也需要一个属于自己的Docker管理系统，或者是基于现有的开源方案构建一个具有企业特色的Docker管理系统

主要研究内容

在Docker资源的管理方面，官方提供了命令行管理方式和相关的API。其中命令行方式的Docker管理具有命令数量庞大、命令功能复杂、需要在每个装有Docker主机上执行、学习成本较高、使用效率较低等缺点，因此迫切需要一个具有图形化界面的Docker管理系统，从而解决上述问题，提高开发效率

本文通过对Docker的学习和分析，站在企业的角度构建了一个Docker管理系统。考虑到该系统的构建成本较高，本文通过调用现有的Docker管理系统Portainer的API来实现，即基于Portainer设计与实现了一个具有企业特色的Docker管理系统

论文结构

第一章为绪论。主要介绍了本课题的背景和研究意义，本课题在国内外的主要研究现状和本课题研究的主要内容。

第二章为相关技术。介绍了本Docker管理系统使用的相关技术：容器化技术、Docker、Portainer和JavaFX编程。其中重点讲解了Docker API和Portainer API及JavaFX编程

第三章为需求分析。对系统的功能需求和非功能需求等几个方面进行分析。

第四章为概要设计。主要介绍了系统的架构设计、功能模块设计两个方面。

第五章为详细设计与实现。主要介绍了系统的开发环境，各个功能模块的详细设计与实现过程。

第六章为测试。主要从功能测试和非功能测试两个方面介绍了本Docker管理系统的测试阶段。

第七章是对本文的总结。

相关技术

本章详细介绍了本Docker管理系统使用的相关技术：容器化技术、Docker、Portainer、JavaFX。容器化技术是当前非常流行的一种操作系统层虚拟化技术；Docker是一个充分利用了容器化技术的开源容器引擎，其相关资源（包括容器、镜像等）是本系统的主要管理对象；Portainer是一个开源的基于Web的Docker管理系统，它包含UI和API两大部分，本系统通过调用它提供的API来实现Docker资源的管理；JavaFX是一个开源的下一代客户端应用程序平台，是本系统的GUI库

容器化技术

容器化是软件开发的一种方法，通过该方法可将应用程序或服务、其依赖项及其配置（抽象化为部署清单文件）一起打包为容器映像。 容器化应用程序可以作为一个单元进行测试，并可以作为容器映像实例部署到主机操作系统 (OS)。

就像船只、火车或卡车运输集装箱而不论其内部的货物一样，软件容器充当软件部署的标准单元，其中可以包含不同的代码和依赖项。 按照这种方式容器化软件，开发人员和 IT 专业人员只需进行极少修改或不修改，即可将其部署到不同的环境。

容器还会在共享 OS 上将应用程序彼此隔离开。 容器化应用程序在容器主机上运行，而容器主机在 OS（Linux 或 Windows）上运行。 因此，容器的占用比虚拟机 (VM) 映像小得多。

每个容器可以运行整个 Web 应用或服务，如图 2-1 所示。 在此示例中，Docker 主机是容器主机，而 App1、App2、Svc 1 和 Svc 2 是容器化应用程序或服务。

图2.1 单主机多容器示例

容器化的另一个优势在于可伸缩性。 通过为短期任务创建新容器，可以快速扩大。 从应用程序的角度来看，实例化映像（创建容器）类似于实例化服务或 Web 应用等进程。 但出于可靠性考虑，在多个主机服务器上运行同一映像的多个实例时，通常要使每个容器（映像实例）在不同容错域中的不同主机服务器或 VM 中运行。

总而言之，容器在整个应用程序生命周期工作流中提供以下优点：隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。 [5]

Docker

Docker简介

大致而言，Docker是一种工具，旨在使用容器化技术更轻松地创建，部署和运行应用程序。容器使开发人员可以将应用程序与所需的所有部分（如库和其他依赖项）打包在一起，从而简化环境搭建这一步骤。Docker将应用程序和开发环境分开，让我们能够像管理应用程序一样来管理开发环境。通过使用Docker技术来快速提交、测试和部署代码，能极大地减少编写代码和运行代码之间的时间间隔

具体而言，Docker使用Go语言编写，充分利用了Linux内核特征来实现它的功能。Docker是一个容器引擎，使用诸如名称空间（namespace）和控制组（cgroups）之类的Linux内核功能在操作系统之上创建容器，并自动在容器上部署应用程序。后端存储方面，Docker 使用 Copy-on-write 联合文件系统（UnionFS，如overlay2）。Docker组合namespaces、control groups以及UnionFS到一个被称为container format的包装器中，默认使用的container format是libcontainer。

namespaces提供了名为容器的隔离空间，当你运行一个容器的时候，Docker为该容器创建了一系列的namespaces（Linux中）：

pid：进程隔离（PID：Process ID）。

net：管理网络接口（NET：Networking）。

ipc：管理对IPC资源的访问（IPC：InterProcess Communication）。

mnt：管理文件系统安装点（MNT：Mount）。

uts：隔离内核和版本标识符。 （UTS：Unix Timesharing System）。

这些namespaces提供了一层隔离，容器的每个模块都在单独的名称空间中运行，并且对它们的访问仅限于该名称空间

cgroups则限制了一个应用能访问的特定资源集，cgroups允许Docker引擎共享可用的硬件资源，并能添加限制和约束。例如，你可以限制某个容器使用的内存大小

UnionFS是通过创建层来实现操作的文件系统，从而使得Docker非常轻量快速。Docker引擎使用UnionFS来为容器提供构建模块。Docker引擎支持多个UnionFS变种，包括AUFS、btrfs、vfs、overlay2和DeviceMapper等 [6]

Docker API

Docker Engine API是Docker Engine提供的HTTP API。 它是Docker客户端用于与引擎通信的API，因此Docker客户端可以做的所有事情都可以通过API来完成。大多数客户端命令直接映射到API（例如docker ps是GET/containers/json）。 值得注意的例外是运行容器，运行容器包含多个API调用。

API使用标准的HTTP状态代码来指示API调用的成功或失败。响应的主体将是JSON，格式为：

{

"message": "page not found"

}

API通常在每个Docker版本中都会更改，因此对API调用进行了版本控制以确保客户端不会意外中断。对于Docker Engine 17.06，API版本为1.30。要锁定此版本，请在URL前面加上/v1.30。例如，调用/ info与调用/v1.30/info相同。不久的将来引擎版本将支持此版本的API，因此即使客户端与更新的引擎进行通信，您的客户端也将继续工作。在以前的Docker版本中，无需提供版本即可访问API。现在已弃用此行为，它将在将来的Docker版本中删除。API使用开放式架构模型，这意味着服务器可以向响应中添加额外的属性。同样，服务器将忽略任何其他查询参数和请求正文属性。编写客户端时，您需要忽略响应中的其他属性，以确保它们在与较新的Docker守护程序进行通信时不会中断。 [7]

Portainer

Portainer简介

Portainer是一个轻量级的管理UI，可让您轻松管理不同的Docker环境（Docker主机或Swarm集群）。 Portainer的目标是易于部署和使用。它由一个可以在任何Docker引擎上运行的容器组成（可以部署为Linux容器或Windows本机容器，也支持其他平台）。 Portainer允许你管理所有Docker资源（容器，映像，卷，网络等）。它与独立的Docker引擎和Docker Swarm模式兼容。 [3]

Portainer API

Portainer API是Portainer提供的HTTP API。Portainer UI使用它，并且可以使用HTTP API完成对UI的所有操作。

大多数API主机都需要进行身份验证以及使用某种级别的授权。 Portainer API使用JSON Web令牌来管理身份验证，因此要求您使用Bearer身份验证机制在每个请求的Authorization标头中提供一个令牌。例如：

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsImV4cCI6MTQ5OTM3NjE1NH0.NJ6vE8FY1WG6jsRQzfMqeatJ4vh2TWAeeYfDhP71YEE

Portainer不会暴露指定的主机来管理Docker资源（创建容器，删除卷等）。相反，它充当Docker HTTP API的反向代理。这意味着您可以通过Portainer HTTP API执行Docker请求。为此，您可以使用/endpoints/{id}/docker 来访问特定的主机上的Docker。该方式具有受限的访问策略，因此您仍然需要进行身份验证才能查询相关信息。此主机上的Docker的任何查询都将代理到关联的Docker API（请求和响应对象与Docker API中记录的对象相同）。 [8]

JavaFX

JavaFX是一个软件平台，可用于创建和交付可在各种设备上运行的桌面应用程序以及富Internet应用程序（RIA）。 JavaFX旨在取代Swing作为Java SE的标准GUI库，但是在可预见的将来，两者会共同存在。JavaFX支持Microsoft Windows，Linux和macOS，以及Web浏览器。JavaFX应用程序可以在任何可以运行Java SE的桌面上运行，也可以在任何可以运行Java ME的手机上运行。 [9]

随着2018年JDK 11的发布，Oracle已将JavaFX纳入OpenJDK章程下的OpenJFX项目，以加快其开发速度。在2022年3月之前，对于当前的长期版本（Java JDK 8），也可以使用Oracle对JavaFX的“高级支持”。 [9]

OpenJFX是一个开放源代码的下一代客户端应用程序平台，适用于基于Java的桌面，移动和嵌入式系统。其目标是开发一种现代，高效且功能齐全的工具包，用于开发富客户端应用程序。 [10]

本章小结

本章介绍了本Docker管理系统的相关技术：容器化技术、Docker、Portainer和JavaFX。其中容器化技术是应用开发部署复杂化的产物，Docker是使用容器化技术中的佼佼者，Portainer是一个基于Web的Docker管理系统，JavaFX是Java中的一个图形化组件库，类似于AWT和Swing。

需求分析

本项目是一个Docker管理系统，在功能方面应当实现对Docker常用功能的所有管理能力，主要包括容器、镜像、卷、网络等，此外还应当实现用户管理、主机管理、应用模板、Registries管理等功能。非功能需求方面，首先面向的主要用户是大量使用Docker的应用开发人员，而开发人员通常使用各种平台，因此本项目应当具有跨平台的特点；其次高效且简单的操作方式是开发人员所希望的；界面方面，简洁大方有特色即可，过于优美、新颖是不必要的；性能方面，准确性和及时反馈能力是必需的，这要求用户每次在界面上的操作都应当准确无误地传递给Docker Daemon，用户在点击界面上的控件后不应等待太长时间。

功能需求

功能需求是需求分析的核心部分。本文的目标是构建一个具有Docker资源管理能力的系统，通过调查分析可以得到如下的整体需求（图3.1）：

图3.1 整体需求用例图

本系统主要面向两类用户：普通用户（开发人员）和系统管理员（运维人员）。普通用户通过登录系统后使用普通用户的界面创建容器等；系统管理员首先需要初始化整个系统，其中主要包括创建一个系统管理员账号以供自身使用，然后便能使用系统管理员账号登录系统，通过使用管理员界面完成其它工作，如为开发团队分配主机以供其搭建开发环境使用等等。

其中普通用户界面应当包含如下内容：查看自己有权限查看的Registries（权限由系统管理员分配）；使用特定主机管理容器、镜像、卷、网络资源，其中包括每个资源的增删改查能力，还有查看特定主机的信息，包括CPU、内存等等；使用现有应用模板快速构建应用，这一功能将大大提高搭建环境的效率，几乎可以算是一键完成了。普通用户界面包含的功能如图3.2所示：

图3.2 普通用户界面用例图

管理员界面应当包含如下内容：首先他包含了普通用户界面的所有功能（在图中以extends关键字体现），除此之外，它还包含如下内容：管理所有的Registries、Endpoints、Endpoints所属的组、Endpoints的标签、用户、团队、应用模板，以及查看特定Endpoint上发生的事件，修改系统的设置。如图3.3所示：

图3.3 管理员用户界面用例图

通过对上述需求的整合，可得到如下功能需求图（图3.4）：

图3.4 功能需求图

非功能需求

平台方面，由于本系统面向的主要用户是应用开发人员，而应用开发人员可能使用各种平台，所以本项目应该有跨平台的能力

操作方面，本系统要求高效、简单。因为对于应用开发人员而言，高效简单操作方式是必需的需求

界面方面，本系统要求简洁、大方、有特色。对于应用开发人员而言，界面优美、新颖、有创意并不是重点，重要的是简洁即可。比如像IDEA那样的界面

性能方面，本系统要求准确性高达99%、反馈时间低于1秒。用户的每次在界面上的操作都应当准确无误地传递给Docker Daemon；用户在点击界面上的控件后不应等待太长时间

本章小结

本章介绍了本Docker管理系统的需求分析阶段，对系统的功能需求和非功能需求两大方面进行了分析，功能需求通过用例图说明，非功能需求通过文字描述说明。功能需求主要包括普通用户界面和管理员用户界面。其中普通用户界面主要包括查看及使用他的Registries、使用App Templates、使用Endpoints，使用Endpoints又包括了其中的容器、镜像、卷、网络等资源；管理员用户界面除了包括普通用户界面的内容之外还包括管理Registries、修改Settings、管理Endpoints及相关内容、管理用户及团队和管理应用模板。非功能需求主要包括平台、操作、界面、性能四大方面的需求。平台方面要求跨平台，操作方面要求高效简单，界面方面要求简洁大方，性能方面要求准确快速。

概要设计

根据前述的功能需求和非功能需求，对该系统做出设计。本章介绍了该系统的概要设计，重点介绍了该系统的系统架构设计和功能模块设计。

系统架构

本文所设计的Docker管理系统主要利用了Portainer的API，而Portainer又主要通过调用Docker的API来完成对Docker资源的管理，因此本系统具有如下总体架构（图4.1）：

图4.1 系统构架

其中DockerM即本系统的名称，意为Docker管理系统（Docker Management）；Portainer是本系统的主要依赖，是一个提供管理Docker资源API的基于Web的Docker管理系统；Endpoint即主机，一个Endpoint通常对应一个Docker；Docker Container即Docker容器，容器是Docker中最重要的概念，通常作为一个应用提供一种服务

可以看到，本系统DockerM支持多个Portainer会话，通过调用Portainer的API来完成每个主机上Docker的管理，管理的主要对象是Docker的容器，其它管理对象还包括镜像、卷、网络等

功能模块

本系统的功能模块主要由需求分析中的功能需求得出，即应当具有各类对象的管理能力，包括初始化及登录模块、App Templates的创建、App Templates的查看、App Templates的修改、App Templates的删除，Registries的创建、Registries的查看、Registries的修改、Registries的删除，Settings的修改，Endpoints Groups的创建、Endpoints Groups的查看、Endpoints Groups的修改、Endpoints Groups的删除，Endpoints Tags的创建、Endpoints Tags的删除，Users的创建、Users的查看、Users的修改、Users的删除，Teams的创建、Teams的查看、Teams的修改、Teams的删除，Endpoints的创建、查看、修改、删除。如图4.2所示：

图4.2 功能层次结构图

注意其中的Endpoints模块，其内部又包含了容器的创建、查看、修改、删除，镜像的创建、查看、修改、删除，卷的创建、读取、修改、删除，网络的创建、读取、修改、删除，信息的查看，事件的查看几大模块。如图4.3所示：

图4.3 功能层次结构图2

本章小结

本章介绍了本系统的概要设计阶段，主要包括两大内容：系统架构和功能模块。系统架构介绍了本系统和Portainer、Docker、Docker容器之间的关系；功能模块阐述了本系统的模块划分。

详细设计与实现

根据对Docker管理系统的概要设计，将系统核心部分分为Init & Login、App Templates、Registries、Settings、Endpoint Groups、Endpoint Tags、Users、Teams、Endpoints九大部分，其中Endpoints又分为Containers、Images、Volumes、Networks、Info、Events六大模块。本章介绍了这些模块的详细设计与实现。

详细设计

初始化与登录

初始化或登录是本系统运行的第一步必要步骤，该模块主要通过调用Portainer的API——POST / users/admin/init或POST /auth来分别实现初始化和登录。其具体流程如图5.1所示：

图5.1 初始化与登录模块活动图

该模块类的设计如下：

图5.2 初始化与登录模块类图

其中CallApiUtil是一个工具类，主要负责调用Portainer的API，封装了许多常用的API调用，如获取数据项Users、Teams、Endpoints等等。PrimaryController则是一个控制器，负责主页面的各种事件的响应，比如点击菜单栏中的File-New等。事实上，初始化与登录模块即File-New的功能，它会创建一个新的Portainer会话，而这个过程是通过由PrimaryController创建一个用于创建新的会话的向导，通过该向导得到Portainer相关信息，包括URL、Username、Password以及boolean类型代表是否是初始化的Init，成功后PrimaryController将调用CallApiUtil的构造函数创建一个新的会话。如图5.1所示

其UI界面状态图如图5.3所示：

图5.3 初始化与登录模块UI状态图

即实现一个简单的向导，依次输入Portainer URL,、Username、Password和Init来实现获取该模块的参数

主面板

主面板是本系统初始化或登录后的界面，它负责整合各个模块，是各大模块的入口。通过和主面板进行交互，用户可以到达任意模块。其状态图如下所示：

图5.4 主面板状态图

系统启动后，进入started状态，在这种状态下，点击菜单栏中的File->Quit将退出程序，点击Help->Shortcut将弹出本系统常用的快捷键页面，点击Help->Help将查看本系统的使用手册，点击Help->About将查看本系统的关于页面。弹出以上界面后点击关闭又将回到started状态

started状态中，首先进入的是初始状态，点击File->New后将创建一个新的Portainer会话，输入前述初始化与登录模块中的参数后进入载入Endpoint状态，当然，到达该状态的操作还包括File->Open、File->Open Recent。在载入Endpoint状态下，如果点击了File->Close，则系统将关闭会话，回到初始状态。如果点击File->Save且是第一次点击，则系统进入保存文件状态，在弹出的文件选择框中选择相应的文件并确定后系统回到载入Endpoint的状态；点击File->Save As和第一次点击File->Save类似，故不同志赘述；如果不是第一次点击File->Save，则系统在保存到上一次保存的文件后继续处于载入Endpoint状态

此外，对于其中的loadedEndpoint状态，它的具体内容如下：

图5.5 loadedEndpoint具体内容

载入Endpoint后，主界面将展示多个Endpoint信息。此时点击Add将会弹出Add endpoint对话框，点击Close又会回到展示Endpoints的界面；点击特定的Endpoint将进入clickedEndpoint状态，在此状态下，左边会弹出导航栏，中间默认展示的是EndpointInfo页面，内容主要是各种资源的数量，包括容器、镜像、卷、网络，右边是该Endpoint的一些系统信息。

在“展示EndpointInfo页面”这一状态下，点击中间页面中的任一资源将会切换到相应的资源展示页面，比如点击Containers将会展示Containers页面等等。同时导航栏一直保持展示的状态。如果在相应的资源展示页面选中了特定的资源，则会在右边显示该资源的详细信息。

在整个“clickedEndpoint”状态下，选择导航栏中的任一项将跳转到对应的页面，比如点击导航栏中Templates，中间页面将展示Templates的内容。选中特定的Template后，又会在右边显示相应的详细信息。

应用模板

应用模板用于简化应用部署。将常用的应用及相关配置做成模板（类似于docker-compose.yml），可以实现一键部署，从而大大提高部署效率。该模块主要通过保存部署容器时需要的各种参数实现模板的构造。对其主要的操作包括创建一个新的模板、删除已有的模板、编辑已有的模板、根据已有的模板部署到容器。

创建一个模板的流程如下：

输入模板必要的基本信息：标题、描述；

输入模板可选信息：名字、Logo URL、注意事项、适用平台、分类；

输入容器相关信息：使用的Registry、镜像、命令、主机名、网络、端口绑定、卷绑定、标签、重启策略等；

输入权限控制信息；

检查输入；

提交并保存到模板库中。

删除一个模板的流程则比较简单，直接删除即可。

编辑已有模板的流程和创建模板的流程类似，故不再赘述。

部署模板的流程如下：

选中要部署的模板；

输入容器名称、要使用的网络、访问控制等信息；

检查输入；

提交并根据模板部署容器。

Registries

Registries模块用于保存常用的Registry。在Docker中，Registry类似于Linux中的开源镜像站URL，是从镜像库获取镜像的链接。本系统具有管理Registries的能力，主要包括添加、删除、修改、读取等操作。Registries的管理将大大方便用户拉取镜像。

添加一个Registry的流程如下：

输入基本信息：名称和URL

输入可选的认证信息：用户名和密码

输入访问控制信息

检查输入

提交并保存

删除一个Registry的流程则比较简单，直接删除即可。

编辑已有Registry的流程和添加的流程类似，故不再赘述。

读取Registry的流程也比较简单，直接读取即可

设置

本系统中的设置主要是指Portainer中的设置，包括使用外部模板、为非管理员用户关闭挂载绑定、为非管理员用户关闭提权模式、为非管理员用户启用卷管理、启用主机管理特征等。设置模块中的操作只包括读取和更新。读取时通过调用Portainer API获取设置信息并展示到页面上，更新时在修改完表单后提交即可，系统会调用API完成设置的更新

主机组

Endpoint组模块用于将Endpoint分类，从而便于管理Endpoint，比如为每一个开发团队分配一组Endpoint。该模块的主要操作包括创建、读取、更新、删除。

创建的流程如下：

弹出创建对话框

输入基本信息：名字、描述、Tags、包含的Endpoint

输入访问控制信息

检查输入

提交

读取时，直接调用API获取Endpoint组列表，然后将列表模型在UI上展示出来即可

更新时，同样弹出一个对话框，该对话框和创建时的对话框一致。输入要修改的信息后点击提交即可。系统将会把提交新信息通过调用API的方式传递给Portainer，让Portainer进行更新操作

删除时，直接选中要删除的组，点击删除即可。系统同样会调用API完成删除工作

主机标签

Endpoint Tags模块同样用于将Endpoint分类，通过使用该模块同样会让管理Endpoint变得更加简单方便。该模块负责完成对Endpoint Tags的创建和删除操作。

创建时，直接输入Tag名即可。系统会调用Portainer API完成创建。

删除时，直接选中特定Tag后点击删除按钮即可。系统同样会调用相关API完成删除操作。

用户

用户模块是本系统中比较重要的一个模块。如前所述，本系统支持用户管理，不同的用户具有不同的权限，且可以为每个用户分配其能使用的资源。本模块负责用户的管理，主要包括添加、读取、更新、删除。

添加一个用户的流程如下：

弹出添加用户对话框

输入用户信息：用户名、密码、确认密码、是否管理员、添加到哪些团队

检查输入

提交

提交后，后台将调用Portainer相关API完成用户的添加操作并反馈到展示页面

读取时，主要通过调用API获取用户模型，然后通过控制器将其展示到Users界面

更新时，同样使用添加时的对话框，以原来的数据填充表单，用户完成修改并提交后在后台调用API完成修改

删除时，选中待删除的用户，点击删除按钮，系统调用删除的API完成删除操作

团队

团队是一组用户的集合，团队模块用于使团队的管理更加简单。该模块主要包括团队的创建、读取、更新和删除。

创建一个团队的流程如下：

点击Add按钮，弹出创建团队的对话框；

输入团队的信息：团队名、团队领导者、团队成员；

检查输入；

提交；

读取时，主要通过团队页面的控制器调用Portainer API获取团队数据，然后将该数据传递给相关的Table控件

更新时，同样会弹出一个对话框，该对话框和创建时的一致，该对话框的控制器会将该团队的信息填充到该对话框的控件中，用户完成修改并提交后将会把该修改后的团队信息发送给相关的API，完成相应的修改

删除时，用户选中需要删除的团队，然后点击删除按钮，系统会调用删除API完成删除

主机

Endpoint是本系统中非常重要的一个概念。在本系统中，一个Endpoint通常指一个主机，它和一个Docker或一个Portainer Agent对应。对于Endpoint的管理包括很多内容，这也是本系统的核心部分。这里首先要讲的是对Endpoint的四种操作：创建、读取、更新、删除

Endpoint创建的流程如下：

点击Add按钮，弹出添加Endpoint对话框

输入Endpoint信息：类型、名称、URL、所属Group、Tags

检查输入

提交

读取时，Endpoint展示页面对应的控制器会通过调用Portainer API获取Endpoint信息，然后将其展示到相应的控件中（比如Table）。

更新时，弹出一个样式及控件内容和创建时一致的对话框，并将要修改的Endpoint的信息填充到对应的控件中，等待用户修改并提交后直接调用API上传新的信息

删除时，选中想要删除的Endpoint，然后点击Delete按钮即可

后文将阐述Endpoint中的对各种资源的管理的模块的设计，主要包括容器、镜像、卷、网络，其次还包括Endpoint本身的信息（比如Docker版本等）、Endpoint上发生的事件（比如创建了一个新的容器等）。

容器

容器是Docker中最重要的概念，通常而言，一个容器代表一个应用（服务），比如Web应用等。对于容器的操作主要包括创建、读取、更新、删除。除此之外，还包括启动、重启、停止、强行停止（杀死）、暂停、继续、重建、复制、查看日志、查看Inspect，查看统计、使用控制台、Attach、通过容器构建镜像，当然，修改其访问权限也是应当实现的操作。

创建一个容器的流程如下：

输入容器基本信息：名称、使用的Registry、镜像

输入容器的端口映射信息：比如 9000:1234

输入访问控制信息

选择是否自动移除（一次性容器）

检查输入

添加

完成后系统调用Portainer API将新建的容器信息发送给Portainer，再由Portainer发送给Docker（反向代理），以完成容器的创建，然后调用API重新获取Docker容器的信息，刷新展示页面

读取容器的流程如下：

调用API获取容器信息

将容器信息应用到UI控件上

更新容器的流程如下：

选择要更新的容器，在右边弹出该容器的详细信息

选择要修改的内容

修改完成

点击其它位置以确认修改

更新页面

删除一个容器的流程如下：

选择要删除的容器

点击删除按钮

完成删除操作

更新页面

启动一个容器的流程如下：

选择要启动的容器

点击启动按钮

完成启动操作

更新页面

重启一个容器的流程如下：

选择要重启的容器

点击重启按钮

完成重启操作

更新页面

停止一个容器的流程如下：

选择要停止的容器

点击停止按钮

完成停止操作

更新页面

杀死一个容器的流程如下：

选择要杀死的容器

点击杀死按钮

完成杀死操作

更新页面

暂停一个容器的流程如下：

选择要暂停的容器

点击暂停按钮

完成暂停操作

更新页面

继续一个容器的流程如下：

选择要继续的容器

点击继续按钮

完成继续操作

更新页面

重建操作是使用构建该容器时的配置重新构建，该操作将会删除该容器。重建一个容器的流程如下：

选择要重建的容器

点击重建按钮

完成重建操作

更新页面

复制操作是使用构建该容器时的配置再构建一个新的容器。复制一个容器的流程如下：

选择要复制的容器

点击复制按钮

完成复制操作

更新页面

查看一个容器的日志的流程如下：

选择要查看日志的容器

点击查看日志按钮

弹出日志查看对话框

获取日志

展示到页面

查看容器Inspect信息的流程如下：

选择要查看Inspect的容器

点击查看Inspect按钮

弹出Inspect查看对话框

获取Inspect

展示到页面

查看容器统计信息的流程如下：

选择要查看统计信息的容器

点击查看统计信息按钮

弹出统计信息查看对话框

实时获取统计信息

实时展示到页面

使用控制台的流程如下：

选择要使用控制台的容器

点击控制台按钮

弹出Shell选择对话框

输入要执行的命令（要使用的Shell）

输入用户名

点击连接

弹出Shell对话框，此时输入任意Shell命令即可执行

Attach的流程如下：

选择要Attach的容器

点击Attach按钮

弹出Attach对话框

通过容器构建镜像的流程如下：

选择要构建镜像的容器

点击构建镜像Tab

输入构建镜像的参数：Registry、Image name

完成构建

镜像

镜像同样是Docker中非常重要的资源，其重要性仅次于容器。对于镜像而言，通常会进行如下常规操作：创建、读取、更新、删除。此外还包括导入、导出、查看Dockerfile这些操作

创建一个镜像主要有三种方法：使用Dockerfile、从Registry pull、通过容器构建。在该模块中，主要需要考虑的是前两种方法，最后一种方法在前文已经描述过，故不再赘述。

使用Dockerfile创建镜像的流程如下:

点击构建按钮

弹出构建对话框（构建Tab）

输入名字

选择获取Dockerfile的方法

提交

在输出Tab查看构建过程的输出。完成后刷新镜像页面

从Registry Pull即直接从库中拉取镜像，具体流程如下：

点击Pull按钮

选择Registry

输入镜像Tag

提交

刷新镜像页面

读取镜像的流程如下：

调用API获取镜像信息

展示到UI控件中（如Table）

更新镜像的流程如下：

点击要更新的镜像

修改要更新的内容

点击其它位置应用修改

刷新页面

删除镜像的流程如下：

选中要删除的镜像

点击删除按钮

完成删除

刷新页面

导入镜像的流程如下：

点击导入按钮

打开文件选择对话框

选择要导入的文件并确认

提交

刷新页面

导出镜像的流程如下：

选中要导出的镜像

点击导出按钮

选择要导出到的文件并确认

提交

查看Dockerfile的流程如下：

获取镜像的Dockerfile

展示到页面

卷

卷（Volume）是Docker中的存储“设备”，用来保存容器中的某些信息。对卷的操作包括创建、读取、更新、删除。

创建一个卷的流程如下：

点击Add Volume按钮

输入卷的基本信息：名字

输入卷的驱动信息：使用的驱动、驱动选项、是否使用NFS卷

输入卷的访问控制信息

提交

刷新页面

读取卷的流程如下：

调用API获取所有卷的信息

在UI控件上展示

更新卷的流程如下：

选中要更新的卷

在其详细页面选择要修改的内容并做出修改

点击其它位置确认修改

刷新页面

删除卷的流程如下：

选择要删除的卷

点击删除按钮

完成删除操作

刷新页面

网络

网络是容器可选的部分，对外服务的容器通常会用到网络，不对外服务的容器则不会用到网络。对网络的操作主要包括创建、读取、更新、删除。

创建一个网络的流程如下：

点击Add Network按钮，弹出添加网络对话框

输入网络基本信息：名字

输入网络驱动（Driver）信息：选择使用的驱动、驱动选项

输入网络配置：子网、IP地址范围

输入网络高级配置：Labels、是否限制外部访问、是否启用手动容器Attach

输入访问控制信息

提交

刷新页面

读取网络的流程如下：

获取网络信息

在UI控件上展示

更新网络的流程如下：

选择要更新的网络

在详细页面修改要更新的内容

点击其它位置确认更新

刷新页面

删除一个网络的流程如下：

选中要删除的网络

点击删除按钮

刷新页面

信息

信息模块用于展示Endpoint的信息。主页面（通常是中间的页面）展示各种资源的数量，包括容器、镜像、网络、卷。详细页面（通常是右边的页面）展示主机的详细信息，包括两大内容：主机信息和Docker引擎信息。主机信息包括主机名、OS信息、内核版本、CPU数量、内存容量；Docker引擎信息包括版本、根目录、存储驱动、日志驱动、卷插件、网络插件。

事件

事件模块用于展示在该Endpoint上发生的Docker相关的事件，比如容器启动、容器创建等事件信息，以供用户参考

实现

组件的选择

本文在具体的实现细节上进行了深入的思考。首先，通过需求已经得出了跨平台的需求，在综合考虑了几种方案后最终采用了Java语言作为系统主要编程语言；然后，考虑到日志记录在本系统中的重要性，又通过查阅资料得知SLF4J的抽象机制可以使得其具有良好的拓展性和可维护性，且其原生支持logback，因此，本项目采用了SLF4J+logback的日志机制；其次，本项目大量涉及对JSON的处理（序列化和反序列化），考虑的各大Java JSON库的稳定性和可靠性，本项目采用了谷歌的GSON作为本项目的JSON库；此外，考虑到本项目的依赖较多，因此采用了Maven进行依赖管理；最重要的是，为了简化UI的开发，实现UI控件和程序的分离，本项目使用JavaFX作为UI开发包，还使用了外部控件集ControlsFX，并使用MVC架构；最后，测试方面，本项目采用了Junit4+TestFX作为测试框架

开发环境

本系统使用的开发环境如下：

开发工具：IntelliJ IDEA 2020.1.1 (Ultimate Edition) + JDK11.0.6 + JRE11.0.6 + JavaFX11.0.2

开发环境：Windows10，Docker Engine - Community 19.03.5 + API 1.40，Portainer 1.23.2

代码结构

图5.6 代码结构

Modules依赖

图5.7 Modules依赖

运行效果

Home(Endpoints)

图5.8 Home

Host(EndpointInfo)

图5.9 Host

Containers

图5.10 Containers

Images

图5.11 Images

Networks

图5.12 Networks

Volumes

图5.13 Volumes

本章小结

本章介绍了本系统的详细设计与实现。根据概要设计中的功能模块划分，明确了每个功能模块需要实现的具体操作及操作步骤，然后阐述了一些在实现过程中考虑的问题，最后展示了本系统的运行效果。

测试

本系统在功能及非功能方面均有需求，因此，本系统主要进行了功能测试和非功能测试。

功能测试指根据项目的需求规格说明书，逐条核实本系统功能完成情况，本系统需要完成对Docker的各种资源的管理，包括容器、镜像、卷、网络。管理的操作除了常规的增、删、改、查外，还包括一些资源相关的特定的操作，比如容器的启动、停止、日志查看、使用控制台，镜像的拉取、导入、导出等。另外，为了实现对多个Docker的管理能力，还需要完成对Endpoint的管理，同样除了增、删、改、查之外还包括分组、标签等内容。并且，为了实现用户管理功能，还需要实现用户的增、删、改、查，以及对团队的管理。总之，功能测试的内容非常之多。

非功能测试主要包括平台、操作、界面、性能四大方面。平台方面，由于需要跨平台，因此应当在三大主流平台上进行充分的测试；操作方面，同样需要对各种操作进行测试，使各种操作尽可能方便高效；界面方面，需要对每一个页面的风格进行统一处理；性能方面，需要对程序各个页面的响应时间及正确性进行测试，以满足性能上的需求。

功能测试

功能测试按照重要性优先的原则进行，即按照如下顺序进行：测试初始化及登录模块，测试Endpoint管理模块，测试容器管理模块，测试镜像管理模块，测试卷管理模块，测试网络管理模块，测试Endpoint信息模块，测试Endpoint事件模块，测试其它模块。模块划分参见图4.2 功能层次结构图和图4.3 功能层次结构图2。

这里不一一详细介绍测试用例，仅以一个测试用例为例（初始化及登录模块测试）介绍测试用例的设计和测试结果：

表6.1 初始化及登录模块测试用例

测试编号	测试名称	测试目的	测试步骤	测试结果
01	初始化及登录模块测试	验证通过该模块是否能成 功初始化Portainer及完成登录操作	测试正常步骤初始化系统 测试正常步骤登录系统 测试各种错误输入	成功获取到Endpoints（说明初始成功） 成功获取到Endpoints 提示输入错误，并要求重新输入

非功能测试

非功能测试按照如下顺序进行：界面、性能、操作、平台。

界面方面，主要对界面网络是否统一进行了测试，由于设计界面考虑了这一要素，因此，测试时发现界面相当统一，此外，本系统的界面也符合需求分析中的简洁、大方、有特色。

性能方面，主要对各种操作的时间性能进行了测试，经测试，大多页面基本符合需求中的要求（反馈时间小于1秒，准确性高于99%）。但是Template页面的性能严重不符合要求，其反应时间长达10秒以上，经过简单分析后，发现问题出在对在线图片的请求上，这些图片似乎放在国外的网站，因此访问较慢，初步想到的解决方法包括使用多线程并提示用户图片正在加载或者直接取消图片的加载。

操作方面，本系统基本实现了高效、简单的需求。首先，高效方面，本系统在多个地方进行了细节上的优化，比如为了用户可以直接地查看特定容器的信息，本系统将原本设计为的在新页面进行查看的方式改为直接在右侧边栏进行查看。操作方面，各个操作都简单易懂，对于大多概念也都尽可能采用了原生的名词，比如Endpoint，也正因如此，将原本的汉化打算给放弃了

平台方面，由于时间关系，仅在Windows上完成了测试。测试结果良好。

本章小结

本章介绍了本系统的测试阶段，从功能测试和非功能测试两大方面完成了测试。功能测试方面，已编码的部分基本符合需求分析中的要求；非功能测试方面，界面、操作、平台均已基本符合需求分析中的要求，但是性能方面还存在一定的问题，有待改进。

结论

随着应用的复杂化，应用开发环境的搭建也变得越来越复杂。容器化技术的出现解决了这个问题，其中最具有代表性的莫过于Docker。Docker官方提供的管理程序默认为CLI方式，这大大提高了学习成本，降低了开发效率，因此开发一个具有图形化界面的Docker管理系统是必要的。这正是本文研究的主要内容。本文基于Portainer设计并实现了一个具有图形化界面的Docker管理系统。本文按照软件工程中的方法，从需求分析开始，逐步完成了系统的分析与设计，最后通过Java代码实现。

本系统在设计与实现上，依然存在很多问题，需要后续对本系统进行进一步的完善。

致谢

本文的完成离不开导师王黎明的悉心指导。王黎明老师有效的监督在整个过程中起到了非常重要的作用，并且王黎明老师为我解答了很多设计与实现上的问题。在这衷心表示感谢。

另外，本文参考了许多Docker、Portainer、JavaFX相关的资料，以其官方网站为主，众多问答网站为辅。在此向他们表达我的感激之情。

最后，也感谢我的父母和弟弟，有了他们的理解和支持，才让我能够在家中顺利完成本次毕业设计。

参考文献

[1]	kevana, “kevana/DockerUI,” 09 06 2013. [联机]. Available: https://github.com/kevana/ui-for-docker. [访问日期: 03 01 2020].
[2]	shipyard, “shipyard/shipyard,” GitHub, Inc., 24 10 2017. [联机]. [访问日期: 03 01 2020].
[3]	portainer, “portainer/portainer,” 09 06 2013. [联机]. Available: https://github.com/portainer/portainer. [访问日期: 28 05 2020].
[4]	Rancher, “Enterprise Kubernetes Management | Rancher,” Rancher, [联机]. Available: https://rancher.com/. [访问日期: 03 01 2020].
[5]	olprod, “容器和 Docker 简介,” Microsoft , 31 08 2018. [联机]. Available: https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/container-docker-introduction/. [访问日期: 12 05 2020].
[6]	Docker, “Docker overview,” [联机]. Available: https://docs.docker.com/get-started/overview/. [访问日期: 03 01 2020].
[7]	Docker, Inc., “Docker Engine API v1.30 Reference,” Docker，Inc., [联机]. Available: https://docs.docker.com/engine/api/v1.30/. [访问日期: 12 05 2020].
[8]	Portainer, “Portainer | 1.23.2 | deviantony | SwaggerHub,” Portainer, [联机]. Available: https://app.swaggerhub.com/apis/deviantony/Portainer/1.23.2/. [访问日期: 12 05 2020].
[9]	Wikipedia, “JavaFX - Wikipedia,” Wikipedia, [联机]. Available: https://en.wikipedia.org/wiki/JavaFX. [访问日期: 12 05 2020].
[10]	Oracle Corporation, “JavaFX,” Oracle Corporation, [联机]. Available: https://openjfx.io/. [访问日期: 12 05 2020].
[12]	portainer, “portainer/portainer,” portainer, 09 06 2020. [联机]. Available: https://github.com/portainer/portainer. [访问日期: 12 05 2020].