Docker系列04—跨主机网络方案(overlay/weave)
在前面详细讲解了几种网络模式:none,host,bridge,container。他们解决了单个主机间的容器的通信问题,并不能实现多个主机容器之间的通信。
跨主机网络方案包括两大类:
1,docker原生的:overlay和macvlan
2,第三方方案:flannel、weave和calico
overlay
Docker overlay网络需要一个key-value数据库用于保存网络状态信息,包括Network、Endpoint、IP等。
consul、Etcd、Zookeeper都是Docker支持的key-value软件,这里我们使用consul。
在docker主机docker01(10.0.0.11)和docker02(10.0.0.12)上实践各种跨主机网络方案,在10.0.0.11上部署支持的组件,比如consul。
第一步:启动路由转发功能
【两台主机都需开启】
[root@docker01 ~]# echo "1" > /proc/sys/net/ipv4/ip_forward
[root@docker02 ~]# echo "1" > /proc/sys/net/ipv4/ip_forward
第二步:运行consul
以容器的方式运行consul
[root@docker01 ~]# systemctl restart docker
[root@docker01 ~]# systemctl enable docker
[root@docker01 ~]# docker pull progrium/consul
Using default tag: latest
latest: Pulling from progrium/consul [root@docker01 ~]# docker run -d -p 8500:8500 -h consul --name consul --restart=always progrium/consul -server -bootstrap
### 其中 -h 为指定容器内部的主机名;--restart=always 为docker启动时,此容器自动启动;-server -bootstrap 表示当在群集中,加上这两个选项可以使其以master的身份出现 [root@docker01 ~]# netstat -lntp | grep 8500
tcp6 0 0 :::8500 :::* LISTEN 1576/docker-proxy
第三步:浏览器访问
通过ip+port的方式访问
第四步:修改docker启动文件
需要修改的是docker01和docker02的docker daemon 的配置文件。
【注】在这里我们使用的是两台。如果是3台,可以为3台都设置:表示这3台准备跨主机;也可以将第一台设置为只存放consul,2,3台修改配置文件即可:表示后两台准备跨主机。
【注】只修改[Service]内容即可
[root@docker01 ~]# cat /usr/lib/systemd/system/docker.service
...
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2376 --cluster-store=consul://10.0.0.11:8500 --cluster-advertise=eth0:2376
# ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
... ### 各项解释如下
# /var/run/docker.sock 表示Docker的一个编程接口;
# '-H tcp://0.0.0.0:2376' 表示使用本机的2376/tcp端口;
# '--cluster-store=consul://10.0.0.11:8500' 表示运行了consul的服务器IP和端口
# '--cluster-advertise=eth0:2376' 表示从本机的eth0网卡通过2376端口收集网络信息,也有可能是ens33。
[root@docker02 ~]# cat /usr/lib/systemd/system/docker.service
...
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2376 --cluster-store=consul://10.0.0.11:8500 --cluster-advertise=eth0:2376
# ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
...
重新加载docker daemon,修改了文件的服务都需要重启
[root@docker01 ~]# systemctl daemon-reload
[root@docker01 ~]# systemctl restart docker
这里注意,如果consul容器没有设置为自启,那么需要手动启动
[root@docker02 ~]# systemctl daemon-reload
[root@docker02 ~]# systemctl restart docker
第五步:浏览器查看
这时发现两个主机已经注册上去了
第六步:创建overlay网络
在docker01上创建网络ov_ken
[root@docker01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c8d69c4c3ffe bridge bridge local
f5d3339712b4 host host local
93359dad0336 none null local [root@docker01 ~]# docker network create -d overlay ov_ken [root@docker01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c8d69c4c3ffe bridge bridge local
f5d3339712b4 host host local
93359dad0336 none null local
70c2c8b47ead ov_ken overlay global ### 这个时候docker02上不用添加就会自动添加
[root@docker02 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
7c9560f3b566 bridge bridge local
e76394dcf8f8 host host local
4cdde3f7dce2 none null local
70c2c8b47ead ov_ken overlay global
### 因为创建ov_ken时docker01将overlay网络信息存入了consul,docker02从consul读取了新网络的数据。
查看网络信息
[root@docker01 ~]# docker network inspect ov_ken
[
{
"Name": "ov_ken",
"Id": "70c2c8b47ead8af423a6b562aba42f1106454b58577c27cf47143a9eacdb419a",
"Created": "2020-04-13T11:47:08.079686153+08:00",
"Scope": "global",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "10.0.0.0/24",
"Gateway": "10.0.0.1"
}
]
},
### 由此可以看出docker自动为ov_ken分配的ip地址空间为10.0.0.0/24
在overlay网络中运行容器
运行一个busybox容器并使用overlay网络
[root@docker01 ~]# docker run -d -it --name busybox --network ov_ken busybox:latest
3492931078a8d2cf2a7e77b7a8518c3f89f64c0b803a79ffc9742965581d96fa
6.1 跨主机连通
在docker02上运行新的容器
[root@docker02 ~]# docker run -it -d --name busybox_02 --network ov_ken busybox:latest
53e708b68a31d5f08bce6f949426e64ce333e4ee9f28396271b2a6eb3daad182
查看容器的网络
[root@docker02 ~]# docker exec busybox_02 ip r
default via 172.18.0.1 dev eth1
10.0.0.0/24 dev eth0 scope link src 10.0.0.3
172.18.0.0/16 dev eth1 scope link src 172.18.0.2
测试使用docker01上的容器ping docker02上的容器
[root@docker01 ~]# docker exec busybox ip r
default via 172.18.0.1 dev eth1
10.0.0.0/24 dev eth0 scope link src 10.0.0.2
172.18.0.0/16 dev eth1 scope link src 172.18.0.2 [root@docker01 ~]# docker exec busybox ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=3.657 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=0.351 ms
--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.351/2.004/3.657 ms
至此:overlay容器是可以直接通信,同时docker也实现了DNS服务。
6.2 overlay网络隔离
不同的overlay网络是相互隔离的。我们创建第二个overlay网络ov_ken2并运行容器
[root@docker01 ~]# docker network create -d overlay ov_ken2
0dd70337a3f1504c9d24891c9e37d74af35abcad0ce3751ac0efa5ad138cb12c [root@docker01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c8d69c4c3ffe bridge bridge local
2436e5e8c848 docker_gwbridge bridge local
f5d3339712b4 host host local
93359dad0336 none null local
70c2c8b47ead ov_ken overlay global
0dd70337a3f1 ov_ken2 overlay global [root@docker01 ~]# docker run -d -it --name busybox_03 --network ov_ken2 busybox:latest
a5963fd6a9b863613269f773069fa99630fc88d7af49a3b355e78c9060858699 [root@docker01 ~]# docker exec busybox_03 ip r
default via 172.18.0.1 dev eth1
10.0.1.0/24 dev eth0 scope link src 10.0.1.2
172.18.0.0/16 dev eth1 scope link src 172.18.0.3
### 在这里我们能看到docker给第二个overlay分配的ip地址为10.0.1.0/24 [root@docker01 ~]# docker exec busybox_03 ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
--- 10.0.0.3 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
### 在这里我们发现ov_ken和ov_ken2并不互通
如果要实现busybox_03和busybox_02的互通,可以将busybox_03也连接到ov_ken网络上
[root@docker01 ~]# docker network connect ov_ken busybox_03
### 意思为,将busybox_03添加到ov_ken网络 [root@docker01 ~]# docker exec busybox_03 ip r
default via 172.18.0.1 dev eth1
10.0.0.0/24 dev eth2 scope link src 10.0.0.4
10.0.1.0/24 dev eth0 scope link src 10.0.1.2
172.18.0.0/16 dev eth1 scope link src 172.18.0.3
### 这个时候会发现busybox_03会被添加一个新的ov_ken的网段IP [root@docker01 ~]# docker exec busybox_03 ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=6.571 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=0.411 ms
--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.411/3.491/6.571 ms
weave
weave不依赖分布式数据库(例如etcd和consul)交换网络信息,每个主机上只需要运行weave组件就能建立起跨主机容器网络。接下来在docker01和docker02上部署weave并实践weave的各项特性
一、安装部署weave
weave安装非常简单,在docker01和docker02上执行如下命令:
[root@docker01 ~]# curl -L git.io/weave -o /usr/local/bin/weave
[root@docker01 ~]# chmod a+x /usr/local/bin/weave [root@docker02 ~]# curl -L git.io/weave -o /usr/local/bin/weave
[root@docker02 ~]# chmod a+x /usr/local/bin/weave
在docker01上启动weave
[root@docker01 ~]# weave launch
注:在执行weave launch命令,启动weave相关服务。weave所有的组件都是以容器的方式运行的,会自动从docker hub上获取最新的image并启动容器
查看启动的容器
[root@docker01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e089485df46f weaveworks/weave:2.6.2 "/home/weave/weaver …" 55 seconds ago Up 54 seconds weave
weave运行了一个容器:
weave是主程序,负责建立weave网络,收发数据,提供DNS服务等
在docker01中启动容器
[root@docker01 ~]# eval $(weave env)
[root@docker01 ~]# docker run -d -it --name busybox_01 --rm busybox:latest
a1a4f0931a65b36067f8d27e6474fee584d003ac302e9c7ac4e517f48b0d46a2
注意:在这里因为是测试,用到了 --rm :关闭即删除,删除,删除 !!
首先执行 eval $(weave env) 很重要,其作用是将后续的docker命令发送给weave proxy处理,如果要恢复之前的环境,可执行eval $(weave env --restore)。
查看当前容器busybox_01的网络配置
[root@docker01 ~]# docker exec busybox_01 ip r
default via 172.17.0.1 dev eth0
10.32.0.0/12 dev ethwe scope link src 10.32.0.1
172.17.0.0/16 dev eth0 scope link src 172.17.0.2
224.0.0.0/4 dev ethwe scope link ### 由此可以看出,docker给其分配的地址为10.32.0.0/12网段,并且IP地址为10.32.0.1
### 而且busybox_01有两个网络接口eth0和ethwe,其中eth0是默认连接的bridge网络,即网桥docker0。分配的IP 10.32.0.1/12 ethwe与weave相关
在当前主机上再运行一个容器
[root@docker01 ~]# docker run -d -it --name busybox_02 --rm busybox:latest
88f208a2ca486eccc346ed7239f44836587796ce7898f48b65e162bfebe522e6 [root@docker01 ~]# docker exec busybox_02 ip r
default via 172.17.0.1 dev eth0
10.32.0.0/12 dev ethwe scope link src 10.32.0.2
172.17.0.0/16 dev eth0 scope link src 172.17.0.3
224.0.0.0/4 dev ethwe scope link
测试同一个主机上的busybox_01和busybox_02的连通性
[root@docker01 ~]# docker exec busybox_01 ping -c 2 busybox_02
PING busybox_02 (10.32.0.2): 56 data bytes
64 bytes from 10.32.0.2: seq=0 ttl=64 time=0.051 ms
64 bytes from 10.32.0.2: seq=1 ttl=64 time=0.050 ms --- busybox_02 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.050/0.050/0.051 ms ### 在这里是可以连通的,并且还可以解析
二、weave跨主机通信
第一步:再docker02上执行以下命令
[root@docker02 ~]# weave launch 10.0.0.11
## 格式 weave launch [docker01的ip地址]
这里必须指定docker01的ip地址,这样docker01和docker02才能加入到同一个weave网络。
第二步:开启路由转发功能
[root@docker01 ~]# echo "1">/proc/sys/net/ipv4/ip_forward
[root@docker02 ~]# echo "1">/proc/sys/net/ipv4/ip_forward
第三步:启动容器
[root@docker02 ~]# docker run -d -it --name busybox_03 --rm busybox:latest
82503c97ee984d5a330f635eeca825edc795c7ef96a8fc592da9dc8598764594
第四步:测试连通性
[root@docker02 ~]# docker exec busybox_03 ping -c 2 busybox_02
PING busybox_02 (10.32.0.2): 56 data bytes
64 bytes from 10.32.0.2: seq=0 ttl=64 time=1.477 ms
64 bytes from 10.32.0.2: seq=1 ttl=64 time=1.187 ms --- busybox_02 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.187/1.332/1.477 ms ### 可以发现主机docker02上运行的容器可以与docker01上的容器通信,而且可以解析主机名
三、weave网络隔离
默认配置下,weave使用一个大subnet(例如10.32.0.0/12),所有的主机的容器都是从这个地址空间中分配IP,因为同属一个subnet,容器可以直接通信。如果要实现网络隔离,可以通过环境变量WEAVE_CIDR为容器分配不同的subnet的IP,举例如下:
[root@docker02 ~]# docker run -d -it -e WEAVE_CIDR=net:10.32.2.0/24 --name busybox_04 --rm busybox:latest
d3a4430bbc111c9f7eca1ab9c4b6f432f63d9e45d66ea0d0d66b3dfecfa75eb6
在这里WEAVE_CIDR=net:10.32.2.0/24的作用是使容器分配到IP 10.32.2.2。由于10.32.0.0/12 与 10.32.2.0/24位于不同的subnet,所以无法ping到busybox_02。
[root@docker02 ~]# docker exec busybox_04 ping -c 2 busybox_02
PING busybox_02 (10.32.0.2): 56 data bytes
--- busybox_02 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
当然也无法ping同同主机上的busybox_03
除了subnet,我们还可以直接为容器分配特定的ip:
[root@docker02 ~]# docker run -d -it -e WEAVE_CIDR=ip:10.32.6.6/24 --name busybox_05 busybox:latest
b2eccb69e37e5da8558237163103f050a3efc118cf71bf11c62c68b572e2b8a6
[root@docker02 ~]# docker exec busybox_05 ip r
default via 172.17.0.1 dev eth0
10.32.6.0/24 dev ethwe scope link src 10.32.6.6
172.17.0.0/16 dev eth0 scope link src 172.17.0.4
224.0.0.0/4 dev ethwe scope link
四、weave与外网的连通性
weave是一个私有的VLAN网络(容器可以访问外网),默认与外部网络隔离。外部网络如何才能访问到weave中的容器呢?
答案:
首先将主机加入到weave网络。
然后把主机当作访问的网关。
第一步:要将主机加入到weave,执行weave expose。
[root@docker02 ~]# weave expose
10.32.2.129
第二步:查看ip
这个10.32.2.129会被配置到host2的weave网桥上。
[root@docker02 ~]# ip a
...
13: weave: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue state UP qlen 1000
link/ether c6:77:0c:f6:e2:29 brd ff:ff:ff:ff:ff:ff
inet 10.32.2.129/12 brd 10.47.255.255 scope global weave
valid_lft forever preferred_lft forever
inet6 fe80::c477:cff:fef6:e229/64 scope link
valid_lft forever preferred_lft forever
第三步:测试连通性
ping同一主机的busybox_03
[root@docker02 ~]# docker exec busybox_03 ip r
default via 172.17.0.1 dev eth0
10.32.0.0/12 dev ethwe scope link src 10.44.0.0
172.17.0.0/16 dev eth0 scope link src 172.17.0.2
224.0.0.0/4 dev ethwe scope link [root@docker02 ~]# ping -c 2 10.44.0.0
PING 10.44.0.0 (10.44.0.0) 56(84) bytes of data.
64 bytes from 10.44.0.0: icmp_seq=1 ttl=64 time=0.123 ms
64 bytes from 10.44.0.0: icmp_seq=2 ttl=64 time=0.048 ms
--- 10.44.0.0 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.048/0.085/0.123/0.038 ms
ping docker01上的busybox_01
[root@docker01 ~]# docker exec busybox_01 ip r
default via 172.17.0.1 dev eth0
10.32.0.0/12 dev ethwe scope link src 10.32.0.1
172.17.0.0/16 dev eth0 scope link src 172.17.0.2
224.0.0.0/4 dev ethwe scope link [root@docker02 ~]# ping -c 2 10.32.0.1
PING 10.32.0.1 (10.32.0.1) 56(84) bytes of data.
64 bytes from 10.32.0.1: icmp_seq=1 ttl=64 time=2.16 ms
64 bytes from 10.32.0.1: icmp_seq=2 ttl=64 time=0.337 ms --- 10.32.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.337/1.252/2.168/0.916 ms
接下来要让其他非weave主机访问到busybox_01和busybox_03,只需要将网关指向docker01。例如在新的主机docker03上添加如下路由:
[root@docker03 ~]# ip route add 10.32.0.0/12 via 10.0.0.11 [root@docker03 ~]# ping -c 2 10.32.0.1
PING 10.32.0.1 (10.32.0.1) 56(84) bytes of data.
64 bytes from 10.32.0.1: icmp_seq=1 ttl=63 time=1.45 ms
64 bytes from 10.32.0.1: icmp_seq=2 ttl=63 time=2.25 ms
--- 10.32.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.450/1.852/2.254/0.402 ms
注意:10.32.0.0/12是weave网络使用的默认subnet,如果此地址空间与现有IP冲突,可以通过 --ipalloc-range 分配特定的subnet。
weave launch --ipalloc-range 10.2.0.0/16
不过需要确保所有的host都使用相同的subnet
【注意】如果出现以下情况
[root@docker02 ~]# docker ps
Cannot connect to the Docker daemon at unix:///var/run/weave/weave.sock. Is the docker daemon running?
需要执行
[root@docker02 ~]# eval $(weave env --restore)
Docker系列04—跨主机网络方案(overlay/weave)的更多相关文章
- docker 跨主机网络:overlay 简介
简介 docker 在早前的时候没有考虑跨主机的容器通信,这个特性直到 docker 1.9 才出现.在此之前,如果希望位于不同主机的容器能够通信,一般有几种方法: 使用端口映射:直接把容器的服务端口 ...
- Docker 跨主机网络方案分析
PS:文章首发公众号,欢迎大家关注我的公众号:aCloudDeveloper,专注技术分享,努力打造干货分享平台,二维码在文末可以扫,谢谢大家. 上篇文章介绍了容器网络的单主机网络,本文将进一步介绍多 ...
- Docker跨主机网络——overlay
前言 在Docker网络--单host网络一文中,我为大家总结了Docker的单机网络相关知识和操作,单机网络比较容易.本文我为大家总结Docker跨主机通信相关知识.同样本文大部分内容以CloudM ...
- centos7下安装docker(15.2跨主机网络-overlay)
为支持容器跨主机通信,Docker提供了overlay driver,使用户可以创建基于VxLAN的overlay网络.VxLAN可将二层数据封装到UDP进行传输,VxLAN提供与VLAN相同的以太网 ...
- Docker 跨主机网络 overlay(十六)
目录 一.跨主机网络概述 二.准备 overlay 环境 1.环境描述 2.创建 consul 3.修改 docker 配置文件 4.准备就绪 三.创建 overlay 网络 1.在 host1 中创 ...
- 跨主机网络概述 - 每天5分钟玩转 Docker 容器技术(48)
前面已经学习了 Docker 的几种网络方案:none.host.bridge 和 joined 容器,它们解决了单个 Docker Host 内容器通信的问题.本章的重点则是讨论跨主机容器间通信的方 ...
- Docker 跨主机网络
Docker提供两种原生的跨主机网络: Overlay 和 Macvlan libnetwork & CNM libnetwork 是 docker 容器网络库,最核心的内容是其定义的 C ...
- 跨主机网络-overlay(18)
docker overlay跨主机网络 .环境 docker版本 Docker version -ce, build fc4de44 3台主机 192.168.55.51 host1 192.168. ...
- [Kubernetes]谈谈容器跨主机网络
继上篇文章:[Kubernetes]浅谈容器网络,自己给自己挖的坑,这篇文章来谈谈容器跨主机网络. 要理解容器"跨主通信"的原理,就要来谈谈 Flannel 这个项目. Flann ...
随机推荐
- springboot利用redis做缓存
首先 配置redis redis: password: 123456 host: 127.0.0.1 port: 6379 #103.249.252.109:10086 expireSeconds: ...
- JavaScript实时显示当前时间
1.HTML部分 <div id="div1">显示当前时间!</div> 2.css部分 #div1 { width: 700px; height: 50 ...
- shell-的变量-局部变量
1. 定义本地变量 本地变量在用户当前的shell生产期的脚本中使用.例如,本地变量OLDBOY取值为ett098,这个值只在用户当前shell生存期中有意义.如果在shell中启动另一个进程或退出, ...
- NB 的开源项目遍地开花——GitHub 热点速览 Vol.41
作者:HelloGitHub-小鱼干 本周的 GitHub 热点速览的关键词便是 nb,也是本周特推之一的项目名字,这个功能如名字一般 nb 的项目是一个脚本,帮你处理笔记.书签.归档和知识库应用程序 ...
- JS中实现Trim(),TrimStart(),TrimEnd() 的方法
//去除字符串头尾空格或指定字符 String.prototype.Trim = function (c) { if (c == null || c == "") { var st ...
- 【折半枚举+二分】POJ 3977 Subset
题目内容 Vjudge链接 给你\(n\)个数,求出这\(n\)个数的一个非空子集,使子集中的数加和的绝对值最小,在此基础上子集中元素的个数应最小. 输入格式 输入含多组数据,每组数据有两行,第一行是 ...
- BOOST库 消息队列
直接贴实验代码: /******* boost 消息队列 **********/ #if 1 #include <boost/thread/thread.hpp> #include < ...
- python爬虫 -掘金
import json from time import sleep import requests url = "https://web-api.juejin.im/query" ...
- 【Azure Redis 缓存 Azure Cache For Redis】当使用Jedis客户端连接Redis时候,遇见JedisConnectionException: Could not get a resource from the pool / Redis connection lost
问题情形 当在执行Redis一直指令时,有可能会遇见如下几种错误: 1) redis.clients.jedis.exceptions.JedisConnectionException: Could ...
- Chimm.Excel —— 使用Java 操作 excel 模板文件生成 excel 文档
Chimm.Excel -- 设置模板,填充数据,就完事儿了~ _____ _ _ _____ _ / __ \ | (_) | ___| | | | / \/ |__ _ _ __ ___ _ __ ...