티스토리 뷰
서론.
이번 포스팅 글은 SpringBoot와 Mysql을 이용한 애플리케이션에 대하여 NCP(네이버 클라우드 플랫폼) 서버에 Github Action을 이용하여 자동 배포를 적용해 보았던 일대기를 정리한 글입니다.
이 글에서는 SpringBoot 와 Mysql에 대해서는 서버 띄우는 환경 세팅만을 공유합니다. 오직 CI/CD에 대한 적용 기를 주된 포스팅이 되겠습니다.
이번 포스팅에서 배포 자동화 세팅 방식은 무료로 제공해주는 서버에 Docker 허브를 이용하여 이미지를 푸시하고 그 후 이미지를 컨테이너에 올리는 방법을 선택하였습니다.
이유라고 하면.... 좀 더 간단하게 배포를 진행할 수 있다는?? 장점?? 이기에 선택하였습니다.^^
자세한 정보 : https://github.com/rhkdgur/Rout-Doo-backend
Workflow 생성하기.
Github Action을 사용하기 위해서는 우선 Acton 카테고리를 선택합니다.
아래와 같이 Choos a workflow가 나오게 되고 SpringBoot를 이용하는 경우 Maven 또는 Gradle 자신이 만든 프로젝트 프레임워크 형태에 따라 Java with Maven 또는 Java with Gradle을 선택합니다.
선택하게 되면 아래 이미지와 같이 gradle.yml이라는 workflow 파일이 생성됩니다. 기본적인 스크립트 내용은 제공됩니다.
해당 파일을 커밋하게 된다면 파일이 저장이 됩니다. 경로는 ./github/workflows/....yml
이렇게 기본적인 Github Action을 이용한 자동 배포 환경 세팅을 완료되었습니다.
위의 스크립트 내용을 다시 자신의 입맛에 맞게 세팅을 해야 합니다.
SpringBoot DockerFile.
- 애플리케이션 jar 파일을 띄우기 위한 스크립트 파일
FROM openjdk:17.0.2-jdk-slim-buster AS builder
WORKDIR /app
COPY gradlew settings.gradle build.gradle ./
COPY gradle ./gradle
COPY src/main ./src/main
RUN ./gradlew clean bootJar
FROM openjdk:17.0.2-slim-buster
WORKDIR /app
COPY --from=builder /app/build/libs/dailyroutine.jar dailyroutine.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar" ,"dailyroutine.jar"]
- Dockerfile을 실행하게 되면 자동으로 dailyroutine.jar라는 내장 서버가 탑재된 애플리케이션이 실행이 됩니다.
docker-compose.yml
- 데이터베이스 docker image 환경 설정
version: "3.8"
services:
# mariadb 설정
db_master:
image: mariadb:10.6 # mariadb 10.6 버전 이미지 사용
container_name: mariadb-master # container 이름 설정
restart: always
environment:
MARIADB_ROOT_PASSWORD: ${ROOT_PASSWORD} # container 생성 후 root의 비밀번호를 저장한 파일을 설정함으로서 root 비밀번호를 설정
MARIADB_DATABASE: ${DATABASE}
MARIADB_USER: ${USER}
MARIADB_PASSWORD: ${PASSWORD}
volumes:
- ./mariadb/data:/var/lib/mysql # 각종 log와 index등이 volume이 저장되는 디렉토리
- ./mariadb/config/:/etc/mysql/conf.d # 이전에 나왔던 ./master/config/my.cnf 를 써줌으써 설정을 reference해줍니다.
ports:
- "33306:3306" # 이 옵션은 docker engine에게 각 container에게 포트를 수동으로 설정하게 해줍니다.
networks:
- dailyroutine-net
networks:
dailyroutine-net:
#externel: true
- Mariadb를 설치하여 데이터베이스 서버를 구축합니다.
주요 스크립트 구성
Runners
- Job을 실행시키기 위한 애플리케이션
- Github에 의해 호스팅 되고 있는 서버
Step
- Job안에서 순차적으로 실행되는 프로세스 단위
- 하나의 job에 여러 task를 넣을 수 있음
- step은 action이나 shell commend로도 생성될 수 있음
- 하나의 job에 있는 step은 runner에 의해 실행
- 하나의 Job 내에서 각각의 Step은 다양한 Task로 인해 생성된 데이터를 공유할 수 있음
Jobs
- 한 가지 Runner안에서 실행되는 여러 가지 step들의 모음
- 각각의 step들은 일종의 shell script처럼 실행이 됩니다.
- 작업마다 실행환경이 다름
- Job은 다른 Job에 의존관계를 가질 수 있고, 병렬 실행도 가능
- Step들은 순서에 따라 실행되며 Step끼리 데이터들을 공유할 수 있음
- N개의 도커 이미지를 생성하여 하나의 작업을 수행
스크립트 설명
아래 workflow 스크립트는 제 프로젝트에 맞게 재구성한 스크립트입니다.
아래 스크립트는 SpringBoot 정보를 가진 Dockerfile을 실행하여 자동 배포를 진행하는 스크립트입니다.
name: Java CI with Gradle
on:
push:
branches: [ "develop" ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Gradle 권한 부여
- name: Grant permission for gradlew
run: chmod +x ./gradlew
- name: Docker build
uses: docker/build-push-action@v3.2.0
with:
context: .
file: ./Dockerfile
push: false
tags: dailyroutine:latest
- run: /usr/bin/docker save -o dailyroutine.tar dailyroutine:latest
# tar 파일 권한 수정
- name: Set permission for tar file
run: chmod 664 dailyroutine.tar
- name: Send docker image file to test server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.DEVELOP_HOST}}
username: ${{ secrets.POSTOFFICE_NAME }} # dailyroutine
password : ${{ secrets.POSTOFFICE_PASS }} # daily!23
port: ${{ secrets.DEVELOP_PORT}}
source: "dailyroutine.tar"
target: "/home/dailyroutine/versions"
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Bootup server
uses: cross-the-world/ssh-scp-ssh-pipelines@latest
with:
host: ${{ secrets.DEVELOP_HOST}}
user: ${{ secrets.POSTOFFICE_NAME }} # dailyroutine
pass: ${{ secrets.POSTOFFICE_PASS }} # daily!23
port: ${{ secrets.DEVELOP_PORT}}
connect_timeout: 120s
first_ssh: |
docker stop dailyroutine || true;
docker rm dailyroutine || true;
docker image rm dailyroutine:latest || true;
- name: Load versions to image
run : sshpass -p ${{ secrets.POSTOFFICE_PASS }} ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=60 -T -p ${{ secrets.DEVELOP_PORT}} ${{ secrets.POSTOFFICE_NAME }}@${{ secrets.DEVELOP_HOST}} "
docker load -i /home/dailyroutine/versions/dailyroutine.tar;
"
- name: Run Docker container on server
uses: cross-the-world/ssh-scp-ssh-pipelines@latest
with:
host: ${{ secrets.DEVELOP_HOST}}
user: ${{ secrets.POSTOFFICE_NAME }}
pass: ${{ secrets.POSTOFFICE_PASS }}
port: ${{ secrets.DEVELOP_PORT}}
connect_timeout: 120s
first_ssh: |
docker run -d -p 32468:8080 --name dailyroutine -e SPRING_PROFILES_ACTIVE=nCloud --network docker_dailyroutine-net dailyroutine:latest;
스크립트에 대한 상세 설명.
1. build and Steps
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
기본 세팅되는 내용으로 JDK 17이 설치되며, actions/checkout@v4는 브랜치에서 코드를 가져온다는 의미입니다.
2. SpringBoot Dockerfile Build 및 gradlew 권한 변경
# Gradle 권한 부여
- name: Grant permission for gradlew
run: chmod +x ./gradlew
- name: Docker build
uses: docker/build-push-action@v3.2.0
with:
context: .
file: ./Dockerfile
push: false
tags: dailyroutine:latest
- run: /usr/bin/docker save -o dailyroutine.tar dailyroutine:latest
- 프로젝트의 Dockerfile을 실행하여 Docker image 스크립트를 실행합니다.
- 생성되는 파일은 dailyroutine:latest라는 tag 명칭인 image가 되고 해당 정보는 dailyroutine.tar 파일로 압축됩니다.
3. tar 파일 권한 변경 및 tar 파일 이전
# tar 파일 권한 수정
- name: Set permission for tar file
run: chmod 664 dailyroutine.tar
- name: Send docker image file to test server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.DEVELOP_HOST}}
username: ${{ secrets.POSTOFFICE_NAME }} # dailyroutine
password : ${{ secrets.POSTOFFICE_PASS }} # daily!23
port: ${{ secrets.DEVELOP_PORT}}
source: "dailyroutine.tar"
target: "/home/dailyroutine/versions"
- Set permission for tar file : Docker build를 통해 생성된 dailyroutine.tar 파일에 대하여 권한을 664로 변경합니다.
- tar 파일이 처음 생성 되었을 때 권한이 없어 load 하지 못하는 현상이 발생했었음....
- Send docker image file to test server : tar 파일의 위치를 커스텀한 위치로 이전
4. 애플리케이션 server 갱신 build
- name: Bootup server
uses: cross-the-world/ssh-scp-ssh-pipelines@latest
with:
host: ${{ secrets.DEVELOP_HOST}}
user: ${{ secrets.POSTOFFICE_NAME }} # dailyroutine
pass: ${{ secrets.POSTOFFICE_PASS }} # daily!23
port: ${{ secrets.DEVELOP_PORT}}
connect_timeout: 120s
first_ssh: |
docker stop dailyroutine || true;
docker rm dailyroutine || true;
docker image rm dailyroutine:latest || true;
- docker stop dailyroutine || true; : 컨테이너에 올라가 있는 dailyroutine을 정지
- docker rm dailyroutine || true; : 정지된 dailyroutine을 삭제
- docker image rm dailyroutine:latest || true; : 기존에 존재하는 이미지 파일을 삭제
- 기존 image를 삭제하고 새롭게 갱신되는 image 파일을 가져오기 위함
- true ;(세미콜론) 은 해당 결과가 존재하면 실행 아니면 넘어간다는 의미입니다. ;(세미콜론)을 설정하지 않는다면, && 이슈가 발생하여 문구 이슈가 발생할 수 있습니다.
5. SSH 서버 유지 설정 및 tar 파일 image 불러오기
- name: Load versions to image
run : sshpass -p ${{ secrets.POSTOFFICE_PASS }} ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=60 -T -p ${{ secrets.DEVELOP_PORT}} ${{ secrets.POSTOFFICE_NAME }}@${{ secrets.DEVELOP_HOST}} "
docker load -i /home/dailyroutine/versions/dailyroutine.tar;
"
해당 스크립트 같은 경우 사용해도 되고 안 해도 됩니다. 저 같은 경우 docker load -i 가 실행 되는 과정에서 지속적으로 SSH가 끊기는 현상이 발생하였습니다.
Socket exception: Connection reset by peer (104) 에러 메시지가 발생을 하였고, 에러 메시지에 대한 내용을 확인해 보니 네트워크 문제, 시간 초과, 서버의 SSH 설정 문제 등 여러 가지 원인이 되는 부분이었습니다...
NCP에서 제공하는 무료 서버여서 그런지는 모르지만... 네트워크 속도, SSH 자동 세팅해 주는 부분에서 디테일하게 서버를 구축하지 못하여 발생할 수 있는 부분이라고 보입니다... (서버만 잘 구축하면?? 발생하지 않을 수 있을 듯? 합니다.. ^^)
ServerAliveInterval 및 ServerAliveCountMax 옵션을 추가하여 SSH 연결이 오랜 시간 동안 유지될 수 있도록 설정합니다. 이를 통해 서버가 주기적으로 신호를 보내어 연결을 유지합니다.
StrictHostKeyChecking를 no로 설정한 이유는 기본적으로 StrictHostKeyChecking은 원격 호스트에 연결할 로컬에 설치된 key값과 비교하여 허용여부를 확인합니다. 하지만 host key 허용여부를 비활성화하여 확인하지 않을 수 있도록 no를 주어 진행할 수 있습니다.
가끔가다 Pseudo-terminal will not be allocated because stdin is not a terminal라는 불편한 메시지가 발생할 수 있습니다. 번역해 보자면 `stdin이 터미널이 아니기 때문에 의사 터미널은 할당되지 않습니다.`
즉, Pseudo-terminal 할당 문제는 ssh 명령이 비상호작용 모드에서 실행될 때 발생할 수 있습니다.
단일 명령어에서는 이상이 없지만,
예를 들어
test.sh 에 실행명령을 작성하고, ssh {host} < test.sh 형식으로 할 때 메시지가 발생합니다.
위의 예시처럼 발생할 여지가 있는 스크립트이기 때문에 -T 플래그를 명시적으로 추가하여 터미널 할당을 비활성화할 수 있습니다.
ServerAliveCountMax=60 -T -p ${{ secrets.DEVELOP_PORT}}
6. 이미지 컨테이너 실행
- name: Run Docker container on server
uses: cross-the-world/ssh-scp-ssh-pipelines@latest
with:
host: ${{ secrets.DEVELOP_HOST}}
user: ${{ secrets.POSTOFFICE_NAME }}
pass: ${{ secrets.POSTOFFICE_PASS }}
port: ${{ secrets.DEVELOP_PORT}}
connect_timeout: 120s
first_ssh: |
docker run -d -p 32468:8080 --name dailyroutine -e SPRING_PROFILES_ACTIVE=nCloud --network docker_dailyroutine-net dailyroutine:latest;
docker run을 통하여 이미지를 컨테이너에 올리는 작업에 해당합니다.
docker run -d -p 32468:8080 --name dailyroutine -e SPRING_PROFILES_ACTIVE=nCloud --network docker_dailyroutine-net dailyroutine:latest;
위의 run 과정을 해석해 보자면..
- -d : 백그래운드에서 실행.
- -p 32456:8080 : 외부 접속 포트는 32456 , 내부 포트는 8080 지정
- -e SPRING_PROFILES_ACTIVE=ncloud : SpringBoot application.yml에 선언된 profile을 선택하는 작업
- ncloud profile을 실행.
- --network docker_dailyroutine-net : Mariadb를 컨테이너에 올리는 과정에서 network를 docker_dailyroutine-net으로 설정하였습니다. 이와 같은 network를 연결되도록 설정해 주는 작업
7. 빌드 결과
위와 같이 자동 배포에 성공하게 되면, docker 서버 내에 이미지 파일과 컨테이너에 잘 실행되는 것을 확인할 수 있습니다.
마치며...
NCP 서버에 올려 직접 github action을 1부터 10까지 해보는 것은 정말 오랜만인 것 같습니다...
회사 외 사이드 프로젝트로 자동 배포를 해보니.. 여러 시행착오들이 있었고 또 그 과정에서 그냥 지나쳤던 여러 문제들 또는 시스템에 대하여 깊게 생각해 보는 시간이었고 좋은 경험이 된 것 같아 좋았습니다..
다음에는 NCP가 아닌 AWS에 올려보는 작업도 진행해 볼 예정이에요~~ ^^
- Total
- Today
- Yesterday
- insert
- mybatis
- dfs
- 알고리즘
- 캘린더
- 개념 이해하기
- 네이버 클라우드
- docker
- 권한
- 리눅스
- ncp
- 도커
- 격리수준
- hazelcast
- 이미지
- Java
- dockerfile
- Lock
- Linux
- Quartz
- MySQL
- Cache
- 정의
- 컨테이너
- leatcode
- 스케줄러
- centos7
- 캐시
- LocalDate
- spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |