2021년 5월 20일 목요일

AWS 자동배포 연습해보기

 

이전 포스팅(https://cotmulgyu.blogspot.com/2020/07/blog-post.html)에서 github의 webhooks 를 통해 push가 되면 젠킨스에서 빌드하고 특정 명령어를 실행해서 배포할 수 있음을 확인하였다.

이번에는 CI/CD 서버에서 빌드 후 실제 배포 서버(aws)에 빌드된 파일을 보내고 배포서버에서 바로 배포까지 할 수 있도록 해본다!

  • aws 인스턴스 생성과정은 생략

1_프로젝트 설정

REPOSITORY=/home/ec2-user/project/todolist
PROJECT_NAME=to-do-list

echo "> Build 파일복사"

cp $REPOSITORY/zip/*.jar $REPOSITORY/


echo "> 현재 구동중인 애플리케이션 pid 확인"

CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)

echo "현재 구동중인 애플리케이션 pid: $CURRENT_PID"


if [ -z "$CURRENT_PID" ]; then
        echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
        echo "> kill -15 $CURRENT_PID"
        kill -15 $CURRENT_PID
        sleep 5
fi

echo "> 새 애플리케이션 배포"

JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)

echo "> JAR Name: $JAR_NAME"


echo "> $JAR_NAME 실행"

nohup java -jar \
        -Dspring.config.location=classpath:/application.yml,/home/ec2-user/project/properties/application-oauth.properties \
        -Dspring.profiles.active=real \
        $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
  • AWS CodeDeploy에서 사용할 appspec.yml 파일을 생성한다.

version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ec2-user/project/todolist/zip # S3에서 이동시킬 경로
    overwrite: yes

permissions: # 권한설정이 없으면 root로 설정되므로 다음의 설정 필요
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  ApplicationStart:
    - location: start.sh # 애플리케이션 실행
      runas: ec2-user


2_AWS 설정

  • http://m.yes24.com/Goods/Detail/83849117 해당 책을 참고하면 자세하게 설명되어있다...

  • IAM

    • Identity and Access Management(IAM)는 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스. IAM을 사용하여 리소스를 사용하도록 인증(로그인) 및 권한 부여(권한 있음)된 대상을 제어함
    • IAM > 사용자 추가
      • 엑세스유형: 프로그래밍 방식 엑세스
      • 권한 설정: 기존정책 직접 연결 > AmazonS3FullAccess , AWSCodeDeployFullAccess 체크
      • 엑세스키와 비밀 엑세스 키는 잘 관리하도록한다..
  • S3 버킷

    • AWS의 S3 서비스는 일종의 파일 서버임 (파일들을 저장하고, 접근 권한을 관리, 검색을 지원)
    • S3 > 버킷 만들기
      • 다른 건 기본설정..
      • 퍼블릭 엑세스 차단 : 모든퍼블릭 엑세스 차단 체크
  • CodeDeploy

    • AWS CodeDeploy는 Amazon EC2 인스턴스 및 온프레미스에서 실행 중인 인스턴스를 비롯하여 모든 인스턴스에 대한 코드 배포를 자동화하는 서비스

    • IAM > 역할만들기

      • 역할은 EC2에서 사용할 것임
      • 신뢰할 수 있는 유형의 개체 선택 : AWS 서비스
      • 이 역할을 사용할 서비스 선택 : EC2
      • 정책 : AmazonEC2RoleforAWSCodeDeploy 체크
    • EC 인스턴스로 이동 > 인스턴스 우클릭 > 인스턴스 설정 > IAM 역할 연결/바꾸기 > 아까 역할 추가후 재부팅 (보안-IAM 연결 수정)

    • CodeDeploy 에이전트 설치

      • CodeDeploy의 요청을 받을 수 있도록 EC2에 에이전트를 설치한다.

      • EC2에서 다음 명령어 입력

        aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-  northeat-2
        
        
        chmod -x ./install
        
        
        sudo ./install auto
        
    • CodeDeploy 권한 생성

      • CodeDeploy가 EC2에 접근하려면 권한이 필요하다.
      • IAM > 역할 생성
        • 서비스 : AWS 서비스 > CodeDeploy
        • 사용사례 선택 : CodeDeploy
        • 권한 : AWSCodeDeployRole
    • CodeDeploy 생성

      • CodeDeploy > 애플리케이션 생성

      • 컴퓨팅 플랫폼 : EC2/온프레미스

      • 배포그룹 생성

        • 서비스역할: CodeDeploy IAM 역할 선택
        • 배포유형 : 현재 위치
        • 환경 구성 : Amazon EC2 인스턴스
        • 배포 구성 : CodeDeployDefault.AllAtOnce
        • 로드 밸런서 : 로드 밸런싱 활성화 해제


3_젠킨스 설정

  • 젠킨스 - Jenkins 관리 - 플러그인 관리 - 다음 플러그인 설치 (AWS CodeDeploy Plugin for Jenkins)

    • 해당 플러그인은 빌드 후 AWS CodeDeploy를 사용할 수 있는 플러그인으로 빌드 후 특정 경로를 압축하여 S3에 업로드한다. 그리고 설정한 배포를 시작한다



  • 이전에 설정한 Build-Execute Shell 부분에 자신의 프로젝트에 맞는 빌드명령어를 실행한다. (build, bootJar 등)




  • Build-Execute Shell 부분에 AWS CodeDeploy에서 사용할 파일(appspec.yml, start.sh)과 빌드된 jar를 포함하여 복사시킨다.

    • 여기있는 파일들을 aws S3 Bucket으로 이동시킴 (zip)

    • CodeDeploy 말고 다른플러그인으로 S3 업로드만도 가능한데 그러면 여기서 미리 압축이 필요하다.




  • 빌드 후 조치 추가 - Deploy an application to AWS CodeDeploy

    • AWS CodeDeploy Application Name : AWS CodeDeploy 에서 만든 어플리케이션 이름

    • AWS CodeDeploy Deployment Group : 설정한 그룹 이름

    • AWS CodeDeploy Deployment Config : 설정한 CodeDeploy 배포 구성

    • S3 Bucket : AWS S3 Bucket 이름

    • S3 Prefix : 버킷에 생성될 디렉토리

    • Subdirectory : 젠킨스 서버에서 포함될 파일이 저장될 디렉토리

    • Include Files : 포함될 파일

    • Use Access/Secret keys : AWS 에서 생성한 IAM 정보





  • 젠킨스 빌드 후 AWS S3 버킷을 확인해보면 다음과 같이 설정한 디렉토리 밑에 zip 파일이 생기는 것을 확인할 수 있음



  • AWS CodeDeploy 에서 아까 생성한 애플리케이션을 들어가보면 다음과 같이 배포 이력을 볼 수 있음



    • 배포 실패 시 /var/log/aws/codedeploy-agent 경로에서 로그확인 가능!!
    • appspec.yml 에 명시된 경로에 s3 zip파일이 풀려있는 것을 확인할 수 있다.



    • start.sh에 의해 jar가 복사되고 애플리케이션 로그를 볼 수 있는 nohup.out 파일이 생성된 것을 확인할 수 있음




2021년 4월 4일 일요일

Entity 생성 - 컬럼에 default 값 관련 메모

Entity - 컬럼에 default 값

토이프로젝트 중 컬럼에 기본 값을 넣고 싶어서 다음과 같은 어노테이션을 사용하였는데, 내가 생각하는 null을 insert 하였을 때 기본 값이 들어가지 않았다.

@Column(columnDefinition="varchar(10) default 'N'")
private Long flag;

테이블 스키마 생성에만 관여하고 insert할 때는 영향이 없는 것 같다. (이유는 더 찾아봐야겠따...)

김영한님은 다음과 같이 사용하신다고 한다. https://www.inflearn.com/questions/83662

...
제가 신규로 진행하는 프로젝트들은 테이블이 제공하는 default를 거의 사용하지 않습니다.

엔티티에 중심으로 개발하다 보니, 객체에 값을 넣는 방식으로 주로 진행합니다.

쉽게 이야기해서 생성자에서 기본값을 미리 설정하는 방식을 사용합니다.

다른 예를 들어드리면, 실무에서 @Index(인덱스 조건)도 DDL을 생성할 때만 사용하기 때문에 사실은 적을 필요가 없지만, 
그래도 엔티티에 이 애노테이션이 있으면, 개발자분들이 엔티티만 보고 인덱스를 생각하고 JPQL을 작성할 수 있기 때문에 사용합니다. 
말씀하신 default도 그런 관점에서 저는 적으면 좋겠다고 생각합니다.
...

위 내용과 같이 생성자에서 값을 그냥 넣어주던가 이렇게 사용하는 방법도 있다.

@Column
private Long flag = "N";

2021년 3월 15일 월요일

샘플데이터 생성하기

 로컬에서 조회 기능 확인할 때 자동으로 생성되는 샘플데이터가 있으면 편하다.

다음과 같이 생성하자

@Component
@RequiredArgsConstructor
public class InitContents {

    private final InitContentsService initContentsService;

    @PostConstruct
    public void init(){
        initContentsService.init();
    }

    @Component
    static class InitContentsService{

        @PersistenceContext
        private EntityManager em;

        @Transactional
        public void init(){

            Board board = new Board("board1");
            em.persist(board);

            Topic topic1 = new Topic("topic1", board);
            Topic topic2 = new Topic("topic2", board);
            em.persist(topic1);
            em.persist(topic2);


            Card card1 = new Card("card1", "des1", topic1);
            Card card2 = new Card("card2", "des2", topic1);
            Card card3 = new Card("card3", "des3", topic2);
            Card card4 = new Card("card4", "des4", topic2);
            Card card5 = new Card("card5", "des5", topic2);
            Card card6 = new Card("card6", "des6", topic2);
            em.persist(card1);
            em.persist(card2);
            em.persist(card3);
            em.persist(card4);
            em.persist(card5);
            em.persist(card6);

            CheckList checkList1 = new CheckList("checkList1", card1);
            CheckList checkList2 = new CheckList("checkList2", card1);
            em.persist(checkList1);
            em.persist(checkList2);

            CheckItem checkItem1 = new CheckItem("checkItem1",checkList1);
            CheckItem checkItem2 = new CheckItem("checkItem2",checkList1);
            CheckItem checkItem3 = new CheckItem("checkItem3",checkList1);
            CheckItem checkItem4 = new CheckItem("checkItem4",checkList2);
            CheckItem checkItem5 = new CheckItem("checkItem5",checkList2);

            checkItem1.update("checkItem1", "N", "Y");
            checkItem2.update("checkItem2", "N", "Y");

            em.persist(checkItem1);
            em.persist(checkItem2);
            em.persist(checkItem3);
            em.persist(checkItem4);
            em.persist(checkItem5);

            checkList1.addCheckItem(checkItem1);
            checkList1.addCheckItem(checkItem2);
            checkList1.addCheckItem(checkItem3);
            checkList2.addCheckItem(checkItem4);
            checkList2.addCheckItem(checkItem5);

            Label label1 = new Label("label1", "green");
            Label label2 = new Label("label2", "red");
            Label label3 = new Label("label3", "blue");

            em.persist(label1);
            em.persist(label2);
            em.persist(label3);

            CardLabel cardLabel1 = new CardLabel(card1, label1);
            CardLabel cardLabel2 = new CardLabel(card2, label2);
            CardLabel cardLabel3 = new CardLabel(card1, label3);
            CardLabel cardLabel4 = new CardLabel(card4, label3);

            card1.addCardLabel(cardLabel1);
            card2.addCardLabel(cardLabel2);
            card1.addCardLabel(cardLabel3);
            card4.addCardLabel(cardLabel4);

        }
    }
}
  • 왜 바로 @PostContruct 에 넣지 않고 따로 빈을 만들었는가?

    This is as defined, actually: init methods (such as @PostConstruct methods) are always called on the target instance itself.
    The proxy will only be generated once the target instance has been fully initialized...
    In other words, the @Transactional proxy isn't even created at the point of the @PostConstruct call yet.
    
    
    @PostConstructor 는 인스턴스 자체에서 호출됨
    프록시는 인스턴스가 완전히 초기화된 후에 생성됨
    그래서 @Transcational 프록시는 @PostConstruct 호출 시점에 생성되지 않음

2021년 3월 1일 월요일

@NoArgsConstructor(access = AccessLevel.PROTECTED)

@NoArgsConstructor(access = AccessLevel.PROTECTED) 쓰는 이유


  • JPA는 기본적으로 디폴트 생성자가 필요함(파라미터가 없는 생성자)

    • protected 까지만 허용됨
    • @NoArgsConstructor(access = AccessLevel.PROTECTED) 을 통해 가능
  • 그렇다고 public 으로 만들면 객체 생성이 일관하지 않게 막 생성될 수 있음!

    • 그래서 protected로 제한하자. (JPA에 필요하기 때문에 private X)

// title이 필수인데 실수로 누락될 수 있음
// 혹은 이상한 곳에서 setter로 인해 엔티티 값이 변경될 수 있음
Board board1 = new Board();
board1.setContent("content1");


// setter를 사용하지 말고 이렇게 사용하자
Board board2 = Board.builder()
  .title("title1")
  .content("content1")
  .build();

board2.updateInfo("updateTitle", "updateContent");

2021년 2월 14일 일요일

메이크 타임 책 메모

메이크 타임 메모

심심해서 읽은 책.

'매일 하이라이트를 지정하여 그 하이라이트를 방해없이 집중해서 진행하자'

하루를 메일, SNS, 미디어에 끌려다녀 하루를 만족스럽게 보내지 못한 사람들을 위한 책이다.

미라클 모닝과 뭔가 비슷한 느낌...


  • 우리가 배운 첫번 째 교훈은 우선순위가 높은 하나의 목표로 하루를 시작하면 마법 같은 무언가가 일어난다는 것이다.

    • 스프린트의 각요일에 우리는 한 가지 중요한 일에 주의를 집중했다.
  • 메이크 타임의 네 단계 프로세스

    • 하이라이트 : 초점을 선택하는 것으로 하루하루를 시작하라.

      • 물론 하이라이트가 그날 하는 유일한 일은 아니다. 하지만 그 일이 당신의 최우선이 될 것이다.
    • 초집중 : 방해꾼을 물리쳐 하이라이트를 처리할 시간을 만들어라.

    • 에너지 충전 : 뇌를 충전하기 위해 몸을 돌보아라

      • 운동, 음식, 잠, 조용한 휴식, 직접 사람을 만나 대화하며 배터리를 충전한다
    • 돌아보기 : 시스템을 조절하고 개선하라

  • 메이크 타임의 목표는 수도승이 되곘다는 맹세가 아니라 실행할 수 있고 융통성 있는 일련의 습관이다.

  • 하루가 끝날 무렵 누군가가 "오늘의 하이라이트는 뭐였나요?"라고 물어보면 뭐라고 대답하고 싶은가? 하루를 돌아봤을 때 어떤 활동이나 성취나 순간을 음미하고 싶은가? 그것이 바로 당신의 하이라이트이다.

  • 오늘 절대적으로 꼭 해야 하는 무언가가 있다면 그 일을 하이라이트로 정하라.

  • 하이라이트는 60~90분이 가장 적합하다. 의미 있는 무언가를 하기에 충분할 뿐만 아니라 스케줄에서 만들어내기에도 적당한 시간이다.

  • 복리와 비슷하다. 하이라이트에 오래 집중을 유지할수록 그 일이 더 매력적으로 느껴져 더 잘할 수 있다.

  • 막힌 채로 있어라. 포기하지 말라

    • 텅 빈 스크린을 물끄러미 바라보거나 종이에 써보거나 이리저리 걸어다니뇌 지금 하는 프로젝트에 초점을 유지하라.
    • 뇌의 조용한 부분들은 일을 진행하면서 앞으로 나아가고 있다.
    • 결국 막힌 부분이 풀릴 것이다.
  • 주말에 늦잠을 자는 것은 몸속 시계에 혼란을 주고 본래 부족하던 잠에서 회복하기가 훨씬 더 어려워질 수 있다.

    • 매일 같은 시간에 알람을 설정하라
  • 일정표를 싹 비울 필요는 없다. 특별한 무언가에 주의를 집중할 60~90분만 있으면 된다.

    • 목표는 중요한 일을 할 시간을 만들고 더 균형을 잡고 오늘을 더 즐기는 것이다.

2021년 1월 18일 월요일

[인프런] 자바 ORM 표준 JPA 프로그래밍 - 기본편 수강 후기

[인프런] 자바 ORM 표준 JPA 프로그래밍 - 기본편 수강 후기

인프런에서 구매한 김영한님 첫번째 강의다. (2020년 10월 완강했지만 후기는 지금 씀...)

https://www.inflearn.com/course/ORM-JPA-Basic

대충 구글링해서 사용했던 JPA를 이렇게 쓰면 안되겠다고 생각하던 중 유튜브에서 봤던 강의가 생각났고, 인프런에 강의 또한 생겼다는 사실도 알게되어 바로 알아보게 되었다.

유튜브에 있는 'JPA프로그래밍 기본기 다지기'의 유료버전이라고 볼 수 있다. (121,000)

좀 더 실습을 할 수 있으며, 자세한 설명을 들을 수 있다.

- JPA : Java Persistence API

  - 자바 진영의 ORM 기술 표준

- ORM? : Object-relational mapping (객체 관계 매핑)

  - 객체는 객체대로 설계
  - 관계형 데이터베이스는 관계형 데이터베이스대로 설계
  - ORM 프레임워크가 중간에서 매핑
  - 대중적인 언어에는 대부분 ORM 기술이 존재
  - JPA는 애플리케이션과 JDBC 사이에서 동작

솔직히 책은 두껍고, 딱딱할 것 같아 접하기 어려웠는데, 인강은 대단했다.

누구나 쉽게 이해할 수 있게 설명해주셨고 유명한 스타 인강강사처럼 명쾌했다.

책이 쉽게 손에 가지않는다?? 이 강의를 듣자!! (물론 나는 현업에서 사용 못해봄...^^)

2020년 11월 12일 목요일

미라클모닝 책 메모

 

미라클 모닝 책 메모

출근 길에 읽을 책이 없어서 방황하다가 예전에 군대에서 읽어본 것 같은 미라클 모닝책을 구매해보았다.
어찌보면 뻔한 '일찍 일어나서 하루를 시작하자' 이지만 항상 책에는 배울 점이 조금씩은 있는 것 같다.

나도 한 시간 일찍 일어나서 물 한 잔, 오늘 해야할 일 정리(trello), 가벼운 운동으로 하루를 시작해보고 있다.
나름 상쾌하게 하루가 시작된다.(물론 일찍 일어날 수 있다면...)


  • 이런 책의 목적은 고이 보존하는 게 아니라 그 책에서 뽑아낼 수 있는 가치를 극대화하는 데 있기 때문이다. 아무 때나 책을 다시 펼쳐서 전부 다 다시 읽지 않고도 핵심 내용을 금방 다시 볼 수 있도록 책들에 표시를 한다.

  • 평범함을 극복하는 법을 발견했다. 바로, 목적 있는 삶을 사는 것이다.

    • 평범함에 안주하게 되는 원인들을 물리치기 위해서는 삶의 목표가 필요하다. 마음을 움직이고 영감을 주며 매일 아침 잠에서 깨어나게 만드는 목표라면 어느 것이든 상관없다. 더 나은 삶을 살아가게 하는 목표라면 더할 나위 없다.

    • 삶의 목표는 아무 때나 바꿔도 괜찮다는 사실을 기억하자. 당신이 성장할수록 당신의 목표도 진화할 것이다. 핵심은, 어떤 목표든 간에 선택헤서 지금부터 목표에 맞춰 살아가는 것이다.

    • 지금부터 일주일 동안 삶의 목표에 대해 생각해보고, 명확하게 그려보자. 그리고 매일 확인할 수 있도록 잘 보이는 곳에 적어두자.

  • 긍정적이고 진취적인 사람들과 많은 시간을 보낸다면 성공을 부르는 그들의 태도와 습관이 당신에게 흡수될 것이다.

  • 당신을 믿고 존중하며, 목표지점까지 삶을 이끌어주는 사람들 찾아야 한다. 그런 사람은 우연히 당신 앞에 나타나지 않는다. 영향력 집단을 강화하는 사람을 적극적으로 찾아야 한다.

  • 지금까지 한 번도 성취하지 못했던 성공을 거두기 위해서 지금까지 한 번도 해보지 않았던 노력을 기꺼이 할 준비가 되어 있어야 한다.

  • 미라클 모닝의 여러 의도 중 하나는 생생한 에너지로 가득했던 아침의 기억을 되찾아주는 것이다. 그리고 그 기억을 삶 전체로 확대 시키는 것이다. 일어나야 하기 때문에 잠에서 꺠는 게 아니라 목표를 가지고 침대를 박차고 나오게 하는 것이다.

  • 책을 한 번만 읽고 그 책에서 얻을 수 있는 모든 가치를 내 것으로 만들기는 어렵다. 어느 분야든 통달하기 위해서는 반복이 요구된다. 어떤 아이디어나 전략, 혹은 기술도 마찬가지로 그것에 계속 반복해서 노출해야만 그것들이 잠재의식에 새겨질 수 있다.

  • 나만의 미라클 모닝을 시작하며 머리로만 생각했던 아이디어를 꺼내어 일기장에 옮겨 적는다. 이를 통해 생각만 했을 때에는 절대로 알 수 없었던 통찰력을 얻게 된다. 기록하기는 새로운 아이디어와 당면한 문재의 돌파구, 새로운 깨달음이나 교훈, 성장과 발전의 발자취를 미래에 다시 한 번 확인할 수 있도록 기억을 저장하는 역할을 한다.