[첫화면으로]Perl/Perlbrew

마지막으로 [b]

49 번째 수정본

요놈이 뭐하는 건지는 아래 참고 링크들 읽어보면 잘 나와 있다.

여기서는 주인장이 perlbrew를 쓰면서 겪었던 사항을 정리.

1. 새 버전의 Perl을 인스톨 할 때 옵션
2. local::lib 와 충돌
2.1. perlbrew 자체의 중복 문제
2.2. 추가 설치한 CPAN 모듈들의 공유
2.3. 시스템 펄과 추가 설치한 펄 사이의 스위칭
2.4. 정리
3. 추가 모듈 일괄 설치
4. crontab 에 적용
5. screen 위에서 스위칭
6. CGI 스크립트의 스위칭
7. 참고
8. Comments

1. 새 버전의 Perl을 인스톨 할 때 옵션

다른 건 모르겠고 -Dusethreads는 꼭 넣어서 쓰레드를 지원하도록 컴파일하는 게 좋다고 함.1

2. local::lib 와 충돌

나는 이 gyparkwiki가 있는 서버에 root권한이 없기 때문에, 추가로 설치하는 모듈들은 다 개인 계정에 저장된다. 이 때 저장되는 위치를 지정하고, 차후에 스크립트를 실행할 때 그 모듈을 로드할 수 있도록 경로를 지정해야 하는데, 현재는 Cpan:local::lib 모듈을 사용하고 있다2

local::lib에서 사용할 베이스 디렉토리는 ~/local/perl이었고, 결과적으로 .bash_profile에 적어 준 코드에 의해서
eval $(perl -I$HOME/local/perl/lib/perl5 -Mlocal::lib=$HOME/local/perl)

내가 cpan 을 써서 설치하는 모듈들은 다음 경로에 저장되고, 이 두 경로는 PERL5LIB 환경 변수에 들어간다.

그런데 서버에 있는 펄이 5.8이라서 영 불편한 터라, 5.14를 설치하기 위해 perlbrew를 설치하였는데 여기서 여러 가지 면에서 충돌이 발생했다.

2.1. perlbrew 자체의 중복 문제

perlbrew 홈페이지에서 안내하는 대로 curl -kL http://install.perlbrew.pl | bash를 통해 설치했더니, perlbrew 실행파일이 ~/perl5/perlbrew/bin/ 아래에 설치되었다. 그런데 문제는 내가 예전에 CPAN을 통해서 Cpan:App::perlbrew를 설치한 적이 있었다는 것. 이 때 설치된 실행파일은 ~/local/perl/bin/ 아래에 있었다. 이걸 모른채로 진행을 했더니, 내가 현재 시스템 펄을 쓰고 있느냐 perl-5.14 를 쓰고 있냐에 따라서 서로 다른 perlbrew가 실행되는 경우가 있었다(확실히 확인하지는 못했음).

더 큰 문제는 원래 설치된 버전이 꽤나 오래 전 버전이라서(0.07. 현재 버전은 0.41), perlbrew의 동작 방식이 크게 바뀌었다는 점이다.

게다가 5.14를 설치한 상태에서 현재 사용 가능한 펄 리스트를 뽑아보면
$ perlbrew installed (이 installed라는 명령도 현 버전은 list로 바뀌었음)
perl-5.14.2
/usr/bin/perl
이렇게 나오는데, 시스템 펄로 돌아가기 위해서 perlbrew off를 하지 않고 switch를 했더만 그 이후로 5.14쪽으로 다시 스위치를 도저히 할 수가 없었다.

뒤늦게야 중복 설치되어 있다는 걸 깨닫고 구버전을 제거는 했는데, 뭐가 꼬였는지 그 후로도 스위칭이 잘 안 되어서, 결국 ~/perl5 를 싹 지우고 새로 시도했음.

2.2. 추가 설치한 CPAN 모듈들의 공유

