Docker从认识到实践再到底层原理(九)|Docker Compose 容器编排

前言
那么这里博主先安利一些干货满满的专栏了!
首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。
然后就是博主最近最花时间的一个专栏《Docker从认识到实践再到底层原理》希望大家多多关注!
Docker Compose
1. 概览
参考:比特就业课
1.1 Docker Compose是什么
docker-compose 是 Docker 官方的开源项目,使用 python 编写,实现上调用了Docker 服务的 API 进行容器管理及编排,其官方定义为定义和运行多个 Docker 容器的应用。
docker-compose 中有两个非常重要的概念:
- 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
- 项目(project):由一组关联的应用容器组成的一个完整业务单元,在
docker-compose.yml文件中定义, 整个docker-compose.yml定义一个项目。
Compose的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。通过compose可以方便的管理多个服务。
1.2 为什么要 Docker Compose
Docker 是一个轻量化的应用程序,Docker 官方推荐每个 Docker 容器中只运行一个进程。
如果一个应用需要涉及到 MySQL、nginx 等环境, 那么我们需要分别为应用、数据库和 nginx 创建单独的 docker 容器,然后分别启动容器。想象一下,当我们构建好 Docker 之后,每次启动应用,都至少需要 docker run三次, 或者写一些脚本来实现, 这样会比较繁琐。
另外,这些 docker 容器都是分散独立的,也不方便镜像管理。那既然这些 docker 容器 都是为了同一个应用服务,我们就应该把它们放到一起,这就引出了 docker-compose 来解决这类型的问题。
2. Docker Compose 的安装
我们安装docker的时候已经顺便安装了,大家可以看看前面的章节(第一章)。
检查是否安装上了。
root@ALiCentos7:~$ docker compose version
Docker Compose version v2.20.2
You have new mail in /var/spool/mail/root
root@ALiCentos7:~$
3. Docker Compose 的功能简介
3.1 使用步骤
Compose 使用的步骤:
- 使用
docker-compose.yml定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。 - 最后,执行 docker compose up 命令来启动并运行整个应用程序。
3.2 核心功能
Compose 具有管理应用程序整个生命周期的命令:
启动,停止和重建服务
查看正在运行的服务的状态
流式传输运行服务的日志输出
在服务上运行一次性命令
4. Docker Compose 文件(docker-compose.yml)
4.1 文件语法版本
目前官方支持三个大版本, 即 Version 1、Version 2 及 Version 3, 其中 Version 1 已经被废弃掉了。当前最新的版本是 3.8,它支持的 Docker Engine 版本不得低于 19.03.0。
官方文档:
- https://docs.docker.com/compose/compose-file/compose-versioning/
4.2 文件的基本结构
version: "3.8" # 定义版本, 表示当前使用的 docker-compose 语法的版本
services: # 服务,可以存在多个
servicename: # 服务名字,它也是内部bridge 网络可以使用的DNS name,如果不是集群模式相当于 docker run的时候指定的一个名称
#集群(Swarm)模式是多个容器的逻辑抽象
image: # 必选,镜像的名字
command: # 可选,如果设置,则会覆盖默认镜像里的 CMD 命令
environment: # 可选,等价于 docker container run 里的 --env 选项设置环境变量
volumes: # 可选,等价于docker container run 里的 -v 选项 绑定数据卷
networks: # 可选,等价于 docker container run 里的 --network 选项指定网络
ports: # 可选,等价于 docker container run 里的 -p 选项指定端口映射
expose: # 可选,指定容器暴露的端口
build: #构建目录
depends_on: #服务依赖配置
env_file: #环境变量文件
servicename2:
image:
command:
networks:
ports:
servicename3:
#...
volumes: # 可选,等价于 docker volume create
networks: # 可选,等价于 docker network create
4.3 常见字段格式语法
4.3.1 image
image: redis
image: redis:5
image:
redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398
bc4b991aac7
image: library/redis
image: docker.io/library/redis
image: my_private.registry:5000/redis
先准备好目录先。

把配置文件写好。

启动配置文件里面的所有东西。

会默认创建一个桥网络的,然后把工程相关的容器加入到这个项目的网络里面去,和docker默认网络分开的。


停止和删除。

