본문 바로가기

Develop

ElasticBeanstalk에서 Selenium 크롤링하기: 트러블슈팅부터 실전 팁까지

"웹 스크래핑이 필요한데 AWS에서 Selenium이 작동하지 않아 고생하고 계신가요? 저도 그랬습니다. 하지만 이제는 해결책을 찾았습니다."

들어가며

데이터를 수집하다 보면 언젠가는 마주치게 되는 순간이 있습니다. 바로 동적 웹페이지에서의 데이터 추출이죠. Beautiful Soup으로는 한계가 있어 Selenium을 써야 하는데... AWS Elastic Beanstalk 환경에서 이를 구현하는 것은 생각보다 까다로울 수 있습니다.

이 글에서는 제가 겪었던 문제들과 해결 방법을 단계별로 상세히 공유하려고 합니다.

Elastic Beanstalk이란?

"Docker? EC2? 인프라 구성이 너무 복잡하지 않나요?"

Elastic Beanstalk(EB)은 AWS가 제공하는 PaaS(Platform as a Service) 솔루션입니다. 복잡한 인프라 설정 없이도 애플리케이션을 배포하고 관리할 수 있죠.

Django 프로젝트에서의 장점

  1. 자동 스케일링 지원
  2. 로드 밸런싱 자동 구성
  3. 버전 관리 및 롤백 용이
  4. SSL 인증서 관리 간편화

🚨 트러블슈팅 가이드

Case 1: Chrome과 ChromeDriver 버전 불일치

"왜 자꾸 브라우저가 실행되지 않을까요?"

  File "/home/hm/.pyenv/versions/hmapps/lib/python3.9/site-packages/selenium/webdriver/remote/errorhandler.py", line 245, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 128
Current browser version is 130.0.6723.58 with binary path /opt/google/chrome/chrome
Stacktrace:
#0 0x55623325681a <unknown>
#1 0x556232f24e50 <unknown>
#2 0x556232f63a94 <unknown>
#3 0x556232f62a07 <unknown>                                                                                                             #4 0x556232f5d8ad <unknown>
...
#17 0x5562332457e2 <unknown>                                                                                                            #18 0x55623325560c <unknown>
#19 0x7fa6fda7d609 start_thread
 
 

AWS Beanstalk에서 Chromedriver와 Chrome의 버전이 맞지 않으면 Selenium이 제대로 작동하지 않을 수 있습니다. 이런 경우, ChromeDriverManager()를 사용해 자동으로 버전을 맞추는 방식이 있습니다. 매번 최신 버전으로 업데이트가 가능하기 때문에 버전 불일치로 인한 문제를 미연에 방지할 수 있어요. 그래서 저는 아래와 같이 코드를 수정했습니다.

 

    # Chrome 옵션 설정
    chrome_options = Options()
    chrome_options.add_argument("--headless")  # Headless 모드 활성화
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.binary_location = "/usr/bin/chromium-browser"  # Ubuntu/Debian

    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service, options=chrome_options)
 

Case 2: ChromeDriverManager 설치 문제

"자동으로 관리하면 편할 텐데..."

 

ChromeDriverManager().install()을 사용하면 드라이버 설치가 자동으로 이루어지지만, 저는 새로운 문제를 만났어요.

    rv = old_popen_init(self, *a, **kw)
  File "/home/hm/.pyenv/versions/3.9.10/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/home/hm/.pyenv/versions/3.9.10/lib/python3.9/subprocess.py", line 1821, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 8] Exec format error: '/home/hm/.wdm/drivers/chromedriver/linux64/130.0.6723.58/chromedriver-linux64/THIRD_PARTY_NOTICES.chromedriver'

 

THIRD_PARTY_NOTICES.chromedriver  파일을 실행하려다 실패했네요. 해당 경로에서 파일을 살펴보았습니다. 

$ ls -al  ~/.wdm/drivers/chromedriver/linux64/130.0.6723.58/chromedriver-linux64
total 17864
drwxrwxr-x 2 hm hm     4096 Oct 22 10:16 .
drwxrwxr-x 3 hm hm     4096 Oct 22 10:16 ..
-rwxrwxr-x 1 hm hm 17733152 Oct 22 10:16 chromedriver
-rw-rw-r-- 1 hm hm     1536 Oct 22 10:16 LICENSE.chromedriver
-rwxr-xr-x 1 hm hm   538881 Oct 22 10:16 THIRD_PARTY_NOTICES.chromedriver

 

chromedriver라는 실행파일은 별도로 있고, THIRD_PARTY_NOTICES.chromedriver 는 텍스트 파일이네요.

$ head  ~/.wdm/drivers/chromedriver/linux64/130.0.6723.58/chromedriver-linux64/THIRD_PARTY_NOTICES.chromedriver
--------------------
License notice for CityHash
--------------------
// Copyright (c) 2011 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is

 

이유는 알 수 없지만, chromedriver 실행하도록 변경하고 재시도해 보았습니다. 그리고는 새로운 이슈를 만났어요.

 

Case 3: 실행 권한 문제

"설치는 됐는데 실행이 안 되네요?"

  File "/home/hm/.pyenv/versions/hmapps/lib/python3.9/site-packages/selenium/webdriver/common/selenium_manager.py", line 112, in run
    raise SeleniumManagerException(f"Selenium Manager failed for: {command}.\n{result}{stderr}")
