云原生的 CI/CD 框架tekton - pipeline(一)

文章目录

    • 1. 官方介绍
    • 2. 组件
      • 2.1 Tekton Pipelines
      • 2.2 部署pipeline
      • 2.3 部署dashborad
        • 2.3.1 task
        • 2.3.2 taskrun
        • 2.3.3 Pipeline
        • 2.3.4 PipelineRun
    • 3. 案例
      • 案例1: 拉取代码并查看readme
        • step1: 创建task - 拉取代码
        • step2: 创建task - 查看reamde
        • step3: 创建task的编排 - pipeline
        • step4: 创建secrets - ssh key/username
        • step5: 创建serviceaccount
        • step6: 创建pipelinerun
      • 案例2:构建和推送镜像
        • step1: 创建task - 拉取代码
        • step2: 创建task - 构建代码
        • step3: 创建task - 打包镜像
        • step4: 创建secret - docker 认证
        • step5: 创建serviceaccount
        • step6: 创建task编排 - pipeline
        • step7: 创建pipelinerun

1. 官方介绍

Tekton 的前身是 Knative 项目的 build-pipeline 项目,这个项目是为了给 build 模块增加 pipeline 的功能,但是随着不同的功能加入到 Knative build 模块中,build 模块越来越变得像一个通用的 CI/CD 系统,于是,索性将 build-pipeline 剥离出 Knative,就变成了现在的 Tekton,而 Tekton 也从此致力于提供全功能、标准化的云原生 CI/CD 解决方案。

Tekton 是一个功能强大且灵活的 Kubernetes 原生开源框架,用于创建持续集成和交付(CI/CD)系统。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。

一句话总结:Tekton 是云原生的 CI/CD 框架,是云原生的的 CI/CD 解决方案

2. 组件

Tekton 由一些列组件组成:

  • Tekton Pipelines:是 Tekton 的基础,它定义了一组 Kubernetes CRD 作为构建块,我们可以使用这些对象来组装 CI/CD 流水线。
  • Tekton Triggers:允许我们根据事件来实例化流水线,例如,可以我们在每次将 PR/MR 合并到 GitHub/GitLab 仓库的时候触发流水线实例和构建工作。
  • Tekton CLI:提供了一个名为 tkn的命令行界面,它构建在 Kubernetes CLI 之上,运行和 Tekton 进行交互。
  • Tekton Dashboard:是 Tekton Pipelines的基于 Web 的一个图形界面,可以线上有关流水线执行的相关信息。
  • Tekton Catalog:是一个由社区贡献的高质量 Tekton 构建块(任务、流水线等)存储库,可以直接在我们自己的流水线中使用这些构建块。用于访问 Tekton Catalog的 Web 图形界面工具叫Tekton Hub
  • Tekton Operator:是一个 Kubernetes Operator,可以让我们在 Kubernetes 集群上安装、更新、删除 Tekton 项目。

2.1 Tekton Pipelines

参考链接:https://tekton.dev/docs/concepts/concept-model/

Tekton Pipeline中有5类对象,核心理念是通过定义yaml定义构建过程。

  • task:task表示一个任务,task里可以定义一系列的steps,例如编译代码、构建镜像、推送镜像等,每个step实际由一个Pod执行。
  • taskRun:task只是定义了一个模版,taskRun才真正代表了一次实际的运行,当然你也可以自己手动创建一个taskRun,taskRun创建出来之后,就会自动触发task描述的构建任务。
  • PipelineResource:表示pipeline input资源,比如github上的源码,或者pipeline output资源,例如一个容器镜像或者构建生成的jar包等,目前已经弃用。
  • Pipeline:一个或多个task、PipelineResource以及各种定义参数的集合。
  • PipelineRun:类似task和taskRun的关系,pipelineRun也表示某一次实际运行的pipeline,下发一个pipelineRun CRD实例到kubernetes后,同样也会触发一次pipeline的构建。

2.2 部署pipeline

# install
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
# moinitor
kubectl get pods --namespace tekton-pipelines --watch

2.3 部署dashborad

# install
kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/release-full.yaml
# monitor
kubectl port-forward -n tekton-pipelines service/tekton-dashboard 9097:9097
# expose port
kubectl port-forward -n tekton-pipelines service/tekton-dashboard 9097:9097

pipeline和task的关系
在这里插入图片描述
pipeline和pipelinerun的关系
在这里插入图片描述

2.3.1 task

Task 就是一个任务执行模板,之所以说 Task 是一个模板是因为 task 定义中可以包含变量,Task 在真正执行的时候需要给定变量的具体值。

