😀
fistkim TECH BLOG
  • Intro
  • 강의
    • Reactive Programming in Modern Java using Project Reactor
      • Reactor execution model 1
      • Reactor execution model 2
      • Reactor execution model 3 - parallelism
      • Reactor execution model 4 - overview
      • Transform
      • Combine
      • Side Effect Methods
      • Exception/Error handling
      • retry, retryWhen, repeat
      • BackPressure
      • Cold & Hot Streams
    • NEXTSTEP 클린코드 with java 9기
      • 정리노트
    • NEXTSTEP DDD 세레나데 2기
      • CH01 도메인 주도 설계 이해
      • CH02 크게 소리 내어 모델링 하기
      • CH03 도메인 주도 설계 기본 요소
      • CH04 도메인 주도 설계 아키텍처
      • CH05 도메인 이벤트
    • NEXTSTEP 인프라 공방 1기
      • 망 분리하기
      • 통신 확인하기
      • 도커 컨테이너 이해하기
      • [미션 1] 서비스 구성하기 실습
      • [미션 2] 서비스 배포하기 실습
      • 서버 진단하기
      • 어플리케이션 진단하기
      • [미션 3] 서비스 운영하기
      • 웹 성능 진단하기
      • 부하 테스트
      • k6
      • [미션 4] 성능 테스트
      • 리버스 프록시 개선하기
      • 캐싱 활용하기
      • [미션 5] 화면 응답 개선하기
      • Redis Annotation 및 설정
      • 인덱스 이해하기 & DB 튜닝
      • [미션 6-1] 조회 성능 개선하기
      • [미션 6-2] DB 이중화 적용
    • NEXTSTEP 만들면서 배우는 Spring 3기
      • CH01 올바른 방향 바라보기
      • CH02 HTTP 이해 - 웹 서버 구현
        • HTTP 파싱
        • HTTP 웹 서버 구현
      • CH03 MVC - @MVC 프레임워크 구현
        • Servlet 다시 짚기
        • Cookie, Session 다시 짚기
        • MVC 프레임워크 구현
      • CH04 나만의 라이브러리 구현
      • CH05 DI - DI 프레임워크 구현
      • CH06 Aspect OP
    • 스프링 시큐리티
      • 스프링 시큐리티 아키텍처
      • WebAsyncManagerIntegrationFilter
      • SecurityContextPersistenceFilter
      • HeaderWriterFilter
      • CsrfFilter
      • (+) 스프링 시큐리티 + JWT
      • (+) 마치며
    • 더 자바, 코드를 조작하는 다양한 방법
      • CH01 JVM 이해하기
      • (+) 클래스 로더 이해하기
      • CH02 바이트 코드 분석 및 조작
      • (+) jacoco
      • CH03 리플렉션
      • CH04 다이나믹 프록시
      • CH05 애노테이션 프로세서
    • 더 자바, 애플리케이션을 테스트하는 다양한 방법
      • CH01 JUnit 5
      • CH02 Mockito
      • (+) Spy vs Mock
      • CH03 도커와 테스트
      • CH04 성능 테스트
      • (+) VisualVM
      • (+) 테스트 자동화
      • CH05 운영 이슈 테스트
      • CH06 아키텍처 테스트
    • 모든 개발자를 위한 HTTP 웹 기본 지식
      • CH01 인터넷 네트워크
      • CH02 HTTP 기본
      • CH03 HTTP 메서드 속성
      • CH04 HTTP 메서드 활용
      • CH05 HTTP 상태코드
      • CH06 HTTP 헤더1 - 일반 헤더
      • CH07 HTTP 헤더2 - 캐시와 조건부 요청
      • (+) HTTPS 원리
    • 스프링 프레임워크 핵심 기술
      • CH01 IOC 컨테이너
      • CH02 AOP
      • (+) 스프링 의존성 관리
      • (+) 생성자 주입 장점
    • 코딩으로 학습하는 GoF의 디자인 패턴
      • 객체 생성
        • 싱글톤 패턴
        • 팩토리 메소드 패턴
        • 추상 팩토리 패턴
        • 빌더 패턴
        • 프로토타입 패턴
      • 구조
        • 어댑터 패턴
        • 브릿지 패턴
        • 컴포짓 패턴
      • 행동
        • (작성중)
    • 실전 Querydsl
      • CH01 프로젝트 환경구성
      • CH02 예제 도메인 모델
      • CH03 기본문법
      • CH04 중급 문법
      • CH05 실무활용 (스프링 데이터 JPA와 Querydsl)
      • CH06 스프링데이터JPA 가 제공하는 Querydsl 기능
      • (+) 별칭(alias)
      • (+) Slice 쿼리
    • 스프링 데이터 JPA
      • CH01 핵심개념이해 1
      • CH02 핵심개념이해 2
      • CH03 핵심개념이해 3
      • CH04 Spring Data Common
      • CH05 Spring Data JPA
    • 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
      • CH01 지연 로딩과 조회 성능 최적화
      • CH02 컬렉션 조회 최적화
      • CH03 전체 정리
    • 초보를 위한 쿠버네티스 안내서
      • CH01 쿠버네티스 시작하기
      • CH02 쿠버네티스 알아보기
      • CH03 쿠버네티스 실습 준비
      • CH04 쿠버네티스 기본 실습
    • Flutter Provider Essential
      • CH01 Introduction
      • CH02 Provider Overview
      • CH03 TODO App
      • CH04 Weather App
      • CH05 Firebase Authentication App
    • Flutter Bloc Essential
      • CH01 Introduction
      • CH02 Bloc Overview
      • CH03 TODO App
      • CH04 Weather App
      • CH05 Firebase Authentication App
    • Flutter Advanced Course - Clean Architecture With MVVM
      • CH01 Introduction
      • CH02 Clean Architecture 4 Layer
      • CH03 MVVM
      • CH04 Data Layer
      • (+) Data Layer - response to model
      • (+) Data Layer - Network
      • CH05 Domain Layer
      • CH06 Presentation Layer
      • CH07 Application Layer
      • (+) Application Layer - l10n
      • (+) Application Layer - DI
      • (+) Application Layer - environment
    • 자바 알고리즘 입문
      • CH01 문자열
      • CH02 Array(1, 2 차원 배열)
      • CH03 Two pointers, Sliding window[효율성: O(n^2)-->O(n)]
      • CH04 HashMap, TreeSet (해쉬, 정렬지원 Set)
      • CH05 Stack, Queue(자료구조)
      • CH06 Sorting and Searching(정렬, 이분검색과 결정알고리즘)
      • CH07 Recursive, Tree, Graph(DFS, BFS 기초)
      • CH08 DFS, BFS 활용
      • CH09 Greedy Algorithm
      • CH10 dynamic programming(동적계획법)
  • 도서
    • 만들면서 배우는 클린 아키텍처
      • 학습목표
      • CH01 계층형 아키텍처의 문제는 무엇일까?
      • CH02 의존성 역전하기
      • CH03 코드 구성하기
      • CH04 유스케이스 구현하기
      • CH05 웹 어댑터 구현하기
      • CH06 영속성 어댑터 구현하기
      • CH07 아키텍처 요소 테스트하기
      • CH08 경계 간 매핑하기
      • CH09 어플리케이션 조립하기
      • CH10 아키텍처 경계 강제하기
      • CH11 의식적으로 지름길 사용하기
      • CH12 아키텍처 스타일 결정하기
    • 클린 아키텍처
      • 들어가며
      • 1부 소개
        • 1장 설계와 아키텍처란?
        • 2장 두 가지 가치에 대한 이야기
      • 2부 벽돌부터 시작하기: 프로그래밍 패러다임
        • 3장 패러다임 개요
        • 4장 구조적 프로그래밍
        • 5장 객체 지향 프로그래밍
        • 6장 함수형 프로그래밍
      • 3부 설계 원칙
        • 7장 SRP: 단일 책임 원칙
        • 8장 OCP: 개방-폐쇄 원칙
        • 9장 LSP: 리스코프 치환 원칙
        • 10장 ISP: 인터페이스 분리 원칙
        • 11장 DIP: 의존성 역전 원칙
      • 4부 컴포넌트 원칙
        • 12장 컴포넌트
        • 13장 컴포넌트 응집도
        • 14장 컴포넌트 결합
      • 5부
        • 15장 아키텍처란?
    • 스프링 입문을 위한 자바 객체 지향의 원리와 이해
      • CH01 사람을 사랑한 기술
      • CH02 자바와 절차적/구조적 프로그래밍
      • CH03 자바와 객체 지향
      • (+) 자바 코드 실행에 따른 메모리 적재과정
      • CH04 자바가 확장한 객체 지향
      • CH05 객체 지향 설계 5 원칙 - SOLID
      • CH06 스프링이 사랑한 디자인 패턴
      • CH07 스프링 삼각형과 설정 정보
      • (부록) 람다(lambda)
    • 객체지향의 사실과 오해
      • CH01 협력하는 객체들의 공동체
      • CH02 이상한 나라의 객체
      • CH03 타입과 추상화
      • CH04 역할, 책임, 협력
      • CH05 책임과 메시지
      • CH06 객체 지도
      • CH07 함께 모으기
      • (+) 인터페이스 개념 바로잡기
    • 도메인 주도 개발 시작하기
      • CH01 도메인 모델 시작하기
      • CH02 아키텍처 개요
      • CH03 애그리거트
      • CH04 리포지터리와 모델 구현
      • CH05 스프링 데이터 JPA를 이용한 조회 기능
      • CH06 응용 서비스와 표현 영역
      • CH07 도메인 서비스
      • CH08 애그리거트 트랜잭션 관리
      • CH09 도메인 모델과 바운디드 컨텍스트
      • CH10 이벤트
      • CH11 CQRS
    • 자바 ORM 표준 JPA 프로그래밍
      • CH01 JPA 소개
      • CH02 JPA 시작
      • CH03 영속성 관리
      • CH04 엔티티 매핑
      • CH05 연관관계 매핑 기초
      • CH06 다양한 연관관계 매핑
      • CH07 고급 매핑
      • CH08 프록시와 연관관계 관리
      • CH09 값 타입
      • CH10 객체지향 쿼리 언어
      • CH11 웹 애플리케이션 제작
      • CH12 스프링 데이터 JPA
      • CH13 웹 애플리케이션과 영속성 관리
      • CH14 컬렉션과 부가 기능
      • CH15 고급 주제와 성능 최적화
      • CH16 트랜잭션과 락, 2차 캐시
    • 소프트웨어 세상을 여는 컴퓨터과학
      • CH01 컴퓨터 과학 소개
      • CH02 데이터 표현과 디지털 논리
    • 이펙티브 자바
      • 1 장 들어가기
      • 2장 객체 생성과 파괴
        • [01] 생성자 대신 정적 팩터리 메서드를 고려하라
        • [02] 생성자에 매개변수가 많다면 빌더를 고려하라
        • [03] private 생성자나 열거 타입으로 싱글턴임을 보증하라
        • [04] 인스턴스화를 막으려거든 private 생성자를 사용하라
        • [05] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
        • [06] 불필요한 객체 생성을 피하라
        • [07] 다 쓴 객체 참조를 해제하라
        • [08] finalizer 와 cleaner 사용을 피하라
        • [09] try-finally 보다는 try-with-resources 를 사용하라
      • 3장 모든 객체의 공통 메서드
        • [10] equals는 일반 규약을 지켜 재정의하라
        • [11] equals 를 재정의하려거든 hashCode도 재정의하라
        • [12] toString 을 항상 재정의하라
        • [13] clone 재정의는 주의해서 진행하라
        • [14] Comparable 을 구현할지 고려하라
      • 4장 클래스와 인터페이스
        • [15] 클래스와 멤버의 접근 권한을 최소화하라
  • 토픽
    • 서버 모니터링
      • CPU 사용량
      • 메모리 사용량
      • 스레드 풀
    • Spring Boot Monitoring
      • Spring actuator
      • Spring eureka
      • Prometheus
      • grafana
      • Spring actuator + Prometheus + grafana
    • JAVA 데일리 토픽
      • 메모리 누수(memory leak)
      • 객체 참조의 유형
      • 커스텀 스레드 풀
      • Mark And Compact
      • serialVersionUID 이해하기
      • 함수형 인터페이스
      • 메소드 참조
      • equals()와 hashCode()가 무엇이고 역할이 무엇인지
      • StringBuffer vs StringBuilder
      • String vs StringBuilder, StringBuffer
      • String interning
    • JAVA GC
    • 프로그래머스 문제 풀기
      • 해시
      • 스택/큐
      • 힙(Heap)
      • 정렬
      • 완전탐색
      • DFS/BFS
    • 데이터베이스 구성 및 작동 흐름
    • 데이터베이스 JOIN 원리
    • 객체지향생활체조 원칙
    • 상태(state), 상속(inheritance), 합성(composition) 의 상관관계
    • java enum은 메모리에 언제, 어떻게 할당되는가
    • Checked Exception vs UnChecked Exception
    • Reactive Streams 원리탐구 - 간단한 예제 직접 작성해보기
    • Flutter Basic
    • Flutter StatefulWidget 생명주기
    • Flutter 가 위젯을 그리는 원리
    • Flutter 클린 아키텍처
      • application layer
        • 패키지 구조 및 레이어 설명
        • environment
        • dependency injection
        • go_router
        • foreground & background
        • 다국어처리 (l10n, i18n)
        • Global 처리(시스템 점검, fore->back 등)
        • connection_manager
        • permission_manager
        • push_notification_manager
        • firebase 연동
      • data layer
        • 패키지 구조 및 레이어 설명
        • network
        • repository
      • domain layer
        • 패키지 구조 및 레이어 설명
      • presentation layer
        • 패키지 구조 및 레이어 설명
        • resources
    • 기술 관련 포스팅 읽기
  • 기타
    • 작업일지
      • 2023. 10
      • 2023. 09
      • 2023. 08
      • 2023. 07
      • 2023. 06
      • 2023. 05
      • 2023. 04
      • 2023. 03
      • 2023. 02
      • 2023. 01
      • 2022. 12
    • Business Model
      • 아이디어 불패의 법칙
      • 린 모바일 앱 개발
      • 린 스타트업
      • 제로투원
      • MIT 스타트업 바이블
      • 린치핀
    • 백로그 종합
