본문 바로가기

Docker 시작하기 - RestAPI편

5) 쿠버네티스 CircleCI를 이용해 무중단 배포하기

반응형

기본적인 쿠버네티스의 작동 방식은 알게 되었고. 이제 자동으로 개발서버에 소스를 반영하는 방법으로 공부해 보자.

 

여러가지 CI/CD툴이 있겠지만 오늘은 Circleci 를 통해 무중단 자동배포를 해보도록 하겠다.

 

배포 과정은 아래와 같다.

 

1) 소스를 개발하고 Github에 push 한다. 

2) circleci에 미리 github와 연동되어진 프로젝트로 build가 작동한다.

3) 2번에서 build와 동시에 docker push를 한 이미지를 쿠버네티스 master node의 instance에 이미지 update shell를

   실행한다.

 

간단히 위와 같은 과정을 거치게 된다.

 

1번 과장은 생략. 다들 github는 다들 사용하니깐..

 

2번 부터 알아 보자. circleci를 private으로 설치를 할 수 있겠지만 여기서는 간단히 http://circleci.com/

 

Continuous Integration and Delivery

Get the best continuous integration and delivery for any platform, in our cloud or on your own infrastructure.

circleci.com

로 접속해서 가입하고 github와 연결하면 github의 repo 기준으로 project를 생성해 준다. 

 

 

위처럼 처음 project 화면에서 보면 set up project 버튼이 활성화 되어 있는것이 보인다. 

 

이것은 소스의폴더의 ./circleci/config.yml 이 없으면  프로젝트 설정이 없다고 설정을 하라는 말이다.

 

위 그림에서 springboot-jwt 는 이미 아래 그림처럼 circleci/config.yml이 추가 되어 있다.

 

 

이 그림처럼 이미 config.yml 파일이 등록 되어있으면 버튼이 비활성화 되어 있다. 

 

요기서 결국 config.yml에 설정된 내용대로 github에 push가 되면 자동으로 build가 시작된다.

 

config.yml을 살펴 보자.

 

# Java Gradle CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
#
version: 2
jobs:
  build:
    docker:
      # specify the version you desire here
      - image: circleci/openjdk:8-jdk

      # Specify service dependencies here if necessary
      # CircleCI maintains a library of pre-built images
      # documented at https://circleci.com/docs/2.0/circleci-images/
      # - image: circleci/postgres:9.4

    working_directory: ~/repo

    environment:
      # Customize the JVM maximum heap limit
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout
      # Download and cache dependencies
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-

      - run: gradle dependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}
      - run: gradle test
      - run: gradle build
      - store_artifacts:
          path: build/libs
      # docker build를  하기 위해 docker를 이미 설치한다.
      - setup_remote_docker:
          version: 19.03.13
      - run: docker build --build-arg JAR_FILE=build/libs/my_react_app-0.0.1-SNAPSHOT.jar --tag kangzu8743/apitest:v$CIRCLE_BUILD_NUM .
      - run: docker login -u kangzu8743 -p rkdtjrwn0!
      - run: docker push kangzu8743/apitest:v$CIRCLE_BUILD_NUM
      - run: sudo apt-get update
      #ssh 비밀번호 입력을 위해 sshpass를 설치한다.
      - run: sudo apt-get install sshpass
      - run: sshpass -p "rkdtjrwn0!" ssh -o StrictHostKeyChecking=no rkdtjrwn0722@34.64.xxx.xxx "./test.sh $CIRCLE_BUILD_NUM"

 

위 코드에서 중요한점은  몇가지 기억!!

 

1) gradle 빌드를 하고 나면 빌드된 jar파일의 위치를 설정

  - store_artifacts:
          path: build/libs

 

2) docker 를 사용하기 위해 docker를 설치해야함.

- setup_remote_docker:
          version: 19.03.13

