jenkin migration할 때 어떤 plugin을 설치했는지 모를 때,

jenkins 홈디렉토리에 plugins 디렉토리를 찾아 설치할 수 있다.


jenkins docker에 plugin 목록으로 사용되니 알아두면 좋다.


JENKINS_HOME이 /var/lib/jenkins/plugins 이거라면 아래와 같이 실행해 플러그인 설치 목록을 확인할 수 있다. 


$ cd  /var/lib/jenkins/plugins


$ ls -al *.jpi  | awk '{print $9}'  | sed 's/.jpi//'

antisamy-markup-formatter

ant

build-timeout

changelog-history

config-file-provider

credentials

external-monitor-job

git-client

github-api

github

github-oauth

git

icon-shim

javadoc

job-restrictions

junit

ldap

locale

mailer

mapdb-api

matrix-auth

matrix-project

naginator

pam-auth

parameterized-trigger

plain-credentials

pyenv

python

ruby-runtime

scm-api

script-security

shiningpanda

ssh-agent

ssh-credentials

ssh-slaves

structs

subversion

token-macro

windows-slaves

workflow-step-api


Posted by 김용환 '김용환'

마이크로 서비스에서는 json 로그로 저장되도록 하는 것이 중요한데..

logback에 json 출력 기능이 있다. 



logstash logback 라이브러리를 쓰는 것도 있지만.. (아래 링크 참조)


http://knight76.tistory.com/entry/log-log4j%EC%97%90%EC%84%9C-json-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EC%B6%9C%EB%A0%A5%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0



 spring boot의 logback에도 json 출력한다.



<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<include resource="org/springframework/boot/logging/logback/defaults.xml" />

<property name="LOG_FILE"

value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}" />

<include resource="org/springframework/boot/logging/logback/console-appender.xml" />


<appender name="JSON_FILE"

class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${LOG_PATH}/order-${PID}.json</file>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>order-${PID}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>

<maxHistory>7</maxHistory>

</rollingPolicy>

<encoder

class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">

<providers>

<arguments />

<stackTrace />

<timestamp>

<timeZone>UTC</timeZone>

</timestamp>

<pattern>

<pattern>

{

"severity": "%level",

"service": "order",

"pid": ${PID:-},

"thread": "%thread",

"logger": "%logger",

"message": "%message"

}

</pattern>

</pattern>

</providers>

</encoder>

</appender>

<root level="INFO">

<appender-ref ref="JSON_FILE" />

<appender-ref ref="CONSOLE" />

</root>

</configuration>

Posted by 김용환 '김용환'


javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. 에러가 발생하면 라이브러를 추가한다.


2018-07-30T16:04:06,790 DEBUG o.s.b.f.s.DefaultListableBeanFactory.doCreateBean(539) - Eagerly caching bean 'mvcValidator' to allow for resolving potential circular references

2018-07-30T16:04:06,796 DEBUG o.s.b.f.s.DefaultListableBeanFactory.invokeInitMethods(1670) - Invoking afterPropertiesSet() on bean with name 'mvcValidator'

2018-07-30T16:04:06,797 DEBUG o.h.v.i.e.r.DefaultTraversableResolver.detectJPA(70) - Cannot find javax.persistence.Persistence on classpath. Assuming non JPA 2 environment. All properties will per default be traversable.

2018-07-30T16:04:06,798 DEBUG o.s.v.b.OptionalValidatorFactoryBean.afterPropertiesSet(43) - Failed to set up a Bean Validation provider

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

        at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:102)

        at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:45)

        at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:423)

        at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:264)


Caused by: java.lang.NoSuchMethodError: javax.el.ExpressionFactory.newInstance()Ljavax/el/ExpressionFactory;

        at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:98)

        ... 46 common frames omitted




spring boot에서는 아래와 같이 build.gradle 파일을 수정한다. 


    compile('javax.el:javax.el-api:3.0.0')

    compile('org.glassfish:javax.el:3.0.0')




pom.xml 파일을 수정한다.
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.0</version>
</dependency>



