가동 중지 시간을 최소화하면서 Java 웹앱을 배포하기 위한 모범 사례는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1640333

문제

대규모 Java 웹앱(>100MB .war)을 배포할 때 현재 다음 배포 프로세스를 사용하고 있습니다.

  • 애플리케이션 .war 파일은 개발 시스템에서 로컬로 확장됩니다.
  • 확장된 애플리케이션은 개발 시스템에서 라이브 환경으로 rsync:ed됩니다.
  • rsync 후 라이브 환경의 앱 서버가 다시 시작됩니다.이 단계는 반드시 필요한 것은 아니지만 배포 시 애플리케이션 서버를 다시 시작하면 "java.lang.OutOfMemoryError:PermGen 공간"은 빈번한 클래스 로딩으로 인해 발생합니다.

이 접근 방식의 좋은 점은 다음과 같습니다.

  • rsync는 개발 시스템에서 실제 환경으로 전송되는 데이터의 양을 최소화합니다.전체 .war 파일을 업로드하는 데는 10분 이상 걸리는 반면, rsync에는 몇 초 정도 걸립니다.

이 접근 방식의 나쁜 점은 다음과 같습니다.

  • rsync가 실행되는 동안 파일이 업데이트되므로 애플리케이션 컨텍스트가 다시 시작됩니다.이상적으로는 rsync가 아직 실행 중일 때가 아니라 rsync가 완료된 후에 다시 시작해야 합니다.
  • 앱 서버를 다시 시작하면 약 2분의 가동 중지 시간이 발생합니다.

다음 속성을 가진 배포 프로세스를 찾고 싶습니다.

  • 배포 프로세스 중 가동 중지 시간이 최소화됩니다.
  • 데이터 업로드에 소요되는 최소 시간입니다.
  • 배포 프로세스가 앱 서버별로 진행되는 경우 앱 서버는 오픈 소스여야 합니다.

질문:

  • 명시된 요구 사항을 고려할 때 최적의 배포 프로세스는 무엇입니까?
도움이 되었습니까?

해결책

전쟁 파일로 변경을 추진할 때 RSYNC가 잘 작동하지 않는다는 것이 주목되었습니다. 그 이유는 전쟁 파일이 본질적으로 zip 파일이며 기본적으로 압축 된 멤버 파일로 작성되기 때문입니다. 멤버 파일 (압축 전)의 작은 변경으로 인해 ZIP 파일의 대규모 차이가 발생하여 RSYNC의 Delta-Transfer 알고리즘이 비효율적입니다.

가능한 솔루션 중 하나는 사용하는 것입니다 jar -0 ... 원래 전쟁 파일을 만듭니다. 그만큼 -0 옵션을 알려줍니다 jar 전쟁 파일을 만들 때 멤버 파일을 압축하지 않도록 명령하십시오. 그럼 언제 rsync 기존 버전과 새로운 버전의 전쟁 파일을 비교할 때 델타 전송 알고리즘은 작은 차이를 생성 할 수 있어야합니다. 그런 다음 rsync가 diffs (또는 원본 파일)를 압축 형태로 보냅니다. 예를 들어 rsync -z ... 또는 압축 데이터 스트림 / 전송.

편집 : 전쟁 파일이 어떻게 구성되는지에 따라 사용해야 할 수도 있습니다. jar -0 ... 구성 요소 JAR 파일을 작성합니다. 이것은 안정된 제 3 자 JAR 파일이 아닌 종종 변경 될 수있는 JAR 파일에 적용됩니다.

이론적 으로이 절차는 정기 전쟁 파일을 보내는 것보다 크게 개선되어야합니다. 실제로 나는 이것을 시도하지 않았으므로 그것이 효과가 있다고 약속 할 수 없습니다.

단점은 배포 된 전쟁 파일이 훨씬 커질 것입니다. 이로 인해 WebApp 시작 시간이 더 길어질 수 있지만 그 효과는 미미한 것으로 생각됩니다.


