#command

[Linux] CentOS 7 메모리 사용률 분석 완벽 가이드: free 명령어 변화와 실질 사용량 계산

CentOS 7(RHEL 7)부터 free 명령어의 출력 형식이 변경되었습니다. 기존의 buffers/cache 라인이 사라지고 available 컬럼이 추가된 배경을 이해하고, 정확한 메모리 사용률을 계산하는 방법을 정리합니다.

0. 배경 지식: CentOS 6 vs 7 차이점

리눅스는 남는 메모리를 적극적으로 캐시(Cache)로 사용하여 성능을 높입니다. 따라서 'Used'가 높다고 해서 반드시 메모리가 부족한 것은 아닙니다.

  • CentOS 6 이하: -/+ buffers/cache 행을 통해 실질 사용량을 확인했습니다.
  • CentOS 7 이상: 커널이 직접 계산한 available 컬럼을 통해 "즉시 사용 가능한 메모리"를 판단합니다.

Test Environment

  • OS: CentOS 7 (Kernel 3.10.0-957.el7.x86_64)

1. 메모리 확인 명령어 (free)

가장 기본적인 확인 방법입니다. -m 옵션은 Megabyte 단위로 출력합니다.

[root@localhost sw]# free -m
              total        used        free      shared  buff/cache   available
Mem:           7803         892        4686         263        2224        6277
Swap:          8064           0        8064

실시간 모니터링 및 상세 보기

-w(wide) 옵션을 주면 buffer와 cache를 분리해서 볼 수 있고, -s(seconds) 옵션으로 반복 조회가 가능합니다.

# 1초 간격으로 buffer/cache를 분리하여 출력
free -mw -s 1

2. 메모리 사용률 계산 공식 (Calculation)

단순히 used / total로 계산하면 캐시 메모리까지 사용량에 포함되어 수치가 과도하게 높게 나옵니다. 목적에 따라 계산 방식을 달리해야 합니다.

공식 A: OS 관점의 사용률 (캐시 포함)

메모리가 실제로 얼마나 할당되어 있는지(반납되지 않은 상태) 확인할 때 사용합니다.

$$ Usage(\%) = \frac{used}{total} \times 100 $$

공식 B: 애플리케이션 관점의 실질 사용률 (권장)

"실제로 메모리가 부족한가?"를 판단할 때는 available 값을 기준으로 해야 합니다.

$$ Usage(\%) = \frac{total - available}{total} \times 100 $$

Tip: sar -r 1 명령어를 통해서도 %memused(메모리 사용률)를 확인할 수 있습니다.

3. 주요 파라미터 설명 (Parameters)

CentOS 7 free 명령어의 각 컬럼이 의미하는 바는 다음과 같습니다.

파라미터 설명
total 설치된 전체 물리 메모리 크기
used 현재 할당되어 사용 중인 메모리 (total - free - buff/cache)
free 아무 용도로도 사용되지 않는 완전한 유휴 메모리
shared tmpfs(램디스크) 등 프로세스 간 공유되는 메모리
buff/cache 커널 버퍼 및 파일 시스템 캐시 (필요시 즉시 반환 가능)
available (핵심) 스와핑(Swapping) 없이 새로운 프로세스 실행에 즉시 할당 가능한 메모리 추정치

Next Step:
메모리 사용률이 비정상적으로 높다면(available 부족), top 명령어로 메모리 누수(Leak)가 의심되는 프로세스를 찾거나, echo 3 > /proc/sys/vm/drop_caches 명령어로 캐시를 강제 반환하여 가용 공간을 확보하는 방법을 검토해 보세요.

Open Stream →
#command

[Linux] Java 실제 설치 경로 확인 방법: which vs readlink 차이점 및 JAVA_HOME 설정

리눅스 환경에서 java 명령어의 실제 설치 위치(Absolute Path)를 찾는 방법을 정리합니다. 단순 경로 확인용인 which와 심볼릭 링크의 원본을 찾아주는 readlink -f의 차이점을 이해하고, 이를 통해 JAVA_HOME을 설정하는 팁을 다룹니다.

0. 배경 지식 (Context)

리눅스(특히 CentOS/RHEL 계열)는 alternatives 시스템을 사용하여 여러 버전의 프로그램을 관리합니다. 때문에 /usr/bin/java는 실제 실행 파일이 아니라, 여러 단계의 바로가기(Symbolic Link)로 연결된 껍데기일 확률이 높습니다.

Test Environment

  • OS: CentOS 7 (3.10.0-957.el7.x86_64)
  • Package: OpenJDK 1.8

1. 명령어 경로 확인 (which)

가장 기본적으로 사용하는 명령어입니다. 환경 변수 $PATH에 등록된 경로 중 어디에 있는 명령어가 실행되는지 알려줍니다.

[wasadm@localhost ~]$ which java
/usr/bin/java
한계점: 위 결과인 /usr/bin/java는 실제 파일이 아닌 심볼릭 링크일 가능성이 높아서, 이 경로를 JAVA_HOME으로 설정하면 오류가 발생합니다.

2. 원본 경로 추적 (readlink -f)

심볼릭 링크가 아무리 여러 단계로 꼬여 있어도, 최종 원본 파일의 절대 경로를 한 번에 찾아주는 가장 확실한 명령어입니다.

