redis 3.2를 설치한 후, 접근이 막혀있다. 보안을 높였다. 


$ telnet 1.2.3.4 6379


Escape character is '^]'.

-DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.

Connection closed by foreign host.




HA 기능을 사용한다면, HA 애플리케이션에서 정상적으로 동작하는지 접근할 수 있도록 localhost에서 레디스 redis-cli 실행해서 config set protected-mode no를 실행해 외부 접근을 가능하게 한다. 


$ redis-cli

> CONFIG SET protected-mode no


외부에서 접근가능해져 HA 애플리케이션이 접근할 수 있다.


$ telnet 1.2.3.4 6379

Escape character is '^]'.

OK



이제는 정상적으로 접근된다. 



처음부터 protected-mode가 동작되지 않도록 하려면, redis.conf 에 다음을 추가한다.


bind 0.0.0.0


모든 IP에서 들어올 수 있도록 설정한다는 의미를 가진다. 



참조 : https://redis.io/topics/security

Posted by '김용환'
,


쉘에서 mongodb 클라이언트를 사용하려면, sudo yum install mongodb를 실행해 mongodb를 설치한다. 


그리고, mongo 커맨드에 mongodb 서버에 커맨드를 실행한다. 



$ mongo abc.google.io:27017/mydb --eval "db.api_feeds_list"

MongoDB shell version: 2.4.14

connecting to: abc.google.io:27017/mydb

mydb.api_feeds_list



db 상태를 볼 수 있다. 하지만, 바로 js 객체로만 나온다. 


$ mongo abc.google.io:27017/mydb --eval "db.stats()"

MongoDB shell version: 2.4.14

connecting to: abc.google.io:27017/mydb

[object Object]



DB 상태를 보기 위해 printjson과 같은 함수를 써서 상세하게 볼 수 있다. 


$ mongo abc.google.io:27017/mydb --eval "printjson(db.stats())"

MongoDB shell version: 2.4.14

connecting to: abc.google.io:27017/mydb

{

"db" : "mydb",

"collections" : 620,

"objects" : 326613,

"avgObjSize" : 1669.3089405504375,

"dataSize" : 545218001,

"storageSize" : 250097664,

"numExtents" : 0,

"indexes" : 620,

"indexSize" : 29995008,

"ok" : 1

}




마찬가지로 object Object로 리턴하면 printjson으로 확인할 수 있다. 


$ mongo abc.google.io:27017/mydb --eval "printjson(db.api_feeds_list.findOne())"

MongoDB shell version: 2.4.14

connecting to: abc.google.io:27017/mydb

{

"_id" : ObjectId("564c808630325673616b25ad"),

"date" : "20151117",

"max" : 18133,

"sum" : 49779120,

"stdev" : 17.49,

"max_user" : 75847769,

"uniq_user" : 8153500,

"mean" : 6.1

}



자바 스크립트를 안다면, 다음처럼 파일로 전달해 처리할 수 있다. 


$ cat > mytest.js

printjson(db.stats())


$ mongo abc.google.io:27017/mydb mytest.js

MongoDB shell version: 2.4.14

connecting to: abc.google.io:27017/mydb

{

"db" : "mydb",

"collections" : 620,

"objects" : 326635,

"avgObjSize" : 1669.559428720131,

"dataSize" : 545336544,

"storageSize" : 250134528,

"numExtents" : 0,

"indexes" : 620,

"indexSize" : 29995008,

"ok" : 1

}




Posted by '김용환'
,




grafana, telegraf, influxDB로 실시간 모니터링은 구축했지만, jvm 모니터링을 구축한 내용이다.



이전 내용은 http://knight76.tistory.com/entry/grafana를 참조한다.






telegraf를 이용해 자바를 모니터링하는 방법이 있다. 


- 웹 애플리케이션에 jolokia 활용하기 (https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia)

