대표적인 파일 적재 형태

데이터를 적재하는 파일 형식은 대표적으로 2가지 방법이 있다.
Data in Row Format (행 기반), columnar file format (열 기반) 2가지 방법이 있다( 식견이 좁아 이 2개만 아는 것 일 수 있다.)

name email phoneNum
tom tom@gmail.com 010-0000-0001
justin justin@gmail.com 010-0000-0002
kim kim@gmail.com 010-0000-0003
  • 위와 같은 테이블이 존재한다 가정하고 아래 설명을 하겠습니다.

Data in Row Format (행 기반)

많은 사람들이 사용하는 Mysql 등 Row orented databased 에서 사용하는 파일 형태이다.

  • 간단하게 사진으로 보여주면 아래와 같은 형식으로 계속 옆으로 붙는다고 생각하면 된다.
    Pasted image 20231206140619.png
# CSV
name,email,phonNum
tom    , tom@gmail.com    , 010-0000-0001
justin , justin@gmail.com , 010-0000-0002
kim    , kim@gmail.com    , 010-0000-0003

# TSV
# 실제로는 tab으로 구분하지만, 보기 편하기 위해 줄을 맞 
name     email              phonNum
tom      tom@gmail.com      010-0000-0001
justin   justin@gmail.com   010-0000-0002
kim      kim@gmail.com      010-0000-0003

# JSON
{
	{name : tom    , email : tom@gmail.com    , phoneNum : 010-0000-0001} ,
	{name : justin , email : justin@gmail.com , phoneNum : 010-0000-0002} ,
	{name : kim    , email : kim@gmail.com    , phoneNum : 010-0000-0003}
}

위와 같이 본다면 CSV는 \n이 header가 되고, Json은 ,가 header가 된다고 봐도 될 것 같다.

  • 장점
    • 삽입 삭제에 용이하다
      • 그럼으로 쓰기 집약적인 작업에 적합하
      • 위에 코드와 같이 단순히 뒤에 붙여 넣으면 되기 때문 !
    • 모든 파일에 접근할 때 columnar보다 사용하기 좋다.
  • 단점
    • 데이터 집합 조회 시 성능 저하
      • 조회할 때 매번 모든 row를 읽어서 처리하기 때문

columnar file format (열 기반)

Spark ,Hive, 등에서 사용되는 파일 형식이다.
즉, 보통 대용량 데이터 처리할 때 많이 사용한다. (이때만 사용하는 것은 아니지만…?)
대표적으로 Parquet, ORC…등이 존재한다.
csv처럼 파일을 열어서 읽어서 보려고 했는데, 제대로 오지 않고, 검색하면 어떤 방식으로 적재가 되는 도식화된 사진만 있어
임의의 모양으로 예시를 들겠다.

name    |type-String|len-10 : tom          , justin          , kim
email   |type-String|len-10 : tom@gmail.com, justin@gmail.com, kim@gmail.com
phonNum |type-String|len-10 : 010-0000-0001, 010-0000-0002   , 010-0000-0003

대충 예시를 들면 위와 같다.
즉 열 기반이기 때문에 매번 Header를 추가하지 않고, 구분자만 추가하여 header에 사용되는 메모리를 절약할 수 있다.

  • 장점
    • 특정 칼럼만 읽어 처리하여 읽기 성능이 좋다
      • 예를 들어 select name,email from userTB where name = 'tom' 와 같은 조건의 쿼리문이 있다 예를 들면
      • where name = 'tom' 조건에 의해 name을 갖고 있는 칼럼만 읽어서 해당 인덱스를 가져온다
      • select name,email 이기 때문에 해당 name,email칼럼만 읽어서 조회하면 된다
      • 그로 인해 phonNum이라는 칼럼은 읽어드릴 필요가 없어진다. 그럼으로 보다 우수한 조회 성능을 갖을 수 있다
    • 압축 시 보다 좋은 성능을 보여준다
      • 100%는 아니지만, 같은 칼럼은 비슷한 값이 들어있기 때문에 보통은 최적화가 가능하다
      • 그럼으로 대용량 데이터 처리 할 때 용이하다
  • 단점
    • 데이터 삽입 시 데이터의 마지막 위치를 파악해야 한다.
    • 레코드 재 구성 시 느리다.
      • 각 행에서 세그먼트를 하나씩 읽어야 하기 때문
    • 파일을 읽기 전에 메타 정보를 읽고 행을 재 구성하기 때문에 CPU/ RAM등 리소스 소모가 더 크다

