Theppitak's blog

My personal blog.

31 กรกฎาคม 2551

Dynamic Debian Mirroring

ดูแล Debian mirror ที่ debianclub.org มาได้จะครบปีแล้ว โดยอาศัย rsync ตาม วิธีการของ Debian โดย sync วันละสองรอบ เช้า-เย็น พร้อมกับ ป้อนต่อไปอีกสองแห่ง คือ LTN และ mirror ใน มข.

ปัญหาทางเทคนิคที่พบ มีมาเรื่อย ๆ ต้องเข้าไปสั่งแบบ manual เป็นระยะ ๆ เพื่อให้มัน update อยู่เสมอ โดยบังคับตัวเองให้ใช้ mirror ปลายทางอย่าง LTN เพื่อจะได้รู้ทันทีที่มีปัญหาเกิดขึ้น

สำหรับปัญหาที่พบ ไม่มีอะไรร้ายแรงเหมือน สมัยทำที่ LTN แต่ก็ยังมีเรื่องจุกจิกอยู่บ้าง พอสรุปได้ดังนี้

  • แหล่งที่อยู่ใกล้ไม่ค่อย update ในสมัยเริ่มแรกนั้น แหล่งที่ใกล้ที่สุดคือฮ่องกง แต่การ update จะไม่สม่ำเสมอ แถมในช่วงแรกยังไม่เปิด anonymous rsync ให้อีกต่างหาก จึงต้องใช้แหล่งถัดไป คือที่สเปน แต่แล้วก็ต้องเปลี่ยนไปมา เพราะหลายครั้งที่สเปนก็ไม่ update ก็เข้าไปเปลี่ยนแหล่ง sync เป็นคราว ๆ ไป เนเธอร์แลนด์บ้าง นิวซีแลนด์บ้าง แต่โดยเฉลี่ยแล้ว แหล่งในเอเชียไม่ค่อย update ในช่วงแรก แถมระยะทางในเครือข่ายยังไล่เลี่ยกับยุโรปอีกต่างหาก
  • การ update ล่าช้า โดยปกติผมตั้ง cron ไว้ให้เริ่ม sync หลังกำหนด update ของ ftp-master 2 ชั่วโมง โดยกะว่า mirror หลักต่าง ๆ คง sync กันพอสมควรแล้ว แต่ก็ปรากฏว่า บางวันพอถึงกำหนดที่ว่า mirror ก็ยัง sync กันไม่เสร็จ อาจเป็นเพราะวันนั้นมี update เป็นปริมาณมาก หรือไม่ก็มีปัญหาบางจุดในเครือข่าย
  • สคริปต์ rsync ตายกลางคัน อันนี้นาน ๆ จะเจอที เนื่องจาก debianclub อาจจะอยู่ใกล้ backbone แต่ถ้าเป็นสมัย LTN ล่ะก็ เจอประจำ

เหล่านี้ ทำให้ต้องเข้าไปสั่งแบบ manual เป็นครั้งคราว สั่งบ่อยเข้า ก็พบว่าตัวเองกำลังทำงานเป็นแพตเทิร์นบางอย่างซ้ำ ๆ เลยชักเบื่อ เขียนเป็นสคริปต์เลือก mirror รายวันเลยดีกว่า

สคริปต์นั้น จะเลือก mirror ที่ดีที่สุด (ใกล้และ update ที่สุด) โดยแบ่งการทำงานเป็น 2 ขั้น:

  1. รอให้ master เปลี่ยนแปลง เพื่อจัดการกับปัญหาการ update ล่าช้า โดยแทนที่สคริปต์จะเริ่ม sync ทันที ก็เช็ก master เสียก่อน ว่าเปลี่ยนแปลงหรือยัง ถ้ายัง ก็ sleep ไปก่อน โดยตื่นขึ้นมาเช็กเป็นระยะ ๆ จนกว่าจะพบการเปลี่ยนแปลง จึงจะทำงานต่อไป
  2. เลือก mirror ที่ดีที่สุด โดยทำ ranking 20 อันดับแรกของ mirror ต่าง ๆ ด้วย netselect เอาไว้ แล้วไล่เช็ก timestamp ของแหล่งต่าง ๆ เริ่มจากแหล่งที่ใกล้ที่สุดก่อน ไล่ไปเรื่อย ๆ จนพบแหล่งที่ timestamp มากกว่าหรือเท่ากับของ master ถ้าไล่จนหมดรายชื่อแล้วไม่มีใครใหม่เลย ก็ sleep คอยเช็กเป็นระยะ ๆ ถ้าทำทั้งหมดนี้แล้ว ยังไม่พบ mirror ที่ดี ก็ค่อยบังคับ sync จาก master เป็นคำตอบสุดท้าย

