MongoDB와 Airflow 컨테이너를 빌드하는 과정에서 위와 같은 에러가 발생했다.
에러 내용은 pip 명령어가 패키지를 다운로드 하는 동안 타임 아웃이 발생했다는 것이었다..
이 에러는 인터넷 연결이 불안정하거나, 특정 네트워크 조건에서 패키지 다운로드 시간이 오래 걸릴 때 발생할 수 있다고 한다.
Airflow에 관련된 Dockerfile에 requirements.txt 파일로부터 의존성을 설치하도록 하는 명령어가 있었다.
여기서 문제가 발생한 것 같다.
그래서 컨테이너를 빌드하기 전에 미리 의존성을 로컬 환경에서 다운로드 후, 빌드 시 해당 파일을 사용하는 방법을 사용해보기로 했다.
pip download -r requirements.txt -d ./packages
Ubuntu에 pip을 설치한 적이 없기 때문에 pip을 설치하라는 메시지가 나왔다.
pip 설치하기
sudo apt update
sudo 권한을 사용하기 위해 비밀번호를 입력하면 update가 시작된다.
sudo apt install python3-pip
pip을 설치하는 명령어이다.
위 명령어를 실행했더니 다음과 같은 에러가 발생했다.
dpkg가 중단된 상태라는 의미로, 아래 명령을 실행해 dpkg 구성을 완료해준다.
sudo dpkg --configure -a
다시 pip을 설치해준다음, 성공적으로 설치되었는지 확인해준다.
pip3 --version
잘 설치된 것을 확인할 수 있다.
다시 의존성을 로컬에 다운로드 해보자.
의존성 설치 중 타임 아웃 에러가 발생했다.
Collecting gensim==4.3.2
Downloading gensim-4.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (26.5 MB)
━━━━━━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.7/26.5 MB 11.1 MB/s eta 0:00:02
ERROR: Exception:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/pip/_vendor/urllib3/response.py", line 438, in _error_catcher
yield
File "/usr/lib/python3/dist-packages/pip/_vendor/urllib3/response.py", line 519, in read
data = self._fp.read(amt) if not fp_closed else b""
File "/usr/lib/python3/dist-packages/pip/_vendor/cachecontrol/filewrapper.py", line 90, in read
data = self.__fp.read(amt)
File "/usr/lib/python3.10/http/client.py", line 466, in read
s = self.fp.read(amt)
File "/usr/lib/python3.10/socket.py", line 705, in readinto
return self._sock.recv_into(b)
File "/usr/lib/python3.10/ssl.py", line 1303, in recv_into
return self.read(nbytes, buffer)
File "/usr/lib/python3.10/ssl.py", line 1159, in read
return self._sslobj.read(len, buffer)
TimeoutError: The read operation timed out
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/pip/_internal/cli/base_command.py", line 165, in exc_logging_wrapper
status = run_func(*args)
File "/usr/lib/python3/dist-packages/pip/_internal/cli/req_command.py", line 205, in wrapper
return func(self, options, args)
File "/usr/lib/python3/dist-packages/pip/_internal/commands/download.py", line 129, in run
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/resolver.py", line 94, in resolve
result = self._result = resolver.resolve(
File "/usr/lib/python3/dist-packages/pip/_vendor/resolvelib/resolvers.py", line 481, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "/usr/lib/python3/dist-packages/pip/_vendor/resolvelib/resolvers.py", line 348, in resolve
self._add_to_criteria(self.state.criteria, r, parent=None)
File "/usr/lib/python3/dist-packages/pip/_vendor/resolvelib/resolvers.py", line 172, in _add_to_criteria
if not criterion.candidates:
File "/usr/lib/python3/dist-packages/pip/_vendor/resolvelib/structs.py", line 151, in __bool__
return bool(self._sequence)
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 155, in __bool__
return any(self)
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 143, in <genexpr>
return (c for c in iterator if id(c) not in self._incompatible_ids)
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 47, in _iter_built
candidate = func()
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/factory.py", line 215, in _make_candidate_from_link
self._link_candidate_cache[link] = LinkCandidate(
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/candidates.py", line 288, in __init__
super().__init__(
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/candidates.py", line 158, in __init__
self.dist = self._prepare()
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/candidates.py", line 227, in _prepare
dist = self._prepare_distribution()
File "/usr/lib/python3/dist-packages/pip/_internal/resolution/resolvelib/candidates.py", line 299, in _prepare_distribution
return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True)
File "/usr/lib/python3/dist-packages/pip/_internal/operations/prepare.py", line 487, in prepare_linked_requirement
return self._prepare_linked_requirement(req, parallel_builds)
File "/usr/lib/python3/dist-packages/pip/_internal/operations/prepare.py", line 532, in _prepare_linked_requirement
local_file = unpack_url(
File "/usr/lib/python3/dist-packages/pip/_internal/operations/prepare.py", line 214, in unpack_url
file = get_http_url(
File "/usr/lib/python3/dist-packages/pip/_internal/operations/prepare.py", line 94, in get_http_url
from_path, content_type = download(link, temp_dir.path)
File "/usr/lib/python3/dist-packages/pip/_internal/network/download.py", line 146, in __call__
for chunk in chunks:
File "/usr/lib/python3/dist-packages/pip/_internal/cli/progress_bars.py", line 304, in _rich_progress_bar
for chunk in iterable:
File "/usr/lib/python3/dist-packages/pip/_internal/network/utils.py", line 63, in response_chunks
for chunk in response.raw.stream(
File "/usr/lib/python3/dist-packages/pip/_vendor/urllib3/response.py", line 576, in stream
data = self.read(amt=amt, decode_content=decode_content)
File "/usr/lib/python3/dist-packages/pip/_vendor/urllib3/response.py", line 512, in read
with self._error_catcher():
File "/usr/lib/python3.10/contextlib.py", line 153, in __exit__
self.gen.throw(typ, value, traceback)
File "/usr/lib/python3/dist-packages/pip/_vendor/urllib3/response.py", line 443, in _error_catcher
raise ReadTimeoutError(self._pool, None, "Read timed out.")
pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.
스왑 메모리 설정하기
- 패키지 다운로드 중 시스템 리소스, 특히 메모리 부족으로 인해 프로세스가 계속 Killed 되었다.
- 리소스 부족 문제를 해결하기 위해 스왑 메모리를 추가로 설정할 수 있다.
현재 스왑 메모리 확인하기
sudo swapon --show
기존에 설정된 스왑 파일이 있으면 비활성화 하기
sudo swapoff -a
새 스왑 파일 생성하기
-> 4GB의 스왑 파일을 생성했다. (처음에 2GB로 설정했더니 gensim 받는 도중 프로세스가 멈춰버렸다.)
sudo fallocate -l 4G /swapfile
스왑 파일 권한 설정하기
sudo chmod 600 /swapfile
스왑 파일 형식 설정하기
-> 스왑 파일을 스왑 공간으로 사용하도록 설정한다.
sudo mkswap /swapfile
스왑 파일 활성화하기
sudo swapon /swapfile
새로 설정한 스왑 파일 확인하기
sudo swapon --show
재부팅 후에도 스왑 파일이 유지되도록 설정하기
sudo nano /etc/fstab
파일이 열리면 마지막 줄에 다음 문장 추가하기
-> /swapfile none swap sw 0 0
저장 후 종료하면 끝
의존성 파일 로컬에 다운로드하기
pip download -r requirements.txt -d ./packages
-> 파일 경로는 정확히 지정해주어야 한다.
드디어 정상적으로 packages 파일에 의존성을 모두 다운로드 받았다.!!
Dockerfile 수정하기
로컬에 requirements.txt 파일에 있는 패키지들을 다운받아놨으므로, 기존의 Dockerfile에 작성한 requirements.txt 파일을 다운로드 받는 부분을 수정해주어야 한다.
- 기존 Dockerfile
# DockerfileAir 예시
FROM python:3.11-slim-buster
# 작업 디렉토리 설정
WORKDIR /opt/airflow
# 시스템 패키지 및 필수 툴 설치
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& apt-get clean
# Airflow 설치
RUN pip install --no-cache-dir apache-airflow==2.5.0
# Django와 기타 필요한 패키지 설치
COPY ./Encore_Final_Project/requirements.txt /opt/airflow/requirements.txt
RUN pip install --no-cache-dir -r /opt/airflow/requirements.txt
# Django 프로젝트를 컨테이너에 복사
COPY ./Encore_Final_Project /opt/airflow/Encore_Final_Project
# 필요 시 추가 설정 (예: 환경 변수 설정)
ENV PYTHONPATH="/opt/airflow/Encore_Final_Project:$PYTHONPATH"
# Airflow와 Django를 위한 추가 설정
ENV AIRFLOW__CORE__LOAD_EXAMPLES=False
ENV AIRFLOW__CORE__EXECUTOR=LocalExecutor
ENV AIRFLOW__CORE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres/airflow
# 기본적으로 Airflow 웹서버를 시작합니다
CMD ["airflow", "db", "init"] && ["airflow", "webserver"]
- 수정한 Dockerfile
# DockerfileAir 예시
FROM python:3.11-slim-buster
# 작업 디렉토리 설정
WORKDIR /opt/airflow
# 시스템 패키지 및 필수 툴 설치
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& apt-get clean
# Airflow 설치
RUN pip install --no-cache-dir apache-airflow==2.5.0
# Django와 기타 필요한 패키지 설치
COPY ./Encore_Final_Project/requirements.txt /opt/airflow/requirements.txt
# 로컬에 있는 패키지 설치 (로컬에서 다운로드한 패키지 사용)
RUN pip install --no-index --find-links=/opt/airflow/packages -r /opt/airflow/requirements.txt
# Django 프로젝트를 컨테이너에 복사
COPY ./Encore_Final_Project /opt/airflow/Encore_Final_Project
# 필요 시 추가 설정 (예: 환경 변수 설정)
ENV PYTHONPATH="/opt/airflow/Encore_Final_Project:$PYTHONPATH"
# Airflow와 Django를 위한 추가 설정
ENV AIRFLOW__CORE__LOAD_EXAMPLES=False
ENV AIRFLOW__CORE__EXECUTOR=LocalExecutor
ENV AIRFLOW__CORE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres/airflow
# 기본적으로 Airflow 웹서버를 시작합니다
CMD bash -c "airflow db init && airflow webserver"
- docker-compose.yml
version: '3.9'
services:
mongo1:
image: mongo
volumes:
- db1:/data/db
ports:
- 27017:27017
networks:
- mynetwork
healthcheck:
test: test $$(mongosh --port 27017 --quiet --eval "db.adminCommand('ping').ok") -eq 1
interval: 20s
timeout: 10s
retries: 6
postgres:
image: postgres:13
environment:
POSTGRES_USER: airflow
POSTGRES_PASSWORD: airflow
POSTGRES_DB: airflow
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- mynetwork
healthcheck:
test: ["CMD-SHELL", "pg_isready -U airflow"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:latest
networks:
- mynetwork
airflow-webserver:
build:
context: .
dockerfile: DockerfileAir
depends_on:
mongo1:
condition: service_healthy
postgres:
condition: service_healthy
redis:
condition: service_started
environment:
AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CORE__EXECUTOR: LocalExecutor
AIRFLOW__CORE__LOAD_EXAMPLES: "False"
AIRFLOW__WEBSERVER__RBAC: "True"
PYTHONPATH: /opt/airflow/Encore_Final_Project # Django 프로젝트의 PYTHONPATH 설정
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
- /mnt/c/MyDocker/Encore_Final_Project:/opt/airflow/Encore_Final_Project
- ./packages:/opt/airflow/packages # 다운로드한 패키지 경로를 추가
ports:
- "8080:8080"
command: ["airflow", "webserver"]
restart: always
networks:
- mynetwork
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
airflow-scheduler:
build:
context: .
dockerfile: DockerfileAir
depends_on:
airflow-webserver:
condition: service_healthy
environment:
AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
PYTHONPATH: /opt/airflow/Encore_Final_Project
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
- /mnt/c/MyDocker/Encore_Final_Project:/opt/airflow/Encore_Final_Project
- ./packages:/opt/airflow/packages # 다운로드한 패키지 경로를 추가
command: scheduler
restart: always
networks:
- mynetwork
airflow-init:
build:
context: .
dockerfile: DockerfileAir
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
mongo1:
condition: service_healthy
environment:
AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CORE__EXECUTOR: LocalExecutor
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
- /mnt/c/MyDocker/Encore_Final_Project:/opt/airflow/Encore_Final_Project
command: ["airflow", "db", "init"]
restart: "no"
networks:
- mynetwork
node-app:
build:
context: .
dockerfile: DockerfileNode
volumes:
- ./app:/app
networks:
- mynetwork
networks:
mynetwork:
volumes:
db1:
postgres-data:
requirements.txt로 설치하는 것 vs. 패키지를 미리 로컬에 다운로드한 후 설치하는 것
- requirements.txt 로 설치
pip이 requirements.txt 파일을 읽어 그 안에 명시된 패키지들을 인터넷(PyPl)에서 다운로드한다.
따라서 패키지를 설치하려면 반드시 인터넷 연결이 필요하며, 서버 환경에서 인터넷 연결이 제한적이거나 느릴경우 패키지 설치가 느려지거나 실패할 수 있다.
- 패키지를 로컬에 다운로드 후 설치 (로컬 packages 폴더 사용)
미리 다운로드한 패키지 파일을 로컬의 packages 폴더에 저장하고, 설치할 때는 인터넷 대신 로컬에서 설치한다.
인터넷 연결이 필요하지 않으며, 인터넷 환경이 제한적이거나 불안정한 환경에서 유용하게 사용할 수 있는 방법이다.
'Data Engineering > Docker' 카테고리의 다른 글
[Linux/Docker] Flask로 구현한 챗봇을 Docker와 Render를 이용해 배포해보자 (1) - 실패 (4) | 2024.11.18 |
---|---|
[Windows/Docker] Windows에 WSL2를 기반으로 Docker Desktop 설치해보자 (0) | 2024.11.17 |
[Docker] Docker 디스크 사용량 확인하고, 용량 이슈 해결하자 (0) | 2024.09.04 |
[Linux/Docker] Docker Resistry에 대해 알아보고 이미지를 Push, Pull해보자 (0) | 2024.08.24 |
[Linux/Docker] Sinatra를 사용해보고 Ubuntu 서버에 배포해보자 (0) | 2024.08.24 |