Build and push an image with Tekton

Create a Pipeline to fetch the source code, build, and push an image with Kaniko and Tekton.

This page includes content about running Tekton with specific platforms and cloud providers. The accuracy and freshness of this vendor documentation varies by vendor.

If you want to contribute with platform-specific documentation, follow the vendor contributions guidelines.

This guide shows you how to:

  1. Create a Task to clone source code from a git repository.
  2. Create a second Task to use the cloned code to build a Docker image and push it to a registry.

If you are already familiar with Tekton and just want to see the example, you can skip to the full code samples.

Prerequisites

  1. To follow this How-to you must have a Kubernetes cluster up and running and kubectl properly configured to issue commands to your cluster.

  2. Install Tekton Pipelines:

    kubectl apply --filename \
    https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
    

    See the Pipelines installation documentation for other installation options and vendor specific instructions.

  3. Install the Tekton CLI, tkn, on your machine.

If this is your first time using Tekton Pipelines, we recommend that you complete the Getting Started tutorials before proceeding with this guide.

Clone the repository

Create a new Pipeline, pipeline.yaml, that uses the git clone Task to clone the source code from a git repository:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-build-push
spec:
  description: | 
    This pipeline clones a git repo, builds a Docker image with Kaniko and
    pushes it to a registry
  params:
  - name: repo-url
    type: string
  workspaces:
  - name: shared-data
  tasks:
  - name: fetch-source
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: shared-data
    params:
    - name: url
      value: $(params.repo-url)

Then create the corresponding pipelinerun.yaml file:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: clone-build-push-run-
spec:
  pipelineRef:
    name: clone-build-push
  podTemplate:
    securityContext:
      fsGroup: 65532
  workspaces:
  - name: shared-data
    volumeClaimTemplate:
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
  params:
  - name: repo-url
    value: https://github.com/google/docsy-example.git

For this how-to we are using a public repository as an example. You can also use git clone with private repositories, using SSH authentication.

Build the container image with Kaniko

To build the image use the Kaniko Task from the community hub.

  1. Add the image reference to the params section in pipeline.yaml:

    params: 
    - name: image-reference
      type: string
    

    This parameter is used to add the tag corresponding the container registry where you are going to push the image.

  2. Create the new build-push Task in the same pipeline.yaml file:

    tasks:
    ...
      - name: build-push
        runAfter: ["fetch-source"]
        taskRef:
          name: kaniko
        workspaces:
        - name: source
          workspace: shared-data
        params:
        - name: IMAGE
          value: $(params.image-reference)
    

    This new Task refers to kaniko, which is going to be installed from the community hub. A Task has its own set of workspaces and params passed down from the parameters and Workspaces defined at Pipeline level. In this case, the Workspace source and the value of IMAGE. Check the kaniko Task documentation to see all the available options.

  3. Instantiate the build-push Task. Add the value of image-reference to the params section in pipelinerun.yaml:

    params:
    - name: image-reference
      value: container.registry.com/sublocation/my_app:version
    

    Replace container.registry.com/sublocation/my_app:version with the actual tag for your registry. You can set up a local registry for testing purposes.

Check the full code samples to see how all the pieces fit together.

Container registry authentication

In most cases, to push the image to a container registry you must provide authentication credentials first.

  1. Set up authentication with the Docker credential helper and generate the Docker configuration file, $HOME/.docker/config.json, for your registry. This step is different depending on your registry.

    Check your cloud provider documentation to complete this step.

  2. Create a Kubernetes Secret, docker-credentials.yaml with your credentials:

    apiVersion: v1
    kind: Secret
    metadata:
      name: docker-credentials
    data:
      config.json: efuJAmF1...
    

    The value of config.json is the base64-encoded ~/.docker/config.json file. You can get this data with the following command:

    cat ~/.docker/config.json | base64 -w0
    
  3. Update pipeline.yaml and add a Workspace to mount the credentials directory:

    At the Pipeline level:

    workspaces:
    - name: docker-credentials
    

    And under the build-push Task:

    workspaces:
    - name: dockerconfig
      workspace: docker-credentials
    
  4. Instantiate the new docker-credentials Workspace in your pipelinerun.yaml file by adding a new entry under workspaces:

    - name: docker-credentials
      secret:
        secretName: docker-credentials
    

GKE Workload Identity