결론

결론적으로 Row Oriented와 Col Oriented는 아래와 같은 차이를 보인다.
어떤 것이 100%더 좋다는 없다고 생각한다.
단지 어떤 상황에 어떤 것이 더 적합한 것만 있는 것 같다.
Pasted image 20231206155149.png

[ Reference ]


개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

SparkSql?

Spark에서 RDD는 값은 표현하지만, Schema에 대한 방법이 없는데 이것을 대체하는 방법이라고 보면된다.
간단히 말하면 Spark에서 Hive 혹은 Mysql과 같은 RDB의 SQL을 사용할 수 있게 만들어 주는 것이라고 보면 쉽다

  • SQL작성시 ANSI-SQL 및 Hive-QL문법을 사용가능하다.
    • Hive-QL문법을 대다수 지원하지만, 모두 지원하는 것은 아니다 예외 항목이 있으니 홈페이지를 확인해보는 것이 좋다
    • 2.0버전부터 SQL:2003표준을 지원한다

방식

기본적으로 Spark에서 데이터를 다루는 방법은 SQL를 사용하는것 혹은 DataSet API을 사용하는 방법이 있다.

  • Dataset

주요 프로그래밍 구성요소

SparkSession

  • RDD 사용시 SparkContext를 사용한것 처럼 DataSet을 사용할 때 SparkSession을 사용한다
  • UDF를 등록하기 위한 목적으로 사용
  • 생성 기본 형태
    • val ss = SparkSession.builder().config(conf).getOrCreate()
  • Spark 2.0부터 사용됨 기존에는SQLContext, HiveContext를 구분하여 사용했다
    • 2.0버전부터는 Hive를 기본 지원하기 때문에 SparkSession을 사용하면 된다
      • 지원을 한다는 것 이지 필수적으로 hive를 필수로 사용해야 하는 것은 아니다
      • 만약 Hive를 사용하고 Hive를 사용하고 싶다면, hive-site.xml, core-site.xml, hdfs-site.xml을 spark의 conf 디렉토리에 추가하면 된다
        • 저게 싫다면 spark-submit의 경우 --file 에 추가해도 된다.
      • .enableHiveSupport() 를 추가해주면 된다.

DataSet

  • 분산 오브젝트 컬렉션에 대한 프로그래밍 모델
    - Transformation, Action 연산을 포함하고 있다.
    • Spark 1.6 버전부터 DataFrame이라는 API를 사용함
    • Spark 2.0 버전부터 Dataset이 Spark SQL의 Main API로 지정
      • 이때부터 DataFrame은 미지원
        • Scala에서는 Type Alias를 허용 하여 하위 호환 유지가 됨.
          • DataSet을 따라 들어가보면 아래와 같이 구성되어 있다.
          • def toDF(): DataFrame = new Dataset[Row](sparkSession, queryExecution, RowEncoder(schema))
          • 즉 DataFrame이 있긴 하지만, Dataset으로 대체 됐다고 보면 된다.

DataFrame

  1. DataSet이 나오기 전에 사용하던 DataSet
  2. 맞는 표현은 아니지만, 간단한 비유로는 Row 와 Column구조를 가진 RDD
    1. RDD와 같이 분산 데이터 처리를 위한것.
    2. RDD : value에 초점을 맞춤
    3. DataFrame : value + schema를 다룸
  3. Row타입을 사용한다

