O RabbitMQ é uma aplicação Erlang. A linguagem evolui rapidamente e cada nova versão tem otimizações de performance. Isto é bom até que você tenta instalar o pacote DEB do RabbitMQ no seu servidor Debian-like e nota que o gerenciador de pacote instalou uma versão antiga do Erlang.

Para o Debian Squeeze, o pacote erlang-nox vem com o Erlang R14A (lançado em junho de 2010). O pacote backports vem com o Erlang R15B01 (lançado em abril de 2012). Para o Ubuntu LTS atual (Precise), erlang-nox vem com o Erlang R14B04 (lançado em outubro de 2011) enquanto que o último pacote do Raring - assim como o pacote do backports do Debian Squeeze - vem com o Erlang R15B01.

Esse post mostra como compilar a última versão do Erlang (R16B) no Debian Squeeze e como satisfazer a dependência erlang-nox do pacote DEB RabbitMQ.

Compilando o Erlang

O processo de compilação é muito simples, nós iremos basicamente seguir os passos disponíveis na documentação oficial do Erlang. Primeiro, instale algumas de suas dependências:

$ sudo aptitude install -y build-essential libssl-dev ncurses-dev m4

Explicando as dependências:

  • build-essential: lista de pacotes considerados essenciais para criar pacotes Debian. Alguns desses pacotes são necessários para compilar o Erlang, como o gcc e make
  • libssl-dev: necessários para suporte SSL
  • ncurses-dev: para melhor suporte no terminal
  • m4: necessário para compilar com suporte HiPE (High Performance Erlang)

Faça o download e compile o Erlang:

$ sudo mkdir -p /opt/src/erlang /opt/erlang
$ cd /opt/src/erlang
$ sudo curl -O http://www.erlang.org/download/otp_src_R16B.tar.gz
$ sudo tar xzvf otp_src_R16B.tar.gz
$ sudo mv otp_src_R16B r16b
$ cd r16b
$ sudo ./configure --prefix=/opt/erlang/r16b --enable-hipe --with-ssl
$ sudo make
$ sudo make install

Finalmente, crie os links simbólicos dos binários Erlang (se você não quiser usar links simbólicos, você pode executar configure com a opção --bindir=/usr/bin):

$ sudo ln -s /opt/erlang/r16b/bin/dialyzer /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/epmd /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/erl /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/erlc /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/run_erl /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/run_test /usr/bin
$ sudo ln -s /opt/erlang/r16b/bin/typer /usr/bin

Instalando o RabbitMQ

Faça o download do pacote DEB do RabbitMQ disponível no site do projeto:

$ cd /tmp && curl -O http://www.rabbitmq.com/releases/rabbitmq-server/v3.1.0/rabbitmq-server_3.1.0-1_all.deb

Como já discutimos, este pacote depende do pacote erlang-nox:

$ dpkg -I rabbitmq-server_3.1.0-1_all.deb
 Package: rabbitmq-server
 Version: 3.1.0-1
 Architecture: all
 Maintainer: RabbitMQ Team <packaging@rabbitmq.com>
 Installed-Size: 4460
 Depends: erlang-nox (>= 1:12.b.3) | esl-erlang, adduser, logrotate
 Section: net
 Priority: extra
 Homepage: http://www.rabbitmq.com/
 Description: AMQP server written in Erlang
  RabbitMQ is an implementation of AMQP, the emerging standard for high
  performance enterprise messaging. The RabbitMQ server is a robust and
  scalable implementation of an AMQP broker.

Se você tentar instalar o pacote usando o aptitude, o gerenciador de pacote irá instalar uma versão antiga do Erlang. A forma mais fácil de prevenir isso é usando o dpkg com a opção --ignore-depends:

