sudo 설정하기

|

sudo의 필요성

Linux 환경에서 sudo는 root계정으로 로그인하지 않은 상태로 sudoers의 설정에 따라 특정 명령을 사용할 수 있도록 해줍니다.
이에 따라 서버 관리자들이 root계정 사용을 최소화 하고 sudo를 이용하여 작업함으로써 누가 어떤 커맨드를 사용했는지 추적이 가능해집니다.

[acct 미설치시]
$ dpkg -l | grep acct
$ sudo apt-get install acct
[사용자 커맨드 추적]
$ lastcomm -u {사용자ID}

설정 수정방법

/etc/sudoers 에 각 사용자별 사용 가능한 설정이 포함되어 있으나 vi로 직접 수정하기 보다는 visudo 명령으로 수정하기를 권장합니다.

$ visudo

visudo edit

사용자 설정

특정 사용자에게 명령 권한을 설정할 때는 아래와 같이 설정 할 수 있습니다.

(user) (host)=(runUser[:runGroup])  [option:](command)

# User privilege specification
root    ALL=(ALL:ALL) ALL
# kimjh 사용자는 password입력없이 모든 명령을 실행 할 수 있습니다.
kimjh   ALL=(ALL) NOPASSWD:ALL
# docker 계정은 localhost에서 vi명령어를 admin그룹의 kimjh 계정의 권한으로 실행할 수 있습니다.
docker  localhost=(kimjh:admin) /usr/bin/vi

그룹 설정

특정 그룹에 명령 권한을 설정할 때는 아래와 같이 설정 할 수 있습니다.

%(group) (host)=(runUser[:runGroup])  [option:](command)

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# docker 그룹에 속한 계정은 localhost에서 vi명령어를 admin그룹의 kimjh 계정의 권한으로 실행할 수 있습니다.
%docker  localhost=(kimjh:admin) /usr/bin/vi

Alias 설정

Alias는 특정 호스트나 유저, 커맨드등을 하나로 묶어 Alias형태로 제공하고 아래와 같은 형식으로 정의합니다.

Alias_Type NAME = item1, item2 ....
Alias_Type NAME1 = item1, item2 .... : NAME2 = item3, item4 ....
  • Alias_Type은 User_Alias, Runas_Alias, Host_Alias, Cmnd_Alias를 사용할 수 있습니다.
  • NAME은 대문자 영문, 숫자, 언더바(_)문자를 사용합니다.
  • 같은 Alias_Type을 두번째 예시와 같이 세미콜론(:)을 이용하여 여러개의 이름을 지정할 수 있습니다.

User_Alias

특정 계정이나 그룹을 Alias로 지정하여 할 수 있으며 해당 Alias를 이용하여 여러 사용자의 권한을 제어할 수 있습니다.

User_Alias NAME = user1, user2 ....

user정보는 아래와 같이 사용 가능합니다.

  • userName
  • #uid
  • %groupName
  • %#gid
  • Other_User_Alias
  • ! (해당 계정이 아닌 사용자)

    # 계정명이 kimjh, uid가 1000, 그룹명이 docker, gid가 899이거나 # 계정명이 guest, uid가 1001이 아닌 계정 User_Alias MANAGER = kimjh, #1000, %docker, %#899, !guest, !#1001 # 계정명이 agent이거나 MANAGER Alias에 속한 계정 User_Alias AGENT = agent, MANAGER

또한 user정보에 특수문자가 포함되거나 공백이 포함될 경우 더블쿼터(“)로 묶어 사용하거나 백슬래쉬()를 이용할 수 있습니다.

Runas_Alias

어떤 계정의 권한으로 명령을 실행 할 지에 대한 Alias설정을 지정합니다.
아래 예제와 같이 설정 된 상태에서 docker계정으로 vi명령을 실행 할 경우 Runas_Alias에 설정된 계정으로 실행되게 됩니다.

[/etc/sudoers]
# User privilege specification
docker ALL=(R_ROOT) NOPASSWD:/usr/bin/vi

docker@kimjh:$ sudo vi test
docker@kimjh:$ ls -atrl test
-rw-r--r-- 1 root root 22  2월 24 11:28 test

문법은 User_Alias와 동일하게 사용합니다.

Host_Alias

