블로그

이효정

배포를 시작하기 전에 반드시 알아야 할 것들

 CI/CD 파이프라인을 구성할 때 고려해야 하는 요소 [CI/CD환경]빌드-> 컨테이너 빌드 -> kubectl , helm으로 배포Jenkins로 파이프라인을 구축( 빌드-> 컨테이너 빌드 -> 배포)개발자 빌드 -> DevOps엔지니어 Jenkins 파이프라인 구축 2번이 편한 것처럼 보이지만 수정할 일이 생겼을 때 1번처럼 담당하는 사람에 따라 분리하는 것이 좋다.1번으로 구성하고 각 과정에 트리거만 걸어놓으면 자동 배포가 된다. [배포와 인프라 환경 관계]1:NJenkins (소스빌드 컨테이너 빌드) -> ArgoCD를 통해 배포(kubectl, helm)장점: 관리 편의성 / 단점: 장애시 운영 환경에 영향도 높음 1:1(더 많이 사용)개발, 운영 환경마다 ArogoCD를 두기장점: 운영 환경에서 장애 영향 없음 / 단점: 이중 관리에 대한 부담  [CI/CD Tool]온라인: 깃허브 액션------------------------> 보안 good오프라인: jenkins , jenkinsX, Tekton------> 공공, 의료, 금융기관은 데이터가 인터넷 영역으로 올라가면 안됨. 그래서 오프라인 툴을 사용한다.  [Docker 대체]도커 대체 이유: 무겁다, Daemon 필요(리눅스의 background에서 항상 돌아가야 사용 가능하다)대체제: buildahpodman과 skopeo 함께 사용배포 전략을 세울 때 고려해야 하는 요소 [Recreate]v1->v2 하고싶으면 기존 pod 삭제시킨다. 이때 downtime발생한다. 그리고 v2 새로운 파드가 만들어진다.배포는 Deployment 업데이트 하면 된다. / 에러 롤백 가능,트래픽 제어 불가능 [RollingUpdate]v1 서비스 진행->v2동시에 파드가 호출되는 구간있고 v2파드로 전환된다.배포는 Deployment 업데이트 하면 된다. / 에러 롤백 가능,트래픽 제어 불가능, 서비스 중단 없음 [Bule/Green]v2 deployment 만든다. label은 v2로 만든다. 서비스 셀렉터는 v1에서 v2로 수정한다. 트래픽이 v2으로만 들어가게 된다.수동 배포 시 롤백 빠름 / 스크립트를 통해 자동 배포 가능 / v2에 과도한 트래픽 유입시 문제 발생운영에서만 테스트한다--> blue/green 배포 쓰세요~ [Canary]배포용으로 새 서비스 만든다. Ingress 컨트롤러인 엔진 X 랑 각 서비스에 Ingress라는 리소스도 만들어 준다.트래픽 양을 조절할 수 있다.특정 헤더 값에 한해서만 v2 트래픽 유입콜드 스타트 방지, 두 버전 비교 가능 (A/B 테스트)단계별로 구축해보는 배포 파이프라인 Level1: 초반 구성할 때는 간단하고 직관적인 형태로 완성하기 Level2: Jenkins Pipeline 사용 Level3: Kustomize, Helm 배포쿠버네티스 앱들을 늘리기는 쉬워졌다. 하지만 하나씩 복사해서 네임이나 env를 수정하는 것도 귀찮다이렇게 동적 구성으로 한다면 조금 더 편해집니다. 출처: [인프런 쿠버네티스 어나더 클래스] 강좌 자료  Level4: ArgoCD 배포 분리 

데브옵스 · 인프라쿠버네티스복습배포3주차인프런

이혜리

[인프런 워밍업 클럽 1기/BE] 3번째 발자국

