카테고리 보관물: Computer

컨테이너와 표준화

부산에서 부두 근처를 지나다 보면 부두에 쌓인 컨테이너들과 컨테이너선, 컨테이너를 싣고 다니는 트럭들을 볼 수 있습니다. 최초의 컨테이너선은 1956년 운항을 시작한 말콤 맥린이라는 사람의 배였다고 합니다. 당시 컨테이너들은 규격이 일정하지 않았지만 이후 컨테이너의 표준화와 항구 대형 크레인 설치를 통해 화물 운송 효율이 증가하고 선박 운송 비용이 감소하여 국제 무역이 크게 증가했다고 합니다. 컨테이너는 단순한 박스이지만 세계 경제를 바꾼 혁신이었죠(마크 레빈슨, 더 박스).

도커에서 말하는 컨테이너가 바로 이 컨테이너입니다. 그래서 도커의 로고도 컨테이너선을 따라 만들었죠. 컴퓨터에서 컨테이너는 가상화 기술의 일종입니다. 실제로는 격리된 프로세스들의 집합이지만, 호스트 운영체제와 구분된 별도의 운영체제인 것처럼 생각할 수 있습니다. 컨테이너 기술은 도커 전에도 존재했지만 2013년 공개된 도커에서 컨테이너 기술을 쉽게 쓸 수 있도록 만든 후 컨테이너 기술이 널리 쓰이게 되었습니다.

종류별로 컨테이너들을 쌓아놓고 작업에 따라 필요한 컨테이너를 꺼내서 그 안에서 작업하고, 다 쓴 후에는 다시 넣어놓는 방식으로 일하는 경우를 생각해봅시다. 도커에서 쌓아놓은 컨테이너에 해당하는게 이미지입니다. 도커에는 필요한 프로그램들을 미리 설치해놓은 이미지들이 있습니다. 필요할 때 원하는 이미지를 실행해서 메모리에 올리면 그게 도커 컨테이너입니다. 메모리에 올린 후에는 컨테이너 안의 프로그램들을 이용해 작업을 할 수 있죠. 작업이 끝나면 컨테이너를 정지시키는데, 이미지는 그대로 남아 있어서 나중에 필요할 때 다시 쓸 수 있습니다. 이미지가 필요 없다면 지울 수도 있고, 직접 원하는 프로그램을 설치해서 나만의 이미지를 만들 수도 있습니다.

컨테이너는 해상 운송 뿐 아니라 컴퓨터 가상화 방식도 바꿨습니다.

소프트웨어를 이용한 연구에서 발생하는 몇 가지 문제들 예방하기

저는 대학원생 때부터 수학적/공학적 알고리즘을 개발하고 프로그래밍을 통해 구현해서 검증하는 방식의 연구를 많이 해왔습니다. 컴퓨터를 이용해 연구를 수행하다 보니 다양한 소프트웨어 관련 문제들을 겪어 왔습니다.

  1. 정전으로 클러스터 서버 전원이 꺼지면서 하드디스크가 고장나 소스 코드 파일이 손상되는 문제
  2. 실수 소스 코드를 지워버리는 문제 (리눅스 커맨드라인에서 파일을 지우면 복구하기가 매우 어렵습니다)
  3. 코드 수정 후 결과가 달라졌는데 한꺼번에 너무 많은 부분을 수정해서 어디를 어떻게 수정했는지 찾기 어려워지는 문제
  4. 동일한 코드인데 다른 서버에서 컴파일이 안 되는 문제
  5. 동일한 코드인데 다른 서버에서 실행 결과가 달라지는 문제
  6. 새 서버가 들어올 때마다 연구에 필요한 프로그램들을 설치하기 전까지 사용하지 못하는 문제

대학원생 때는 이러한 문제들로 인해 시간을 많이 빼앗겼는데, 새로운 기술들로 인해 이제는 위의 상황들이 발생하지 않거나 발생해도 심각한 문제가 되지 않습니다.

