DevOps

[Docker Compose] Kong Gateway(3.7.X) Local 개발 환경 구축

nineDeveloper 2024. 7. 15. 00:15
728x90

🚦 Kong Gateway 및 PostgreSQL 구성 Docker Compose 스크립트


📌 Spec

📌 config/kong.yaml

# https://docs.konghq.com/gateway/latest/install/docker/#prepare-your-configuration-file
# a very minimal declarative config file
_format_version: "3.0"
_transform: true

📌 docker-compose.yml

x-kong-config: &kong-env
  KONG_DATABASE: postgres # # kong에서 사용하는 Datasoure
  KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong} # postgresql 데이터베이스
  KONG_PG_HOST: postgres   # postgresql host
  KONG_PG_PORT: 5432                  # postgresql 포트
  KONG_PG_USER: ${POSTGRES_USER:-kong} # postgresql 사용자
  KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password # postgresql 비밀번호
services:
  postgres:
    container_name: postgres
    image: postgres:16
    environment:
      POSTGRES_DB: ${KONG_PG_DATABASE:-kong}
      POSTGRES_USER: ${KONG_PG_USER:-kong}
      POSTGRES_PASSWORD_FILE: /run/secrets/kong_postgres_password
      TZ: Asia/Seoul
    ports:
      - "5432:5432"
    volumes:
      - type: bind
        source: ${PWD}/postgres_data
        target: /var/lib/postgresql/data
    secrets:
      - kong_postgres_password
    healthcheck:
      test:
        [
          "CMD",
          "pg_isready",
          "-d",
          "${KONG_PG_DATABASE:-kong}",
          "-U",
          "${KONG_PG_USER:-kong}"
        ]
      interval: 30s
      timeout: 30s
      retries: 3
    restart: on-failure
    stdin_open: true
    tty: true
    privileged: true
    user: ${UID}
    networks:
      - kong-net
  pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4
    ports:
      - "5555:80"
    volumes:
      - ${PWD}/pgadmin:/var/lib/pgadmin:rw
    environment:
      PGADMIN_DEFAULT_EMAIL: example@pgadmin.com
      PGADMIN_DEFAULT_PASSWORD: pgadmin
      TZ: Asia/Seoul
    restart: unless-stopped
    depends_on:
      - postgres
    networks:
      - kong-net
  kong-migrations:
    image: kong:3.7
    command: kong migrations bootstrap
    environment:
      <<: *kong-env
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    depends_on:
      - postgres
  kong-migrations-up:
    image: kong:3.7
    command: kong migrations up && kong migrations finish
    environment:
      <<: *kong-env
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    depends_on:
      - kong-migrations
  kong-gateway:
    # image: kong:3.7
    build:
      context: .
    networks:
      - kong-net
    environment:
      <<: *kong-env
      TZ: Asia/Seoul
      KONG_PROXY_ACCESS_LOG: /dev/stdout # kong log 출력
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:${KONG_ADMIN_PORT:-8001}, ${LOCAL_IP}:8001, 0.0.0.0:8444 ssl
      KONG_PROXY_LISTEN: "0.0.0.0:8000 reuseport backlog=16384, 0.0.0.0:8443 http2 ssl reuseport backlog=16384" # proxy port 변경
      # KONG_PROXY_LISTEN: "0.0.0.0:8000 proxy_protocol, 0.0.0.0:8443 http2 ssl proxy_protocol" # proxy port 변경
      # KONG_PROXY_LISTEN: 0.0.0.0:${KONG_PROXY_PORT:-8000}
      # KONG_PROXY_URL: http://${LOCAL_IP}:8000
      KONG_PROXY_URL: http://localhost:8000
      KONG_ADMIN_GUI_LISTEN: "0.0.0.0:8002, 0.0.0.0:8445 ssl" # kong manager 포트
      # KONG_ADMIN_GUI_URL: "http://host.docker.internal:8002"     # kong manager 주소
      KONG_ADMIN_GUI_URL: "http://localhost:8002"     # kong manager 주소
      # KONG_ADMIN_GUI_API_URL: "http://${LOCAL_IP}:8001" # kong manager에서 사용하는 admin api(CORS 방지)
      KONG_ADMIN_GUI_API_URL: "http://localhost:8001" # kong manager에서 사용하는 admin api(CORS 방지)
      KONG_TRUSTED_IPS: 0.0.0.0/0,::/0 # This trusts all IPs
      KONG_REAL_IP_RECURSIVE: ON
      # default X-Real-IP X-Forwarded-For
      # KONG_REAL_IP_HEADER: proxy_protocol
      KONG_REAL_IP_HEADER: X-Forwarded-For
    volumes:
      # 호스트머신 타임존 사용
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      # File Log 파일 볼륨 바인드 마운트
      # - tmp:/tmp:rw
      # https://docs.konghq.com/gateway/latest/install/docker/#prepare-your-configuration-file
      - ${PWD}/config:/opt/kong
    secrets:
      - kong_postgres_password
    ports:
      - '${KONG_PROXY_PORT:-8000}:8000' # proxy port
      # - '18443:18443' # proxy port (ssl)
      - '${KONG_ADMIN_PORT:-8001}:8001' # admin api port
      - '8002:8002' # kong manager
      - '8443:8443'
      - '8444:8444'
      - '8445:8445'
      - '8003:8003'
      - '8004:8004'
    extra_hosts:
      - "host.docker.internal:host-gateway"
      - "otherhost:10.10.0.1"
      - 'api.test.com:10.10.0.2'
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 10s
      retries: 10
    security_opt:
      - no-new-privileges
    restart: on-failure:5
    # read_only: true
    user: "${UID}"
    # privileged: true
    depends_on:
      # - postgres
      - kong-migrations-up
