ข้ามไปยังเนื้อหาหลัก
ลด 50% ทุกแพลน เวลาจำกัด เริ่มต้นที่ $2.48/mo
13 min left
เครื่องมือนักพัฒนาและ DevOps

วิธีวาง Visual Regression Testing แบบ self-host ไว้ในไปป์ไลน์ CI ของคุณ

S โดย Sajjad 13 นาทีในการอ่าน
Self-hosted visual regression testing running in a CI pipeline with BackstopJS and Docker

เทสต์ visual regression ของคุณผ่านฉลุยบนแล็ปท็อป พอ push ขึ้นไป ไปป์ไลน์รัน แล้วเทสต์ชุดเดิมกลับทำให้เบราว์เซอร์ล่มแบบเงียบๆ หรือไม่ก็แจ้ง “ความเปลี่ยนแปลง” ห้าสิบจุดที่จริงแล้วเป็นแค่ความต่างของ anti-aliasing โค้ดคุณไม่ได้เปลี่ยนอะไรเลย แต่ CI ไม่เห็นด้วย

ช่องว่างระหว่าง “รันบนเครื่องได้” กับ “รันบน CI ได้” เกือบทุกครั้งเป็นปัญหาของอินฟราสตรักเจอร์ ไม่ใช่ปัญหาของเทสต์ การทำ visual regression testing แบบ self-host ในไปป์ไลน์ CI ให้เชื่อถือได้ต้องต่อสี่ชิ้นส่วนเข้าด้วยกันให้ถูก: รันเนอร์ CI ที่ใช้ Docker executor, คอนเทนเนอร์ที่ตั้งค่าไว้ไม่ให้ headless Chromium ใช้ shared memory จนหมด, เครื่องมือเทียบภาพที่ตั้งให้ปล่อยรายงานที่ CI อ่านได้ และ VPS ที่มีแรมพอกับที่เบราว์เซอร์ headless ใช้จริง

คู่มือนี้จะประกอบสแตกนั้นตั้งแต่ต้นจนจบ เมื่อจบบทความคุณจะได้ .gitlab-ci.yml ที่ใช้งานได้จริง (หรือของเทียบเท่าบน Gitea), สภาพแวดล้อม Docker ที่หยุด Chromium ไม่ให้ล่ม, BackstopJS ที่ปล่อยรายงาน JUnit ให้ไปป์ไลน์อ่านได้ และ VPS ขนาดพอเหมาะสำหรับรันทั้งหมดนี้

TL;DR (สรุปย่อ)

  • รันเบราว์เซอร์ใน Docker image เดียวกันทั้งบนเครื่องและบน CI ผลบวกลวงส่วนใหญ่มาจากความต่างของฟอนต์และการเรนเดอร์ระหว่างสภาพแวดล้อม พอ pin อิมเมจไว้ ปัญหาก็หายไป
  • แก้ค่าเริ่มต้น /dev/shm ที่ 64MB ขนาดหน่วยความจำแบบแชร์ค่าเริ่มต้นของ Docker ทำให้ renderer ของ Chromium ขาดหน่วยความจำจนล่มแบบเงียบๆ บน CI ให้ตั้ง shm_size: '2gb' ใน docker-compose.yml หรือส่ง --disable-dev-shm-usage ให้ Chromium
  • ใช้ BackstopJS เป็นเครื่องมือหลักที่ self-host ได้ มันใช้ไลเซนส์ MIT (v6.3.25) ออกแบบมาให้ใช้ Docker เป็นหลักผ่านแฟล็ก --docker และปล่อยรายงาน JUnit ได้ ถ้าคุณใช้ Playwright อยู่แล้ว toHaveScreenshot() ที่มีมาในตัวก็เป็นจุดเริ่มต้นที่ดีโดยไม่ต้องติดตั้งอะไรเพิ่ม
  • ตั้งเป้าที่ 4GB RAM เป็นค่าพื้นฐาน เผื่อหน่วยความจำไว้เยอะๆ ในการรัน CI จริง จ็อบถ่ายภาพหน้าจอด้วย Chromium อาจกินแรมตอนแคปเจอร์ทั้งหน้ามากกว่าตอนอยู่เฉยๆ หลายเท่า ดังนั้น 4GB RAM จึงเป็นค่าพื้นฐานที่ปลอดภัยกว่าสำหรับจ็อบหนึ่งถึงสองจ็อบแบบเรียงลำดับหรือขนานเบาๆ

