目录

  • 1 准备工作
    • 1.1 集群环境说明
    • 1.2 部署方案
  • 2 配置文件
    • 2.1 命名空间
    • 2.2 Mysql
      • 2.2.1 deployment
      • 2.2.2 ConfigMap
      • 2.2.3 Secret
      • 2.2.4 Service
    • 2.3 Redis
      • 2.3.1 deployment
      • 2.3.2 Service
    • 2.4 SpringCloud
      • 2.4.1 image
      • 2.4.2 deployment
      • 2.4.3 Service
    • 2.5 Vue
      • 2.5.1 image
      • 2.5.2 deployment
      • 2.5.3 Service
    • 2.6 Ingress
  • 3 部署
  • 4 总结

1 准备工作

1.1 集群环境说明

本地搭建了3台centOS系统的虚拟机,2台作为master节点,1台作为worker节点。其中m3为worker节点(原计划搭建3master节点的高可用集群,但限制于本地环境资源)

系统类型 IP地址 节点角色 CPU Memory Hostname
centos-7.6 192.168.199.111 master 2核 2G m1
centos-7.6 192.168.199.107 master 2核 2G m2
centos-7.6 192.168.199.174 worker 2核 2G m3

集群环境的具体搭建步骤在此就不赘述了。k8s的搭建方式有2种,一种是使用二进制文件安装,一种是使用kubeadm安装。2种安装方式各有优劣,kubeadm安装比较简单,适合初学者,此次实践使用该安装方式。
特别说明,当前集群基本实现了k8s的高可用,使用keepalived来防止单点故障,设置虚IP192.168.199.155,当m1宕机后,虚IP会漂移到m2,继续提供服务。更进一步的实现方式是使用haproxy对2台master进行负载均衡,该实现有待研究。

1.2 部署方案


此次部署的应用架构为:mysql+redis+springcloud+vue。
我们都知道k8s的最小调度单位是pod,所以应用中的每个服务也是以pod为单位来进行管理。每种pod都虚拟化出一层service,集群内部通过访问servicename来访问不同的服务。对于压力比较大的springcloud服务,会进行横向扩容,会有多个实例,也就是有多个pod,当前端访问后端接口时,k8s会通过springcloud 的service 来对多个pod进行负载均衡。多个springcloud服务要分多个deployment和service,之前的想法是把多个服务分别作为多个容器放在1个pod中,这是一个严重跑偏的思路。外部服务发现采用ingress-nginx的方案来实现。

2 配置文件

2.1 命名空间

k8s 的隔离机制是使用命名空间进行隔离,不同的命名空间网络是不通的。创建一个命名空间,把应用相关的所有资源放在一个命名空间下。

#Namespace
apiVersion: v1
kind: Namespace
metadata:name: springcloud-namespacelabels:name: springcloud-namespace

2.2 Mysql

2.2.1 deployment

mysql的版本选择mysql5.7,使用官方镜像。

containers:
- name: mysqlimage: mysql:5.7imagePullPolicy: IfNotPresentports:- containerPort: 3306args:- "--collation-server=utf8mb4_unicode_ci"- "--character-set-server=utf8mb4"     - "--init-connect='SET NAMES UTF8MB4'"env:- name: MYSQL_DATABASEvalue: "parking"- name: MYSQL_ROOT_PASSWORDvalue: "mysqlpw"

镜像的拉取策略是当本地不存在时拉取镜像,本地存在则使用本地镜像。容器端口使用3306.设置2个环境变量,一个是容器启动时创建一个名字叫parking的库,另一个是root用户的密码。此处密码为明文,后面会实现如何对该密码进行加密。

初始化容器

initContainers:
- name: mysql-initimage: busyboximagePullPolicy: IfNotPresentcommand:  - sh- "-c"- |set -exrm -fr /var/lib/mysql/lost+foundvolumeMounts:- name: mysql-initdbmountPath: /docker-entrypoint-initdb.d

参考其他k8s部署文章,这个容器的作用是对mysql的数据做初始化操作。initContainers的作用是在containers启动之前进行启动,初始化容器和主容器挂载到宿主机的同一个目录下,当mysql启动时,/docker-entrypoint-initdb.d 目录下就会有初始化的sql文件(具体查看下面的configmap)。查看mysql5.7官方镜像的Dockerfile,我们会知道mysql启动时会执行/docker-entrypoint-initdb.d 目录下的sql脚本,则可以完成mysql的初始化操作。

