build: reworked ci/gha docs
Signed-off-by: David Karlsson <david.karlsson@docker.com>
|
@ -145,12 +145,6 @@ guides:
|
|||
title: Create your own base image (advanced)
|
||||
- path: /develop/scan-images/
|
||||
title: Scan images
|
||||
- sectiontitle: Set up CI/CD
|
||||
section:
|
||||
- path: /ci-cd/best-practices/
|
||||
title: CI/CD Best practices
|
||||
- path: /ci-cd/github-actions/
|
||||
title: Configure GitHub Actions
|
||||
- sectiontitle: Deploy your app to the cloud
|
||||
section:
|
||||
- path: /cloud/aci-integration/
|
||||
|
@ -1583,6 +1577,16 @@ manuals:
|
|||
title: Install Buildx
|
||||
- path: /build/buildx/multiple-builders/
|
||||
title: Using multiple builders
|
||||
- sectiontitle: Continuous integration
|
||||
section:
|
||||
- path: /build/ci/
|
||||
title: CI with Docker
|
||||
- sectiontitle: GitHub Actions
|
||||
section:
|
||||
- path: /build/ci/github-actions/
|
||||
title: Introduction
|
||||
- path: /build/ci/github-actions/examples/
|
||||
title: Examples
|
||||
- path: /build/release-notes/
|
||||
title: Release notes
|
||||
- sectiontitle: Docker Compose
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
This tutorial walks you through the process of setting up and using Docker GitHub
|
||||
Actions for building Docker images, and pushing images to Docker Hub. You will
|
||||
complete the following steps:
|
||||
|
||||
1. Create a new repository on GitHub.
|
||||
2. Define the GitHub Actions workflow.
|
||||
3. Run the workflow.
|
||||
|
||||
To follow this tutorial, you need a Docker ID and a GitHub account.
|
||||
|
||||
### Step one: Create the repository
|
||||
|
||||
Create a GitHub repository and configure the Docker Hub secrets.
|
||||
|
||||
1. Create a new GitHub repository using
|
||||
[this template repository](https://github.com/dvdksn/clockbox/generate).
|
||||
|
||||
The repository contains a simple Dockerfile, and nothing else. Feel free to
|
||||
use another repository containing a working Dockerfile if you prefer.
|
||||
|
||||
2. Open the repository **Settings**, and go to **Secrets** > **Actions**.
|
||||
|
||||
3. Create a new secret named `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
|
||||
4. Create a new
|
||||
[Personal Access Token (PAT)](https://docs.docker.com/docker-hub/access-tokens/#create-an-access-token)
|
||||
for Docker Hub. You can name this token `clockboxci`.
|
||||
|
||||
5. Add the PAT as a second secret in your GitHub repository, with the name
|
||||
`DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
With your repository created, and secrets configured, you're now ready for
|
||||
action!
|
||||
|
||||
### Step two: Set up the workflow
|
||||
|
||||
Set up your GitHub Actions workflow for building and pushing the image to Docker
|
||||
Hub.
|
||||
|
||||
1. Go to your repository on GitHub and then select the **Actions** tab.
|
||||
2. Select **set up a workflow yourself**.
|
||||
|
||||
This takes you to a page for creating a new GitHub actions workflow file in
|
||||
your repository, under `.github/workflows/main.yml` by default.
|
||||
|
||||
3. In the editor window, copy and paste the following YAML configuration.
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
|
||||
- `name`: the name of this workflow.
|
||||
- `on.push.branches`: specifies that this workflow should run on every push
|
||||
event for the branches in the list.
|
||||
- `jobs`: creates a job ID (`build`) and declares the type of machine that
|
||||
the job should run on.
|
||||
|
||||
For more information about the YAML syntax used here, see
|
||||
[Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions){:
|
||||
target="blank" rel="noopener"}.
|
||||
|
||||
### Step three: Define the workflow steps
|
||||
|
||||
Now the essentials: what steps to run, and in what order to run them.
|
||||
|
||||
{% raw %}
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/clockbox:latest
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
The previous YAML snippet contains a sequence of steps that:
|
||||
|
||||
1. Checks out the repository on the build machine.
|
||||
2. Signs in to Docker Hub, using the
|
||||
[Docker Login](https://github.com/marketplace/actions/docker-login){:
|
||||
target="blank" rel="noopener"} action and your Docker Hub credentials.
|
||||
3. Creates a BuildKit builder instance using the
|
||||
[Docker Setup Buildx](https://github.com/marketplace/actions/docker-setup-buildx){:
|
||||
target="blank" rel="noopener"} action.
|
||||
4. Builds the container image and pushes it to the Docker Hub repository, using
|
||||
[Build and push Docker images](https://github.com/marketplace/actions/build-and-push-docker-images){:
|
||||
target="blank" rel="noopener"}.
|
||||
|
||||
The `with` key lists a number of input parameters that configures the step:
|
||||
|
||||
- `context`: the build context.
|
||||
- `file`: filepath to the Dockerfile.
|
||||
- `push`: tells the action to upload the image to a registry after building
|
||||
it.
|
||||
- `tags`: tags that specify where to push the image.
|
||||
|
||||
Add these steps to your workflow file. The full workflow configuration should
|
||||
look as follows:
|
||||
|
||||
{% raw %}
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/clockbox:latest
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
### Run the workflow
|
||||
|
||||
Save the workflow file and run the job.
|
||||
|
||||
1. Select **Start commit** and push the changes to the `main` branch.
|
||||
|
||||
After pushing the commit, the workflow starts automatically.
|
||||
|
||||
2. Go to the **Actions** tab. It displays the workflow.
|
||||
|
||||
Selecting the workflow shows you the breakdown of all the steps.
|
||||
|
||||
3. When the workflow is complete, go to your
|
||||
[repositories on Docker Hub](https://hub.docker.com/repositories){:
|
||||
target="blank" rel="noopener"}.
|
||||
|
||||
If you see the new repository in that list, it means the GitHub Actions
|
||||
successfully pushed the image to Docker Hub!
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
title: Introduction to GitHub Actions
|
||||
description: >
|
||||
Docker maintains a set of official GitHub Actions for building Docker images.
|
||||
keywords: github, actions, gha, ci, build, introduction, tutorial
|
||||
redirect_from:
|
||||
- /ci-cd/github-actions/
|
||||
---
|
||||
|
||||
GitHub Actions is a popular CI/CD platform for automating your build, test, and
|
||||
deployment pipeline. Docker provides a set of official GitHub Actions for you to
|
||||
use in your workflows. These official actions are reusable, easy-to-use
|
||||
components for building, annotating, and pushing images.
|
||||
|
||||
The following GitHub Actions are available:
|
||||
|
||||
- [Build and push Docker images](https://github.com/marketplace/actions/build-and-push-docker-images){:
|
||||
target="blank" rel="noopener" class=""}: build and push Docker images with
|
||||
BuildKit.
|
||||
- [Docker Login](https://github.com/marketplace/actions/docker-login){:
|
||||
target="blank" rel="noopener"}: sign in to a Docker registry.
|
||||
- [Docker Setup Buildx](https://github.com/marketplace/actions/docker-setup-buildx){:
|
||||
target="blank" rel="noopener"}: initiates a BuildKit builder
|
||||
- [Docker Metadata action](https://github.com/marketplace/actions/docker-metadata-action){:
|
||||
target="blank" rel="noopener"}: extracts metadata from Git reference and
|
||||
GitHub events.
|
||||
- [Docker Setup QEMU](https://github.com/marketplace/actions/docker-setup-qemu){:
|
||||
target="blank" rel="noopener"}: installs [QEMU](https://github.com/qemu/qemu)
|
||||
static binaries for multi-arch builds.
|
||||
- [Docker Buildx Bake](https://github.com/marketplace/actions/docker-buildx-bake){:
|
||||
target="blank" rel="noopener"}: enables using `docker buildx bake`.
|
||||
|
||||
Using Docker's actions provides an easy-to-use interface, while still allowing
|
||||
flexibility for customizing build parameters.
|
||||
|
||||
## Get started with GitHub Actions
|
||||
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
This tutorial has shown you how to create a simple GitHub Actions workflow,
|
||||
using the official Docker actions, to build and push an image to Docker Hub.
|
||||
|
||||
There are many more things you can do to customize your workflow to better suit
|
||||
your needs. To learn more about some of the more advanced use cases, take a look
|
||||
at the advanced examples, such as
|
||||
[building multi-platform images](examples.md#multi-platform-images), or
|
||||
[using cache storage backends](examples.md#cache).
|
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
description: Overview of using Docker for continuous integration
|
||||
keywords: ci, build
|
||||
redirect_from:
|
||||
- /ci-cd/best-practices/
|
||||
title: Continuous integration with Docker
|
||||
---
|
||||
|
||||
Continuous Integration (CI) is the part of the development process where you're
|
||||
looking to get your code changes merged with the main branch of the project. At
|
||||
this point, development teams run tests and builds to vet that the code changes
|
||||
don't cause any unwanted or unexpected behaviors.
|
||||
|
||||
{:
|
||||
.invertible }
|
||||
|
||||
There are several uses for Docker at this stage of development, even if you
|
||||
don't end up packaging your application as a container image.
|
||||
|
||||
## Docker as a build environment
|
||||
|
||||
Containers are reproducible, isolated environments that yield predictable
|
||||
results. Building and testing your application in a Docker container makes it
|
||||
easier to prevent unexpected behaviors from occurring. Using a Dockerfile, you
|
||||
define the exact requirements for the build environment, including programming
|
||||
runtimes, operating system, binaries, and more.
|
||||
|
||||
Using Docker to manage your build environment also eases maintenance. For
|
||||
example, updating to a new version of a programming runtime can be as simple as
|
||||
changing a tag or digest in a Dockerfile. No need to SSH into a pet VM to
|
||||
manually reinstall a newer version and update the related configuration files.
|
||||
|
||||
Additionally, just as you expect third-party open source packages to be secure,
|
||||
the same should go for your build environment. You can scan and index a builder
|
||||
image, just like you would for any other containerized application.
|
||||
|
||||
The following links provide instructions for how you can get started using
|
||||
Docker for building your applications in CI:
|
||||
|
||||
- [GitHub Actions](https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action){:
|
||||
target="blank" rel="noopener" class=""}
|
||||
- [GitLab](https://docs.gitlab.com/runner/executors/docker.html){:
|
||||
target="blank" rel="noopener" class=""}
|
||||
- [Circle CI](https://circleci.com/docs/using-docker/){: target="blank"
|
||||
rel="noopener" class=""}
|
||||
- [Render](https://render.com/docs/docker){: target="blank" rel="noopener"
|
||||
class=""}
|
||||
|
||||
### Docker in Docker
|
||||
|
||||
You can also use a Dockerized build environment to build container images using
|
||||
Docker. That is, your build environment runs inside a container which itself is
|
||||
equipped to run Docker builds. This method is referred to as "Docker in Docker".
|
||||
|
||||
Docker provides an official [Docker image](https://hub.docker.com/_/docker) that
|
||||
that you can use for this purpose.
|
||||
|
||||
## What's next
|
||||
|
||||
Docker maintains a set of official GitHub Actions that you can use to build,
|
||||
annotate, and push container images on the GitHub Actions platform. See
|
||||
[Introduction to GitHub Actions](./github-actions/index.md) to learn more and
|
||||
get started.
|
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
description: Best practices for using Docker Hub for CI/CD
|
||||
keywords: CI/CD, GitHub Actions,
|
||||
title: Best practices for using Docker Hub for CI/CD
|
||||
---
|
||||
|
||||
According to the [2020 Jetbrains developer survey](https://www.jetbrains.com/lp/devecosystem-2020/){:target="_blank" rel="noopener" class="_"} , 44% of developers are now using some form of continuous integration and deployment with Docker containers. We understand that a large number of developers have got this set up using Docker Hub as their container registry for part of their workflow. This guide contains some best practices for doing this and provides guidance on how to get started.
|
||||
|
||||
We have also heard feedback that given the changes [Docker introduced](https://www.docker.com/blog/scaling-docker-to-serve-millions-more-developers-network-egress/){:target="_blank" rel="noopener" class="_"} relating to network egress and the number of pulls for free users, that there are questions around the best way to use Docker Hub as part of CI/CD workflows without hitting these limits. This guide covers best practices that improve your experience and uses a sensible consumption of Docker Hub which mitigates the risk of hitting these limits, and contains tips on how to increase the limits depending on your use case.
|
||||
|
||||
## Inner and outer loops
|
||||
|
||||
To get started, one of the most important things when working with Docker and any CI/CD is to understand when you need to test with the CI, and when you can do this locally. At Docker, we think about how developers work in terms of their inner loop (code, build, run, test) and their outer loop (push changes, CI build, CI test, deployment).
|
||||
|
||||

|
||||
|
||||
Before you think about optimizing your CI/CD, it is important to think about your inner loop and how it relates to the outer loop (the CI). We know that most users don't prefer 'debugging through the CI’. Therefore, it is better if your inner loop and outer loop are as similar as possible. We recommend that you run unit tests as part of your `docker build` command by adding a target for them in your Dockerfile. This way, as you are making changes and rebuilding locally, you can run the same unit tests you would run in the CI on your local machine using a simple command.
|
||||
|
||||
The blog post [Go development with Docker](https://www.docker.com/blog/tag/go-env-series/){:target="_blank" rel="noopener" class="_"} is a great example of how you can use tests in your Docker project and re-use them in the CI. This also creates a shorter feedback loop on issues and reduces the amount of pulls and builds your CI needs to do.
|
||||
|
||||
## Optimizing CI/CD deployments
|
||||
|
||||
Once you get into your actual outer loop and Docker Hub, there are a few things you can do to get the most of your CI and deliver the fastest Docker experience.
|
||||
|
||||
First and foremost, stay secure. When you are setting up your CI, ensure you are using a Docker Hub access token, rather than your password.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> You can create new access tokens from your [Security](https://hub.docker.com/settings/security){:target="_blank" rel="noopener" class="_"} page on Docker Hub.
|
||||
|
||||
Once you have created access tokens and have added it to a secrets store on your platform, you need to consider when to push and pull in your CI/CD, along with where from, depending on the change you are making.
|
||||
|
||||
The first thing you can do to reduce the build time and reduce your number of calls is make use of the **build cache** to reuse layers you have already pulled. You can do this on many platforms by using buildX (buildkits) caching functionality and whatever cache your platform provides. For example, see [Optimizing the GitHub Actions workflow using build cache](../github-actions#optimizing-the-workflow).
|
||||
|
||||
The other change you may want to make is only have your release images go to Docker Hub. This would mean setting up functions to push your PR images to a more local image store to be quickly pulled and tested, rather than promoting them all the way up to production.
|
||||
|
||||
## Next steps
|
||||
|
||||
We know there are a lot more tips and tricks for using Docker in CI.
|
||||
However, we think these are some of the important things, considering the [Docker Hub rate limits](../docker-hub/download-rate-limit.md).
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> If you are still experiencing issues with pull limits after you are authenticated, you can consider upgrading to a [Docker subscription](https://www.docker.com/pricing){:target="_blank" rel="noopener" class="_"}.
|
||||
|
||||
For information on how to configure GitHub Actions CI/CD pipeline, see [Configure GitHub Actions](github-actions.md).
|
|
@ -1,288 +0,0 @@
|
|||
---
|
||||
description: Configure GitHub Actions
|
||||
keywords: CI/CD, GitHub Actions,
|
||||
title: Configure GitHub Actions
|
||||
---
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD
|
||||
pipeline with Docker. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"}
|
||||
on CI/CD best practices.
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Use a sample Docker project as an example to configure GitHub Actions.
|
||||
2. Set up the GitHub Actions workflow.
|
||||
3. Optimize your workflow to reduce build time.
|
||||
4. Push only specific versions to Docker Hub.
|
||||
|
||||
## Set up a Docker project
|
||||
|
||||
Let's get started. This guide uses a simple Docker project as an example. The
|
||||
[SimpleWhaleDemo](https://github.com/usha-mandya/SimpleWhaleDemo){:target="_blank" rel="noopener" class="_"}
|
||||
repository contains a Nginx alpine image. You can either clone this repository,
|
||||
or use your own Docker project.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Before we start, ensure you can access [Docker Hub](https://hub.docker.com/)
|
||||
from any workflows you create. To do this:
|
||||
|
||||
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository
|
||||
and click **Settings** > **Secrets** > **New secret**.
|
||||
|
||||
2. Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID
|
||||
as value.
|
||||
|
||||
3. Create a new Personal Access Token (PAT). To create a new token, go to
|
||||
[Docker Hub Settings](https://hub.docker.com/settings/security) and then click
|
||||
**New Access Token**.
|
||||
|
||||
4. Let's call this token **simplewhaleci**.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
5. Now, add this Personal Access Token (PAT) as a second secret into the GitHub
|
||||
secrets UI with the name `DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Set up the GitHub Actions workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we
|
||||
can access Docker Hub from any workflow. Now, let's set up our GitHub Actions
|
||||
workflow to build and store our images in Hub.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push.
|
||||
We'll then add a tag to specify to always go to the latest version. Lastly,
|
||||
we'll echo the image digest to see what was pushed.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and add the following content:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
name: ci
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to
|
||||
do it for every push against the master branch of our project:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The branch name may be `main` or `master`. Verify the name of the branch for your repository and update the configuration accordingly.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our workflow
|
||||
(what jobs), we are going to add our build one and select that it runs on the
|
||||
latest Ubuntu instances available:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, we can add the steps required:
|
||||
* The first one checks-out our repository under `$GITHUB_WORKSPACE`, so our workflow
|
||||
can access it.
|
||||
* The second one will use our PAT and username to log into Docker Hub.
|
||||
* The third will setup Docker Buildx to create the builder instance using a
|
||||
BuildKit container under the hood.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, let the workflow run for the first time and then tweak the Dockerfile to
|
||||
make sure the CI is running and pushing the new image changes:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Optimizing the workflow
|
||||
|
||||
Next, let's look at how we can optimize the GitHub Actions workflow through
|
||||
build cache using the registry. This allows to reduce the build time as it
|
||||
will not have to run instructions that have not been impacted by changes in
|
||||
your Dockerfile or source code and also reduce number of pulls we complete
|
||||
against Docker Hub.
|
||||
|
||||
In this example, we need to add some extra attributes to the build and push
|
||||
step:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:buildcache,mode=max
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
As you can see, we are using the `type=registry` cache exporter to import/export
|
||||
cache from a cache manifest or (special) image configuration. Here it will be
|
||||
pushed as a specific tag named `buildcache` for our image build.
|
||||
|
||||
Now, run the workflow again and verify that it uses the build cache.
|
||||
|
||||
## Push tagged versions and handle pull requests
|
||||
|
||||
Earlier, we learnt how to set up a GitHub Actions workflow to a Docker project,
|
||||
how to optimize the workflow by setting up cache. Let's now look at how we can
|
||||
improve it further. We can do this by adding the ability to have tagged versions
|
||||
behave differently to all commits to master. This means, only specific versions
|
||||
are pushed, instead of every commit updating the latest version on Docker Hub.
|
||||
|
||||
You can consider this approach to have your commits pushed as an edge tag to
|
||||
then use it in nightly tests. By doing this, you can always test the last
|
||||
changes of your active branch while reserving your tagged versions for release
|
||||
to Docker Hub.
|
||||
|
||||
First, let us modify our existing GitHub workflow to take into account pushed
|
||||
tags and pull requests:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags:
|
||||
- 'v*'
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This ensures that the CI will trigger your workflow on push events
|
||||
(branch and tags). If we tag our commit with something like `v1.0.2`:
|
||||
|
||||
{% raw %}
|
||||
```console
|
||||
$ git tag -a v1.0.2
|
||||
$ git push origin v1.0.2
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, go to GitHub and check your Actions
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Let's reuse our current workflow to also handle pull requests for testing
|
||||
purpose but also push our image in the GitHub Container Registry.
|
||||
|
||||
First we have to handle pull request events:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry){:target="_blank" rel="noopener" class="_"},
|
||||
use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow){:target="_blank" rel="noopener" class="_"}
|
||||
for the best security and experience.
|
||||
|
||||
Now let's change the Docker Hub login with the GitHub Container Registry one:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Remember to change how the image is tagged. The following example keeps ‘latest'
|
||||
as the only tag. However, you can add any logic to this if you prefer:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
tags: ghcr.io/<username>/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
> **Note**: Replace `<username>` with the repository owner. We could use
|
||||
> {% raw %}`${{ github.repository_owner }}`{% endraw %} but this value can be mixed-case, so it could
|
||||
> fail as [repository name must be lowercase](https://github.com/docker/build-push-action/blob/master/TROUBLESHOOTING.md#repository-name-must-be-lowercase){:target="_blank" rel="noopener" class="_"}.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, we will have two different flows: one for our changes to master, and one
|
||||
for our pushed tags. Next, we need to modify what we had before to ensure we are
|
||||
pushing our PRs to the GitHub registry rather than to Docker Hub.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this guide, you have learnt how to set up GitHub Actions workflow to an
|
||||
existing Docker project, optimize your workflow to improve build times and
|
||||
reduce the number of pull requests, and finally, we learnt how to push only
|
||||
specific versions to Docker Hub.
|
||||
|
||||
## Next steps
|
||||
|
||||
You can now consider setting up nightly builds, test your image before pushing
|
||||
it, setting up secrets, share images between jobs or automatically handle
|
||||
tags and OCI Image Format Specification labels generation.
|
||||
|
||||
To look at how you can do one of these, or to get a full example on how to set
|
||||
up what we have accomplished today, check out [our advanced examples](https://github.com/docker/build-push-action/tree/master/docs/advanced){:target="_blank" rel="noopener" class="_"}
|
||||
which runs you through this and more details.
|
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 386 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 116 KiB |
|
@ -6,235 +6,9 @@ description: Learn how to Configure CI/CD for your application
|
|||
|
||||
{% include_relative nav.html selected="4" %}
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD pipeline with Docker containers. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"} on CI/CD best practices .
|
||||
## Get started with GitHub Actions
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Use a sample Docker project as an example to configure GitHub Actions
|
||||
2. Set up the GitHub Actions workflow
|
||||
3. Optimize your workflow to reduce the number of pull requests and the total build time, and finally,
|
||||
4. Push only specific versions to Docker Hub.
|
||||
|
||||
## Set up a Docker project
|
||||
|
||||
Let’s get started. This guide uses a simple Docker project as an example. The [SimpleWhaleDemo](https://github.com/usha-mandya/SimpleWhaleDemo){:target="_blank" rel="noopener" class="_"} repository contains an Nginx alpine image. You can either clone this repository, or use your own Docker project.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Before we start, ensure you can access [Docker Hub](https://hub.docker.com/) from any workflows you create. To do this:
|
||||
|
||||
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository and click **Settings** > **Secrets** > **New secret**.
|
||||
|
||||
2. Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
|
||||
3. Create a new Personal Access Token (PAT). To create a new token, go to [Docker Hub Settings](https://hub.docker.com/settings/security) and then click **New Access Token**.
|
||||
|
||||
4. Let’s call this token **simplewhaleci**.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
5. Now, add this Personal Access Token (PAT) as a second secret into the GitHub secrets UI with the name `DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Set up the GitHub Actions workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we can access Docker Hub from any workflow. Now, let’s set up our GitHub Actions workflow to build and store our images in Hub. We can achieve this by creating two Docker actions:
|
||||
|
||||
1. The first action enables us to log in to Docker Hub using the secrets we stored in the GitHub Repository.
|
||||
2. The second one is the build and push action.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push. We’ll then add a tag to specify to always go to the latest version. Lastly, we’ll echo the image digest to see what was pushed.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and add the following content:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
```yaml
|
||||
name: CI to Docker Hub
|
||||
```
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to do it for every push against the master branch of our project:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The branch name may be `main` or `master`. Verify the name of the branch for your repository and update the configuration accordingly.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our action (what jobs), we are going to add our build one and select that it runs on the latest Ubuntu instances available:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
|
||||
Now, we can add the steps required. The first one checks-out our repository under `$GITHUB_WORKSPACE`, so our workflow can access it. The second is to use our PAT and username to log into Docker Hub. The third is the Builder, the action uses BuildKit under the hood through a simple Buildx action which we will also setup
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, let the workflow run for the first time and then tweak the Dockerfile to make sure the CI is running and pushing the new image changes:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Optimizing the workflow
|
||||
|
||||
Next, let’s look at how we can optimize the GitHub Actions workflow through build cache. This has two main advantages:
|
||||
|
||||
1. Build cache reduces the build time as it will not have to re-download all of the images, and
|
||||
2. It also reduces the number of pulls we complete against Docker Hub. We need to make use of GitHub cache to make use of this.
|
||||
|
||||
Let us set up a Builder with a build cache. First, we need to set up cache for the builder. In this example, let us add the path and keys to store this under using GitHub cache for this.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
And lastly, after adding the builder and build cache snippets to the top of the Actions file, we need to add some extra attributes to the build and push step. This involves:
|
||||
|
||||
- Setting up the builder to use the output of the buildx step, and then
|
||||
- Using the cache we set up earlier for it to store to and to retrieve
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, run the workflow again and verify that it uses the build cache.
|
||||
|
||||
## Push tagged versions to Docker Hub
|
||||
|
||||
Earlier, we learnt how to set up a GitHub Actions workflow to a Docker project, how to optimize the workflow by setting up a builder with build cache. Let’s now look at how we can improve it further. We can do this by adding the ability to have tagged versions behave differently to all commits to master. This means, only specific versions are pushed, instead of every commit updating the latest version on Docker Hub.
|
||||
|
||||
You can consider this approach to have your commits go to a local registry to then use in nightly tests. By doing this, you can always test what is latest while reserving your tagged versions for release to Docker Hub.
|
||||
|
||||
This involves two steps:
|
||||
|
||||
1. Modifying the GitHub workflow to only push commits with specific tags to Docker Hub
|
||||
2. Setting up a GitHub Actions file to store the latest commit as an image in the GitHub registry
|
||||
|
||||
First, let us modify our existing GitHub workflow to only push to Hub if there’s a particular tag. For example:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This ensures that the main CI will only trigger if we tag our commits with `Vn.n.n.` Let’s test this. For example, run the following command:
|
||||
|
||||
```console
|
||||
$ git tag -a v1.0.2
|
||||
$ git push origin v1.0.2
|
||||
```
|
||||
|
||||
Now, go to GitHub and check your Actions
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, let’s set up a second GitHub action file to store our latest commit as an image in the GitHub registry. You may want to do this to:
|
||||
|
||||
1. Run your nightly tests or recurring tests, or
|
||||
2. To share work in progress images with colleagues.
|
||||
|
||||
Let’s clone our previous GitHub action and add back in our previous logic for all pushes. This will mean we have two workflow files, our previous one and our new one we will now work on.
|
||||
Next, change your Docker Hub login to a GitHub container registry login:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry), use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) for the best security and experience.
|
||||
|
||||
You may need to [manage write and read access of GitHub Actions](https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio) for repositories in the container settings.
|
||||
|
||||
You can also use a [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
|
||||
Remember to change how the image is tagged. The following example keeps ‘latest’ as the only tag. However, you can add any logic to this if you prefer:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, we will have two different flows: one for our changes to master, and one for our pull requests. Next, we need to modify what we had before to ensure we are pushing our PRs to the GitHub registry rather than to Docker Hub.
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|
|
@ -8,331 +8,9 @@ redirect_from:
|
|||
|
||||
{% include_relative nav.html selected="5" %}
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD pipeline with Docker containers. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"} on CI/CD best practices.
|
||||
## Get started with GitHub Actions
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Set up continuous integration (CI) pipeline using GitHub Actions;
|
||||
2. Enable Docker Hub access for continuous deployment (CD) tools;
|
||||
3. Optimize your GitHub Actions-based CI/CD pipeline to reduce the number of pull requests and the total build time; and
|
||||
4. Release only specific versions of your application to Docker Hub.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Before we begin, it must be said that Continuous Integration (CI) and Continuous Deployment (CD) each is a _huge_ subject, with many different approaches and opinions. The approach chosen in this guide is optimised for pedagogical clarity and simplicity and is not meant to be the One True Way to test and release software.
|
||||
|
||||
## Choose a sample project
|
||||
|
||||
Let’s get started. This guide uses a simple Go project as an example. In fact, it is the same project we got acquainted with in [Build Images](build-images.md) part of this guide. The [olliefr/docker-gs-ping](https://github.com/olliefr/docker-gs-ping){:target="_blank" rel="noopener" class="_"} repository contains the full source code and the `Dockerfile`. You can either fork it or to follow along and set up one of your own Go projects in a fashion described in this section.
|
||||
|
||||
Thus, as long as you have a GitHub repo with a project and a `Dockerfile`, you can complete this part of the tutorial.
|
||||
|
||||
## Enable access to Docker Hub
|
||||
|
||||
The [Docker Hub](https://hub.docker.com/) is a hosted repository service provided by Docker for finding and sharing container images.
|
||||
|
||||
Before we can publish our Docker image to Docker Hub, we must grant GitHub Actions access to Docker Hub API.
|
||||
|
||||
To set up the access to Docker Hub API:
|
||||
|
||||
1. Create a new Personal Access Token (PAT) for Docker Hub.
|
||||
|
||||
* Go to the [Docker Hub Account Settings](https://hub.docker.com/settings/security) and then click **New Access Token**.
|
||||
* Let's call this token `docker-gs-ping-ci`. Input the name and click **Create**.
|
||||
* Copy the token value, we'll need it in a second.
|
||||
|
||||
2. Add your Docker ID and PAT as secrets to your GitHub repo.
|
||||
|
||||
* Navigate to your GitHub repository and click **Settings** > **Secrets** > **New secret**.
|
||||
* Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
* Create a new secret with the name `DOCKER_HUB_ACCESS_TOKEN` and use the token value from the step (1).
|
||||
|
||||
Your GitHub repository **Secrets** section would look like the following.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now it will be possible to refer to these two variables from our workflows. This will open up an opportunity to publish our image to Docker Hub.
|
||||
|
||||
## Set up the CI workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we can access Docker Hub from any GitHub Actions workflow. But before setting out to build the images for releasing our software, let's build a CI pipeline to run the tests first.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and update the starter template to match the following:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
name: Run CI
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to do it for every push against the main branch of our project:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
The `workflow_dispatch` is optional. It enables to run this workflow manually from the Actions tab.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our workflow. A workflow run is made up of one or more _jobs_ that can run sequentially or in parallel.
|
||||
|
||||
The first job we would like to set up is the one to build and run our tests. Let it be run on the latest Ubuntu instance:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
A job is a sequence of _steps_. For this simple CI pipeline we would like to:
|
||||
|
||||
1. Set up Go compiler environment.
|
||||
2. Check out our code from its GitHub repository.
|
||||
3. Fetch Go modules used by our application.
|
||||
4. (Optional) Build the binary for our application.
|
||||
5. Build the Docker Image for our application.
|
||||
6. Run the functional tests for our application against that Docker image.
|
||||
|
||||
Building the binary in step 4 is actually optional. It is a "smoke test". We don't want to be building a Docker image and attempting functional testing if our application does not even compile. If we had "unit tests" or some other small tests, we would run them between steps 4 and 5 as well.
|
||||
|
||||
The following sequence of `steps` achieves the goals we just set.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.4
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Fetch required Go modules
|
||||
run: go mod download
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: false
|
||||
tags: ${{ github.event.repository.name }}:latest, ${{ github.repository }}:latest
|
||||
|
||||
- name: Run functional tests
|
||||
run: go test -v ./...
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
As is usual with YAML files, be aware of indentation. The complete workflow file for reference is available in the project's repo, under the name of `.github/workflows/ci.yml`.
|
||||
|
||||
This should be enough to test our approach to CI. Change the workflow file name from `main.yml` to `ci.yml` and press **Start commit** button. Fill out the commit details in your preferred style and press **Commit new file**. GitHub Actions are saved as YAML files in `.github/workflows` directory and GitHub web interface would do that for us.
|
||||
|
||||
Select **Actions** from the navigation bar for your repository. Since we've enabled `workflow_dispatch` option in our Action, GitHub will have started it already. If not, select "CI/CD to Docker Hub" action on the left, and then press **Run workflow** button on the right to start the workflow.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Should the run fail, you can click on the failing entry to see the logs and amend the workflow YAML file accordingly.
|
||||
|
||||
## Set up the CD workflow
|
||||
|
||||
Now, let’s create a GitHub Actions workflow to build and store the image for our application in Docker Hub. We can achieve this by creating two Docker actions:
|
||||
|
||||
1. The first action enables us to log in to Docker Hub using the secrets we stored in the GitHub Repository settings.
|
||||
2. The second one is the build and push action.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push. We’ll then add a tag to specify to always go to the latest version. Lastly, we’ll echo the image digest to see what was pushed.
|
||||
|
||||
Now, we can add the steps required. Start a blank new workflow, just as we did before. Let's give it a file name of `release.yml` and amend the template body to match the following.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
name: Release to Docker Hub
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*.*.*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.4
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Fetch required Go modules
|
||||
run: go mod download
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ github.event.repository.name }}:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This workflow is similar to the CI workflow, with the following changes:
|
||||
|
||||
* This workflow is only triggered when a git tag of the format `*.*.*` is pushed to the repo. The tag meant to be a semantic version, such as `3.5.0` or `0.0.1`.
|
||||
* The very first step is to login into Docker Hub using the two secrets that we had saved in the repository settings previously.
|
||||
* The _build and push_ step now has `push: true` and since we had logged into Docker Hub this will result in the latest image being published.
|
||||
* The image digest step prints out the image metadata to the log.
|
||||
|
||||
Let's save this workflow and check the _Actions_ page for the repository on GitHub. Unlike the CI workflow, this new workflow cannot be triggered manually - this is how we set it up. So, in order to test it, we have to tag some commit. Let's tag the `HEAD` of the `main` branch:
|
||||
|
||||
```console
|
||||
$ git tag -a 0.0.1 -m "Test release workflow"
|
||||
|
||||
$ git push --tags
|
||||
Enumerating objects: 1, done.
|
||||
Counting objects: 100% (1/1), done.
|
||||
Writing objects: 100% (1/1), 169 bytes | 169.00 KiB/s, done.
|
||||
Total 1 (delta 0), reused 0 (delta 0)
|
||||
To https://github.com/olliefr/docker-gs-ping.git
|
||||
* [new tag] 0.0.1 -> 0.0.1
|
||||
```
|
||||
|
||||
This means our tag was successfully pushed to the main repo. If we switch to the GitHub UI, we would see that the workflow has already been triggered:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Plot twist! Despite having explained how to add secrets to the repository, we had forgotten to do it ourselves. And the workflow run results in error:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
That's easy to fix. We follow our own guide and add the secrets to the repository settings. But how do we re-run the workflow? We need to remove the tag and reapply it.
|
||||
|
||||
To remove the tag on the `remote`:
|
||||
|
||||
```console
|
||||
$ git push origin :refs/tags/0.0.1
|
||||
To https://github.com/olliefr/docker-gs-ping.git
|
||||
- [deleted] 0.0.1
|
||||
```
|
||||
|
||||
And to re-apply it locally and push:
|
||||
|
||||
```console
|
||||
$ git tag -fa 0.0.1 -m "Test release workflow"
|
||||
Updated tag '0.0.1' (was d7d3edc)
|
||||
$ git push origin --tags
|
||||
Enumerating objects: 1, done.
|
||||
Counting objects: 100% (1/1), done.
|
||||
Writing objects: 100% (1/1), 170 bytes | 170.00 KiB/s, done.
|
||||
Total 1 (delta 0), reused 0 (delta 0)
|
||||
To https://github.com/olliefr/docker-gs-ping.git
|
||||
* [new tag] 0.0.1 -> 0.0.1
|
||||
```
|
||||
|
||||
And the workflow is triggered again. This time it completes without issues:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Since the image we've just pushed to Docker Hub is public, now it can be pulled by anyone, from anywhere:
|
||||
|
||||
```console
|
||||
$ docker pull olliefr/docker-gs-ping
|
||||
Using default tag: latest
|
||||
latest: Pulling from olliefr/docker-gs-ping
|
||||
540db60ca938: Already exists
|
||||
adcc1eea9eea: Already exists
|
||||
4c4ab2625f07: Already exists
|
||||
c5e7595549f7: Pull complete
|
||||
3df88182f7ac: Pull complete
|
||||
56c9181b0012: Pull complete
|
||||
04b91de9e9ed: Pull complete
|
||||
7a1dde643d3d: Pull complete
|
||||
f815a8b426ad: Pull complete
|
||||
7a6b1ee48c34: Pull complete
|
||||
ca1a2b73aa81: Pull complete
|
||||
Digest: sha256:81bedd562952757322a07a26634b01e3916db375cc695843124f79641e433029
|
||||
Status: Downloaded newer image for olliefr/docker-gs-ping:latest
|
||||
docker.io/olliefr/docker-gs-ping:latest
|
||||
```
|
||||
|
||||
We've just build a simple (maybe even naive) CI/CD workflow. There are many ways in which it can be improved. We'll look at some of these ways in the next section.
|
||||
|
||||
## Optimizing the workflow
|
||||
|
||||
Next, let’s look at how we can optimize the GitHub Actions workflow through build cache. This has two main advantages:
|
||||
|
||||
1. Build cache reduces the build time as it will not have to re-download all of the images, and
|
||||
2. It also reduces the number of pulls we complete against Docker Hub. We need to make use of GitHub cache to make use of this.
|
||||
|
||||
Let us set up a Builder with a build cache. First, we need to set up the builder, and then to configure the cache. In this example, let us add the path and keys to store this under using GitHub cache for this.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
And lastly, after adding the builder and build cache snippets to the top of the Actions file, we need to add some extra attributes to the build and push step. This involves:
|
||||
|
||||
* Setting up the builder to use the output of the buildx step, and then
|
||||
* Using the cache we set up earlier for it to store to and to retrieve
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: false
|
||||
load: true
|
||||
tags: ${{ github.event.repository.name }}:latest, ${{ github.repository }}:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, run the CI workflow again and verify that it uses the build cache by checking the workflow log.
|
||||
|
||||
## Wrap up
|
||||
|
||||
GitHub Actions are an immensely powerful way to automate your CI and CD pipelines, and [there is a lot of documentation](https://docs.github.com/en/actions) to aid you in that mission. It is not, however, the _only_ way to integrate containers into your workflow. The aim of this section was to show you some of the basic things that are possible. There is a multitude of CI and CD tools on the market and you are very welcome to investigate what integration with the container ecosystem they provide. Well defined, automated CI pipelines can save you and your team a lot of effort.
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|
|
@ -6,292 +6,9 @@ description: Learn how to Configure CI/CD for your application
|
|||
|
||||
{% include_relative nav.html selected="5" %}
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD pipeline with Docker containers. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"} on CI/CD best practices .
|
||||
## Get started with GitHub Actions
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Use a sample Docker project as an example to configure GitHub Actions
|
||||
2. Set up the GitHub Actions workflow
|
||||
3. Optimize your workflow to reduce the number of pull requests and the total build time
|
||||
4. Push only specific versions to Docker Hub
|
||||
5. Optimize your image using multi-stage builds
|
||||
|
||||
## Set up a Docker project
|
||||
|
||||
Let’s get started. This guide uses a simple Docker project as an example. The [SimpleWhaleDemo](https://github.com/usha-mandya/SimpleWhaleDemo){:target="_blank" rel="noopener" class="_"} repository contains an Nginx alpine image. You can either clone this repository, or use your own Docker project.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Before we start, ensure you can access [Docker Hub](https://hub.docker.com/) from any workflows you create. To do this:
|
||||
|
||||
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository and click **Settings** > **Secrets** > **New secret**.
|
||||
|
||||
2. Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
|
||||
3. Create a new Personal Access Token (PAT). To create a new token, go to [Docker Hub Settings](https://hub.docker.com/settings/security) and then click **New Access Token**.
|
||||
|
||||
4. Let’s call this token **simplewhaleci**.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
5. Now, add this Personal Access Token (PAT) as a second secret into the GitHub secrets UI with the name `DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Set up the GitHub Actions workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we can access Docker Hub from any workflow. Now, let’s set up our GitHub Actions workflow to build and store our images in Hub. We can achieve this by creating two Docker actions:
|
||||
|
||||
1. The first action enables us to log in to Docker Hub using the secrets we stored in the GitHub Repository.
|
||||
2. The second one is the build and push action.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push. We’ll then add a tag to specify to always go to the latest version. Lastly, we’ll echo the image digest to see what was pushed.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and add the following content:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
```yaml
|
||||
name: CI to Docker Hub
|
||||
```
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to do it for every push against the master branch of our project:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
```
|
||||
> **Note**
|
||||
>
|
||||
> The branch name may be `main` or `master`. Verify the name of the branch for your repository and update the configuration accordingly.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our action (what jobs), we are going to add our build one and select that it runs on the latest Ubuntu instances available:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
|
||||
Now, we can add the steps required. The first one checks-out our repository under `$GITHUB_WORKSPACE`, so our workflow can access it. The second is to use our PAT and username to log into Docker Hub. The third is the Builder, the action uses BuildKit under the hood through a simple Buildx action which we will also setup
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, let the workflow run for the first time and then tweak the Dockerfile to make sure the CI is running and pushing the new image changes:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Optimize the workflow
|
||||
|
||||
Next, let’s look at how we can optimize the GitHub Actions workflow through build cache. This has two main advantages:
|
||||
|
||||
1. Build cache reduces the build time as it will not have to re-download all of the images, and
|
||||
2. It also reduces the number of pulls we complete against Docker Hub. We need to make use of GitHub cache to make use of this.
|
||||
|
||||
Let us set up a Builder with a build cache. First, we need to set up cache for the builder. In this example, let us add the path and keys to store this under using GitHub cache for this.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
And lastly, after adding the builder and build cache snippets to the top of the Actions file, we need to add some extra attributes to the build and push step. This involves:
|
||||
|
||||
Setting up the builder to use the output of the buildx step, and then
|
||||
Using the cache we set up earlier for it to store to and to retrieve
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, run the workflow again and verify that it uses the build cache.
|
||||
|
||||
## Push tagged versions to Docker Hub
|
||||
|
||||
Earlier, we learnt how to set up a GitHub Actions workflow to a Docker project, how to optimize the workflow by setting up a builder with build cache. Let’s now look at how we can improve it further. We can do this by adding the ability to have tagged versions behave differently to all commits to master. This means, only specific versions are pushed, instead of every commit updating the latest version on Docker Hub.
|
||||
|
||||
You can consider this approach to have your commits go to a local registry to then use in nightly tests. By doing this, you can always test what is latest while reserving your tagged versions for release to Docker Hub.
|
||||
|
||||
This involves two steps:
|
||||
|
||||
1. Modifying the GitHub workflow to only push commits with specific tags to Docker Hub
|
||||
2. Setting up a GitHub Actions file to store the latest commit as an image in the GitHub registry
|
||||
|
||||
First, let us modify our existing GitHub workflow to only push to Hub if there’s a particular tag. For example:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This ensures that the main CI will only trigger if we tag our commits with `V.n.n.n.` Let’s test this. For example, run the following command:
|
||||
|
||||
```console
|
||||
$ git tag -a v1.0.2
|
||||
$ git push origin v1.0.2
|
||||
```
|
||||
|
||||
Now, go to GitHub and check your Actions
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, let’s set up a second GitHub action file to store our latest commit as an image in the GitHub registry. You may want to do this to:
|
||||
|
||||
1. Run your nightly tests or recurring tests, or
|
||||
2. To share work in progress images with colleagues.
|
||||
|
||||
Let’s clone our previous GitHub action and add back in our previous logic for all pushes. This will mean we have two workflow files, our previous one and our new one we will now work on.
|
||||
Next, change your Docker Hub login to a GitHub container registry login:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry), use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) for the best security and experience.
|
||||
|
||||
You may need to [manage write and read access of GitHub Actions](https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio) for repositories in the container settings.
|
||||
|
||||
You can also use a [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
|
||||
Remember to change how the image is tagged. The following example keeps ‘latest’ as the only tag. However, you can add any logic to this if you prefer:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, we will have two different flows: one for our changes to master, and one for our pull requests. Next, we need to modify what we had before to ensure we are pushing our PRs to the GitHub registry rather than to Docker Hub.
|
||||
|
||||
## Optimize your image using multi-stage builds
|
||||
|
||||
Now, let’s take a look at the Dockerfile and see how we can optimize it to work in development, as well as get smaller images to run containers in production. As this is the last step in the file, it will be used by default to build the image when you run a docker build without specifying the target:
|
||||
|
||||
```console
|
||||
$ docker build --tag java-docker .
|
||||
docker build --tag java-docker .
|
||||
|
||||
[+] Building 1.2s (15/15) FINISHED
|
||||
=> [internal] load build definition from Dockerfile
|
||||
=> => transferring dockerfile: 37B
|
||||
=> [internal] load .dockerignore
|
||||
=> => transferring context: 2B
|
||||
=> [internal] load metadata for docker.io/library/openjdk:11-jre-slim
|
||||
=> [internal] load metadata for docker.io/library/openjdk:16-alpine3.13
|
||||
=> [internal] load build context
|
||||
=> => transferring context: 11.48kB
|
||||
=> [production 1/2] FROM docker.io/library/openjdk:11-jre-slim@sha256:85795599f4c765182c414a1eb4e272841e18e2f267ce5010ea6a266f7f26e7f6
|
||||
=> [base 1/6] FROM docker.io/library/openjdk:16-alpine3.13@sha256:49d822f4fa4deb5f9d0201ffeec9f4d113bcb4e7e49bd6bc063d3ba93aacbcae
|
||||
=> CACHED [base 2/6] WORKDIR /app
|
||||
=> CACHED [base 3/6] COPY .mvn/ .mvn
|
||||
=> CACHED [base 4/6] COPY mvnw pom.xml ./
|
||||
=> CACHED [base 5/6] RUN ./mvnw dependency:go-offline
|
||||
=> CACHED [base 6/6] COPY src ./src
|
||||
=> CACHED [build 1/1] RUN ./mvnw package
|
||||
=> CACHED [production 2/2] COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
|
||||
=> exporting to image
|
||||
=> => exporting layers
|
||||
=> => writing image sha256:c17469b9e2f30537060f48bbe5d9d22003dd35edef7092348824a2438101ab3a
|
||||
=> => naming to docker.io/library/java-docker
|
||||
```
|
||||
|
||||
The second interesting point is that this step doesn’t take the base target or a JDK image as reference. Instead, it uses a Java Runtime Environment image.
|
||||
Note that you don’t need a large image with all the development dependencies to run your application in production. Limiting the number of dependencies in production images can significantly limit the attack surface.
|
||||
|
||||
```dockerfile
|
||||
FROM openjdk:11-jre-slim as production
|
||||
EXPOSE 8080
|
||||
|
||||
COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
|
||||
|
||||
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]
|
||||
```
|
||||
|
||||
The container will also automatically expose its 8080 port and copies the Java Archive produced in build step to use it at container startup.
|
||||
|
||||
The production image built in this way only contains a runtime environment with the final application archive, just what you need to start your Spring Pet Clinic application.
|
||||
|
||||
```console
|
||||
$ docker build --tag java-docker:jdk . --target development
|
||||
$ docker build --tag java-docker:jre .
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
java-docker jre c17469b9e2f3 3 hours ago 270MB
|
||||
java-docker jdk 4c15436d8ab7 5 hours ago 567MB
|
||||
```
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|
|
@ -6,235 +6,9 @@ description: Learn how to develop your application locally.
|
|||
|
||||
{% include_relative nav.html selected="5" %}
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD pipeline with Docker containers. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"} on CI/CD best practices .
|
||||
## Get started with GitHub Actions
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Use a sample Docker project as an example to configure GitHub Actions
|
||||
2. Set up the GitHub Actions workflow
|
||||
3. Optimize your workflow to reduce the number of pull requests and the total build time, and finally,
|
||||
4. Push only specific versions to Docker Hub.
|
||||
|
||||
## Set up a Docker project
|
||||
|
||||
Let’s get started. This guide uses a simple Docker project as an example. The [SimpleWhaleDemo](https://github.com/usha-mandya/SimpleWhaleDemo){:target="_blank" rel="noopener" class="_"} repository contains an Nginx alpine image. You can either clone this repository, or use your own Docker project.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Before we start, ensure you can access [Docker Hub](https://hub.docker.com/) from any workflows you create. To do this:
|
||||
|
||||
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository and click **Settings** > **Secrets** > **New secret**.
|
||||
|
||||
2. Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
|
||||
3. Create a new Personal Access Token (PAT). To create a new token, go to [Docker Hub Settings](https://hub.docker.com/settings/security) and then click **New Access Token**.
|
||||
|
||||
4. Let’s call this token **simplewhaleci**.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
5. Now, add this Personal Access Token (PAT) as a second secret into the GitHub secrets UI with the name `DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Set up the GitHub Actions workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we can access Docker Hub from any workflow. Now, let’s set up our GitHub Actions workflow to build and store our images in Hub. We can achieve this by creating two Docker actions in the YAML file below:
|
||||
|
||||
1. The first action enables us to log in to Docker Hub using the secrets we stored in the GitHub Repository.
|
||||
2. The second one is the build and push action.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push. We’ll then add a tag to specify to always go to the latest version. Lastly, we’ll echo the image digest to see what was pushed.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and add the following content:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
```yaml
|
||||
name: CI to Docker Hub
|
||||
```
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to do it for every push against the master branch of our project:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The branch name may be `main` or `master`. Verify the name of the branch for your repository and update the configuration accordingly.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our action (what jobs), we are going to add our build one and select that it runs on the latest Ubuntu instances available:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
|
||||
Now, we can add the steps required. The first one checks-out our repository under `$GITHUB_WORKSPACE`, so our workflow can access it. The second is to use our PAT and username to log into Docker Hub. The third is the Builder, the action uses BuildKit under the hood through a simple Buildx action which we will also setup
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, let the workflow run for the first time and then tweak the Dockerfile to make sure the CI is running and pushing the new image changes:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Optimizing the workflow
|
||||
|
||||
Next, let’s look at how we can optimize the GitHub Actions workflow through build cache. This has two main advantages:
|
||||
|
||||
1. Build cache reduces the build time as it will not have to re-download all of the images, and
|
||||
2. It also reduces the number of pulls we complete against Docker Hub. We need to make use of GitHub cache to make use of this.
|
||||
|
||||
Let us set up a Builder with a build cache. First, we need to set up cache for the builder. In this example, let us add the path and keys to store this under using GitHub cache for this.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
And lastly, after adding the builder and build cache snippets to the top of the Actions file, we need to add some extra attributes to the build and push step. This involves:
|
||||
|
||||
Setting up the builder to use the output of the buildx step, and then
|
||||
Using the cache we set up earlier for it to store to and to retrieve
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, run the workflow again and verify that it uses the build cache.
|
||||
|
||||
## Push tagged versions to Docker Hub
|
||||
|
||||
Earlier, we learnt how to set up a GitHub Actions workflow to a Docker project, how to optimize the workflow by setting up a builder with build cache. Let’s now look at how we can improve it further. We can do this by adding the ability to have tagged versions behave differently to all commits to master. This means, only specific versions are pushed, instead of every commit updating the latest version on Docker Hub.
|
||||
|
||||
You can consider this approach to have your commits go to a local registry to then use in nightly tests. By doing this, you can always test what is latest while reserving your tagged versions for release to Docker Hub.
|
||||
|
||||
This involves two steps:
|
||||
|
||||
1. Modifying the GitHub workflow to only push commits with specific tags to Docker Hub
|
||||
2. Setting up a GitHub Actions file to store the latest commit as an image in the GitHub registry
|
||||
|
||||
First, let us modify our existing GitHub workflow to only push to Hub if there’s a particular tag. For example:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This ensures that the main CI will only trigger if we tag our commits with `V.n.n.n.` Let’s test this. For example, run the following command:
|
||||
|
||||
```console
|
||||
$ git tag -a v1.0.2
|
||||
$ git push origin v1.0.2
|
||||
```
|
||||
|
||||
Now, go to GitHub and check your Actions
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, let’s set up a second GitHub action file to store our latest commit as an image in the GitHub registry. You may want to do this to:
|
||||
|
||||
1. Run your nightly tests or recurring tests, or
|
||||
2. To share work in progress images with colleagues.
|
||||
|
||||
Let’s clone our previous GitHub action and add back in our previous logic for all pushes. This will mean we have two workflow files, our previous one and our new one we will now work on.
|
||||
Next, change your Docker Hub login to a GitHub container registry login:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry), use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) for the best security and experience.
|
||||
|
||||
You may need to [manage write and read access of GitHub Actions](https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio) for repositories in the container settings.
|
||||
|
||||
You can also use a [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
|
||||
Remember to change how the image is tagged. The following example keeps ‘latest’ as the only tag. However, you can add any logic to this if you prefer:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, we will have two different flows: one for our changes to master, and one for our pull requests. Next, we need to modify what we had before to ensure we are pushing our PRs to the GitHub registry rather than to Docker Hub.
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|
|
@ -6,235 +6,9 @@ description: Learn how to Configure CI/CD for your application
|
|||
|
||||
{% include_relative nav.html selected="4" %}
|
||||
|
||||
This page guides you through the process of setting up a GitHub Action CI/CD pipeline with Docker containers. Before setting up a new pipeline, we recommend that you take a look at [Ben's blog](https://www.docker.com/blog/best-practices-for-using-docker-hub-for-ci-cd/){:target="_blank" rel="noopener" class="_"} on CI/CD best practices.
|
||||
## Get started with GitHub Actions
|
||||
|
||||
This guide contains instructions on how to:
|
||||
|
||||
1. Use a sample Docker project as an example to configure GitHub Actions
|
||||
2. Set up the GitHub Actions workflow
|
||||
3. Optimize your workflow to reduce the number of pull requests and the total build time, and finally,
|
||||
4. Push only specific versions to Docker Hub.
|
||||
|
||||
## Set up a Docker project
|
||||
|
||||
Let’s get started. This guide uses a simple Docker project as an example. The [SimpleWhaleDemo](https://github.com/usha-mandya/SimpleWhaleDemo){:target="_blank" rel="noopener" class="_"} repository contains an Nginx alpine image. You can either clone this repository, or use your own Docker project.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Before we start, ensure you can access [Docker Hub](https://hub.docker.com/) from any workflows you create. To do this:
|
||||
|
||||
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository and click **Settings** > **Secrets** > **New secret**.
|
||||
|
||||
2. Create a new secret with the name `DOCKER_HUB_USERNAME` and your Docker ID as value.
|
||||
|
||||
3. Create a new Personal Access Token (PAT). To create a new token, go to [Docker Hub Settings](https://hub.docker.com/settings/security) and then click **New Access Token**.
|
||||
|
||||
4. Let’s call this token **simplewhaleci**.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
5. Now, add this Personal Access Token (PAT) as a second secret into the GitHub secrets UI with the name `DOCKER_HUB_ACCESS_TOKEN`.
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Set up the GitHub Actions workflow
|
||||
|
||||
In the previous section, we created a PAT and added it to GitHub to ensure we can access Docker Hub from any workflow. Now, let’s set up our GitHub Actions workflow to build and store our images in Hub. We can achieve this by creating two Docker actions:
|
||||
|
||||
1. The first action enables us to log in to Docker Hub using the secrets we stored in the GitHub Repository.
|
||||
2. The second one is the build and push action.
|
||||
|
||||
In this example, let us set the push flag to `true` as we also want to push. We’ll then add a tag to specify to always go to the latest version. Lastly, we’ll echo the image digest to see what was pushed.
|
||||
|
||||
To set up the workflow:
|
||||
|
||||
1. Go to your repository in GitHub and then click **Actions** > **New workflow**.
|
||||
2. Click **set up a workflow yourself** and add the following content:
|
||||
|
||||
First, we will name this workflow:
|
||||
|
||||
```yaml
|
||||
name: CI to Docker Hub
|
||||
```
|
||||
|
||||
Then, we will choose when we run this workflow. In our example, we are going to do it for every push against the master branch of our project:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The branch name may be `main` or `master`. Verify the name of the branch for your repository and update the configuration accordingly.
|
||||
|
||||
Now, we need to specify what we actually want to happen within our action (what jobs), we are going to add our build one and select that it runs on the latest Ubuntu instances available:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
```
|
||||
|
||||
Now, we can add the steps required. The first one checks-out our repository under `$GITHUB_WORKSPACE`, so our workflow can access it. The second is to use our PAT and username to log into Docker Hub. The third is the Builder, the action uses BuildKit under the hood through a simple Buildx action which we will also setup
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
steps:
|
||||
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, let the workflow run for the first time and then tweak the Dockerfile to make sure the CI is running and pushing the new image changes:
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
## Optimizing the workflow
|
||||
|
||||
Next, let’s look at how we can optimize the GitHub Actions workflow through build cache. This has two main advantages:
|
||||
|
||||
1. Build cache reduces the build time as it will not have to re-download all of the images, and
|
||||
2. It also reduces the number of pulls we complete against Docker Hub. We need to make use of GitHub cache to make use of this.
|
||||
|
||||
Let us set up a Builder with a build cache. First, we need to set up cache for the builder. In this example, let us add the path and keys to store this under using GitHub cache for this.
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
And lastly, after adding the builder and build cache snippets to the top of the Actions file, we need to add some extra attributes to the build and push step. This involves:
|
||||
|
||||
- Setting up the builder to use the output of the buildx step, and then
|
||||
- Using the cache we set up earlier for it to store to and to retrieve
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Now, run the workflow again and verify that it uses the build cache.
|
||||
|
||||
## Push tagged versions to Docker Hub
|
||||
|
||||
Earlier, we learnt how to set up a GitHub Actions workflow to a Docker project, how to optimize the workflow by setting up a builder with build cache. Let’s now look at how we can improve it further. We can do this by adding the ability to have tagged versions behave differently to all commits to master. This means, only specific versions are pushed, instead of every commit updating the latest version on Docker Hub.
|
||||
|
||||
You can consider this approach to have your commits go to a local registry to then use in nightly tests. By doing this, you can always test what is latest while reserving your tagged versions for release to Docker Hub.
|
||||
|
||||
This involves two steps:
|
||||
|
||||
1. Modifying the GitHub workflow to only push commits with specific tags to Docker Hub
|
||||
2. Setting up a GitHub Actions file to store the latest commit as an image in the GitHub registry
|
||||
|
||||
First, let us modify our existing GitHub workflow to only push to Hub if there’s a particular tag. For example:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This ensures that the main CI will only trigger if we tag our commits with `Vn.n.n.` Let’s test this. For example, run the following command:
|
||||
|
||||
```console
|
||||
$ git tag -a v1.0.2
|
||||
$ git push origin v1.0.2
|
||||
```
|
||||
|
||||
Now, go to GitHub and check your Actions
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, let’s set up a second GitHub action file to store our latest commit as an image in the GitHub registry. You may want to do this to:
|
||||
|
||||
1. Run your nightly tests or recurring tests, or
|
||||
2. To share work in progress images with colleagues.
|
||||
|
||||
Let’s clone our previous GitHub action and add back in our previous logic for all pushes. This will mean we have two workflow files, our previous one and our new one we will now work on.
|
||||
Next, change your Docker Hub login to a GitHub container registry login:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry), use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) for the best security and experience.
|
||||
|
||||
You may need to [manage write and read access of GitHub Actions](https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio) for repositories in the container settings.
|
||||
|
||||
You can also use a [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
|
||||
Remember to change how the image is tagged. The following example keeps ‘latest’ as the only tag. However, you can add any logic to this if you prefer:
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
{:width="500px"}
|
||||
|
||||
Now, we will have two different flows: one for our changes to master, and one for our pull requests. Next, we need to modify what we had before to ensure we are pushing our PRs to the GitHub registry rather than to Docker Hub.
|
||||
{% include gha-tutorial.md %}
|
||||
|
||||
## Next steps
|
||||
|
||||
|
|