EC2 서버에 JAVA를 설치하고 환경 설정을 모두 했는데도 아래와 같은 에러가 발생했다.
[ 에러 로그 ]
[ 환경 설정 ]
위치: /etc/profile.d/jdk.sh
export JAVA_HOME=/opt/jdk-17
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=/opt/jdk-17/jre/lib:/opt/jdk-17/lib/tools.jar
[ appspec.yml ]
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/backend
file_exists_behavior: OVERWRITE
permissions:
- object: /home/ec2-user/backend
owner: ec2-user
mode: 755
type:
- file
hooks:
ApplicationStop:
- location: scripts/stop_was.sh
timeout: 60
runas: ec2-user
ApplicationStart:
- location: scripts/run_new_was.sh
timeout: 60
runas: ec2-user
[ run_new_was.sh ]
# run_new_was.sh
#!/bin/bash
# JAVA_HOME과 PATH 설정을 명시적으로 로드
if [ -f /etc/profile.d/jdk.sh ]; then
source /etc/profile.d/jdk.sh
fi
echo "start run_new_was"
echo "current user: $USER"
echo "JAVA_HOME: $JAVA_HOME"
echo "PATH: $PATH"
shopt -q login_shell && echo "Login shell" || echo "Non-login shell"
PROJECT_ROOT="/home/ec2-user/backend"
JAR_FILE="$PROJECT_ROOT/build/libs/backend-0.0.1-SNAPSHOT.jar"
TARGET_PORT=8080
nohup java -jar -Dspring.profiles.active=prod -Dserver.port=${TARGET_PORT} ${JAR_FILE} > /home/ec2-user/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0
CodeDeploy는 /etc/profile이나 ~/.bashrc에 있는 쉘스크립트를 읽어와 환경변수를 로딩하지 못한다. 따라서 아래의 2가지 방법을 사용할 수 있다.
서버를 이관해야한다거나 새로운 프로젝트를 시작할 때 기존 프로젝트에 남아있는 코드를 최대한 활용할 수 있으면 좋겠다고 생각해서, 쉘스크립트에서 변수를 로딩하는 방식을 선택했다.
(Parameter Store 이용하는 방식 참고: https://blog.zooneon.dev/aws-codedeploy-environment-variables/)
아래 코드를 쉘 스크립트 초반에 추가해서 환경변수를 로딩하도록 했다. 경로는 JAVA_HOME 을 설정한 스크립트로 해주면 된다.
# JAVA_HOME과 PATH 설정 로드
if [ -f /etc/profile.d/jdk.sh ]; then
source /etc/profile.d/jdk.sh
fi
[ /etc/profile.d/jdk.sh ]
export JAVA_HOME=/opt/jdk-17
export PATH=$PATH:$JAVA_HOME/bin
bash 공식 문서를 보면, 로그인 여부에 따라 시작할 때 다른 파일을 참조한다고 나와있다.
interactive login shell의 경우, /etc/profile 파일을 읽은 후, ~/.bash_profile, ~/.bash_login, ~/.profile 차례대로 읽는다고 한다. 그리고 /etc/profile 에서는 /etc/profile.d 안에 있는 스크립트도 읽는다.
그리고 interactive non-login shell 의 경우 ~/.bashrc 파일을 읽는다.
로그인 여부에 따라 시작할 때 다른 쉘을 참조하는 것 맞지만, 이 모든 건 interactive 즉, 대화형 쉘인 경우에만 해당한다. 그럼 그 반대는 뭐냐면, Non-interactive, 비대화형 쉘이다.
대화형 쉘은 사용자와 상호작용하는 걸 말하는데, 사용자에게 a와 b를 입력받아서 더한 값을 출력하는 걸 예로 말할 수 있을 것 같다.
비대화형 쉘이 좀 더 흔히 접하는 방식일 것 같은데, 배포 스크립트를 실행한다거나 로그 수집, 모니터링을 위해 주기적으로 스크립트를 실행하는 것들을 예로 들 수 있다.
[ 대화형 쉘로 판단되는 경우 ]
[ 비대화형 쉘로 판단되는 경우 ]
[ 대화형 쉘 여부 파악하기 ]
[[ $- == *i* ]] && echo ‘Interactive’ || echo ‘not-interactive’
비대화형일 경우 /etc/environment에서 변수를 읽어온다고한다. 거기에 export BASH_ENV = "비대화형 시작 시 로딩하고 싶은 스크립트 위치"
이런식으로 써오면 읽어오는 것 같은데, 비대화형에 대한 정보는 많이 없어서 잘 모르겠다.
그렇다면, /etc/environment를 따로 설정하지 않은 경우에서
"비대화형 비로그인 상태라면 환경변수를 로딩하지 못할까?"
이 부분이 궁금해서 테스트를 해봤다.
우선 sudo su root로 비로그인 상태로 전환한 후에 쉘스크립트를 직접 수행해봤다.
비대화형 쉘에 비로그인 상태인데도 JAVA_HOME과 PATH 에 대한 값을 잘 읽어왔다. 왜 그런가 했는데, 부모 프로세스로부터 상속받을 수도 있다고 한다. 상속이 언제 어떤 경우에 되는지 정확한 건 잘 모르겠다.
case $- in
*i*) echo "Interactive shell" ;;
*) echo "Non-interactive shell" ;;
esac
shopt -q login_shell && echo "Login shell" || echo "Non-login shell"
이렇게 로그를 찍어서 확인해본 결과, 비대화형에 비로그인 상태로 쉘을 실행하고 있는 걸 알 수 있었다. 위에서 실험한 것처럼 비대화형에 비로그인 상태라도 환경변수 값을 상속받을 수도 있지만, CodeDeploy의 경우, 독립된 다른 환경에서 제한된 권한으로 스크립트를 실행하기 때문에 .bashrc 파일을 읽어오지 못할 수도 있다.