Jenkins를 사용하면서 Execute Shell 단계에서 다음과 같은 오류를 만난 적이 있습니다.

FATAL: Unable to produce a script file
java.nio.charset.UnmappableCharacterException: Input length = 1
	at java.base/java.nio.charset.CoderResult.throwException(CoderResult.java:275)
	at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:307)
	...
Caused: java.io.IOException: Failed to create a temp file on /var/lib/jenkins/workspace/...

 

이 오류는 임시 파일을 생성하는 과정에서 발생했으며, 이를 해결하기 위해 몇 가지 단계를 거쳤습니다. 이 글에서는 문제 원인과 해결 방법을 공유합니다.

 

 

1. 문제 원인

이 오류는 Jenkins가 쉘 스크립트를 실행하기 위해 임시 파일을 생성하려고 할 때 발생합니다. 주요 원인은 다음과 같습니다:

  • Jenkins가 사용하는 임시 디렉터리의 권한 문제.
  • Java 인코딩 설정이 맞지 않음.
  • Jenkins 작업 디렉터리에서 임시 파일 생성 실패.

 

2. 해결 방법: 임시 디렉터리 변경

Jenkins가 사용하는 임시 디렉터리를 명시적으로 설정하고, 적절한 권한을 부여하면 문제를 해결할 수 있습니다.

1단계: Jenkins 설정 파일 수정

Amazon Linux 2에서는 Jenkins 설정 파일이 /usr/lib/systemd/system/jenkins.service에 위치합니다.

 

설정 파일을 열어 수정합니다

sudo vi /usr/lib/systemd/system/jenkins.service

 

Environment 블록에 다음 내용을 추가합니다

Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.io.tmpdir=/tmp/jenkins_tmp -Dfile.encoding=UTF-8"

 

 

2단계: 임시 디렉터리 생성 및 권한 설정

새로 정의한 임시 디렉터리(/tmp/jenkins_tmp)를 생성하고, Jenkins 사용자에게 적절한 권한을 부여합니다.

 

디렉터리 생성 및 Jenkins 권한 부여

sudo mkdir -p /tmp/jenkins_tmp
sudo chown -R jenkins:jenkins /tmp/jenkins_tmp

 

 

3단계: Jenkins 재시작

Jenkins 설정을 적용하려면 서비스를 재시작해야 합니다.

 

sudo systemctl daemon-reload
sudo systemctl restart jenkins
sudo systemctl status jenkins

 

 Jenkins를 사용하는 도중 Jenkins 재시작 후 플러그인이 정상 동작하지 않고, 플러그인 버전이 낮아 재설치조차 불가능한 문제를 겪었습니다.

 해당 문제로 Jenkins 버전 업그레이드하면서 겪었던 JSONObject["scm"] is not a JSONObject 오류와 이를 해결한 과정을 공유합니다. 이 문제를 해결하기 위해 진행했던 과정과 원인을 분석한 결과를 바탕으로, 비슷한 문제를 겪는 분들께 도움이 되었으면 합니다.

 

 

문제 상황

Jenkins 재시작 후 플러그인 비정상 작동
Jenkins를 재시작한 뒤 일부 플러그인(Git 관련 등)이 제대로 작동하지 않았습니다. 특히 Git 플러그인을 사용하는 빌드 작업에서 아래와 같은 오류가 발생했습니다

 
JSONObject["scm"] is not a JSONObject

 

 

플러그인 버전 낮음 및 재설치 불가
플러그인 관리 메뉴에서 확인해보니 몇몇 플러그인의 버전이 매우 낮은 상태였고, 이를 재설치하려고 해도 플러그인 업데이트 서버와의 통신 오류로 인해 재설치가 진행되지 않았습니다.

java.io.FileNotFoundException: https://updates.jenkins.io/download/plugins/plain-credentials/...

 

 

Jenkins 업그레이드 후 오류 지속
Jenkins 자체의 문제가 의심되어 최신 LTS 버전(2.4 LTS)으로 업그레이드했지만, 문제가 지속되었습니다.

 

 

문제 해결 과정

업그레이드 후 플러그인 재설치

Jenkins를 업그레이드한 뒤에도 문제가 발생했으므로, 우선 플러그인 재설치를 시도했습니다.
하지만 플러그인 관리 메뉴에서 일부 플러그인은 여전히 재설치가 불가능하거나, 최신 버전임에도 불구하고 오류가 계속되었습니다.

 

Git(SCM) 플러그인 오류 분석

