https://debezium.io/blog/2019/02/05/debezium-0-9-0-final-released/

(구글 번역기를 돌리고 중요한 부분만 발췌 및 정리)

직접 debezium을 사용해보고 테스트 한 후, 이 부분을 안 읽고 넘어가는게 좋은 것 같다. 

 

*Debezium이란 무엇인가?*
Debezium은 데이터베이스에서 로우 레벨의 변경 사항을 캡처하여 애플리케이션이 변경 내용을보고 응답 할 수 있게 하는 분산 서비스 집합입니다. Debezium은 각 데이터베이스 테이블에 커밋 된 모든 로우 레벨 변경 내용을 트랜잭션 로그에 기록합니다. 각 애플리케이션은 관심있는 트랜잭션 로그를 읽기 만하면 모든 이벤트가 발생한 순서와 동일한 순서로 표시된다.

 

*"Debezium"이라는 이름은 어디에서 왔는가?*
이름은 여러 데이터베이스의 약어와 같은 " DB "와 주기율표의 많은 요소 이름에 사용되는 " -ium "접미사 의 조합이다.

 


*변경 데이터 캡처(CDC)란 무엇입니까?*
변경 데이터 캡처는 다른 소프트웨어가 데이터 변경 사항에 응답할 수 있도록 데이터의 변경 사항을 모니터링하고 캡처하는 용어이다.  Debezium은 기본적으로 다양한 데이터베이스 시스템 모니터링을 지원 하는 현대적이고 분산 오픈 소스 변경 데이터 캡처 플랫폼이다.

 


*Debezium이 모니터링 할 수있는 데이터베이스는 무엇입니까?*
MySQL 데이터베이스 서버 , MongoDB 복제 세트 또는 샤드 클러스터 , PostgreSQL 서버, Oracle Server (XStream 기반) 용 Debezium 커넥터 와 Debezium 0.8 / 0.9부터의 미리보기 버전으로 출시된 SQL Server 데이터베이스가 있다.

 


*Debezium의 용도는?*
Debezium의 주요 용도는 데이터베이스의 데이터가 변경 될 때마다 애플리케이션이 거의 즉시 응답 할 수 있게 하는 것이다. 애플리케이션은 추가, 업데이트, 삭제 이벤트로 무엇이든 할 수 있다. 이벤트를 사용해 캐시에서 데이터를 제거 시기를 알 수 있다. 또한 데이터로 검색 색인을 업데이트할 수 있다. 하나 이상의 모바일 장치에 푸시 알림을 보낼 수 있다. 

 


*Debezium이 분산 시스템인 이유는 무엇인가?*
Debezium은 결함과 실패를 허용하도록 설계되었고 수행하는 유일한 방법은 분산 시스템을 사용하는 것이다. Debezium은 모니터링 프로세스 또는 커넥터를 여러 시스템에 분산시킬 수 있고 문제가 발생하면 커넥터를 다시 시작할 수 있다.

 

*애플리케이션이 단일 데이터베이스를 직접 모니터링 할 수 있는가?*
예. Debezium 커넥터를 내장할 수 있다.

 


*Debezium 플랫폼 구성은?
Debezium 시스템은 여러 부분으로 구성되어 있다. Apache Kafka 브로커 클러스터는 Debezium이 모든 이벤트를 기록하고 모든 이벤트는 모든 이벤트를 사용하는 영구, 복제, 파티션된 트랜잭션 로그를 제공한다. 

각 Debezium 커넥터는 하나의 데이터베이스 클러스터 및 서버를 모니터링한다. 

모든 커넥터는 이벤트와 다른 정보를 주키퍼가 아닌 Apache Kafka에 저장한다. Apache Kafka는 각 테이블의 이벤트를 별도 토픽(topic) 유지, 복제, 분할한다. 

 

*Debezium은 원본 데이터베이스에 어떤 영향을 주는가?*
Debezium이 모니터링하기 전에 데이터베이스를 설정할 작업이 있다. 예를 들어, MySQL 서버는 로우 레벨의 binlog를 사용하고 binlog를 읽는 권한을 가진 사용자를 하나 생성해 설정해야 한다. Debezium 커넥터는 권한 있는 사용자를 포함하여 올바른 정보로 구성되야 한다.

