한 해가 바뀌면 같은 자리에서 다시 멈춥니다

10월 초에 이 서버의 업로드 폴더 권한을 고쳤습니다. 매달 1일에만 이미지 업로드가 실패하던 문제였고, 연도 폴더 두 칸의 소유자와 모드를 바꾸는 것으로 끝났습니다. 그때 글에 11월 1일이 되면 실제 업로드로 다시 재겠다고 적어 두었습니다. 고쳤다는 말과 고쳐졌다는 말은 다르기 때문입니다.

11월 1일이 지났습니다. 성공했습니다. 그런데 같은 방법으로 2027년 1월을 미리 시험해 보니 실패했습니다. 한 달 뒤는 괜찮고 두 달 뒤에 다시 멈춥니다. 고친 자리가 한 칸 낮았습니다. 매달 1일에만 멈추던 업로드를 다룰 때 놓친 것이 무엇인지 이 글에 적습니다.

11월 1일에는 성공했습니다

이 서버에는 블로그가 세 개 있고 업로드 폴더가 따로 나뉩니다. 11월 1일 아침에 세 폴더가 모두 만들어졌습니다.

폴더 생성 시각 모드 소유자
sites/2/2026/11 11-01 09:27:06.236 2775 apache:apache
sites/3/2026/11 11-01 09:31:06.534 2775 apache:apache
2026/11 11-01 09:41:25.128 2775 apache:apache

세 폴더 모두 모드가 2775이고 소유자가 apache입니다. 2775의 앞자리 2는 setgid 비트이고, 이것이 붙어 있으면 그 안에 새로 생기는 것이 부모의 그룹을 물려받습니다. 10월에 직접 그렇게 만들어 두었고 11월에 그대로 상속됐습니다. 예측한 대로입니다.

세 시각이 09시 27분, 31분, 41분으로 흩어져 있는 것이 눈에 걸립니다. 자정이 아니라 아침이고, 세 블로그가 14분씩 벌어져 있습니다. 1일 0시에 일괄로 만드는 무언가가 있다면 이렇게 되지 않습니다.

폴더를 만든 것은 업로드가 아니었습니다

그 시각의 접속 로그를 열었습니다. 세 폴더의 생성 시각과 초 단위로 맞는 요청이 있었습니다.

09:27:06  POST /wp-cron.php  200  "WordPress/7.0.8; https://kilho.org"
09:31:06  POST /wp-cron.php  200  "WordPress/7.0.8; https://damyo.net"
09:41:25  POST /wp-cron.php  200  "WordPress/7.0.8; https://wp.demobox.top"

요청을 보낸 주소는 서버 자기 자신이고 User-Agent에 어느 블로그가 보낸 것인지 적혀 있습니다. 세 번의 요청 시각이 세 폴더의 생성 시각과 같습니다. 폴더를 만든 것은 누군가의 이미지 업로드가 아니라 예약 작업입니다.

그래서 “매달 1일에만 업로드가 실패한다”는 증상의 설명이 바뀝니다. 1일에 사람이 업로드를 하다가 실패한 것이 아닙니다. 그 달의 첫 예약 작업이 폴더를 만들려다 실패했고, 그다음에 사람이 업로드를 하려니 폴더가 없어서 또 실패한 것입니다. 실패는 사람이 손대기 전에 이미 일어나 있었습니다. 워드프레스 크론을 깨우는 것은 방문자가 아니었다는 것을 전에 재 봤는데, 이번에는 그 크론이 폴더까지 만들고 있었습니다.

소수점까지 같은 시각이 하나 더 있습니다

연도 폴더의 생성 시각을 같이 꺼내 보면 이상한 것이 보입니다.

uploads/2026        2026-04-01 11:37:23.068697107
uploads/2026/04     2026-04-01 11:37:23.068697107   ← 나노초까지 같다

sites/2/2026        2026-07-03 19:18:04.762645770
sites/2/2026/07     2026-07-03 19:18:04.763645777   ← 1밀리초 차이