สิ่งที่คู่มือนี้ไม่ครอบคลุม

นี่คือคู่มือประกอบระบบสำหรับอินฟราสตรักเจอร์ที่คุณตัดสินใจจะตั้งขึ้นมาอยู่แล้ว มีบางเรื่องที่เราตั้งใจไม่พูดถึง:

  • นี่ไม่ใช่บทเปรียบเทียบเครื่องมือแบบเต็ม คู่มือนี้โฟกัสที่สแตกซึ่งคุณดูแลได้จริงในสภาพแวดล้อม CI แบบ self-host: BackstopJS, Docker และรันเนอร์ของคุณเอง
  • ไม่ได้ครอบคลุมการ self-host Argos Argos เป็นโอเพนซอร์ส แต่ตัวโปรดักต์และเอกสารสาธารณะเน้นไปที่แอป Argos แบบโฮสต์ให้และการเชื่อมต่อกับ CI มากกว่าเส้นทาง self-host ที่ใช้งานจริงได้ง่ายๆ สำหรับเป้าหมายของคู่มือนี้ BackstopJS จึงเป็นตัวอย่างที่ปลอดภัยกว่า
  • ไม่ได้แนะนำ Lost Pixel โปรเจกต์นั้นถูกอาร์ไคฟ์ไปเมื่อเมษายน 2026 ให้ใช้ BackstopJS สำหรับงานใหม่
  • ไม่ได้กลับมาถกเรื่อง self-host กับ SaaS แบบมีคนดูแลอีกรอบ ถ้าคุณมาถึงตรงนี้ แปลว่าคุณตัด Percy หรือ Chromatic ออกไปแล้ว

ข้อกำหนดเบื้องต้น

คุณต้องเตรียมของไม่กี่อย่างให้พร้อมก่อนพิมพ์คำสั่งแรก:

  • รันเนอร์ CI แบบ self-host หรือแผนที่จะติดตั้งขึ้นมา (มีอธิบายด้านล่าง)
  • ติดตั้ง Docker บนโฮสต์ที่รันเนอร์อยู่แล้ว
  • โปรเจกต์ Node.js ที่มีเป้าหมายสำหรับเทสต์อยู่แล้ว (URL ของแอปที่รันอยู่ หรือชุดเส้นทางของคอมโพเนนต์ที่จะแคปเจอร์)
  • สิทธิ์เข้าเชลล์ของ VPS หรือโฮสต์ที่รันเนอร์อยู่

ทำไมเทสต์ภาพผ่านบนเครื่องแต่พังบน CI

เบราว์เซอร์ที่เรนเดอร์ baseline ของคุณบน macOS ไม่ใช่เบราว์เซอร์ตัวเดียวกับที่เรนเดอร์ภาพเปรียบเทียบในคอนเทนเนอร์ CI บน Linux เอกสารของ Playwright เองพูดตรงๆ ว่า การเรนเดอร์ของเบราว์เซอร์เปลี่ยนได้ตาม OS ของโฮสต์ เวอร์ชัน การตั้งค่า ฮาร์ดแวร์ แหล่งจ่ายไฟ โหมด headless และปัจจัยอื่นๆ คำแนะนำของเขาก็ตรงไม่แพ้กัน: ถ้าอยากได้ภาพหน้าจอที่คงที่ ให้รันเทสต์ในสภาพแวดล้อมเดียวกับที่ สร้างภาพหน้าจอ baseline ขึ้นมา.