Debezium 커넥터는 원본 데이터베이스 내부에 정보를 저장하지 않는다. 그러나 커넥터를 실행하면 원본 데이터베이스에 추가 부하가 발생할 수 있다.

 


*데이터베이스의 이벤트는 어떻게 구성해야 하는가?*
대부분의 커넥터는 단일 데이터베이스 테이블에 대한 모든 이벤트를 단일 토픽에 저장한다. 또한 토픽 내의 모든 이벤트는 완전히 순서화되어 모든 이벤트의 순서를 유지한다. (에러가 발생해 이벤트가 복제되더라도 모든 이벤트를 적용한 후 최종 결과는 그대로 유지된다)


*순서


Kafka Connect 서비스는 커넥터의 이벤트를 직렬화하여 Kafka에 저장한다. JSON 변환기는 매우 일반적이며 매우 간단하지만 전체 이벤트 정보를 직렬화 할 수밖에 없다. 따라서 JSON에 표시된 이벤트는 실제로 길고 크다.

Confluent의 Avro Converter 는 두 가지 면에서 뛰어나다. 첫째, 커넥터의 스키마를 Apache Avro 스키마로 변환하므로 페이로드를 매우 컴팩트한 바이너리 포맷으로 직렬화할 수 있다. 둘째, 연속적으로 많은 이벤트가 동일한 스키마(Avro 스키마)를 사용하고 별도의 스키마 레지스트리에 이러한 Avro 스키마를 등록함으로써 각 직렬화된 이벤트에 스키마 버전의 작은 식별자를 배치 할 수 있다. Avro Converter와 Schema Registry는 함께 작동하여 각 스키마의 히스토리를 추적할 수 있다.

한편, 동일한 Avro Converter는 컴팩트 이진 포맷 이벤트를 디코딩하고, 해당 메시지에서 사용하는 스키마 버전의 식별자를 읽습니다. 스키마 버전이 Schema Registry에서 Avro 스키마를 다운로드하는 것을 아직 보지 못한 경우, 마지막으로 Avro 스키마를 사용하여 이벤트의 바이너리 페이로드를 디코딩한다. 반복적으로 많은 이벤트가 동일한 스키마(Avro 스키마 버전)를 공유하므로 변환기는 원시 압축 이벤트를 소비자가 예상하는 동일한 스키마와 페이로드로 간단하게 디코딩할 수 있다.

 


*Confluent의 Avro Converter는 어떻게 사용합니까?*
Confluent의 Avro Converter 와 Debezium을 함께 사용할 수 있다. Avro Converter는 훨씬 똑똑하고 기본적으로 사용되는 JSON 변환보다 훨씬 더 콤팩트 이벤트 메시지를 직렬화한다.

Debezium 커넥터를 Kafka Connect 작업자 서비스에 배포하는 경우 Avro 변환기 JAR을 사용할 수 있는지 확인하고 Avro 변환기를 사용하도록 작업자 서비스를 구성한다. 예를 들어 변환기를 Confluent Schema Registry로 지정해야 한다. 그다음 Debezium 커넥터 (또는 실제로 다른 Kafka Connect 커넥터)를 작업자 서비스에 배치하기만 하면 된다. 

 



*Debezium이 중지되거나 충돌하면 어떻게 되는가?*


데이터베이스의 변경 이벤트를 사용하기 위해 애플리케이션은 Kafka 브로커에 연결하고 데이터베이스와 관련된 항목에 대한 모든 이벤트를 사용하는 Kafka 소비자를 생성한다. 소비자는 주기적으로 각 토픽의 위치(일명 오프셋)를 저장하도록 설정된다. 애플리케이션이 정상적으로 종료되고 소비자를 종료하면 소비자는 각 항목의 마지막 이벤트에 대한 오프셋을 저장한다. 나중에 애플리케이션이 재시작되면 소비자는 해당 오프셋을 검색하여 각 항목의 바로 다음 이벤트를 읽는다. 따라서 정상적인 운영 시나리오에서 애플리케이션은 모든 이벤트를 정확히 한 번(at once) 본다.

