Run a Java app that resides on AWS EC2 as a daemon

I've had a hard time running a resident Java (and a language running on a JVM such as Scala) application as a daemon on AWS EC2 (though not specifically limited to EC2), so I'll leave it here.

Aside from "EC2 ??" ...

Requirements

Java application implementation

Service rc script

Create an rc script to start your Java application as a Linux service.

There are various styles of rc scripts, but I think that at least this is fine. In (Init.d and Start Scripts for Scala / Java Server Apps I customized it a little based on the contents introduced)

/etc/init.d/example


#!/bin/bash

### BEGIN INIT INFO
# Provides:          example
# chkconfig:         2345 99 99
# Required-Start:    $local_fs $network $named networking
# Required-Stop:     $local_fs $network $named networking
# Short-Description: example
# Description:       example
### END INIT INFO

DAEMON_NAME=example
DAEMON_DIR=/home/ec2-user/example
MODULE_NAME=example-server

START_SCRIPT="java -Duser.timezone=UTC -jar $DAEMON_DIR/$MODULE_NAME.jar"
#The process ID file is/var/Create under run
PID_FILE=/var/run/$DAEMON_NAME.pid
#Lock file/var/lock/Create under subsys
#If you do not create a lock file, the service will not be stopped when you shut down the machine.
LOCK_FILE=/var/lock/subsys/$DAEMON_NAME

# ***********************************************
# ***********************************************

ARGS="" # optional start script arguments
DAEMON=$START_SCRIPT

# colors
red='\e[0;31m'
green='\e[0;32m'
yellow='\e[0;33m'
reset='\e[0m'

echoRed() { echo -e "${red}$1${reset}"; }
echoGreen() { echo -e "${green}$1${reset}"; }
echoYellow() { echo -e "${yellow}$1${reset}"; }

start() {
  # $!Save the process ID of the process started in (Write it to the process ID file later)
  PID=`$DAEMON $ARGS > /dev/null 2>&1 & echo $!`
}

case "$1" in
start)
    if [ -f $PID_FILE ]; then
        PID=`cat $PID_FILE`
        echo $PID
        if [ -z "`ps axf | grep -w ${PID} | grep -v grep`" ]; then
            start
        else
            echoYellow "Already running [$PID]"
            exit 0
        fi
    else
        start
    fi

    if [ -z $PID ]; then
        echoRed "Failed starting"
        exit 3
    else
        #If the service can be started normally, create a process ID file and lock file.
        echo $PID > $PID_FILE
        touch $LOCK_FILE
        echoGreen "Started [$PID]"
        exit 0
    fi
;;

status)
    if [ -f $PID_FILE ]; then
        PID=`cat $PID_FILE`
        if [ -z "`ps axf | grep -w ${PID} | grep -v grep`" ]; then
            echoRed "Not running (process dead but pidfile exists)"
            exit 1
        else
            echoGreen "Running [$PID]"
            exit 0
        fi
    else
        echoRed "Not running"
        exit 3
    fi
;;

stop)
    if [ -f $PID_FILE ]; then
        PID=`cat $PID_FILE`
        if [ -z "`ps axf | grep -w ${PID} | grep -v grep`" ]; then
            echoRed "Not running (process dead but pidfile exists)"
            exit 1
        else
            PID=`cat $PID_FILE`
            #When stopping the process-Do not kill at 9
            #When forced to terminate, the shutdown hook is not executed and the processing being executed ends in the middle.
            kill -HUP $PID
            echoGreen "Stopped [$PID]"
            #If the service can be stopped normally, delete the process ID file and lock file.
            rm -f $PID_FILE
            rm -f $LOCK_FILE
            exit 0
        fi
    else
        echoRed "Not running (pid not found)"
        exit 3
    fi
;;

restart)
    $0 stop
    $0 start
;;

*)
    echo "Usage: $0 {status|start|stop|restart}"
    exit 1
esac

Register as a service

Deploy the implemented application module and rc script to the EC2 instance.

Here is an example of a template for building EC2 with Cloud Formation.

