[5/27] TIL - Docker

Sangwon Jwa·2024년 5월 27일

데브코스 TIL

목록 보기
37/54
post-thumbnail

📖 학습 주제


  1. Airflow 운영상의 어려움
  2. Docker
  3. 간단한 Hello World 프로그램
  4. Docker에서 Ubuntu 실행하기
  5. Docker로 MySQL 실행하기

✏️ 주요 메모 사항 소개


Airflow 운영상의 어려움

관리해야 하는 DAG의 수가 많아질수록 데이터 품질이나 데이터 리니지 이슈 이외에도 라이브러리 충돌, Worker의 부족, Worker 서버들의 관리와 활용 이슈와 같은 다양한 문제가 발생할 수 있다.

1. 라이브러리 충돌

여러 DAG를 사용하게 되면서 각 DAG에 따라 실행에 필요한 라이브러리/모듈이 달라지기 시작한다. 이렇다 보니 라이브러리/모듈의 충돌 이슈가 발생할 수 있다. 이로 인해 DAG 혹은 Task 별로 별도의 독립공간을 만들어주는 것이 필요한데 이 때 사용할만한 것이 바로 Docker이다.

  • Docker to the rescue
  • Dag 혹은 Task 코드를 Docker Image로 만들고 이를 독립된 공간(Docker Container)안에서 실행

2. Worker의 부족

DAG가 많아지면서 필요한 리소스도 많아지게 된다. 특히 Airflow Worker의 부족 현상을 겪게 되는데, 이 때 우리는 다음 두가지 방법으로 서버의 용량을 늘려주어야 한다.

  • Scale Up
  • Scale Out

보통 이런 Scale Up과 Scale Out 작업은 클라우드 서비스를 사용한다. K8s와 같은 컨테이너 기술을 사용해서 필요한대로 서버를 요청하게 구성할 수 있다. 이는 비단 Airflow만이 아니라 여러대의 서버가 필요한 환경, 서버가 탄력적으로 돌아가야 하는 모든 상황에서 사용할 수 있다.


3. 낮은 Server Utilization

Airflow 전용 하드웨어를 지정했지만 서버들이 항상 바쁘지 않은 경우에는 여러가지 이슈를 만들어낼 수 있다.

  • 서비스별로 Capacity 관리를 해야함
  • 각 서비스에 속한 서버들은 보면 utilizaiton이 낮은 이슈가 발생

이 역시 K8s와 같은 컨테이너 기술의 도입으로 해결이 가능하다.


해결책

  1. 태스크나 DAG 코드를 Docker Image로 만들어서 Docker Container 형태로 실행
    • 라이브러리 / 모듈 충돌 방지
    • 개발 환경과 프로덕션 환경을 동일하게 유지
  1. Airflow Worker를 K8s에서 필요한 대로 동적으로 할당하여 사용
    • 전용 서버를 Airflow에 할당하지 않고 Container Orchestration 서비스를 통해 할당해서 사용하고 리턴

Airflow에서 이를 해결하는 방법은 3가지가 있다.

  1. Airflow Operator로 KubernetesPodOperator를 사용
    • 특정 Task를 Docker Image로 만들어 K8s에서 실행

 

  1. Airflow Operator로 DockerOperator를 사용
    • 특정 Task를 Docker Image로 만들어 Docker Container 위에서 실행

 

  1. Airflow Executor로 아래를 사용
    • KubernetesExecutor
      • 모든 DAG 코드가 Docker Image로 빌드되어 K8s에서 실행
    • CeleryKubernetesExecutor
      • CeleryExecutor와 KubernetesExecutor를 동시에 사용하는 방법을 제공해주는 Executor
      • 이는 Airflow 로드가 전체적으로 큰데 소수의 task만 Isolation을 필요로 하는 경우
    • LocalKuberneesExecutor
      • LocalExecutor와 KubernetesExecutor를 동시에 사용하는 방법을 제공해주는 Executor

 

  • Executor란 Task들을 관리하고 실행하는 역할을 수행하는 기능 (병렬 혹은 일렬 실행이나 어느 worker에서 실행할지 등등) 다양한 수의 Executor 타입이 존재한다.
    • Sequential Executor : 디폴트로 설치되며 Sqlite와 같은 싱글스레드 DB에서만 사용가능
    • Local Executor : task들을 Airflow 마스터 노드안에서 실행
    • Celery Executor : 다수의 Worker 노드가 있는 경우 사용되며 Celery 큐를 사용해 task들을 worker 노드로 분산하여 실행
    • Kubernetes Executor : K8s 클러스터를 사용하여 task들을 독립된 환경에서 사용
    • Local Kubernetes Executor와 Celery Kubernetes Executor도 존재

