Build Docker image NodeJS App (Express) using Dockerfile, .dockerignore, Layer Caching

이미지
 이번 글에서는 Dockerfile 과 .dockerignore 파일을 이용하여 express 프레임워크를 사용한 node JS application 을 build 하고 Dockerfile 작성 시 Image layer 캐싱에 대하여 간단하게 정리해보겠다. 우선, 정말로 간단한 express application 이다.  https://expressjs.com/ko/starter/installing.html user-service-api 라는 directory 를 생성하고, npm 을 통해 express 를 사용할 수 있도록 하였다. index.js 를 아래와 같이 작성했다. 3000 port 로 서빙한다. / path 접근 시 정적으로 name, email 을 형식의 object 를 array 로 내려준다. 이제 Dockerfile 을 작성해보자. node 최신 이미지를 base image 로 사용하고, /app 을 working directory 로 설정한다. 그리고 현재 user-service-api directory 밑에 모든 파일을 /app 밑에 ADD 한다. 그리고 npm 명령어를 통해서 의존성을 설치하고, CMD 를 통해 container 가 run 할 때 index.js 를 실행한다. docker -t user-service-api:latest . 를 통해서 build 한다. npm install 명령어를 하게 되면 package.json 의 dependencies 를 node_modules 밑에 설치하게 된다. 설치된 의존성에 대한 snapshot 이 package-lock.json 파일에 남는다. 따라서 image 를 빌드할 시점에 node_modules 밑에 있는 의존성은 추가될 필요가 없다. 그래서 .dockerignore 파일을 작성하여 제외시키도록 하자. 또한 Dockerfile 및 .git 아래에 있는 해당 레포지토리에 대한 정보들 또한 build 에 필요없는 정보라서 같이 제외시킨다. 이외에도 build 에 필요하지...

Docker -v option, --volumes-from option Using Nginx Image

이미지
아래의 두 가지 예제를 nginx 이미지를 통해서 간단하게 정리해보겠다. 1.  Docker -v 옵션을 통해서 host machine 의 directory 를 컨테이너의 directory 에 mount 시키는 예제 2. Docker --volumes-from 옵션을 통해서 여러 컨테이너가 host machine 의 directory 를 공유하는 예제 환경은 MacOS 이고 Docker 는 이미 설치되었다는 전제이다. 1. 우선 website 라는 directory 를 공유할 경로에 생성하고, 해당 경로안에 index.html 을 간단하게 생성한다. 아래와 같이 정말 간단한 태그 하나 작성한다. 2. docker run -v 옵션을 이용하여 index.html 이 있는 경로 {호스트 머신 경로}:/user/share/nginx/html 을 통해서 컨테이너를 run 한다. 아래의 차이점은 마지막에 :ro 를 붙여줬는데, ro 는 readOnly 을 줄인것이다. 즉, 해당 컨테이너 안에서 해당 경로를 읽기용으로만 사용할 수 있다는 말이고 쓰기는 불가능하다는 말이다. 브라우저에서 8080 포트 접근 시 생성했던 index.html 이 서빙 되는것 확인. docker ps 커맨드로 컨테이너 확인. 다음은, --volumes-from 옵션을 통해서 host machine 의 같은 경로를 공유해보겠다. -p 옵션을 통해서 8081 포트를 사용했다. --volumes-from 옵션에서는 이미 앞서 생성했던 컨테이너 이름을 사용하여 같은 경로를 공유하도록 설정했다. 브라우저에서 8081 포트 접근 시 동일하게 서빙 되는것 확인. docker ps 커맨드를 통해서 2개의 컨테이너가 각각 8080, 8081 포트를 사용하고 있는것을 확인.

Angular - Build Docker Image Using Github Action ( Feat. ECR )