如果把 Tekton 的 Task 有点儿类似于定义一个函数,Task 通过 inputs.params 定义需要哪些入参,并且每一个入参还可以指定默认值。Task 的 steps 字段表示当前 task 是有哪些步骤组成的,每一个步骤具体就是基于镜像启动一个 container 执行一些操作,container 的启动参数可以通过 task 的入参使用模板语法进行配置。

Demo1:test-task.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: test
spec:params:- name: usernametype: stringdefault: "hello,cs"steps:- name: echoimage: alpinescript: |#!/bin/shecho "$(params.username)"

Demo2:task-git.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: clone-code-testdescription: This Task will clone code and search file
spec:steps:- name: clone-code-testimage: alpine/gitscript: |#!/bin/shecho "Hello World"git clone https://gitlab.com/kaixuan.wang/app.gitcd appcat README.md

也可以对demo2进行调整如下:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: clone-code-testdescription: This Task will clone code and search file
spec:params:- name: repo-urltype: stringdefault: "https://gitlab.com/kaixuan.wang/gitops.git"steps:- name: clone-code-testimage: alpine/gitscript: |#!/bin/shecho "Hello World"git clone $(params.repo-url)ls -l gitops
#  params:
#    - name: repo-url
#      value: git@jihulab.com:cs-test-group1/kxwang/test.git
2.3.2 taskrun

Task 定义好以后是不能执行的,就像一个函数定义好以后需要调用才能执行一样。所以需要再定义一个 TaskRun 去执行 Task。

TaskRun 主要是负责设置 Task 需要的参数,并通过 taskRef 字段引用要执行的 Task。

Demo1:

test-task-run.yaml(赋值username)

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:name: test-task-run #taskrun name
spec:taskRef:name: testparams:- name: usernamevalue: "Tekton"

test-task-2.yaml(不赋值)

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:name: test-task-run-1 #taskrun name
spec:taskRef:name: test

通过tkn工具查看
在这里插入图片描述
然而在实际使用过程中,我们一般很少使用TaskRun,因为它只能给一个Task 传参,Tekton提供了给多个Task同时传参的解决方案Pipeline和PipelineRun。

Demo2:taskrun-git.yaml

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:generateName: taskrun-git-  # name: taskrun-git
spec:taskRef:name: clone-code
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:generateName: taskrun-git-
spec:taskRef:name: clone-code
#  params:
#  - name: repo-url
#    value: "https://gitlab.com/test-ce/group/123.git"
2.3.3 Pipeline

一个 TaskRun 只能执行一个 Task,当需要编排多个 Task 的时候就需要用到 Pipeline。

Pipeline 是一个编排 Task 的模板。

  • Pipeline 的 params 声明了执行时需要的入参。
  • Pipeline 的 spec.tasks 定义了需要编排的 Task。
  • Tasks 是一个数组,数组中的 task 并不是通过数组声明的顺序去执行的,而是通过 runAfter 来声明 task 执行的顺序。

Tekton controller 在解析 CRD 的时候会解析 Task 的顺序,然后根据 runAfter 设置生成的依次树依次去执行。Pipeline 在编排 Task 的时候需要给每一个 Task 传入必须的参数,这些参数的值可以来自 Pipeline 自身的 params 设置。下面是一个Demo:

pipeline.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:name: test-pipeline
spec:params:- name: usernametype: stringtasks:- name: test-1taskRef:name: testparams:- name: usernamevalue: $(params.username)
#    - name: clone-code-test
#      runAfter:
#        - test-1
#      taskRef:
#        name: clone-code-test
2.3.4 PipelineRun

和 Task 一样 Pipeline 定义完成以后也是不能直接执行的,需要 PipelineRun 才能执行 Pipeline。PipelineRun 的主要作用是给 Pipeline 传入必要的入参,并执行 Pipeline。

pipelinerun.yaml

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:generateName: test-pipeline-run-
spec:pipelineRef:name: test-pipelineparams:- name: usernamevalue: "Tekton"

3. 案例

案例1: 拉取代码并查看readme

step1: 创建task - 拉取代码

安装git-clone task

kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.9/git-clone.yaml

或者自己创建一个git-clone task

