Cópias remotas de arquivos

Uma das tarefas mais comuns de um administrador de sistemas é fazer cópias remotas de arquivos – ou seja – de uma máquina para outra. Em rede, isto pode ser feito de diversas maneiras, como por exemplo: FTP, HTTP, SSH, RSYNC, entre outros. Este artigo mostra as principais formas de fazer isso. Vou tentar ser o mais direto possível.
Vamos usar aqui dois servidores exemplo:
  • servidor1.example.com (192.168.0.1)
  • servidor2.example.com (192.168.0.2)

SSH

O protocolo do SSH é seguro o suficiente para fazer cópias entre servidores em uma rede e é sempre o protocolo recomendado. Vamos agora para como fazer isso:

Copiando um arquivo simples de uma máquina para outra

servidor1$ scp maluco@servidor2.example.com:~/arquivo.txt ./
O comando acima copia o arquivo.txt do HOME (~) do usuário maluco, do servidor2 para o diretório atual do servidor1. Bem parecido com o comando cp.

Copiando todo um diretório de uma máquina para outra

servidor1$ scp -r maluco@servidor2.example.com:/home/maluco /tmp
Ou seja, copio o diretório maluco do servidor2 para a máquina atual, no diretório /tmp.
servidor1$ scp -Cpr maluco@servidor2.example.com:/home/maluco /tmp
O parâmetro -C agora faz a mesma ação anterior, só que agora compactando os dados antes de enviar pela rede. O ssh usa formato gzip e pode-se usar essa opção caso se queira economizar na banda de rede.
O parâmetro -p preserva todos modos e timestamps de arquivos, ideal para fazer uma cópia mais exata.

Usando o tar e o ssh para cópias mais exatas ainda

Por exemplo, um backup de sistema. O tar é ótimo para criar um arquivamento que preserva todas as permissões, usuários e grupos donos, modos, links simbólicos, etc. Desse jeito dá pra juntar os dois:
servidor1$ tar -cpzf - /etc | ssh maluco@servidor2.example.com "cat > /var/lib/backup/servidor1-etc.tar.gz"
O comando acima vai compactar o diretório /etc, só que ao invés de jogar o resultado em um arquivo, joga como STDIN, que o comando ssh pega e executa no servidor2 o comando cat, que por sua vez pega o STDIN e joga para o arquivo /var/lib/backup/servidor1-etc.tar.gz.
Em outras palavras, ele compacta o diretório /etc e já joga no outro servidor direto. Isso economiza tempo e espaço no servidor origem.
servidor1$ tar -C /home -cpzf - maluco | ssh maluco@servidor2.example.com "tar -C /home -xpzf -"
Agora ao invés de usar o cat, ele jogou diretamente o diretório na máquina remota, descompactado. É parecido com a opção -rp do scp, só que com a vantagem do tar: preserva modos, permissões, donos, links, etc.
Para mais informações sobre STDIN, STDOUT, pipes (|), tenho um outro artigo falando sobre isso.

HTTP e FTP

A primeira coisa que você precisa saber é que eu não vou ensinar a compartilhar os arquivos por HTTP e FTP neste artigo. É um assunto totalmente diferente, então vamos assumir que você já tenha os arquivos servidos via HTTP ou FTP em algum lugar :)
Pra quem já é um pouco acostumado com esses protocolos, um programa sempre vem na mente: wget. Mas além dele também há um comando muito útil para fazer operações mais complexas: o lftp.

Pegando arquivos e páginas via HTTP/FTP

servidor1$ wget -c 'http://servidor2.example.com/arquivo.txt' -O /home/maluco/informacoes.txt
O comando acima pega o arquivo.txt do servidor1 e o escreve (-O) em /home/maluco/informacoes.txt. Se você não especificar o -O, ele coloca no diretório atual com o mesmo nome remoto (no caso, arquivo.txt). O -c é para ele tente continuar um download caso o arquivo já exista, e a não ser que você queira sobrescrever os arquivos destino, é bom sempre usar a opção.
servidor1$ wget -c -r 'http://www.devin.com.br'
Já este vai fazer uma cópia recursiva de todo o site www.devin.com.br. Isso inclui os links e imagens, que o wget pega automaticamente do código HTML.

Mirrors com lftp

E falando em recursividade, o lftp fornece ótimos recursos para fazer este tipo de download. Por exemplo, eu estou afim de criar um espelho interno do repositório de pacotes do CentOS. Vamos supor que eu tenha escolhido o mirror kernel.org, via HTTP:
servidor1$ lftp
lftp :~> open http://mirrors.kernel.org/centos/5.5/os/x86_64/
lftp mirrors.kernel.org:/centos/5.5/os/x86_64> mirror -verbose
[...]
O lftp se encarrega de examinar o diretório e pegar todo o seu conteúdo: arquivos e sub-diretórios, salvando no diretório atual. A opção –verbose vai mostrando na tela o que o lftp está fazendo (analizando os diretórios, baixando os arquivos…).
O mesmo pode ser feito com um comando só, um ótimo jeito para se fazer isso automaticamente em shell-scripts:
servidor1$ lftp -c "open http://mirrors.kernel.org/centos/5.5/os/x86_64/ && mirror --verbose"
Ou seja, com o parâmetro -c, consigo executar os dois comandos (conectar, fazer mirror) de modo não-interativo, e depois ele sai do programa (para não sair, usa-se o -e ao invés de -c). O mirror também analisa e continua os downloads de onde parou, caso precise.
A mesma coisa pode ser feita via FTP, vamos supor agora que é preciso também de usuário e senha…
servidor1$ lftp -c "open ftp://usuario:senha@mirrors.kernel.org/centos/5.5/os/x86_64/ && mirror --verbose"
O lftp ainda suporta uma porrada de opções. São centenas. Nesse caso, a manpage sempre ajuda (man lftp). Uma das opções que eu acho muito legal é a exclude. Com a opção exclude, eu consigo fazer um download recursivo, retirando arquivos com nomes que não quero. Por exemplo:
servidor1$ lftp -c "set mirror:exclude-regex '(\.svn|SRPMS)' && open ftp://mirrors.kernel.org/centos/5.5/os/ && mirror --verbose"
No comando acima, eu estou excluindo qualquer arquivo ou diretório que tenha o contenha o nome .svn ou SRPMS. Isso me faz baixar apenas os diretórios i386 e x86_64. Fácil não? Aí é só usar a imaginação. Os exemplos aqui são muito comuns na hora de criar espelhos de repositórios e arquivos entre sistemas.