그 다음은, 시스템 펄을 사용하면서 추가로 개인 계정에 설치한 CPAN 모듈들을 5.14에서도 쓸 수 없을까 하는 거였다. 아니면 전부 다시 설치해야 하는데 엄두가 안 나는 일이라서.

그래서 이리 저리 구글링해 봤는데, 녹록치 않아 보인다.

일단, 가장 큰 문제는 펄만으로 짜여진 모듈 말고 c 등을 써서 만들어진 XS모듈들은 펄의 메이저 버전이 바뀌면 호환이 되지 않는다는 점.
# perlbrew는 5.14를 쓰도록 지정된 상태에서 5.8 모듈 경로가 PERL5LIB 변수에 들어 있으면 이렇게 에러가 난다.
$ perlbrew switch perl-5.14.2
perl: symbol lookup error: /home/gypark/local/perl/lib/perl5/i386-linux-thread-multi/auto/IO/IO.so: undefined symbol: Perl_Tstack_sp_ptr

그나마 다행스럽게도(?), XS모듈이 들어가는 경로의 이름이 시스템 펄은 i386-linux-thread-multi였고, 5.14의 경우는 i686-linux-thread-multi였다. 그래서 순수 펄 모듈은 ~/local/perl/lib/perl5/ 아래에서 공유하고, XS모듈은 저 두 디렉토리 아래서 각각 관리하게 할 수 있지 않을까 싶긴 했는데...

그 다음은 ~/local/perl/bin/ 아래 설치되는 실행 스크립트들이 문제. 당장 perlbrew라거나, cpanm 등 스크립트들이, "5.14를 쓰도록 스위칭되고 있는 상황에서 PERL5LIB에 시스템 펄 쪽 모듈인 i386-linux-thread-multi 디렉토리가 추가되어 있으면" 여지없이 위와 같은 symbol lookup error를 낸다. 따라서 펄 스위칭을 할 때마다 PERL5LIB 값을 고쳐줘야 하는데, 앞으로도 잘 될지 보장도 없고 해서, 결국 다 포기하고 5.14용 모듈은 전부 설치하기로 함.

최종적으로 경로는 다음과 같이 잡힌다.
~
 /local/perl                              - local::lib 에서 관리하는 베이스 경로
                                            (Module::Build나 MakeMaker에서 install base로 지정됨)
            /bin                            - 실행 스크립트가 설치되는 곳
            /man                            - 맨페이지
            /lib                            - 모듈 설치
                /perl5                        - 순수 펄 모듈
                /i386-linux-thread-multi      - XS 모듈

 /perl5/perlbrew                          - $PERLBREW_ROOT
                /bin                        - perlbrew, patchperl, cpanm 이 설치됨
                /perls                      - perlbrew install 명령으로 설치하는 배포본들이 이 아래 설치됨
                      /perl-5.14.2            - perl-5.14.2 로 스위칭했을 때 $PERLBREW_PERL 값이 'perl-5.14.2'로 설정
                                  /bin          - 실행 스크립트
                                  /man          - 맨페이지, $PERLBREW_MANPATH
                                  /lib
                                      /5.14.2                          - 코어 모듈들이 여기에 설치됨
                                             /i686-linux-thread-multi  - 코어 모듈 중 XS 모듈들
                                      /site_perl                       
                                                /5.14.2                         - cpan을 사용하여 설치하는 추가 모듈
                                                       /i686-linux-thread-multi - 추가 모듈 중 XS 모듈
                      /perl-5.12.4
                      /perl-5.10.1
                        ...

즉 cpan을 써서 추가로 설치한 모듈들은 ~/perl5/perlbrew/perls/perl-5.14.2/lib/site_perl/5.14.2 또는 그 아래 i686-linux-thread-multi에 설치됨.

2.3. 시스템 펄과 추가 설치한 펄 사이의 스위칭

perlbrew switch로 추가 설치한 펄로 스위칭하거나, perlbrew switch-off로 시스템 펄로 되돌릴 수 있는데, 시스템 펄을 쓰는 경우에만 local::lib가 적용되어야 한다. 따라서 .bash_profile에 다음과 같이 해 주었다.

