Kubernetes 部署微服务电商平台(16)

一、概念

  微服务就是很小的服务,小到一个服务只对应一个单一的功能,只做一件事。这个服务可以单独部署运行,服务之间可以通过RPC来相互交互,每个微服务都是由独立的小团队开发,测试,部署,上线,负责它的整个生命周期。 

  在做架构设计的时候,先做逻辑架构,再做物理架构,当你拿到需求后,估算过最大用户量和并发量后,计算单个应用服务器能否满足需求,如果用户量只有几百人的小应用,单体应用就能搞定,即所有应用部署在一个应用服务器里,如果是很大用户量,且某些功能会被频繁访问,或者某些功能计算量很大,建议将应用拆解为多个子系统,各自负责各自功能,这就是微服务架构

  微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资源的API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。这些服务的集中化管理已经是最少的,它们可以用不同的编程语言编写,并使用不同的数据存储技术。

  分布式服务顾名思义服务是分散部署在不同的机器上的,一个服务可能负责几个功能,是一种面向SOA架构的,服务之间也是通过rpc来交互或者是webservice来交互的。逻辑架构设计完后就该做物理架构设计,系统应用部署在超过一台服务器或虚拟机上,且各分开部署的部分彼此通过各种通讯协议交互信息,就可算作分布式部署,生产环境下的微服务肯定是分布式部署的,分布式部署的应用不一定是微服务架构的,比如集群部署,它是把相同应用复制到不同服务器上,但是逻辑功能上还是单体应用。

  微服务相比分布式服务来说,它的粒度更小,服务之间耦合度更低,由于每个微服务都由独立的小团队负责,因此它敏捷性更高,分布式服务最后都会向微服务架构演化,这是一种趋势, 不过服务微服务化后带来的挑战也是显而易见的,例如服务粒度小,数量大,后期运维将会很难。

二、准备条件

  确保harbor镜像仓库可以正常使用,且各个节点都可以访问私有镜像仓库。

[root@ren8 harbor]# docker-compose ps
       Name                 Command          State          Ports        
-------------------------------------------------------------------------
harbor-adminserver   /harbor/start.sh        Up                          
harbor-db            /usr/local/bin          Up      3306/tcp            
                     /docker-entr ...                                    
harbor-jobservice    /harbor/start.sh        Up                          
harbor-log           /bin/sh -c              Up      127.0.0.1:1514->1051
                     /usr/local/bin/ ...             4/tcp               
harbor-ui            /harbor/start.sh        Up                          
nginx                nginx -g daemon off;    Up      0.0.0.0:443->443/tcp
                                                     , 0.0.0.0:4443->4443
                                                     /tcp,               
                                                     0.0.0.0:80->80/tcp  
registry             /entrypoint.sh serve    Up      5000/tcp            
                     /etc/ ...                                           

  上传需要的镜像至私有harbor镜像仓库

[root@ren7 ~]# unzip sock-shop.zip 
Archive:  sock-shop.zip
  inflating: complete-demo.yaml      
  inflating: image.tar.gz            