# docker network create kong-net
networks:
  kong-net:
    driver: bridge
    # external: true
volumes:
  tmp:
secrets:
  kong_postgres_password:
    file: ./POSTGRES_PASSWORD

📌 Dockerfile

Timezone 설정 문제로 Dockerfile 빌드 Step 추가

FROM kong:3.7
# https://stynxh.github.io/2020-07-26-set-timezone-when-ubuntu-docker-image-build/
ARG DEBIAN_FRONTEND=noninteractive
ENV UID=${UID}
ENV TZ=Asia/Seoul
USER root
RUN apt-get update && apt-get install -y tzdata
USER ${UID}

📌 POSTGRES_PASSWORD

Secrets 설정으로 패스워드를 별도의 파일로 분리

kong1234

🚦 기동/중지/재시작/로그보기 스크립트


📌 기동: start.sh

#!/bin/bash
UID="$(id -u)"
# LOCAL_IP=$(osascript -e "IPv4 address of (system info)")
# LOCAL_IP=$(ifconfig | grep 'inet ' | grep -Fv 127.0.0.1 | awk '{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2;exit}')
# PUBLIC_IP=$(curl -s ifconfig.me)
# PRIVATE_IP=$(hostname -i)
# LOCAL_IP=$(ipconfig getifaddr en0)
LOCAL_IP=$(hostname -I | awk '{print $1}')
PWD=$(pwd)
export UID LOCAL_IP PWD
echo UID=$UID, LOCAL_IP=$LOCAL_IP, PWD=$PWD
echo "docker compose up --build --force-recreate -d"
docker compose up --build --force-recreate -d
# echo "docker compose up --build -d"
# docker compose up --build -d

📌 중지: stop.sh

#!/bin/bash
UID="$(id -u)"
# LOCAL_IP=$(osascript -e "IPv4 address of (system info)")
# LOCAL_IP=$(ifconfig | grep 'inet ' | grep -Fv 127.0.0.1 | awk '{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2;exit}')
# PUBLIC_IP=$(curl -s ifconfig.me)
# PRIVATE_IP=$(hostname -i)
# LOCAL_IP=$(ipconfig getifaddr en0)
LOCAL_IP=$(hostname -I | awk '{print $1}')
PWD=$(pwd)
export UID LOCAL_IP PWD
echo UID=$UID, LOCAL_IP=$LOCAL_IP, PWD=$PWD
echo "docker compose down"
docker compose down