특정 실행권한에 대한 host를 Alias로 설정하여 허용되지 않은 사용자가 원격 접속하여 실행하는 것을 방지하기 위해 지정합니다.
아래 예제와 같이 설정 된 상태에서 docker계정은 211.63.24.9번 IP나 kimjh 호스트 서버에서만 vi명령을 실행 할 수 있습니다.

[/etc/sudoers]
# Host alias specification
Host_Alias H_LOCAL = 211.63.24.9, kimjh

# User privilege specification
docker H_LOCAL=(ALL) NOPASSWD:/usr/bin/vi

Cmnd_Alias

실행 명령어를 Alias로 지정할 수 있습니다.
아래 예제와 같이 설정 된 상태에서 docker계정은 vi명령과 vim명령을 실행 할 수 있습니다.

# Cmnd alias specification
Cmnd_Alias CMD_VIM = /usr/bin/vi, /usr/bin/vim

# User privilege specification
docker    ALL=(ALL:ALL) CMD_VIM

참고

sudo 사용의 필요성
Linux/Unix로그 파일
sudoers 파일
sudoers(5) - Linux man page
How To Edit the Sudoers File on Ubuntu and CentOS

TED영상을 보고 30일 동안 매일 블로그 포스팅 도전 후기

|

아래 TED의 영상을 보고 ‘30일동안 매일 블로그 포스팅하기’에 도전한 후기입니다.

TED - “맷 커츠: 30일동안 새로운 것 도전하기”

시작

출근길에 TED영상을 보다가 3분채 안되는 이 영상을 보게 되었습니다.
30일이면 나 자신을 바꾸기에 충분한 시간이라는 말에 홀려 가볍게 해 볼 수 있는게 무엇이 있을까 생각하다가 근래 만들었던 GitHub Page에 기술 포스팅을 해보는 것으로 정했습니다.
지금 생각해보면 절대 가볍게 할 수 있는 일은 아니였습니다.

고비

첫 날은 기존부터 개념 정리차 공부하고 있던 Agile관련 포스팅을 진행했습니다.
글 쓰는게 익숙하지 않았지만 그래도 공부했던 내용들이 있어 나름 어렵지 않게 포스팅 했습니다. 하지만 고비는 그 이후였습니다.
아무 글이나 쓰는게 아닌 기술 포스팅을 하려니 먼저 공부를 해야했고 그 내용을 정리해서 글을 올려야 했습니다.
에버노트에 쓰는건 나만 알아보게 대충 적어놓으면 되는 것이였으나 포스팅은 인터넷에 검색되어 남도 보게 되는 글이기 때문에 몇 번이고 사실 검증을 해야했고 두서 없이 필요한 글만 올릴 수도 없었습니다. 게다가 코딩은 익숙했지만 정리해서 포스팅하는것은 익숙하지 않아 생각보다 많은 시간이 걸렸습니다.(이 때문에 야근도 하게 되었죠…)

한 두번은 쉬워 보이는 일도 매일 쭉 한다고 정해놓고 하게 되니 절대 쉬운게 아니였습니다. 특히 “매일”이라는 조건이 가장 어려웠습니다.

변화

“공부-일-포스팅”을 동시에 하기 위해 매일 1시간, 최소 30분 이상 일찍 출근해서 공부했으며 출퇴근 길에도 항상 윈탭으로 기술문서를 읽고 검색하게 되었습니다.
일하는 도중에도 “이건 오늘 정리해서 포스팅하자.”하는 부분을 다시 공부하고 검증하게 되었으며 일이 바쁠땐 집에가서 새벽까지 문서를 쓰기도 했습니다.
매일 글을 쓰다보니 처음보다 생각의 정리나 글 쓰는 요령이 붙어 속도가 나기 시작하고 기술에 대해서도 필요한 부분만 확인하는것이 아니라 해당 기술에 대한 다른 부분까지 같이 검토하게 되는 습관이 생겼습니다.
출퇴근길에 의미없이 하던 인터넷 서핑도 자기 개발에 투자하게 되었으며, 에버노트에 간단히 메모해두고 잊혀졌던 했던 잡 지식들도 차츰 정리할 수 있었습니다.

결과

