새로운 홈페이지와 기술 블로그를 추가하면서 가장 먼저 세운 원칙은 “기존 서비스에 영향을 주지 않는다”였습니다. 이 글에서는 이미 운영 중인 제품 옆에 WordPress를 별도 환경으로 배치하고, 공개 전후 상태를 검증한 과정을 정리합니다.
왜 격리가 먼저였나
TAPAS에는 서로 다른 역할을 맡은 서비스가 이미 운영되고 있습니다. 이런 환경에서 회사 홈페이지를 추가할 때 가장 위험한 선택은 새 서비스의 편의를 위해 기존 네트워크, 데이터베이스 또는 배포 경로를 함께 사용하는 것입니다.
따라서 WordPress를 설치하기 전에 다음 경계를 먼저 정했습니다.
- WordPress 전용 프로젝트와 Docker 네트워크를 사용한다.
- 데이터베이스 포트를 호스트나 인터넷에 공개하지 않는다.
- 웹 서버로 전달되는 포트는 loopback 주소에만 바인딩한다.
- 테마와 플러그인은 프로젝트 안에서 명시적으로 관리한다.
- 기존 제품의 저장소와 설정 파일을 WordPress 컨테이너에 연결하지 않는다.
작게 분리한 실행 구조
실행 구조는 Cloudflare, nginx, WordPress, MariaDB의 네 단계로 나눴습니다. 외부 요청은 Cloudflare를 거쳐 전용 nginx 가상 호스트로 들어오고, nginx만 loopback upstream을 통해 WordPress에 접근합니다. MariaDB는 Docker 내부 네트워크에서만 WordPress와 통신합니다.
Internet
↓
Cloudflare
↓
nginx virtual host
↓
127.0.0.1:8085
↓
WordPress ── internal network ── MariaDB
WordPress upstream을 loopback에만 바인딩하면 호스트 밖에서 해당 포트로 직접 접근할 수 없습니다. 데이터베이스에는 아예 호스트 포트 매핑을 만들지 않았습니다.
services:
wordpress:
ports:
- "127.0.0.1:8085:80"
networks:
- internal
db:
networks:
- internal
# published port 없음
컨테이너에도 최소 권한 적용하기
네트워크만 분리해도 충분하지 않습니다. WordPress와 데이터베이스 컨테이너에는 CPU·메모리·프로세스 수 제한을 지정하고, no-new-privileges를 적용했습니다. 로그는 크기와 보관 파일 수를 제한해 디스크를 무제한으로 사용하지 않도록 했습니다.
- 전용 Compose 프로젝트와 내부 네트워크
- 컨테이너별 CPU·메모리·PID 제한
no-new-privileges:true- 크기 제한이 있는 JSON 로그
- 테마와 플러그인의 read-only 마운트
- 서버 로컬 비밀 설정 파일과 제한된 파일 권한
관리 편의를 위해 컨테이너에 호스트 전체 경로나 Docker 소켓을 전달하지 않았습니다. WordPress에서 파일 편집 기능도 비활성화해 관리자 화면을 통한 코드 변경 경로를 줄였습니다.
배포보다 중요한 자동 검증
컨테이너가 실행 중이라는 사실만으로는 서비스가 정상이라고 판단할 수 없습니다. 배포 후 한 번의 명령으로 주요 경계를 확인할 수 있도록 검증 스크립트를 만들었습니다.
cd /opt/tapas-wordpress
./scripts/verify.sh
검증 항목에는 다음 내용이 포함됩니다.
- WordPress와 MariaDB 컨테이너가 정상 실행 중인지
- 홈페이지, 기술 블로그, 고객 피드백 페이지의 핵심 마커가 응답에 포함되는지
- WordPress의 공개 URL 설정이 실제 도메인과 일치하는지
- 전용 테마와 피드백 플러그인이 활성화되어 있는지
- MariaDB 포트가 호스트에 공개되지 않았는지
- nginx 설정 문법이 유효한지
특히 데이터베이스 비공개 여부는 설정 파일만 읽지 않고, 실행 중인 컨테이너의 실제 포트 상태를 검사합니다. 선언된 설정과 현재 실행 상태가 달라질 수 있기 때문입니다.
공개 전후에 지킨 순서
외부 공개는 다음 순서로 진행했습니다.
- loopback 주소에서 WordPress와 테마를 먼저 검증한다.
- 전용 nginx 가상 호스트를 추가한다.
nginx -t가 성공한 경우에만 설정을 reload한다.- Cloudflare 프록시와 Origin TLS 연결을 확인한다.
- WordPress의 사이트 URL을 공개 주소로 변경한다.
- 데스크톱과 모바일 화면을 확인한다.
- 홈페이지뿐 아니라 기존 제품과 피드백 페이지도 다시 검사한다.
새 홈페이지가 정상이어도 기존 서비스가 영향을 받았다면 배포는 성공한 것이 아닙니다. 그래서 회귀 검증 범위를 새 WordPress 사이트에 한정하지 않았습니다.
작업 중 발견한 실제 함정
테마가 컨테이너에 정상적으로 마운트됐는데도 WordPress에서 찾을 수 없는 문제가 있었습니다. 원인은 코드가 아니라 호스트 파일 권한이었습니다. 디렉터리와 파일이 웹 서버 사용자가 읽을 수 없는 권한으로 복사되어 있었습니다.
이 문제는 다음 교훈을 남겼습니다.
- 마운트 성공과 애플리케이션의 실제 읽기 가능 여부는 별개다.
- 컨테이너 안에서 실행되는 사용자 기준으로 권한을 검증해야 한다.
- 배포 스크립트에는 파일 존재 여부뿐 아니라 기능 활성화 검사도 포함해야 한다.
격리는 구조가 아니라 운영 습관이다
서비스를 별도 컨테이너에 넣는 것만으로 격리가 완성되지는 않습니다. 변경 범위를 제한하고, 금지할 작업을 정하고, 공개 전후에 같은 기준으로 검증해야 실제 운영 격리가 유지됩니다.
이번 작업에서 가장 유용했던 원칙은 단순했습니다.
새 서비스의 정상 동작과 기존 서비스의 무영향을 함께 증명해야 배포가 끝난다.
앞으로 TAPAS Engineering에서는 tapas-eye의 터미널 UI 설계, tapas-bot의 문서 기반 RAG, 실제 운영 과정에서 발견한 문제와 해결 방법을 계속 기록하겠습니다. 의견이나 다뤘으면 하는 주제는 고객 피드백에서 남길 수 있습니다.