[root@ren7 sock-shop]# ls
complete-demo.yaml  image.tar.gz
[root@ren7 sock-shop]# tar zxvf image.tar.gz 
image/
image/mongo.latest.tar.gz
image/user.0.4.7.tar.gz
image/rabbitmq.3.6.8.tar.gz
image/catalogue.0.3.5.tar.gz
image/payment.0.4.3.tar.gz
image/front-end.0.3.12.tar.gz
image/shipping.0.4.8.tar.gz
image/carts.0.4.8.tar.gz
image/orders.0.4.7.tar.gz
image/queue-master.0.3.1.tar.gz
image/catalogue-db.0.3.0.tar.gz
image/user-db.0.4.0.tar.gz
[root@ren7 sock-shop]# ls
complete-demo.yaml  image  image.tar.gz
[root@ren7 sock-shop]# cd image
[root@ren7 image]# ls
carts.0.4.8.tar.gz         payment.0.4.3.tar.gz
catalogue.0.3.5.tar.gz     queue-master.0.3.1.tar.gz
catalogue-db.0.3.0.tar.gz  rabbitmq.3.6.8.tar.gz
front-end.0.3.12.tar.gz    shipping.0.4.8.tar.gz
mongo.latest.tar.gz        user.0.4.7.tar.gz
orders.0.4.7.tar.gz        user-db.0.4.0.tar.gz
[root@ren7 image]# docker images
REPOSITORY                                          TAG                 IMAGE ID            CREATED             SIZE
reg.yunwei.com/learn/httpd                          latest              66a97eeec7b8        13 days ago         154MB
reg.yunwei.com/learn/busybox                        latest              19485c79a9bb        8 weeks ago         1.22MB
calico/node                                         v3.0.6              15f002a49ae8        18 months ago       248MB
calico/kube-controllers                             v2.0.4              f8e683e673ec        18 months ago       55.1MB
calico/cni                                          v2.0.5              b5e5532af766        18 months ago       69.1MB
coredns/coredns                                     1.0.6               d4b7466213fe        20 months ago       39.9MB
mirrorgooglecontainers/kubernetes-dashboard-amd64   v1.8.3              0c60bcf89900        20 months ago       102MB
mirrorgooglecontainers/heapster-amd64               v1.5.1              c41e77c31c91        20 months ago       75.3MB
mirrorgooglecontainers/pause-amd64                  3.1                 da86e6ba6ca1        22 months ago       742kB
mirrorgooglecontainers/heapster-influxdb-amd64      v1.3.3              577260d221db        2 years ago         12.5MB
[root@ren7 image]# for ls in `ls`;do docker load -i $ls;done
[root@ren7 image]# docker images |grep sock-shop
reg.yunwei.com/sock-shop/mongo                      latest              05b3651ee24e        12 months ago       382MB
reg.yunwei.com/sock-shop/user                       0.4.7               c276a3cc0418        21 months ago       35.7MB
reg.yunwei.com/sock-shop/catalogue                  0.3.5               0bd359b6d6e8        2 years ago         41.2MB
reg.yunwei.com/sock-shop/rabbitmq                   3.6.8               8cdcbee37f62        2 years ago         179MB
reg.yunwei.com/sock-shop/payment                    0.4.3               4f2c23055dcd        2 years ago         32.5MB
reg.yunwei.com/sock-shop/front-end                  0.3.12              b54402ef78a5        2 years ago         120MB
reg.yunwei.com/sock-shop/shipping                   0.4.8               4fc533e8180a        2 years ago         199MB
reg.yunwei.com/sock-shop/carts                      0.4.8               c00473736118        2 years ago         198MB
reg.yunwei.com/sock-shop/orders                     0.4.7               8275c5b9181b        2 years ago         198MB
reg.yunwei.com/sock-shop/queue-master               0.3.1               76f0de7a12ac        2 years ago         179MB
reg.yunwei.com/sock-shop/catalogue-db               0.3.0               9d0c5eb88949        2 years ago         400MB
reg.yunwei.com/sock-shop/user-db                    0.4.0               196601f91030        2 years ago         717MB

  使用脚本将所需镜像上传至harbor仓库:

#!/bin/bash
a=`docker images |grep sock-shop |awk '{print $1}'`
for i in `echo $a`;do
b=`docker images |grep $i |awk '{print $2}'`
c=$i:$b
#echo $c
docker push $c &> /dev/null
done
echo "done"

