도커 공식 포스트그레스큐엘 이미지를 쓸 때

이 글은 PostgreSQL 서버를 보다 쉽게 설치하고, 사용하기 위한 사용자들(국내에서는 흔히 개발자라고 하죠)을 위한 글입니다.
실무 운영 환경에서는 그다지 도움이 되지는 않습니다.
간단히 버전별 비교를 하거나,
잠깐 자료 정리를 하기 위해서 데이터베이스가 필요할 때
참고할만한 글입니다.

PostgreSQL 컨테이너 만들기

PostgreSQL 공식 Docker 이미지 이름은 postgres 입니다.

도커 공식 PostgreSQL 이미지는 기본값으로 데이터클러스터 영역을 도커 볼륨을 사용하도록 설정되어있습니다. 즉, 컨테이너를 만들 때, 따로 이 영역으로 사용할 도커 볼륨을 만들지 않았다면, 임의로 도커 볼륨을 만들어 사용합니다. 이때, 임의 이름 컨테이너가 만들어지는데, 이 때, 임의의 볼륨도 함께 만들어집니다.
$ docker run -d -e POSTGRES_PASSWORD=password postgres
2c73aac0c3af2474e32405787b0d87ee06ca8cd82f27057e7fbcf4fb387081e9
$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS      NAMES
2c73aac0c3af   postgres   "docker-entrypoint.s…"   8 seconds ago   Up 7 seconds   5432/tcp   quizzical_poincare
$ docker volume ls
DRIVER    VOLUME NAME
local     6a3f3cc38d6a232b0029f649e7a0697ab936dc992f530707e48ba7ebea5f378f
postgres 이미지 기본 entrypoint는 docker-entrypoint.sh 스크립트로 하는 일은 데이터클러스터가 없으면, initdb 명령을 실행합니다. 이 때 기본값으로 사용하는 데이터디렉터리는 /var/lib/postgresql/data 입니다.  또한 이 initdb 작업이 필요한 상황이라면, 컨테이너를 만들 때, -e 옵션으로 반드시 POSTGRES_PASSWORD 환경 변수 값을 지정해야합니다. (이미 만들어진 데이터클러스터를 쓴다면 굳이 POSTGRES_PASSWORD 설정이 꼭 필요하지는 않습니다.)

도커는 이렇게 임의의 볼륨을 사용하는 컨테이너가 만들어졌을 때, 이 컨테이너를 더 이상 쓰지 않아, 삭제될 경우 (--rm 옵션으로 컴테이너가 종료되면 자동으로 그 컨테이너를 지울 경우도 포함 해서) 이 때 같이 만들어진 임의의 볼륨을 삭제하지 않습니다. 이렇게 해서 데이터클러스터를 다시 사용할 수 있습니다.
이 볼륨도 지워야한다면, docker volume rm 명령으로 수동으로 지워야합니다.
$ docker stop quizzical_poincare
quizzical_poincare
$ docker rm quizzical_poincare
quizzical_poincare
$ docker run -d -v 6a3f3cc38d6a232b0029f649e7a0697ab936dc992f530707e48ba7ebea5f378f:/var/lib/postgresql/data  postgres
0a35b2d912b111b32976bdb7ccaf25f5fca05e8d4c51981cef1c456ff9d1343a
$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS      NAMES
0a35b2d912b1   postgres   "docker-entrypoint.s…"   5 seconds ago   Up 5 seconds   5432/tcp   quirky_feynman
$ docker volume ls
DRIVER    VOLUME NAME
local     6a3f3cc38d6a232b0029f649e7a0697ab936dc992f530707e48ba7ebea5f378f
이렇기 때문에, postgres 도커 이미지를 실행할 때는 -v 옵션으로 볼륨을 지정하는 것이 좋습니다. 문제는 그 볼륨 이름이 임의의 문자열이기 때문에, 이미지와 컨테이너와 볼륨의 조합이 어떤지 기억해 두지 않는다면, 해당 컨테이너를 지우고, (지워졌거나) 다시 그 볼륨을 사용하려고 할 때 힘듭니다.

