공지사항 크롤러가 로컬에서는 잘 동작하는데 EC2 서버에 올라가면 문제였다. 그래서 크롤링이 되지 않는 오류가 발생했다.
아래는 docker logs [컨테이너아이디]
명령어를 통해 찍은 로그이다.
주요 내용은 아래 내용이다.
Caused by: org.openqa.selenium.SessionNotCreatedException: Could not start a new session. Response code 500. Message: unknown error: Chrome failed to start: exited abnormally.
셀레니움을 통해 크롤링을 해야하는데 크롬이 제대로 실행이 되지 않는 오류가 발생한 것이다.
[CrawlerController.java 일부]
public void getAllNotice() throws InterruptedException, ParseException {
log.info("==== "+ "새 공지사항 크롤링 시작 시각: "+ String.valueOf(LocalDateTime.now())+"====");
// Headless 모드로 Chrome 실행
ChromeOptions options = new ChromeOptions();
// Headless 모드 활성화
options.addArguments("--headless");
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--ignore-ssl-errors=yes");
options.addArguments("--ignore-certificate-errors");
// WebDriver 인스턴스 생성
WebDriver driver = new ChromeDriver(options);
[Dockerfile]
# 기본 이미지를 설정합니다.
FROM ubuntu:22.04
# Java 설치
RUN apt-get update && \
apt-get install -y openjdk-17-jdk wget gnupg unzip && \
rm -rf /var/lib/apt/lists/*
# 작업 디렉토리를 설정합니다.
WORKDIR /app
# 스프링 어플리케이션 JAR 파일을 컨테이너로 복사합니다.
COPY ./build/libs/notify-crawler-0.0.1-SNAPSHOT.jar /app/notify-crawler.jar
# 크롬 브라우저와 크롬 드라이버를 설치합니다.
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
apt-get update && \
apt-get install -y google-chrome-stable && \
wget -q "https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_linux64.zip" -O /tmp/chromedriver.zip && \
unzip /tmp/chromedriver.zip -d /usr/local/bin/ && \
rm /tmp/chromedriver.zip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 환경 변수를 설정합니다.
ENV CHROME_DRIVER_PATH=/usr/local/bin/chromedriver
ENV SPRING_PROFILES_ACTIVE=prod
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH=$PATH:$JAVA_HOME/bin
# 포트를 엽니다.
EXPOSE 8082
# 어플리케이션을 실행합니다.
ENTRYPOINT ["java", "-Dwebdriver.chrome.driver=/usr/local/bin/chromedriver", "-jar", "/app/notify-crawler.jar"]
도커 파일을 보면 크롬 브라우저와 크롬 드라이버를 설치하는 코드가 포함되어있다.
[build.gradle]
dependencies {
//Jsoup
implementation 'org.jsoup:jsoup:1.14.3'
// Selenium WebDriver
implementation 'org.seleniumhq.selenium:selenium-java:4.1.0'
// Chrome WebDriver
implementation 'io.github.bonigarcia:webdrivermanager:5.0.3'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.kafka:spring-kafka'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.projectlombok:lombok:1.18.28'
testImplementation 'org.springframework.boot:spring-boot-starter-security'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0'
}
build.gradle에도 셀레니움과 크롬 웹 드라이버를 포함하고 있다.
로컬에서는 크롬과 크롬드라이버를 시스템에 직접 다운 받고 실행했지만 서버에서는 도커로 띄우기 때문에 도커 컨테이너 안에 크롬과 크롬 드라이버를 설치하고 이것과 셀레니움이 서로 버젼이 맞아서 잘 돌아가야한다.
따라서 크롬 드라이버 버젼, 크롬 버젼, 셀레니움 버젼의 호환 문제가 발생했거나 호환성이 문제 없어도 스프링 코드가 도커 컨테이너 내에서 설치경로를 인식하지 못해 크롬을 불러내지 못하는 것이 문제일 것이다.
원래는 3개를 다 맞추려고 했는데 셀레니움 공식 문서 를 보니 셀레니움 4.6버젼부터 selenium manager
를 포함하고 있어서 자동으로 브라우저 버젼을 인식하여 브라우져와 호환되는 드라이버를 다운로드 하고 설정해준다는 것을 알게 되었다.
따라서 실제 해결방안에 적은 아래방법과 같이 진행하였다.
[수정 완료한 Dockerfile]
# JDK 17을 포함한 OpenJDK 이미지를 베이스 이미지로 사용
FROM openjdk:17-jdk-slim
# 필수 패키지 설치
RUN apt-get update && apt-get install -y wget curl unzip
# 작업 디렉토리를 설정합니다.
WORKDIR /app
# 스프링 애플리케이션 JAR 파일을 컨테이너로 복사합니다.
COPY ./build/libs/notify-crawler-0.0.1-SNAPSHOT.jar /app/notify-crawler.jar
# 크롬 설치
RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN apt-get install -y ./google-chrome-stable_current_amd64.deb
RUN rm ./google-chrome-stable_current_amd64.deb
# 크롬 버전 확인
RUN google-chrome --version
# 포트를 엽니다.
EXPOSE 8082
# 어플리케이션을 실행합니다.
ENTRYPOINT ["java", "-jar", "/app/notify-crawler.jar"]
보면 드라이버를 설치하는 부분과 환경변수를 설정하는 부분들을 제거했다.
build.gradle
은 셀레니움 버젼을 4.6 이상으로 바꾸고 웹 드라이버 의존성을 삭제[기존의 build.gradle]
// Selenium WebDriver
implementation 'org.seleniumhq.selenium:selenium-java:4.1.0'
// Chrome WebDriver
implementation 'io.github.bonigarcia:webdrivermanager:5.0.3'
[수정 완료한 build.gradle]
// Selenium WebDriver
implementation 'org.seleniumhq.selenium:selenium-java:4.8.0'
그런데 여전히 오류가 발생했다.
그래서 이번에는 크롬 드라이버 옵션을 추가하였다.
ChromeOptions options = new ChromeOptions();
// Headless 모드 활성화
options.addArguments("--headless");
options.addArguments("--no-sandbox"); // 추가한 옵션
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--disable-gpu"); //추가한 옵션
options.addArguments("--ignore-ssl-errors=yes");
options.addArguments("--ignore-certificate-errors");
추가한 옵션은 아래와 갗은 기능을 한다.
짜잔!! 아래의 결과처럼 오류를 해결하였다!!
이걸로 하루를 날렸었는데 블로그를 쓰면서 각잡고 하다보니 금방 해결했다 😀😀
참고: