STAGE 21 · Lv.100
24/7 무인 운영 시스템
자가 치유 WordPress, 자동 에러 복구, 성능 자동 최적화, 보안 자동 대응, 종합 모니터링 시스템을 구축합니다.
CAPSTONE: 스스로 관리하는 WordPress 시스템
무인 운영의 비전: 사람 없이도 돌아가는 시스템
24/7 무인 운영 시스템은 WordPress 자동화의 궁극적 목표입니다. 사이트가 스스로 문제를 감지하고, 자동으로 복구하며, 성능을 최적화하고, 보안 위협에 대응합니다. 사람은 전략적 의사결정과 창의적 작업에만 집중하면 됩니다.
자가 치유 시스템의 3대 원칙:
1. 감지 (Detect): 문제를 실시간으로 감지 - 다운타임, 성능 저하, 보안 위협, 리소스 부족
2. 분석 (Diagnose): 문제의 원인을 자동 분석 - 어떤 프로세스가 문제인지, 어떤 파일이 변경되었는지
3. 복구 (Recover): 자동으로 복구하고 결과를 보고 - 서비스 재시작, 캐시 정리, 파일 복원, 롤백
자가 치유
문제 감지 → 자동 복구
사람 개입 최소화
사람 개입 최소화
80%
일반 장애의
자동 복구율
자동 복구율
실시간
보안 위협 감지
→ 즉시 차단
→ 즉시 차단
100%
모든 조치 기록
감사 추적 가능
감사 추적 가능
자가 치유 시스템 아키텍처
헬스 체크
(1분 간격)
(1분 간격)
→
이상 감지
(임계값 초과)
(임계값 초과)
→
원인 분석
(자동 진단)
(자동 진단)
→
자동 복구
(단계적)
(단계적)
→
검증 + 보고
(알림 발송)
(알림 발송)
// ============================================
// 자가 치유 WordPress 시스템 (완전판)
// mu-plugins/self-healing-system.php
// ============================================
class Self_Healing_WordPress {
private $log_table;
private $max_recovery_per_hour = 5; // 시간당 최대 복구 시도
public function __construct() {
global $wpdb;
$this->log_table = $wpdb->prefix . 'self_healing_log';
// 1분마다 헬스 체크 실행
add_action('self_healing_check', array($this, 'run_comprehensive_check'));
if (!wp_next_scheduled('self_healing_check')) {
wp_schedule_event(time(), 'every_minute', 'self_healing_check');
}
// 크론 간격 추가
add_filter('cron_schedules', function($schedules) {
$schedules['every_minute'] = array('interval' => 60, 'display' => 'Every Minute');
return $schedules;
});
}
// ========================================
// 종합 헬스 체크
// ========================================
public function run_comprehensive_check() {
// 안전장치: 시간당 복구 횟수 제한
if ($this->get_recovery_count_last_hour() >= $this->max_recovery_per_hour) {
$this->notify('자가 치유 시스템 일시 중지: 시간당 복구 한도 도달', 'warning');
return;
}
$issues = array();
// 1. 데이터베이스 연결 확인
if (!$this->check_db_connection()) {
$issues[] = array('type' => 'db_down', 'severity' => 'critical');
}
// 2. 디스크 공간 확인
$disk = $this->check_disk_space();
if ($disk['free_percent'] 'disk_critical', 'severity' => 'critical', 'value' => $disk);
} elseif ($disk['free_percent'] 'disk_warning', 'severity' => 'warning', 'value' => $disk);
}
// 3. PHP 메모리 확인
$memory = $this->check_memory();
if ($memory['percent'] > 90) {
$issues[] = array('type' => 'memory_high', 'severity' => 'warning', 'value' => $memory);
}
// 4. WP Cron 건강
if (!$this->check_cron_health()) {
$issues[] = array('type' => 'cron_stuck', 'severity' => 'warning');
}
// 5. PHP 에러 급증 감지
$error_spike = $this->detect_error_spike();
if ($error_spike > 50) {
$issues[] = array('type' => 'error_spike', 'severity' => 'high', 'value' => $error_spike);
}
// 6. 자체 응답 시간 측정
$response = $this->measure_self_response();
if ($response > 5000) {
$issues[] = array('type' => 'slow_response', 'severity' => 'warning', 'value' => $response);
}
// 7. DB 테이블 손상 확인
if ($this->check_db_corruption()) {
$issues[] = array('type' => 'db_corruption', 'severity' => 'high');
}
// 8. .htaccess/.maintenance 파일 이상 확인
if ($this->check_maintenance_mode()) {
$issues[] = array('type' => 'stuck_maintenance', 'severity' => 'high');
}
// 각 이슈에 대해 자동 복구 시도
foreach ($issues as $issue) {
$this->auto_heal($issue);
}
}
// ========================================
// 자동 복구 로직 (이슈 유형별)
// ========================================
private function auto_heal($issue) {
$action_taken = '';
$result = 'success';
switch ($issue['type']) {
case 'disk_critical':
// 긴급 디스크 정리
$freed = 0;
$freed += $this->cleanup_revisions(7); // 7일 이상 리비전
$freed += $this->cleanup_transients(); // 만료된 transients
$freed += $this->cleanup_spam(); // 스팸 + 휴지통
$freed += $this->cleanup_logs(); // 로그 파일 압축
$freed += $this->cleanup_orphan_meta(); // 고아 메타 데이터
$action_taken = "긴급 디스크 정리: {$freed}MB 확보";
break;
case 'disk_warning':
$freed = $this->cleanup_revisions(30);
$freed += $this->cleanup_transients();
$action_taken = "예방적 디스크 정리: {$freed}MB 확보";
break;
case 'memory_high':
wp_cache_flush();
if (function_exists('opcache_reset')) opcache_reset();
$action_taken = '오브젝트 캐시 + OPcache 정리';
break;
case 'cron_stuck':
delete_transient('doing_cron');
$spawned = spawn_cron();
$action_taken = 'WP Cron 리셋 및 재시작' . ($spawned ? ' (성공)' : ' (실패)');
break;
case 'error_spike':
// 최근 활성화된 플러그인 찾아 비활성화
$culprit = $this->find_error_source();
if ($culprit) {
deactivate_plugins($culprit);
$action_taken = "문제 플러그인 비활성화: {$culprit}";
} else {
$action_taken = '에러 급증 감지, 원인 플러그인 특정 불가';
$result = 'partial';
}
break;
case 'slow_response':
wp_cache_flush();
$this->rebuild_rewrite_rules();
// 페이지 캐시 워밍업
$this->warm_page_cache();
$action_taken = '캐시 재구축 + 리라이트 규칙 리셋';
break;
case 'db_corruption':
global $wpdb;
$wpdb->query("REPAIR TABLE {$wpdb->posts}");
$wpdb->query("REPAIR TABLE {$wpdb->postmeta}");
$wpdb->query("REPAIR TABLE {$wpdb->options}");
$action_taken = '손상된 DB 테이블 복구';
break;
case 'stuck_maintenance':
$maintenance_file = ABSPATH . '.maintenance';
if (file_exists($maintenance_file)) {
$mtime = filemtime($maintenance_file);
if (time() - $mtime > 600) { // 10분 이상 유지보수 모드
unlink($maintenance_file);
$action_taken = '장기 유지보수 모드 해제 (10분+ 경과)';
}
}
break;
}
if ($action_taken) {
$this->log_action($issue, $action_taken, $result);
$this->notify_action($issue, $action_taken, $result);
}
}
// ========================================
// 헬퍼 함수들
// ========================================
private function check_db_connection() {
global $wpdb;
return (bool) @$wpdb->get_var("SELECT 1");
}
private function check_disk_space() {
$free = disk_free_space(ABSPATH);
$total = disk_total_space(ABSPATH);
return array(
'free_percent' => round($free / $total * 100, 1),
'free_gb' => round($free / 1073741824, 2),
'total_gb' => round($total / 1073741824, 2),
);
}
private function check_memory() {
$usage = memory_get_peak_usage(true);
$limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
return array(
'percent' => round($usage / $limit * 100, 1),
'used_mb' => round($usage / 1048576, 1),
'limit_mb' => round($limit / 1048576, 1),
);
}
private function check_cron_health() {
$crons = _get_cron_array();
if (!is_array($crons)) return false;
$overdue = 0;
foreach ($crons as $ts => $hooks) {
if ($ts $threshold_time) $recent_errors++;
}
}
return $recent_errors;
}
private function find_error_source() {
$debug_log = WP_CONTENT_DIR . '/debug.log';
if (!file_exists($debug_log)) return null;
$lines = array_slice(file($debug_log), -50);
$plugin_errors = array();
foreach ($lines as $line) {
if (preg_match('/wp-content\/plugins\/([^\/]+)/', $line, $m)) {
$plugin_errors[$m[1]] = ($plugin_errors[$m[1]] ?? 0) + 1;
}
}
if (empty($plugin_errors)) return null;
arsort($plugin_errors);
$top_culprit = key($plugin_errors);
// 에러가 10건 이상인 플러그인만 비활성화
if ($plugin_errors[$top_culprit] >= 10) {
$plugins = get_plugins();
foreach ($plugins as $file => $data) {
if (strpos($file, $top_culprit . '/') === 0) return $file;
}
}
return null;
}
private function cleanup_revisions($days) {
global $wpdb;
$deleted = $wpdb->query($wpdb->prepare(
"DELETE FROM {$wpdb->posts} WHERE post_type = 'revision' AND post_date query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_timeout_%' AND option_value query("DELETE a FROM {$wpdb->options} a LEFT JOIN {$wpdb->options} b ON REPLACE(a.option_name, '_transient_', '_transient_timeout_') = b.option_name WHERE a.option_name LIKE '%_transient_%' AND a.option_name NOT LIKE '%_transient_timeout_%' AND b.option_name IS NULL");
return 1;
}
private function cleanup_spam() {
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->comments} WHERE comment_approved = 'spam'");
$wpdb->query("DELETE FROM {$wpdb->comments} WHERE comment_approved = 'trash'");
return 0.5;
}
private function cleanup_logs() {
$debug_log = WP_CONTENT_DIR . '/debug.log';
if (file_exists($debug_log) && filesize($debug_log) > 50 * 1024 * 1024) {
// 50MB 이상이면 마지막 1만 줄만 보존
$lines = file($debug_log);
$keep = array_slice($lines, -10000);
file_put_contents($debug_log, implode('', $keep));
return round((count($lines) - 10000) * 0.0001, 2);
}
return 0;
}
private function cleanup_orphan_meta() {
global $wpdb;
$wpdb->query("DELETE pm FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID WHERE p.ID IS NULL");
return 0.5;
}
private function measure_self_response() {
$start = microtime(true);
wp_remote_get(home_url('/'), array('timeout' => 10, 'sslverify' => false));
return round((microtime(true) - $start) * 1000);
}
private function check_db_corruption() {
global $wpdb;
$result = $wpdb->get_results("CHECK TABLE {$wpdb->posts}, {$wpdb->options}", ARRAY_A);
foreach ($result as $row) {
if (isset($row['Msg_type']) && $row['Msg_type'] === 'error') return true;
}
return false;
}
private function check_maintenance_mode() {
$file = ABSPATH . '.maintenance';
return file_exists($file) && (time() - filemtime($file)) > 600;
}
private function rebuild_rewrite_rules() {
flush_rewrite_rules(false);
}
private function warm_page_cache() {
$pages = array('/', '/blog/', '/shop/');
foreach ($pages as $page) {
wp_remote_get(home_url($page), array('timeout' => 5, 'sslverify' => false));
}
}
// ========================================
// 로깅 및 알림
// ========================================
private function log_action($issue, $action, $result) {
global $wpdb;
$wpdb->insert($this->log_table, array(
'issue_type' => $issue['type'],
'severity' => $issue['severity'],
'issue_value' => json_encode($issue['value'] ?? null),
'action_taken' => $action,
'result' => $result,
'created_at' => current_time('mysql'),
));
}
private function notify_action($issue, $action, $result) {
$severity_emoji = array(
'critical' => '🚨', 'high' => '🔴', 'warning' => '⚠️', 'info' => 'ℹ️'
);
$emoji = $severity_emoji[$issue['severity']] ?? '🔧';
wp_remote_post(MAKE_HEALING_WEBHOOK, array(
'body' => json_encode(array(
'event' => 'self_healing_action',
'site' => home_url(),
'issue_type' => $issue['type'],
'severity' => $issue['severity'],
'action' => $action,
'result' => $result,
'message' => "{$emoji} [{$issue['type']}] {$action} ({$result})",
'timestamp' => current_time('c'),
)),
'headers' => array('Content-Type' => 'application/json'),
));
}
private function get_recovery_count_last_hour() {
global $wpdb;
return (int) $wpdb->get_var(
"SELECT COUNT(*) FROM {$this->log_table} WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)"
);
}
private function notify($message, $severity) {
wp_remote_post(MAKE_HEALING_WEBHOOK, array(
'body' => json_encode(array('event' => 'self_healing_notice', 'message' => $message, 'severity' => $severity)),
'headers' => array('Content-Type' => 'application/json'),
));
}
}
new Self_Healing_WordPress();
🔒
여기까지는 미리보기입니다
24/7 무인 운영 시스템
무료 가입하면 이어서 볼 수 있고, 강의를 완료할 때마다 XP와 레벨이 쌓입니다.
Google로 3초 만에 시작 →🧵 Threads로 시작무료 공개 강의 둘러보기 (Lv.1~3)무료 가입하면 이어서 볼 수 있고, 강의를 완료할 때마다 XP와 레벨이 쌓입니다.