ftpしか使えない共用サーバに自動デプロイするには、lftpでミラーリングアップロードする

共用サーバでSSH接続が使える場合は、Jenkinsにジョブを作成して、rsyncやansibleで自動デプロイできます。

FTP接続しか使えない場合は、どのように自動デプロイすればいいでしょうか。

ftpの生コマンドを使うのはハードルが高そうね

ftpのミラーリングアップロードができるコマンドを探すと、lftpやwputがありました。今回、lftpを使って、自動デプロイを設定したので、lftpを説明します。

インストール方法

Ubuntu 18.04

$ sudo apt install lftp $ lftp --version LFTP | Version 4.8.1 | Copyright (c) 1996-2017 Alexander V. Lukyanov LFTP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LFTP. If not, see <http://www.gnu.org/licenses/>. Send bug reports and questions to the mailing list <lftp@uniyar.ac.ru>. Libraries used: GnuTLS 3.5.18, idn2 2.0.4, Readline 7.0, zlib 1.2.11

MacOS X

$ brew install lftp $ lftp --version LFTP | Version 4.9.1 | Copyright (c) 1996-2020 Alexander V. Lukyanov 〜省略〜

使い方

インタラクティブモード

lftpコマンドを起動すると、インタラクティブモードに入ります。プロンプトが「lftp :~>」に変化します。

$ lftp lftp :~>

(1)lftpのopenコマンドでFTPサーバに接続します。以下、ユーザ名、ホスト名は架空です。

ユーザ名とパスワードをカンマ区切りで並べます。

lftp :~>open -u admin@example.jp,password sv123.example.jp lftp admin@example.jp@sv123.example.jp:~>

(2)ローカルの deploy下を FTPサーバの /public_html下にミラーリングアップロードします。

lftp admin@example.jp@sv123.example.jp:~>cd /public_html lftp admin@example.jp@sv123.example.jp:/public_html>mirror --reverse --verbose --overwrite deploy/ ./ # 実際にはアップロードしないドライランの例 lftp admin@example.jp@sv123.example.jp:/public_html>mirror --dry-run --just-print --reverse --verbose --overwrite deploy/ ./

(3)FTPサーバの /public_html/cgi-bin/xxx.cgi を chmod a+x します。

lftp admin@example.jp@sv123.example.jp:/public_html>chmod a+x ./cgi-bin/xxx.cgi

