본문으로 건너뛰기

DNS 리졸버 쿼리 로그를 Data Query 서비스를 통해 조회

Object Storage에 저장된 DNS 리졸버 쿼리 로그Data CatalogData Query를 활용해 SQL 기반으로 조회하는 방법을 안내합니다.

기본 정보

시나리오 소개

이 튜토리얼에서는 VPC 내 DNS 리졸버의 쿼리 로그를 Object Storage에 저장한 뒤, Data CatalogData Query를 활용해 SQL 기반으로 로그를 조회하는 방법을 단계별로 안내합니다.

주요 내용은 다음과 같습니다.

  • DNS 리졸버 쿼리 로그를 Object Storage 버킷에 저장 설정
  • Data Catalog에서 로그 버킷과 연결된 데이터베이스 생성
  • Data Query에서 테이블 생성, 파티션 동기화 및 로그 데이터 조회
  • (부록) 활용 쿼리 예시

시작하기 전에

DNS 리졸버 쿼리 로그를 Data Query로 조회하려면 Object Storage 버킷이 먼저 생성되어 있어야 합니다. Object Storage 버킷 생성 가이드를 참고하여 버킷을 준비한 후 아래 단계를 진행하세요.

시작하기

Step 1. DNS 리졸버 쿼리 로그 저장 활성화

DNS 리졸버 쿼리 로깅 설정 가이드를 참고하여 VPC 내 DNS 리졸버에 쿼리 로깅을 설정합니다.

Step 2. Data Catalog 설정

Data Catalog 서비스에서 카탈로그와, 로그가 저장되고 있는 버킷과 연결된 데이터베이스를 생성합니다.

  1. 카카오클라우드 콘솔 > Analytics > Data Catalog 메뉴로 이동합니다.

  2. 카탈로그 메뉴에서 [카탈로그 생성] 버튼을 클릭합니다.

  3. 카탈로그 생성 팝업창에서 아래 정보를 입력한 후, [생성] 버튼을 클릭합니다.

    구분설정값
    유형Standard 카탈로그
    이름카탈로그 이름 (예: catalog)
    VPC 설정카탈로그에 해당하는 네트워크 선택 (VPC-서브넷당 1개만 생성 가능)
  4. 생성한 카탈로그(catalog)를 클릭하여 데이터베이스 목록에서 [데이터베이스 생성] 버튼을 클릭합니다.

  5. 데이터베이스 생성 팝업창에서 아래 정보를 입력한 후, [생성] 버튼을 클릭합니다.

    구분설정값
    유형Standard 데이터베이스
    카탈로그위에서 생성한 catalog 선택
    이름데이터베이스 이름 (예: dns_query)
    경로s3 연결: 체크 / 버킷 이름: Object Storage 버킷 이름 입력 / 디렉터리: KCLogs/kr-central-2
    속성 (선택)-
    설명 (선택)-
    참고

    Object Storage에 DNS 리졸버 쿼리 로그는 지정된 버킷에 아래와 같은 형식으로 적재됩니다.

    {bucket-name}/KCLogs/{region-name}/{year=yyyy/month=mm/day=dd}/{az-name}_{project-id}_{vpc-id}_{logger-id}_{start-time}_{end-time}.log.gz

Step 3. Data Query 조회

Data Catalog에서 생성한 데이터베이스를 통해 테이블을 만들고 파티션을 동기화한 뒤, 로그 데이터를 조회합니다.

  1. 카카오클라우드 콘솔 > Analytics > Data Query > 쿼리 편집기 메뉴로 이동합니다.

  2. 아래와 같이 데이터 원본과 데이터베이스를 선택합니다.

    구분설정값
    데이터 원본Data Catalog에서 생성한 카탈로그 catalog 선택
    데이터베이스Data Catalog에서 생성한 데이터베이스 dns_query 선택
  3. 우측의 쿼리 편집기에서 테이블을 생성합니다.

    테이블 생성
    CREATE TABLE ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME} (
    availability_zone varchar,
    project_id varchar,
    query_class varchar,
    query_name varchar,
    record_type varchar,
    region varchar,
    response_code varchar,
    response_count varchar,
    response_protocol varchar,
    response_size varchar,
    response_time varchar,
    timestamp varchar,
    version varchar,
    vpc_id varchar,
    year varchar,
    month varchar,
    day varchar
    )
    WITH (
    format = 'JSON',
    external_location = 's3a://${BUCKET_NAME}/KCLogs/kr-central-2', -- 현재 kr-central-2 리전만 지원
    partitioned_by = ARRAY['year', 'month', 'day']
    );
    환경변수설명
    CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
    DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
    TABLE_NAME🖌 테이블 이름
    BUCKET_NAME🖌 버킷 이름
  4. 새로운 쿼리 탭을 열어 파티션 동기화 쿼리를 실행합니다. 테이블 생성 쿼리와 같은 탭에서 실행하면 파티션이 정상적으로 인식되지 않을 수 있습니다.

    파티션 동기화
    CALL system.sync_partition_metadata(
    schema_name => '${DATABASE_NAME}',
    table_name => '${TABLE_NAME}',
    mode => 'FULL',
    case_sensitive => true
    );
    환경변수설명
    DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
    TABLE_NAME🖌 테이블 이름
  5. 파티션 동기화 성공 후, 파티션 조회를 진행합니다.

    파티션 조회
    SELECT *
    FROM ${CATALOG_NAME}.${DATABASE_NAME}."${TABLE_NAME}$partitions";
    환경변수설명
    CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
    DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
    TABLE_NAME🖌 테이블 이름

    쿼리 결과로 year, month, day별 파티션이 조회되면 정상입니다.