애플리케이션이 예기치 않게 중단되면 재시작할 때 애플리케이션의 사용자는 각 항목에 대해 마지막에 저장된 오프셋을 조회하고 각 항목의 마지막 오프셋에서 이벤트를 시작한다. 대부분의 경우 애플리케이션은 종료 이전에 본 것과 동일한 이벤트를 보고(그러나 오프셋을 저장한 후) 아직 보지 못한 이벤트가 뒤따른다. 따라서 애플리케이션은 모든 이벤트를 최소한 한 번 봅니다. 애플리케이션은 오프셋를 더 자주 저장한다면 클라이언트의 성능과 처리량에 부정적인 영향을 미친다.

Kafka 소비자는 각 토픽에서 가장 최근의 오프셋으로 연결하고 읽기를 시작하도록 구성할 수 있다. 이로 인해 이벤트가 누락될 수 있다. 

 


*Debezium이 멈추거나 종료될 때 어떻게 해야 하는가?


Debezium의 동작은 중단된 컴포넌트에 따라 다르다. Kafka 브로커 중 상당수가 정지하거나 종료된다면 각 항목 파티션이 최소 동기화 개수가 복제본 개수보다 적을 경우 커넥터와 애플리케이션이 차단된다. 카프카 브로커를 재시작하거나 새로운 브로커를 새로 얻어야 동작한다. 따라서 동기화된 복제본의 최소 수는 가용성에 매우 큰 영향을 미치며 일관성을 유지하기 위해서는 항상 1이상(3이 아닌 경우)이어야 한다.

Kafka Connect 서비스는 각 커넥터의 위치와 오프셋을 주기적으로 저장하도록 설정된다. 클러스터에 있는 Kafka Connect 서비스 인스턴스 중 하나가 정상적으로 중지되면 해당 프로세스에서 실행중인 모든 커넥터가 정상적으로 중지되며(즉, 모든 위치와 오프셋이 저장된다) 해당 커넥터가 다른 Kafka Connect 서비스 인스턴스에서 다시 시작된다. 커넥터가 재시작되면 중복 이벤트가 기록되지 않고 중단된 부분부터 정확하게 이벤트를 계속 저장한다.

Kafka Connect 서비스 클러스터에서 실행 중인 커넥터 중 하나가 정상적으로 중지되면 현재 작업을 완료하고 Kafka에서 최신 위치 및 오프셋을 저장한다. 다운 스트림 애플리케이션은 새로운 이벤트가 추가될 때까지 기다릴 것이다.

클러스터의 Kafka Connect 서비스 인스턴스 중 예기치 않게 충돌이 발생 하면 충돌한 프로세스에서 실행중인 모든 커넥터가 동일한 클러스터의 다른 Kafka Connect 서비스 인스턴스에서 다시 시작된다. 그러나 이러한 커넥터가 재시작되면 충돌이 발생하기 전에 커넥터에서 마지막으로 기록한 오프셋에서 시작하여 데이터베이스의 이벤트를 기록하기 시작한다 . 이는 새로 재시작 한 커넥터가 충돌 이전에 이전에 기록한 것과 동일한 이벤트를 기록 할 수 있음을 의미하며 이러한 중복은 항상 다운 스트림을 스트리밍하는 애플리케이션에서 볼 수 있다.

 


*모니터링되는 데이터베이스가 중지되거나 종료되면 어떻게 될까?*
Debezium이 모니터하는 데이터베이스가 중지되거나 종료하면 Debezium 커넥터가 통신을 다시 설정하려 한다. Debezium은 정기적으로 커넥터의 위치와 오프셋을 Kafka에 저장하기에 커넥터가 통신을 설정하면 커넥터는 마지막으로 저장된 위치와 오프셋에서 계속해서 읽어야 한다.

 


*애플리케이션에서 소비하려 할 때 중복 이벤트가 발생하는 이유는 무엇인가?*
모든 시스템이 명목상으로 실행 중이거나 일부 또는 모든 시스템이 정상적으로 종료되면 애플리케이션에서 모든 이벤트를 정확히 한 번 볼 수 있다 . 그러나  잘못되면 한 번 이상 이벤트를 볼 수 있다 .

Debezium의 시스템이 충돌하면 항상 마지막 위치/오프셋을 저장할 수있는 것은 아니다. 다시 시작되면 마지막으로 있었던 것으로 시작하여 복구되고 소비하는 애플리케이션은 항상 모든 이벤트를 볼 수 있지만 복구 중 중복 된 메시지가 나타날 수 있다.

