콘텐츠로 이동

2023 01 18

2023-01-18

배포 전략 & 쿠버네티스

  • 배포 전략
    • Rolling Update
      • 점진적으로 교체하는 방식
      • 배포/롤백 둘 다 조금 느림
    • Blue/Green
      • 기존 Blue, 신규 Green
      • 로드밸런서가 포인팅 하는 것 싹 옮겨줌
      • 즉각적인 배포
      • 2배의 리소스를 투입할 수 있는 여건이 필요
    • Canary
      • 일부 서버에서 단계적으로 트래픽 전환
      • 실시간으로 새로운 버전 테스트 가능
      • 트래픽 전환으로 빠른 롤백 가능
  • CI/CD
    • 어플리케이션을 짧은 주기로 사용자에게 전달하는 것
    • CI: Continuous Integration
    • CD: Continuous Delivery / Deployment
    • 배포 파이프라인 예시
      • start -> build image -> deliver image to image registry -> update tag -> argocd -> sync -> merge branch
  • Docker
    • 컨테이너 기반의 오픈소스 가상화 플랫폼
    • 환경에 상관없이 어플리케이션 구축/테스트/배포
    • Docker Container
      • 격리된 공간에서 프로세스가 동작함
      • 여러 Container가 동일한 머신에서 실행 가능하며, 커널을 공유함
      • Container 실행에 필요한 파일 및 설정값을 포함함
      • Container는 이미지로 구동
    • Docker image 만드는 법
      • Dockerfile: 자체 DSL 언어를 사용하기
      • jib: Java 어플리케이션을 간단하게 이미지화 할 수 있음
        • Gradle 플러그인으로 사용해보자
      • docker build를 통해 Docker daemon이 레지스트리로 부터 이미지를 가져오고, 이미지를 만듦
      • 만들어진 이미지를 통해 Docker run으로 컨테이너로 띄움
  • 쿠버 개요
    • 쿠버네티스
      • 2014년 구글에서 만든 오픈소스 컨테이너 오케스트레이션
    • Cluster (상위 개념)
      • 어플리케이션 컨테이너를 실행하기 위한 일련의 노드 머신
      • 컨트롤 플레인 및 하나 이상의 노드를 포함하고 있음
      • YAML 파일로 원하는 상태를 지정해주세요
    • 컨트롤 플레인 (Master Node)
      • 어느 어플리케이션을 실행하고 어플리케이션이 어느 컨테이너 이미지를 사용할지 원하는 상태로 유지 관리
      • 컨트롤 플레인은 etcd, controller manager, scheduler, kube apiserver로 이루어짐
        • etcd <-> kube apiserver
        • scheduler <-> kube apiserver
        • controller manager <-> kube apiserver
      • scheduler: 어느 Pod를 어느 Node에 배치할지
      • etcd: 설정값들의 저장소
      • controller manager: 리소스 매니저, Pod 관리자
      • kube apiserver: Node와 소통하는 역할
    • 노드 (Worker Node)
      • 어플리케이션과 워크로드를 실제로 실행
      • 컨테이너는 반드시 Pod안에서 구동됨
      • Node안에 보통 하나의 Pod를 담아 구동시킴
      • 노드는 pod, kubelet으로 구성됨
      • pod: 컨테이너를 구동시킴
      • kubelet: 마스터 노드, 다른 워커 노드와 통신하는 역할 수행
  • 쿠버 구성 요소
    • Object: 템플릿을 통해 리소스의 바라는 상태를 정의해두면 이를 바탕으로 오브젝트 생성/삭제
      • Namespace: 클러스터 내부를 논리적인 단위로 구분해 사용
      • Pod: 쿠버네티스에서 컨테이너가 실행되는 최소 단위
      • Service
    • Controller: Pod 관리하는 역할
      • ReplicaSet: Self-Healing, 가용성 보장
      • Deployment: 무상태 어플리케이션 배포시 사용
      • DaemonSet: 모든 노드에 동일한 파트 실행시 사용
      • StatefulSet: 상태 있는 파드 관리
      • Job: 일회성 작업
      • Cronjob: 주기적 작업
    • Service: Pod가 생성/삭제 되면서 IP 변경될 수 있는데, 이를 고정된 주소로 박아버리는 것을 도와줌
      • ClusterIP: 클러스터 내부에서 접근 가능한 IP 제공
      • NodePort: 노드에 노출되어 외부에서 접근 가능한 서비스
      • LoadBalancer: 외부 로드밸런서 pod에 연결
    • Ingress: 클러스터 내부로 접근하는 요청 어찌 처리할지 정의해둔 규칙

