docker 容器连接, dockerfile

文章目录

  • [docker 容器连接](#docker 容器连接)
    • [端口映射 -P/-p](#端口映射 -P/-p)
    • [docker port 容器名:查"容器端口"对应到了"宿主机哪个地址和端口"](#docker port 容器名:查“容器端口”对应到了“宿主机哪个地址和端口”)
      • [docker port 容器名 查看这个容器所有已经发布的端口映射](#docker port 容器名 查看这个容器所有已经发布的端口映射)
      • [docker port 容器名 5000/docker port 容器名 5000/tcp.只看容器内部这个端口映射到了宿主机哪里](#docker port 容器名 5000/docker port 容器名 5000/tcp.只看容器内部这个端口映射到了宿主机哪里)
    • [Docker 容器互联](#Docker 容器互联)
      • [容器命名 --name](#容器命名 --name)
    • 新建网络-d(driver):这个网络用什么网络驱动创建"
    • [配置 DNS.给 Docker daemon 设置默认 DNS 服务器](#配置 DNS.给 Docker daemon 设置默认 DNS 服务器)
      • [把域名翻译成 IP 地址,这个东西就是 DNS。域名解析系统,把人容易记的名字,翻译成机器要用的地址](#把域名翻译成 IP 地址,这个东西就是 DNS。域名解析系统,把人容易记的名字,翻译成机器要用的地址)
      • [为什么要配置 DNS?遇到域名时,该去问谁来翻译?"](#为什么要配置 DNS?遇到域名时,该去问谁来翻译?”)
      • [为什么 Docker 里经常要配 DNS?不配置 DNS容器默认继承系统或 Docker 的默认解析方式。配置 DNS你是在明确告诉 Docker:以后容器解析域名,优先去问这几个 DNS 服务器。](#为什么 Docker 里经常要配 DNS?不配置 DNS容器默认继承系统或 Docker 的默认解析方式。配置 DNS你是在明确告诉 Docker:以后容器解析域名,优先去问这几个 DNS 服务器。)
      • dns服务器是谁配置的?怎么配置?电脑也没配置,仿佛自动可以解析
      • DNS服务器怎么实现
      • [docker run -it --rm ubuntu cat etc/resolv.conf.开一个临时 Ubuntu 容器,看看容器内部正在使用哪个 DNS 配置](#docker run -it --rm ubuntu cat etc/resolv.conf.开一个临时 Ubuntu 容器,看看容器内部正在使用哪个 DNS 配置)
        • [不是在 /etc/docker/daemon.json 文件 配置的吗?为什么看cat etc/resolv.conf?Docker 的容器 DNS 配置最终会映射到容器里的 resolver 配置文件](#不是在 /etc/docker/daemon.json 文件 配置的吗?为什么看cat etc/resolv.conf?Docker 的容器 DNS 配置最终会映射到容器里的 resolver 配置文件)
      • [为什么要给容器配置DNS?给容器配置 DNS,是为了让容器能正确地把域名解析成 IP,从而正常访问软件源、网站、接口和各种网络服务。](#为什么要给容器配置DNS?给容器配置 DNS,是为了让容器能正确地把域名解析成 IP,从而正常访问软件源、网站、接口和各种网络服务。)
      • 手动指定容器的配置
        • [docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu](#docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu)
          • [-h 主机名,给容器起名](#-h 主机名,给容器起名)
          • [--dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。](#--dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。)
            • [etc/hosts = 把"域名或主机名"手动对应到 IP 地址的本地表](#etc/hosts = 把“域名或主机名”手动对应到 IP 地址的本地表)
  • [Docker Dockerfile](#Docker Dockerfile)
    • [什么是 Dockerfile?文本文件,里面写的是:"怎么一步一步制作一个 Docker 镜像。"](#什么是 Dockerfile?文本文件,里面写的是:“怎么一步一步制作一个 Docker 镜像。”)
      • [RUN:用于执行后面跟着的命令行命令。shell 格式是"让 shell 帮你执行命令",exec 格式是"直接执行程序本身"。](#RUN:用于执行后面跟着的命令行命令。shell 格式是“让 shell 帮你执行命令”,exec 格式是“直接执行程序本身”。)
        • [shell格式,RUN <命令行命令>。# <命令行命令> 等同于,在终端操作的 shell 命令。](# <命令行命令> 等同于,在终端操作的 shell 命令。)
        • [exec 格式:RUN "可执行文件", "参数1", "参数2"](#exec 格式:RUN ["可执行文件", "参数1", "参数2"])
      • [Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。下载、使用、清理,尽量放在同一个 RUN 里完成](#Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。下载、使用、清理,尽量放在同一个 RUN 里完成)
      • [ docker build -t nginx:v3 . 使用当前目录中的 Dockerfile 和相关文件,构建一个名为 nginx:v3 的镜像](# docker build -t nginx:v3 . 使用当前目录中的 Dockerfile 和相关文件,构建一个名为 nginx:v3 的镜像)
        • [. 是构建上下文是当前目录。](#. 是构建上下文是当前目录。)
      • [上下文路径。Dockerfile 在构建时"能访问哪些本地文件"。只准用哪个目录里的材料做镜像](#上下文路径。Dockerfile 在构建时“能访问哪些本地文件”。只准用哪个目录里的材料做镜像)
    • 指令详解
      • COPY,从上下文目录中复制文件或者目录到容器里指定路径。
        • [如果目标以 / 开头,就是相对于镜像根目录的绝对路径;如果不以 / 开头,它就是相对于当前 WORKDIR 的相对路径。目标不存在时,Docker 会创建缺失的目录](#如果目标以 / 开头,就是相对于镜像根目录的绝对路径;如果不以 / 开头,它就是相对于当前 WORKDIR 的相对路径。目标不存在时,Docker 会创建缺失的目录)
        • [结尾有没有 / 很重要](#结尾有没有 / 很重要)
        • [copy默认从 build context 复制,也可以用 --from 从 build stage、named context 或另一个 image 复制](#copy默认从 build context 复制,也可以用 --from 从 build stage、named context 或另一个 image 复制)
      • ADD
        • [本地 tar 自动解压](#本地 tar 自动解压)
        • [ADD 支持从远程 URL 加文件,也支持把 Git 仓库作为源并克隆到镜像里的目标目录](#ADD 支持从远程 URL 加文件,也支持把 Git 仓库作为源并克隆到镜像里的目标目录)
      • CMD
        • [CMD <shell 命令>](#CMD <shell 命令>)
        • [CMD "可执行文件", "参数1", "参数2"(exec form)](#CMD ["可执行文件", "参数1", "参数2"](exec form))
        • [CMD "参数1", "参数2" 给 ENTRYPOINT 提供默认参数的](#CMD ["参数1", "参数2"] 给 ENTRYPOINT 提供默认参数的)
        • [docker run 能覆盖 CMD](#docker run 能覆盖 CMD)
        • [多个 CMD 只有最后一个生效"](#多个 CMD 只有最后一个生效”)
      • [ENTRYPOINT 规定"容器启动时一定要执行的主程序".-entrypoint 覆盖 Dockerfile 里的 ENTRYPOINT](#ENTRYPOINT 规定“容器启动时一定要执行的主程序”.-entrypoint 覆盖 Dockerfile 里的 ENTRYPOINT)
      • [ENV 用来在镜像里设置环境变量。ENV 会持久保留到最终镜像和容器里](#ENV 用来在镜像里设置环境变量。ENV 会持久保留到最终镜像和容器里)
      • [ARG 只在构建过程中可用,默认不会成为镜像里的环境变量,也不会自动出现在容器里](#ARG 只在构建过程中可用,默认不会成为镜像里的环境变量,也不会自动出现在容器里)
      • [VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去](#VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去)
        • [容器里的文件,默认是跟着容器走的.重启容器:容器还是那个容器,里面原来写进去的文件通常还在. 删除容器:那个容器本身没了,写在它自己里面的数据通常也就没了](#容器里的文件,默认是跟着容器走的.重启容器:容器还是那个容器,里面原来写进去的文件通常还在. 删除容器:那个容器本身没了,写在它自己里面的数据通常也就没了)
        • [VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去](#VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去)
          • [挂到宿主机自己指定的目录。宿主机目录:容器目录 → 你自己指定挂到本机哪里](#挂到宿主机自己指定的目录。宿主机目录:容器目录 → 你自己指定挂到本机哪里)
          • [挂到 Docker 自己管理的一块存储。这叫 volume。你不给宿主机具体目录,只给一个卷名。卷名:容器目录 → 交给 Docker 管,宿主机具体位置你通常不用管。docker volume ls 列出docker 管理的卷](#挂到 Docker 自己管理的一块存储。这叫 volume。你不给宿主机具体目录,只给一个卷名。卷名:容器目录 → 交给 Docker 管,宿主机具体位置你通常不用管。docker volume ls 列出docker 管理的卷)
        • [VOLUME /data,它只是在镜像里声明:容器里的 /data 是个适合挂外部存储的位置。真正"外部存储挂哪里",是在 docker run 时决定的](#VOLUME /data,它只是在镜像里声明:容器里的 /data 是个适合挂外部存储的位置。真正“外部存储挂哪里”,是在 docker run 时决定的)
        • 忘了挂载,也会自动挂匿名卷"
        • [docker run -v 能"修改挂载点](#docker run -v 能“修改挂载点)
        • [镜像运行时,Docker 会为 /myvol 创建新 volume,并把镜像里这个位置原本已有的数据初始化到新 volume 里](#镜像运行时,Docker 会为 /myvol 创建新 volume,并把镜像里这个位置原本已有的数据初始化到新 volume 里)
      • [EXPOSE 仅仅只是声明端口](#EXPOSE 仅仅只是声明端口)
      • [WORKDIR:WORKDIR 会为后续的 RUN、CMD、ENTRYPOINT、COPY、ADD 设置工作目录;如果目录不存在,会自动创建。给后面的命令指定"默认当前目录"](#WORKDIR:WORKDIR 会为后续的 RUN、CMD、ENTRYPOINT、COPY、ADD 设置工作目录;如果目录不存在,会自动创建。给后面的命令指定“默认当前目录”)
        • [RUN 文件系统变化会保留,shell 的"当前状态"不会保留。](#RUN 文件系统变化会保留,shell 的“当前状态”不会保留。)
        • [WORKDIR 可以写多次,而且相对路径会基于前一个 WORKDIR 继续拼。绝对路径,直接重设](#WORKDIR 可以写多次,而且相对路径会基于前一个 WORKDIR 继续拼。绝对路径,直接重设)
      • HEALTHCHECK
      • [ONBUILD 先埋一个触发器,现在不执行,等别人拿这个镜像当基础镜像时再执行](#ONBUILD 先埋一个触发器,现在不执行,等别人拿这个镜像当基础镜像时再执行)
      • [LABEL 给镜像贴"说明标签"](#LABEL 给镜像贴“说明标签”)
      • [STOPSIGNAL:设置"当 Docker 要停止容器时,先发给容器主进程的那个信号"](#STOPSIGNAL:设置“当 Docker 要停止容器时,先发给容器主进程的那个信号”)

docker 容器连接

端口映射 -P/-p

-P:镜像里声明的端口自动映射到宿主机的随机端口

-P:把镜像里通过 EXPOSE 声明的所有端口,自动发布到宿主机的随机高位端口。Docker 官方也区分了 EXPOSE 和发布端口:EXPOSE 只是声明,真正发布到宿主机要靠 -p 或 -P

-p :宿主机IP:宿主机端口:容器端口/协议

宿主机IP:\]宿主机端口:容器端口\[/协议

如果不写宿主机 IP,Docker 默认会把端口发布到 所有网络接口,也就是通常看到的 0.0.0.0;如果写了 127.0.0.1,那就只有宿主机自己能访问;如果写了 /udp,那发布的就是 UDP 端口,不再是默认的 TCP。
TCP 和 UDP 是两套不同的端口映射。 也就是说,宿主机的 5000/tcp 和 5000/udp 不是同一个东西。




docker port 容器名:查"容器端口"对应到了"宿主机哪个地址和端口"

docker port 容器名 查看这个容器所有已经发布的端口映射

docker port 容器名 5000/docker port 容器名 5000/tcp.只看容器内部这个端口映射到了宿主机哪里

Docker 容器互联

容器命名 --name

新建网络-d(driver):这个网络用什么网络驱动创建"

-d 是 --driver,意思是:创建网络时,指定用哪一种网络驱动(network driver)。这个网络用什么网络驱动创建"。

Docker 官方说明,docker network create 里内置常见驱动有 bridge 和 overlay;如果你不写 --driver,默认会创建 bridge 网络。

bridge:一台机器里的容器网络

overlay:多台机器之间的容器网络


bridge:一台机器内部,给几个容器拉了一个局域网。同一台电脑里的容器互相连网


overlay:跨多台机器,把各台机器上的容器接成一个"虚拟大局域网"。跨多台电脑,把容器接到一个大网里


docker network create -d bridge test-net。在当前这台 Docker 主机上,建一个给容器用的本地桥接网络 test-net

配置 DNS.给 Docker daemon 设置默认 DNS 服务器

把域名翻译成 IP 地址,这个东西就是 DNS。域名解析系统,把人容易记的名字,翻译成机器要用的地址

DNS 就是把域名翻译成 IP 地址的系统;配置 DNS,就是指定"找谁来做这个翻译"。

配置 DNS 里的 IP,不是你要访问的目标地址,而是你拿来"问路"的服务器地址。

输入域名 → 问 DNS → 拿到目标 IP → 再访问网站。


为什么要配置 DNS?遇到域名时,该去问谁来翻译?"

为什么 Docker 里经常要配 DNS?不配置 DNS容器默认继承系统或 Docker 的默认解析方式。配置 DNS你是在明确告诉 Docker:以后容器解析域名,优先去问这几个 DNS 服务器。





dns服务器是谁配置的?怎么配置?电脑也没配置,仿佛自动可以解析

第一层:路由器/运营商在配
路由器,"网络总管"



电脑 → 路由器 → 运营商网络 → 互联网 → 网站服务器




第二层:公司网络管理员在配
第三层:自己手动配

DNS服务器怎么实现

DNS 服务器的实现,本质上就是一个维护"域名记录"的网络服务:它要么自己保存答案,要么按分层规则替你继续询问,最后把域名对应的结果返回给客户端。











docker run -it --rm ubuntu cat etc/resolv.conf.开一个临时 Ubuntu 容器,看看容器内部正在使用哪个 DNS 配置

-it

给你一个交互终端环境。

不过这条命令里其实不是必须,因为 cat 不是交互程序,不加通常也能跑。

--rm

容器运行结束后自动删除。

所以这是一个"一次性临时容器"。
启动一个临时 Ubuntu 容器

在容器里执行 cat etc/resolv.conf

把文件内容输出到终端

命令结束,容器自动删除

不是在 /etc/docker/daemon.json 文件 配置的吗?为什么看cat etc/resolv.conf?Docker 的容器 DNS 配置最终会映射到容器里的 resolver 配置文件

"因为 daemon.json 是配置来源,resolv.conf 是容器里的实际结果,所以要看结果文件来验证配置是否生效。"



为什么要给容器配置DNS?给容器配置 DNS,是为了让容器能正确地把域名解析成 IP,从而正常访问软件源、网站、接口和各种网络服务。



手动指定容器的配置

docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu

临时开一个 Ubuntu 容器,主机名叫 host_ubuntu,查域名优先问 114.114.114.114,短主机名默认补 .test.com
--dns 解决的是:问谁查地址

--dns-search 解决的是:遇到短名字时,自动补什么后缀。自动补域名后缀

-h 主机名,给容器起名


--dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
etc/hosts = 把"域名或主机名"手动对应到 IP 地址的本地表




Docker Dockerfile

什么是 Dockerfile?文本文件,里面写的是:"怎么一步一步制作一个 Docker 镜像。"

它是干什么的

它告诉 Docker:

以哪个基础镜像开始

要装哪些软件

要复制哪些文件进去

工作目录设在哪

容器启动时执行什么命令




RUN:用于执行后面跟着的命令行命令。shell 格式是"让 shell 帮你执行命令",exec 格式是"直接执行程序本身"。

shell格式,RUN <命令行命令>。# <命令行命令> 等同于,在终端操作的 shell 命令。


exec 格式:RUN "可执行文件", "参数1", "参数2"

exec 格式里第一个元素,一般就是要直接运行的"可执行文件"或命令。

exec 格式里,第一个元素就是要直接启动的程序,后面的元素都是传给它的参数。




Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。下载、使用、清理,尽量放在同一个 RUN 里完成






$ docker build -t nginx:v3 . 使用当前目录中的 Dockerfile 和相关文件,构建一个名为 nginx:v3 的镜像

-t nginx:v3

给构建出来的镜像打标签。

这里:

nginx 是镜像名

v3 是标签 tag

. 是构建上下文是当前目录。


上下文路径。Dockerfile 在构建时"能访问哪些本地文件"。只准用哪个目录里的材料做镜像


为什么要有这个概念?





指令详解

COPY,从上下文目录中复制文件或者目录到容器里指定路径。

COPY = 从构建可访问的来源里拿文件,复制到当前镜像阶段里;源路径看 context,目标路径看 / 和 WORKDIR,目标不存在会自动建,结尾 / 会影响它被当成文件还是目录。

如果目标以 / 开头,就是相对于镜像根目录的绝对路径;如果不以 / 开头,它就是相对于当前 WORKDIR 的相对路径。目标不存在时,Docker 会创建缺失的目录




结尾有没有 / 很重要
copy默认从 build context 复制,也可以用 --from 从 build stage、named context 或另一个 image 复制




ADD

ADD 比 COPY 多了一些"额外能力",但正因为它会偷偷多做事,所以平时更推荐用 COPY。 Docker 官方现在的定义是:ADD 除了能从 build context 复制本地文件/目录,还能直接处理 远程 URL 和 Git 仓库;而 COPY 更"单纯",主要就是复制文件

本地 tar 自动解压



ADD 支持从远程 URL 加文件,也支持把 Git 仓库作为源并克隆到镜像里的目标目录


CMD

CMD 是给容器设置"默认启动命令"。

如果你运行容器时没有额外指定命令,就执行它;如果你在 docker run 后面又写了别的命令,通常会把它覆盖掉。Docker 官方对 CMD 的定义就是:为执行中的容器提供默认命令,且一个 Dockerfile 里只有最后一个 CMD 生效。

CMD <shell 命令>
CMD "可执行文件", "参数1", "参数2"(exec form)
CMD "参数1", "参数2" 给 ENTRYPOINT 提供默认参数的
docker run 能覆盖 CMD


多个 CMD 只有最后一个生效"

ENTRYPOINT 规定"容器启动时一定要执行的主程序".-entrypoint 覆盖 Dockerfile 里的 ENTRYPOINT

ENTRYPOINT 用来规定"容器启动时一定要执行的主程序",而 CMD 更像是给这个主程序提供"默认参数"。 Docker 官方也是这么描述两者配合关系的:ENTRYPOINT 更适合把容器当成一个可执行程序来用,CMD 则适合给 ENTRYPOINT 提供默认参数;docker run 镜像 参数... 会覆盖 CMD,并把这些参数追加到 exec 形式的 ENTRYPOINT 后面




菜鸟应该是写错了

ENV 用来在镜像里设置环境变量。ENV 会持久保留到最终镜像和容器里

ENV 用来在镜像里设置环境变量。

设置以后,这个变量不仅能在后续的 RUN、CMD、ENTRYPOINT 等指令里用到,而且将来用这个镜像启动出来的容器里也会保留下来。




ARG 只在构建过程中可用,默认不会成为镜像里的环境变量,也不会自动出现在容器里

ARG 就是 构建时参数:它主要给 Dockerfile 的构建过程用,可以让你在 docker build 时传不同的值;但它不会像

ENV 那样自动变成运行中容器里的环境变量



VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去

"挂外部存储"就是把容器里的某个目录,接到容器外面去。外面可以是你电脑上的一个真实目录,也可以是 Docker 帮你管理的一块数据区

容器里的文件,默认是跟着容器走的.重启容器:容器还是那个容器,里面原来写进去的文件通常还在. 删除容器:那个容器本身没了,写在它自己里面的数据通常也就没了




VOLUME 这个目录里的数据,不要只放在容器自己身上,最好单独存出去



"挂外部存储"就是把容器里的某个目录,接到容器外面去。外面可以是你电脑上的一个真实目录,也可以是 Docker 帮你管理的一块数据区

挂到宿主机自己指定的目录。宿主机目录:容器目录 → 你自己指定挂到本机哪里
挂到 Docker 自己管理的一块存储。这叫 volume。你不给宿主机具体目录,只给一个卷名。卷名:容器目录 → 交给 Docker 管,宿主机具体位置你通常不用管。docker volume ls 列出docker 管理的卷
VOLUME /data,它只是在镜像里声明:容器里的 /data 是个适合挂外部存储的位置。真正"外部存储挂哪里",是在 docker run 时决定的


忘了挂载,也会自动挂匿名卷"
docker run -v 能"修改挂载点
镜像运行时,Docker 会为 /myvol 创建新 volume,并把镜像里这个位置原本已有的数据初始化到新 volume 里

EXPOSE 仅仅只是声明端口


WORKDIR:WORKDIR 会为后续的 RUN、CMD、ENTRYPOINT、COPY、ADD 设置工作目录;如果目录不存在,会自动创建。给后面的命令指定"默认当前目录"

RUN 文件系统变化会保留,shell 的"当前状态"不会保留。

RUN 的文件改动会保留到下一层,所以你 RUN mkdir /app 创建的目录,后面通常也还在。

但是你在某个 RUN 里临时 cd 到别的目录,这个"当前目录状态"不会自动延续到下一个 RUN





WORKDIR 可以写多次,而且相对路径会基于前一个 WORKDIR 继续拼。绝对路径,直接重设


HEALTHCHECK




ONBUILD 先埋一个触发器,现在不执行,等别人拿这个镜像当基础镜像时再执行

ONBUILD 是在当前 Dockerfile 构建完成之后,当该镜像被别的 Dockerfile FROM 时才执行






LABEL 给镜像贴"说明标签"



STOPSIGNAL:设置"当 Docker 要停止容器时,先发给容器主进程的那个信号"


信号(signal)就是操作系统发给进程的一种"通知"






相关推荐
XIAOHEZIcode3 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户03284722207019 小时前
如何搭建本地yum源(上)
运维
武子康20 小时前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
大树884 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质4 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工4 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
Alsn864 天前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
酣大智4 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化