김창준 씨의 함께 자라기(애자일로 가는 길) 중 구글의 실험 내용을 정리한 아주 일부분만 발췌한다.



Oxygen Project : 구글이 관리자를 없애는 실험을 2002년에 실험했지만 결과는 좋지 않았다. 이에 인재 분석팀을 꾸려 뛰어난 관리자의 특징을 찾는 연구인 Oxgen Project를 2008년에 시작했다.


이후 Aristotle Project를 진행했고 2015년에 발표했다.

https://rework.withgoogle.com/print/guides/5721312655835136/



김창준씨가 중요하게 여긴 세 부분은 다음과 같다.


1. 팀에 누가 있는지(전문가, 내향/외향), 지능 등)보다 팀원들이 서로 어떻게 상호작용하고 자신의 일을 어떻게 바라보는지가 훨씬 중요했다.



2. 5가지 성공적 팀의 특징을 찾았는데, 그 중 압도적 높은 예측력을 보인 변수는 팀의 심리적 안전감(내 생각, 의견, 질문, 걱정, 실수가 드러났을 때 처벌받거나 놀림받지 않을 거라는 믿음)이었다.


3. 팀 토론 등 특별히 고안된 활동(gTeams execise)라 불리는 활동인데, 10분간 5가지 성공적인 팀의 특징에 대해 팀원들이 답하고 팀이 얼마나 잘하는 요약 보고서를 보고, 결과에 대해 면대면 토론을 하고 팀이 개선하게 자원(교육 등)을 제공하는 것이라 한다.





참고로..


외국의 좋은 IT회사에서는 관리자는 면대면 토론을 항상 진행하고 멘토링을 진행한다고 한다. 심리적 안정감을 계속 주는 활동을 진행한다고 한다. 



https://rework.withgoogle.com/print/guides/5721312655835136/ 



팀은 무엇인가?  데이터를 모으고 효율을 측정하는 방법, 효율적인 팀을 정의하는 방법이 있다.


The researchers found that what really mattered was less about who is on the team, and more about how the team worked together. In order of importance:







참고할만한다. 문화를 만드는데 중요한 것 같다..





Posted by '김용환'
,



Spark 데이터 프레임의 StatFunctions 패키지 함수 중 monotonically_increasing_id를 사용하면

데이터 프레임의 로우에 할당된 고유 ID를 출력한다.



import org.apache.spark.sql.functions.monotonically_increasing_id
df.select(monotonically_increasing_id()).show(5)



결과


+-----------------------------+

|monotonically_increasing_id()|

+-----------------------------+

|                            0|

|                            1|

|                            2|

|                            3|

|                            4|

+-----------------------------+

only showing top 5 rows


Posted by '김용환'
,



Spark에서 DataFrame으로 장난치다가

 requirement failed: Currently correlation calculation for columns with dataType string not supported.라는 에러를 만날 수 있다. 


이는 데이터 타입을 inferScheme을 통해 추론하는데 데이터 타입이 Long/Int로 변환되야 하는데 String 타입으로 변환된 컬럼 데이터를 corr라는 sql 함수로 계산하다가 에러가 발생한 것이다.


이럴 때는 명시적으로 StructType을 사용해 스키마를 지원하는 것이 좋다.



import org.apache.spark.sql.types.{StructType, StructField, StringType, LongType}

val myManualSchema = StructType(Array(
StructField("InvoiceNo", LongType, true),
StructField("StockCode", StringType, true),
StructField("Description", StringType, true),
StructField("Quantity", LongType, true),
StructField("InvoiceDate", StringType, true),
StructField("UnitPrice", LongType, true),
StructField("CustomerID", StringType, true),
StructField("Country", StringType, true)
))

val df = spark.read.format("csv")
.option("header", true)
.schema(myManualSchema)
.load("origin-source/data/retail-data/by-day/2010-12-01.csv")


Posted by '김용환'
,


ssh-keyscan, ssh-keygen 예시






$ ssh-keyscan github.com


github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccK...





$ ssh-keyscan -H github.com


|1|IVo9dmTn5FMnAkZ+4xWUuevH5To=|BQBOJ80KCa5BxDJxjGV+ElrfSvw= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccK..





$ ssh-keygen -lf <(ssh-keyscan github.com 2>/dev/null)


2048 SHA256:nThbg6kXUpJWGl7E1IGO,,, github.com (RSA)







$ ssh-keygen -lf <(ssh-keyscan -H github.com 2>/dev/null)


2048 SHA256:nThbg6kXUpJWGl7E1IGO... (RSA)

Posted by '김용환'
,





