Deployment Practices for Java Applications on Linux

In modern software development, deploying Java applications on Linux servers is a common practice. This article will detail the deployment process, best practices, and common troubleshooting solutions for Java applications in a Linux environment, helping developers efficiently complete application deployment tasks.

1. Pre-deployment Preparation

1.1 System Environment Check

Before deploying a Java application, it is essential to check the environment configuration of the target server:

# Check operating system version
lsb_release -a
uname -a

# Check Java environment
java -version
echo $JAVA_HOME

# Check memory and CPU
free -h
lscpu

# Check disk space
df -h

# Check network connection
ping -c 4 google.com

1.2 Security Settings

Ensure the server’s security configuration:

# Configure firewall (using ufw as an example)
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 8080  # Application port

# Create a dedicated user to run the application
sudo adduser appuser
sudo usermod -aG sudo appuser

# Set up SSH key authentication (recommended)
mkdir -p /home/appuser/.ssh
chmod 700 /home/appuser/.ssh
# Add public key to authorized_keys file

1.3 Directory Structure Planning

Establishing a reasonable directory structure facilitates management and maintenance:

# Application directory structure
sudo mkdir -p /opt/myapp/{bin,conf,lib,logs,temp}
sudo chown -R appuser:appuser /opt/myapp

# Log directory
sudo mkdir -p /var/log/myapp
sudo chown appuser:appuser /var/log/myapp

# Configuration file directory
sudo mkdir -p /etc/myapp
sudo chown appuser:appuser /etc/myapp

2. Manual Deployment Process

2.1 WAR Package Deployment (for Tomcat)

# 1. Stop Tomcat service
sudo systemctl stop tomcat

# 2. Backup existing application
sudo cp /opt/tomcat/webapps/myapp.war /opt/tomcat/webapps/myapp.war.backup

# 3. Upload new WAR package
scp myapp.war appuser@server:/tmp/

# 4. Deploy application
sudo mv /tmp/myapp.war /opt/tomcat/webapps/

# 5. Start Tomcat service
sudo systemctl start tomcat

# 6. Check deployment status
sudo tail -f /opt/tomcat/logs/catalina.out

2.2 JAR Package Deployment (for Spring Boot applications)

# 1. Upload JAR package
scp myapp.jar appuser@server:/tmp/

# 2. Stop existing application
sudo systemctl stop myapp

# 3. Deploy new version
sudo mv /tmp/myapp.jar /opt/myapp/
sudo chown appuser:appuser /opt/myapp/myapp.jar

# 4. Start application
sudo systemctl start myapp

# 5. Check application status
sudo systemctl status myapp

2.3 Configuration File Management

# Application configuration file
sudo vim /etc/myapp/application.properties

# Environment-specific configuration
sudo vim /etc/myapp/application-prod.properties

# Database configuration
sudo vim /etc/myapp/database.properties

3. Writing Startup Scripts

3.1 Simple Startup Script

Create a basic startup script:

#!/bin/bash
# /opt/myapp/bin/start.sh

APP_NAME="myapp"
APP_HOME="/opt/myapp"
JAR_FILE="$APP_HOME/myapp.jar"
LOG_FILE="/var/log/myapp/app.log"
PID_FILE="$APP_HOME/app.pid"

# Check if the application is already running
if [ -f $PID_FILE ]; then
    PID=$(cat $PID_FILE)
    if [ -d "/proc/$PID" ]; then
        echo "$APP_NAME is already running. PID: $PID"
        exit 1
    else
        rm $PID_FILE
    fi
fi

# Start application
echo "Starting $APP_NAME..."
nohup java -jar $JAR_FILE \
    --spring.profiles.active=prod \
    --server.port=8080 \
    > $LOG_FILE 2>&1 &

echo $! > $PID_FILE
echo "$APP_NAME started. PID: $(cat $PID_FILE)"

3.2 Stop Script

#!/bin/bash
# /opt/myapp/bin/stop.sh

APP_NAME="myapp"
PID_FILE="/opt/myapp/app.pid"

if [ -f $PID_FILE ]; then
    PID=$(cat $PID_FILE)
    if [ -d "/proc/$PID" ]; then
        echo "Stopping $APP_NAME. PID: $PID"
        kill $PID
        
        # Wait for application to stop
        while [ -d "/proc/$PID" ]; do
            sleep 1
        done
        
        rm $PID_FILE
        echo "$APP_NAME stopped."
    else
        echo "$APP_NAME is not running."
        rm $PID_FILE
    fi
else
    echo "$APP_NAME is not running."
fi

3.3 Restart Script

#!/bin/bash
# /opt/myapp/bin/restart.sh

APP_HOME="/opt/myapp"

$APP_HOME/bin/stop.sh
sleep 5
$APP_HOME/bin/start.sh

4. systemd Service Configuration

Create a professional systemd service file:

# /etc/systemd/system/myapp.service

[Unit]
Description=My Java Application
After=network.target

[Service]
Type=simple
User=appuser
Group=appuser

# Working directory
WorkingDirectory=/opt/myapp

# Start command
ExecStart=/usr/bin/java \
  -Xms512m \
  -Xmx2g \
  -server \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -jar /opt/myapp/myapp.jar \
  --spring.profiles.active=prod \
  --server.port=8080

# Environment variables
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
Environment=SPRING_CONFIG_LOCATION=file:/etc/myapp/

# Restart policy
Restart=always
RestartSec=10

# Log configuration
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

# Security settings
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Enable and manage the service:

# Reload systemd configuration
sudo systemctl daemon-reload

# Enable service to start on boot
sudo systemctl enable myapp.service

# Start service
sudo systemctl start myapp.service

# Check service status
sudo systemctl status myapp.service

# View service logs
sudo journalctl -u myapp.service -f