Powered by GitBook
On this page
  • 사전 개념
  • Forward Proxy, Reverse Proxy
  • TLS
  • 1. 미션 요구사항
  • 2. 미션 실습
  • 1) TLS 적용 & Reverse Proxy 구축
  • 2) 운영 데이터베이스 연결하기(+ git submodule을 이용한 flavor별 설정파일 분기)
  • 3) CI&CD 파이프라인 구축
  • 4) 서비스 컨테이너 기동
  • 5) 그 외 추가(데이터베이스 테이블 스키마 버전관리, 정적 테스트, 로컬 테스트, git issue 활용)
  • 총정리
  1. 강의
  2. NEXTSTEP 인프라 공방 1기

[미션 2] 서비스 배포하기 실습

Previous[미션 1] 서비스 구성하기 실습Next서버 진단하기

Last updated 1 year ago

사전 개념

Forward Proxy, Reverse Proxy

Forward Proxy는 클라이언트가 특정 도메인에 연결하려고 시도할 때 클라이언트를 인터넷과 직접 연결시키지 않고, 해당 클라이언트의 요청을 대리로 수행해서 인터넷 연결, 결과값 수신, 이를 전달하는 모든 절차를 대신해주는 Proxy를 의미한다. 보안이 철저한 회사에서 정해진 사이트에서만 검색되게 한다던가 하는 기능을 수행하는데 사용된다.