EC2AutoScalingConfig:
  Type: AWS::AutoScaling::LaunchConfiguration
  Metadata:
    # CloudFormation:Settings to enable Init to get resources from S3
    AWS::CloudFormation::Authentication:
      S3AccessCreds:
        type: S3
        roleName:
          Ref: EC2Role
        buckets:
          -S3 bucket name where modules etc. are stored

    #EC2 instance initialization process. Get the module from S3 and deploy it.
    AWS::CloudFormation::Init:
      configSets:
        Setup:
          - PreDeploy
          - DeployExampleServer
          - PostDeploy
          - StartApplication
      #Preprocessing. Installation of required packages, etc. It is better to keep it in AMI.
      PreDeploy:
        packages:
          yum:
            java-1.8.0-openjdk-devel: []
            awslogs: []
        files:
          /home/ec2-user/awslogs-agent-setup.py:
            source: https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py
            mode: "000755"
          /home/ec2-user/awslogs.conf:
            content: |+
              [general]
              state_file = /var/lib/awslogs/agent-state

              [/var/log/messages]
              log_group_name = /example/ec2
              log_stream_name = {instance_id}-sysmsg
              file = /var/log/messages
              datetime_format = %b %d %H:%M:%S
              buffer_duration = 5000
              initial_position = start_of_file
        commands:
          SetDefaultJVM:
            command: alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
      #Deploy application
      DeployExampleServer:
        files:
          #Get module from S3
          /home/ec2-user/example/example-server.jar:
            source:
              Fn::Sub: https://s3-${AWS::Region}.amazonaws.com/S3 bucket name where modules etc. are stored/example-server-1.0.0.jar
            owner: ec2-user
            group: ec2-user
          #Get rc script from S3
          /home/ec2-user/example/example:
            source:
              Fn::Sub: https://s3-${AWS::Region}.amazonaws.com/S3 bucket name where modules etc. are stored/example
            owner: ec2-user
            group: ec2-user
            mode: "000755"
          #Transfer the log output by the application to CloudWatch with awslogs
          /home/ec2-user/example/conf/awslogs.conf:
            content: |+

              [example]
              log_group_name = /example/example-server
              log_stream_name = {instance_id}-{{name}}
              file = /root/logs/example.log
              encoding = utf_8
              datetime_format = %Y/%m/%d %H:%M:%S.%f
              time_zone = UTC
              buffer_duration = 5000
              initial_position = start_of_file
              multi_line_start_pattern =  {datetime_format}
            owner: ec2-user
            group: ec2-user
          #A cron schedule that automatically restarts the application's JVM process when it goes down
          /home/ec2-user/example/conf/cron.conf:
            content: |+
              */5 * * * * /sbin/service example start > /dev/null 2>&1
            owner: ec2-user
            group: ec2-user
        #Note that if you write multiple commands in commands, they will be executed in alphabetical order.
        commands:
          #Place the rc script (/etc/init.d Below symbolic link)
          A_RegisterService:
            command: ln -s /home/ec2-user/example /etc/init.d/example
          #Add with chkconfig and set the application to start / stop automatically when the EC2 instance starts / stops
          B_AddChkconfig:
            command: chkconfig --add example && chkconfig example on
          C_AppendAwsLogsConf:
            command: cat /home/ec2-user/example/conf/awslogs.conf >> /home/ec2-user/awslogs.conf
          D_AppendCrontab:
            command: cat /home/ec2-user/example/conf/cron.conf >> /home/ec2-user/cron.conf

      PostDeploy:
        commands:
          A_PipUpgrade:
            command:
              pip install --upgrade pip
          B_ConfigureAwsLogs:
            command:
              Fn::Sub: python /home/ec2-user/awslogs-agent-setup.py -n -r ${AWS::Region} -c /home/ec2-user/awslogs.conf
          C_ConfigureCron:
            command: crontab /home/ec2-user/cron.conf
        services:
          sysvinit:
            awslogs:
              enabled: true
              ensureRunning: true
            crond:
              enabled: true
              ensureRunning: true

      StartApplication:
        commands:
          A_ManagedContentsSynchronizer:
            command: service example start
  Properties:
    :
    UserData:
      Fn::Base64:
        Fn::Sub: |+
          #!/bin/bash
          #Give the launched EC2 instance a descriptive name
          INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
          START_TIME=`date '+%Y%m%d%H%M'`
          aws ec2 create-tags --region ${AWS::Region} --resources ${!INSTANCE_ID} --tags Key=\"Name\",Value=example-${!START_TIME}

          # ---- Run cfn-init
          yum -y update
          yum update -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource EC2AutoScalingConfig --configsets Setup

that's all.

Recommended Posts

Run a Java app that resides on AWS EC2 as a daemon
Run TensorFlow on a GPU instance on AWS
Suspicious attacks that came as soon as I launched a blog on EC2
Periodically run a python program on AWS Lambda
# 2 Build a Python environment on AWS EC2 instance (ubuntu18.04)
Run GPU version tensorflow on AWS EC2 Spot Instances
Run YOLO v3 on AWS v2
Run YOLO v3 on AWS
# 3 Build a Python (Django) environment on AWS EC2 instance (ubuntu18.04) part2
Building a development environment with Maven on Google App Engine [Java]
Building an environment to run ChainerMN on a GPU instance on AWS
Preparing to run Flask on EC2
Run a Linux server on GCP
Run Python on Schedule on AWS Lambda
Implement a Django app on Hy
Run Matplotlib on a Docker container
Run headless-chrome on a Debian-based image
Run TensorFlow2 on a VPS server
Register as a package on PyPI
How to set up a jupyter notebook on ssh destination (AWS EC2)
Modules cannot be imported in Python on EC2 run from AWS Lambda
Run a batch of Python 2.7 with nohup on Amazon Linux AMI on EC2
Points that I often get hooked on writing as a MySQL beginner