$ sudo dpkg -i --ignore-depends=erlang-nox
(Reading database ... 42128 files and directories currently installed.)
Preparing to replace rabbitmq-server 3.1.0-1 (using rabbitmq-server_3.1.0-1_all.deb) ...
Unpacking replacement rabbitmq-server ...
Setting up rabbitmq-server (3.1.0-1) ...
Adding group `rabbitmq' (GID 107) ...
Done.
Adding system user `rabbitmq' (UID 105) ...
Adding new user `rabbitmq' (UID 105) with group `rabbitmq' ...
Not creating home directory `/var/lib/rabbitmq'.
Starting message broker: rabbitmq-server.
Processing triggers for man-db ...

Você pode verificar que o RabbitMQ está executando com o comando rabbitmqctl:

$ sudo rabbitmqctl status
Status of node rabbit@squeeze64 ...
[{pid,1776},
 {running_applications,[{rabbit,"RabbitMQ","3.1.0"},
                        {mnesia,"MNESIA  CXC 138 12","4.8"},
                        {os_mon,"CPO  CXC 138 46","2.2.11"},
                        {xmerl,"XML parser","1.3.3"},
                        {sasl,"SASL  CXC 138 11","2.3.1"},
                        {stdlib,"ERTS  CXC 138 10","1.19.1"},
                        {kernel,"ERTS  CXC 138 10","2.16.1"}]},
...

Resolvendo a dependência

O que fizemos quando instalamos o RabbitMQ foi simplesmente pedir para o dpkg ignorar a dependência durante o processo de instalação. O problema é que para o gerenciador de pacote, a dependência não foi satisfeita. Se você tentar instalar ou atualizar um pacote, o gerenciador de pacote irá sugerir a remoção do pacote rabbitmq-server por causa de sua dependência não satisfeita:

$ sudo aptitude install tree
The following NEW packages will be installed:
  tree
0 packages upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 32.4 kB of archives. After unpacking 98.3 kB will be used.
The following packages have unmet dependencies:
  rabbitmq-server: Depends: erlang-nox (>= 1:12.b.3) but it is not going to be installed. or
                            esl-erlang which is a virtual package.
The following actions will resolve these dependencies:

     Remove the following packages:
1)     rabbitmq-server

Accept this solution? [Y/n/q/?]

Se você não aceitar a solução, o aptitude irá sugerir a instalação de um monte de pacotes Erlang. Isto não é o que queremos, nós queremos ter o RabbitMQ instalado com a versão do Erlang que compilamos. Para resolver esta dependência, nós iremos criar um pacote “falso” usando equivs. Da documentação do equivs-build: “equivs-build é um programa que cria pacotes Debian aos quais podem ser usados para informar para o dpkg sobre pacotes instalados localmente e de suas dependências. Pacotes vazios que apenas requerem outros pacotes também podem ser criados com equivs. Estes podem ser usados como pacotes “perfis” aos quais apenas marcam outros pacotes para instalação”.

Antes de instalar o equivs, desinstale o RabbitMQ:

$ sudo aptitude purge -y rabbitmq-server

Então instale o equivs:

$ sudo aptitude install -y equivs

Crie um arquivo com o seguinte conteúdo:

Section: interpreters
Priority: optional
Standards-Version: 3.6.2

Package: erlang-nox
Version: 1:12.b.3
Maintainer: The Maintainer <maintainer@example.com>
Description: Dummy Erlang package.
  This package provides a dummy package for erlang-nox, a dependency of the rabbitmq-server package.

Ou apenas copie e cole os seguintes comandos:

$ erlang_nox="Section: interpreters
Priority: optional
Standards-Version: 3.6.2

Package: erlang-nox
Version: 1:12.b.3
Maintainer: The Maintainer <maintainer@example.com>
Description: Dummy Erlang package.
  This package provides a dummy package for erlang-nox, a dependency of rabbitmq-server package from RabbitMQ."
$ IFS='%'; echo $erlang_nox > erlang-nox; unset IFS

Então execute o comando equivs-build para criar o pacote DEB:

$ equivs-build erlang-nox
...
dpkg-deb: building package `erlang-nox' in `../erlang-nox_12.b.3_all.deb'.

The package has been created.
Attention, the package has been created in the current directory,
not in ".." as indicated by the message above!

Agora, instale o pacote DEB recém-criado e o pacote DEB do RabbitMQ:

$ sudo dpkg -i erlang-nox_12.b.3_all.deb rabbitmq-server_3.1.0-1_all.deb

Você pode verificar que o pacote “falso” foi instalado:

$ aptitude show erlang-nox
Package: erlang-nox
State: installed
Automatically installed: no
Version: 1:12.b.3
Priority: optional
Section: interpreters
Maintainer: The Maintainer <maintainer@example.com>
Uncompressed Size: 36.9 k
Description: Dummy Erlang package.
 This package provides a dummy package for erlang-nox, a dependency of rabbitmq-server package from RabbitMQ.

Agora você não precisa mais se preocupar com o aptitude reclamando sobre a dependência não satisfeita do pacote rabbitmq-server. Ao menos não quando for tentar instalar novos pacotes. Espere…

Marcando o pacote como mantido (held)

Tudo bem até que você tenta atualizar os pacotes do seu sistema e se depara com algo assim:

$ sudo aptitude safe-upgrade
Resolving dependencies...
The following NEW packages will be installed:
  erlang-asn1{a} erlang-base{a} erlang-corba{a} erlang-crypto{a} erlang-docbuilder{a} erlang-edoc{a} erlang-erl-docgen{a} erlang-eunit{a}
  erlang-ic{a} erlang-inets{a} erlang-inviso{a} erlang-mnesia{a} erlang-odbc{a} erlang-os-mon{a} erlang-parsetools{a} erlang-percept{a}
  erlang-public-key{a} erlang-runtime-tools{a} erlang-snmp{a} erlang-ssh{a} erlang-ssl{a} erlang-syntax-tools{a} erlang-tools{a} erlang-webtool{a}
  erlang-xmerl{a} libltdl7{a} libsctp1{a} lksctp-tools{a} odbcinst{a} odbcinst1debian2{a} unixodbc{a}
The following packages will be upgraded:
  erlang-nox
1 packages upgraded, 31 newly installed, 0 to remove and 0 not upgraded.
Need to get 20.8 MB of archives. After unpacking 36.5 MB will be used.
Do you want to continue? [Y/n/?]

Veja o arquivo erlang-nox que criamos anteriormente. Ele declara a versão como 1:12.b.3 enquanto a versão do pacote erlang-nox do Debian Squeeze é a 1:14.a-dfsg-3squeeze1. Se nós criássemos o pacote “falso” declarando uma versão mais recente, nós teríamos o mesmo problema se uma nova versão fosse lançada.

Para resolver isto, apenas marque o pacote como mantido com o aptitude:

$ sudo aptitude hold erlang-nox

aptitude show irá mostrar o pacote como instalado e mantido:

$ aptitude show erlang-nox
Package: erlang-nox
State: installed [held]
...

Agora você está livre das restrições de pacotes e usar a versão desejada do Erlang para executar não apenas o RabbitMQ mas qualquer aplicação Erlang!