Docker란?

Docker를 사용하는 이유는 내가 만든 프로그램이 다른 컴퓨터에서도 돌아갈 수 있게 하기 위해서 이다. 안 돌아가는 이유는 여러가지가 있을 수 있겠지만 다음과 같은 이유일 수 있다.

  1. 설치 과정에서 중요한 파일이 빠짐
  2. 사용하는 라이브러리 등의 버전이 안 맞음 (가장 골치 아픈 문제)
  3. 환경 설정이 안 맞는 것이 존재

따라서 Docker의 목표는 자신의 컴퓨터 환경을 그대로 패키징해서 다른이에게 줌으로써 같은 환경을 제공하여 소프트웨어를 일관되게 빌드하고, 실행하고, 배포하는 것이다.


VM vs Docker

VM

  • 장점

    • 소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
    • 다수의 소프트웨어를 각 VM단에서 독립적으로 실행 가능
  • 단점

    • 각 VM은 자신만의 OS를 필요로 함 (가상 하드웨어위에서 돌기 때문)
      • 유료 OS라면 라이센스 비용 발생
      • 그러다보니 시작하는데 오래 걸림
    • 자원을 많이 사용함 (VM들끼리 자원을 나눠써야함)

Docker

  • 장점

    • 소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
    • 다수의 소프트웨어를 각 컨테이너단에서 독립적으로 실행 가능
    • 자원 소비가 적음 (lightweight)
      • 몇 십개에서 몇 백개의 container를 실행 가능
    • 호스트 OS를 사용 (별도 비용 없음)
      • 따라서 빠르게 실행됨
  • 단점

    • 많은 수의 Docker Container를 관리하는 것은 쉽지 않음
    • Host OS를 사용하기에 Cross-platform compatibility를 항상 지원하진 않음
    • GUI 소프트웨어 개발에 적합치 않음

Docker 프로그램 개발 프로세스

Docker Image 생성

  • 먼저 대상 소프트웨어를 선택한다
    • 다수의 컴포넌트로 구성되는 소프트웨어라면 각각이 Docker Image로 만들어져야할 수도 있음
  • 이를 Docker Image로 빌드 (Dockerization)
    • Dockerfile이란 텍스트 파일로 세부 정보를 기술
      • 해당 소프트웨어를 이미지로 바꾸기 위한 Docker에게 주는 명령들을 포함
    • Docker Image : 하나의 Docker Container안에서 실행됨
      • Dockerfile을 기준으로 만들어지며 소프트웨어를 실행하기 위해 필요한 모든 것을 포함

 

Docker Image 구성 요소

  1. 기본 OS(우분투, 데미안, ...)와 같은 소프트웨어의 실행 환경
  2. 소프트웨어 자체 (코드)
  3. 소프트웨어가 필요로 하는 라이브러리
  4. 파일 시스템 스냅샷 : 이는 스택화된 형태로 구현
  5. 환경 설정 변수 : 빌드할 때 변수와 실행 때의 변수 두가지가 존재
  6. 메타 데이터 : 이미지 자체에 대한 정보 (버전, 작성자, 설명 등등)

위 정보와 설치 관련 실행 순서등이 Dockerfile에 기술됨, Docker Image는 다수의 파일로 구성 (docker image ls)