ตัวสคริปต์มีเนื้อหาดังนี้ (license: GPL-2):

การอ่าน timestamp ของ mirror

get_site_stamp()
{
  SITE=$1
  wget -q -t 1 -T 5 -O - ftp://$SITE/debian/project/trace/ftp-master.debian.org
}

ตัวเลือกต่าง ๆ ของ wget มีเหตุผลดังนี้

  • -q ให้มันเงียบ ๆ ไม่ต้องแสดง progress
  • -t 1 ลองครั้งเดียวพอ ไม่ต้องลองซ้ำหลายครั้ง เนื่องจากถ้าเจอปัญหา mirror ล่มไป จะได้ไม่ต้องเสียเวลาคอยนาน
  • -T 5 หยุดรอแค่ 5 วินาที ซึ่งเป็นเวลาที่มักจะได้คำตอบจาก mirror ที่ตอบสนองปกติ และถ้า mirror ไหนล่มไป ก็ไม่ต้องคอยนาน
  • -O - เพื่อให้เขียนเนื้อหาออก stdout เป็นผลลัพธ์ของฟังก์ชันนี้
  • แฟ้ม timestamp ใช้ ftp ไม่ใช่ http เพื่อเลี่ยงการได้เนื้อหาที่ค้างใน proxy หรือในกรณีที่บาง mirror ห้าม access ไดเรกทอรีนี้ผ่าน http

การเปรียบเทียบ timestamp

date_cmp()
{
  DATE1=`date -d "$1" "+%Y%m%d%H%M%S"`
  DATE2=`date -d "$2" "+%Y%m%d%H%M%S"`
  expr $DATE1 - $DATE2
}

timestamp จากเซิร์ฟเวอร์ จะได้มาในรูปวัน-เวลาแบบนี้

Wed Jul 30 20:00:01 UTC 2008

ก็จัดการแปลงรูปแบบด้วยคำสั่ง date ให้อยู่ในรูปตัวเลขที่เรียงลำดับ ปี-เดือน-วัน-ชั่วโมง-นาที-วินาที แล้วก็จับลบกันแบบเลขคณิต เพื่อดูว่าอันไหนก่อนอันไหนหลัง

การรอ master เปลี่ยนแปลง

wait_master_change()
{
  LOCAL_STAMP=`get_site_stamp localhost`
  MASTER_STAMP=`get_site_stamp ftp.debian.org`
  RETRIES=0
  while [ `date_cmp "$MASTER_STAMP" "$LOCAL_STAMP"` -le 0 ] \
          && [ $RETRIES -lt 15 ]
  do
    RETRIES=`expr $RETRIES + 1`
    date "+%H:%M:%S [$RETRIES] - Master unchanged, sleeping." >&2
    sleep 600
    MASTER_STAMP=`get_site_stamp ftp.debian.org`
  done
  if [ `date_cmp "$MASTER_STAMP" "$LOCAL_STAMP"` -le 0 ]; then
    # master not changed, return failure code
    return 1
  else
    # master changed, return sucess code
    return 0
  fi
}

