키바나 7.0부터 mobile을 지원한다.. (beta 1)




https://github.com/elastic/kibana/issues/2563


https://github.com/elastic/kibana/pull/29896



이젠 점점 elasticsearch가 무서워진다~

Posted by '김용환'
,





ansible-playbook 커맨드를 실행하기 전에 플레이북을 확인할 수 있는 플래그가 있다.


1. 문법 체크


--syntax-check 플래그는 플레이북 문법이 유효한지 확인하지만 실행하지 않는다.


$ ansible-playbook --syntax-check playbook.yml





2. 호스트 나열하기


--list-hosts 플래그는 플레이북이 실행될 호스트를 출력하지만 플레이북을 실행하지 않는다.


$ ansible-playbook --list-hosts playbook.yml






3. 태스크 나열하기 


--list-tasks 플래그는 플레이북이 실행될 태스크를 출력하지만 플레이북을 실행하지 않는다.


$ ansible-playbook --list-tasks playbook.yml






4. 체크 모드


-C 플래그와 --check 플래그는 앤서블을 체크 모드를 실행한다(때때로 드라이 런(dry-run)이라 한다) 


즉 플레이북의 각 태스크가 호스트 수정 여부를 알려주지만 서버에 어떠한 변경이 발생하지 않는다.



$ ansible-playbook -C playbook.yml

$ ansible-playbook --check playbook.yml



체크 모드를 사용할  때의 어려움 중 하나는 플레이북의 처음 부분이 실행될 때만 플레이북의 뒷 부분이 성공할 수 있다는 점이다.





5. diff (파일 변경을 표시한다)

-D 플래그와 -diff 플래그는 원격 머신에서 변경된 모든 파일의 출력 비교 정보를 표시한다. 


정상적으로 실행되었다면 앤서블이 파일을 변경하는 방법을 표시하기 위해 -D 플래그와 -diff 플래그를 --check와 함께 사용하면 도움이 된다.


$ ansible-playbook -D --check playbook.yml

$ ansible-playbook --diff --check playbook.yml



앤서블이 파일(예시: copy, template, lineinfile과 같은 모듈을 사용)을 수정하면 다음처럼 .diff 포맷의 변경 사항을 표시한다.


-loglevel = "error"

+loglevel = "warning"





Posted by '김용환'
,



앤서블에서 실행할 태스크를 제어할 수 있다.


가끔 앤서블이 플레이북의 모든 태스크를 실행하는 것을 원치 않을 수 있다. 


특히 플레이북을 처음 작성하고 디버깅할 때 더욱 그렇다. 앤서블은 실행할 태스크를 제어할 수 있는 커맨드 라인 옵션을 제공한다.




1) step


--step 플래그는 다음처럼 각 태스크를 실행하기 전에 앤서블 프롬프트를 표시한다.


Perform task: install packages (y/n/c):


태스크를 실행하거나(y) 건너 뛰거나(n) 프롬프트를 표시하지 않고 나머지 플레이북을 계속 실행하도록 앤서블에 알릴 수 있다(c).


$ ansible-playbook --step playbook.yml






2) start-at-task



--start-at-task <태스크 이름> 플래그는 처음부터가 아닌 지정된 태스크에서 플레이북을 실행하기 시작하도록 앤서블에 알려준다. 


태스크 중 하나에 버그가 있어서 태스크 중 하나가 실패해서 실패한 태스크를 수정해 해당 태스크부터 시작해 플레이북을 다시 실행하고 싶을 때 유용할 수 있다.



$ ansible-playbook --start-at-task="install packages" playbook.yml




3) tags



앤서블에서는 하나 이상의 태그를 태스크 또는 플레이에 추가할 수 있다. 예를 들어 foo라는 태그가 붙은 플레이와 bar와 quux라는 태그가 붙은 플레이가 있다.



- hosts: myservers

  tags:

   - foo

  tasks:

   - name: install editors

     apt: name={{ item }}

     with_items:

       - vim

       - emacs

       - nano


   - name: run arbitrary command

     command: /opt/myprog

     tags:

       - bar

       - quux