Reverse Proxy

클라이언트->인터넷->목표서버 의 순서에서 목표서버의 바로 앞, 인터넷의 바로 뒤에 위치하여 인터넷을 통해 들어오는 클라이언트의 여러 요청들을 받아서 이를 목표서버에 전달하고 결과값을 받아서 인터넷으로 전달해주는 역할을 한다.

1. 미션 요구사항

이번 미션 요구사항의 포인트는 크게 아래와 같았다고 개인적으로 생각한다. ㄱ) Reverse Proxy 설정하여 TLS 처리 ㄴ) 내부망으로 준비해뒀던 인스턴스에 DB 설치 후 서비스 인스턴스에서 연결 그리고 추가적으로 운영 파일들에 대해서 git submodule + private repository 로 관리하는 방법이 매우 재밌었다.

2. 미션 실습

1) TLS 적용 & Reverse Proxy 구축

SSL 적용을 회사 입사하여 처음 해봤고, 회사에서도 처음부터 적용한 것이 아니라 누군가 적용해놓은 것을 그대로 다른 서버에 복붙하는 식으로 적용했지 이렇게 처음부터 암호화를 적용해본 적이 없어서 무척 재미있었다. 역시 누군가 해놓은 것을 눈으로 보고 ‘이해하고 있다’, ‘나도 할 수 있겠다’ 라고 오만하게 생각하기 보다는 직접 A to Z 를 해보는게 중요하다는 걸 느꼈다.

