แก้ปัญหาการ cache ไฟล์ใน Gitlab-ci

DevOps Aug 16, 2019

ในการทำระบบ CI/CD ให้กับแอพพลิเคชั่นของภาษาที่ต้องมีการติดตั้ง Dependencies ต่างๆ เช่น Nodejs (node_module) หรือ PHP (vender) เป็นต้น ส่วนใหญ่ผมจะแยกเป็น 2 Job คือ job สำหรับติดตั้ง dependencies และ job ที่จะนำ dependencies นั้นไปใช้ เช่น

stages:
  - build
  - release

#
# Install node_module
#
npm:
  stage: build
  image: node:10-alpine
  script:
    - '[ -f package-lock.json ] && rm package-lock.json'
    - npm i
  cache:
    key: npm-cache
    paths:
      - node_modules
#
# Create docker image
#
docker-image:
  stage: release
  image: docker:latest
  services:
    - docker:18-dind
  cache:
    key: npm-cache
    paths:
      - node_modules
    policy: pull
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:master .
    - docker push $CI_REGISTRY_IMAGE:master

จากโค็ดด้านบน

  • npm job จะทำการติดตั้ง node_modules แล้วจากนั้นก็ทำการ upload เก็บไว้โดยใช้เป็นชื่อ npm-cache
  • docker-image job ก็จะทำการเอา npm-cahce key จากนั้นด้านโหลดลงมาเป็น node_modules อีกครั้ง แล้วผมก็ทำการ build เป็น docker image เก็บไว้ที่ registry ของ gitlab

เมื่อมีการเรียก pipeline นี้อีกครั้ง

  • npm job ก็จะทำการโหลดไฟล์ npm-cache key มา เราก็จะได้ node_modules แล้วค่อยติดตั้ง npm i อีกครั้ง ซึ่งการติดตั้งครั้งนี้จะเร็วขึ้นเพราะเรามี node_modules ไว้แล้ว เมื่อจบก็จะทำการ upload เก็บไว้โดยใช้เป็นชื่อ npm-cache อีกครั้ง
  • docker-image job ก็จะทำการเอา npm-cahce key โหลดลงมาเป็น node_modules อีกครั้ง แล้วก็ทำงานตาม script ไปเรื่อยๆ

การทำ caching แบบนี้จะทำให้ pipeline เราทำงานเร็วขึ้นมา เพราะไม่จำเป็นต้องติดตั้ง node_modules ทุกครั้ง

ปัญหาของ cache ใน gitlab-ci.yml

แต่เดียวก่อน!!! จากที่ผมใช้งานในบ้างครั้ง สมมติใน package.json เรามี dependencies 2 ตัว pipeline เราก็ทำงานปกติไปตาม Step คือ

  • ติดตั้ง node_modules
  • อัพโหลดเก็บไว้
  • job ต่อมาโหลดมาใช้งาน

คราวนี้เรามีการเพิ่ม dependencies เป็น 4 ตัว pipeline ในขั้นตอน npm i ก็จะติดตั้งเฉพาะ dependencies ที่เพิ่มเข้ามา แล้วอัพโหลด node_modules เก็บไว้เหมือนเดิม

ปัญหามันอยู่ช่วงนี้ กล่าวคือ job ต่อมามันดันไปโหลด node_modules ของเก่าที่มี dependencies 2 ตัว มาใช้ แล้วพอเราเอาแอพพลิเคชั่นไปใช้งานจริงขึ้น Server ก็จะเจอ error ว่า dependencies ไม่ครบทันที

ซึ่งการเรียก cache ไฟล์ผิดนี้เกิดขึ้นบ่อยมาก ถ้ามันเกิดแบบนี้ผมก็จะต้องเรียก pipeline ตัวนี้ซ้ำๆ สักสองถึงสามรอบถึงจะโอเค

วิธีแก้ปัญหา cache ใน gitlab-ci

ผมจะใช้ artifacts มาช่วยในการเก็บไฟล์ dependencies ต่างๆ แล้วให้ Job ที่ต้องการไปเรียกใช้ เช่น

stages:
  - build
  - release

#
# Install node_module
#
npm:
  stage: build
  image: node:10-alpine
  script:
    - '[ -f package-lock.json ] && rm package-lock.json'
    - npm i
  cache:
    key: npm-cache
    paths:
      - node_modules
  artifacts:
    paths:
      - node_modules
    expire_in: 1 days
    when: always
#
# Create docker image
#
docker-image:
  stage: release
  image: docker:latest
  services:
    - docker:18-dind
  dependencies:
    - npm
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:master .
    - docker push $CI_REGISTRY_IMAGE:master

ใน npm job ผมจะเพิ่ม artifacts เข้ามาเก็บ node_modules ไว้ด้วย และก็ให้มัน cache เก็บไว้แบบเดิม

...
...
artifacts:
  paths:
    - node_modules
  expire_in: 1 days
  when: always
...
...

ต่อมา docker-image job จากแต่ก่อนจะโหลด cache มาใช้ คราวนี้ก็ให้โหลด artifacts มาใช้เลย

...
...
dependencies:
  - npm
...
...

เพียงเท่านี้ก็จะจบปัญหาเรื่องการนำ dependencies มาไม่ครบแล้ว ส่วนใครมีวิธีอื่นที่ดีกว่านี้ก็แนะนำมาได้นะครับ

Arnon Kijlerdphon

IT manager & DevOps @Twin Synergy Co.,Ltd

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.