Properties 파일은 어디에 위치하는게 좋을까요?

2013-01-16 13:03

일단 Java Spring기준으로 이야기 해보면 PropertiesLoaderSupport로 부터 상속받은 다양한 Configurer 클래스들을 사용하고 있는데요. Resource array로 정의되어있는 locations는 1. classpath 2. file 3. ftp 4. http 정도를 지원 하고 있는데요

대부분의 properties, message 파일들은 classpath: 형식으로 정의하여 project소스코드에 같이 넣어 사용하고 있습니다. 하지만 J2EE Module 형식으로 다른 하부 Project에 연결이 되어있기때문에 엔터프리이즈 환경에서 RAC형식의 Oracle을 사용하거나 다양한 DBMS를 사용할 경우 DB의 순차작업 (RAC1<->RAC2)으로 인하여 database설정을 변경 하거나 message를 수정하여 적용할 경우 모든 하부 Project를 다시 build하고 배포해야한다는 단점이 있는것 같습니다.

그래서 굳이 이러한 파일들을 classpath로 local에 놓아두는것이 효율적인가? 에 대한 고민을 하다 이런 설정파일을 모두 Http로 받아와서 처리 하도록 변경 하였습니다.

이렇게 됐을 경우의 장단점은 장점 1. 모든 properties를 중앙집중적으로 관리 할수있다. - db접속정보, db pool 설정 등을 하나의 파일로 관리하므로 해당 변경이 db자원을 효율적으로 분배 배정할 수 있다.(DBMS의 Connection 갯수가 1000개를 넘어가게 되면서 각 모듈 프로젝트에서 사용하는 자원을 한곳에서 파악 할수있다.) 2. 설정의 변경및 배포가 용이 하다. - 설정 변경시 각 모듈 프로젝트에서 사용하는 core project를 변경후 각 모듈을 모두 build하고 재 배포할 필요없이 Http로 가져오는 파일 하나의 설정만 배포후 build없이 WAS를 restart만 하면 적용이 된다.

단점 1. Http로 서비스 되는 만큼 보안에 취약할 경우가 발생할 수 있다. - 이럴경우 나의 해결 방안은 사내 ip로만 접속가능한 서비스를 하나 두는 걸로 간단히 해결했지만 이또한 완벽한 보안은 안되지 않을까? 한다. 그런데 이런 db접속정보나 설정 정보가 classpath에 있는것보다 http가 더 취약하다고 할 수 있을까? 2. Http로 서비스되는 설정파일에 대한 소스관리와 배포를 따로 해야한다. - 모듈 프로젝트와는 별개의 소스관리가 이루어져야하는 불편함이 있을 수 있다.
3. test및 local개발시 번거로울 수 있다. - 대부분의 설정 정보들은 test, real로 두벌씩 만들어 개발시에는 test 설정파일을 이용하도록 하고 서비스시에는 real설정 정보들을 사용 하도록 하고 있기때문에 test의 설정파일까지 http로 가져오게 되면 개발시 번거로운 일들이 발생 할 수 있다. 그래서 나의 경우는 test나 개발 환경에서는 기존의 classpath의 설정 파일을 가지고 오게 하고 서비스 시에는 JAVA_OPTS인자로 기존 override하도록 처리 하였다.

이정도가 될듯 한데 이걸 고민하게된 시작은 설정정보를 가지는 하나의 core project에 20개 이상의 하부 프로젝트들이 생기면서 core의 설정 파일을 변경 하게 되면 모든 각각의 하부 프로젝트들을 build 후 배포 하는 작업이 너무 불편하게 느껴서이다.

간단하게 J2EE Module 프로젝트 다이어그램으로 보면 변경전

변경후

위와 같이 접근 방식만 변경 하더라도 상당한 효과를 볼수있다

더불어 테스트를 위하여서는 local파일을 읽고 자바옵션으로 설정은 아래와 같이 처리 하였다. 1. Srping 설정

<bean
		class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="searchContextAttributes" value="true" />
		<property name="contextOverride" value="true" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="locations">
			<list>
				<value>classpath:spring/dbpool.properties</value>
				<value>classpath:spring/connection.properties</value>
				<value>classpath:spring/solr.properties</value>