คำแนะนำประโยคเดียวนั้นคือเหตุผลที่การตั้งค่าทั้งหมดนี้มีอยู่ ความไม่ตรงกันสามอย่างอธิบายความล้มเหลวลวงได้เกือบทั้งหมด:

  • ความต่างของฟอนต์และ anti-aliasing เครื่องพัฒนาของคุณกับคอนเทนเนอร์ CI มาพร้อมแพ็กเกจฟอนต์และการเรนเดอร์ระดับ sub-pixel ที่ต่างกัน หน้าที่มีข้อความเยอะจึงขึ้นความต่างทุกรอบแม้ไม่มีอะไรเปลี่ยน
  • การเรนเดอร์แบบ headless กับแบบมีหน้าต่าง เบราว์เซอร์ที่มีหน้าต่างกับแบบ headless อาจจัดเลย์เอาต์หน้าเดียวกันต่างกันเล็กน้อย
  • shared memory หมด อันนี้คือตัวเงียบ ค่าเริ่มต้นของ /dev/shm ใน Docker คือ 64MB ส่วน headless Chromium ใช้ shared memory สำหรับโปรเซส renderer ของมัน พอใช้จนหมด Chromium ก็ล่มโดยไม่มีข้อความ error ที่ช่วยอะไรได้เลย จ็อบของคุณก็แค่ตายหรือค้างไป

สองข้อแรกแก้ได้ด้วยการ pin Docker image ตัวเดียวไว้ใช้ทั้งตอนสร้าง baseline และตอนเปรียบเทียบ ข้อที่สามแก้ในหัวข้อการตั้งค่า Docker ด้านล่าง แก้ครบทั้งสามข้อแล้วเทสต์ที่เคย “ไม่นิ่งบน CI” ก็จะให้ผลแน่นอนทุกครั้ง

Three reasons visual regression tests pass locally but fail in CI: font and anti-aliasing differences between the dev machine and the CI container, headless versus headed rendering differences, and shared-memory exhaustion from Docker's 64MB /dev/shm default crashing headless Chromium

เลือกเครื่องมือ: BackstopJS หรือการเทียบภาพในตัวของ Playwright

ถ้าคุณใช้ Playwright ทำเทสต์ end-to-end อยู่แล้ว ทางที่เร็วที่สุดคือ assertion toHaveScreenshot() ที่มีมาในตัว: ไม่ต้องเพิ่ม dependency, เทียบภาพระดับพิกเซล และมี baseline ที่ต่อท้ายชื่อแพลตฟอร์มให้เลย นี่คือจุดจบที่สมเหตุสมผลสำหรับชุดเทสต์เล็กๆ และสำหรับทีมที่มีหน้าจอราวไม่ถึงห้าสิบหน้าและไม่มีไลบรารีคอมโพเนนต์ร่วม เท่านี้ก็อาจพอแล้ว

จุดที่มันไปต่อไม่ได้คือเวิร์กโฟลว์การรีวิว การเทียบภาพของ Playwright ดูแค่พิกเซลและไม่มี UI สำหรับรีวิวใน PR การอนุมัติชุดความเปลี่ยนแปลงที่ถูกต้องจึงหมายถึงต้องสร้าง snapshot ใหม่ด้วยมือ พอจำนวน baseline เพิ่มขึ้น วงจรอนุมัติแบบแมนนวลนี้ก็เริ่มทรมาน

BackstopJS คือเครื่องมือเฉพาะทางที่สร้างมาเพื่อวงจรนั้นโดยตรง ใช้ไลเซนส์ MIT (ปัจจุบัน v6.3.25) เน้น Docker เป็นหลักผ่านแฟล็ก --docker และปล่อย JUnit XML ที่รันเนอร์ CI อ่านได้เอง เวิร์กโฟลว์ baseline ของมัน (สร้าง reference, เทสต์เทียบกับมัน, approve ความต่างที่ยอมรับให้กลายเป็น baseline ใหม่) คือโมเดลการทำงานที่ระบบ VRT แบบ self-host ส่วนใหญ่ลงเอยด้วยความต้องการแบบนั้น ตลอดคู่มือนี้เราจะใช้ BackstopJS เป็นตัวอย่างจริง

