7장에서 도커 컴포즈 사용법을 배웠다. 해당 과정에서는 여러 개의 컨테이너로 구성된 애플리케이션을 YAML로 정의하고 도커 컴포즈 명령을 사용해 이 애플리케이션을 관리하는 방법을 배웠다. 이후 운영 환경에 적합하도록 모니터링을 추가했다.
이번엔 운영 환경을 위한 설정이 필요없는 환경을 위해 도커 컴포즈로 다시 돌아와 보자. 이런 행위가 가능 한 것은 패키지에 모든 의존 모듈이 함께 들어가기 때문이다. 하지만 환경에 따라 애플리케이션의 동작을 달리해야할 필요는 있다.
다시 도커 컴포즈가 왜 필요한지 생각해보자.
이는 여러 개의 컨테이너로 구성된 애플리케이션을 단일 도커 엔진 호스트에서 실행하기 위한 동구이다.
여러 환경을 구성해 용도별로 사용(테스트 환경, 사용자 테스트 환경, 시스템 테스트 환경 등...)하려면, 환경마다 애플리케이션이 다르게 동작해야한다. 무턱대고 여러 컨테이너가 이유 없이 같은 포트를 통해 요청을 받거나, 서버에 있는 파일을 여러 컨테이너가 쓰려하면 안된다.
이런 기능을 하기 위해선 도커 컴포즈 파일에서 이런 기능을 지원해야한다.
그 전에 먼저 도커 컴포즈가 어떤 도커 리소스가 애플리케이션의 일부인지 아닌지 판단하는 원리를 고려해야한다. 이 기준은 레이블의 명명 규칙을 따른다.
docker-compose -f ./numbers/docker-compose.yml up -d
(무작위 숫자 애플리케이션(8장)을 실행
docker-compose -f ./todo-list/docker-compose.yml up -d
(to-do 애플리케이션(6장)을 실행
docker-compose -f ./todo-list/docker-compose.yml up -d
(한번 더 실행)
여기서 확인해야하는 것은 한번 더 실행에서 running이 떴다는 것이다. 이는 새로운 컨테이너를 실행하지 않았다는 의미이다.
도커 컴포즈는 도커 리소스가 어떤 애플리케이션의 일부인지 아닌지를 판정하기 위해 프로젝트라는 개념을 사용한다. 프로젝트 이름의 기본값은 도커 컴포즈 파일이 들어 있던 디렉터리명인데, 도커 컴포즈가 도커 리소를 만들 때 이 프로젝트 이름을 리소스의 이름에 접두사로 붙이고, 컨테이너 이름에는 번호를 접미사로 붙인다.
docker-compose -f ./todo-list/docker-compose.yml -p todo-test up -d
docker container ls
docker container port todo-test-todo-web-1 80
웹 사이트가 무작위 포트를 개방하므로 새로 실행한 애플리케이션에 접근하려면 개방한 포트를 따로 알아야 한다. 프로젝트 이름을 따로 지정해 주었기 때문에 컴포즈의 입장에서 이 애플리케이션은 기존에 실행 중인 것과 별개가 된다. 또 새로운 프로젝트 이름과 일치되는 리소스가 없기 때문에 새로운 컨테이너를 실행하게 된다.
이와 같은 방법으로 같은 애플리케이션도 여러 벌 실행할 수 있다. 하지만 해당 기능은 무작위로 정해진 공개 포트를 일일이 알아내야 하기 때문에 귀찮다. 따라서 설정을 오버라이딩하는 것이 더 좋은 방식이다.
하나의 애플리케이션을 여러 설정으로 실행해야 할 필요가 생긴 경우 대부분의 컴포즈 파일을 여러개 두는 방법을 사용한다. 하지만 컴포즈 파일의 내용은 90%이상이 중복이니 수정 시 누락이 발생한다면 환경 차이 문제로 되살아난다. 오버라이드가 이를 해결할 수 있다.
도커 컴포즈는 여러 파일을 합쳐 컴포즈 파일을 구성하는데, 나중에 지정된 파일의 내용이 이전 파일의 내용을 오버라이드한다.
도커 컴포즈는 하나 이상의 파일이 인자로 지정됐을 때 이들 파일을 병합한다. 특히 config 부명령이 이때 유용하다. 이 명령은 입력 파일의 내용을 검증해 내용이 유효한 경우에만 최종 출력을 준다. 이 출력은 실제 반영되는 컴포즈 파일이 되므로 오버라이드 파일을 적용한 결과를 예상할 수 있다.
docker-compose -f ./todo-list/docker-compose.yml -f ./todo-list/docker-compose-v2.yml config
config 부명령은 설정 파일의 내용이 유효한지 확인할 뿐 실행하지 않는다. 대신 기본 파일과 오버라이드 파일이 병합된 컴포즈 파일을 미리 볼 수 있다. 위의 스크린샷을 보면 이 병합된 파일의 내용에서 다른 모든 속성은 기본 파일의 내용을 다르지만 imapge 속성의 값만 오버라이드 파일의 내용이 반영된 것을 알 수 있다.
도커 컴포즈가 오버라이드 파일을 병합하는 순서는 인자로 받은 순서를 따른다. 즉 인자로 지정된 순서가 먼저인 파일이 순서가 나중인 파일보다 우선한다. config로 병합된 파일의 내용을 보면 모든 속성이 알파벳순으로 정렬돼 있다.
docker container rm -f $(docker container ls -aq)
docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-dev.yml -p numbers-dev up -d
//개발 환경용
docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml -p numbers-test up -d
//테스트 환경용
docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-uat.yml -p numbers-uat up -d
//인수 테스트 환경용
이제 애플리케이션 세 개를 실행 중이다. 이들 각각은 모두 별개의 도커 네트워크를 사용하므로 서로 독립적이다. 이들 모두를 한 서버에서 실행하고, 포트를 달리해서 관련 환경에 접근할 수 있다.
이제 인수테스트 환경은 http://localhost에서, 시스템 환경은 http://localhost:8080, 개발 환경은 http://localhost:8088로 접근 할 수 있다. 환경별로 실행된 컨테이너는 도메인 네임을 사용해 서로를 식별하지만, 같은 도커 네트워크에 접속한 컨테이너끼리만 통신이 가능하다.
도커 컴포즈는 클라이언트에서 동작하는 도구이다. 그러므로 애플리케이션을 관리하려면 컴포즈 파일에 접근이 가능해야 하며, 애플리케이션을 실행할 때 지정한 프로젝트 이름도 알고 있어야 한다. 테스트 환경의 애플리케이션을 종료하려면 해당 환경의 컨테이너와 네트워크를 제거해야 한다. 일반적인 상황에선 docker-compose down 명령을 실행하면 되지만, 프로젝트 이름을 기본값에서 변경한 지금은 docker-compose up 명령을 실행할 때 사용한 모든 파일과 프로젝트 정보를 정확히 지정해야 한다.
docker-compose down
프로젝트 이름을 기본값에서 변경하지 않고, 기본 컴포즈 파일만 사용했다면 해당 명령 동작
docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml down
프로젝트 이름 변경 없이 오버라이드 파일만 진행했다면 해당 명령이 동작해야함
docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml -p numbers-test down
프로젝트 이름을 기본값에서 변경했으니 애플리케이션을 종료할 대도 다시 프로젝트 이름을 정확하게 지정해야 함
이러한 오류는 컴포즈 오버라이드 파일에 네트워크의 이름이 명시적으로 지정됐기 때문이다. 두 번째 명령에 프로젝트 이름을 따로 지정하지 않았으므로 기본값인 numbers가 사용됐으며, 컴포즈는 이 프로젝트 이름을 따라 numbers-numbers-web-1과 numbers-numbers-api-1이라는 이름의 컨테이너를 찾는다. 그러나 애플리케이션을 실행할 때 numbers-api라는 프로젝트 이름을 사용했으니 앞서 언급한 해당 컨테이너는 생성된 적이 없다. 따라서 이미 제거 됐다 판단하고 프로젝트 이름이 붙지 않고 명시적으로 이름이 지정된 네트워크를 찾아내 이를 제거하려고 시도한다. 하지만 이 네트워크에는 아직 연결된 컨테이너가 있기 때문에 네트워크도 제거할 수 없다.
여러 개의 애플리케이션을 도커 네트워크를 사용해 분리하고 각 환경 간의 차이를 도커 오버라이드 파일을 통해 기술할 수 있었다. 그러나 환경 간에 애플리케이션 설정을 달리해야하는 경우도 있다.
이번 장에선 환경 변수와 설정 파일을 모두 다루기 위해 도커 컴포즈의 세부적인 동작을 자세히 소개한다.
환경에 따라 다음 세 가지 설정값에 변화를 준다.
로깅 : 환경별 로그 수준을 달리해 개발 환경에서 좀 더 자세한 로그를 출력하도록 할 수 있다.
데이터베이스 프로바이더 : 애플리케이션 컨테이너에 포함된 파일 형태의 간이 데이터베이스와 별도의 데이터베이스 컨테이너를 선택 할 수 있다.
데이터베이스 커넥션 문자열 : 별도의 데이터베이스를 사용하는 경우 적용할 데이터베이스 접속 정보를 지정할 수 있다.
비밀값은 도커 컴포즈, 도커 스웜, 쿠버네티스에서 지원하는 기능이다. 설정값을 주입하기에 매우 유입한 기능이다. 컴포즈 파일에서 비밀값의 원본 위치와 대상 위치를 모두 지정할 수 있는데, 원본 위치는 컨테이너 런타임이 비밀값의 값을 읽어 오는 곳이고 대상 위치는 컨테이너 안에서 비밀값이 위치할 경로를 의미한다.
비밀값이 해당 파일에 정의돼 있지 않다면 단독으로 사용할 수 없다. 따라서 오버라이드를 해야하는데 이 때 추가로 프로퍼티를 정의한다. 프로퍼티란 애플리케이션에 설정값을 주입해 동작을 변화시키는 역할을 한다. 원하는 것만 골라 적용해도 되지만 각각 나름의 장점이 있다.
environment : 프로퍼티는 컨테이너 안에서 사용되는 환경 변수를 추가한다. 이 환경 변수 값을 적용하면 애플리케이션이 데이터베이스로 파일 데이터베이스인 SQLite를 사용한다. (설정값을 전달하는 방법 중 가장 간단한 방법임)
env_file : 텍스트 파일의 경로를 값으로 받는다. 이 파일에 정의된 환경 변수가 컨테이너에 적용된다. 텍스트 파일에 변수 이름과 값을 등호로 구분해 한 줄에 하나씩 정의한다. 같은 환경 변수를 여러 번 정의하지 않아도 환경 변수를 여러 컴포넌트에서 공유해 사용할 수 있다.
secrets : services나 networks처럼 컴포즈 파일의 최상위 프로퍼티이며, 이 프로퍼티의 실제 값 혹은 경로가 정의된다.
docker container rm -f $(docker container ls -aq)
docker-compose -f ./todo-list-configured/docker-compose.yml -f ./todo-list-configured/docker-compose-dev.yml -p todo-dev up -d
오버라이드 파일을 적용해 애플리케이션 실행
curl http://localhost:8089/list
실행된 애플리케이션에 요청 보내기
docker container logs --tail 4 todo-dev-todo-web-1
애플리케이션 로그 확인
개발 환경에는 비밀값과 환경 변수로 설정값을 주입하며, 이 비밀값과 환경 변수는 컴포즈 파일과 컴포즈 파일에 지정된 경로에 있는 설정 파일로부터 읽어 오는 방법을 사용했다.
이번에는 호스트 컴퓨터의 환경변수 값을 컨테이너에 전달하는 방법을 이용해 설정값을 주입해보자.
특별한 기술을 필요로 하는 것은 아니고 달러 기호와 중괄호를 이용하면 된다.
문제 발생
와 파일이 없다는데? 책과 비교해보니 .env가 없었다.
때문에 나는 직접 .env를 작성했다.
이를 통해 아무 인자 없이 컴포즈 명령을 실행하면 .env 파일에 포함된 기본값을 컴포즈 파일 및 컴포즈 실행 옵션으로 사용한다. 이 기본값을 적용하면 코어 컴포즈 파일에 테스트 환경의 설정값이 적용되므로 데이터베이스 컨테이너와 웹 컨테이너가 모두 생성된다.
컴포즈 파일과 함께 환경 파일을 잘 관리하면 어떤 파일이 어떤 환경의 설정값에 해당하는지에 대한 문서 역할을 대신 할 수 있다. 다만 도커 컴포즈는 .env 파일만을 환경 파일로 간주하기 때문에 환경 파일 여러 개를 만들어 바꿔 가면서 사용할 수 없다.
이제 핵심이다
도커 컴포즈를 이용해 서로 다른 환경을 정의하는 방법을 3가지 핵심 영역 위주로 살펴보았다.
이러한 설정 워크플로에서 가장 중요한 점은 모든 환경에서 같은 이미지를 사용한다는 것이다.