윈도우 10에서 OpenSSH 사용하기.

|

윈도우 10에 openssh 가 내장되어 있어 putty나 xshell 사용할 필요 없이
맥이나 리눅스처럼 ssh 명령어를 통해 서버접근을 할 수 있다는 정보를 얻어 찾아보았다.

1. 시작메뉴(윈도우 키) 선택 후 찾기 버튼 클릭 -> 앱 및 기능 검색 후 선택.

2. 선택적 관리 기능 선택.

3. OpenSSH 서버 선택 후 설치 클릭.

4. 뒤로 돌아간 후 OpenSSH 클라이언트 설치된 것 확인.

5. 윈도우 재시작. (재시작을 한 후 로그인 해야 시스템에 적용됨.)

6. cmd 창 열고 ssh 실행해서 설치여부 확인.

7. cmd 창에서 ssh 접근 확인. (처음 접근 시 yes 타이핑 하고 시작.)

  • 접속방식은 아래와 같다.
 ssh 아이디@서버아이피
 또는
 ssh 서버아이피 -l 아이디
 
 포트가 다를경우 
 ssh 아이피 -l 아이디 -p 포트

기타. Windows 10 호스트 키 저장경로

%UserProfile%.ssh\known_hosts

  • 접속하는 서버를 재설치 하거나 설정 변경이 있는 경우 호스트 키가 일치하지 않아 접속에 문제가 생길 수 있음.
  • 윈도우 10에 설치된 OpenSSH의 호스트키는 위 경로에 저장되며 편집 가능하다.
  • 만일 접속에 문제가 생기는 서버가 있을 경우 편집기에서 확인 후 해당 라인을 삭제한 다음 저장 후 재접속을 하면 된다.

참고자료:

  • https://amorfati-1000.tistory.com/50
  • https://extrememanual.net/12589

내 계정으로 GitHub에 Commits 시 contributions 그래프(초록색 타일)이 표시가 되지 않는 경우 확인할 것.

|

GitHub은 커밋이 연결된 사용자의 이메일을 사용한다.
만약 계정에 등록된 이메일과 로컬 git config 설정에 등록된 이메일의 정보가 다를 경우 Github contributions 그래프 (초록색 타일) 에 표시되지 않는다.

뭔가 작업을 열심히 했는데 그래프에 나오지 않으면 조금 기분이 그러하고 억울하기에 아래와 같이 확인해본다.

로컬에서 확인할 것

$ git config user.email

jmlim@xxx.com

이메일이 맞지 않을경우 전역 설정 변경하기.

$ git config –global user.email “jmlim@xxx.com”

깃헙에서 확인할 것.

1 . 로그인 후 계정의 settings의 email 메뉴 선택.

(url : https://github.com/settings/emails)

[이미지 참고]

2. GitHub 계정에서 연결된 이메일을 확인 후 로컬에 등록한 이메일이 없으면 Add email address로 추가.

  • Add email address 로 등록 후 해당 이메일로 아래 해당제목의 메일이 갈 것임.

    [GitHub] Please verify your email address.

3. 클릭 후 확인

다시 github 에 접근해보면 그동안 커밋했던 내용이 contributions 그래프에 보여지게 될 것이다.


AWS EC2 Linux(Amazon Linux) 에 메이븐(maven) 설치하기

|

AWS EC2 Linux(Amazon Linux) 에 메이븐(maven) 설치하기.

아주 간단한 방법으로 Maven을 설치 가능하다.

sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
sudo yum install -y apache-maven
mvn --version

출처:

  • https://gist.github.com/sebsto/19b99f1fa1f32cae5d00

Ubuntu-18.04 데스크탑에서 Intellij 사용시 한글텍스트 깨짐 현상 해결

|

인텔리제이 설치 후 파일을 열어본 순간 한글이 네모로 나오는 현상이 발생하였다.

아래 블로그를 참고 후 해결하였다.

sudo apt install fonts-nanum
sudo fc-cache -fv
  1. 나눔 이라는 단어가 포함된 글꼴 패키지 모두 설치
  2. 글꼴 캐시 삭제 (시간이 오래 걸린다.)
  3. 리부팅 (글꼴 캐시 삭제 후 알림창 나옴.)

출처 :

  • http://memo.polypia.net/archives/3043
  • https://followers.tistory.com/26

docker를 통한 ngrinder 설치 및 사용

|

nGrinder 란?

네이버에서 성능 측정 목적으로 개발된 오픈소스 프로젝트이며, The Grinder라는 오픈소스 기반으로 개발되었다. nGrinder는 서버에 대한 부하 테스트를 하는 것으로 서버의 성능을 측정할 수 있다.

nGrinder Architecture

Controller, Agent, Target 서버로 나누어져 있음.

[ngrinder system 아키텍쳐 이미지]

Controller

  • 퍼포먼스 테스팅(부하테스트)를 위해 웹 인터페이스를 제공
  • 테스트 프로세스를 체계화
  • 테스트 결과를 수집해 통계로 보여줌

Agent: Controller의 명령을 받아 실행.

  • agent 모드가 실행될 때 target이 된 머신에 프로세스와 스레드를 발생시켜 부하를 발생.
  • moniter 모드가 실행되면 대상 시스템의 cpu와 memory를 모니터링.

Target: 부하테스트를 받는 머신.

Controller 및 Agent 설치 및 설명 (도커로 설치)

docker

직접 다운받아서 설치해도 되지만 귀찮은 것들을 만져줘야하는 번거로움이 있어 docker사용을 추천한다.

docker 설치 참고 자료

  • mac: https://docs.docker.com/docker-for-mac/install/
  • windows: https://docs.docker.com/docker-for-windows/install/
  • linux: https://docs.docker.com/install/linux/docker-ce/centos/

nGrinder controller:

$ docker run -d -v ~/ngrinder-controller:/opt/ngrinder-controller -p 8080:80 -p 16001:16001 -p 12000-12009:12000-12009 ngrinder/controller:3.4

grinder controller는 포트옵션으로 웹 포트, 에이전트와의 연결, 부하관리를 위한 포트들로 구성되어 있으며 자세한 내용은 https://hub.docker.com/r/ngrinder/controller/ 에서 확인할 수 있다.

nGrinder agent:

$ docker run -v ~/ngrinder-agent:/opt/ngrinder-agent -d ngrinder/agent:3.4 controller_ip:controller_port
agent는 controller_ip:controller_webport 부분을 옵션 argument로 전달해야 한다.

ex) controller가 떠있는 instance의 public ip가 192.168.100.12이고 80번 포트를 웹포트로 열었다면 “192.168.100.12:80” 을 뒤에 붙여주면 된다.

