앞서 Golang Application docker 빌드 에서 Golang 애플리케이션을 Docker 에서 빌드하고 Docker 이미지 형태로 배포하는 것을 살펴봤다. 하지만 대부분의 애플리케이션은 데이터베이스 혹은 다른 애플리케이션과 통신을 해야 한다. 이 경우 단일 docker 이미지 형태로 배포하면 테스트하기가 쉽지 않다.
docker compose를 이용하면, Docker 네트워크를 통해서 여러 개의 컨테이너로 구성된 서비스를 구축할 수 있다.
여기에서는 docker compose를 이용해서 MySQL 데이터베이스를 사용하는 Go 애플리케이션을 만들어 볼 것이다.
우리는 counter 애플리케이션을 만들려고 한다. 이 애플리케이션은 Go로 만들어진 count API 서버와 count 정보를 저장하는 MySQL 데이터베이스로 구성된다. 이 애플리케이션은 최종적으로 Kubernetes에 배포될 건데, 배포 전에 로컬에서 테스트 환경을 갖추길 원하고 있다. 개발팀은 docker-compose를 이용해서 로컬 빌드/개발 환경을 구축하기로 했다. 아키텍처는 아래와 같다.
데이터베이스와 관련된 정보를 환경변수를 통해서 관리하기로 했다. 환경변수를 이용해서 관리 하면, 별도의 환경파일을 관리 할 필요가 없고, Docker 컨테이너를 실행할 때, 컨테이너 변수로 데이터베이스 설정 값을 넘길 수 있다는 장점이 있다. 나중에 ECS, Kubernetes 등으로 배포할 때도 환경변수를 이용하여 좀 더 쉽게 배포 할 수 있다.
docker-compose로 실행 할 경우, mysql 컨테이너와 counter app 컨테이너를 따로 실행하게 된다. 이때 mysql 컨테이너가 실행 된 후 counter app 컨테이너를 실행하도록 설정을 할 것이다. 그런데 mysql의 경우 컨테이너가 실행된 뒤에도 실제 연결이 가능할 때까지 시간이(약 10초) 걸리게 된다. 이때 연결을 시도하면 에러가 발생하므로 10초 간격으로 3번 연결 시도를 하도록 코드를 변경했다.
참고로 이 예제코드는 코드안에서 데이터베이스 연결을 retry 체크하는데, 이렇게 하지 않고 docker-compose 설정을 변경해서 컨테이너가 종료되면 컨테이너를 다시 시작하도록 할 수 있다. 아마 이 방법이 더 편할 건데, docker-compose 설정파일 부분에서 자세히 살펴보도록 하겠다.
DB 연결이 끝났다면 "CREATE DATABASE IF NOT EXISTS dbName" 쿼리를 이용해서 dbName 데이터베이스가 없을 경우 새로 데이터베이스를 생성하도록 했다.
environment: 컨테이너가 시작될 때 설정될 환경 변수들이다. Go 애플리케이션은 이 환경변수를 읽어서 데이터베이스에 연결할 수 있다. MYSQL_HOST는 데이터베이스 서버의 이름인데, services 블록의 서비스 이름 "counter-db"으로 서버를 찾을 수 있다.
이 문서에서 실행한 docker-compose 애플리케이션 아키텍처는 아래와 같이 묘사할 수 있을 것이다.
services는 counter-app 과 docunter-db 컨테이너로 구성된다. 이들 컨테이너는 joinc-nw로 서로 연결된다. 각 컨테이너는 IP가 아닌 service name 으로 찾을 수 있기 때문에 IP를 설정할 필요가 없다.
Kubernetes 나 ECS와 같은 컨테이너 기반으로 애플리케이션을 실행 할 경우, 로컬 테스트 환경 구성이 까다로워질 수 있는데, docker-compose.yml을 함께 배포하면 로컬 개발환경도 깔끔하게 구성 할 수 있다.
조금 더 부지런하면 minikube로 구축할 수도 있는데, 개발 팀이 minikube를 이용해서 개발환경을 구축하는 것은 너무 복잡하다. docker-compose를 검토해 보자.
소개
앞서 Golang Application docker 빌드 에서 Golang 애플리케이션을 Docker 에서 빌드하고 Docker 이미지 형태로 배포하는 것을 살펴봤다. 하지만 대부분의 애플리케이션은 데이터베이스 혹은 다른 애플리케이션과 통신을 해야 한다. 이 경우 단일 docker 이미지 형태로 배포하면 테스트하기가 쉽지 않다.
docker compose를 이용하면, Docker 네트워크를 통해서 여러 개의 컨테이너로 구성된 서비스를 구축할 수 있다.
여기에서는 docker compose를 이용해서 MySQL 데이터베이스를 사용하는 Go 애플리케이션을 만들어 볼 것이다.
조건
이 문서를 따라하기 위해서는 아래의 조건이 갖춰져 있어야 한다.
Ubuntu 리눅스: Ubuntu 리눅스가 설치된 로컬 PC 혹은 가상 머신
Docker 설치
Docker-compose 설치: Ubuntu Linux에 docker compose 설치하기 참고.
Golang Application docker 빌드: GoLang 애플리케이션을 dockerizing 하는 법.
git 설치: 예제는 github에서 다운로드(clone)할 수 있다.
애플리케이션 아키텍처
우리는 counter 애플리케이션을 만들려고 한다. 이 애플리케이션은 Go로 만들어진 count API 서버와 count 정보를 저장하는 MySQL 데이터베이스로 구성된다. 이 애플리케이션은 최종적으로 Kubernetes에 배포될 건데, 배포 전에 로컬에서 테스트 환경을 갖추길 원하고 있다. 개발팀은 docker-compose를 이용해서 로컬 빌드/개발 환경을 구축하기로 했다. 아키텍처는 아래와 같다.
GoLang 애플리케이션은 2개의 API를 제공한다.
GET /api/count: 현재 count 값을 읽어서 리턴한다.
PUT /api/count: count의 값을 1만큼 증가 시킨다.
GoLang 애플리케이션
전체 예제 코드는 https://github.com/yundream/joinc-go-hello 에서 다운로드 할 수 있다.
예제 코드 치고는 코드 길이가 제법된다. 중요 부분을 하나씩 떼어내서 살펴보자.
API 개발을 위해서 gorilla 웹 툴킷을 이용했다. gin, Revel 같은 웹 프레임워크에 비교해서 기능은 부족하지만 간단하게 사용 할 수 있어서 널리 사용하고 있다.
데이터베이스를 관리하기 위해서 ORM 라이브러리인 gorm을 사용했다. docker-compose로 애플리케이션을 개발 할경우, 데이터베이스 스키마를 만들어줘야 하는데 gorm의 automigration을 이용해서 자동으로 테이블을 생성할 수 있다.
gorm이 사용할 Counter 구조체다. 나중에 gorm은 이 구조체를 기반으로 테이블 스키마를 만들고, 쿼리를 실행한다.
GET /api/count과 POST /api/count 2개의 API를 만들었다.
GET /api/count 과 POST /api/count를 위한 handler 함수다. getCounter는 count 값을 읽어서 리턴하고, addCounter는 count를 1 증가시킨다.
데이터베이스와 관련된 정보를 환경변수를 통해서 관리하기로 했다. 환경변수를 이용해서 관리 하면, 별도의 환경파일을 관리 할 필요가 없고, Docker 컨테이너를 실행할 때, 컨테이너 변수로 데이터베이스 설정 값을 넘길 수 있다는 장점이 있다. 나중에 ECS, Kubernetes 등으로 배포할 때도 환경변수를 이용하여 좀 더 쉽게 배포 할 수 있다.
docker-compose로 실행 할 경우, mysql 컨테이너와 counter app 컨테이너를 따로 실행하게 된다. 이때 mysql 컨테이너가 실행 된 후 counter app 컨테이너를 실행하도록 설정을 할 것이다. 그런데 mysql의 경우 컨테이너가 실행된 뒤에도 실제 연결이 가능할 때까지 시간이(약 10초) 걸리게 된다. 이때 연결을 시도하면 에러가 발생하므로 10초 간격으로 3번 연결 시도를 하도록 코드를 변경했다.
참고로 이 예제코드는 코드안에서 데이터베이스 연결을 retry 체크하는데, 이렇게 하지 않고 docker-compose 설정을 변경해서 컨테이너가 종료되면 컨테이너를 다시 시작하도록 할 수 있다. 아마 이 방법이 더 편할 건데, docker-compose 설정파일 부분에서 자세히 살펴보도록 하겠다.
DB 연결이 끝났다면 "CREATE DATABASE IF NOT EXISTS dbName" 쿼리를 이용해서 dbName 데이터베이스가 없을 경우 새로 데이터베이스를 생성하도록 했다.
이제 환경 변수 값을 이용해서 dsn 을 만들고 mysql 데이터베이스에 연결한다.
연결하고 나면 db.Migrator() 를 호출해서 Counter 구조체를 기반으로 테이블을 마이그레이션 한다. 만약 테이블이 없건나, 테이블 스키마가 변경됐다면 자동으로 테이블을 생성 혹은 변경해준다.
테이블을 새로 만드는 경우 db.Create를 호출해서 count 를 0으로 설정한다.
예제 코드를 docker 이미지로 만들기
Make가 설치돼 있으면 "make"를 실행하면 된다. 혹은 build 명령을 이용해서 직접 이미지를 만들어도 된다.
docker-compose.yml 설정파일
이제 docker-compose.yml 파일을 분석해 보자.
joinc-nw 를 만들었다. driver를 따로 설정하지 않았는데, 이 경우 bridge driver가 설정된다. 위 설정은 아래와 같이 바꿀 수 있다.
counter-app 서비스 설정을 보자.
image: joinc/counter:latest 이미지로 컨테이너를 만든다.
ports: 8000 번 포트로 포트포워딩 했다.
depends_on: counter-mysql 컨테이너가 실행되고 나면, 실행한다.
networks: joinc-nw 네트워크를 사용한다.
environment: 컨테이너가 시작될 때 설정될 환경 변수들이다. Go 애플리케이션은 이 환경변수를 읽어서 데이터베이스에 연결할 수 있다. MYSQL_HOST는 데이터베이스 서버의 이름인데, services 블록의 서비스 이름 "counter-db"으로 서버를 찾을 수 있다.
MySQL 데이터베이스의 service 블록 정보를 보자.
image: mysql 최신 이미지로 컨테이너를 실행한다.
ports: mysql의 기본 포트 3306을 사용한다.
envorionment: MySQL 서버의 root 패스워드를 설정한다.
networks: joinc-nw를 사용한다.
docker-compose 실행
docker-compose up 명령으로 애플리케이션을 실행해보자.
docker-compose ps 명령으로 컨테이너를 확인해 보자.

curl 을 이용해서 GET /api/count 와 POST /api/count가 잘 작동하는지 테스트해보자.
잘 작동하는 걸 확인할 수 있다.
정리
이 문서에서 실행한 docker-compose 애플리케이션 아키텍처는 아래와 같이 묘사할 수 있을 것이다.

services는 counter-app 과 docunter-db 컨테이너로 구성된다. 이들 컨테이너는 joinc-nw로 서로 연결된다. 각 컨테이너는 IP가 아닌 service name 으로 찾을 수 있기 때문에 IP를 설정할 필요가 없다.
Kubernetes 나 ECS와 같은 컨테이너 기반으로 애플리케이션을 실행 할 경우, 로컬 테스트 환경 구성이 까다로워질 수 있는데, docker-compose.yml을 함께 배포하면 로컬 개발환경도 깔끔하게 구성 할 수 있다.
조금 더 부지런하면 minikube로 구축할 수도 있는데, 개발 팀이 minikube를 이용해서 개발환경을 구축하는 것은 너무 복잡하다. docker-compose를 검토해 보자.
Recent Posts
Archive Posts
Tags