본문 바로가기

자바 심화/TIL

Docker - 기본 사용 및 Cl / CD

개요

Docker를 통한 CI/CD 방법 중 GitLab를 통한 CI/CD 방법에 대해 배워본다.

 

CI/CD 개념

 

CI / CD

개요팀 프로젝트를 하면서 애플리케이션 자동 배포 및 여러 이점으로 CI/CD를 도입해 사용했고 편리했던 경험이 있었다.CI/CD를 직접 설정해보지 않았기 때문에 CI/CD에 대해 공부하고 프로젝트에

eleunadeu.tistory.com

 

GitHub Actions를 통한 CI/CD

 

CI/CD 2

개요GiHub Actions에 대해 학습하고, GitHub Actions를 사용한 CI/CD 과정과 프로젝트에서 사용한 Docker 설정 파일에 대해 분석을 해 볼 것이다. GitHub Actions 깃허브 액션은 깃허브에서 제공하는 CI/CD 플랫

eleunadeu.tistory.com

 

Docker?

애플리케이션을 컨테이너라는 단위로 패키징, 배포, 실행할 수 있도록 도와주는 오픈 소스 플랫폼.

컨테이너는 애플리케이션과 필요한 모든 종속성(라이브러리, 설정 파일 등)을 하나의 독립된 환경에 담아 실행할 수 있게 하며, 이를 통해 어디서든 동일한 환경에서 애플리케이션을 실행할 수 있다.

 

주요 특징

 

  1. 경량 컨테이너: 가상 머신(VM)보다 가벼운 환경으로, 호스트 OS의 커널을 공유해 더 빠르고 효율적으로 실행된다.
  2. 이식성: 개발 환경과 운영 환경의 불일치 문제를 줄여, 동일한 이미지를 여러 플랫폼에서 실행 가능.
  3. 자동화된 빌드: Dockerfile을 사용해 이미지를 코드로 정의하고 버전 관리 가능.
  4. 확장성: 여러 컨테이너를 오케스트레이션 도구(Kubernetes, Docker Swarm 등)와 함께 사용하여 대규모 애플리케이션 배포 가능.

 

주요 용도

 

  • 개발 환경 표준화
  • CI/CD 파이프라인 구축
  • 마이크로서비스 아키텍처 구현
  • 애플리케이션 배포 및 관리 간소화

 

Docker 사용 실습

Docker의 기능을 활용해 2개의 애플리케이션을 컨테이너로 만들어 컨테이너의 컨트롤러로 다른 컨테이너를 호출할 수 있도록 한다.

 

1. Project 생성

  1. SpringInitializr를 사용해 web, lombok, open feign를 추가한 프로젝트를 2개 생성한다.
  2. 생성한 프로젝트를 실습에 사용할 폴더에 넣고 인텔리제이를 통해 프로젝트를 연다.

 

2. Project 코드 생성

- Application.java

@EnableFeignClients
@SpringBootApplication
public class AApplication {

    public static void main(String[] args) {
       SpringApplication.run(AApplication.class, args);
    }

}
  • 각 Application 코드에 @EnableFeignClients 어노테이션을 추가한다.

- BController.java

@RestController
public class BController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}
  • /hello 엔드포인트를 호출하면 "hello"를 반환하도록 한다.

- AController.java

@RestController
@RequiredArgsConstructor
public class AController {

    private final BServiceClient bServiceClient;

    @GetMapping("/hi")
    public String hi() {
        String hello = bServiceClient.hello();
        return "service-a : hi ####### service-b: " + hello;
    }
}
  • OpenFeign을 사용해 /hi 엔드포인트를 호출하면 B 애플리케이션의 /hello도 호출하도록 한다.

- BServiceClient.java

@FeignClient(name = "service-b", url = "${service.b.url}")
public interface BServiceClient {

    @GetMapping("/hello")
    String hello();
}
  • Service-b와 통신하기 위해 FeignClient 구현(서비스 A에서 서비스 B의 기능을 호출하기 위함)

- service b application.yml

spring:
  application:
    name: b

server:
  port: 8080
  • 애플리케이션 이름과 사용할 포트만 지정한다.

- service a application.yml

spring:
  application:
    name: a

service:
  b:
    url: http://service-b:8080

server:
  port: 8080
  • 추가적으로 서비스 b의 URL을 등록한다.

참고