1-3번 문제는 소스 코드 버전 관리 시스템을 이용해, 4-6번 문제는 Docker 컨테이너를 이용해 예방/해결할 수 있습니다. 1, 2번은 Git 로컬 저장소와 GitHub 원격 저장소에 2중으로 코드를 백업하기 때문에 소스 코드가 지워져도 금방 복구가 가능합니다. 3번의 경우 git diff 명령으로 수정 부분을 쉽게 확인할 수 있습니다. 한꺼번에 많은 부분을 수정하기보다는 작게 나눠서 수정하고 자주 commit하는게 좋습니다. 4, 5번은 서버의 라이브러리나 컴파일러 등이 달라서 생기는 문제인데, Docker 컨테이너 가상환경을이용해 서버의 환경을 동일하게 만들어 주면 연구 결과 재현성(reproducibility)을 높일 수 있습니다. 6번 또한 시스템에 Docker만 설치해 놓으면 금방 Docker 이미지를 받아 컨테이너를 실행하여 다른 서버와 동일한 연구 환경을 준비할 수 있습니다.

Git과 GitHub는 제가 대학원에 있을 때 공개 되었습니다. 버전 관리 소프트웨어는 그 전부터 존재했죠. 박사과정 때는 Mercurial이라는 소프트웨어를 잠깐 이용했었는데, 이후 Git과 GitHub가 버전 관리 업계를 거의 평정(?)했죠. Docker는 제가 박사과정을 마친 후에 나왔습니다. 컨테이너 기술은 그 이전에도 있었지만 Docker가 컨테이너 기술을 대중화했죠. 연구에서 프로그래밍을 진지하게 다룬다면 Git은 거의 필수입니다. Docker는 서버 컴퓨터를 많이 다루는 연구실에서 유용하게 쓸 수 있습니다.

AI가 연구도 집어 삼키고 있습니다

소프트웨어가 세상을 집어 삼키고 있습니다

소프트웨어가 세상을 집어 삼키고 있습니다(Andreessen). 각종 아날로그 매체에 담겨있던 정보들은 디지털 비트로 증발하고 있죠(Negroponte; Tercek). 레코드판, 카세트테이프, 비디오테이프가 증발하였고, 신문과 책도 증발하고 있습니다. 증발된 정보는 클라우드에(cloud) 존재하며, 사용자가 원할 때(on-demand) 아날로그 매체 없이 비트만 받아 텔레비전, 컴퓨터, 스마트폰으로 보고 들을 수 있습니다.

하드웨어 장치들도 증발하고 있습니다. 비디오플레이어, CD 플레이어, MP3 플레이어, 카메라, 계산기 등은 이제 컴퓨터 소프트웨어나 손바닥 안의 앱으로 존재합니다. 각 기관들에서 운영하던 서버 컴퓨터들도 클라우드로 옮겨가고 있습니다. 소프트웨어 개발자들은 이제 인프라도 코드로 관리합니다(Morris).

코로나19는 회사 사무실들을 증발시켰고, 학교와 학원 교실에서 진행되던 수업들을 증발시켰습니다. 재택 근무와 온라인 강의는 코로나19 이후에도 일정 부분 남아 있겠죠. 앞으로 많은 사회적 경제적 활동들이 가상의 소프트웨어 세상인 메타버스에서 일어날 것입니다(Dionisio et al.).

소프트웨어가 연구도 집어 삼키고 있습니다

다양한 과학 기술 분야의 연구도 컴퓨터 소프트웨어로 진행되고 있습니다(Hannay et al.; Prabhu et al.). 수치해석을 이용한 컴퓨터 시뮬레이션은 많은 물리 모형 실험을 대체했습니다. 디지털 트윈은 현실 상황을 컴퓨터로 재현합니다(Negri et al.). 물론, 실제 계산은 CPU, GPU와 같은 하드웨어에서 수행하지만 사용자는 소프트웨어를 이용해 하드웨어를 제어하죠.

AI가 소프트웨어를 집어 삼키고 있습니다

그런데 세상을 집어 삼키고 있는 소프트웨어를 AI가 집어 삼키고 있습니다(Huang). 머신러닝/딥러닝을 통해 기존의 소프트웨어들로는 하지 못했던 일들도 가능해지고 있습니다. 생성AI는 코드를 작성해주기도 합니다.