또한 네트워크 오류로 Debezium 커넥터가 쓰기 확인을받지 못하게되어 동일한 이벤트가 한 번 이상 저장된다.


*카프카 란 무엇인가?*
Apache Kafka 는 모든 메시지를 복제, 파티션 분할, 완전히 정렬 된 트랜잭션 로그에 저장하는 빠르고 확장 가능하고 내구성이 뛰어난 분산 메시징 시스템이다. 소비자는 로그에서 소비자의 오프셋를 ​​추적하고 다른 모든 소비자들과 마찬가지로 이 오프셋을 제어 할 수 있다. 즉, 특정 소비자는 로그의 처음부터 시작할 수 있고 다른 사용자는 가장 최근에 저장된 메시지를 따라 잡을 수 있는다. 카프카는 동적 브로커 집단으로 운영된다. 각 로그 파티션은 여러 브로커에 복제되므로 모든 브로커가 실패하더라도 클러스터에 여전히 여러 복사본이 존재한다.

Debezium 커넥터는 모든 이벤트를 Kafka 클러스터에 기록하고 애플리케이션은 Kafka를 통해 이벤트를 사용한다.

*카프카 커넥트 란 무엇입니까?*
Kafka Connect는 Apache Kafka와 다른 시스템간에 데이터를 확장 가능하고 안정적으로 스트리밍하기 위한 프레임워크이다. Kafka 커뮤니티에 최근에 추가된 기능으로, 대량의 데이터를 Kafka 안팎으로 이동시키는 커넥터를 간단하게 정의할 수 있고 프레임 워크는 커넥터의 오프셋을 올바르게 저장하는 데 많은 노력을 기울인다. Kafka Connect 서비스에는 커넥터 관리 및 배포를위한 RESTful API가 있습니다. 서비스는 클러스터 될 수 있으며 커넥터가 항상 실행 중인지 확인하여 클러스터에 커넥터를 자동으로 분배한다.

Debezium은 Kafka Connect 프레임 워크를 사용한다. Debezium의 모든 커넥터는 Kafka 커넥터 소스 커넥터 이며, Kafka Connect 서비스를 사용하여 배치 및 관리 할 수 ​​있다.



출처 : https://debezium.io/docs/faq/

Posted by '김용환'
,

트위터는 2013년부터

루비온 레일즈의 모노리식 구조에서 SOA(service oriented architecture)로 점진적인 변화를 주고 있다.

현재는 99.9%로 레일즈를 걷어냈고 스칼라(finagle, finatra)를 사용해 개발하고 있다.

 

 

https://blog.twitter.com/engineering/en_us/a/2013/new-tweets-per-second-record-and-how.html

 

New Tweets per second record, and how!

New Tweets per second record, and how!

blog.twitter.com

https://blog.twitter.com/engineering/en_us/a/2011/finagle-a-protocol-agnostic-rpc-system.html

 

Finagle: A Protocol-Agnostic RPC System

Finagle: A Protocol-Agnostic RPC System

blog.twitter.com

 

 

thrift idl은 다음과 같다. scrooge를 사용해서 약간 특이하게 되어 있다. 

namespace java idl.thrift
#@namespace scala idl.thrift


service HelloService {
  string hi();
}

finangle server는 간단하게 아래와 같이 구현할 수 있다.

 

package example

import com.twitter.finagle.Thrift
import com.twitter.util.{Await, Future}
import idl.thrift.HelloService

object Server {

  class HelloImpl extends HelloService.MethodPerEndpoint {
    def hi(): Future[String] = Future.value("Hello World")
  }

  val server = Thrift.server.serveIface("localhost:8080", new HelloImpl)

  def main(args: Array[String]): Unit = {
    println("Starting thrift server(8080)...")
    Await.result(server)
  }
}

 

 

클라이언트는 다음과 같이 사용할 수 있다. 

package client

import com.twitter.finagle.Thrift
import com.twitter.util.Await
import idl.thrift.HelloService