다른 접근법은 전적으로 전쟁 파일을보고 (거의) 결코 변하지 않을 가능성이있는 도서관 항아리를 식별 할 수 있는지 확인하는 것입니다. 이 항아리를 전쟁 파일에서 꺼내어 Tomcat 서버에 별도로 배포하십시오. common/lib 예배 규칙서; 예 : 사용 rsync.

다른 팁

업데이트:

이 답변이 처음 작성된 이후 가동 중지 시간 없이 Tomcat에 war 파일을 배포하는 더 나은 방법이 나타났습니다.최신 버전의 Tomcat에서는 war 파일 이름에 버전 번호를 포함할 수 있습니다.예를 들어 파일을 배포할 수 있습니다. ROOT##001.war 그리고 ROOT##002.war 같은 맥락으로 동시에.이후의 모든 것 ## 컨텍스트 경로의 일부가 아닌 Tomcat에 의해 버전 번호로 해석됩니다.Tomcat은 앱의 모든 버전을 계속 실행하고 새로운 요청과 세션을 완전히 실행된 최신 버전으로 제공하는 동시에 시작된 버전에서 이전 요청과 세션을 정상적으로 완료합니다.버전 번호 지정은 tomcat 관리자와 catalina ant 작업을 통해서도 수행할 수 있습니다.더 많은 정보 여기.

원래 답변:

Rsync는 델타 전송 알고리즘이 파일의 변경 사항을 찾고 압축되지 않은 파일의 작은 변경으로 인해 결과 압축 버전이 크게 변경될 수 있으므로 압축된 파일에는 효과적이지 않은 경향이 있습니다.이러한 이유로 네트워크 대역폭에 병목 현상이 발생하는 경우 압축된 버전보다는 압축되지 않은 war 파일을 재동기화하는 것이 합리적일 수 있습니다.

배포를 수행하기 위해 Tomcat 관리자 애플리케이션을 사용하는 데 어떤 문제가 있습니까?전체 war 파일을 원격 위치에서 Tomcat 관리자 앱으로 직접 업로드하고 싶지 않다면 이를 프로덕션 상자의 자리 표시자 위치로 rsync(위에서 언급한 이유로 압축하지 않음)하고 war로 다시 패키징할 수 있습니다. 그런 다음 현지 관리자에게 전달하십시오.Tomcat 관리자 앱을 사용하여 배포 스크립트를 작성할 수 있도록 Tomcat과 함께 제공되는 멋진 개미 작업이 있습니다.

귀하의 접근 방식에는 귀하가 언급하지 않은 추가 결함이 있습니다.애플리케이션이 부분적으로 배포되는 동안(rsync 작업 중) 변경된 인터페이스가 동기화되지 않거나 새/업데이트된 종속성을 사용할 수 없는 등의 일관되지 않은 상태에 있을 수 있습니다.또한 rsync 작업에 소요되는 시간에 따라 애플리케이션이 실제로 여러 번 다시 시작될 수 있습니다.Tomcat에서 변경된 파일 수신 및 다시 시작 동작을 끌 수 있고 꺼야 한다는 것을 알고 계십니까?실제로 프로덕션 시스템에는 권장되지 않습니다.Tomcat 관리자 앱을 사용하면 언제든지 수동으로 또는 Ant 스크립트를 사용하여 애플리케이션을 다시 시작할 수 있습니다.

물론 다시 시작하는 동안 사용자는 애플리케이션을 사용할 수 없습니다.그러나 가용성이 너무 걱정된다면 로드 밸런서 뒤에 중복 웹 서버가 있을 것입니다.업데이트된 war 파일을 배포할 때 배포가 끝날 ​​때까지 로드 밸런서가 일시적으로 모든 요청을 다른 웹 서버로 보내도록 할 수 있습니다.다른 웹 서버에 대해 헹구고 반복하십시오.

다운타임이 고려되는 모든 환경에서는 중복성을 통해 안정성을 높이기 위해 일종의 서버 클러스터를 실행하고 있을 것입니다.클러스터에서 호스트를 꺼내 업데이트한 다음 다시 클러스터에 넣습니다.혼합 환경에서 실행할 수 없는 업데이트가 있는 경우(예: DB에 호환되지 않는 스키마 변경이 필요함) 적어도 잠시 동안 전체 사이트를 중단해야 합니다.비결은 원본을 삭제하기 전에 교체 프로세스를 시작하는 것입니다.