연구에 있어서도, 제 전공 분야에서 체감상 최근 발표되는 연구의 반 이상은 머신러닝을 이용한 연구인듯 합니다. 물리탐사 자료처리 분야가 예전부터 컴퓨터를 많이 사용하던 분야라 머신러닝 도입이 빠르기는 합니다. 탄성파 탐사 연구실에서도 최근에는 딥러닝을 이용한 연구들을 주로 진행하고 있습니다. 이제는 머신러닝이 수치해석과 같은 필수 도구가 되었습니다.

이슈 관리 프로그램을 이용한 작업 방법

이슈 관리 프로그램

이슈(Issue)는 소프트웨어 개발에서 기능 추가, 버그 수정 등의 작업을 의미합니다. 이슈 관리 프로그램들로는 JIRA, YouTrack, Redmine, GitHub Issues 등 다양한 소프트웨어들이 있습니다.

제가 이슈 관리 프로그램을 본격적으로 사용하기 시작한 것은 2020년입니다. 그 전에도 연구용 소프트웨어 개발을 위해 Trello 보드를 이용해 간단하게 이슈를 관리했습니다. 하지만 GPU 병렬 연산 최적화 프로젝트를 위해 본격적인 이슈 관리 프로그램의 필요성을 느껴 BitBucket 저장소에 JIRA를 연결해 사용했습니다. Git으로 소스코드를 관리하며 커밋 메시지를 통해 이슈를 자동으로 관리하는 기능도 유용하지만 제게는 필요한 기능들을 적어놓고 하나씩 구현하는 작업 방식 자체가 개발 속도 향상에 큰 도움이 되었습니다.

작업 절차: 시행 착오(작은 성공, 빠른 실패, 빠른 피드백)

요구사항에 따른 초기 설계 후에 제가 이슈 관리 프로그램을 이용해 소프트웨어를 개발하는 절차는 다음과 같습니다.

  1. 필요한 작업을 30분~1시간 작업 분량 정도의 작은 단위로 나눠 이슈 관리 프로그램에 등록합니다. 짧은 시간 작업해서 빠르게 피드백을 얻을 수 있도록 작업을 잘게 나눕니다. 이슈는 생각 날 때마다 수시로 등록합니다.
  2. 작업할 이슈를 하나 정해서 해당 개발 작업을 수행합니다. 한 번에 하나의 이슈에만 집중해서 개발을 진행합니다.
  3. 작업 결과를 점검합니다. 원하는 결과가 나오면 작은 성공을 얻은 것이죠. 원하는 결과가 안 나왔다면 피드백을 통해 프로그램이나 이슈를 수정합니다.
  4. 잠깐 쉬었다가 2번 과정으로 (반복)

이 때 빠르게 피드백을 얻을 수 있도록 하나 하나의 실험(이슈)을 작게 설계합니다. 큰 기능도 작게 나눠서 구현하며 중간중간 확인하면 실패의 리스크를 줄일 수 있습니다. 실험별 성공/실패를 판단하기 위해서는 원하는 결과가 명확해야겠죠.

GPU 최적화 때 성공적인 결과는 수치해석 결과가 달라지지 않으면서 계산 속도가 향상되는 것이었습니다. 속도가 향상될 수 있을 만한(일부는 불확실한) 내용들을 이슈 관리 프로그램에 등록해 놓고 하나씩 구현한 후 GPU 프로파일러로 실행하며 속도와 결과를 비교했습니다. 동일한 결과에 계산 속도가 빨라지면 다음 이슈로 넘어갑니다. 속도가 빨라지지 않거나 결과가 달라지면 수정 이전의 코드로 돌아가고 해당 이슈는 취소하는 방식으로 작업했습니다.

HPC 작업 관리 프로그램에서는 추가하는 기능에 따라 원하는 결과가 다릅니다. 입력이 잘 되는지, 배치 파일이 잘 만들어지는지, 작업 제출이나 취소가 잘 되는지, 모니터링이 잘 되는지 등을 확인하며 피드백을 받습니다. 잘 안 되면 버그를 찾아 고치거나 다른 구현 방식을 시도합니다.

보통 연구를 진행할 때는 어떤 결과가 나올지 잘 모르는 경우가 많기 때문에 연구용 수치해석 소프트웨어 개발 과정과는 약간 차이가 있네요.