<!-- real 환경에서 java_opts 의 값을 override하도록 처리 -->
				<value>${db-config}</value>
				<value>${solr-config}</value>
				<value>${dbpool-config}</value>
			</list>
		</property>
	</bean>
  1. java options

    JAVA_OPTS="-Ddb-config=[http://dev.configure.slipp.net/connection.propertiesnfig](http://dev.configure.slipp.net/connection.propertiesnfig) -solr-config=[http://dev.configure.slipp.net/solr.propertiesnfig](http://dev.configure.slipp.net/solr.propertiesnfig) -Ddbpool-config=[http://dev.configure.slipp.net/dbpool.properties](http://dev.configure.slipp.net/dbpool.properties) -Dcom.sun.management.jmxremote ..."
    

위와같이 수정하여 db서버의 작업이 발생시에 모든 모듈의 담당자들이 대기하면서 각 서비스들을 build하고 재배포하는 비효율적인 업무를 간단히 was의 restart로만으로 적용 되도록 하였다. 물론 어느정도의 규모가 되고 RAC구성처럼 failover처리가 되는 설정의 변경사항이 있는 상황에서의 조건일듯 하지만 기본적으로 J2EE Module project가 늘어가면서 생기는 불편함은 어느정도 줄일수 있을것이라 생각 되는데 다른분들의 의견은 어떠신지 물어봅니다.(향후는 dynamic configure를 처리하도록 해볼 예정이다.)

8개의 의견 from SLiPP

2013-01-16 16:07

zookeeper를 이용해서 설정정보를 관리하는 것이 어떨가요?

watcher를 이용하면 restart도 없을것 같은데 테스트를 안해봤습니다. ^ ^;; 다만 가능성만

2013-01-16 21:15

설정 파일을 외부로 분리해 관리하는 것도 좋은 시도라 생각한다. 하지만 다음과 같은 조건이 갖추어져야 한다고 생각한다.

  • 네가 말한대로 보안상 이슈가 있기 때문에 이 부분은 확실하게 처리하지 않으면 이슈가 될 듯 하다. 내부 IP에서만 접근 가능하도록 제한하면 될 것으로 생각한다. 단, 이 경우 웹 서버가 증가하면서 IP가 확대되는 경우 이슈가 발생하는 경우를 종종 경험했다. 아무래도 운영하면서 고려해야할 부분이 한 가지 증가하는 단점이 발생한다.
  • 설정 파일에 대한 버전 관리가 되어야 하지 않을까? 소스와 설정 파일 관리가 이원화되어 관리되기 때문에 설정 파일을 새로 배포한 후 WAS를 재기동하는 시점에 문제가 발생할 경우 이전 설정 파일로 원복할 수 있어야 한다. 하지만 이와 같이 버전 관리를 할 경우 WAS의 소스를 재배포해야되는 단점이 있기 때문에 최초 목적한 바를 달성하기 힘든 상황이 생긴다.

개인적으로는 설정 파일을 외부에 두는 것도 좋은 시도이기는 하지만 소스 코드와 설정 파일이 이원화되어 배포하고 관리해야 하기 때문에 유지보수 관점에서 추천할 방법은 아니라고 생각한다.

나도 지금까지 프로젝트를 진행하면서 properties와 같은 설정 파일을 외부에 빼서 관리하고 싶었으나 아직까지 시도하지 못했다. 아무래도 외부로 분리해서 관리하는 것이 개발하는 시점에 부담을 줄 수 있기 때문이다. 하지만 소스 코드와의 변경되는 시점이 다르기 때문에 별도의 모듈로 분리해서 관리하는 것이 좋다고 생각한다. 모듈은 jar 파일로 분리해서 관리하고 배포하는 시점에 하나로 묶여 같이 배포되는 방식이 좋지 않을까 생각한다. 물론 좀 더 철저하게 관리하려면 maven 사내 저장소와 같은 곳에 설정 파일을 가지는 jar파일을 버전 관리하고 각각의 프로젝트에서 버전을 변경함으로써 새로운 버전의 jar 파일로 빌드할 수 있도록 하는 방식이 되겠지.