# 구문: readlink -f [심볼릭_링크_경로]
[wasadm@localhost ~]$ readlink -f /usr/bin/java

# 출력 결과 (실제 설치 위치)
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-7.b13.el7.x86_64/jre/bin/java

위 결과에서 마지막 /bin/java를 제외한 앞부분이 바로 JAVA_HOME이 됩니다.


3. 링크 구조 단계별 확인 (ls -l)

readlink가 어떻게 동작하는지 ls -l 명령어로 한 단계씩 추적해보면 리눅스의 alternatives 구조를 이해할 수 있습니다.

Step 1: 실행 파일 확인

[wasadm@localhost ~]$ ls -l /usr/bin/java
lrwxrwxrwx 1 root root 22 Feb  5 01:36 /usr/bin/java -> /etc/alternatives/java

/usr/bin/java/etc/alternatives/java를 가리키고 있습니다.

Step 2: alternatives 확인

[wasadm@localhost ~]$ ls -l /etc/alternatives/java
lrwxrwxrwx 1 root root 71 Feb  5 01:36 /etc/alternatives/java -> /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-7.b13.el7.x86_64/jre/bin/java

/etc/alternatives/java가 비로소 실제 JDK가 설치된 경로를 가리키고 있습니다. readlink -f는 이 과정을 한 번에 수행해 줍니다.


4. 활용: JAVA_HOME 환경 변수 설정

위에서 찾은 경로를 활용하여 환경 변수를 등록합니다. (/etc/profile 또는 ~/.bash_profile)

# 원본 경로: /usr/lib/jvm/java-1.8.0-.../jre/bin/java
# bin/java 앞부분까지만 복사

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-7.b13.el7.x86_64/jre
export PATH=$JAVA_HOME/bin:$PATH

Next Step:
만약 서버에 여러 버전의 Java가 설치되어 있어 기본 실행 버전을 변경하고 싶다면, alternatives --config java 명령어를 사용하여 간편하게 스위칭할 수 있습니다.

Open Stream →
#command

[Linux] 파일/디렉토리 권한 관리 완벽 가이드: chmod 명령어와 755, 777의 의미

리눅스 환경(CentOS 7)에서 파일 및 디렉토리의 접근 권한을 제어하는 chmod 명령어 사용법을 정리합니다. 숫자 모드(755, 644)와 문자열 모드(u+x)의 차이를 이해하고, -R 옵션을 통해 하위 경로까지 일괄 적용하는 실무 예제를 다룹니다.

0. 배경 지식: 권한의 구조 (Permission Structure)

리눅스의 파일 권한은 소유자(User), 그룹(Group), 기타(Other) 세 가지 대상에 대해 읽기(r), 쓰기(w), 실행(x) 권한을 부여하는 구조입니다.

권한 확인 (ls -l)

ls -l
# 결과 예시: drwxr-xr-x (755)
# d(디렉토리) | rwx(소유자) | r-x(그룹) | r-x(기타)

1. 권한 변경 (chmod)

chmod(Change Mode) 명령어를 사용하여 파일이나 디렉토리의 권한을 변경합니다. 이미 생성된 파일의 현재 상태를 변경하는 명령어입니다.

옵션 설명

  • -R (--recursive): 디렉토리 내부의 모든 파일과 하위 디렉토리까지 재귀적으로(일괄) 변경합니다. (가장 많이 사용)

실무 사용 예시

# 1. WAS 디렉토리: 소유자만 모든 권한(7), 나머지는 접근 불가(0)
chmod -R 700 ./was

# 2. 웹 루트 및 바이너리: 소유자(7), 그룹/기타는 읽기/실행만(5) - 일반적인 설정
chmod -R 755 ./web
chmod -R 755 ./bin

# 3. 이미지 업로드 폴더: 누구나 쓰고 읽기 가능 (보안 주의!)
chmod -R 777 ./img

2. 권한 설정 방식 A: 숫자 모드 (Octal Mode)

가장 직관적이고 많이 사용되는 방식입니다. 각 권한에 할당된 점수(4, 2, 1)를 더해서 표현합니다.

권한 (Permission) 기호 (Symbol) 숫자 값 (Octal)
Read (읽기) r 4
Write (쓰기) w 2
Execute (실행) x 1

조합 예시

  • 7 (rwx): 4 + 2 + 1 (모든 권한)
  • 6 (rw-): 4 + 2 (읽기/쓰기 - 일반 파일)
  • 5 (r-x): 4 + 1 (읽기/실행 - 실행 파일, 디렉토리)
  • 0 (---): 권한 없음

3. 권한 설정 방식 B: 문자열 모드 (Symbolic Mode)

특정 대상의 권한만 콕 집어서 추가(+)하거나 제거(-)할 때 유용합니다.

대상 (Who) 연산자 (Op) 권한 (Permission)
u: User (소유자)
g: Group (그룹)
o: Other (기타)
a: All (전체, ugo)
+: 추가
-: 제거
=: 지정
r: 읽기
w: 쓰기
x: 실행

사용 예시

# run.sh 파일에 실행(x) 권한 추가 (모든 사용자에게)
chmod +x run.sh