매우 간편하다. 미션에서는 cerbot을 따로 설치할 것 없이 docker를 이용해서 cerbot을 실행시키고 인증서 발급을 하여 개인키, 공개키를 만들고 인증서 발급을 신청하고 CA에 의해 암호화된 인증서를 받아 저장까지 완료하였다.

$ docker run -it --rm --name certbot \
  -v '/etc/letsencrypt:/etc/letsencrypt' \
  -v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
  certbot/certbot certonly -d 'yourdomain.com' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory

-v 로 경로를 지정해주는 이유는 해당 경로로 CA로부터 받을 예정인 인증서와 certbot이 만든 개인키를 저장하기 위함이다. 예제는 저 주소로 하긴 했는데, 내 컴퓨터의 경로 주소는 사실 내가 편한대로 해도 상관없을 것 같다. 아무튼 certbot 컨테이너를 이용해서 인증서를 발급치 받으면 이를 nginx 설정에 적용해주고, dns 서버 설정에도 적용해준다.

nginx 설정

events {}

http {
  upstream app {
    server 172.17.0.1:8080; <-- 앱을 도커에 띄울 것이므로 172.17.0.1 설정
  }

  # Redirect all traffic to HTTPS
  server {
    listen 80;
    return 301 https://$host$request_uri;
  }

  server {
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/[도메인주소]/fullchain.pem; <-- nginx 컨테이너 내 인증서 위치
    ssl_certificate_key /etc/letsencrypt/live/[도메인주소]/privkey.pem; <-- nginx 컨테이너 내 개인키 위치

    # Disable SSL
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # 통신과정에서 사용할 암호화 알고리즘
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

    # Enable HSTS
    # client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제
    # 이를 통해 http에서 https로 redirect 되는 request를 minimize 가능
    # https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Strict-Transport-Security
    add_header Strict-Transport-Security "max-age=31536000" always;

    # SSL sessions
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
      proxy_pass http://app;
    }
  }
}