이와 같은 방식으로 개발 및 운영할 경우 http://www.slipp.net/wiki/pages/viewpage.action?pageId=950399 문서에서 다루고 있는 방법으로 로컬 개발과 개발 서버, 실 서버의 설정 파일을 분리해서 관리할 수 있을 것으로 생각한다.

위와 같은 시도를 한 이유가 설정 파일을 배포하고 WAS만 재시작하려는 의도인데, 배포 작업을 개선하면 이 같은 효과를 낼 수 있지 않을까? 먼저 배포를 위한 WAR 파일을 하나 만들고, 배포 서버에서 점진적으로 무점검 배포를 할 수 있는 구조를 만든다면 배포에 대한 부담은 덜 수 있지 않을까 생각한다. 물론 이 같은 배포 과정을 만들기가 생각보다 많은 노력이 필요하지만 서비스를 안정적으로 운영한다는 측면에서는 충분히 투자할만한 가치가 있다고 생각한다.

배포 과정 전체가 명령 하나로 실행할 수 있는 방식으로 개선하고, 배포 버전에 대한 소스 관리를 철저히 한다면 난 오히려 classpath에서 관리하는 것이 좋다고 본다.

마지막으로 프로젝트 규모에 따라 달라지겠지만 이 같은 필요가 있는 프로젝트는 생각보다 많지 않을 듯하다. 중,소규모의 프로젝트는 소스 코드와 설정 파일은 같이 관리해도 큰 이슈가 없으리라 생각한다.

2013-01-17 13:53

@자바지기 기본적으로 중,소규모 프로젝트에는 이런 이슈가 없다는 것은 맞다. 중,대형의 구분이 힘들지만 대략 하나의 core project를 보는 module project들이 5개 이상이 될경우에는 이런 방법을 한번 생각 해보아야 하지 않나 한다. 또한 여기선 언급하지 않았지만 java뿐만이 아니라 ruby, python도 동일한 configure를 사용하기 때문에 이럴 경우에도 외부로 설정을 빼는게 의미있는 경우가 있을것 같다.

그리고 물론 설정 file도 버젼관리를 하여야 한다. 하지만 이걸 jar로 묶어서 module project에서 build하여 배포하는건 지금 나의 환경에는 맞지 않는것 같다. (현재 하나의 core를 보는 module 프로젝트들이 batch를 포함하여 20개 가까운 project들이 있는데 각 project를 webinstrano로 build하는데만 하나의 프로젝트에 5분 이상이 소요되고 있어서 build없이 각 module project WAS의 재시작만으로 변경된 설정을 가져가게 하려 한거다. 그리고 점점더 프로젝트들이 무거워 지면서 build시간 자체가 길어지고 있는 중이고)

원래 의도가 배포를 최소화 하면서 설정을 변경하는 것이기때문에 만일 설정들을 jar로 묶고 이것을 각 module project에서 배포한다면 classpath에 들어있는 상황이랑 같은 것이 될듯 하다.

설정 파일의 버젼관리를 하더라도 jar로 포함시켜 버리면 properties변경이 발생할 경우 core-project 1개 + module-project 10개 일경우 10번의 build와 배포가 이루워 져야 하는데 설정을 외부로 빼면 각 하부 module-project에서 사용하는 WAS만 재시작 하면 되니 시간이 많이 단축 될것 같다. 소스코드 개발시에도 properties가 바뀌지 않는 한 기존과 build, 배포가 동일 하기때문에 특별히 overhead가 되는 부분도 없지 않을까? 한다.

그리고 이야기 했던것 처럼 배포 명령어 로 처리하는 방안도 고려 하고있다.(예를 들어 tomcat startup.sh 파일을 두벌 만든후 webinstrano에서 상황에 따라 실행할 명령을 다르게 함)

2013-01-17 14:43

@jhindhal.jhang 내가 말한 버전 관리는 웹 서버에서 관리하는 properties 설정 파일에 대한 버전 관리를 의미한다. 물론 버전 관리 도구를 통해서 버전 관리는 할 것이라 생각하고 버전 관리 도구에서 관리하는 설정 파일을 웹 서버에 배포했을 때도 버전 관리가 되어야 하지 않을까?

