Reputation: 739
I don't know how to run a cached Docker image in Github Actions.
I've followed a tutorial about Publishing Docker images to implement a task that would cache, build and push Docker image to a DockerHub.
I need to build, cache and run the image, the image publishing is optional.
My goal is to speed up CI workflow.
Here is the Github Actions workflow:
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Check Out Repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- 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-
- 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: ivan123123/c_matrix_library:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
#- name: Run Docker container
# run: ???
# Upload gcovr code coverage report
- name: Upload GCC Code Coverage Report
uses: actions/upload-artifact@v2
with:
name: coveragereport
path: ./builddir/meson-logs/coveragereport/
- name: Upload code coverage reports to codecov.io page
run: bash <(curl -s https://codecov.io/bash)
Edit:
I've found no solution to running cached Docker image, but I have managed to build cached image every time I run CI workflow with docker/setup-buildx-action@v1 action. Because the image is cached, we don't need to download every Docker image dependencies thus saving time from 3 minutes originally to only 40 seconds.
Below is the Github Actions workflow:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache register
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ hashFiles('**/Dockerfile') }}
- name: Build Docker image
uses: docker/build-push-action@v2
with:
context: ./
file: ./Dockerfile
builder: ${{ steps.buildx.outputs.name }}
load: true
tags: c_matrix_library:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Run Docker container
run: docker run -v "$(pwd):/app" c_matrix_library:latest
Upvotes: 22
Views: 30818
Reputation: 79248
For windows, it doesn't appear that buildx
or docker/build-push-action
work (for windows server github action environments). So something like this seems like it might work on windows:
on: workflow_dispatch
jobs:
publish:
# Windows Server 2022 (https://github.com/actions/runner-images)
runs-on: windows-2022
environment: build
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Create cache directory
run: mkdir docker-cache
- name: Cache docker image
id: cache-docker
uses: actions/cache@v4
with:
path: C:\Users\runneradmin\AppData\Local\docker-cache
key: ${{ runner.os }}-docker-cache
- name: Login docker
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_USERNAME: ${{ vars.GH_USERNAME }}
run: echo $env:GH_TOKEN | docker login ghcr.io -u $env:GH_USERNAME --password-stdin
- name: Pull docker image
if: steps.cache-docker.outputs.cache-hit != 'true'
run: docker pull swift:5.9.2-windowsservercore-ltsc2022 || true
- name: Build docker
if: steps.cache-docker.outputs.cache-hit != 'true'
working-directory: load/docker
run: docker build --cache-from swift:5.9.2-windowsservercore-ltsc2022 . -t lancejpollard/swift
- name: Tag docker
if: steps.cache-docker.outputs.cache-hit != 'true'
working-directory: load/docker
run: docker tag lancejpollard/swift:latest ghcr.io/lancejpollard/swift:latest
- name: Save docker
if: steps.cache-docker.outputs.cache-hit != 'true'
run: docker save lancejpollard/swift -o "$($env:LOCALAPPDATA)/docker-cache/lancejpollard.swift.tar"
- name: Restore docker
if: steps.cache-docker.outputs.cache-hit == 'true'
run: docker load < "$($env:LOCALAPPDATA)/docker-cache/lancejpollard.swift.tar"
- name: Run docker
working-directory: load/docker
run: docker run -v ${PWD}:/ --name swift -i -d -t -p lancejpollard/swift
However, for this large swift image, which takes roughly 20 minutes to build, the cache still takes > 10 minutes to docker load
. So not that much of an improvement.
Edit: Actually, testing a few more times, it takes ~15min to build my swift image without caching. It takes ~25min to docker save
the .tar, so that is ~40min on the save path. It takes ~3min to load the cache, and ~10min to docker load
, which is 13min also. So it's the same to build with the cache and without the cache. And if you need to save the cache, double or triple the build time. So it's a net loss to cache like this :/
Upvotes: 1
Reputation: 17188
If you want to cache a published Docker image that lives in the Docker Repository, you can do:
- name: Restore MySQL Image Cache if it exists
id: cache-docker-mysql
uses: actions/cache@v3
with:
path: ci/cache/docker/mysql
key: cache-docker-mysql-5.7
- name: Update MySQL Image Cache if cache miss
if: steps.cache-docker-mysql.outputs.cache-hit != 'true'
run: docker pull mysql:5.7 && mkdir -p ci/cache/docker/mysql && docker image save mysql:5.7 --output ./ci/cache/docker/mysql/mysql-5.7.tar
- name: Use MySQL Image Cache if cache hit
if: steps.cache-docker-mysql.outputs.cache-hit == 'true'
run: docker image load --input ./ci/cache/docker/mysql/mysql-5.7.tar
- name: Start containers
run: docker compose up -d
When docker compose up runs, if a service uses the Docker image mysql:5.7
image, it's going to skip downloading it.
Upvotes: 15
Reputation: 922
This question is a bit old now, but I've found the documented way of running a built image from the docker/build-push-action
in a subsequent step. In short, you have to set up a local registry.
The yaml
below has been directly copy + pasted from here.
name: ci
on:
push:
branches:
- 'main'
jobs:
docker:
runs-on: ubuntu-latest
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
-
name: Build and push to local registry
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: localhost:5000/name/app:latest
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:latest
Upvotes: 4
Reputation: 1751
This might not fully answer you question since I think there is no actual way of running your cached image.
But you can speed up your build using Github's cache, I have posted a complete tutorial about this that you can read here
Summarizing you can setup Docker buildx and then use GH cache with build-push-action:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: ivan123123/c_matrix_library:latest
cache-from: type=gha
cache-to: type=gha
Just found a reference in build-push action that might be useful to you:
https://github.com/docker/build-push-action/blob/master/docs/advanced/share-image-jobs.md
Upvotes: 3
Reputation: 4732
Edit:
As mentioned by Romain in the comments. The initial solution will pull the image at the beginning of the workflow and as such will not use the image that is built during the workflow. The only solution seem to be running docker run
yourself in the step:
- name: Run my docker image
run: >
docker run -t ivan123123/c_matrix_library:latest
...
On a side note. Using this solution might get a bit complicated if you use services in your job. In which case, the networking between your container and the service containers will be troublesome
Original answer:
To run the image you can use the following:
- name: Run my docker image
uses: docker://ivan123123/c_matrix_library:latest
with:
entrypoint: ...
args: ...
The entrypoint
and args
are optional. You can find more info here. One limitation though is that you can use any variable or context in the uses
field. You can only hardcode the name and tag of the image.
Upvotes: 1