이렇게 다양한 시행 착오를 통해 배우며 경험을 쌓아가고 있습니다. 특별한 것은 없지만 나름대로 과학적 방법론, 애자일 개발, 린 스타트업, 리스크 관리, 뽀모도로 기법, 인지심리학 등을 통합 적용한 의식적인 개발 방법이고, 이 작업 방법도 세부적으로 시행 착오를 거치며 조금씩 바꿔가는 중입니다.

JIRA에서 Linear로

JIRA가 좋기는 하지만 이름이 별로 마음에 안 들고^^; 프로그램 인터페이스가 약간 불편하고, 결정적으로 최근에 JIRA 개발사에서 MacOS용 프로그램 개발을 중단해서 다른 이슈 관리 프로그램들을 찾아봤습니다. Linear라는 소프트웨어를 알게 되었는데, 수학적인 이름이 마음에 들고^^ 프로그램 인터페이스가 멋지고, MacOS에서 키보드를 이용해 대부분의 작업을 수행할 수 있어서 Linear로 옮겼습니다. 현재는 Linear와 GitHub를 사용하고 있습니다.

바흐와 함께

평소 소프트웨어를 개발할 때 바흐의 “평균율 클라비어곡집”이나 “골드베르크 변주곡”을 배경 음악으로 틀어놓고 작업하는 경우가 많습니다. 이번 여름엔 방열기 공사로 연구실 벽에 구멍이 뚫려 있어서 조용히 작업하고 있네요. 글을 쓰다 보니 “바흐: 천상의 음악”이라는 책을 반 정도 읽다가 만 것이 생각나 책을 꺼냈습니다. 이제 책을 읽어야겠습니다^^

슈퍼컴퓨터 작업 관리 프로그램 제작 시작

한 컴퓨터 회사의 의뢰로 7월 말부터 슈퍼컴퓨터(High Performance Computer)의 작업 관리 프로그램을 만들기 시작했습니다. 이 프로그램의 목적은 클러스터 서버 사용자들이 작업 스케줄러 프로그램 사용법을 몰라도 쉽게 서버에 작업(Job)을 제출하고 모니터링할 수 있도록 만들어주는 것입니다.

작업 스케줄러(Job Scheduler)

클러스터 컴퓨터는 보통 여러 사용자들이 사용합니다. 여러 사용자가 동시에 자신의 작업을 실행하면 멀티태스킹으로 인해 전체적인 계산 효율이 떨어지게 됩니다. 서버 자원(CPU, GPU 등)을 효율적으로 사용하기 위해 작업들에 계산 자원을 효율적으로 할당하는 역할을 하는 것이 작업 스케줄러입니다.

작업 스케줄러에는 Slurm, PBS, TORQUE, Oracle Grid Engine 등 여러 가지들이 있는데 일단 만들고 있는 것은 Slurm의 명령어를 이용하는 GUI (Graphical User Interface)입니다. 이런 GUI 프로그램들은 대부분 상용이고 공개 프로그램도 소수 있습니다.

최소 기능 제품 (MVP, Minimum Viable Product)

회사의 피드백을 받아가면서 빠르게 작업할 수 있도록 최소한의 기능을 담은 제품을 만들었습니다. 현재 일반적인 MPI 작업을 제출하고 모니터링하고 취소할 수 있습니다.

다음은 현재 진행중인 작업을 확인/취소할 수 있는 페이지입니다.

다음은 현재 클러스터 컴퓨터의 전체/파티션(노드 그룹)별/노드별 CPU/Memory 사용량을 모니터링할 수 있는 페이지입니다.

아래는 작업에 필요한 파일들을 서버에 업로드하고 관리하는 페이지입니다.

다음은 작업 제출에 필요한 정보를 입력하고 제출할 수 있는 페이지입니다.

고급 사용자의 경우 앞에서 입력한 정보를 바탕으로 생성된 작업 제출 스크립트를 확인/수정하고 제출할 수도 있습니다.

앞으로 상용 해석 프로그램(Abaqus, Ansys, StarCCM+ 등) 작업 제출 기능과 사용자 관리 기능 등을 추가할 계획입니다.

