nitip.at

nitip.at

ทำ Task scheduler ด้วย alpine docker

ทำ Task scheduler ด้วย alpine docker

Nitipat Lowichakornthikun's photo
Nitipat Lowichakornthikun
·Oct 4, 2018·

2 min read

วันนี้เราจะมาสร้าง Task scheduler กันครับ ยกตัวอย่างโจทย์ที่ผมได้รับมาคือ “การที่ระบบจะต้องสามารถอัพเดทข้อมูลของตัวสถิติเว็บไซต์ทุก ๆ 1 ชั่วโมง เนื่องจากมีการคำนวณข้อมูลที่มากจึงไม่ได้ใช้การคำนวณใหม่แบบ Real-time ครับ” เราจะเห็นว่า Task แนวนี้มีเยอะครับ อาทิ

  • การ Backup database ในทุก ๆ วัน
  • การต้องอัพเดท หรือ Generate report ในทุก ๆ x ชั่วโมง
  • และอีกมากมาย ที่ต้องเป็นงานที่ทำซ้ำ ๆ และ อยู่ใน Backgroud process ด้วย

สำหรับใครที่ยังอ่านแล้ว งง ๆ อยู่ ผมแนะนำว่าลองศึกษาเพิ่มเติมในส่วนของเรื่อง Task scheduler โดยการใช้งาน cron ได้ที่นี่น่ะครับ คุณจะได้สามารถอ่านที่ผมเขียนและเข้าใจได้ไม่ยากนัก

วิธีไหนดี?

วิธีการตั้ง Cron ในการทำให้ Task ใน container สามารถรันได้ (โปรเจกต์ที่เราพัฒนาอยู่ตอนนี้ใช้งาน Docker container เป็นหลักครับ) คร่าว ๆ ที่พอนึกออกตอนนี้มี 4 วิธีด้วยกันครับ

  1. ตั้งค่า Cron แบบทั่วไป ใน crontab ของเครื่องที่เป็น Host ของตัว Docker container อันนี้ก็สามารถหาวิธีทำได้ทั่วไปเลยครับ แต่วิธีนี้จะทำให้เวลานำเอางานชิ้นนี้ไป Deploy ที่ใหม่ เราก็ต้องเสียเวลาในการไป Config crontab ของเครื่องใหม่เอง อันนี้ทำให้เกิดการผิดพลาดได้สูงและเสี่ยงพอตัว
  2. ตั้งเครื่องมาใหม่อีกเครื่องนึง หรือ หา Cron service ที่สามารถยิง API เข้ามาหาในระบบเราได้ วิธีนี้ยังไม่ตอบโจทย์กับเราเนื่องจากเราไม่ต้องการให้ API endpoint นี้ public ออกไป อีกทั้งเรามองว่า Task ตัวนี้จะถูก Execute ด้วย command ครับ ดังนั้นวิธีนี้เลยตกไปก่อน
  3. ใช้การตั้งค่า Cron ภายใน container ของตัว Process application เลย ตอนแรกผมก็กะว่าจะใช้วิธีนี้ครับ เพราะ ตัวอย่างคือตอนนี้ผมใช้ php-fpm บน Alpine อยู่ ซึ่งตัวมันเองก็มี cron มาให้เราใช้ได้เลย แต่วิธีนี้ก็จะทำให้ Container นี้มันทำงานหลายหน้าที่เกินไปครับ ทั้งต้องคอย Serve ข้อมูลของระบบเรา อีกทั้งยังต้องทำงานในส่วนของ crontask อีกด้วย
  4. แยก Container ใหม่ขึ้นมาอีกอันทำหน้าที่เดียวในการตั้งค่าเกี่ยวกับ Crontask และลง Dependencies ต่าง ๆ แค่เท่าที่ใช้ในการรัน Task นั้น ๆ ขึ้นมา ซึ่งวิธีนี้ดีที่สุด ณ​ ตอนนี้ครับ มันทำให้เราจัดการง่ายและดูแลได้ หากเกิดปัญหาก็สามารถไปแก้ได้ในเฉพาะจุดนี้ หรือ หาก Container นี้มีปัญหามันก็ไม่ลากใครพังไปด้วย