DataFrameReader / DataFrameWriter

  1. DataFrameReader
    1. SparkSession.read를 이용하여 메소드 접근
      1. 데이터 소스를 지정하여 데이터를 읽어 데이터 프레임을 생성해준다
  2. DataFrameWriter
    1. Dataset.write를 이용하여 특정 데이터 소스로 적재한다.

Row, Column

  1. SQL의 Row,Column과 같은 표현 모델이라고 생각하면 된다.

functions

  1. 데이터 처리시 사용하는 API
    1. select, sum, filter 등 여러가지 메소드들이 존재한다
      1. 공식 docs 에서 확인할 수 있다

StructType / StructField

  1. 스키마 정보를 정의하는 API
  2. StructType (List(StructField,StructField,StructField)) 와 같은 형식으로 생성한다(Scala)

GroupedData, GroupedDataSetP

  1. groupBy 메소드 등에 이해 군집 연산을 수행할 때 사용

개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

'프로그래밍 및 IT > Spark' 카테고리의 다른 글

spark-Setting  (0) 2023.11.28
spark-scheduling  (0) 2023.11.28
WordCount  (0) 2023.10.13
RDD Action  (0) 2023.10.13
RDD Transformation  (0) 2023.10.13

Spark Setting

Spark를 실행할 때 여러가지 설정을 할 수 있다.
상세한 정보들은 너무 많기 때문에 help 명령어를 사용하여 어떤 설정을 할 수 있는지 확인해 볼 수 있다.
Spark 공식홈페이지에서 본인이 사용하는 Spark 버전에 따라 확인해봐도 된다.

간단 예시 spark-submit 예시

spark-submit \
  --class {실행할 파일의 클래스} \
  --name {어블리케이션 명} \
  --master {클러스터 메니져 정보} \
  --deploy-mode {디플로이 모드} \
  --files {추가할 파일} \
  --jars {추가할 Jars 파일들} \
  --conf {추가할 설정} \  
  --driver-cores {드라이버 코어수} \
  --driver-memory {드라이버 메모리} \
  --executor-cores {익세큐터 코어수} \
  --executor-memory {익세큐터 메모리} \
  --num-executors {익세큐터 수} \
  {실행할 파일 경로}

위와 같이 Spark Submit할 때 해도 되지만, spark-defaults.conf에 미리 설정하여 사용해도 된다.
Spark Submit혹은 spark-defaults.conf에 추가한 값들이 모여 Spark Properties에 반영된다.
설정 우선순위 : code -> spark-submit -> spark-defaults.conf

[ Reference ]


개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

'프로그래밍 및 IT > Spark' 카테고리의 다른 글

SparkSql  (0) 2023.11.28
spark-scheduling  (0) 2023.11.28
WordCount  (0) 2023.10.13
RDD Action  (0) 2023.10.13
RDD Transformation  (0) 2023.10.13

Spark-Scheduling?

개인적으로 Spark를 사용하는 이유는 cluster-mode를 사용하기 위해라고 생각한다
Yarn등을 이용하여 각 Driver,Excutor에게 core,memory등을 할당 하는데, 많이 할당한다고 항상 좋은 결과를 보는 것은 아니다
어디서 본지 기억이 나지 않지만, core는 1~5개, memory는 4gb이상을 사용하는 것이 좋다고 봤었다.

Cluster Scheduling

Static resource allocation

정적(고정) 자원 할당

  • 명칭 그대로 최초 할당한 그대로 어플리케이션이 종료될 때 까지 자원을 점유하는 방식
  • Pipe line에 따라 데이터를 처리하여 필요한 자원 사전 계산 후 할당하면 좋은 방식이다.

Dynamic resource allocation