มีเครื่องมือสองตัวที่คุณอาจเห็นคนแนะนำที่อื่นแต่ไม่อยู่ในบทนี้: Argos ไม่ถูกพูดถึงเพราะตัวโปรดักต์สาธารณะของมันเน้นแอปแบบโฮสต์ให้และการเชื่อมต่อกับ CI ส่วน Lost Pixel ถูกอาร์ไคฟ์ไปแล้ว BackstopJS จึงเป็นตัวอย่างที่ปลอดภัยกว่าสำหรับระบบ self-host ที่ใช้งานได้จริง

แกนBackstopJSPlaywright toHaveScreenshot()
เวิร์กโฟลว์รีวิวใน PRมีรายงานในตัวและคำสั่ง approveต้องสร้าง snapshot ใหม่ด้วยมือ
การจัดการ baselineคำสั่ง reference / test / approveไฟล์ snapshot แยกตามเทสต์
การเรนเดอร์ที่ทำให้เหมือนกันด้วย Dockerแฟล็ก --dockerนำ image ของคุณเองมา
ภาระการติดตั้งเป็น dependency แยกต่างหากมีอยู่แล้วถ้าคุณใช้ Playwright

หากอยากเห็นการเปรียบเทียบเชิงลึกที่ครอบคลุมเครื่องมือและแง่มุมมากกว่านี้ บทเปรียบเทียบ BackstopJS, Argos และ Lost Pixel ของเราครอบคลุมการตัดสินใจเลือกทั้งหมด

ติดตั้งรันเนอร์ CI แบบ self-host บน VPS

ชิ้นส่วนที่ทำให้การเรนเดอร์คงเส้นคงวาจริงๆ คือ Docker executor: รันเนอร์จะรันทุกจ็อบในคอนเทนเนอร์ที่คุณกำหนดเอง แปลว่าสภาพแวดล้อมของเบราว์เซอร์เหมือนกันทุกครั้ง เลือกแพลตฟอร์มรันเนอร์ตามว่าคุณอยากให้มันทำอะไรได้อีกมากแค่ไหน

GitLab CE แบบ self-host คือตัวเลือกที่หนักกว่า: มีทั้ง repository, CI/CD, registry, issue และ merge request รวมอยู่ในอินสแตนซ์เดียว ส่วนสำคัญสำหรับคู่มือนี้คือ Docker executor ของ GitLab Runner ซึ่งทำให้ทุกจ็อบ visual regression รันอยู่ในอิมเมจคอนเทนเนอร์ที่ pin ไว้ตัวเดียวกันทุกครั้ง GitLab, Gitea, Jenkins, Forgejo, Portainer และ Docker ล้วนติดตั้งแบบคลิกเดียวได้จาก มาร์เก็ตเพลส Cloudzy ซึ่งย่นเวลาติดตั้งลงได้มาก

Gitea ที่ใช้ act-runner คือทางเลือกที่เบากว่า Gitea เป็นบริการ Git ที่เขียนด้วย Go พร้อมเอนจินเวิร์กโฟลว์ที่เข้ากันได้กับ GitHub Actions (ผ่าน act-runner) ดังนั้นถ้าทีมคุณคุ้นกับซินแท็กซ์ของ Actions อยู่แล้ว นี่คือรันเนอร์เล็กและเร็วที่ตั้งขึ้นมาได้ไว

ไม่ว่าจะเลือกอันไหน เป้าหมายของการตั้งค่าก็เหมือนกัน: ได้รันเนอร์ที่รับจ็อบและรันมันด้วย Docker executor ทุกอย่างที่ตามมา (การตั้งค่าคอนเทนเนอร์, BackstopJS, ไปป์ไลน์ตัวอย่าง) ล้วนถือว่ามี executor นั้นอยู่แล้ว

ตั้งค่า Docker ไม่ให้ Chromium ล่ม

นี่คือความล้มเหลวที่กินเวลาคนมากที่สุด Docker เมานต์ /dev/shm ไว้ที่ 64MB โดยดีฟอลต์ headless Chromium ใช้พาร์ทิชัน shared memory นั้นสำหรับโปรเซส renderer และตอนแคปเจอร์ภาพทั้งหน้ามันต้องการมากกว่า 64MB หลายเท่า พอใช้จนหมด renderer ก็ตาย และมักไม่มีข้อความ error ที่ชี้ไปที่ shared memory เลย จ็อบก็ค้าง หมดเวลา หรือรายงานว่าเบราว์เซอร์ล่มแบบกลางๆ