📌 재시작: restart.sh

#!/bin/bash
UID="$(id -u)"
# LOCAL_IP=$(osascript -e "IPv4 address of (system info)")
# LOCAL_IP=$(ifconfig | grep 'inet ' | grep -Fv 127.0.0.1 | awk '{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2}')
# LOCAL_IP=$(ifconfig | awk '/inet /&&!/127.0.0.1/{print $2;exit}')
# PUBLIC_IP=$(curl -s ifconfig.me)
# PRIVATE_IP=$(hostname -i)
# LOCAL_IP=$(ipconfig getifaddr en0)
LOCAL_IP=$(hostname -I | awk '{print $1}')
PWD=$(pwd)
export UID LOCAL_IP PWD
echo UID=$UID, LOCAL_IP=$LOCAL_IP, PWD=$PWD
echo "docker compose down"
docker compose down
# echo "docker compose up --build --force-recreate -d"
# docker compose up --build --force-recreate -d
echo "docker compose up -d"
docker compose up -d

📌 로그보기: tail.sh

#!/bin/bash
# 로그 보기 (이전 로그 100라인만 출력)
echo "docker compose logs -f -t -n 100"
docker compose logs -f -t -n 100
# 전체 로그 보기
# https://docs.docker.com/reference/cli/docker/compose/logs/
# --no-color : 단색 출력을 생성
# --no-log-prefix: 접두사 출력 안함
#  -f, -follow: 로그 출력을 따라감
#  -t, -timestamps: 타임 스탬프 표시
#  -n --tail all: 각 컨테이너의 로그 끝부터 표시
# echo 'docker compose logs -f -t -n all
# docker compose logs -f -t -n all

🚦 Kong Conf 설정 및 logrotate 설정


📌 Docker Compose 에서 설정

Docker Compose 사용시 logrotate 설정은 필요 없음

HTTP Server 에 요청한 Client 의 IP 를 식별하기 위한 표준 설정

trusted_ips = 0.0.0.0/0,::/0
real_ip_header = X-Forwarded-For

Docker Compose 에서는 environment 로 설정

environment:
  KONG_TRUSTED_IPS: 0.0.0.0/0,::/0 # This trusts all IPs
  KONG_REAL_IP_RECURSIVE: ON
  KONG_REAL_IP_HEADER: X-Forwarded-For

📌 Linux 에서 설정

Kong conf 설정 파일 적용

  • PATH: /etc/kong

위의 경로에 설정파일 복사

Kong logrotate 설정

# kong logrotate 생성
$ vim /etc/logrotate.d/kong
  • kong logrotate 설정 내용 입력