4.3.2 command
覆盖容器启动的默认命令。
command: ["bundle", "exec", "thin", "-p", "3000"]
command: bundle exec thin -p 3000



4.3.3 entrypoint
覆盖容器默认的 entrypoint。
两种格式。
entrypoint: /code/entrypoint.sh
entrypoint:
- php
- -d
- zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
- -d
- memory_limit=-1
- vendor/bin/phpunit


4.3.4 environment
添加环境变量。您可以使用数组或字典、任何布尔值,布尔值需要用引号引起来,以确保 YML 解析器不会将其转换为 True 或 False。
# map语法
environment:
RACK_ENV: development
SHOW: "true"
USER_INPUT:
# 数组语法
environment:
- RACK_ENV=development
- SHOW=true
- USER_INPUT


4.3.5 networks
指定容器运行的网络。


要调整网络的信息也很容易。如下面的例子所示即可。
services:
frontend:
image: awesome/webapp
networks:
front-tier:
ipv4_address: 172.16.238.10
networks:
front-tier:
ipam:
driver: default
config:
- subnet: "172.16.238.0/24"
4.3.6 volumes
将主机的数据卷或者文件挂载到容器里。
#短语法 对应 -v 的精简的配置
services:
db:
image: postgres:latest
volumes:
- "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
- "/localhost/data:/var/lib/postgresql/data"
#完整语法 对应 --mount 的详细的配置
services:
backend:
image: awesome/backend
volumes:
- type: volume
#命名卷
source: db-data
target: /data
volume:
nocopy: true
#绑定卷
- type: bind
source: /var/run/postgres/postgres.sock
target: /var/run/postgres/postgres.sock
volumes:
db-data:
4.3.7 ports
端口映射,其实就是命令中的-p选项。
#完整语法
ports:
- target: 80
host_ip: 127.0.0.1 # 设置成 0.0.0.0 就是所有机器都能来访问
published: 8080
protocol: tcp
mode: host
- target: 80
host_ip: 127.0.0.1
published: 8000-9000
protocol: tcp
mode: host
#短语法
ports:
- "3000"
- "3000-3005"
- "8000:8000"
- "9090-9091:8080-8081"
- "49100:22"
- "127.0.0.1:8001:8001"
- "127.0.0.1:5000-5010:5000-5010"
- "6060:6060/udp"
4.3.8 expose
暴露端口,但不映射到宿主机,只被连接的服务访问。
expose:
- "3000"
- "8000"
4.3.9 build
这个参数我们学完dockerFile之后就能理解了,现在先不讲。
4.3.10 depends_on
设置依赖关系。
docker compose up :以依赖性顺序启动服务。在以下示例中,先启动 db 和redis ,才会启动 web。
docker compose up SERVICE :自动包含 SERVICE 的依赖项。在以下示例中,docker compose up web 还将创建并启动 db 和 redis。
docker compose stop :按依赖关系顺序停止服务。在以下示例中,web 在 db 和redis 之前停止。
这个参数比较复杂,大家来看看例子。
version: "3.7"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
可以指定条件,healthy 需要配置 healthcheck 来完成。
services:
web:
build: .
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
redis:
image: redis
db:
image: postgres
healthcheck案例。
version: "3.8"
services:
web:
image: nginx:1.24.0
environment:
TEST: 1
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: "bit@123"
volumes:
- /data/maxhou/mysqldata/varlib/:/var/lib/mysql
healthcheck:
test: mysql --user=root --password='bit@123' -e "SELECT 1;"
interval: 10s
timeout: 5s
retries: 10


4.3.11 env_file
从文件添加环境变量。可以是单个值或列表的多个值。
env_file: .env
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env