부록

부록 1. 쿼리 예시

1. 특정 날짜 전체 로그 조회
SELECT *
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}' AND month = '${MONTH}' AND day = '${DAY}';
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
2. query_name별 DNS 쿼리 수 - 상위 10개
SELECT query_name, COUNT(*) AS query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}' AND month = '${MONTH}' AND day = '${DAY}'
GROUP BY query_name
ORDER BY query_count DESC
LIMIT 10;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
3. 시간대별 DNS 쿼리 실패 수
SELECT
SUBSTR("timestamp", 12, 2) AS hour,
COUNT(*) AS failed_query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
AND response_code != 'NOERROR'
GROUP BY SUBSTR("timestamp", 12, 2)
ORDER BY failed_query_count DESC
LIMIT 10;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
4. 실패가 많은 도메인 TOP 5
SELECT
query_name,
COUNT(*) AS failed_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
AND response_code != 'NOERROR'
GROUP BY query_name
ORDER BY failed_count DESC
LIMIT 5;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
5. 응답 시간이 오래 걸린 쿼리 TOP 10 (오류 제외)
SELECT
query_name,
response_time,
response_code,
response_size,
response_protocol
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
AND regexp_like(response_time, '^[0-9]+(\.[0-9]+)?$')
ORDER BY CAST(response_time AS DOUBLE) DESC
LIMIT 10;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
6. 특정 query_name을 비정상적으로 많이 요청한 VPC
-- HAVING COUNT(*) > 1000은 기준값이므로 환경에 맞게 조정 필요
SELECT
vpc_id,
query_name,
COUNT(*) AS query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
GROUP BY vpc_id, query_name
HAVING COUNT(*) > 1000
ORDER BY query_count DESC
LIMIT 10;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
7. 시간대별 DNS 쿼리 사용량 (1일 기준)
SELECT
SUBSTR("timestamp", 12, 2) AS hour,
COUNT(*) AS query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
GROUP BY SUBSTR("timestamp", 12, 2)
ORDER BY hour;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
8. 일별 DNS 쿼리 사용량 (월간 기준)
SELECT
day,
COUNT(*) AS query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
GROUP BY day
ORDER BY day;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
9. 응답 코드별 요청 분포
SELECT
response_code,
COUNT(*) AS request_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
GROUP BY response_code
ORDER BY request_count DESC;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
10. 의심스러운 쿼리 패턴 탐지
-- query_name에 DROP, DELETE, UNION 키워드가 포함된 의심 쿼리 집계
SELECT
query_name,
COUNT(*) AS occurrence
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
AND (query_name LIKE '%DROP%' OR query_name LIKE '%DELETE%' OR query_name LIKE '%UNION%')
GROUP BY query_name
ORDER BY occurrence DESC;

-- record_type이 TXT이고 비정상적으로 긴 query_name 확인 (기준값 100자는 환경에 맞게 조정 필요)
SELECT
query_name,
response_size,
response_code,
LENGTH(query_name) AS query_length
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
AND record_type = 'TXT'
AND LENGTH(query_name) > 100
ORDER BY query_length DESC
LIMIT 20;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30
11. 쿼리 유형(record_type) 분포
SELECT
record_type,
COUNT(*) AS query_count
FROM ${CATALOG_NAME}.${DATABASE_NAME}.${TABLE_NAME}
WHERE year = '${YEAR}'
AND month = '${MONTH}'
AND day = '${DAY}'
GROUP BY record_type
ORDER BY query_count DESC;
환경변수설명
CATALOG_NAME🖌 Data Catalog에서 생성한 카탈로그 이름
DATABASE_NAME🖌 Data Catalog에서 생성한 데이터베이스 이름
TABLE_NAME🖌 테이블 이름
YEAR🖌 연도, 예: 2026
MONTH🖌 월, 예: 04
DAY🖌 일, 예: 30