cat task.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: git-clone
spec:workspaces:- name: output- name: ssh-directoryparams:- name: urltype: string- name: revisiontype: stringdefault: ""steps:- name: cloneimage: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.40.2"env:- name: PARAM_URLvalue: $(params.url)- name: PARAM_REVISIONvalue: $(params.revision)- name: WORKSPACE_OUTPUT_PATHvalue: $(workspaces.output.path)- name: WORKSPACE_SSH_PATHvalue: $(workspaces.ssh-directory.path)script: |#!/usr/bin/env shmkdir $HOME/.sshcp $WORKSPACE_SSH_PATH/* $HOME/.ssh/chmod 400 $HOME/.ssh/*set -euCHECKOUT_DIR="${WORKSPACE_OUTPUT_PATH}"/ko-app/git-init \-url="${PARAM_URL}" \-revision="${PARAM_REVISION}" \-path="${CHECKOUT_DIR}"cd "${CHECKOUT_DIR}"EXIT_CODE="$?"if [ "${EXIT_CODE}" != 0 ] ; thenexit "${EXIT_CODE}"fi
step2: 创建task - 查看reamde

cat show-readme.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: show-readme
spec:workspaces:- name: sourcesteps:- name: show-readmeimage: alpine:latestscript: |cat $(workspaces.source.path)/README.md
step3: 创建task的编排 - pipeline

cat pipeline.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:name: cat-branch-readme
spec:params:- name: repo-urltype: string- name: branch-nametype: stringworkspaces:- name: shared-data- name: git-credentialstasks:- name: fetch-repotaskRef:name: git-cloneworkspaces:- name: outputworkspace: shared-data- name: ssh-directoryworkspace: git-credentialsparams:- name: urlvalue: $(params.repo-url)- name: revisionvalue: $(params.branch-name)- name: show-readmerunAfter: ["fetch-repo"]taskRef:name: show-readmeworkspaces:- name: sourceworkspace: shared-data
step4: 创建secrets - ssh key/username

secrets-ssh.yaml

apiVersion: v1
kind: Secret
metadata:name: gitlab-sshannotations:tekton.dev/git-0: jihulab.com #不需要指定协议,如果带http://或者https://会报错
type: kubernetes.io/ssh-auth
data:ssh-privatekey: <cat ~/.ssh/id_rsa | base64 -w0>known_hosts: <cat ~/.ssh/known_hosts | base64 -w0>

创建secrets - username

secrets-basic.yaml

apiVersion: v1
kind: Secret
metadata:name: gitlab-authannotations:tekton.dev/git-0: https://jihulab.com #需要指定协议,如果不带会报错
type: kubernetes.io/basic-auth
stringData:username: kxwang@jihulab.compassword: xxxxxxx
step5: 创建serviceaccount

gitlab-sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:name: gitlab-sa
secrets:
- name: gitlab-auth
- name: gitlab-ssh
step6: 创建pipelinerun

cat pipelinerun.yaml

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:generateName: git-clone-checking-out-a-branch
spec:serviceAccountName: gitlab-sapipelineRef:name: cat-branch-readmepodTemplate:securityContext:fsGroup: 65532workspaces:- name: shared-datavolumeClaimTemplate:spec:accessModes:- ReadWriteOnceresources:requests:storage: 256MivolumeMode: Filesystem- name: git-credentialssecret:secretName: gitlab-sshparams:- name: repo-urlvalue: git@jihulab.com:cs-test-group1/kxwang/test.git- name: branch-namevalue: master

案例2:构建和推送镜像

step1: 创建task - 拉取代码

同上

step2: 创建task - 构建代码

task-build.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: build-1
spec:workspaces:- name: source # 名称params:- name: skipunittype: stringdefault: "true"steps:- name: buildimage: maven:3-jdk-8workingDir: $(workspaces.source.path)script: |#/usr/bin/env shmvn clean package -Dmaven.test.skip=$(params.skipunit)volumeMounts:- name: cachemountPath: /workspace/.m2subPath: m2-cache- name: cachemountPath: /workspace/.cachesubPath: m2-cachevolumes:- name: cacheemptyDir: {}
step3: 创建task - 打包镜像

task-package.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:name: package-1
spec:workspaces:- name: source # 名称params:- name: image_desttype: string- name: image_tagtype: stringdefault: "latest"- name: DockerfilePathtype: stringdefault: Dockerfile- name: Contexttype: stringdefault: .steps:- name: packageimage: docker:stableworkingDir: $(workspaces.source.path)script: |#/usr/bin/env shdocker login registry.ap-southeast-1.aliyuncs.comdocker build -t $(params.image_dest):$(params.image_tag) -f $(params.DockerfilePath) $(params.Context)docker push $(params.image_dest):$(params.image_tag)volumeMounts:- name: dockersorckmountPath: /var/run/docker.sockvolumes:- name: dockersorckhostPath:path: /var/run/docker.sock
step4: 创建secret - docker 认证

1、config.json(~/.docker/config.json)

{"auths": {"registry.ap-southeast-1.aliyuncs.com": {"auth": "d2t4XzA0MjJAMTYzLmNvbTp3a3g3MzE1MDg4MDY="}}
}