三、部署微服务

  编辑complete-demo.yaml文件后,执行

  1 apiVersion: extensions/v1beta1
  2 kind: Deployment
  3 metadata:
  4   name: carts-db
  5   labels:
  6     name: carts-db
  7   namespace: sock-shop
  8 spec:
  9   replicas: 1
 10   template:
 11     metadata:
 12       labels:
 13         name: carts-db
 14     spec:
 15       containers:
 16       - name: carts-db
 17         image: reg.yunwei.edu/sock-shop/mongo
 18         ports:
 19         - name: mongo
 20           containerPort: 27017
 21         securityContext:
 22           capabilities:
 23             drop:
 24               - all
 25             add:
 26               - CHOWN
 27               - SETGID
 28               - SETUID
 29           readOnlyRootFilesystem: true
 30         volumeMounts:
 31         - mountPath: /tmp
 32           name: tmp-volume
 33       volumes:
 34         - name: tmp-volume
 35           emptyDir:
 36             medium: Memory
 37       nodeSelector:
 38         beta.kubernetes.io/os: linux
 39 ---
 40 apiVersion: v1
 41 kind: Service
 42 metadata:
 43   name: carts-db
 44   labels:
 45     name: carts-db
 46   namespace: sock-shop
 47 spec:
 48   ports:
 49     # the port that this service should serve on
 50   - port: 27017
 51     targetPort: 27017
 52   selector:
 53     name: carts-db
 54 ---
 55 apiVersion: extensions/v1beta1
 56 kind: Deployment
 57 metadata:
 58   name: carts
 59   labels:
 60     name: carts
 61   namespace: sock-shop
 62 spec:
 63   replicas: 1
 64   template:
 65     metadata:
 66       labels:
 67         name: carts
 68     spec:
 69       containers:
 70       - name: carts
 71         image: reg.yunwei.edu/sock-shop/carts:0.4.8
 72         ports:
 73          - containerPort: 80
 74         env:
 75          - name: ZIPKIN
 76            value: zipkin.jaeger.svc.cluster.local
 77          - name: JAVA_OPTS
 78            value: -Xms64m -Xmx128m -XX:PermSize=32m -XX:MaxPermSize=64m -XX:+UseG1GC -Djava.security.egd=file:/dev/urandom
 79         securityContext:
 80           runAsNonRoot: true
 81           runAsUser: 10001
 82           capabilities:
 83             drop:
 84               - all
 85             add:
 86               - NET_BIND_SERVICE
 87           readOnlyRootFilesystem: true
 88         volumeMounts:
 89         - mountPath: /tmp
 90           name: tmp-volume
 91       volumes:
 92         - name: tmp-volume
 93           emptyDir:
 94             medium: Memory
 95       nodeSelector:
 96         beta.kubernetes.io/os: linux
 97 ---
 98 apiVersion: v1
 99 kind: Service
