Spring Cloud + MSA 애플리케이션 개발 15(애플리케이션 배포 1 - Docker Container)

지원·2024년 3월 5일
0

MSA개발

목록 보기
14/15

그동안 만들었던 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
# bootstrap.yml
encrypt:
  key-store:
#    location: file://${user.home}/Desktop/keystore/apiEncryptionKey.jks
    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 옵션과 같이 넘겨주면 된다.

순서

  1. pom.xml 에서 version 만 변경
  2. DokerFile 생성
  3. build
  4. run
  5. docker logs NAME
  • 로그 확인

Container 만들기

  1. mvn clean compile package
  2. docker build --tag supportkim/discovery-service:1.0 .
    -> DockerHub 에 올리고 싶다면 docker push NAME:TAG 로 올리면 된다.
    -> 올린 후 DockerHub 사이트에 들어가서 로그인을 하면 정상적으로 올라가있다.
    -> 나중에 다른쪽에 있는 배포 툴이나 환경에서도 지금 만든 이미지들을 바로 사용할 수 있다.
  3. 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
  1. docker ps -a
  • 확인하면 총 3개 (rabbitmq , config-service , discovery-service) 의 컨테이너가 기동
  1. 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:
    # build: .
    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 # 172.18.0.1 ~
  • 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 으로 종료할 수 있다.

참고자료

profile
덕업일치

0개의 댓글