우리네 장

PostgreSql Connection 관련, 작은 Troubleshooting 본문

작업/아이리스

PostgreSql Connection 관련, 작은 Troubleshooting

qpmi1zm29 2022. 11. 23. 10:28

이번에 유지보수를 맡게 된 프로젝트가 있다. STG에서 수기로 해당 프로젝트의 was을 스크립트를 이용해 shutdown과 startup만 해주었을 뿐인데, 아주 무수히 많은 에러가 나왔다....ㅎ


 

 

일단, 서버 재기동 시에 크게 2가지 Exception이 발생하였다.

 

1. java.net.KnownHostException 이 발생하면서 GUID 생성을 못한다는 에러

2. FATAL : remaining connection slots are reserved for non-replication superuser and rds_superuser connections 

 

 

1. java.net.KnownHostException 가 발생하면서 GUID 생성을 못한다는 에러

 

위 에러는 ehcahe 라이브러리에서 발생하는 에러이다.

ehcahe에서 내부적으로 java.net.InetAddress.getLocalhost()를 호출하는데, 

이 함수가 hostname을 lookup하여 이와 매칭되는 ip를 찾는다.

 

hostname?
- 네트워크에 연결된 장치들에게 부여된 고유한 이름

 

 

그런데 서버 장비의 /etc/hosts 파일에, 장비 ( hostname ) 에 대한 ip 즉, DNS 설정이 되어있지 않아 발생하였다. 

 

그래서 만약 장비에 대한 ip가 172.21.21.21 이고 hostname이 qpmi1zm29라면

hosts 파일에 qpmi1zm29 172.21.21.21 이라고 설정이 되어있어야 한다. 

 

 

 

2. FATAL : remaining connection slots are reserved for non-replication superuser and rds_superuser connections 

 

 

2번 에러는 위 에러 문구와 함께,  DB에 커넥션을 하는 도중에 SQLException이 발생했다고 로그가 생성되었다.

그래서 혹시나 방화벽 문제인가 싶어 아래 명령어로 통신 테스트를 진행하였다.

 

telnet은 보안 문제로 인해 지원을 안해주는 경우가 많은데 curl은 대부분의 경우 사용이 가능하다.

그러나 curl의 단점은 포트를 지정할 수 없다는 것인데, 다행이도 curl에서 telnet schema를 사용할 수 있었다.

 

아래와 같이 사용한다.

 

curl -v telnet://[ip or domain]:[port]

 

확인 결과 EC2 장비와 RDS는 정상적으로 연결이 되어 있었다.

 

위 에러를 구글링 해보니, DB Connection 초과로 인한 에러라는 내용을 발견 하였다. 

해당 db는 하나의 서비스가 아닌 여러 서비스가 참조하고 있는 DB 였는데,  서버를 재기동하면서 발생한 에러였기 때문에, 다른 서버가 맺은 Connection으로 인해 pool 할당이 되지 않는다고 생각했다. 

 

그래서 질의를 통해 사용 중인 DB의 MAX_CONNECTION과 현재 CONNECTION을 맺고 있는 연결의 수를 확인하였다. 

 

select * from pg_catalog.pg_settings where NAME ='max_connections'; --max_connection 수 구하기

select usename, count(*) from pg_catalog.pg_stat_activity group by usename; --현재 연결되어 있는 connection 구하기

 

 

RDS는 사용 중인 DB 스펙에 따라 자동으로 MAX_CONNECTION 을 계산하여 설정을 해준다고 한다. 

( https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/UserGuide/CHAP_Limits.html#RDS_Limits.MaxConnections )

 

위 질의문으로 확인 시 현재 MAX 값은 450 정도인데 할당된 CONNECTION이 꽉 차 있었다. 

 

그래서 Tomcat 서버의 DATASOURCE 설정에서 초기 할당 값 및 최대 할당 값을 수정하기 위해서 장비에 접속을 했다. 

 

🤔 그런데 현재 어플리케이션 이름으로 연결되어 있는 CONNECTION 의 개수는 50개 였는데, 설정된 최대 값은 20개 였다. 

이해가 되지 않았다. 어떻게 최대 값을 넘겨서 Pool이 할당이 될 수 있지???

 

 

 

알고보니, 장비에 동일한 process가 여러 개 떠있었다. 근데 이것도 이해가 안되었다. 수기로 shutdown과 startup을 하는데 어떻게 여러 개가 떠있나??..

 

여기서 주의할 점은 Pool 을 할당해야 하기 때문에, 서버가 기동 될 때 초기 세팅값만큼의 여유 Pool 개수가 남아 있어야 한다는 것이다. 

즉, 내가 톰캣에 initial 값을 25를 해놓았다면, PostgreSql에 남아있는  현재 할당 가능한  CONNECTION의 개수가 최소 25개 이상이어야 한다는 의미이다. 

쨌든 그래서 일단 여러 개의 프로세스를 모두 죽이고 하나의 프로세스만 남긴 후 재기동을 하니 정상 실행이 되었다.

 

 

 

근데 왜?? 여러 개가 떴을까?? 🤔 🤔

인프라 팀의 지원을 받아 확인해 보았는데, 서버 재기동 시,  Tomcat 스크립트를 수동으로 재기동 한 뒤에 +

Jenkins로 한 번 더 빌드를 하였는데 이게 문제였다. 

 

Jenkins가 사용하는 스크립트 중  stopApp.sh 안에 Tomcat  인스턴스 경로가 잘못 설정되어 있었다. 

그래서 shutdown을 엉뚱하게 하고 startup을 하니까, 인스턴스가 꺼지지 않고 시작만 계속 되었던 것이었다.... 😱

( Jenkins가 사용하는 스크립트를 관리하는 방법은 여러가지가 있다. gitlab을 통해 관리하는 경우도 있고, S3에 스크립트를 올려놓고 빌드가 진행될 때 해당 경로에서 복사해서 사용하는 경우도 있다. )

 

 

 

스크립트 수정 😍

 

톰캣 종료 시도를 한 번만 했던, 이전 스크립트와 달리, 혹여나 여러 개의 인스턴스가 떠있을 경우를 대비하여,

while문을 통해 어플리케이션 이름으로 떠있는 인스턴스가 존재하지 않을 떄까지 종료시키도록 하였습니다.

#!/bin/bash 
 
echo "stopApp" 
 
TomcatWasPID=$(ps aux | grep 어플리케이션이름 | grep data) 
while [[ "$TomcatWasPID" == *"어플리케이션이름"* ]] 
do 
echo 'kill tomcat' 
sudo -u tomcat 톰캣 shutdown 스크립트 경로
ps aux | grep 어플리케이션이름 | grep data | awk '{print $2}' | xargs kill -9 
sleep 5 
 
TomcatWasPID=$(ps aux | grep 어플리케이션이름 | grep data) 
 
done