
이번엔 docker hub를 활용하여 배포를 해보려 한다.


Docker로 만든 이미지들을 보관하는 hub이다.
Github와 같은 맥랙이라고 이해하면 된다.
서비스를 배포하고 운영하다보면, 기타 여러 이슈들이 생겨
이전 버전으로 돌아가는 경우들이 필요로 한데
이러한 경우에 손쉽게 docker hub에 저장된 이미지를 받아와 컨테이너를 실행시키면 된다!!
여기서 좀 더 나아가면 쿠버네티스라는 오케스트레이션 엔진이 존재한다.
위와 같은 docker 컨테이너들이 MSA 적용을 할 경우엔 수 백개가 존재하게 된다.
이런 경우 그 수많은 컨테이너들을 관리하는데는 어려움이 생기게 되고
이를 위해 만들어진 것이 오케스트레이션 엔진 중 하나인 쿠버네티스이다.
쿠버네티스 외에도 아마존, docker, apache에서 만든 여러 도구가 있으나
쿠버네티스가 가장 널리 사용되고 있다.
수동으로 컨테이너들을 관리하지 않고, 쿠버네티스가 자동으로 관리하여
배포뿐만 아니라, 필요하다면 스케일링을 통한 로드밸런싱을 진행한다.
또한 장애 발생시에 복구와 같은 기능도 가지고 있어서 굉장히 유용하게 사용이 된다.
하지만 최근 버전부터는 docker를 정식 지원하지 않고 있다.
다시 본론으로 들어와서 docker hub 사용을 위해 회원가입을 진행한다.
https://hub.docker.com/
Github과 마찬가지로 private과 public 사용이 가능하다.
private의 경우 무료는 1개만 repository 생성이 가능하다.
name space 생성 -> create reapository -> 옵션 선택 (private or public) -> 생성 완료
위의 단계로 repo 생성이 가능하다.
생성하고 나면 아래 이미지와 같이 정보 확인이 가능하고, push를 통해 docker image 업로드가 가능하다.

