그동안 만들었던 Microserivce 나 Microserivce 를 지원하는 시스템들 중에서 어떤 것들이 배포 대상이 되는지, 배포 방법을 알아보고 직접 배포까지 해보자.
애플리케이션 배포 구성
배포 방법
1. Docker + Local
2. JAR file + Local
3. IntelliJ IDEA + Local
4. Docker + AWS EC2
5. Docker + Docker Swarm Mode + AWS EC2 (요즘은 많이 사용하지 않음)
6. Docker + Kubernetes + AWS EC2 (거의 표준으로 사용)
원래 기본적으로 Local 에서 하는 방법은 3번의 방식이고 이번에 해볼 방식은 1번의 방식이다.
Create Bridge Network
- Bridge network
-> 아무런 설정 없이 사용할 수 있는 것으로 호스트 PC 와는 별도의 가상의 네트워크를 만들고 그 가상의 네트워크에서 우리가 만든 컨테이너를 배치하고 사용하는 방식
- Host network
-> 우리가 사용하는 호스트 네트워크와 게스트 네트워크와 같이 사용
- None network
-> 네트워크를 사용하지 않는다.
-> io 네트워크만 사용 , 외부와 단절
docker entwork create ecommerce-network
docker network ls
- 해당 명령어를 입력하면 bridge network 로 만들어진 것을 확인할 수 있다.
- 위에서 설명한 bridge , host , none 3개는 기본적으로 만들어져있다.
docker system prune 는 중지된 컨테이너 , 사용되지 않는 네트워크 , 불필요한 리소스를 삭제하는 명령어
docker network create --gateway 172.18.0.1 --subnet 172.18.0.0/16 ecommerce-network
- gateway 와 subnet 을 지정할 수 있다. (가급적이면 지정하는 것이 좋다)
docker network inspect ecommerce-network 는 해당 네트워크 상세 정보를 확인할 수 있는 명령어이다.
컨테이너를 사용하기 위한 네트워크를 만들어서 사용하면 좋은점
- 일반적인 컨테이너는 하나의 게스트 OS 로 고유한 IP 주소를 가진다.
- 또한 도커에서는 컨테이너를 배포할 때 IP 주소를 순ㄴ차적으로 할당을 한다. (비어있는 IP 주소부터)
- 유레카 서버가 172.17.0.2 라고 하고, user-service 가 172.17.0.3 이라고 할 때 만약 유레카 서버의 IP 주소가 달라지게 된다면 유레카 서버와 통신하는 user-service 는 연결을 하지 못한다.
- 하지만 같은 네트워크를 사용하면 IP 주소가 아닌 Container id or Container name 으로 호출할 수 있다.
즉 Container 를 만들 때 --name 옵션을 사용해서 name 을 할당한다면 IP 주소가 달라진다고 하더라도 우리가 필요했던 특정한 서버에 접속하기 위해 name 만으로도 가능하다.
- 즉 IP 주소가 달라지더라도 계속 똑같은 name 으로 접속하면 된다.
- 이러한 이유로 컨테이너를 사용하기 위한 별도의 네트워크를 만들어야 하는 이유이다.
가상의 네트워크를 만들게 되면 Docker Server 가 만들어지고 여기 안에 Container 를 넣고 컨테이너끼리 통신할 때는 name 을 통해서도 할 수 있다.
RabbitMQ
Run RabbitMQ
docker run -d --name rabbitmq --network ecommerce-network -p 15672:15672 -p 5672:5672 -p 15671:15671 -p 5671:5671 -p 4369:436
9 -e RABBITMQ_DEFAULT_USER=guest -e RABBITMQ_DEFAULT_PASS=guest rabbitmq:management
- 위에 명령어를 실행하면 RabbitmQ 가 실행된다.
- -d 는 백그라운드 옵션
- --name 은 컨테이너 구별 및 네트워크에서도 구별하기 위해서 사용
- --network ecommerce-network 위에서 만들었던 네트워크를 설정
- 만약 설정하지 않으면 도커가 가지고 있었던 bridge 네트워크를 사용한다.
- 이래도 상관 없긴 하지만 현재 ecommerce-network 을 만들었기 때문에 해당하는 네트워크가 아닌 다른 네트워크에 섞여서 저장될 수 있다.
- 그렇게 되면 통신할 수 있는 방법이 없어지기 떄문에 우리가 생성했던 네트워크를 지정해서 컨테이너를 생성하는게 좋다.
-p 는 컨테이너의 포트들
사용하고 있는 컨테이너 포트와 실제 host pc 에서 사용하는 포트를 포워딩
-e 환경 변수로써 user , pw 를 넘겨준다.
rabbitmq:management 가 호출하려는 이미지 이름이라고 생각하면 된다.
- docker network inspect ecommerce-network 명령어로 네트워크 상세 정보를 보면 rabbitmq 라는 컨테이너가 들어있는 것을 확인할 수 있다.
- docker ps -a 로 컨테이너를 보면 현재 기동중이며 PORTS 도 알 수 있다.
따로 서버를 기동하지 않았는데도 15672 port 로 접속하면 들어가지는 이유
- run 명령어에 Host 와 컨테이너를 포워딩을 했기 때문에 15672 port 로 접속하면 이와 포워딩된 rabbitmq 컨테이너로 들어가서 접속이 되는 것이다.
Configuration Service
Config Server 를 도커 이미지화 시키기 위해서는 이미지 파일을 만들어야 한다.
해야할 것
- Dockerfile 만들고 명령어 넣기
-> - config-server 에서는 암호화 하는 것도 했었는데 이때 필요한 .jks 파일까지 프로젝트 안에 넣어줘야 암호를 풀 수 있다.
-> 그래서 COPY 명령어로 .jks 파일도 복사한다.
-> bootstrap.yml 에 있는 .jks 파일 경로를 적는 부분도file:/apiEncryptionKey.jks 로 바꿔주면 된다.
- pom.xml 에서 version 1.0 으로 바꾸기
- 그런후 mvn clean compile 로 jar 파일을 생성한다.
- build -> push -> run
DockerFile
FROM openjdk:21-ea-11-slim
VOLUME /tmp
COPY apiEncryptionKey.jks apiEncryptionKey.jks
COPY target/config-service-1.0.jar ConfigServer.jar
ENTRYPOINT ["java" , "-jar" , "ConfigServer.jar"]
- FROM jdk21 버전으로 부터 도커 이미지를 만듦
- COPY jks 를 루트 디렉토리에 복사
- docker build --t supportkim/config-service:1.0 .
-> 마지막 . 은 현재 디렉토리에 도커 파일을 실행하라는 것
application.yml
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
encrypt:
key-store:
location: file:/apiEncryptionKey.jks
- config-server.application.yml 파일에 Rabbitmq 에 대한 정보가 들어있다.
- 근데 host 정보가 local 되어 있는데 이렇게 되면 오류가 발생한다.
- 현재 같은 네트워크 안에 있지만 서로 다른 IP 주소를 가지기 때문에 host 에 rabbitmq 가 가지는 IP 를 넣어줘야 한다.
- 확인하는 방법은 docker network inspect ecommerce-network 명령어를 실행하면 containers 에 rabbbitmq 가 있고 거기에 IP Address 로 확인하면 된다.
- 하지만 이런 방법은 rabbitmq 의 IP 주소가 변경되는 경우가 있을 수 있기 때문에 좋지 않은 방법이다.
- 마지막에 location 을 local 위치가 아닌 프로젝트 안으로 변경해주면 된다.
docker run -d -p 8888:8888 --network ecommerce-network -e "spring.rabbitmq.host=rabbitmq"
-e "spring.profiles.active=default" --name config-service supportkim/config-service:1.0
- 그래서 -e 옵션을 사용해서 host 에 name 으로 접근할 수 있도록 넣어준다.
- -e 옵션에 profile 정보도 넘길 수 있다.
- 이름을 config-service 로 지정
- 정상적으로 기동되는 것을 확인할 수 있다.
- docker network inspect ecommerce-network 를 확인하면 config-service 도 연결된 것을 확인
spring:
cloud:
config:
server:
git:
uri: https://github.com/supportlaver/spring-cloud-config.git
- 또 배포를 하기 때문에 config 파일들을 git-repo-local 와 같은 로컬 저장소가 아닌 git 에 올려서 사용해야 한다.
- 그래서 uri: 에 파일의 경로가 아닌 git 주소를 넣어주면 된다.
mvn build(mvn compile) -> docker build(다시 쟁성 , 덮어쓰기) -> docker images
docker images 의 결과를 보면 지금 바로 만든 것도 있지만 none , none 으로 되어 있는 것도 존재한다.
- 같은 이미지로 중복해서 만들게 되면 가장 마지막에 만들어진게 사용되는것 이니까 이전에 만든것은 사용되지 않는 것으로 간주하고 none 으로 되는 것이다.
- 그래서 없애주기 위해서 이미 실행된 컨테이너를 먼저 중지 시킨다. (docker stop)
- docker system prune 명령어로 불필요한 리소스들 , 사용되지 않는 컨테이너 삭제
- 다시 docker images 를 보면 none 이 없어진 것을 확인
다시 위에 있는 docker run 명령어를 실행
- 실행 후 network 에 잘 들어있는지 확인 (docker network inspect)
현재 2개의 Container 가 생성되고 실행중이며, 계속 만들었던 서비스를 Container 로 만들어서 실행해보자.
Discovery Service
위에서 한 것처럼 똑같이 DockerFile 을 만들고 이미지화 시켜서 컨테이너를 만들 수 있도록 해보자.
spring:
application:
name: discoveryservice
cloud:
config:
uri: http://127.0.0.1:8888
name: ecommerce
- application.yml 에서 위와 같이 수정하면 config 를 할 때 config-service 에서 ecommerce.yml 파일을 참조할 수 있도록 한다.
- 8888 port 가 config-serivce
- 하지만 위에서 설명했듯이 127.0.0.1 은 local 환경이기 때문에 이렇게 설정하면 안 되고 config-service:8888 와 같이 name 으로 접근해야 한다.
- 해당 파라미터는 docker run 할 떄 -e 옵션과 같이 넘겨주면 된다.
순서
- pom.xml 에서 version 만 변경
- DokerFile 생성
- build
- run
- docker logs NAME
Container 만들기
- mvn clean compile package
- docker build --tag supportkim/discovery-service:1.0 .
-> DockerHub 에 올리고 싶다면 docker push NAME:TAG 로 올리면 된다.
-> 올린 후 DockerHub 사이트에 들어가서 로그인을 하면 정상적으로 올라가있다.
-> 나중에 다른쪽에 있는 배포 툴이나 환경에서도 지금 만든 이미지들을 바로 사용할 수 있다.
- docker run
- docker run -d -p 8761:8761 --network ecommerce-network -e "spring.cloud.config=http://config-service:8888" --name discovery-service supportkim/discovery-service
- docker ps -a
- 확인하면 총 3개 (rabbitmq , config-service , discovery-service) 의 컨테이너가 기동
- docker network inspect ecommerce-network
- 여기서도 3개의 컨테이너가 해당 네트워크에 있는 것을 확인
Apigateway Service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka
- 해당 localhost 가 아닌 같은 네트워크에서 name 으로 접근할 수 있도록 바꿔줘야한다.
DockerFile
FROM openjdk:21-ea-11-slim
VOLUME /tmp
COPY target/apigateway-service-1.0.jar ApigatewayService.jar
ENTRYPOINT ["java" , "-jar" , "ApigatewayService.jar"]
mvn clean compile -> docker build -> docker push -> docker run
docker run
docker run -d -p 8000:8000 --network ecommerce-network
-e "spring.cloud.config.uri=http://config-service:8888"
-e "spring.rabbitmq.host=rabbitmq"
-e "eureka.client.service-url.defaultZone=http://discovery-service:8761/eureka/"
--name apigateway-service supportkim/apigateway-service:1.0
- application.yml 에 들어있는 로컬 환경에 정보를 containerName 으로 접근할 수 있도록 바꿔준다.
- 해당 apigateway 는 유레카 서버에 등록하기 위해서 eureka.client.service-rul.defaultZone 도 지정을 하기 위해서 discovery-service 라는 Name 을 넣어줬다.
- 마지막에 supportkim/... 은 여기서 컨테이너를 해당 docker image 를 가지고 와서 만들겠다.
MariaDB (MySQL)
DockerFile
FROM mariadb
ENV MYSQL_ROOT_PASSWORD password
ENV MYSQL_DATABASE mydb
COPY ./mysql_data/data /var/lib/mysql
EXPOSE 3306
ENTRYPOINT ["mysqld"]
- ROOT 라는 아이디에 PASSWORD 는 test1357
- ./mysql 이라는 폴더 전체를 컨테이너의 /var/lib/mysql 에다가 복사하도록 한다.
- 그래서 mydb 가 있는 mysql 폴더를 일단 아무 폴더를 만들어서 복사한다.
-> cp -R /mysql경로/... ./복사할경로/...
DockerFile 있는 곳에서 docker build -> docker run
docker build -t supportkim/my-mysqldb:1.0 .
docker run -d -p 3306:3306 --network ecommerce-network --name mariadb supportkim/my-mysqldb:1.0
docker exec -it mariadb /bin/bash 로 container 접속
- 도커 컨테이너 내부로 들어갈 수 있고 여기에서 mysql -hlocalhost -uroot -p 로 접속하고 password 를 입력하면 DB 에 들어갈 수 있다.
docker ps -a 명령어로 정상적으로 컨테이너들이 만들어진 것을 확인
Kafka
Zookeper + Kafka Server(Broker) 를 기동해야한다.
docker-compose 로 실행
- 우리가 실행하려고 하는 도커 컨테이너를 하나의 스크립트 파일로 실행할 수 있도록 만들어준다.
- 여러가지 설정 파일들이 한 곳에 모여있고 명령어 하나로 실행 시켜야할 도커 컨테이너를 한꺼번에 실행한다.
order-service 와 같은 곳에서 kafka 를 사용할 때 ProducerFacotry 에서 kafka 서버에 정보를 미리 넣어주면서 초기화를 시켰다.
- 이때 127.0.0.1 로 넘겨줬지만 배포를 하면 local 이 아닌 컨테이너 이름으로 서로 호출을 해야하기 때문에 이 부분을 수정해야 한다.
- 따라서 우리가 기동하려는 zookeeper , kafka server 의 IP 주소를 직접 지정해야한다.
- 이러한 파일들을 docker-compose 파일로 만든다.
- 직접 작성하기 보다는 zookeeper + kafka 를 같이 사용할 수 있는 docker-compose 파일을 제공하는 곳에서 가지고 오면 된다.
- git clone https://github.com/wurstmeister/kafka-docker.git
docker-compose-single-broker.yml
version: '2'
services:
zookeeper:
image: wurstmeister/zookeeper
ports:
- "2181:2181"
networks:
my-network:
ipv4_address: 172.18.0.100
kafka:
image: wurstmeister/kafka
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_HOST_NAME: 172.18.0.101
KAFKA_CREATE_TOPICS: "test:1:1"
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- zookeeper
networks:
my-network:
ipv4_address: 172.18.0.101
networks:
my-network:
external: true
name: ecommerce-network
- zookeeper 는 100번을 사용하고 kafka 는 101 번을 사용하도록 했다.
- 직접 이렇게 지정을 해줘야 order-service 같은 곳에서 kafka 를 사용할 때 IP 주소를 넣을 수 있다.
- networks 지정한 my-network 는 우리가 지금 계속 사용하고 있는 ecommerce-network 를 사용하도록 지정해주면 된다.
- 저장한 후 docker-compose -f docker-compose-single-broker.yml up -d
-> 현재 docker-compose 파일로 실행하기 때문에 명령어가 달라지고 up 명령어로 실행할 수 있고 down 으로 종료할 수 있다.
참고자료