이 포스팅을 끝으로 30일간의 도전은 성공하였으며 오늘 저녁은 아름답게 치맥을 먹으며 마무리 할 수 있을것 같습니다. 30일간의 도전으로 얻었다고 생각드는 것은 아래와 같습니다.

  • 글쓰기에 대한 부담감이 없어지고 글 쓰는 실력이 늘었습니다.
  • 공부했거나 사용한 기술에 대해 한번 더 생각하고 정리하는 습관이 들었습니다.
  • 매일 공부하는 습관을 들이고 발전적인 삶을 사는 것에 대한 보람을 느꼈습니다.
  • 기술에 대해 누군가와 논의할 때 좀 더 생각을 정리하고 더 잘 표현하게 된 것 같습니다.

부작용

“매일”이라는 압박 때문에 아래와 같은 부작용이 있었습니다.

  • 공부한 내용을 포스팅하지 않고 포스팅을 하기 위한 공부를 할 때가 있었습니다.(주객전도…)
  • 하루에 하나씩 올려야 하기 때문에 깊이 있는 내용을 쓰기가 어려웠습니다.
  • 정말 공부를 할 수 없는 상황이 됬을때를 대비해 미리 포스팅을 축적하기도 했습니다.

회고

이번 도전을 하면서 최근 한달은 정말 열심히 공부했던것 같습니다. 흡사 시험전날 벼락치기를 30일동안 한 기분같기도 합니다.
일이 바빠져서 포기하려 할 때도 있었으나 와이프가 옆에서 응원해주어 포기하지 않고 끝까지 해 낼수 있었고 작은 것 하나를 도전했는데 정말 큰 결실을 맺은것 같습니다. 이 도전은 끝났지만 30일동안의 변화를 경험했기 때문에 다른 도전도 계획해보려합니다.
이 포스팅을 보시는 분들도 작은것 하나라도 도전해보시면 나 자신을 변화 시키는데 큰 도움이 될 거라 생각합니다.

(SpringBoot) Enum 사용하기 - `@Enumerated`

|

OKKY-enum 활용에서 enum 공통모듈까지 글을 보고 괜찮다 싶어서 제가 만들어 놨던 프로젝트에 적용해 보았습니다.

우선 아래와 같이 enum을 선언했습니다.

    public enum EnumCode
    {
        L("대형"),  // name : L, ordinal : 0
        M("중형"),  // name : M, ordinal : 1
        S("소형");  // name : S, ordinal : 2

        private String description;

        StoreTypeCode(String description)
        {
            this.description = description;
        }

        public String getDescription()
        {
            return description;
        }
    }
    

Entity객체를 통해 테이블 컬럼에 ‘L’, ‘M’, ‘S’값을 저장할 예정이며 아래와 같이 설정하였습니다.

    @Enumerated(value = EnumType.STRING)
    @Column(name="enum_code", nullable = false)
    private EnumCode enumCode;
    

위와 같이 @Enumerated 어노테이션을 선언해주면 해당 컬럼은 enum객체를 이용하여 저장하겠다고 선언이 되며 value값은 아래와 같이 설정 할 수 있습니다.

  • EnumType.ORDINAL
    • enum객체의 ordinal()메서드를 이용하여 컬럼값을 저장합니다.
  • EnumType.STRING
    • enum객체의 name()메서드를 이용하여 컬럼값을 저장합니다.

Repository에서는 아래와 같이 enum객체를 이용하여 불러오기가 가능해집니다.

    @Repository
    public interface IJpaRepository extends JpaRepository<MyEntity, Long>
    {
        List<MyEntity> findByEnumCode(EnumCode code);
        Page<MyEntity> findByEnumCode(EnumCode code, Pageable pageable);
    }
    

Controller에서도 자동 맵핑이 가능한데 @InitBinder를 이용할 수 있습니다.

    @ControllerAdvice
    public class CommonDataBinder
    {
        @InitBinder
        public void enumCodeBinding(WebDataBinder binder)
        {
            binder.registerCustomEditor(EnumCode.class, new PropertyEditorSupport() {
                @Override public void setAsText(String text) throws IllegalArgumentException
                {
                    super.setValue(EnumCode.valueOf(text));
                }
            });
        }
    }

    public class ParamForm
    {
        private EnumCode storeTypeCode;

        public EnumCode getEnumCode()
        {
            return enumCode;
        }

        public void setEnumCode(EnumCode enumCode)
        {
            this.enumCode = enumCode;
        }
    }

    @RestController
    public class MyEntityController
    {
        @RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json")
        public List<MyEntity> getList(ParamForm form)
        {
            return jpaRepository.findByEnumCode(form);
        }
    }
    