# perlbrew에 의한 설정을 먼저 적용한다
if [ -f $HOME/perl5/perlbrew/etc/bashrc ]
then
    source /home/gypark/perl5/perlbrew/etc/bashrc
fi

# $PERLBREW_PERL 변수의 값이 빈 스트링인 경우는 off 상태이다.
# 이 때만 local::lib 의 설정을 적용한다.
if [ "$PERLBREW_PERL" == "" ]
then
    eval $(perl -I$HOME/local/perl/lib/perl5 -Mlocal::lib=$HOME/local/perl)
fi

# 마지막으로, 내가 따로 만들어 쓰는 모듈이 들어가는 경로를 추가
export PERL5LIB=$HOME/git/myperlmodules:$PERL5LIB

이렇게 해서, 로그인하는 시점에 local::lib 설정을 적용할지 말지를 결정할 수 있다.

남은 문제는, 로그인한 후에 perlbrew 를 써서 시스템 펄과 추가 설치한 펄 사이를 오갈 때 local::lib 에 의한 설정을 적용했다 없앴다 하는 과정이 필요하다는 것이다. (특히나 시스템 펄에서 추가 설치한 펄로 스위칭할 때, PERL5LIB 변수의 값 때문에 여기서도 심볼 룩업 에러가 난다)

이것은, 로그인할 때 불리는 perlbrew/etc/bashrc를 고쳐주었다. 이 부분은 정확히 고친 건지 확신은 없지만, 테스트해보면 잘 동작하는 듯.
--- bashrc.original 2012-02-23 01:48:35.000000000 +0900
+++ bashrc  2012-02-23 04:40:10.000000000 +0900
@@ -39,6 +39,28 @@
 }
 __perlbrew_set_path