Git 플러그인을 사용하는 빌드에서 JSONObject["scm"] is not a JSONObject 오류가 지속적으로 발생했습니다.
이를 통해 Git 플러그인(SCM 관련) 또는 빌드 환경의 JSON 데이터 파싱에 문제가 있다고 판단했습니다.

  • 로그 분석 결과, Git 플러그인이 JSON 데이터를 읽는 과정에서 문제가 발생하고 있음을 확인했습니다.
  • 하지만 Git 플러그인을 재설치해도 문제가 해결되지 않았습니다.

Build Timeout 플러그인 문제 발견

문제를 해결하기 위해 구글링 중, Build Timeout 플러그인의 오류임을 발견했습니다:

  • 플러그인 관리 메뉴에서 해당 플러그인이 활성화되지 않은 상태로 표시.

Build Timeout 플러그인 재설치 후 문제 해결

  • Build Timeout 플러그인을 삭제한 뒤 다시 설치하고 Jenkins를 재시작했습니다.
  • 이후, Git 플러그인의 JSONObject["scm"] 오류가 사라지고 빌드가 정상적으로 진행되었습니다.

 

원인 분석

문제가 해결된 후, 왜 Build Timeout 플러그인 재설치가 Git(SCM) 관련 오류를 해결했는지 분석해본 결과, 다음과 같은 원인을 추정할 수 있었습니다:

  1. 플러그인 간 의존성
    Jenkins 플러그인들은 서로 의존 관계를 가질 수 있습니다.
    Build Timeout 플러그인은 빌드 작업 시간 제한을 설정하며, Git 플러그인이나 SCM 관련 기능과 연계될 가능성이 있습니다.
    • Build Timeout 플러그인의 손상 또는 삭제로 인해 Git 플러그인이 사용하는 API나 공통 라이브러리가 비정상적으로 작동한 것으로 보입니다.
  2. 플러그인 버전 불일치
    Jenkins 업그레이드 후, 기존 플러그인 버전이 Jenkins의 새로운 API와 호환되지 않아 문제가 발생했을 가능성이 큽니다.
    • Build Timeout 플러그인을 재설치하면서 최신 버전으로 교체되었고, Git 플러그인과의 의존성 문제도 해결되었습니다.

 

이번 경험을 통해 Jenkins 관리의 복잡성을 다시 한번 실감했습니다. 비슷한 문제를 겪는 분들에게 이 글이 작은 도움이 되길 바랍니다! 

해결 방법

아래의 단계별 가이드를 따라 문제를 해결해 보세요.

1. Docker Desktop 실행 확인

먼저 Docker Desktop이 실행 중인지 확인하세요.

  1. Windows 작업 표시줄에서 Docker Desktop 아이콘을 찾습니다.
  2. 실행되지 않았다면 Docker Desktop을 시작하세요.
  3. 실행 중에도 에러가 발생한다면 Docker Desktop을 재시작합니다.

 

에러 원인 분석

Docker가 Windows에서 작동할 때 네임드 파이프(named pipe)를 통해 Docker Daemon과 통신합니다. 그러나 다음과 같은 상황에서 이 통신이 실패할 수 있습니다:

  • Docker Desktop이 비정상적으로 종료되었거나 실행되지 않음.
  • Docker 서비스(com.docker.service)가 중지됨.
  • Docker CLI가 Docker Desktop 데몬과 연결되지 않음.
  • Windows Subsystem for Linux 2(WSL2)와의 통합이 문제를 일으킴.
  • Docker Desktop 설치 파일이 손상됨.

 

AWS EC2를 사용하다 보면 인스턴스의 성능을 최적화하거나 비용을 절감하기 위해 인스턴스 유형을 변경하는 경우가 많습니다. 하지만 이번 경험은 제게 인스턴스 유형 변경 시 CPU 아키텍처가 바뀌면 심각한 문제가 발생할 수 있다는 점을 상기시켜 주었습니다.

 

문제 상황

기존 EC2 인스턴스는 r5a.large (AMD 기반) 유형을 사용 중이었습니다. 성능 개선을 위해 r6i.large (Intel 기반)으로 인스턴스 유형을 변경했습니다. 변경 후 인스턴스를 시작했는데, 예상치 못한 문제가 발생했습니다:

  1. SSH 접근 불가: 인스턴스가 시작되었지만 SSH로 접속이 불가능했습니다.
  2. Emergency Mode: AWS 직렬 콘솔(Serial Console)을 통해 접속했을 때 인스턴스가 Emergency Mode로 부팅되어 있었습니다.

 

시도했던 해결 방법

문제를 해결하기 위해 여러 방법을 시도했지만, 결국 실패하고 다른 접근 방식을 선택해야 했습니다.

 