Reverse Proxy 기동(위에서 설정한 nginx.conf를 적용한 nginx 기동)

docker를 이용해서 아래와 같이 Dockerfile을 만들어서 이미지를 build 하고 이를 컨테이너로 띄운다.

FROM nginx

COPY nginx.conf /etc/nginx/nginx.conf
COPY fullchain.pem /etc/letsencrypt/live/[도메인주소]/fullchain.pem
COPY privkey.pem /etc/letsencrypt/live/[도메인주소]/privkey.pem

여기서 fullchain.pem이 인증서이고 privkey.pem가 개인키이다. COPY 로 시작되는 명령들은 nginx 컨테이너 내 nginx 설정, 인증서, 개인키의 경로를 잡아주는 것인데 앞에는 내가 설정한 nginx.conf 위치, 발급받은 인증서와 개인키의 위치를 지정해주고 뒤에는 지금 build하려고 하는 nginx 컨테이너 내에서 이 파일들이 어디에 위치하게 될지를 설정한다.

그렇기 때문에 지금 설정해주는 인증서, 개인키의 위치와 앞서 nginx.conf에서 설정한 인증서, 개인키의 위치가 일치해야한다.

이렇게 Dockerfile 작성이 끝나면 아래와 같이 nginx를 Reverse Proxy Server로 기동한다.

