워드프레스 + containerd 조합이면, 관리형 워드프레스 호스팅보다 VPS/Kubernetes 쪽이 더 맞는 경우가 많고, 가성비는 보통 “작은 VPS + containerd 기반 직접 운영”이 가장 좋습니다. 다만 워드프레스 자체는 컨테이너화가 가능하지만, 대부분의 사이트에는 Kubernetes 같은 복잡한 오케스트레이션이 과하다고 보는 의견이 많습니다.
추천 순위
containerd 관점
containerd는 Kubernetes의 기본 런타임으로 널리 쓰이며, Docker Engine보다 가볍고 Kubernetes와의 궁합이 좋다고 안내됩니다. 그래서 “워드프레스 + containerd”를 굳이 같이 쓴다면, 보통은 K3s/Kubernetes + containerd 조합으로 VPS 위에 올리는 구성이 자연스럽습니다. 반대로 워드프레스만 단독 운영한다면, containerd를 넣는 것보다 단일 VPS에 Nginx/PHP-FPM/MariaDB를 더 단순하게 구성하는 편이 비용 대비 효율이 좋습니다.
현실적인 조합
-
최저 가성비: DigitalOcean 또는 Vultr의 소형 VPS에 containerd/K3s로 직접 구성.
-
한국 체감 속도 우선: Vultr 서울 리전 쪽이 가장 먼저 검토할 만합니다.
-
운영 편의성 우선: Cloudways 같은 관리형 서비스를 쓰되, containerd 직접 운용 목적은 포기하는 편이 낫습니다.
-
초기 블로그/소규모 사이트: 컨테이너 오케스트레이션 없이 가벼운 VPS 한 대로 시작하는 편이 더 싸고 관리도 쉽습니다.
추천 기준
-
예산이 가장 중요하면 DigitalOcean/Vultr 소형 VPS를 우선 보세요.
-
한국 방문자 비중이 높으면 서울 리전이 있는 Vultr를 먼저 보세요.
-
자동복구, 이식성, 배포 일관성이 필요하면 K3s + containerd 조합이 적합합니다.
-
워드프레스 하나만 운영한다면 containerd는 과투자일 수 있습니다.
원하시면 다음 단계로 “월 1만 원대 / 3만 원대 / 5만 원대”로 나눈 실제 구성안까지 정리해드릴 수 있습니다.
월 1만 원대면 관리형 워드프레스보다 직접 운영형 VPS가 현실적이고, containerd는 보통 K3s나 단일 노드 컨테이너 스택에 붙이는 방식이 맞습니다. 한국 기준으로는 Vultr 서울 리전 + 아주 작은 VPS가 가장 무난한 출발점이고, 더 단순하게는 WordPress만 올리는 저가 VPS도 가능합니다.
1만 원대 구성안
가장 현실적인 1만 원대 설계
-
VPS 1대를 잡습니다. 워드프레스는 1vCPU/1GB RAM급에서도 시작은 가능하지만, 이미지 최적화와 캐시 없이 쓰면 빠듯합니다.
-
containerd + Nginx + PHP-FPM + MariaDB 또는 K3s 단일 노드로 올립니다.
-
외부 DB는 피하고 같은 VPS 안에 DB를 두면 비용이 내려갑니다. 대신 백업은 꼭 따로 가져가야 합니다.
-
캐시 플러그인과 정적 파일 최적화를 넣으면 작은 사양에서도 버틸 수 있습니다.
추천 플랫폼
-
Vultr: 서울 리전이 있어 국내 체감이 좋고, 직접 운영형에 잘 맞습니다.
-
DigitalOcean: 가격이 단순하고 시작 비용이 낮아서 예산 관리가 쉽습니다.
-
Linode/Akamai: 안정적인 VPS 운영에 무난하지만, 체감 성능은 리전 선택이 중요합니다.
-
카페24 워드프레스 VPS: 국내 지원이 편하지만 1만 원대보다는 위로 올라가는 편입니다.
사양 감각
-
1인 블로그, 하루 방문자 적음: 1vCPU / 1GB RAM / 25~40GB SSD 정도로 시작.
-
이미지 많은 블로그: 1vCPU / 2GB RAM이 훨씬 안정적입니다.
-
containerd/K3s까지 넣을 때: 최소 2GB RAM 쪽이 편하고, 1GB는 매우 타이트합니다.
-
트래픽이 늘면: VPS보다 캐시 CDN과 스토리지 분리를 먼저 고려하는 게 가성비가 좋습니다.
비용을 깎는 방법
-
외부 관리형 DB를 쓰지 말기.
-
도메인과 서버를 분리해 관리하고, 백업은 오브젝트 스토리지로 빼기.
-
캐시 플러그인 + 이미지 압축으로 CPU/메모리 사용량 낮추기.
-
멀티사이트, Kubernetes, 고가 백업 옵션은 초기엔 생략하기.
한 줄 추천
예산이 정말 월 1만 원대라면 Vultr 서울 리전 소형 VPS 1대 + containerd(선택) + WordPress 단일 사이트가 가장 균형이 좋습니다. 운영 편의성보다 가성비를 우선하면 이 구성이 맞고, 편의성이 필요하면 1만 원대는 빠르게 넘어갑니다.
원하면 다음 답변에서 “VPS 1대 기준 실제 설치 스택”을 Ubuntu + containerd + WordPress + Nginx + MariaDB 형태로 바로 써드릴게요.