มีวิธีแก้สองทาง และใช้ทางไหนก็ได้

วิธีที่ A: เพิ่มขนาด shared memory ใน Compose คีย์ shm_size กำหนดขนาดพาร์ทิชัน /dev/shm ของคอนเทนเนอร์ เอกสารอ้างอิงไฟล์ Docker Compose ยืนยันว่า shm_size ใช้กำหนดขนาดของ shared memory ที่คอนเทนเนอร์ของเซอร์วิสใช้ได้ ตั้งค่ามันในบล็อกของเซอร์วิส:

# docker-compose.yml
services:
  vrt:
    image: backstopjs/backstopjs:6.3.25
    shm_size: '2gb'          # override the 64MB default
    volumes:
      - ./:/src
    working_dir: /src

วิธีที่ B: สั่ง Chromium ไม่ให้ใช้ /dev/shm เลย แฟล็ก --disable-dev-shm-usage ทำให้ Chromium เขียนไฟล์ shared memory ลง /tmp แทนพาร์ทิชัน shared memory ส่งมันไปในอาร์กิวเมนต์ตอนเปิดเบราว์เซอร์ ในไฟล์คอนฟิก backstop.json คือ engineOptions ส่วนใน Playwright คือ launchOptions:

// backstop.json (fragment)
{
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--disable-dev-shm-usage", "--no-sandbox"]
  }
}

วิธี A สะอาดกว่าเมื่อคุณคุมไฟล์ Compose ได้ ส่วนวิธี B เป็นทางเลือกที่ย้ายไปไหนก็ใช้ได้เมื่อคุณแก้ได้แค่แฟล็กตอนเปิดเบราว์เซอร์ ใช้ทั้งสองอย่างพร้อมกันก็ไม่เสียหาย

เคล็ดลับ: ใช้ Docker image ตัวเดียวกันเป๊ะทั้งบนเครื่องและบน CI BackstopJS ให้สิ่งนี้ฟรีผ่านแฟล็ก --docker ซึ่งรันการแคปเจอร์ในอิมเมจอ้างอิงที่ pin ไว้ ทำให้เครื่องคุณกับรันเนอร์เรนเดอร์เหมือนกัน นี่คือสิ่งที่กำจัดผลบวกลวงจากฟอนต์และ anti-aliasing: pin อิมเมจครั้งเดียวแล้วเลิกไล่จับความต่างลวงๆ

Two fixes for headless Chromium crashing in Docker CI: raising the container's shared memory with shm_size set to 2gb in docker-compose.yml, or passing the --disable-dev-shm-usage flag so Chromium writes shared-memory files to /tmp instead of the 64MB /dev/shm partition

ตั้งค่า BackstopJS สำหรับ CI

BackstopJS สร้างรายงาน JUnit XML ที่รันเนอร์ CI อ่านเพื่อตัดสินว่าบิลด์ผ่านหรือไม่ ซึ่งเป็นเหตุผลทั้งหมดของการต่อมันเข้ากับไปป์ไลน์แทนที่จะรันเองด้วยมือ ติดตั้งแล้วสร้างไฟล์คอนฟิกเริ่มต้น:

npm install --save-dev backstopjs
npx backstop init

จากนั้นชี้ backstop.json ไปที่หน้าที่คุณอยากแคปเจอร์ แล้วเปิดรายงาน CI:

// backstop.json (fragment)
{
  "id": "my_app_vrt",
  "viewports": [
    { "label": "desktop", "width": 1440, "height": 900 }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://app:3000/",
      "selectors": ["document"]
    }
  ],
  "report": ["CI"],
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--disable-dev-shm-usage", "--no-sandbox"]
  }
}