Scala 타입

  • 참고: https://docs.scala-lang.org/tour/unified-types.html
  • Scala 타입 계층
    • Any: 모든 타입의 슈퍼 클래스 (equals, hashCode, toString 등 정의되어있음)
      • AnyVal: Value 타입을 나타냄
        • Double
        • Float
        • Long
        • Int
        • Short
        • Byte
        • Unit: 아무런 의미없는 단일 타입 ()로 나타냄, 무조건 리턴타입 있어야해서 도입함
        • Boolean
        • Char
      • AnyRef: Ref 타입을 나타냄. Value가 아닌 친구는 모두 AnyRef의 서브타입. (java.lang.Object와 동일)
        • List
        • Option
        • User Class
          // Any의 하위타입을 집어넣을 수 있음
          val list: List[Any] = List(
            "a string",
            732,
            'c',
            true,
            () => "hello"
          )
          
          list.foreach(element => println(element))
          
          a string
          732
          c
          true
          <function>
          
  • Scala 타입 캐스팅

    • Byte -> Short -> Int -> Long -> Float -> Double
    • Char -> Int
    • 정상적인 타입 변환 방향
      val x: Long = 987654321
      val y : Float = x.toFloat // 9.8765434E8 (부동 소수점으로 인한 손실 발생 가능)
      
      val face: Char = '😊'
      val number: Int = face // 9876
      
    • 비정상적인 타입 변환 방향
      val x: Long = 987654321
      val y: Float = x.toFloat
      val z: Long = y // Float -> Long 불가
      
  • **Nothing & Null
    • Nothing
      • 모든 타입의 subtype (bottom type)
      • 예외 발생, 프로그램 종료, 무한루프 돌 때 Nothing을 씁니다
      • 평가되지 않는 식을 표현할 때(?) 쓰는 듯 합니다.
      • 아직 잘 모르겠음
    • Null
      • AnyRef 밑에 있는 subtype
      • Scala 코드에선 딱히 쓰실 필요 없고,
      • 다른 JVM 언어의 호환을 위해 Null 타입을 만들어두긴 했어요
      • Null 대신 쓸 수 있는거 마련되어 있어요

Scala 클래스

  • 참고: https://docs.scala-lang.org/tour/classes.html
  • class 정의하기

    • class 키워드로 만들어주세요
    • class 이름은 대문자로 시작해주세요
      class User
      val user1 = new User
      val user2 = User()
      val user3 = new User()
      
    • Point 클래스 만들어보기
      // 4개의 멤버 : x, y, move, toString
      // 기본 생성자가 클래스 시그니처에 붙어서 정의됨
      class Point(var x: Int, var y: Int) {
      
        def move(dx: Int, dy: Int): Unit =
          x = x + dx
          y = y + dy
      
        override def toString: String =
          s"($x, $y)"
      }
      
      val point1 = new Point(2, 3)
      point1.x // 2
      println(point1) // (2, 3)
      
  • 생성자
    • 생성자에 기본값을 줄 수 있어
      class Point(var x : Int = 0, var y : Int = 0)
      
      val origin = Point() // (0, 0)
      val point1 = Point(1) // (1, 0)
      val point2 = Point(y = 2) // (0, 2)
      
  • Private Member와 게터/세터

    • default public
    • 세터 문법이 독특함
      • _= : getter로 쓴 친구의 이름과 동일한 이름으로 지정해야해
        class Point {
          private var _x = 0
          private var _y = 0
          private val bound = 100
        
          def x = _x
          def x_= (newValue : Int) : Unit = {
            if (newValue < bound) {
              _x = newValue
            } else {
              printWarning
            }
          }
        
          def y = _y
          def y_= (newValue : Int) : Unit = {
            if (newValue < bound) {
              _y = newValue
            } else {
              printWarning
            }
          }
        
          private def printWarning = println("WARNING: OUT OF BOUNDS")
        }
        
    • 기본 생성자의 인자는 val/var로 지정해서 생성할 수 있어. 다만 val/var 써주면 public이 기본이긴 해
      • 하지만 val이 불변이니, 세터로 주입할 생각 X
        class Point(val x: Int, val y: Int)
        
        val point = Point(1, 2)
        point.x = 3 // Error!
        
    • 그냥 아무것도 안쓰면, val + private이 기본이야!
      class Point(x: Int, y: Int)
      val point = Point(1, 2)
      point.x // Error!
      

Scala 파라미터

  • Default Parameter 주의할 점
    • 메서드에 디폴트 파라미터를 가진다면 애매한 상황이 발생할 수 있어
    • 따라서 스칼라는 (같은 이름 + 디폴트 파라미터) 에 대응되는 메서드를 만들 수 없어
    • 아래 상황에서 A.func() 어떤거 호출할지 모르겠지?
      object A {
        def func(x: Int = 34): Unit
        def func(y: String = "abc"): Unit
      }
      
  • Argument Parameter에 이름 지정해주기
    • 메서드 호출시, 이름이 지정된 파라미터는 순서와 상관없이 있음
    • 그러나,,, 어떤건 이름이 정해져있고, 어떤건 이름이 없다면...
      • 이름 없는 아규먼트가 무조건 먼저와야 하며, 해당 이름없는 친구는 메서드 시그니처 순서대로 투입됨
        def printName(first: String, last: String): Unit = println(first + " " + last)
        
        printName("John", "Smith") // "John Smith"
        printName(first = "John", last = "Smith") // "John Smith"
        printName(last = "Smith", first = "John") // "John Smith"
        
        printName(last = "Smith", "John") // Error!!
        