1. Emergency Mode에서 로그 확인

journalctl -xb

부팅 중 네트워크 관련 오류 및 sshd 서비스가 제대로 실행되지 않았다는 점을 확인했습니다.

2. 네트워크 및 SSH 복구

Emergency Mode에서 네트워크와 SSH를 복구하려고 시도했습니다:

ip link set eth0 up dhclient eth0 systemctl start sshd

그러나 네트워크와 SSH 서비스가 완전히 복구되지 않았습니다.

3. 파일 시스템 및 부트로더 복구

다음 명령을 통해 부트로더와 커널을 복구하려고 했습니다:

sudo yum reinstall kernel sudo dracut -f sudo grub2-mkconfig -o /boot/grub2/grub.cfg sudo grub2-install /dev/xvda

하지만 여전히 문제가 해결되지 않았습니다.

4. 볼륨 교체 및 AMI 복구

  • 볼륨 교체: 문제의 루트 볼륨을 스냅샷으로 생성한 뒤, 새로 생성한 인스턴스에 연결하여 복구를 시도.
  • AMI 복구: 기존 볼륨을 기반으로 새로운 AMI를 생성하고 다른 인스턴스에서 실행.

여러 시도에도 불구하고 인스턴스는 Emergency Mode를 벗어나지 못했습니다.

 

 

최종 해결 방법

모든 복구 시도가 실패하자, 다음과 같은 방법으로 문제를 해결했습니다:

  1. 애플리케이션 복제:
    • 문제의 애플리케이션과 데이터를 로컬 환경으로 복제.
  2. 새로운 서버 생성:
    • 새로운 EC2 인스턴스를 생성하고 애플리케이션을 재배포.
  3. 서비스 재개:
    • 새 인스턴스에서 정상적으로 서비스를 운영할 수 있었습니다.

 

 

결론 및 교훈

이번 경험을 통해 두 가지 중요한 교훈을 얻었습니다:

1. CPU 아키텍처 변경 시 주의

  • AMD에서 Intel로, 또는 ARM(r6g)에서 Intel(r6i)로 CPU 아키텍처가 변경될 때 부트가 실패할 가능성이 높습니다.
  • 특히 부트로더나 커널이 해당 아키텍처를 지원하지 않는 경우 문제가 발생합니다.

2. AMI를 미리 생성하자

  • 인스턴스 유형 변경 전에는 반드시 AMI를 생성해두는 것이 중요합니다.
  • AMI는 문제 발생 시 빠르게 복구할 수 있는 가장 안전한 방법입니다.

문제 상황

프로젝트 환경을 새로 설정하면서 원본 서버의 설정을 대상 서버로 복제하는 작업을 진행했습니다. 원본 서버는 Tomcat에서 SSL(443번 포트)이 활성화된 상태로 잘 동작하고 있었기 때문에, 대상 서버에서도 동일한 설정을 적용했습니다.

그러나, Tomcat을 시작하자마자 아래와 같은 에러가 발생했습니다

 

Caused by: java.net.BindException: 주소가 이미 사용 중입니다
	at sun.nio.ch.Net.bind0(Native Method)
	...

Tomcat의 로그를 확인해보니, 443번 포트가 이미 사용 중이라는 내용이었습니다. 하지만 원본 서버에서는 이런 문제가 없었기 때문에 한참 헤매게 되었습니다.

 

원인 분석

대상 서버를 자세히 살펴보니, **Apache HTTP 서버(httpd)**가 설치되어 있었습니다.

Apache HTTP 서버는 기본적으로 80번(HTTP) 포트443번(HTTPS) 포트를 사용합니다. Tomcat에서도 443번 포트를 사용하려고 설정되어 있었기 때문에, 두 서버가 충돌하면서 포트 사용 문제가 발생한 것입니다.

결국 대상 서버에 HTTP 서버가 설치된 사실을 몰랐던 것이 문제의 원인이었습니다.

 

해결 방법

이 문제를 해결하기 위해 아래 단계를 거쳤습니다:

 

포트 충돌 확인 먼저, 대상 서버에서 443번 포트를 누가 사용 중인지 확인했습니다

sudo lsof -i :443


COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
httpd    1234    root   10u  IPv4  12345      0t0  TCP *:https (LISTEN)

 

 

  • 해결 방안 선택
    • Apache HTTP 서버를 비활성화 또는 제거
    • Tomcat의 SSL 포트를 변경
    • Apache를 리버스 프록시로 설정하여 Tomcat과 연동
    이번 환경에서는 HTTP 서버가 불필요했기 때문에, Apache HTTP 서버를 중지하는 방법을 선택했습니다.

 

  • Apache HTTP 서버 중지