순서대로 설명을 하자면
서비스 docker iamge로 생성 -> docker hub push -> (필요한 곳에서 pull) -> docker run
앞선 포스트에서 설명했듯 Nginx와 React를 묶어서 하나의 이미지로 만들 것이다.
그리고 중요한 점은 Dockerfile과 nginx.conf 파일을 react 최상위 폴더에 두어야 한다.
도커 이미지로 만들기 위해서는 dockerfile이라는 파일이 필요하다.
해당 파일은 docker image의 세팅 파일 역할이다.
어떻게 하냐에 따라 docker image가 다르게 만들어진다.
일반적으로는 버전을 명시하고, build에 필요한 설정을 해둔다. (여기서 외부에서 환경변수 주입도 가능)
프론트엔드의 경우 백엔드와 약간 다른데
react build > nginx 이미지를 통한 docker build 로 이루어진다.
Nginx > react가 아닌 Nginx > Nginx (react)로 이루어지기 때문이다.
# nginx 이미지 사용
FROM nginx:latest
# root에 /app 폴더 생성
RUN mkdir /app
# work dir 고정
WORKDIR /app
# work dir에 dist 폴더 생성
RUN mkdir ./dist
# host pc의 현재 경로의 build 폴더를 work dir의 build 폴더로 복사
ADD ./dist ./dist
# nginx의 default.conf 삭제
RUN rm /etc/nginx/conf.d/default.conf
# host pc의 nginx.conf를 아래 경로에 복사
COPY ./nginx.conf /etc/nginx/conf.d
# 80 포트 개방
EXPOSE 80
# container 실행 시 자동으로 실행할 command. nginx 시작함
CMD ["nginx", "-g", "daemon off;"]
위는 dockerfile의 예시이다. 각 명령어에 대한 설명은 comment를 참고하면 된다.
일반적으로 build 명령어를 통해 react를 build하면 /build 폴더가 생성되지만 webpack을 사용했기에 dist라는 폴더가 생성되었다. 이 부분은 프론트엔드에서의 설계를 따라가면 된다.
nginx.conf의 경우 자동 생성된 nginx.conf가 아닌 내가 만든 nginx.conf를 통해 nginx를 실행하는 것이다.
server {
listen 80;
location / {
root /app/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
}
위와 마찬가지로 80번 포트를 개방했기에 80번 포트를 열어두었고
정적 파일 호스팅을 위해 위치를 /app/dist로 하였다.
일단은 jenkins 혹은 Github Actions 설정을 하지 않았기 때문에, 수동으로 Github에서 파일을 받아와야 한다.
현재 코드가 public 저장소에 권한 없이 올려져 있어 clone 명령어를 통해 가져올 수 있지만
만약 priavte에 접근 권한이 필요하다면, gh를 설치하여 로그인하고 공개키를 생성해 github에 등록을 해야 push&pull이 가능하다.
public이기에 clone을 통해 코드를 가져왔다.

현재 Dockerfile과 nginx.conf파일이 있는 것이 확인 된다.
여기서 중요한 건 꼭!! react root 폴더 안에 들어가서 작업을 해야한다
이상태에서 build를 통해 폴더를 생성한다. npm을 이용해야하니 npm을 먼저 설치하자
이후 npm install > npm run build를 통해 빌드를 진행
필자는 먼저 이전에 hub에 올려뒀었던 파일을 받아 진행하려고 한다.
# docker login
docker login
위 커맨드로 docker hub에 로그인을 하면 된다.
그러면 username과 password를 요구할 텐데
username는 로그인 이메일
password는 Docker hub > My Account > Security > Access Token 에서 액세스 토큰을 발급받아
해당 토큰을 비밀번호로 입력해주며 된다.
액세스 토큰의 경우 발급시 최초 1회만 알려주기 때문에, 잊어버리지 않도록 꼭 주의하자!!
(다시 재발급을 받아도 되긴 하다)
이러면 docker hub 연결 완료!!
먼저 push를 해보자!!
push를 하기 위한 단계는 코드 github에서 받기 > react build > 이미지 만들기 > docker hub push
# 도커 이미지 생성
sudo docker build -t 이미지명:tag
# 도커 이미지 푸시
sudo docker push 저장소/이미지명:tag
pull은 그냥 저장소에서 pull 받으면 된다.
이후에 sudo docker images로 받은 이미지를 확인하고, 명령어를 통해 컨테이너 실행하면 된다.
내가 컨테이너 실행을 위해 사용한 명령어는
sudo docker run -i -e TZ=Asia/Seoul --name 컨테이너 이름 --network 네트워크 이름 -p 3000:80 -d 사용할 이미지
위와 같다.
기본적으로 docker run을 통해 실행을 한다.
TZ : 타임존 설정name : 컨테이너 이름 설정network : 네트워크 설정 (이 부분 중요, 추후에 다시 설명 예정, 만약 백엔드와 DB를 사용하지 않는다면 설정 미필요)p : 포트 포워딩 (3000포트로 들어오면 80번 포트로 연결, react를 3000번으로 열었기 때문)d : 사용할 이미지 선택 (docker hub에서 pull 받은 이미지 or 만든 이미지)네트워크 생성은 sudo docker network create 네트워크이름을 통해 가능하다.
각 옵션은 각자의 서비스에 맞게 수정을 하면 된다.
이렇게 하면 프론트엔드 배포는 끝이다.
앞에서 대부분의 설정을 해 뒀고, 또한 spring boot의 경우는 IDE를 사용하지 않고
서버를 빌드해서 실행해 본 경우가 많이 있을 거라 어렵지 않게 이미지 만들고 실행이 가능하다.
./gradlew clean bootJar
root 폴더로 이동한 뒤 위 명령어를 통해 build가 가능하다.
clean의 경우 기존의 빌드 파일을 제거하고 새롭게 빌드한다는 옵션이다.
빌드가 완료되면, build 폴더안에 필요한 파일들이 생성된다.
Dockerfile은 항상 취상위 폴더 안에 있어야 한다. Spring Boot도 마찬가지!!
FROM --platform=linux/amd64 openjdk:17-alpine
# 개인키 복사
COPY apiEncryptionKey.jks /apiEncryptionKey.jks
# jar 설정
ARG JAR_FILE=/build/libs/mail-service-1.0.jar
# yml 복사 (spring config를 사용하여 필요x)
#COPY src/main/resources/application.yml /app/application.yml
# jar 복사
COPY ${JAR_FILE} /MailService.jar
# PORT
# EXPOSE 9004
# Set the entry point for the container to run the Spring Boot application
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod","/MailService.jar"]
각 옵션에 대해서 설명을 하자면
FROM --platform : 실행환경openjdk:17-alpine : 뒤에 alpine은 경량화 버전을 의미한다. 좀 더 가볍고 보안적으로 뛰어나 대부분은 빌드에서 사용한다COPY : 중간의 개인키의 경우 현재 아키텍처에서 spring config를 사용하기 때문에 해당 키를 사용해야 하기 때문이다.yml : yml파일을 사용하여 설정을 할 경우 복사가 필요jar : jar 파일의 위치 명시EXPOSE : 서버의 포트 설정 (나의 경우는 특정 포트로 오픈을 하지 않아 설정하지 않았다)ENTRYPOINT : 어떻게 실행할 지를 명시해주는 것, 일반적으로 command shell에서 자바 실행시키는 것과 같다. profile의 역할 명시, 외부 환경 변수 주입 등이 가능하다.위에서 프론트엔드와 동일한 방식이다.
# docker image build
sudo docker build -t namespace/저장소:tag명
# docker image push to docker hub
sudo docker push namespace/이미지명:tag
# docker image pull from docker hub (tag 생략시 latest 최신 버전)
sudo docker pull namespace/test
sudo docker run -i -e TZ=Asia/Seoul -e "SPRING_PROFILES_ACTIVE=dev" --network lost-in-frost --name c101-backend -p 8081:8081 -d lostinfrost/c101-backend:latest
대부분의 옵션은 프론트엔드와 비슷하다
약간 다른점은
e : 외부 환경변수 주입 (profile dev 활성화)p : 포트포워딩 (백엔드의 경우 원하는 포트로 열어주면 된다, MSA 사용시에는 포트로 서비스를 찾이 않기에 포트포워딩 필요 X)백엔드의 경우 여러 서버를 각 서버에 맞게 설정만 변경해서 빌드 후 배포해주면 된다.
위에서 말했듰이, network가 중요하다.
일반적으로 프론트엔드만 띄워서 사용하려고 하면 network는 필요없다.
하지만 서버, DB와의 통신이 필요한 경우는 network설정은 필수다.
도커 컨테이너를 여러 개를 띄우고, 그 안에서 여러 포트가 살아있기에
네트워크를 설정해주지 않으면, 통신이 원할하지 않기에 네트워크를 설정해 그 안에서 통신 할 수 있도록 해주는 것이다.
네트워크의 경우는,sudo docker network create 네트워크이름,sudo docker network ls를 통해 네트워크 생성과 생성된 네트워크 확인이 가능하다.
DB로 Mysql을 사용하지만 다른 RDBMS도 비슷하다.
일반적으로 DB를 서버에 설치하여 사용하려면 직접 Mysql을 설치하고 관련된 설정을 해야하지만, docker를 사용하면 간편하게 mysql 사용이 가능하다.
먼저 mysql의 이미지를 받아준다.
# 이미지 받기
sudo docker pull mysql
# 이미지 확인
sudo docker images
그 다음 컨테이너를 실행할 때의 옵션이 가장 중요하다.
여기서 root 계정 설정과 DB 생성등이 가능하기 때문이다.
sudo docker run --name mysql -p 3306:3306 --network Lost_in_Frost -e MYSQL_ROOT_PASSWORD=비밀번호 MYSQL_DATABASE=lostinfrost -e MYSQL_USER=lostinfrost -e MYSQL_PASSWORD=c101 -d mysql:8..0.33
name : 컨테이너 이름p : 포트포워딩 (기본 port 3306, 변경 가능)network : 사용할 네트워크 설정MYSQL_ROOT_PASSWORD : root password 설정MYSQL_DATABASE : default DBMYSQL_USER, MYSQL_PASSWORD : 기본 유저 생성-d : 사용할 이미지 선택 (여러 버전중에 선택 가능)mysql 서버에 직접 접속해 작업을 해야하는 경우는
sudo docker exec -i -t "컨테이너 이름" /bin/bash를 통해 가능하다.
redis의 경우도 손쉽게 가능하다.
sudo docker pull redis
sudo docker images
redis 이미지를 받는다.
그리고 mysql과 비슷한 방식으로 실행시에 옵션을 지정하면 된다.
docker run -p 6379:6379 --network 네트워크이름 --name redis -d redis:latest --requirepass "비밀번호 지정"
p : 포트포워딩 (기본 포트 6379)network : 네트워크 지정requirepass : redis 비밀번호를 지정redis 서버 직접 접속해 작업을 해야하는 경우는
sudo docker exec -i -t "컨테이너이름" redis-cli -a "비밀번호"
비밀번호를 지정하지 않았다면, 접속해서
config set requirepass "비밀번호"를 통해 가능하다.
RabbitMQ도 쉽다.
sudo docker pull rabbitmq
sudo docker images
docker run -p 15672:15672 -p 5672:5672 -p 15671:15671 -p 5671:5671 -p 4369:4369 --network lost-in-frost -d --name rabbitmq -e RABBITMQ_DEFAULT_USER=유저명 -e RABBITMQ_DEFAULT_PASS=비밀번호 rabbitmq:management
RabbitMQ의 경우, 사용하는 포트가 많아서 해당 포트들을 다 열어줘야 한다.
RABBITMQ_DEFAULT_USER, RABBITMQ_DEFAULT_PASS : 기본 유저 설정