최종 프로젝트에서 애플리케이션 배포를 위해 GitHub Actions 의 파이프라인을 구축하여 EC2 인스턴스에 Docker 컨테이너를 실행시키기로 했다.
1 기술 채택
- GitHub Actions
- GitHub 저장소를 사용해온 친숙함이 가장 크게 작용
- 추가적인 설치 필요 없이 GitHub 환경에서 이용 가능
- 공개 Repository 의 경우 특정 사용시간내에서 무료로 제공
- AWS
- 직접 서버를 설치하고 관리하는 작업 대비 시간과 비용 측면에서 효율적
- 프로젝트가 불명확한 상태에서 필요한 리소스를 언제든지 늘이거나 줄일 수 있는 유연성, 확장성
- 온라인/오프라인 상 많은 학습 자료
- Docker
- 애플리케이션의 구성과 종속성을 캡슐화하기 때문에 환경의 일관성을 보장할 수 있는 점
- 프로젝트에 사용하는 백/DB 를 단일 서버에서 실행하여야 하는데 환경을 분리해 영향을 최소화할 수 있는 점 (비용문제가 명확해진 뒤에 DB 는 Amazon RDS 로 옮겼다.)
- 개인 사용자 계정으로 무료 버전 활용 가능
2 환경설정
- GitHub Actions
- 필요한 Secrets 설정
- 프로젝트 루트에 .github/workflows 디렉토리 생성
- AWS
- 인스턴스 생성 (프리티어)
- 보안 그룹 설정
- Docker
- docker 계정 생성
- 개인 repository 확인
3 EC2 인스턴스
ubuntu 운영체제
프리티어 t2.micro 인스턴스
파이프라인 구축 전 인스턴스 내에서 직접 compose 실행을 해보기 위해 docker 관련 세팅을 했었다.
타임존 설정
` sudo su `
` sudo timedatectl set-timezone Asia/Seoul sudo echo Asia/Seoul > /etc/timezone `
docker 설치
` sudo apt update `
` sudo apt install docker.io `
` sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose `
` sudo chmod +x /usr/local/bin/docker-compose `
이후 docker 명령어 실행 시 'permisson denied' 발생
docker 사용자그룹, 권한 추가
` sudo groupadd docker `
` sudo usermod -a -G docker $USER `
추가 후 인스턴스에 다시 접속 후 로그인
` docker login `
4 Docker 설정
Dockerfile 작성
ROM openjdk:17
COPY build/libs/MovieToGather-0.0.1-SNAPSHOT.jar app.jar
ENV SPRING_PROFILES_ACTIVE=prod
ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=prod"]
파이프라인에서 ./gradlew build 후 빌드되는 Dockerfile 로 openjsk:17 을 베이스이미지로 하여 build/libs 내의 jar 파일을 prod Profile 로 실행하는 내용을 작성하였다.
*처음 빌드할 때 dockerfile 로 작성했다가 인식을 못하는 문제가 있었다.
*프로젝트 루트 디렉토리
Docker-compose.yml 작성
version: '3.8'
services:
movietogather-postgres:
image: postgres:alpine
container_name: movietogather-postgres
env_file:
- .env
restart: always
environment:
POSTGRES_DB: movietogatherdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
TZ: "Asia/Seoul"
volumes:
- movietogatherdb:/var/lib/postgresql/data
ports:
- 5432:5432
movietogather-backend:
image: ${DOCKER_USERNAME}/${DOCKER_REPONAME}:latest
container_name: movietogather-backend
env_file:
- .env
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_DATASOURCE_URL: ${POSTGRES_URL}
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET}
AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR: ${AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-ID: ${AUTH_GOOGLE_ID}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-SECRET: ${AUTH_GOOGLE_SECRET}
TZ: "Asia/Seoul"
depends_on:
- movietogather-postgres
ports:
- 8080:8080
restart: on-failure
volumes:
movietogatherdb:
*프로젝트 루트 디렉토리
프로젝트 초기, 비용 문제가 명확하지 않아 인스턴스 내에 데이터베이스 서버와 백엔드 서버를 같이 실행하기로 하였고 여러 서버를 한 인스턴스 내에서 효율적으로 관리하기 위한 방법으로 Docker 를 선택했다.
각각의 이미지를 별도로 실행할 수도 있지만 docker-compose 를 이용하여 compose 내의 서비스 간 동일한 네트워크를 구성할 수 있고 여러 실행 설정들을 한 번에 지정할 수 있다. 데이터베이스를 RDS 로 옮긴 이후에도 설정의 간편성, 프로젝트의 가용성을 위해 compose 명령어로 백엔드 서버를 실행 중이다.
데이터베이스를 관리하기 위하여 컨테이너 볼륨을 생성했다. 이후 db 컨테이너를 종료했다가 다시 실행시켜도 데이터가 유지돼서 활용할 수 있었다.
현재 docker-compose.yml
version: '3.8'
services:
movietogather-backend:
env_file:
- .env
image: ${DOCKER_USERNAME}/${DOCKER_REPONAME}:latest
volumes:
- /home/ubuntu/logs:/app/logs
container_name: movietogather-backend
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_DATASOURCE_URL: ${POSTGRES_URL}
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET}
AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR: ${AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-ID: ${AUTH_GOOGLE_ID}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-SECRET: ${AUTH_GOOGLE_SECRET}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_NAVER_CLIENT-ID: ${AUTH_NAVER_ID}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_NAVER_CLIENT-SECRET: ${AUTH_NAVER_SECRET}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT-ID: ${AUTH_KAKAO_ID}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT-SECRET: ${AUTH_KAKAO_SECRET}
SPRING_DATA_REDIS_HOST: ${REDIS_HOST}
SPRING_DATA_REDIS_PORT: ${REDIS_PORT}
TZ: "Asia/Seoul"
ports:
- 8080:8080
로그 파일 관리를 위해 백엔드 컨테이너의 볼륨도 설정해줬다.
컨테이너 내에 app/logs 디렉토리를 생성, 지정하고
호스트 시스템의 /home/ubuntu/logs 디렉토리를 마운트했다.
이후 생성된 로그파일은 WinSCP 를 이용하여 간편하게 확인했다.
5 CI/CD 파이프라인 구축
deploy workflow 구성
runner : ubuntu
trigger : main/ deb/ feat/cd 브랜치에 push 됐을 때
steps:
- 체크아웃
- JDK17 설치
- Github Actions 가상 환경 상 프로젝트 빌드, 테스트를 위함
- Gradle 캐싱
- 빌드 시간 단축
- Gradle 권한 부여
- Gralde build (+테스트)
- Docker install
- Docker login
- Docker image build
- 작성한 Dockerfile 로 백엔드 애플리케이션 image build
- Docker image push
- Docker Repository 에 build 한 image push
- EC2 명령어 수행
- docker login
- 실행 중인 docker-compose down
- 기존 docker image 삭제
- ec2 내에 있는 docker-compose.yml 파일 실행
name: MovieToGather-Deploy
on:
push:
branches: [ main, dev, feat/cd ]
jobs:
build-and-push-docker:
runs-on: ubuntu-latest
env:
API_KEY: ${{ secrets.API_KEY }}
KAKAO_MAP_AK: ${{ secrets.KAKAO_MAP_AK }}
TMAP_AK: ${{ secrets.TMAP_AK }}
AUTH_JWT_SECRET: ${{ secrets.AUTH_JWT_SECRET }}
AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR: ${{ secrets.AUTH_JWT_ACCESSTOKENEXPIRATIONHOUR }}
AUTH_JWT_REFRESHTOKENEXPIRATIONHOUR: ${{ secrets.AUTH_JWT_REFRESHTOKENEXPIRATIONHOUR }}
AUTH_GOOGLE_ID: ${{ secrets.AUTH_GOOGLE_ID }}
AUTH_GOOGLE_SECRET: ${{ secrets.AUTH_GOOGLE_SECRET }}
AUTH_NAVER_ID: ${{ secrets.AUTH_NAVER_ID }}
AUTH_NAVER_SECRET: ${{ secrets.AUTH_NAVER_SECRET }}
AUTH_KAKAO_ID: ${{ secrets.AUTH_KAKAO_ID }}
AUTH_KAKAO_SECRET: ${{ secrets.AUTH_KAKAO_SECRET }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
REDIS_HOST: ${{ secrets.REDIS_HOST }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_REPONAME: ${{ secrets.DOCKER_REPONAME }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: JDK 17 설치
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'corretto'
- name: Gradle 캐싱
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Gradle 권한 부여
run: chmod +x gradlew
- name: Gradle 빌드
run: ./gradlew build
- name: Docker install
uses: docker/setup-buildx-action@v1
- name: Docker 로그인
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Docker image 빌드
run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPONAME }}:latest
- name: Docker image push
run: docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPONAME }}:latest
- name: Deploy to EC2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
sudo docker-compose down || true
sudo docker image rm ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPONAME }}:latest || true
sudo docker-compose up -d
*workflow 에서 사용된 변수들은 github repository secrets 에 설정한다.
docker-compose 가 실행되면서 docker repository 에 push 됐던 image 를 pull 하여 컨테이너를 실행시킨다.
6 발생했던 문제들
1) Docker 의 높은 학습 곡선
도커는 많은 이점을 제공하는 도구인 동시에 학습 곡선이 매우 가파르게 느껴졌다. 특히, 컨테이너의 데이터를 보존하기 위한 볼륨 관리가 어렵게 느껴졌다. 예를 들어, 데이터베이스 컨테이너를 사용하는 동안 데이터의 지속성을 보장하기 위해 주기적으로 백업을 수행하는 과정이 복잡하게 느껴졌다.
이러한 어려움을 극복하기 위해, 도커와 관련된 기술을 보다 체계적으로 학습하기 위한 기간을 미리 정하고, 도커 작업을 하는 팀원과 서로 지식을 공유했다. 이 과정에서 다양한 자료와 도커 공식 문서를 참고하여, 볼륨 관리와 데이터 보존 방법에 대해 깊이 있는 이해를 할 수 있었다.
결과적으로, 데이터베이스를 Amazon RDS로 이전했지만, 이 과정을 통해 도커에 대한 깊은 학습 기회를 가질 수 있었다. 도커의 볼륨 관리와 데이터 보존 방법에 대한 이해를 넓히는 이러한 경험은 도커를 활용한 다양한 애플리케이션의 배포와 운영에 대한 이해를 높이는 데 큰 도움이 되었다.
2) 환경변수 문제
Github Actions 을 통한 CI/CD 파이프라인을 구성하던 중, contextLoads() 실패와 같은 빌드 에러에 직면했다. 로컬 환경에서는 문제가 없었지만, Ci 환경과 EC2 서버에서는 동일한 문제가 발생했다.
MovieToGatherApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
Caused by: io.github.cdimascio.dotenv.DotenvException at DotenvParser.java:76
처음에는 .env 파일에 정의된 변수들이 올바르지 않다고 생각해서 변수를 삭제하고 다시 작성하는 일을 반복했지만 해결되지 않았다.
그러던 중 로컬에서 dotenv 라이브러리를 비활성화하고 테스트 했을 때 동일한 에러가 발생하고
서버에서 ./gradlew bootRun 을 실행했을 때는 로컬처럼 정상적으로 실행되는 걸 발견했다.
build 와 bootRun 의 차이가 라이브러리 비활성화에 영향을 끼친다고 판단하여 이 둘의 작동 방식을 알아보았다.
./gradlew
Gradlew Wrapper 의 실행 파일로 자바, 코틀린 등을 사용하는 프로젝트의 의존성 관리와 빌드 자동화를 위한 도구
./gradlew build
프로젝트 루트를 기준으로 class path 설정
프로젝트를 컴파일하고 실행하여 실행 가능한 jar 파일을 생성
./gradlew bootRun
resources 디렉토리 내를 class path 로 설정
애플리케이션을 직접 실행, 별도의 jar 파일 생성 없이 빠르게 실행, 테스트 가능
결국에는 cicd 과정 중 .env 파일을 루트 디렉토리에 생성하고 또 ec2 내에서 직접 실행했을 때도 .env 파일을 루트 디렉토리에 위치한 채로 생성된 jar 파일을 실행했기 때문에 build 과정과 실행과정에서 변수를 찾지 못해 위와 같은 에러가 발생한 것이었다. 파일 위치를 resources 하위로 옮긴 뒤 정상적으로 실행되는 것을 확인했다.
이 경험은 문제에 접근할 때 구조적 사고의 중요성을 깨닫게 해주었다. 단순히 처음에 내가 변수를 무작정 다시 작성해본 것과 같은, 문제의 표면만 보고 해결책을 찾으려 하기 보다는
전체적인 시스템을 이해하고 내가 지금 어떤 도구를 사용하고 있고 이 도구가 어떤 방식으로 동작하는지, 그래서 어떻게 다루어야 하는지를 아는 것이 중요함을 배웠다.
3) 인스턴스
(1) 접속 불가
배포 이틀 차에 평소처럼 사이트를 접속했는데 UI 는 정상적으로 보이지만 데이터가 호출되지 않았다. 백엔드로의 요청이 계속 실패상태. 인스턴스를 확인하려 하니 접속도 되지 않아 서버 확인도 불가능한 상황. 로그 파일을 확인하려 해도 접속이 안되니 확인을 못했다.
단서를 찾을 방법이 없어서 바로 도움을 구하러 갔고 제일 먼저 이런 상황에 인스턴스를 중지했다 다시 시도하라는 말을 들었다.
중지했다 다시 시작하니 정상적으로 작동이 됐다.
프리티어 인스턴스를 사용 중이었고 그렇다 보니 이런 일이 종종 발생한다고 한다. 그때는 중지했다 다시 시작해보고 그럼에도 접속이 되지 않으면 이런 때 인프라 확장 문제를 적용할 수 있다고 한다.
스케일 업의 경우는 현재 사용 중인 프리티어가 아닌 더 높은 성능의 인스턴스로 전환.
스케일 아웃의 경우 서버를 추가하여 각 서버에 걸리는 부하를 분배해주는 방향.
현재 인스턴스의 메모리 사용량은 78%인 것을 확인했고 당장의 서버 개선보다는 swap 메모리를 활용하여 메모리를 확보하는게 낫다고 판단하여 swap 메모리를 적용하여 60%대 까지 낮췄다.
swap 메모리 적용
이후에 프로젝트를 팀원들과 계속 관리하면서 위와 같은 다른 방법들을 적용할 계획이다.
(2) docker
배포 삼일 차, 팀원이 리드미를 추가하는 과정에서 cd 실패 에러가 발생했다.
알림을 보고 actions 를 확인해보니 다음과 같은 에러를 확인할 수 있었다.
err: Stopping *** ...
err: Stopping *** ... done
err: Removing *** ...
err: Removing *** ... error
err: ERROR: for *** removal of container 921bf405dc343560d335545360abc8f17fa81b2a64322d221f061016cd466a44 is already in progress
err: Removing network ***_default
err: Error response from daemon: No such image: ***/***:latest
err: Creating network "***_default" with the default driver
err: Pulling *** (***/***:latest)...
out: latest: Pulling from ***/***
out: Digest: sha256:dcf21c509604dafceff42104774316a318fa53912ee4af28c90fb106cfdb0b96
out: Status: Downloaded newer image for ***/***:latest
err: Creating *** ...
err: Creating *** ... error
err: ERROR: for *** Cannot create container for service ***: Conflict. The container name "/***" is already in use by container "e9367c355b3b3dbc016a50acacd9dde47fb189581b51064531258622624f738a". You have to remove (or rename) that container to be able to reuse that name.
err: ERROR: for *** Cannot create container for service ***: Conflict. The container name "/***" is already in use by container "e9367c355b3b3dbc016a50acacd9dde47fb189581b51064531258622624f738a". You have to remove (or rename) that container to be able to reuse that name.
컨테이너를 실행하고 네트워크를 생성하는 과정에서 문제가 발생하였는데 로그를 보면 동일한 컨테이너와 네트워크가 이미 존재하기 때문에 발생했다.
프로젝트 deploy workflow 는 실행중인 compose 를 멈추고 컨테이너 삭제, 이미지 삭제 후 실행한다.
정상적으로 docker-compose down 명령어가 실행이 됐다면 컨테이너와 네트워크가 삭제되었을 텐데
인스턴스 내에서 확인해보니 docker-compose 로 인한 컨테이너가 stop 되지도 않았고 실행 중인 상태였다.
err: Removing *** ... error
down 으로 stop하는 과정에 문제가 생겨서 실행중인 컨테이너를 remove 하려고 하니 에러가 발생했고 또 그 상태에서 명령어대로 compose up 을 실행하니 문제가 생겨 deploy 에 실패한 것 같았다.
결과적으로 docker-compse down 명령어가 제대로 실행되지 않았다.
직접 실행중인 컨테이너를 멈추고 네트워크도 정리한 뒤에 workflow 를 다시 run 하니 정상적으로 실행이 되었고 그 이후에 발생한 action 도 정상적으로 실행됐다.
docker-compose down 실패의 경우 대부분 permission denied 문제로 발생하던데 이것과는 관련이 없었다.
모니터링서버에서 해당 시간대를 확인해보니
파이프라인 실행 중에 CPU 사용량이 천장을 찍었다.
프리티어 인스턴스가 많이 힘들었나보다.
처음 계획대로 db 서버도 같은 인스턴스 내에서 운영했다면 진작에 터졌을 문제인 것 같다
일단은 이대로 유지를 하고 마찬가지로 이후에 프로젝트를 관리하면서 서버를 확장하든지 하는 방향으로 해야할 것 같다.
4) 그 외
(1) copy 명령어 타임아웃
배포 초기에는 repository 에 존재하는 compose 파일을 파이프라인 과정 중에 ec2 서버로 복사해서 실행시키는 것을 택했었고 잘 수행이 됐었다.
# EC2 배포
- name: Copy docker-compose.yml
uses: appleboy/scp-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
source: "./docker-compose.yml"
target: "./"
그러던 중 갑작스럽게 Copy steps 에서 실패가 나왔고 이후에도 계속 같은 에러로 실패했다.
drone-scp version: v1.6.14
tar all files into /tmp/rLaJAPFjDc.tar.gz
remote server os type is unix
scp file to server.
2024/03/19 07:32:52 error copy file to dest: ***, error message: dial tcp 13.124.106.150:22: i/o timeout
drone-scp error: error copy file to dest: ***, error message: dial tcp 13.124.106.150:22: i/o timeout
appleboy/scp-action 과 관련해서 같은 이슈를 찾아봤고
https://github.com/appleboy/scp-action/issues/52
방화벽 설정을 수정하는 명령어를 참고했다.
` sudo ufw allow 22 `
인스턴스 내에서 명령어를 실행하고 한 번 성공하고 계속 사용하다 그 뒤에 다시 똑같은 에러로 실패했다.
인스턴스 내에서 .env 파일을 관리하는 김에 compose.yml 도 같이 관리하기로 하고 steps 에서 복사 과정을 삭제하고 바로 compose up 을 실행시켜서 사용했다.
이후 정리하면서 다시 찾아보니 master 버전과 0.1.4 버전에서 동일한 오류가 있었고 0.1.3 버전에서는 정상작동한다는 것을 알게됐다. 당시에는 실패하면 안된다는 압박감과 부담감에 파일을 직접 관리하는 방향을 선택했지만 이슈란을 더 꼼꼼하게 살펴봤다면 버전 변경만으로 손쉽게 해결할 수 있지 않았을까.
또한 workflow 를 작성하기 위한 많은 자료들을 참고하면서 그 과정에 사용되는 명령어들을 주의깊에 살펴볼 생각을 못했었다. 프로젝트가 진행된 지금 scp-action 이 0.1.7 버전까지 나왔고 위의 에러 없이 정상적으로 수행되고 있었다.
아마 계속 반복하고 사용하다보니 보는 눈이 넓어져서 이제야 보이는게 아닐까 싶지만 새로운 것을 접했을 때 조급한 마음을 관리할 필요가 있는 것 같다.
7 프론트 서버 S3 업로드
버킷 이름에는 식별하기 쉽도록 구입한 도메인과 똑같은 이름을 입력했다. ( 도메인 엔드포인트는 탄력적 ip 지정 )
객체 소유권 비활성화
퍼블릭 엑세스 차단 해제
이외의 설정은 모두 기본 설정대로 버킷 만들기 선택
생성된 버킷
속성 탭 선택
정적 웹 사이트 호스팅 편집 활성화
권한 탭 선택
버킷 정책 편집
버킷 정책 생성기를 통해 생성된 JSON 을 작성했다.
이후 생성된 버킷에 파일을 업로드한다.
우리 프로젝트의 경우 vue.js 를 이용하여 프론트서버를 만들었고
npm run build 후 만들어진 dist 디렉토리 내의 파일과 폴더들을 업로드했다.
8 간단 회고
최종 프로젝트에서 배포 업무에 자원하고 작업하면서 많은 것들을 배웠다.
처음 시작할 때만 하더라도 내가 알고 있던 지식은 ec2 가 aws 에서 제공하는 서비스라는 것, 그리고 github actions 를 통해 자동으로 배포할 수 있다는 것 뿐이었다.
docker 를 채택한 이유가 위에 나와있듯이 이 기술을 사용함으로써 얻을 수 있는 편리함이었는데 그만큼의 학습량을 요구한다는 것을 예상하지 못했다. 때문에 프로젝트 2주차부터 docker 학습에 긴 시간을 소비했고 이후에도 환경변수 관련 문제로 여러 번 막막함과 어려움을 느꼈다. 삽질의 흔적이 프로젝트 저장소에 많이 남아있는데 그 흔적만큼 똑같은 명령어를 반복적으로 입력하다보니 자연스럽게 리눅스 명령어도 많이 익히게 되었다.
무엇보다 가장 기억에 많이 남는건 환경변수 관련이다. 변수를 인식하지 못하는 문제로 며칠을 앓았고 이를 통해 알게된 여러 실행 방식과 애플리케이션의 작동 방식들을 새롭게 알게 된 게 영향을 많이 끼친 것 같다. 지난 4개월동안 나는 정말 배운대로 배운 것만 써먹었고 그것들에 대해서 더 알려고 하지 않았다는게 부끄럽게 느껴지기도 했다. ./gradlew 을 여태 수십번? 수백 번은 내 손으로 입력하고 있었는데 그걸 모르고 앉았다니...역시 어려움을 마주하면 배울게 있구나 싶었다...물론 그 당시에는 내가 내 손으로 맡은 배포를 망칠까봐 힘든 마음도 많이 있었지만.
회고를 하고 정리를 하려면 무엇이 문제인지 정확히 알아야 한다. 지금 이렇게 정리를 해보면서도 내가 놓친 부분들이 있어 작성하는데에만 몇 시간이 걸렸다. 이 과정들을 계속 반복하다보면 또 쌓이는게 많아지고 이걸 왜 몰랐지? 하는 순간이 생길텐데 그때까지 꾸준하게 잘 해내야겠다.
'왕초보일지' 카테고리의 다른 글
TMDB 영화 Open API 구현 (0) | 2024.04.03 |
---|---|
240315 TIL | 3주차 마무리 전체 테스트 (0) | 2024.03.15 |
스프링 부트 build contextLoads() Failed (1) | 2024.03.06 |
240227 TIL | ERD 설계 고민 (2) | 2024.02.29 |
240223 TIL | (0) | 2024.02.23 |