If you are running your Pipelines on Google Kubernetes Engine (GKE), create a cluster with Workload Identity enabled or enable Workload Identity on an existing cluster. This allows you to to run your pipeline and push images to Artifact Registry without authentication credentials. If you are using Workload Identity, skip step 2 when you run your pipeline.

Set up an Artifact Registry repository:

  1. Enable the Artifact Registry API:

    gcloud services enable artifactregistry.googleapis.com
    
  2. Create a Docker repository to push the image to:

    gcloud artifacts repositories create <repository-name> \
      --repository-format=docker \
      --location=us-central1 --description="Docker repository"
    

    Replace:

    • <repository-name> with the name of your repository.
    • <location> with the name of your preferred location. For example, us-central1.

Configure the GKE cluster to allow the Pipeline to push images to Artifact Registry:

  1. Create a Kubernetes Service Account:

    kubectl create serviceaccount <sa-name>
    

    Where <sa-name> is the name of the service account. For example, tekton-sa.

  2. Create a Google Service Account with the same name:

    gcloud iam service-accounts create <sa-name>
    
  3. Grant the Google Service Account permissions to push to the Artifact Registry container repository:

    gcloud artifacts repositories add-iam-policy-binding <ar-repository> \
      --location <location> \
      --member=serviceAccount:build-robot@<project_id>.iam.gserviceaccount.com \
      --role=roles/artifactregistry.reader \
      --role=roles/artifactregistry.writer
    

    Where

  4. Set up the Workload Identity mappings on the Kubernetes cluster:

    kubectl annotate serviceaccount \
    <sa-name> \
    iam.gke.io/gcp-service-account=build-robot@<project_id>.iam.gserviceaccount.com
    
  5. Set up Workload Identity mappings for the Google Service Account:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:<project_id>.svc.id.goog[default/<sa-name>]" \
      build-robot@<project_id>.iam.gserviceaccount.com
    

This creates two service accounts, an IAM service account and a Kubernetes service account, and “links” them. Workload Identity allows workloads in your GKE cluster to impersonate IAM service accounts to access Google Cloud services.

Use Docker authentication

If you prefer to use Docker authentication to push your image to Artifact Registry, there are two options:

In both cases your credentials are saved to a Docker configuration file in your user home directory: $HOME/.docker/config.json. Use this file to follow the “General Authentication” instructions.

See the complete files in the full code samples section.

Run your Pipeline