아래처럼 바로 쓰면 됩니다. 이 구성은 VPS 1대에서 Ubuntu 위에 containerd 기반으로 WordPress를 올리고, Nginx를 리버스 프록시로 두고, MariaDB를 같은 서버 내부에 두는 가장 단순한 실전형입니다.
목표 구조
-
Ubuntu: OS.
-
containerd: 컨테이너 런타임.
-
WordPress: PHP-FPM 컨테이너.
-
Nginx: 호스트 또는 별도 컨테이너에서 80/443 처리.
-
MariaDB: 데이터 저장용 컨테이너.
-
볼륨: WordPress 업로드와 DB 데이터 영속화.
권장 배치
옵션 A: 가장 단순
-
Nginx는 호스트 설치
-
WordPress + MariaDB는 containerd 위 컨테이너
-
장점: 디버깅이 쉽고, HTTPS 붙이기 편함.
-
단점: 완전한 “전부 컨테이너”는 아님.
옵션 B: 전부 컨테이너
-
Nginx / WordPress / MariaDB 모두 컨테이너
-
장점: 이식성과 재현성이 좋음.
-
단점: VPS 1대에서 네트워크/볼륨/헬스체크 관리가 조금 더 복잡함.
바로 쓰는 설치 스택
1) Ubuntu 기본 준비
-
Ubuntu 22.04 LTS 또는 24.04 LTS 추천.
-
서버 기본 패키지 업데이트 후, 방화벽에서 80, 443, 22만 열어둡니다.
-
시간대는
Asia/Seoul로 맞춥니다.
2) containerd 설치
-
Ubuntu에 containerd 설치.
-
systemd cgroup사용. -
ctr또는nerdctl로 컨테이너 관리. -
단일 VPS라면 K3s까지 안 올리고, 직접 컨테이너 실행만 해도 충분합니다.
3) MariaDB 컨테이너
-
데이터 볼륨:
/srv/mariadb -
포트: 내부 전용
3306 -
환경변수:
-
DB 이름:
wordpress -
DB 사용자:
wpuser -
비밀번호: 강한 값
-
-
외부 노출은 하지 않습니다.
4) WordPress 컨테이너
-
이미지:
wordpress:php8.x-fpm계열 권장 -
업로드 볼륨:
/srv/wordpress -
연결 대상 DB:
mariadb:3306 -
wp-config.php는 환경변수 또는 초기 설치 화면으로 생성.
5) Nginx
-
호스트 Nginx를 추천합니다.
-
역할:
-
80/443 수신
-
정적 파일 처리
-
PHP 요청을 WordPress FPM으로 전달
-
Let’s Encrypt 연동.
-
디렉터리 예시
/srv/
mariadb/
wordpress/
nginx/권한은 다음처럼 정리합니다.
sudo mkdir -p /srv/mariadb /srv/wordpress /srv/nginx
sudo chown -R root:root /srv
sudo chmod -R 755 /srv실행 순서
-
Ubuntu 업데이트.
-
containerd 설치.
-
MariaDB 컨테이너 실행.
-
WordPress 컨테이너 실행.
-
Nginx 설정.
-
도메인 연결.
-
HTTPS 설정.
-
백업 설정.
추천 설정값
1GB RAM으로도 시작은 가능하지만, WordPress와 DB를 함께 돌리면 캐시 없이는 빠듯합니다. 그래서 실사용 기준 최소 2GB RAM이 훨씬 편합니다.
운영 팁
-
DB 백업은 매일 덤프 떠서 별도 저장소로 빼세요.
-
WordPress 업로드 폴더는 반드시 볼륨으로 유지하세요.
-
Redis 캐시는 트래픽 늘기 전까지는 생략해도 됩니다.
-
CDN은 이미지가 많아질 때 붙이세요.
-
Kubernetes/K3s는 단일 블로그에는 과합니다.
한 줄 구성안
Ubuntu 24.04 + containerd + MariaDB 컨테이너 + WordPress FPM 컨테이너 + 호스트 Nginx가, 월 1만 원대 VPS에서 가장 현실적인 조합입니다.
원하면 다음 답변에서 바로 이어서 docker-compose가 아니라 containerd + nerdctl 기준 실행 명령어 세트로 써드리겠습니다.