Posted by 김용환 '김용환'

mvnw 설치하기

general java 2018.07.11 20:25


mvnw(gradlew와 비슷한 툴)를 실행하는 방법이다.


먼저 mvn을 먼저 설치하고 다음 커맨드를 실행한다.


$ mvn -N io.takari:maven:wrapper -Dmaven=3.5.3


메이븐 버전은 항상 고려한다.

https://maven.apache.org/docs/ 




패키징 한다. 스프링 부트의 경우 설정에 따라서 데몬 까지 한번에 실행될 수 있다. 


$ ./mvnw clean package


Posted by 김용환 '김용환'



zookeeper 클러스터에 zookeeper 신규 서버 추가하기


아래 github gist를 참고하면 좋을 것 같다. 

(3.4 이후에서 잘 동작한다)

https://gist.github.com/miketheman/6057930


추가적으로 zookeeper 클러스터의 서버 대수보다 더 많은 서버 대수를 추가할 때는 조심하는 게 좋을 것 같다. 

항상 쿼럼을 유지해야 하니까..




정상적으로 클러스터링되었는지 확인하는 방법은 3가지가 있다.


1. 로그 파일을 통해 확인 (클러스터링 장비 중 하나에서 zookeeper 재시작 시  에러 난다)

zookeeper.log 


2. 각 장비의 data file system 파일이 동일하다. 


3. 다음 커맨드를 실행해서 클러스터링 상태를 확인할 수 있다.


상태 정보와 함께 Zxid, follower


$ echo stat | nc 장비 2181


Latency min/avg/max: 0/0/0

Received: 2

Sent: 1

Connections: 1

Outstanding: 0

Zxid: 0xa00000160

Mode: follower

Node count: 11467





이전 커맨드를 실행시 zookeeper 에서 다음 커맨드 에러가 발생한다면.. 정상적이지 않다는 것이다.


This ZooKeeper instance is not currently serving requests


https://github.com/apache/zookeeper/search?utf8=%E2%9C%93&q=ZK_NOT_SERVING&type=



다양한 원인이 있는데. 지금 제대로 동기가 안된 거일 수 있다. 그러나 다른 장비는 정상적이라면..

config/zoo.cfg 이슈일지 꼼꼼히 살펴본다.

Posted by 김용환 '김용환'


zookeeper node에 ttl 기능이 생길 예정이다. 아직 릴리즈 되지 않은 3.6.0에 추가되었다. 



https://issues.apache.org/jira/browse/ZOOKEEPER-1925


Here are only some problems with app-level cleaning:
– Slow for large trees. If the network latency is high it takes a very long time for the app to walk whole tree (e.g. 1M nodes).
– Risky. If the cleanup application crash (or becomes unreachable) it puts whole ZK cluster to danger.
– The implementation might be error-prone if multiple cleanup threads are used.

– Has to be implemented separately by each developer (until a proven implementation shared as a library).


여러 이슈가 있어서. 3.6.0으로 미룬 상태이다..


zookeeper에 ttl이 있고 성능만 좋다면 앞으로 많이 사용할 듯 싶다. 



Posted by 김용환 '김용환'

