1.14.3.1 Building and Pushing an Image

In this guide we are going to show how to build and publish container images using the oci-build task and registry-image resource. This guide assumes you understand how to build container images with Dockerfile's and publish to Docker Hub or another image registry using the docker cli.

This is one way of building and pushing images. There are many other ways to accomplish this same task in Concourse.

First we need a Dockerfile. You can store this in your own repo or reference the github.com/concourse/examples repo. The rest of this post assumes you use the examples repo. All files in this blog post can be found in the examples repo.

The Dockerfile

Dockerfile
FROM busybox

RUN echo "I'm simple!"
COPY ./stranger /stranger
RUN cat /stranger

Defining Pipeline Resources

Now we can start building out our pipeline. Let's declare our Resources first. We will need one resource to pull in the repo where our Dockerfile is located, and a second resource pointing to where we want to push the built container image to.

There are some Variables in this file that we will fill out when setting the pipeline.

build-push.yml
resources:
# The repo with our Dockerfile
- name: concourse-examples
  type: git
  icon: github
  source:
    uri: https://github.com/concourse/examples.git
    branch: master

# Where we will push the image to
- name: simple-image
  type: registry-image
  icon: docker
  source:
    repository: ((image-repo-name))/simple-image
    username: ((registry-username))
    password: ((registry-password))

Create the Job

Next we will create a job that will build and push our container image.

To build the job we will need to pull in the repo where the Dockerfile is.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples

Build the Image

The second step in our job will build the container image.

To build the container image we are going to use the oci-build-task. The oci-build-task is a container image that is meant to be used in a Concourse task to build other container images. Check out the README in the repo for more details on how to configure and use the oci-build-task in more complex build scenarios.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-image
    privileged: true # oci-build-task must run in a privileged container
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task

Next we will add concourse-examples as an input to the build task to ensure the artifact from the get step (where our Dockerfile is fetched) is mounted in our build-image step.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-image
    privileged: true # oci-build-task must run in a privileged container
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task
      inputs:
      - name: concourse-examples

The oci-build-task outputs the built container image in a directory called image. Let's add image as an output of our task so we can publish it in a later step.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-image
    privileged: true # oci-build-task must run in a privileged container
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task
      inputs:
      - name: concourse-examples
      outputs:
      - name: image

Defining the Build Context

Next we need to tell the oci-build-task what the build context of our Dockerfile is. The README goes over a few other methods of creating your build context. We are going to use the simplest use-case. By specifying CONTEXT the oci-build-task assumes a Dockerfile and its build context are in the same directory.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-image
    privileged: true # oci-build-task must run in a privileged container
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task
      inputs:
      - name: concourse-examples
      outputs:
      - name: image
      params:
        CONTEXT: concourse-examples/Dockerfiles/simple
      run: # binary used to build the image
        path: build

Publish the Container Image

To push the container image add a put step to our job plan and tell the registry-image resource where the tarball of the container image is.

The put step will push the container image using the information defined previously in the resource's source.

build-push.yml
resources: ... # omitting resource section from above

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-image
    privileged: true # oci-build-task must run in a privileged container
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task
      inputs:
      - name: concourse-examples
      outputs:
      - name: image
      params:
        CONTEXT: concourse-examples/Dockerfiles/simple
      run: # binary used to build the image
        path: build
  - put: simple-image
    params:
      image: image/image.tar

The Entire Pipeline

Putting all the pieces together, here is our pipeline that builds and pushes a container image.

build-push.yml
resources:
# The repo with our Dockerfile
- name: concourse-examples
  type: git
  icon: github
  source:
    uri: https://github.com/concourse/examples.git
    branch: master

# Where we will push the image
- name: simple-image
  type: registry-image
  icon: docker
  source:
    repository: ((image-repo-name))/simple-image
    username: ((registry-username))
    password: ((registry-password))

jobs:
- name: build-and-push
  plan:
  - get: concourse-examples
  - task: build-task-image
    privileged: true
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: concourse/oci-build-task
      inputs:
      - name: concourse-examples
      outputs:
      - name: image
      params:
        CONTEXT: concourse-examples/Dockerfiles/simple
      run:
        path: build
  - put: simple-image
    params:
      image: image/image.tar

You can set the pipeline with the following fly command, updating the variable values with real values the pipeline can use to run.

fly -t <target> set-pipeline -p build-and-push-image \
  -c ./examples/pipelines/build-and-push-simple-image.yml \
  --var image-repo-name=<repo-name> \
  --var registry-username=<user> \
  --var registry-password=<password>

Further Readings

Understanding what the build context is is important when building container images. You can read Dockerfile Best Practices for more details about build contexts.

The inputs section of the oci-build-task's README has examples on how to create a build context with multiple inputs and other complex build scenarios.

Read the README's in the oci-build-task and registry-image resource to learn more about their other configuration options.