object ScalaEchoClient {
  def main(args: Array[String]): Unit = {
    println("Starting Scala client...")

    val methodPerEndpoint: HelloService.MethodPerEndpoint =
      Thrift.client.build[HelloService.MethodPerEndpoint]("localhost:8080")

    val response = methodPerEndpoint.hi()
    response onSuccess {
      result: String => println(result)
    }

    Await.result(response)
  }

 

Posted by '김용환'
,


nginx에 json 로그가 없을 것이라 생각했는데.. 찾아보니 json을 제공한다. 
http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format

그냥 지원하지 않을 것 같은 느낌인데.. 사실은 이미 지원하고 있다. 


Syntax:    log_format name [escape=default|json|none] string ...;
Default:    
log_format combined “...“;
Context:    http

 


log_format json_combined escape=json
 ‘{’
   ‘“time_local”:“$time_local”,’
   ‘“remote_addr”:“$remote_addr”,’
   ‘“remote_user”:“$remote_user”,’
   ‘“request”:“$request”,’
   ‘“status”: “$status”,’
   ‘“body_bytes_sent”:“$body_bytes_sent”,’
   ‘“request_time”:“$request_time”,’
   ‘“http_referrer”:“$http_referer”,’
   ‘“http_user_agent”:“$http_user_agent”’
 ‘}’;

 

 

access_log /var/log/nginx/access.log json_combined;

 


특히 기본 모듈에서 제공하는 기능이라 따로 모듈 추가를 하지 않아도 된다. 

 

 




Posted by '김용환'
,

 

 

https://knight76.tistory.com/entry/Spark-Streaming-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-DB%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%98%EB%8A%94-%EC%BD%94%EB%93%9C

 

[Spark] Streaming 데이터를 DB에 저장하는 코드

Spark에서 Streaming 데이터를 DB에 저장할 때. 일반적인 데이터 프레임에서 저장하는 방식을 사용할 수 없다. (만약 사용하면 streaming 데이터 프레임에서 그렇게 저장할 수 없다라는 에러가 나온다) 따라서 Sin..

knight76.tistory.com

 

 

이전 코드를 더 다듬어 DB 요청쪽 성능 효과를 얻는 코드를 두니, 참고하길 바란다.

 

 

jdbc 연결하는 클래스..

class JdbcSink(url: String, tablename: String) extends ForeachWriter[DeserializedFromKafkaRecord]{
  val driver = "com.mysql.cj.jdbc.Driver"
  var statement:Statement = _
  var connection:Connection  = _

  def open(partitionId: Long, version: Long): Boolean = {
    Class.forName(driver)
    connection = DriverManager.getConnection(url)
    this.statement = connection.createStatement()
    true
  }

  override def process(record: DeserializedFromKafkaRecord): Unit = {
    if (StringUtils.isEmpty(record.value)) {
      throw new IllegalArgumentException
    }

    val value = record.value.replace("'", "").replace("\"", "")
    //println("insert into " + tablename + "(value) values(\"" + value + "\")")
    statement.executeUpdate("insert into " + tablename + "(value) values(\"" + value + "\")")
  }

  override def close(errorOrNull: Throwable): Unit = {
    connection.close()
  }

 

jdbc 저장하는 JdbcSink 클래스를 object로 감싼 wrapper, 그래야 매번 DB 연객 인스턴스 생성이 없게 한다.

import org.apache.spark.sql.ForeachWriter
import streaming.KafkaAvroDemo._

object DBSink {

  val writer:ForeachWriter[DeserializedFromKafkaRecord] = new JdbcSink(sinkDBUrl, sinkTable)
  writer.open(0, 0)

}

 

실제 예시 코드이다. 

 

    val spark = SparkSession
      .builder()
      .appName("KafkaSparkStreaming")
      .config("spark.master", "local[2]")
      .getOrCreate()

    import spark.implicits._

    val ssc = new StreamingContext(spark.sparkContext, INTERVAL)

    val dataframe = spark.readStream
        .format("kafka")
        .option("kafka.bootstrap.servers", kafkaBrokers)
        .option("subscribe", kafkaTopic)
        .option("startingOffsets", "latest")
        .option("maxOffsetsPerTrigger", 20)
        .load()
        .map( x=> {
          val props = new Properties()
          props.put(AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistry)
          props.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, "true")
          val vProps = new kafka.utils.VerifiableProperties(props)
          val decoder = new KafkaAvroDecoder(vProps)
          val avroSchema = new RestService(schemaRegistry).getLatestVersion(kafkaTopic + "-value")
          val messageSchema = new Schema.Parser().parse(avroSchema.getSchema)

          DeserializedFromKafkaRecord(decoder.fromBytes(x.getAs[Array[Byte]]("value"),
            messageSchema).asInstanceOf[GenericData.Record].toString)
        }
        )