การตั้งค่ารายงาน CI คือสิ่งที่ทำให้เกิดไฟล์ JUnit XML ใช้แฟล็ก --docker เวลารันคำสั่งเหล่านี้บนเครื่องของคุณเอง ส่วนภายในจ็อบ CI ที่ใช้อิมเมจ backstopjs/backstopjs อยู่แล้ว ให้รัน backstop test ตรงๆ ไปเลย เวิร์กโฟลว์ baseline มีสามคำสั่ง:

npx backstop reference --docker   # capture the approved baseline
npx backstop test --docker        # run the comparison, emit the report
npx backstop approve              # accept the current diffs as the new baseline

ข้อควรระวังหนึ่งเรื่องการจัดการ baseline: ทุกครั้งที่ UI เปลี่ยนแปลงอย่างถูกต้อง คุณต้องรัน approve เพื่อรับรองภาพหน้าจอชุดใหม่ พอเกินราวร้อยซีนาริโอ ขั้นตอนอนุมัตินี้กลายเป็นต้นทุนการดำเนินงานจริง: ต้องมีคนไล่ดูความต่างแล้วตัดสินว่าอันไหนตั้งใจให้เป็น และภาระนี้โตขึ้นตามจำนวนคนในทีมที่ใช้งาน งานดูแลตรงนี้คือราคาที่แท้จริงของการทำ visual regression testing ในสเกลใหญ่ และควรเผื่อไว้ในแผนก่อนจะทุ่มกับชุดเทสต์ขนาดใหญ่

ตัวอย่างคอนฟิกไปป์ไลน์ CI ที่ใช้งานได้จริง

ไปป์ไลน์ทำงานเป็นสองส่วนตามตรรกะ: สร้าง baseline หนึ่งครั้ง (หรือเมื่อต้องการ) แล้วเทียบกับมันทุกครั้งที่มีการเปลี่ยนแปลง ตัวอย่างด้านล่างเป็นคอนฟิก GitLab CI ที่ใช้ Docker executor และใส่วิธีแก้ shared memory ไว้แล้วผ่านแฟล็กตอนเปิดเบราว์เซอร์ใน backstop.json

# .gitlab-ci.yml
stages:
  - visual-regression

visual_regression:
  stage: visual-regression
  image:
    name: backstopjs/backstopjs:6.3.25
    entrypoint: [""]
  script:
    - backstop test
  artifacts:
    when: always
    reports:
      junit: backstop_data/ci_report/xunit.xml
    paths:
      - backstop_data/bitmaps_test
      - backstop_data/html_report
    expire_in: 1 week

บรรทัด junit ส่งรายงานให้ GitLab เพื่อให้ความล้มเหลวโผล่ใน UI ของ merge request ส่วนบรรทัด paths เก็บบิตแมปของความต่างไว้ให้คุณดูได้ว่าอะไรเปลี่ยนไปจริงๆ บน Gitea ที่ใช้ act-runner ขั้นตอนเดียวกันแปลงเป็นเวิร์กโฟลว์สไตล์ Actions ได้แบบนี้:

# .gitea/workflows/visual-regression.yaml
name: Visual Regression
on: [push, pull_request]

jobs:
  vrt:
    runs-on: ubuntu-latest
    container:
      image: backstopjs/backstopjs:6.3.25
      options: --shm-size=2gb          # raise /dev/shm for the job container
    steps:
      - uses: actions/checkout@v4
      - run: backstop test

สังเกตออปชันคอนเทนเนอร์ --shm-size=2gb ในเวิร์กโฟลว์ของ Gitea นั่นคือวิธีแก้ shared memory แบบเดียวกับวิธี A แต่ใช้ที่ระดับคอนเทนเนอร์ของจ็อบ ในกรณีที่ไม่มีไฟล์ Compose แยกให้แก้

เลือกขนาด VPS สำหรับ headless Chromium

ตัวจำกัดตรงนี้คือ RAM ไม่ใช่ CPU การใช้ CPU ของ headless Chromium มาเป็นช่วงพุ่ง (พุ่งตอนแคปเจอร์แล้วนิ่งระหว่างรอบ) จำนวนคอร์ระดับกลางๆ จึงตามทันสบาย หน่วยความจำต่างหากที่เป็นเพดานตัดสินว่าคุณรันกี่จ็อบพร้อมกันได้ โปรเซส headless Chromium อยู่ที่ราว 300–500MB ตอนว่าง และไต่ขึ้นไป 1–2GB ตอนแคปเจอร์ภาพทั้งหน้า ตัวกำหนดขนาดของคุณจึงเป็น RAM ต่อหนึ่งจ็อบที่รันพร้อมกัน