You are ready to install the Tasks and run the pipeline.

  1. Install the git-clone and kaniko Tasks:

    tkn hub install task git-clone
    tkn hub install task kaniko
    
  2. Apply the Secret with your Docker credentials.

    kubectl apply -f docker-credentials.yaml
    
  3. Apply the Pipeline:

    kubectl apply -f pipeline.yaml
    
  4. Create the PipelineRun:

    kubectl create -f pipelinerun.yaml
    

    This creates a PipelineRun with a unique name each time:

    pipelinerun.tekton.dev/clone-build-push-run-4kgjr created
    
  5. Use the PipelineRun name from the output of the previous step to monitor the Pipeline execution:

    tkn pipelinerun logs  clone-build-push-run-4kgjr -f
    

    After a few seconds, the output confirms that the image was built and pushed successfully:

    [fetch-source : clone] + '[' false '=' true ]
    [fetch-source : clone] + '[' false '=' true ]
    [fetch-source : clone] + '[' false '=' true ]
    [fetch-source : clone] + CHECKOUT_DIR=/workspace/output/
    [fetch-source : clone] + '[' true '=' true ]
    [fetch-source : clone] + cleandir
    [fetch-source : clone] + '[' -d /workspace/output/ ]
    [fetch-source : clone] + rm -rf '/workspace/output//*'
    [fetch-source : clone] + rm -rf '/workspace/output//.[!.]*'
    [fetch-source : clone] + rm -rf '/workspace/output//..?*'
    [fetch-source : clone] + test -z 
    [fetch-source : clone] + test -z 
    [fetch-source : clone] + test -z 
    [fetch-source : clone] + /ko-app/git-init '-url=https://github.com/google/docsy-example.git' '-revision=' '-refspec=' '-path=/workspace/output/' '-sslVerify=true' '-submodules=true' '-depth=1' '-sparseCheckoutDirectories='
    [fetch-source : clone] {"level":"info","ts":1654637310.4419358,"caller":"git/git.go:170","msg":"Successfully cloned https://github.com/google/docsy-example.git @ 1c7f7e300c90cd690ca5be66b43fe58713bb21c9 (grafted, HEAD) in path /workspace/output/"}
    [fetch-source : clone] {"level":"info","ts":1654637320.384655,"caller":"git/git.go:208","msg":"Successfully initialized and updated submodules in path /workspace/output/"}
    [fetch-source : clone] + cd /workspace/output/
    [fetch-source : clone] + git rev-parse HEAD
    [fetch-source : clone] + RESULT_SHA=1c7f7e300c90cd690ca5be66b43fe58713bb21c9
    [fetch-source : clone] + EXIT_CODE=0
    [fetch-source : clone] + '[' 0 '!=' 0 ]
    [fetch-source : clone] + printf '%s' 1c7f7e300c90cd690ca5be66b43fe58713bb21c9
    [fetch-source : clone] + printf '%s' https://github.com/google/docsy-example.git
    
    [build-push : build-and-push] WARN
    [build-push : build-and-push] User provided docker configuration exists at /kaniko/.docker/config.json 
    [build-push : build-and-push] INFO Retrieving image manifest klakegg/hugo:ext-alpine 
    [build-push : build-and-push] INFO Retrieving image klakegg/hugo:ext-alpine from registry index.docker.io 
    [build-push : build-and-push] INFO Built cross stage deps: map[]                
    [build-push : build-and-push] INFO Retrieving image manifest klakegg/hugo:ext-alpine 
    [build-push : build-and-push] INFO Returning cached image manifest              
    [build-push : build-and-push] INFO Executing 0 build triggers                   
    [build-push : build-and-push] INFO Unpacking rootfs as cmd RUN apk add git requires it. 
    [build-push : build-and-push] INFO RUN apk add git                              
    [build-push : build-and-push] INFO Taking snapshot of full filesystem...        
    [build-push : build-and-push] INFO cmd: /bin/sh                                 
    [build-push : build-and-push] INFO args: [-c apk add git]                       
    [build-push : build-and-push] INFO Running: [/bin/sh -c apk add git]            
    [build-push : build-and-push] fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
    [build-push : build-and-push] fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
    [build-push : build-and-push] OK: 76 MiB in 41 packages
    [build-push : build-and-push] INFO[0012] Taking snapshot of full filesystem...        
    [build-push : build-and-push] INFO[0013] Pushing image to us-east1-docker.pkg.dev/tekton-tests/tektonstuff/docsy:v1 
    [build-push : build-and-push] INFO[0029] Pushed image to 1 destinations               
    
    [build-push : write-url] us-east1-docker.pkg.dev/my-tekton-tests/tekton-samples/docsy:v1
    

Full code samples

The Pipeline:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-build-push
spec:
  description: |
    This pipeline clones a git repo, builds a Docker image with Kaniko and
    pushes it to a registry    
  params:
  - name: repo-url
    type: string
  - name: image-reference
    type: string
  workspaces:
  - name: shared-data
  - name: docker-credentials
  tasks:
  - name: fetch-source
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: shared-data
    params:
    - name: url
      value: $(params.repo-url)
  - name: build-push
    runAfter: ["fetch-source"]
    taskRef:
      name: kaniko
    workspaces:
    - name: source
      workspace: shared-data
    - name: dockerconfig
      workspace: docker-credentials
    params:
    - name: IMAGE
      value: $(params.image-reference)

The PipelineRun:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: clone-build-push-run-
spec:
  pipelineRef:
    name: clone-build-push
  podTemplate:
    securityContext:
      fsGroup: 65532
  workspaces:
  - name: shared-data
    volumeClaimTemplate:
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
  - name: docker-credentials
    secret:
      secretName: docker-credentials
  params:
  - name: repo-url
    value: https://github.com/google/docsy-example.git
  - name: image-reference
    value: container.registry.com/sublocation/my_app:version 

The Docker credentials Kubernetes Secret:

apiVersion: v1
kind: Secret
metadata:
  name: docker-credentials
data:
  config.json: efuJAmF1...

Use your credentials as the value of the data field. Check the registry authentication section for more information.

Further reading


Last modified January 29, 2023: Rework left-hand nav order (dbe0a93)