สวัสดีครับวันนี้ผมจะมาแชร์ประสบการณ์การทำเว็บที่ไม่เคยคิดว่าตัวเองจะได้ทำ สิ่งที่ดูเหมือนจะง่าย แต่ก็ไม่ได้กินหมูซะทีเดียว อันคือการอัพเดท wordpress นั่นเอง ตามหัวข้อโพสนี้เลย
แล้วทำไมมันถึงไม่ง่ายล่ะ ?
ปกติแล้วเราก็กดในเว็บได้เลยใช่มั้ยล้า เราอยากจะอัพ theme plugin core เราก็ติ๊กเลือกผ่านข้างหน้าเว็บได้เลย แต่นั้นคือท่าปกติครับ ถ้าสมมุติว่าเรา config ระบบเองทั้งหมด เราอาจจะมีการตั้ง dir permission จำกัดสิทธิ์ของ web server ในการ read/write บ้าง dir ของ wordpress เช่น wp-content ครับ
แล้วถ้าถามว่าจะทำไปทำไม คำตอบคือเพื่อความปลอดภัยครับ ใช่ แต่มันทำให้เราทำงานยากขึ้นมากเลยทีเดียว ถ้าเราดูแลเว็บแค่ 2-3 เว็บอันนี้ถ้าไม่ขี้เกียจมันก็ไม่เกินความสามารถ แต่ถ้าเราดูแลเว็บระดับ 10,20,30 หรือ 100,200 แบบผม การจะ remote ไปที่ server แล้วอัพเดททีละ site ขี้คงแตกก่อน แล้ว wordpress เนี้ย เค้าก็อัพเดท plugin กันถี่ซะด้วย สมมุติว่าเมื่อวานกด update all ไปแล้ว วันนี้ตื่นมาอาจจะเกิดมันแจ้งเตือนว่ามี plugin ที่เป็นเวอร์ชั่นเก่ายังไม่ได้อัพเดท 5 ตัว ++ ก็ได้นะครับ 55555 🥲
เข้าเรื่องเลย สำหรับ server stuck ที่ผมทำงานด้วยก็อาจจะแปลกๆ สักหน่อย
คือมี load balancer แล้วก็ vm ที่เป็น webserver + php ข้างหลัง แล้วก็แชร์ stoagre กัน แล้วก็แยก database ไว้อีกชุด ดังนั่นไม่ว่าเราจะมีกี่เครื่องทุกเรื่องจะใช้ข้อมูลชุดเดียวกัน ถ้าล่มก็ดับทั้งหมด แต่ผมไม่ได้เป็นคน design ดังนั่นก็อย่างที่บอกว่าเหมือนจะง่ายแต่ก็ไม่ได้กินหมู ตอนอัพก็เสียวมาก ตอนนี้เรากำลังทำงานอยู่บนระบบที่กำลัง production จริงอยู่ ซึ่งก็ไม่ควร เสี่ยงมาก แต่อันนี้มี backup ก็พออุ่นใจได้นิดนึง แต่ถ้าสมมุติว่าอัพแล้วพังก็ชิพหายอีกนั้นแหละ จะโดนด่าเอา
สิ่งที่ต้องใช้คือ wp-cli ครับ ผมทำ wordpress มานานระดับนึง ผมก็เพิ่งเคยใช้เลย ขอสาระภาพ แต่พอได้ใช้เห้ยย อันนี้ของดี
โดยขั้นแรกสิ่งที่เราต้องรู้คือเรามีเว็บอยู่ที่ไหนบ้าง อันนี้หมายถึงอยู่บน dir ไหนบน web server ดังนั้นเราจะมี script ที่จะช่วยเราไม่งั้นตาย
<?php
function findAllWordPressPaths($rootDirectory) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootDirectory, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
$logFile = 'wordpress.log';
foreach ($iterator as $file) {
if ($file->isDir() && $file->getBasename() === 'wp-includes' && file_exists($file->getPathname() . '/version.php')) {
$wp_install_path = $file->getPath();
require_once($wp_install_path . '/wp-includes/version.php');
global $wp_version;
$wp_config_file = $wp_install_path . '/wp-config.php';
$db_info = [];
if (file_exists($wp_config_file)) {
$wp_config_contents = file_get_contents($wp_config_file);
preg_match("/define\(\s*'DB_NAME'\s*,\s*'(.+?)'\s*\);/", $wp_config_contents, $matches);
$db_info['DB_NAME'] = isset($matches[1]) ? $matches[1] : 'Not Found';
preg_match("/\$table_prefix\s*=\s*'(.+?)'\s*;/", $wp_config_contents, $matches);
$db_info['Table_Prefix'] = isset($matches[1]) ? $matches[1] : 'Not Found';
} else {
$db_info['DB_NAME'] = 'Config File Not Found';
$db_info['Table_Prefix'] = 'Config File Not Found';
}
// Log data to wordpress.log
$logData = "WordPress installation path: $wp_install_path" . PHP_EOL;
$logData .= "WordPress version: $wp_version" . PHP_EOL;
$logData .= "Database name: {$db_info['DB_NAME']}" . PHP_EOL;
$logData .= "Table Prefix: {$db_info['Table_Prefix']}" . PHP_EOL;
$logData .= "-----------------------------" . PHP_EOL;
file_put_contents($logFile, $logData, FILE_APPEND);
}
}
}
$rootDirectory = '/hostdata/xxxx';
findAllWordPressPaths($rootDirectory);
?>
โดย Script นี้จะสแกน dir ที่กำหนดโดยใช้ patter ของ wordpress เช่นถ้ามีอันนี้ คือ wordpress แน่ๆ แล้วก็ให้ list มาหน่อย file นั้นมันอยู่ไหน แล้วก็ให้ output ออกมาหน่อย เป็น wordpress.log
WordPress installation path: /hostdata/xxxx/jobintosh01/public_html
WordPress version:
Database name: xxxx
Table Prefix: Not Found
ผลลัพธ์ก็จะประมาณนี้จากอันสิ่งที่เราต้องการคือแค่เฉพาะ path มันอย่างเดียว ข้อมูลอื่นๆ ไว้ดูประกอบ
#!/bin/bash
input_file="/root/wordpress-update/wordpress.txt"
output_file="/root/wordpress-update/cleaned_paths.txt"
grep "^WordPress installation path:" "$input_file" | cut -d: -f2- | sed 's/^ //' > "$output_file"
echo "Cleaned paths have been saved to $output_file:"
cat "$output_file"
เราก็จะได้ผลลัพธ์ออกมาแบบนี้
/hostdata/xxxx/jobintosh01/public_html
/hostdata/xxxx/jobintosh02/public_html
/hostdata/xxxx/jobintosh03/public_html/home
/hostdata/xxxx/jobintosh04/public_html
/hostdata/xxxx/jobintosh05/public_html
ยังไม่จบบ ต้องทำต่ออีก
เนื่องจากเพื่อความปลอดภัยแต่ละ dir จะมี owner คนละ user ครับ เราจะไม่ให้สิทธิ์ root อย่างเด็ดขาด แต่โดยดีหน่อยตอนที่สร้าง dir เป็นที่เว็บตรงกับชื่อ user เลยง่ายขึ้นนิดนึง โดย Script นี้จะ เพิ่ม wp core update , theme , plugin –allow-root เพื่อให้รันได้ก่อนอย่างไม่ติดขัด ละจากนั้นโดย get ชื่อ owner จาก path แล้วใช้คำสั่ง chown คืนสิทธิ์
#!/bin/bash
INPUT_FILE="cleaned_paths.txt"
OUTPUT_SCRIPT="update-all.sh"
echo "#!/bin/bash" > "$OUTPUT_SCRIPT"
echo "" >> "$OUTPUT_SCRIPT"
while IFS= read -r path; do
username=$(echo "$path" | awk -F'/xxxx/|/public_html' '{print $2}')
echo "echo \"Updating WordPress for: $path\"" >> "$OUTPUT_SCRIPT"
echo "wp core update --path=$path --allow-root" >> "$OUTPUT_SCRIPT"
echo "wp theme update --all --path=$path --allow-root" >> "$OUTPUT_SCRIPT"
echo "wp plugin update --all --path=$path --allow-root" >> "$OUTPUT_SCRIPT"
echo "chown -R $username:apache $path" >> "$OUTPUT_SCRIPT"
echo "" >> "$OUTPUT_SCRIPT"
done < "$INPUT_FILE"
FINAL_PATH="/hostdata/xxxx/jobintosh01/public_html"
FINAL_USERNAME=$(echo "$FINAL_PATH" | awk -F'/xxxx/|/public_html' '{print $2}')
chmod +x "$OUTPUT_SCRIPT"
echo "Script $OUTPUT_SCRIPT has been created successfully!"
เมื่อรัน Script นี้แล้วเราจะได้ file sh อีก 1 ตัวเป็น Final Boss ที่จะเอามางัดกับระบบนี้ 555555 ผมเขียน Blog นี้เพื่ออยากกลับมาอ่านว่าตัวเองทำอะไร รู้สึกเหนื่อยกว่าตอนทำจริงอีกนะ
echo "Updating WordPress for: /hostdata/xxxx/jobintosh01/public_html"
wp core update --path=/hostdata/xxxx/jobintosh01/public_html --allow-root
wp theme update --all --path=/hostdata/xxxx/jobintosh01/public_html --allow-root
wp plugin update --all --path=/hostdata/xxxx/jobintosh01/public_html --allow-root
chown -R jobintosh01:apache /hostdata/xxxx/jobintosh01/public_html
อันนี้คือ final script แล้วจากอันก็ ./update-all.sh รอความสำเร็จได้เลย
แต่ยังไม่จบขอบ่นเพิ่มอีกเนื่องจากตอนนี้ต้องไล่อัพเดทเว็บที่ไม่มี admin ดูแลแล้วอย่างน้อยให้มันปลอดภัยหน่อย ไม่โดนสแปนเว็บพนัน เว็บหวยลงก็โอเคแล้วมั้ง แต่เว็บอะมีเป็นร้อยจะอัพเดทอย่างไหลเรื่อยก็เหมือนฝันไปหน่อย มันก็มี error บ้าง เช่น บางเว็บมันเป็น wordpress v5 ที่เก่ามากๆ แล้ว เว็บมันอาจจะ white screen of dead ไปแล้ว อันนี้จะรัน update แล้วไปติดที่เว็บพวกนี้เยอะ เราต้อง Control + C แล้วเอา web นั้นออกจาก list แล้วรันต่อ อันนี้คือความเหนื่อย
แต่เว็บที่มันเป็น wordprsss v6 ไม่เป็นนะ อันนี้คือโชคดีไปแหละ ไม่เหนื่อยมาก