본문 바로가기
도커/개발자를 위한 쉬운 도커

5.3 컨테이너 애플리케이션 구성: PostgreSQL 컨테이너 구성

by limdae94 2024. 7. 23.
 

개발자를 위한 쉬운 도커 강의 | 데브위키 - 인프런

데브위키 | 현업 개발자가 도커를 사용한 경험을 녹여낸 새로운 커리큘럼으로 기존 교재 및 강의와 차별된 강의를 제공합니다. 단순한 명령어 사용법이 아닌 도커를 왜 사용해야하는지 대한 근

www.inflearn.com

 

1. 데이터베이스 컨테이너 구성

1. OS 구성 및 PostgreSQL 설치

데이터베이스 컨테이너를 구성에 대해 학습한다. leafy application에서 사용할 데이터베이스는 PostgreSQL을 사용한다. PostgreSQL은 데이터를 저장하고 조회할 수 있는 데이터베이스 서버이다. PostgresSQL는 Nginx처럼 이미지에 소프트웨어가 포함되어 있기 때문에 별도의 설정없이 PostgresSQL 이미지만 실행해도 데이터베이스를 사용할 수 있다. 하지만 기본 이미지에는 아무 데이터도 없기 때문에 우리는 초기 데이터를 구성하는 SQL을 작성해서 Postgres 이미지에 전달할 것이다. 먼저 Postgres 13 버전으로 베이스 이미지를 사용하자. PostgreSQL 베이스 이미지 안에는 기본 OS 파일 시스템 위에 PostgreSQL DB를 설치한 상태로 이미지를 시작할 수 있다.

 

2. 환경 설정 파일 작성

OS 구성 및 PostgreSQL 설치한 상태에서 서버 설정을 변경하기 위해선 설정 파일을 변경해야 한다. 이 설정 파일은/etc/postgresql/custom.conf 파일로 복사해서 서버를 실행할 때 여기에 있는 설정 파일을 사용하도록 지정할 것이다. 다음으로 DB 서버의 초기 데이터를 세팅해야 한다. 데이터는 SQL문을 실행해서 테이블을 생성하고 데이터를 삽입할 수 있다.

 

3. SQL 문 작성

그래서 빌드 컨텍스트에 제가 실행하고 싶은 SQL문을 작성한 다음 이 SQL문을 Postgres 이미지 안의 /docker-entrypoint-initdb.d/ 폴더에 넣어 놓으면, 해당 Postgres 이미지가 컨테이너로 실행될 때 자동으로 이 폴더 안에 있는 SQL문을 실행해 준다.

 

데이터베이스 실행

마지막으로 컨테이너를 실행할 때의 Postgres를 실행시키기 위해 실행 명령은 CMD"postgres"로 지정한다. "-c" 옵션을 통해서 config_file/etc/postgresql/custom.conf파일로 지정해야 한다.

 

2. 데이터베이스 이미지 실행하기

# leafy 소스코드 다운로드
git clone https://github.com/daintree-henry/leafy.git

다음으로 소스 코드를 다운로드 받고 파일의 내용을 한번 확인해 보자. 먼저 터미널을 열어서 EasyDocker 폴더로 이동한다. ls를 실행해 보시면 이전 실습에서 사용한 빌드 폴더를 확인할 수 있다. 강의 자료에 있는 git clone 명령을 사용해서 소스 코드를 다운로드 받아보자. 다시 ls를 실행해 보시면 leafy 폴더가 생성이 된 걸 확인할 수 있다.

 

cd leafy/leafy-postgresql

# leafy postgres 폴더로 이동
cd /leafy/leafy-postgresql

cd 명령을 사용해서 leafy-postgresql로 이동한다.

 

실습 도커파일 브랜치

# 실습 도커파일을 직접 작성
git switch --init

# 도커파일 작성 건너뛰기
git switch 01-dockerfile

