데이터가치연구소 공식 블로그

행정정보공동이용 ESB 에이전트 설치기

기관공유데이터 관리시스템(단독형)을 구축하면서 행정정보공동이용센터 연계용 ESB 에이전트를 설치했습니다.

설치가이드는 20쪽 남짓인데, 실제로는 가이드에 없는 함정이 여섯 개쯤 있었습니다. 같은 길을 가실 분들을 위해 정리해 둡니다.


환경

  • OS: RHEL 8.10 (최소 설치)
  • 서버: WAS + 연계 통합 구성 (NIA 단독형 3티어 가이드 기반, 연계를 WAS에 합침)
  • 컨테이너: podman (NIA 패키지 v2.1.0)
  • 에이전트: ESB_AGENT 2.5
  • GPKI 표준 API: gpkiapiJava v1.5.2.0 (64bit)

에이전트는 컨테이너가 아니라 호스트에 직접 설치합니다. 이 점을 처음에 헷갈렸는데, NIA 패키지의 orgstd-link 컨테이너는 연계 업무를 처리하는 쪽이고, ESB 에이전트는 그 결과물을 실어 나르는 별개의 프로그램입니다.


1. 준비물

파일관리주체
ESB_AGENT_2.5.tar행공센 유지보수사업단
gpkiapiJava.tar (64bit)인증관리센터
gpkiapi.lic (라이선스)인증관리센터
배치용 서버인증서 (.cer/.key)인증관리센터

주의할 점!

GPKI API는 32bit와 64bit가 따로 옵니다. 파일명 끝을 꼭 확인하세요.

gpkiapiJava_v1.5.1.0_..._32bit_....tar   ← 이거 아님
gpkiapiJava_v1.5.2.0_..._64bit_....tar   ← 이것

2. OS 준비

최소 설치라 필요한 게 몇 개 빠져 있었습니다.

sudo dnf install -y java-1.8.0-openjdk-headless openldap-clients cyrus-sasl-lib

JDK는 -headless로 충분합니다. 에이전트는 java -jar로만 돌고 컴파일할 일이 없거든요. -devel을 깔면 X11 라이브러리가 줄줄이 딸려옵니다.

cyrus-sasl-lib는 가이드에 없는데 반드시 필요합니다. 이유는 4장에서 설명하겠습니다.

JAVA_HOME은 링크로

ls -ld /usr/lib/jvm/jre-1.8.0-openjdk
# → /etc/alternatives/jre_1.8.0_openjdk 를 가리킴

readlink -f $(which java)로 나오는 실경로에는 버전 문자열이 박혀 있어서, JDK 패치 후 경로가 사라집니다. 버전 고정 심볼릭 링크(jre-1.8.0-openjdk)를 쓰면 패치를 자동으로 따라갑니다.

echo 'export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk' >> ~/.bash_profile
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bash_profile

3. GPKI 표준 API 설치

mkdir -p ~/gpki/api && cd ~/gpki/api
tar xf gpkiapiJava_v1.5.2.0_*64bit*.tar
ln -s gpkiapiJava_v1.5.2.0 current     # 버전 바뀌어도 설정 안 고치려고

검증 스크립트 경로가 가이드와 다릅니다

가이드에는 javaDoc/jtest/class/run64.sh라고 되어 있는데, 실제로는 jtest/run.sh 입니다. 루트 바로 아래이고 class 폴더도, run64도 없습니다. 두 번 헛걸음했습니다.

cd ~/gpki/api/current/jtest
sh run.sh

4. 함정 ① — libsasl2.so.2

run.sh를 돌리자마자 이게 나왔습니다.

java.lang.UnsatisfiedLinkError: libgpkiapi.so: libsasl2.so.2:
cannot open shared object file: No such file or directory