외부 기관을 위한 소프트웨어 제작

외부 기관을 위한 소프트웨어 제작은 이번이 세 번째입니다.

첫 번째는 2019년 국방과학연구소의 GPU를 이용한 수중채널모델링 프로그램이었습니다. 파동 전파를 이용하는 프로그램이었기 때문에, 제 연구 분야와도 관련이 있었죠.

두 번째는 2020년 부산대/대우조선해양의 쇄빙선 모델링 프로그램의 GPU 병렬화 프로젝트였습니다. 제 전공 분야가 아니었기에 2019년 처음 의뢰가 들어왔을 때는 거절했었죠. 부산대에서 자체적으로 1년간 진행했으나 원하는 성능이 나오지 않는다고 2020년 여름에 다시 의뢰가 들어왔습니다. 그래서 8월 한 달간 프로그램 개발을 진행했고 만족할만한 성능을 얻었습니다. 해당 경험을 통해 보유 기술을 전공 분야에만 한정하는 것보다 기술을 필요로 하는 다른 분야에 적용하는 것이 사회적으로 더 큰 가치가 있을 수 있다는 것을 깨달았습니다.

앞의 두 번은 모두 수치해석 프로그램이었지만 이번 프로그램은 직접적인 수치해석 프로그램은 아닙니다. GUI 프로그램이나 웹 개발도 평소 연구와 거리가 멀기에 몇 달 전 의뢰가 들어왔을 때 처음에는 거절했었죠. 회사에서는 다른 곳에 의뢰해서 개발을 진행했으나 결과가 마음에 들지 않아서 제게 다시 의뢰가 들어왔고, 이번에도 한여름에 개발을 진행하고 있습니다. 앞의 프로젝트와 패턴이 유사하네요. 이번 2학기 지구물리 기계학습 수업과 내년 1학기 지구물리 클러스터 컴퓨팅 수업에서 웹 개발을 일부 다룰 계획인데, 이번 개발이 좋은 경험이 될 듯 합니다.

Docker 이미지 저장 경로 바꾸기

Docker pull을 사용하다가 리눅스 서버의 root에 마운트된 디스크가 꽉 차서 Docker Root 경로를 바꾸는 방법을 찾아봤습니다(참고: link).

현재 Docker Root Dir이 /var/lib/docker, 변경할 이미지 저장 경로는 /data/docker/root라고 하겠습니다. 이미지 저장 경로는 미리 만들어 둡니다. 참고로, 현재 Docker Root Dir은 다음 명령으로 확인할 수 있습니다.

$ docker info |grep Root

아래 작업들은 root 계정으로 진행합니다. 먼저 docker daemon을 정지합니다.

# service docker stop

/etc/docker/daemon.json 파일에 다음 내용을 추가합니다(Docker v17.05.0 이상).

{ 
   "data-root": "/data/docker/root"
}

기존의 Docker 데이터를 새로운 경로로 복사합니다. 기존 데이터를 지우기 전에 Docker가 잘 작동하는 것을 확인하려고 합니다. 이를 위해 기존 경로의 이름을 바꾸고 Docker를 재시작합니다.

# cp -rp /var/lib/docker/* /data/docker/root/
# mv /var/lib/docker /var/lib/docker.old
# service docker start

Docker가 잘 작동하는지 확인한 후 기존 경로를 삭제합니다.

# docker info |grep Root
# docker images
# rm -rf /var/lib/docker.old

Windows Subsystem Linux 2 (WSL2) Setting

Windows에서 Linux를 사용하기 위해 설치하는 방법입니다.

Ubuntu 설치

참고: https://docs.microsoft.com/ko-kr/windows/wsl/install

Windows 10 Build 19041 이상 또는 Windows 11 이상에서 Windows PowerShell을 관리자 권한으로 실행한 후 다음 명령을 실행합니다.

$ wsl --install

설치하면서 사용자 아이디와 비밀번호를 지정해주면 Ubuntu Linux를 사용할 수 있습니다. 그러나 X-window 프로그램을 사용하기 위해서는 아래 추가 설정이 필요합니다.

X windows 설정 (WSL2)