sudo systemctl stop httpd
sudo systemctl disable httpd

 

교훈

  1. 환경 확인의 중요성
    새로운 서버 환경에서는 **기본적으로 설치된 서비스(예: HTTP 서버)**가 무엇인지 먼저 확인해야 합니다. 특히, 잘 알려진 포트(80, 443 등)는 다른 서비스가 사용하고 있을 가능성이 높습니다.
  2. 효율적인 디버깅 방법
    포트 충돌 문제를 해결하기 위해서는 다음 명령어를 적극적으로 활용하면 좋습니다:
    • netstat -tuln → 현재 사용 중인 포트 확인
    • lsof -i :<port> → 특정 포트를 점유하고 있는 프로세스 확인
  3. 원본 서버와 대상 서버의 차이점 확인
    원본 서버에서 아무 문제가 없더라도, 대상 서버의 환경이 다를 수 있다는 점을 항상 염두에 두어야 합니다.

 

마무리

이번 경험은 단순한 포트 충돌이었지만, 대상 서버 환경을 제대로 이해하지 않고 설정을 복제하려다 발생한 문제였습니다. 다음부터는 환경 구성 전에 기본 설정을 꼼꼼히 확인하고, 효율적인 디버깅 과정을 통해 시간을 절약할 수 있을 것 같습니다.

비슷한 문제를 겪고 계신 분들께 도움이 되었길 바랍니다! 

 

문제 상황

최근 Tomcat에서 애플리케이션을 실행하던 중, JVM 메모리 관련 에러로 인해 애플리케이션이 정상적으로 작동하지 않는 문제가 발생했습니다. 아래는 당시 발생한 에러 메시지입니다:

 

NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000600000000, 8589934592, 0) failed; error='Not enough space' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 8589934592 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /home/project/apache-tomcat-9.0.54/bin/hs_err_pid23479.log

 

이 메시지는 다음을 나타냅니다:

  • JVM이 8GB의 메모리를 요청했으나, 서버의 물리 메모리가 부족하여 요청이 거부되었습니다.
  • 물리 메모리(7.7GB)가 JVM이 요청한 메모리보다 적었기 때문에, 시스템이 더 이상 JVM의 메모리 요청을 처리할 수 없었습니다.

원인 분석

  1. JVM 메모리 설정 과도
    Tomcat에서 JVM 힙 메모리 설정이 기본값보다 과도하게 높게 설정되어 있었습니다:
    • 초기 힙 메모리(-Xms): 8GB
    • 최대 힙 메모리(-Xmx): 8GB
  2. 물리 메모리 초과
    서버의 물리 메모리(7.7GB)가 JVM 메모리 설정을 감당할 수 없었기 때문에, JVM이 실행 중 메모리 할당 에러를 발생시켰습니다.

문제의 기존 설정

Tomcat의 setenv.sh 파일에 다음과 같은 JVM 메모리 설정이 있었습니다

export CATALINA_OPTS="$CATALINA_OPTS -Xms8192m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewSize=4096m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxNewSize=4096m"

 

해결 과정

이 문제를 해결하기 위해 다음과 같이 setenv.sh 파일을 수정했습니다:

수정된 setenv.sh 내용

# 기존 JVM 옵션 (주석 처리)
# export CATALINA_OPTS="$CATALINA_OPTS -Xms8192m"
# export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
# export CATALINA_OPTS="$CATALINA_OPTS -XX:NewSize=4096m"
# export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxNewSize=4096m"

# 새로운 JVM 메모리 옵션 설정
export CATALINA_OPTS="$CATALINA_OPTS -Xms2g"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx4g"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewSize=512m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxNewSize=1024m"

변경된 설정

  • 초기 힙 메모리(-Xms): 8GB → 2GB
  • 최대 힙 메모리(-Xmx): 8GB → 4GB
  • Young Generation 초기 크기(-XX:NewSize): 4GB → 512MB
  • Young Generation 최대 크기(-XX:MaxNewSize): 4GB → 1GB

 

설정 확인

JVM 메모리 옵션 확인

Tomcat이 올바르게 설정되었는지 확인하려면 아래 명령어를 사용하세요

jps -lvm

#결과 예시
15190 org.apache.catalina.startup.Bootstrap start -Xms2g -Xmx4g -XX:NewSize=512m -XX:MaxNewSize=1024m

 

결론