동적 자원 할당

  • 자원을 필요에 따라 동적으로 할당하는 방식

    • Spark-Shell , 혹은 web기반으로 동작하면 항상 많은 자원을 할당받아 자원 낭비가 발생할 수 있는 상황에 합리적인 선택
  • 공통

    • spark.dynamicAllocation.enabled true 설정을 해야한다
  • Standalone

    • spark.shuffle.service.enabled true
  • Yarn

    • 모든 node manager에 spark-{version}-yarn-shuffle.jar를 class path등록해야 한다
    • yarn-site.xml에 아래와 같은 설정이 있어야 한다.(안 되어 있으면 설정 후 재시작)
    	<property>
            <name>yarn.nodemanager.aux-services</name>
            <value>mapreduce_shuffle,spark_shuffle</value>
        </property>
        <property>
            <name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
            <value>org.apache.spark.network.yarn.YarnShuffleService</value>
        </property>
        <property>
            <name>spark.shuffle.service.enabled</name>
            <value>true</value>
        </property>
    
  • yarn에서 별도 Shuffle Service를 실행시킨 이유는 동작 도중 Executor가 제거될 수 있기 때문

    • Mapper에 저장된 데이터Reducer단계에 Network를 통해 데이터를 읽어 가는데 이때 Executor가 삭제되면 해당 데이터는 삭제
    • 그러한 문제가 발생하지 않기 위해 Shuffle data를 따로 관리 할 수 있는 Shuffle Process를 설정하는 것

Application Scheduling

Spark Context는 기본적으로 Multi-Thread 방식으로 실행된다.
그로인하여 N+1개의 Action Function을 동시 실행해도 문제가 발생하지 않는다.

  • Scheduling으로 FIFO방식으로 구동 된다
    • 후속 작업은 이전 작업이 끝날 때 까지 대기해야한다
    • Fair scheduler 방식도 가능하다
      • Round robin
    • 설정
      • Code 단 : conf.set("spark.scheduler.mode", "Fair")
      • spark-default.conf에서 설정해도 된다.

fairscheduler.xml에 poll 설정을 하여 원하는 Pool이용하는 코드를 작성할 수 있다

<allocations>
  <pool name="production">
    <schedulingMode>FAIR</schedulingMode>
    <weight>1</weight>
    <minShare>2</minShare>
  </pool>
  <pool name="test">
    <schedulingMode>FIFO</schedulingMode>
    <weight>2</weight>
    <minShare>3</minShare>
  </pool>
</allocations>
  • fairscheduler.xml에 기본적으로 작성되어있는 것을 가져왔다.
    • weight : 우선순위
    • minShare : Pool의 최초 CPU Core
val conf = new SparkConf()  
  .setAppName("application_name")  
  .setMaster("local[*]")  
  .set("spark.local.ip", "localhost")  
  .set("spark.driver.host", "localhost")  
  // .set("spark.scheduler.mode", "Fair")  //이와 같이 특정 scheduler 사용가능
  .set("spark.scheduler.allocation.file" ,"file path") // 
val sc = new SparkContext(conf)
sc.setLocalProperty("spark.scheduler.pool", "production") // pool설정  
sc.setLocalProperty("spark.scheduler.pool", null) // 기본 pool설정
  • 위와 같이 사용하면 scheduler mode : FAIR , 최소 CPU core : 2
  • 이와 같이 N+1명의 사용자가 접근하면 사용자에 따른 자원 할당을 할 수 있다

[ Reference ]


개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

'프로그래밍 및 IT > Spark' 카테고리의 다른 글

SparkSql  (0) 2023.11.28
spark-Setting  (0) 2023.11.28
WordCount  (0) 2023.10.13
RDD Action  (0) 2023.10.13
RDD Transformation  (0) 2023.10.13

Docker Compose?

간단히 말하면 Dockerfile을 이용하여 생성한 Docker Image를 Services로 묶어 구성을 작성한 Yaml파일을 관리 도구
추가적으로 작성하자면, Dockerfile를 이용하여 구축 시 Docker Run에 상당히 많은 옵션값들을 작성해야 한다
Docker Compose를 이용하면 해당 옵션들을 작성 후 간단한 명령줄로 Docker Container를 생성 할 수 있다