section531강. 대출 기능 개발하기32강. 책 반납 기능 개발하기33강. 조금 더 객체지향적으로 개발할 수 없을까?34강. JPA 연관관계에 대한 추가적인 기능들35강. 책 대출/반납 기능 리팩토링과 지연 로딩1. 대출기능 개발 - 새로운 테이블 생성현재 user, book 2개의 테이블이 존재한다. 하지만 이 2개의 테이블 만으로는 대출 기능을 만들 수 없다. 새로운 테이블 user_loan_history 이 필요하다.create table user_loan_history ( id bigint auto_increment, user_id bigint, book_name varchar(255), is_return tinyint(1), primary key (id) )user_id : 어떤 유저가 빌렸는지 알 수 있도록, 유저의 id를 가지고 있도록 한다.is_return : 타입은 tinyint 인데, entity 객체의 필드 중 boolean에 매핑하게 되면, true인 경우 1, false인 경우 0이 저장된다.2. 책 반납 기능 개발 - @ManyToOne 으로 리팩토링  위의 HTTP Body 는 반납 request 의 요청 형식이다. 그런데 '책 대출' 과 '책 반납'의 api body가 똑같다. 이때 똑같더라도 별개의 class로 작성하는 것이 좋다. 두 기능 중 한 기능에 변화가 생겼을때, 유연하고 다른 부가적인 문제없이 대처할 수 있기 때문이다.아래는 반납 관련 DTO와 Controller, service 내용이다.DTOpublic class BookReturnRequest { private String userName; private String bookName; public BookReturnRequest(String userName, String bookName) { this.userName = userName; this.bookName = bookName; } public String getUserName() { return userName; } public String getBookName() { return bookName; } }Controller @PutMapping("/book/return") public void returnBook(@RequestBody BookReturnRequest request){ bookService.returnBook(request); }Service@Transactional public void returnBook(BookReturnRequest request) { User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalArgumentException::new); UserLoanHistory history = userLoanHistoryRepository.findByUserIdAndBookName(user.get Id(), request.getBookName()) .orElseThrow(IllegalArgumentException::new); history.doReturn(); }위의 코드를 조금 더 객체지향적으로 개발하기 위해서JPA 연관관계를 활용할 수 있다.  이렇게 바꾸기 위해서는 UserLoanHistory와 User 가 서로 직접 알고 있어야 한다.UserLoanHistory의 userId 를 user로 변경해보자. @Entity public class UserLoanHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @JoinColumn(nullable = false) @ManyToOne private User user; private String bookName; private boolean isReturn; public Long getId() { return id; } public String getBookName() { return bookName; } public boolean isReturn() { return isReturn; } public UserLoanHistory(User user, String bookName) { this.user = user; this.bookName = bookName; this.isReturn = false; } public void doReturn(){ this.isReturn = true; } public UserLoanHistory() { } }@ManyToOne 은 N(나) : 1(너) 관계로 위에서는 N이 UserLoanHistory가 되고, 1이 User가 된다.User 클래스에서 1명의 유저는 N개의 UserLoanHistroy를 가지고 있을 수 있기 때문에, UserLoanHistroy를 List 형태로 가지고 있어야 한다.@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @Column(nullable = false, length = 20, name = "name") //name varchar(20) private String name; @Column(nullable = false) private Integer age; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) //주인이 가진 필드 이름 // fetch = FetchType.LAZY private List<UserLoanHistory> userLoanHistories = new ArrayList<>(); protected User() { } public Long getId() { return id; } public String getName() { return name; } public int getAge() { return age; } public void updateName(String name){ this.name = name; } public User(String name, int age) { if (name == null || name.isEmpty()) throw new IllegalArgumentException(String.format("잘못된 name(%s)이 들어왔습니다.", name)); this.name = name; this.age = age; } public void loanBook(String bookName){ this.userLoanHistories.add(new UserLoanHistory(this, bookName)); } public void returnBook(String bookName){ UserLoanHistory targetHistroy = this.userLoanHistories.stream() .filter(history -> history.getBookName().equals(bookName)) .findFirst() .orElseThrow(IllegalArgumentException::new); targetHistroy.doReturn(); } }요 List에는 @OneToMany 를 붙여준다.이때 연관관계의 주인을 정해주어야 한다.  현재 user 테이블과 user_loan_history 테이블을 보면, user_loan_history는 user를 알고 있다. 반면 user는 user_loan_history 를 알지 못한다. 즉, 관계의 주도권을 user_loan_history 가 가지고 있는 것이다.테이블에서는 이를 알 수 있지만,JPA 에서는 모르는 상태이니 mappedBy 옵션을 달아주어 이제 알려주자.user가 주인이 아니므로, @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) //주인이 가진 필드 이름 // fetch = FetchType.LAZY private List<UserLoanHistory> userLoanHistories = new ArrayList<>();이렇게 하여 user 와 userLoanHistory가 서로를 알 수 있도록 하였다. 하지만, 여전히 BookService는 User와 UserLoanHistory를 각자 다루고 있다. 온전히 협력하지 못하므로 이를 수정해보자.+ @JoinColumn 은 연관관계의 주인 클래스에서 사용할 수 있다. Service 코드에서 UserLoanHistory를 직접 사용하지 않고, User 를 통해 대출 기록을 저장하도록 변경해보자.일단 BookService는 아래와 같이 변경했다. @Transactional public void loanBook(BookLoanRequest request) { //1. 책 정보를 가져온다. Book book = bookRepository.findByName(request.getBookName()) .orElseThrow(IllegalArgumentException::new); //2. 대출기록 정보를 확인해서 대출중인지 확인합니다. //3. 먄약에 확인했는데 대출중이라면 예외를 발생시킨다. if (userLoanHistoryRepository.existsByBookNameAndIsReturn(book.getName(), false)) //대여중임 throw new IllegalArgumentException("이미 대출되어 있는 책입니다."); //4. 유저 정보를 가져온다. User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalArgumentException::new); user.loanBook(book.getName()); }위에서 UserLoanHistory 객체를 직접적으로 사용하지 않고 있다. user 객체의 함수인 loanBook를 불러오고 있는데 여기 메소드를 살펴보면, public void loanBook(String bookName){ this.userLoanHistories.add(new UserLoanHistory(this, bookName));User 의 필드중 userLoanHistories 에 UserLoanHistory 객체를 집어넣는다.이렇게 바꾸어서 User 와 UserLoanHistory 2개 객체가 서로 협력하도록 변경했다.section 6배포를 하기 위해서는 aws 의 ec2를 사용한다.ec2는 계속 돌아가는 전용 컴퓨터와 비슷한 개념이다. ec2 인스턴스를 생성한 후, 이에 ssh 연결하여 필요한 것들을 설치한 후, 프로젝트 build 후 실행을 background에서 하면 된다.회고강의를 90% 들은 시점에서 들은 생각은 이 강의와 인프런 워밍업 클럽 스터디를 하기 잘했다는 것이다. 이제는 기본적으로 api를 보낼 수 있으니, 앞으로는 시큐리티 부분과 테스트 코드 위주로 공부를 더 해나가려 한다.

백엔드백엔드aws배포jpa연관관계

채널톡 아이콘