In this tutorial we will build a ci pipeline using concourse to build and push a image to dockerhub automatically, whenever a new git commit is made to the master branch.
Our Project Setup
Our Directory Tree:
$ find . ./Dockerfile ./ci ./ci/pipeline.yml ./README.md ./docker-tunnel
The project used in this example is not important, but you can check it out at https://github.com/ruanbekker/docker-remote-tunnel
Our Pipeline
A visual to see how the pipeline will look like in concourse:
Our pipeline definition will consist of 3 resources, github repo
, dockerhub image
and a slack resource
to inform use whether a build has completed.
Then we are specifying that the job should be triggered on a git commit for the master branch, build and push to our dockerhub repo.
Our pipeline definition ci/pipeline.yml
:
resources: - name: git-repo type: git source: uri: git@github.com:ruanbekker/docker-remote-tunnel.git branch: master private_key: ((github_private_key)) - name: docker-remote-tunnel-image type: docker-image source: repository: ruanbekker/docker-remote-tunnel tag: test username: ((dockerhub_user)) password: ((dockerhub_password)) - name: slack-alert type: slack-notification source: url: ((slack_notification_url)) resource_types: - name: slack-notification type: docker-image source: repository: cfcommunity/slack-notification-resource tag: v1.3.0 jobs: - name: build-cached-image plan: - get: git-repo trigger: true - task: build-cached-image-workspace config: platform: linux image_resource: type: docker-image source: repository: rbekker87/build-tools outputs: - name: workspace inputs: - name: git-repo run: path: /bin/sh args: - -c - | output_dir=workspace cat << EOF > "${output_dir}/Dockerfile" FROM alpine ADD git-repo /tmp/git-repo RUN mv /tmp/git-repo/docker-tunnel /usr/bin/docker-tunnel RUN apk --no-cache add screen docker openssl openssh-client apache2-utils RUN /usr/bin/docker-tunnel -h RUN rm -rf /tmp/git-repo EOF cp -R ./git-repo "${output_dir}/git-repo" - put: docker-remote-tunnel-image params: build: workspace on_failure: put: slack-alert params: channel: '#system_events' username: 'concourse' icon_emoji: ':concourse:' silent: true text: | *$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME* ($BUILD_NAME) FAILED to build image https://ci.domain.com/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME on_success: put: slack-alert params: channel: '#system_events' username: 'concourse' icon_emoji: ':concourse:' silent: true text: | *$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME* ($BUILD_NAME) SUCCESS - Image has been published https://ci.domain.com/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME - name: test plan: - get: docker-remote-tunnel-image passed: [build-cached-image] trigger: true - get: git-repo passed: [build-cached-image] - task: run-tests image: docker-remote-tunnel-image config: platform: linux inputs: - name: git-repo run: dir: git-repo path: sh args: - /usr/bin/docker-tunnel - --help on_failure: put: slack-alert params: channel: '#system_events' username: 'concourse' icon_emoji: ':concourse:' silent: true text: | *$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME* ($BUILD_NAME) FAILED - Testing image failure https://ci.domain.com/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME on_success: put: slack-alert params: channel: '#system_events' username: 'concourse' icon_emoji: ':concourse:' silent: true text: | *$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME* ($BUILD_NAME) SUCCESS - Testing image Succeeded https://ci.domain.com/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME
Note that our secret information is templatized and saved in our local credentials.yml
which should never be stored in version control:
slack_notification_url: https://api.slack.com/aaa/bbb/ccc dockerhub_user: myuser dockerhub_password: mypasswd github_private_key: |- -----BEGIN RSA PRIVATE KEY----- some-secret-data -----END RSA PRIVATE KEY------
Set the Pipeline:
Now that we have our pipeline definition, credentials and application code (stored in version control), go ahead and set the pipeline, which will save the pipeline configuration in concourse:
# pipeline name: my-docker-app-pipeline $ fly -t scw sp -n main -c pipeline.yml -p my-docker-app-pipeline -l credentials.yml
Now the pipeline is saved on concourse but in a paused state, go ahead and unpause the pipeline:
$ fly -t scw up -p my-docker-app-pipeline
Test your Pipeline
Make a commit to master and head over to concourse and look at it go:
Thanks for reading, make sure to check out my other posts on #concourse
Top comments (2)
Nice post! I recently had to do this and having more resources like this available would have been helpful. 🙌
Have you played around with this new OCI registry image resource type/build task at all?
I haven't, but I think the goal of eliminating the Docker daemon (and eventually privileged containers as well) requirement is a noble one.
Running Docker within Concourse's Garden containers can be rough sometimes. 😔
Hey Tim,
I have not played around with them, but thanks for mentioning them, those look really useful. Think I will start to use them right away.
Totally agree about moving away from the docker daemon is a great one!
Thanks so much for the comment.