如何使用K8S实现自动化部署
Jenkins+K8s+Docker 自动化部署
verison
1.16.3 (建议指定版本,不同版本对组件兼容性不一)
部署环境: 百度云/阿里云
ps:文件中的IP不可代入;资料文件可以私聊我/或者其他使用问题都可以交流,欢迎评论
部署前必备
修改/ets/hosts文件
将所有节点写入
192.168.0.128 k8s-master
192.168.0.131 k8s-node1
192.168.0.132 k8s-node2
192.168.0.135 k8s-node3
关闭防火墙和关闭****SELinux**
systemctl stop fifirewalld systemctl disable fifirewalld setenforce 0 临时关闭 vi /etc/sysconfig/selinux 永久关闭 改为SELINUX=disabled
设置系统参数
设置允许路由转发,不对bridge的数据进行处理
创建文件
vi /etc/sysctl.d/k8s.conf
内容如下:
net.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1vm.swappiness = 0
执行文件
sysctl -p /etc/sysctl.d/k8s.conf
kube-proxy开启ipvs****的前置条件
vi /etc/sysconfig/modules/ipvs.modules modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4执行 chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
所有节点关闭****swap
swapoff -a 临时关闭 vi /etc/fstab 永久关闭
注释掉以下字段
/dev/mapper/cl-swap swap swap defaults 0 0
创建 k8s yum 源
vim /etc/yum.repos.d/kubernetes.repo[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
安装docker环境
$ wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O/etc/yum.repos.d/docker-ce.repo$ yum -y install docker-ce$ systemctl enable docker && systemctl start docker**$ docker --version
安装k8s
安装:
yum install -y kubelet-1.16.3 kubeadm-1.16.3 kubectl-1.16.3
kubelet设置开机启动(注意:先不启动,现在启动的话会报错)
systemctl enable kubelet
查看版本
kubelet --version
初始化
– master 节点需要完成初始化
kubeadm init --kubernetes-version=**1.16.3** --apiserver-advertise-address=192.168.255.14 --image-repository registry.aliyuncs.com/google_containers --service-cidr=10.1.0.0/16 --pod-network-cidr=10.244.0.0/16
- 记住加入主节点的密钥,后面子节点加入集群环境的时候需要执行此命令
kubeadm join 192.168.255.14:6443 --token 6o05b2.9bloexv68xwasjjb \--discovery-token-ca-cert-hash sha256:fd03cb1bdd7293b4bde56709d2c787f345089c229ac3067900dfc5c1323b0f0d
初始化入失败如何处理
在节点上执行 kubeadm reset
常见错误:
错误一:[WARNING IsDockerSystemdCheck]: detected “cgroupfs” as the Docker cgroup driver
作为Docker cgroup驱动程序。,Kubernetes推荐的Docker驱动程序是“systemd”
解决方案:修改Docker的配置: vi /etc/docker/daemon.json,加入
{
“exec-opts”: [“native.cgroupdriver=systemd”]
}
然后重启Docker
配置kubectl 工具
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
安装calico网络组件
下载calico的yaml文件
mkdir k8s cd k8s wget https://docs.projectcalico.org/v3.10/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml --no-check-certificatesed -i 's/192.168.0.0/10.244.0.0/g' calico.yaml
创建calico服务
kubectl apply -f calico.yaml
等待几分钟,查看所有Pod的状态,确保所有Pod都是Running状态
nfs-client的安装(所有节点都需要安装)
安装命令
yum install -y nfs-utils systemctl enable nfs 开机启动
以下操作只需在master节点上进行
创建共享目录
mkdir -p /opt/nfs/jenkins vi /etc/exports 编写NFS的共享配置 /opt/nfs/jenkins *(rw,no_root_squash) *代表对所有IP都开放此目录,rw是读写
3)启动服务
service start nfs
4)查看NFS共享目录
创建NFS client provisioner
nfs-client-provisioner 是一个Kubernetes的简易NFS的外部provisioner,本身不提供NFS,需要现有
的NFS服务器提供存储。
上传nfs-client-provisioner构建文件
其中注意修改deployment.yaml,使用之前配置NFS服务器和目录
构建nfs-client-provisioner的pod资源
kubectl create -f .
查看pod是否创建成功
安装jenkins-master
上传Jenkins-Master构建文件
其中有两点注意:
第一、在StatefulSet.yaml文件,声明了利用nfs-client-provisioner进行Jenkins-Master文件存储
第二 、Service发布方法采用NodePort,会随机产生节点访问端口
创建kube-ops的namespace
因为我们把Jenkins-Master的pod放到kube-ops下
kubectl create namespace kube-ops
构建Jenkins-Master的pod资源
kubectl create -f .
查看pod是否创建成功
kubectl get pods -n kube-ops
查看信息,并查看Pod运行在那个Node上
kubectl describe pods -n kube-ops
查看分配的端口
kubectl get service -n kube-ops
最终访问的地址为 http://master-ip:31713
安装基本的插件
Localization:Chinese
Git
Pipeline
Extended Choice Parameter
Jenkins与Kubernetes整合
安装Kubernetes插件
系统管理->插件管理->可选插件
实现Jenkins与Kubernetes整合
系统管理->系统配置->云->新建云->Kubernetes
名称不要随便写,后面会用到
kubernetes地址采用了kube的服务器发现:https://kubernetes.default.svc.cluster.local
namespace填kube-ops,然后点击Test Connection,如果出现 Connection test successful 的提
示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信
Jenkins URL 地址:http://jenkins.kube-ops.svc.cluster.local:8080
构建Jenkins-Slave自定义镜像
Jenkins-Master在构建Job的时候,Kubernetes会创建Jenkins-Slave的Pod来完成Job的构建。我们选择
运行Jenkins-Slave的镜像为官方推荐镜像:jenkins/jnlp-slave:latest,但是这个镜像里面并没有Maven
环境,为了方便使用,我们需要自定义一个新的镜像:
准备材料:
构建出一个新镜像:
jenkins-slave-maven:latest
在目录下 执行 docker build -t jenkins-slave-maven:latest . 进行构建镜像
镜像上传到Harbor
先登录harbor
docker login -u admin -p Harbor123456 192.168.0.133:85 (192.168.0.133:85 harbor的地址)
docker tag jenkins-slave-maven:latest 192.168.0.133:85/library/jenkins-slave-maven:latest
docker push 192.168.0.133:85/library/jenkins-slave-maven:latest
Jenkins+Kubernetes+Docker完成微服务持续集成
拉取代码,构建镜像
1)创建NFS共享目录
让所有Jenkins-Slave构建指向NFS的Maven的共享仓库目录
jenkins创建项目,编写构建Pipeline
创建一个Jenkins流水线项目
构建分支参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FhhhrxXw-1648864695613)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637736197532.png)]
构建发布服务选项参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YVeA20We-1648864695614)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637736224416.png)]
Number of Visible Items 代表下面的参数面板默认展示几个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKReJFwc-1648864695614)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637736369949.png)]
delimiter 分隔符可随意设定,但要与 pipeline脚本中的分隔符保持一致
// 第三步stage('构建镜像,部署项目'){//把选择的项目信息转为数组def selectedProjects = "${project_name}".split(',')
编写pipeline脚本
def git_address = ""; //git项目地址
def git_auth = "982116f3-def0-4a6f-8327-e08cbaf2872c"
//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "127.0.0.1:18085" //自己的harbor仓库地址
//Harbor的项目名称
def harbor_project_name = "pa-sas"
//Harbor的凭证
def harbor_auth = "4c4bd142-864d-4573-890a-031ad2155bcd"
def secret_name = "registry-auth-secret"
//k8s凭证
def k8s_auth = "f5e3a4c1-e9a3-4a54-afb9-8245e7c08cd2"podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [containerTemplate(name: 'jnlp', image: "127.0.0.1:18085/library/jenkins-slave-maven:latest"),containerTemplate(name: 'docker', image: "docker:stable",ttyEnabled: true,command: 'cat'),],volumes: [hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),nfsVolume(mountPath: '/usr/local/apache-maven/repo', serverAddress: '127.0.0.1' , serverPath: '/opt/nfs/maven'), //NFS 服务的IP],
)
{node("jenkins-slave"){// 第一步stage('拉取代码'){checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])}// 第二步stage('代码编译'){//编译并安装公共工程sh "mvn -f pa-framework-model clean install"sh "mvn -f pa-framework-utils clean install"sh "mvn -f pa-framework-common clean install"}// 第三步stage('构建镜像,部署项目'){//把选择的项目信息转为数组def selectedProjects = "${project_name}".split(',')for(int i=0;i<selectedProjects.size();i++){//取出每个项目的名称和端口def currentProject = selectedProjects[i];//项目名称def currentProjectName = currentProject.split('@')[0]//项目启动端口def currentProjectPort = currentProject.split('@')[1]//定义镜像名称def imageName = "${currentProjectName}:${tag}"//编译,构建本地镜像sh "mvn -f ${currentProjectName} clean package dockerfile:build"container('docker') {//给镜像打标签sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"//登录Harbor,并上传镜像withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {//登录sh "docker login -u ${username} -p ${password} ${harbor_url}"//上传镜像sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"}//删除本地镜像sh "docker rmi -f ${imageName}"sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"}def deploy_image_name = "${harbor_url}/${harbor_project_name}/${imageName}"//部署到K8Ssh """sed -i 's#\$IMAGE_NAME#${deploy_image_name}#' ${currentProjectName}/deploy.ymlsed -i 's#\$SECRET_NAME#${secret_name}#' ${currentProjectName}/deploy.yml"""kubernetesDeploy configs: "${currentProjectName}/deploy.yml", kubeconfigId: "${k8s_auth}"} }}
}
Pipeline 说明
git_address : git地址
git_auth :需 在jenkins中配置相应的凭据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWWT7bgH-1648864695614)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637725118468.png)]
harbor_url
harbor仓库地址
harbor_project_name
harbor仓库中的项目名称,如果没有,需要事先在harbor仓库中建立
harbor_auth
同样git_auth,在jenkins中建立相应的凭据
secret_name
k8s 建立harbor凭证,用于在harbor中拉取镜像
k8s 建立harbor凭证
在master节点中登录harbor
docker login -u admin -p Harbor123 127.0.0.1:85
生成令牌
kubectl create secret docker-registry **registry-auth-secret** --docker-server=127.0.0.1:85 --docker-username=admin --docker-password=Harbor123 --
docker-email=test@sec.com
查看令牌
kubectl get secret
构建k8s_auth
与harbor和git生成令牌的方式不一样。
需要jenkins安装kcd插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPnDjC2E-1648864695615)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637725716389.png)]
安装完成之后,添加凭证
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0EymWtZb-1648864695615)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637725771324.png)]
获取凭证内容
到k8s的Master节点复制
cat /root/.kube/config
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnacwXAt-1648864695616)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637726002806.png)]
复制整个文件内容至Content内容框内即可。
至此,流水线发布所需工作已做完
微服务配置文件
编写deploy.yml,用于 K8S 发布
---
apiVersion: v1
kind: Service
metadata:name: pa-govern-gatewaylabels:app: pa-govern-gateway
spec:type: NodePortports:- port: 18080name: pa-govern-gatewaytargetPort: 18080nodePort: 31003 # 指定pod暴露出的接口 只需要在网关项目指定protocol: TCPselector:app: pa-govern-gateway
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: pa-govern-gateway
spec:serviceName: "pa-govern-gateway"replicas: 1selector:matchLabels:app: pa-govern-gatewaytemplate:metadata:labels:app: pa-govern-gatewayspec:imagePullSecrets:- name: $SECRET_NAMEcontainers:- name: pa-govern-gatewayimage: $IMAGE_NAMEports:- containerPort: 18080podManagementPolicy: "Parallel"
编写Dockerfile,用于docker生成服务镜像
#FROM java:8
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} myservice.jar
EXPOSE 18080
ENTRYPOINT ["java","-jar","-Xms2048M","-Xmx2048M","/myservice.jar"]
pom文件增加docker编译插件
<build><finalName>pa-govern-gateway-18080</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin><plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><version>1.3.6</version><configuration><repository>${project.artifactId}</repository><buildArgs><JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE></buildArgs></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId></plugin></plugins></build>
kubectl 常用命令及常见问题
kubectl get nodes 查看所有主从节点的状态
kubectl get ns 获取所有namespace资源
kubectl get pods -n {$nameSpace} 获取指定namespace的pod
kubectl describe pod的名称 -n {$nameSpace} 查看某个pod的执行过程
kubectl logs --tail=1000 pod的名称 | less 查看日志
kubectl create -f xxx.yml 通过配置文件创建一个集群资源对象
kubectl delete -f xxx.yml 通过配置文件删除一个集群资源对象
kubectl delete pod名称 -n {$nameSpace} 通过pod删除集群资源
kubectl get service -n {$nameSpace} 查看pod的service情况
kubectl get svc -n kubernetes-dashboard
如何查看 systemctl 启动的日志
journalctl -f
pod 一直处于pending状态 如果查看问题
kubectl describe pod -n kube-ops jenkins-0 查看日志
安装calico 时的问题 running状态 但是 not ready
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0du5Cz9-1648864695617)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637560572704.png)]
解决方案 :在calico的yaml中 增加以下配置
name: IP_AUTODETECTION_METHOD
value: “interface=eth.*” eth 为本地网卡的前缀名称
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pkzgNsdf-1648864695617)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637560550193.png)]
无法解析域名的问题
修改coredns组件里的configmap
kubectl edit cm -n kube-system coredns
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZZnlWxo-1648864695618)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1637639481335.png)]
docker 构建镜像无权限问题
node 打标签,用于部署时选择节点 (master节点上执行)
kubectl label node k8s-node1 type=node1
如何让你的机器加入到K8s环境中
执行部署前必备流程
安装docker环境
安装K8s
yum install -y kubelet-1.16.3 kubeadm-1.16.3 kubectl-1.16.3
systemctl enable kubelet
执行加入节点的命令
kubeadm join 192.168.255.14:6443 --token 6o05b2.9bloexv68xwasjjb--discovery-token-ca-cert-hash sha256:fd03cb1bdd7293b4bde56709d2c787f345089c229ac3067900dfc5c1323b0f0d
加入成功后启动kubelet
systemctl start kubelet
回到Master节点查看,如果Status全部为Ready,则说明加入成功
kubectl get nodes
在你的机器上安装nfs-client
yum install -y nfs-utils
systemctl enable nfs 开机启动
systemctl start nfs
showmount -e 192.168.255.14(安装nfs的master机器IP)
查看nfs是否可正常读取到共享地址
在master机器上给你的机器打上标签
kubectl label node k8s-node1 type=node1
如果已有标签,加上 --overwrite 进行覆盖
修改服务的deploy.yml配置文件,发布到指定node
装K8s
yum install -y kubelet-1.16.3 kubeadm-1.16.3 kubectl-1.16.3
systemctl enable kubelet
执行加入节点的命令
kubeadm join 192.168.255.14:6443 --token 6o05b2.9bloexv68xwasjjb--discovery-token-ca-cert-hash sha256:fd03cb1bdd7293b4bde56709d2c787f345089c229ac3067900dfc5c1323b0f0d
加入成功后启动kubelet
systemctl start kubelet
回到Master节点查看,如果Status全部为Ready,则说明加入成功
kubectl get nodes
在你的机器上安装nfs-client
yum install -y nfs-utils
systemctl enable nfs 开机启动
systemctl start nfs
showmount -e 192.168.255.14(安装nfs的master机器IP)
查看nfs是否可正常读取到共享地址
在master机器上给你的机器打上标签
kubectl label node k8s-node1 type=node1
如果已有标签,加上 --overwrite 进行覆盖
修改服务的deploy.yml配置文件,发布到指定node
如何使用K8S实现自动化部署相关推荐
- K8S搭建自动化部署环境(三)Jenkins下载、安装和启动
各位大佬,前文如下: K8S搭建自动化部署环境(一)安装Kubernetes K8S搭建自动化部署环境(二)安装K8S管理工具Kuboard V3 一.jenkins 下载 jenkins下载地址:h ...
- K8S搭建自动化部署环境(四)Jenkins多分支流水线Blue Ocean的安装和使用
各位大佬,前文如下: K8S搭建自动化部署环境(一)安装Kubernetes K8S搭建自动化部署环境(二)安装K8S管理工具Kuboard V3 K8S搭建自动化部署环境(三)Jenkins下载.安 ...
- k8s+jenkins自动化部署
一:环境准备 1. 四台服务器:根据具体需求分配 A :master (192.168.0.115) 2核4g 用于k8s的主节点 B: node1 (192.168.0.126) 2核8g C: n ...
- K8S实战集训第一课 Ansible自动化部署k8s、弹性伸缩、Helm包管理、k8s网络模型介绍
Ansible自动化部署K8S集群 一.Ansible自动化部署K8S集群 1.1 Ansible介绍 Ansible是一种IT自动化工具.它可以配置系统,部署软件以及协调更高级的IT任务,例如持续部 ...
- Jenkins+Docker+K8S+Git+CICD自动化部署
1.构建流程图 自动触发jenkins部署通过svn或Git的hooks来实现,是否自动触发根据项目内部沟通决定,我们目前没有自动触发,原因是QA在测试的过程中不希望被自动触发的部署打断,不过也可以方 ...
- kubernetes+Azure DevOps实现.Net Core项目的自动化部署均衡负载
1. 前言 前前后后学习kubernetes也有一个来月了,关于kubernetes的博客也写了有十多篇.但是技术如果无法落地到实际的应用场景终归是纸上谈兵,所以就有了这一出:通过结合kubernet ...
- asp.net core结合Gitlab-CI实现自动化部署
一.前言 在之前的文章中写过k8s+Jenkins+GitLab-自动化部署asp.net core项目 的topic,这次讲解一下gitlab-ci的CI/CD,说实话,自动化部署是在是非常的舒服, ...
- 架构设计:服务自动化部署和管理流程
本文源码:GitHub·点这里 || GitEE·点这里 一.分布式服务 从常规分布式架构系统来说,划分出十来个独立的微服务模块是很常见的,然后不同的开发人员分工几个服务块,负责日常开发和维护,微服务 ...
- 【k8s系列001】K8s集群部署H2O
一.k3s集群部署 https://docs.rancher.cn/docs/k3s/_index k3s官网 1.安装master curl -sfL http://rancher-mirror.c ...
- 小米Redis的K8s容器化部署实践
本文讲述了小米是如何将Redis Cluster部署在K8S上提供高质量的服务的 往期文章回顾:HBase Region Read Replicas功能详解 背景 Why K8S How K8s Wh ...
最新文章
- 你熟悉Git常用的命令吗?(点赞+收藏)
- (初学必看)deep graph library(dgl)库的入门引导
- golang设置默认地区
- python正则group()的用法—正则提取括号内以及其他符号内内容
- JDBC预状态通道设置时间格式的问题
- unity镜头边缘羽化_【后期修图】如何利用Ps中的自适应广角滤波器校正镜头失真?...
- Flash开发的基本概念
- 交互系统的构建之(三)TTS语音合成的加盟
- vue中使用百度地图,悬浮窗搜索功能
- 三星GalaxyS21或取消附赠有线耳机:捆绑卖新款无线耳机
- mysql 显示各列的数据类型命令_mysql中查看库中某个表的所有列和对应的字段类型...
- 配置VIM语法高亮及自动缩进
- 请详细描述listview与gridview的异同点_专利和著作权有什么异同点,听听专业人士怎么说...
- win8修复计算机u盘,Win8系统U盘里的文件不见了怎么办?
- 实验一 结构化分析(软件工程)
- 命令行解压cab文件
- 众泰Z700导航升级高德地图
- 网络扫描及安全评估实验实验报告
- HTTP请求的TCP瓶颈分析
- 华为软件类校招 2014年9月3日 熟悉机考环境 1.记票统计 2.求最大递增数 3.Word Maze(单词迷宫)
热门文章
- 2020电子设计竞赛G题 - 非接触物体尺寸形态测量
- linux两台电脑共享文件夹怎么设置,快速几步完美实现两台电脑共享上网的设置...
- 矩阵键盘控制拉闭幕式流水灯
- Tyvj P1288 飘飘乎居士取能量块
- VS2019CPU/内存诊断功能
- java游戏猿人时代_猿人时代游戏下载-猿人时代游戏(附攻略)正版下载v1.0.0-第五驱动...
- 技术人如何才能做好绩效管理提升团队绩效?
- 配置访问路径自定义的swagger接口说明文档api
- charles——教程——转载
- AI上推荐 之 NeuralCF与PNN模型(改变特征交叉方式)