그리고 git switch 명령을 사용해서 도커 파일을 직접 작성하고 싶으실 때는 00-init 로 스위치해 주면 된다. 도커 파일 작성을 건너뛰려면 git switch에 01-docker 파일로 이동하시면 됩니다. ls를 눌러보시면 도커 파일을 확인하실 수 있다.

 

 

/leafy/leafy-postgresql

이제 VS 코드로 이 leafy 폴더를 열어보자. leafy 폴더는 leafy -backend, leafy -frontend, leafy -postgreSQL 폴더로 나누어져 있다.

 

/config/postgresql.conf

# POSTGRESQL.CONF FILE
# ---------------------

# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'        # IP 주소, 호스트명 또는 '*'로 모든 IP에 대한 연결을 허용합니다.
max_connections = 100         # 동시 접속자 수 제한
authentication_timeout = 5min   # 인증 시간 초과 시간 (5분)
password_encryption = md5     # 패스워드 암호화 방식

# QUERY TUNING
work_mem = 64MB              # 개별 연결에서 사용 가능한 메모리 양
shared_buffers = 256MB       # 공유 메모리 버퍼 크기
effective_cache_size = 2GB   # 임시 파일 및 인덱스 생성 시 사용할 메모리 크기

# ERROR REPORTING AND LOGGING
log_destination = 'stderr'   # 로그 파일 출력 대상 설정
logging_collector = off        # 로그 수집기를 사용하도록 설정

# REPLICATION
wal_level = replica          # 스트리밍 복제 구성
max_wal_senders = 5          # 스트리밍 복제 전송자의 최대 수

# PERFORMANCE
effective_io_concurrency = 200   # 파일 I/O 수행을 위해 사용할 동시성 레벨
random_page_cost = 1.1           # 임의 액세스 비용 인덱스 스캔시 고려

먼저 leafy-postgresql 폴더를 열어보면 config/postgresql.conf 파일을 확인할 수 있다. 이 설정 파일의 내용을 PostgreSQL 이미지로 복사해서 우리가 원하는 설정으로 변경할 수 있다. 위의 코드처럼 여러 가지 서버 상태 설정을 수정할 수 있다. 자세한 설정 내용은 주석을 참고하자. 중요한 것은 우리가 원하는 설정 파일을 기본 이미지에 덮어 쓰게 함으로써 원하는 상태의 서버 설정으로 만들 수 있다는 것이다.

 

/init/init.sql

그리고 /leafy-postgresql/init/init.sql을 열어보면 이렇게 테이블과 데이터를 생성하는 SQL문을 확인할 수 있다. 테이블은 user, plant, userplant, plantlog 로 총 4개의 테이블을 생성한다. 각각의 테이블에는 개발에 사용할 샘플 데이터들을 INSERT문이 작성되어 있다. Postgres 이미지에서 이 SQL문을 실행시키면 원하는 상태의 데이터를 가질 수 있는 이미지를 빌드할 수 있을 것이다.

 

leafy 애플리케이션 스키마

init.sql이 생성하는 데이터베이스의 스키마를 살펴보자.

  • plant 같은 경우 식물 이름과 식물 타입, 식물 설명, 식물 이미지의 URL, 키우기 적합한 온도와 물을 주는 주기 같은 식물에 대한 정보를 저장하는 테이블이다.
  • users 테이블은 사용자의 이름과 이메일, 비밀번호와 성별 같은 사용자의 정보를 관리한다.
  • user_plants 테이블은 유저 테이블과 식물 테이블을 연결하는 맵핑 테이블이다. 유저가 어떤 식물을 소유하고 있는지에 대한 정보를 관리하고, 소유한 정보에서 식물에 대한 닉네임도 관리하고 있다.
  • plants_logs 테이블은 사용자가 작성하는 다이어리를 저장한다. 다이어리를 작성한 날짜와 물을 주었는지 여부를 체크할 수 있다.

 

Dockerfile 구성하기

# PostgreSQL 13 버전을 베이스 이미지로 사용
FROM postgres:13

# init.sql 파일을 /docker-entrypoint-initdb.d/ 로 복사, /docker-entrypoint-initdb.d/ 에 있는 sql문은 컨테이너가 처음 실행 시 자동 실행됨
COPY ./init/init.sql /docker-entrypoint-initdb.d/

