Docker Secret + Spring Boot + MySQL (2)

형석이의 성장일기·2024년 2월 29일
0

Docker Secret

목록 보기
3/3
post-thumbnail

이젠 application.properties에 있는 DB 접근과 관련된 환경변수들을 Docker Secret 적용을 위해 변경합니다.

→ application.properties 에서 application.yml 로 변경
→ 주요 변수들을 ${docer-secret-.. } 로 변경

spring:
  datasource:
    url: ${docker-secret-mysql-url}
    username: ${docker-secret-mysql-user}
    password: ${docker-secret-mysql-user-pw}

docker-secret:
  bind-path: /run/secrets

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:mybatis/mapper/*.xml

management:
  endpoints:
    web:
      exposure:
        include: "*"

변경했으면 Git Repository에 Push 합니다.

그리고 EC2에 제가 방금 올린 Repository을 clone 합니다.

git clone https://github.com/gudtjr2949/docker-secret-with-springboot.git


밑에 보이는 캡쳐에 있는 gradle/ 와 같은 디렉토리 레벨에서

./gradlew clean build

명령어를 입력해 jar 파일을 생성합니다.

그리고 jar 파일을 실행하는 명령어를 Dockerfile에 넣으면 됩니다.

FROM openjdk:17-alpine

# 작업 디렉토리 설정
WORKDIR /app

# 호스트의 target 디렉토리에서 JAR 파일을 컨테이너의 /app 디렉토리로 복사
COPY /docker-secret-with-springboot/refactoring/build/libs/refactoring-0.0.1-SNAPSHOT.jar refactoring-0.0.1-SNAPSHOT.jar

CMD ["java", "-jar", "refactoring-0.0.1-SNAPSHOT.jar"]
spring:
  datasource:
    url: ${docker-secret-mysql-url}
    username: ${docker-secret-mysql-user}
    password: ${docker-secret-mysql-user-pw}

docker-secret:
  bind-path: /run/secrets

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:mybatis/mapper/*.xml

management:
  endpoints:
    web:
      exposure:
        include: "*"

참고로 전

docker-secret-with-springboot/refactoring/src.. 가 Spring Boot 프로젝트 디렉토리,
docker-secret-with-springboot와 같은 레벨에 Dockerfile과 docker-compose.yml 파일을 뒀습니다.


다음 Docker Secret에 사용할 환경변수를 등록해줍니다.

echo -n "jdbc:mysql://54.89.210.153:3306/common?useSSL=false&allowPublicKeyRetrieval=true"| sudo docker secret create mysql-url -

echo -n "testUser"| sudo docker secret create mysql-user -

echo -n "1234"| sudo docker secret create mysql-user-pw -

아래 명령어를 통해 Docker Secret 이 잘 생성되었는지 확인합니다.

sudo docker secret ls

그리고 아래 명령어를 통해 Service를 시작할려고 했는데!

sudo docker stack deploy -c docker-compose.yml refacotring

Caused by: java.lang.IllegalArgumentException: URL must start with 'jdbc'…

에러가 뜹니다..

딱 봐도 Docker Secret 적용이 안된 것 같은데;;


며칠동안 여러 블로그와 ChatGPT에서 찾아본 결과..!

Spring Boot에서 application.yml이나 application.properties에서 Docker Secret 의 변수들을 사용하기 위해선 Java 코드로 후처리를 거쳐야 하더군요..

그래서 걍 Docker Secret 후처리기 클래스 하나 추가했슴다..

GitHub - kwonghung-YIP/spring-boot-docker-secret: "spring.datasource.password" is no more a plain text password store in application.yml

이 분 코드 참고하세요!! 만약 Kwong-Hung YIP 이 분이 없었더라면 전 완성하지 못했을 꺼에요..


코드를 설명해드리자면,

public class DockerSecretProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        String bindPathPpty = environment.getProperty("docker-secret.bind-path");

        if (bindPathPpty!=null) {
            Path bindPath = Paths.get(bindPathPpty);
            if (Files.isDirectory(bindPath)) {
                Map<String,Object> dockerSecrets;
                try {
                    dockerSecrets =
                            Files.list(bindPath)
                                    .collect(
                                            Collectors.toMap(
                                                    path -> {
                                                        File secretFile = path.toFile();
                                                        return "docker-secret-" + secretFile.getName();
                                                    },
                                                    path -> {
                                                        File secretFile = path.toFile();
                                                        try {
                                                            byte[] content = FileCopyUtils.copyToByteArray(secretFile);
                                                            return new String(content);
                                                        } catch (IOException e) {
                                                            throw new RuntimeException(e);
                                                        }
                                                    }
                                            ));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                MapPropertySource pptySource = new MapPropertySource("docker-secrets", dockerSecrets);
                environment.getPropertySources().addLast(pptySource);
            }
        }
    }
}
  1. docker-secret.bind-path 프로퍼티에서 Docker 시크릿이 저장된 경로를 가져옵니다.
  2. 시크릿이 저장된 경로가 설정되어 있으면 해당 경로의 파일을 읽어와 Docker 시크릿을 추출합니다.
  3. Docker 시크릿은 파일 이름을 기반으로 docker-secret-<filename>의 형식으로 맵에 저장됩니다.
  4. 추출된 Docker 시크릿은 MapPropertySource를 사용하여 환경 속성에 추가됩니다.
  5. 최종적으로 환경에 Docker 시크릿이 추가되어 애플리케이션에서 사용할 수 있게 됩니다.

그리고 이 후처리기를 사용하기 위해 resource/META-INF/spring-factoris 에

org.springframework.boot.env.EnvironmentPostProcessor=common.refactoring.DockerSecretProcessor

이 코드 한 출 추가해줘야 합니다.

전 후처리기가

여기 있어서

common.refactoring.DockerSecretProcessor

이렇게 입력했습니다.


이후 다시 Spring Boot 빌드하고, Docker 이미지 생성하고, service 실행 명령어 날리면?

sudo docker stack deploy -c docker-compose.yml refacotring

잘 실행됩니다!!

접속도 잘 됩니다.

Postman으로 요청 날려보면

DB 와 연결이 되서 정상적으로 잘 날아갑니다!

이렇게 Docker Secret으로 주요 비밀번호와 같은 환경변수를 설정하고 느낀점은

  1. 확실히 컨테이너 내부에서 보관되기 때문에 접근하기 어렵고, 평문으로 Dockerfile이나 docker-compose 에 명시하지 않기 때문에 안전하다!
  2. 만약 여러개의 서버를 운영중이고, 만약 환경변수 변경이 필요하면 일일이 변경할 필요 없이 Docker Secret만 수정하면 됨! (여러 컨테이너에 동시에 제공할 수 있음을 의미함)

또한 다음에 알아봐야 하는건 저 DockerSecretProcessor 가 안전한 방법인지 알아봐야겠습니다.

profile
이사중 .. -> https://gudtjr2949.tistory.com/

0개의 댓글