[Golang] 외부 라이브러리 의존없이 Go 정적 빌드(Static Build)하기

go build 명령어 실행 시 아래와 같이 지정해주면, 시스템 라이브러리 (libstdc 등)에 의존하지 않는 정적 프로그램으로 빌드할 수 있다.

CGO_ENABLED=0 GOOS=linux GOARCH=$(dpkg --print-architecture) go build -a -ldflags '-w -extldflags "-static"' -o executable_name *.go

Environment Variables

  • CGO_ENABLED=0: [필수] 외부 C 라이브러리에 의존하는 CGO를 비활성화한다.
  • GOOS=linux: linux OS를 타겟으로 빌드한다. (가능한 값 보기)
  • GOARCH=$(dpkg --print-architecture): 현재 빌드하는 시스템 아키텍쳐를 대상으로 빌드한다 (가능한 값 보기)
    • dpkg 명령어에 의존하므로 빌드하는 시스템은 Debian-based여야 한다. Ubuntu OS나 일반적인 golang latest Docker image의 경우 Debian-based이므로 문제가 없다.

Build Arguments

  • -a: 이미 빌드된 패키지라고 해도 다시 빌드한다.
  • -ldflags '-w': DWARF 디버깅 정보를 삭제(strip)한다.
  • -ldflags '-extldflags "-static"': [필수] 외부 링커 (ld)에게 정적 빌드를 진행함을 알린다.

(Optional) Extra Arguments

  • -tags netgo: 시스템 Network 라이브러리 (C-based)를 사용하지 않고 Go 내부의 Network 구현을 사용한다. CGO_ENABLED=0을 세팅하지 않을 경우 개별 라이브러리를 비활성화할 수 있는듯 하다.

Advantages

정적 프로그램으로 빌드할 경우 libc를 포함한 외부 라이브러리에 대한 의존이 없어서 단독으로 실행이 가능하다.

예를 들어, 위에서 빌드한 프로그램 ./main을 아래와 같이 Docker scratch 이미지에 넣고 단독으로 실행이 가능하다 (scratch 이미지의 경우 내용물이 빈 이미지이다.)

mkdir build
cd build
CGO_ENABLED=0 GOOS=linux GOARCH=$(dpkg --print-architecture) \
  go build -a -ldflags '-w -extldflags "-static"' -o main ../main.go

cat > Dockerfile <<EOF
FROM scratch

COPY main /main
ENTRYPOINT /main
EOF

docker build . -t my-image:latest
docker run -it --rm my-image:latest

jungin500/efinextboot – 멀티부팅을 좀더 간단하게

Windows/Ubuntu를 멀티부팅할 일이 잦아서 자주 사용하던 chengxuncc/booToLinux 유틸리티에는 몇가지 문제점이 존재한다.

  1. 한글이 깨지는 문제
  2. 느린 실행속도

해당 레포지토리 소스코드를 살펴본 결과 Golang으로 작성되었으며 생각보다 간단하게 구성되었다. 각 문제의 해결 방법을 찾을 수 있을것 같아 직접 밑바닥부터 다시 구현하였다.

Features

  • 한글 깨짐 문제 수정 (cmd 인코딩 변경)
  • Powershell 라이브러리 사용하지 않아 속도 향상

[Linux] PyTorch CUDA 오류 해결 – libcudnn_cnn_infer.so, libnvrtc.so

Could not load library libcudnn_cnn_infer.so.8. Error: libnvrtc.so: cannot open shared object file: No such file or directory

Anaconda/Miniconda로 최신 PyTorch 2.0과 CUDA 라이브러리(pytorch-cuda=11.8)를 설치하고 PyTorch 라이브러리를 로드할 때 이런 오류가 발생하는 경우가 있다.

해당 문제는 pytorch-cuda로 설치된 libnvrtc.so 파일이 제대로 링크되지 않아 발생한 문제이다. 이를 해결하기 위해서는 아래와 같이 기존의 libnvrtc.so.11.2libnvrtc.so.11.8 등을 프로그램이 열심히 찾고있는 libnvrtc.so로 링크해주면 된다.

현재 사용중인 conda 환경을 activate하고, 아래 Snippet을 실행하여 링크를 진행하여 문제없이 작동하였다.