Mirror reverso (upload de arquivos)

No caso de mirror de um mirror reverso, ao invés de pegar os arquivos de um sistema remoto, eu coloco os arquivos no sistema remoto. A sintaxe é praticamente a mesma:
servidor1$ lftp -c "lcd /var/lib/backup && open ftp://usuario:senha@servidor2.example.com/backup/ && mirror -R --verbose"
O que mudou aqui: o comando lcd vai para o diretório local, onde os arquivos se encontram. Depois eu abro uma conexão FTP com servidor2.example.com, utilizando um usuário e senha. Em seguida eu faço um mirror reverso (opção -R), que ao invés de fazer download dos arquivos, vai fazer upload. Simples dessa forma.

rsync

O rsync foi feito especialmente para você sincronizar os arquivos de dois lugares e por isso ele é otimizado para isso. Geralmente é utilizado para copiar diretórios e seus conteúdos da forma mais exata possível (preservando permissões, modos, timestamps). Além disso, antes de começar a copiar, ele analisa o destino (ou origem, dependendo do caso) e vê o que realmente tem que ser copiado. Se um arquivo já existe no destino, para que copiar de novo?
O rsync pode funcionar via protocolo próprio, ou via ssh. Se existe um servidor rsync, pode-se usar ele. Para criar um, vai um pouco fora do escopo deste artigo, mas não é tão complicado também. Na maioria das vezes, você vai querer usar o rsync via ssh, que é o caso mais comum.

Sincronizando um diretório no servidor remoto

servidor1$ rsync -e "ssh" -avr /var/lib/backup/ maluco@servidor2.example.com:/var/lib/backup/
No comando acima, utilizei o ssh (-e “ssh”) para fazer o transporte. As opções -avr copiam recursivamente (-r), mostrando tudo o que for sendo copiado (-v) e da forma mais exata possível, preservando (-a). Até aí tudo bem…
… Mas preste bastante atenção! O rsync é muito sensível com a barra no final da origem e destino. Então coloque isso na cabeça:
  • Quando não se usa a barra no final, o rsync copia o diretório e todo seu conteúdo;
  • Quando usar a barra no final, o rsync copia apenas o conteúdo do diretório.
Confuso? Eu também fiquei. No exemplo anterior, eu copiei o conteúdo do diretório backup (localizado em /var/lib) para o /var/lib/backup do servidor2. Isso significa que o diretório /var/lib/backup deve existir no servidor2, senão é capaz de dar erro. Ficou assim:
servidor2$ ls /var/lib/backup
1   11  13  15  17  19  20  22  24  26  28  3   31  33  35  37  39  40  42  44  46  48  5   6  8
10  12  14  16  18  2   21  23  25  27  29  30  32  34  36  38  4   41  43  45  47  49  50  7  9
E se eu tirar uma barra?
servidor1$ rsync -e "ssh" -avr /var/lib/backup maluco@servidor2.example.com:/var/lib/backup/
Nesse caso, olha só como ia ficar:
servidor2$ ls /var/lib/backup
backup
 
servidor2$ ls /var/lib/backup/backup
1   11  13  15  17  19  20  22  24  26  28  3   31  33  35  37  39  40  42  44  46  48  5   6  8
10  12  14  16  18  2   21  23  25  27  29  30  32  34  36  38  4   41  43  45  47  49  50  7  9
Como tirei a barra, ele também copiou o diretório backup, ao invés de apenas seu conteúdo. Então lembre-se sempre… Com barra: só conteúdo; Sem barra: diretório e conteúdo.
Assim como lftp, o rsync também aceita excluir arquivos por nome:
servidor1$ rsync -e "ssh" --exclude='.svn' -avr /var/www/html/aplicacao maluco@servidor2.example.com:/var/www/html
No comando acima, sincronizamos o diretório /var/www/html/aplicacao, mas excluindo qualquer operação com arquivos que tem no nome “.svn“. Uma prática muito comum por aí.
Por padrão, o rsync não remove arquivos e diretórios do destino. Isso significa que a sincronização é apenas em uma mão. Se no servidor1 novos arquivos surgirem, ou arquivos forem modificados, o rsync vai sincronizar. Mas se um arquivo ou diretório for removido, ele não será removido no servidor2. Para resolver isso, usa-se a opção –delete:
servidor1$ rsync -e "ssh" --exclude='.svn' --delete -avr /var/www/html/aplicacao maluco@servidor2.example.com:/var/www/html
Dessa forma você evita de ficar acumulando “lixo” no servidor2.
http://www.devin.com.br/copias-remotas-de-arquivos/