5. Best Practices for Configuration Management

5.1 Environment Variable Configuration

# /etc/myapp/environment.sh

export APP_ENV=production
export DB_HOST=localhost
export DB_PORT=3306
export DB_NAME=myapp
export DB_USER=myapp_user
export DB_PASSWORD=secure_password

export REDIS_HOST=localhost
export REDIS_PORT=6379

export LOG_LEVEL=INFO
export LOG_PATH=/var/log/myapp

5.2 Configuration File Template

# /etc/myapp/application.properties

# Database configuration
spring.datasource.url=jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Redis configuration
spring.redis.host=${REDIS_HOST}
spring.redis.port=${REDIS_PORT}

# Log configuration
logging.level.root=${LOG_LEVEL}
logging.file.path=${LOG_PATH}

# Server configuration
server.port=8080
server.servlet.context-path=/

# JVM configuration (passed via startup parameters)
# -Xms512m -Xmx2g -XX:+UseG1GC

6. Monitoring and Maintenance

6.1 Application Status Monitoring

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

APP_URL="http://localhost:8080/actuator/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $APP_URL)

if [ $RESPONSE -eq 200 ]; then
    echo "Application is healthy"
    exit 0
else
    echo "Application is unhealthy. HTTP Status: $RESPONSE"
    exit 1
fi

6.2 Log Rotation Configuration

# /etc/logrotate.d/myapp

/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 appuser appuser
    postrotate
        systemctl reload myapp > /dev/null 2>/dev/null || true
    endscript
}

6.3 Performance Monitoring Script

#!/bin/bash
# /opt/myapp/bin/monitor.sh

PID_FILE="/opt/myapp/app.pid"

if [ -f $PID_FILE ]; then
    PID=$(cat $PID_FILE)
    if [ -d "/proc/$PID" ]; then
        # Get process information
        CPU=$(ps -p $PID -o %cpu --no-headers)
        MEM=$(ps -p $PID -o %mem --no-headers)
        THREADS=$(ps -p $PID -o nlwp --no-headers)
        
        echo "PID: $PID"
        echo "CPU Usage: ${CPU}%"
        echo "Memory Usage: ${MEM}%"
        echo "Thread Count: $THREADS"
    else
        echo "Application is not running"
    fi
else
    echo "PID file not found"
fi

7. Troubleshooting

7.1 Common Startup Issues

# Check port usage
netstat -tulnp | grep :8080
lsof -i :8080

# Check disk space
df -h

# Check memory usage
free -h

# View application logs
tail -f /var/log/myapp/app.log
journalctl -u myapp.service -f

7.2 Out of Memory Issues

# Add JVM parameters for memory analysis
-Xmx2g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/myapp/

# Use jstat to monitor GC
jstat -gc <PID> 1s

# Use jmap to analyze heap memory
jmap -heap <PID>
jmap -histo <PID>

7.3 Database Connection Issues

# Test database connection
mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASSWORD -e "SELECT 1;"

# Check connection pool configuration
# Look for connection pool related information in application logs
grep -i "connection\|pool" /var/log/myapp/app.log

8. Introduction to Automated Deployment

8.1 Using Shell Scripts to Simplify Deployment

#!/bin/bash
# /opt/myapp/bin/deploy.sh

APP_NAME="myapp"
APP_HOME="/opt/myapp"
BACKUP_DIR="/opt/myapp/backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup current version
if [ -f "$APP_HOME/myapp.jar" ]; then
    cp $APP_HOME/myapp.jar $BACKUP_DIR/myapp_$TIMESTAMP.jar
    echo "Backup created: myapp_$TIMESTAMP.jar"
fi

# Stop application
$APP_HOME/bin/stop.sh

# Deploy new version
cp /tmp/myapp.jar $APP_HOME/
chown appuser:appuser $APP_HOME/myapp.jar

# Start application
$APP_HOME/bin/start.sh

# Wait for application to start
sleep 10

# Health check
$APP_HOME/bin/health-check.sh

8.2 Environment Configuration Management

#!/bin/bash
# /opt/myapp/bin/configure.sh

ENV=$1
CONFIG_DIR="/etc/myapp"

case $ENV in
    "dev")
        cp $CONFIG_DIR/application-dev.properties $CONFIG_DIR/application.properties
        ;;
    "test")
        cp $CONFIG_DIR/application-test.properties $CONFIG_DIR/application.properties
        ;;
    "prod")
        cp $CONFIG_DIR/application-prod.properties $CONFIG_DIR/application.properties
        ;;
    *)
        echo "Usage: $0 {dev|test|prod}"
        exit 1
        ;;
esac

echo "Configuration switched to $ENV environment"

9. Conclusion

Deploying Java applications on Linux is a comprehensive task that involves multiple aspects. Through this article, we have learned:

  1. 1. Pre-deployment Preparation: Environment checks, security settings, and directory planning
  2. 2. Manual Deployment Process: Methods for deploying WAR and JAR packages
  3. 3. Script Writing: Writing startup, stop, and restart scripts
  4. 4. Service Management: Professional service management using systemd
  5. 5. Configuration Management: Best practices for environment variables and configuration files
  6. 6. Monitoring and Maintenance: Application status monitoring and log management
  7. 7. Troubleshooting: Diagnosis and solutions for common issues
  8. 8. Introduction to Automation: Using scripts to simplify the deployment process

In actual projects, it is recommended to:

  • • Establish standardized deployment processes
  • • Use configuration management tools (such as Ansible, Puppet)
  • • Implement Continuous Integration/Continuous Deployment (CI/CD)
  • • Establish a comprehensive monitoring and alerting mechanism
  • • Regularly perform backups and disaster recovery drills

Through continuous practice and improvement, an efficient and reliable Java application deployment system can be established.

Leave a Comment