Tomcat을 예로 사용 - CATALINA_BASE를 사용하여 실행 코드와 별도로 Tomcat의 모든 작업 디렉터리를 찾을 수 있는 디렉터리를 정의할 수 있습니다.소프트웨어를 배포할 때마다 디스크의 이전 코드 옆에 새 코드가 상주할 수 있도록 새 기본 디렉터리에 배포합니다.그런 다음 새 기본 디렉터리를 가리키는 또 다른 Tomcat 인스턴스를 시작하고 모든 것을 시작하고 실행한 다음 로드 밸런서에서 이전 프로세스(포트 번호)를 새 프로세스로 바꿀 수 있습니다.

스위치 전체에서 세션 데이터를 보존하는 것이 염려된다면 모든 호스트에 세션 데이터를 복제할 파트너가 있도록 시스템을 설정할 수 있습니다.해당 호스트 중 하나를 삭제하고, 업데이트하고, 세션 데이터 백업을 선택하도록 다시 가져온 다음 두 호스트를 전환할 수 있습니다.클러스터에 여러 쌍이 있는 경우 릴리스 요구 사항, 기업 요구 사항 등에 따라 모든 쌍의 절반을 삭제한 다음 대량 전환을 수행하거나 한 번에 한 쌍씩 수행할 수 있습니다. .그러나 개인적으로 저는 세션을 그대로 유지하면서 업그레이드를 시도하는 것보다 최종 사용자가 가끔 활성 세션이 손실되는 상황을 겪도록 허용하는 것을 선호합니다.

이는 모두 IT 인프라, 릴리스 프로세스 복잡성 및 개발자 노력 간의 균형입니다.클러스터가 충분히 크고 원하는 것이 충분히 강하다면 대부분의 업데이트에 대해 가동 중지 시간 없이 교체할 수 있는 시스템을 설계하는 것은 충분히 쉽습니다.대규모 스키마 변경으로 인해 실제 다운타임이 발생하는 경우가 많습니다. 업데이트된 소프트웨어는 일반적으로 기존 스키마를 수용할 수 없고 데이터를 새 DB 인스턴스에 복사하고 스키마 업데이트를 수행한 다음 서버를 새 DB로 전환하는 작업을 수행할 수 없기 때문입니다. 새 DB가 복제된 후 이전 DB에 기록된 데이터가 누락될 것입니다.물론 리소스가 있는 경우 업데이트되는 모든 테이블에 대해 새 테이블 이름을 사용하도록 개발자에게 새 앱을 수정하도록 작업할 수 있으며, 다음과 같이 데이터로 새 테이블을 올바르게 업데이트하는 라이브 DB에 트리거를 배치할 수 있습니다. 이전 버전에 의해 이전 테이블에 기록됩니다(또는 뷰를 사용하여 한 스키마를 다른 스키마에서 에뮬레이션할 수도 있음).새로운 앱 서버를 가동하고 클러스터로 교체하세요.게임을 구축할 수 있는 개발 리소스가 있는 경우 다운타임을 최소화하기 위해 플레이할 수 있는 게임이 많이 있습니다.

아마도 소프트웨어 업그레이드 중 가동 중지 시간을 줄이는 가장 유용한 메커니즘은 앱이 읽기 전용 모드에서 작동할 수 있도록 하는 것입니다.이는 일부 필요한 기능을 사용자에게 제공하지만 데이터베이스 수정 등이 필요한 시스템 전체 변경을 수행할 수 있는 기능을 제공합니다.앱을 읽기 전용 모드로 설정한 다음 데이터를 복제하고, 스키마를 업데이트하고, 새 DB에 대해 새 앱 서버를 가져온 다음, 새 앱 서버를 사용하도록 로드 밸런서를 전환하세요.유일한 가동 중지 시간은 읽기 전용 모드로 전환하는 데 필요한 시간과 로드 밸런서 구성을 수정하는 데 필요한 시간입니다(대부분 가동 중지 시간 없이 처리할 수 있음).

