I wish to have a GitHub Action that deploys all services in GKE if the service has been changed (either in manifest, source code or dependency). I wish that the action uses cache for the building.
The action looks like this
name: Deploy to staging
on:
push:
branches:
- develop
paths:
- "k8s-manifest/**/*"
- "lib/**"
- "services/**"
- "skaffold.yaml"
env:
PROJECT_ID: ${{ secrets.STAGING_PROJECT }}
GKE_CLUSTER: parallax-cluster
GKE_ZONE: europe-west3-a
SKAFFOLD_DEFAULT_REPO: gcr.io/${{ secrets.STAGING_PROJECT }}/parallax
SKAFFOLD_PROFILE: staging
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Check out repository on develop branch
uses: actions/checkout@v2
with:
ref: develop
- name: Install gcloud
uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: '309.0.0'
service_account_key: ${{ secrets.STAGING_SA_BUILD_AND_DEPLOY }}
project_id: ${{ secrets.STAGING_PROJECT }}
- name: Install kustomize, kubectl and skaffold
uses: daisaru11/setup-cd-tools@v1
with:
kubectl: '1.19.2'
kustomize: '3.8.4'
skaffold: '1.14.0'
- name: Configure docker
run: |
gcloud --quiet auth configure-docker
- name: Connect to cluster
run: |
gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE"
- name: Build and deploy to cluster
run: |
skaffold run \
-l skaffold.dev/run-id=LEAVE_UNCHANGED
while my skaffold.yaml looks like this
apiVersion: skaffold/v2alpha2
kind: Config
build:
local:
concurrency: 0
artifacts:
- image: python-backend
context: .
docker:
dockerfile: services/backend/Dockerfile
- image: horizon-worker
context: .
docker:
dockerfile: services/horizon_worker/Dockerfile
- image: react-frontend
context: services/frontend
profiles:
- name: staging
deploy:
kustomize:
path: k8s-manifest/staging
- name: production
deploy:
kustomize:
path: k8s-manifest/production
- name: local
deploy:
kustomize:
path: k8s-manifest/local
activation:
- kubeContext: minikube
I'd expect the above action to only rebuild affected images
The above action rebuilds all images - also the ones that already exist in the default repo and which hasn't had any changes. I'd expect that skaffold run would realize that the unaffected images wouldn't be rebuild (works if I do skaffold run from my local machine).
I have considered setting up a self-hosted runner which always will have the cache available but I imagine that my task is very common for the community so I'm probably just missing something essential here to do. What is the best practice?
Dealing with the same thing here
The easy (but bad) solution is just to tag all images with :latest, e.g.:
build:
tagPolicy:
sha256: {}
...and then using cacheFrom: image:latest
This will override your past images and loses the commit identifier, but it does make caching work - this is a non-starter for me, so I tried:
1) Manually fetching my images and then using variable substitution to dynamically add the image tag to cacheFrom, e.g.:
docker:
dockerfile: redacted
cacheFrom:
- eu.gcr.io/redacted/image:{{.CACHED_IMAGE_TAG}}
But it's not a templated field, so it can't perform the variable substitution cries in wasted time
2) What I will probably do next: manually add the :latest tag to all my images immediately after running skaffold; that way I can point cacheFrom to image:latest, while keeping the commit tags intact, e.g:
# For each image deployed by skaffold, do:
LAST_IMAGE=$(gcloud container images list-tags eu.gcr.io/**project_id**/**image** --limit=1 --format="get(tags)" | grep '[^;]*$' -o)
gcloud container images add-tag eu.gcr.io/**project_id**/**image**:$LAST_IMAGE eu.gcr.io/**project_id**/**image**:latest
Doesn't feel great but it should work in theory - hopefully someone has a better suggestion?
I run skaffold run -v debug in debug mode and found some interesting logs.
2020-10-02T23:16:13.7729379Z time="2020-10-02T23:16:13Z" level=debug msg="Could not import artifact from Docker, building instead (import of missing images disabled)"
The whole issue can be resolved by setting tryImportMissing 馃槃
apiVersion: skaffold/v2beta8
kind: Config
build:
tagPolicy:
sha256: {}
local:
concurrency: 0
tryImportMissing: true
Why isn't it true by default? We should also improve the docs around local caching.
tryImportMissing is disabled by default as it can be faster to rebuild than download, especially if you have a slow connection to your registry.
I think this issue will be fixed with #4850 providing you to save and restore your Skaffold cache file. Normally it's saved in ~/.skaffold/cache but it can be set with the --cache-file path/to/cache/file.
Great with the tryImportMissing!
How do you suggest to use cache file together with GitHub Actions for instance?
It looks like the cache action would work?
@mr-bjerre did you have a chance to try the cache-action?
Try this. Keep in mind that this doesn't work for local builds. If you delete images from your registry the cache is no longer stable.
- name: Cache skaffold image builds & config
uses: actions/cache@v2
with:
path: ~/.skaffold/
key: fixed
Thanks for the suggestions !
Question before trying it out: I only have to add that additional step suggested by @StarpTech in GitHub actions? I'll leave everything else as stated on my original post? I.e. the skaffold.yaml as well?
@mr-bjerre yes. The action must be inserted before calling skaffold.
It works !! I'm kinda mind blown by that magic - thanks a lot!
keep an eye to https://github.com/GoogleContainerTools/skaffold/issues/4889
I might have closed this a little too early. Now my images are no longer being cached. I'm getting the following message in the post cache step in my Github Action.
Post job cleanup.
Cache hit occurred on the primary key fixed, not saving cache.
My actions file look like this
name: Deploy to staging
on:
workflow_dispatch:
push:
branches:
- develop
paths:
- "k8s-manifest/**/*"
- "lib/**"
- "services/**"
- "skaffold.yaml"
env:
PROJECT_ID: ${{ secrets.STAGING_PROJECT }}
GKE_CLUSTER: parallax-cluster
GKE_ZONE: europe-west3-a
SKAFFOLD_DEFAULT_REPO: gcr.io/${{ secrets.STAGING_PROJECT }}/parallax
SKAFFOLD_PROFILE: staging
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Check out repository on develop branch
uses: actions/checkout@v2
with:
ref: develop
- name: Install gcloud
uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: "309.0.0"
service_account_key: ${{ secrets.STAGING_SA_BUILD_AND_DEPLOY }}
project_id: ${{ secrets.STAGING_PROJECT }}
- name: Install kustomize, kubectl and skaffold
uses: daisaru11/setup-cd-tools@v1
with:
kubectl: "1.19.2"
kustomize: "3.8.4"
skaffold: "1.15.0"
- name: Cache skaffold image builds & config
uses: actions/cache@v2
with:
path: ~/.skaffold/
key: fixed
- name: Configure docker
run: |
gcloud --quiet auth configure-docker
- name: Connect to cluster
run: |
gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE"
- name: Build and deploy to cluster
run: |
skaffold run \
-l skaffold.dev/run-id=LEAVE_UNCHANGED
In skaffold.yaml I do not set tryImportMissing and the tagPolicy is gitCommit.
I haven't tried this action, but from the docs it says:
Once you create a cache, you cannot change the contents of an existing cache but you can create a new cache with a new key.
I suspect you need to make your cache key have a unique suffix, like the git commit SHA, and use restore-keys? Untested:
key: fixed-${{ github.sha }}
restore-keys: |
fixed-${{ github.sha }}
fixed-
fixed was a bad example. If you hit the cache with the same key the cache isn't updated. In order to create a good cache key, we need something like a lock file but we haven't because the artifact is generated after each build.
@briandealwis exactly. The cache behind fixed- will be updated with the most recent and fixed-${{ github.sha }} ensure that cache is updated after every run.
The suggestion from @briandealwis seems to be working perfectly. Thanks.
Most helpful comment
I run
skaffold run -v debugin debug mode and found some interesting logs.The whole issue can be resolved by setting
tryImportMissing馃槃Why isn't it true by default? We should also improve the docs around local caching.