100 metadata:
101   name: carts
102   labels:
103     name: carts
104   namespace: sock-shop
105 spec:
106   ports:
107     # the port that this service should serve on
108   - port: 80
109     targetPort: 80
110   selector:
111     name: carts
112 ---
113 apiVersion: extensions/v1beta1
114 kind: Deployment
115 metadata:
116   name: catalogue-db
117   labels:
118     name: catalogue-db
119   namespace: sock-shop
120 spec:
121   replicas: 1
122   template:
123     metadata:
124       labels:
125         name: catalogue-db
126     spec:
127       containers:
128       - name: catalogue-db
129         image: reg.yunwei.edu/sock-shop/catalogue-db:0.3.0
130         env:
131           - name: MYSQL_ROOT_PASSWORD
132             value: fake_password
133           - name: MYSQL_DATABASE
134             value: socksdb
135         ports:
136         - name: mysql
137           containerPort: 3306
138       nodeSelector:
139         beta.kubernetes.io/os: linux
140 ---
141 apiVersion: v1
142 kind: Service
143 metadata:
144   name: catalogue-db
145   labels:
146     name: catalogue-db
147   namespace: sock-shop
148 spec:
149   ports:
150     # the port that this service should serve on
151   - port: 3306
152     targetPort: 3306
153   selector:
154     name: catalogue-db
155 ---
156 apiVersion: extensions/v1beta1
157 kind: Deployment
158 metadata:
159   name: catalogue
160   labels:
161     name: catalogue
162   namespace: sock-shop
163 spec:
164   replicas: 1
165   template:
166     metadata:
167       labels:
168         name: catalogue
169     spec:
170       containers:
171       - name: catalogue
172         image: reg.yunwei.edu/sock-shop/catalogue:0.3.5
173         ports:
174         - containerPort: 80
175         securityContext:
176           runAsNonRoot: true
177           runAsUser: 10001
178           capabilities:
179             drop:
180               - all
181             add:
182               - NET_BIND_SERVICE
183           readOnlyRootFilesystem: true
184       nodeSelector:
185         beta.kubernetes.io/os: linux
186 ---
187 apiVersion: v1
188 kind: Service
189 metadata:
190   name: catalogue
191   labels:
192     name: catalogue
193   namespace: sock-shop
194 spec:
195   ports:
196     # the port that this service should serve on
197   - port: 80
198     targetPort: 80
199   selector:
200     name: catalogue
201 ---
202 apiVersion: extensions/v1beta1
203 kind: Deployment
204 metadata:
205   name: front-end
206   namespace: sock-shop
207 spec:
208   replicas: 1
209   template:
210     metadata:
211       labels:
212         name: front-end
213     spec:
214       containers:
215       - name: front-end
216         image: reg.yunwei.edu/sock-shop/front-end:0.3.12
217         resources:
218           requests:
219             cpu: 100m
220             memory: 100Mi
221         ports:
222         - containerPort: 8079
223         securityContext:
224           runAsNonRoot: true
225           runAsUser: 10001
226           capabilities:
227             drop:
228               - all
229           readOnlyRootFilesystem: true
230       nodeSelector:
231         beta.kubernetes.io/os: linux
232 ---
233 apiVersion: v1
234 kind: Service
235 metadata:
236   name: front-end
237   labels:
238     name: front-end
239   namespace: sock-shop
240 spec:
241   type: NodePort
242   ports:
243   - port: 80
244     targetPort: 8079
245     nodePort: 30001
246   selector:
247     name: front-end
248 ---
249 apiVersion: extensions/v1beta1
250 kind: Deployment
251 metadata:
252   name: orders-db
253   labels:
254     name: orders-db
255   namespace: sock-shop
256 spec:
257   replicas: 1
258   template:
259     metadata:
260       labels:
261         name: orders-db
262     spec:
263       containers:
264       - name: orders-db
265         image: reg.yunwei.edu/sock-shop/mongo
266         ports:
267         - name: mongo
268           containerPort: 27017
269         securityContext:
270           capabilities:
271             drop:
272               - all
273             add:
274               - CHOWN
275               - SETGID
276               - SETUID
277           readOnlyRootFilesystem: true
278         volumeMounts:
279         - mountPath: /tmp
280           name: tmp-volume
281       volumes:
282         - name: tmp-volume
283           emptyDir:
284             medium: Memory
285       nodeSelector:
286         beta.kubernetes.io/os: linux
287 ---
288 apiVersion: v1
289 kind: Service
290 metadata:
291   name: orders-db
292   labels:
293     name: orders-db
294   namespace: sock-shop
295 spec:
296   ports:
297     # the port that this service should serve on
298   - port: 27017
299     targetPort: 27017
300   selector:
301     name: orders-db
302 ---
303 apiVersion: extensions/v1beta1
304 kind: Deployment
305 metadata:
306   name: orders
307   labels:
308     name: orders
309   namespace: sock-shop
310 spec:
311   replicas: 1
312   template:
313     metadata:
314       labels:
315         name: orders
316     spec:
317       containers:
318       - name: orders
319         image: reg.yunwei.edu/sock-shop/orders:0.4.7
320         env:
321          - name: ZIPKIN
322            value: zipkin.jaeger.svc.cluster.local
323          - name: JAVA_OPTS
324            value: -Xms64m -Xmx128m -XX:PermSize=32m -XX:MaxPermSize=64m -XX:+UseG1GC -Djava.security.egd=file:/dev/urandom
325         ports:
326         - containerPort: 80
327         securityContext:
328           runAsNonRoot: true
329           runAsUser: 10001
330           capabilities:
331             drop:
332               - all
333             add:
334               - NET_BIND_SERVICE
335           readOnlyRootFilesystem: true
336         volumeMounts:
337         - mountPath: /tmp
338           name: tmp-volume
339       volumes:
340         - name: tmp-volume
341           emptyDir:
342             medium: Memory
343       nodeSelector:
344         beta.kubernetes.io/os: linux
345 ---
346 apiVersion: v1
347 kind: Service
348 metadata:
349   name: orders
350   labels:
351     name: orders
352   namespace: sock-shop
353 spec:
354   ports:
355     # the port that this service should serve on
356   - port: 80
357     targetPort: 80
358   selector:
359     name: orders
360 ---
361 apiVersion: extensions/v1beta1
362 kind: Deployment
363 metadata:
364   name: payment
365   labels:
366     name: payment
367   namespace: sock-shop
368 spec:
369   replicas: 1
370   template:
371     metadata:
372       labels:
373         name: payment
374     spec:
375       containers:
376       - name: payment
377         image: reg.yunwei.edu/sock-shop/payment:0.4.3
378         ports:
379         - containerPort: 80
380         securityContext:
381           runAsNonRoot: true
382           runAsUser: 10001
383           capabilities:
384             drop:
385               - all
386             add:
387               - NET_BIND_SERVICE
388           readOnlyRootFilesystem: true
389       nodeSelector:
390         beta.kubernetes.io/os: linux
391 ---
392 apiVersion: v1
393 kind: Service
394 metadata:
395   name: payment
396   labels:
397     name: payment
398   namespace: sock-shop
399 spec:
400   ports:
401     # the port that this service should serve on
402   - port: 80
403     targetPort: 80
404   selector:
405     name: payment
406 ---
407 apiVersion: extensions/v1beta1
408 kind: Deployment
409 metadata:
410   name: queue-master
411   labels:
412     name: queue-master
413   namespace: sock-shop
414 spec:
415   replicas: 1
416   template:
417     metadata:
418       labels:
419         name: queue-master
420     spec:
421       containers:
422       - name: queue-master
423         image: reg.yunwei.edu/sock-shop/queue-master:0.3.1
424         ports:
425         - containerPort: 80
426       nodeSelector:
427         beta.kubernetes.io/os: linux
428 ---
429 apiVersion: v1
430 kind: Service
431 metadata:
432   name: queue-master
433   labels:
434     name: queue-master
435   annotations:
436     prometheus.io/path: "/prometheus"
437   namespace: sock-shop
438 spec:
439   ports:
440     # the port that this service should serve on
441   - port: 80
442     targetPort: 80
443   selector:
444     name: queue-master
445 ---
446 apiVersion: extensions/v1beta1
447 kind: Deployment
448 metadata:
449   name: rabbitmq
450   labels:
451     name: rabbitmq
452   namespace: sock-shop
453 spec:
454   replicas: 1
455   template:
456     metadata:
457       labels:
458         name: rabbitmq
459     spec:
460       containers:
461       - name: rabbitmq
462         image: reg.yunwei.edu/sock-shop/rabbitmq:3.6.8
463         ports:
464         - containerPort: 5672
465         securityContext:
466           capabilities:
467             drop:
468               - all
469             add:
470               - CHOWN
471               - SETGID
472               - SETUID
473               - DAC_OVERRIDE
474           readOnlyRootFilesystem: true
475       nodeSelector:
476         beta.kubernetes.io/os: linux
477 ---
478 apiVersion: v1
479 kind: Service
480 metadata:
481   name: rabbitmq
482   labels:
483     name: rabbitmq
484   namespace: sock-shop
485 spec:
486   ports:
487     # the port that this service should serve on
488   - port: 5672
489     targetPort: 5672
490   selector:
491     name: rabbitmq
492 ---
493 apiVersion: extensions/v1beta1
494 kind: Deployment
495 metadata:
496   name: shipping
497   labels:
498     name: shipping
499   namespace: sock-shop
500 spec:
501   replicas: 1
502   template:
503     metadata:
504       labels:
505         name: shipping
506     spec:
507       containers:
508       - name: shipping
509         image: reg.yunwei.edu/sock-shop/shipping:0.4.8
510         env:
511          - name: ZIPKIN
512            value: zipkin.jaeger.svc.cluster.local
513          - name: JAVA_OPTS
514            value: -Xms64m -Xmx128m -XX:PermSize=32m -XX:MaxPermSize=64m -XX:+UseG1GC -Djava.security.egd=file:/dev/urandom
515         ports:
516         - containerPort: 80
517         securityContext:
518           runAsNonRoot: true
519           runAsUser: 10001
520           capabilities:
521             drop:
522               - all
523             add:
524               - NET_BIND_SERVICE
525           readOnlyRootFilesystem: true
526         volumeMounts:
527         - mountPath: /tmp
528           name: tmp-volume
529       volumes:
530         - name: tmp-volume
531           emptyDir:
532             medium: Memory
533       nodeSelector:
534         beta.kubernetes.io/os: linux
535 ---
536 apiVersion: v1
537 kind: Service
538 metadata:
539   name: shipping
540   labels:
541     name: shipping
542   namespace: sock-shop
543 spec:
544   ports:
545     # the port that this service should serve on
546   - port: 80
547     targetPort: 80
548   selector:
549     name: shipping
550 ---
551 apiVersion: extensions/v1beta1
552 kind: Deployment
553 metadata:
554   name: user-db
555   labels:
556     name: user-db
557   namespace: sock-shop
558 spec:
559   replicas: 1
560   template:
561     metadata:
562       labels:
563         name: user-db
564     spec:
565       containers:
566       - name: user-db
567         image: reg.yunwei.edu/sock-shop/user-db:0.4.0
568         ports:
569         - name: mongo
570           containerPort: 27017
571         securityContext:
572           capabilities:
573             drop:
574               - all
575             add:
576               - CHOWN
577               - SETGID
578               - SETUID
579           readOnlyRootFilesystem: true
580         volumeMounts:
581         - mountPath: /tmp
582           name: tmp-volume
583       volumes:
584         - name: tmp-volume
585           emptyDir:
586             medium: Memory
587       nodeSelector:
588         beta.kubernetes.io/os: linux
589 ---
590 apiVersion: v1
591 kind: Service
592 metadata:
593   name: user-db
594   labels:
595     name: user-db
596   namespace: sock-shop
597 spec:
598   ports:
599     # the port that this service should serve on
600   - port: 27017
601     targetPort: 27017
602   selector:
603     name: user-db
604 ---
605 apiVersion: extensions/v1beta1
606 kind: Deployment
607 metadata:
608   name: user
609   labels:
610     name: user
611   namespace: sock-shop
612 spec:
613   replicas: 1
614   template:
615     metadata:
616       labels:
617         name: user
618     spec:
619       containers:
620       - name: user
621         image: reg.yunwei.edu/sock-shop/user:0.4.7
622         ports:
623         - containerPort: 80
624         env:
625         - name: MONGO_HOST
626           value: user-db:27017
627         securityContext:
628           runAsNonRoot: true
629           runAsUser: 10001
630           capabilities:
631             drop:
632               - all
633             add:
634               - NET_BIND_SERVICE
635           readOnlyRootFilesystem: true
636       nodeSelector:
637         beta.kubernetes.io/os: linux
638 ---
639 apiVersion: v1
640 kind: Service
641 metadata:
642   name: user
643   labels:
644     name: user
645   namespace: sock-shop
646 spec:
647   ports:
648     # the port that this service should serve on
649   - port: 80
650     targetPort: 80
651   selector:
652     name: user
View Code
[root@ren7 sock-shop]# kubectl create namespace sock-shop  #创建指定的命名空间
namespace/sock-shop created
[root@ren7 sock-shop]# kubectl get ns
NAME              STATUS   AGE
default           Active   7d19h
kube-node-lease   Active   7d19h
kube-public       Active   7d19h
kube-system       Active   7d19h
sock-shop         Active   2s
[root@ren7 sock-shop]#  kubectl apply -f complete-demo.yaml   #执行编排文件

  查看前端service的访问端口:

