赞
踩
Docker Swarm 是 Docker 公司推出的用来管理 docker 集群的工具, 使用 Docker Swarm 可以快速方便的实现 高可用集群 ,Docker Compose 只能编排单节点上的容器, Docker Swarm 可以让我们在单一主机上操作来完成对 整个容器集群 的管理工作,使用Docker Swarm 可以 让单一主机上的容器快速部署到10个、20个或者200个主机上面,实现高可用集群. 从 Docker 1.12.0 版本开始, Docker Swarm 已经包含在 Docker 引擎中( docker swarm ),并且已经内置了服务发现工具,就不需要像之前一样,再配置 Etcd 或者 Consul 来进行服务发现配置了.Dokcer Swarm是 Docker 官方的提供的集群部署管理工具,基于原生 Docker ,相比 K8s 要 更简单 、 更容易上手, 如果您的节点(物理机)在1000 台以内,完全可以使用 Docker Swarm 实现部署,学会Docker Swarm,学 K8s 会更简单一些, Swarm 和 Kubernetes 比较类似,但是更加轻,具有的功能也较kubernetes更少一些
- 是docker host集群管理工具
- docker官方提供的
- docker 1.12版本以后
- 用来统一集群管理的,把整个集群资源做统一调度
- 比kubernetes要轻量化
- 实现scaling 规模扩大或缩小
- 实现rolling update 滚动更新或版本回退
- 实现service discovery 服务发现
- 实现load balance 负载均衡
- 实现route mesh 路由网格,服务治理
官网地址: https://docs.docker.com/engine/reference/commandline/swarm/
Docker swarm 中有 节点 的概念, 一个节点就可以通俗的理解为一个机器 ( 可以是物理服务器也可以是虚拟机、云等等)
节点分为: 管理 (manager) 节点和工作 (worker) 节点
管理节点(Manager):管理节点用于 Swarm 集群的管理 , 负责集群控制,能过监控集群状态、分发任务到工作节点 , docker swarm 命令基本只能在管理节点执行 (节点退出集群命令 docker swarm leave 可以在工作节点执行), 一个 Swarm 集群可以有多个管理节点 , 但 只有一个管理节点可以成为 leader , leader 通过 raft 协议实现工作节点(Worker):是任务执行节点,管理节点将服务 (service) 下发至工作节点执行, 管理节点默 认也作为工作节点
节点的有效性有三种:
一个三管理器群最多可以容忍一名管理器的损失一个五管理器群最多可以同时丢失两个管理器节点一个 N 管理器集群最多可以容忍丢失 (N-1)/2 管理器Docker 建议 一个 swarm 最多使用 7个管理器 节点
管理节点也可以是工作节点,工作节点的数量没有限制,经过测试,Swarm 可拓展性的极限是在 1000个节点(主机)上运行 50000 个部署容器
任务 ( Task )是 Swarm 中的最小的调度单位,目前来说就是 一个单一的容器服务 ( Services ) 是指一组任务的集合( 多个容器的集群 ),服务定义了任务的属性
关闭seLinux、关闭防火墙、配置主机hostname
通过getenforce命令查看SELiunx
- [root@MiWiFi-R3L-srv ~]# getenforce
- Disabled
- 修改/etc/selinux/config 文件
- 将SELINUX=enforcing改为SELINUX=disabled
- #停止防火墙
- systemctl stop firewalld
-
- #永久关闭防火墙
- #systemctl disable firewalld
-
- #开启防火墙
- systemctl start firewalld
-
- #查看防火墙状态
- systemctl status firewalld
如果只是修改hostname可以通过如下命令
hostname newHostname
注意:这种修改方式只有当前有效,等服务器重启后hostname 就会失效,回到原来的 hostname
永久修改,重启后生效
vi /etc/hostname
这里用四台虚拟机来操作:
manager 节点 manager_81 192.168.31.81
worker 节点 worker_117 192.168.31.117
worker 节点 worker_140 192.168.31.140
worker 节点 worker_241 192.168.31.241
关闭他们的SELinux,关闭防火墙,以及设置其hostname
首先就在 manager 这个节点上执行如下操作,表示要将它设置为 manager ,并且设置自己的通讯IP为192.168.31.81,相关命令语法如下:
- #docker swarm init 快速初始化一个集群
- docker swarm init --advertise-addr 192.168.31.81
- [root@manager_81 ~]# docker swarm init --advertise-addr 192.168.31.81
- Swarm initialized: current node (qu1ydd2t6occ8fo76rvaksidd) is now a manager.
-
- To add a worker to this swarm, run the following command:
-
- docker swarm join --token SWMTKN-1-6afkz1ub7m8q37cehxmjiirs6a0r25qt1hzf0no1c0xcny55qc-d3gtv0qcsuhivozbomp4d73ha 192.168.31.81:2377
-
- To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
-
- [root@manager_81 ~]
上述的docker swarm join 为加入集群的命令
docker swarm join --token SWMTKN-1-6afkz1ub7m8q37cehxmjiirs6a0r25qt1hzf0no1c0xcny55qc-d3gtv0qcsuhivozbomp4d73ha 192.168.31.81:2377
把上面命令依次在工作节点worker_117,worker_140,worker_241上运行,这样这几个工作节点就加入了集群了
在manager_81管理节点上运行命令 docker node ls 查看所有集群节点注意:该命令只能在manager节点上运行
也可以通过命令 docker node inspect 节点ID 查看节点详情
- #查看manager节点信息
- [root@manager_81 ~]# docker node inspect qu1ydd2t6occ8fo76rvaksidd
- [
- {
- "ID": "qu1ydd2t6occ8fo76rvaksidd",
- "Version": {
- "Index": 9
- },
- "CreatedAt": "2023-11-23T08:52:01.533912216Z",
- "UpdatedAt": "2023-11-23T08:52:04.095241126Z",
- "Spec": {
- "Labels": {},
- "Role": "manager",
- "Availability": "active"
- },
- "Description": {
- "Hostname": "manager_81",
- "Platform": {
- "Architecture": "x86_64",
- "OS": "linux"
- },
- "Resources": {
- "NanoCPUs": 1000000000,
- "MemoryBytes": 819965952
- },
- "Engine": {
- "EngineVersion": "24.0.7",
- "Plugins": [
- {
- "Type": "Log",
- "Name": "awslogs"
- },
- {
- "Type": "Log",
- "Name": "fluentd"
- },
- {
- "Type": "Log",
- "Name": "gcplogs"
- },
- {
- "Type": "Log",
- "Name": "gelf"
- },
- {
- "Type": "Log",
- "Name": "journald"
- },
- {
- "Type": "Log",
- "Name": "json-file"
- },
- {
- "Type": "Log",
- "Name": "local"
- },
- {
- "Type": "Log",
- "Name": "logentries"
- },
- {
- "Type": "Log",
- "Name": "splunk"
- },
- {
- "Type": "Log",
- "Name": "syslog"
- },
- {
- "Type": "Network",
- "Name": "bridge"
- },
- {
- "Type": "Network",
- "Name": "host"
- },
- {
- "Type": "Network",
- "Name": "ipvlan"
- },
- {
- "Type": "Network",
- "Name": "macvlan"
- },
- {
- "Type": "Network",
- "Name": "null"
- },
- {
- "Type": "Network",
- "Name": "overlay"
- },
- {
- "Type": "Volume",
- "Name": "local"
- }
- ]
- },
- "TLSInfo": {
- "TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBazCCARCgAwIBAgIUY13k5tC6JLjbjGEPjuXUiJVQ6O0wCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMjMxMTIzMDg0NzAwWhcNNDMxMTE4MDg0\nNzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABKXU197cjuB2Yul9m8TSDWJs7DySbr6W07TLCXspF+oklU3heaYgSXas3Y5h\nW5apP18B2cfK0cowIgaSAT0mEbGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBSjxbGtYF8NNoDaZcT98Jazqq3G6DAKBggqhkjO\nPQQDAgNJADBGAiEAmXzeGCNDSmjxdaGwfQD+XM4+sMIpFClxeBRUr+dTmZECIQDE\nZvih28ozoVDfCOexIWc9ggnem/tyTskK2yMvQyB0bg==\n-----END CERTIFICATE-----\n",
- "CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh",
- "CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpdTX3tyO4HZi6X2bxNINYmzsPJJuvpbTtMsJeykX6iSVTeF5piBJdqzdjmFblqk/XwHZx8rRyjAiBpIBPSYRsQ=="
- }
- },
- "Status": {
- "State": "ready",
- "Addr": "192.168.31.81"
- },
- "ManagerStatus": {
- "Leader": true,
- "Reachability": "reachable",
- "Addr": "192.168.31.81:2377"
- }
- }
- ]
发现,角色是Manager
他是一个Leader
注意:
可以有多个manager节点,但只能有一个是Leader
当又有一台服务器要加入这个worker集群时,可以通过docker swarm join-token worker来查看加入集群命令
- [root@manager_81 ~]# docker swarm join-token worker
- To add a worker to this swarm, run the following command:
-
- docker swarm join --token SWMTKN-1-6afkz1ub7m8q37cehxmjiirs6a0r25qt1hzf0no1c0xcny55qc-d3gtv0qcsuhivozbomp4d73ha 192.168.31.81:2377
-
- [root@manager_81 ~]#
当又有一台服务器要加入这个manager集群时,可以通过docker swarm join-token manager来查看加入集群命令,这里就不再演示
- [root@manager_81 ~]# docker swarm join-token manager
- To add a manager to this swarm, run the following command:
-
- docker swarm join --token SWMTKN-1-6afkz1ub7m8q37cehxmjiirs6a0r25qt1hzf0no1c0xcny55qc-2bogd7dp5r5wss6s3k255sq4v 192.168.31.81:2377
-
- [root@manager_81 ~]#
当不需要某个工作节点时,可以在其节点上执行命令:docker swarm leave -f, 然后在管理节点上执行命令 docker node rm 节点ID -f 删除节点
- #管理节点上执行
- docker node rm 9xtzwe9h5w36snbheow3yzsy9 -f
-
- #工作节点上执行
- docker swarm leave -f
管理节点可以降级成工作节点,工作节点也可以升级成管理节点
- #节点降级,由管理节点降级为工作节点
- docker node demote 节点ID
- #eg:节点降级
- docker node demote rpa61so05v8wcy5y9ohzn9tv1
-
- #节点升级,由工作节点升级为管理节点
- docker node promote 节点ID
- #eg:节点升级
- docker node promote rpa61so05v8wcy5y9ohzn9tv1
以前是通过docker run来创建部署容器,但是该命令 docker run 容器启动,不具备扩所容,它只能在当前机器上运行,不能部署到多台服务器上,这时可以通过docker service来部署,因为docker service 服务,具有扩所容,滚动更新、回滚等功能,他会自动地部署到dokcer swarm对应的集群中(部署到多台服务器上),下面举个例子来说明
创建一个nginx镜像服务(需要在mananger上运行):
docker service create --name nginx01 --replicas 2 -p 80:80 nginx
通过上面命令,创建一个服务nginx01,这个服务里面有两个nginx容器,这两个容器会随机部署到上面四个服务器上(通过算法实现)
--name nginx服务名称
--replicas 2 指定容器的副本,这里有2个,它会把nginx容器部署到集群这四台服务器上的随机两台上面,部署好后就可以在这四台服务器上任意一台访问nginx服务了
- [root@manager_81 ~]# docker service create --name nginx01 --replicas 2 -p 80:80 nginx
- 797ddmq429mgst9y2su8ezywt
- overall progress: 0 out of 2 tasks
- 1/2: preparing [=================================> ]
- overall progress: 0 out of 2 tasks
- 1/2: preparing [=================================> ]
- overall progress: 2 out of 2 tasks
- 1/2: running [==================================================>]
- 2/2: running [==================================================>]
- verify: Service converged
通过docker service ls查看创建的服务
- [root@manager_81 ~]#
- [root@manager_81 ~]# docker service ls
- ID NAME MODE REPLICAS IMAGE PORTS
- 797ddmq429mg nginx01 replicated 2/2 nginx:latest *:80->80/tcp
通过docker service ps 服务名 查看服务里运行的容器详情
- [root@manager_81 ~]# docker service ps nginx01
- ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
- bypoyr3h2b8u nginx01.1 nginx:latest worker_140 Running Running 45 seconds ago
- ml9j2f9xx14u nginx01.2 nginx:latest manager_81 Running Running 3 minutes ago
发现nginx01服务里面运行了两个nginx服务,一个在worker_140,一个在manager_81上,可以在这两台机器上通过docker ps查看
虽然nginx只部署到了四台机器上的两台中,但还是可以随机访问这四台机器,因为他们是一个集群,底层有一个负载均衡操作,通过ingress网卡处理:
- docker service update --replicas 10 nginx01
-
- #也可以使用
- docker service scale nginx01=10
-
- #通过这个命令就会把10个nginx容器随机部署到这四台服务器上(通过算法部署)
这里有一个需要注意的地方:
当其中的某个容器挂掉后,swarm会自动地创建一个新的容器并随机分配到机器上(通过算法分析得出哪个机比较空闲,并分配容器),从而保证容器的个数可用,保证了负载均衡操作
- #查看服务
- [root@manager_81 ~]# docker service ls
- ID NAME MODE REPLICAS IMAGE PORTS
- 797ddmq429mg nginx01 replicated 6/6 nginx:latest *:80->80/tcp
-
- #删除服务
- [root@manager_81 ~]# docker service rm nginx01
- nginx01
-
- #发现服务已被删除
- [root@manager_81 ~]#
- [root@manager_81 ~]# docker service ls
- ID NAME MODE REPLICAS IMAGE PORTS
创建一个新的nginx服务,并映射html目录,暴露端口,扩容8个nginx容器,演示一下随机调度(负载均衡)的功能
docker service create --name nginx --replicas 8 -p 80:80 --mount type=bind,src=/root/wwwroot/html,dst=/usr/share/nginx/html nginx
--mount: 相当于以前的-v(映射数据卷)
mkdir -p /root/wwwroot/html
- vi index.html
-
- this is 192.168.31.xxx web
如下:
说明集群没问题,通过上面访问显示,发现: 当访问81这台机器时,没有显示81这个上面的内容,这是因为在访问的时候,swarm通过ingress(特殊的overlay网络,具有负载均衡的功能 )网络算法,计算哪一台机器资源比较充足空闲,然后就指定到哪一台上面去访问,并不是到具体机器上,而是根据其算法计算得来的
参考 [Docker]九.Docker compose讲解
在/root/wwwroot/下新建Dockerfile,并写入以下内容
- FROM centos:centos7
- ADD ginGorm.tar.gz /root
- WORKDIR /root
- RUN chmod -R 777 ginGorm
- WORKDIR /root/ginGorm
- EXPOSE 8080
- ENTRYPOINT ["./goweb"]
也可以把build好的镜像发布到远程仓库:
docker build -t docker.io/gowebimg:latest .
docker push docker.io/gowebimg:latest
docker pull gowebimg:latest
如果没有发布到docker上,则需把打包(docker save 镜像ID 镜像.tar)好的镜像复制到各个集群机器上,然后解压镜像(docker load -i 镜像包名, docker tag 镜像ID 镜像名:版本),下面展示各个机器上的镜:
- #指定docker-compose 版本
- version: "3"
-
- #services下就是容器的编写
- services:
- #容器,
- myMysql:
- #来自哪个镜像
- image: mysql
- #容器名,如果不指定,则使用myMysql这个容器默认名称
- container_name: myMysql
- #环境变量:配置用户名,密码等
- environment:
- MYSQL_ROOT_PASSWORD: 123456
- #restart: 表示容器挂掉后是否重启, always 一直重启
- restart: always
- #映射的端口
- ports:
- - 3306:3306
- #数据卷的挂载
- volumes:
- - /root/mysql/conf.d:/etc/mysql/conf.d
- - /root/mysql/data:/var/lib/mysql
- #配置goweb01项目容器
- goweb01:
- #build编译成镜像,可以使用上面的/root/wwwroot/Dockerfile里面的
- #build:
- # context: ./
- # dockerfile: Dockerfile
- #当然,也可以使用已经编译号的gowebimg镜像
- image: gowebimg:v1.0.1
-
- #container_name: goweb01
- restart: always
- #ports:
- # - 8080:8080
-
- deploy:
- replicas: 6 #副本数量
- resources: #资源
- limits: #配置cpu
- cpus: "0.5" # 设置该容器最多只能使用 50% 的 CPU
- memory: 500M # 设置该容器最多只能使用 500M内存
- restart_policy: #定义容器重启策略, 用于代替 restart 参数
- condition: on-failure #只有当容器内部应用程序出现问题才会重启
-
- #depends_on表示创建好myMysql这个容器后,再来创建goweb01这个容器
- depends_on:
- - myMysql
- #配置nginx容器
- nginx:
- image: nginx
- container_name: nginx
- restart: always
- ports:
- - 80:80
- depends_on:
- - goweb01
- volumes:
- - /root/nginx/conf.d/:/etc/nginx/conf.d
- deploy:
- replicas: 6 #副本数量
- resources: #资源
- limits: #配置cpu
- cpus: "0.5" # 设置该容器最多只能使用 50% 的 CPU
- memory: 500M # 设置该容器最多只能使用 500M内存
- restart_policy: #定义容器重启策略, 用于代替 restart 参数
- condition: on-failure #只有当容器内部应用程序出现问题才会重启
在这之前需要在服务器上运行命令docker swarm leave -f 退出集群
- root@manager_81 wwwroot]# docker swarm init --advertise-addr 192.168.31.81
- Swarm initialized: current node (7issgqc81r4xrl07y5qxedtpf) is now a manager.
-
- To add a worker to this swarm, run the following command:
-
- docker swarm join --token SWMTKN-1-1ytez4j3d9iccq7x92kw8dgx9v3q7w68ip24xavz5ec9zsoowc-577xpv4w5o73etzuhcql9bvd9 192.168.31.81:2377
-
- To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
-
- [root@manager_81 wwwroot
docker swarm join --token SWMTKN-1-1ytez4j3d9iccq7x92kw8dgx9v3q7w68ip24xavz5ec9zsoowc-577xpv4w5o73etzuhcql9bvd9 192.168.31.81:2377
注意:
一般需要把mysql单独拿出来做成mysql集群,因为mysql有一个读写分离的操作,在这里就不处理,后续可以自己弄一下
在使用docker-compose.yml之前,都建议把需要用到的镜像在每台机器上都下载下来,方便使用
下面部署:
- #docker stack deploy --compose-file 为固定写法, gowebSwarm 为服务名称,可随意
- docker stack deploy --compose-file docker-compose.yml gowebSwarm
- #查看nginx服务部署详情
- [root@manager_81 wwwroot]# docker service ps gowebSwarm_nginx
- ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
- ggsuom0uhaji gowebSwarm_nginx.1 nginx:latest manager_81 Running Running about a minute ago
- uls0crwvl45s gowebSwarm_nginx.4 nginx:latest worker_140 Running Failed 7 minutes ago "task: non-zero exit (1)"
- ggsuom0uhaji gowebSwarm_nginx.1 nginx:latest manager_81 Running Running about a minute ago
- uls0crwvl45s gowebSwarm_nginx.4 nginx:latest worker_140 Running Failed 7 minutes ago "task: non-zero exit (1)"
发现nginx部署在81和140上,查看
- #查看gowebimg部署详情
- [root@manager_81 wwwroot]# docker service ps gowebSwarm_goweb01
- ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
- 8x0jlmxlai3g gowebSwarm_goweb01.1 gowebimg:v1.0.1 worker_241 Running Running 2 minutes ago
- dsndspg6ljae gowebSwarm_goweb01.2 gowebimg:v1.0.1 manager_81 Running Running 4 minutes ago
- 5fuq5kywo1aq gowebSwarm_goweb01.3 gowebimg:v1.0.1 worker_241 Running Running 2 minutes ago
- m6lpunm0lyi4 gowebSwarm_goweb01.4 gowebimg:v1.0.1 worker_140 Running Running 4 minutes ago
发现goweb部署在241.81,140上,其中241上有两个:
在浏览器上访问,发现没问题,这样,一个nginx集群,goweb项目集群就搭建好了
下面还需在外部搭建一个nginx负载均衡的服务器,当外部访问这个nginx时,通过它转发到nginx集群,让集群去处理,这样才算是一个真正的负载均衡操作,达到的效果如下:
当访问www.test.com时,随机访问的服务器为192.168.31.81,192.168.31.114,192.168.31.241,192.168.31.117这几台
每台集群机器上的nginx配置文件(/root/nginx/conf.d/)
- upstream gowebtest{
- ip_hash;
- server goweb01:8080;
- }
- server {
- listen 80;
- server_name docker.test.com;
- location / {
- #设置主机头和客户端真实地址,以便服务器获取客户端真实IP
- #禁用缓存
- proxy_buffering off;
- #反向代理的地址
- proxy_pass http://gowebtest;
- }
- #error_page 404 /404.html;
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
- upstream bakeaaa {
- ip_hash;
- server 192.168.31.81 weight=1;
- server 192.168.31.140 weight=1;
- server 192.168.31.117 weight=1;
- server 192.168.31.241 weight=1;
- }
-
- server {
- listen 80;
- server_name www.test.com;
- #charset koi8-r;
- #access_log /var/log/nginx/host.access.log main;
- location / {
- #设置主机头和客户端真实地址,以便服务器获取客户端真实 IP
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- #nginx服务器与被代理服务连接超时时间,代理超时,超时后会请求其他server ip
- proxy_connect_timeout 1s;
- #禁用缓存
- proxy_buffering off;
- proxy_pass http://bakeaaa;
- }
- #error_page 404 /404.html;
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root /usr/share/nginx/html;
- }
- }
在server{}下添加如下两行配置:
- add_header backendCode $upstream_status;
- add_header BackendIP "$upstream_addr;" always;
然后下载nginx
docker pull nginx
然后配置conf
可以通过docker run或者docker service 启动一个nginx服务,这里目前就一台nginx对外暴露,可以用docker run启动
- [root@MiWiFi-R3L-srv conf.d]# docker images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- nginx latest a6bd71f48f68 3 days ago 187MB
- [root@MiWiFi-R3L-srv conf.d]# docker run -it -d --name nginxweb -p 80:80 -v /root/nginx/conf.d/:/etc/nginx/conf.d nginx
- 30bbc795b79bc763205e0acfe111352efa96f2b91065a4923070786ccbdec8cd
- [root@MiWiFi-R3L-srv conf.d]# docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 30bbc795b79b nginx "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp nginxweb
- [root@MiWiFi-R3L-srv conf.d]#
这样就完成了一个负载均衡操作,对上面负载均衡的说明:
当外部通过浏览器访问域名时,通过DNS域名转发,访问到了nginx主机(192.168.31.250)这台服务器,nginx服务器通过ip_hash转发给设置的几个server ip 服务器,其中接收到的服务器再通过底层的igress网络,使用算法计算得出哪一台服务器资源比较充足,就访问哪一台服务器
当然,上面的swarm集群目前只有一台manager结点,3台worker结点,一般在正式环境中,manager结点数在3~7台,worker结点也应该更多台,这样才能处理高并发
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。