ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Docker tomcat 8.5 컨테이너에 웹 어플리케이션(war) 배포하기
    Docker 2020. 6. 26. 17:57

    docker에 tomcat 8.5 컨테이너를 실행하고 로컬 PC에서 빌드한 war 파일을 배포 하는 방법을 소개하겠다.

     

    " 나는 Docker로 웹 어플리케이션(WAS)을 개발한다. "


    1.  나는 Docker로 웹 어플리케이션을 개발한다.

    2.  Docker tomcat 8.5 컨테이너에 웹 어플리케이션(war) 배포하기 

    3.  Tomcat에 웹 어플리케이션(war)을 포함하여 이미지 만들기

    4.  나만의 Tomcat base image 만들기

     

    전통적인 방식에서 Java 웹 어플리케이션을 개발하는 것을 생각해 보자. 제일 먼저 java 버전, JSP 서블릿 버전, 스프링프레임워크(Springframework) 버전, tomcat 버전 등의 기술요소를 자신의 목적에 맞게 결정할 것이다. 빌드툴은 가장 많이 사용하는 maven이나 gradle을 선택할 것이다. 필요한 기술요소들을 결정 하였으면 이제 프로젝트를 생성하여 본격적인 개발을 진행하게 된다.

     

    전통적인 웹 어플리케이션 개발 싸이클

    이클립스에서 maven webapp Sample 프로젝트를 생성 한다. maven 프로젝트는 가장 많이 사용하는 방식으로 1번과 같은 디렉토리 구조를 갖는다. 웹 디렉토리 루트(webapp)에 index.jsp을 생성하고  'Hello World!'를 <body> tag내에 작성 한다. 이클립스에 톰캣 server를 등록하고 생성된 웹 프로젝트를 모듈로 등록한다.

     

    이클립스는 다양한 plug-in을 제공 하는데 servers는 그 중 하나이다. 이클립스 내에서 tomcat과 프로젝트를 연동하여 구동하게 해 준다. Servers에서 tomcat 서버를 제어할 수 있어 개발시에 매우 편리하다.

     

    Tomcat을 실행 시키고 웹 브라우저를 이용하여 로컬 서버에 접속한다.  코드가 잘 작성 되었는지 웹 페이지를 확인하다.

     

    개발자들은 이클립스 개발툴을 사용하여 이러한 일련의 과정을 반복하면서 자바 웹 어플리케이션을 개발하게 된다. 가장 일반적인 웹 어플리케이션을 개발하는 방법이다.

     

     

    여기까지는 Docker 개발자랑 다른것이 없다. 이제부터 개발된 소스를 Docker 컨테이너에 배포하는 과정을 진행 하도록 하겠다. 먼저 Docker에 대해 간단히 살펴보자. 

     

    Docker 이해하기

    " Docker를 이해하기 위해 익숙한 물리 서버 개발 환경을 기준으로  비교해 보자."

     

    1번의  물리서버는 개발자에게 익숙한 모습이다. 하나의 물리 장비에 Tomcat이 설치 되어 있고 여기에 웹 어플리케이션을 구동하는 구조이다. 물리 서버는 웹 전용으로만 사용된다. 2번은 VM 구조이다. 큰 컴퓨팅 자원을 가상화 OS(Hypervisor)를 이용하여 여러개의 가상서버(VM)으로 나눈다. 각  VM에는 Guest OS라 불리는 OS를 설치하여 사용한다. 그 위에 bin/lib가 위치하고 톰캣을 설치하여 웹 어플리케이션이 서비스된다. 3번은 경우가 Docker안에서 구동되는 컨테이너 모습이다. 컨테이너에서는 bin/lib레이어 위에 톰캣을 설치하고 war를 배포한다. 빨간 점선이 기능적으로 동일한 웹 어플리케이션을 제공하는 영역이 된다.

     

    성능으로 보면 물리서버를 단독으로 사용하는 것이 가장 좋다. 그러나 자원의 낭비가 심하다. 이를 효율적으로 사용하기 위해 가상화 기술이 탄생한 것이다. 그런데 VM에서는 개별적으로 돌아가는 Guest OS로 인한 성능 이슈가 발생한다. 이와 달리 컨테이너 환경(Docker)에서는 가상서버의 Guest OS 영역이 없다. bin/lib 레이어에서 어플리케이션이 바로 실행된다. 이러한 구조는 가상서버(VM)와 다른 컨테이너의 장점이다. 가볍고 성능이 빠르다. 

     

    Docker 아키텍처

    Docker 컨테이너 아키텍처를 살펴보자. 내 컴퓨터에 Client 와 Docker host가 있고  Registry는 Docker hub로 가정 하겠다. docker 를 설치하면 docker host라고 하는 가상의 컴퓨팅 레이어가 내 컴퓨터에 설치된다. 개발자는 커맨드 창에서  CLI 명령으로 docker deamon을 제어할 수 있다. docker host에는 실행 모듈들이 컨테이너(Containers) 단위로 실행되게 되는데 이 컨테이너가 Docker의 핵심이다. 여기서 실행되는 이미지는 원격에 있는 이미지 저장소에서 가져 오게 된다. 로컬에 다운받은 이미지를 docker 명령으로 인스턴스화(실행) 하게 된다.

     

     

    박스로 선택된 곳이 자바 웹 어플리케이션을 실행할 톰캣 컨테이너이다. 컨테이너 이미지는 독립적으로 구동되는 완벽한 실행 환경을 갖게 된다. 하나의 물리서버에 실행 되지만 컨테이너들 끼리는 서로 고립되어 서로에게 간섭을 주지 않게 된다.

     

    하나의 컨테이너를 하나의 물리 서버라고 생각 하자.

    하나의 개별 컨테이너안을 들여다 보면 라이브러리 위에 프로그램이 설치된 구조로 되어 있다. bin/lib는 시스템 실행에 필요한 최소한의 모듈을 포함한다. 여기에 필요한 프로그램(tomcat)을 설치하여 하나의 이미지로 생성한다.

     

    이미지는 새로운 개념이 아니다. 윈도우 시스템을 깔고 그 위에 자주 사용하는 프로그램을 설치하기 위해 하루 이상의 시간을 소비한 경험을 떠올려 보자. 윈도우 초기화 때마다 반복되는 작업이 싫어 deamon 툴을 이용하여 윈도우를 이미지로 생성하였다. 이후 이미지 설치를 통해 빠른 시간에 동일한 환경을 구성할 수 있었다. 

     

    컨테이너 이미지도 이와 동일한 개념이다. 필요한 시스템을 설치하고 복잡한 환경구성을 한 후 이를 이미지로 저장한다. 이렇게 생성된 이미지를 간단한 명령으로 실행하면 복잡한 시스템을 바로 사용할 수 있게 된다. 개발자에게는 신세계가 펼쳐지는 것이다. tomcat, gitlab, mysql, jenkins를 설치 하고자 하면 이미지를 로컬에 내려받고 실행만 하면 된다. 수분 내에  이 복잡한 개발 환경을 갖게 되는 것이다. 

     

    이미지는 docker deamon 을 통해 실행되어 인스턴스화 된다. docker 구조가 너무 복잡 하다고 여겨지면 간단하게 하나의 컨테이너를 하나의 물리 서버라 생각 하자. 그러면 이해하기가 쉬워 진다.

     

    Tomcat 8.5 설치하기

    docker에 톰캣 이미지를 설치하자. tomcat 이미지를 검색한다. 커맨드 창에서 아래 명령을 수행하면 이미지 목록이 출력된다.

    docker search tomcat
    NAME                          DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
    tomcat                        Apache Tomcat is an open source implementati…   2761                [OK]                
    tomee                         Apache TomEE is an all-Apache Java EE certif…   79                  [OK]                
    dordoka/tomcat                Ubuntu 14.04, Oracle JDK 8 and Tomcat 8 base…   54                                      [OK]
    bitnami/tomcat                Bitnami Tomcat Docker Image                     35                                      [OK]
    kubeguide/tomcat-app          Tomcat image for Chapter 1                      28                                      
    consol/tomcat-7.0             Tomcat 7.0.57, 8080, "admin/admin"              17                                      [OK]
    cloudesire/tomcat             Tomcat server, 6/7/8                            15                                      [OK]
    aallam/tomcat-mysql           Debian, Oracle JDK, Tomcat & MySQL              13                                      [OK]
    arm32v7/tomcat                Apache Tomcat is an open source implementati…   10                                      
    rightctrl/tomcat              CentOS , Oracle Java, tomcat application ssl…   6                                       [OK]
    maluuba/tomcat7-java8         Tomcat7 with java8.                             5                                       
    unidata/tomcat-docker         Security-hardened Tomcat Docker container.      4                                       [OK]

     

     Docker Image Naming rule은 'registry호스트명/repo명/이미지명:태그'로 구성된다. registry Host명은 표준 dns규칙을 따른다. 저장소는 Microsoft, Amazon AWS, Google Cloud Platform 등 다양할 수 있다. 두번째는 리포지토리명이다. 마지막은 이미지명과 태그로 구성된다. 태그는 다양한 버전을 붙여 사용하게 된다.

     

     

    먼저, Tomcat 8.5 이미지를 로컬에 가져온다. docker pull 명령을 통해 이미지를 가져올 수 있다. 

    docker pull tomcat:8.5
    8.5: Pulling from library/tomcat
    Digest: sha256:a3fbac94a2b8a72eeb3c9d4d0f7db90ee9ebd770ec3baa373ed130d7636e9fea
    Status: Downloaded newer image for tomcat:8.5
    docker.io/library/tomcat:8.5
    

     

    로컬 PC에 download 받은 이미지는 docker image ls 명령으로 확인 가능하다. download 받은 이미지가 많은 경우 grep 명령을 통해 확인이 가능하다.

    docker image ls | grep tomcat
    tomcat                               8                      e010d327a904        2 weeks ago         529MB
    tomcat                               8.5                    e010d327a904        2 weeks ago         529MB

     

    이미지를 실행해 보자. 이미지는 docker run 명령으로 실행 된다. 이 명령을 통해 컨테이너가 생성되고 인스턴스(실행)화 된다.  여기서 '-d'는 백그라운드 수행을 의미한다. 리눅스에서 명령 맨 마지막에 &를 붙이면 콘솔창을 빠져 나와도 명령이 계속 수행되는 것과 동일하다. 만약 이 옵션을 제거하면 커맨드 창을 중지하는 즉시 docker 이미지 실행이 중지 된다. '-p' 는 포트 포워딩 옵션이다. 아래 명령을 해석하면 로컬 호스트에 10000 포트로 접속을 하면 컨테이너 내부에 7000번 포트로 수행되는 어플리케이션으로 트래픽을 전달하라는 의미이다. 명령 맨 마지막에는 실행할 이미지명을 적어 준다.

    docker run -d --name="my-tomcat" -p 10000:7000 tomcat:8.5
    290d7bf753544a4744fea2554fdb38a99a615eb145bd8119bd04fbec358b988e

     

     

    실행된 이미지를 확인해 보자. 실행된 이미지는 docker ps 명령으로 확인이 가능하다.

    docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                               NAMES
    290d7bf75354        tomcat:8.5            "catalina.sh run"   4 minutes ago       Up 4 minutes        8080/tcp, 0.0.0.0:10000->7000/tcp   my-tomcat

     

    이제 브라우저에서 tomcat이 잘 구동 되었는지 접속해 보자. 페이지가 실행될 수 없다는 에러 메시지가 나온다. 

     

    .

    트러블 슈팅

    컨테이너에 접속하여 로그를 확인해 보자. docker ps명령을 수행하면 container name을 확인할 수 있다. docker logs 명령으로 컨테이너 로그를 확인할 수 있다.

    docker logs -f my-tomcat
    INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
    INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 1.1.1d  10 Sep 2019]
    INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
    INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
    INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 891 ms
    INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
    INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.56
    INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
    INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 85 ms

     

     로그를 보면 tomcat 서버는 잘 구동 되었다. 그럼 무엇이 문제인가?  로그를 분석해 보면 포트가 8080번으로 구동된 것을 확인할 수 있다. 접속이 안되었던 것은 포트가 잘못 설정되어서 발생한 문제였다. 포트 포워딩으로 연결한 컨테이너의 포트는 7000번이었는데 내부에 실행된 톰캣 포트는 8080이어서 트래픽이 톰캣까지 전달되지 않았다. 그래서 페이지가 동작하지 않았던 것이다. 톰캣 이미지를 변경할 수 없으므로 포트포워딩의 포트 번호를 변경해 주자. 변경하는 방법은 기존 컨테이너를 삭제하고 다시 생성해 주면된다. 삭제하기 위해서는 먼저 실행 중인 컨테이너를 중지해야 한다.

    컨테이너를 삭제하지 않고 이전에 수행했던 docker run 명령을 다시 수행 할 경우 컨테이너가 존재한다고 에러를 발생할 것이다. 그때에는 아래의 절차데로 컨테이너를 중지하고 삭제한 후 다시 명령을 수행 하면 된다.

    docker stop my-tomcat
    docker rm my-tomcat

     

    잘 삭제가 되었는지 확인해 보자.

    docker container list -a |grep my-tomcat

     

    결과값이 나오지 않으면 삭제가 잘 된 것이다. 이제 docker run 명령을 수정하자. 이번에는 7000번이 아니라 8080으로 수정하겠다.

    docker run -d --name="my-tomcat" -p 10000:8080 tomcat:8.5
    599f367663eb848593d0b6a39d656c2afd7a1c5936a44593676e9fd417a9be1c

    다시 브라우저에 접속해 보자. 아까와는 다른 메시지가 보일 것이다. 예상대로 서버가 잘 구동된 것을 브라우저를 통해 확인 할 수 있다. 그런데 페이지가 없어 404 Not Found 에러가 발생 하였다. 페이지가 없어서 난 명령이니 무시해도 된다. tomcat 서버만 잘 구동 되었는지 확인 하자.

     

     

    이제 위에서 만들 웹 어플리케이션을 배포하여 'hello world!'를 화면에 출력해 보자.

    Doker Tomcat Container에 war를 배포하기

    메이븐(maven) 빌드를 이용하여 웹 프로젝트를 war로 아카이빙 하자. 톰캣은 ROOT.war로 아카이빙 하여 webapp에 넣게 되면 context path가 '/'이 된다. 그러면 'http://localhost:10000/' 로 접속이 가능해 진다. 만약 sample.war로 아카이빙하면 'http://localhost:10000/sample'로 접속해야 한다. 

    mvn clean install
    [INFO] Scanning for projects...
    [INFO] 
    [INFO] -----------------------< docker-web-app:sample >------------------------
    [INFO] Building sample Maven Webapp 0.0.1-SNAPSHOT
    [INFO] --------------------------------[ war ]---------------------------------
    [INFO] --- maven-war-plugin:2.2:war (default-war) @ sample ---
    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access by com.thoughtworks.xstream.core.util.Fields (file:/Users/juheon/.m2/repository/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.jar) to field java.util.Properties.defaults
    WARNING: Please consider reporting this to the maintainers of com.thoughtworks.xstream.core.util.Fields
    WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    WARNING: All illegal access operations will be denied in a future release
    [INFO] Packaging webapp
    [INFO] Processing war project
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  2.431 s
    [INFO] ------------------------------------------------------------------------

     

    컨테이너 내부의  tomcat 구조를 살펴보자.  /usr/local/tomat 이 root 디렉토리이다. 루트 디렉토리의   webapp 폴더에 maven 빌드결과 생성된 ROOT.war를 복사하면 된다.

    보통 원격지 서버에 파일을 보내기 위해서는 FTP 프로토콜을 이용한다. Docker의 경우는 Docker에서 제공하는 기능을 사용하면 간단하게 파일을 컨테이너에 복사할 수 있다. 명령은 docker cp를 사용하면 된다.

    docker cp ./ROOT.war my-tomcat:/usr/local/tomcat/webapps/

     

    다시 브라우저에 접속해 보자. 브라우저에 반가운 'Hello World!'를 확인할 수 있다.

     

    Summary and Next

     지금까지 docker 환경에서 tomcat을 설치하고 war를 배포하는 방법에 대해 배웠다. tomcat 이미지로 부터 docker run 명령으로 tomcat 컨테이너를 실행하고 빌드된 ROOT.war 파일을 실행중인 tomcat 컨테이너에 docker cp 명령으로 간단하게 배포 하였다.

     그런데 여기서 문제가 하나 발생 하였다. 이미지의 성질이 변경된 것이다. 컨테이너 환경에서 이미지는 변경되지 않는 성질(immutable) 을 갖는다. 이것이 컨테이너의 큰 장점이다. 변경되지 않는(immutable) 이미지의 성격으로 어디서나 동일한 컨테이너가 실행되게 된다. 그러나 여기서는 이미지에 변경(war 배포)을 가했다. 원래 이미지와 다른 모양의 이미지가 된 것이다.

     이러한 방식은 개발시에는 가능 하지만 실제 Docker를 이용하여 이미지를 실행 시킬 경우에는 하면 안된다. 그럼 어떻게 해야 하나? 다음 포스팅에서 이 문제를 어떻게 다루어야 하는지 설명 하도록 하겠다.

    'Docker' 카테고리의 다른 글

    Docker로 웹 어플리케이션(WAS) 개발하기 - Tomcat 8.5 , war  (0) 2020.06.26

    댓글

Designed by Tistory.