참고: https://evandde.github.io/wsl2-x/

WSL2에서는 X-window 설정 방법이 달라졌습니다.

  • 먼저 Linux X-window 화면을 Windows 상에 띄워주는 Xming 설치 프로그램을 다운로드하고 실행합니다.
  • 설치된 Xming 프로그램을 한 번 실행했다가 종료합니다.
  • Xming 단축아이콘을 만들고 우클릭-속성-대상에서 명령어 끝에 -ac를 추가해줍니다.
  • Windows PowerShell을 관리자 권한으로 실행한 후 다음 명령어를 입력합니다.
$ Set-NetFirewallRule -DisplayName "Xming X Server" -Enabled True -Profile Any
  • Ubuntu Linux 창에서는 다음 명령어를 입력해줍니다.
$ echo 'export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '"'"'{print $2}'"'"'):0' >> ~/.bashrc
  • 방화벽 허용: 제어판-업데이트 및 보안-Windows 보안-Windows 보안 열기-방화벽 및 네트워크 보호-방화벽에서 앱 허용-Xming을 허용해줍니다.
  • 앞에서 만든 단축아이콘을 이용해 Xming을 다시 실행하면 Linux에서 X-window 프로그램들을 Windows 화면에 띄울 수 있습니다.

Seismic Unix 설치

탄성파 자료 처리를 위해 Seismic Unix를 $HOME/SU에 설치하는 방법입니다.

  • Ubuntu에서 ~/.bashrc 파일에 다음 줄들을 추가합니다.
export CWPROOT=$HOME/SU
export PATH=$PATH:$CWPROOT/bin
  • Ubuntu Linux 창에서 다음 명령어들을 실행합니다. XDR 형식이 아닌, native binary 형식을 사용하도록 만들었는데, XDR 형식을 사용할 경우 제일 아래 두 줄에서 XDRFLAG= 부분을 지워주면 됩니다.
$ source ~/.bashrc
$ apt install -y build-essential libx11-dev libxt-dev freeglut3 freeglut3-dev libxmu-dev libxi-dev gfortran
$ cd $CWPROOT 
$ wget 'https://nextcloud.seismic-unix.org/s/LZpzc8jMzbWG9BZ/download?path=%2F&files=cwp_su_all_44R19.tgz' -O cwp_su_all_44R19.tgz
$ tar -zxvf cwp_su_all_44R19.tgz 
$ cd src 
$ make XDRFLAG= install
$ make XDRFLAG= xtinstall

GNU plot 설치

과학 기술 그래프를 그리는데 사용하는 gnuplot을 설치하는 방법입니다(참고로 Windows용 gnuplot도 있습니다).

  • Ubuntu Linux에서 다음 명령을 실행합니다.
$ sudo apt install gnuplot gnuplot-x11
  • Ubuntu에서 ~/.bashrc 파일에 다음 줄을 추가합니다.
export GNUTERM=x11

이제 Linux 창을 새로 시작하면 gnuplot을 사용할 수 있습니다.

LaTeX 설치

LaTeX는 Linux에 설치할 수도 있으나 Windows용도 있으므로 Windows에서 사용한다면 MikTex 설치 프로그램을 받아서 실행하면 됩니다. 설치하면 PowerShell 또는 Windows 명령 프롬프트에서 latex, pdflatex 등의 명령을 사용할 수 있습니다. TexMaker와 같은 전용 에디터를 사용하면 더 쉽게 LaTeX 문서를 만들 수 있습니다.

서버의 Docker 컨테이너에서 실행한 jupyter, tensorboard에 접속하기

Docker와 GitHub를 이용한 소프트웨어 연구 환경 구축에서 Docker 프로젝트 템플릿에 대해 소개해드렸습니다. 리눅스 서버에서 프로젝트 템플릿을 이용해 만든 Docker 컨테이너를 실행하고, 컨테이너 내에서 Jupyter lab/notebook 또는 tensorboard를 실행한 후 데스크탑 PC의 웹브라우저를 이용해 접속하는 방법을 살펴보겠습니다. 아래 명령들을 실행하기 전에 먼저 컨테이너를 만들고(build.sh) 실행해주세요(run.sh).

