快速开始

目录

Tekton Results 的安装

本文档说明如何使用已有数据库安装 Tekton Results。

NOTE

当前不支持内置的 PostgreSQL 数据库,请使用外部数据库。

关于外部数据库请参考 Using PostgreSQL from Data Services

前提条件

  1. 集群中必须已安装 Tekton Pipelines。

    TIP

    以下说明假设您已默认将 Results 安装在 tekton-pipelines 命名空间中。

    如果您安装在其他命名空间,请将 tekton-pipelines 替换为您的命名空间。

  2. 创建数据库 root 密码。

    用户必须生成数据库 root 密码并将其存储在 Kubernetes Secret 中,安装时 Tekton Results 默认期望该 Secret 具有以下属性:

    • 命名空间:tekton-pipelines
    • 名称:tekton-results-postgres
    • 包含以下字段:
      • POSTGRES_USER=<your_username>
      • POSTGRES_PASSWORD=<your_password>

    您可以使用以下命令快速生成一个 Secret

    kubectl create secret generic tekton-results-postgres --namespace="tekton-pipelines" --from-literal=POSTGRES_USER={POSTGRES} --from-literal=POSTGRES_PASSWORD={PASSWORD}
  3. 生成证书/密钥对。注意:可以使用任何证书管理软件完成此操作!

    Tekton Results 期望将证书/密钥对存储在名为 tekton-results-tlsTLS Kubernetes Secret 中。

    • 生成新的自签名证书
    openssl req -x509 \
      -newkey rsa:4096 \
      -keyout key.pem \
      -out cert.pem \
      -days 365 \
      -nodes \
      -subj "/CN=tekton-results-api-service.tekton-pipelines.svc.cluster.local" \
      -addext "subjectAltName = DNS:tekton-results-api-service.tekton-pipelines.svc.cluster.local"
    TIP

    如果您的 openssl 版本过低且不支持某些参数,请从 openssl binaries 升级您的 openssl 版本。

    • 使用证书创建新的 TLS Secret。
    kubectl create secret tls -n tekton-pipelines tekton-results-tls \
      --cert=cert.pem \
      --key=key.pem
  4. 如果使用 PVC 存储日志,请创建一个 PVC

    cat <<EOF | kubectl create -f -
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: tekton-logs
      namespace: tekton-pipelines
    spec:
      accessModes:
        - ReadWriteOnce  # 访问模式,表示单个节点可读写
      resources:
        requests:
          storage: 100Gi  # 请求的存储大小
    EOF
    • 请根据需要调整 PVC 大小。

使用 operator CRD 安装

$ cat <<EOF | kubectl create -f -
apiVersion: operator.tekton.dev/v1alpha1
kind: TektonResult
metadata:
  name: result
spec:
  # 更新为数据库地址
  db_host: <database_host>
  db_port: 5432
  db_name: <database_name>
  # 使用外部数据库
  is_external_db: true
  # 之前创建的包含数据库账户信息的 secret
  secret_name: tekton-results-postgres
  # 基础设置
  server_port: 8080
  # 目标命名空间
  targetNamespace: tekton-pipelines
  # 禁用认证
  auth_disable: true
  db_enable_auto_migration: true
  log_level: debug
  # 使用日志存储服务
  logs_api: true
  logs_type: File
  logs_path: /logs
  logs_buffer_size: 92160
  logging_pvc_name: tekton-logs
EOF
YAML 字段说明
  • db_host:数据库主机地址。
  • db_port:数据库端口。
  • db_name:数据库名称。
  • is_external_db:是否使用外部数据库。
    • true:使用外部数据库。
  • secret_name:包含数据库账户信息的 secret 名称。
    • 即前一步创建的 secret。
  • server_port:服务器端口。
  • targetNamespace:Results 部署所在的命名空间。
  • auth_disable:是否禁用认证。
  • db_enable_auto_migration:是否启用自动数据库迁移。
  • log_level:日志级别。
  • logs_api:是否启用日志 API。
    • true:启用日志 API。
  • logs_type:日志类型。
    • File:将日志存储在文件中。
  • logs_path:日志路径。
  • logs_buffer_size:日志缓冲区大小。
  • logging_pvc_name:用于存储日志的 PVC 名称。
    • 即前一步创建的 PVC。
