Summary
3개의 DataNode
와 1개의 NameNode
가 각각의 Container로 구성된 Hadoop(v.2.9.2) Docker 환경을 구성한다.
각 Hadoop server가 실행 중인 Container 간에 Data 공유가 필요하기 때문에 volume
을 이용하여 Data를 관리한다.
사실 bind mounts
, tmpfs
방식으로 Data를 안전하게 존속시킬 수 있는 방식이 있으나, 특별한 경우를 제외하고 volume
을 사용하는 것을 추천한다.
Container의 Writable Layer에서 Data를 저장할 수 있지만, 아래와 같은 문제점으로 별도로 Data를 관리하는 방법을 추천한다.
1. Container가 삭제되면 Data 삭제
2. 다른 프로세스에서 Container에 저장된 Data를 사용하기 어려움
3. Data를 다른 곳으로 쉽게 옮길 수 없음
: Container의 Writable Layer에는 Container가 실행 중인 Host Machine과 밀접하게 연결되어 있기 때문에..
4. File System은 Host File System에 직접 쓰는 data volume 보다 성능이 떨어짐
: Container의 Writable Layer에 Data를 저장하기 위해서는 File System을 관리하는 Storage Driver가 필요하고,
Storage Driver는 Linux 커널을 사용하여 공용 File System을 제공
Tree structures
- hadoop의 기본 환경 구성하는 base image
build
, base image 를 기반으로 namenode, datanode의 Dockerfile을 작성하여 직접 image를build
,docker-compose
를 활용해 배포하는 방법을 활용한다.
├── base
│ ├── Dockerfile
│ └── core-site.xml
├── datanode
│ ├── Dockerfile
│ ├── hdfs-site.xml
│ └── start.sh
├── docker-compose.xml
└── namenode
├── Dockerfile
├── hdfs-site.xml
└── start.sh
Base
1. Base Dockerfile
# 기반이 되는 이미지를 설정. ubuntu 18 기반인 zulu의 openjdk 8버전을 선택
FROM azul/zulu-openjdk:8
RUN sed -i 's/archive.ubuntu.com/ftp.neowiz.com/g' /etc/apt/sources.list
# 바이너리를 내려받기 위해 설치
RUN apt-get update && apt-get install -y curl openssh-server wget vim
ENV HADOOP_VERSION=2.9.2
ENV HADOOP_URL=http://mirror.apache-kr.org/hadoop/common/hadoop-$HADOOP_VERSION/hadoop-$HADOOP_VERSION.tar.gz
# SSH 관련 설정
RUN mkdir /var/run/sshd
RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config
# Hadoop 내려받고 /opt/hadoop에 압축 해제
RUN curl -fSL "$HADOOP_URL" -o /tmp/hadoop.tar.gz \
&& tar -xvf /tmp/hadoop.tar.gz -C /opt/ \
&& rm /tmp/hadoop.tar.gz
# 데이터 디렉토리 생성 및 설정 폴더의 심볼릭 링크 생성
RUN ln -s /opt/hadoop-$HADOOP_VERSION /opt/hadoop \
&& mkdir /opt/hadoop/dfs \
&& ln -s /opt/hadoop-$HADOOP_VERSION/etc/hadoop /etc/hadoop \
&& rm -rf /opt/hadoop/share/doc
# 로컬의 core-site.xml 파일을 복제
ADD core-site.xml /etc/hadoop/
# 실행 환경에 필요한 환경 변수 등록
ENV HADOOP_PREFIX /opt/hadoop
ENV HADOOP_CONF_DIR /etc/hadoop
ENV PATH $HADOOP_PREFIX/bin/:$PATH
ENV JAVA_HOME /usr/lib/jvm/zulu-8-amd64
EXPOSE 22
CMD /usr/sbin/sshd -D
2. Base core-site.xml
- fs.defaultFS : Namenode의 위치를 찾는 설정으로 읽기 / 쓰기 요청 할 때 사용하는 항목
- URI hostname : Namenode container의 host name 지정
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://namenode:9000/</value>
<description>NameNode URI
</description>
</property>
</configuration>
3. Build
docker build -t hadoop-base:2.9.2 .
Namenode
- base image를 기반으로 Namenode Container 빌드하기 위한 환경 구성
- Namenode 용 hdfs-site.xml
- FsImage, EditLog 저장하기 위한 로컬 파일 시스템 경로
1. Dockerfile
# 로컬에 생성한 base 이미지
FROM hadoop-base:2.9.2
# NameNode Web UI 응답 여부를 통해 Healthcheck
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:50070/ || exit 1
# 설정 파일 복제
ADD hdfs-site.xml /etc/hadoop/
# FsImage, EditLog 파일 경로를 volume으로 연결
RUN mkdir /opt/hadoop/dfs/name
VOLUME /opt/hadoop/dfs/name
# 실행 스크립트 복제
ADD start.sh /start.sh
RUN chmod a+x /start.sh
# NameNode의 HTTP, IPC 포트 노출
EXPOSE 50070 9000
# 시작 명령어 등록
CMD ["/start.sh", "/opt/hadoop/dfs/name", "/usr/sbin/sshd"]
2. Namenode hdfs-site.xml
- dfs.namenode.name.dir : FSImage / EditLog 파일을 저장하는 경로
- dfs.blocksize : HDFS 파일 블록의 크기
- 이외의 설정은하둡 문서에서 확인
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///opt/hadoop/dfs/name</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>10485760</value>
</property>
<property>
<name>dfs.client.use.datanode.hostname</name>
<value>true</value>
</property>
<property>
<name>dfs.namenode.rpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.servicerpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.http-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.https-bind-host</name>
<value>0.0.0.0</value>
</property>
</configuration>
3. Namenode start.sh
- 시작 스크립트인 Start.sh에서는 Namenode의 네임스페이스가 포맷되었는지 확인하고, 포맷되기 전이라면 구동전 포맷을 먼저 진행
#!/bin/bash
# 네임스페이스 디렉토리를 입력받아서
NAME_DIR=$1
echo $NAME_DIR
# 비어있지 않다면 이미 포맷된 것이므로 건너뛰고
if [ "$(ls -A $NAME_DIR)" ]; then
echo "NameNode is already formatted."
# 비어있다면 포맷을 진행
else
echo "Format NameNode."
$HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR namenode -format
fi
# NameNode 기동
$HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR namenode
4. Build
sudo docker build -t hadoop-basename:2.9.2 .
DataNode
- 파일 블록을 저장하는 로컬 파일 시스템 경로
- Datanode용 hdfs-site.xml 설정
1. Dockerfile
FROM hadoop-base:2.9.2
# DataNode Web UI 응답 여부를 통해 Healthcheck
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:50075/ || exit 1
RUN mkdir /opt/hadoop/dfs/data
VOLUME /opt/hadoop/dfs/data
ADD start.sh /start.sh
RUN chmod a+x /start.sh
# WebUI, 데이터전송
EXPOSE 50075 50010
CMD ["/start.sh", "/usr/sbin/sshd"]
2. Datanode hdfs-site.xml
<configuration>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///opt/hadoop/dfs/data</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>10485760</value>
</property>
<property>
<name>dfs.datanode.use.datanode.hostname</name>
<value>true</value>
</property>
</configuration>
3. Datanode start.sh
#!/bin/sh
$HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR datanode
4. Build
sudo docker build -t hadoop-datanode:2.9.2 .
Docker-compose
version: "3.4"
# 이미지와 네트워크 정보에 대한 base service를 지정
x-datanode_base: &datanode_base
image: hadoop-datanode:2.9.2
networks:
- bridge
services:
namenode:
image: hadoop-namenode:2.9.2
container_name: namenode
hostname: namenode
ports:
- 50070:50070
#- 9870:9870
- 9000:9000
volumes:
- /data/hadoop_data/name:/opt/hadoop/dfs/name
- /tmp:/tmp
networks:
- bridge
datanode01:
<<: *datanode_base
container_name: datanode01
hostname: datanode01
volumes:
- /data/hadoop_data/name:/opt/hadoop/dfs/data
datanode02:
<<: *datanode_base
container_name: datanode02
hostname: datanode02
volumes:
- /data/hadoop_data/name:/opt/hadoop/dfs/data
datanode03:
<<: *datanode_base
container_name: datanode03
hostname: datanode03
volumes:
- /data/hadoop_data/name:/opt/hadoop/dfs/data
volumes:
namenode:
datanode01:
datanode02:
datanode03:
start
sudo docker-compose -f docker-compose.yml up -d
Hadoop 3.0.0-Alpha 1 이후 변경된 포트
- Hadoop 2.x 버전에서 Hadoop 3.0.0-Alpha 1 으로 버전 업이 되면서 포트들이 많이 변경 되었는데, 참고하면 될 것 같다.
구분 | Hadoop 2.x | Hadoop 3.x |
---|---|---|
Namenode | 50470 | 9871 |
50070 | 9870 | |
8020 | 9820 | |
Secondary NN | 50091 | 9869 |
50090 | 9868 | |
Datanode | 50020 | 9867 |
50475 | 9865 | |
50075 | 9864 |
해당 파일들은 hadoop-docker 에서 확인 가능