NVRTC_LIB_DIR="${CONDA_PREFIX}/lib"
NVRTC_FILEPATH=$(find ${NVRTC_LIB_DIR} -type f -name 'libnvrtc.so.*')
NVRTC_FILENAME=${NVRTC_FILENAME##*/}
ln -sv ${NVRTC_FILENAME} "${NVRTC_LIB_DIR}/libnvrtc.so"

위 명령어 실행 결과로 아래와 같이 파일이 링크된 것을 확인할 수 있다. 혹시나 /lib/libnvrtc.so로 링크된 경우에는 conda activate를 한 뒤 다시 시도해보자. 현재 위치에 잘못 생성된 libnvrtc.so 파일을 삭제해주도록 하자.

'/home/jungin500/miniconda3/envs/torch/lib/libnvrtc.so' -> 'libnvrtc.so.11.8.89'

하지만 cuDNN 버전과 CUDA 버전이 달라지면 위 방법이 먹히지 않아 다른 오류가 발생할 수도 있다.

[macOS][Karabiner][V13] macOS Control <=> Command 키 토글

서두

본 글은 macOS에서 Command 키나 Control 키가 먹히지 않을 경우, Karabiner를 이용하여 두 키를 서로 바꾸어(토글) 사용할 수 있는 해결 방법을 공유한다. 키가 고장났을때도 유용할 것 같다.

최근 sickcodes/Docker-OSX를 WSL2 위에서 돌려서 만족하면서 써보고 있는데, Windows 키가 WSL2 KVM QEMU에서 제대로 전달되지 않아 macOS에서 Command 키를 사용할 수 없다는 문제가 발생했다. 해당 문제를 Karabiner를 이용하여 해결해보았다.

Karabiner 소개

macOS에서 키 바인딩을 편리하게 바꿀수 있는 도구이다. 이전 글에서도 이 어플리케이션을 이용하여 한/영키를 Shift+Space로 편리하게 자동 전환하는 JSON 스크립트를 만든적이 있었다.

[V13] left_shift twice to toggle between Control 스크립트 설치

간단하게 아래 페이지를 열어 스크립트를 Karabiner에 적용할 수 있다: 링크
(주소가 매우 긴데, 이는 JSON 스크립트 Body 자체가 인코딩되어 들어있기 때문이다. 원본 JSON 스크립트는 이 링크에서 확인할 수 있다)

Press left_shift twice to toggle between Control and Command key. Another key pressed during each left_shift press will be considered as a cancel, and timeout will also considered as cancel.

사용법

Karabiner에 스크립트를 적용 완료하였다면, “Left Shift”를 두번 연타하여 Control과 Command를 토글할 수 있게 된다. 상태가 총 두 가지인데,

  • 일반 상태: Control 키와 Command 키가 각각 원래 역할을 한다.
  • 토글 상태: Control 키는 Command 키로, Command 키는 Control 키로 동작한다.

위와 같은 방식으로 동작하게 된다. 토글의 조건이 몇가지가 있는데, 신경쓰인다면 확인해보는것도 좋을것 같다.

  • Shift를 두번 연타할 때에는 500ms 이내로 연타하면 된다.
  • 처음 Shift pressed event 이후 다음 Shift pressed event 전까지 임의의 키가 입력되면 무시된다.
  • 처음 Shift pressed event에서 Shift가 modifier key로 사용되었다면 (예, Shift+A 등의 조합키) 무시된다.

여기서 무시된다는 것은, 해당 동작이 토글을 바꾸는 데에 효과가 없다는 것을 의미한다. 이는 빠른 타이핑 도중에 여러번 Shift가 눌리는 상황을 고려하여 만들었다.

개선 사항

  • 알고리즘이나 키 바인딩 관련한 개선이 필요한 경우 해당 Gist에 댓글로 부탁드립니다.
  • 더 간단한 구현이 가능할 경우 공유해주시면 매우 환영입니다!

[macOS] 네이버 웨일 브라우저 AdGuard 사용하기

기본적으로 macOS에서 AdGuard를 설치해서 사용하게 되면 Browser Assistant Extension도 작동하지 않고, 일부 광고들이 필터링되지 않는 문제가 있다. 이를 해결하기 위해서는…

  1. Browser Assistant Extension과 AdGuard 연결
  2. AdGuard에서 네이버 웨일을 브라우저로 등록

위와 같이 서로 등록을 해주어야 하는 번거로운 과정이 필요하다.

[1] AdGuard Browser Assistant Extension과 AdGuard 연결

Browser Assistant Extension을 설치하였다면 AdGuard가 설치되지 않았다고 뜬다.
사용중인 터미널로 /Users/사용자명/Library/Application Support/Naver/Whale/NativeMessagingHosts/com.adguard.browser_extension_host.nm.json 파일을 새로 생성하고 아래와 같이 값을 입력하여 저장한다.

{
"name": "com.adguard.browser_extension_host.nm",
"description": "AdGuard Native message connector",
"path": "/Applications/Adguard.app/Contents/MacOS/adguard-nm",
"type": "stdio",
"allowed_origins": [ "chrome-extension://fbohpolgemkbfphodcfgnpjcmedcjhpn/", "chrome-extension://hhaeiccdiaojoofohjiennalnphobkaf/", "chrome-extension://mkphddcgnalfkgjbklbepofehokofiek/", "chrome-extension://lcelghcpbepaoamjahfegdgocagfgpdi/", "chrome-extension://calilkfbhgibagenlbchfbiafnacldki/", "chrome-extension://calilkfbhgibagenlbchfbiafnacldki/" ]
}

AdGuard 앱 설치 위치가 달라지면 path를 달리 설정하여야 한다. 입력 후 Whale을 재시작하면 Browser Extension이 정상적으로 작동하는 것을 확인할 수 있다.

[2] AdGuard에서 네이버 웨일을 브라우저로 등록

AdGuard의 기본 브라우저 리스트에는 안타깝게도 네이버 웨일이 등록되어있지 않다. 기본적으로 AdGuard는 등록된 어플리케이션에 대하여 필터링을 수행하므로, 네이버 웨일을 아래와 같이 등록해주도록 하자.

먼저 macOS Menu Bar에 있는 AdGuard 아이콘 클릭 후, 설정으로 진입한다.

다음으로, 우측 “네트워크” 탭에서 “애플리케이션…”으로 선택 창으로 진입한다.

기본적으로 애플리케이션 리스트에 갖가지 브라우져들이 있지만 네이버 웨일은 없다. 설치되어있는 네이버 웨일을 추가해 주도록 한다.

닫기 버튼으로 나간 뒤, 창을 닫으면 자동으로 적용된다. 개인적으로 네이버 메인 화면에서 중간 배너와 우측 배너가 보이지 않으면 적용된 것이라고 본다.

Why?

같은 macOS 환경에서, Google Chrome을 설치하고 그 안에 AdGuard Browser Extension을 사용해보면 정상 작동하는것을 확인할 수 있다. 사실 AdGuard의 애플리케이션 필터링 리스트에 웨일이 없었던 것이 근본 문제였을지, 아니면 네이버 웨일이 상단 과정에서 넣었던 NativeMessagingHosts에 대한 버그를 가지고 있는지는 모르겠다.

어찌 되었든, 위 과정을 거치고 나니 Custom Rule 등등이 문제없이 적용되어 쾌적한 브라우징이 가능했다!

참고자료

[1] AdGuard Browser Assistant under Vivaldi Browser possible?

[M1/M2] 수월한 macOS 한/영 전환 환경 만들기

환경

  • 메인 PC: Macbook Air M1
  • 작동 가능한 원격 환경 (키보드/마우스 공유 포함)
    • Microsoft Remote Desktop – Windows 11 (x64)
    • Parallels Desktop 18 – Windows 11 (ARM64)
    • Synergy (M1 Macbook Air가 Host, Windows 11이 Guest)

목표

  • macOS를 메인으로 사용할 때, Synergy Guest인 Windows, Parallels Guest인 Windows, 그리고 Remote Desktop 모두에서 한/영 전환을 문제없이 하기 위함
  • 오른쪽 Command키를 Shift+Space와 동일한 효과를 나도록 함

준비물

세팅 방법 – Windows 파트

Windows에서는 날개셋 입력기만 설치하고 주 입력기로 설치하면 된다. 기본적으로 날개셋 입력기는 Shift+Space와 한/영키 두 가지 모두 한/영 변환이 가능하다. 이는 모든 Windows Guest에 설치해야 작동할 것이다.

세팅 방법 – macOS 파트

macOS에서는 여러 프로그램을 설치할 필요가 있다. 기본적으로 아무 세팅도 하지 않은 상태에서는 Caps Lock키가 한/영 전환키이며, 길게 누르면 기존 Windows와 동일한 Caps Lock Toggle을 수행하게 된다. 이를 바꿔서, 우리는 Shift+Space를 한/영 전환키로 바꿔보고 원격이나 Synergy에서도 활용할 수 있게 해보자. (각 프로그램의 설치 과정은 상세히 기록하지 않았다. 일반적인 설치 방법을 참고하면 된다.)

  1. Karabiner-Elements 설치: 설치는 일반적인 방식대로 진행하면 된다.
  2. Right Command to Left Shift+Space 프리셋 먹이기: Karabiner-Elements에 먹일 수있는 Preset이다. 이를 적용하고 나면 오른쪽 Command 키가 시스템 전역으로 Left Shift+Space로 대체된다.
  3. 구름 입력기 설치: 설치 과정을 진행하고 난 뒤 언어 입력기 설정에서 기본 입력기인 “두벌식”을 구름 아이콘의 “두벌식”으로 변경하자. 이 때부터는 Caps Lock을 통한 한/영 전환이 비활성화된다.
  4. com.apple.symbolichotkeys.plist 수정: 기본적으로 설정>키보드>단축키>입력 소스>입력 메뉴에서 다음 소스 선택 부분을 Shift+Space로 변경하면 끝나는데, macOS Monterey에서는 이를 허용하지 않는것 같다. plist 파일을 수정하고 재 부팅하면 이때부터는 값이 변경되어 있으므로 오른쪽 Command나 Shift+Space를 통해 한/영 전환을 사용할 수 있을 것이다.

세팅 방법 – Parallels Desktop 18

Parallels Desktop 18에서는 기본적으로 입력소스 전환 키를 윈도우의 LALT+Shift로 매핑하는것 같다. 하지만 우리는 Windows Guest에 날개셋 입력기를 설치하였으므로, 이를 Override해보자.

먼저 Parallels 제어 센터에서 설정으로 들어간다

다음으로는 단축키에서 Windows 11 섹션으로 들어간다. 기본적으로 Shift+Space가 언어 변경으로 매핑되고 끌 수 없는것을 확인할 수 있다.

이는 엔트리를 추가하여 Override할 수 있다. 아래 + 버튼으로 Shift+Space를 그대로 Shift+Space로 전달하는 룰을 추가한다.

이렇게 하면 Windows 안에서도 언어 전환이 가능하다.

한계점

언어 전환시 언어 자체의 동기화는 되지 않는다. 이 말은, Windows에서는 한글로 입력 중이나 macOS에서는 영어 상태일 수 있다는 의미이다. 큰 의미는 없지만, 언어 선택이 완벽하게 일관성있게 작동하지 않으므로 나중에 문제가 생길수도 있을 것이다.

또한 날개셋 입력기는 기본 Windows 입력기가 아니다. 웬만한 상황에서는 잘 작동할 것이라 생각하나 일부 게임/뱅킹 프로그램이 작동하지 않거나 입력기 프로그램을 차단할 수 있으므로, 해당 상황에서는 기본 Windows 입력기로 전환하여 사용하는 것이 좋을 것이다.

[Kubernetes] PyTorch 학습 시 Pod의 빠른 종료를 위한 yaml 설계 방법

[새로운 방법 발견!]

아래 방법을 계속 사용하다가, yaml에 직접 넣어서 사용할 수 있는 구문을 찾았습니다.

spec:
  terminationGracePeriodSeconds: 0

해당 구문을 이용하여 Pod delete시 바로 중단 및 삭제되도록 할 수 있습니다.


기존 방법으로는 kubectl delete 시 --force --grace-period=0 옵션을 주어야만 강제 종료되고, 학습 process에 실제로 Ctrl+C를 주는 것과 동일하게 SIGTERM 을 보낼 수는 없었습니다. 이러한 문제점은 학습 프로세스의 종료시 callback들 (wandb 등)이 정상 작동하지 않는다는 문제점이 있습니다.이를 해결할 수 있는 방안을 소개 드립니다.
다만 아래와 같은 몇 가지 drawback이 있으므로 참고하여주시고, 더 좋은 방법이 있다면 공유 부탁드립니다.

  1. pip install 이 불가능하므로 미리 requirements.txt를 설치한 image를 준비해야 함
  2. runAsUserrunAsGroup 을 1003으로 고정해야 하므로 HOME 변수를 /workspace로 고정해야만 사용 가능

기존 방식

apiVersion: v1
kind: Pod
metadata:
  name: jungin500-mobilenetv2
spec:
  securityContext:
    runAsUser: 0
    runAsGroup: 0
    fsGroup: 1003

  restartPolicy: Never

  volumes:
    - name: shmdir
      emptyDir:
        medium: Memory
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: lab-pvc

  containers:
    - name: gpu-container
      image: ghcr.io/jungin500/mobilenet_v2
      volumeMounts:
        - mountPath: /dev/shm
          name: shmdir
        - mountPath: /home/lab
          name: pvc-volume
      env:
        - name: TZ
          value: Asia/Seoul
      command:
        - "/bin/sh"
        - "-c"
      args:
        - >-
          set -x &&
          groupadd -g 1003 lab &&
          useradd -m -d /workspace -s /bin/bash -u 1003 -g lab lab &&
          runuser -u lab -- git clone https://jungin500:*****@github.com/jungin500/mobilenet_v2 mobilenet_v2 &&
          cd /workspace/mobilenet_v2 &&
          runuser -u lab -- git checkout 7d198633d19c2005e22b118ba27082eca3f2846a &&
          runuser -u lab -- pip3 install -r requirements.txt &&
          runuser -u lab -- /bin/bash -c "mkdir -p /home/lab/jungin500/mobilenet_v2_220718 || true" &&
          runuser -u lab -- python3 train.py ****
      securityContext:
        allowPrivilegeEscalation: false

      resources:
        requests:
          nvidia.com/gpu: 1
        limits:
          nvidia.com/gpu: 1

변경된 방식

apiVersion: v1
kind: Pod
metadata:
  name: jungin500-shutdown-signal-test
spec:
  securityContext:
    # [1] runAsUser, runAsGroup을 Fix
    runAsUser: 1003
    runAsGroup: 1003
    fsGroup: 1003

  restartPolicy: Never

  volumes:
    - name: shmdir
      emptyDir:
        medium: Memory
    - name: workdir
      emptyDir: {}
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: lab-pvc

  # [2] git clone, 학습 결과물 폴더 링크 세팅등을 initContainers에서 진행
  # 이 때, pip install 등은 불가능하므로 미리 requirements.txt를 설치한 image를 준비해야 함
  initContainers:
    - name: clone-directory-set
      image: alpine/git
      volumeMounts:
        - mountPath: /workspace
          name: workdir
        - mountPath: /home/lab
          name: pvc-volume
      workingDir: /workspace
      env:
        - name: TZ
          value: Asia/Seoul
        - name: HOME
          value: /workspace
      command:
        - "/bin/sh"
        - "-c"
      args:
        - >-
          set -x &&
          git clone https://jungin500:***@github.com/jungin500/mobilenet_v2 mobilenet_v2 &&
          cd /workspace/mobilenet_v2 &&
          git checkout 7d198633d19c2005e22b118ba27082eca3f2846a &&
          mkdir -p /home/lab/jungin500/mobilenet_v2_test || true
  
  # [3] 기타 작업 (폴더 생성, 이동 등)은 불가능함
  #     argument도 string의 형태가 아닌 array로 주어야 작동함
  containers:
    - name: gpu-container
      image: ghcr.io/jungin500/mobilenet_v2
      volumeMounts:
        - mountPath: /dev/shm
          name: shmdir
        - mountPath: /workspace
          name: workdir
        - mountPath: /home/lab
          name: pvc-volume
      workingDir: /workspace/mobilenet_v2
      env:
        - name: TZ
          value: Asia/Seoul
        - name: HOME
          value: /workspace
      command:
        - "/opt/conda/bin/python3.8"
        - "train.py"
      args:
        - "--arg1"
        - "arg1_value"
      securityContext:
        allowPrivilegeEscalation: false

      resources:
        requests:
          nvidia.com/gpu: 1
        limits:
          nvidia.com/gpu: 1

구체적으로 변경되어야 하는 부분은 다음과 같습니다.

spec.securityContext.runAsUser
spec.securityContext.runAsGroup
spec.volumes.name[workdir]
spec.initContainers
spec.containers.volumeMounts[mountPath=/workspace]
spec.containers.env[name=HOME]
spec.containers.command
spec.containers.args