จริง ๆ ก็อาจจะมีวิธีการแก้ไขเรื่องนี้ได้มากกว่านี้น่ะครับ เพื่อน ๆ คนไหนมีเทคนิคที่น่าสนใจลองแชร์กันได้ครับ สำหรับผมวันนี้ผมจะเลือกวิธีที่ 4 มาใช้ และ จะพาทุกคนทำไปด้วยกันครับ

สำหรับตัว alpine หรือ linux os ขนาดกระทัดรัดไซส์น่ารักกำลังดี ตอนนี้แทบจะทุกภาษามีคนทำไว้อยู่บน alpine แล้วทั้งสิ้นครับ ลองค้นหาดูได้เลยครับ ในวันนี้เราจะใช้เป็นตัว php-fpm:alpine กันครับ

เริ่มกันเลย

  1. ก่อนเริ่มครับ ทำการติดตั้ง Docker ให้เรียบร้อย สำหรับคนที่ยังไม่มีก็ติดตั้งเลยครับ จะได้ทดลองทำไปพร้อม ๆ กันเลย
  2. สั่งคำสั่ง clone repo นี้ ซึ่งผมได้เตรียมไฟล์ไว้แล้ว จะได้รวดเร็วในการทดลองยิ่งขึ้นครับ

git clone https://github.com/nitipatl/crontask-php-alpine

3. เมื่อคุณเข้าไปยังโฟลเดอร์นั้นจะพบกันกับไฟล์และโฟลเดอร์ย่อยดังนี้

.
├── Dockerfile
├── docker-compose.yml
├── readme.md
├── src
│ ├── log.txt
│ └── time.php
└── update_time.sh

update_time.sh shell script ที่ทำหน้าที่ในการเรียกใช้งานสคริปต์ของ php

src/time.php script php ซึ่งจะเขียนเวลาเป็น unixtime ออกมา

src/log.txt คือไฟล์ที่เราต้องการนำเอาผลที่ได้จากการรัน shell script มาใส่ไว้

ให้ลองไล่เปิดดู Code แต่ละอันกันครับ สิ่งที่เราจะทำคือการสั่งให้ทุก ๆ 15 นาที ระบบจะต้องสั่ง update_time.sh ให้ทำงานขึ้นมา

สำหรับ Dockerfile คือการเพิ่มเติม command จาก based image ตามนี้

FROM php:7.2-fpm-alpine
WORKDIR /app
COPY update_time.sh /etc/periodic/15min/update_time
RUN chmod +x /etc/periodic/15min/update_time
COPY src .

ซึ่งนำเอา update_time.sh ย้ายไปไว้ที่ /etc/periodic/15min ครับ โดยสังเกตว่าชื่อไฟล์ script ที่เอาไปวางต้องไม่มี . (ดอท) ของนามสกุลอยู่น่ะครับ

ส่วน docker-compose.yml มีการใส่ไว้แบบนี้ในส่วนของ command หลังจาก start containr ไว้แบบนี้ครับ เพื่อให้สั่งรัน crond และ -f คือ การทำงานแบบ foreground

command: crond -f

4. เราจะเริ่มการทดสอบได้โดย สั่ง

docker-compose up -d

เพื่อสั่งให้ Container ทำงานขึ้นมา

5. ทดลองสั่งรันดูว่าจะสามารถทำงานผ่าน cron ได้ไหม

docker-compose exec cron-php run-parts /etc/periodic/15min

ให้ทดลองเปิดไฟล์ src/log.txt จะมีหน้าตาออกมาประมาณนี้ (คือมี unix time แสดงผลในบรรทัดใหม่ในทุก ๆ ครั้งที่เราสั่งรัน command ด้านบน)

แล้วแบบนี้มันมีการตั้งค่าเวลาแบบอื่น ๆ ไหม ถ้าไม่อยากได้ 15 นาที แต่เป็น 1 ชั่วโมงล่ะ?

$ docker-compose exec cron-php ls /etc/periodic
15min daily hourly monthly weekly

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

อ้างอิง

Running cron tasks on a Docker Alpine container
Running cron tasks on a Docker Alpine
containerdevopsheaven.com

 
Share this