내 조언은 폭발 버전과 함께 rsync를 사용하지만 전쟁 파일을 배포하는 것입니다.

  1. WebApp 버전을 폭발시킬 수있는 라이브 환경에서 임시 폴더를 만듭니다.
  2. RSYNC 폭발 버전.
  3. 성공 후 RSync는 라이브 환경 기계의 임시 폴더에서 전쟁 파일을 만듭니다.
  4. 서버 배포 디렉토리의 오래된 전쟁을 임시 폴더의 새로운 전쟁으로 바꾸십시오.

jboss 컨테이너 (Tomcat을 기반으로하는) Beacause는 원자력과 빠른 작동으로 새로운 전쟁을 새로운 전쟁으로 교체하는 것이 좋습니다.

웹 서버에서 현재 웹 애플리케이션의 로컬 사본을 해당 디렉토리에 RSYNC로 만들고 심지어 상징적 링크를 사용하여 "GO"로 많은 다운 타임없이 Tomcat을 포인트로 포인트 할 수 없습니까?

RSYNC에 대한 귀하의 접근 방식은 추출 된 전쟁에 대한 접근 방식이 꽤 좋습니다. 또한 생산 서버에 열선을 활성화해서는 안된다고 생각하기 때문에 다시 시작됩니다. 그렇다면 유일한 단점은 서버를 다시 시작해야 할 때 다운 타임입니다.

응용 프로그램의 모든 상태가 데이터베이스에서 보유되어 있다고 가정하므로 다른 사용자가 다른 앱 서버 인스턴스에있는 동안 일부 사용자가 하나의 앱 서버 인스턴스에서 작업하는 데 아무런 문제가 없습니다. 그렇다면,

두 개의 앱 서버를 실행하십시오: 두 번째 앱 서버 (다른 TCP 포트에서 듣는)를 시작하고 애플리케이션을 배포하십시오. 배포 후 Apache HTTPD의 구성 (mod_jk 또는 mod_proxy)을 업데이트하여 두 번째 앱 서버를 가리 키십시오. Apache HTTPD 프로세스를 우아하게 다시 시작합니다. 이렇게하면 다운 타임이없고 새 사용자가 없으며 요청은 자동으로 새 앱 서버로 리디렉션됩니다.

앱 서버의 클러스터링 및 세션 복제 지원을 사용할 수 있다면 두 번째 앱 서버가 시작 되 자마자 재 동기화되므로 현재 로그인 된 사용자에게는 부드럽습니다. 그런 다음 첫 번째 서버에 액세스 할 수없는 경우 종료하십시오.

이것은 응용 프로그램 아키텍처에 따라 다릅니다.

내 응용 프로그램 중 하나는로드 밸런싱 프록시 뒤에 있습니다. 그곳에서 가운데가 가동 중지 시간을 효과적으로 근절합니다.

정적 파일이 큰 전쟁의 큰 부분이라면 (100mo는 꽤 큽니다), 전쟁 밖으로 나가서 애플리케이션 서버 앞에 웹 서버 (예 : Apache)에 배치하면 속도가 빨라질 수 있습니다. 게다가 Apache는 일반적으로 서블릿 엔진보다 정적 파일을 제공하는 데 더 나은 작업을 수행합니다 (대부분의 사람들이 해당 영역에서 상당한 진전을 이루더라도).

따라서 큰 뚱뚱한 전쟁을 일으키는 대신 다이어트와 농산물에 넣습니다.

  • Apache 용 정적 파일이있는 큰 뚱뚱한 지퍼
  • 서블릿 엔진의 뚱뚱한 전쟁.

선택적으로, 전쟁을 더 얇게 만드는 과정에서 더 나아가십시오. 가능하면 응용 프로그램 서버 레벨에서 자주 변하지 않는 문지 및 기타 항아리를 배치하십시오.

더 가벼운 전쟁을 성공적으로 만들면 아카이브보다는 디렉토리를 귀찮게하지 않을 것입니다.