연도 폴더와 그 안의 첫 월 폴더가 사실상 같은 순간에 만들어졌습니다. 두 번에 걸쳐 만든 것이 아니라 한 번의 재귀 생성으로 두 칸이 같이 생긴 것입니다. 워드프레스는 필요한 경로를 위에서부터 만들어 내려오므로, 그 순간에는 연도 폴더를 만들 수 있는 상태였다는 뜻이 됩니다. 지금은 그렇지 않습니다. 이것이 다음 절로 이어집니다.

2027년 1월 1일에는 실패합니다

11월이 됐으니 다음 고비는 12월 1일이 아니라 1월 1일입니다. 달이 바뀌는 것과 해가 바뀌는 것은 만들어야 하는 폴더 수가 다릅니다. 12월은 2026/12 한 칸이고, 1월은 2027과 2027/01 두 칸입니다.

기다리지 않고 미리 재 봤습니다. 실제로 쓰이는 사용자인 apache 권한으로 연도 폴더를 직접 만들어 봤습니다.

만들려는 폴더 결과
uploads/2027 Permission denied
uploads/sites/2/2027 Permission denied
uploads/sites/3/2027 Permission denied

세 블로그 전부 실패합니다. 워드프레스 쪽 함수로도 같은 결과가 나옵니다. 경로를 2027년 1월로 지정해 업로드 폴더를 물어보면 경로는 돌려주지만 오류가 함께 붙습니다.

subdir = /2027/01
error  = 디렉터리 wp-content/uploads/sites/2/2027/01 생성 불가.
         서버의 부모 디렉터리가 쓰기 가능한가요?

시험으로 만든 것은 전부 지웠고, 지금 각 블로그의 연도 폴더는 2026 하나뿐입니다.

고친 자리가 한 칸 낮았습니다

이유는 권한 사슬을 위에서부터 보면 바로 나옵니다.

경로 모드 소유자 apache가 쓸 수 있나
wp-content 755 root:root 아니오
uploads 755 root:root 아니오
uploads/sites 755 root:root 아니오
uploads/sites/2 755 root:root 아니오
uploads/sites/2/2026 2775 apache:apache 예

10월에 고친 것은 맨 아래 줄입니다. 연도 폴더를 열었으니 그 안에 월 폴더를 만드는 것은 됩니다. 11월 1일이 성공한 이유가 이것입니다. 그런데 연도 폴더 자체를 만드는 데 필요한 것은 그 위 칸이고, 그 위 칸은 닫혀 있습니다.

10월에는 이 판단을 다르게 했습니다. 맨 위가 닫혀 있는데도 한 블로그가 일곱 달 동안 멀쩡했으니 맨 위를 열 필요가 없다고 봤습니다. 그 관찰 자체는 맞았습니다. 일곱 달이 모두 같은 해 안이었기 때문입니다. 해를 넘는 사례가 그 일곱 달 안에 없었고, 그래서 검증되지 않은 가정이 그대로 남았습니다.

어떻게 쟀는가

폴더 생성 시각은 파일 시스템이 기록한 생성 시각을 나노초까지 그대로 읽었습니다. 수정 시각이 아니라 생성 시각입니다. 둘은 다를 수 있고, 10월에 문제를 추적할 때 이 차이가 단서였습니다.

로그는 살아 있는 접속 로그를 썼습니다. 11월 1일 오전 기록은 회전 보관된 파일이 아니라 지금 쓰이는 파일에 들어 있습니다. 처음에 회전된 쪽을 열어 아무것도 못 찾았는데, 로그가 1일 0시에 회전하므로 1일 오전은 새 파일에 있습니다.

권한 시험은 추측을 피하기 위해 넣었습니다. 모드와 소유자를 보고 “안 될 것이다”라고 적을 수도 있었지만, 실제로 쓰이는 사용자 권한으로 mkdir을 해 보는 것과는 다릅니다. php-fpm 풀이 apache로 돌기 때문에 apache로 시험했습니다. 명령줄 도구는 다른 사용자로 돌아서 결과가 달라질 수 있습니다.

이 숫자가 말하지 못하는 것