이미지
 얼마전까지 백엔드만 겁나 파서 백엔드 마스터가 되어야지! 라고 생각했다. 근데 요즘들어서 다시 백엔드만 해가지고는 개발 실력 성장에 한계를 느껴서 또 다시 생각이 바뀌고 있다. 그리고 역시 프론트는 재미있구나,,! 몇년 전까지만 해도 React, Angular, Vue 가 삼대장이라 불리는 만큼 강세였는데, 요즘에는 또 춘추전국 시대 같다. Svelte 니 lit-element 니, Stencil 이니 하믄서 처음 보는 것들이 계속 나온다. ㅎㅎ  이제 겨우 React, Angular, Vue 기초만 잠깐 맛보았는데 ㅎㅎ  예전의 3대장을 맛보다가, Angular 에 꽂혔는데, 이유는 바로 TypeScript 다. Angular 는 TypeScript 기반이다.  JS 자체가 워낙 Type 이 약하다 보니, 이게 number 인지 string 인지 참... 곤란한 경험을 한 적도 많다. Interface 설계 시 정확한 Type 계약을 하지 않고, 더욱이 JS 를 잘못 사용하면 지옥이 열리는 것을 경험해서 그런가 더욱이 TypeScript 가 내겐 끌렸다. ㅎㅎ Angular 로 프론트를 개발하고, Spring Boot 로 백엔드를 개발을 요즘하고 있다. 개발하고 테스트하고 운영하려면 배포를 해야되는데, 음.. 어떻게 하는것이 좋을까 고민도 많이 했다. 그러다가, AWSKRUG 에다가 아래와 같이 조언을 구해보았다. 친절한 twkiiim 님이 나의 질문에 너무너무 친절하게 도움을 주셨다. ㅎㅎ 나의 질문에서 알 수 있듯이, 일단 수동으로 ng build --prod 를 통해서 dist 밑에 static files 들을 Spring Boot resources/static 밑에 우겨놓고 Jenkins 에서 Spring Boot 만 jar 로 build 해서 배포하는 반수자동(?) 을 했었는데..  이러한 상황에서 좋은 방법을 찾아야했다. twkiiim 님의 답변을 정리해보자면 아래와 같다. 1. Only CSR...

Java - Exception ( Feat. Effective Java)

이미지
이전 글  에서 Error 와 Exception 에 대해서 정리해보았다. 이번 글에서는 Java 개발자라면 읽어야 한다는 Effective JAVA Third Edition 기준으로 예외 핸들링에 대해서 정리해보겠다. 책에서는 이렇게 말하고있다.  "예외를 제대로 활용한다면 프로그램의 가독성, 신뢰성, 유지보수성이 높아지지만, 잘못 사용하면 반대의 효과만 나타낸다." 그렇다면, 제대로 활용하는 방법에 대해서 책이 알려주는 내용을 정리해보겠다. 예외는 진짜 상황에만 사용하라 책에서는 운이 없다면 언젠가 다음과 같은 코드를 마주할지 모른다고 한다. 나는 운이 없는가 보다.. ㅎㅎ 해당 코드를 보고 무슨 일은 하는 코드인지 알기 어렵다. 전혀 직관적이지 않다. 해당 이유만으로 코드를 이렇게 작성하면 안된다는 이유가 분명하다. 해당 코드를 분석하면, 아주 끔찍한 방식으로 배열을 순회하고 있다. 무한루프를 돌다가 배열의 끝에 도달하여 ArrayIndexOutOfBoundsException 이 발생하면 끝이 난다.  해당 코드를 표준적인 관용구대로 작성했다면 바로 이해할 수 있을것이다.  왜 예외를 써서 끔찍한 코드를 만들어 냈을까?  책에서는 잘못된 추론을 근거로 성능을 높이려 시도할 경우 발생할 수 있다고 한다. 나의 개인적인 생각으로는 아마 해당 코드 작성자가 정확한 배열 순회 메커니즘을 이해하지 못한다거나, 혹은 겉멋(?)이 들어서가 아닐까 의심이 된다. ArrayIndexOutOfBoundsException 을 사용한 코드가 표준 관용구보다 성능이 안좋다. try-catch 블록 안에 코드를 넣으면 JVM 이 적용할 수 있는 최적화가 제한 된다. try catch block 사용은 소량의 데이터 케이스에서 성능 차이는 미비하여, 하드웨어 성능으로 어느정도 커버가 가능하다고 말할 수 있다.  하지만 제일 중요한 점은 가독성을 해치는 것이라고 나는 생각한다. 이는 유지보수에 상당한 어려움을 유발한다. try cat...

Java - Error, Exception

이미지
 Error 는 JVM 실행에 문제가 생겼다는 것을 의미한다. JVM 위에서 실행되는 프로그램은 실행 불능이 된다. 개발자는 안타깝게도 Error 에 대처할 방법이 전혀 없다. Exception 은 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류이다. Exception 이 발생하면 프로그램이 곧바로 종료된다는 점에서는 Error 와 동일하다. 하지만 Exception 은 Exception Handling 을 통해서 프로그램을 종료하지 않고 정상 실행 상태를 유지되도록 할 수 있다. Virtual Machine Error 의 종류에는 java.lang.OutOfMemoryError, java.lang.StackOverflowError, java.lang.UnknownError, java.lang.InternalError 등이 있다. Exception 은 두 가지로 나뉜다. Checked Exception 과 Unchecked Exception 이다. Checked Exception 은 컴파일러 체크 예외라고도 하는데, 자바 소스를 컴파일하는 과정에서 예외 처리 코드가 필요한지 검사하기 때문이다. Checked Exception 에 예외 처리 코드가 없다면 컴파일 오류가 발생한다.  Unchecked Exception 의 경우에는 컴파일 과정에서 예외 처리 코드를 검사하지 않는 예외를 말한다. 컴파일 과정에서 예외 처리 코드를 검사하지 않지만, Unchcked Exception 도 마찬가지로 예외 처리가 필요하다. Checked Exception 은 Exception 을 상속받는다. Unchecked Exception 은 RuntimeException 을 상속받는다. RuntimeException 은  또한 Exception 을 상속받지만, JVM 은 RuntimeException 을 상속했는지 여부를 보고 Checked Exception 과 Unchecked Exception 을 구별한다. RuntimeE...