스프링 보안 이슈(https://spring.io/blog/2018/04/09/cve-2018-1275-address-partial-fix-for-cve-2018-1270)가 발생했다.


Spring Framework, versions 5.0.x prior to 5.0.5 and versions 4.3.x prior to 4.3.16, and older unsupported versions allow applications to expose STOMP over WebSocket endpoints with a simple, in-memory STOMP broker through the spring-messaging module. A malicious user (or attacker) can craft a message to the broker that can lead to a remote code execution attack.


https://spring.io/blog/2018/04/05/multiple-cve-reports-published-for-the-spring-framework




spring 2.0.1과 spring 1.5.12를 사용하길 권고한다고 떴다.


https://spring.io/blog/2018/04/05/spring-boot-2-0-1-available-now


https://spring.io/blog/2018/04/10/spring-boot-1-5-12-available-now



요약하면.



Spring Framework 5.0.x ->Spring Framework 5.0.5 로 업그레이드

Spring Framework 4.3.x -> Spring Framework 4.3.16 로 업그레이드


Spring Boot 2.0.0 -> Spring Boot 2.0.1로 업그레이드

Spring Boot 1.x.0 -> Spring Boot 1.5.12로 업그레이드



Posted by 김용환 '김용환'


spring boot에서 DB 접속할 때 hikari cp로 연결해서 사용할 수 있다.


JdbcTemplate.query로 DB 연결 후 실행하기 전에 boot가 초기화할 때부터 DB에 붙고 싶다면.

spring.datasource.initializer의 값을 true로 설정한다.

spring.datasource.initialize=true
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.hikari.jdbc-url=jdbc:oracle:thin:@1.1.1.1:1521:oradb
spring.datasource.hikari.username=username
spring.datasource.hikari.password=pssword
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.validation-timeout=10000


Posted by 김용환 '김용환'



build.gradle에 최신 logstash-logback-encoder를 추가했다.


compile('net.logstash.logback:logstash-logback-encoder:5.0')




https://github.com/logstash/logstash-logback-encoder


Standard Fields

These fields will appear in every LoggingEvent unless otherwise noted. The field names listed here are the default field names. The field names can be customized (see Customizing Standard Field Names).

FieldDescription
@timestampTime of the log event. (yyyy-MM-dd'T'HH:mm:ss.SSSZZ) See customizing timezone.
@versionLogstash format version (e.g. 1) See customizing version.
messageFormatted log message of the event
logger_nameName of the logger that logged the event
thread_nameName of the thread that logged the event
levelString name of the level of the event
level_valueInteger value of the level of the event
stack_trace(Only if a throwable was logged) The stacktrace of the throwable. Stackframes are separated by line endings.
tags(Only if tags are found) The names of any markers not explicitly handled. (e.g. markers from MarkerFactory.getMarker will be included as tags, but the markers from Markers will not.)




기본 포맷은 다음과 같다.


{"@timestamp":"2018-03-26T16:09:15.692+09:00","@version":"1","message":"data","logger_name":"com.kakao.sauron.api.controller.TestController","thread_name":"http-nio-8080-exec-3","level":"INFO","level_value":20000}



보통 caller 관점에서의 line_number도 필요하는데.. 


includeCallerData를 추가하면 관련 정보가 나타난다. 





<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
</encoder>




caller 쪽 데이터를 출력할 수 있다.


{"@timestamp":"2018-03-26T16:22:03.196+09:00","@version":"1","message":"data","logger_name":"com.kakao.sauron.api.controller.TestController","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"caller_class_name":"com.kakao.sauron.api.controller.TestController","caller_method_name":"helloWorld","caller_file_name":"TestController.java","caller_line_number":28}





너무 많이 나와서.. 필드 이름을 조금 줄일 수 있다. 


<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>


{"@timestamp":"2018-03-26T16:28:58.886+09:00","@version":"1","message":"afdsafasfsad","logger":"com.kakao.sauron.api.controller.TestController","thread":"http-nio-8080-exec-1","level":"INFO","levelVal":20000,"caller":{"class":"com.kakao.sauron.api.controller.TestController","method":"helloWorld","file":"TestController.java","line":28}}






이정도가 제일 무난한 정도인 듯 하다.

<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>
</appender>
<logger name="jsonLogger" additivity="false" level="DEBUG">
<appender-ref ref="consoleAppender"/>
</logger>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
</root>




물론 여기에 더 해야할 점은 로그 파일의 용량, rotation을 적용해야 한다.

상용에서 사용하려면 더 신경쎠야 한다. 

Posted by 김용환 '김용환'



gradle에서 특정 라이브러리는 사용하고 싶지 않다면.


configuration-> compile.exclude를 사용한다.




주의할 점은 group, module이 분류되어 있다는 점이다.



configurations {
compile.exclude group: "org.slf4j", module : "slf4j-log4j12"
compile.exclude group: "javax.servlet", module : "servlet-api"
}


Posted by 김용환 '김용환'