Docker 컨테이너를 통해 서비스 간 호출을 할 때 서비스의 yml 파일에서 port 번호가 동일해도 괜찮은 이유?
- 컨테이너 간 네트워크 격리와 호스트-컨테이너 포트 매칭 구조

 1. 컨테이너 간 네트워크 격리:
	- Docker는 각 컨테이너를 독립된 네트워크 네임스페이스에서 실행.
	- 각 컨테이너는 자신의 내부 네트워크에서 localhost:8080으로 서비스를 실행하지만, 다른 컨테이너와 충돌하지 않는다.
	- 두 컨테이너는 독립된 네트워크 공간에서 실행되므로 문제되지 않음.
 2. 컨테이너 외부와의 포트 매핑:
	- Docker는 컨테이너 내부 포트(8080)를 외부 호스트에 노출할 때 다른 포트로 매핑할 수 있음.
 3. 컨테이너 간 이름 기반 통신:
	- Docker Compose나 Docker 네트워크를 사용하면, 서비스 간 통신에서 컨테이너 이름을 사용해 서로 접근한다.

 

 

3. 각 서비스 코드 이미지 등록

  • Window 환경의 경우 Docker Desktop을 실행해야 Docker 관련 명령어를 인텔리제이 터미널에서 사용 가능했다.

- Dockerfile 생성

FROM openjdk:17-jdk-slim

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]
  • 각 프로젝트 루트 폴더에 Dockerfile 코드를 추가해 Docker 이미지로 만들 수 있도록 준비한다.

- Docker 네트워크 생성

docker network create my-network
  • 인텔리제이 터미널에 해당 명령어를 입력해 도커 네트워크를 생성한다.
    • Window에서 실습을 진행했기 때문에 편의성을 위해 인텔리제이의 터미널을 사용했다.

- 프로젝트 빌드 및 이미지 생성

-- 위치 이동
cd com.service.b
-- 빌드 명령어
./gradlew clean bootJar
-- 이미지 생성
docker build -t img-service-b .
-- 마지막의 . 은 도커파일의 위치
  • 빌드할 프로젝트의 루트 폴더로 진행 후 프로젝트를 빌드 후 명령어를 통해 이미지를 생성했다.

- 컨테이너 생성

docker run -d --name service-b --network my-network -p 18081:8080 img-service-b
  • 위 명령어를 통해 생성한 이미지를 사용해 컨테이너를 생성했다.
    • 컨테이너 이름, 네트워크, 포트를 설정

- 컨테이너 확인 및 접속

docker ps
  • 컨테이너가 정상적으로 생성되었으면, 주소로 접속해 service-b가 호출되는지 확인

다른 서비스에 대해서도 위와 같은 과정을 반복한 후 주소로 접속해 다른 서비스를 호출할 수 있는지 확인

 

Docker Compose 활용

- 기존 컨테이너 삭제

docker stop 서비스_A_컨테이너_아이디
docker rm 서비스_A_컨테이너_아이디
  • 명령어를 통해 컨테이너를 정지한 후 삭제한다.
    • 아이디 앞 2개만 입력해도 명령이 정상 동작한다.
    • docker rm -f 를 사용하면 실행 중인 컨테이너도 삭제가 가능하다.

- docker-compose.yml 파일 생성

  • 2개의 프로젝트의 상위 폴더에서 생성한다.
version: '3.8'

services:
  service-a:
    image: img-service-a
    ports:
      - "18080:8080"
    environment:
      - SERVICE_B_URL=http://service-b:8080
    depends_on:
      - service-b

  service-b:
    image: img-service-b
    ports:
      - "18081:8080"

networks:
  default:
    driver: bridge
  • docker compose를 통해 사용할 서비스와 네트워크를 정의한다.

- Docker Compose 실행 및 확인

docker compose up -d
docker compose ps
  • 정상적으로 생성된 것을 확인한 후 주소로 접속해서 서비스를 호출한다.

 

Docker 네트워크, 컨테이너, 이미지 삭제

실습 후 필요 없어진 컨테이너, 네트워크, 이미지를 삭제한다.

- 컨테이너 목록 확인
docker ps
- 삭제할 컨테이너 중지
docker stop <container_name_or_id>
- 컨테이너 삭제
docker rm <container_name_or_id>
- 모든 중지된 컨테이너 삭제
docker container prune

- 전체 네트워크 확인
docker network ls
- 특정 네트워크 삭제
docker network rm <network_name_or_id>
- 모든 사용자 네트워크 삭제(미 사용)
docker network prune

- 이미지 확인
docker images
- 특정 이미지 삭제
docker rmi <image_name_or_id>
- 모든 이미지 삭제
docker rmi $(docker images -q)

 

 

GitLab을 통한 CI/CD

  • GitLab을 사용해 CI/CD 환경을 구축하고 CI 과정을 자동화.
    1. GitLab 프로젝트를 만들고 개발한 코드를 Push
    2. GitLab이 Push 된 코드를 Docker Image로 생성 ECR에 등록
    3. Docker Image를 통해 Docker container를 ECS에 실행