-t <태그 이름> 플래그와 --tags <태그 이름> 플래그를 사용해 특정 태그를 포함한 플레이와 태스크만 실행하도록 앤서블에 알린다. --skip-tags <태그 이름> 플래그를 사용해 앤서블에 특정 태그를 포함한 플레이와 태스크를 건너 뛰라고 알린다.



태그를 실행하거나 건너뛰기

$ ansible-playbook -t foo,bar playbook.yml

$ ansible-playbook --tags=foo,bar playbook.yml

$ ansible-playbook --skip-tags=baz,quux playbook.yml


Posted by '김용환'
,



프로그래머 열정을 말하다 책을 봤다. 너무 재미있었다.

http://www.yes24.com/Product/Goods/6185848



원서는 My Job Went to India: 52 Ways to Save Your Job (Pragmatic Programmers)  이다. 

https://www.amazon.com/Job-Went-India-Pragmatic-Programmers/dp/0976694018  (2005년에 쓰여진 책이라니!!)





44살의 개발자 아저씨가 보기에 정말 중요한 내용인 것 같다. 다 완벽하고 좋은 말은 아니지만...


개발을 계속 하고 싶고, 프로그래밍하는 것이 즐겁다면 꼭 보면 좋을 것 같다.




책 내용을 간결히 요약한 블로그가 있으니. 참고하길 바란다.


http://jehyunpark.github.io/book/2017/02/12/the-passionate-programmer.html



Posted by '김용환'
,