GPKI 모듈이 RHEL 6 시절 빌드라 구버전 SASL 라이브러리(so.2)를 찾는데, RHEL 8은 so.3만 제공합니다. 게다가 최소 설치엔 그마저도 없었습니다. 그리고 ldd로는 안 잡힌다는 점입니다. 모듈이 dlopen으로 실행 시점에 로드하는 구조라 ldd | grep 'not found'가 깨끗하게 나옵니다. 그래서 반드시 run.sh를 실제로 돌려봐야 합니다.

sudo dnf install -y cyrus-sasl-lib
sudo ln -s libsasl2.so.3 /usr/lib64/libsasl2.so.2
sudo ldconfig

SASL2는 ABI가 안정적이라 호환 링크로 통합니다. 다만 OS 라이브러리 폴더에 링크를 추가한 조치라, OS 재설치 후엔 다시 해야 합니다.

참고로 OpenSSL은 걱정 안 하셔도 됩니다. el6 빌드라 1.0 계열을 찾을 줄 알았는데, ldd 결과 1.1에 링크되어 있어서 RHEL 8 기본으로 충족됐습니다. 가이드가 요구하는 openssl-1.0.2k 소스는 결국 안 썼습니다.


5. 함정 ② — 라이선스가 디렉터리

인증관리센터에서 받은 license 항목이 파일이 아니라 디렉터리였습니다. 진짜 라이선스 파일은 그 안에 있습니다.

license/
└── gpkiapi.lic      ← 이게 진짜
license_temp/
└── gpkiapi.lic      ← 임시 라이선스

cp -r license ~/esb_agent/lic/gpkiapi.lic 했다가 gpkiapi.lic이라는 이름의 디렉터리가 생겨서 한참 헤맸습니다.