# postgresql.conf 파일을 /etc/postgresql/postgresql.conf 로 복사, 기본 설정 파일을 덮어쓰기하여 새로운 설정 적용
COPY ./config/postgresql.conf /etc/postgresql/custom.conf

# 계정정보 설정
ENV POSTGRES_USER=myuser
ENV POSTGRES_PASSWORD=mypassword
ENV POSTGRES_DB=mydb

EXPOSE 5432

CMD ["postgres", "-c", "config_file=/etc/postgresql/custom.conf"]

다음으로 postgres 이미지를 만들기 위한 Dockerfile 을 확인해 보자. Dockerfile 내용을 보면서 새롭게 작성한다면 그대로 작성하면 된다. 먼저 FROM 지시어에 베이스 이미지를 postgres의 13 버전으로 지정한다. postgres 이미지를 실행하면 기본적인 데이터베이스 서버를 실행할 수 있는 상태로 제공된다. 하지만 데이터가 없기 때문에 아까 확인한 init.sql을 이 이미지 내에서 실행시켜야 한다.

그래서 COPY 지시어를 사용해서 /leafy-postgresql/init/init.sql 파일을 이미지 안의 /docker-entrypoint-initdb.d/ 폴더로 복사해야 한다. /docker-entrypoint-initdb.d/ 폴더는 postgres 이미지가 실행될 때 자동으로 실행하는 SQL을 저장하기로 약속된 폴더이다. 그리고 /config/postgresql.conf 파일을 postgres 이미지 안에 /etc/postgresql/custom.conf 파일로 복사해야 한다. 그리고 postgres의 계정 정보를 설정하기 위해서 환경 변수를 지정해야 한다.

ENV 지시어를 사용해서 POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB 환경 변수를 사용해서 기본 사용자명과 패스워드, 그리고 기본 db를 지정할 수 있다.

그리고 EXPOS *지시어를 사용해서 postgres가 사용하는 포트인 *5432를 추가한다.

마지막으로 CMD 지시어에서는 Postgres SQL을 실행하는 명령인 "postgres""-c"을 통해서 config_file 파일을 /etc/postgresql/custom.conf로 지정한다. 이렇게 설정 파일을 직접 지정하면 이미지 안에 있는 기본 default 설정 파일을 사용하지 않고 저희가 빌드를 통해 주입한 설정 파일을 사용하게 된다.

 

이미지 레지스트리 실습

# 컨테이너 호스트 머신 간 파일 복사
docker cp 원본위치 복사위치

# 컨테이너 -> 호스트머신으로 파일 복사
docker cp 컨테이너명:원본위치 복사위치

# 호스트머신 -> 컨테이너로 파일 복사
docker cp 원본위치 컨테이너명:복사위치

도커에서 Dockerfile을 빌드할 때 내부에서 어떤 식으로 이미지가 구성되는지를 관찰하자. 먼저 실습에 사용될 명령어 중에 추가로 학습할 명령어는 docker cp 명령어이다. docker cp 명령을 사용하면 실행 중인 컨테이너로 특정 파일을 복사할 수 있다. 그리고 컨테이너 내부에 있는 파일도 호스트 머신으로 복사해 올 수 있다.

먼저 기본 명령어 사용 방법은 docker cp 명령에 복사할 원본 파일의 위치와 그리고 한 칸 띄고 복사될 위치를 지정하면 된다. 그래서 컨테이너에서 호스트 머신으로 파일을 복사할 때는 원본 위치의 앞에 컨테이너명에 콜론(:)을 붙여주시면 된다. 그리고 호스트 머신에서 컨테이너로 파일을 복사할 때는 원본 위치는 호스트 머신의 파일위치를 지정하면 된다. 복사할 위치 앞에 복사할 컨테이너의 이름과 콜론(:)을 추가하면 된다.

 

docker run -d --name postgres -e POSTGRES_PASSWORD=password postgres:13