Docker Image 실행

  • Container를 통해 Docker Image안의 소프트웨어를 실행
    • Container는 자체 파일 시스템을 가진 특수한 프로세스로 이미지의 파일 시스템이 로딩됨
  • Image를 Container 안에서 실행 (docker run ...)

Docker Image 등록 : Docker Hub

  • 만든 Docker Image가 나 혼자만 사용하는 것이 아니라 공유가 필요하다면 Docker Registry에 등록
  • On-prem registry 와 Cloud Regitry가 존재
    • docker hub이 가장 유명하고 널리 쓰임
  • 여기에 등록을 하면 회사내 혹은 퍼블릭하게 이미지를 공유 가능

Docker Hub이란 Docker가 제공해주는 Docker Registry 서비스로 Docker Image를 공유하고 찾기 위한 서비스이다.
다음을 포함한 여러가지 기능들을 제공한다

  1. Teams & Organizations
  2. Public & Private Repo 제공
  3. Official Images
  4. Github와 연동을 통한 Automated Build 제공

Docker Hub링크 : https://hub.docker.com/


실습1 - 간단한 Hello World 프로그램

node.js로 만든 간단한 프로그램을 단계별로 이미지 빌드부터 최종적으로 다른 서버에서 실행까지 전체 과정을 다뤄보자

만들려는 프로그램은 Node.js로 구성된 초간단 웹 서비스로, app.js 파일이 전부인 서비스이다. 이를 실행하려면 node 런타임 환경이 필요하고 보통 이를 실행하려면 node app.js명령어를 사용한다.

만약 Docker 없이 프로그램을 실행한다면 먼저 OS를 선택하고, Node를 설치한 다음, 코드를 복사하고 프로그램을 실행해야할 것이다. 이 내용을 Dockerfile로 기술하면 Docker Image 생성이 가능하고 이를 한번에 처리할 수 있을 것이다.

// app.js 내용
console.log("Hello Docker!");

Dockerfile 작성

Dockerfile은 Docker에게 소프트웨어 설치 명령을 기술하는 파일이다.

  1. 먼저 베이스 이미지를 기술 (FROM)
  2. 다음으로 코드를 복사
  3. 마지막으로 코드 실행

Dockerfile 사용 가능 기타 키워드

  • ARG : Docker Image를 만들 때 사용되는 변수 지정. 최종 이미지에는 안 들어감
  • ENV : 컨테이너가 실행될 때 사용되는 환경변수. 최종 이미지에 저장됨
  • USER : 컨테이너를 실행할 때 사용할 유저 ID
  • EXPOSE : 서비스 사용 포트번호
  • RUN : 빌드 시 실행되어야하는 명령들이 저장됨 (docker build)
    • EX) RUN apt-get update && apt-get install -y curl
  • ENTRYPOINT : Container가 시작할 때 실행되어야 하는 명령어를 지정하는데 사용 (docker run)
    • CMD 명령어와 굉장히 흡사하지만 ENTRYPOINT의 우선순위가 더 높다는 차이가 있다
    • CMD, ENTRYPOINT 둘다 한 Dockerfile에서 여러번 실행되면 각각 마지막 것만 사용됨
    • 아래의 경우 docker run 실행 시 동일한 결과가 나옴
    • CMD 사용을 권장

CMDENTRYPOINT 둘을 한 Dockerfile에서 같이 지정 가능하다. 같이 사용하는 경우 ENTRYPOINT가 기본 명령이 되고 CMD가 인자를 제공하는 방식으로 작동한다. ENTRYPOINT--entrypoint 옵션을 통해서만 덮어쓰기가 가능하다.

작성된 Dockerfile 예시를 보면서 더 이해해보자.

이 내용을 바탕으로 Dockefile을 만들어보자. 적당한 디렉토리에 app.js파일과 Dockerfile 두개를 작성하자


Docker Image 생성



Docker Container로 실행


Docker Hub로 공유

