OPS.md — thegioikeycap.vn
Vận hành hằng ngày: chạy lệnh, xem log, deploy, chẩn đoán.
Quy trình test đơn hàng nằm ở docs/TASK_PROTOCOL.md §Quy tắc Test.
Chuẩn bị môi trường
# Nạp secret vận hành (Pancake, Brevo, AllinGo, Cloudflare)
set -a; source /srv/thegioikeycap.vn/.secrets.env; set +a
# Rút gọn WP-CLI cho tiện
cd /srv/thegioikeycap.vn/docker
WP="docker compose exec -T -u www-data php wp --path=/srv/thegioikeycap.vn/www"
⚠️ .secrets.env là chmod 600, chủ sở hữu root, không nằm trong git.
WP-CLI
Luôn chạy trong container php với user www-data — chạy bằng root sẽ tạo
file sai quyền và WordPress không ghi được nữa.
$WP plugin list --status=active
$WP option get siteurl
$WP cache flush
$WP rewrite flush
# Truy vấn DB — KHÔNG có `wp db query` (container thiếu mysql client)
$WP eval 'global $wpdb; echo $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts}");'
Docker
cd /srv/thegioikeycap.vn/docker
docker compose ps
docker compose restart php # sau khi đổi cấu hình PHP
docker compose logs -f php
docker compose exec -T php php -v
Hai service: keycap_php (PHP-FPM 8.3) và keycap_mysql (MySQL 8.4).
nginx chạy trên host, không trong compose.
Cấu hình PHP-FPM Pool, RAM & WP-Cron (2026-09-24)
- FPM Pool & Container RAM: Tăng
pm.max_children = 32,pm.start_servers = 8,pm.min_spare_servers = 4,pm.max_spare_servers = 12,pm.max_requests = 2000quadocker/php/zz-pool.conf. Container limit bộ nhớ tăng lên2G. - PHP memory_limit & OPcache: Nâng
memory_limit = 1024M,opcache.memory_consumption = 256M,opcache.max_accelerated_files = 20000quadocker/php/php.inivà vhost nginx. - MySQL InnoDB Buffer Pool: Nâng
innodb_buffer_pool_size = 512M,innodb_redo_log_capacity = 256Mquadocker/mysql/my.cnf(toàn bộ database ~38MB nằm trọn vẹn trong RAM). - WP-Cron CLI:
DISABLE_WP_CRON = truetrongwp-config.phpđể giải phóng HTTP worker. Cron hệ thống (root crontab) chạywp cron event run --due-nowmỗi 1 phút qua WP-CLI độc lập.
Log
tail -f /srv/thegioikeycap.vn/logs/php/error.log
tail -f /srv/thegioikeycap.vn/logs/nginx/error.log
tail -f /srv/thegioikeycap.vn/logs/nginx/access.log
⚠️ Không phải lỗi nào cũng vào error.log. ArgumentCountError từng gây
HTTP 500 trắng trang giỏ hàng mà log trống trơn. Trang trắng + log sạch không
có nghĩa là code đúng — hãy nghi ngờ chữ ký callback.
Log có logrotate. Đường dẫn từng bị sai một lần, kiểm tra nếu log ngừng xoay.
Deploy / cập nhật code
Code chạy trực tiếp từ working tree — không có bước build. Sửa file là có hiệu lực, nhưng:
- PHP: đợi tới 60 giây (OPcache
revalidate_freq=60). Muốn có ngay:docker compose restart php. - CSS/JS: PHẢI bump
TGK_VERSIONtrongfunctions.php. Đây là hằng số CỐ ĐỊNH (không phảifilemtime), và nó là toàn bộ chuỗi?ver=củamain.css+main.js. Quên bump thì khách cũ vẫn chạy bản cache cũ — trên máy mình thấy đúng, trên máy khách thì không, và không có lỗi nào để lần ra.
# Ép OPcache nhả ngay
docker compose exec -T php kill -USR2 1 2>/dev/null || docker compose restart php
nginx / SSL
- vhost đang chạy:
docker/nginx-thegioikeycap.vn.CUTOVER.conf - Đầu file có ghi quy trình rollback về kiến trúc Astro cũ. Đọc trước khi sửa.
- Bản lưu Astro:
/root/backups/thegioikeycap-astro-frontend-FULL-20260905.tar.gz - ACME challenge:
/srv/thegioikeycap.vn/.well-known/acme-challenge/
Chụp màn hình + QA hành vi thật (Playwright)
⚠️ Sau migrate VPS 09/2026, đây là công cụ đúng — KHÔNG còn chromium snap.
Chromium snap thuộc user root của VPS cũ, không tồn tại ở đây.
Tool có sẵn: /opt/screenshot-tool/shot.mjs — chụp full-page, in kèm
<meta robots>, console error và request lỗi (>=400):
cd /opt/screenshot-tool
node shot.mjs 'https://thegioikeycap.vn/keycap/' /đường/dẫn/ra.png 1440
Chạy node được ở BẤT KỲ đâu có node_modules — tức là phải chạy TỪ
/opt/screenshot-tool, hoặc copy script sang đó trước, nếu không
Cannot find package 'playwright'.
⚠️ Kiểm tra giao diện tĩnh (chụp ảnh) KHÔNG đủ cho nút bấm / form / JS.
Bẫy đã sập thật: nút "Mua ngay" (xem docs/WP_THEME.md §3d) chỉ lộ ra khi
bấm bằng trình duyệt thật — test bằng curl tự ghép payload tay đã báo
đúng vì gõ tay đủ mọi tham số, thứ mà một cú click thật trên đúng nút đó không
bao giờ tạo ra. Test nút bấm bằng Playwright thao tác thật (page.click()),
không suy diễn từ request tự dựng:
// Chạy từ /opt/screenshot-tool: node ten-file.mjs
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await (await browser.newContext({ viewport: { width: 1440, height: 900 } })).newPage();
await page.goto('https://thegioikeycap.vn/...', { waitUntil: 'networkidle' });
await page.locator('.selector-can-bam').click();
await page.waitForTimeout(2000);
console.log('URL sau khi bấm:', page.url());
await browser.close();
⚠️ Chromium headless mặc định bị should_track() coi là bot → trang trả về
không có hàng đợi event, không có cookie _fbp. Muốn kiểm tra tracking phải
set userAgent giả trình duyệt thật khi tạo context.
Đối chiếu tồn kho Woo ↔ Pancake
Chạy sau mỗi lần test đơn, và khi nghi ngờ lệch kho.
$WP eval '$o=[]; foreach(wc_get_products(["limit"=>-1,"return"=>"ids"]) as $i){
$p=wc_get_product($i);
foreach(($p->is_type("variable")?$p->get_children():[$i]) as $v){
$x=wc_get_product($v); if($x) $o[strtoupper($x->get_sku())]=$x->get_stock_quantity(); }}
echo json_encode($o);'
So với Pancake:
GET /shops/{shop}/products/variations — đối chiếu display_id ↔ SKU và
remain_quantity ↔ tồn Woo.
Hiện khớp 36/36 biến thể (chốt 06/09/2026).
Kiểm tra email
$WP eval 'var_dump( wp_mail("thegioikeycap+test@gmail.com","Test","Nội dung") );'
- Trả
false→ cấu hình SMTP hỏng, không phải nội dung sai. Container không có MTA nênwp_mail()phụ thuộc hoàn toàn vào FluentSMTP. - Nhật ký gửi: WordPress admin → FluentSMTP → Email Logs.
- ⚠️ Không bao giờ test bằng
@example.com— hard bounce, hại uy tín tên miền.
Sao lưu
Script tự động: /srv/thegioikeycap.vn/docker/backup.sh (chạy hàng ngày qua cron lúc 20:00 UTC / 03:00 sáng VN, giữ 14 ngày).
# DB thủ công
/srv/thegioikeycap.vn/docker/backup.sh
# Code + uploads thủ công
tar czf /srv/backups/thegioikeycap.vn/tgk-www-$(date +%F).tar.gz -C /srv/thegioikeycap.vn www
Kho backup: /srv/backups/thegioikeycap.vn/.
Bảng tra nhanh khi có sự cố
| Triệu chứng | Nghi ngờ đầu tiên |
|---|---|
| Sửa code mà không thấy đổi | OPcache 60 giây |
| Trang trắng, log trống | Sai số tham số callback (ArgumentCountError) |
| Email không tới | FluentSMTP / Brevo, không phải wp_mail() |
| Tồn kho lệch | Đơn test dọn sai — hoàn kho hai lần |
| Pixel "không bắn" | User-Agent bị coi là bot |
| Mô tả danh mục bị mất sau khi lưu | Đoạn nới kses trong inc/category-content.php bị gỡ |
| Danh mục không vào sitemap | Rank Math → Sitemap → Product Categories → Include Empty Terms |
| Đơn không sang POS | send-app còn đang tắt sau lần test |
| Cước ship sai | TGK_ALLINGO_MODE đang test → giá giả lập |