2022. 3. 16. 13:46ㆍ개발/Docker-K8s
이전에 구축했던 무중단 배포 프로젝트에 Docker를 적용하려고한다. 최종적인 아키텍쳐 모습은 다음과 같다.
Dockerfile과 배포 Script는 아래 저장소를 참고해주세요
Docker 설치
먼저 배포가 이뤄질 서버에 Docker 엔진을 설치한다. OS로 Amazon Linux 를 사용했기때문에 sudo amazon-linux-extras install docker 명령어를 통해 Docker를 설치한다
sudo service docker start 명령어를 통해 Docker를 시작한다. sudo docker --version 명령어를 통해 성공적으로 시작되었는지 확인한다.
Dockerfile 작성
먼저 이미지 생성을 위한 Dockerfile을 작성한다.
새로 만들 이미지가 alpine Java8을 사용할수 있도록 FROM을 통해 부모 이미지를 추가한다
ARG JAR_FILE을 통해 이미지 생성시 사용한 Argument를 받을 수 있도록 한다.
COPY를 통해 JAR파일과 db 접속정보가 담긴 yaml을 이미지에 복사한다.
ENTRYPOINT를 통해 컨테이너 생성시 수행할 명령어를 셋팅한다.
컨테이너 생성시 자동으로 셸이 실행되는게 아니기 때문에 ENTRYPOINT 첫 부분에는 셸 실행을 위한 명령어를 작성한다. 여기서 한가지 주의할 점은 jdk-alpine버전은 /bin/bash가 없어서 /bin/sh로 해야한다는 점이다.
${IDLE_PROFILE}은 컨테이너를 실행할때 환경변수로 입력된 값을 받을 변수이다.
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
COPY application-db.yml /application-db.yml
ENTRYPOINT ["/bin/sh","-c","java -Dspring.config.location=classpath:/application-${IDLE_PROFILE}.yml,/application-db.yml -Dspring.profiles.active=${IDLE_PROFILE} -jar ./app.jar"]
배포 스크립트 재작성
start.sh를 변경한다. 기존에 start.sh에서 Jar 명령어를 수행했다면, 이번엔 Jar 실행부분을 제거하고 Docker 이미지 빌드와 컨테이너 생성에 대한 명령어를 작성하면된다.
기존엔 JDK가 배포서버에 설치되어 있어야만 java 명령어가 수행되었는데 Docker를 사용함으로써 배포 서버에 별도로 JDK를 설치하지 않아도 컨테이너를 통해 정상적인 배포가 가능하게된다. 따라서 배포 서버가 추가되더라도 귀찮은 설치작업을 하지 않아도 된다.
#!/usr/bin/env bash
PROJECT_PATH=/home/ec2-user/toy-project-board/project
DOCKER_PATH=/home/ec2-user/toy-project-board/deploy
ABSPATH=$(readlink -f $0) # 현재파일 절대경로 (링크가 있다면 실제 경로를 찾도록한다)
ABSDIR=$(dirname $ABSPATH) # ABSPATH가 있는 디렉토리 (ABSPATH는 파일명이 포함된 경로이기때문)
source ${ABSDIR}/profile.sh # profile.sh를 실행한다.
sudo rm $PROJECT_PATH/build/libs/*jar
echo "> Start Build"
cd $PROJECT_PATH # 프로젝트가 저장된 경로로 이동
sudo chmod ugo+rwx gradlew # 권한부여
sudo ./gradlew build # 빌드시작
sudo rm $DOCKER_PATH/demo*
echo "> copy Jar"
cp $PROJECT_PATH/build/libs/*jar $DOCKER_PATH/ # jar 파일을 복사
echo "> copy Dockerfile"
cp $PROJECT_PATH/Dockerfile $DOCKER_PATH/ # Dockerfile
JAR_NAME=$(ls -tr $DOCKER_PATH/*.jar | tail -n 1) # 실행할 Jar명 가져오기
echo "> Run $JAR_NAME"
# Docker 컨테이너 생성 및 실행 명령어 (run 명령어)
IDLE_PROFILE=$(find_idle_profile)
IDLE_PORT=$(find_idle_port)
echo ">Run $JAR_NAME with IDLE_PROFILE"
echo ">IDLE_PROFILE $IDLE_PROFILE"
# 이미지로 만들 파일 확인
JAR_FILE="${JAR_NAME##*/}"
echo ">JAR_FILE==> $JAR_FILE"
sudo docker build -t toy-project-board:sample --build-arg JAR_FILE=$JAR_FILE $DOCKER_PATH
sudo docker run -d -p ${IDLE_PORT}:${IDLE_PORT} --name ${IDLE_PROFILE} -e "IDLE_PROFILE=${IDLE_PROFILE}" toy-project-board:sample
stop.sh를 변경한다. PID를 찾는대신 컨테이너의 ID를 찾도록 한다. docker ps 명령어에 -q 옵션을 사용하면 결과 중에서 컨테이너ID만 출력한다. (-a는 전체 , -f는 필터를 의미한다.)
#!/usr/bin/env bash
ABSPATH=$(readlink -f $0) # 현재파일 절대경로 (링크가 있다면 실제 경로를 찾도록한다)
ABSDIR=$(dirname $ABSPATH) # ABSPATH가 있는 디렉토리 (ABSPATH는 파일명이 포함된 경로이기때문)
source ${ABSDIR}/profile.sh # profile.sh를 실행한다.
IDLE_PROFILE=$(find_idle_profile)
CONTAINER_NAME=$(sudo docker ps -aqf "name=${IDLE_PROFILE}")
echo ">$CONTAINER_NAME"
if [ -z ${CONTAINER_NAME} ]
then
echo ">구동중인 컨테이너가 없습니다."
else
echo ">docker rm -f $CONTAINER_NAME"
sudo docker rm -f ${CONTAINER_NAME}
sleep 5
fi
확인
Jenkins를 통해 2회 배포요청을 한 결과 호스트의 포트 8081과 8082가 정상적으로 컨테이너와 매핑된걸 확인할 수 있다. 잘보면 IMAGE의 이름이 하나는 Hash값인데 toy-proejct-board:sampel 이미지를 만들때 기존에 존재하던 이름이 있으면, 기존의 이름을 <none>으로 만들어버린다. 그렇기 때문에 8081포트가 사용하는 이미지는 이름 대신 이미지ID가 출력된다.
발생한 에러
1. ENTRYPOINT 구동시 jar의 Argument를 인식하지 못하는 문제
Dockerfile에서 Jar파일을 실행할때 ENTRYPOINT을 잘못 작성해서 발생한 문제였다. java -jar [Argument] [실행할 Jar파일] 순으로 입력했어야했는데 java -jar [실행파일] [Argument]순으로 실행했다. 이렇게 하면 Application 실행은 잘 실행되나 환경변수들이 하나도 인식되지 않았다. 당연히 실행 명령어를 의심하지 않았고, 이 과정에서 시간을 많이 잡아먹었다.
참고자료
컨테이너 생성시 Spring Profile 적용하는 방법
--build-arg로 전달한 파라미터를 Dockerfile에서 사용하는 방법
Bash 셸 스크립트에서 특정 문자 기준으로 Split하고 가장 마지막 인덱스 값을 찾는방법
ENRTYPOINT에서 /bin/bash 안될때
셸 스크립트에서 컨테이너 이름으로 Docker ID얻기
nginx의 Configuration을 확인하는 방법
CodeDeploy Agent 설치
'개발 > Docker-K8s' 카테고리의 다른 글
쿠버네티스 컴포넌트와 오브젝트 (1) | 2023.11.03 |
---|---|
Kubectl과 Namespace (1) | 2023.10.04 |
Docker 엔진 기본 명령어 및 apache 샘플 사이트 서비스 하기 (0) | 2022.03.10 |