이 접근법의 강점 :

  1. 정적 파일은 Apache에서 "배포 된"핫 "배포"될 수 있습니다 (예 : 현재 디렉토리를 가리키는 기호 링크를 사용하고 새 파일을 압축하고 Symlink 및 Voilà를 업데이트하십시오).
  2. 전쟁은 더 얇아지고 배치하는 데 시간이 덜 걸릴 것입니다.

이 접근법의 약점 :

  1. 서버 (웹 서버)가 하나 더있어서 더 복잡하게 추가됩니다.
  2. 빌드 스크립트를 변경해야합니다 (큰 거래 IMO가 아님).
  3. RSYNC 로직을 변경해야합니다.

이것이 귀하의 질문에 대한 답변 여부는 확실하지 않지만, 내가 한 몇 가지 프로젝트에서 사용하거나 만난 배포 프로세스에 대해서만 공유하겠습니다.

당신에게 동시에, 나는 전체 전쟁 재배치 또는 업데이트를하는 것을 기억하지 못합니다. 대부분의 경우 업데이트는 몇 개의 JSP 파일, 라이브러리, 일부 클래스 파일로 제한됩니다. 영향을받는 아티팩트를 관리하고 결정할 수 있으며 일반적으로 업데이트 스크립트와 함께 업데이트를 ZIP 파일로 포장했습니다. 업데이트 스크립트를 실행하겠습니다. 스크립트는 다음을 수행합니다.

  • 덮어 쓰는 파일을 오늘날의 날짜와 시간을 가진 폴더로 백업하십시오.
  • 내 파일을 풀고
  • 응용 프로그램 서버를 중지하십시오
  • 파일을 이동하십시오
  • 응용 프로그램 서버를 시작하십시오

가동 중지 시간이 우려되는 경우, 일반적으로 상태를 공유하지 않고 끈적 끈적한 세션 라우팅을 제공하는 라우터를 사용하더라도 내 프로젝트는 일반적으로 HA입니다.

내가 궁금한 또 다른 것은 왜 rsync해야합니까? 스테이징/개발 환경에서 결정을 결정하고 Live와 함께 델타 점검을 수행하지 않고 필요한 변경 사항을 알 수 있어야합니다. 대부분의 경우 데이터베이스 연결, SMTP 서버 등과 같은 프로덕션 서버 사용 자원을 정의하는 특정 속성 파일과 같이 어쨌든 파일을 무시하기 위해 RSYNC를 조정해야합니다.

이것이 도움이되기를 바랍니다.

Permspace 세트는 무엇입니까? 나는 이것도 성장할 것으로 기대하지만 ~해야 한다 구형 수업을 모으고 나서 내려 가세요? (또는 클래스 로더가 여전히 주위에 앉습니까?)

Outloud를 생각하면 별도의 버전 또는 날짜 이름 디렉토리로 동기화 될 수 있습니다. 컨테이너가 Symbolic Links를 지원하는 경우 루트 프로세스를 사용하여 Symbolic Link를 통해 컨텍스트의 파일 시스템 루트를 전환 한 다음 Sigcont를 전환 할 수 있습니까?

초기 컨텍스트는 다시 시작됩니다. 모든 컨테이너에는 클래스 파일 또는 정적 리소스 변경에서 자동 유입을 비활성화하는 구성 옵션이 있습니다. web.xml 변경에서 자동 재배치를 비활성화 할 수 없으므로이 파일이 마지막으로 업데이트되는 파일입니다. 따라서 Auto Redeploy를 비활성화하고 Web.xml을 마지막으로 업데이트하는 경우 컨텍스트 재시작이 표시됩니다. ~ 후에 전체 업데이트.

새 버전의 WebApp을 별도의 디렉토리에 업로드 한 다음 실행 중 하나와 교환하거나 Symlinks를 사용합니다. 예를 들어, "MyApp"이라는 Tomcat WebApps 디렉토리에 Symlink가 있으며, 여기에는 "MyApp-1.23"이라는 현재 WebApp을 가리 킵니다. 새 웹 앱을 "MyApp-1.24"에 업로드합니다. 모든 것이 준비되면 서버를 중지하고 Symlink를 제거하고 새 버전을 가리키는 새 버전을 만들고 서버를 다시 시작하십시오.

