Ubunt Web Service 설치
Oracle Cloud Ubuntu 배포 가이드
아키텍처 개요
[Oracle Cloud - 공인 IP]
|
[OCI 보안 목록]
(포트: 80, 443, 22)
|
[Ubuntu VM 인스턴스]
|
[Nginx (80/443)]
SSL: Let's Encrypt
|
+----------------------+----------------------+
| | |
[/worklog] [/worklog1] [/worklog2]
:5173, :5174 :5175, :5176 :5177, :5178
(Node.js) (Node.js) (Node.js)
| | |
+----------------------+----------------------+
|
[/api]
:8090, :8095
(Spring Boot JAR)
|
+----------+----------+
| |
[PostgreSQL] [Redis]
:5432 :6379
(Docker/Native) (Docker/Native)
1단계: OCI 인스턴스 설정
컴퓨트 인스턴스 생성
인스턴스 사양 (권장):
- Shape: VM.Standard.E2.1.Micro (무료 티어) 또는 VM.Standard.E4.Flex
- 이미지: Canonical Ubuntu 22.04
- OCPU: 1-2
- 메모리: 8-16 GB
- 부트 볼륨: 50-100 GB
- VCN: 신규 생성 또는 기존 사용
- 공인 IP: 공인 IPv4 할당
OCI 보안 목록 규칙 (인그레스)
VCN의 보안 목록에 다음 규칙 추가:
- SSH (임시, 나중에 자신의 IP로 제한)
- Stateless: No
- Source: 0.0.0.0/0
- IP Protocol: TCP
- Source Port Range: All
- Destination Port Range: 22
- HTTP
- Stateless: No
- Source: 0.0.0.0/0
- IP Protocol: TCP
- Source Port Range: All
- Destination Port Range: 80
- HTTPS
- Stateless: No
- Source: 0.0.0.0/0
- IP Protocol: TCP
- Source Port Range: All
- Destination Port Range: 443
2단계: 초기 Ubuntu 설정
인스턴스에 SSH 접속
ssh ubuntu@<OCI-공인-IP>
시스템 업데이트
sudo apt update && sudo apt upgrade -y
필수 패키지 설치
sudo apt install -y curl wget git unzip build-essential ufw fail2ban
Node.js 22.x 설치
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
Java 17 설치
sudo apt install -y openjdk-17-jdk
설치 확인
node --version # v22.x 이어야 함
java -version # 17.x 이어야 함
Docker & Docker Compose 설치 (PostgreSQL, Redis용)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker ubuntu
newgrp docker
Docker Compose v2 설치
# 기존 패키지 업데이트
sudo apt update
# 필요 패키지 설치
sudo apt install -y ca-certificates curl gnupg lsb-release
# Docker GPG key 추가
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Docker repository 추가
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 패키지 업데이트
sudo apt update
# Docker Compose Plugin 설치
sudo apt install docker-compose-plugin
Docker 확인
docker --version
docker compose version
3단계: 방화벽 설정 (Ubuntu UFW)
UFW 방화벽 설정
sudo ufw default deny incoming
sudo ufw default allow outgoing
SSH 허용 (보안 강화를 위해 22를 사용자 정의 포트로 변경 가능)
sudo ufw allow 22/tcp
HTTP/HTTPS 허용
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
UFW 활성화
sudo ufw --force enable
상태 확인
sudo ufw status verbose
4단계: Nginx 설치
Nginx 설치
sudo apt install -y nginx
기본 사이트 중지
sudo systemctl stop nginx
5단계: PostgreSQL & Redis 설정 (Docker)
Docker Compose 파일 생성
디렉토리 생성
sudo mkdir -p /opt/moremong/docker
docker-compose.yml 생성
sudo tee /opt/moremong/docker/docker-compose.yml > /dev/null <<'EOF'
version: '3.8'
services:
postgresql:
image: postgres:17.4
container_name: moremong-postgres
restart: always
environment:
POSTGRES_DB: moremong
POSTGRES_USER: moremong
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U moremong"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7.2.3
container_name: moremong-redis
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
ports:
- "127.0.0.1:6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
driver: local
redis_data:
driver: local
EOF
.env 파일 생성
sudo tee /opt/moremong/docker/.env > /dev/null <<'EOF'
POSTGRES_PASSWORD=강력한_비밀번호_1로_변경
REDIS_PASSWORD=강력한_비밀번호_2로_변경
EOF
보안 권한 설정
sudo chmod 600 /opt/moremong/docker/.env
서비스 시작
cd /opt/moremong/docker
sudo docker compose up -d
서비스 확인
sudo docker compose ps
Compose 프로젝트 전체 중지
sudo docker compose stop
Compose 프로젝트 전체 삭제
sudo docker compose down
상태 확인
sudo docker ps # 실행 중 컨테이너 확인
sudo docker compose ps # Compose 프로젝트 상태 확인
6단계: 애플리케이션 파일 배포
애플리케이션 디렉토리 생성
sudo mkdir -p /opt/moremong/{restapi,backend,frontend,frontend1,frontend2}
sudo chown -R ubuntu:ubuntu /opt/moremong
리포지토리 클론 (또는 파일 업로드)
cd /opt/moremong/repo
git clone <저장소-URL>
또는 scp/rsync로 빌드된 파일 업로드
7단계: 백엔드 빌드 & 배포
cd /opt/moremong/repo/restapi
프로덕션 환경 파일 생성
tee .env.production > /dev/null <<'EOF'
JASYPT_ENCRYPTOR_PASSWORD=여기에_마스터_비밀번호_입력
SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=8090
#데이터베이스
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/moremong
SPRING_DATASOURCE_USERNAME=moremong
SPRING_DATASOURCE_PASSWORD=강력한_비밀번호_1로_변경
# Redis
SPRING_DATA_REDIS_HOST=localhost
SPRING_DATA_REDIS_PORT=6379
SPRING_DATA_REDIS_PASSWORD=강력한_비밀번호_2로_변경
# OAuth (암호화된 값 추가)
# KAKAO_CLIENT_ID=ENC(...)
# KAKAO_CLIENT_SECRET=ENC(...)
# NAVER_CLIENT_ID=ENC(...)
# NAVER_CLIENT_SECRET=ENC(...)
EOF
프로덕션 JAR 빌드
# 권한 오류 발생시
chmod +x gradlew
# build
./gradlew -Pprod clean bootJar
배포 디렉토리로 JAR 복사
cp build/libs/\*.jar /opt/moremong/backend/moremong-restapi.jar
환경 파일 복사
cp .env.production /opt/moremong/backend/.env
백엔드 인스턴스 1용 systemd 서비스 생성
sudo tee /etc/systemd/system/moremong-api-1.service > /dev/null <<'EOF'
[Unit]
Description=Moremong Backend API Instance 1
After=network.target docker.service
Wants=docker.service
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/moremong/backend
EnvironmentFile=/opt/moremong/backend/.env
Environment="SERVER_PORT=8090"
ExecStart=/usr/bin/java \
-Xms512m \
-Xmx1024m \
-Djava.security.egd=file:/dev/./urandom \
-jar /opt/moremong/backend/moremong-restapi.jar
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=moremong-api-1
[Install]
WantedBy=multi-user.target
EOF
백엔드 인스턴스 2용 systemd 서비스 생성
sudo tee /etc/systemd/system/moremong-api-2.service > /dev/null <<'EOF'
[Unit]
Description=Moremong Backend API Instance 2
After=network.target docker.service
Wants=docker.service
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/moremong/backend
EnvironmentFile=/opt/moremong/backend/.env
Environment="SERVER_PORT=8095"
ExecStart=/usr/bin/java \
-Xms512m \
-Xmx1024m \
-Djava.security.egd=file:/dev/./urandom \
-jar /opt/moremong/backend/moremong-restapi.jar
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=moremong-api-2
[Install]
WantedBy=multi-user.target
EOF
8단계: 프론트엔드 빌드 & 배포
cd /opt/moremong/repo/moremong-front
프로덕션 .env 생성
tee .env.production > /dev/null <<'EOF'
PUBLIC_API_URL=https://moremong.com
BASE_PATH=/worklog
EOF
BASE_PATH=/worklog로 빌드
BASE_PATH=/worklog npm run build
배포 디렉토리로 복사
cp -r build /opt/moremong/frontend/
cp .env.production /opt/moremong/frontend/.env
cp package.json package-lock.json /opt/moremong/frontend/
프로덕션 의존성 설치
cd /opt/moremong/frontend
npm ci --omit=dev
프론트엔드 인스턴스용 systemd 서비스 생성
프론트엔드 인스턴스 1 (worklog)
sudo tee /etc/systemd/system/moremong-front-1.service > /dev/null <<'EOF'
[Unit]
Description=Moremong Frontend /worklog Instance 1
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/moremong/frontend
EnvironmentFile=/opt/moremong/frontend/.env
Environment="PORT=5173"
Environment="HOST=127.0.0.1"
ExecStart=/usr/bin/node build
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=moremong-front-1
[Install]
WantedBy=multi-user.target
EOF
프론트엔드 인스턴스 2 (worklog)
sudo tee /etc/systemd/system/moremong-front-2.service > /dev/null <<'EOF'
[Unit]
Description=Moremong Frontend /worklog Instance 2
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/moremong/frontend
EnvironmentFile=/opt/moremong/frontend/.env
Environment="PORT=5174"
Environment="HOST=127.0.0.1"
ExecStart=/usr/bin/node build
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=moremong-front-2
[Install]
WantedBy=multi-user.target
EOF
frontend1과 frontend2 반복 (필요시)
BASE_PATH=/worklog1로 frontend1 빌드
cd /opt/moremong/repo/moremong-front1
BASE_PATH=/worklog1 npm run build
cp -r build /opt/moremong/frontend1/
… 포트 5175, 5176으로 유사한 systemd 서비스 생성
BASE_PATH=/worklog2로 frontend2 빌드
cd /opt/moremong/repo/moremong-front2
BASE_PATH=/worklog2 npm run build
cp -r build /opt/moremong/frontend2/
… 포트 5177, 5178으로 유사한 systemd 서비스 생성
9단계: Nginx 설정
SSL용 Certbot 설치
Certbot 설치
sudo apt install -y certbot python3-certbot-nginx
SSL 인증서 발급 (moremong.com을 실제 도메인으로 변경)
sudo certbot certonly --nginx -d moremong.com -d www.moremong.com --non-interactive --agree-tos -m your-email@example.com
인증서 위치:
/etc/letsencrypt/live/moremong.com/fullchain.pem
/etc/letsencrypt/live/moremong.com/privkey.pem
Nginx 설정 생성
sudo tee /etc/nginx/nginx.conf > /dev/null <<'EOF'
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 2048;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 20M;
# Gzip 압축
gzip on;
gzip_vary on;
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/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate limiting (요청 제한)
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
# 업스트림 백엔드
upstream backend_api {
least_conn;
server 127.0.0.1:8090 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8095 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream frontend_worklog {
least_conn;
server 127.0.0.1:5173 max_fails=3 fail_timeout=30s;
server 127.0.0.1:5174 max_fails=3 fail_timeout=30s;
keepalive 16;
}
upstream frontend_worklog1 {
least_conn;
server 127.0.0.1:5175 max_fails=3 fail_timeout=30s;
server 127.0.0.1:5176 max_fails=3 fail_timeout=30s;
keepalive 16;
}
upstream frontend_worklog2 {
least_conn;
server 127.0.0.1:5177 max_fails=3 fail_timeout=30s;
server 127.0.0.1:5178 max_fails=3 fail_timeout=30s;
keepalive 16;
}
# HTTP에서 HTTPS로 리다이렉트
server {
listen 80;
listen [::]:80;
server_name moremong.com www.moremong.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 서버
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name moremong.com www.moremong.com;
# SSL 설정
ssl_certificate /etc/letsencrypt/live/moremong.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/moremong.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# 최신 SSL 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 백엔드 API
location /api {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
}
# OAuth2 엔드포인트 (백엔드)
location ~ ^/(oauth2|login) {
limit_req zone=api burst=10 nodelay;
proxy_pass http://backend_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Management 엔드포인트 (접근 제한)
location /management {
allow 127.0.0.1;
deny all;
proxy_pass http://backend_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
}
# 프론트엔드 /worklog
location /worklog {
limit_req zone=general burst=20 nodelay;
proxy_pass http://frontend_worklog;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 프론트엔드 /worklog1
location /worklog1 {
limit_req zone=general burst=20 nodelay;
proxy_pass http://frontend_worklog1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 프론트엔드 /worklog2
location /worklog2 {
limit_req zone=general burst=20 nodelay;
proxy_pass http://frontend_worklog2;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 루트 리다이렉트
location = / {
return 301 https://$host/worklog;
}
# 헬스 체크 엔드포인트
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
EOF
Nginx 설정 테스트
sudo nginx -t
아직 시작하지 말 것 - 서비스 대기
10단계: 모든 서비스 시작
systemd 리로드
sudo systemctl daemon-reload
백엔드 인스턴스 시작
sudo systemctl enable moremong-api-1 moremong-api-2
sudo systemctl start moremong-api-1 moremong-api-2
백엔드 상태 확인
sudo systemctl status moremong-api-1
sudo systemctl status moremong-api-2
프론트엔드 인스턴스 시작
sudo systemctl enable moremong-front-1 moremong-front-2
sudo systemctl start moremong-front-1 moremong-front-2
프론트엔드 상태 확인
sudo systemctl status moremong-front-1
sudo systemctl status moremong-front-2
Nginx 시작
sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl status nginx
로그 확인
sudo journalctl -u moremong-api-1 -f
sudo journalctl -u moremong-front-1 -f
11단계: 보안 강화
1. Fail2ban 설정
fail2ban 설치 및 설정
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
destemail = your-email@example.com
sendername = Fail2Ban
[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
[nginx-limit-req]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 10
EOF
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo fail2ban-client status
2. SSH 강화
SSH 설정 백업
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
보안 강화 적용
sudo tee -a /etc/ssh/sshd_config > /dev/null <<'EOF'
#보안 강화
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
PrintMotd no
AcceptEnv LANG LC\_\*
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
MaxSessions 5
EOF
SSH 재시작
sudo systemctl restart sshd
3. 자동 보안 업데이트
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
자동 업데이트 활성화
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null <<'EOF'
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
EOF
4. 사용하지 않는 서비스 비활성화
사용하지 않는 서비스 목록 확인 및 비활성화
sudo systemctl disable bluetooth.service
sudo systemctl disable cups.service
불필요한 패키지 제거
sudo apt autoremove -y
5. 파일 권한 설정
애플리케이션 파일 보안
sudo chown -R ubuntu:ubuntu /opt/moremong
sudo chmod -R 755 /opt/moremong
sudo chmod 600 /opt/moremong/backend/.env
sudo chmod 600 /opt/moremong/frontend/.env
sudo chmod 600 /opt/moremong/docker/.env
Nginx 보안
sudo chown -R root:root /etc/nginx
sudo chmod 644 /etc/nginx/nginx.conf
6. 감사 로깅 활성화
sudo apt install -y auditd
sudo systemctl enable auditd
sudo systemctl start auditd
감사 규칙 추가
sudo tee -a /etc/audit/rules.d/audit.rules > /dev/null <<'EOF'
-w /etc/passwd -p wa -k passwd_changes
-w /etc/group -p wa -k group_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
-w /opt/moremong -p wa -k moremong_changes
EOF
sudo systemctl restart auditd
7. 로그 로테이션 설정
sudo tee /etc/logrotate.d/moremong > /dev/null <<'EOF'
/var/log/nginx/\*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
EOF
12단계: 모니터링 설정
1. Node Exporter 설치 (Prometheus용)
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvfz node_exporter-1.7.0.linux-amd64.tar.gz
sudo mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/
rm -rf node_exporter-1.7.0.linux-amd64\*
systemd 서비스 생성
sudo tee /etc/systemd/system/node_exporter.service > /dev/null <<'EOF'
[Unit]
Description=Node Exporter
After=network.target
[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address=127.0.0.1:9100
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
2. 로그 모니터링 설정
로그 뷰어 lnav 설치
sudo apt install -y lnav
모든 로그 확인
sudo lnav /var/log/nginx/\*.log
sudo journalctl -u moremong-api-1 -u moremong-front-1 -f
13단계: 백업 전략
데이터베이스 백업 스크립트
sudo tee /opt/moremong/backup-db.sh > /dev/null <<'EOF'
#!/bin/bash
BACKUP*DIR="/opt/moremong/backups"
TIMESTAMP=$(date +%Y%m%d*%H%M%S)
POSTGRES_CONTAINER="moremong-postgres"
mkdir -p $BACKUP_DIR
# PostgreSQL 백업
docker exec $POSTGRES_CONTAINER pg_dump -U moremong moremong | gzip > $BACKUP_DIR/postgres_$TIMESTAMP.sql.gz
# Redis 백업
docker exec moremong-redis redis-cli --rdb /data/dump.rdb
docker cp moremong-redis:/data/dump.rdb $BACKUP_DIR/redis_$TIMESTAMP.rdb
# 7일 이전 백업 삭제
find $BACKUP*DIR -name "postgres*_.sql.gz" -mtime +7 -delete
find $BACKUP*DIR -name "redis*_.rdb" -mtime +7 -delete
echo "백업 완료: $TIMESTAMP"
EOF
sudo chmod +x /opt/moremong/backup-db.sh
crontab에 추가 (매일 새벽 2시)
(crontab -l 2>/dev/null; echo "0 2 \* \* \* /opt/moremong/backup-db.sh >> /var/log/moremong-backup.log 2>&1") | crontab -
14단계: 헬스 체크 스크립트
sudo tee /opt/moremong/health-check.sh > /dev/null <<'EOF'
#!/bin/bash
echo "=== Moremong 헬스 체크 ==="
echo "타임스탬프: $(date)"
echo ""
# 서비스 확인
echo "백엔드 API 1:" $(systemctl is-active moremong-api-1)
echo "백엔드 API 2:" $(systemctl is-active moremong-api-2)
echo "프론트엔드 1:" $(systemctl is-active moremong-front-1)
echo "프론트엔드 2:" $(systemctl is-active moremong-front-2)
echo "Nginx:" $(systemctl is-active nginx)
echo "PostgreSQL:" $(docker ps --filter name=moremong-postgres --format "")
echo "Redis:" $(docker ps --filter name=moremong-redis --format "")
echo ""
# 포트 상태 확인
echo "=== 포트 상태 ==="
netstat -tuln | grep -E ':(80|443|5173|5174|8090|8095) '
echo ""
# 디스크 사용량 확인
echo "=== 디스크 사용량 ==="
df -h /
echo ""
# 메모리 사용량 확인
echo "=== 메모리 사용량 ==="
free -h
echo ""
EOF
sudo chmod +x /opt/moremong/health-check.sh
15단계: 배포 체크리스트
운영 전 이 체크리스트 실행
1. 모든 서비스 실행 확인
sudo systemctl status moremong-api-1 moremong-api-2
sudo systemctl status moremong-front-1 moremong-front-2
sudo systemctl status nginx
sudo docker compose -f /opt/moremong/docker/docker-compose.yml ps
2. 엔드포인트 테스트
curl -I http://localhost:8090/management/health
curl -I http://localhost:5173/worklog
curl -I https://moremong.com/worklog
curl -I https://moremong.com/api/management/health
3. 에러 로그 확인
sudo journalctl -u moremong-api-1 --since "1 hour ago" | grep -i error
sudo journalctl -u moremong-front-1 --since "1 hour ago" | grep -i error
sudo tail -n 100 /var/log/nginx/error.log
4. SSL 확인
curl -I https://moremong.com
openssl s_client -connect moremong.com:443 -servername moremong.com < /dev/null
5. OAuth 로그인 테스트
브라우저에서: https://moremong.com/worklog
로그인 클릭, /worklog/oauth/callback으로 리다이렉트 확인
6. 방화벽 확인
sudo ufw status verbose
OCI 보안 목록에 80, 443 포트가 열려있는지 확인
7. 헬스 체크 실행
/opt/moremong/health-check.sh
8. 백업 테스트
/opt/moremong/backup-db.sh
ls -lh /opt/moremong/backups/
16단계: OAuth 제공자 업데이트
OAuth 제공자 콘솔에서 리다이렉트 URI 업데이트:
Google Cloud Console:
- 승인된 리다이렉트 URI: https://moremong.com/login/oauth2/code/google
Kakao Developers:
- Redirect URI: https://moremong.com/login/oauth2/code/kakao
Naver Developers:
- Callback URL: https://moremong.com/login/oauth2/code/naver
백엔드 application.yml 업데이트:
application:
oauth2:
allowed-redirect-uris:
- https://moremong.com/worklog/oauth/callback
- https://moremong.com/worklog1/oauth/callback
- https://moremong.com/worklog2/oauth/callback
17단계: 성능 튜닝
프로덕션용 JVM 튜닝
/etc/systemd/system/moremong-api-1.service 편집
ExecStart를 최적화된 플래그로 업데이트:
ExecStart=/usr/bin/java \
-server \
-Xms1g \
-Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+UseStringDeduplication \
-XX:+OptimizeStringConcat \
-Djava.security.egd=file:/dev/./urandom \
-Dspring.profiles.active=prod \
-jar /opt/moremong/backend/moremong-restapi.jar
리로드 및 재시작
sudo systemctl daemon-reload
sudo systemctl restart moremong-api-1 moremong-api-2
PostgreSQL 튜닝
- docker-compose.yml에 PostgreSQL 튜닝 추가
sudo tee -a /opt/moremong/docker/docker-compose.yml <<'EOF'
command:
- "postgres"
- "-c"
- "max_connections=200"
- "-c"
- "shared_buffers=256MB"
- "-c"
- "effective_cache_size=1GB"
- "-c"
- "maintenance_work_mem=64MB"
- "-c"
- "checkpoint_completion_target=0.9"
- "-c"
- "wal_buffers=16MB"
- "-c"
- "default_statistics_target=100"
EOF
PostgreSQL 재시작
cd /opt/moremong/docker
sudo docker compose restart postgresql
문제 해결 명령어
# 서비스 로그 확인
sudo journalctl -u moremong-api-1 -n 100 --no-pager
sudo journalctl -u moremong-front-1 -n 100 --no-pager
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
# 서비스 재시작
sudo systemctl restart moremong-api-1 moremong-api-2
sudo systemctl restart moremong-front-1 moremong-front-2
sudo systemctl restart nginx
# 네트워크 연결 확인
sudo netstat -tuln | grep LISTEN
curl -I http://localhost:8090/management/health
curl -I http://localhost:5173/worklog
# Docker 로그
sudo docker compose -f /opt/moremong/docker/docker-compose.yml logs -f
# Nginx 테스트 및 리로드
sudo nginx -t
sudo nginx -s reload
# 디스크 공간 확인
df -h
sudo du -sh /opt/moremong/\*
sudo docker system df
# 프로세스 모니터링
htop
ps aux | grep java
ps aux | grep node
빠른 롤백 절차
# 모든 서비스 중지
sudo systemctl stop moremong-api-1 moremong-api-2
sudo systemctl stop moremong-front-1 moremong-front-2
# 백업에서 데이터베이스 복원
cd /opt/moremong/backups
LATEST*BACKUP=$(ls -t postgres*\*.sql.gz | head -1)
gunzip < $LATEST_BACKUP | docker exec -i moremong-postgres psql -U moremong -d moremong
# 이전 JAR/빌드 버전 배포
# (/opt/moremong/versions/에 버전별 백업 유지)
sudo cp /opt/moremong/versions/backend-v1.0.0.jar /opt/moremong/backend/moremong-restapi.jar
# 서비스 재시작
sudo systemctl start moremong-api-1 moremong-api-2
sudo systemctl start moremong-front-1 moremong-front-2
댓글남기기