# 기본 postgres:13 이미지를 사용해 컨테이너 실행
docker run -d --name postgres -e POSTGRES_PASSWORD=password postgres:13

이제 실습을 위해서 두 개의 터미널을 열자. 왼쪽 터미널에서 postgres 13 버전의 이미지를 컨테이너로 실행한다. docker run -d --name에 이름은 postgres로 지정한다. –ePOSTGRES_PASSWORDpassword로 지정해서 초기 패스워드를 지정해야 된다. 그리고 이미지 이름을 postgres의 13 버전으로 지정한다. 이미지가 없기 때문에 이미지를 다운로드 받는 걸 확인할 수 있을 것이다. 그리고 정상적으로 실행된 것을 확인할 수 있다.

 

docker exec -it postgres bin/bash

# postgres 컨테이너로 shell 접속
docker exec -it postgres bin/bash

그럼 다시 왼쪽 터미널에서 docker exec 명령을 사용해서 Postgres 컨테이너 안에서 shell 명령을 실행한다. docker exec -it postgres 에 컨테이너 이름을 지정한다. 실행시킬 명령어는 bin/bash 명령을 실행한다.

 

postgres 컨테이너 안의 파일 목록

그럼 이제 Postgres 컨테이너 안에서 shell 명령을 실행한다. 이 내부 파일 시스템을 확인하기 위해서 ls / 명령으로 확인해 보면 이렇게 postgres 컨테이너 안에 파일 목록을 확인할 수 있다.

 

cat /var/lb/postgresql/data/postgresql.conf

# postgres:13 이미지의 기본 설정 파일 확인
cat /var/lib/postgresql/data/posgresql.conf

초기 설정 파일을 먼저 cat 명령어로 확인해 보자. cat /var/lib/postgresql/data/ 데이터 폴더에 postgres.conf 파일을 열어보면 실제로 초기 이미지 안에 있는 설정 파일이 아래 그림와 같이 되어 있는 것을 확인할 수 있다. 이 파일을 복사한 다음에 활용하는 것도 좋은 방법이다.

 

ls -al /docker-entrypoint-initdb.d/

# 해당 폴더에 sql문이 있을 경우 컨테이너 실행 시 자동으로 sql을 실행
ls -al /docker-entrypoint-initdb.d/

 

그리고 ls 명령을 사용해서 ls -al/docker-entrypoint-initdb.d/ 폴더의 내용을 확인해 보면 지금 현재는 아무런 파일도 없는 것을 확인할 수 있다.

 

docker cp ./config/postgresql.conf postgres:etc/postgresql/custom.conf

# 호스트 머신의 ./config/postgresql.conf 파일을 postgres 컨테이너의 /etc/postgresql/custom.conf 파일로 복사
docker cp ./config/postgresql.conf postgres:etc/postgresql/custom.conf

이제 실행 중인 컨테이너 안으로 설정 파일과 sql문을 복사해 오자. 먼저 docker cp 명령을 사용해서 ./config/postgresql.conf 파일을 postgres:etc/postgersql/custom.conf 파일로 지정한다.

 

cat /etc/postgresql/custom.conf

# 컨테이너 내부로 파일이 잘 복사되었는지 확인
cat /etc/posthresql/custom.conf

이제 컨테이너에서 복사가 잘 되었는지 확인하기 위해서 cat 명령으로 /etc/postgresql/custom.conf 파일을 열어보면 이렇게 파일이 잘 복사된 것을 확인할 수 있다.

 

docker cp ./init/init.sql postgres:docker-entrypoint-initdb.d

# 호스트 머신의 ./init/init.sql 파일을 컨테이너의 /docker-entrypoint-initdb.d/init.sql 파일로 복사
docker cp ./init/init.sql postgres:docker-entrypoint-initdb.d

그리고 두 번째로 복사할 파일은 docker cp 명령으로 ./init/init.sql 파일을 postgres:docker-entrypoint-initdb.d 파일로 복사한다. postgres 이미지 컨테이너 내부로 복사하는 것이다. 정상적으로 복사가 될 것이다.

 