# 그룹(g)에게 쓰기(w) 권한 제거
chmod g-w file.txt

# 기타 사용자(o)에게 읽기(r) 권한만 할당
chmod o=r file.txt

Next Step:
파일의 권한뿐만 아니라 소유권을 변경해야 할 때는 chown(Change Owner) 명령어를 사용해야 합니다. chown user:group filename 형식을 함께 익혀두세요.

Open Stream →
#EAP7

[JBoss EAP 7] Datasource 패스워드 암호화 가이드: Picketbox Security Domain 설정

보안 컴플라이언스 준수를 위해 JBoss EAP 7의 standalone.xml 또는 domain.xml 파일 내에 저장되는 DB 패스워드를 암호화하는 방법을 정리합니다. org.picketbox 모듈을 이용하여 암호화된 문자열을 생성하고, Security Domain을 통해 데이터소스와 연동합니다.

0. 배경 및 준비 (Context)

JBoss 설정 파일에 비밀번호를 평문으로 저장하면 보안 감사 시 지적 대상이 됩니다. 이를 해결하기 위해 JBoss는 Security Domain을 통해 인증 과정을 위임하는 방식을 제공합니다.

Test Environment

  • OS: CentOS 7.2
  • WAS: JBoss EAP 7.2
  • JDK: 1.8

1. 비밀번호 암호화 문자열 생성 (Encryption)

가장 먼저 실제 DB 패스워드를 암호화된 문자열로 변환해야 합니다. JBoss 내부 모듈인 picketbox를 Java로 실행하여 변환합니다.

암호화 스크립트 (create_encrypt_pw.sh)

JBoss 패치(CP)가 적용되면 .overlays 디렉토리에 라이브러리가 변경될 수 있습니다. 아래 스크립트는 패치 여부를 자동으로 감지하여 적절한 jar 파일을 찾아 실행합니다.

#!/bin/sh

# 1. 환경 변수 설정 (사용자 환경에 맞게 수정 필수)
export JAVA_HOME="/SW/was/java1.8"
export PATH="$JAVA_HOME/bin:$PATH"
JBOSS_HOME="/SW/was/JBoss7.2"

# 2. 라이브러리 경로 탐색 (Overlay 지원)
OVERLAY_DIRECTORY="$JBOSS_HOME/modules/system/layers/base/.overlays"
SEARCH_DIRECTORY="$JBOSS_HOME/modules/system/layers/base/org/picketbox/main"