클라우드 시스템을 관리하는 기술(http://www.hanbit.co.kr/store/books/look.php?p_code=B9686521083)에서 발췌한 자료이다.



Site Reliability 관행

1. 개발자(coder)만 고용한다.

2. 서비스마다 SLA를 둔다.

3. 성능을 측정하고 SLA를 만족하지 못하는 사항을 보고한다.

4. 오류 계산을 활용하고 개시(lauch)들이 반드시 오류 예산을 지키게 한다.

5. 공통의 직원 풀에서 SRE팀과 개발팀에 인원을 공급한다.

6. 여분의 (운영팀의 수용 능력을 넘는) 운영 (OPS) 작업은 개발팀(DEV)로 흘러가게 한다.

7. SRE 운영 부하가 50%를 넘지 않게 한다.

8. 운영팀의 작업의 5%를 개발팀과 공유한다.

9. 호출대기 팀은 한 장소에 적어도 여덟 명으로 구성하거나 여러 장소들 각각에 적어도 여섯 명으로 구성한다.

10. 호출 대기의 한 교대 근무(shift) 기간에 발생하는 사건이 최대 2회를 넘지 않도록 한다.

11. 모든 사건에 대해 사후 분석(postmortem)을 수행한다.

12. 사후 분석이 누군가를 비난하는 기회가 되어서는 안된다. 사람보다는 공정과 기술에 초점을 두어야 한다.


Posted by '김용환'
,


swagger에서 api를 정의할 때 Uncaught TypeError: Cannot read property 'withMutations' of null  에러가 발생했다.


이 이유는 api 정의할 때 parameters의 값이 null이기 때문에 발생하는 것이다.




parameters: null,


또는


parameters: ,





=>



parameters:[]


로 하면 동작한다.



Posted by '김용환'
,



$ sudo logrotate /etc/logrotate.d/uwsgi.logrotate


error: skipping "/var/log/sentry/sentry-cron.stderr.log" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.


이렇게 에러가 나는 이유는 디렉토리의 권한이 너무 많이 있기 때문에 디렉토리의 group, other에 write 권한 을 제외한다면 더 이상 에러가 발생하지 않을 것이다.

Posted by '김용환'
,



vagrant 1.7 버전부터는 기본적으로 SSH 키를 각 머신마다 다르게 준다.

따라서 각 머신에 동일한 SSH 키를 사용하려면 다음 정보를 추가한다.


  config.ssh.insert_key = false



Posted by '김용환'
,

a type was inferred to be `Any`; this may indicate a programming error.


이런 코드를 만난다면, Any 타입으로 먼가 작업할 때 scala 컴파일러가 불평하는 것인데..

이럴 때는 Some으로 감싸면 쉽게 해결된다.




Seq[Seq[Any]] 를 리턴하는 코드라면

실제로는 Some으로 감싸서 (실제로는 Seq[Seq[Some(Any]]) 리턴하게 하면 컴파일 이슈는 사라지고 에러도 발생하지 않는다.

Posted by '김용환'
,



scala-guice는 google-guice를 이용해 scala quill에 맞게 inject를 구현한 간단 코드이다.


libraryDependencies ++= Dependencies.guice


val guice = Seq(
"com.google.inject" % "guice" % "4.2.2",
"com.google.inject.extensions" % "guice-assistedinject" % "4.2.2",
"net.codingwell" %% "scala-guice" % "4.2.2"
)




scala quill 를 사용할 때 데이터 타입 관련해서 Encoder/Decoder로 사용할 Implicits를 구현한다.


package com.google.quill

import java.util.Date

import io.getquill.MappedEncoding
import org.joda.time.DateTime

object Implicits {
implicit val dateTimeDecoder = MappedEncoding[Date, DateTime](Decoders.fromDateField)
implicit val dateTimeEncoder = MappedEncoding[DateTime, Date](_.toDate)
}

object Decoders {
def fromDateField(date: Date): DateTime = {
new DateTime(date)
}
}




데이터베이스에서 사용하는 Orders 클래스를 정의한다.



package com.google.datalake.dao.shopping

import org.joda.time.DateTime

case class Orders(
id: Long,
payment_id: Long,
refund_id: Option[Long],
channel_id: Long,
seller_id: Long,
buyer_id: Option[Long],
buyer_user_id: Long, #...
)



properties 정보

orderDB.dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
orderDB.dataSource.url="jdbc:mysql://test.google.com:3306/buy?useTimezone=true&serverTimezone=UTC&characterEncoding=UTF-8"
orderDB.dataSource.user=xxx
orderDB.dataSource.password=xxx
orderDB.dataSource.cachePrepStmts=true
orderDB.dataSource.prepStmtCacheSize=250




실제 Inject 중심 클래스를 정의한다. google-guice의 AbstractModule를 상속하고 scala-guide의 ScalaModule를 믹싱한 DataBaseModule을 정의한다. 

package com.google.datalake.db.modules

import com.google.inject._
import com.google.datalake.db.SelectOrderDB
import io.getquill.{MysqlJdbcContext, SnakeCase}
import net.codingwell.scalaguice.ScalaModule
import com.google.inject.AbstractModule

class DataBaseModule extends AbstractModule with ScalaModule {

override def configure(): Unit = {
bind(classOf[SelectOrderDB]).asEagerSingleton()
}

@Provides
def provideDataBaseSource(): MysqlJdbcContext[SnakeCase] = {
new MysqlJdbcContext(SnakeCase, "orderDB")
}
}


quill을 이용한 DAO 코드이다. MysqlJdbcContext를 inject해서 사용할 수 있게 한다.


package com.google.datalake.db

import com.google.inject.Inject
import com.google.datalake.dao.shopping.Orders
import io.getquill.{MysqlJdbcContext, SnakeCase}

class SelectOrderDB @Inject()(val ctx: MysqlJdbcContext[SnakeCase]) {

import ctx._

def findById(id: Long) = {
val q = quote {
query[Orders].filter(_.id == lift(id))
}
ctx.run(q)
}

def findByIds(ids: List[Long]): Unit = {
val q = quote {
query[Orders]
.filter(p => liftQuery(ids).contains(p.id))
}
ctx.run(q)
}
}


실제 테스크 코드는 다음과 같다.

package com.google.datalake.db

import com.google.inject.Guice
import com.google.datalake.db.modules.DataBaseModule
import org.scalatest.FunSuite

class SelectOrderDBTest extends FunSuite {

val injector = Guice.createInjector(new DataBaseModule()).getInstance(classOf[SelectOrderDB])
val selectOrderDB = injector.asInstanceOf[SelectOrderDB]

test("find by id") {
val row = selectOrderDB.findById(71721303)

println(row)
}

}







Posted by '김용환'
,