Scala Trait

  • Trait 사용법

    • trait 예약어
    • generic, abstract method를 통해서 확장성을 노려보세요
      trait Iterator[A] {
        def hasNext: Boolean
        def next(): A
      }
      
    • extend, override를 통해 구현해주세요
    • 제네릭이 있다면 확정지어주세요
      trait Iterator[A] {
        def hasNext: Boolean
        def next(): A
      }
      
      class IntIterator(to: Int) extends Iterator[Int] {
        private var current = 0
        override def hasNext: Boolean = current < to
        override def next(): Int = {
          if (hasNext) {
            val t = current
            current += 1
            t
          } else 0
        }
      }
      
      val iterator = new IntIterator(10)
      iterator.next()
      iterator.next()
      
  • Subtyping
    • trait이 요구되는 곳에 trait 서브타입을 대입하여 사용할 수 있다
      import scala.collection.mutable.ArrayBuffer
      
      trait Pet {
        val name: String
      }
      
      class Cat(val name: String) extends Pet
      class Dog(val name: String) extends Pet
      
      val dog = new Dog("Harry")
      val cat = new Cat("Sally")
      
      val animals = ArrayBuffer.empty[Pet]
      animals.append(dog)
      animals.append(cat)
      animals.forEach(pet => println(pet))
      

Scala Tuple

  • Tuple 개요
    • 튜플은 불변이다!
    • 메서드에서 여러개를 리턴할 때 유용함
      val ingredient = ("Sugar", 25) // (String, Int)
      
  • Tuple Element 접근
    • _1, _2 로 인덱스에 접근가능
    • 타입 패턴 매칭이 가능함. 쪼개서 나눠담는게 가능
      val ingredient = ("Sugar", 25)
      val (name, quantity) = ingredient
      
      println(name) // "Sugar"
      println(quantity) // 25
      println(ingredient._1) // "Sugar"
      println(ingredient._2) // 25
      
      val numPairs = List((2, 5), (3, -7), (20, 56))
      
      for ((a, b) <- numPairs) {
        println(a * b)
      }
      

Scala Mixin

  • Mixin으로 클래스를 합성하기
    • Mixin은 클래스를 합성할 때 사용할 수 있는 trait이다
    • 아래 예시에서 D는 B라는 부모클래스를 상속받았으며, C 믹스인을 가진다
      • 클래스는 하나만 상속받을 수 있지만, 믹스인은 여러개 받을 수 있음
        abstract class A {
          val message: String
        }
        
        class B extends A {
          val message = "I'm an instance of class B"
        }
        
        trait C extends A {
          def loudMessage = mess
        }
        class D extends B with C
        
        val d = new D
        println(d.message)
        println(d.loudMessage)
        
  • Mixin 예시
    abstract class AbsIterator {
      type T
      def hasNext: Boolean
      def next(): T
    }
    
    class StringIterator(s: String) extends AbsIterator {
      type T = Char
      private var i = 0
      def hasNext = i < s.length
      def next() = {
        val ch = s charAt i
        i += 1
        ch
      }
    }
    
    trait RichIterator extends AbsIterator {
      def foreach(f: T => Unit): Unit = while (hasNext) f(next())
    }
    
    class RichStringIter extends StringIterator("Scala") with RichIterator
    val richStringIter = new RichStringIter
    richStringIter.foreach(println)
    

Mixin?

  • 참고: https://ko.wikipedia.org/wiki/%EB%AF%B9%EC%8A%A4%EC%9D%B8
  • 설명
    • 다른 클래스의 부모클래스가 되지 않으면서, 다른 클래스에서 사용할 수 있는 메서드를 포함하는 클래스
    • 코드 재사용성을 높이고, 다이아몬드 문제 제거
    • 메서드가 포함된 인터페이스로 볼 수 있음

30분 스칼라

  • 익명함수
    • 세상에 함수를 이렇게 정의할 수 있어
      def add1(x: Int, y:Int) = x + y
      def add2 = (x:Int, y:Int) => x + y
      def add3:(Int, Int) => Int = _ + _ // _를 통해 익명의 매개변수가 어디로 갈지 알려줘야 해
      def add4 = (_ + _):(Int, Int) => Int
      
  • 중첩 for문
    • 아니 뭔 for 문을 이따구로;;
      for (a <- 1 to 3; b <- 10 to 12) {
        println(a, b)
      }
      
  • 클래스
    • 자바 스타일과 스칼라 스타일의 차이를 한 번 보자보자 어디보자
      class JPerson() {
        var _name: String = null
        def this(_name: String) = {
          this()
          this._name = _name
        }
      
        // 스칼라 스타일 Setter
        def name_=(_name:String) = this._name = _name
        // 자바 스타일 Setter
        def setName(_name:String) = this._name = _name
      
        // 스칼라 스타일 Getter
        def name = this._name
        // 자바 스타일 Getter
        def getName() = name
      }