https://hub.docker.com/ 에서 회원가입을 한 뒤 repository를 만들어보자. 자신의 Account 계정명과 Repository Name을 기억해두자. 현재 예시에서는 hjjwa1234/hello-world-docker

그 후, 터미널로 이동하여 다음 명령을 실행한다.

docker image ls
docker tag hello-world-docker:latest hjjwa1234/hello-world-docker:latest
docker login --username = hjjwa1234
docker push hjjwa1234/hello-world-docker

docker tag hello-world-docker:latest hjjwa1234/hello-world-docker:latest : 현재 이미지의 별칭을 우리가 만든 repository의 이름으로 변경

hjjwa1234/hello-world-docker repository에 우리가 push할 수 있는 권한이 있다는 것을 알리기 위해 docker login --username = hjjwa1234명령어를 실행한 뒤 비밀번호를 입력해주자

push 명령을 실행한 뒤 docker hub 웹페이지에서 우리가 만든 이미지 파일이 잘 올라갔는지 확인해보자


Docker Hub에서 Image 받아 실행

우리가 방금 올린 Image파일을 다른 리눅스 서버에서 다운받아 실행해보자. 이 실습에서는 Docker를 사용하는 사람들에게 4시간동안 서버를 하나 무료로 제공해주는 서비스를 이용해서 테스트를 진행한다. (https://labs.play-with-docker.com/)

docker 계정을 연동해서 접속한 뒤 Alt + Enter(MAC : Option + Enter)로 터미널 윈도우를 최대화 하고 아래 명령을 실행해보자

docker version
docker pull hjjwa1234/hello-world-docker
docker image ls
docker run hjjwa1234/hello-world-docker

만일 로컬에 다운로드 받은 이미지가 없다면 docker hub에서 pull을 알아서 수행하게 된다

  • 만약 실행시 다음과 같은 에러가 난다면 build 할 때 --platform 옵션 사용이 필요하다
WARNING: The requested image's platform 
(linux/arm64/v8) does not match the detected host 
platform (linux/amd64/v3) and no specific platform was 
requested exec /usr/local/bin/docker-entrypoint.sh: exec 
format error


실습2 - Docker에서 Ubuntu 실행하기

이번엔 각자 Docker 위에서 Linux OS중 하나인 Ubuntu를 Docker 컨테이너로 실행해보자.

docker run ubuntu 명령어를 실행하면 현재 Local 환경에서 ubuntu라는 도커 이미지가 없기 때문에 Docker Hub에서 자동으로 ubuntu를 pull하게 된다. (ubuntu는 docker hub에서 official image가 있기 때문)

설치한 뒤 nano를 실행해보면 컨테이너 안의 Linux(Ubuntu) 서버에서 잘 실행되는 걸 확인할 수 있다.


실습3 - Docker로 MySQL 실행하기

터미널을 실행한 뒤 먼저 MySQL docker image를 다운로드 하자 (docker pull mysql/mysql-server:8.0)

다운로드 받은 이미지로 Docker Container를 실행, --name 옵션을 이용해서 읽기 쉬운 이름으로 설정해보자 (docker run --name=mysql_container mysql/mysql-server:8.0)

  • docker exec 명령어를 사용해서 컨테이너를 실행할 수도 있지만 docker run과는 차이점이 있다.
    • docker exec : 이미 실행이 된 컨테이너에 어떠한 명령들을 보내는 작업 (-it 옵션으로 Interactive하게 실행)
    • docker run : 새로운 컨테이너를 실행

다음으론 새로운 터미널을 열어서 MySQL root 계정의 패스워드를 찾자 (docker logs mysql_container 2>&1 | grep GENERATED) 여기서 찾은 PASSWORD는 MySQL에에 접속할 때 필요하기 때문에 복사해두자.

마지막으로 MySQL shell 실행하기 (docker exec -it mysql_container mysql -uroot -p)

docker를 중단하고 싶다면 docker stop ['컨테이너 ID' or '컨테이너 이름'] 명령어를 사용해주자


0개의 댓글