How to run cached Docker image in Github Action?
Asked Answered
M

5

20

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
Misspend answered 1/3, 2021 at 11:36 Comment(4)
how do you know that your action did not use cashe? any errors ?Nonjoinder
The action uses the cache but I don't know how do I run the cached image? Would docker run ivan123123/c_matrix_library run the cached image or if it would pull the image from Dockerhub? My goal is to speed up CI workflow.Misspend
This doesn't work on windows :/Laparotomy
@Lance, Does GitHub runs on Windows? I am curious about your remark.Melissamelisse
S
13

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.

Sierra answered 22/9, 2022 at 22:58 Comment(0)
C
4

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
Cirsoid answered 16/9, 2022 at 21:32 Comment(0)
T
3

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

Edit

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

Transsonic answered 25/9, 2021 at 9:1 Comment(2)
Thanks for answering, but I think I've already posted a workaround in the EDIT section of my question which is similar to your 'solution'.Misspend
It is similar... but differences there are huge. Note cache arguments are pretty different. You can go to the build-push docs and spend some time reading the explanation thereTranssonic
F
1

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.

Foreandaft answered 1/3, 2021 at 12:51 Comment(8)
Would this task run the cached image or would it firstly pull the image from Dockerhub?Misspend
The docker images referenced by a "uses" are pulled from DockerHub, and this is done at the very beginning while setting up the job, so I don't think you can cache it. If you want to use a docker image you have just built, you need to explicitely invoke docker run.Ansell
@RomainPrévost yeah I just realized. I am sorry Ivan but I think my answer is not the right solution. Like Romain mentioned it is pulling the image at the beginning of the workflow.Foreandaft
@Foreandaft Yes I can confirm it now once I tested it in GitHub integration. I'll edit my question with a solution I found in the meantime.Misspend
I tried to cache a folder where the built Docker image was saved and then run it like you would run any other Docker image, but I can't find the path for some reason.Misspend
@IvanVnucec maybe you can try docs.docker.com/engine/reference/commandline/save to save the image wherever you want it.Foreandaft
I might try and once I have working solution, I will edit my question. I think that the docker/build-push-action@v2 Github Action does saving and loading implicitly (see my edited question).Misspend
I also had to deal with some issues related to this action in the past and I think that if you try to do more advanced stuff and need more control, you will be better off running docker commands directly or build your own composite actions.Foreandaft
L
1

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 :/

Laparotomy answered 23/1 at 11:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.