if [ -d "$OVERLAY_DIRECTORY" ]; then
    # 최신 패치 디렉토리 확인
    PATCH_SUBDIRECTORY=$(ls -dt $OVERLAY_DIRECTORY/* | grep "CP" | head -n 1)
    if [ -n "$PATCH_SUBDIRECTORY" ]; then
        echo "Info: Patch directory detected: $PATCH_SUBDIRECTORY"
        SEARCH_DIRECTORY="$PATCH_SUBDIRECTORY/org/picketbox/main"
    fi
fi

# 3. Classpath 설정
export CLASSPATH=$(find "$SEARCH_DIRECTORY" -name "*.jar" -print | tr '\n' ':')$CLASSPATH

# 4. 암호화 실행
echo ""
read -p "Enter Database Password : " PASSWORD
echo ""
echo "####################################################"
java org.picketbox.datasource.security.SecureIdentityLoginModule "$PASSWORD"
echo "####################################################"
echo ""

실행 결과 예시

Encoded password: 9fdd42c2a7390d3

출력된 Encoded password 값을 복사해 둡니다.


2. Security Domain 설정 (standalone.xml)

생성한 암호화 패스워드를 사용하는 보안 도메인을 정의합니다. security 서브시스템 내에 작성합니다.

설정 내용

<subsystem xmlns="urn:jboss:domain:security:2.0">
    <security-domains>
        
        <security-domain name="encryptedSecurityDB" cache-type="default">
            <authentication>
                <login-module code="org.picketbox.datasource.security.SecureIdentityLoginModule" flag="required">
                    <module-option name="username" value="sa"/>
                    <module-option name="password" value="9fdd42c2a7390d3"/> <!-- 암호화된 값 -->
                    <module-option name="managedConnectionFactoryName" value="jboss.jca:service=LocalTxCM"/>
                </login-module>
            </authentication>
        </security-domain>
        
    </security-domains>
</subsystem>
옵션 설명:
  • username: DB 접속 계정 ID (평문)
  • password: 위에서 생성한 암호화된 패스워드
  • managedConnectionFactoryName: jboss.jca:service=LocalTxCM (고정값 사용)

3. Datasource 연동 설정

이제 데이터소스가 평문 비밀번호 대신 위에서 만든 Security Domain을 바라보도록 수정합니다.

수정 전 (Before)

<datasource ...>
    ...
    <security>
        <user-name>sa</user-name>
        <password>admin1234</password>
    </security>
</datasource>

수정 후 (After)

user-namepassword 태그를 삭제하고 security-domain 태그를 추가합니다.

<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
    <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
    <driver>h2</driver>
    
    <security>
        <security-domain>encryptedSecurityDB</security-domain>
    </security>
</datasource>

4. 검증 (Verification)

  1. 설정 저장 후 JBoss 서버를 재기동(Reload)합니다.
  2. 관리 콘솔 또는 CLI에서 Test Connection을 수행하여 연결 성공 여부를 확인합니다.
  3. 연결 성공 시, 설정 파일에 비밀번호 평문이 남아있지 않은지 재확인합니다.

Next Step:
만약 비밀번호 뿐만 아니라 키스토어 패스워드 등 JBoss 설정 전반의 민감 정보를 보호해야 한다면, Vault(볼트) 구성을 추가로 검토해 보십시오.

Open Stream →
#EAP7

[JBoss EAP 7] Windows 환경 도메인 모드(Domain Mode) 구축: Master/Slave 구성 및 CLI 제어

JBoss EAP 7.2를 도메인 모드로 구성하여 중앙 집중식 관리 환경을 구축합니다. Windows Server 환경에서 마스터(Master)와 슬레이브(Slave) 호스트를 설정하고, add-user를 통해 보안 인증(Secret Value)을 처리하는 과정, 그리고 CLI를 이용한 제어 방법을 다룹니다.

0. 배경 지식 (Context)

도메인 모드는 Domain Controller(Master)가 전체 정책을 관리하고, Host Controller(Slave)들이 실제 서버 인스턴스를 구동하는 구조입니다.

  • Master: 설정 및 배포 중앙 관리 (서버 인스턴스를 직접 띄우지 않는 것을 권장)
  • Slave: 실제 애플리케이션이 구동되는 서버 그룹 관리

Test Environment

  • OS: Windows NT (10/Server)
  • Version: JBoss EAP 7.2
  • Topology: One-Box 구성 (하나의 서버에 Master/Slave 디렉토리 분리)

1. 관리자 계정 생성 및 인증 토큰 확보

도메인 컨트롤러(Master)와 호스트 컨트롤러(Slave) 간의 통신을 위해서는 보안 인증이 필요합니다. add-user.bat를 통해 계정을 생성하고 Secret Value를 확보해야 합니다.

실행 절차

F:\app\Redhat\JBoss7.2\bin> add-user.bat

# 1. 관리자 계정 선택
What type of user do you wish to add?
 a) Management User (mgmt-users.properties)
 b) Application User (application-users.properties)
(a): a

# 2. 계정 정보 입력 (Username: admin / Password 설정)
# ... (중략) ...

# 3. 프로세스 간 연결 승인 (중요!)
Is this new user going to be used for one AS process to connect to another AS process?
e.g. for a slave host controller connecting to the master ...
yes/no? yes

# 4. 결과값 복사
To represent the user add the following to the server-identities definition
Check Point: 마지막에 출력된 <secret value="..." /> 문자열을 반드시 복사해 두십시오. Slave 설정 시 사용됩니다.

2. 설정 파일 수정 (Configuration)

Master와 Slave 각각의 host.xml 파일을 수정하여 역할을 정의합니다.

1) Master 설정 (host-master.xml)

자신이 도메인 컨트롤러임을 명시합니다.

<host xmlns="urn:jboss:domain:8.0" name="test-master">
    
    <domain-controller>
       <local/>
    </domain-controller>
    ...
</host>

2) Slave 설정 (host-slave.xml)

Master에 연결하기 위한 인증 정보와 고유한 호스트 이름을 설정합니다. 한 장비에서 띄울 경우 포트 충돌 방지를 위해 관리 포트를 변경해야 합니다.

<host xmlns="urn:jboss:domain:8.0" name="slave-node01">

    <server-identities>
        <secret value="YWRtaW4xMiMk" />
    </server-identities>

    <domain-controller>
        <remote security-realm="ManagementRealm">
            <discovery-options>
                <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9990}"/>
            </discovery-options>
        </remote>
    </domain-controller>

    <management-interfaces>
        <http-interface security-realm="ManagementRealm">
            <http-upgrade enabled="true"/>
            <socket interface="management" port="${jboss.management.http.port:19990}"/>
        </http-interface>
    </management-interfaces>
</host>

3. 도메인 기동 (Start Scripts)

디렉토리(domain/base/dir)를 분리하여 운영하는 환경을 가정한 기동 스크립트입니다.

Master 기동

F:\app\Redhat\JBoss7.2\bin\domain.bat ^
 -Djboss.domain.base.dir="F:\app\Redhat\JBoss7.2\master" ^
 -b=192.168.0.6 ^
 -bmanagement=192.168.0.6 ^
 --host-config=host-master.xml

Slave 기동 (Node 01 & 02)

Slave는 --master-address 옵션으로 Master를 바라보며, -Djboss.socket.binding.port-offset으로 서비스 포트 충돌을 방지합니다.

:: Slave Node 01 (Port Offset: 1000)
F:\app\Redhat\JBoss7.2\bin\domain.bat ^
 -Djboss.domain.base.dir="F:\app\Redhat\JBoss7.2\node01" ^
 -b=192.168.0.6 ^
 -bmanagement=192.168.0.6 ^
 --host-config=host-slave.xml ^
 --master-port=9990 ^
 --master-address=192.168.0.6 ^
 -Djboss.socket.binding.port-offset=1000

:: Slave Node 02 (Port Offset: 2000)
F:\app\Redhat\JBoss7.2\bin\domain.bat ^
 -Djboss.domain.base.dir="F:\app\Redhat\JBoss7.2\node02" ^
 -b=192.168.0.6 ^
 -bmanagement=192.168.0.6 ^
 --host-config=host-slave.xml ^
 --master-port=9990 ^
 --master-address=192.168.0.6 ^
 -Djboss.socket.binding.port-offset=2000

4. CLI를 이용한 서버 제어 (Management)

도메인 모드에서는 jboss-cli를 통해 특정 호스트의 특정 서버 인스턴스만 제어할 수 있습니다.

기본 구문

/host=[HOST_NAME]/server-config=[SERVER_NAME]:[start|stop|restart]

실행 예시

:: 접속 (Master Controller IP로 접속)
F:\app\Redhat\JBoss7.2\bin> jboss-cli.bat --connect controller=192.168.0.6:9990

:: 특정 서버(test01) 정지
[domain@192.168.0.6:9990 /] /host=slave-node01/server-config=test01:stop
{
    "outcome" => "success",
    "result" => "STOPPING"
}

:: 특정 서버(test01) 기동
[domain@192.168.0.6:9990 /] /host=slave-node01/server-config=test01:start
{
    "outcome" => "success",
    "result" => "STARTING"
}

Next Step:
구성이 완료되었다면 관리 콘솔(http://192.168.0.6:9990)에 접속하여 Runtime 탭에서 Topology가 정상적으로 인식되는지 확인하십시오.

Open Stream →
#EAP7

[JBoss EAP 7] MS SQL Server Non-XA 데이터소스 연결 오류: connection-url 무시 현상 해결

JBoss EAP 7에서 MS SQL Server용 Non-XA Datasource를 설정할 때, <datasource-class> 태그를 사용하면 <connection-url> 설정이 무시되어 "Connection refused" 에러가 발생하는 현상을 해결합니다. 올바른 설정 방법은 무엇인지 정리합니다.

1. 문제 현상 (Issue)

MS SQL Server와 연결하기 위해 데이터소스를 설정하고 테스트 연결을 시도했으나, 아래와 같은 Connection refused 에러가 발생하며 실패합니다.

에러 로그

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 
The TCP/IP connection to the host localhost, port 1433 has failed. 
Error: "Connection refused. Verify the connection properties. ... Make sure that TCP connections to the port are not blocked by a firewall."

방화벽이나 DB 서버 상태는 정상이지만, JBoss가 localhost:1433(기본값)으로만 접속을 시도하거나 URL 정보를 전혀 읽지 못하는 상황입니다.


2. 원인 분석 (Root Cause)

이 문제는 JBoss의 데이터소스 설정 우선순위 메커니즘 때문에 발생합니다.

  • Non-XA 환경의 특성: JDBC 4 표준을 따르는 드라이버를 사용할 때, 일반적으로 java.sql.Driver 메커니즘(URL 방식)을 사용합니다.
  • 충돌 발생: 설정 파일(standalone.xml)에 <datasource-class>가 명시되면, JBoss는 이를 우선시하여 javax.sql.DataSource 구현체를 사용하려고 시도합니다.
  • 결과: 이 과정에서 <connection-url> 속성은 무시(Ignored)됩니다. 결과적으로 JDBC URL 정보가 전달되지 않아 연결에 실패하게 됩니다.

3. 해결 방법 (Resolution)

Non-XA 데이터소스 설정에서는 datasource-class를 제거하고, driver-classconnection-url 조합을 사용해야 합니다.

설정 파일 수정 (standalone.xml)

<datasource-class> 태그가 있는 라인을 삭제하십시오.

<datasource jndi-name="java:/mssql" pool-name="mssqljdbc" statistics-enabled="true">
    
    <connection-url>jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ucpost</connection-url>
    <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
    
    <driver>sqlserver</driver>
    ...
</datasource>

4. 적용 및 검증 (Verification)

  1. 설정 파일 저장 후 JBoss를 재기동(Reload)합니다.
  2. 관리 콘솔 또는 CLI에서 데이터소스 연결 테스트(Test Connection)를 수행합니다.
  3. 정상적으로 Success 메시지가 뜨는지 확인합니다.
Tip: 만약 XA Datasource(분산 트랜잭션용)를 설정하는 경우에는 반대로 xa-datasource-class 설정이 필수적이며 URL 방식은 사용되지 않을 수 있습니다. 설정하려는 타입(XA vs Non-XA)을 명확히 구분해야 합니다.
Open Stream →
#EAP7

[JBoss EAP 7] 마이그레이션 이슈 해결: Apache CXF 라이브러리 충돌 (WFLYWS0059)

JBoss EAP 6.4에서 7.x로 마이그레이션 시, 애플리케이션에 포함된 Apache CXF 라이브러리와 JBoss 내부의 WebServices 서브시스템이 충돌하여 배포가 실패하는 현상(WFLYWS0059)을 해결합니다. jboss-deployment-structure.xml을 통한 모듈 제외(Exclude) 설정을 다룹니다.

1. 문제 현상 및 로그 분석 (Issue)

기존 EAP 6에서는 잘 돌던 애플리케이션이 EAP 7 배포 시 PARSE 단계에서 실패하며 아래와 같은 에러를 뱉어냅니다.

에러 로그

WFLYSRV0153: Failed to process phase PARSE of deployment "sso.war"
Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: 
WFLYWS0059: Apache CXF library (cxf-api-2.7.8.jar) detected in ws endpoint deployment; 
either provide a proper deployment replacing embedded libraries with container module dependencies 
or disable the webservices subsystem for the current deployment adding a proper jboss-deployment-structure.xml descriptor to it.

원인 분석

  • JBoss EAP 7: 기본적으로 최신 Apache CXF 기반의 WebServices 서브시스템을 로드합니다.
  • 애플리케이션: WEB-INF/lib 안에 자체적인 CXF 라이브러리(예: cxf-api-2.7.8.jar)를 포함하고 있습니다.
  • 충돌: JBoss는 애플리케이션이 자체 라이브러리 대신 컨테이너가 제공하는 모듈을 사용하길 권장하며, 중복이 감지되면 배포를 중단시킵니다.

2. 해결 방법 (Resolution)

애플리케이션이 JBoss의 WebServices 기능을 사용하지 않고, "내가 가진 라이브러리를 쓰겠다"고 선언해야 합니다. 이를 위해 jboss-deployment-structure.xml 파일을 생성하여 컨테이너의 특정 서브시스템을 비활성화합니다.

설정 파일 위치

  • WAR 파일: WEB-INF/jboss-deployment-structure.xml
  • EAR 파일: META-INF/jboss-deployment-structure.xml

작성 내용

JBoss의 webservicesjaxrs 서브시스템을 제외(exclude)하고, 필요한 기본 API만 의존성(dependencies)으로 추가합니다.

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        
        <!-- 1. JBoss 내부 서브시스템 비활성화 -->
        <exclude-subsystems>
            <!-- JBoss의 CXF 모듈이 로드되지 않도록 차단 -->
            <subsystem name="webservices" />
            <!-- RESTful 서비스 충돌 방지 (필요 시) -->
            <subsystem name="jaxrs" />
        </exclude-subsystems>
        
        <!-- 2. 필수 API 의존성 추가 -->
        <!-- 서브시스템은 뺐지만, 기본적인 Java EE API는 필요하므로 명시적 로드 -->
        <dependencies>
            <module name="javax.xml.ws.api"/>
            <module name="javax.jws.api"/>
        </dependencies>
        
    </deployment>
</jboss-deployment-structure>

3. 적용 결과 및 주의사항

  • 결과: 위 파일을 추가하고 재배포하면, JBoss는 해당 애플리케이션에 대해 WebServices 서브시스템 초기화를 건너뛰고 WEB-INF/lib 내의 jar 파일들을 우선적으로 로딩합니다.
  • 주의: 이 방식을 사용하면 JBoss 관리 콘솔에서 제공하는 웹 서비스 모니터링이나 관리 기능을 해당 애플리케이션에 대해서는 사용할 수 없게 됩니다. (애플리케이션 자체 프레임워크인 Spring-CXF 등이 전적으로 처리함)
Open Stream →
#EAP7

[JBoss EAP 7.2] Windows Service 등록 및 멀티 인스턴스 구성 가이드 (Jsvc, Port Offset)

JBoss EAP 7.2를 Windows 서비스로 등록하여 시스템 부팅 시 자동으로 실행되도록 설정합니다. Apache Jsvc를 활용한 서비스 등록 방법, 멀티 인스턴스 구성을 위한 포트 오프셋(Port Offset) 설정, 그리고 특수문자 패스워드 처리 팁을 정리합니다.

0. 사전 준비 (Prerequisites)

JBoss EAP 7부터는 서비스 등록을 위해 JBoss Core Services (JBCS) - Apache Jsvc 패키지가 필요할 수 있습니다. Red Hat 포털에서 해당 바이너리를 다운로드하여 준비합니다.

필수 다운로드 및 환경 변수

  • JBoss EAP 7.2: 설치 파일 압축 해제
  • Apache Jsvc: Windows용 바이너리 (sbin 폴더 내 prunsrv.exe 등 포함)
  • 환경 변수:
    • JAVA_HOME: JDK 설치 경로
    • NOPAUSE=1: 서비스 종료 시 배치 파일이 멈추지 않도록 필수 설정

1. 단일 인스턴스 서비스 등록

기본적으로 제공되는 service.bat 스크립트를 사용하여 서비스를 등록합니다.

등록 명령어

아래 명령어를 관리자 권한 CMD 창에서 실행합니다. 로그 경로(/logpath)는 미리 생성되어 있어야 합니다.

:: JBoss bin 디렉토리로 이동
cd %JBOSS_HOME%\bin

:: 서비스 등록 실행
service.bat install /name "JBoss7-Server01" ^
 /controller "localhost:9990" ^
 /config "standalone-ha.xml" ^
 /jbossuser "admin" ^
 /jbosspass "admin1@34" ^
 /logpath "E:\app\Redhat\waslog\testsvr01"
주의 (Password Special Characters):
비밀번호에 &, <, >, | 등의 특수문자가 포함된 경우 CMD 창에서 오류가 발생할 수 있습니다. 가능한 경우 특수문자를 피하거나, 쌍따옴표(")로 감싸고 이스케이프 문자(^)를 사용하는 등 주의가 필요합니다.

2. 멀티 인스턴스(Multi-Instance) 구성

하나의 장비에 여러 개의 JBoss 서비스를 등록할 때는 서비스 이름, 포트, 로그 경로가 겹치지 않아야 합니다.

Step 1: Port Offset 설정 (XML 수정)

service.bat의 파라미터로 포트 오프셋을 넘기는 것이 불안정할 수 있으므로, 설정 파일(standalone.xml) 자체를 수정하는 것을 권장합니다.

<socket-binding-group name="standard-sockets" default-interface="public" 
    port-offset="${jboss.socket.binding.port-offset:100}">
    </socket-binding-group>

Step 2: Jsvc 경로 수정 (필요 시)

멀티 인스턴스 환경에서 각 서버별로 다른 Jsvc 바이너리를 사용해야 하거나 경로가 특수한 경우, service.bat 파일을 열어 PRUNSRV 경로를 수동으로 지정해야 할 수 있습니다.

rem service.bat 파일 편집
set PRUNSRV=

rem 커스텀 경로 우선 확인 로직 예시
if exist "%JBOSS_HOME%\..\test01-jbcs-jsvc-1.1\sbin\prunsrv.exe" (
  set PRUNSRV="%JBOSS_HOME%\..\test01-jbcs-jsvc-1.1\sbin\prunsrv.exe"
) else if exist "%JBOSS_HOME%\bin\prunsrv.exe" (
  set PRUNSRV="%JBOSS_HOME%\bin\prunsrv.exe"
) else (
  echo Please install native utilities into expected location...
  goto cmdEnd
)

Step 3: 추가 인스턴스 등록

서비스 이름과 컨트롤러 포트, 로그 경로를 변경하여 등록합니다.

service.bat install /name "JBoss7-Server02" ^
 /controller "localhost:10090" ^
 /config "standalone-ha.xml" ^
 /jbossuser "admin" ^
 /jbosspass "admin1@34" ^
 /logpath "E:\app\Redhat\waslog\testsvr02"

3. 서비스 제어 및 삭제

등록된 서비스는 Windows 서비스 관리자(services.msc) 또는 명령어로 제어할 수 있습니다.

서비스 삭제 (Uninstall)

설정을 변경하거나 재등록할 경우 기존 서비스를 삭제해야 합니다. /name 옵션에 등록했던 서비스명을 정확히 입력합니다.

:: 서비스 중지
sc stop JBoss7-Server01

:: 서비스 삭제 (service.bat 이용)
service.bat uninstall /name "JBoss7-Server01"

4. 검증 (Verification)

  1. 서비스 실행: net start JBoss7-Server01
  2. 포트 확인: netstat -an | findstr "9990" (또는 오프셋이 적용된 포트)
  3. 로그 확인: 지정한 /logpath 경로에 로그 파일이 생성되는지 확인합니다.
Open Stream →
#JBoss

[JBoss EAP 7] JSESSIONID 형식 변경 이슈 분석 및 jvmRoute(Instance-ID) 설정 가이드

JBoss EAP 7 도입 시 JSESSIONID 뒤에 예상치 못한 문자열(서버명, 그룹명 등)이 붙거나 길이가 늘어나는 현상을 분석합니다. 이는 Undertow 엔진의 JvmRouteHandler 동작 방식 때문이며, 이를 제어하기 위한 설정 방법을 정리합니다.

1. 이슈 현상 및 원인 분석 (Context)

EAP 6와 EAP 7은 세션 ID를 생성하고 관리하는 방식에 차이가 있습니다. 특히 클러스터링 환경에서 Sticky Session(세션 고정)을 위해 ID 뒤에 붙이는 식별자(Route)의 기본 동작이 다릅니다.

버전별 차이점

구분 JBoss EAP 6.x (JBossWeb) JBoss EAP 7.x (Undertow)
Engine Apache Tomcat Fork Undertow
Format [ID].[jvmRoute] [ID].[instance-id]
Default 설정 없으면 ID만 생성하거나 Hostname 부착 강제로 jboss.node.name 부착

기술적 원인 (Undertow JvmRouteHandler)

EAP 7의 웹 엔진인 Undertow는 io.undertow.server.JvmRouteHandler를 통해 세션 ID를 처리합니다. 이 핸들러는 클러스터링 설정 여부와 관계없이, 설정된 instance-id가 있다면 무조건 세션 ID 뒤에 붙이도록 설계되어 있습니다. 또한, 이 값은 null이거나 비활성화(disable)할 수 없으며 기본적으로 노드 이름을 따라갑니다.


2. 해결 및 설정 방법 (Configuration)

JSESSIONID 뒤에 붙는 값을 제어하기 위해서는 undertow 서브시스템의 instance-id 속성을 명시적으로 변경해야 합니다.

설정 파일 수정 (standalone.xml / domain.xml)

Undertow 서브시스템 설정에서 instance-id 값을 원하는 식별자(기존 jvmRoute와 동일한 역할)로 지정합니다.

<subsystem xmlns="urn:jboss:domain:undertow:3.1">
    <buffer-cache name="default"/>
    <server name="default-server">
        <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
        <ajp-listener name="ajp" socket-binding="ajp"/>
        <host name="default-host" alias="localhost">
            ...
        </host>
    </server>
    
    <servlet-container name="default" default-session-timeout="30" instance-id="${jboss.node.name}" />
    
</subsystem>

CLI를 이용한 변경

운영 중인 환경에서 값을 변경하려면 관리 CLI를 사용하는 것이 안전합니다.

# instance-id를 'node1'으로 변경하는 예시
/subsystem=undertow/servlet-container=default:write-attribute(name=instance-id, value="node1")

# 설정 적용을 위해 리로드 필요
reload
주의 (Warning):
instance-id를 변경하면 기존에 발급된 세션 ID와 매핑이 달라지므로, 기존 사용자들의 세션이 끊길(Invalidate) 수 있습니다. 서비스 점검 시간에 수행하십시오.

3. 검증 (Verification)

설정 변경 후 애플리케이션을 호출하여 쿠키 값을 확인합니다.

개발자 도구 확인

  1. 브라우저 F12 (개발자 도구) 실행
  2. Application 탭 > Cookies 선택
  3. JSESSIONID의 값 확인

결과 예시:

  • 변경 전: abcde12345.server-one-instance-group-a (길고 복잡함)
  • 변경 후: abcde12345.node1 (설정한 값으로 깔끔해짐)

Next Step:
만약 앞단의 웹 서버(Apache/IHS)와 mod_cluster 또는 mod_jk로 연동 중이라면, 웹 서버 설정의 jvmRoute 값과 JBoss의 instance-id 값이 정확히 일치해야 스티키 세션(Sticky Session)이 정상 작동합니다. 반드시 양쪽 설정을 맞춰주세요.

Open Stream →
#VSCode

[VS Code] 한글 깨짐 현상 완벽 해결: Auto Guess Encoding 및 인코딩 수동 변경

Windows 환경(CP949/EUC-KR)에서 작성된 텍스트 파일을 VS Code(기본 UTF-8)로 열었을 때 발생하는 한글 깨짐 현상을 해결합니다. 파일의 인코딩을 자동으로 인식하게 하는 설정과 수동으로 인코딩을 지정하여 다시 여는 방법을 정리합니다.

0. 배경 및 원인 (Context)

한글 윈도우 메모장에서 저장한 파일은 기본적으로 EUC-KR(또는 CP949) 인코딩을 사용합니다. 반면, VS Code는 전 세계 표준인 UTF-8을 기본값으로 파일을 엽니다. 이 해석 방식의 차이로 인해 한글이 ''와 같이 깨져 보이게 됩니다.

Test Environment

  • OS: Windows 10
  • Software: Visual Studio Code

1. Method A: 자동 인코딩 인식 설정 (Auto Guess Encoding)

VS Code가 파일 내용을 분석하여 인코딩을 자동으로 추측하도록 설정합니다. 매번 인코딩을 바꿀 필요가 없어 편리합니다.

설정 방법

  1. 설정 진입: 메뉴에서 File > Preferences > Settings (단축키: Ctrl + ,)
  2. 검색: 검색창에 encoding 입력
  3. 옵션 체크: Files: Auto Guess Encoding 항목을 찾아 체크박스 선택(Check)
Note: 이 기능은 확률에 기반하여 추측하므로, 간혹 영문만 있는 파일이나 특수한 경우 인코딩을 잘못 인식할 수도 있습니다.

2. Method B: 수동으로 다시 열기 (Reopen with Encoding)

자동 설정이 불안하거나 특정 파일만 깨질 때 사용하는 가장 확실한 방법입니다.

작업 절차

  1. VS Code 우측 하단 상태 표시줄(Status Bar)에 있는 UTF-8 클릭.
  2. 상단 명령 팔레트에서 "Reopen with Encoding (인코딩하여 다시 열기)" 선택.
  3. 목록에서 "Korean (EUC-KR)" 검색 후 선택.

이렇게 하면 파일 내용은 깨지지 않고 정상적으로 보입니다.


3. Method C: UTF-8로 영구 변환 (Save with Encoding)

EUC-KR 파일을 향후 호환성을 위해 UTF-8로 변경하여 저장하고 싶을 때 사용합니다.

작업 절차

  1. 위의 Method B를 통해 한글이 정상적으로 보이는 상태로 만듭니다.
  2. 우측 하단 상태 표시줄의 인코딩(EUC-KR) 클릭.
  3. 상단 명령 팔레트에서 "Save with Encoding (인코딩하여 저장)" 선택.
  4. "UTF-8" 선택.

이제 이 파일은 영구적으로 UTF-8로 저장되어, 어떤 환경에서도 한글이 깨지지 않습니다.


4. 결과 확인 (Verification)

설정을 적용하거나 인코딩을 변경한 후, 깨져 보이던 한글 주석이나 텍스트가 정상적으로 표시되는지 확인합니다.

Before (깨짐)

Broken Korean Text

After (정상)

Fixed Korean Text

Next Step:
팀 프로젝트를 진행한다면 팀원 간의 인코딩 통일을 위해 .editorconfig 파일을 프로젝트 루트에 생성하여 charset = utf-8을 강제하는 방법을 권장합니다.

Open Stream →