- stats-jvm-profile를 java agent를 사용하는 방법(https://github.com/etsy/statsd-jvm-profiler)


두 방법 모두 javaagent 기반이라 등록/해제 모두 재시작을 해야 한다는 점이 좀 걸렸다. 웹도 아니면 jolokia가 불편하기도 하기도 하다. 더 간단한 방법으로 보고 싶었다. 



jmx를 순수하게 사용하는 방식을 사용했다. 결론은 대만족이다. 

cmdline-jmxclient 0.10.3을 다운받아 jmx 호출을 통해 jvm 메모리 부분을 모니터링하도록 스크립트를 구성했다. 


그리고, influxDB http write api를 이용해서 저장했다. (단순한 질의와 파일을 이용한 bulk 작업이 가능하다)

influxDB 1.1 이전에는 json을 지원했지만, influxDB 1.1부터는 그냥 property와 sql형태만 지원하는 것 같다. 

(참조 : https://docs.influxdata.com/influxdb/v1.1/guides/writing_data/)



crontab에 아래 스크립트(g1, cms 메모리 정보)를 만들어 jvm이 떠 있는 장비로 jmx을 모니터링 정보를 influxDB에 저장하도록 했다. 




<핵심 스크립트>



# timestamp 구성하기 

t="$(date -u +%s)"

oo="000000000"

timestamp=$t$oo

echo $timestamp 


/usr/java/default/bin/java -jar cmdline-jmxclient-0.10.3.jar - abc.google.io:10100 "java.lang:type=MemoryPool,name=G1 Eden Space" "Usage" 2>> /tmp/javalog2

used="$(cat /tmp/javalog2 | grep used | cut -f2 -d" ")"

curl -i -XPOST 'http://influxdb.google.io:8086/write?db=telegraf' -d "javamem,server=abc.google.io,type=g1-eden value=$used $timestamp"




많이 보면 좋긴 하지만, 잘 동작한다고 가정하고 주요 지표만 보도록 했다. 

만약 다른 지표를 추가해 세밀히 볼 수 있을 것이다. 



g1일 때 

"java.lang:type=Memory" "HeapMemoryUsage"

"java.lang:type=MemoryPool,name=G1 Survivor Space"

"java.lang:type=MemoryPool,name=G1 Old Gen"

"java.lang:type=MemoryPool,name=G1 Eden Space"



cms일 때 

"java.lang:type=Memory" "HeapMemoryUsage"

"java.lang:type=MemoryPool,name=Par Eden Space" "Usage"

"java.lang:type=MemoryPool,name=Par Survivor Space" "Usage"

"java.lang:type=MemoryPool,name=CMS Old Gen" "Usage"




기존 grafana에 jvm 모니터링 추가한 내용이다. jvm 서버 재시작 없이 gc 내용을 볼 수 있게 만들었다. 




Posted by '김용환'
,




보통 여러 데몬을 한 번에 kill하려면, grep을 사용하면 쉽게 종료할 수 있다. 


$ ps -ef | grep python | grep -v grep |  awk '{print $2}' | xargs kill -9



grep을 쓰기가 좀 그러면, awk로 대안해서 사용할 수 있다. 


$ ps -ef | awk '/python/ && !/awk/ {print $2}' | xargs -r kill -9


Posted by '김용환'
,



내가 crontab을 생성할 때 자주 사용하는 템플릿이다. 


* * * * * cd /home/www && /home/www/send_java_info_to_influxdb.sh > /home/www/log/client.log 2>&1



먼저 디렉토리를 변경하고, 

&&

스크립트를 실행하고

>

로그를 잘 저장한다




Posted by '김용환'
,


nginx의 next_upstream이 완벽하게 HA를 제공하는지 테스트하는 예시이다. 



다음 코드를 참조했다. 


https://gist.github.com/bradmontgomery/2219997#file-dummy-web-server-py



#!/usr/bin/env python

"""

Very simple HTTP server in python.

Usage::

    ./dummy-web-server.py [<port>]

Send a GET request::

    curl http://localhost

Send a HEAD request::

    curl -I http://localhost

Send a POST request::

    curl -d "foo=bar&bin=baz" http://localhost

"""

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

import SocketServer


class S(BaseHTTPRequestHandler):

    def _set_headers(self):

        self.send_response(200)

        self.send_header('Content-type', 'text/html')

        self.end_headers()


    def do_GET(self):

        self._set_headers()

        self.wfile.write("<html><body><h1>hi!</h1></body></html>")


    def do_HEAD(self):

        self._set_headers()

        

    def do_POST(self):

        # Doesn't do anything with posted data

        self._set_headers()

        self.wfile.write("<html><body><h1>POST!</h1></body></html>")

        

def run(server_class=HTTPServer, handler_class=S, port=80):

    server_address = ('', port)

    httpd = server_class(server_address, handler_class)

    print 'Starting httpd...'

    httpd.serve_forever()


if __name__ == "__main__":

    from sys import argv


    if len(argv) == 2:

        run(port=int(argv[1]))

    else:

        run()





nginx 설정은 다음과 같다. 


    server {

        listen       8888;

        server_name  localhost;


        #charset koi8-r;


        #access_log  logs/host.access.log  main;


        location / {

            proxy_pass http://backends;

            proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;

            proxy_http_version 1.1;

            proxy_set_header Host $host;

           proxy_set_header X-Real-IP  $remote_addr;

           proxy_set_header X-Forwarded-For $remote_addr;

        }





두 디렉토리에 각각 파이썬 simple web server 를 다운받고 포트를 각각 8080, 8081로 바꿔 개별 디렉토리에서 실행한다.


$ python server.py



테스트를 해보면, 정상적으로 잘 동작한다. proxy_pass설정에 따라 처음은 8080, 다음은 8081로 잘 돌아가면서 동작한다. 


$ curl http://localhost:8888

<html><body><h1>hi!</h1></body></html>




이번에는 8081포트로 실행한 파이썬 서버에서 GET을 호출시 503 헤더 응답을 보내도록 수정하고 다시 실행한다.

curl로 get 요청을 하나 보낸다. 


127.0.0.1 - - [29/Dec/2016 20:51:05] "GET / HTTP/1.1" 503 -


내부적으로 문제가 발생한 것을 보고 8081 파이썬 서버로 GET을 보낸다. (retry 확인)


127.0.0.1 - - [29/Dec/2016 20:51:05] "GET / HTTP/1.1" 200 -




이번에는 8081포트로 실행한 파이썬 서버에 POST를 호출시 503 헤더 응답을 보내도록 수정한다.  

http 요청이 8081에 전달되면 에러가 발생한다. 


$ curl -X POST http://localhost:8888

<html><body><h1>POST!</h1></body></html>[/usr/local/nginx/conf] curl -X POST http://localhost:8888

<!DOCTYPE html>

<html>

<head>

<title>Error</title>

<style>

    body {

        width: 35em;

        margin: 0 auto;

        font-family: Tahoma, Verdana, Arial, sans-serif;

    }

</style>

</head>

<body>

<h1>An error occurred.</h1>

<p>Sorry, the page you are looking for is currently unavailable.<br/>

Please try again later.</p>

<p>If you are the system administrator of this resource then you should check

the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>

<p><em>Faithfully yours, nginx.</em></p>

</body>

</html>




그리고 파이썬 서버에서는 에러가 발생했다. 

127.0.0.1 - - [29/Dec/2016 11:56:48] "POST / HTTP/1.1" 503 -


그리고 retry는 되지 않는다. 비멱등 메소드는 retry를 지원하지 않는다. 




의외로 nginx의 next stream(proxy_next_upstream)이 완벽한  L7 health check 대용으로 생각하는 분이 많다는 점이다. 

1.9.12까지는 비멱등 메소드(POST, LOCK, PATCH)에 대해서 retry를 지원했으나, 1.9.13부터는 비멱등 메소드 호출시 retry를 해주지 않는다. 



문서로 명시적으로 설명되어 있다. 


http://nginx.org/en/CHANGES

Changes with nginx 1.9.13                                        29 Mar 2016

    *) Change: non-idempotent requests (POST, LOCK, PATCH) are no longer
       passed to the next server by default if a request has been sent to a
       backend; the "non_idempotent" parameter of the "proxy_next_upstream"
       directive explicitly allows retrying such requests.

참고로 proxy_request_buffering를 사용한다 하더라도 동일하다. 

'nginx' 카테고리의 다른 글

[openresty] lua 처음 다루기  (0) 2017.01.24
openresty 1.11.2 설치  (0) 2017.01.19
[nginx+passenger] 설치  (0) 2016.06.30
[nginx] http2 적용하기  (0) 2016.06.18
[nginx] 499 에러  (0) 2016.06.10
Posted by '김용환'
,

tr 커맨드 팁

unix and linux 2016. 12. 28. 09:39



tr 커맨드는 표준 입력를 변환(translate) 한다.


aZ라는 파일이 다음처럼 존재한다.


$ cat aZ

abc DEF



보통 tr에서 정규표현식을 사용할 때는 따옴표(quote)를 써야 한다고 알려져 있다.



$ tr '[a-z]' '[A-Z]' < aZ

ABC DEF


$ tr "[a-z]" "[A-Z]" < aZ

ABC DEF


$ tr '[A-Z]' '[a-z]' < aZ

abc def


$ tr "[A-Z]" "[a-z]" < aZ

abc def



하지만, from이나 to 중 하나에만 따옴표를 써도 동작한다.

$ tr '[a-z]' [A-Z] < aZ
ABC DEF


소문자는 대문자로, 대문자는 소문자로 바꾸려면 다음과 같은 패턴을 사용한다. 

$ tr "[a-zA-Z]" "[A-Za-z]" < aZ
ABC def


Posted by '김용환'
,


unterminated substitute in regular expression 발생 시, 구분자 /를 맨 마지막에 사용했는지 확인한다. 


$ cat xx

1

2

jan -1




$ sed '/jan/s/1/5' xx

sed: 1: "/jan/s/1/5": unterminated substitute in regular expression




정규 표현식에 /를 사용하니 unterminated substitute in regular expression이 발생하지 않았다. 


$ sed '/jan/s/1/5/' xx

1

2

jan -5


Posted by '김용환'
,



쉘 스크립트에서는 here tag, here document에 설명되어 있다. 


여러 라인의 배시 커맨드를 간결하게 해준다고 보면 된다. 하지만 리터털이 아닌 표준입력으로 취급한다.




공식문서는 다음과 같다.


http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04


2.7.4 Here-Document

The redirection operators "<<" and "<<-" both allow redirection of subsequent lines read by the shell to the input of a command. The redirected lines are known as a "here-document".

The here-document shall be treated as a single word that begins after the next <newline> and continues until there is a line containing only the delimiter and a <newline>, with no <blank> characters in between. Then the next here-document starts, if there is one. The format is as follows:

[n]<<word
    here-document
delimiter

where the optional n represents the file descriptor number. If the number is omitted, the here-document refers to standard input (file descriptor 0). It is unspecified whether the file descriptor is opened as a regular file, a special file, or a pipe. Portable applications cannot rely on the file descriptor being seekable (see XSH lseek).

If any part of word is quoted, the delimiter shall be formed by performing quote removal on word, and the here-document lines shall not be expanded. Otherwise, the delimiter shall be the word itself.

If no part of word is quoted, all lines of the here-document shall be expanded for parameter expansion, command substitution, and arithmetic expansion. In this case, the <backslash> in the input behaves as the <backslash> inside double-quotes (see Double-Quotes). However, the double-quote character ( ' )' shall not be treated specially within a here-document, except when the double-quote appears within "$()""``", or "${}".

If the redirection operator is "<<-", all leading <tab> characters shall be stripped from input lines and the line containing the trailing delimiter. If more than one "<<" or "<<-" operator is specified on a line, the here-document associated with the first operator shall be supplied first by the application and shall be read first by the shell.

When a here-document is read from a terminal device and the shell is interactive, it shall write the contents of the variable PS2, processed as described in Shell Variables, to standard error before reading each line of input until the delimiter has been recognized.




here document에 대한 간단한 예시이다.  << 다음에 오는 구분자이기 때문에 마지막에 오는 구분자와 짝만 맞추면 된다. 관례상 EOF를 많이 쓰는 것 같다. 


cat << XXXX

> a="world"

> $a

> XXXX

a="world"

world



예시에서 here tag를 EOF로 가정하고 간단한 예시를 소개한다. 


리디렉션  <<을 사용해 한번에 배시를 처리할 수 있다. 



$ cat << EOF

> echo $a

> $a

> EOF

echo world

world



여러 라인으로 구성된 배시를 파일로 저장할 수 있다. 


$ cat << EOF > print_a.sh

> #!/bin/sh

> echo $a

> EOF

$ chmod 755 print_a.sh

$ ./print_a.sh

world



EOF처럼 EOS도 테스트할 수 있다.


$ cat << EOF

> h

> f

> p

> EOF

h

f

p




here tag 또는 here document는 표준 출력만 하기 때문에, 리터럴로 저장하려면, $ 또는 `를 사용해야 한다.

여러 라인으로 구성된 배시의 결과를 변수로 지정할 수 있다. 



$ multiline=$(cat << EOF

echo $a

$a

EOF

)


$ echo $multiline

echo world world




문서를 보면 expand라는 단어가 나오는데, 이 개념은 아래 예시를 통해 이해할 수 있다. << EOF를 사용하면 따옴표안의 변수 값이 변환되지만, <<\EOF를 사용하면 따옴표 안에 변수 값이 변환되지 않는다. 



$ cat << EOF

> a="world"

> printf '[%s]\n' "$a"

> EOF

a="world"

printf '[%s]\n' "world"



$ cat <<\EOF

> a="world"

> printf '[%s]\n' "$a"

> EOF

a="world"

printf '[%s]\n' "$a"




<<에 마이너스 기호(<<-)는 탭을 무시하겠다는 의미를 가진다. 






Posted by '김용환'
,

paste 커맨드

unix and linux 2016. 12. 26. 21:57



paste 커맨드는 파일들의 내용을 나란히 탭으로 구분해 출력하는 리눅스 커맨드이다. 



[~] cat job_name

devops

engineer

[~] cat names

sammuel.kim

jax.moon



[~] paste names job_name

sammuel.kim devops

jax.moon engineer




탭이 싫다면, -d 옵션과 delimiter를 줄 수 있다. 


$ paste -d":" names job_name

sammuel.kim:devops

jax.moon:engineer




만약 여러개의 delimiter를 줄 수 있고, 파일 단위로 사용된다. 


$ paste -d'+-' names job_name names

sammuel.kim+devops-sammuel.kim

jax.moon+engineer-jax.moon



파일이 4개이고, delimiter가 2개이면, delimiter가 반복되어 사용된다. 


$ paste -d'+-' names job_name names job_name

sammuel.kim+devops-sammuel.kim+devops

jax.moon+engineer-jax.moon+engineer




-s 옵션을 사용하면 여러 파일이 아닌 하나의 동일 파일을 사용한다.



$ paste -s names

sammuel.kim jax.moon



탭이 기본 구분자라서 +를 구분자로 사용할 수 있다.


$ paste -s  -d'+' names

sammuel.kim+jax.moon





대시(-)를 쓰면 무엇이 나올까? 표준입력을 받는 의미로 쓰인다. 



$ cat job_name -

devops

engineer


$ cat job_name -

devops

engineer

// zero 입력 

zero

zero



표준 입력을 ls로부터 받고 구분자를 공백으로 두려면 다음과 같은 명령어를 사용한다.


$ ls | paste -d' ' -s -

Applications Desktop Documents Downloads Dropbox



ls | paste는 echo *의 덜 복잡한 포맷이 된다.


Posted by '김용환'
,