위와 같이 설정하면 ‘/list?enumCode=L’과 같이 호출시 enumCode는 @InitBinder구문에 의해 자동으로 enum객체로 변경되어 파라메터 맵핑됩니다.

참고

OKKY-enum 활용에서 enum 공통모듈까지 JPA and Enums via @Enumerated

Gradle 설치 및 초기화

|

Gradle에 대해서 공부한 내용을 요약한 포스팅입니다.

Gradle 이란?

보통 Maven의 장점과 Ant의 장점을 합쳐 놓은 빌드 툴로 불리우는데 XML대신 Groovy DSL로 작성되어 라인수가 훨씬 적고 task단위로 만들어 실행 할 수 있으며 개발자가 필요한 빌드로직을 조합하여 사용 가능합니다. 그리고 Gradle Wrapper를 사용하여 Gradle이 설치되지 않은 환경에서도 빌드 가능합니다.

설치방법

Gradle 수동설치 링크에서 Install 설치파일을 다운받아 풀고 GRADLE_HOME, 실행파일 경로를 PATH에 잡아주면 됩니다.
추가로 윈도우 환경에서 UTF-8 빌드환경을 만들기 위해 아래와 같이 GRADLE_OPTS을 설정합니다.

GRADLE_OPTS="-Dfile.encoding=UTF-8"

초기화하기(cmd)

명령어를 통해 gradle을 초기화 하는 방법은 아래와 같습니다.

$ gradle init --type java-library

type값은 basic, java-library, pom, groovy-library, scala-library가 있습니다.
위와 같이 실행하면 gradle의 기본 설정 생성과 함께 src 기본폴더가 생성됩니다.(자세한 구조는 아래 IDE에서…)

초기화하기(IntelliJ)

  • Gradle타입의 새 프로젝트를 선택하고 Next를 클릭합니다.

new project

  • GroupId, ArtifactId를 선택하고 Next를 클릭합니다.

new project

  • 필요한 선택항목을 선택 후 Next를 클릭합니다.

new project

  • Use auto-import : dependency 추가시 자동으로 import하는 옵션입니다.
  • Create directories for empty content roots automatically : 이 항목을 선택하면 자동으로 src폴더와 하위 구조가 생성됩니다.
  • Use default gradle wrapper : Gradle Wrapper를 생성해줍니다.(gradlew.bat …)
  • Use gradle wrapper task configuration : Gradle Wrapper를 task를 통해 실행할 수 있도록 스크립트를 만듭니다.

new project

  • Use local gradle distribution : 로컬에 설치한 gradle경로를 잡아줍니다.

  • 선택후 Finish를 누르면 다음과 같이 gradle기반 JavaProject가 생성됩니다.

new project

new project

※ 위 커맨드 명령어로 만들때도 같은 구조로 생성됩니다.

그 외에 template을 기반으로 하는 프로젝트 생성 방법은 Gradle 기반의 템플릿 프로젝트 생성을 참고하시기 바랍니다.

참고

권남 - Gradle
Gradle Build - Installation

대용량 처리를 위한 서비스 구성

|

일반적인 서비스의 기본구성

일반적인 서비스 구성이 위와 같은 상황에서 Client가 늘어날 경우 웹 서버나 DB서버에서 병목현상이 발생할 수 있으며 병목지점별로 해결 방안이 필요합니다.

Web서버 확장

Web서버가 stateless한 구조일 경우 아래와 같이 다수의 Web서버를 두어 부하를 분산 시킬 수 있습니다.

병목현상해결을 위한 웹 서버의 확장

stateful : 서버쪽에 client와 server의 연속된 동작 상태정보를 저장하는 형태
stateless : 서버쪽에 client와 server의 연속된 동작 상태정보를 저장하는 않는 형태

DB 확장

일반적인 서비스의 DB구성

DB구성이 위와 같을 경우 성능향상을 위해 “Scale Up”과 “Scale Out”을 고려해 볼 수 있습니다.

scale up : 장비의 성능을 높여 성능향상
scale out : 장비의 개수를 늘려 성능향상

DB Read/Write 분리,분산

대부분의 서비스는 Read가 Write보다 대략 7:3, 8:2비율로 더 많은데 이럴때 Read/Write DB를 분리하면 DB서버의 부하를 줄일 수 있습니다.

Read/Write DB 분리

일반적으로 Master DB를 Write, Replication되는 Slave DB를 Read로 사용하는데 기본적으로 4대로 구성합니다.