[root@ren7 sock-shop]# kubectl get service -n sock-shop
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
carts          ClusterIP   10.68.203.204   <none>        80/TCP         2m3s
carts-db       ClusterIP   10.68.87.50     <none>        27017/TCP      2m3s
catalogue      ClusterIP   10.68.9.9       <none>        80/TCP         2m2s
catalogue-db   ClusterIP   10.68.23.220    <none>        3306/TCP       2m2s
front-end      NodePort    10.68.19.124    <none>        80:30001/TCP   2m1s
orders         ClusterIP   10.68.207.186   <none>        80/TCP         2m
orders-db      ClusterIP   10.68.41.203    <none>        27017/TCP      2m
payment        ClusterIP   10.68.217.133   <none>        80/TCP         119s
queue-master   ClusterIP   10.68.34.156    <none>        80/TCP         118s
rabbitmq       ClusterIP   10.68.207.90    <none>        5672/TCP       115s
shipping       ClusterIP   10.68.12.207    <none>        80/TCP         113s
user           ClusterIP   10.68.13.106    <none>        80/TCP         110s
user-db        ClusterIP   10.68.219.95    <none>        27017/TCP      112s

三、登录浏览器验证

  浏览器中输入集群中任意节点的ip+NodePort端口

 

posted @ 2019-10-29 09:14  Wolf_Coder  阅读(652)  评论(0编辑  收藏  举报