Jupyter lab/notebook

서버의 프로젝트 내 docker 디렉토리에서 다음 명령을 실행합니다.

$ ./exec.sh lab (또는 ./exec.sh notebook)

위 명령을 실행하면 jupyter lab/notebook이 background로 실행됩니다. 실제로는 다음 명령이 실행됩니다.

$ jupyter lab[notebook] --no-browser --ip=0.0.0.0 --NotebookApp.token=****

위에서 ****는 접속 비밀번호로, Env.sh 파일에서 지정해줍니다.

컨테이너의 jupyter port, tensorboard port와 서버의 해당 port는 컨테이너 실행시 연결된 상태이므로 이제 데스크탑 PC의 port를 서버의 해당 port로 연결하면 됩니다. 참고로, Port 번호는 Env.sh 파일에서 바꿀 수 있습니다.

데스크탑 PC에서 서버로 연결하기 위해 웹브라우저에서 server_ip:8888 주소로 접속하면 됩니다. 만약, localhost 주소로 사용하고 싶다면 다음 ssh tunneling 명령을 실행합니다. 리눅스나 MacOS의 경우 터미널에서, 윈도우즈의 경우 PowerShell에서 실행하면 됩니다.

$ ssh -N -L 8888:localhost:8888 your_id@server_ip

위 명령을 통해 서버와 데스크탑 PC의 port를 연결했다면 데스크탑 웹브라우저에서 localhost:8888 주소로 서버 컨테이너의 jupyter lab/notebook에 접속하여 사용할 수 있습니다.

TensorBoard

텐서보드도 사용할 수 있습니다. 텐서보드 파일이 src/logs 디렉토리에 있을 경우 docker 컨테이너 내에서 src 디렉토리로 이동한 후 tensorboard를 실행합니다.

$ ./exec.sh # 컨테이너 내 터미널 접속
$ cd src
$ tensorboard --bind_all --logdir=logs

위 명령을 실행한 후에는 tensorboard port를 이용하여 앞에서와 같이 웹브라우저에서 server_ip:6006으로 접속하던가 ssh tunneling 명령을 실행합니다.

$ ssh -N -L 6006:localhost:6006 your_id@server_ip

이제 데스크탑 웹브라우저에서 localhost:6006 주소로 서버 컨테이너에서 실행한 tensborboard에 접속할 수 있습니다.

참고로, Jupyter lab/notebook과 TensorBoard를 동시에 사용한다면 다음과 같이 한 번에 연결할 수도 있습니다.

$ ssh -N -L 8888:localhost:8888 -L 6006:localhost:6006 your_id@server_ip

포트란과 런타임 다형성 (runtime polymorphism)

포트란에서의 객체지향프로그래밍 다형성과 관련하여 함수 오버로딩연산자 오버로딩에 대해 글을 올린 적이 있습니다. 함수 오버로딩은 다른 함수를 같은 이름으로 사용하는 것으로, 이를 통해 하나의 함수 이름으로 다른 인수(argument)를 사용하는 것과 같은 효과를 나타낼 수 있습니다. 연산자 오버로딩은 함수를 이용해 기존 연산자의 기능을 재정의하거나 새로운 연산자를 만드는 기능이었죠. 런타임 다형성은 하나의 함수에 서로 다른 인수를 사용하는 기능으로, 상속 및 오버라이딩과 관련이 있습니다.

함수 오버로딩에서도 하나의 함수 이름으로 다른 인수를 사용하는 것과 같은 효과를 낼 수 있다고 하였는데, 실제로는 인수에 따라 다른 함수들을 정의하고 같은 이름으로 사용한 것인 반면에, 런타임 다형성을 이용하면 하나의 함수만 정의하고 서로 다른 인수를 사용할 수 있습니다. 이 때 인수들은 아무 인수나 되는 것은 아니고 서로 상속 관계에 있어야 합니다. 코드를 살펴보겠습니다.

module m_hero
    type:: hero_t
    contains
        procedure::speak => speak_hero
    end type 

    type,extends(hero_t):: ironman_t
    contains
        procedure:: speak => speak_ironman
    end type

    type,extends(hero_t):: spiderman_t
    contains
        procedure:: speak => speak_spiderman
    end type