$ docker run -d -p 80:80 -p 443:443 --name proxy nextstep/reverse-proxy

여기서 포트를 80을 잡아주는 이유는 의미 그대로 http로 들어올 시에 이를 redirect시켜주기 위함이며, 443은 https를 캐치하기 위함이다. 맨 밑에 칠판에 판서한 그림에 정리가 되어있긴 한데, 궁극적으로 원하는 절차는 아래와 같다.

ㄱ) 80은 443으로 redirect ㄴ) 443(https)는 암호화 절차 적용하고 서비스 띄워둔 포트인 8080으로 redirect ㄷ) 컨테이너로 띄워둔 서비스(172.17.0.1:8080)로 request 받음

2) 운영 데이터베이스 연결하기(+ git submodule을 이용한 flavor별 설정파일 분기)

이 과정에서 비로소 이쁘게 망분리를 해놨던 내부망과 연결하게 되었다. 별 것 아닌데도 막상 의도한대로 연결되고 돌아가는것을 보니 재미가 있었다. 운영 데이터베이스 연결과정은 아래 절차에 의해서 진행되었다.

ㄱ) 미리 만들어둔 private repository(flavor별 설정파일이 있는)를 git submodule을 이용하여 src/main/resources/config에 추가 ㄴ) target profile 에 해당하는 .properties 파일 내에 DB인스턴스 private IP, DB id & password, 스키마 정보 등을 작성 ㄷ) build할 웹 서비스의 Dockerfile 상에 ENTRYPOINT에 “-Dspring.profiles.active=prod”와 같이 profile 설정 ㄹ) docker build -> run 하여 DB 연결 되는지 테스트 cf. DB 인스턴스에 위 2) 번에서 입력한 정보대로 DB 정보가 구축되어 있어야 한다. 나는 빠르게 하려고 컨테이너를 사용했다.

$ docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1004 mysql
$ docker exec -it [docker ps로 알아낸 container ID] bash
$ docker mysql -u root -p => 아까 설정한 비밀번호 1004로 접속
$ create schema [.properties 에서 설정한 접속 스키마 명]

미션에서는 빠른 구현을 위해서 DB 인스턴스 내 mysql 을 컨테이너로 기동했지만, 강사님 말씀대로 컨테이너가 추구하는 철학과 DB의 영속성이 성질이 맞지 않기 때문에 제대로 하려면 컨테이너 방식이 아니고 DB인스턴스에 직접 mysql을 직접 설치해서 사용해야한다.

.properties 파일에는 아래와 같이 DB 인스턴스의 Private IP와 적절한 포트 번호를 지정해주고, 스키마도 잘 잡아준다. 당연한 이야기지만 이 설정을 통해 내부망의 DB 인스턴스에 접속이 되게 하려면 DB 인스턴스의 보안그룹 설정에서 접속하려는 인스턴스의 Private IP가 해당 포트로 허용되어 있어야 하며 여기 접속하려는 인스턴스도 라우팅 테이블에 의해서 목적지에 대한 학습이 되어 있어야 한다.

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://[ DB instance Private IP Address ]:3306/fistkim101?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=1004