挂载

   volumeMounts:- name: mysqldatamountPath: /var/lib/mysql - name: mysql-confmountPath: /etc/mysql/conf.d/- name: mysql-initdbmountPath: /docker-entrypoint-initdb.dvolumes:- name: mysqldatahostPath:path: /data/mysql_springcloud- name: mysql-confconfigMap:name: mysql-config- name: mysql-initdb#emptyDir: {}configMap:name: mysql-initsql

mysql的挂载主要是三部分内容,初始化脚本、配置文件和数据文件。初始化脚本和配置文件都是用ConfigMap来管理的,下面具体会有。
数据文件的挂载采用的是挂载在宿主机的目录下,这个挂载方式是有问题的,在生产环境中绝对不能这样实现。因为pod调度在哪台服务器上不是不变的,虽然我们可以使用亲和性来控制pod的调度,但是当mysql所在节点宕机,pod会被调度到另外的机器上,此时就会数据丢失,并且这样也没有实现mysql的高可用。
正确的部署方式是使用共享存储(PV、PVC),如果想要实现高可用,则需要使用StatefulSet,即有状态的服务。因为对于Deployment来说,其所管理的Pod的IP、名字,启停顺序等都是随机的,但是mysql如果要实现主从,那么pod一定是要区分的。这部分的实现之后研究。官方社区给出一个实现方式,目前看来较复杂,可以参考。

健康检查

livenessProbe:exec:command:- /bin/sh- "-c"- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"- mysql -h 127.0.0.1 -u root -e "SELECT 1"initialDelaySeconds: 30                            periodSeconds: 20                                   timeoutSeconds: 5                                   successThreshold: 1                                 failureThreshold: 3
readinessProbe:                                       exec:command:- /bin/sh- "-c"- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"- mysql -h 127.0.0.1 -u root -e "SELECT 1"initialDelaySeconds: 30                             periodSeconds: 20                                  timeoutSeconds: 5                                  successThreshold: 1                                 failureThreshold: 3   

存活检查和就绪检查都是去查询来查看mysql的服务是否可用。容器启动30秒后进行健康检查,健康检查间隔20秒,健康检查命令超时时间5秒。从错误到正确,只需成功一次,就认为是健康的。健康检查的命令执行3次都失败认为真的失败,不健康了,放弃检查。当健康检查失败,容器会在当前节点重启。

2.2.2 ConfigMap

#mysql 配置文件
apiVersion: v1
kind: ConfigMap
metadata:name: mysql-confignamespace: springcloud-namespace
data:my.cnf: |[mysqld]   skip_external_lockinglower_case_table_names=1skip_host_cacheskip_name_resolvecharacter-set-server = utf8mb4collation-server = utf8mb4_general_ciinit_connect='SET NAMES utf8mb4'default-storage-engine=INNODB
#mysql 初始化脚本
apiVersion: v1
kind: ConfigMap
metadata:name: mysql-initsqlnamespace: springcloud-namespace
data:execute.sql: |-create database testappdb default character set utf8;

2.2.3 Secret

#mysql密码
apiVersion: v1
kind: Secret
metadata:name: mysql-pwdnamespace: springcloud-namespace
data:mysql-root-pwd: bXlzcWxwd2Q=

密码部分建议直接使用命令,不要保存在yaml文件中。

2.2.4 Service

apiVersion: v1
kind: Service
metadata:name: mysql-svcnamespace: springcloud-namespacelabels:name: mysql-svc
spec:type: ClusterIPports:- port: 3306protocol: TCPtargetPort: 3306selector:app: mysql

2.3 Redis

2.3.1 deployment

redis 使用了最新的官方镜像

apiVersion: apps/v1
kind: Deployment
metadata:name: redis-deploynamespace: springcloud-namespace
spec:replicas: 1template:metadata:labels:app: redis-demospec:containers:- name: redis-containerimage: redisimagePullPolicy: IfNotPresent                           ports:- containerPort: 6379

2.3.2 Service