1. GitLab에 빈 프로젝트 생성

 

 

2. Spring 프로젝트 생성 및 GitLab 연동

Spring 프로젝트를 생성(인텔리제이 or initializr 사용) 후 인텔리제이를 통해 GitLab과 연동한다.

  • GitLab 프로젝트의 Code Clone with HTTPS 복사

인텔리제이 상단 메뉴 VCS   Enable Version Control Integration 선택

 

상단 메뉴 Git → Manage Remotes 선택

 

Git Remotes 메뉴에 복사한 URL 붙여넣고 OK 선택

GitLab 연동 완료 후 Git 메뉴에서 Pull을 눌러 프로젝트를 풀한다.

 

3. 프로젝트 코드 생성

@RestController
public class SampleController {

    @GetMapping("/sample")
    public String sample() {
        return "Hello World";
    }
}
spring.application.name=sample

server.port=8080
FROM openjdk:17-jdk-slim

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]
  • 테스트를 위한 간단한 파일만 작성한다.

 

AWS 설정

1. 보안 그룹 추가

  • 보안 그룹 생성 후 인바운드 규칙을 위와 같이 편집한다.

2. ECR 리포지토리 생성

 

3. ECS 클러스터 생성

  • 서버리스 선택 후 생성

4. 태스크 정의 메뉴에서 새 태스크 정의 생성

 

5. 컨테이너 정보 입력

  • 이미지 URL에 ECR 리포지토리 URL 입력

6. 생성된 클러스터에서 서비스 생성

 

7. 로드 밸런싱 설정

 

8. AWS IAM에서 사용자 생성

  • 사용자 상세페이지에서 엑세스 키를 만들고 기타 선택 생성된 액세스 키와 비밀 액세스 키 저장(메모장 등에)

9. 깃랩 CI 파일 작성

  • Settings > CI/CD > Variables에서 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 추가
  • Key 값은 8번 과정에서 저장한 값을 입력

10. 프로젝트 루트 폴더에 .gitlab-ci.yml 생성

services:
  - docker:stable-dind
stages:
  - build jar
  - build and push docker image
  - deploy
variables:
  APPLICATION_NAME: "01"
  TAG_NAME: "latest"
  DOCKER_IMAGE: "group-18934028/project-01"
build:
  image: openjdk:17-jdk-slim
  stage: build jar
  script:
    - chmod +x gradlew
    - ./gradlew clean build
  artifacts:
    paths:
      - build/libs/*.jar
docker build:
  image: docker:latest
  stage: build and push docker image
  rules:
    - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_REF_NAME == "main"
      variables:
        TAG_NAME: "latest"
    - if: $CI_COMMIT_BRANCH == "develop" || $CI_COMMIT_REF_NAME == "develop"
      variables:
        TAG_NAME: "develop"

  script:
    - apk add --update --no-cache curl py3-pip py3-virtualenv
    - python3 -m venv /tmp/venv
    - source /tmp/venv/bin/activate
    - pip install awscli
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set region ap-northeast-2
    - docker build -t $DOCKER_IMAGE .
    - docker tag $DOCKER_IMAGE:latest 730335597998.dkr.ecr.ap-northeast-2.amazonaws.com/$DOCKER_IMAGE:$TAG_NAME
    - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 730335597998.dkr.ecr.ap-northeast-2.amazonaws.com
    - docker push 730335597998.dkr.ecr.ap-northeast-2.amazonaws.com/$DOCKER_IMAGE:$TAG_NAME

deploy:
  image: python:3.9-slim
  stage: deploy
  script:
    - python3 -m venv /tmp/venv
    - source /tmp/venv/bin/activate
    - pip install awscli
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set region ap-northeast-2
    - aws ecs update-service --cluster project-cluster --service project-01-service --task-definition project-01-task:1 --force-new-deployment

 

11. 프로젝트 Commit, Push 후 파이프라인 확인

 

12.  확인

ECS 클러스터 상세 페이지의 로드 밸런서의 DNS 이름 복사 후 웹에 입력하고 엔드포인트에 접근해 결과 확인

 

 

정리

  • Docker를 통한 프로젝트 컨테이너 관리 방법과 GitLab을 통한 CI/CD 방법을 배우게 되었다.

'자바 심화 > TIL' 카테고리의 다른 글

Redis - Cache  (0) 2024.12.02
Redis - Redis Template  (0) 2024.11.29
MSA - 기초 5  (0) 2024.11.27
MSA - 기초 4  (0) 2024.11.26
MSA - 기초 3  (0) 2024.11.25