실행하기

브라우저에서 아래 주소 입력

http://controller_ip:port
id: admin
pw: admin

Agent: Any ==> Controller: 16001 Agent: Any ==> Controller: 12000 ~ 1200x

==> 는 단방향 통신을 뜻함.

16001 : 테스트를 하지 않은 에이전트가 컨트롤러에게 “할 일 없으니 테스트 가능” 이라는 메세지를 알려주는 포트

  • 컨트롤러는 “테스트가 실행하는데 해당 테스트는 1200x에서 발생하니, 해당 포트에 접속해서 테스트 실행 준비” 라는 메세지를 에이전트에게 지시를 한다.

12000 ~ 1200x 포트는 “테스트 실행, 테스트 종료” 와 같은 컨트롤러 명령어와 에이전트별 테스트 실행 통계를 초별로 수집하는 포트.

nGrinder 관련 용어 설명

[ngrinder 설정화면]

  • vuser : virtual user로 동시에 접속하는 유저의 수를 의미. (vuesr = agent * process * thread)
  • TPS : 초당 트랜잭션의 수 - 초당 처리 수
  • 트랜잭션 : HTTP Request가 성공할 때마다, 트랜잭션 수가 1씩 증가.
  • Peak TPS : 초당 처리 수의 최대치.
  • Response Time : 사용자가 request한 시점에서 시스템이 response할 때까지 걸린 시간.
  • Think Time : 사용자에게 전달된 정보는 사용자가 해당 내용을 인지하고 다음 동작을 취할 때까지의 생각하는 시간

스크립트

  • nGrinder 에 로그인을 하면 스크립트 관리 화면에서 SVN URL을 확인할 수 있다. (ngrinder 에 svn 이 내장되어있다.)

[ngrinder 스크립트 관리]

예를 들어 위 화면에서는 http://ngrinder-staging.nhncorp.com:8080/svn/admin/project SVN URL로 Groovy 메이븐 프로젝트를 접근할 수 있다.

인텔리제이에서 엔그라인더의 그루비 스크립트 메이븐 임포트 시 참고자료

  • https://github.com/naver/ngrinder/wiki/Import-Groovy-Maven-Project-in-IntelliJ

스크립트 작성 시 참고

  • https://junoyoon.tistory.com/entry/Groovy-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B5%AC%EC%A1%B0?category=487801

인텔리제이에서 엔그라인더 그루비 스크립트 실행 시 vm option 에 추가.

-javaagent:/Users/jmlim/.m2/repository/net/sf/grinder/grinder-dcr-agent/3.9.1/grinder-dcr-agent-3.9.1.jar

Get, Post 요청 샘플 스크립트.

  • application/json 요청 (jsonBody 형태의 요청일 경우..)
import HTTPClient.Cookie
import HTTPClient.CookieModule
import HTTPClient.HTTPResponse
import HTTPClient.NVPair
import groovy.json.JsonOutput
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

import static net.grinder.script.Grinder.grinder
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat

/**
 * ngrinder get, post 요청 샘플 스크립트.
 */
@RunWith(GrinderRunner)
class SampleGetPostRunner {

    public static GTest test
    public static HTTPRequest request
    public static NVPair[] headers = []
    public static Cookie[] cookies = []

    static String url = "http://테스트할아이피:포트"
    static String commonPath = "/test"

    static String findUrl = url + commonPath + "/find";
    static String createUrl = url + commonPath + "/create"