RAMเหมาะกับ
2GBขั้นต่ำแบบเฉียดฉิว: จ็อบเดียวแบบเรียงลำดับ
4GBค่าพื้นฐานที่แนะนำ: จ็อบพร้อมกัน 1–2 จ็อบ
8GBจ็อบพร้อมกัน 3–4 จ็อบ
16GBชุดเทสต์ขนาดใหญ่และไปป์ไลน์แบบขนาน

ตัวเลขเหล่านี้เป็นแนวทางจากคนที่ใช้งานจริงซึ่งได้มาจากการรัน CI จริง ไม่ใช่เบนช์มาร์กของผู้ขาย ให้ถือเป็นจุดเริ่มต้นคร่าวๆ แล้วคอยดูค่าหน่วยความจำสูงสุดของคุณเอง

ใจความสำคัญของหัวข้อนี้: 4GB RAM คือค่าพื้นฐานที่เชื่อถือได้ และเผื่อเพิ่มอีกราว 2GB ต่อจ็อบ visual regression ที่รันพร้อมกันเพิ่มขึ้นหนึ่งจ็อบ

ข้อควรระวังของการทำ VRT แบบ self-hosted คือ runner ที่หน่วยความจำไม่พอจะไม่ล้มเหลวแบบส่งเสียงดัง แต่จะทำให้ Chromium แครชอย่างเงียบ ๆ ซึ่งเป็นอาการเดียวกับที่การตั้งค่าทั้งหมดนี้มีไว้เพื่อป้องกัน การเผื่อหน่วยความจำให้พอคือประกันที่ถูกที่สุดเท่าที่คุณจะซื้อได้ ถ้าคุณไม่อยากสร้างเครื่อง runner เองตั้งแต่ต้น มาร์เก็ตเพลสของ Cloudzy มีการติดตั้งแบบคลิกเดียวสำหรับ GitLab แบบ self-hosted และ Gitea แบบ self-hosted แบบ self-host ที่พาคุณไปถึงอินสแตนซ์ CI ที่รันได้ภายในไม่กี่นาที บน VPS ที่คุณเลือกขนาดให้พอดีกับหน่วยความจำที่ headless Chromium ในชุดเทสต์ของคุณต้องการ

VPS memory sizing guide for headless Chromium visual regression jobs: 2GB for a single sequential job, 4GB as the recommended baseline for one to two concurrent jobs, 8GB for three to four concurrent jobs, and 16GB for large suites and parallel pipelines

คำถามที่พบบ่อย

ตั้งค่า visual regression testing ในไปป์ไลน์ CI/CD อย่างไร

ติดตั้ง BackstopJS ชี้ backstop.json ไปที่หน้าเป้าหมาย แล้วตั้งออปชันรายงาน CI ให้ปล่อย JUnit XML ใช้ backstop reference --docker และ backstop test --docker เมื่อรันจากเครื่องของคุณ เพื่อให้ภาพหน้าจอเรนเดอร์ในอิมเมจ Docker ที่ pin ไว้ ส่วนบน CI ให้รันจ็อบภายในอิมเมจ backstopjs/backstopjs แล้วเรียก backstop test ตรงๆ จากนั้นเผยแพร่รายงาน JUnit จาก backstop_data/ci_report/xunit.xml ใช้รันเนอร์แบบ self-host ที่มี Docker executor และมีอย่างน้อย 4GB RAM สำหรับ headless Chromium

headless Chromium ต้องการ RAM เท่าไรในคอนเทนเนอร์ CI