selenium.common.exceptions.SeleniumManagerException: Message: Selenium Manager failed for: /home/hm/.pyenv/versions/hmapps/lib/python3.9
/site-packages/selenium/webdriver/common/linux/selenium-manager --browser chrome --output json.
The chromedriver version cannot be discovered

 

이 오류 메시지를 읽고서 한참 생각했어요. 또 뭐를 놓친 거지. chatgpt, google 여기저기서 검색했습니다. 여러 가지 시도 후 저는 파일에 실행권한을 추가하니 이 오류는 해결이 되었네요.

$ chmod a+x chromedriver

 

이처럼 세 관문을 통과하고 성공적으로 실행이 되었어요라고 얘기를 하면 좋겠지요. 당연하게도 또 다른 문제를 만났습니다. 사실 이 문제가 제일 해결하기 어려웠어요. 왜냐면 실행 후 5분가량 시간이 지나면 timeout이라며 해당 프로세스가 그냥 종료가 되었기 때문입니다. 어딘가에 단서를 남겨 두었을 테지만 제 능력으로 찾기 어려웠어요. 지금부터는 객관적인 에러 메시지에 근거한 접근이 아닌, 상상으로 원인에 다가갔습니다. 그중에 하나가 자원의 제약이었어요. selenium 은 브라우저를 띄우는 작업이 내부적으로 필요한데, free tier 용의 ec2 자원은 cpu 또는 memory에 제한이 있지 않을까 의심을 시작했습니다. 그 내용은 아래 't3.micro 메모리 부족 해결하기'에 추가적으로 남겨 봅니다. 

💡 실용적인 팁

코드 재배포 없이 Selenium 테스트하기

ElasticBeanstalk의 장점은 코드 배포가 상당히 간단하다는 것입니다. 아래 명령어 하나 입력하는 것 만으로 로컬의 내 코드가 ec2 환경에 적용이 되어, django 서비스가 재시작이 됩니다. 

$ eb deploy

 

전통적인 방법으로 원격 서버에 ssh 진입 후, 형상관리 툴을 이용해 최신 코드 다운로드, 코드 적용, db migration, 서비스 재시작을 일일이 타이핑했던 것에 비하면 상당히 우아하고 간결합니다. 다만 이 모든 것을 한 번에 실행할 수 있지만, 배포에 시간이 필요했습니다. 복잡하고 무거운 코드가 아니어서 5분 이내로 배포 후 health check까지 완료됩니다. 단순히 원격 환경에서 chromedriver의 작동여부 판단하기 위해 매번 5분을 기다려야 하는 것은 우아한 방법은 아니었습니다. 그래서 아래와 같은 간단한 한 줄짜리 스크립트를 만들었습니다. 이 명령어가 실행이 된다면 프로젝트 코드에서 selenium 도 정상적으로 실행이 됩니다.

$ source /var/app/venv/*/bin/activate

$ python3 -c "from selenium import webdriver; from selenium.webdriver.chrome.options import Options; options = Options(); options.binary_location = '/usr/bin/chromium-browser'; options.add_argument('--headless'); options.add_argument('--no-sandbox'); options.add_argument('--disable-dev-shm-usage'); options.add_argument('--disable-gpu'); options.add_argument('--memory-pressure-off'); options.add_argument('--single-process'); driver = webdriver.Chrome(options=options); driver.get('https://www.ssg.com'); print(driver.title); driver.quit()"

 

t3.micro 메모리 부족 해결하기

상용 서비스가 아닌 단계에서 고사양의 ec2 type을 사용하는 것은 경제적, 심리적 부담입니다. 어떻게든지 free tier에서 끝을 봐야 합니다. 다행히도 사용자도 많지 않아 트래픽도 많지 않고, 단순히 코드의 작동 유무 판단만 필요하므로, 요청-응답속도가 즉각적이지 않아도 됩니다. 3초 이내로 응답이 오면 좋지만, 5초까지도 개발과정에서는 감내할만하다고 생각합니다. 메모리 부족으로 실행이 되지 않는 것보다야 어느 정도 시간이 걸리더라도 응답만 볼 수 있다면 의미 있습니다. chromedriver 실행 실패가 어떤 이유인지 명확히 알 수 없는 상황이어서, 구글링을 통해 얻은 답을 참고하여 파일을 메모리처럼 사용하는 방법을 시도했습니다. 운이 좋게도 실패했던 명령어는 성공했습니다. 결과적으로 생각건대, 메모리 부족이 영향을 주었습니다.

sudo dd if=/dev/zero of=/swapfile bs=1M count=1024
sudo mkswap /swapfile
sudo swapon /swapfile

 

참고: https://repost.aws/knowledge-center/ec2-memory-swap-file 

마치며

AWS Beanstalk에서 Selenium을 실행하는 것은 쉽지 않습니다. 내 개발환경에서 실행되면 코드가 원격환경에서 실패하면 시작부터 의욕을 잃기 쉽습니다. 이전에도 시도와 실패의 경험이 한 손가락으로 셀 수 있을 정도였습니다. 이번에는 그러고 싶지 않아 좀 더 진득하게 밀어붙여 보았습니다. google, stackoverflow, chatgpt, claude 등 여러 온라인 자원의 도움에 감사를 드립니다.

 

반응형