วนอ่าน timestamp ของ master (ทึกทักเอา ftp.debian.org เป็น master แต่ความจริงเป็นคนละเซิร์ฟเวอร์กับ ftp-master) มาเปรียบเทียบกับในเครื่อง รอจนกว่า timestamp ของ master จะล้ำหน้าของเครื่องเรา (มีบางขณะเหมือนกัน ที่ของเราล้ำหน้าของ master อันเนื่องมาจากเวลาที่เหลื่อมกันเล็กน้อยระหว่างการ sync ของ ftp.debian.org กับการ sync ของ mirror อื่น) หรือถ้าครบกำหนดการวนอ่าน (คิดเป็นเวลาหลับ ๆ ตื่น ๆ ก็ร่วม 2 ชั่วโมงครึ่ง) แล้วคืนค่าเป็นรหัสบอก

ฟังก์ชันนี้คืนค่าเป็น return code ของเชลล์ คือ 0 = สำเร็จ, 1 = ล้มเหลว

การตรวจหา mirror ที่ดีที่สุด

choose_best_mirror()
{
  MASTER_STAMP=`get_site_stamp ftp.debian.org`
  if [ $? -ne 0 ]; then
    echo "Failed to fetch master timestemp." >&2
    exit 1
  fi
  if [ ! -f /var/local/debian-mirrors-rank ]; then
    echo "/var/local/debian-mirrors-rank missing." >&2
    echo "Run '/usr/local/bin/rank-debian-mirrors' to update it." >&2
    exit 1
  fi
  for site in `cat /var/local/debian-mirrors-rank`; do
    SITE_STAMP=`get_site_stamp $site`
    if [ ! -z "$SITE_STAMP" ] \
         && [ `date_cmp "$SITE_STAMP" "$MASTER_STAMP"` -ge 0 ]
    then
      echo $site
      return
    fi
  done
}

ผมตั้ง cron ในรอบต่างหากนอกสคริปต์นี้ เพื่อทำ ranking ของ mirror ด้วย netselect แล้วเขียนลงในไฟล์ debian-mirrors-rank เก็บไว้ เพื่อให้สคริปต์นี้ไปอ่าน rank มาใช้

จากนั้น สคริปต์นี้ก็วนตรวจ timestamp ของแหล่งต่าง ๆ ตามลำดับจากใกล้ไปไกล ถ้าเจอแหล่งไหนที่ timestamp ใหม่ทัดเทียมกับ master (บางแหล่งอาจล้ำหน้า master เล็กน้อย เป็นเรื่องปกติ) ก็หยุดแล้วเขียนชื่อแหล่งนั้นออกทาง stdout เพื่อคืนค่าทันที

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

สคริปต์นี้ ใช้เวลาเขียน/แก้อยู่นานพอสมควร เพราะมีโอกาสทดสอบจริงได้ไม่เกินวันละสองครั้ง แล้วแต่ละครั้งก็มีปัญหาใหม่ ๆ เข้ามาเรื่อย ๆ ตอนนี้ก็ใช้การได้ดีในระดับหนึ่ง แต่ก็ยังมีโอกาสเจอปัญหาใหม่ ๆ ได้อีก เช่นเมื่อเช้านี้ที่เจอมาหมาด ๆ ก็คือคำสั่ง rsync ตายกลางคัน ต้องเข้าไปสั่งแบบ manual เอา ปัญหานี้ยังไม่แก้ด้วยสคริปต์ ยอม manual ไปก่อน จนกว่าจะได้ไอเดียดี ๆ หรือเริ่มซ้ำซากพอจะให้เบื่อ :-)

หมายเหตุ: ในระยะหลัง แหล่ง sync หลักของ debianclub เริ่มเปลี่ยนมาเป็นไซต์ในเอเชียมากขึ้น โดยหนักที่ไต้หวัน รองลงมาอยู่นอกเอเชียไม่ไกล คือนิวซีแลนด์ ถัดไปเป็นฮ่องกงยามที่เธอลุกขึ้นมา update ตัวเอง และนาน ๆ จะได้ sync จากเกาหลีบ้างเหมือนกัน (แต่เฉพาะปัญหาเมื่อเช้านี้ ที่พึ่งของเรากลายเป็นไอร์แลนด์)

ป้ายกำกับ: ,

0 ความเห็น:

แสดงความเห็น (มีการกลั่นกรองสำหรับ blog ที่เก่ากว่า 14 วัน)

<< กลับหน้าแรก

hacker emblem