ls /docker-entrypoint-initdb.d/
init.sql

# 컨테이너 내부로 파일이 잘 복사되었는지 확인
ls /docker-entrypoint-initdb.d/

다시 ls 명령을 사용해서 /docker-entrypoint-initdb.d/ 폴더를 확인해 보면, 안에 init.sql 파일이 복사된 것을 확인할 수 있다.

 

psql -U postgres -c "\d"

# 현재 DB의 테이블 정보 확인
psql -U postgres -c "\d"

그럼 이제 복사한 SQL문을 실행해 보자. SQL문은 psql 명령으로 확인할 수 있다. psql –u postgres –c 에서 더블 쿼트("")로 묶어주고 역슬래시(\) D를 입력하면 현재 데이터를 확인하실 수 있다. 지금은 SQL문을 실행하기 전이기 때문에 아무 데이터도 없는 것을 확인할 수 있다.

 

psql -U postgres -f /docker-entrypoint-initdb.d/init.sql
수행된 SQL 쿼리문
데이터베이스 테이블 스키마

# /docker-entrypoint-initdb.d/init.sql 파일 실행
psql -U postgres -f /docker-entrypoint-initdb.d/init.sql

이제 동일하게 psql -U postgres -f 명령으로 /docker-entrypoint-initdb.d/init.sql 파일 안의 SQL문을 실행해 보자. 정상적으로 명령이 실행되었으면 다시 이전에 실행했던 데이터 확인 명령을 실행해 보면 이렇게 테이블 4개와 시퀀스 4개가 생성된 것을 확인할 수 있다. Q를 입력해서 나오고, EXIT 명령을 사용해서 컨테이너에서 나온다.

 

docker rm -f postgres

# 실습 컨테이너 삭제
docker rm -f postgres

그리고 docker rm -f 명령으로 Postgres를 사용해서 실습에 사용한 컨테이너를 삭제한다. 이렇게 직접 컨테이너를 실행해서 원하는 상태로 만들어 봤다. 이제 이 과정을 Dockerfile로 작성해서 이미지를 빌드한다. 이제 leafy-postgres 이미지를 빌드해 보자.

 

# Dockerfile 파일 확인
cat Dockerfile

cat 명령으로 Dockerfile을 열어보고 우리가 작성한 내용과 차이가 있는지 한번 비교한다. 이 Dockerfile의 내용을 자세히 살펴보면 이전에 이미지 내에서 실행했던 명령들이 지시어로 작성되어 있다는 것을 다시 한번 확인할 수 있다.

 

docker network ls

# 네트워크 리스트 확인
docker network ls

먼저 docker network ls 명령을 사용해서 leafy 네트워크가 생성되어 있는지 확인한다. 이전 실습에서 만들었기 때문에 leafy 네트워크가 있다면 그냥 넘어가면 된다.

 

docker network create leafy-network

# 새로운 네트워크 생성(leafy-network가 없을 경우에만)
docker network create leafy-network

실습을 뛰어넘어서 네트워크가 없다면 docker network create 명령으로 leafy-network 네트워크를 생성해 주면 된다.

 

docker build -t 레지스트리계정명/leafy-postgres:1.0.0 .

# 도커파일을 사용해 postgres 이미지 빌드
docker build -t 레지스트리계정명/leafy-postgres:1.0.0 .

그럼 이제 Dockerfile을 사용해서 이미지를 빌드한다. docker build -t 명령에 레지스트리계정명과 이미지는 leafy-postgres의 1.0.0 버전으로 지정한다. 그리고 마지막에 점(.)을 추가하고 엔터를 눌러주면 이미지가 빌드 된 것을 확인할 수 있다.

 

docker push 레지스트리계정명/leafy-postgres:1.0.0

# 빌드한 이미지 push
docker push 레지스트리계정명/leafy-postgres:1.0.0

그리고 docker push 명령을 사용해서 이미지를 push 한다.

 

docker run -d --name leafy-postgres --network leafy-network 레지스트리계정명/leafy-postgres:1.0.0

