Tomcat 容器化部署

Containerization and Deployment

概述

容器化技术为Tomcat应用部署提供了标准化、可移植和可扩展的解决方案。本文详细介绍Tomcat的Docker容器化、Kubernetes部署、Docker Compose编排和容器优化技术。

1. Docker基础容器化

1.1 基础Dockerfile

# Dockerfile - 基础Tomcat容器
FROM openjdk:11-jre-slim

# 设置环境变量
ENV CATALINA_HOME /opt/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
ENV JAVA_OPTS="-Xms512m -Xmx2048m -XX:+UseG1GC"

# 创建tomcat用户
RUN groupadd -r tomcat && \
    useradd -r -g tomcat tomcat

# 下载和安装Tomcat
RUN apt-get update && \
    apt-get install -y wget && \
    wget -O /tmp/tomcat.tar.gz \
    https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.65/bin/apache-tomcat-9.0.65.tar.gz && \
    tar -xzf /tmp/tomcat.tar.gz -C /opt && \
    mv /opt/apache-tomcat-9.0.65 $CATALINA_HOME && \
    rm /tmp/tomcat.tar.gz && \
    rm -rf $CATALINA_HOME/webapps/examples && \
    rm -rf $CATALINA_HOME/webapps/docs && \
    chown -R tomcat:tomcat $CATALINA_HOME && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 复制配置文件
COPY server.xml $CATALINA_HOME/conf/
COPY context.xml $CATALINA_HOME/conf/
COPY web.xml $CATALINA_HOME/conf/

# 复制应用
COPY app.war $CATALINA_HOME/webapps/