โปรเซส headless Chromium ใช้ราว 300–500MB ตอนว่าง และพุ่งไป 1–2GB ตอนแคปเจอร์ภาพทั้งหน้า ถ้าอยากให้ CI เชื่อถือได้ ให้วางแผนที่ราว 4GB RAM ต่อจ็อบพร้อมกันหนึ่งถึงสองจ็อบ และเพิ่มอีกราว 2GB ต่อจ็อบขนานที่เพิ่มขึ้นแต่ละจ็อบ CPU มาเป็นช่วงพุ่งมากกว่าจะเป็นตัวจำกัด RAM ต่างหากที่ตัดสินว่าคุณรันกี่จ็อบพร้อมกันได้

ทำไมเทสต์ visual regression ผ่านบนเครื่องแต่ล้มเหลวบน CI

การเรนเดอร์ของเบราว์เซอร์ต่างกันไปตามสภาพแวดล้อม: ฟอนต์, anti-aliasing, โหมด headless กับแบบมีหน้าต่าง และ OS ล้วนเปลี่ยนผลลัพธ์ วิธีแก้คือรันทั้งการสร้าง baseline และการเปรียบเทียบใน Docker image เดียวกันเพื่อให้เรนเดอร์เหมือนกัน แฟล็ก --docker ของ BackstopJS ทำแบบนี้ให้ ด้วยการแคปเจอร์ภายในอิมเมจอ้างอิงที่ pin ไว้

แก้ปัญหา /dev/shm ของ Chromium ใน Docker CI อย่างไร

ค่าเริ่มต้นของ /dev/shm ใน Docker คือ 64MB ซึ่งเล็กเกินไปสำหรับ renderer ของ headless Chromium และทำให้ล่มแบบเงียบๆ ให้เพิ่มขนาดด้วย shm_size: '2gb' ใน docker-compose.yml (หรือ --shm-size=2gb ที่คอนเทนเนอร์ของจ็อบ) หรือส่ง --disable-dev-shm-usage ในแฟล็กตอนเปิด Chromium เพื่อให้มันเขียนไฟล์ shared memory ลง /tmp แทน

ควรใช้ toHaveScreenshot ของ Playwright หรือเครื่องมือ visual regression เฉพาะทาง

toHaveScreenshot() ที่มีมาในตัวของ Playwright เพียงพอสำหรับชุดเทสต์เล็กๆ: ราวไม่ถึงห้าสิบหน้าจอ ไม่มีไลบรารีคอมโพเนนต์ร่วม และไม่ต้องการ UI สำหรับรีวิวใน PR ให้ย้ายไปใช้เครื่องมือเฉพาะทางอย่าง BackstopJS เมื่อคุณต้องการเวิร์กโฟลว์อนุมัติ baseline ที่จริงจัง รายงานสำหรับรีวิว และการเรนเดอร์ที่ทำให้เหมือนกันด้วย Docker ซึ่งจะสำคัญขึ้นเมื่อจำนวน baseline ของคุณเพิ่มขึ้น

Lost Pixel ยังมีคนดูแลอยู่ไหม

ไม่ Lost Pixel ถูกอาร์ไคฟ์ไปเมื่อเมษายน 2026 และไม่ใช่ตัวเลือกสำหรับงานใหม่ ให้ใช้ BackstopJS สำหรับระบบ visual regression testing แบบ self-host

แชร์

บทความเพิ่มเติมจากบล็อก

อ่านต่อ

Four Portainer alternatives, Dockge, Arcane, Dockhand, and Komodo, shown as labeled Docker management UI panels
เครื่องมือนักพัฒนาและ DevOps

ทางเลือกแทน Portainer: คู่มือเลือกใช้ Dockge, Arcane, Dockhand และ Komodo ตามกรณีใช้งาน

เปรียบเทียบสี่ทางเลือกแทน Portainer ได้แก่ Dockge, Arcane, Dockhand และ Komodo ทั้งฟีเจอร์ ไลเซนส์ และการใช้งาน เพื่อหาตัวที่เหมาะกับคุณ

Bill 13 นาทีในการอ่าน

พร้อมติดตั้งหรือยัง? เริ่มต้น $2.48/เดือน

คลาวด์อิสระ ตั้งแต่ปี 2008 AMD EPYC, NVMe, 40 Gbps คืนเงินภายใน 14 วัน