이 과정을 통해 Tomcat의 JVM 메모리 설정을 서버 환경에 맞게 조정하였습니다. 변경 후에는 더 이상 메모리 부족 에러가 발생하지 않았으며, 서버 리소스 사용이 안정화되었습니다.

교훈

JVM 메모리 옵션(-Xms, -Xmx)을 설정할 때는:

  1. 서버의 물리 메모리와 애플리케이션의 요구사항을 고려합니다.
  2. 필요 이상으로 높은 설정은 시스템에 오히려 부하를 줄 수 있습니다.
  3. GC 로깅 등을 통해 애플리케이션의 실제 메모리 사용량을 파악하고 설정을 최적화하세요.

'Trouble Shooting > java' 카테고리의 다른 글

Tomcat SSL 설정 중 포트 충돌 문제  (0) 2024.11.26

최근 Windows 환경에서 Docker를 사용해 Python Django 앱을 실행하던 중 예상치 못한 문제가 발생했습니다. uWSGI를 통해 서버를 구동하려고 했으나, 아래와 같은 에러 메시지가 출력되었습니다

 

Thu Nov 21 16:29:43 2024 - *** uWSGI listen queue of socket ":8000" (fd: 3) full !!! (18956672/16384) ***

 

문제 상황

Docker Desktop for Windows와 WSL 2를 사용 중이었으며, Ubuntu 배포판을 기본 WSL 환경으로 설정해 Docker 컨테이너를 실행하고 있었습니다. 하지만 위와 같은 에러가 지속적으로 발생하며 컨테이너가 제대로 작동하지 않았습니다.

해결 과정

이 문제를 해결하기 위해 다음 단계를 진행했습니다:

    • WSL 설정 확인 및 수정
      • Docker Desktop 설정에서 Resources > WSL integration 항목을 확인했습니다.
      • 아래 이미지처럼 기본 WSL 통합 설정이 활성화되어 있었고, 추가로 Ubuntu 배포판도 통합 대상으로 설정되어 있었습니다.

 

 

 

  • Refetch Distros
    • "Refetch distros" 버튼을 클릭해 WSL 통합 배포판 정보를 다시 가져왔습니다.
  • Docker 재시작
    • Docker Desktop을 완전히 종료하고 다시 실행했습니다.
  • 컨테이너 및 이미지 초기화
    • 문제를 일으킨 컨테이너를 중지하고 삭제했습니다.
    • 관련 이미지를 모두 삭제한 후, docker-compose를 사용해 빌드를 새로 진행했습니다.

 

결과

위 단계를 수행한 후 uWSGI 관련 에러가 더 이상 발생하지 않았습니다. Django 앱은 정상적으로 실행되었고, Docker와 WSL 통합 설정의 문제가 원인이었음을 확인할 수 있었습니다.

결론

Windows에서 Docker와 WSL 2를 활용할 때, 설정 불일치로 인해 다양한 문제가 발생할 수 있습니다. 특히 WSL 통합 설정과 관련하여 문제가 발생했을 경우:

  • WSL 통합 설정을 확인 및 업데이트
  • Docker Desktop 재시작
  • 컨테이너와 이미지를 초기화하고 새로 빌드

 

 

 

드물게 사용자에게 SSMS 설치 문제가 발생하는 경우가 있으며 일반적인 오류 메시지는 다음과 같습니다.

  • "MSI 패키지를 설치할 수 없음"
  • “Microsoft ODBC Driver 17 for SQL Server: 이전 설치에서는 변경 사항을 적용하려면 컴퓨터를 재부팅해야 했습니다.”
  • “설치 중에 오류 발생(0x80070643)”

제안된 해결 방법

앞서 언급한 또는 유사한 오류 메시지 중 하나가 표시되면서 설치가 실패하는 경우 일반적으로 SSMS 설치를 시작하기 전에 다음 단계에 따라 "Microsoft ODBC driver 17 for SQL Server"를 제거하면 설치가 성공할 수 있습니다.

  1. SSMS, Visual Studio 또는 SQL Server Profiler를 비롯한 모든 관련 애플리케이션을 닫습니다.
  2. 제어판 > 프로그램 추가/제거로 이동합니다.
  3. "Microsoft ODBC Driver 17 for SQL Server" 항목을 찾아 제거합니다. 이 단계에서 다시 시작해야 할 수 있습니다.
  4. SSMS 설치를 시작합니다. 최신 버전은 여기에서 사용할 수 있습니다.

 

방법이 안된다면

레지스트리 에디터 (실행 win + R -> regedit)

 

컴퓨터\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

위 경로로 이동

PendingFileRenameOperations 삭제 후 재설치

+ Recent posts