요소들

설정할 수 있는 것들이 너무 많아 모든 것을 정리하지는 않음.
e.g blkio_config, cpu, mem

Expose

컨테이너 간 Port 노출
Host에게 노출되는 것은 아님

port

Host에게 Port 노출

Network

Container 에서 사용할 Docker Networks 지정

Network_mode

Container의 Network 모드 설정

None : Network 미사용
Host : Host의 Network 사용
service:{Name} : 특정 Service에만 접근 허용

CheatSheet


version: "3.7" # compose file format설정.

servies : # services 정의, 클러스터 정의라고 봐도 무관할듯
	container_name : # 생성할 container 명
		image : Image_Name #사용할 Image
        attach : true # 서비스 로그 수집 여부 
		build  :  # 아래 옵션을안 쓰고 경로를 작성해도 무관
		  context: . # Docker file 경로
	      dockerfile : Dockerfile # Docker file 명
		container_name: my-web-container # 컨테이너에 부여할 이름
		labels:
	      # 방식 1
		  com.example.description: "Accounting webapp"
		  # 방식 2
		  - "com.example.description=Accounting webapp"
		# 환경 변수
		env_file: # 환경 변수 파일 추가
			- a.env
			- b.env
		environment: #환경변수 추가
			# 방식 1
			EVN_VAL1: VAL1
			EVN_VAL2: VAL2
			EVN_VAL3
			# 방식 2
			- EVN_VAL1=VAL1
			- EVN_VAL2=VAL2
			- EVN_VAL3
		restart : unless-stop # 재시작 여부
		
		# 실행
		command : # dockerfile CMD Overrides 
			# 방법 1
			sh -c "/script1.sh &&/script2.sh"
			
			# 방법 2
			sh -c "
			/script1.sh &&
			/script2.sh
			"
			
			# 방법 3
			- sh
		    - -c
			- |
			  /script1.sh
			  /script2.sh
		entrypoint : # dockerfile entrypoint Overrides 
  
		# 종속
		depends_on : # 종속관계 추가
			containerName : # 종속될 컨테이너
				restart : true/false # 종속된 컨테이너 업데이트 시 재 시작
				condition : service_healthy # 상태
		healthcheck:
		  test: ["CMD", "curl", "-f", "http://localhost"]
		  interval: 1m30s
		  timeout: 10s
		  retries: 3
		  start_period: 40s
		  start_interval: 5s
		# 네트워크
		expose :
			"8080"
		port :
			8080:8080
		network_mode : "host"
		networks:
	      - bapa-network
	      - bapa2-network

Network

Host 에서 생성한 네트워크 혹은 Docker 내부의 네트워크를 사용하기 위해 사용

services :
	....

networks :
	networName : 
		driver : driver Name
		driver_opts:
			driver_val : driver_val # Driver의 옵션 추가
		external : true #외부 네트워크 사용
		internal : false # 외부와 격리된 독립 네트워크
		label : metadata # 해당 네트워크의 메타 정보 저장
		name : network name # 특수 문자를 포함한 네트워크 잠조 시 사용
		ipam :
			driver : ipam DriverName
			config :
			  - subnet: 172.28.0.0/16
	          ip_range: 172.28.5.0/24
	          gateway: 172.28.5.254
	          aux_addresses:
	            host1: 172.28.1.5
		    options :
			    driver_val1: driver_val

Volumes

docker에 추가 volumes을 사용하고 싶으면 아래와 같이 정의하여 사용할 수 있다.

services :
	....
volumes:
	volumes_name : # 볼륨 명
		name : "volumes_name" # volume의 다른 이름을 붙여 사용하고 싶다면 사
		driver : drive_name # 사용할 docker volume driver
		driver_opts :  # docker volume driver 설정, key-value 로 사
			key : value
		    ...
			
	    external: true/false # 외부 volume 여무, 즉 기존에 생성한 volumes를 사용 할 것인가 ?
	    labels : # key-values로 구성된 volume의 메타데이터 설정
		    key : value
		    ...