+# local::lib와 혼용을 위한 설정
+# 아래 두 변수의 값을 자신의 환경에 맞춰 적어줌
+_LOCAL_LIB_INC="$HOME/local/perl/lib/perl5"
+_LOCAL_LIB_ROOT="$HOME/local/perl"
+
+# local::lib 에 의한 설정을 제거
+__unset_local_lib () {
+    export PERL5LIB="$PERL5LIB"
+    eval $(perl -I$_LOCAL_LIB_INC -Mlocal::lib=--deactivate-all,$_LOCAL_LIB_ROOT)
+}
+
+# local::lib 설정 적용
+__set_local_lib () {
+    eval $(perl -I$_LOCAL_LIB_INC -Mlocal::lib=--deactivate-all,$_LOCAL_LIB_ROOT)
+    export _MY_PERL5LIB="$PERL5LIB"
+    eval $(perl -I$_LOCAL_LIB_INC -Mlocal::lib=$_LOCAL_LIB_ROOT)
+    if [ -n "$_MY_PERL5LIB" ]; then
+        export PERL5LIB="$(perl -e 'print join ":", $ENV{_MY_PERL5LIB}, grep { index($_, $ENV{_MY_PERL5LIB}) } split/:/,$ENV{PERL5LIB};')"
+    fi
+    unset _MY_PERL5LIB
+}
+
 perlbrew () {
     local exit_status
     local short_option
@@ -70,6 +92,7 @@
                         eval $line
                     done
                     IFS=$OLD_IFS
+                    __unset_local_lib
                     __perlbrew_set_path
                 fi
             fi
@@ -79,6 +102,7 @@
               if [[ -z "$2" ]] ; then
                   command perlbrew switch
               else
+                  __unset_local_lib
                   perlbrew use $2
                   __perlbrew_reinit $2
               fi
@@ -88,12 +112,14 @@
             unset PERLBREW_PERL
             eval `perlbrew env`
             __perlbrew_set_path
+            __set_local_lib
             echo "perlbrew is turned off."
             ;;

         (switch-off)
             unset PERLBREW_PERL
             __perlbrew_reinit
+            __set_local_lib
             echo "perlbrew is switched off."
             ;;

펄 버전, @INC, 환경변수, PATH를 출력하는 스크립트를 만들어서 테스트해 보았다:
[gypark@www temp]$ perlbrew list
* perl-5.14.2
[gypark@www temp]$ ./t.pl
version: 5.014002
/home/gypark/git/myperlmodules          (<----- 로그인할 때 추가로 지정해 준 PERL5LIB 경로)
/home/gypark/perl5/perlbrew/perls/perl-5.14.2/lib/site_perl/5.14.2/i686-linux-thread-multi     (<----- perlbrew 모듈 경로)
(생략)
.
----------------
[PERL5LIB][/home/gypark/git/myperlmodules]
(생략)
[PERLBREW_PERL][perl-5.14.2]          (<----- 현재 사용하도록 설정된 펄)
(생략)

----------------
[PATH]
/home/gypark/perl5/perlbrew/bin
/home/gypark/perl5/perlbrew/perls/perl-5.14.2/bin          (<----- 현재 사용하는 펄의 path)
/home/gypark/bin
(생략)

[gypark@www temp]$ perlbrew off          (<----- off 해서 끄면)
perlbrew is turned off.

[gypark@www temp]$ ./t.pl
version: 5.008008          (<----- 버전이 5.8.8로 돌아가고)
/home/gypark/git/myperlmodules          (<----- 이 경로는 여전히 제일 위에 남고)
/home/gypark/local/perl/lib/perl5/i386-linux-thread-multi          (<----- 여기 3줄이 local::lib의 적용)
/home/gypark/local/perl/lib/perl5/i386-linux-thread-multi
/home/gypark/local/perl/lib/perl5
/usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi          (<----- 시스템 펄의 모듈 경로)
(생략)
.
----------------
[PERL5LIB][/home/gypark/git/myperlmodules:/home/gypark/local/perl/lib/perl5/i386-linux-thread-multi:/home/gypark/local/perl/lib/perl5]
(생략)
[PERLBREW_PERL][]          (<----- perlbrew가 꺼졌음)
(생략)
[PERL_LOCAL_LIB_ROOT][/home/gypark/local/perl]          (<----- 이 3줄에 있는 변수들도 local::lib 적용으로 생긴 것)
[PERL_MB_OPT][--install_base /home/gypark/local/perl]
[PERL_MM_OPT][INSTALL_BASE=/home/gypark/local/perl]

----------------
[PATH]
/home/gypark/local/perl/bin          (<----- local::lib의 적용)
/home/gypark/perl5/perlbrew/bin          (<----- 이 아래 5.14.2 path는 사라졌음)
/home/gypark/bin
(생략)

2.4. 정리

정리하면

3. 추가 모듈 일괄 설치

5.8을 쓰면서 추가로 설치하여 ~/local/perl 내에 설치한 모듈들을 다시 5.14에도 설치해야 했는데, 그러려면 목록을 얻어야 한다.

여담으로, cpan을 실행했더니만 명령행 히스토리 기능이 동작을 안 하더라. Term::ReadLine::Perl을 추가로 설치해주면 된다.

한가지 더, perlbrew install-cpanm하면 cpanm을 따로 $PERLBREW_ROOT/bin 아래 설치하여 현재 스위칭한 펄에 무관하게 쓸 수 있게 해 준다.

4. crontab 에 적용

이 서버에는 RSS를 만들기 위해 crontab을 써서 일정 시간마다 실행되는 스크립트들이 있다. 그런데 이것 때문에 좀 고생을 하게 되었다:

crontab상에서 .bashrc나 .bash_profile등의 스타트업 파일을 불러오는 방법은 못 찾았다6

별 수 없이 crontab의 각 항목마다 각각 설정해줘야 했는데, 대충 다음 두 가지 방법을 찾았음

1) 명시적으로 사용할 펄 버전을 지정하는 방법7
PERLBREW_PERL="perl-5.14.2"
10 0 * * *   PATH="$HOME/perl5/perlbrew/perls/$PERLBREW_PERL/bin:$PATH" /usr/bin/env perl /some/script_path
# 또는 환경 변수를 쓰지 않고
10 0 * * *   $HOME/perl5/perlbrew/perls/perl-5.14.2/bin/perl /some/script_path
또는 perl을 실행하지 않고 곧바로 스크립트 이름을 적는 경우:
10 0 * * *   PATH="$HOME/perl5/perlbrew/perls/perl-5.14.2/bin:$PATH" /some/script_path