#redis service
apiVersion: v1
kind: Service
metadata:name: redis-servicenamespace: springcloud-namespace
spec:ports:- port: 6379protocol: TCPtargetPort: 6379selector:app: redis-demotype: ClusterIP

2.4 SpringCloud

此次实践的重点是k8s的部署,所以只选取了1个spring cloud服务进行部署。目前对微服务不是很懂,应该也没有涉及注册中心等内容。

2.4.1 image

首先看下项目的配置文件:

#mysql
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://${MYSQL_IP}:${MYSQL_PORT}/parking?characterEncoding=utf8&pinGlobalTxToPhysicalConnection=true
spring.datasource.username=${MYSQL_USERNAME}
spring.datasource.password=${MYSQL_PASSWORD}
spring.datasource.dbtype=mysql
#redis
spring.redis.pool.maxTotal=1024
spring.redis.pool.maxIdle=200
spring.redis.pool.maxWaitMillis=1000
spring.redis.pool.testOnBorrow=true
spring.redis.pool.testOnReturn=true
spring.redis.host=${REDIS}
spring.redis.port=6379
spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=600000

mysql和redis的连接信息写成从环境变量中取

#Dockerfile
FROM openjdk:8 COPY parking-base.jar /parking-base.jarCMD ["java","-jar","/parking-base.jar"]

构建镜像

docker build -t  parking-base:v1 .

2.4.2 deployment

#springcloud deploy
apiVersion: apps/v1
kind: Deployment
metadata:name: springcloud-deploynamespace: springcloud-namespace
spec:strategy:rollingUpdate:maxSurge: 50%        maxUnavailable: 50%   type: RollingUpdateselector:matchLabels:app: springcloud-demoreplicas: 1template:metadata:labels:app: springcloud-demospec:affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 1preference:matchExpressions:- key: nodeoperator: Invalues:- m2containers:- name: springcloud-demo-containerimage: parking-base:v1imagePullPolicy: IfNotPresentlivenessProbe:exec:command:- /bin/sh- "-c"- ps -ef|grep java|grep -v grepinitialDelaySeconds: 30                            periodSeconds: 20                                   timeoutSeconds: 5                                   successThreshold: 1                                 failureThreshold: 3                                 readinessProbe:                                       exec:command:- /bin/sh- "-c"- ps -ef|grep java|grep -v grepinitialDelaySeconds: 30                             periodSeconds: 20                                  timeoutSeconds: 5                                  successThreshold: 1                                 failureThreshold: 3   ports:- containerPort: 9010env:- name: MYSQL_USERNAMEvalue: "root"- name: MYSQL_PASSWORDvalue: "mysqlpw"- name: MYSQL_IPvalue: "mysql-svc"- name: MYSQL_PORTvalue: "3306"- name: REDISvalue: "redis-service"

1.环境变量:末尾的环境变量是mysql用户、mysql密码、mysql ip、mysql端口和reids ip,分别与上面的配置文件对应
2.健康检查:检查容器中的java进程是否存活,如果java进程死掉了,就重启容器
3.更新机制:滚动更新,maxSurge: 50%,最大可超出实例数的百分比。假设2个实例,每次最多多启动1个实例。maxUnavailable: 50%,可容忍不可用实例数的百分比。假设2个实例,允许有一个不可用。
4.亲和性:在之前的部署中,由于m3这个worker节点的资源有限,而k8s默认会把服务优先部署在worker节点,导致部署后访问接口pod奔溃。此处使接口服务更加亲和m2这个master节点,部署时k8s会把这个pod调度到m2上(此处需要保证m2作为master节点具有和worker节点一样的能力,默认pod是不能被调度到master上的)。

2.4.3 Service

apiVersion: v1
kind: Service
metadata:name: springcloud-servicenamespace: springcloud-namespace
spec:ports:- port: 80protocol: TCPtargetPort: 9010selector:app: springcloud-demotype: ClusterIP

目标端口是容器的端口,容器的端口是服务的端口。

2.5 Vue

2.5.1 image

基础镜像选择的是nginx官方的最新镜像