2、secret-docker.yaml

apiVersion: v1
kind: Secret
metadata:name: docker-credentialsannotations:tekton.dev/docker-0: https://registry.ap-southeast-1.aliyuncs.com
type: kubernetes.io/basic-auth
stringData:username: wkx_0422@163.compassword: 123456
step5: 创建serviceaccount

gitlab-sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:name: gitlab-sa
secrets:
- name: gitlab-auth
- name: gitlab-ssh
- name: docker-credentials
step6: 创建task编排 - pipeline

pipeline.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:name: clone-build-push-1
spec:description: |This pipeline clones a git repo, builds a Docker image with Kaniko andpushes it to a registryparams:- name: repo-urltype: string- name: image_desttype: string- name: image_tagtype: stringworkspaces:- name: shared-datatasks:# 拉取代码- name: fetch-sourcetaskRef:name: git-cloneworkspaces:- name: outputworkspace: shared-dataparams:- name: urlvalue: $(params.repo-url)# 打包- name: build-codetaskRef:name: build-1workspaces:- name: sourceworkspace: shared-datarunAfter:- fetch-source# 构建并推送镜像- name: package-imagerunAfter: ["build-code"]taskRef:name: package-1workspaces:- name: sourceworkspace: shared-dataparams:- name: image_destvalue: $(params.image_dest)- name: image_tagvalue: $(params.image_tag)
step7: 创建pipelinerun

pipelinerun.yaml

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:generateName: clone-build-push-run-
spec:serviceAccountName: gitlab-sapipelineRef:name: clone-build-push-1podTemplate:securityContext:fsGroup: 65532workspaces:- name: shared-datavolumeClaimTemplate:spec:accessModes:- ReadWriteOnceresources:requests:storage: 128Miparams:- name: repo-urlvalue: https://jihulab.com/cs-test-group1/kxwang/test.git- name: image_destvalue: registry.ap-southeast-1.aliyuncs.com/my_image_repo/demo- name: image_tagvalue: v2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/200974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试面试题解析--什么题是必问的?

设计测试用例的主要方法有哪些&#xff1f;简述一下缺陷的生命周期&#xff1f;测试流程&#xff1f;项目流程&#xff1f;验收测试中和β测试区别&#xff1f;如何维护测试用例&#xff1f;每天测多少用例怎么分配的测试的一天能找多少bug你在上一家公司&#xff0c;写没写过测…

Selenium+Unittest+HTMLTestRunner框架更改为Selenium+Pytest+Allure(二)

1 代码框架 整体项目结构如图&#xff1a; Common&#xff1a;公共库 Logs&#xff1a; 日志目录 Page&#xff1a; 页面元素 Report&#xff1a;测试报告 TestCase&#xff1a;测试用例 TestData&#xff1a; 测试数据 2 单模块运行 直接上代码&#xff1a; # -*- coding…

详细介绍如何使用 SSD 进行实时物体检测:单次 MultiBox 探测器-含源码

介绍 在实时对象检测中,主流范例传统上采用多步骤方法,包括边界框、像素或特征重采样以及高质量分类器应用的提议。虽然这种方法已经实现了高精度,但其计算需求往往阻碍了其对实时应用的适用性。然而,单次多框检测器 (SSD) 代表了基于深度学习的对象检测的突破性飞跃。SSD…

SpringCloud | Dubbo 微服务实战——注册中心详解

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 |Eureka,Nacos,Consul,Zookeeper在Spring Cloud和Dubbo中实战 引言 在项目开发过程中&#xff0c;随着项目不断扩大&#xff0c;也就是业务的不断增多&#xff0c;我们将采用集群&#xf…

647. Palindromic Substrings 516. Longest Palindromic Subsequence

647. Palindromic Substrings Given a string s, return the number of palindromic substrings 回文子串 in it. A string is a palindrome when it reads the same backward as forward. A substring is a contiguous sequence of characters within the string. nomal: …

LeetCode //C - 221. Maximal Square