    @BeforeProcess
    static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test = new GTest(1, "api.test.com")
        request = new HTTPRequest()
        // Set header datas
        List<NVPair> headerList = new ArrayList<NVPair>()
        headerList.add(new NVPair("Content-Type", "application/json"))
        headerList.add(new NVPair("Authorization", "Bearer 토큰토큰"))

        headers = headerList.toArray()
        grinder.logger.info("before process.");
    }

    @BeforeThread
    void beforeThread() {
        test.record(this, "test")
        grinder.statistics.delayReports = true;
        grinder.logger.info("before thread.");
    }

    @Before
    void before() {
        request.setHeaders(headers)
        cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
        grinder.logger.info("before thread. init headers and cookies");
    }

    @Test
    void test() {
        find();

        create();
    }


    /**
     * get test
     */
    void find() {
        HTTPResponse result = request.GET(findUrl)
        resultCheck(result)
    }

    /**
     * post test
     */
    void create() {
        String goodsId = "1234";
        Integer quantity = 2;

        Map<String, Object> paramData = new HashMap<>();
        List<Map<String, Object>> items = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        item.put("goods_id", goodsId);
        item.put("quantity", quantity);
        items.add(item);
        paramData.put("items", items);

        String json = JsonOutput.toJson(paramData);
        HTTPResponse result = request.POST(createUrl, json.getBytes())
        resultCheck(result)
    }

    /**
     * 결과값 체크
     * @param result
     */
    void resultCheck(result) {
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
}

  • 일반 form post 요청일 경우
import HTTPClient.HTTPResponse
import HTTPClient.NVPair
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

import static net.grinder.script.Grinder.grinder
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat

/**
 * ngrinder get, post 요청 샘플 스크립트.
 */
@RunWith(GrinderRunner)
class TestRunner {

    public static GTest test
    public static HTTPRequest request
    public static NVPair[] headers = []

    static String url = "https://타겟url"
    static String commonPath = "/api"

    static String appinitUrl = url + commonPath + "/app"

    @BeforeProcess
    static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test = new GTest(1, "타겟명")
        request = new HTTPRequest()
        grinder.logger.info("before process.");
    }

    @BeforeThread
    void beforeThread() {
        test.record(this, "test")
        grinder.statistics.delayReports = true;
        grinder.logger.info("before thread.");
    }

    @Before
    void before() {
        request.setHeaders(headers)
    }

    @Test
    void test() {
        NVPair param1 = new NVPair("appcode", "999");
        NVPair param2 = new NVPair("appversion", "x");
        NVPair param3 = new NVPair("hashcode", "1122334455667788");

        NVPair[] params = [param1, param2, param3]

        HTTPResponse result = request.POST(appinitUrl, params)
        resultCheck(result)
    }

    /**
     * 결과값 체크
     * @param result
     */
    void resultCheck(result) {
        println result
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
}

  • 파일 업로드 post 요청 (multipart/form-data)
import HTTPClient.Codecs
import HTTPClient.HTTPResponse
import HTTPClient.NVPair
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

import static net.grinder.script.Grinder.grinder
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat

/**
 * ngrinder get, post 요청 샘플 스크립트.
 */
@RunWith(GrinderRunner)
class MultipartTestRunner {

    public static GTest test
    public static HTTPRequest request
    public static NVPair[] headers = []

    static String url = "http://타겟url"
    static String commonPath = "/api"

    static String moreUploadUrl = url + commonPath + "/upload-app-file"

    @BeforeProcess
    static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test = new GTest(1, "http://타겟명")
        request = new HTTPRequest()
        grinder.logger.info("before process.");
    }

    @BeforeThread
    void beforeThread() {
        test.record(this, "test")
        grinder.statistics.delayReports = true;
        grinder.logger.info("before thread.");
    }

    @Before
    void before() {
        headers = [
                new NVPair("Content-Type", "multipart/form-data")
        ]
        request.setHeaders(headers)
    }

    @Test
    void moreUpload() {
        NVPair param1 = new NVPair("appcode", "999");
        NVPair param2 = new NVPair("appversion", "17");
        NVPair param3 = new NVPair("hashcode", "11223344556677");

        NVPair[] params = [param1, param2, param3]

        NVPair[] files = [new NVPair("userfile", "경로/파일명.확장자")]

        def data = Codecs.mpFormDataEncode(params, files, headers)
        HTTPResponse result = request.POST(moreUploadUrl, data)

        resultCheck(result)
    }

    /**
     * 결과값 체크
     * @param result
     */
    void resultCheck(result) {
        println result
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
}

참고자료:

  • https://brownbears.tistory.com/25
  • http://blog.naver.com/PostView.nhn?blogId=simpolor&logNo=221318391959&parentCategoryNo=&categoryNo=27&viewDate=&isShowPopularPosts=true&from=search
  • https://junoyoon.tistory.com/entry/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4%EC%97%90-Groovy-%EB%A9%94%EC%9D%B4%EB%B8%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%9E%84%ED%8F%AC%ED%8A%B8
  • https://gist.github.com/ihoneymon/a83b22a42795b349c389a883a7bbf356