mkdir -p ~/esb_agent/{lic,certs}
cp ~/install/license/gpkiapi.lic ~/esb_agent/lic/
cp ~/install/cert/*.cer ~/install/cert/*.key ~/esb_agent/certs/
chmod 600 ~/esb_agent/certs/*.key

인증서는 _env(암호화)·_sig(서명) 각각 .cer+.key로 4개 한 세트입니다. 배치용 서버인증서는 CN이 정보유통 ID와 같아야 합니다.

openssl x509 -in ~/esb_agent/certs/*_sig.cer -noout -subject -enddate

6. 함정 ③ — 라이선스 IP 불일치

run.sh를 다시 돌리니 에러 코드가 바뀌었습니다.

ErrCode=1100  Load license is failed          → 파일 못 찾음 (jtest/에 복사하면 해결)
ErrCode=1111  Match IP does not exist         → 파일은 읽었는데 IP가 안 맞음

GPKI 라이선스는 IP 귀속입니다. 그런데 신청서에 적은 IP가 NAT 접속 주소였고, 서버의 실제 NIC 주소는 달랐습니다.

hostname -I
# 10.x.x.x  10.x.x.x     ← 앞이 실제 NIC, 뒤는 podman 브리지

GPKI API는 서버 커널이 아는 로컬 IP만 검사합니다. NAT 장비에 붙은 주소는 서버가 모릅니다. 인증관리센터에 로컬 IP 기준으로 재발급 받으니 해결됐습니다.

신청서 쓸 때 ip -4 addr로 실제 주소를 확인하고 적으세요. 접속 주소를 적으면 저처럼 재발급 받아야 합니다.

두번째 IP는 podman이 만든 가상 브리지라 어디에도 쓰면 안 됩니다.


7. 에이전트 설정

mkdir -p ~/esb_agent && cd ~/esb_agent
tar xf ~/install/ESB_AGENT_2.5.tar
vi agent.properties

im.name은 IM_기관구분코드_정보유통ID 형식이고, 기관구분코드는 CO/GO/LG/BO 중 하나입니다. 저희 경우 배포본에 이미 기관값이 채워져 있었습니다.

im.host에는 서버 로컬 IP를 넣습니다. NAT 주소를 넣으면 바인딩 실패합니다.

송수신 폴더

설치환경요약서에 제출한 경로 그대로 만듭니다. 이 경로는 행공센이 어댑터를 생성할 때 고정되므로 제출 후 바꾸려면 재요청입니다.

sudo mkdir -p /share_data/esb/{send/work,recv/work}
sudo chown -R 1000:1000 /share_data/esb

/share_data가 컨테이너 볼륨이라 호스트에 없을 수 있습니다. 저희는 bind mount로 노출했습니다.

echo "/home/was_user/v2.1.0/volumes/share_data /share_data none bind 0 0" | sudo tee -a /etc/fstab
sudo mount -a

8. systemd 등록 — 함정 ④⑤

가이드에는 im.sh start 수동 실행만 있고 자동 기동 절차가 없습니다. 재부팅하면 에이전트가 안 뜹니다. 유닛을 직접 만들었는데, 여기서 두 번 더 걸렸습니다.

[Unit]
Description=ESB Agent (IM)
After=network-online.target
RequiresMountsFor=/share_data

[Service]
Type=forking
User=was_user
WorkingDirectory=/home/was_user/esb_agent/im/bin
Environment=JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk
Environment=PATH=/usr/lib/jvm/jre-1.8.0-openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=LD_LIBRARY_PATH=/home/was_user/gpki/api/current/lib64
ExecStart=/home/was_user/esb_agent/im/bin/im.sh start
ExecStop=/home/was_user/esb_agent/im/bin/im.sh stop
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

함정 ④ — PATH 줄이 필수입니다. im.sh가 JAVA_HOME을 안 보고 PATH의 java만 찾습니다. systemd는 .bash_profile을 읽지 않으니, PATH를 안 주면 java: command not found로 죽습니다. 수동으로는 되는데 재부팅 후에만 실패하는 전형적인 원인입니다.

함정 ⑤ — 종료 코드 143. 에이전트가 정상 종료할 때 143(SIGTERM)을 반환하는데, 이걸 성공으로 선언하지 않으면 systemctl stop을 해도 failed로 찍힙니다. 서비스는 멀쩡한데 상태만 실패로 보이는 거라, SuccessExitStatus=143 한 줄이면 해결됩니다.

PID 파일 위치 주의

im.sh가 PID 파일을 $HOME/im.pid에 쓰는데, systemd로 띄우면 $HOME이 작업 디렉터리로 잡혀 im/bin/im.pid에 생깁니다. 수동 기동과 systemd 기동을 섞어 쓰면 서로 stop이 안 됩니다. 한 가지로 통일하세요.

비정상 종료 후 InstanceManager Already Running!!이 나오면 잔존 PID 파일 때문입니다.

ps -ef | grep IM                       # 프로세스 없는 것 확인
rm -f ~/esb_agent/im/bin/im.pid
sudo systemctl start esb-agent

9. 기동 확인

sudo systemctl enable --now esb-agent
ps -ef | grep startup.jar

IM이 뜬 뒤 행공센에서 어댑터가 내려오면 프로세스가 IM 1개 + 어댑터 2개(송신·수신) 로 늘어납니다. 어댑터는 우리가 만드는 게 아니라 행공센이 요약서 기준으로 생성해 원격 배포합니다.

ls ~/esb_agent/adaptor/log/       # AD_..._SND_01 / AD_..._RCV_01 폴더 생기면 배포 완료

마치며 — 삽질 요약

함정증상해결
jtest 경로가이드와 다름jtest/run.sh
libsasl2.so.2UnsatisfiedLinkErrorcyrus-sasl-lib + 호환 링크
license가 디렉터리gpkiapi.lic이 폴더가 됨안의 파일을 복사
라이선스 IPErrCode=1111로컬 IP로 재발급
systemd PATH재부팅 후만 실패Environment=PATH=
종료 코드 143stop해도 failedSuccessExitStatus=143

가이드만 믿고 갔으면 일주일은 더 걸렸을 것 같습니다. 특히 ldd가 깨끗해도 실행은 실패할 수 있다는 것과, 라이선스는 접속 주소가 아니라 NIC 주소를 본다는 두 가지는 꼭 기억해 두시면 좋겠습니다.

guest
0 Comments
Oldest
Newest Most Voted