TIP

更多数据库配置请参考 PostgreSQL Configuration

部署完成后,您可以看到 results-api / results-retention-policy-agent / results-watcher 的状态为 Running

$ kubectl get pods -n tekton-pipelines

NAME                                                    READY   STATUS    RESTARTS   AGE
tekton-results-api-68bb657996-bb487                     1/1     Running   0          14s
tekton-results-retention-policy-agent-c6ff5575b-prczg   1/1     Running   0          14s
tekton-results-watcher-6d7c68f576-p2gbl                 1/1     Running   0          14s

功能验证

可以通过一个简单的 taskrun 来验证功能。

1. 创建一个 TaskRun 资源

$ cat <<'EOF' | kubectl create -f -
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: hello
spec:
  taskSpec:
    steps:
      - name: hello
        image: alpine
        command: ["echo", "hello"]
EOF
NOTE

对于隔离环境,请将 alpine 镜像地址更换为网络中可用的其他镜像。

2. 等待任务执行完成

$ kubectl get taskruns.tekton.dev -n default -w

hello                                        Unknown     Pending     3s
hello                                        True        Succeeded   11s         0s

3. 使用 curl 查询结果

更多使用方式请参考官方文档 Tekton Results API

准备权限
$ cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tekton-results-readonly
rules:
  - apiGroups: ["results.tekton.dev"]
    resources: ["results", "records"]
    verbs: ["get", "list"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: tekton-results-impersonate
rules:
  - apiGroups: [""]
    resources: ["users", "groups", "serviceaccounts"]
    verbs: ["impersonate"]
  - apiGroups: ["authentication.k8s.io"]
    resources: ["uids"]
    verbs: ["impersonate"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-results-impersonate
subjects:
  - kind: ServiceAccount
    name: impersonate-admin
    namespace: tekton-pipelines
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: tekton-results-impersonate

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-results-user
  namespace: default
subjects:
  - kind: ServiceAccount
    name: impersonate-user
    namespace: user-namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: tekton-results-readonly
EOF
验证权限
$ kubectl create --validate=false --as=system:serviceaccount:tekton-pipelines:tekton-results-watcher -n tekton-pipelines -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: results.tekton.dev
    resource: results
    verb: get
EOF

apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
metadata:
  creationTimestamp: null
spec:
  resourceAttributes:
    group: results.tekton.dev
    resource: results
    verb: get
status:
  allowed: true
  reason: 'RBAC: allowed by ClusterRoleBinding "tekton-results-watcher" of ClusterRole
    "tekton-results-watcher" to ServiceAccount "tekton-results-watcher/tekton-pipelines"'
端口转发

将服务代理到本地端口 8080

$ kubectl port-forward -n tekton-pipelines service/tekton-results-api-service 8080
获取令牌
$ token=$(kubectl create token impersonate-admin -n tekton-pipelines)
查询结果
$ curl -s -k \
  -H 'authorization: Bearer '${token} \
  -H 'Impersonate-User: system:serviceaccount:user-namespace:impersonate-user' \
  https://localhost:8080/apis/results.tekton.dev/v1alpha2/parents/default/results

{"results":[{"name":"default/results/0b165174-fd12-47f9-a8d8-c4ffcb1be0d4", "id":"65662a09-216a-4e6b-960c-260a2b05de9c", "uid":"65662a09-216a-4e6b-960c-260a2b05de9c", "createdTime":"2025-02-20T05:59:52.403661Z", "createTime":"2025-02-20T05:59:52.403661Z", "updatedTime":"2025-02-20T06:00:09.033206Z", "updateTime":"2025-02-20T06:00:09.033206Z", "annotations":{"object.metadata.name":"large-result-pipeline-run-c2mg4", "tekton.dev/pipeline":"large-result-pipeline"}, "etag":"65662a09-216a-4e6b-960c-260a2b05de9c-1740031209033206096", "summary":{"record":"default/results/0b165174-fd12-47f9-a8d8-c4ffcb1be0d4/records/0b165174-fd12-47f9-a8d8-c4ffcb1be0d4", "type":"tekton.dev/v1.PipelineRun", "startTime":null, "endTime":"2025-02-20T06:00:09Z", "status":"SUCCESS", "annotations":{}}}, {"name":"default/results/c308db48-9613-4652-8b5a-edd10eb9bda9", "id":"98d2bda3-2039-4e03-b78a-4e44637df4f2", "uid":"98d2bda3-2039-4e03-b78a-4e44637df4f2", "createdTime":"2025-02-20T05:25:44.226246Z", "createTime":"2025-02-20T05:25:44.226246Z", "updatedTime":"2025-02-20T13:12:20.823170Z", "updateTime":"2025-02-20T13:12:20.823170Z", "annotations":{"object.metadata.name":"hello"}, "etag":"98d2bda3-2039-4e03-b78a-4e44637df4f2-1740057140823170246", "summary":{"record":"default/results/c308db48-9613-4652-8b5a-edd10eb9bda9/records/c308db48-9613-4652-8b5a-edd10eb9bda9", "type":"tekton.dev/v1.TaskRun", "startTime":null, "endTime":"2025-02-20T13:12:20Z", "status":"SUCCESS", "annotations":{}}}, {"name":"default/results/e536090f-0819-46ae-86e6-1fe2fce6dc56", "id":"a7e8b252-bbce-4cb1-aa22-71cfb5a83639", "uid":"a7e8b252-bbce-4cb1-aa22-71cfb5a83639", "createdTime":"2025-02-20T13:13:33.323381Z", "createTime":"2025-02-20T13:13:33.323381Z", "updatedTime":"2025-02-20T13:13:45.480690Z", "updateTime":"2025-02-20T13:13:45.480690Z", "annotations":{"object.metadata.name":"large-result-pipeline-run-twnxc", "tekton.dev/pipeline":"large-result-pipeline"}, "etag":"a7e8b252-bbce-4cb1-aa22-71cfb5a83639-1740057225480690745", "summary":{"record":"default/results/e536090f-0819-46ae-86e6-1fe2fce6dc56/records/e536090f-0819-46ae-86e6-1fe2fce6dc56", "type":"tekton.dev/v1.PipelineRun", "startTime":null, "endTime":"2025-02-20T13:13:45Z", "status":"SUCCESS", "annotations":{}}}], "nextPageToken":""}

4. 使用 tkn-results CLI 查询结果

准备 CLI

tkn 是与 Tekton 交互的命令行工具,您可以从 官方页面 下载。

您需要使用 golang 构建 tkn-results

# 1. 克隆仓库
git clone https://github.com/tektoncd/results.git

# 2. 进入项目目录
cd results

# 3. 构建二进制文件
go build -o tkn-results ./cmd/tkn-results/main.go

# 4. 将二进制文件复制到 PATH 中的目录
查询执行记录列表
$ tkn results --insecure records list default/results/-

default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/records/209dcb81-e3c0-47cd-b082-d910e15702ae  tekton.dev/v1.TaskRun                   2025-02-19 16:17:11 +0800 CST           2025-02-19 16:17:21 +0800 CST
default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/records/a750035d-0479-34a1-9db2-9a256b53243f  results.tekton.dev/v1alpha3.Log         2025-02-19 16:17:21 +0800 CST           2025-02-19 16:17:21 +0800 CST
查询单条执行记录
$ tkn results --insecure records get -o textproto default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/records/209dcb81-e3c0-47cd-b082-d910e15702ae

name: "default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/records/209dcb81-e3c0-47cd-b082-d910e15702ae"
id: "0805bbd0-e390-40dc-a1ea-f65ae9401930"
uid: "0805bbd0-e390-40dc-a1ea-f65ae9401930"
data: {
  type: "tekton.dev/v1.TaskRun"
  value: "{\"kind\": \"TaskRun\", \"spec\": {\"timeout\": \"1h0m0s\", \"taskSpec\": {\"steps\": [{\"name\": \"hello\", \"image\": \"152-231-registry.alauda.cn:60070/ops/ubuntu:latest\", \"command\": [\"echo\", \"hello\"], \"computeResources\": {}}]}, \"serviceAccountName\": \"default\"}, \"status\": {\"steps\": [{\"name\": \"hello\", \"imageID\": \"alpine\", \"container\": \"step-hello\", \"terminated\": {\"reason\": \"Completed\", \"exitCode\": 0, \"startedAt\": \"2025-02-19T08:17:20Z\", \"finishedAt\": \"2025-02-19T08:17:20Z\", \"containerID\": \"containerd://5e2cb72ca758fc976744b06b5ee97564bcc0958ca9cee2a54fdaab64323a1bf9\"}, \"terminationReason\": \"Completed\"}], \"podName\": \"hello-pod\", \"taskSpec\": {\"steps\": [{\"name\": \"hello\", \"image\": \"152-231-registry.alauda.cn:60070/ops/ubuntu:latest\", \"command\": [\"echo\", \"hello\"], \"computeResources\": {}}]}, \"startTime\": \"2025-02-19T08:17:10Z\", \"conditions\": [{\"type\": \"Succeeded\", \"reason\": \"Succeeded\", \"status\": \"True\", \"message\": \"All Steps have completed executing\", \"lastTransitionTime\": \"2025-02-19T08:17:21Z\"}], \"provenance\": {\"featureFlags\": {\"Coschedule\": \"workspaces\", \"MaxResultSize\": 4096, \"EnableAPIFields\": \"beta\", \"EnableArtifacts\": false, \"EnableParamEnum\": false, \"DisableCredsInit\": false, \"EnableStepActions\": true, \"SetSecurityContext\": false, \"AwaitSidecarReadiness\": true, \"EnableKeepPodOnCancel\": false, \"EnableTektonOCIBundles\": false, \"ResultExtractionMethod\": \"termination-message\", \"SendCloudEventsForRuns\": false, \"DisableAffinityAssistant\": false, \"EnableProvenanceInStatus\": true, \"EnforceNonfalsifiability\": \"none\", \"EnableCELInWhenExpression\": false, \"VerificationNoMatchPolicy\": \"ignore\", \"ScopeWhenExpressionsToTask\": false, \"RequireGitSSHSecretKnownHosts\": false, \"RunningInEnvWithInjectedSidecars\": true}}, \"completionTime\": \"2025-02-19T08:17:21Z\"}, \"metadata\": {\"uid\": \"209dcb81-e3c0-47cd-b082-d910e15702ae\", \"name\": \"hello\", \"labels\": {\"app.kubernetes.io/managed-by\": \"tekton-pipelines\"}, \"namespace\": \"default\", \"finalizers\": [\"results.tekton.dev/taskrun\", \"chains.tekton.dev\"], \"generation\": 1, \"annotations\": {\"results.tekton.dev/log\": \"default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/logs/a750035d-0479-34a1-9db2-9a256b53243f\", \"chains.tekton.dev/signed\": \"true\", \"results.tekton.dev/record\": \"default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/records/209dcb81-e3c0-47cd-b082-d910e15702ae\", \"results.tekton.dev/result\": \"default/results/209dcb81-e3c0-47cd-b082-d910e15702ae\", \"pipeline.tekton.dev/release\": \"8bfd3273cbe7776847884d7fa5004481e6be8f4a\"}, \"managedFields\": [{\"time\": \"2025-02-19T08:17:10Z\", \"manager\": \"kubectl-create\", \"fieldsV1\": {\"f:spec\": {\".\": {}, \"f:taskSpec\": {\".\": {}, \"f:steps\": {}}}}, \"operation\": \"Update\", \"apiVersion\": \"tekton.dev/v1\", \"fieldsType\": \"FieldsV1\"}, {\"time\": \"2025-02-19T08:17:21Z\", \"manager\": \"controller\", \"fieldsV1\": {\"f:metadata\": {\"f:finalizers\": {\"v:\\\"chains.tekton.dev\\\"\": {}}, \"f:annotations\": {\"f:chains.tekton.dev/signed\": {}, \"f:pipeline.tekton.dev/release\": {}}}}, \"operation\": \"Update\", \"apiVersion\": \"tekton.dev/v1\", \"fieldsType\": \"FieldsV1\"}, {\"time\": \"2025-02-19T08:17:21Z\", \"manager\": \"controller\", \"fieldsV1\": {\"f:status\": {\".\": {}, \"f:steps\": {}, \"f:podName\": {}, \"f:taskSpec\": {\".\": {}, \"f:steps\": {}}, \"f:artifacts\": {}, \"f:startTime\": {}, \"f:conditions\": {}, \"f:provenance\": {\".\": {}, \"f:featureFlags\": {\".\": {}, \"f:Coschedule\": {}, \"f:MaxResultSize\": {}, \"f:EnableAPIFields\": {}, \"f:EnableArtifacts\": {}, \"f:EnableParamEnum\": {}, \"f:DisableCredsInit\": {}, \"f:DisableInlineSpec\": {}, \"f:EnableStepActions\": {}, \"f:SetSecurityContext\": {}, \"f:AwaitSidecarReadiness\": {}, \"f:EnableKeepPodOnCancel\": {}, \"f:ResultExtractionMethod\": {}, \"f:SendCloudEventsForRuns\": {}, \"f:EnableKubernetesSidecar\": {}, \"f:DisableAffinityAssistant\": {}, \"f:EnableProvenanceInStatus\": {}, \"f:EnforceNonfalsifiability\": {}, \"f:EnableCELInWhenExpression\": {}, \"f:VerificationNoMatchPolicy\": {}, \"f:EnableConciseResolverSyntax\": {}, \"f:RequireGitSSHSecretKnownHosts\": {}, \"f:RunningInEnvWithInjectedSidecars\": {}}}, \"f:completionTime\": {}}}, \"operation\": \"Update\", \"apiVersion\": \"tekton.dev/v1\", \"fieldsType\": \"FieldsV1\", \"subresource\": \"status\"}, {\"time\": \"2025-02-19T08:17:21Z\", \"manager\": \"watcher\", \"fieldsV1\": {\"f:metadata\": {\"f:finalizers\": {\".\": {}, \"v:\\\"results.tekton.dev/taskrun\\\"\": {}}, \"f:annotations\": {\".\": {}, \"f:results.tekton.dev/log\": {}, \"f:results.tekton.dev/record\": {}, \"f:results.tekton.dev/result\": {}}}}, \"operation\": \"Update\", \"apiVersion\": \"tekton.dev/v1\", \"fieldsType\": \"FieldsV1\"}], \"resourceVersion\": \"1592422\", \"creationTimestamp\": \"2025-02-19T08:17:10Z\"}, \"apiVersion\": \"tekton.dev/v1\"}"
}
etag: "0805bbd0-e390-40dc-a1ea-f65ae9401930-1739953041309658421"
created_time: {
  seconds: 1739953031
  nanos: 16922000
}
create_time: {
  seconds: 1739953031
  nanos: 16922000
}
updated_time: {
  seconds: 1739953041
  nanos: 309658000
}
update_time: {
  seconds: 1739953041
  nanos: 309658000
}
查询日志列表
$ tkn results --insecure logs list default/results/-

default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/logs/a750035d-0479-34a1-9db2-9a256b53243f  results.tekton.dev/v1alpha3.Log         2025-02-19 16:17:21 +0800 CST           2025-02-19 16:17:21 +0800 CST
查询单条日志
$ tkn results --insecure logs get -o textproto default/results/209dcb81-e3c0-47cd-b082-d910e15702ae/logs/a750035d-0479-34a1-9db2-9a256b53243f

content_type: "text/plain"
data: "[prepare] 2025/02/19 08:17:12 Entrypoint initialization\n\n[hello] hello\n\n"

参考资料