배포판이 넣어 주는 httpd.conf 는 systemd 가 데몬으로 띄우는 것을 전제로 한다. 컨테이너에서는 그 전제가 세 군데에서 어긋난다.
| 전제 | 컨테이너에서 어긋나는 지점 |
|---|---|
| 프로세스가 백그라운드로 떨어진다 | PID 1 이 바로 끝나 컨테이너가 죽는다 |
로그를 /var/log/httpd/*.log 파일에 쓴다 |
docker logs 에 아무것도 안 나온다 |
/run · /var/run 에 PID 파일과 소켓을 쓴다 |
비-root 로 실행하면 쓰기가 막힌다 |
세 가지를 설정으로 정리하면 기동 로그가 깨끗해진다.
RHEL 계열 패키지는 -D FOREGROUND 를 준다.
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
공식 httpd 이미지는 같은 일을 하는 httpd-foreground 스크립트를 가지고 있으므로 그것을 쓴다.
CMD ["httpd-foreground"]
httpd -k start 는 쓰지 않는다. 데몬으로 떨어지면서 PID 1 이 즉시 종료된다.
로그 수집기가 읽는 것은 컨테이너의 표준 출력이다. 설정에서 직접 가리킨다.
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
/dev/stdout 이 다른 것을 가리키는 이미지에서는 파일 디스크립터를 직접 쓴다. 이쪽이 더 안전하다.
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 combined
패키지가 넣어 둔 로그 파일 경로를 심볼릭 링크로 돌리는 방법도 쓰인다. 설정을 통째로 마운트해 갈아 끼우는 구성에서는 이쪽이 편하다.
RUN ln -sf /dev/stdout /var/log/httpd/access_log \
&& ln -sf /dev/stderr /var/log/httpd/error_log
컨테이너를 비-root 로 돌리면 /run 과 /var/run 은 root 소유라 쓸 수 없다. 쓰기 가능한 경로로 옮긴다.
PidFile /tmp/httpd.pid
CGI 를 쓴다면 mod_cgid 의 유닉스 소켓도 같이 옮긴다. ScriptSock 은 파일명 접두사이고 실제 파일에는 프로세스 ID 가 붙는다[1].
ScriptSock /tmp/cgisock
CGI 를 전혀 쓰지 않는다면 모듈 자체를 적재하지 않는 편이 낫다. conf.modules.d 에서 해당 줄을 주석 처리한다.
#LoadModule cgid_module modules/mod_cgid.so
mod_cgid 를 mod_cgi 로 바꿔 끼우는 것으로 해결하려 들지 않는다. mod_cgid 는 스레드 MPM(event · worker)용이고 mod_cgi 는 비스레드 MPM(prefork)용이다. RHEL 계열 기본 MPM 은 event 이므로, 모듈만 바꾸면 MPM 과 어긋난다.
| 메시지 | 수준 | 원인과 조치 |
|---|---|---|
AH00558: Could not reliably determine the server's fully qualified domain name |
warn | ServerName 이 없어 IP 로 대신했다. ServerName localhost 한 줄이면 사라진다 |
AH02282: No slotmem from mod_heartmonitor |
notice | mod_lbmethod_heartbeat 가 mod_heartmonitor 의 공유 메모리를 못 찾았다. 로드밸런싱을 쓰지 않으면 두 모듈을 적재하지 않는다 |
AH00098: pid file ... overwritten -- Unclean shutdown of previous Apache run? |
warn | 이전 실행의 PID 파일이 남아 있었다. 동작에는 영향이 없다. 시작 스크립트에서 지우면 사라진다 |
AH01238: cgid daemon failed to initialize |
crit | mod_cgid 가 소켓을 만들지 못했다. ScriptSock 을 쓰기 가능한 경로로 옮기거나 모듈을 끈다 |
AH01243: Couldn't bind unix domain socket ... |
error | 같은 원인. 이전 소켓 파일이 남아 있는 경우도 포함된다 |
AH00099 · AH00100: could not create/log pid to file |
error | PID 파일 경로에 쓰기 권한이 없다. PidFile 을 옮긴다 |
AH00489: Apache/2.4.x configured -- resuming normal operations |
notice | 정상 기동 완료 |
AH00489 가 찍히면 그 위의 warn · notice 는 대부분 남겨 둬도 되는 것들이다. 서비스가 안 뜨는 것과 로그가 지저분한 것을 섞어서 보지 않는다.
ServerName localhost
PidFile /tmp/httpd.pid
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
AddDefaultCharset UTF-8
시작할 때 남은 PID 파일을 지우려면 entrypoint 스크립트를 두고 마지막을 exec 로 맺는다. exec 가 있어야 httpd 가 PID 1 을 넘겨받아 docker stop 의 SIGTERM 을 받는다.
#!/bin/sh
rm -f /tmp/httpd.pid
exec /usr/sbin/httpd -D FOREGROUND
docker exec -it web httpd -t
docker exec -it web httpd -M | grep -E 'cgid|php|proxy'
docker logs -f web
httpd -t 가 Syntax OK 를 내야 재시작이 의미가 있다. 설정을 마운트로 갈아 끼웠다면 컨테이너를 재시작한다.
.php 를 실행시키는 설정.restart 와 graceful 의 차이.exec "$@" 로 PID 1 을 넘기는 방법.ScriptSock 기본값은 cgisock 이고 실제 소켓 파일명에는 프로세스 ID 가 확장자로 붙는다 — 2026-09-20 확인. https://httpd.apache.org/docs/2.4/mod/mod_cgid.html ↩︎