2024. 9. 29. 00:37ㆍDocker/사용
1. 서론 - 도입 과정
1. 도입 - Docker, 컨테이너와 이미지까지
Docker의 학습 사유부터 CICD까지의 과정을 간략하게 설명하고 해당 코드들을 기록하려고 한다.
초기에 프로젝트를 진행할 때 가장 걸리던 점이 서로의 개발환경이 다르다는 점이었다. 그래서 개발자가 업데이트가 된 코드 및 테스트를 진행하기 위해서는 각자 작업하는 환경에 맞는 세팅 값을 설정해줘야 했다. 또한 개발을 진행하다 보면 DB, Redis 등의 여러 가지 개발에 필요한 소프트웨어들이 존재하는데 그때에 맞춰서 해당 툴 및 데이터베이스를 깔아서 각 개발자마다 환경을 세팅해야 했다. 그렇기에 이때 생각한 것이 ‘컨테이너 개념을 배워서 적용시켜 보자’라는 것이었다.
2. 중기 - DockerCompose까지
이미지, 컨테이너 개념을 배우고 해당 기능을 도커를 사용해서 개발자에게 필요한 도구 및 툴을 컨테이너화를 통해 쉽게 사용하게 되었다. 이제 dockerFile을 통해 각 개발자가 작성한 코드를 쉽게 이미지화 및 컨테이너화를 하기위해 Docker Compose 기능을 사용하게 되었다.
Docker compose 기능을 통해서는 개발 환경 및 개발된 어플리케이션 환경을 쉽게 다중컨테이너화 하여 컨트롤할 수 있었으며 기존의 dockerFile을 하나씩 컨트롤하는 것이 아닌 dokcer compose 하나의 파일을 가지고 컨트롤할 수 있게 되었다. 다만 여기서도 문제점이 존재하였는데 개발자가 작성한 기능을 테스트하기 위해서는 결국 build파일을 다른 사용자에게 전달해야 한다는 단점이 존재하였다. 그래야 이를 통해 Docker compose에서 dockerFile을 올바르게 호출하여 새로운 컨테이너가 생성될 수 있었다.
3. 마지막 - Git Action을 통한 이미지 생성
개발자는 기능을 만들고 나면 로컬에서 API 테스트를 진행하고, 이상이 없으면 Dev에 PR 및 merge를 진행하는 식으로 기능이 구현이 되는 방식이었다. 이때 feature-branch에서 dev로 PR을 날리는 이벤트 트리거가 발생을 하고 이때 Git Action을 사용하는 것을 생각했다. 깃 액션을 통해 merge가 되는 순간 docker repository에 이미지를 생성 후 저장하는 과정을 거치게 된다. 그렇게 이미지가 레포지토리에 올라가게 되면 이제 docker compose 파일에서는 image를 가져와서 사용하는 방식으로 변경하였다. 해당 방식으로 기존의 ‘중기’에서 발생한 build파일을 공유해야 한다는 단점을 제거하였다.
2. 탐구 - 적용과정
1.DockerFile
프로젝트의 기본 설정을 포함한 Dockerfile을 작성하여, 모든 개발자가 동일한 환경에서 개발할 수 있도록 하였다.
프로젝트를 진행함에 있어 모든 개발자가 동일한 개발 환경에서 작업할 수 있도록 Dockerfile을 작성하게 되었다. 초기 개발 환경 구축 시, 개발자마다 사용하는 OS, 설치된 라이브러리 및 버전이 달라 코드 실행 시 예상치 못한 오류가 발생하는 경우가 많았다. 이러한 문제를 해결하고자 각 개발자들이 동일한 환경을 구축할 수 있도록 Dockerfile을 통해 기본적인 프로젝트 환경을 정의하였다.
그래서 Frontend, Backend 둘 다 DockerFile을 작성해서 빌드 시 필요한 명령어를 기입해 주었다.
FROM node:18
WORKDIR /app
COPY package.json .
RUN npm install
# .env 파일을 복사
COPY .env .env
COPY . .
RUN npm run build
CMD ["npm","run" ,"start:mac"]
EXPOSE 3000
FROM amazoncorretto:17
LABEL org.opencontainers.image.source=https://github.com/GraduationDku/tastyHub
ARG JAR_FILE=./build/libs/*.jar
Run find . -type f -name "*.jar"
COPY ${JAR_FILE} app.jar
Run find . -type f -name "app.jar"
EXPOSE 8080
ENTRYPOINT ["java", "-jar","app.jar"]
2.DockerCompose
Docker Compose는 여러 컨테이너를 하나의 YAML 파일로 정의하여, 각 컨테이너 간의 네트워크 설정, 볼륨 마운트, 환경 변수 설정 등을 체계적으로 관리할 수 있게 해 준다. 이를 통해, 개발자는 복잡한 컨테이너 환경을 손쉽게 구축하고 관리할 수 있었다.
예를 들어, docker-compose.yml 파일을 작성하여 웹 서버, 데이터베이스, 캐시 서버 등을 각각 정의하고, 이들 간의 네트워크 연결 및 데이터 볼륨을 설정하였다. 이렇게 하면 단순히 docker-compose up 명령어 한 번으로 전체 개발 환경을 시작하고 관리할 수 있게 되어, 각종 개발 도구 및 소프트웨어를 일일이 설치하고 설정하던 번거로운 과정을 해소할 수 있었다.
version: '3.8'
volumes:
tasty-data: {}
tasty-cache: {}
refresh-email-data: {}
tasty-logs: {}
services:
frontend:
container_name: front-react
image : skyriv213/tastyhub-front:latest
ports:
- "3000:3000"
nginx:
image: nginx:latest
container_name: nginx2
ports:
- "80:80" # HTTP 포트
- "443:443" # HTTPS 포트
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf # Nginx 설정 파일
- ./nginx/localhost.crt:/etc/nginx/localhost.crt # SSL 인증서
- ./nginx/localhost-key.pem:/etc/nginx/localhost-key.pem # SSL 키
depends_on:
- backend # 'backend' 서비스가 준비된 후에 Nginx가 시작됨
database:
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 1234
MYSQL_DATABASE: tastyhub
ports:
- 3306:3306
volumes:
- tasty-data:/var/lib/mysql
redis:
image: redis
container_name: storage_redis
restart: always
ports:
- 6379:6379
volumes:
- refresh-email-data:/data
redis2:
image: redis
container_name: cache_redis
restart: always
ports:
- 6380:6380
volumes:
- tasty-cache:/data
backend:
container_name: back_spring
image : skyriv213/tastyhub
platform: linux/x86_64 # 추가된 라인
restart: always
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://database:3306/tastyhub?useSSL=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: 1234
SERVER_PORT: 8080
ports:
- 8080:8080
volumes:
- tasty-logs:/app/logs # 로그 파일 저장을 위한 볼륨
depends_on:
- database
- redis
Docker Compose 파일은 프런트엔드, 백엔드, 데이터베이스, Redis, 그리고 Nginx를 포함한 다양한 서비스를 정의하고, 각 서비스 간의 의존성과 네트워크 연결을 설정하여 애플리케이션 전체를 다중 컨테이너 환경으로 관리할 수 있도록 구성하였다.
3.GitAction
기존에는 개발자가 로컬에서 기능을 구현한 후 테스트를 진행하고, 문제가 없을 시 Dev 브랜치에 PR을 보내고, 코드가 Merge 될 때 수동으로 Docker 이미지를 빌드하고 레포지토리에 업로드하는 과정이 필요했다.
이러한 수작업을 자동화하기 위해 GitHub Actions를 활용하게 되었다. GitHub Actions는 코드가 특정 브랜치에 PR 또는 Merge 될 때마다 정해진 워크플로우를 실행할 수 있도록 도와준다. 이를 통해 코드가 Dev 브랜치에 Merge 되는 순간, 자동으로 Docker 이미지가 빌드되고, Docker Hub와 같은 레포지토리에 푸시되도록 설정하였다. 이미지가 레포지토리에 업로드되면, Docker Compose 파일에서는 해당 이미지를 가져와 사용할 수 있게 되어, 배포 과정이 자동화되었다.
Java 애플리케이션 CI/CD 예시:
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation. for test
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: <https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle>
name: Java CI with Gradle
on:
pull_request:
branches: [ "dev" ]
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'
- name: Cache Gradle dependencies
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: Test with Gradle
run: ./gradlew build
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
- name: Build with Gradle Wrapper
run: ./gradlew clean build
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: Verify .jar file existence
run: ls -la ./${{ secrets.WorkingDirectory }}/build/libs/
- name: Upload test results
uses: actions/upload-artifact@v3
with:
name: test-results
path: ${{ secrets.WORKINGDIRECTORY }}/build/test-results/test
#last
name: Docker Image CD
on:
push:
branches: [ "dev","main" ]
#dddd
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: make application.yml
if: |
contains(github.ref, 'main') ||
contains(github.ref, 'dev')
run: |
# mkdir ./src/main/resources # resources 폴더 생성
cd ./src/main/resources # resources 폴더로 이동
touch ./application.yml # application.yml 생성
echo "${{ secrets.DEVLOCAL }}" > ./application.yml # github actions에서 설정한 값을 application.yml 파일에 쓰기
shell: bash
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
- name: Build with Gradle Wrapper
run: ./gradlew clean build
working-directory: ${{ secrets.WORKINGDIRECTORY }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REPOSITORY_USERNAME }}
password: ${{ secrets.DOCKER_REPOSITORY }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ${{ secrets.WORKINGDIRECTORY }}
push: true
tags: ${{ secrets.DOCKER_REPOSITORY_USERNAME }}/tastyhub:${{ github.sha }}, ${{ secrets.DOCKER_REPOSITORY_USERNAME }}/"repository-name":latest
프런트엔드 애플리케이션 CI/CD 예시:
#last
name: Docker Image CD
on:
push:
branches: [ "dev","main" ]
#dddd
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REPOSITORY_USERNAME }}
password: ${{ secrets.DOCKER_REPOSITORY }}
- name: Create .env file
run: |
cd ./fe
touch ./.env
echo "${{secrets.FRONT_ENV}}" >./.env
shell: bash
- name: Build frontend Docker image
run: |
docker build -t "dockerId"/"repository-name" -f ./fe/Dockerfile ./fe
- name: Push Docker image to DockerHub
run: |
docker push "dockerId"/"repository-name"
3. 결론 - 적용 전/후 비교
1. 적용 전
- 개발자마다 각기 다른 개발 환경으로 인해 코드 실행 시 오류 발생 빈도가 높았다.
- 수작업으로 각각의 개발환경에서 실행 Config 생성 및 라이브러리 설치를 진행해야 했다.
- 빌드 파일을 공유하고 관리하는 데 많은 시간이 소요되었다.
2. 적용 후
- 모든 개발자가 동일한 개발 환경에서 작업할 수 있게 되어, 개발 과정의 일관성과 효율성이 증가하였다.
- GitHub Actions를 통한 CI/CD 파이프라인 구축으로 이미지 빌드 및 배포 과정이 자동화되었다.
- Docker Compose를 사용하여 전체 개발 환경을 통합 관리할 수 있게 되어, 배포와 운영이 간편해졌다.
'Docker > 사용' 카테고리의 다른 글
[Docker] Docker HTTPS 적용 (0) | 2024.02.17 |
---|---|
[docker] spring boot 배포하기 (0) | 2023.12.27 |