Github Action 으로 Spring 배포 (docker, ECR)

Kyojun Jin·2024년 11월 30일
0

Insta Tistory Automator

목록 보기
7/8

Spring (혹은 다른) 앱을 도커 이미지로 만들어 ECR 로 배포한다.

약 70번의 시도 끝에 성공.

Github Actions

알게 된 것.

1. job 들은 각각 환경이 다르다.

env와 현재 디렉토리마저 완전 초기화된다.

2. job 상단에 default working-directory는 run 에만 해당된다

다른 것들(use actions 등)에는 영향을 주지 않는다.

defaults:
  run:
    working-directory: blah/blah

3. actions/cache, actions/setup-java 등 post action 이 딸려서 마지막에 cache 를 해주는 것들은 전체 액션이 실패하면 동작하지 않는다.

즉 캐싱을 안 한다. 중간 단계에서 뭔가 실패하더라도 그 전에 그래들 빌드에 성공했더라면, 그리고 그 캐시를 사용하고 싶다면 앞뒤를 cache/restore, cache/save 액션으로 감싸야 한다. 그게 싫다면 빌드가 포함된 job 을 작게 유지한다.

4. docker compose 에서 pull 한 이미지는 캐싱할 수 없다.

적어도 범용적인 기능은 아닌 것 같다. 다만 docker build 시에 생성되는 데이터는 캐싱이 가능하다.

5. docker 공식문서에서 나오는 것들은 github actions 에서 사용하지 못할 수도 있다.

예를 들어 build secret 관련해서 build 시에 현재 환경변수를 secret 으로 넘기는 것이 있는데 이는 github의 ubuntu-latest 에서는 사용할 수 없다.
해당 기능에 대한 설명은 공식 문서를 참고한다. 안 되는 이슈 -> 해당 기능은 최신 버전인데 Github action의 ubuntu-latest 에서 사용하는 docker 는 아직 업데이트 안 된 것으로 추정.

6. Github actions 에서 job은 완전 새로운 shell 에서 실행된다.

코드를 불러오려면 actions/checkout 을 해야 한다. 일부만 불러오는 것도 된다. job 끼리 데이터를 주고 받으려면 actions/upload-artifact, actions/download-artifact 를 해야 한다. 코드 받아서 build 하고(코드 -> jar), 그걸 이미지로 만들어 Push 할 때(jar->이미지) jar 파일이 필요한데 이걸 전달할 때 사용했다.

7. GitHub actions 에서 toJson 의 결과값의 key에는 쌍따옴표가 없다.

아무리 docker 로 실행 환경을 똑같이 하더라도 뭔가 다른 게 있다. 로컬에서 alpine/java:21-jre 이미지에서 jq 를 사용하면 멀쩡한데 Github 에서는 뜬금 없이 jq: parse error: Invalid numeric literal at line 2, column 19 에러가 뜬다. line2 라는 건 첫번째 json 값이 문제라는 건데 내가 갖고 있는 그 값은 분명 숫자를 포함하긴 한다. 근데 로컬에서 그 이미지에서 일부러 그 값의 json 형식을 잘못 맞추면 column 값이 다르다. 뭔가 이상하다. 공식 문서는 pretty-print JSON 이라고 한다. 뭐지?;; 스택 오버플로우에 따르면 최소 2년 전부터 그랬다. 이 기현상에 대해서 아무도 모르고 있나? 아니면 toJson의 출력을 파일로 저장할 생각은 나밖에 안 했던 건가? jq 를 yq 로만 바꾸면 잘 돌아간다.

8. docker 에서 ECR 로 푸시를 하면 dangling image 까지 같이 올라간다.

docker 새 기능 중에 provenance 라고 docker build 시 메타데이터를 제공해주는 게 있는데 이걸 켜면 그렇다. build-push-action 같이 올인원 액션을 쓸 때는 이걸 꺼야 한다. (지금 보니까 이거 false 로 해도 올라가는 경우가 종종 있다. ECR lifecycle policy 에서 untagged image 를 지워주는 것을 설정한다.

9. OIDC 로그인을 했다고 해서 바로 ECR 로 푸시할 수 있는 건 아니다.

amazon-ecr-login 라는 액션이 또 필요하다. 이걸 안 하면 아무리 IAM 에 권한을 잘 넣어줘도 401 에러가 뜬다. IAM에 권한이 없다면 403이 뜰 것이다.

10. Dockerfile의 RUN 안에서 지정되는 환경변수는 휘발적이다.

ARG는 RUN 뿐 아니라 다른 모든 부분에서 쓸 수 없다. ARG에서 지정된 건 ENV 로 옮겨야 한다.

11. docker 의 container 를 내려도(stop) 데이터는 남아있다.

container 자체를 지워야 한다. db 가 휘발적이라고 계속 생각하다 보니 이 점을 간과했다. postgresql 은 docker compose 로 올려도 db 가 이미 있으면 POSTGRES_DB 로 무슨 값을 주든 DB 생성 안 한다.

 

비밀번호 관리

Github에 자체적으로 레포지터리마다 비밀번호나 환경변수를 관리해주고 있긴 하다. 근데 그것을 배포 시에 어떻게 써먹느냐가 문제다.

  1. 변수마다 secret에 저장한다.
  2. yaml이나 properties 파일 통째로 저장한 다음 실행 시 spring.config.import 한다.

2번은 각 변수에 대한 재사용성이 너무 떨어진다. Github Secret은 평문으로 저장이 안 되므로 뭐 하나 수정할 때마다 다른 값도 다 찾아서 써줘야 한다. 기각!

1번을 채택하면 다음 옵션이 남는다.

1. 각 변수를 docker build 시에 ENV 로 저장한다.

Docker 에서는 ENV나 ARG에 비밀 정보를 담지 않을 것을 권한다. Secret으로 전달해야 한다.

2. 각 변수를 application-secrets.properties 에 저장하고 import 한다.

파일이 남는다. 이거 지워줘야 하는데 그러면 spring 측에 파일 지우는 코드가 필요하다. 뭔가 좋은 방식은 아닌 듯함

3. 각 변수를 파일에 적어놨다가 image 실행 순간에 환경변수로 export 한다.

돈 안 드는 측면에선 이게 그나마 보안이 괜찮다. 환경변수 접근 제한은 아마존이 알아서 해줄 것이다.

4. Spring Cloud Config Server 를 구축한다.

공수가 너무 많이 든다. 그리고 이거 올려도 aws secret manager 값이랑 비슷할 거 같다.

5. jasypt library 를 적용한다.

deprecated 됐다. 요즘 spring boot (3.4~) 에선 돌아가질 않는다. 이거 하려고 몇 시간 버렸다. 회사에서 공식적으로 만드는 라이브러리 아니면 안 쓰는 게 좋다. 관리가 안 된다.

6. Java VM 변수로 전달한다.

파일에도 안 남고 환경변수도 아니라 출력도 못해서 좋을 줄 알았는데 프로세스 정보에서 볼 수 있단다.

7. Cloud 서비스를 이용한다. 예를 들어 AWS Secrets Manager 를 이용한다.

돈이 드는데 가장 쉬운 방법이다. 하나당 0.5달러 기본으로 들고, API 호출 10000건부터 과금한다는데 기업도 아니고 1만건을 채울 리는 없지만 내가 필요한 비밀 정보가 8개라 치면 4달러, 5500원이다.

0개의 댓글