# 빌드한 이미지를 사용해 leafy-postgres 컨테이너 실행
docker run -d --name leafy-postgres --network leafy-network 레지스트리계정명/leafy-postgres:1.0.0

그리고 빌드한 이미지를 컨테이너도 실행해 보자. docker run -d --name leafy-postgres로 지정한다. leafy-postgres 이름은 나중에 스프링 부트에서 접근할 때 사용되기 때문에 꼭 leafy-postgres로 지정해야 된다. 그리고 --network leafy-network를 지정한다. 레지스트리계정명에 leafy-postgres 1.0.0으로 방금 빌드한 이미지로 컨테이너를 실행한다.

 

docker logs leafy-postgres
생성된 테이블 확인하기
정상적으로 수행된 출력 결과

# leafy-postgres 컨테이너의 로그 확인
docker logs leafy-postgres

그리고 docker logs 명령을 사용해서 leafy-postgres 컨테이너의 로그를 확인해 보면 이렇게 init.sql문이 정상적으로 실행되었고 서버도 정상적으로 실행된 것을 확인할 수 있다.

 

docker exec -it leafy-postgres su postgres bash -c "psql --username=myuser --dbname=mydb"

# leafy-postgres 컨테이너 내에서 명령어 실행 후 결과 출력
docker exec -it leafy-postgres su postgres bash -c "psql --username=myuser --dbname=mydb"

그럼 이제 Postgres SQL에 데이터를 한번 조회해 보자. docker exec 명령을 사용할 것이다. 컨테이너명인 leafy-postgres에 실행할 명령어는 su postgres bash -c에 더블 쿼트로 묶어 주자. 더블 쿼트 내용 안에는 PostgreSQL의 명령어인 psql --username=myuser로 지정하고, --dbname=mydb로 지정하면 이렇게 PostgreSQL의 터미널로 접근할 수 있다.

 

SELECT * FROM users;

# users 테이블의 데이터 확인
SELECT * FROM users;

여기서 select 명령을 사용해서 users 테이블의 데이터를 확인한다. 정상적으로 데이터가 들어가 있는 것을 확인할 수 있다. Q를 눌러서 나온 다음에 마찬가지로 plants 테이블에도 데이터를 확인하고, user_plants와 plant_logs에도 초기 데이터가 정상적으로 삽입되어 있는 것을 확인할 수 있다. EXIT로 터미널에서 나온다. Postgres 이미지의 레이어를 확인해 보자.

 

docker image history 레지스트리계정명/leafy-postgres:1.0.0

# leafy-postgres 이미지의 레이어 확인
docker image history 레지스트리계정명/leafy-postgres:1.0.0

도커 이미지, 히스토리에, 레지스트리 계정에, repeat Postgres 1.0.0으로 이미지에 대한 히스토리를 조회해 본다.

 

Postgresql Layers

leafy Postgres SQL 이미지는 총 31개의 레이어로 이루어져 있다. 여기서 0번부터 24번까지는 PostgreSQL의 기본 이미지인 Postgres 13 버전의 레이어이다. 이 PostgreSQL 이미지도 데비안 11이라는 OS 이미지를 베이스 이미지로 하고 있다. 정리하자면 Postgres 이미지는 데비안이라는 OS 이미지를 베이스로 빌드 되었다. 우리는 이 Postgres 이미지를 베이스로 해서 leafy Postgres 이미지를 새롭게 빌드한 것이다. 모든 이미지들은 이렇게 레이어의 중첩 관계를 가질 수 있다. 그래서 25번 레이어부터는 우리가 직접 작업한 내역을 확인할 수 있다. init.sql가 postgresql.conf 파일을 내부에 복사했다. 그리고 ENV를 통해서 PostgreSQL의 기본 계정을 지정했다. 이번 시간에는 PostgreSQL의 데이터베이스를 이미지로 빌드해 봤다. 다음 시간에는 Spring Boot로 개발된 백엔드 애플리케이션을 빌드해보자.