그 외

# Extension
# 간단히 생각하면 변수 선언이라고 보면된다.
# x-{name} : &{name}
# Superset 예시
x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset:${TAG:-latest-dev}
x-superset-common: &superset-common
  image: apachesuperset.docker.scarf.sh/apache/superset:${TAG:-latest-dev}
  depends_on:
    - db
    - redis

# Fragments
# 간단히 말하면 Extension을 사용하는 것.
# $를 이용하여 생성, *를 이용하여 사용
services :
  superset :
    <<: *superset-common # superset-common에 선언된것을 모두 사용하겠다는 의미
    image: *superset-image # 이와같이 1개만 가져와서 사용할 수 있다.

# interpolation
# 변수를 사용할 대 Default값을 설정하는 느낌이다.
# Trino 이미지 가져오는 예시
# TRINO_IMAGE_NAME 이 변수에 값이있으면 TRINO_IMAGE_NAME를 사용하고 없으면 trinodb/trino:433를 사용하겠다
image: ${TRINO_IMAGE_NAME:-trinodb/trino:433}

# 아래와 같이 작성하면 TRINO_IMAGE_NAME값이 없으면 Compose가 실패한다
image: $$TRINO_IMAGE_NAME

[ Reference ]


개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

'프로그래밍 및 IT > docker' 카테고리의 다른 글

docker  (0) 2023.11.22

Docker?

서비스를 패키징 및 배포를 하는데 유용한 오픈소스 프로그램
즉 , 컨테이너를 모듈식 가상 머신처럼 사용 가능하게 만들어주는 프로그램
카카오 컨퍼런스 이것을 보면 너무 컨테이너 관련 설명을 잘해주신다

기존 Linux Container과 차이점

Pasted image 20231122151223.png
Red Hat

위 사진을 보면 LXC는 간단히 설명하면 VM과 같은 머신을 이용하여 가상 환경을 띄우는 것 인데,
VM은 각 환경마다 OS 를 구축을 해야하지만, Docker은 Docker Engine 상에 프로세스를 띄워 사용하는 방식이기 때문에 보다 가벼워 졌다고 볼 수 있다.
과거에 Vagrant로 Hadoop EchoSystem을 구축해본 경험이 있는데 상당이 느리게 구축하였다, 하지만 Docker를 이용하니 상당히 빠른시간에 Build 및 구축이되서 상당히 당황스러웠다.

장점

  • 위와 같이 각각의 App, Service? 단위로 독립된 공간에 존재하여 서로 충돌이 발생하지 않게 구성하기 용이하다.
    • 마이크로 아키텍쳐 구성을 하기 용이하다.
  • 배포 시 이미지를 그대로 운영 서버에 배포하면 된다.
  • 같은 이미지를 사용하면, 같은 환경이라 봐도 무방하기 때문에 환경에 대한 제약이 적어져 배포, 확장이 용이해진다.

기본 준비 사항

window 기본적으로 Docker는 linux kernel 기반으로 동작을 하는 것으로 알고 있다.
Window에서 wsl 설정을 하여 우분투 환경을 구축 하고 docker desktop을 설치 혹은 docker를 설치하여 사용할 수 있다.
Docker Doc 혹은 MS Doc에 들어가서 따라하면 쉽게 할 수 있을 것 이다.

Docker 를 설치하였다면, Dockerfile혹은 docker-compose을 이용하여 이미지 빌드, 배포를 하여 사용할 수 있다.

[ Reference ]


개인적으로 공부한 내용 포스팅 중
잘못된 정보는 지적해주시면 좋겠습니다!

'프로그래밍 및 IT > docker' 카테고리의 다른 글

docker-compose  (1) 2023.11.27

+ Recent posts