반응형
반응형

이번에 m1 칩셋이 출시되면서, 맥 제품들 가격군은 내려가고 성능도 좋아졌는데 굳이 기존 2017년도 구형 맥 장비를 초기화하고 테스트를 해달라는 요청이 있어서, ruby / gem을 이용해서 fastlane을 설치하다가 발생한 이슈를 정리해보기로 했다.

 

파일들만 대충 백업하고, 공장초기화를 진행했고, fastlane에서 제공하는 공식 설치 가이드(https://docs.fastlane.tools/getting-started/ios/setup/)를 참고해서 진행을 했다.

 

xcode-select 키워드를 이용한 Command line Tools는 정상적으로 진행되었는데,

이후에 gem 명령어를 사용하는 부분이 문제가 있었고, 로그를 분석해보니 ruby/config.h를 못찾는 문제가 있었다.

결국 뭔가 환경적인 문제라는 이야기인데...

 

OSX가 최근 Bigsur로 올라가게 되면서, 대부분의 라이브러리가 그 기준을 따라가게 된 것이 문제라는 생각이 들어, 구글링을 해보다보니, 심볼릭 링크로 연결만 만들어주면 해결이 되는 것으로 보였다.

(맥은 역시 어렵다.. ㅠㅠ)

 

결론은, 아래와 같은 부분에 대해 조치가 필요하다.

// 이 경로에 보면, universal-darwin20만 있을텐데, 우리는 universal-darwin19를 만들어줘야 한다.
$ cd /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0

// universal-darwin19라는 이름으로 링크 걸기
$ ln -s universal-darwin20 universal-darwin19

 

이후에 gem을 이용한 모든 설치 및 업데이트가 정상화된 것 처럼 보였다.

하지만, 명령어 창에 fastlane을 입력하면 아무것도 안나왔고, 이 부분도 조금 살펴보니 /usr/bin 폴더에 cp나 ln을 사용할 수 없는 것이 원인이었다.

 

해결방법은 복구모드로 들어가서, SIP라는 애를 disable 시키면 된다고는 하는데.. 맥에서 괜히 enable을 default로 해둔것이 아닌가 싶어서, 환경변수로 해결하기로 함.

 

환경변수는 대략 아래처럼 만들어주면 된다. 

CI툴이나 기타 솔루션에서 fastlane을 이용할 계획이 있다면, 가급적 root에 설정하는 것이 좋음.

(해당 툴에서 명령어 실행할 계정을 명시할 수 있다면 굳이 안해도 됨)

$ cd ~
$ vi .bash_profile

# .bash_profile

# fastlane 버전은 적용시점마다 다를 수 있으니, 본인 환경에 맞게 수정해야 함.
export PATH=${PATH}:/Library/Ruby/Gems/2.6.0/gems/fastlane-2.176.0/bin/

 

반응형
,
반응형

이번에 WD 10TB를 구매하면서, 기존에 예비용으로 쓰던 USB 2TB 외장하드의 데이터를 10TB로 옮기는 작업을 주말간 진행했다.

 

그동안 모았던 자료들은 대부분 RSS 통해서 받아진 자료이고, 관리를 안하다보니 2TB를 거의 꽉 채운 데이터를 옮겨야 했었고, GUI 환경은 아무래도 대용량 처리에 적합하지 않으므로 rsync로 옮기기로 결정!

 

데이터가 적을 경우에는 Rsync를 쓰던 cp를 쓰던 mv를 쓰던 사실 문제가 안된다.

하지만, 우리에겐 시간이 없기 때문에 최대한 고효율을 뽑아야 한다.

 

그러던 중 머리에 떠오른 것은 Rsync가 Single Thread이지만, xargs를 통해 Multiple Thread처럼 처리를 하면 좀 더 효율적이지 않을까하는 생각이 들었다. (결국은 Disk-iops의 한계가 있으므로 Thread를 적정량 설정해야 하겠다.)

 

아래와 같은 커맨드를 이용해봤다.

Source Directory의 목록을 ls로 가져오고, xargs를 통해 rsync와 연계하는데 쓰레드는 5개로 세팅

 

$ ls /root/2T/Video/TV | xargs -I {} -P 5 -n 1 rsync -avz --progress /root/2T/Video/TV/{} /root/10T/Video/TV/

 

이 명령어를 실행하고, 프로세스 목록을 보면, 서브프로세스들이 생성되며 작업을 잘 나눠서 처리하는 것을 볼 수 있다.

앞으로도 효율적인 대용량 데이터 처리에 참고해야겠다.

반응형
,
반응형

코로나19 때문에 집에서 한 2주를 쉬니깐(회사에서 나오지 말라고 했음), 게임만 하다가 지쳐서.. 공부를 해야겠다는 욕구가 들어 페이스북을 켰고(?), 좋은 자료를 발견하게 되어, 오랜만에 포스팅을 함.

 

기존에 MySQL을 운영해본 경험으로 보면, 기본 제공하는 기능만으로 절대 HA(고가용성)을 지원할 수 없는 것이 MySQL 인 것 같다. 

 

경쟁사 제품인 MS SQL Server와 비교했을 때도, 성능 면에서라던지 여러가지 기능이 부족해보이긴 하지만, 결론적으로 사용하다보면 DBMS 자체가 다 비슷하다고 본다. 유료냐 무료냐의 차이일 뿐.

 

MySQL은 HA를 지원하지 않지만, Replication은 지원을 하기 때문에, 데이터가 유실될 경우 Replication 구성을 통해 복원도 가능하며, 사용자가 은근히 많아서 지원하는 툴도 많다. 유료버전을 쓰면 Cluster구성도 가능하다고 하는데, 돈주고 굳이 그렇게 쓰는사람이 몇이나 될까?

 

MySQL을 운영하다보면, Replication만으로 운영을 하는 것은 한계가 있다는 것을 깨닫고, 좀 더 효율적인 분산을 위해 Application 레벨에서의 Sharding도 구현을 해보고, Redis를 앞에 둬서 Cache Aside 패턴으로 DBMS의 부하를 줄이는 방법도 고려를 했다.

 

다만, 이 방법들은 서비스해야 하는 Application Server가 동시접속자를 10만 ~ 100만정도가 예상될 경우, DBMS에서 감당해야하는 Connection 문제가 생기게 된다.

 

동시접속자 100만을 받아야한다고 칠 때, Application Server하나가 동시접속자 5천을 커버하고, DBMS 하나가 2만명을 받아야 한다고 쳐보면, Application Server가 200대, DBMS가 50대 필요할 것이고, Application 레벨에서 Connection Pool을 구현해서 최소 30개의 Connection을 유지해야 한다면, DBMS가 감당해야 하는 Connection 수는 상상을 초월하게 될 것이다. (30 * 200 * 50 = 30만!)

 

이런 이슈를 피하기 위해, 중간에 Middle Ware 개념의 Query Dedicate Server를 만들어서 운영하는 경우도 있긴한데, 구현 및 관리하기가 어렵고, 일반적으로 자체구현한 Middle Ware가 DBMS Native Protocol을 지원하는 것은 쉽지 않으므로, 성능이슈가 생기게 된다.

 

이런 것들을 해결해줄만한 아래와 같은 솔루션이 있었다는 것을 최근에 알게 되었다.

https://www.proxysql.com/

 

이 솔루션이 해주는 역할은 명확하다.

 

1. Application과 DBMS 사이에서 Middle Ware역할을 해주면서, Connection Multiplexing을 제공해준다.

(Connection Multiplexing이란, 출발지에서 요청한 수 많은 커넥션이 있을 때, 그 커넥션의 성격이 비슷한 경우 하나의 커넥션으로 인식하여, 효율적으로 사용할 수 있도록 해주는 기능을 의미한다.)

 

2. MySQL Native Protocol을 지원하기 때문에, Application에서 MySQL 붙는 것처럼 IP정보만 교체해주면 되고, 그렇기 때문에 성능 이슈가 없을 거라고 생각됨.

 

3. Query를 Routing할 수 있도록 Admin기능을 제공해준다. 

(ProxySQl이 제공하는 Meta Table에다가 MySQL Host들을 Seq 기반으로 등록하고, 출발지의 Connection의 정보 중 MySQL User와 MySQL DB정보를 읽어서 내가 원하는 MySQL Host로 보낼 수 있도록 제공)

 

3번 같은 경우는 HA Proxy와 L7을 이용해서도 충분히 만들어낼 수는 있는데, Connection Multiplexing이 안되는 것은 둘째치고, HA Proxy 설정하는 것이 만만치는 않아서, 실 서비스에 사용을 고려하진 못했었다.

 

좋은 솔루션을 알게 되었으니, 직접 세팅해보면서 이후에 추가로 포스팅해야겠음.

반응형
,
반응형

리얼포스를 거의 7년간 사용하다보니, 가장 큰 단점이 휴대성이었다. (물론 다른 키보드보단 가볍긴 함.)

 

HHKB 사용자들을 보면, 키보드만 쏙 빼서 가방에 넣는데, 리얼포스는 줄을 감아야돼서 불편..

 

우연찮게 이런 고민을 해결해줄만한 제품을 회사 사람을 통해서 소개 받게 되었고, 마침 이 사람 동생분이 키보드 악세사리 관련 사업을 하고 있어서 바로 구매를 결심함.

 

사업장 이름이 Garbage Collector다.. Java 개발을 주로 하는 나로썬, 친숙한 이름이었고 이름만 들어도 뭔가 믿을만한 분이라는 것을 느꼈기 때문에, 구매에 고민이 거의 없었음.

 

내가 구매한 제품은 이 제품! 리얼포스 탈착식 케이블!

구매링크

 

리얼포스 뒷단에 달린 길~다란 USB 케이블을 제거 후에, 이 제품을 장착하면 HHKB처럼 탈착식 키보드가 되는 구조이다.

 

휴대성을 개선하기 위해 30만원 넘는 돈을 주고, HHKB을 사려고 했던 나로썬 참 반가운 제품이었음.

 

 

장착 과정 샷! 소개해주신 분이 직접 장착도 진행해주심. ㄳㄳ

 

우선 리얼포스 껍데기를 분리하고, 기판을 살펴보면, 아래와 같은 나사가 하나 나온다. (청소를 자주 하긴했는데... 분리하니 더럽다)

 

 

이 나사를 분리하고, 구매한 제품을 끼워주면 된다.

 

나사가 꽤 단단하게 박혀있기 때문에, 나사가 마모되지 않도록 주의하자. 

 

 

뺀찌까지 동원해서, 드디어 나사와 케이블을 분리함!

 

 

이제 구매한 제품을 꺼내보도록 하자.

 

방금 분리한 케이블을 대체할 케이블이 들어있고, 접착제가 들어있다.

 

 

케이블을 방금 분리한 위치에 끼운 뒤, 나사로 조여주고 뚜껑을 덮어서 다시 완전체로 만들어준다.

 

 

최종적으로 아래와 같은 모습이 되게 됨.

 

기존에 길다란 케이블에서 USB C 케이블만 연결하면 동작하도록 변형되었다!

 

 

이제 준비해둔 C-Type 케이블과 결합하면 최종적으로 완성!

 

 

사진에 있는 C-Type 케이블은 내가 최초 구매자라고 베타테스트 용도로 주신 케이블이고, 조만간 판매예정이라고 한다.

 

반응형
,
반응형

podman을 사용하면, container 표준기술이기 때문에, docker처럼 docker service daemon에 의존성 없이 container관리가 가능하다는 소식을 듣고, 설치부터 container 실행까지 해보던 중, 이미지를 가져올 수 없다는 에러가 발생하여, 그에 대한 해결책을 정리한다.

 

우선 podman 설치는 ubuntu 기본 repo에서 제공하지 않으므로, 별도의 repo를 등록해야 하고, 14.04 같은 예전버전에서는 repo를 등록해도 패키지를 찾을 수 없어서, 18.04에서 설치를 해서 진행을 했다.

(참고 : podman 설치 : https://computingforgeeks.com/how-to-install-podman-on-ubuntu/)

 

podman이 가장 좋은 게, docker / docker-compose.yml을 거의 그대로 활용이 가능하다.

다만, docker-compose 명령어를 그대로 쓰는 건 아니고, podman-compose라는 모듈을 별도로 설치해야 하는데, 아직 exec / ps 키워드는 개발이 덜 된 듯 하다.

(참고 : podman-compose 설치 : https://github.com/containers/podman-compose)

 

docker-compose.yml을 podman-compose를 이용해서 up을 했는데, 이미지를 땡겨올 수 없다는 에러가 발생했다.

(Error: unable to pull redis: image name provided is a short name and no search registries are defined in the registries config file.)

 

podman이 docker의 키워드를 모두 이용할 수는 있지만, 기본적으로 바라보는 repository가 설정이 되어 있지 않아 발생하는 에러이며, /etc/container 경로에 registries.conf 파일을 생성하여, 아래 내용을 입력해줌으로써, podman이 docker.io repo를 참조할 수 있게끔 설정해주면 된다. 


# This is a system-wide configuration file used to
# keep track of registries for various container backends.
# It adheres to TOML format and does not support recursive
# lists of registries.

# The default location for this configuration file is /etc/containers/registries.conf.

# The only valid categories are: 'registries.search', 'registries.insecure',
# and 'registries.block'.

[registries.search]
registries = ['docker.io']

# If you need to access insecure registries, add the registry's fully-qualified name.
# An insecure registry is one that does not have a valid SSL certificate or only does HTTP.
[registries.insecure]
registries = []


# If you need to block pull access from a registry, uncomment the section below
# and add the registries fully-qualified name.
#
# Docker only
[registries.block]
registries = []

 

 

반응형
,
반응형

쿼리 결과를 텍스트로 뽑아내는 것 까지는 성공했는데, ANSI 인코딩으로 파일이 만들어지는 바람에,

텍스트에 한글이 포함된 경우 해당 텍스트를 받아야하는 로그 서버에서 인코딩 에러가 발생을 하였다.

 

utf-8로 인코딩을 위해 sqlcmd의 옵션들을 검토하다가 해당 옵션을 발견함!


sqlcmd -E -o "output할 파일의 절대경로" -Q "실행할 쿼리" -f 65001 -h-1 -W

 

[옵션에 대한 설명]

-o : output할 파일명 (가급적 절대경로)

-Q : 실행할 쿼리 (SP도 가능함)

-f 65001 : 이것이 파일로 output하면서 ANSI를 utf-8로 변환해주는 옵션이다.

-h-1 : sqlcmd로 쿼리 결과를 파일로 뽑아낼 경우 상단에 ----------------- 가 붙는다. 이걸 제거해주는 역할

-W : sqlcmd로 쿼리 결과를 파일로 뽑는 경우 어마어마하게 공백이 추가되어 쓸데 없이 byte수가 늘어난다. 공백을 제거해주는 역할을 한다.

반응형
,
반응형

1. 전역 변수 Assign은 어떻게?


<body th:with="str='str',num=1,num2=2">

문자열 입력 시, 필히 quote로 묶어주어야 한다.

 

 

2. 페이지 Include는?


<div th:include="include1">

.html은 생략할 수 있으며, 모든 예제가 div로 되어 있어서 div를 넣을 지 말지 많이 고민했는데,

thymeleaf는 freemarker와는 다르게 어쩔 수 없이 하나의 태그가 포함되어야 하므로,

적당히 div를 통해 묶어주던가 css를 조작하여 페이지에 최대한 영향이 없는 태그를 넣는다.

그것도 싫다면 th:block을 사용하면 된다! (이게 더 좋을듯)


<th:block th:include="include1">

 

 

3. 조건문 사용 시 || 나 && 사용이 불가능함.


<div th:if="${abc} == 1 or ${abc} == 2">

영어로 작성해주어야 한다. 물론 &&는 and로 적어야 함.

 

 

4. Loop는 어떻게 할까?


<ul th:each="v : ${values}">
  <li th:id="${v.id}">
</ul>

include와 마찬가지로 freemarker처럼 html 포맷을 무시한 태그들 중앙에 loop을 돌릴 순 없고, 태그 하나가 필요하다.

사용법은 freemarker와 크게 다르진 않다.

 

 

5. 값이 없는 경우 다른 값으로 대체 하기


<input th:value="${abc != null} ? ${abc} : 123">

일반적인 프로그래밍 언어에서 지원하는 3항 연산자가 가능! 하지만 freemarker보다 불편한건 함정

조건문을 중괄호로 전부 묶어주는게 포인트다.

 

 

6. thymeleaf로 전환하니깐 IDE에서 계속 노란불(Warning)이 들어오는 경우.


<html xmlns:th="http://www.thymeleaf.org">

사실 무시해도 되는 Warning인데, 성격상 노란불 들어오는 것을 용납을 못하기 때문에 찾아봤다.

상단에 저렇게만 추가해줘서, html에게 th가 뭔지만 알려주면 된다.

 

 

7. onclidk 이벤트에 javascript:xxxxx('${abc}') 형태로 문자열 연결을 하려는데 타입이 다른 변수들은 연결할 수 없다고 에러가 나는데 어케함?


<button type="button" th:onclick="javascript:send([[${abc}]])">send click!!</button>

사실 전부 문자열인데, 왜 에러가 나는 지 이해가 잘 안됐고, 문자열과 변수를 묶어줄 때 사용하는 | 도 먹히질 않았음.

저렇게 대괄호로 변수 부분만 묶어주면, quote도 알아서 들어가고 심플하다.

 

 

드디어 구닥다리 freemarker에서 벗어나 thymeleaf로 오게 되었음.

바꾸고 나니깐 뭔가 렌더링도 더 빨라진 느낌이다.

 

반응형
,
반응형

내부에 기괴한 시스템에서 POST Method밖에 지원을 못한다고해서, nginx에서 html을 업로드하고 405 method not allowed 에러를 우회할 수 있는 방법에 대해 알아보았다.

 

405에러가 발생할 경우 무시하고 uri를 돌려줘라라는 의미인데, 의외로 잘 동작한다.

location / {                
    root   /var/www/html;
    index  index.html index.htm;
    error_page 405 = $uri;
}

 

파라미터 주고 받는거 전혀 없는 static page를 POST Method로 굳이 호출해야 하는 것도 좀 이상하긴하지만, 사내에 기괴한 시스템 등에서 POST Method밖에 허용을 못하겠다고 한다면, 이렇게 해결하면 된다.

반응형
,
반응형

python도 그렇고, windows도 그렇고 escape처리가 다른 언어 및 OS에 비해 까다롭기 때문에, win_command 처럼 문자열을 많이 입력해야 하는 모듈의 경우 escape 처리에 신경을 많이 써야 한다.

 

msi 파일 등을 무인 설치 하고, 설치된 디렉토리에 가서 뭔가 명령어를 입력해야 하는 경우, C:\Program Files 같이 중간에 띄어쓰기가 있는 경우 최근에 삽집을 했기 때문에 해당 내용에 대해 정리함.

 

C:\Program Files\Abc eef 경로에 있는 execute.cmd를 -r argument까지 넣어서 실행해야 하는 경우 아래와 같이 하면 문제가 없다. (참고로 win_shell 모듈 사용의 경우에는 powershell 기반이라 그런지 첫 글자에 \가 붙어서 문제가 발생하게 된다.)

---
 - name: test escape string
   win_command: '"C:\Program Files\Abc eef\execute.cmd" -r'

 

다음 예시로, 위와 동일한 경로에서 argument까지 넣어서 실행 한 후 추가 argument에 ansible_fact에서 가져온 정보까지 넣어야 하는 경우 아래와 같이 하면 된다.

---
 - name: test escape string with ansible_facts
   win_command: '"C:\Program Files\Abc eef\execute.cmd" -r {{ ansible_facts["hostname"] }}'

 

두 개의 예시를 보면 알겠지만, 띄어쓰기가 포함된 경로는 Double quote로 묶어주는 것이 핵심이고, 경로만 묶는 것이 아닌 실행파일까지 한 번에 묶는 것이 중요하다. 그리고 전체 커맨드를 Single quote로 묶어주는 것도 중요하며, ansible_facts를 기본적으로 Single quote로 많이 사용하는 데, 전체 커맨드를 Single quote로 묶어준 경우 Double quote로 바꿔서 사용해도 문제가 없다.

반응형
,
반응형

Ansible의 Windows 모듈 중에 win_package에서는 특정 URL에서 msi 파일이나 exe 파일을 다운받아서 설치하는 과정까지 한 번에 해주는 기능을 제공한다.

 

아래와 같이 작성하면 일반적으로 문제가 없을 거라고 생각했다.

...
 - name: win_package test
   win_package:
     path: MSI파일을 받을 URL 입력
     arguments:
       - /quiet
...

 

실행해보면 아래와 같은 Fatal 에러가 발생한다.

fatal: [x.x.x.x]: FAILED! => {"changed": false, "msg": "product_id is required when the path is not an MSI or the path is an MSI but not local", "reboot_required": false, "restart_required": false}

 

MSI파일이 로컬 내에 위치해 있지 않은 경우 product_id를 넣으라는 의미인데..

Windows를 잘 모르는 내 입장에서 product_id를 어떻게 찾아야 할 지 막막해서 구글링을 해본 결과 다음과 같은 링크를 찾게 됨.

powershell을 이용해서 msi 파일의 정보를 뽑아내는데, 그 중에 ProductCode (ansible에서 이야기하는 product_id)를 뽑아낼 수 있다는 내용이다.

https://www.scconfigmgr.com/2014/08/22/how-to-get-msi-file-information-with-powershell/ 

 

How to get MSI file information with PowerShell

If you’re an ConfigMgr administrator like me, you will often find yourself in the situation where you may want to get the Product Code from a MSI file. Or perhaps you’re interested in the Product Version or simply just the Product Name. A while back I wrot

www.scconfigmgr.com

 

혹시 위 링크의 정보가 사라질 수 있으므로, Powershell 스크립트와 실행법에 대해 이 글에 복붙을 해보기로 한다.

param(
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [System.IO.FileInfo]$Path,
 
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet("ProductCode", "ProductVersion", "ProductName", "Manufacturer", "ProductLanguage", "FullVersion")]
    [string]$Property
)
Process {
    try {
        # Read property from MSI database
        $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
        $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0))
        $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"
        $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
        $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
        $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
        $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
 
        # Commit database and close view
        $MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
        $View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)           
        $MSIDatabase = $null
        $View = $null
 
        # Return the value
        return $Value
    } 
    catch {
        Write-Warning -Message $_.Exception.Message ; break
    }
}
End {
    # Run garbage collection and release ComObject
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
    [System.GC]::Collect()
}

 

실행법은 파워쉘을 연 후에 다음과 같은 커맨드를 입력하면 된다.


.\파일명.ps1 -Path "MSI파일의 경로" -Property ProductCode

위의 powershell 스크립트를 이용해서 win_package 모듈 사용 시 아래와 같이 product_id를 추가해주면 에러가 사라지게 된다.

...
 - name: win_package test
   win_package:
     path: MSI파일을 받을 URL 입력
     product_id: Powershell 스크립트를 이용해 얻은 product_id (single quote를 붙여주자.)
     arguments:
       - /quiet
...

 

반응형
,
반응형