성능을 위해 프로덕션 서버에서 자동 재조정을 비활성화하지만, 비 원자 방식으로 WebApp 내에 파일을 변경하면 정적 파일이나 JSP 페이지조차도 링크가 깨지거나 악화되는 방식으로 변경 될 수 있으므로 문제가 발생할 수 있습니다.

실제로 WebApp은 실제로 공유 저장 장치에 위치하므로 클러스터링,로드 밸런스 및 장애 조치 서버에는 동일한 코드가 있습니다.

상황의 주요 단점은 RSYNC가 수정 또는 추가 된 파일 만 전송할 수 있으므로 업로드가 더 오래 걸린다는 것입니다. 이전 웹 폴더 폴더를 새로운 폴더에 먼저 복사하고 상당한 차이를 만들고 실제로 문제가되는 경우 rsync를 복사 할 수 있습니다.

Tomcat 7에는 "라는 멋진 기능이 있습니다.병렬 배포"이것은이 유스 케이스를 위해 설계되었습니다.

요점은 .war를 WebApps 또는 Symlinked 바로 아래에서 디렉토리로 확장한다는 것입니다. 응용 프로그램의 연속 버전은 명명 된 디렉토리에 있습니다 app##version, 예를 들어 myapp##001 그리고 myapp##002. Tomcat은 기존 세션을 이전 버전으로 처리하고 새 버전으로 이동하는 새로운 세션을 처리합니다.

캐치는 당신이되어야한다는 것입니다 매우 Permgen 누출에주의하십시오. 이것은 많은 Permgen을 사용하는 성배에서 특히 그렇습니다. VisualVM은 당신의 친구입니다.

프록시가있는 2 개 이상의 Tomcat 서버를 사용하면됩니다. 그 프록시는 Apache/Nignix/Haproxy 일 수 있습니다.

이제 각 프록시 서버에는 포트가 포함 된 "in"및 "out"URL이 구성되어 있습니다.

먼저 서비스를 중단하지 않고 Tomcat에서 전쟁을 복사하십시오. 전쟁이 배치되면 Tomcat 엔진에 의해 자동으로 열립니다.

참고 크로스 점검 unpackwars = "true"및 autode elplice = "true"node "host"내부 Server.xml의 "true"

이것을 좋아해 보인다

  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true"
        xmlValidation="false" xmlNamespaceAware="false">

이제 Tomcat의 로그를보십시오. 오류가 없으면 성공적으로 올라갔습니다.

이제 테스트를 위해 모든 API를 누르십시오

이제 프록시 서버로 오십시오.

새로운 전쟁 이름으로 배경 URL 매핑을 변경하기 만하면됩니다. Apache/Nignix/Haproxy와 같은 프록시 서버에 등록하는 데 시간이 거의 걸리지 않으므로 최소 다운 타임을 느낄 수 있습니다.

나타내다 -- https://developers.google.com/pagespeed/module/domains URL 매핑

Resin을 사용하고 있으며 Resin은 웹 앱 버전을 지원합니다.

http://www.caucho.com/resin-4.0/admin/deploy.xtp#versioningandgracefulupgrades

업데이트 : Watchdog 프로세스는 Permgenspace 문제에도 도움이 될 수 있습니다.

"모범 사례"가 아니라 내가 방금 생각한 것입니다.

GIT와 같은 DVC를 통해 웹 앱을 배포하는 것은 어떻습니까?

이렇게하면 서버로 전송할 파일을 알아낼 수 있습니다. 당신은 또한 그것이 파열되는 것으로 판명되면 그것을 뒤로 물러나는 좋은 방법을 가지고 있습니다.

몇 가지 매개 변수를 가져오고 서버간에 파일을 rsync하는 bash 스크립트를 작성했습니다. 더 큰 아카이브를 위해 RSYNC 전송 속도를 높이십시오.

https://gist.github.com/3985742

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top