그래서, -v 옵션을 지정하기 전에 사용자가 미리 볼륨을 만듭니다.
운영환경이라면, 이 볼륨은 당연히 소프트웨어 정의 저장공간으로 도커 호스트와 별개로 운영되는 영구 저장공간이겠지만, 일반 개발환경에서는 단순히 로컬 디스크를 쓸 것이고, 이 때 이 데이터베이스를 위해 사용할 볼륨을 간단하게 만들고 그 볼륨을 컨테이너를 만들 때 사용합니다.
$ docker volume create postgres13-data
postgres13-data
$ docker volume ls
DRIVER    VOLUME NAME
local     postgres13-data
$ docker run -d -e POSTGRES_PASSWORD=password -v postgres13-data:/var/lib/postgresql/data postgres
2a5284255846bdc80aa3093b52eff4357ade24216b42aad3faa4e064ea61b174
$ docker ps -a
CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS                    PORTS      NAMES
2a5284255846   postgres                   "docker-entrypoint.s…"   5 seconds ago    Up 4 seconds              5432/tcp   adoring_dhawan
이렇게 만든 데이터클러스터용 볼륨을 사용할 때 기억해야할 것은 PostgreSQL 메이저 버전 단위로 그 볼륨을 재활용할 수 있다는 것입니다. 그래서, 볼륨 이름에 그 메이저 버전을 지정해 놓는 것이 좋습니다.

태그 선택

Docker postgres 태그 설명

postgres 이미지의 태그는 버전입니다.
  • 태그 없음: 가장 마지막 공식 배포판
  • 9.6, 10, 11, 12, 13: 메이저 버전의 마지막 배포판
  • 9.6.x, 10.x, 11.x, 12.x, 13.x: 각 메이저별 패치 버전 배포판
이런식입니다.

무슨 태그를 쓸까?

가장 최신 버전을 잠깐 살펴 볼 때는 태그를 사용하지 않고 그냥 이미지 이름만 사용해서 컨테이너를 만드는 것이 한 방법이겠지만, 이렇게 만들어진 컨테이너가 사용하는 데이터 클러스터 영역을 재활용할 때, 도커 이미지가 메이저 버전 단위로 업그레이드 되었을 때, 손이 많이 가게 됩니다.

PostgreSQL은 메이저 버전이 바뀌게 되면, pg_upgrade 명령으로 사용하고 있던 데이터클러스터를 기반으로 새 메이저 버전용 데이터클러스터를 만들어 업그레이드해야 사용할 수 있습니다.  아쉽게도 아직 이런 작업은 사용자가 직접해야합니다. 그렇기 때문에, 통상 응용 프로그램 개발용 PostgreSQL 도커 컨테이너를 쓴다면, 패치 버전까지 미리 딱 정할 것인지, 메이저버전까지만 정할 것인지를 선택해야합니다.

1. 메이저버전
postgres:13 이렇게 태그를 메이저버전으로만 선택하는 경우 도커 이미지 관리자에 의해 언제든지 임의로 docker pull 작업으로 그 이미지들이 업그레이드 될 수 있습니다.
즉, 해당 메이저 버전의 패치 작업은 단순히 컨테이너 중지, 삭제, 재생성 작업을 함으로 도커 서비스 전체 형상 관리가 좀 더 단순해 질 수 있습니다.  단점은 docker ps 명령으로 해당 컨테이너가 정확하게 무슨 버전을 쓰고 있지는 알 수 없다는 점입니다.

2. 패치버전
일반적으로 이렇게 고정된 태그를 쓰는 경우는 그 패치 버전별 비교를 위해서 이거나, 패치 작업 뒤 기타 문제로 긴급하게 하위 패치 버전으로 다운그래이드를 염두해 둘 때입니다. 단점은 도커 호스트에서 관리해야할 postgres 이미지들이 다양해 진다는 것입니다.

결국 태그 선택은 사용자의 몫입니다.
기억해야할 것은 docker 는 pull 명령으로 이미지를 자동 업데이터할 수 있고, 이런 장점을 잘 이용하면, 그 이미지를 사용하는 컨테이너들의 버전 패치는 자동화 할 수 있다는 것입니다. (물론 postgres 이미지와 볼륨을 따로 지정하는 경우에 한정 되어서 말이죠)


데이터베이스 기본 설정값

도커 공식 이미지로 PostgreSQL을 실행할 경우, 실행되는 데이터베이스의 기본 설정값이 너무 보수적입니다.