Tomcat War Deploy 404 (Feat. EC2) - Maven Clean

이미지
 오늘의 글은 나의 경험담을 정리하려고 한다. 무슨 경험이라고? 내가 삽질한 경험이다. 우선, 개발 환경부터 간단하게 정리해보겠다.  단일 EC2 환경이다. 아래와 같이 여러 가지 개발 툴 을 설치했다. - Jenkins (9090 port) - Tomcat (8080 port)  - Github - Maven Tomcat 에 Github 의 Spring 프로젝트를 Maven 을 이용하여 War 로 패키징하는 간단한 Deploy 방식이였다. Deploy 는 Jenkins 프리스타일 프로젝트를 사용했다. 1. Git Parameter Plugin 을 이용해서 브랜치를 선택할 수 있게 한다. 2. NodeJS Plugin 과 기존에 설치해 둔 Maven 을 이용하여 Shell Script 를 실행하여 War 패키징 및 해당 EC2 Tomcat 에 올리고 실행하게 한다. 아래는 간단한 shell script 이다.  maven PATH 를 잡아주고, 미리 설정해 둔 S3에서 properties 파일을 가져온다. 그리고 mvn 명령어를 통해서 war 로 패키징하고, 패키징 한 war 를 S3 에서 버전관리 한다. 그리고 톰캣을 우선 정지하고, 새로 패키징한 war 를 tomcat 경로에 맞게 올린다. 그리고 잠시 스레드를 5초 동안 잠재운다.  그후에 다시 톰캣을 시작한다. 이러한 과정에서 여러 브랜치를 아마 대략 70 번 (?) 정도 배포 했다.  70번 밑으로 배포는 정상적으로 배포가 잘 되었고, 테스트로 잘 할 수 있었다.  그러나 70번 정도가 넘어가자, 달콤한 404 를 맞이하게 된다. 뭐지? 왜 잘 되던게 갑자기 안될까??? 제일 짜증나는 부분이다. 삽질을 하다가 tomcat log 를 찾아봤다. /var/log/tomcat8/catalina.out 아래와 같이 ConflictingBeanDefinitionException 을 확인한다.  ConflictingBeanDefinitionEx...

Jenkins - Publish over SSH (feat. AWS EC2)

이미지
 많은 사람들이 알다시피, Jenkins 는 유용한 CI / CD 오픈소스 툴이다. 온프레미스 환경 및 클라우드 환경을 넘나들며 유용하게 사용할 수 있다. Jenkins 가 제공하는 수많은 기능들이 있다. 그 수 많은 기능들 중에 이번 글에서 정리할 내용은 Publish over SSH Plugin 이다. 기존에 이미 Jenkins 는 EC2 에 설치가 되어있고 배포 대상이 되는 EC2 도 있다는 전제하에 정리해보겠다.  혹시라도 Jenkins 설치 방법이 궁금하다면 예전 글에서 정리한 것을 참고하길 바란다.  https://infondgndg91.blogspot.com/2020/06/install-jenkins-in-aws-ec2.html 이번 글에서 무엇을 정리할 것인가 나열하겠다. 1. Github 에서 Source code 를 땡겨온다. ( 이번 글에서 Github 설정은 다루지 않는다. ) 2. Gradle 로 Spring boot project 를 jar 로 빌드한다. ( 이번 글에서 Gradle 설정은 다루지 않는다. ) 3. 빌드한 jar 를 Publish over SSH 플러그인을 통해서 Deploy EC2 에 전송한다. 4. Deploy EC2 에 있는 shell script 를 Jenkins EC2 에서 원격 호출한다. 우선, Publish over SSH 플러그인 부터 설치해보자. 아래는 해당 플러그인 공식 Documentation 이다. https://plugins.jenkins.io/publish-over-ssh/ admin 으로 들어가든, 권한이 있는 계정으로 들어가자. Jenkins 관리로 들어가자. 그리고, 플러그인 관리로 들어가자. 나는 이미 설치를 했기 때문에, 설치된 플러그인 목록에서 검색을 통해서 확인할 수 있다. 설치가 되지 않은 상태라면, 설치 가능 목록에서 Publish over SSH 검색 후 설치하면 된다. 플러그인 설치가 되었다면, 이제 시스템 설정으로 들어가자.  들어가서, 스...