Master : Write
Slave 1,2,3 : Read
1번서버 장애시 2번서버는 서비스를 하며 3번 서버는 서비스를 중단하고 1번서버 복구를 위한 DB복사를 진행해야합니다.
서비스중인 DB에서 복사시 부하가 가중되므로 여분의 DB가 필요합니다.

  • Eventual Consistency

    Master의 내용을 Slave로 Replication하는 과정은 동기/비동기 방식이 있는데 비동기식일 경우 데이터 불일치가 발생할 수 있습니다.
    불일치하더라도 시간이 지나면 데이터가 같아지는데 이를 “Eventual Consistency”라고 합니다.
    데이터 일관성이 중요한 경우 Read를 분리할때 위와 같은 문제점을 인지해야합니다.

Read/Write DB 분기방식으로는 아래와 같은 방법이 있습니다.

  • DBProxy 서버를 이용
    • 프록시 서버가 쿼리를 분석하여 select시는 Read서버, 그 외엔 Master서버로 분기해줍니다.
    • MySql Proxy, MaxScale …
  • MySql Replication Jdbc Driver 사용
    • Jdbc Driver상에서 내부적으로 readonly 옵션에 따라 Master/Slave장비를 선택해줍니다.
    • MySQL에서ReplicationDriver사용시장애취약점리포트-기능테스트” 포스팅을 보시면 취약점에 대한 테스트 결과및 Oracle측의 답변이 있습니다.(2012년도 포스팅이니 현재는 해결이 됬는지 모르겠습니다.)
    • 권남 - MySQL JDBC“에도 역시 여러가지 문제점들이 도출되어 있습니다.
    • 결론적으로 사용 안하는 쪽이 나을듯 합니다.
  • Spring LazyConnectionDataSourceProxy + AbstractRoutingDataSource 사용
    • Spring에서 Transaction readonly 옵션을 사용하여 분기하는 방법입니다.
    • AbstractRoutingDataSource : 여러개의 DateSource를 하나로 묶고 자동 분기처리
    • LazyConnectionDataSourceProxy : 트랜잭션 시작되더라도 실제 커넥션이 필요한 경우에 데이터소스에서 커넥션을 반환

Write증가시 파티셔닝

write가 증가하게 되면 Master로부터 Replication을 받기 위해 Slave의 write IO가 증가하게 됩니다.
그렇게 되면 Read Slave를 아무리 늘려도 성능개선이 미미해지는데 이럴때는 Write를 줄이는 파티셔닝을 해야합니다.

Read/Write DB 분리

  • 파티셔닝(Partitioninig)

    • 성능,가용성,정비용이성을 목적으로 논리적 데이터 요소들을 다수의 테이블로 쪼개는 행위
    • 수직분할(Vertical Partitioninig)
      • 테이블의 Column 단위로 파티셔닝하는 방법
      • 스키마가 서로 달라집니다.
    • 수평분할(Sharding : Horizontal Partitionning)
      • 테이블의 Row 단위로 파티셔닝하는 방법
      • 스키마는 동일합니다.
  • 파티션 방법

    • 수동 파티셔닝 : 분석된 테이블 정보를 이용하여 파티션 뷰를 직접 생성
    • 파티션 테이블 :
      • Range 파티셔닝
        • 특정 기간 별로 파티션을 나눔
        • 주로 날짜조건 사용
      • Hash 파티셔닝
        • Hash함수에 적용한 결과값이 같은 레코드별로 나눔
        • 변별력 좋고 데이터분포가 고른 컬럼을 선정해야 효과적
      • List 파티셔닝
        • 사용자에 의해 미리 정해진 그룹핑 기준에 따라 분할
      • 결합 파티셔닝
        • 위 파티션 기법을 조합하여 사용
    • 자세한 설정방법은 구루비 DB 스터디 - 1. 테이블 파티셔닝을 참조

참고

대용량 서버구축을 위한 Memcached와 Redis
Java 에서 DataBase Replication Master/Slave (write/read) 분기 처리하기
MySQL에서 Replication Driver 사용 시 장애 취약점 리포트
권남 - MySQL JDBC
H2DB - LazyConnectionDataSourceProxy 예제
구루비 DB 스터디 - 1. 테이블 파티셔닝
오라클 성능 고도화 원리와 해법 2 [11-1B]