3) CI&CD 파이프라인 구축

실무에서 Jenkins만 사용하다가 git action을 처음 써봤는데, 개인적으로 굉장히 만족스러웠다. 일단 관리 포인트가 많아지는게 무척 피곤한 일인데(모르겠다 내 성격 탓인지 단순한게 좋다) CI&CD이슈를 뭔가 다른 곳에 ‘들어가서’ 빌드가 잘되고 있나, 스크립트는 어떤식으로 짜고.. 이런 관리를 하는게 너무 귀찮았다. git action은 어차피 git에서 CI&CD 까지 한번에 다룰 수 있어서 좋았고 CI&CD 의 미시적인 전략 자체를 어딘가에 ‘들어가서’ 확인하는게 아니라 IDE 상에서 소스로 확인할 수 있어서 좋았다. 특히 git action을 호출하고 CI&CD 절차를 시각적으로 볼 수 있는데 이 역시 매우 좋았다. 아래는 내가 짜놓은 절차에 맞게 자동으로(내가 따로 설정할 것 없이 yml을 참조하여 자동으로) 보여지는 CI&CD가 도식화된 그림이다.

UI가 매우 깔끔하고 아주 마음에 든다

git action을 사용하면서 매우 뼈아픈 실수를 했는데, 너무 우습게 보고 공식 document를 안보고 그냥 사용법을 여러 블로그들을 보면서 익혀서, self-hosted가 작동을 안하는 데에서 엄청 시간 소요를 많이했다. 6시간 정도는 삽질을 한 것 같다. 뭐든 처음 학습 하는 것은 공식 document를 꼼꼼히, 적어도 대략적으로라도 읽고 ‘뭐가 어디있는지’ 정도는 reference에 대해 알고 해야겠다고 다시 한번 생각했다.

내가 작성한 git action을 위한 파일은 아래와 같다. 아래는 미션 수행을 위해서 짠 것이고, 실제로 내가 실무에 적용한다면 self-hosted를 적극 활용 할 것이기 때문에 실무용 yaml파일은 아래와 조금 달라질 것 같다.

name: production

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    name: test
    steps:
      - name: checks-out this repository under GITHUB_WORKSPACE(enable to access)
        uses: actions/checkout@v2
        with:
          token: $
          submodules: recursive

      - name: set up JDK 11
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'adopt'

      - name: grant execute permission for gradlew
        run: chmod +x gradlew

      - name: test with Gradle
        run: ./gradlew test

  docker_image_build_and_push:
    runs-on: ubuntu-latest
    needs: [ test ]
    name: docker build and push
    steps:
      - name: checks-out this repository under GITHUB_WORKSPACE(enable to access)
        uses: actions/checkout@v2
        with:
          token: $
          submodules: recursive

      - name: set up JDK 11
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'adopt'

      - name: grant execute permission for gradlew
        run: chmod +x gradlew

      - name: test with Gradle
        run: ./gradlew build -x test

      - name: set up QEMU
        uses: docker/setup-qemu-action@v1

      - name: set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: login to DockerHub
        uses: docker/login-action@v1
        with:
          username: $
          password: $

      - name: docker image build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: $/infra-subway:latest

  slack_notification:
    runs-on: ubuntu-latest
    needs: [ test, docker_image_build_and_push ]
    name: slack notification
    steps:
      - uses: actions/checkout@v2
      - name: Slack Notification
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: $

4) 서비스 컨테이너 기동

서비스를 위한 Dockerfile을 만들어서 위에서 작성해둔 CI&CD 과정에 이것이 쓰이게 만들면 된다. 제대로 CI&CD 파일을 작성하고 필요한 정보를 secrets에 설정해뒀다면 docker hub에 docker image가 push 될 것이다. 내가 만약 실무에 이를 적용한다면 해당 과정은 ubuntu-latest로 처리하고 모든게 성공하면 이 최신의 이미지를 self-hosted가 pull 받아서 해당 이미지를 run 시키도록 할 것이다.