깃허브를 깃 서버로 사용한다면 깃허브 웹 사이트에서 SSH 키 지문(http://bit.ly/1DffcxK)을 조회할 수 있다.


이 글을 쓰는 시점에서 깃허브의 base64 포맷의 SHA256 RSA 지문(최신 포맷)은 SHA256:abc...이며 16진수 MD5 RSA 지문(이전 포맷)은 11:22:33:...:44의 포맷이다.


OpenSSH 6.8에서 기본 지문 포맷을 16진수 MD5에서 base64 SHA256으로 변경했고 현재 번역하는 시점의 7.9에서도 여전히 동일한 포맷을 사용 중이다


https://www.openssh.com/txt/release-7.9


Posted by '김용환'
,


ssh agent forwarding(에이전트 포워딩)에 sudo를 사용할 때 참고할만한 싸이트


https://gist.github.com/scottjacobsen/4281310




> sudo SSH_AUTH_SOCK=$SSH_AUTH_SOCK git clone git@github.com:my-github-account/my-repo.git


Posted by '김용환'
,



spark 앱을 로컬(Intellij)에서 테스트/실행할 때 

아래와 같이 SparkDriver 를 바인딩하지 못해 에러가 나는 경우가 종종 발견할 수 있다.




WARN Utils: Service 'sparkDriver' could not bind on port 0. Attempting port 1.


..java.net.BindException: Can't assign requested address: Service 'sparkDriver' failed after 16 retries!






이럴 때는 다음 코드를 참조해서 SparkConf 인스턴스를 생성할 때 다음 코드로 수정한다. 더 이상 발생하지 않을 것이다.



lazy val sparkConf: SparkConf  = new SparkConf().setMaster("local[1]") //상관없음

                              .setAppName("spark-test") 

                              .set("spark.driver.host", "localhost"); // 반드시 필수




Posted by '김용환'
,


rawdata를 case class로 정의해서 spark Dataset으로 encoding할 때 아래 에러가 발생할 수 있다.



Error:(14, 31) Unable to find encoder for type Flight. An implicit Encoder[Flight] is needed to store Flight instances in a Dataset. Primitive types (Int, String, etc) and Product types (case classes) are supported by importing spark.implicits._  Support for serializing other types will be added in future releases.

    val flights = flightsDF.as[Flight]

Error:(14, 31) not enough arguments for method as: (implicit evidence$2: org.apache.spark.sql.Encoder[Flight])org.apache.spark.sql.Dataset[Flight].

Unspecified value parameter evidence$2.

    val flights = flightsDF.as[Flight]



먼저 case class를 main 앞에 위치시키고 import spark.implicits._ 를 main 안에 위치한다.



case class Flight(DEST_COUNTRY_NAME: String, 
           ORIGIN_COUNTRY_NAME: String, count: BigInt)

object FlightMain extends SparkHelper {
def main(args: Array[String]): Unit = {

// 3.2
import sparkSession.implicits._

val flightsDF = sparkSession.read.parquet("origin-source/data/flight-data/parquet/2010-summary.parquet")
val flights = flightsDF.as[Flight]


Posted by '김용환'
,



jenkins pipeline 공부에 도움이 되는 것 같아 링크 중심으로 정리했다.



1. pipeline


pipeline 개념, 공식 문서

https://jenkins.io/doc/book/pipeline/


한글 - 사례

https://kingbbode.tistory.com/35


https://jojoldu.tistory.com/355


https://limsungmook.github.io/2016/11/09/jenkins-pipeline/




2. multi-branch


특정 branch 뿐 아니라 pr, branch도 테스트할 수 있다. jenkinsfile(pipeline)을 포함하면 금상첨화이다.


공식 문서

https://jenkins.io/doc/book/pipeline/multibranch/


사례


https://www.praqma.com/stories/jenkins-pipeline/




3. blue ocean에서 pipeline 편집하기


공식 문서

https://jenkins.io/doc/tutorials/create-a-pipeline-in-blue-ocean/


쉽게 파이프라인을 구성할 수 있다. 



ci/cd 실제 사례



Posted by '김용환'
,


 데비안/우분투에서 다음과 같은 에러가 발생한다면 호스트의 설정을 변경해야 한다.



WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.

WARNING: No memory limit support

WARNING: No swap limit support




참고 자료는 다음과 같다.

(호스트 머신의 재시작을 잊지 말자!)





https://docs.docker.com/install/linux/linux-postinstall/



Your kernel does not support cgroup swap limit capabilities

On Ubuntu or Debian hosts, You may see messages similar to the following when working with an image.

WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.

This warning does not occur on RPM-based systems, which enable these capabilities by default.

If you don’t need these capabilities, you can ignore the warning. You can enable these capabilities on Ubuntu or Debian by following these instructions. Memory and swap accounting incur an overhead of about 1% of the total available memory and a 10% overall performance degradation, even if Docker is not running.

  1. Log into the Ubuntu or Debian host as a user with sudo privileges.

  2. Edit the /etc/default/grub file. Add or edit the GRUB_CMDLINE_LINUX line to add the following two key-value pairs:

    GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
    

    Save and close the file.

  3. Update GRUB.

    $ sudo update-grub
    

    If your GRUB configuration file has incorrect syntax, an error occurs. In this case, repeat steps 2 and 3.

    The changes take effect when the system is rebooted.


Posted by '김용환'
,