#Dockerfile
FROM nginxCOPY parking  /usr/share/nginx/html/parkingCOPY default.conf  /etc/nginx/conf.d/COPY nginx.conf  /etc/nginx/
#default.conf
server {listen 80;server_name springcloud.demo.com;location / {root   /usr/share/nginx/html/;index  index.html index.htm;}  location  ~ ^/(base|parkingpay|sys|core|manage|manage|cloud|direct|disperse|monitor|supervise|support)/v1 {proxy_pass http://springcloud-service;proxy_http_version 1.1;proxy_set_header X-Real-IP $remote_addr;proxy_set_header Host $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_redirect   off;client_max_body_size 100m;expires -1;break;}
}

proxy_pass 的地址是后端接口Service的service name
镜像构建

docker build -t parking-ui:v1 .

2.5.2 deployment

#nginx deploy
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploynamespace: springcloud-namespace
spec:strategy:rollingUpdate:maxSurge: 50%               maxUnavailable: 50%         type: RollingUpdateselector:matchLabels:app: nginx-demoreplicas: 1template:metadata:labels:app: nginx-demospec:containers:- name: nginx-containerimage: parking-ui:v1imagePullPolicy: IfNotPresent                           ports:- containerPort: 80

2.5.3 Service

#nginx service
apiVersion: v1
kind: Service
metadata:name: nginx-servicenamespace: springcloud-namespace
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: nginx-demotype: ClusterIP

2.6 Ingress

controller使用的是ingress-nginx,由于yaml太长此处就不贴了,具体可以查看官网。

#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/affinity: cookienginx.ingress.kubernetes.io/session-cookie-hash: sha1nginx.ingress.kubernetes.io/session-cookie-name: routename: springcloud-ingressnamespace: springcloud-namespace
spec:rules:- host: springcloud.demo.comhttp:paths:- path: /backend:serviceName: nginx-serviceservicePort: 80

此处我们设置一个域名为 springcloud.demo.com,在本地的hosts文件中配置映射关系,调用的服务是nginx的service name。

3 部署

去到master节点执行命令

[root@m3 parking]# kubectl apply -f parking.yaml
namespace/springcloud-namespace created
configmap/mysql-config created
configmap/mysql-initsql created
secret/mysql-pwd created
deployment.apps/mysql-deployment created
service/mysql-svc created
deployment.apps/redis-deploy created
service/redis-service created
deployment.apps/springcloud-deploy created
service/springcloud-service created
deployment.apps/nginx-deploy created
service/nginx-service created
ingress.extensions/springcloud-demo-ingress created

查看部署情况

[root@m3 parking]# kubectl apply -f parking.yaml
[root@m3 parking]# kubectl get all -o wide -n springcloud-namespace
NAME                                     READY   STATUS    RESTARTS   AGE   IP             NODE   NOMINATED NODE   READINESS GATES
pod/mysql-deployment-5486d788cf-9crsk    1/1     Running   0          83m   172.22.2.215   m3     <none>           <none>
pod/nginx-deploy-766c7dc559-4jx98        1/1     Running   0          83m   172.22.2.217   m3     <none>           <none>
pod/redis-deploy-54b57b76bf-8m4px        1/1     Running   0          83m   172.22.2.216   m3     <none>           <none>
pod/springcloud-deploy-b9cccd6c7-7qqbv   1/1     Running   0          83m   172.22.1.175   m2     <none>           <none>NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE   SELECTOR
service/mysql-svc             ClusterIP   10.109.40.228   <none>        3306/TCP   83m   app=mysql
service/nginx-service         ClusterIP   10.104.81.76    <none>        80/TCP     83m   app=nginx-demo
service/redis-service         ClusterIP   10.102.23.193   <none>        6379/TCP   83m   app=redis-demo
service/springcloud-service   ClusterIP   10.97.214.89    <none>        80/TCP     83m   app=springcloud-demoNAME                                 READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS                   IMAGES                                          SELECTOR
deployment.apps/mysql-deployment     1/1     1            1           83m   mysql                        mysql:5.7                                       app=mysql
deployment.apps/nginx-deploy         1/1     1            1           83m   nginx-container              harbor.guojiaxing.red/private/parkingui:v1      app=nginx-demo
deployment.apps/redis-deploy         1/1     1            1           83m   redis-container              redis                                           app=redis-demo
deployment.apps/springcloud-deploy   1/1     1            1           83m   springcloud-demo-container   harbor.guojiaxing.red/private/parking-base:v3   app=springcloud-demoNAME                                           DESIRED   CURRENT   READY   AGE   CONTAINERS                   IMAGES                                          SELECTOR
replicaset.apps/mysql-deployment-5486d788cf    1         1         1       83m   mysql                        mysql:5.7                                       app=mysql,pod-template-hash=5486d788cf
replicaset.apps/nginx-deploy-766c7dc559        1         1         1       83m   nginx-container              harbor.guojiaxing.red/private/parkingui:v1      app=nginx-demo,pod-template-hash=766c7dc559
replicaset.apps/redis-deploy-54b57b76bf        1         1         1       83m   redis-container              redis                                           app=redis-demo,pod-template-hash=54b57b76bf
replicaset.apps/springcloud-deploy-b9cccd6c7   1         1         1       83m   springcloud-demo-container   harbor.guojiaxing.red/private/parking-base:v3   app=springcloud-demo,pod-template-hash=b9cccd6c7

所有的资源服务都已经启动完成

访问浏览器

进行登录操作,发现提示未登录请登录
查看接口地址

接口地址正确
查看接口日志

[root@m3 parking]# kubectl logs -f springcloud-deploy-b9cccd6c7-7qqbv -n springcloud-namespace
2019-08-03 09:11:31.003 DEBUG [-,625a956d1361ae48,625a956d1361ae48,true] 1 --- [nio-9010-exec-7] com..config.CorsFilter    : *****************进入全局过滤器,拦截所有请求******************
2019-08-03 09:11:31.026 DEBUG [-,625a956d1361ae48,625a956d1361ae48,true] 1 --- [nio-9010-exec-7] c.c.c.c.o.service.SecurityServiceImpl    : 你的当前请求地址://logon
2019-08-03 09:11:31.026  INFO [-,625a956d1361ae48,625a956d1361ae48,true] 1 --- [nio-9010-exec-7] c.c.c.c.o.service.SecurityServiceImpl    : 你还没有登录,请先登录!

说明服务调用还是正确的。日志也看不出来是什么问题。问题可能出在微服务应用上。

4 总结

此次k8s的部署基本成功,但仍然有问题和不完美的地方。
从服务的状态来看状态正常且通过健康检查,有问题的话pod状态会异常。外部请求—>ingress—>前端—>后端—>数据库,从日志看请求也正常的到达后端,这个过程的访问是没有问题的。目前对springcloud不了解,只简单的学习过springboot(部署过一个简单的springboot应用没有出现问题),出现问题没有办法定位。目前怀疑还是springcloud的问题,之后学习下这方面的知识才好处理。
对于k8s的部署,不完美的地方在于mysql的高可用方案。并不是纠结于mysql一定要部署在k8s集群中,大多数应用的mysql都不用k8s部署,主要这其中涉及了共享存储和无头服务的应用,是难点也是k8s的特色,是一个比较好的实践机会。
接触k8s时间不久,初次实现应用的k8s部署,有不正确的地方还请指正!

Kubernetes实践:使用k8s部署微服务应用相关推荐

  1. 高可用集群篇(五)-- K8S部署微服务

    高可用集群篇(五)-- K8S部署微服务 一.K8S有状态服务 1.1 什么是有状态服务 1.2 k8s部署MySQL 1.2.1 创建MySQL主从服务 1.2.2 测试主从配置 1.2.3 k8s ...

  2. K8s部署微服务(springboot+vue)

    文章目录 前言 一.使用到的K8s资源 1.1 Deployment 1.2 Service 二.Springboot基础服务部署 2.1 网关gateway 2.2 鉴权auth 2.3 文件fil ...

  3. 从运维角度看微服务 k8s部署微服务【偏理论】【AL】

    从运维角度看微服务 & 部署微服务[偏理论] 1.微服务的特点 服务组件化: 每个服务独立开发.部署,有效避免一个服务的修改引起整个系统重新部署. 技术栈灵活: 约定通信方式,使得服务本身功能 ...

  4. k8s部署微服务项目

    之前用docker-compose部署微服务项目,但是只能单节点的(那你用微服务架构干啥?),所以想搞一下k8s集群,网上找了下资料没有视频专门讲这一块,自己找了很多资料,搞了蛮长时间的,所以记录一下 ...

  5. k8s部署微服务组件eureka

    本文是在CentOS7环境上,搭建微服务集群系统简单样例. 架构描述: k8s集群:2台master node,1台slave node,均安装maven.docker.k8s环境:k8s集群系统用来 ...

  6. 一文搞懂用k8s部署微服务应用

    1.构建镜像: 参考:https://core815.blog.csdn.net/article/details/119535868中,步骤1-3完成镜像构建. 2.上传镜像到Docker Hub d ...

  7. kubernetes(k8s)部署微服务并通过ingress实现外部访问

    1. 部署微服务 1.1 创建k8s命名空间 kubectl create namespace simple-microservice 1.2 资源脚本(ruoyi-server.yaml) apiV ...

  8. Blazor+Dapr+K8s微服务之基于WSL安装K8s集群并部署微服务

     前面文章已经演示过,将我们的示例微服务程序DaprTest1部署到k8s上并运行.当时用的k8s是Docker for desktop 自带的k8s,只要在Docker for desktop中启用 ...

  9. 中国.NET开发者峰会特别活动-基于k8s的微服务和CI/CD动手实践报名

    2019.11.9 的中国.NET开发者峰会将在上海举办,到目前为止,大会的主题基本确定,这两天就会和大家会面,很多社区的同学基于对社区的信任在我们议题没有确定的情况下已经购票超过了300张,而且分享 ...

  10. k8s+jenkins+docker部署微服务实现CI/CD

    "所爱隔山海,山海不可平,海有舟可渡,山有路可行,此爱翻山海,山海皆可平." 作为一个想搞开发的,最近似乎都在干运维,不知道有没有跑偏... 2021.5.14 一般的中小公司个人 ...

最新文章

  1. 读博无门就业碰壁,孤独当了7个月“民科”后,我的论文中了顶会
  2. SAP QM 事务代码QE01录入结果后回车,为啥不弹出Manual Valuation窗口?
  3. 想学python有什么用-学python日常工作有什么用?
  4. 程序员随笔:使用来自服务器的图像(有源码)
  5. 团队作业——随堂小测(同学录)
  6. 三个基本原理和概念 - 计算机图形学、数据加密、数据挖掘
  7. 计算机ftp怎么登陆新用户,多用户登录ftp
  8. fpga板子怎么和电脑连_windows7台式电脑怎么连接路由器?台式win7电脑连路由器步骤...
  9. Ubuntu下编译运行C#——mono tools
  10. 格力可以考虑收购一个手机品牌
  11. AI上推荐 之 xDeepFM模型(显隐性高阶特征交互的组合策略)
  12. 理解条件随机场(转)
  13. 关于数字万用表你需要知道的知识
  14. 医院信息系统源码 HIS源码
  15. 在线制作banner php,分享HTML5制作Banner的实例
  16. 如何在CSDN上删除博客、删除自己上传的资源
  17. 多益网络社招iq_18年多益秋招iq测试题
  18. V神最新演讲:万字长文透露以太坊20重大升级!
  19. IG中最多案赞数居然是一棵蛋?!网友共同合作打破美国女星记录
  20. 第一章:Java语言概述与环境开发

热门文章

  1. 解决lay out无法使用闪退问题,SketchUp 2022 MAC中文 (草图大师) 支持M1intel芯片,支持monterey最新系统
  2. “数据折叠:那些AI背后“标数据的人”正在回家
  3. 扩展 MQL5 标准库和重用代码
  4. 石墨文档服务器版,石墨文档功能一览
  5. python基础知识学完之后再如何学_已学完 Python 基础知识,应该如何继续提升算法能力,以及如何过渡到机器学习?...
  6. Win10升级后出现的OEM分区处理
  7. jszip 解压压缩包_JavaScript 实现的 zip 压缩和解压缩工具包Zip.js使用详解
  8. java-php-python-ssm校园流浪猫图鉴管理系统的设计与实现计算机毕业设计
  9. SOUI控件的自绘和消息处理
  10. python rect用法,功能