    val query = dataframe
      .writeStream
        .foreachBatch((batchDF, batchId) => {

          batchDF.foreachPartition(rows => {
            rows.foreach(row => {
              DBSink.writer.process(row)
              println(row)
            })
          })
        })
      .outputMode("append")
      .start()
    query.awaitTermination()

 

 

 

 

Posted by '김용환'
,

 

<마음에 드는 부분..>

고객센터를 자산으로 여기고 내재화했다.

그래서 이를 통해 고객이 무엇을 원하는지를 잘 파악해서 서비스에 반영했다.

 

 

일반 기업이 서비스가 물건을 사는 고객에게 제공하는 덤이나 공짜 선물 같은 부수적인 것으로 여긴다면, 자포스에게 서비스는 돈을 받고 파는 물건과 같고 브랜드를 알리고 고객의 충성도를 쌓기 위한 투자로 여겨진다.다시 말해 자포스는 '신발'을 파는 것이 아니라 '고객의 감동 체험'을 파는 것이다.

 

https://gsrealdesign.tistory.com/entry/자포스-닷컴-아마존은-왜-최고가에-자포스를-인수했나

 

자포스 닷컴 - 아마존은 왜 최고가에 자포스를 인수했나

www.zappos.com 은2009년 12억 달러(한화로 약 1조3천억 - GS SHOP의 2009년 매출 5천6백억) 매출을 올린 제화에서 시작해 의류, 가방, 액세서리 등으로 품목을 넓혀가고 있는 온라인 쇼핑몰 사이트다. 인터넷으..

gsrealdesign.tistory.com

 

 

Posted by '김용환'
,

이 책은 평범한 개발자를 위한 책으로서, 그리고 좋은 멘토와 같은 책이니 추천 5개짜리이다.

 

나는 첫 회사에서 다닐 때부터 느꼈던 "나는 모자란 사람이다" 라는 생각이 항상 있다.

능력이 부족한 사람이기에 쉽게 가능했고,

블로그도 그냥 내가 검색해서 찾기 위한 로깅으로 사용 중이긴 하지만, 부족하다라는 생각부터 시작한 것이다.

 

누군가에게 도움이나 된다면 참 좋겠다 생각하기도 했지만..

여전히 난 잘 모른다. 많이 물어보고 책을 보며 지금껏 살고 있다.

뛰어난 사람이 되기 보다는 그냥 개발자로서 버티면서 살아가는게 쉽지 않으면서 여기까지 온게 신기하고 하다.

 

'프로그래머의 길, 멘토에게 묻다' 책에서 "가장 뒤떨어진 이가 되라" 라는 내용이 참 와닿았다. 

내가 그렇게 일해왔기 때문일런지 모르겠다. 제대로 모른다고 생각해서 지금까지 살아남았으니..

"일하면서 성찰하기", "부숴도 괜찮은 장난감", "실패에서 배우라", "배운 것을 공유하라", "배운 것을 기록하라"도 내가 평상시에 추구하는 개발 태도이니. 비슷한 것 같기도 하다. 그렇다고 해서 미친듯이 개발을 추구하는 사람도 아니라서 지금까지 일하고 있는 것이 아닐까 싶기도 하다.

 

"지속적인 동기 부여"가 가장 어려운 부분인 것 같다. 왜 내가 개발을 해야 하나? 하는 질문은 꽤나 어렵기도 하다.

이 질문은 재미있어서 라고 답하기 어려운 부분이 있다. 내가 먼가 미친듯이 재미있어서 일하는 게 때로는 조직원의 성장을 파괴(조직원의 조직 이동, 질투..)하기도 하는 것을 느꼈다.

그래서 아마도 좋은 먹거리(좋은 성과)는 누군가에게 주고 나는 정리 안된 뭔가를 해야 하는 것에 동기 부여하려니 조금은 쉽지 않다.

그래도 이게 어쩌면 지속적인 동기 부여가 되기도 한다. 때로는 강하게 커뮤니케이션하는 나 자신에 괴리감에 있기도 하고..

 

신기술, 잘 모르는 프로그래밍 언어, 신선한 아키텍처에 대한 신선함, 부족함, 금전적인 부분이 동기부여가 되기도 한다. 

 

 

이 책이 주는 묘미는 좋은 선배가 얘기해 주는 멘토와 같다.

 

직장을 계속 다니며 다니는 평범한 개발자에게는 필요한 내용이 가득 담겨 있으니 보면 좋을 것 같다.

 

 

 

 

 

Posted by '김용환'
,

helm을 통해 kubernetes jenkins을 설치할 때. 주의해야 할 점이 있다.

 

 

 

jenkins 잡이 실행, 에러 정보가 모두 pods로 쌓인다. (Completed|Error)

 

 

처음에는 대수롭게 생각하지 않았는데. 이게 싸이면 결국 kubernetes 클러스터는 자원 부족으로 문제가 발생한다.

 

그래서 아래와 같은 커맨드로 종료하거나..

 

 

kubectl delete pod $(kubectl get pods | grep -E "Completed|Error" | awk '{print $1}') —force —grace-period=0

 

 

kubernetes job 등록해서.. 삭제하도록 해야 한다.

 

spec:
schedule: "*/30 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: kubectl-runner
image: wernight/kubectl
command: ["sh", "-c", "kubectl get jobs | awk '$4 ~ /[2-9]d$/ || $3 ~ 1' | awk '{print $1}' | xargs kubectl delete job"]
restartPolicy: Never

 

 

Posted by '김용환'
,

git 저장소를 옮길 때 branch, commit 모두 옮기는 방법이다. 

 

1.  다운 받고 싶은 git 저장소를 얻는다. (git 디렉토리를 보면 일반적으로 git clone한 것과 다르다. git의 메타 데이터를 읽는다)

$ git clone --bare https://github.com/knight76/ansiblebook

$ cd ansiblebook.git

$ ls -al

drwxr-xr-x  11 samuel.kim  staff  352 Jun 23 18:20 .
drwxr-xr-x  28 samuel.kim  staff  896 Jun 23 18:20 ..
-rw-r--r--   1 samuel.kim  staff   23 Jun 23 18:20 HEAD
drwxr-xr-x   2 samuel.kim  staff   64 Jun 23 18:20 branches
-rw-r--r--   1 samuel.kim  staff  176 Jun 23 18:20 config
-rw-r--r--   1 samuel.kim  staff   73 Jun 23 18:20 description
drwxr-xr-x  13 samuel.kim  staff  416 Jun 23 18:20 hooks
drwxr-xr-x   3 samuel.kim  staff   96 Jun 23 18:20 info
drwxr-xr-x   4 samuel.kim  staff  128 Jun 23 18:20 objects
-rw-r--r--   1 samuel.kim  staff  479 Jun 23 18:20 packed-refs
drwxr-xr-x   4 samuel.kim  staff  128 Jun 23 18:20 refs

 

 

2.  Organization와 저장소를 생성한다.

 

 

3. bare clone 한 디렉토리에서 새로 생성한 저장소에 mirror push를 수행 한다 

git push --mirror https://github.com/knight76/new_ansiblebook.git

 

Posted by '김용환'
,

 

 

https://dzone.com/articles/java-garbage-collection-3

 

자바 11 : Epsilon and the Z garbage collector (ZGC).
자바 12 : Shenandoah Garbage Collector (edited) 

큰 메모리를 기반의 자바 애플리케이션에 ZGC를 사용하면 g1 gc 보다 좋다고 한다. 

 

Posted by '김용환'
,

 

https://dzone.com/articles/build-trust-with-scrum

 

스크럼에 신뢰가 중요하다라는 내용의 글이다.

 

신뢰 부족하면 관계가 깨지고 투명성이 사라진다. 사람들은 방어하기 시작한다. 아무도 팀으로 일하거나 목표를 위해 협업하기를 원하지 않는다.



“누군가가 당신을 신뢰하기를 원한다면 먼저 신뢰해야 한다.“라고 말하거 누군가를 신뢰하기 위해 먼저 용감해야 한다고 말할 수 있다.
프로젝트 오너, 개발자들이 서로 신뢰해야 프로젝트는 성공해야 한다는 아주 기본적인 내용이다.

 

개발 뿐 일까. 모은 일이 깨지는 것은 다 신뢰 문제였단 것 같다. 

 

 

Posted by '김용환'
,