지난 글에서도 언급했듯이,
데이터베이스가 처음 실행되고 접속이 확인되면 응용 프로그램을 사용하기 전에 먼저 어느 정도의 서버 설정이 필요하기는 합니다.  특히나 메모리 관련해서 설정을 하지 않으면 성능이 많이 떨어집니다.

기본적으로 살펴보아야할 설정값들은 다음과 같습니다.
  • effective_cache_size
  • maintenance_work_mem
  • max_wal_size
  • min_wal_size
  • shared_buffers
  • wal_buffers
  • work_mem
alter system SQL 명령으로 변경하고, 해당 컨테이너를 다시 실행하고 사용하는 것이 좋습니다.

여기서 고민해볼말한 것이, logging_collector 설정인데, 기본값은 off 입니다. 이말은 데이터베이스 서버가 기록하는 모든 서버 로그는 표준 출력(stdout)으로 보냅니다. 즉, 이 로그는 docker logs 명령으로 살펴볼 수 있으며, 도커에서 특별히 로그 관리를 따로 지정하지 않는다면, 최대 10MB의 마지막 로그만 보관합니다. 서버 로그를 서버 독립적으로 관리하고싶다면, log* 관련 환경 설정도 변경이 필요합니다.

또 한 부분은 한국어 관련 설정입니다.

도커 공식 postgres 이미지는 영어 환경을 기반으로 합니다.
시간은 UTC 기반입니다.
이렇게 한국, 한국어 기반으로 변경 하고자 한다면, 데이터베이스 서버의 환경 설정을 바꾸기 보다, 그 컨테이너가 실행되는 OS 환경 설정을 한국, 한국어 환경으로 설정하고, 그 기반 위에 데이터베이스 서버가 실행되게 하는 것이 좋습니다. 
이 말은 공식 postgres 이미지를 다시 한번 수정해서 새로운 한국어 postgres 이미지를 만들어 사용하겠다는 것을 의미합니다.

통상 이 작업은 Dockerfile을 만들고, docker build 명령으로 한국어 환경에 특화된 postgres 이미지를 만들어서 사용합니다.

예제 Dockerfile 내용은 다음과 같습니다.
FROM postgres:13.1
RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
    sed -i 's/# ko_KR.UTF-8 UTF-8/ko_KR.UTF-8 UTF-8/' /etc/locale.gen && \
    locale-gen
ENV LANG=ko_KR.utf8 \
    LC_COLLATE=C \
    POSTGRES_INITDB_ARGS=--data-checksums
나머지 사용법은 동일합니다.
$ docker build -t postgres-ko:13.1 .
$ docker run -d -e POSTGRES_PASSWORD=password -v postgres13-data:/var/lib/postgresql/data postgres-ko:13.1
문제는 이런 식으로 공식 이미지를 기반으로 또 새로운 이미지를 독자적으로 만든다면, 계속 바뀌는 공식 이미지에 맞춰 계속 해당 한국어용 이미지를 계속 관리해야한다는 부담이 생기게됩니다.

컨테이너 패치하기

이미 사용중인 12.4 데이터베이스 서버를 12.5로 패치하고자 하는데, 그 서버가 컨테이너 기반으로 운영 되고 있는 경우 작업 방법을 소개합니다.

간단합니다.
그 컨테이너를 지우고 다시 만든다.

즉, 다시 만들 때, 예전에 처음 만들 때 사용했던 그 옵션들을 그대로 사용해야한다는 것입니다. 

사용했던 태그가 메이저 버전이었다면, 다음과 같은 작업으로 진행됩니다.

docker pull postgres:12
docker stop pg12
docker rm pg12
docker run -d --name pg12 -v pg12:/var/lib/postgresql/data postgres:12

패치 버전까지 있는 태그를 사용하는 경우도 마찬가지겠죠.
물론 network 관련도 기존에 쓰던 것과 같은 환경으로 재구축해야하는 것도 빼먹지 말아야합니다.
(이 글은 docker network 관련 글이 아니여서 여기서는 생략합니다.)


마치며

이 글에는 쿠버네티스 같은 통합 컨테이너 관리 솔루션을 사용할 때의 이야기는 빠져있습니다.
이 글에서도 소개하고 있는 그 볼륨 문제를 아직 깔끔하게 해결하지 못했기 때문입니다.