(4)FTPサーバの /public_html/tmp/* を 削除します。

lftp admin@example.jp@sv123.example.jp:/public_html>mrm ./tmp/*

(5)lftpを終了します。

lftp admin@example.jp@sv123.example.jp:/public_html>exit $

openコマンド

openコマンドは、FTPサーバに接続します。

ユーザ名とパスワードをカンマ区切りで並べます。パスワードがカンマを含む場合、動作は不明です。

lftp :~>open -u admin@example.jp,password sv123.example.jp lftp admin@example.jp@sv123.example.jp:~>

lftp起動時のオプションでユーザ名、パスワード、ホスト名を指定することもできます。

$ lftp -u admin@example.jp,password sv123.example.jp lftp admin@example.jp@sv123.example.jp:~>

-u オプションを使わずに、ホストをftp://username:password@host 形式で指定することもできます。ユーザ名が "@" を含むときは、URLエンコードすることで、渡すことができました。

パスワードがカンマ","を含むときも、URLエンコードすることで、渡すことができそうです。

$ lftp ftp://admin%40example.jp,password@sv123.example.jp lftp admin@example.jp@sv123.example.jp:~>

help

lftp :~> help open 使い方: open [OPTS] <site> Select a server, URL or bookmark -e <cmd> execute the command just after selecting -u <user>[,<pass>] use the user/password for authentication -p <port> use the port for connection -s <slot> assign the connection to this slot -d switch on debugging mode <site> host name, URL or bookmark name

mirrorコマンド

mirrorコマンドは、FTPサーバのディレクトリ下をまるごとローカルにダウンロードしたり、ローカルのディレクトリ下をまるごとアップロードします。

mirrorコマンドのオプション説明
--reverseアップロード
--verbose経過を表示する。
--overwriteサーバー側にファイルが存在する場合、指定しないと、サーバー側のファイルを削除して、ローカルからアップロードする。
指定すると、サーバー側のファイルを削除せずに、ローカルからアップロードして上書きする。
--dry-run --just-printドライラン、実際にはアップロードしない

--reverseオプションをつけないと、ダウンロードします。

--reverseオプションをつけると、ローカルのディレクトリ下をまるごとサーバ側にアップロードします。

--dry-run --just-print オプションをつけると、アップロードはしないけれども、どのようなファイルをアップロードするかを確認することができます。

サーバー側に同名ファイルが存在するとき、--overwriteオプションをつけないと、次のように、まず削除してから、アップロードします。

Removing old file `index.php' Transferring file `index.php' ---> DELE index.php <--- 250 DELE command successful ---> PROT C <--- 200 Protection set to Clear ---> PASV <--- 227 Entering Passive Mode (???,???,???,???,???,???). ---- データソケットを (???,???,???,???) のポート 60008 に接続中 ---- Data connection established ---> STOR index.php <--- 150 Opening BINARY mode data connection for index.php ---- データソケットを閉じています <--- 226 Transfer complete ---> MFMT 20200329145023 index.php <--- 213 Modify=20200329145023; index.php ---> SITE CHMOD 644 ./index.php <--- 200 SITE CHMOD command successful

何か気になることでも?

わずかな時間ですが、ファイルがなくなる瞬間があります。その瞬間にファイルにアクセスしているユーザにはエラーを返す可能性があります。

--overwriteオプションをつけると、削除せずに、上書きアップロードします。

Overwriting old file `index.php' Transferring file `index.php' ---> PROT C <--- 200 Protection set to Clear ---> PASV <--- 227 Entering Passive Mode (???,???,???,???). ---- データソケットを (???,???,???,???) のポート 60021 に接続中 ---- Data connection established ---> STOR index.php <--- 150 Opening BINARY mode data connection for index.php ---- データソケットを閉じています <--- 226 Transfer complete ---> MFMT 20200330145316 index.php <--- 213 Modify=20200330145316; index.php ---> SITE CHMOD 644 ./index.php <--- 200 SITE CHMOD command successful

心配はなくなったのかしら?

残念ながら心配は残ります。ファイルがなくなりませんが、上書きでファイルオープンした直後はファイルの長さが0になる瞬間があります。

その瞬間にファイルにアクセスしているユーザがいると、なんらかのエラー、phpファイルならサーバーエラー、jsファイルならjsエラー、cssファイルならcssエラーになる可能性があります。

削除するよりはエラーになる可能性が低くなる思い、--overwriteオプションをつけました。

50歩100歩かもね

help

lftp :~> help mirror 使い方: mirror [OPTS] [remote [local]] Mirror specified remote directory to local directory -R, --reverse reverse mirror (put files) Lots of other options are documented in the man page lftp(1). When using -R, the first directory is local and the second is remote. If the second directory is omitted, basename of the first directory is used. If both directories are omitted, current local and remote directories are used. See the man page lftp(1) for a complete documentation.

chmodコマンド

ファイルやディレクトリの権限モードを変更します。LinuxやMacOS Xのchmodコマンドとほぼ同じ使い方です。

モードは、0755のような8進数形式でも、a+w のような書式、どちらも指定できます。

lftp :~> chmod 0755 xxx.yy lftp :~> chmod a+w xxx.yy

help

lftp :~> help chmod 使い方: chmod [OPTS] mode file... Change the mode of each FILE to MODE. -c, --changes - like verbose but report only when a change is made -f, --quiet - suppress most error messages -v, --verbose - output a diagnostic for every file processed -R, --recursive - change files and directories recursively MODE can be an octal number or symbolic mode (see chmod(1))

mrmコマンド

mrmコマンドは、削除したいファイル名にワイルドカード"*"を指定できます。

mrmコマンドは、削除ファイルが見つからない場合、exitコードに非0を返します。Jenkinsジョブで実行するとき、非0はエラー扱いになってしまいます。

lftpのコマンドファイルの最後に「exit 0」とすることで、0を返すこともできますが、本当のエラーがわからなくなってしまいます。

正しいと思われる方法は、lftp の ls でファイル一覧を取得して、ファイルがあれば、lftp の mrm で削除する方法です。この方法は、lftpのコマンドファイルだけで完結できず、シェルスクリプトで判断しなければなりません。

そこで今回は、該当するディレクトリにempty.txtをアップロードしてから、mrmするようにしました。

help

lftp :~> help mrm 使い方: mrm <files> ワイルドカード展開で指定されたファイルを削除します

-e または -c オプション

オプション説明
-c続くコマンドを実行します。コマンドでexitしていなければ、lftpインタラクティブモードにとどまります。
-e続くコマンドを実行します。コマンドでexitしていなくても、lftpをexitします。

さきほどの(1)〜(5)を -cオプションまたは-eオプションで実行するには、次のように指定します。

コマンドとコマンドの区切りは、セミコロンか改行で区切ります。

シェルまたはシェルスクリプトで複数行にわたって指定するには、継続行の "\" が必要で、少々見づらいです。

$ lftp -c "\ open -u admin@example.jp,password sv123.example.jp; \ cd /public_html; \ mirror --reverse --verbose --overwrite deploy/ ./; \ chmod a+x ./cgi-bin/xxx.cgi; \ mrm ./tmp/*; \ exit;"
$ lftp -e "\ open -u admin@example.jp,password sv123.example.jp; \ cd /public_html; \ mirror --reverse --verbose --overwrite deploy/ ./; \ chmod a+x ./cgi-bin/xxx.cgi; \ mrm ./tmp/*; \ "

-f オプション

-cオプションや-eオプションに指定したlftpのコマンドを別ファイルにしておき、-f オプションで指定します。

例えば、deploy_lftp.txt で保存しておきます。シェルスクリプトではないので、継続行の "\" は必要ありません。

open -u admin@example.jp,password sv123.example.jp; cd /public_html; mirror --reverse --verbose --overwrite deploy/ ./; chmod a+x ./cgi-bin/xxx.cgi; mrm ./tmp/*;
$ lftp -f deploy_lftp.txt

実際に実行する前に、deploy_lftp.txtの内容を確認できるので、今回は -f オプションの方法にしました。

ステージング環境と本番環境

ステージング環境と本番環境で、FTP接続情報やFTPサーバの公開ディレクトリが違うことがあります。

例えば、ステージング環境は、FTPサーバの /が公開ディレクトリ、本番環境は、FTPサーバの /public_htmlが公開ディレクトリといった具合です。

最初のバージョン:2ファイル用意する方法

ステージング環境用のlftpコマンドファイル、本番環境用のlftpコマンドファイルを用意する方法です。

open -u admin@example.jp,password sv999.example.jp; cd /; mirror --reverse --verbose --overwrite deploy/ ./; chmod a+x ./cgi-bin/xxx.cgi; mrm ./tmp/*;
open -u admin@example.jp,password sv123.example.jp; cd /public_html; mirror --reverse --verbose --overwrite deploy/ ./; chmod a+x ./cgi-bin/xxx.cgi; mrm ./tmp/*;

最初のバージョンとしては悪くありませんが、問題が2つあります。

1つめは、下3行の同じ処理を2ファイルに書いてしまっていることです。

2つめは、パスワードを含む接続情報をファイルに記述していることです。接続情報は、環境変数から取得できるようにしたいところです。

バージョン2:m4マクロを使う

そこで、m4マクロでlftpコマンドファイルを作成することにしました。

m4はテキスト置換のためのマクロプロセッサで、Linuxに標準でインストールされています。

まず、m4マクロ deploy_lftp.m4 を用意します。このファイルの FTP_USER、FTP_PASS、FTP_HOST、DST_DIR、SRC_DIR、DRY_RUN を外部から与えて置換します。

open -u FTP_USER,FTP_PASS FTP_HOST cd DST_DIR mirror DRY_RUN --reverse --verbose --overwrite SRC_DIR/ ./; chmod a+x ./cgi-bin/xxx.cgi; mrm ./tmp/*;

環境変数からm4マクロに渡してlftpコマンドファイルを作り、lftpを実行するシェルスクリプトを用意します。deploy_lftp.sh とします。chmod +x しておきます。

#!/bin/bash set -u set -e set -x mkdir --parent tmp m4 \ -D DRY_RUN="$DRY_RUN" \ -D FTP_HOST=$FTP_HOST \ -D FTP_USER=$FTP_USER \ -D FTP_PASS=$FTP_PASS \ -D SRC_DIR=$SRC_DIR \ -D DST_DIR=$DST_DIR \ deploy_lftp.m4 >tmp/deploy_lftp.txt lftp -d -f tmp/deploy_lftp.txt

ステージング環境のJenkinsジョブのBuildシェルスクリプト

ここでは環境変数を使いました。ユーザ名やパスワードは、必要に応じてJenkinsのCredentialに登録して使ってください。

export FTP_HOST=stage.example.jp export FTP_USER=admin@example.jp export FTP_PASS=password export SRC_DIR=deploy export DST_DIR=/ # ドライラン export DRY_RUN="--dry-run --just-print" # 実際に実行するとき export DRY_RUN= ./deploy_lftp.sh

ステージング環境のJenkinsジョブのBuildシェルスクリプト

export FTP_HOST=sv123.example.jp export FTP_USER=admin@example.jp export FTP_PASS=password export SRC_DIR=deploy export DST_DIR=/public_html # ドライラン export DRY_RUN="--dry-run --just-print" # 実際に実行するとき export DRY_RUN= ./deploy_lftp.sh

参考URL

lavv17/lftp
sophisticated command line file transfer program (ftp, http, sftp, fish, torrent) - lavv17/lftp
FTP でも rsync みたいにコマンドからファイル転送したい
ターミナルで何でも行う人にとって、FTP の GUI クライアントはもう辛すぎるのです。 コマンド1つで rsync のようにアップロードしたいのです。 同じように考
タイトルとURLをコピーしました