혼자 개발 중인 토이 프로젝트에서 GitHub Actions를 활용한 CI/CD 구축 경험을 작성한 글입니다. 수정할 부분이 있다면 댓글 남겨주세요.
배포 환경
- AWS EC2 (Ubuntu 24.04 LTS - FreeTier)
- GitHub Actions
- Docker/Docker Compose
- Java 21
- SpringBoot 3.x
- Redis
- PostgreSQL
GitHub Actions 를 선택한 이유
CI/CD 도구 중 가장 유명하고 많이 사용되는건 아무래도 오픈소스인 Jenkins 입니다. 그만큼 참고할 자료가 많고 플러그인 기능도 많아서 여러 상황에 대처하기에도 좋습니다. 하지만 별도 서버를 설치해야 하고 UI 도 레거시하며 커스터마이징에 유연한 만큼 손이 많이 갑니다. 또한 대부분의 기능을 GitHub Actions로도 구현할 수 있습니다. 물론 규모가 큰 서비스의 경우, 비용적인 측면에서 Jenkins가 훌륭한 도구일 것 같습니다.
GitHub Actions도 결국 사용량이 많아진다면 유료 플랜을 이용해야 하기 때문에 소규모 팀이나 개인 프로젝트, 오픈 소스 프로젝트에 적합합니다. GitHub Actions의 무료와 유료 플랜의 차이는 아래와 같습니다.
1. 무료 플랜
- 무제한 빌드 시간 (* Private Repository: 월 2,000분)
- 동시 작업 20개
- GitHub-hosted 러너 사용 가능
- 500MB 저장소 용량
- 기본 GitHub 지원
2. 유료 플랜
- Team ($4/사용자/월)
- 프라이빗 저장소 월 3,000분 (약 50시간)
- 동시 작업 40개
- GitHub-hosted 러너 사용 가능
- 2GB 저장소 용량
- 웹/이메일 지원
- Enterprise ($21/사용자/월)
- 프라이빗 저장소 월 50,000분 (약 833시간)
- 동시 작업 180개
- GitHub-hosted 러너 사용 가능
- 50GB 저장소 용량
- 프리미엄 지원
- SAML SSO
- 고급 보안 기능
(자세한 내용은 About billing for GitHub Actions 참고)
따라서 개인 프로젝트 진행 상황, GitHub와 쉬운 연동, 구축 편의성 등을 고려하여 GitHub Actions를 CI/CD 도구로 선택하게 됐습니다.
CI/CD 파이프라인 구축하기
GitHub Actions는 yml 파일로 구성되며 프로젝트의 root 경로에 .github/gitflows/ 경로 아래에 원하는 이름으로 생성하면 됩니다. 저는 ci-cd.yml 이라는 이름으로 생성했습니다. 프로젝트를 계획하며 여러 조건을 상정하다보니 몇 가지 조건들이 추가된게 있습니다. 전체적인 프로세스는 다음과 같습니다.
- 빌드 및 테스트 진행 (CI: Continuous Integration)
- main 또는 release 하위 브랜치에 push 될 때 실행
- ubuntu 가상 환경 생성 (GitHub Actions는 가상 서버를 통해 해당 파일의 내용을 실행합니다.)
- 현재 브랜치의 최신 커밋 소스코드 가져오기
- Java 설치
- 빌드 및 테스트 진행
- 배포 진행 (CD: Continuous Delivery/Deployment)
- 빌드 및 테스트가 완료되면 ubuntu 가상 환경 생성
- 빌드 결과물 가져오기
- DockerHub 로그인
- Docker 이미지 빌드 및 푸시
- EC2 에 접근하여 DockerCompose 로 이미지 가져와서 실행
이제 저의 GitHub Actions 설정 파일을 보며 단계별로 살펴보겠습니다.
name: CI/CD Pipeline
on:
push:
branches:
- "main"
- "release/**"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build and Test
env:
SPRING_PROFILES_ACTIVE: test
run: ./gradlew clean build
# 빌드 결과물 저장
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/
retention-days: 1 # 아티팩트 보관 기간 설정
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 빌드 결과물 다운로드
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts
path: build/
# 브랜치에 따라 다른 환경에 배포
- name: Set environment
id: set-env
run: |
if [[ ${{ github.ref }} == 'refs/heads/main' ]]; then
echo "ENV=prod" >> $GITHUB_OUTPUT
else
echo "ENV=stage" >> $GITHUB_OUTPUT
fi
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Docker Buildx 설정 추가
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Docker 이미지 빌드 및 푸시
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
# 브랜치별로 다른 태그 사용
tags: ${{ secrets.DOCKERHUB_USERNAME }}/stockscope:${{ steps.set-env.outputs.ENV }}
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stockscope:${{ steps.set-env.outputs.ENV }}
cache-to: type=inline
# Github Actions에서 공개 IP 가져오기
- name: Get Public IP
id: ip
uses: haythem/public-ip@v1.3
# AWS CLI 설정
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
# 보안그룹 규칙 추가
- name: Add security group rule
run: |
RULE_ID=$(aws ec2 authorize-security-group-ingress \
--group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} \
--protocol tcp \
--port 22 \
--cidr ${{ steps.ip.outputs.ipv4 }}/32 \
--query 'SecurityGroupRules[0].SecurityGroupRuleId' \
--output text)
echo "RULE_ID=${RULE_ID}" >> $GITHUB_ENV
# 배포 진행
- name: Deploy to EC2
uses: appleboy/ssh-action@master
with:
host: ${{ steps.set-env.outputs.ENV == 'prod' && secrets.PROD_EC2_HOST || secrets.STAGE_EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# Docker Compose 설치 (없는 경우에만)
if ! command -v docker-compose &> /dev/null; then
sudo curl -L "https://github.com/docker/compose/releases/download/v2.30.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
fi
mkdir -p ~/stockscope
cd ~/stockscope
# 기존 컨테이너 상태 확인 및 정리
if [ -f docker-compose.yml ]; then
docker-compose down
fi
# 기존 docker-compose.yml 백업 (선택사항)
[ -f docker-compose.yml ] && mv docker-compose.yml docker-compose.yml.bak
# 현재 브랜치명 추출
BRANCH_NAME="${{ github.ref_name }}"
# 프로젝트의 docker-compose.yml 다운로드
wget https://raw.githubusercontent.com/HyunJaae/stockscope/${BRANCH_NAME}/docker-compose.yml
# .env 파일 생성/업데이트
cat > .env << EOL
DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
ENV=${{ steps.set-env.outputs.ENV }}
DB_NAME=${{ secrets.DB_NAME }}
DB_USERNAME=${{ secrets.DB_USERNAME }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
EOL
# 컨테이너 실행
docker-compose pull
docker-compose up -d
# 보안그룹 규칙 제거 (항상 실행)
- name: Remove security group rule
if: always()
run: |
aws ec2 revoke-security-group-ingress \
--group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} \
--security-group-rule-ids ${RULE_ID}
GitHub Actions 단계별로 살펴보기
워크플로우 이름 및 트리거 이벤트 정의
name: CI/CD Pipeline
on:
push:
branches:
- "main"
- "release/**"
- name: GitHub Actions의 워크플로우의 이름을 정의합니다.
- on: 트리거 이벤트를 정의합니다. (자세한 내용은 Events that trigger workflows 참고)
main 또는 release/ 로 시작하는 모든 브랜치에 push 될 때 워크플로우가 실행됩니다. 배열 형태로도 작성이 가능합니다. ([ "main", "release/**" ])
워크플로우 작업 환경 정의 (빌드 및 테스트)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- jobs: 작업들을 정의합니다. (GitHub Actions 는 하나의 job마다 가상 환경을 생성하여 실행합니다.)
- build: 빌드 작업을 정의합니다.
- runs-on: 가상 환경 유형을 ubuntu 최신 버전으로 설정 (Windows 와 macOS 버전도 있으나 Linux 버전이 가장 저렴하다.)
- steps: job 아래에서 명령을 실행하는 독립적인 단위인 step을 정의합니다. 같은 job 아래에 있기 때문에 데이터를 공유합니다.
- uses: 재사용 가능한 워크플로우인 action을 사용할 수 있는 키워드입니다.
action
컴포넌트나 메서드와 같이 재사용을 위한 집합으로 개인 또는 공용 action을 사용할 수 있습니다.
해당 action의 위치와 버전 정보로 uses 키워드를 통해 사용할 수 있습니다.
ubuntu 최신 버전의 가상 환경에서 현재 워크플로우를 실행시킨 브랜치의 최신 커밋 소스 코드를 가상 환경으로 가져옵니다.
Java 21 설치
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: step 의 이름을 정의합니다.
- uses: Java 설치 action을 실행합니다.
- with: action 호출 시 입력 값을 전달합니다.
전 Java 21 버전을 사용하기 때문에 Eclipse Temurin JDK 21 버전을 설치합니다.
Gradle을 사용하여 프로젝트 빌드 및 테스트
- name: Build and Test
env:
SPRING_PROFILES_ACTIVE: test
run: ./gradlew clean build
- env: step 실행 시 환경 변수를 전달합니다.
- run: step 이 실행할 명령어입니다.
진행 중인 프로젝트를 local, test, prod 로 운영 환경을 구분하고 있고 이 중 local과 prod는 PostgreSQL의 데이터소스를 필요로 합니다. 정확한 빌드와 테스트를 생각하면 워크플로우에 PostgreSQL 이나 Redis 환경을 추가하는 것도 맞겠지만 일반적으로 프로젝트가 운영 서버에 바로 배포되지 않고 스테이징 서버에서 확인 후 정식 배포될테니 워크플로우의 비용을 아끼기 위해 인메모리 DB인 H2를 사용하는 test 환경으로 빌드와 테스트를 진행합니다. 이를 위해 SPRING_PROFILES_ACTIVE의 값으로 test를 환경 변수로 전달합니다.
만약 프로젝트 진행 초기이거나 테스트 코드가 필요 없다면 시간을 절약하기 위해 아래와 같이 테스트 과정을 생략할 수 있습니다.
- name: Build and Test
env:
SPRING_PROFILES_ACTIVE: test
run: ./gradlew clean build -x test # 테스트 제외
빌드 결과물을 아티팩트로 저장
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/
retention-days: 1
- with:
- name: 아티팩트 이름
- path: 저장할 파일 경로
- retention-days: 보관 기간
여기서 말하는 아티팩트는 임시 저장 공간으로 워크플로우 실행 시 실제로 확인할 수 있습니다.
워크플로우 실행과 별개로 관리되며 지정된 기간 동안 보관이 가능합니다. 서두에 기재했던 각 플랜별 특징에서 말하는 저장소가 이 공간을 말합니다. 무료 플랜일 땐 500MB 이기 때문에 효율적으로 사용해야 합니다.
제가 빌드 결과물을 아티팩트에 저장한 이유는 배포 job 을 진행할 때 빌드를 재진행하지 않고 아티팩트에서 다운 받아 활용하기 위함입니다. 또한 시간도 절약할 수 있습니다. 실제로 제 워크플로우에서 빌드와 테스트 step이 1분 1초로 빌드 job의 대부분의 시간을 차지합니다. 하지만 빌드 결과물을 아티팩트로 저장하는데 3초, 다운로드하는 시간은 2초로 빌드와 테스트하는 시간 대비 56초의 시간을 배포 job에서 아낄 수 있습니다.
워크플로우 작업 환경 정의 및 빌드 결과물 다운로드 (배포)
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts
path: build/
- needs: 실행 조건을 정의합니다.
빌드 job이 성공적으로 완료되면 배포(deploy) job이 실행됩니다. 빌드 job과 동일하게 작업 환경을 정의하고 아티팩트에 저장했던 빌드 결과물을 다운로드 받습니다.
환경 변수 설정
- name: Set environment
id: set-env
run: |
if [[ ${{ github.ref }} == 'refs/heads/main' ]]; then
echo "ENV=prod" >> $GITHUB_OUTPUT
else
echo "ENV=stage" >> $GITHUB_OUTPUT
fi
- id: 스텝의 고유 식별자를 정의합니다. 워크플로우 내에서 해당 식별자로 할당된 값을 가져다 쓸 수 있습니다.
- ${{ github.ref }}: 현재 브랜치의 정보를 알 수 있는 표현식입니다.
(자세한 내용은 Accessing contextual information about workflow runs 참고) - $GITHUB_OUTPUT: GitHub Actions의 환경 파일로 작업 단계 간에 값을 전달할 때 사용합니다.
워크플로우에서 사용할 변수를 정의합니다. set-env 라는 식별자를 정의하고 $GITHUB_OUTPUT 에 현재 브랜치가 main 이면 "ENV=prod" 를 입력하고 그 외 브랜치인 경우, "ENV=stage"를 입력합니다. 다른 step 에서 ${{ steps.set-env.outputs.ENV }} 문구를 사용하여 입력 값을 가져올 수 있습니다.
도커 허브에 로그인
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- docker/login-action@v3: 도커 허브에 로그인할 수 있는 action입니다.
- secrets: 프로젝트 레포지토리에 있는 암호화된 환경 변수 저장소입니다. 레포지토리의 Settings에서 아래와 같이 secrets를 확인할 수 있습니다.
도커 허브 로그인을 위해 도커 허브 유저 이름과 액세스 토큰을 Repository secrets 에 생성하고 도커 로그인 action의 입력 값으로 전달합니다. 도커 액세스 토큰은 도커 허브 웹에서 Account Settings의 Security 항목에서 확인할 수 있습니다.
도커 Buildx 설정과 이미지 빌드 및 푸시
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
# 브랜치별로 다른 태그 사용
tags: ${{ secrets.DOCKERHUB_USERNAME }}/stockscope:${{ steps.set-env.outputs.ENV }}
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stockscope:${{ steps.set-env.outputs.ENV }}
cache-to: type=inline
- buildx: 도커의 향상된 빌드 기능을 제공하는 도구입니다. 다양한 CPU 아키텍처를 지원하고 레지스트리 캐시 사용 및 병렬 빌드도 제공합니다.
- context: 빌드 컨텍스트(작업 디렉토리)를 지정합니다. (위 코드에서는 프로젝트 루트 경로를 빌드 컨텍스트로 지정)
- push: 이미지 푸시를 활성화합니다.
- tags: 도커 이미지의 태그를 설정합니다.
- cache-from: 캐시로 사용할 이미지를 설정합니다.
- cache-to: 새로운 캐시의 저장 방식을 설정합니다.
buildx 를 설정하고 프로젝트 루트 경로의 도커 파일을 빌드하고 푸시합니다. 이때 브랜치별로 다른 태그를 사용하고 빌드 시 기존 이미지가 있다면 해당 이미지를 가져와서 변경된 레이어만 빌드하고 이미지에 포함시킵니다.
도커 파일은 아래와 같이 작성되어 있으며 다운로드 받은 프로젝트 빌드 결과물을 카피합니다.
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY build/libs/*.jar app.jar
COPY build/docs/asciidoc /app/static/docs
ENTRYPOINT ["java", "-jar", "app.jar"]
GitHub Actions 에서 IP 가져오기
- name: Get Public IP
id: ip
uses: haythem/public-ip@v1.3
haythem/public-ip@v1.3 action을 통해 현재 워크플로우가 실행되는 가상 환경의 IP를 가져옵니다. 해당 IP를 배포할 EC2의 인바운드 규칙에 추가해야 접근이 가능합니다. 워크플로우 진행 후 추가한 IP는 삭제할 수 있도록 step을 추가할겁니다.
AWS CLI 설정
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
AWS CLI를 통해 보안 그룹에 IP를 추가할 것이기 때문에 CLI 설정을 진행합니다. AWS IAM을 통해 계정을 생성하고 Access key 와 Secret key를 발급 받아 AWS Region 값(ap-northeast-2)과 함께 GitHub Repository Secrets 에 추가합니다. IAM 계정의 정책은 아래와 같이 설정합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "arn:aws:ec2:*:*:security-group/*"
}
]
}
보안그룹 규칙 추가
- name: Add security group rule
run: |
RULE_ID=$(aws ec2 authorize-security-group-ingress \
--group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} \
--protocol tcp \
--port 22 \
--cidr ${{ steps.ip.outputs.ipv4 }}/32 \
--query 'SecurityGroupRules[0].SecurityGroupRuleId' \
--output text)
echo "RULE_ID=${RULE_ID}" >> $GITHUB_ENV
- $GITHUB_ENV: 워크플로우 내에 환경 변수를 등록합니다.
- EC2_SECURITY_GROUP_ID: 추가할 보안 그룹의 아이디입니다. (예시- sg9340239f123d98)
보안 그룹에 규칙을 추가하는 CLI 명령어입니다. 명령어를 실행하면 생성된 보안 그룹 규칙의 식별자 ID 값이 반환되고 이를 환경 변수로 등록합니다. 워크플로우 과정 마지막에 이 ID 값을 통해 생성한 규칙을 삭제합니다.
배포 진행
- name: Deploy to EC2
uses: appleboy/ssh-action@master
with:
host: ${{ steps.set-env.outputs.ENV == 'prod' && secrets.PROD_EC2_HOST || secrets.STAGE_EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# Docker Compose 설치 (없는 경우에만)
if ! command -v docker-compose &> /dev/null; then
sudo curl -L "https://github.com/docker/compose/releases/download/v2.30.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
fi
mkdir -p ~/stockscope
cd ~/stockscope
# 기존 컨테이너 상태 확인 및 정리
if [ -f docker-compose.yml ]; then
docker-compose down
fi
# 기존 docker-compose.yml 백업 (선택사항)
[ -f docker-compose.yml ] && mv docker-compose.yml docker-compose.yml.bak
# 현재 브랜치명 추출
BRANCH_NAME="${{ github.ref_name }}"
# 프로젝트의 docker-compose.yml 다운로드
wget https://raw.githubusercontent.com/HyunJaae/stockscope/${BRANCH_NAME}/docker-compose.yml
# .env 파일 생성/업데이트
cat > .env << EOL
DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
ENV=${{ steps.set-env.outputs.ENV }}
DB_NAME=${{ secrets.DB_NAME }}
DB_USERNAME=${{ secrets.DB_USERNAME }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
EOL
# 컨테이너 실행
docker-compose pull
docker-compose up -d
- PROD_EC2_HOST: 운영 환경 서버의 IP
- STAGE_EC2_HOST: 스테이징 환경 서버의 IP
- EC2_USERNAME: EC2 유저명
- EC2_SSH_KEY: EC2 생성 시 발급한 pem 키
브랜치에 따라 EC2 호스트 정보를 정하고 pem 키를 통해 EC2에 접근합니다. 저는 SpringBoot 프로젝트뿐 아니라 PostgreSQL과 Redis도 사용하기 때문에 Docker Compose 를 사용해 여러 컨테이너를 관리합니다. 따라서 EC2에 Docker Compose가 없다면 설치하고 프로젝트 폴더를 생성합니다.
기존에 Docker Compose 로 실행 중인 컨테이너가 있다면 종료하고 기존 Docker Compose 파일을 백업시킵니다. 그리고 프로젝트의 Docker Compose 파일을 프로젝트 폴더에 다운로드합니다. Docker Compose 파일에서 사용할 환경 변수 파일을 생성하고 실행합니다. Docker Compose 파일은 아래와 같이 작성했습니다.
services:
app:
image: ${DOCKERHUB_USERNAME}/stockscope:${ENV}
container_name: stockscope
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=${ENV}
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/${DB_NAME}
- SPRING_DATASOURCE_USERNAME=${DB_USERNAME}
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}
- SPRING_REDIS_HOST=redis
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15
container_name: stockscope-db
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7
container_name: stockscope-redis
restart: unless-stopped
volumes:
postgres_data:
보안 그룹 규칙 제거
- name: Remove security group rule
if: always()
run: |
aws ec2 revoke-security-group-ingress \
--group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} \
--security-group-rule-ids ${RULE_ID}
- always(): 배포 job 의 실패여부와 상관없이 항상 실행합니다.
앞서 환경 변수로 저장했던 새로 생성한 보안 그룹의 식별자 ID 값으로 AWS CLI를 통해 삭제를 진행합니다.
마치며
지금까지 GitHub Actions 와 Docker 를 통한 CI/CD 파이프라인 구축 과정을 알아봤습니다. 추가적으로 개선할 점은 Gradle 캐시를 활용해서 빌드 시간을 단축하고 아티팩트 저장 시 현재는 모든 빌드 결과물을 저장하지만 도커 파일에서 필요한 파일만 저장하면 더 좋을 것 같습니다. 앞서 언급한 내용들뿐만 아니라 더 세부적인 설정도 가능하니 공식 문서를 참고하시면 좋겠습니다.
긴 글 읽어주셔서 감사합니다.
'Infrastructure' 카테고리의 다른 글
Ubuntu 24.04 에서 Docker 설치하기 (0) | 2024.11.30 |
---|
댓글