5. Docker Compose 命令
5.1 命令清单
| 命令 | 功能 |
|---|---|
docker compose build |
构建服务 |
docker compose config |
规范的格式来显示服务配置 |
docker compose cp |
在本地系统和服务容器直接拷贝文件 |
docker compose create |
创建服务的容器 |
docker compose down |
停止所有容器,并删除容器 |
docker compose events |
从服务器获取实时时间 |
docker compose exec |
在容器中执行命令 |
docker compose images |
列出所有容器使用的镜像 |
docker compose kill |
强制停止服务的容器 |
docker compose logs |
显示日志 |
docker compose ls |
显示所有项目 |
docker compose pause |
暂停服务 |
docker compose port |
列出所有的端口映射 |
docker compose ps |
该命令可以列出项目中目前的所有容器 |
docker compose pull |
拉取服务镜像 |
docker compose push |
推送服务镜像 |
docker compose restart |
重启或者重启某个服务 |
docker compose rm |
删除服务停止的容器 |
docker compose run |
在指定服务容器上执行相关的命令 |
docker compose start |
启动当前停止的某个容器 |
docker compose stop |
停止当前运行的某个容器 |
docker compose top |
显示运行的进程 |
docker compose unpause |
恢复服务 |
docker compose up |
up 命令会构建,(重新)创建,启动, 链接一个服务相关的容器。 默认情况下如果容器已经存在, 将会停止并尝试重新创建他们。并使用之前挂载的卷。 –no-recreate 参数可以让容器不被停止或者重新创建, -d 表示后台运行 |
docker compose version |
查看版本 |
5.2 命令格式
对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器。如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响。docker-compose 命令的基本的使用格式为如下所示。
docker compose [OPTIONS] COMMAND [ARGS...]
5.3 常用选项
-f, --file指定使用的 Compose 模板文件,默认为docker-compose.yml,可以多次指定。-p, --project-name指定项目名称,默认将使用所在目录名称作为项目名。
5.4 常见命令
5.4.1 up
该命令的作用十分强大,它会尝试自动完成包括构建镜像、(重新)创建服务、启动服务并关联服务相关容器的一系列操作,可以直接通过该命令来启动一个项目。
docker compose up [options] [SERVICE...]
参数。
-d 在后台运行服务容器, 推荐在生产环境下使用该选项
--force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用
--no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用
5.4.2 down
停止所有容器,并删除容器和网络。
docker compose down [options] [SERVICE...]
参数。
-v, --volumes 删除容器同时删除目录映射
5.4.3 run
该命令可以在指定服务容器上执行相关的命令。
# 例如:启动一个 ubuntu 服务容器,并执行 ping docker.com 命令
# docker compose run ubuntu ping docker.com
docker compose run [options] SERVICE [COMMAND] [ARGS...]
参数。
-d 后台运行容器
--name NAME 为容器指定一个名字
--entrypoint CMD 覆盖默认的容器启动指令
-e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量
-u, --user="" 指定运行容器的用户名或者 uid
--rm 运行命令后自动删除容器
-p, --publish=[] 映射容器端口到本地主机
6. 综合案例
6.1 综合案例一
#mermaid-svg-A5syRIZs8iDVPjzL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .error-icon{fill:#552222;}#mermaid-svg-A5syRIZs8iDVPjzL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A5syRIZs8iDVPjzL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-A5syRIZs8iDVPjzL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A5syRIZs8iDVPjzL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A5syRIZs8iDVPjzL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A5syRIZs8iDVPjzL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A5syRIZs8iDVPjzL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A5syRIZs8iDVPjzL .marker.cross{stroke:#333333;}#mermaid-svg-A5syRIZs8iDVPjzL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A5syRIZs8iDVPjzL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .cluster-label text{fill:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .cluster-label span{color:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .label text,#mermaid-svg-A5syRIZs8iDVPjzL span{fill:#333;color:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .node rect,#mermaid-svg-A5syRIZs8iDVPjzL .node circle,#mermaid-svg-A5syRIZs8iDVPjzL .node ellipse,#mermaid-svg-A5syRIZs8iDVPjzL .node polygon,#mermaid-svg-A5syRIZs8iDVPjzL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A5syRIZs8iDVPjzL .node .label{text-align:center;}#mermaid-svg-A5syRIZs8iDVPjzL .node.clickable{cursor:pointer;}#mermaid-svg-A5syRIZs8iDVPjzL .arrowheadPath{fill:#333333;}#mermaid-svg-A5syRIZs8iDVPjzL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A5syRIZs8iDVPjzL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A5syRIZs8iDVPjzL .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-A5syRIZs8iDVPjzL .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-A5syRIZs8iDVPjzL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A5syRIZs8iDVPjzL .cluster text{fill:#333;}#mermaid-svg-A5syRIZs8iDVPjzL .cluster span{color:#333;}#mermaid-svg-A5syRIZs8iDVPjzL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A5syRIZs8iDVPjzL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
- nginx反向代理到我们的web服务,对外提供浏览服务
- 查询mysql数据库用户信息返回用户数据
- 创建用户库,写入用户数据
6.1.1 编写数据初始化脚本
设计数据非常简单的一个表信息,写入两条数据,文件为init.sql
drop database if exists test;
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4 ;
use `test`;
CREATE TABLE `users` (
`sno` int(11) DEFAULT NULL,
`sname` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users (sno,sname) VALUES (1,'pony'), (2,'maxhou');

这样手动操作是可以的。但是我们希望,每次启动服务的时候,会直接运行这些,初始化我的数据。所以这些东西要写到一个sql文件里面去。
这个就叫做数据初始化脚本!

6.1.2 创建一个 springboot 应用, 配置 maven 项目
6.1.3 添加启动类
6.1.4 配置数据库信息
注意本地测试的时候红色部分调整为 ip 地址,而我们实际的服务使用的 mysql 的一个服务名称。
6.1.5 配置用户控制器
简单配置,不再去涉及 mapper 那些内容通过 jdbc 直接完成操作。
6.1.6 编译打包
6.1.7 本地测试
6.1.8 编写 docker-compose.yml
6.1.9 创建工程目录和卷目录
6.1.10 将 nginx 的反向代理配置 bit.conf 放入到./nginx/conf.d
6.1.11 将数据库初始化文件 init.sql 放入到./mysql/init 目录
6.1.12 将应用 jar 包放入到./app 目录
6.1.13 启动我们的项目
6.2 综合案例二
6.2.1 什么是 WordPress
WordPress 是使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设属于自己的网站。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一款个人博客系统,并逐步演化成一款内容管理系统软件,它是使用PHP 语言和 MySQL 数据库开发的,用户可以在支持 PHP 和 MySQL 数据库的服务器上使用自己的博客。
WordPress 有许多第三方开发的免费模板,安装方式简单易用。不过要做一个自己的模板,则需要你有一定的专业知识。比如你至少要懂的标准通用标记语言下的一个应用 HTML 代码、CSS、PHP 等相关知识。WordPress 官方支持中文版,同时有爱好者开发的第三方中文语言包,如 wopus 中文语言包。WordPress 拥有成千上万个各式插件和不计其数的主题模板样式。
6.2.2 编写 docker compose

version: "3.8"
services:
wordpress:
image: wordpress
restart: always
depends_on:
db:
condition: service_healthy
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: mywordpressuser
WORDPRESS_DB_PASSWORD: mywordpresspass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wordpress/:/var/www/html
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
MYSQL_USER: mywordpressuser
MYSQL_PASSWORD: mywordpresspass
volumes:
- ./mysqlvarlib/:/var/lib/mysql
healthcheck:
test: mysql -u root -proot -e "select 1;"
interval: 10s
timeout: 5s
retries: 10
通过这个命令检查这个文件的语法是否有问题。
docker compose config
6.2.3 启动服务+安装博客系统
docker compose up -d

直接用8080端口访问。

输入东西,然后安装。


其实我们所有东西都存在这里了。

Docker从认识到实践再到底层原理(九)|Docker Compose 容器编排的更多相关文章
- AspNetCore容器化(Docker)部署(三) —— Docker Compose容器编排
一.前言 上一篇部署了一个最基础的helloworld应用,创建了两个容器和一个network,还算应付得过来. 如果该应用继续引入mysql.redis.job等若干服务,到时候发布一次得工作量之大 ...
- Docker 介绍以及其相关术语、底层原理和技术
https://ruby-china.org/topics/22004 Docker是啥 Docker是一个程序运行.测试.交付的开放平台,Docker被设计为能够使你快速地交付应用.在Docker中 ...
- Docker Compose 容器编排 NET Core 6+MySQL 8+Nginx + Redis
环境: CentOS 8.5.2111Docker 20.10.10Docker-Compose 2.1.0 服务: db redis web nginx NET Core 6+MySQL 8+N ...
- Docker Compose容器编排
Compose是Docker官方的开源项目,可以实现对Docker容器集群的快速编排.Compose 中有两个重要的概念:服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实 ...
- 八、docker compose容器编排
一. Docker-Compose 1.1. 什么是Docker Compose Compose 项目是 Docker 官方的开源项目,负责实现 Docker 容器集群的快速编排,开源代码在 http ...
- 物联网架构成长之路(24)-Docker练习之Compose容器编排
0.前言 一开始学的之后,是想一步到位直接上Kubernetes(K8s)的,后面没想到,好像有点复杂,有些概念不是很懂.因此学习东西还是要循序渐进,慢慢来.先了解单机编排技术Docker Compo ...
- Docker Compose 容器编排
1. 前言 Docker Compose 是 Docker 容器进行编排的工具,定义和运行多容器的应用,可以一条命令启动多个容器. 使用Compose 基本上分为三步: Dockerfile 定义应用 ...
- Docker从入门到实践
一般说来 SPA 的项目我们只要启一个静态文件 Server 就可以了,但是针对传统项目就不一样了,一个项目会依赖很多服务端程序.之前我们的开发模式是在一台开发机上部署开发环境,所有人都在这台开发机上 ...
- docker学习笔记(1)概述、原理学习、常用命令
一.Docker概述 Docker是基于Go语言实现的云开源项目,诞生于2013年初,目前主流的Linux操作系统已支持Docker,如Redhat RHEL6.5/CentOS6.5.Ubuntu ...
- DOCKER 学习笔记4 认识DockerCompose 多容器编排
前言 通过上一节的学习,学会了如何在Linux 环境下搭建Docker并且部署Springboot 项目,并且成功的跑了起来,当然,在生产环境中,不只是需要一个后端的Web 项目,还需要比如 Ngin ...
随机推荐
- 《vuejs快跑构建触手可及的高性能web应用》读书笔记
1.cdn:内容分发网络(CDN)是将资源托管到全世界各处的服务器上以实现快速分发.CDN版本对于开发和快速验证比较有用,但是将unpkg应用于生产环境前,需要检查它是否适合你. 2.假值包括fals ...
- excel常用函数整理(可检索)
目录: 一.数字函数 1.1 sum 1.2 sumif 1.3 sumifs 1.4 sumproduct 1.5 abs二.统计函数 2.1 count 2.2 counta 2.3 counti ...
- 每天学五分钟 Liunx 0100 | 服务篇:进程状态
多任务和 CPU 时间片 前面说了 Liunx 是多用户多任务的,所谓的多任务就是多个进程"同时"执行.比如,同时开多个软件(进程),对于用户来说好像每个软件(进程)都在工作,但是 ...
- idea相关配置及插件安装
对idea相关的配置及好用的插件进行总结下. 一.idea 破解码及配置:https://www.jb51.net/softs/672190.html 二.idea插件: 1.findBugs-ide ...
- 【架构师视角系列】Apollo配置中心之Client端(二)
原创文章,转载请标注.https://www.cnblogs.com/boycelee/p/17978027 目录 声明 配置中心系列文章 一.客户端架构 1.Config Service职责 (1) ...
- 面试官:Redis持久化能关吗?怎么关?
数据持久化是指将数据从内存中,保存到磁盘或其他持久存储介质的过程,这样做的目的是为了保证数据不丢失. 而 Redis 的持久化功能默认是开启的,这样做的目的也是为了保证程序的稳定性(防止缓存雪崩.缓存 ...
- 基于python+django的电影搜索网站-搜索引擎系统设计与实现
该项目是基于python的web类库django开发的一套web网站,给师弟做的课程设计. 本人的研究方向是一项关于搜索的研究项目.在该项目中,笔者开发了一个简单版的搜索网站,实现了对数据库数据的检索 ...
- mysql之力扣数据库题目620有趣的电影优化记录
闲着没事儿刷刷力扣的数据库题目,题目编号620:有趣的电影,下面是题目描述: 优化前的sql及执行时间: 优化后的sql及执行时间: 这里对筛选条件进行了优化: 1.select * 的查找效率要比逐 ...
- 【C++】static 静态成员
静态成员 静态成员包括静态数据成员和静态成员函数,提供了同类对象间数据成员的共享机制 静态成员不是某个对象的成员,是类所有对象的共享成员,只建立一个成员副本 静态成员的定义与引用 定义格式:stati ...
- JDK21更新特性详解
有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 文章更新计划 文章更新计划 | 430: | String T ...