연도 폴더가 처음 생긴 순간에 왜 만들 수 있었는지는 모릅니다. 나노초가 같다는 것은 한 번에 재귀 생성됐다는 뜻이지 그때 권한이 어땠는지를 알려 주지는 않습니다. 그 뒤에 누가 상위 폴더를 root 소유 755로 바꿨는지, 아니면 처음부터 그랬고 다른 경로로 만들어졌는지 가릴 자료가 없습니다.

1월 1일에 실제로 실패할 것이라는 말은 지금 권한이 그대로일 때의 이야기입니다. 그 사이에 고치면 당연히 실패하지 않습니다. 이 글은 예보가 아니라 지금 상태의 측정입니다.

크론이 폴더를 만든다는 것은 시각 일치로 확인했지만, 어느 예약 작업의 어느 함수가 그 경로를 요구했는지까지는 가리지 못했습니다. 작업 목록에 월 단위 주기를 가진 것은 없고 시간·일·주 단위만 있습니다. 그중 하나가 업로드 경로를 물어보면서 생기는 일로 보이는데, 특정하려면 그 달의 첫 요청을 다시 기다려야 합니다.

아직 고치지 않았습니다

지금 고치려면 선택지가 둘입니다. 상위 폴더를 apache가 쓸 수 있게 열면 1월 1일에 재귀 생성이 되고 끝납니다. 다만 열어야 하는 칸이 업로드 폴더의 맨 위라서 범위가 넓어집니다. 다른 방법은 연말에 2027 폴더만 미리 만들어 두는 것입니다. 10월에 한 조치와 같은 모양이고, 그러면 2028년 1월에 또 같은 일을 합니다.

둘 중 무엇이 맞는지는 이 서버를 어떻게 쓰기로 하느냐에 달려 있어서 지금 정하지 않았습니다. 고치지 않은 상태로 적어 둡니다. 고치면 그때 이 글을 고치겠습니다.

다시 잴 것

12월 1일에 2026/12 세 칸이 자동으로 생기는지 한 번 더 봅니다. 같은 해 안이므로 성공해야 합니다. 성공하지 않으면 이 글의 설명이 틀린 것입니다.

2027년 1월 1일에는 세 블로그의 연도 폴더를 봅니다. 그때까지 권한을 고치지 않았다면 폴더가 없어야 하고, 실제 업로드가 실패해야 합니다. 예측과 다르면 상위 폴더 말고 다른 경로로 만들어지는 길이 있다는 뜻입니다.

자주 묻는 것

11월에 성공했는데 왜 다시 깨진다고 합니까

11월에 성공한 것은 열어 둔 연도 폴더 안에 월 폴더를 한 칸 더한 일입니다. 1월은 그 연도 폴더 자체를 새로 만들어야 하고, 그 작업에 필요한 권한은 한 칸 위에 있습니다. 그 위 칸은 닫혀 있습니다. 10월의 조치는 두 경우 중 앞쪽만 해결했고, 그때는 해를 넘는 사례가 측정 구간 안에 없어서 뒤쪽이 드러나지 않았습니다.

12월 1일과 1월 1일이 왜 다릅니까

만들어야 하는 칸 수가 다릅니다. 12월은 이미 열려 있는 2026 폴더 안에 한 칸을 더하는 일이고, 1월은 닫혀 있는 칸 안에 2027 폴더를 새로 만드는 일입니다. 월이 바뀌는 것과 해가 바뀌는 것이 같은 일로 보이지만 경로의 깊이가 다릅니다. 일곱 달 동안 문제가 없었던 것도 그 일곱 달이 모두 한 해 안이었기 때문입니다.

크론이 폴더를 만들었다는 것을 어떻게 가렸습니까

시각으로 가렸습니다. 폴더 세 개의 생성 시각을 나노초까지 읽고, 같은 아침의 접속 로그에서 같은 초에 들어온 요청을 찾았습니다. 세 번 모두 서버가 자기 자신에게 보낸 예약 작업 요청이었고, User-Agent에 어느 블로그가 보낸 것인지 적혀 있어 폴더와 블로그가 짝지어졌습니다. 다만 이것은 상관이고, 그 요청 안에서 어느 함수가 경로를 요구했는지는 가리지 못했습니다.

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다