아래는 Ubuntu + containerd + nerdctl + WordPress + Nginx + MariaDB 기준으로 바로 실행할 수 있는 형태의 예시입니다.
단일 VPS에서 돌리는 실전형 최소 구성이라, 먼저 네트워크/볼륨/환경변수까지 한 번에 잡아두는 방식으로 작성했습니다.
0) 전제
-
OS: Ubuntu 22.04/24.04
-
런타임: containerd
-
CLI: nerdctl
-
웹: Nginx
-
앱: WordPress
-
DB: MariaDB
-
목표: 외부에선 Nginx만 노출, WordPress와 MariaDB는 내부 네트워크로만 통신
1) 디렉터리 생성
sudo mkdir -p /srv/wp/{nginx,wordpress,mariadb,certbot}
sudo chown -R $USER:$USER /srv/wp
cd /srv/wp2) 네트워크 생성
sudo nerdctl network create wp-net3) MariaDB 실행
sudo nerdctl run -d \
--name wp-mariadb \
--network wp-net \
-e MARIADB_ROOT_PASSWORD='RootPass123!' \
-e MARIADB_DATABASE='wordpress' \
-e MARIADB_USER='wpuser' \
-e MARIADB_PASSWORD='WpPass123!' \
-v /srv/wp/mariadb:/var/lib/mysql \
--restart unless-stopped \
mariadb:11.44) WordPress 실행
PHP-FPM 기반 이미지로 띄우는 방식입니다.
sudo nerdctl run -d \
--name wp-wordpress \
--network wp-net \
-e WORDPRESS_DB_HOST='wp-mariadb:3306' \
-e WORDPRESS_DB_NAME='wordpress' \
-e WORDPRESS_DB_USER='wpuser' \
-e WORDPRESS_DB_PASSWORD='WpPass123!' \
-v /srv/wp/wordpress:/var/www/html \
--restart unless-stopped \
wordpress:php8.3-fpm5) Nginx 설정 파일
/srv/wp/nginx/default.conf 파일을 만듭니다.
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass wp-wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
6) Nginx 실행
호스트 Nginx를 써도 되지만, containerd 기준으로 같이 올리면 관리가 단순합니다.
sudo nerdctl run -d \
--name wp-nginx \
--network wp-net \
-p 80:80 \
-v /srv/wp/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
-v /srv/wp/wordpress:/var/www/html \
--restart unless-stopped \
nginx:1.277) HTTPS 붙이기 전 최소 점검
sudo nerdctl ps
sudo nerdctl logs wp-mariadb
sudo nerdctl logs wp-wordpress
sudo nerdctl logs wp-nginx브라우저에서 서버 IP로 접속하면 WordPress 초기 설치 화면이 떠야 합니다.
8) 권장 운영 팁
-
DB 비밀번호는 반드시 바꾸세요.
-
/srv/wp/mariadb와/srv/wp/wordpress는 주기적으로 백업하세요. -
워드프레스 업로드 폴더가 늘어나면 디스크가 금방 찹니다.
-
트래픽이 적으면 이 구성이 충분하고, 캐시는 나중에 붙여도 됩니다.
9) 정리된 실행 순서
sudo mkdir -p /srv/wp/{nginx,wordpress,mariadb}
sudo nerdctl network create wp-net
sudo nerdctl run -d --name wp-mariadb --network wp-net \
-e MARIADB_ROOT_PASSWORD='RootPass123!' \
-e MARIADB_DATABASE='wordpress' \
-e MARIADB_USER='wpuser' \
-e MARIADB_PASSWORD='WpPass123!' \
-v /srv/wp/mariadb:/var/lib/mysql \
--restart unless-stopped mariadb:11.4
sudo nerdctl run -d --name wp-wordpress --network wp-net \
-e WORDPRESS_DB_HOST='wp-mariadb:3306' \
-e WORDPRESS_DB_NAME='wordpress' \
-e WORDPRESS_DB_USER='wpuser' \
-e WORDPRESS_DB_PASSWORD='WpPass123!' \
-v /srv/wp/wordpress:/var/www/html \
--restart unless-stopped wordpress:php8.3-fpm
sudo nerdctl run -d --name wp-nginx --network wp-net \
-p 80:80 \
-v /srv/wp/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
-v /srv/wp/wordpress:/var/www/html \
--restart unless-stopped nginx:1.27
아래는 Ubuntu + containerd + nerdctl + WordPress + Nginx + MariaDB를 systemd 자동 재기동까지 포함해서 운영용으로 정리한 버전입니다.
핵심은 containerd는 systemd로 관리하고, 각 컨테이너는 nerdctl로 띄운 뒤 systemd 서비스에서 부팅 시 재실행하도록 만드는 방식입니다.
구조
-
containerd.service: 런타임 자동 시작 -
wp-mariadb.service: MariaDB 컨테이너 자동 시작 -
wp-wordpress.service: WordPress 컨테이너 자동 시작 -
wp-nginx.service: Nginx 컨테이너 자동 시작
1) 준비
sudo apt update
sudo apt install -y containerd nginx
sudo systemctl enable --now containerdnerdctl은 별도 설치가 필요합니다.
sudo mkdir -p /opt/nerdctl
cd /opt/nerdctl
# 설치 파일은 환경에 맞는 버전을 받아서 배치2) 디렉터리
sudo mkdir -p /srv/wp/{nginx,wordpress,mariadb}
sudo chown -R root:root /srv/wp3) 네트워크
sudo nerdctl network create wp-net4) MariaDB
/etc/systemd/system/wp-mariadb.service
[Unit]
Description=WordPress MariaDB Container
After=containerd.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-mariadb
ExecStart=/usr/bin/nerdctl run --name wp-mariadb --network wp-net \
-e MARIADB_ROOT_PASSWORD=RootPass123! \
-e MARIADB_DATABASE=wordpress \
-e MARIADB_USER=wpuser \
-e MARIADB_PASSWORD=WpPass123! \
-v /srv/wp/mariadb:/var/lib/mysql \
mariadb:11.4
ExecStop=/usr/bin/nerdctl stop wp-mariadb
ExecStopPost=/usr/bin/nerdctl rm -f wp-mariadb
[Install]
WantedBy=multi-user.target
5) WordPress
/etc/systemd/system/wp-wordpress.service
[Unit]
Description=WordPress PHP-FPM Container
After=containerd.service wp-mariadb.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-wordpress
ExecStart=/usr/bin/nerdctl run --name wp-wordpress --network wp-net \
-e WORDPRESS_DB_HOST=wp-mariadb:3306 \
-e WORDPRESS_DB_NAME=wordpress \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=WpPass123! \
-v /srv/wp/wordpress:/var/www/html \
wordpress:php8.3-fpm
ExecStop=/usr/bin/nerdctl stop wp-wordpress
ExecStopPost=/usr/bin/nerdctl rm -f wp-wordpress
[Install]
WantedBy=multi-user.target
6) Nginx
/srv/wp/nginx/default.conf
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass wp-wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
/etc/systemd/system/wp-nginx.service
[Unit]
Description=Nginx Container for WordPress
After=containerd.service wp-wordpress.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-nginx
ExecStart=/usr/bin/nerdctl run --name wp-nginx --network wp-net \
-p 80:80 \
-v /srv/wp/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
-v /srv/wp/wordpress:/var/www/html \
nginx:1.27
ExecStop=/usr/bin/nerdctl stop wp-nginx
ExecStopPost=/usr/bin/nerdctl rm -f wp-nginx
[Install]
WantedBy=multi-user.target
7) 반영
sudo systemctl daemon-reload
sudo systemctl enable --now wp-mariadb
sudo systemctl enable --now wp-wordpress
sudo systemctl enable --now wp-nginx8) 상태 확인
sudo systemctl status wp-mariadb
sudo systemctl status wp-wordpress
sudo systemctl status wp-nginx
sudo nerdctl ps9) 백업 기본형
sudo mkdir -p /backup/wp
sudo tar czf /backup/wp/wordpress-$(date +%F).tgz /srv/wp/wordpress
sudo mysqldump -u root -p'RootPass123!' wordpress > /backup/wp/db-$(date +%F).sql운영 포인트
-
containerd는 systemd가 살리고, 컨테이너는 각 서비스 유닛이 다시 띄우는 구조입니다.
-
Restart=always만 믿기보다systemd enable까지 해 두는 편이 부팅 복원에 더 안전합니다. -
MariaDB와 WordPress는 같은
wp-net에 두고, 외부 포트는 Nginx만 노출하는 구성이 가장 깔끔합니다.
원하시면 다음 답변에서 바로 이어서 HTTPS(Let’s Encrypt)까지 포함한 최종 운영판으로 정리해드리겠습니다.
Ubuntu + containerd + nerdctl + WordPress + Nginx + MariaDB + Let’s Encrypt HTTPS까지 포함한 최종 운영판입니다.
구성은 외부 노출은 Nginx(80/443)만, WordPress/PHP-FPM와 MariaDB는 내부 네트워크에만 두는 방식입니다.
전체 구조
-
wp-mariadb: MariaDB 컨테이너. -
wp-wordpress: WordPress PHP-FPM 컨테이너. -
wp-nginx: Nginx 컨테이너, 80/443 수신. -
certbot: 호스트에서 실행해 Let’s Encrypt 인증서 발급/갱신. -
wp-net: 내부 컨테이너 네트워크.
1) 필수 패키지
sudo apt update
sudo apt install -y containerd nginx certbot python3-certbot-nginx
sudo systemctl enable --now containerd nginxnerdctl도 설치되어 있어야 합니다. 컨테이너 실행은 nerdctl로 하고, 인증서 발급과 갱신은 호스트의 certbot으로 처리하는 흐름이 가장 단순합니다.
2) 디렉터리 준비
sudo mkdir -p /srv/wp/{nginx,wordpress,mariadb,letsencrypt}
sudo chown -R root:root /srv/wpsudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge이 webroot 경로는 HTTP-01 방식 인증을 위해 필요합니다.
3) 컨테이너 네트워크
sudo nerdctl network create wp-net4) MariaDB 실행
sudo nerdctl run -d \
--name wp-mariadb \
--network wp-net \
-e MARIADB_ROOT_PASSWORD='RootPass123!' \
-e MARIADB_DATABASE='wordpress' \
-e MARIADB_USER='wpuser' \
-e MARIADB_PASSWORD='WpPass123!' \
-v /srv/wp/mariadb:/var/lib/mysql \
--restart unless-stopped \
mariadb:11.45) WordPress 실행
sudo nerdctl run -d \
--name wp-wordpress \
--network wp-net \
-e WORDPRESS_DB_HOST='wp-mariadb:3306' \
-e WORDPRESS_DB_NAME='wordpress' \
-e WORDPRESS_DB_USER='wpuser' \
-e WORDPRESS_DB_PASSWORD='WpPass123!' \
-v /srv/wp/wordpress:/var/www/html \
--restart unless-stopped \
wordpress:php8.3-fpm6) Nginx 설정
HTTP + ACME + HTTPS 리다이렉트
/etc/nginx/sites-available/wp.conf:
server {
listen 80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
HTTPS 서버 블록
Let’s Encrypt 인증서 발급 후 아래 블록을 추가합니다.
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
root /var/www/html;
index index.php index.html;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass wp-wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
이런 방식은 Nginx에서 80을 ACME 검증과 리다이렉트에 쓰고, 443에서 실제 서비스를 제공하는 전형적인 구성입니다.
7) Nginx 컨테이너 실행
호스트 Nginx 대신 컨테이너로 돌리려면 아래처럼 합니다.
sudo nerdctl run -d \
--name wp-nginx \
--network wp-net \
-p 80:80 \
-p 443:443 \
-v /srv/wp/nginx:/etc/nginx/conf.d:ro \
-v /srv/wp/wordpress:/var/www/html \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/www/letsencrypt:/var/www/letsencrypt \
--restart unless-stopped \
nginx:1.27호스트 Nginx를 이미 사용 중이면 포트 충돌이 나므로 둘 중 하나만 선택해야 합니다.
8) Let’s Encrypt 발급
도메인 DNS A 레코드가 서버 IP를 가리켜야 합니다. 그 다음 웹루트 방식으로 발급합니다.
sudo certbot certonly --webroot \
-w /var/www/letsencrypt \
-d example.com -d www.example.com발급이 끝나면 Nginx HTTPS 설정에 인증서 경로를 넣고 재시작합니다.
sudo nginx -t
sudo systemctl reload nginx9) 자동 갱신
Let’s Encrypt 인증서는 갱신 주기를 자동화해야 합니다. 보통 certbot renew를 systemd timer나 cron으로 돌립니다.
예시 cron:
sudo crontab -e0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"컨테이너로 Nginx를 돌리는 경우에는 post-hook를 컨테이너 재시작으로 바꾸면 됩니다.
0 3 * * * certbot renew --quiet --post-hook "nerdctl restart wp-nginx"10) systemd 유닛 예시
이미 앞에서 만든 wp-mariadb.service, wp-wordpress.service, wp-nginx.service는 그대로 사용하면 됩니다.
다만 HTTPS 운영판에서는 Nginx가 /etc/letsencrypt와 /var/www/letsencrypt를 읽을 수 있어야 하므로, 컨테이너 실행 인자에 해당 볼륨이 포함돼 있어야 합니다.
11) 최종 실행 순서
-
containerd시작. -
wp-net생성. -
MariaDB 시작.
-
WordPress 시작.
-
Nginx 시작.
-
certbot certonly --webroot로 인증서 발급. -
Nginx HTTPS 설정 반영.
-
자동 갱신 cron 또는 timer 등록.
12) 운영 팁
-
HTTP 80은 반드시 열어둬야 Let’s Encrypt 검증이 됩니다.
-
DB는 외부 포트를 열지 마세요.
-
HTTPS 발급 전에는 HTTP 리다이렉트를 잠시 빼고 인증 먼저 끝내는 게 안전합니다.
-
client_max_body_size는 워드프레스 이미지 업로드 때문에 넉넉하게 잡아두는 편이 좋습니다. -
트래픽이 늘면 Redis 캐시나 CDN을 나중에 추가하면 됩니다.
원하시면 다음 답변에서 이 최종판을 그대로 복붙 가능한 형태로
“실행 명령어 + systemd 유닛 + nginx conf + certbot 갱신” 한 파일 묶음처럼 정리해드리겠습니다.
아래는 그대로 복사해서 시작할 수 있는 최종 운영판 묶음입니다.
구성은 Ubuntu + containerd + nerdctl + WordPress + MariaDB + Nginx + Let’s Encrypt + systemd 자동 재기동 기준입니다. HTTPS는 호스트의 인증서 관리와 Nginx만 연결하고, 앱/DB는 내부 네트워크에만 두는 형태로 정리했습니다.
0) 디렉터리 구조
/srv/wp/
mariadb/
wordpress/
nginx/
letsencrypt/sudo mkdir -p /srv/wp/{mariadb,wordpress,nginx,letsencrypt}
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge1) 필수 패키지
sudo apt update
sudo apt install -y containerd nginx certbot python3-certbot-nginx
sudo systemctl enable --now containerd
sudo systemctl enable --now nginxnerdctl은 별도 설치가 필요합니다.
2) 내부 네트워크
sudo nerdctl network create wp-net3) MariaDB 컨테이너
/etc/systemd/system/wp-mariadb.service
[Unit]
Description=WordPress MariaDB Container
After=containerd.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-mariadb
ExecStart=/usr/bin/nerdctl run --name wp-mariadb --network wp-net \
-e MARIADB_ROOT_PASSWORD=RootPass123! \
-e MARIADB_DATABASE=wordpress \
-e MARIADB_USER=wpuser \
-e MARIADB_PASSWORD=WpPass123! \
-v /srv/wp/mariadb:/var/lib/mysql \
mariadb:11.4
ExecStop=/usr/bin/nerdctl stop wp-mariadb
ExecStopPost=/usr/bin/nerdctl rm -f wp-mariadb
[Install]
WantedBy=multi-user.target
4) WordPress 컨테이너
/etc/systemd/system/wp-wordpress.service
[Unit]
Description=WordPress PHP-FPM Container
After=containerd.service wp-mariadb.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-wordpress
ExecStart=/usr/bin/nerdctl run --name wp-wordpress --network wp-net \
-e WORDPRESS_DB_HOST=wp-mariadb:3306 \
-e WORDPRESS_DB_NAME=wordpress \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=WpPass123! \
-v /srv/wp/wordpress:/var/www/html \
wordpress:php8.3-fpm
ExecStop=/usr/bin/nerdctl stop wp-wordpress
ExecStopPost=/usr/bin/nerdctl rm -f wp-wordpress
[Install]
WantedBy=multi-user.target
5) Nginx 설정
/etc/nginx/sites-available/wp.conf
server {
listen 80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
root /var/www/html;
index index.php index.html;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass wp-wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
Nginx 활성화
sudo ln -sf /etc/nginx/sites-available/wp.conf /etc/nginx/sites-enabled/wp.conf
sudo nginx -t
sudo systemctl reload nginx6) Nginx 컨테이너
호스트 Nginx를 쓸 경우 이 단계는 생략해도 됩니다.
컨테이너로 Nginx를 돌릴 거면 아래처럼 바꿔서 쓰면 됩니다.
/etc/systemd/system/wp-nginx.service
[Unit]
Description=Nginx Container for WordPress
After=containerd.service wp-wordpress.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-nginx
ExecStart=/usr/bin/nerdctl run --name wp-nginx --network wp-net \
-p 80:80 -p 443:443 \
-v /etc/nginx/sites-available/wp.conf:/etc/nginx/conf.d/default.conf:ro \
-v /srv/wp/wordpress:/var/www/html \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/www/letsencrypt:/var/www/letsencrypt \
nginx:1.27
ExecStop=/usr/bin/nerdctl stop wp-nginx
ExecStopPost=/usr/bin/nerdctl rm -f wp-nginx
[Install]
WantedBy=multi-user.target
7) systemd 반영
sudo systemctl daemon-reload
sudo systemctl enable --now wp-mariadb
sudo systemctl enable --now wp-wordpress
sudo systemctl enable --now wp-nginx컨테이너를 직접 운영하면서 systemd로 재기동까지 잡는 방식입니다.
8) Let’s Encrypt 발급
먼저 도메인 A 레코드가 서버를 가리키는지 확인하고, HTTP 80이 열려 있어야 합니다.
sudo certbot certonly --webroot \
-w /var/www/letsencrypt \
-d example.com -d www.example.com발급 후 Nginx를 다시 읽습니다.
sudo nginx -t
sudo systemctl reload nginx9) 자동 갱신
cron 방식
sudo crontab -e0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"Nginx를 컨테이너로 돌리면 post-hook를 아래처럼 바꿉니다.
0 3 * * * certbot renew --quiet --post-hook "nerdctl restart wp-nginx"자동 갱신 설정은 Let’s Encrypt 운영에서 사실상 필수입니다.
10) 실행 순서
sudo systemctl start wp-mariadb
sudo systemctl start wp-wordpress
sudo systemctl start wp-nginx
sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com
sudo nginx -t
sudo systemctl reload nginx11) 운영 체크
sudo systemctl status wp-mariadb
sudo systemctl status wp-wordpress
sudo systemctl status wp-nginx
sudo nerdctl ps
sudo certbot renew --dry-run12) 바로 적용할 값
아래 값만 바꾸면 됩니다.
-
example.com,www.example.com -
RootPass123! -
WpPass123!
13) 한 번에 보는 핵심
-
systemd: containerd와 각 컨테이너 자동 재기동.
-
Nginx: 80은 ACME 검증, 443은 실제 서비스.
-
certbot:
--webroot로 발급 및renew로 자동갱신. -
WordPress/MariaDB: 내부 네트워크 전용으로 분리.
원하면 다음 답변에서 이걸 더 실전적으로 다듬어서 비밀값 분리한 .env 버전과 백업 스크립트까지 같이 붙여드릴게요.
아래는 비밀값 분리한 .env 버전 + 백업 스크립트까지 포함한 완전한 운영판 묶음입니다.
nerdctl은 --env-file을 지원하므로, systemd 서비스에서 --env-file /srv/wp/.env를 추가하고 모든 비밀값을 분리했습니다.
백업은 mysqldump와 tar로 매일 실행되도록 cron까지 포함했습니다.
0) 디렉터리 구조
/srv/wp/
.env # 비밀값
mariadb/
wordpress/
nginx/
letsencrypt/
backup/ # 백업 저장소sudo mkdir -p /srv/wp/{mariadb,wordpress,nginx,letsencrypt,backup}
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
sudo chown -R root:root /srv/wp1) 비밀값 파일
/srv/wp/.env
# MariaDB
MARIADB_ROOT_PASSWORD=RootPass123!
MARIADB_USER=wpuser
MARIADB_PASSWORD=WpPass123!
# WordPress
WORDPRESS_DB_HOST=wp-mariadb:3306
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wpuser
WORDPRESS_DB_PASSWORD=WpPass123!
# Domain
DOMAIN=example.com
권한 설정:
sudo chmod 600 /srv/wp/.env2) MariaDB systemd 서비스
/etc/systemd/system/wp-mariadb.service
[Unit]
Description=WordPress MariaDB Container
After=containerd.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-mariadb
ExecStart=/usr/bin/nerdctl run --name wp-mariadb --network wp-net \
--env-file /srv/wp/.env \
-v /srv/wp/mariadb:/var/lib/mysql \
mariadb:11.4
ExecStop=/usr/bin/nerdctl stop wp-mariadb
ExecStopPost=/usr/bin/nerdctl rm -f wp-mariadb
[Install]
WantedBy=multi-user.target
3) WordPress systemd 서비스
/etc/systemd/system/wp-wordpress.service
[Unit]
Description=WordPress PHP-FPM Container
After=containerd.service wp-mariadb.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-wordpress
ExecStart=/usr/bin/nerdctl run --name wp-wordpress --network wp-net \
--env-file /srv/wp/.env \
-v /srv/wp/wordpress:/var/www/html \
wordpress:php8.3-fpm
ExecStop=/usr/bin/nerdctl stop wp-wordpress
ExecStopPost=/usr/bin/nerdctl rm -f wp-wordpress
[Install]
WantedBy=multi-user.target
4) Nginx 설정
/srv/wp/nginx/default.conf
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name ${DOMAIN} www.${DOMAIN};
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
root /var/www/html;
index index.php index.html;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass wp-wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
5) Nginx 컨테이너 systemd
/etc/systemd/system/wp-nginx.service
[Unit]
Description=Nginx Container for WordPress
After=containerd.service wp-wordpress.service network-online.target
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
ExecStartPre=/usr/bin/nerdctl rm -f wp-nginx
ExecStart=/usr/bin/nerdctl run --name wp-nginx --network wp-net \
-p 80:80 -p 443:443 \
-v /srv/wp/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
-v /srv/wp/wordpress:/var/www/html \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/www/letsencrypt:/var/www/letsencrypt \
--env-file /srv/wp/.env \
nginx:1.27
ExecStop=/usr/bin/nerdctl stop wp-nginx
ExecStopPost=/usr/bin/nerdctl rm -f wp-nginx
[Install]
WantedBy=multi-user.target
6) systemd 반영
sudo systemctl daemon-reload
sudo systemctl enable --now wp-mariadb wp-wordpress wp-nginx7) Let’s Encrypt 발급
sudo certbot certonly --webroot \
-w /var/www/letsencrypt \
-d example.com -d www.example.com.env의 DOMAIN=example.com과 일치시켜야 합니다.
8) 백업 스크립트
/srv/wp/backup.sh
#!/bin/bash
set -e
BACKUP_DIR="/srv/wp/backup"
DATE=$(date +%Y%m%d_%H%M%S)
WP_DIR="/srv/wp/wordpress"
DB_NAME="wordpress"
DB_USER="wpuser"
DB_PASS="WpPass123!"
DB_HOST="wp-mariadb"
LOG_FILE="/srv/wp/backup.log"
mkdir -p "$BACKUP_DIR"
# WordPress 파일 백업
echo "$(date): Starting WordPress backup..." >> "$LOG_FILE"
tar czf "$BACKUP_DIR/wp-files-$DATE.tar.gz" -C "$WP_DIR/.." wordpress >> "$LOG_FILE" 2>&1
# MariaDB 덤프 백업
echo "$(date): Starting DB backup..." >> "$LOG_FILE"
nerdctl exec wp-mariadb mysqldump -u "$DB_USER" -p"$DB_PASS" -h "$DB_HOST" "$DB_NAME" > "$BACKUP_DIR/wp-db-$DATE.sql" >> "$LOG_FILE" 2>&1
# 오래된 백업 삭제 (7일)
find "$BACKUP_DIR" -name "wp-*.tar.gz" -mtime +7 -delete
find "$BACKUP_DIR" -name "wp-*.sql" -mtime +7 -delete
echo "$(date): Backup completed: wp-files-$DATE.tar.gz, wp-db-$DATE.sql" >> "$LOG_FILE"
권한 설정:
sudo chmod +x /srv/wp/backup.sh9) 백업 cron
sudo crontab -e# 매일 새벽 2시 백업
0 2 * * * /srv/wp/backup.sh
# 새벽 3시 Let’s Encrypt 갱신
0 3 * * * certbot renew --quiet --post-hook "nerdctl restart wp-nginx"
10) 전체 실행 순서
# 1. 네트워크
sudo nerdctl network create wp-net
# 2. systemd
sudo systemctl daemon-reload
sudo systemctl enable --now wp-mariadb wp-wordpress wp-nginx
# 3. HTTPS 발급
sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com
# 4. 백업 테스트
sudo /srv/wp/backup.sh
# 5. cron 확인
sudo crontab -l
11) 상태 확인
sudo systemctl status wp-mariadb wp-wordpress wp-nginx
sudo nerdctl ps
sudo certbot renew --dry-run
sudo tail -f /srv/wp/backup.log12) 핵심 변경점
-
.env: 모든 비밀값 분리,chmod 600필수. -
systemd:
--env-file /srv/wp/.env추가. -
백업:
nerdctl exec로 DB 덤프, 파일은tar. -
cron: 백업 + Let’s Encrypt 자동화.
이제 완전한 운영판이 완성됐습니다. .env만 바꾸고 실행하면 끝입니다.
원하시면 다음 답변에서 모니터링/알림 추가나 성능 튜닝까지 이어서 드릴게요.
모니터링(Prometheus + Grafana) + 알림(Discord/Slack) + 성능 튜닝까지 추가한 완전 운영판입니다.
소형 VPS 기준으로 가볍게 구성했으니, containerd + nerdctl로 컨테이너 추가만 하면 됩니다.
1) 모니터링 스택 추가
Prometheus + Grafana + Node Exporter를 컨테이너로 올려서 CPU/메모리/디스크/네트워크를 모니터링합니다.
/srv/wp/monitoring/prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'nginx'
static_configs:
- targets: ['nginx-exporter:9113']
- job_name: 'mariadb'
static_configs:
- targets: ['mariadb-exporter:9216']
Node Exporter systemd
/etc/systemd/system/wp-node-exporter.service
[Unit]
Description=Node Exporter
After=containerd.service network-online.target
Wants=network-online.target
[Service]
Restart=always
ExecStartPre=/usr/bin/nerdctl rm -f wp-node-exporter
ExecStart=/usr/bin/nerdctl run --name wp-node-exporter --network wp-net \
-p 9100:9100 \
prom/node-exporter:latest
ExecStop=/usr/bin/nerdctl stop wp-node-exporter
[Install]
WantedBy=multi-user.target
Prometheus systemd
/etc/systemd/system/wp-prometheus.service
[Unit]
Description=Prometheus
After=containerd.service wp-node-exporter.service network-online.target
[Service]
Restart=always
ExecStartPre=/usr/bin/nerdctl rm -f wp-prometheus
ExecStart=/usr/bin/nerdctl run --name wp-prometheus --network wp-net \
-p 9090:9090 \
-v /srv/wp/monitoring:/etc/prometheus \
prom/prometheus:latest
ExecStop=/usr/bin/nerdctl stop wp-prometheus
[Install]
WantedBy=multi-user.target
Grafana systemd
/etc/systemd/system/wp-grafana.service
[Unit]
Description=Grafana
After=containerd.service wp-prometheus.service network-online.target
[Service]
Restart=always
ExecStartPre=/usr/bin/nerdctl rm -f wp-grafana
ExecStart=/usr/bin/nerdctl run --name wp-grafana --network wp-net \
-p 3000:3000 \
-v /srv/wp/grafana:/var/lib/grafana \
grafana/grafana:latest
ExecStop=/usr/bin/nerdctl stop wp-grafana
[Install]
WantedBy=multi-user.target
적용
sudo mkdir -p /srv/wp/monitoring /srv/wp/grafana
sudo systemctl daemon-reload
sudo systemctl enable --now wp-node-exporter wp-prometheus wp-grafana접속: http://서버IP:3000 (admin/admin)
2) 알림 설정 (Discord Webhook)
Grafana Alerting에서 Discord로 CPU 80% 이상, 디스크 90% 이상 등 알림을 보냅니다.
Grafana 설정
-
Grafana 로그인 → Configuration → Alerting → Contact points
-
New contact point → Name: Discord
-
Type: Discord → Webhook URL:
https://discord.com/api/webhooks/1234567890/abcDEF...
추천 Alert Rule
-
CPU Usage:
100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80 -
Memory Usage:
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85 -
Disk Usage:
100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 > 90
3) 성능 튜닝
MariaDB 튜닝 (/srv/wp/mariadb/my.cnf)
[mysqld]
# 메모리 설정 (2GB RAM 기준)
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
# 연결 설정
max_connections = 100
wait_timeout = 300
# 테이블/쿼리 캐시
table_open_cache = 2000
query_cache_size = 64M
query_cache_type = 1
# 임시 테이블
tmp_table_size = 64M
max_heap_table_size = 64M
MariaDB 컨테이너에 마운트:
sudo nerdctl cp /srv/wp/mariadb/my.cnf wp-mariadb:/etc/mysql/conf.d/
sudo nerdctl restart wp-mariadbNginx 튜닝 (/srv/wp/nginx/default.conf에 추가)
# Gzip 압축
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# 연결 최적화
worker_processes auto;
worker_connections 1024;
# 캐시 설정
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
WordPress 튜닝 (플러그인 추천)
-
Redis Object Cache:
redis://redis:6379 -
LiteSpeed Cache 또는 WP Super Cache
-
Smush (이미지 최적화)
-
Autoptimize (JS/CSS 병합)
PHP-FPM 튜닝
WordPress 컨테이너에서 PHP 설정을 튜닝하려면 커스텀 이미지나 php.ini 마운트가 필요합니다.
4) Redis 캐시 추가 (선택)
Redis systemd
/etc/systemd/system/wp-redis.service
[Unit]
Description=Redis Cache
After=containerd.service network-online.target
[Service]
Restart=always
ExecStartPre=/usr/bin/nerdctl rm -f wp-redis
ExecStart=/usr/bin/nerdctl run --name wp-redis --network wp-net \
-p 6379:6379 \
-v /srv/wp/redis:/data \
redis:7-alpine
ExecStop=/usr/bin/nerdctl stop wp-redis
[Install]
WantedBy=multi-user.target
WordPress 플러그인 Redis Object Cache 설치 후 redis://wp-redis:6379 연결.
5) 전체 적용 순서
# 1. 모니터링 디렉터리
sudo mkdir -p /srv/wp/{monitoring,grafana,redis}
# 2. systemd 추가
sudo systemctl daemon-reload
sudo systemctl enable --now wp-node-exporter wp-prometheus wp-grafana wp-redis
# 3. MariaDB 튜닝
sudo nerdctl cp /srv/wp/mariadb/my.cnf wp-mariadb:/etc/mysql/conf.d/
sudo nerdctl restart wp-mariadb
# 4. Nginx 재시작
sudo nerdctl restart wp-nginx
# 5. Grafana 접속해 Alert 설정
# http://서버IP:3000 → admin/admin
6) 추천 대시보드
Grafana Dashboard ID (Import):
-
Node Exporter Full:
1860 -
Nginx:
12719 -
MariaDB:
7362
7) 한 줄 운영 팁
-
Grafana:
http://서버IP:3000(모니터링) -
Prometheus:
http://서버IP:9090(쿼리) -
Discord 알림: Grafana Alerting 설정
-
Redis: 트래픽 100+일 때 추가
이제 완벽한 운영 환경이 완성됐습니다! VPS 1대로도 충분히 상용 수준입니다.
추가로 CI/CD 파이프라인이나 로드밸런싱까지 원하시면 말씀해주세요.

