하둡 NameNode 초기화 프로세스 분석

  1. 소스 코드 컴파일 및 환경 준비 하둡의 핵심 컴포넌트인 NameNode의 동작 원리를 이해하기 위해 먼저 소스 코드를 컴파일해야 합니다. 지원되는 버전 범위: Hadoop 2.6 ~ 2.7 (기능 변경이 크지 않음) 컴파일 가이드 참고 링크:

빌드 실패는 흔한 현상이며, protocol 등 필수 종속성만 정확히 설정된다면 문제 없음.

  1. NameNode 메인 진입점 탐색 NameNode 실행 시 가장 먼저 호출되는 진입점은 다음과 같은 정적 메서드입니다:
public static void main(String argv[]) throws Exception {
    if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
        System.out.println("옵션 검증 완료");
        System.exit(0);
    }

    try {
        StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
        NameNode namenode = createNameNode(argv, null);

        if (namenode != null) {
            namenode.join(); // 메인 스레드 대기, 종료 시까지 기다림
        }
    } catch (Throwable e) {
        LOG.error("NameNode 시작 실패", e);
        terminate(1, e);
    }
}

해당 메서드는 hadoop-daemon.sh start namenode 또는 hdfs 명령어로 실행될 때 자동으로 호출되며, 실제 초기화 로직을 createNameNode로 위임합니다.

  1. createNameNode 메서드 내부 구조 분석 createNameNode(argv, conf) 메서드는 다양한 실행 옵션에 따라 처리를 분기합니다:
public static NameNode createNameNode(String argv[], Configuration conf)
        throws IOException {
    LOG.info("NameNode 생성 중: " + Arrays.asList(argv));
    
    if (conf == null) conf = new HdfsConfiguration();
    StartupOption startOpt = parseArguments(argv);
    
    if (startOpt == null) {
        printUsage(System.err);
        return null;
    }

    setStartupOption(conf, startOpt);

    switch (startOpt) {
        case FORMAT:
            boolean aborted = format(conf, startOpt.getForceFormat(), startOpt.getInteractiveFormat());
            terminate(aborted ? 1 : 0);
            return null;

        case GENCLUSTERID:
            System.err.println("클러스터 ID 생성:");
            System.out.println(NNStorage.newClusterID());
            terminate(0);
            return null;

        case FINALIZE:
            System.err.println("FINALIZE 옵션은 더 이상 지원되지 않습니다. 업그레이드 완료 후 `hdfs dfsadmin -finalizeUpgrade` 실행 필요.");
            terminate(1);
            return null;

        case ROLLBACK:
            boolean rollbackAborted = doRollback(conf, true);
            terminate(rollbackAborted ? 1 : 0);
            return null;

        case BOOTSTRAPSTANDBY:
            String[] toolArgs = Arrays.copyOfRange(argv, 1, argv.length);
            int rc = BootstrapStandby.run(toolArgs, conf);
            terminate(rc);
            return null;

        case INITIALIZESHAREDEDITS:
            boolean initAborted = initializeSharedEdits(conf, startOpt.getForceFormat(), startOpt.getInteractiveFormat());
            terminate(initAborted ? 1 : 0);
            return null;

        case BACKUP:
        case CHECKPOINT:
            NamenodeRole role = startOpt.toNodeRole();
            DefaultMetricsSystem.initialize(role.toString().replace(" ", ""));
            return new BackupNode(conf, role);

        case RECOVER:
            NameNode.doRecovery(startOpt, conf);
            return null;

        case METADATAVERSION:
            printMetadataVersion(conf);
            terminate(0);
            return null;

        case UPGRADEONLY:
            DefaultMetricsSystem.initialize("NameNode");
            new NameNode(conf);
            terminate(0);
            return null;

        default:
            DefaultMetricsSystem.initialize("NameNode");
            return new NameNode(conf); // 일반적인 NameNode 시작 경로
    }
}

주요 실행 옵션들:

  • -format: 디스크 저장소 초기화
  • -genclusterid: 새로운 클러스터 식별자 생성
  • -upgradeonly: 업그레이드 모드로 시작
  • -bootstrapstandby: 스탠바이 노드 초기화
  • -rollback: 롤백 작업 수행

정상적인 서비스 시작 시 default 브랜치로 진입하여 new NameNode(conf)를 통해 인스턴스 생성.

  1. NameNode 생성자 내부 로직 생성자는 다음과 같은 단계를 거칩니다:
protected NameNode(Configuration conf, NamenodeRole role) throws IOException {
    this.conf = conf;
    this.role = role;

    // 클라이언트 연결 주소 설정 (fs.defaultFS)
    setClientNamenodeAddress(conf);

    String nsId = getNameServiceId(conf);
    String namenodeId = HAUtil.getNameNodeId(conf, nsId);

    // 고가용성 여부 확인
    this.haEnabled = HAUtil.isHAEnabled(conf, nsId);

    // 초기 상태 결정 (예: Standby)
    state = createHAState(getStartupOption(conf));
    this.allowStaleStandbyReads = HAUtil.shouldAllowStandbyReads(conf);

    // HA 컨텍스트 생성
    this.haContext = createHAContext();

    try {
        initializeGenericKeys(conf, nsId, namenodeId);
        initialize(conf);

        // HA 상태 전환 처리
        try {
            haContext.writeLock();
            state.prepareToEnterState(haContext);
            state.enterState(haContext);
        } finally {
            haContext.writeUnlock();
        }
    } catch (IOException | HadoopIllegalArgumentException e) {
        stop();
        throw e;
    }

    this.started.set(true);
}

핵심 구성 요소:

  • namesystem: 파일 시스템 메타데이터 관리 (블록, 데이터노드 제어)
  • conf: 설정 정보 저장
  • role: 현재 역할 (Active, Standby, Backup 등)
  • state: HA 상태 (Active/Standby 등)
  • haEnabled: 고가용성 활성 여부
  • haContext: HA 관련 컨텍스트
  • rpcServer: RPC 서비스 제공 (외부 클라이언트 및 DataNode 통신)

이 과정에서 중요한 부분은 initialize(conf) 메서드이며, 다음 글에서 이 함수 내부에서 수행되는 작업들 — 메트릭 설정, 보안 인증, 웹 서비스 시작, 메타데이터 로드, RPC 서버 생성, 공통 서비스 시작 — 을 상세히 살펴볼 예정입니다.

태그: hadoop namenode high-availability rpc metadata

5월 24일 16:09에 게시됨