예를 들어 다음과 같은 구조가 되겠지.

http://dev.configure.slipp.net/connection.properties-v1.2 http://dev.configure.slipp.net/connection.properties-v1.1 http://dev.configure.slipp.net/connection.properties-v1.0

위와 같이 버전 관리가 되어야 배포 후에 문제가 발생할 경우 원복하기 쉽지 않을까 생각했다. 그런데 다음과 같은 구조로 하면 될 듯 하네.

http://dev.configure.slipp.net/connection.properties => 최종 버전 http://dev.configure.slipp.net/connection.properties-20130115 http://dev.configure.slipp.net/connection.properties-20130110

위와 같이 배포하는 시점에 현재 버전을 배포 일자나 시간을 포함하는 버전으로 변경하고 새로운 버전을 최종 버전으로 관리할 수 있는 스크립트를 만들면 되지 않을까 생각되네. 물론 문제가 발생하면 가장 최근 버전의 설정 파일(위 예에서는 connection.properties-20130115)을 최신 버전으로 원복하는 스크립트를 만들면 될테고...

네가 말한대로 외부로 설정 파일을 분리하는 경우 이 설정 파일을 사용하는 서비스가 모두 한 팀에서 관리하고 있다면 괜찮겠지만 여러 개의 팀에서 같이 관리하는 경우 배포에 대한 의존성이 발생할 가능성이 있다고 판단한다. 즉, 설정 파일을 변경할 때 여러 팀의 의존 관계를 같이 고려한 후에 배포해야 되기 때문이다. 따라서 이 같은 경우에는 오히려 의존성을 강화함으로써 배포의 유연성을 털어트리지 않을까라는 생각이 든다.

이 시도는 내가 지금까지 생각하지 못했던 방식의 설정 파일 분리 방안이라 나름 신선하네. 이와 관련해 경험이 쌓이면 가끔씩 공유해 주면 좋겠다.

2013-01-17 14:58

@자바지기 버전관리 구조는 이야기 한데로 http://dev.configure.slipp.net/connection.properties-v1.2 http://dev.configure.slipp.net/connection.properties-v1.1 http://dev.configure.slipp.net/connection.properties-v1.0 구조로 되고 설정파일에 대한 버전관리를 진행한다.

그리고 이야기한 외부팀이나 공용으로 사용하는 부분은 좀 detail한 부분인데 현재 팀 내부에서도 여러 설정 oracle, mysql, cassandra, MQ...의 설정을 같이 사용하고 있는데 기본적인 룰은 아래와 같이 풀려고 한다.

  1. 동일 resource(DBMS, MQ, solr...)를 사용하는 부분에서는 default 설정을 두고 사용하도록 한다.
  2. 각 module별 특이 설정이나 동일 resource를 사용하더라도 설정이 달라야 할경우는 key를 개별 module에 맞게 설정한다. 즉, ```

    jdbc url

    default.oracle1.jdbc.url=jdbc:oracle:thin:... report.oracle1.jdbc.url=jdbc:oracle:thin:... batch.oracle1.jdbc.url=jdbc:oracle:thin:...

db poll

default.was.maxPoolSize=30 default.was.minPoolSize=1

default.report.maxPoolSize=10 default.report.minPoolSize=1

default.batch.maxPoolSize=10 default.batch.minPoolSize=1

``` 위와 같은 방식으로 설정을 다르게 사용하는 부분에서는 서로간의 의존성이 없도록 문제를 해결했다. 아직 우린 타팀과의 설정을 공유 하는 부분이 없지만 naming rule만 지킨다면 다른팀의 설정과 굳이 충돌날 부분이 없을듯 하다. 또한 위와같이 할경우 dbms의 connection pool에 대한 관리가 한눈에 파악되기 때문에 문제(connection이 부족할경우)가 발생시 쉽게 중앙에서 관리가 된다는 장점도 있구...

의견 추가하기

연관태그

← 목록으로