ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • docker-compose으로 Postgresql Replication 구성
    DB 2022. 1. 17. 10:17
    반응형

    Replication 구성으로 얻을 수 있는 장점으론 당연히 장애로부터 자유(?)로워 질 수 있는 부분도 있지만 서비스적인 측면으로 봤을 때

    • 마스터는 Write(저장, 수정, 삭제)로 활용
    • 슬레이브는 Read(조회)로 활용

    볼 수 있다. 이렇게 분리하면

    • 장애 시 서비스에 끼치는 영향도를 낮춤 단일 서버 구성 시 장애가 발생 되면 저장, 삭제, 수정, 조회등 전체적인 서비스에 영향이 발생 되지만 분리 시에는 장애 발생 서버를 제외한 서비스는 가능하다.
    • 퍼포먼스 향상 Read와 Write 작업을 분리함으로서 서로 간에 작업 영향도를 최대한 격리하여 퍼포먼스를 향상 시킬 수 있다.

    Postgresql(이하 pg)의 Replication은 WAL(write-ahead logging)을 활용하며 pg에선 WAL로 생성된 로그파일을 아래 흐름으로 Replication을 구성한다.

    • 마스터 서버의 모든 작업을 로그로 생성
    • 마스터의 작업 로그를 슬레이브 서버로 전달
    • 로그를 전달 받은 슬레이브 서버에선 복원(재실행)

    환경

    • Postgresql 11
    • Window 11 - WSL2
    • Dockek
      • docker-compose

    WAL 전달 방식

    WAL의 전달 방식은 2가지로 나뉜다. 로그 파일은 $PG_SQL/data/pg_xlog 디렉토리에 쌓이게 된다.

    Log-Shipping

    pg_xlog 디렉토리의 WAL 파일 자체를 슬레이브 서버에 전달(File Copy)

    Streaming

    WAL 파일 여부와 상관없이 로그 내용을 Streaming 형식으로 슬레이브 서버로 전달

    설정

    Replication 설정의 대략적인 흐름을 설명 하자면

    마스터 서버에선

    • Relication 전용 User 생성 - query
    • WAL 관련 설정 - postgresql.conf
    • 생성한 사용자 계정 접근 권한 설정 - pg_hba.conf

    슬레이브 서버에선

    • 마스터 서버 백업(복제/복원) - pg_basebackup 명령어
    • WAL 관련 설정 - postgresql.conf
    • recovery.conf 설정

    이다.

    docker-compose.yml

    마스터 서버는

    • Dockerfile.primary 실행
      • Dockerfile.primary의 ENTRYPOINT에서 setup-primary.sh 실행

    슬레이브 서버는

    • Dockerfile.readonly 실행
      • Dockerfile.readonly의 ENTRYPOINT에서 setup-readonly.sh 실행

    순서를 가진다.

    version: "3"
    services:
      pg_primary:
        build:
          context: ./docker/pg
          dockerfile: Dockerfile.primary
        command: postgres -c log_destination=stderr -c log_statement=all
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: password
        volumes:
          - ./pg_primary_data:/var/lib/postgresql/data
        ports:
          - "5445:5432"
      pg_readonly:
        build:
          context: ./docker/pg
          dockerfile: Dockerfile.readonly
        command: postgres -c log_destination=stderr -c log_statement=all
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: password
        volumes:
          - ./pg_readonly_data:/var/lib/postgresql/data
        depends_on:
          - pg_primary
        ports:
          - "5446:5432"

    Dockerfile

    마스터 - Dockerfile.primary

    FROM postgres:11-alpine
    
    ENV LANG C.UTF-8
    
    COPY ./setup-primary.sh /docker-entrypoint-initdb.d/setup-primary.sh
    RUN chmod 0666 /docker-entrypoint-initdb.d/setup-primary.sh

    슬레이브 - Dockerfile.readonly

    FROM postgres:11-alpine
    
    ENV LANG C.UTF-8
    ENV ENTRYKIT_VERSION 0.4.0
    
    RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && mv entrykit /bin/entrykit \
      && chmod +x /bin/entrykit \
      && entrykit --symlink
    
    RUN apk --update add openssh-client && rm -rf /var/cache/apk/*
    
    COPY ./setup-readonly.sh /setup-readonly.sh
    RUN chmod +x /setup-readonly.sh
    
    ENTRYPOINT [ \
        "prehook", \
            "/setup-readonly.sh", \
            "--", \
        "docker-entrypoint.sh" \
    ]
    CMD ["postgres"]

    슬레이브 서버 경우 마스터 서버와 다르게 아래와 같이 entrykit을 다운 받는다.(https://github.com/progrium/entrykit/)

    ENV ENTRYKIT_VERSION 0.4.0
    
    RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
      && mv entrykit /bin/entrykit \
      && chmod +x /bin/entrykit \
      && entrykit --symlink

    entrykit은 Dockerfile의 ENTRYPOINT를 확장하여 다양한 기능을 사용할 수 있게 해준다.

    여기선 prehook를 사용하였는데 이는 슬레이브 서버가 시작 되기 전에

    • 마스터 서버 정상 실행 여부
    • pg_basebackup 명령어로 마스터 서버 백업(복제/복원)
    • postgresql.conf 및 recovery.conf 설정

    실행 한 후 슬레이브 서버가 시작 된다.

    Shell

    마스터 - setup-primary.sh

    #!/bin/bash
    set -e
    
    psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE ROLE replication_user LOGIN REPLICATION PASSWORD 'replicationpassword';
    EOSQL
    
    psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
    EOSQL
    
    mkdir $PGDATA/archive
    
    cat >> "$PGDATA/postgresql.conf" <<EOF
    wal_level = hot_standby
    max_wal_senders = 10
    max_replication_slots = 10
    synchronous_commit = off
    EOF
    
    echo "host replication replication_user 0.0.0.0/0 md5" >> "$PGDATA/pg_hba.conf"

    setup-master.sh에선 위 상단에 설명한대로

    • Relication 전용 User 생성 - query
    psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE ROLE replication_user LOGIN REPLICATION PASSWORD 'replicationpassword';
    EOSQL
    
    psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
    EOSQL
    • 생성한 사용자 계정 접근 권한 설정 - pg_hba.conf
    echo "host replication replication_user 0.0.0.0/0 md5" >> "$PGDATA/pg_hba.conf"

    실행한다.

    슬레이브 - setup-readonly.sh

    #!/bin/bash
    set -e
    
    if [ ! -s "$PGDATA/PG_VERSION" ]; then
        echo "*:*:*:replication_user:replicationpassword" > ~/.pgpass
        chmod 0600 ~/.pgpass
        until ping -c 1 -W 1 pg_primary
        do
            echo "Waiting for primary to ping..."
            sleep 1s
        done
    
        until pg_basebackup -h pg_primary -D ${PGDATA} -U replication_user -vP -W
        do
            echo "Waiting for primary to connect..."
            sleep 1s
        done
    
        sed -i 's/wal_level = hot_standby/wal_level = replica/g' ${PGDATA}/postgresql.conf
    
        cat > ${PGDATA}/recovery.conf <<EOF
    standby_mode = on
    primary_conninfo = 'host=pg_primary port=5432 user=replication_user password=replicationpassword application_name=pg_readonly'
    primary_slot_name = 'node_a_slot'
    EOF
    
        chown postgres:postgres ${PGDATA} -R
        chmod 700 ${PGDATA} -R
    fi

    setup-readonly.sh은 슬레이브 서버 시작 전에 아래와 같이 마스터 서버가 정상적으로 실행 되었는지 확인한다.

    until ping -c 1 -W 1 pg_primary
    do
        echo "Waiting for primary to ping..."
        sleep 1s
    done

    그 후 아래와 같이

    • 마스터 서버 백업(복제/복원) - pg_basebackup 명령어
    until pg_basebackup -h pg_primary -D ${PGDATA} -U replication_user -vP -W
    do
        echo "Waiting for primary to connect..."
        sleep 1s
    done
    • WAL 관련 설정 - postgresql.conf
    sed -i 's/wal_level = hot_standby/wal_level = replica/g' ${PGDATA}/postgresql.conf
    • recovery.conf 설정
    cat > ${PGDATA}/recovery.conf <<EOF
    standby_mode = on
    primary_conninfo = 'host=pg_primary port=5432 user=replication_user password=replicationpassword application_name=pg_readonly'
    primary_slot_name = 'node_a_slot'
    EOF

    실행한다.

    실행

    해당 docker-compose.yml이 있는 경로에서

    docker-compose -f docker-compose.yml up -d

    명령어 실행 한 후

    docker ps

    위 명령어로 아래와 같이 컨테이너가 정상적으로 실행 되는지 확인한다.

    아래 출력 결과는 실제와 상이하며 정상 실행 여부는 STATUS 상태가 UP인지 확인한다.

    CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS       PORTS                    NAMES
    1d942fcd16a7   pg_repl_pg_readonly   "prehook /setup-read…"   4 hours ago   Up 4 hours   0.0.0.0:5446->5432/tcp   pg_repl-pg_readonly-1
    7d4eb0a9d4f8   pg_repl_pg_primary    "docker-entrypoint.s…"   4 hours ago   Up 4 hours   0.0.0.0:5445->5432/tcp   pg_repl-pg_primary-1

    확인

    테이블 생성

    마스터 서버에서 테이블 및 컬럼 생성 후 슬레이브에서도 동일하게 생성되었는지 확인한다.

    마스터 서버

    아래 명령어로 마스터 서버 컨테이너의 CONTAINER ID 혹은 NAMES을 확인한다.

    docker ps

    필자의 docker 상태로 예로 들자면 마스터 서버의 컨테이너 ID는 7d4eb0a9d4f8 이고 NAMES는 pg_repl-pg_primary-1이다.

    CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS       PORTS                    NAMES
    1d942fcd16a7   pg_repl_pg_readonly   "prehook /setup-read…"   4 hours ago   Up 4 hours   0.0.0.0:5446->5432/tcp   pg_repl-pg_readonly-1
    7d4eb0a9d4f8   pg_repl_pg_primary    "docker-entrypoint.s…"   4 hours ago   Up 4 hours   0.0.0.0:5445->5432/tcp   pg_repl-pg_primary-1

    그 후 아래 명령어로 컨테이너로 접속한다.

    docker exec -it `CONTAINER ID 혹은 NAMES` /bin/bash

    정상적으로 접속하였으면 아래 psql로 postgresql 서버에 접속한다.

    psql -U postgres -d postgres

    그리고 아래와 같이 테스트 테이블을 생성한다.

    create table repl (name varchar(10));

    슬레이브 서버

    위 마스터를 참고하여 슬레이브의 postgresql 서버까지 접속한다. 접속 후 아래 명령어로 테이블이 정상적으로 생성 되어있는지 확인한다.

    \dt

    정상적이라면 아래와 같이 repl 테이블이 조회된다.

            List of relations
     Schema | Name | Type  |  Owner
    --------+------+-------+----------
     public | repl | table | postgres
    (1 row)

    select * from repl; 명령어로 name 컬럼도 정상적으로 생성 되었는지 확인한다. 정상적이면 아래와 같은 결과가 출력된다.

    데이터 저장, 수정, 삭제

    마스터 서버에서 데이터 저장, 수정, 삭제 실행 하고 슬레이브 서버에서도 결과가 동일한지 확인한다.

    저장

    마스터 서버

    위 테이블 생성 부분을 참고하여 마스터의 postgresql 서버까지 접속한다. 그 후 아래와 같이 Insert 문을 실행한다.

    insert into repl(name) values ('devsull');

    슬레이브 서버

    슬레이브 서버의 postgresql 서버에서 select * from repl; 쿼리를 실행하여 아래와 같이 정상적으로 동일한 데이터가 저장 되었는지 확인한다.

    postgres=# select * from repl;
      name
    ---------
     devsull
    (1 row)

    수정

    마스터 서버

    마스터의 postgresql 서버에서 아래와 같이 Update 문을 실행한다.

    update repl set name = 'good';

    슬레이브 서버

    슬레이브 서버의 postgresql 서버에서 위와 동일하게 Select문을 수행하여 데이터가 변경 되었는지 확인한다.

    postgres=# select * from repl;
     name
    ------
     good
    (1 row)

    삭제

    마스터 서버

    마스터의 postgresql 서버에서 아래와 같이 Delete 문을 실행한다.

    delete from repl where name = 'good';

    슬레이브 서버

    슬레이브 서버의 postgresql 서버에서 위와 동일하게 Select문을 수행하여 데이터가 삭제 되었는지 확인한다.

    postgres=# select * from repl;
     name
    ------
    (0 rows)

    위 몇 가지의 확인 과정들이 정상적이면 일단은(?) pg의 replication 구성이 정상적으로 된 것이다.

    본 글에 사용된 설정 파일은 GitHub - sungwookkim/postg11repl에서 확인 할 수 있다.

    그리고 다중 데이터소스 관련 Spring 설정은

    다중 데이터소스 관련 Service 구현은

    에서 참고하실 수 있습니다.

    참고

    WAL이란? :

    https://ko.wikipedia.org/wiki/%EB%A1%9C%EA%B7%B8_%EC%84%A0%ED%96%89_%EA%B8%B0%EC%9E%85

    Postgresql Replication 구축 및 설명 :

    https://medium.com/qodot/postgresql-replication-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-dfd481c4fb04

    https://brunch.co.kr/@daniellim/40

    http://kimchki.blogspot.com/2019/09/postgresql-replication.html

    Postgresql 11 docker-compose 버전 :

    https://blog.takeyuweb.co.jp/entry/2020/04/24/100000

    반응형

    'DB' 카테고리의 다른 글

    PG(Postgresql) 대용량 Insert  (0) 2023.07.03
    Spring boot + MongoDB Multi-Document Transactions  (0) 2022.07.22

    댓글

Designed by Tistory.