本文提供經過生產環境驗證的 Java Web 技術棧 (JDK + Tomcat + Nginx + MySQL) 在 Linux (RHEL/CentOS/Rocky/AlmaLinux 8/9、Ubuntu 20.04/22.04) 上的標準化部署、配置優化、安全加固、監控整合、備份恢復與版本升級全生命週期運維手冊。
# RHEL 8/9 系列 (OpenJDK 17/21 LTS)
dnf install -y java-17-openjdk-devel java-17-openjdk-headless
# 或 21
dnf install -y java-21-openjdk-devel
# Ubuntu 22.04/24.04
apt update && apt install -y openjdk-17-jdk-headless
# 設定預設版本
alternatives --config java
alternatives --config javac
# 下載 (範例 JDK 17.0.10)
wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.10%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz
tar -xzf OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz -C /opt/
ln -sfn /opt/jdk-17.0.10+7 /opt/java/current
# 環境變數 /etc/profile.d/java.sh
export JAVA_HOME=/opt/java/current
export PATH=$JAVA_HOME/bin:$PATH
# 登出重登或 source /etc/profile.d/java.sh
/opt/tomcat/ # 基礎安裝目錄 (只讀)
/opt/tomcat/instances/ # 實例目錄
├── portal/ # 實例 1
│ ├── conf/
│ ├── logs/
│ ├── webapps/
│ ├── temp/
│ └── work/
└── bbs/ # 實例 2
└── ...
create_tomcat_instance.sh
#!/bin/bash
BASE=/opt/tomcat
VER=9.0.89
INST_NAME=$1
HTTP_PORT=$2
SHUTDOWN_PORT=$3
AJP_PORT=$4
mkdir -p $BASE/instances/$INST_NAME/{conf,logs,webapps,temp,work}
cp -r $BASE/apache-tomcat-$VER/conf/* $BASE/instances/$INST_NAME/conf/
# 修改 server.xml 端口
xmlstarlet ed -L \
-u "/Server/@port" -v $SHUTDOWN_PORT \
-u "/Server/Service/Connector[@protocol='HTTP/1.1']/@port" -v $HTTP_PORT \
-u "/Server/Service/Connector[@protocol='AJP/1.3']/@port" -v $AJP_PORT \
$BASE/instances/$INST_NAME/conf/server.xml
# systemd 服務單元
cat > /etc/systemd/system/tomcat@$INST_NAME.service <
使用:./create_tomcat_instance.sh portal 8080 8005 8009
setenv.sh (實例 conf/ 目錄)
CATALINA_OPTS="-Xms4g -Xmx4g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g"
CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45"
CATALINA_OPTS="$CATALINA_OPTS -XX:+ParallelRefProcEnabled -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput"
CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:file=$CATALINA_BASE/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100m"
CATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"
CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=Asia/Taipei"
# /etc/nginx/conf.d/tomcat_upstream.conf
upstream portal_backend {
least_conn;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 64;
}
upstream bbs_backend {
least_conn;
server 127.0.0.1:8180 max_fails=3 fail_timeout=30s;
keepalive 64;
}
# /etc/nginx/conf.d/portal.conf
server {
listen 80;
listen 443 ssl http2;
server_name portal.example.com;
ssl_certificate /etc/nginx/certs/portal.pem;
ssl_certificate_key /etc/nginx/certs/portal.key;
location /static/ {
alias /opt/tomcat/instances/portal/webapps/ROOT/static/;
expires 1y; add_header Cache-Control "public, immutable";
}
location / {
proxy_pass https://portal_backend;
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_read_timeout 60s;
proxy_send_timeout 60s;
}
}
# MySQL 8.0 (RHEL 系列)
dnf install -y https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
dnf install -y mysql-community-server
# MariaDB 10.6 (Ubuntu)
apt update && apt install -y mariadb-server
/etc/my.cnf.d/99-production.cnf
[mysqld]
# 基礎
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
pid-file=/run/mysqld/mysqld.pid
port=3306
bind-address=0.0.0.0
skip-name-resolve
default-authentication-plugin=mysql_native_password
# InnoDB 核心
innodb_buffer_pool_size=12G # 實體記憶體 60~70%
innodb_log_file_size=2G
innodb_log_files_in_group=2
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
innodb_file_per_table=1
innodb_read_io_threads=8
innodb_write_io_threads=8
innodb_io_capacity=2000
innodb_io_capacity_max=4000
# 連線與並發
max_connections=2000
thread_cache_size=100
table_open_cache=4000
table_open_cache_instances=16
# 日誌與複寫
log_bin=mysql-bin
binlog_format=ROW
expire_logs_days=7
sync_binlog=1
gtid_mode=ON
enforce_gtid_consistency=ON
server-id=1
# 效能結構
performance_schema=ON
slow_query_log=ON
long_query_time=1
slow_query_log_file=/var/log/mysql/slow.log
# 字元集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
mysql_secure_installation
# 設定 root 密碼、移除匿名用戶、禁止 root 遠程登入、移除 test 資料庫
# 建立應用帳號 (最小權限原則)
CREATE USER 'app_portal'@'10.0.%' IDENTIFIED BY 'StrongPassw0rd!';
GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON portal_db.* TO 'app_portal'@'10.0.%';
FLUSH PRIVILEGES;
jmx_exporter (Port 9404) + java -javaagent:jmx_prometheus_javaagent.jar=9404:config.yamltomcat_exporter 或解析 manager/status?XML=truenginx-prometheus-exporter 抓取 /nginx_statusmysqld_exporter (需 GRANT PROCESS, REPLICATION CLIENT, SELECT ON performance_schema.* TO 'exporter'@'localhost';)node_exporter (系統指標)| 對象 | 工具/方式 | 頻率 | 保留 | 驗證 |
|---|---|---|---|---|
| MySQL 資料 | xtrabackup (物理熱備) + binlog | 全備每日、增備每小時 | 全備 14 天、增備 7 天 | 每週隨機還原測試 |
| Tomcat 應用/配置 | GitOps (ArgoCD/Flux) + Helm Chart | 每次部署 | 永久 (Git 歷史) | 自動化部署管線驗證 |
| Nginx 配置 | GitOps + 滾動更新 | 每次變更 | 永久 | 語法檢查 + 金絲雀發布 |
| JDK/OS 修補 | dnf-automatic / unattended-upgrades | 每日檢查 | N/A | 預發環境回歸測試 |
systemctl stop tomcat@instance → 替換二進位/套件升級 → systemctl startmysql_upgrade (8.0 內建) + 觀察錯誤日誌將上述標準化流程固化為 Ansible Playbook / Terraform Module / Helm Chart / GitOps Repository,即可實現「基礎設施即代碼」,讓 Java Web 技術棧的部署、變更、擴容、災難恢復全部納入可審計、可重現、可回滾的自動化管線。