CI/CD 파이프라인(GitLab CI + nerdctl) + 로드밸런싱(K3s + Nginx Ingress)까지 추가한 완전 운영판입니다.
소형 VPS 기준으로 가볍게 구성했으니, 기존 containerd 환경에 K3s를 추가로 올려서 확장성을 더합니다.
1) CI/CD 파이프라인 (GitLab CI 추천)
GitLab 무료 티어에서 WordPress 테마/플러그인 변경을 자동 빌드 → 컨테이너 이미지 푸시 → 배포까지 처리합니다.
GitLab CI 설정 (.gitlab-ci.yml)
stages:
- test
- build
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
test:
stage: test
image: wordpress:php8.3-cli
services:
- mariadb:11.4
script:
- cp wp-config.php wp-config-test.php
- wp core install --url=http://localhost --title="Test" --admin_user=admin --admin_password=pass --admin_email=test@example.com
- wp theme activate twenty twenty-four
- wp plugin install --activate --path=/srv/wp-plugins/ your-plugin
- wp core verify-checksums
- wp doctor check --all
artifacts:
reports:
junit: phpunit.xml
build:
stage: build
image: docker:27
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- develop
deploy-staging:
stage: deploy
image: alpine/k8s:1.30.0
script:
- apk add --no-cache kubectl
- echo "$KUBE_CONFIG_STAGING" | base64 -d > kubeconfig
- export KUBECONFIG=kubeconfig
- kubectl set image deployment/wp-app wp-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n staging
- kubectl rollout status deployment/wp-app -n staging
environment:
name: staging
only:
- develop
deploy-prod:
stage: deploy
image: alpine/k8s:1.30.0
script:
- apk add --no-cache kubectl
- echo "$KUBE_CONFIG_PROD" | base64 -d > kubeconfig
- export KUBECONFIG=kubeconfig
- kubectl set image deployment/wp-app wp-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/wp-app -n production
when: manual
environment:
name: production
only:
- main
Dockerfile (WordPress 커스텀 이미지)
FROM wordpress:php8.3-fpm
# 테마/플러그인 추가
COPY ./wp-content/themes /var/www/html/wp-content/themes
COPY ./wp-content/plugins /var/www/html/wp-content/plugins
# Redis 캐시 활성화
RUN pecl install redis && docker-php-ext-enable redis
# 성능 최적화
RUN echo 'opcache.enable=1\nopcache.memory_consumption=256\nopcache.max_accelerated_files=10000' > /usr/local/etc/php/conf.d/opcache.ini
2) 로드밸런싱 (K3s + Nginx Ingress)
단일 VPS에서는 K3s를 containerd 위에 올리고, Nginx Ingress Controller로 로드밸런싱을 구현합니다.
K3s 설치 (containerd 사용)
curl -sfL https://get.k3s.io | sh -s - --container-runtime-endpoint unix:///run/containerd/containerd.sockNginx Ingress 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/baremetal/deploy.yamlWordPress 배포 매니페스트
wordpress-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wp-app
labels:
app: wordpress
spec:
replicas: 2 # 로드밸런싱 대상
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: registry.gitlab.com/your-project/wordpress:latest
ports:
- containerPort: 9000
env:
- name: WORDPRESS_DB_HOST
value: "mysql-service"
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wp-service
port:
number: 9000배포:
kubectl apply -f wordpress-deployment.yaml로드밸런싱: Nginx Ingress가 wp-app Deployment의 2개 Pod로 트래픽을 Round Robin 분배합니다.
3) GitLab Runner 설치 (K3s에서)
kubectl create namespace gitlab-runner
helm repo add gitlab https://charts.gitlab.io
helm install gitlab-runner gitlab/gitlab-runner \
--namespace gitlab-runner \
--set gitlabUrl=https://gitlab.com/ \
--set runnerRegistrationToken=YOUR_TOKEN4) 전체 워크플로우
1. GitLab에 코드 푸시
2. CI/CD 파이프라인 실행 (테스트 → 빌드 → 이미지 푸시)
3. K3s에 자동 배포 (kubectl 또는 ArgoCD)
4. Nginx Ingress가 트래픽 로드밸런싱
5. Grafana에서 모니터링 + Discord 알림5) ArgoCD 연동 (선택, 완전 자동화)
GitOps 방식으로 GitLab 저장소 변경만 감지해서 자동 배포합니다.
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yamlArgoCD UI: kubectl port-forward svc/argocd-server -n argocd 8080:443
6) 로드밸런싱 테스트
# Pod 상태 확인
kubectl get pods -l app=wordpress
# Ingress 상태
kubectl get ingress
# 로그 확인
kubectl logs -l app=wordpress
7) 비용/운영 팁
추천: 트래픽 100명/일 이하면 nerdctl 단독, 1,000명 이상이면 K3s + Ingress.
8) 한 줄 실행
# K3s + Ingress + GitLab CI 완성
curl -sfL https://get.k3s.io | sh -s - --container-runtime-endpoint unix:///run/containerd/containerd.sock
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/baremetal/deploy.yaml
# GitLab에 .gitlab-ci.yml 추가 → 자동화 완료!이제 엔터프라이즈급 운영 환경 완성! CI/CD + 로드밸런싱 + 모니터링까지 모든 게 갖춰졌습니다.