221. Maximal Square Given an m x n binary matrix filled with 0’s and 1’s, find the largest square containing only 1’s and return its area. Example 1: Input: matrix [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,…

图解Spark Graphx实现顶点关联邻接顶点的collectNeighbors函数原理

一、场景案例 在一张社区网络里&#xff0c;可能需要查询出各个顶点邻接关联的顶点集合&#xff0c;类似查询某个人关系比较近的都有哪些人的场景。 在用Spark graphx中&#xff0c;通过函数collectNeighbors便可以获取到源顶点邻接顶点的数据。 下面以一个例子来说明&#…

C语言--每日选择题--Day37

第一题 1. 有以下说明语句&#xff1a;则下面引用形式错误的是&#xff08;&#xff09; struct Student {int num;double score; };struct Student stu[3] {{1001,80}, {1002,75}, {1003,91}} struct Student *p stu; A&#xff1a;p->num B&#xff1a;(p).num C&#…

[论文精读]序列建模使大视觉模型的规模化学习成为可能

本博客是一篇最新论文的精读&#xff0c;论文为UC伯克利大学及约翰霍普金斯大学相关研究者新近(2023.12.1)在arxiv上上传的《Sequential Modeling Enables Scalable Learning for Large Vision Models》 。知名科技新媒体“新智元”给与本文极高评价&#xff0c;并以《计算机视…

STM32(PWM、ADC)

1、PWM 定义 PWM&#xff0c;全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;它通过改变信号的高电平和低电平的持续时间比例来控制输出信号的平均功率或电压。 PWM&#xff0c;全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff…

FPGA实现电机位置环、速度环双闭环PID控制

一、设计思路 主要设计思路就是根据之前写的一篇FPGA实现电机转速PID控制&#xff0c;前面已经实现了位置环的控制&#xff0c;思想就是通过电机编码器的当前位置值不断地修正PID去控制速度。 那为了更好的实现控制&#xff0c;可以在位置环后加上速度环&#xff0c;实现电机位…

scipy

scipy 是什么常用方法 是什么 scipy是Python语言的一个开源数值计算库&#xff0c;主要目的是为科学、工程、计算等领域提供有用的数学算法和函数&#xff0c;包括线性代数、优化、信号处理、傅里叶变换、统计函数等。它是Python科学计算环境的重要组成部分&#xff0c;通常与N…

2021年GopherChina大会-核心PPT资料下载

一、峰会简介 自 Go 语言诞生以来&#xff0c;中国便是其应用最早和最广的国家之一&#xff0c;根据 Jetbrains 在 2021 年初做的调查报告&#xff0c;总体来说目前大概有 110 万专业的开发者 选择 Go 作为其主要开发语言。就其全球分布而言, 居住在亚洲的开发者最多&#xff…

go学习之goroutine和channel

文章目录 一、goroutine(协程)1.goroutine入门2.goroutine基本介绍-1.进程和线程说明-2.程序、进程和线程的关系示意图-3.Go协程和Go主线程 3.案例说明4.小结5.MPG模式基本介绍6.设置Golang运行的CPU数7.协程并发&#xff08;并行&#xff09;资源竞争的问题8.全局互斥锁解决资…

MySQL 8 update语句更新数据表里边的数据

数据重新补充 这里使用alter table Bookbought.bookuser add userage INT after userphone;为用户表bookuser在userphone列后边添加一个类型为INT的新列userage。 使用alter table Bookbought.bookuser add sex varchar(6) after userage ;为用户表bookuser在userage 列后边添…

Oracle-数据库连接数异常上涨问题分析

问题&#xff1a; 用户的数据库在某个时间段出现连接数异常上涨问题&#xff0c;时间持续5分钟左右&#xff0c;并且问题期间应用无法正常连接请求数据库 从连接数的监控上可以看到数据库平常峰值不到100个连接&#xff0c;在问题时间段突然上涨到400以上 问题分析&#xff1a;…

unity | 动画模块之循环滚动选项框

一、作者的话 评论区有人问&#xff0c;有没有竖排循环轮播选项框&#xff0c;我就写了一个 二、效果动画 如果不是你们想要的&#xff0c;就省的你们继续往下看了 三、制作思路 把移动分成里面的方块&#xff0c;还有背景&#xff08;父物体&#xff09;&#xff0c;方块自…

网络模拟与网络仿真

目录 一、概念界定 二、模拟&#xff08;simulation&#xff09;与仿真&#xff08;emulation&#xff09; 2.1 模拟&#xff08;simulation&#xff09; 2.2 仿真&#xff08;emulation&#xff09; 2.3 区分 三、网络模拟与网络仿真 3.1 网络模拟 3.2 网络仿真 3.…

【算法】算法题-20231206

这里写目录标题 一、非自身以外数字的乘积二、最大数三、奇数排序 一、非自身以外数字的乘积 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀…

VA05销售报表屏幕增强

业务需求&#xff1a;在VA05报表界面增强两个字段&#xff08;BELNR1/BELNR2&#xff09;. 第一步&#xff1a;扩展VA05相关表结构 由于新增的字段是按照销售订单行维度展示的&#xff0c;所以本篇加在VBAP表里&#xff08;不扩展表字段&#xff0c;直接写增强&#xff0c;会…