FROM openjdk:11-jre-slim
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} subway.jar
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/subway.jar"]

5) 그 외 추가(데이터베이스 테이블 스키마 버전관리, 정적 테스트, 로컬 테스트, git issue 활용)

가장 좋았던 것은 git issue 에 대한 활용이다. 사실 외부 라이브러리를 사용할 때 뭔가 안될때만 issue란을 봤지 내가 개발을 하면서 이걸 써보진 않았다. 쓸 생각도 안했었고. 왜냐면 회사에서는 jira로 개발 이슈들도 같이 다뤘기 때문이다. 하지만 기획과 무관하게 순수하게 개발적인 이슈는 사실 jira보다는 git에서 관리하는게 더 편한 것 같다는 생각이 들었다. 그래서 앞으로 개인 프로젝트든 실무에서든 git issue를 적극적으로 사용 해봐야겠다는 생각이 들었다.

총정리

Forward Proxy 결국 Forward 라는 단어자체가 위치에 대한 개념을 내포하고 있는데, 결국 클라이언트->인터넷->목표서버 의 순서에서 의 위치가 인터넷의 앞 혹은 뒤 위치에 따라서 Forward 혹은 Reverse 가 정해진다고 이해하니 편했다.

목표는 보안 수위를 올리기 위함인데, 인터넷과 WAS를 직접 붙이게 되면 이미 위험에 노출된 것이므로 그 사이에 Reverse Proxy를 위치시키고 이를 WAS와 연결해두면 클라이언트가 원하는 결과물은 제공해 줄 수 있으면서도 보안의 depth 를 더 깊게 가져갈 수 있다.(참고 : )

사실 SSL의 개념만 알았지 TLS는 이번에 미션 진행하면서 처음 알게된 용어인데, SSL의 취약점을 보완한 것이 TLS라고 이해하면 차이를 이해하기 편한 것 같다. SSL에서 , FREAK, LogJam, POODLE 등의 문제점들이 발생했고 이를 방지할 수 있는 SSLv3.0이 바로 TLS라고 한다.

그래서 기본적인 HTTPS 방식에 대한 flow를 한번 더 정리해보았다. 이 가 정리가 매우 잘되어 있었는데, 칠판에 flow를 나의 말로 정리해보았고 아래와 같이 정리가 되었다.

미션에서는 인증서 발급을 공짜로 해주는 를 이용해서 암호화 통신을 구현했다. 무료 인증서 발급 CA는 라는 곳을 이용했는데 무료이니 만큼 허용 기간이 짧다(90일). TLS를 적용하는 것은 아래와 같은 절차로 이뤄진다.

ㄱ) 를 통해 TLS 인증서 발급 ㄴ) 발급받은 인증 관련 정보와 함께 nginx 설정 해주고 Reverse Proxy 서버 기동

혹시라도 이 글을 보게되는 누군가가 git action을 사용하고자 한다면 꼭 를 한번 훑어보고 사용하길 바란다. 1시간 30분 정도면 거의 다 읽어진다. 그리고 내가 실수했던 host 관련한 부분은 에 있다. 내가 실수한 부분은 각 job별로 파일 혹은 workspace가 공유되지 않는 다는 것을 몰라서 시간을 허비한 것이다. 공식 문서에 매우 친절하고 명확하게 명시되어 있는데도 사용하기만 급급해서 이를 놓쳤다.

라는 것을 이용한 데이터베이스 테이블 스키마의 버전 관리와 라는 플러그인을 활용한 정적 테스트, 그리고 이라는 플러그인을 이용해서 로컬 테스트를 편하게 하는 방법 등을 배웠다.

proxy server
Defense in depth
TLS
HeartBleed
사이트
CA
Let’s Encrypt
cerbot
공식문서
여기
Flyway
Sonarlint
Multirun