학습 주제
주요 메모 사항
먼저 Airflow 운영상의 어려움을 설명
관리해야하는 DAG의 수가 100개를 넘어간다면?
데이터 품질이나 데이터 리니지 이슈 이외에도 다양한 이슈들이 발생
어떤 이슈들이 있을까?
- 라이브러리 충돌
- Worker의 부족
- Worker 서버들의 관리와 활용도 이슈
1. 라이브러리 충돌
라이브러리/모듈의 충돌 이슈가 발생하기 시작함
DAG에 따라 실행에 필요한 라이브러리/모듈이 달라지기 시작
- 예) Python 버전
이로 인해 DAG 혹은 Task별로 별도의 독립공간을 만들어주는 것이 필요
- Docker to the rescue
- Dag 혹은 Task 코드를 Docker Image로 만들고 이를 독립된 공간(Docker Container)안에서 실행
2. Worker의 부족, upgrade 순서
Scale Up
Scale Out - 가능하면 클라우드 서비스 사용
K8s와 같은 컨테이너 기술 사용 -필요한대로 서버 요청
3. 낮은 Server Utilzation 이슈
Airflow 전용 하드웨어를 지정했는데 서버들이 항상 바쁘지 않다면? Peak time
- 만약 10대의 서버에 Airflow를 할당했다면?
서비스별로 전용 서버를 할당하는 것은 여러가지로 이슈를 만들어냄
- 서비스별로 Capacity 관리를 해야함
- 각 서비스에 속한 서버들은 보면 utilization이 낮은 이슈 발생
이 역시 K8s와 같은 컨테이너 기술의 도입으로 해결 가능
해결책
태스크나 DAG 코드를 Docker Image로 만들어서 Docker Container 형태로 실행
- 라이브러리/모듈 충돌을 방지
- 개발 환경과 프로덕션 환경을 동일하게 유지
Airflow Worker를 K8s에서 필요한 대로 동적으로 할당하여 사용
- 전용 서버를 Airflow에 할당하지 않고 Container Orchestration 서비스를 통해 할당해서 사용하고 리턴
Airflow에서 이를 해결하는 방법은 3가지
a. Airflow Operator로 KubernetesPodOperator를 사용
b. Airflow Operator로 DockerOperator를 사용
c. Airflow Executor로 아래를 사용
■ KubernetesExecutor
■ CeleryKubernetesExecutor
■ LocalKubernetesExecutor
-> Airflow Executor이란?
Executor는 Task들을 관리하고 실행하는 역할을 수행
- 병렬 혹은 일렬 실행이나 어느 worker에서 실행할지 등등
다양한 수의 Executor 타입이 존재
Sequential Executor: 디폴트로 설치되며 Sqlite와 같은 싱글스레드 DB에서만 사용가능 worker node가 1개 == master node도 1개
Local Executor: task들을 Airflow 마스터 노드안에서 실행
Celery Executor: 다수의 Worker 노드가 있는 경우 사용되며 Celery 큐를 사용해 task들을 worker 노드로 분산하여 실행
Kubernetes Executor는 K8s 클러스터를 사용하여 task들을 독립된 환경에서 사용
Local Kubernetes Executor와 Celery Kubernetes Executor도 존재
Airflow 아키텍처 : Docker와 K8s를 사용하는 방법
Airflow Operator로 KubernetesPodOperator를 사용
- 이 방식은 특정 태스크를 Docker Image로 만들어 K8s에서 실행
Airflow Operator로 DockerOperator를 사용
- 이 방식은 특정 태스크를 Docker Image로 만들어 Docker Container 위 에서 실행
Airflow Executor로 다음 중의 하나를 사용
- KubernetesExecutor - 모든 DAG 코드가 Docker Image로 빌드되어 K8s에서 실행됨
- CeleryKubernetesExecutor
- CeleryExecutor와 KubernetesExecutor를 동시에 사용하는 방법을 제공해주는 Executor
- 이는 Airflow 로드가 전체적으로 큰데 소수의 task만 Isolation을 필요로 하는 경우
- LocalKubernetesExecutor - LocalExecutor와 KubernetesExecutor를 동시에 사용하는 방법을 제공해주는 Executor
Docker 소개
내가 만든 프로그램이 다른 컴퓨터에서 안 돌아간다면?
설치 과정에서 중요한 파일이 빠짐
사용하는 라이브러리 등의 버전이 안 맞음 -> 가장 골치 아픈 문제!
환경 설정이 안 맞는 것이 존재
-> 내 컴퓨터 환경을 그대로 패키징해서 다른 이에게 줄 수 있다면?
Docker Image: 이렇게 독립적으로 완전하게 만들어진 패키지
Docker Container: 이 Docker Image를 독립된 환경에서 실행한 것
-> Docker의 목표, 소프트웨어를 일관되게 빌드하고 실행하고 배포
그래서 아래와 같은 이미지의 간단한 관리가 가능한 것임
그렇다면 Virtual Machines vs. Docker Containers ?
Virtual Machine 소개
AWS의 EC2가 대표적인 Virtual Machine, GCP의 Compute Engine
하드웨어를 추상화하여 한 컴퓨터 위에 가상 컴퓨터를 올리는 것
필자 생각 : WSL이 윈도우11에서 정말 편함
장점
소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
다수의 소프트웨어를 각 VM단에서 독립적으로 실행가능
단점
각 VM은 자신만의 OS를 필요로 함 (가상 하드웨어위에서 돌기 때문)
- 유료 OS라면 라이센스 비용 필요
- 그러다보니 시작하는데 오래 걸림
자원을 많이 사용함 (VM들끼리 자원을 나눠써야함)
Docker가 나오기 이전에는 VM을 많이 사용했음,
Docker Container 소개
Container 이전에 Docker Engine는 Linux 기술을 사용해 개발되었음
Docker Engine은 기본적으로 Linux 컨테이너를 지원
Linux 컨테이너는 호스트 운영 체제의 커널을 공유하여 프로세스를 격리된 환경으로 실행하는 가벼운 가상화 기술
소프트웨어를 실행하기 위한 독립적이고 분리된 공간
자체 파일 시스템을 갖고 있음 (Volume이라고 부름)
오른쪽 이미지는 지원하는 컨테이너 OS를 설명하는 것
Host OS | 지원하는 컨테이너 OS |
맥 | 경량화된 리눅스 VM이 동작함 |
윈도우 | 윈도우, 리눅스 |
리눅스 | 리눅스 |
장점
소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
- 다수의 소프트웨어를 각 컨테이너단에서 독립적으로 실행가능
자원 소비가 적음 (lightweight)
- 몇 십개에서 몇 백개의 container를 실행 가능
호스트 OS를 사용 (별도 비용 없음)
- 따라서 빠르게 실행됨
단점
많은 수의 Docker Container를 관리하는 것은 쉽지 않음
Host OS를 사용하기에 Cross-platform compatibility를 항상 지원하지 않음
GUI 소프트웨어 개발에 적합치 않음
단점 1번에 많은 수의 Docker Container를 관리하는 것은 쉽지 않음이 K8s에서 해결되는 것
Docker 설치
필자는 Docker for Window가 설치되어 있으나, wsl ubuntu 위에 Docker을 하나 더 설치했음
Docker 프로그램 개발 프로세스
하이레벨 Docker 사용 프로세스
먼저 대상 소프트웨어를 선택
- 다수의 컴포넌트로 구성되는 소프트웨어라면 각각이 Docker Image로 만들어져야할 수도 있음
ㄴ> 이를 Docker Image로 빌드하자: Dockerization이라고 부름
- Dockerfile이란 텍스트 파일로 세부 정보를 기술
- 해당 소프트웨어를 이미지로 바꾸기 위한 Docker에게 주는 명령들을 포함
- Docker Image: 하나의 Docker Container안에서 실행됨!
- Dockerfile을 기준으로 만들어지며 소프트웨어를 실행하기위해 필요한 모든 것을 포함
Docker Image의 구성 요소
- 기본 OS (리눅스라면 우분투, 데비안 등등)와 같은 소프트웨어의 실행환경
- 소프트웨어 자체 (코드)
- 소프트웨어가 필요로 하는 라이브러리
- 파일 시스템 스냅샷: 이는 스택화된 형태로 구현됨 (뒤에서 더 설명)
- 환경 설정 변수: 빌드할 때 변수와 실행 때 변수 두 가지가 존재
- 메타 데이터: 이미지 자체에 대한 정보 (버전, 작성자, 설명 등등)
-> 위 정보와 설치 관련 실행 순서등이 Dockerfile에 기술됨
- Docker Image는 다수의 파일로 구성됨 (“docker image ls”)
Docker Image의 실행
Container를 통해 Docker Image안의 소프트웨어를 실행
- Container는 자체 파일 시스템을 가진 특수한 프로세스로 이미지의 파일 시스템이 로딩됨
Image를 Container 안에서 실행
방법 : docker run, docker exec
-> 두개의 차이 run은 처음 실행하는 image를 컨테이너 안으로 띄우는거 exec는 이미 실행된 docker에 명령을 내리는 것임
Docker Image의 등록: Docker Hub
Docker Registry는 Docker Image들의 보관소
- On-prem registry와 Cloud registry가 존재
- docker hub 가장 유명
여기에 등록을 하면 회사내 혹은 퍼블릭하게 이미지를 공유 가능
Docker Hub이란 무엇인가?
Docker가 제공해주는 서비스로 Docker Image를 공유하고 찾기 위한 서비스
Git과 거의 유사한 거라고 보면 될 듯
실습 1: 간단한 Hello World
만들려는 프로그램 개요
Node.js로 구성된 초간단 웹 서비스, app.js가 전부!
- 하지만 node 런타임 환경이 필요, 보통 이를 실행하려면 `$ node app.js`
node 환경이 설정되어 있어야함!
만들려는 프로그램 실행 (Docker 없이)
---------------------------------------------------------
직접 설치/실행시 순서
a. OS 선택
b. Node 설치
c. 코드 복사
d. 프로그램 실행 (node app.js)
---------------------------------------------------------
위 내용을 Dockerfile에 기술하면 Docker Image를 생성하는 것임
Dockerfile의 생성
Docker에게 소프트웨어 설치 명령을 기술
먼저 베이스 이미지를 기술 (FROM)
다음으로 코드 복사 (COPY)
마지막으로 코드 실행 (CMD)
Dockerfile 사용 가능 기타 키워드
ARG : Docker Image를 만들 때 사용되는 변수 지정. 최종 이미지에는 안 들어감
ENV : 컨테이너가 실행될 때 사용되는 환경변수. 최종 이미지에 저장됨
USER : 컨테이너를 실행할 때 사용할 유저 ID
EXPOSE : 서비스 사용 포트번호
RUN : 빌드시 실행되어야하는 명령들이 지정됨 (docker build)
`$ RUN apt-get update && apt-get install -y cur`
Dockerfile 키워드: CMD vs. ENTRYPOINT
Container가 시작할 때 실행되어야 하는 명령어를 지정하는데 사용 (docker run ~~~)
- 굉장히 흡사한 기능을 제공하지만 우선 순위가 있음
둘다 한 DOCKERFILE에서 여러번 실행되면 각각 마지막 것만 사용됨!
- 아래의 경우 docker run 실행시 동일한 결과가 나옴 -> command2.sh만 실행
-> CMD나 ENTRYPOINT 중 하나만 지정되면 그게 container가 실행될 때 실행
오른쪽 이미지에서 `$ docker run my-image ~~~.sh`를 사용하면 지정한 스크립트를 실행시킬 수 있다는 것
-> 둘이 한 DOCKERFILE에서 같이 지정가능함
둘이 같이 사용되면 ENTRYPOINT가 기본 명령이 되고 CMD가 인자를 제공
즉, entrypoint.sh 매개변수1 매개변수2 과 같은 명령어가 전달되는 것임!!
ENTRYPOINT는 --entrypoint 옵션을 통해서만 덮어쓰기가 가능
그렇다면 CMD or ENTRYPOINT?
최대한 CMD만 사용
ENTRYPOINT를 사용하면 실행시 타이핑을 덜 할 수 있음
- 파라미터를 지정해주면 되기 때문이지만 감춰지기 때문에 오히려 혼란을 줄 수 있음
--- dockerfile ---
ENTRYPOINT ["python"]
CMD ["app.py"]
------ bash ------
python app.py
---------------------
처럼 실행된다는 것이지만 우리는 CMD만 사용할 예정!!
요약
1. ENTRYPOINT가 있으면 CMD 값이 파라미터로 실행됨
2. 아니면 CMD가 실행됨
Dockerfile 더 살펴보기
Dockerfile : Airflow 예시
Docker Image 생성
$ docker build --platform linux/amd64 -t hello-world-docker .
=> [internal] load build definition from Dockerfile
…
=> => writing image
sha256:cb6c638168780afd3d74fc1cddd813917a6a397dad453c8e1a8063635c1521fe 0.0s
=> => naming to docker.io/library/hello-world-docker
Docker build를 실행하면 Dockerfile에서 Run 명령이 실행됨
--platform linux/amd64 명령
만일 Apple M1 chip 기반 맥에서 빌드하는 경우 그 이미지는 ARM 기반 아키텍처로 만들어지기 때문에 일반 리눅스에서 안 돌아감. 그래서 --platform 옵션을 사용해서 linux/amd64로 지정 -> CPU 아키텍처에 영향을 받는다. 이 부분이 조금 이해가 안감
-t hello-world-docker .
-t 태그는 뒤에서 따로 다룰 예정!!, 그리고 맨마지막에 comma . 가 있는데 이는 dockerfile의 PATH를 뜻하는 것임
Docker Image 확인
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world-docker latest cb6c63816878 22 minutes ago 179MB
TAG의 값에 아무것도 지정하지 않았기 때문에 lastest로 지정된 것임
Docker Container로 실행
$ docker run hello-world-docker
Hello Docker!
docker run을 Dockerfile에서 CMD 명령이 실행됨
만일 이 이미지를 다른 컴퓨터에서 실행하고자 한다면?
-> Docker Registry (예를 들면 Docker hub)으로 먼저 등록
Docker : hello-world-docker repo 만들기
1) 회원가입후 Create repository 선택, repo 이름 지정
2) 아까전에 만들었던 app.js를 dockerfile로 등록하고 해당 image를 docker repo에 upload 할 것
Docker에 로그인하고 password를 입력하면 된다.
근데 경고 메세지를 보면 알겠지만 내가 서버에 접속한 계정의 .docker/config.json에 내 패스워드가 저장된다 라고 알려주는데 이를 credential hepler를 사용해서 비밀번호를 암호화 해야하는 것 같다. 아마도 이번 강의에서 다루지는 않을 듯?
Mounted from libaray/node 에서 자동으로 node 라이브러리를 이미지에 import 하는 것 같다.
Push까지 완료 했으면 다시 Docker Web 에서 확인해보자
해당 과정까지 진행했으면 실습은 끝났다. 이제야 도커를 왜 쓰는지 이유를 알겠다!
Docker Hub로부터 받은 Image 실행
다른 서버에서 이미지를 다운로드 받아 컨테이너로 실행하기
- https://labs.play-with-docker.com/를 사용해서 4시간동안 서버를 하나 무료로 사용
- Docker Hub에 로그인이 되어있다면 그냥 계정 연결을 허용하면 됨
왼쪽에 new instance를 클릭, (Memory를 4기가나 준다??)
터미널 윈도우를 Option+Enter(윈도우는 Alt+Enter)로 최대화하고 아래 명령 실행
Docker Desktop에서 로그인
(불필요하지만) Docker Desktop에서 Docker Hub으로 로그인해두면 Desktop에서 Pull, Push 등을 수행 가능 하다는 것만 알아두자, 필자는 desktop으로 진행 안했
여기서 사용해본 Docker 명령 요약
-> 한번씩 직접 해보면 금방 느낌이 온다 git 이랑 거의 비슷하다고 생각함
docker run vs. docker exec
docker run과 docker exec의 차이점은 무엇일까?
- docker run은 새로 Container를 실행하는 것
- docker exec는 실행된 Container에 작업을 하는 것 - 그래서 이 명령은 Container ID가 필요함
- 두 명령 모두 --user root 혹은 -u root를 통해 루트 유저로 연결가능
Docker Image 이름
docker image ls와 docker images와 동일한 결과를 보여줌
앞서 docker tag 명령의 경우 별칭을 만들어주는 것임 (이름을 바꾸는 것이 아님)
- $ docker tag hello-world-docker usiohc/hello-world-docker
아까전에 docker image를 생성하고 push하고 pull했을때, Image ID 가 같다는 것을 알 수 있음, Docker image의 실제 ID는 IMAGE ID임
Image 이름 자체는 REPOSITORY 이름과 TAG로 구성됨, 따로 docker에서 말하는 규칙은 없는 것 같다고 하심
-> 한번에 쓰는 경우 :을 사이에 두고 같이 씀 -> redis:13, keeyong/hello-world-docker:latest
docker image ls에서 Repository에 해당
- Docker Hub에서 다운로드받은 것이라면 어카운트ID(네임스페이스)를 포함할 수 있음
- 하지만 공식이미지는 네임스페이스가 없음
포맷: Repo이름:태그
- 예: keeyong/hello-world-docker:latest
- 예: bitnami/airflow
- 예: hello-world-docker:latest
공식 이미지들의 경우에는 네임스페이스가 없음
- 예: ubuntu:18.04
- 예: node:alpine
Docker tag란?
Docker Image의 버전이나 변형을 나타내는 문자열
- 디폴트 값은 latest
- Docker Image의 부가정보를 나타냄
Docker Image 이름에서 :뒤에 해당
- 예: ubuntu:18.04
- 예: bitnami/airflow
- 예: node:alpine
실습 2:우분투
Docker 위에서 ubuntu를 컨테이너로 실행할 수 있다!
필자는 진행 안함
실습 3: MySQL
MySQL 8.0을 Docker로 실행, 필자는 강의만 보고 진행은 안했음.
먼저 Docker Engine이 실행된 것 확인하고 terminal 프로그램 실행
MySQL docker image를 다운로드
$ docker pull mysql/mysql-server:8.0
다운로드받은 이미지로 Docker container 실행
$ docker run --name=mysql_container mysql/mysql-server:8.0
MySQL root 계정의 패스워드 찾기
$ docker logs mysql_container 2>&1 | grep GENERATED
마지막으로 MySQL shell 실행하기
$ docker exec -it mysql_container mysql -uroot -p
공부하며 어려웠던 내용
강의에서 다루지 않았던 디테일한 내용들이 궁금해졌다. (본문에 조금씩 적어놨음)
'프로그래머스 데브코스-데이터 엔지니어 > TIL(Today I Learned)' 카테고리의 다른 글
06/14 48일차 개발환경 구축을 위한 Docker & K8s (3) (2) | 2023.06.14 |
---|---|
06/13 47일차 개발환경 구축을 위한 Docker & K8s (2) (0) | 2023.06.13 |
06/09 45일차 데이터 파이프라인과 Airflow (5) (0) | 2023.06.09 |
06/08 44일차 데이터 파이프라인과 Airflow (4) (0) | 2023.06.08 |
06/07 43일차 데이터 파이프라인과 Airflow (3) (1) | 2023.06.07 |