2) perlbrew에서 스위칭한 펄을 사용하도록 하는 방법 (권장하지는 않음 - 아래 라슈펠님 커멘트 참조)
10 0 * * *   /bin/bash --login -c "/some/script_path"

perlbrew를 써서 최신 버전의 펄을 사용하는 경우는

아니면 그냥 crontab을 통해 실행할때는 시스템 펄을 사용하도록 하고, 다만 모듈 버전이 문제가 되니 crontab에 PERL5LIB 변수를 적어주어서 내 개인 계정의 모듈 경로를 추가해 주어도 되겠다:
# PERL5LIB 값을 명시해주고
PERL5LIB=/home/gypark/local/perl/lib/perl5:/home/gypark/local/perl/lib/perl5/i386-linux-thread-multi

# 그냥 시스템 펄을 사용하도록
10 0 * * *   /some/script_path

5. screen 위에서 스위칭

ScreenUtility를 실행한 상태에서 그 위에서 perlbrew로 스위칭을 하는 건... 생각처럼 잘 안 된다.

일단 경험상 알아낸 것만 정리하면

6. CGI 스크립트의 스위칭

지금 이 gyparkwiki와 같이 웹을 통해서 실행되는 스크립트를 내 개인 계정의 5.14 위에서 돌게 하려면... shebang 라인에 full path 적어주는 수밖에 없으려나...?

아니면 애초에 너무 무모한 생각인가..?

7. 참고

8. Comments

cpan명령으로 App::perlbrew로 설치해서 perlbrew를 사용한다고 했을때 버젼을 switch하면 아마도 perlbrew는 예전 perl쪽이랑 연결이 유지되거나 링크가 끊기며 그순간 사용불능이 되거나 해서 문제를 일으킬 소지가 많다고 생각되네요.

curl -kL http://install.perlbrew.pl | bash 명령으로 설치해서 단일 파일로 돌아가게 하고 최신버젼 업데이트는
perlbrew self_update 명령을 통해서 하면 문제가 생길 소지가 좀 줄어들 것 같습니다.

정리하면 perlbrew는 cpan명령으로 App::perlbrew모듈을 설치하지말고 웹에서 직접 땡겨서 부트스트리핑하는 단일 패킹된 perlbrew만 쓰자가 되겠네요.
-- aero 2012-2-23 9:26 am

네, 그래야겠더라고요. 겉으로는 차이점을 알아 보지 못해서 고생 좀 했습니다ㅎ
-- Raymundo 2012-2-23 11:26 am

bash에 --login 옵션을 주면 상황에 관계 없이 .bash_profile을 읽습니다만… 애초에 스크립트는 자신에게 필요한 환경 설정은 스스로 하는게 맞습니다. 아니면 다이나믹 라이브러리 패스(LD_LIBRARY_PATH? 시스템에 따라 좀 다릅니다)같은게 바뀐채로 돌아서 엉뚱한 일을 하게 만드는 해킹이 먹히죠. 그런 이유로 suid 걸리면 애초에 스태틱 링크를 하든가 다이나믹 라이브러리 패스를 시스템 차원에서 고정시키는 경우도 있죠.
-- 라슈펠 2012-2-23 1:33 pm

안녕하세요, "bash에 --login 옵션 주는" 게 어느 시점에서 말씀이세요? crontab 에
SHELL=/bin/bash --login
또는
SHELL="/bin/bash --login"
또는
SHELL=/bin/bash\ --login
해 봤는데 아예 전혀 실행이 안 되더라고요.
(이 얘기를 하신 게 아닌 건지 아니면 원래 되던 건데 이것마저 막았다고 봐야 하는 건지)
(그나저나 한 십년 전에 리눅스 사용에 관한 두꺼운 책 다 읽었던 건 하나도 기억이 안 나서 ^^; 계속 도움 받는군요, 감사합니다)
-- Raymundo 2012-2-23 1:54 pm