# 设置权限
RUN chmod +x $CATALINA_HOME/bin/*.sh

# 切换用户
USER tomcat

# 暴露端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# 启动命令
CMD ["catalina.sh", "run"]

1.2 优化的Dockerfile

# 多阶段构建Dockerfile
FROM maven:3.8-openjdk-11-slim AS builder

# 构建应用
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# 运行时镜像
FROM tomcat:9.0-jre11-openjdk-slim

# 环境变量
ENV CATALINA_OPTS="-Xms512m -Xmx2048m -XX:+UseG1GC -XX:+UseStringDeduplication"
ENV JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"

# 删除默认应用
RUN rm -rf $CATALINA_HOME/webapps/ROOT && \
    rm -rf $CATALINA_HOME/webapps/examples && \
    rm -rf $CATALINA_HOME/webapps/docs && \
    rm -rf $CATALINA_HOME/webapps/host-manager

# 复制构建的应用
COPY --from=builder /app/target/myapp.war $CATALINA_HOME/webapps/ROOT.war

# 复制配置文件
COPY config/server.xml $CATALINA_HOME/conf/
COPY config/context.xml $CATALINA_HOME/conf/
COPY config/logging.properties $CATALINA_HOME/conf/

# 健康检查脚本
COPY health-check.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/health-check.sh

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD /usr/local/bin/health-check.sh

EXPOSE 8080

1.3 健康检查脚本

#!/bin/bash
# health-check.sh

HEALTH_URL="http://localhost:8080/health"
TIMEOUT=10

# 检查HTTP响应
response=$(curl -s -o /dev/null -w "%{http_code}" --max-time $TIMEOUT "$HEALTH_URL")

if [ "$response" = "200" ]; then
    echo "Health check passed"
    exit 0
else
    echo "Health check failed: HTTP $response"
    exit 1
fi

2. Docker Compose编排

2.1 基础编排配置

# docker-compose.yml
version: '3.8'

services:
  tomcat:
    build: .
    ports:
      - "8080:8080"
    environment:
      - JAVA_OPTS=-Xms512m -Xmx1024m
      - CATALINA_OPTS=-Dspring.profiles.active=docker
    volumes:
      - ./logs:/opt/tomcat/logs
      - ./webapps:/opt/tomcat/webapps
    depends_on:
      - mysql
      - redis
    networks:
      - app-network
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: appdb
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "3306:3306"
    networks:
      - app-network
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    networks:
      - app-network
    restart: unless-stopped

volumes:
  mysql-data:
  redis-data:

networks:
  app-network:
    driver: bridge

2.2 高可用编排配置

# docker-compose-ha.yml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - tomcat1
      - tomcat2
      - tomcat3
    networks:
      - frontend
      - backend
    restart: unless-stopped

  tomcat1:
    build: .
    environment:
      - JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC
      - JVM_ROUTE=tomcat1
      - REDIS_HOST=redis
    volumes:
      - ./logs/tomcat1:/opt/tomcat/logs
    networks:
      - backend
    restart: unless-stopped

  tomcat2:
    build: .
    environment:
      - JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC
      - JVM_ROUTE=tomcat2
      - REDIS_HOST=redis
    volumes:
      - ./logs/tomcat2:/opt/tomcat/logs
    networks:
      - backend
    restart: unless-stopped

  tomcat3:
    build: .
    environment:
      - JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC
      - JVM_ROUTE=tomcat3
      - REDIS_HOST=redis
    volumes:
      - ./logs/tomcat3:/opt/tomcat/logs
    networks:
      - backend
    restart: unless-stopped

  mysql-master:
    image: mysql:8.0
    command: --server-id=1 --log-bin=mysql-bin --binlog-format=ROW
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_REPLICATION_USER: repl
      MYSQL_REPLICATION_PASSWORD: replpass
    volumes:
      - mysql-master-data:/var/lib/mysql
    networks:
      - backend
    restart: unless-stopped

  mysql-slave:
    image: mysql:8.0
    command: --server-id=2
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
    volumes:
      - mysql-slave-data:/var/lib/mysql
    depends_on:
      - mysql-master
    networks:
      - backend
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory 1gb
    volumes:
      - redis-data:/data
    networks:
      - backend
    restart: unless-stopped

volumes:
  mysql-master-data:
  mysql-slave-data:
  redis-data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

3. Kubernetes部署

3.1 基础Deployment

# tomcat-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-app
  labels:
    app: tomcat
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: tomcat
        image: myregistry/tomcat-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: JAVA_OPTS
          value: "-Xms1g -Xmx2g -XX:+UseG1GC"
        - name: CATALINA_OPTS
          value: "-Dspring.profiles.active=k8s"
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db.host
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: db.password
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: app-config
          mountPath: /opt/tomcat/conf/app.properties
          subPath: app.properties
        - name: logs
          mountPath: /opt/tomcat/logs
      volumes:
      - name: app-config
        configMap:
          name: app-config
      - name: logs
        emptyDir: {}
      restartPolicy: Always

---
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  selector:
    app: tomcat
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  db.host: mysql-service
  db.port: "3306"
  redis.host: redis-service
  app.properties: |
    server.port=8080
    logging.level.root=INFO
    spring.datasource.url=jdbc:mysql://${DB_HOST}:3306/appdb

---
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  db.password: YXBwcGFzcw== # base64 encoded

3.2 Ingress配置

# tomcat-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tomcat-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/use-regex: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - app.example.com
    secretName: app-tls
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: tomcat-service
            port:
              number: 80

3.3 HorizontalPodAutoscaler

# tomcat-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: tomcat-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: tomcat-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

4. 容器优化配置

4.1 JVM容器优化

# JVM容器优化配置
FROM openjdk:11-jre-slim

# 容器感知JVM参数
ENV JAVA_OPTS="-XX:+UseContainerSupport \
               -XX:MaxRAMPercentage=75.0 \
               -XX:+UseG1GC \
               -XX:+UseStringDeduplication \
               -XX:+PrintGCDetails \
               -XX:+PrintGCTimeStamps \
               -Xloggc:/opt/tomcat/logs/gc.log \
               -XX:+UseGCLogFileRotation \
               -XX:NumberOfGCLogFiles=5 \
               -XX:GCLogFileSize=10M"

# 容器优化的启动脚本
COPY start-tomcat.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/start-tomcat.sh

CMD ["/usr/local/bin/start-tomcat.sh"]

4.2 启动脚本优化

#!/bin/bash
# start-tomcat.sh

set -e

# 获取容器资源限制
MEMORY_LIMIT=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
CPU_LIMIT=$(cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us)

echo "Container Memory Limit: $MEMORY_LIMIT bytes"
echo "Container CPU Limit: $CPU_LIMIT"

# 动态计算JVM参数
if [ "$MEMORY_LIMIT" != "9223372036854775807" ]; then
    # 容器有内存限制
    MEMORY_MB=$((MEMORY_LIMIT / 1024 / 1024))
    HEAP_SIZE=$((MEMORY_MB * 70 / 100))

    export JAVA_OPTS="$JAVA_OPTS -Xms${HEAP_SIZE}m -Xmx${HEAP_SIZE}m"
fi

# 设置垃圾回收器
if [ "$MEMORY_MB" -gt 4096 ]; then
    export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC"
else
    export JAVA_OPTS="$JAVA_OPTS -XX:+UseParallelGC"
fi

# 等待依赖服务
echo "Waiting for dependencies..."
wait-for-it.sh $DB_HOST:3306 -t 60
wait-for-it.sh $REDIS_HOST:6379 -t 30

# 启动Tomcat
echo "Starting Tomcat with JAVA_OPTS: $JAVA_OPTS"
exec catalina.sh run

5. 容器监控和日志

5.1 监控配置

# monitoring.yaml
apiVersion: v1
kind: Service
metadata:
  name: tomcat-jmx
  labels:
    app: tomcat
spec:
  ports:
  - port: 9999
    name: jmx
  selector:
    app: tomcat

---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: tomcat-monitor
spec:
  selector:
    matchLabels:
      app: tomcat
  endpoints:
  - port: http
    interval: 30s
    path: /metrics

5.2 日志收集配置

# fluent-bit-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         1
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf

    [INPUT]
        Name              tail
        Path              /var/log/containers/tomcat-*.log
        Parser            docker
        Tag               tomcat.*
        Refresh_Interval  5

    [OUTPUT]
        Name  elasticsearch
        Match tomcat.*
        Host  elasticsearch
        Port  9200
        Index tomcat-logs

  parsers.conf: |
    [PARSER]
        Name        docker
        Format      json
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   On

6. 部署脚本和工具

6.1 Docker部署脚本

#!/bin/bash
# deploy-docker.sh

set -e

IMAGE_NAME="myregistry/tomcat-app"
VERSION=${1:-latest}
ENVIRONMENT=${2:-production}

echo "Deploying Tomcat application..."
echo "Image: $IMAGE_NAME:$VERSION"
echo "Environment: $ENVIRONMENT"

# 构建镜像
echo "Building Docker image..."
docker build -t $IMAGE_NAME:$VERSION .

# 推送到仓库
echo "Pushing to registry..."
docker push $IMAGE_NAME:$VERSION

# 部署
case $ENVIRONMENT in
    "development")
        echo "Deploying to development..."
        docker-compose -f docker-compose.dev.yml up -d
        ;;
    "staging")
        echo "Deploying to staging..."
        docker-compose -f docker-compose.staging.yml up -d
        ;;
    "production")
        echo "Deploying to production..."
        docker-compose -f docker-compose.prod.yml up -d
        ;;
    *)
        echo "Unknown environment: $ENVIRONMENT"
        exit 1
        ;;
esac

# 健康检查
echo "Waiting for application to start..."
sleep 30

if curl -f http://localhost:8080/health; then
    echo "Deployment successful!"
else
    echo "Deployment failed - health check failed"
    exit 1
fi

6.2 Kubernetes部署脚本

#!/bin/bash
# deploy-k8s.sh

set -e

NAMESPACE=${1:-default}
IMAGE_TAG=${2:-latest}
ENVIRONMENT=${3:-production}

echo "Deploying to Kubernetes..."
echo "Namespace: $NAMESPACE"
echo "Image Tag: $IMAGE_TAG"

# 创建命名空间
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -

# 更新镜像标签
sed -i "s//$IMAGE_TAG/g" k8s/tomcat-deployment.yaml

# 应用配置
kubectl apply -f k8s/ -n $NAMESPACE

# 等待部署完成
echo "Waiting for deployment to complete..."
kubectl rollout status deployment/tomcat-app -n $NAMESPACE --timeout=300s

# 检查Pod状态
kubectl get pods -n $NAMESPACE -l app=tomcat

# 获取服务地址
SERVICE_IP=$(kubectl get service tomcat-service -n $NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Service available at: http://$SERVICE_IP"

echo "Deployment completed successfully!"

小结

通过本文学习,你应该掌握:

  1. Tomcat Docker容器化的最佳实践
  2. 多阶段构建和镜像优化技术
  3. Docker Compose服务编排配置
  4. Kubernetes部署和服务管理
  5. 容器自动扩缩容配置
  6. JVM容器优化和资源管理
  7. 容器监控和日志收集
  8. 自动化部署脚本编写

下一篇文章将介绍Tomcat高级配置技巧。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""