3) 마지막으로 docker push 된 이미지를 쿠버네티스의 pod에 업데이트를 해야하기위해 ssh로 test.sh 라는 

   테스트 배포 shell 을 실행한다.

 

 

test.sh 파일 내용이다.

#!/bin/bash 
echo "image change start"
kubectl set image deployment/my-react-app my-react-app=kangzu8743/apitest:v$1
echo "deploy success"
kubectl get pods
exit 0

 

위와같이 간단하다. kubectl set imagse를 하게 되면 쿠버네티스는 기본 설정인 Rolling Update를 하게 된다.

 

무중단 배포 방식에는 여러가지가 있겠지만 . 쿠버네티스는 기본이 Rolling update이다. 

 

1) Rolling update란? 단순히 말하면

기존 v1 pod =3 , 신규 v2 pod = 0

기존 v1 pod = 2 신규 v2 pod =1

기존 v1 pod = 1 신규 v2 pod =2

기존 v1 pod = 0 신규 v2 pod =3

 

이런 식으로 기존 pod와 신규 pod가 잠깐이지만 공존하게되는 방식을 말한다. 

 

실제 이렇게 배포된 api를 소스 변경과 git hub push 까지해서 반영 되는지 확인해 보자.

 

 

기존 API를 호출하면 아래와 같이 Name !!! 으로 데이터가 나오는것을 확인.

 

 

이제 Name!!! 부분을 이름변경 으로 해서 소스를 push해보자.

 

위 그림에서 보면 소스를 push하게 되면 circleci의 Dashboard 화면에서 처럼 build 가 실행되는것을 확인 할 수 있다.

 

빌드과 완료 되면 쿠버네티스 pod에 이미지가 잘 변경 되었는지 확인

 

rkdtjrwn0722@k8s-master:~$ kubectl describe pod my-react-app-85bf656594-hn5tl
Name:         my-react-app-85bf656594-hn5tl
Namespace:    default
Priority:     0
Node:         k8s-worker1/10.178.0.18
Start Time:   Wed, 06 Jan 2021 09:04:44 +0000
Labels:       app=my-react-app
              pod-template-hash=85bf656594
Annotations:  <none>
Status:       Running
IP:           10.244.1.40
IPs:
  IP:           10.244.1.40
Controlled By:  ReplicaSet/my-react-app-85bf656594
Containers:
  my-react-app:
    Container ID:   docker://f35ce1033c7ac8a0f1e7f1f74b1218ba70597a43f7861ee9b5aea7cc85608c0d
    Image:          kangzu8743/apitest:v53
    Image ID:       docker-pullable://kangzu8743/apitest@sha256:c11f43b6e773ac3e74e99d8a614c2a5ed982cd3696286583a0cd6bc0bd16b6fb
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 06 Jan 2021 09:04:48 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-q6zjq (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-q6zjq:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-q6zjq
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  63s   default-scheduler  Successfully assigned default/my-react-app-85bf656594-hn5tl to k8s-worker1
  Normal  Pulling    62s   kubelet            Pulling image "kangzu8743/apitest:v53"
  Normal  Pulled     59s   kubelet            Successfully pulled image "kangzu8743/apitest:v53" in 2.597116473s
  Normal  Created    59s   kubelet            Created container my-react-app
  Normal  Started    59s   kubelet            Started container my-react-app
rkdtjrwn0722@k8s-master:~$ 

 

위 pod 내용을 보면 image 부분이 v53으로 변경된것을 확인.

 

실제 데이터가 변경되었는지 api를 확인해 보자.

 

위 그림처럼 API가 변경된것을 확인 할 수 있다.

 

서버 중단없이 pod가 자연스럽게 Rolling 되는것을 kubectl get pods 로 실시간 확인이 가능하다.

 

여기까지 무중단 배포에 대해 간단히 공부해 봤다. 

 

개발단계에서는 아무 유용해 보이지만 운영환경에서는 고민을 해봐야 하는 문제가 몇가지 생길것 같다.