그냥 crontab에 들어가는 명령을

/bin/bash --login -c <script path/name>

정도로 하시면 될겁니다.
-- 라슈펠 2012-2-23 2:18 pm

아 -c는 빼는게 낫겠군요.
-- 라슈펠 2012-2-23 2:20 pm

꼬박꼬박 콜론 숫자 챙겨서 들여쓰기 하지 않으셔도 되는데요 ^^

실행할 게 쉘스크립트가 아니다보니 -c를 넣어야겠네요. 잘 동작하는 거 확인했습니다. /박수
-- Raymundo 2012-2-23 2:34 pm

근데 .bash_profile 내용에 따라 다르겠지만, .bash_profile 이후에는 아마 script 있는 위치와는 동떨어진 곳에 가 있을 수 있기 때문에, 절대패스가 아니면 안돌거고, 그러면 윗 내용들을 보건대 문제가 될 수도 있겠죠.

아니면 스크립트를 손봐서 interactive shell이 아닌 경우에는 .bashrc를 강제로 읽어본다든가 하는 수도 있긴 하겠죠...

근데 처음 말했던 것처럼 애초에 스크립트에 필요한 환경은 스크립트 안에서 설정하는게 맞아요. 'ㅅ'
-- 라슈펠 2012-2-23 2:34 pm

넵 감사합니다.
-- Raymundo 2012-2-23 2:45 pm

또 하나
perlbrew exec some.pl 는 some.pl 스크립트를 현재 인스톨되어있는 perl들을 다 돌아가며 실행시키는 것 같네요. 어떤 프로그램을 현재 설치된 Perl에 다돌려서 모든 결과를 한번에 보고 싶을때 쓰는 용도인듯....
-- aero 2012-2-24 5:40 pm

악ㅋㅋ 제가 인스톨한 버전이 한 가지 뿐이라서 그걸 착각했군요ㅋㅋ 말씀하신 대로네요.
-- Raymundo 2012-2-25 12:49 am

웹을 통해 실행되는 CGI 스크립트의 경우 쉬뱅 라인에 사용할 perl의 full-path를 적어주는 것이 가장 안전하고 확실한 방법입니다.

그것이 아니라면 cgi를 실행시키는 웹서버 측의 설정 또는 path 경로를 조정해서 해당 디렉터리 하부의 cgi에 대해서는 해당 사용자 펄을 우선 검색할 수 있도록 설정하면 됩니다.

제 경우 버그질라를 perlbrew로 설치한 perl 5.14.2로 실행시키는데 성공했고 첫 번째 방법은 간단히 해결할 수 있었습니다. 그리고 두 번째 방법의 경우 손이 좀 갔는데 아파치 설정을 좀 만져야 해서 번거로웠구요. mod_perl을 사용하지 않는다면 그래도 비교적 간단하게 해결할 수 있답니다. :)
-- keedi 2012-2-28 12:02 pm
이름:  
Homepage:
내용:
 

컴퓨터분류
각주:
1. 3. Twitter:Keedi님께 많은 도움 받았음. 감사합니다.
2. ['모듈설치'페이지의 '주인장의 경우' 섹션] 참고
4. 이 서버에서 수행했던 스크립트를 PC에 가져와서 돌렸더니 동작이 달라졌음
5. 그 반대로 펄은 하위 호환성을 너무 챙겨주는 감이...
6. https://twitter.com/rashper/statuses/172226477813792768 에 의하면 보안상 이유로 막혀 있는 듯?
7. Twitter:nvingTwitter:keedi님 감사합니다

이 수정본 편집일: 2012-2-28 12:02 pm (변경사항 [d])
3129 hits | Permalink | 변경내역 보기 [h] | 현재 수정본 보기 | 49 번째 수정본 소스 보기