Cache docker-compose run ใน Github Actions

Chokchai Phatharamalai
odds.team
Published in
3 min readApr 4, 2022
Photo by Kevin Ku on Unsplash

ปรกติทุกครั้งที่เรา build ของด้วย Github actions เราจะไม่ได้ run บนเครื่องเดิม ทำให้ Docker cache ที่เราเคย build ไว้หายหมด ไม่เหมือนกันกับตอนเรา run บนเครื่องตัวเองที่ layer ที่เคยถูก build ไว้จะถูก cache ทำให้ build ครั้งถัด ๆ ไปเร็วขึ้น

ถ้าเป็นการ build image แล้ว push ไปบน registry เราสามารถใช้ action docker/build-push-action@v2 ได้ ซึ่งจะมี option ให้ใส่ cache-from และ cache-to อยู่แล้ว ง่ายมาก ไม่มีอะไร จะเลือก cache แบบ local, registry หรือไปเก็บใน Github cache API ก็ได้

มีนิดนึงคือตอนนี้ ถ้าใช้ local cache จะมีปัญหาคือ cache บวมไปเรื่อย ๆ ตอนนี้เค้าแก้โดยการ ลบ cache เก่าเอาเอง

ถ้าเป็น docker-compose run หล่ะ?

ถ้าเป็นคำสั่ง docker-compose การ build มันจะใช้คำสั่ง Docker build ที่อยู่บนเครื่องเอง ไม่ได้ใช้ docker/build-push-action@v2 ซึ่งมัน build ผ่าน Buildx อีกที เราเลยไม่ได้ใช้ประโยชน์จาก cache ที่อยู่ใน docker/build-push-action@v2 ตรง ๆ ได้

ด้านล่างจะเล่า 3 ทางที่ผมลองพยายามทำให้มัน cache ได้ ซึ่ง 2 ทางแรก ทำแล้วไม่ work ถ้าใครอยากดูเฉลยสามารถกระโดดข้ามไปอ่านทางสุดท้ายเลยก็ได้ครับ

Mount node_modules ออกมาเก็บไว้

ในกรณีของผม สิ่งที่ทำให้การ run e2e test ช้าคือต้องมาลง cypress ใหม่ทุกรอบ ผมเลยคิดว่าถ้าผม mount node_modules folder ออกมาเก็บไว้ได้ พอ run ครั้งหน้า ก็ไม่ต้องลงใหม่แล้ว เพราะ node_modules มี cypress ที่ลงจากรอบที่แล้วเก็บไว้แล้ว

เดิมที compose file ผมหน้าตาเป็นแบบนี้

version: "3.3"

services:
api:
build:
context: ../../api
command: /app/scripts/start_api_with_test_fixture
e2e:
build:
context: ../../web2
dockerfile: Dockerfile.test
command: yarn test:e2e --headless
volumes:
- '../../web/tests/e2e/screenshots:/app/tests/e2e/screenshots'
- '../../web/tests/e2e/videos:/app/tests/e2e/videos'
environment:
- VUE_APP_BASE_API=http://api:3000
depends_on:
- api

สิ่งที่ผมเพิ่มเข้าไปคือ mount volumn เพิ่มเข้าไปตอน run e2e แต่ผลปรากฏว่าไม่ work เพราะว่าขั้นตอนการ install node_modules มันเกิดขึ้นตอน build ไม่ใช่ตอน run พอผม mount volumn เข้าไปตอน run เลยทำให้ node_modules มันกลายเป็น folder เปล่า ก็เลยแตก

สามารถดู commit speed up e2e tests on local poorprogrammer/cfo@2c71e3d ในส่วนอ้างอิงเพิ่มเติมได้นะครับ

แผนแรกไม่เวิร์ค ผมก็เลยไปแผน 2

ใช้ docker/build-push-action@v2 build cache ก่อน

ผมไปเติมใน file build.yml ของ Github actions เลย ให้มันใช้ docker/build-push-action@v2 ในการ build image ที่ต้อง test ก่อน เพราะคำสั่งนี้สามารถ cache ของได้ เสร็จแล้วค่อยสั่ง docker-compose run e2e test ตามด้านล่าง

- name: Build docker cache for e2e tests
uses: docker/build-push-action@v2
with:
context: "{{defaultContext}}:web2"
file: "Dockerfile.test"
cache-from: type=gha
cache-to: type=gha,mode=max

- name: run e2e tests
run: docker-compose -f compose/test/docker-compose.yml run e2e

ใครอยากรู้รายละเอียดเพิ่มเติม ดู commit Cache with Github API poorprogrammer/cfo@03bbfd8 ด้านล่างเอานะ

สรุปไม่เวิร์คอยู่ดี เพราะ cached ที่เกิดขึ้นจาก docker/build-push-action@v2 มันไม่ถูก reuse ตอนสั่ง docker-compose ผมเดาว่ามันใช้ builder คนละตัวกัน เพราะอีกตัวใช้ Buildx

สูงสุดสู่สามัญ

กลับมาวิธีบ้าน ๆ คือสั่ง build บนเครื่องตัวเอง แล้ว push image นี้ไปเก็บไว้ใน registry แล้วค่อยใช้ docker-compose มาดึง image ที่ install cypress เสร็จแล้วไปใช้แทน

หน้าตา Dockerfile ก็จะต่างกันประมาณนี้

ข้อเสียคือทุกครั้งที่มีการเปลี่ยนแปลง package ที่ต้องใช้ ต้องมา build cache ใหม่แล้ว push ขึ้น registry เอง

แต่มันเวิร์คนะ ใครสนใจไปดู commit try to optimize build time on github actions poorprogrammer/cfo@a685773 ด้านล่างเพิ่มได้

ทำเสร็จแล้วเร็วขึ้นแค่ไหน?

เกือบนาทีนึง 555 แต่ หลาย ๆ build ก็เยอะอยู่นะ

ก่อน optimize 4.30
หลัง optimitze 3.47

หวังว่าจะเป็นประโยชน์ครับ

อ้างอิง

--

--