Artifacts
Why Use Artifacts?
Artifacts are a ContinuousPipe feature that can be used to solve the problems of:
- Insecure images - where an image contains secret values used during the build process
- Large images - where an image contains build tools that are not needed beyond the build process
These problems are addressed by introducing build steps to create separate images, and then using artifacts to copy files and folders from an initial build image to a secondary build image.
For example, you might create an initial build image containing a GitHub access token to pull the contents of a private Git repository, then create a secondary build image and copy the code across. The secondary image would only contain the code and none of the access credentials.
Similarly you might create an initial build image that uses Grunt to build your frontend code, which would need a combination of Ruby, Ruby gems, npm and npm modules to build the code. However, the secondary image would not require these tools once the code was copied across so would be much smaller.
Using Build Steps
Artifacts are used in conjunction with build steps. The standard way to build an image is to use the following configuration, which is effectively a single build step:
tasks:
images:
build:
services:
web:
image: ${IMAGE_NAME}
It defines a Docker image repository location to push the built image. It also assumes that a Dockerfile
is present in the project root directory to provide instructions on how to build the image.
Using a build step, the above can also be written as follows:
tasks:
images:
build:
services:
web:
steps:
- docker_file_path: ./Dockerfile
image: ${IMAGE_NAME}
As you can see, the simple configuration has been replaced with a steps
section, which explicitly defines where the Dockerfile
is located as well as defining a Docker image repository location as before.
It is then quite straight forward to add an additional build step:
tasks:
images:
build:
services:
web:
steps:
- docker_file_path: ./Buildfile
- docker_file_path: ./Dockerfile
image: ${IMAGE_NAME}
This will build two separate Docker images, however as we haven’t introduced any artifacts yet the first image will effectively be built and then discarded, so the the net result will be the same as the previous examples.
It is also possible to specify a build directory, which works in conjunction with the Docker file path:
tasks:
images:
build:
services:
web:
steps:
- docker_file_path: ./Buildfile
build_directory: ./docker
- docker_file_path: ./Dockerfile
build_directory: ./docker
image: ${IMAGE_NAME}
In this configuration the Docker file paths will be ./docker/Buildfile
and ./docker/Dockerfile
respectively.
Using Artifacts
To enable artifacts, a write_artifacts
and read_artifacts
section needs to be added to separate build steps as follows:
tasks:
images:
build:
services:
web:
steps:
- docker_file_path: ./Buildfile
write_artifacts:
- name: built-files-artifact
path: /app/built-files
- docker_file_path: ./Dockerfile
image: ${IMAGE_NAME}
read_artifacts:
- name: built-files-artifact
path: /built-files
The write_artifacts
section in the first build step creates a write artifact with name “built-files-artifact”. It then specifies a path of “/app/built-files” which represents the location where the Docker file Buildfile
will place the results of the build process that it intends to share.
The read_artifacts
section in the second build step creates a corresponding read artifact by referencing the same name as the write artifact i.e. “built-files-artifact”. It then specifies a path of “/built-files” which represents the location where the Docker file Dockerfile
can expect to find the results of the build process that have been shared.
Docker File for First Build Step
The following is a minimal example Docker file to demonstrate the interaction between the artifact and Docker in the first build step:
Buildfile
FROM nginx RUN mkdir -p /app/built-files && touch /app/built-files/test.txt && echo "test1234" > /app/built-files/test.txt
This simply creates a directory that matches the path specified in the write artifact i.e “/app/built-files”, and then creates a file within the directory to populate the artifact.
Note
In practice the contents of the “/app/built-files” directory would be the result of a build process.
Docker File for Second Build Step
The following is a minimal example Docker file to demonstrate the interaction between the artifact and Docker in the second build step:
Dockerfile
FROM nginx COPY ./built-files /usr/share/nginx/html
This uses the Docker COPY instruction to import files into the image, copying them to the web server directory. It imports any files shared in the artifact by referencing the path specified in the read artifact prefixed by a full stop i.e “./built-files”. In this case there is just one file in the artifact, which will be copied from “./built-files/test.txt” to “/usr/share/nginx/html/test.txt”.
Using Artifacts With Secrets
If your initial build image needs to use secure access credentials (such as an auth token) you can supply it as an environment variable:
tasks:
images:
build:
services:
web:
steps:
- docker_file_path: ./Buildfile
environment:
- name: GITHUB_TOKEN
value: ${GITHUB_TOKEN}
write_artifacts:
- name: built-files-artifact
path: /app/built-files
- docker_file_path: ./Dockerfile
image: ${IMAGE_NAME}
read_artifacts:
- name: built-files-artifact
path: /built-files
Here the image in the first build step will be supplied with the enviroment variable GITHUB_TOKEN
, but the image in the second build step will have no record of it.
The following is an example Docker file to demonstrate how the GITHUB_TOKEN
will be used in the first build step:
Buildfile
FROM quay.io/continuouspipe/symfony-php7.1-nginx:stable ARG GITHUB_TOKEN= RUN composer config github-oauth.github.com $GITHUB_TOKEN && \ composer install -o --no-interaction && \ composer clear-cache