/usr/local/kong/logs/*.log {
  su kong kong
  rotate 7
  daily
  missingok
  compress
  delaycompress
  notifempty
  sharedscripts
  postrotate
  if [ -f /usr/local/kong/pids/nginx.pid ]; then
    kill -USR1 `cat /usr/local/kong/pids/nginx.pid`
  fi
  endscript
}
  • kong logrotate 적용
$ sudo /usr/sbin/logrotate -f /etc/logrotate.conf

🚦 Consumers 등록 및 Basic Auth 생성

Kong Gateway 를 이용하여 서비스를 라우팅할때, 특정 대상에 라우팅을 허용할지 여부를 적용할때의 적용 대상

📌 Consumers Basic Auth Authorization 생성 절차

  • Consumers - New Consumer 클릭

  • Consumer 정보 입력

  • Basic Auth Credential 생성

  • Basic Auth Credential 정보 입력

  • Basic Auth Credential 생성 완료

  • Basic Auth Base64 인코딩 데이터를 Header에 Authorization 에 추가하여 Routing 인증에 사용

아래와 같이 Header 에 설정하고 사용

Authorization: Basic dGVzdDp0ZXN0MTIzNA==

🚦 테스트 쉘 스크립트


📌 Kong 서비스 테스트

등록된 서비스들에 대한 통신 테스트를 수행하는 스크립트

Kong Gateway 에 서비스를 등록하고 테스트를 수행해본다

dev_kong_service_check.sh

#!/bin/bash
declare -a remote_hosts=(
    "172.16.0.1:8001" 
    "172.16.0.2:443" 
    "172.16.0.3:443" 
    "172.16.0.4:443" 
    "172.16.0.5:443" 
    "172.16.0.6:443" 
    )
length=${#remote_hosts[@]}
profile='DEV'
success_count=0
fail_count=0
echo "==============================================="
echo "$profile SERVICE TEST START!!"
echo "==============================================="
for each in ${remote_hosts[@]} ;do
        ip=$(echo $each | cut -d ':' -f1)
        port=$(echo $each | cut -d ':' -f2)
        echo "Connecting ${ip}:${port} .."
        telnet $ip $port 2>&1 >/dev/null & WPID=$!
        sleep 1
        if ps -p $WPID > /dev/null ;then
            echo "FAILED TO REACH ($WPID is running)"
            kill $! 2>&1 >/dev/null
            wait $WPID 2>&1 >/dev/null
            fail_count=$((fail_count+1))
        else
            success_count=$((success_count+1))
        fi
        echo "-------------------------------------------------------------------------------------------------"
done
echo total_count=$length, success_count=$success_count, fail_count=$fail_count
echo "==============================================="
echo "$profile SERVICE TEST END!!"
echo "==============================================="

📌 Kong 라우터 테스트

등록된 라우터들에 대한 통신 테스트를 수행하는 스크립트

Kong Gateway 에 라우터를 등록하고 테스트를 수행해본다

dev_kong_route_check.sh

#!/bin/bash

domain='172.16.0.1:8000' # KONG
function route_roop() {
    path=$1
    echo "==============================================="
    echo "path=[$path] TEST START!!"
    echo "==============================================="
    declare -a authorizations=(
        "a3l0dsfdsdf8sdf8sd8fds8dfss"
        "a3l0dsfdsdf8sdf8sd8fds8dfs1"
        "a3l0dsfdsdf8sdf8sd8fds8dfs2"
        "a3l0dsfdsdf8sdf8sd8fds8dfs3"
        "a3l0dsfdsdf8sdf8sd8fds8dfs4"
    )
    declare -a consumers=(
        "test-route1"
        "test-route2"
        "test-route3"
        "test-route4"
        "test-route5"
    )
    length=${#authorizations[@]}

    success_count=0
    fail_count=0
    roop_count=0

    for each in ${authorizations[@]} ;do
        # CURL 테스트시 HTTPS 사이트 SSL 검증에 실패하므로 -k 옵션을 추가하여 검증을 PASS 한다
        # 참고: https://velog.io/@markyang92/curl
        echo curl -s -w '%{http_code}' -o /dev/null -L -k "${path}" --header "Authorization: Basic $each"
        result=$(curl -s -w '%{http_code}' -o /dev/null -L -k "${path}" --header "Authorization: Basic $each")
        sleep 0.1
        consumer="consumer=[${consumers[$roop_count]}], path=[$path]"
        if [ $result -eq 200 ] ;then
            echo "$consumer: SUCCESS!!"
            success_count=$((success_count+1))  
        else
            echo "$consumer: FAIL!!"
            fail_count=$((fail_count+1))
        fi
        roop_count=$((roop_count+1))
        echo "-------------------------------------------------------------------------------------------------"
    done
    echo total_count=$length, success_count=$success_count, fail_count=$fail_count
    echo "==============================================="
    echo "path=[$path] TEST END!!"
    echo "==============================================="
}

default_url="http://${domain}"
# IP, DOMAIN 여부에 따라 HTTP, HTTPS 프로토콜로 변환
if [[ ${domain:0:1} =~ ^[0-9]+$ ]];then
    url=${default_url}
else
    url="https://${domain}"
fi

# DOMAIN 이더라도 HTTP 프로토콜이면 default_url
path='${default_url}/test/api/a'
route_roop $path
# 나머지 API 들은 IP, DOMAIN 에 따라 분기 처리
path='${url}/test/api/b'
route_roop $path
# 나머지 API 들은 IP, DOMAIN 에 따라 분기 처리
path='${url}/test/api/c'
route_roop $path
728x90