contains

    subroutine speak_hero(hero)
    class(hero_t):: hero
    print*,'I am a hero.'
    end subroutine

    subroutine speak_ironman(hero)
    class(ironman_t):: hero
    print*,'I am Iron Man.'
    end subroutine

    subroutine speak_spiderman(hero)
    class(spiderman_t):: hero
    print*,"I'm Peter."
    end subroutine

    subroutine who_are_you(hero)
    class(hero_t):: hero
    call hero%speak()
    end subroutine

end module

위 코드에서 ironman_tspiderman_thero_t를 상속합니다. 모두 각자의 speak 메소드를 가지고 있는데, 서로 다른 type이지만 다음과 같이 who_are_you 서브루틴의 인수로 사용할 수 있습니다.

program test_runtime_polymorphism1
    use m_hero
    type(hero_t):: hero
    type(ironman_t):: ironman
    type(spiderman_t):: spiderman

    call who_are_you(hero)
    call who_are_you(ironman)
    call who_are_you(spiderman)
end program

실행 결과는 예상대로 다음과 같습니다.

I am a hero.
I am Iron Man.
I’m Peter.

이번에는 ihero라는 변수 값에 따라 hero들 중 한 명이 달려가 사람들을 구해야 한다고 생각해봅시다(모두 run, save_people 메소드를 가지고 있다고 합시다). 아래와 같이 구현할 수 있습니다.

program test_runtime_polymorphism2_not_good
    use m_hero
    type(ironman_t):: ironman
    type(spiderman_t):: spiderman
    integer:: ihero = 1

    select case (ihero)
    case (1)
        call who_are_you(ironman)
        !call ironman%run()
        !call ironman%save_people()
    case (2)
        call who_are_you(spiderman)
        !call spiderman%run()
        !call spiderman%save_people()
    end select
end program

그러나 위 코드는 중복이 많습니다. Hulk, Black Widow, Thor 등 새로운 avenger가 올 때마다 코드가 여러 줄씩 늘어나게 됩니다. 아름답지 않습니다! 포인터를 이용하면 hero_t 클래스 변수가 자식 type을 가리키도록 할 수 있습니다.

program test_runtime_polymorphism2_better
    use m_hero
    class(hero_t),pointer:: hero
    type(ironman_t),target:: ironman
    type(spiderman_t),target:: spiderman
    integer:: ihero = 1

    select case (ihero)
    case (1)
        hero => ironman
    case (2)
        hero => spiderman
    end select

    call who_are_you(hero)
    !call hero%run()
    !call hero%save_people()
end program

아름답습니다! 유지 보수하기 훨씬 좋아졌습니다.

Stack overflow로 인한 Segmentation fault 해결하기

포트란, C와 같은 컴파일 언어 사용시 함수나 서브루틴 내에서 선언하는 변수는 기본적으로 메모리의 stack이라는 정적 할당 영역에 저장하게 됩니다. 만약 함수나 서브루틴 내에서 선언한 배열의 크기가 커지면 시스템의 stack 크기를 넘어갈 수 있고(stack overflow) 이 경우 잘못된 메모리 주소를 참조하는 segmentation fault 에러가 발생하게 됩니다. Stack overflow를 방지하는 세 가지 방법을 살펴보겠습니다.

  1. 직접 allocate: 배열을 필요에 따라 함수나 서브루틴 내에서 할당하고 해제하는 방식으로 코드를 수정해줍니다. 이 경우 stack 대신 heap이라는 동적 할당 영역을 사용하므로 stack overflow가 발생하지 않습니다. 그러나 코드를 수정해야 하므로 제 경우 2번이나 3번 방식을 선호합니다.
  2. 시스템 stack 크기 증가: 리눅스에서 ulimit -s unlimited 명령을 이용해 stack 크기를 무한히 크게 만들 수 있습니다.
  3. 컴파일 옵션: 컴파일시 크기가 큰 배열은 stack 대신 heap 공간을 사용하도록 옵션을 추가합니다. ifort에는 -heap-arrays [size], gfortran에는 -fmax-stack-var-size=n 옵션이 있습니다.