2015.12.01

Composerのローカルリポジトリ(satis)を利用してdeployを安全かつ高速にする


はじめに

こんにちは。次世代システム研究室のA.Zです。
composerの普及で、PHPのプロジェクトの外部ライブラリをcomposerで管理するケースが増えました。

私が担当するプロジェクトでも、外部ライブラリの管理はcomposerで行い、deployするタイミングで、外部ライブラリのupdate、またはinstallを行います。

Fig.1 satis導入前のdeployプロセス



しかし、こちらのdeployの仕組みには、以下の問題があります。
  • 外部システムに依存するため、外部システムでトラブルが発生した場合、リリースの遅延・停止が発生する。
  • インターネット経由で外部システムに接続するため、deployに時間がかかる。
以上の問題を解決するため、社内のcomposer用のプライベートリポジトリを構築する必要が出てきました。
今回は、その構築手順を紹介したいと思います。

前提条件

まずは、社内のプライベートリポジトリを利用することで、以下3点の達成を目指します。
  • deployの際の外部システム依存からの完全な脱却
  • ライブラリのバージョンアップのスムーズ化
  • 新規ライブラリの追加の柔軟化

プライベートリポジトリ構築

今回使ってるアプリケーションはSatisです。

公式ページ:
https://github.com/composer/satis

Satisとはcomposer用のリポジトリを生成するアプリケーションです。

Satisは、以下3点ができます。
  • composerパッケージのarchive
  • archiveしたパッケージのcomposerへのdeliver
  • archiveしたいパッケージの自由な設定

satisの構築

satisの構成

今回、satisのリポジトリは、gitlabと同じサーバーで構築します。Satis構築後のdeployプロセスのイメージは以下の通りです。

 

deploy-after

Fig.2 satis導入後のdeployプロセス



 

 

Satisのリポジトリ内容の更新は定期的に行う必要があります。今回、以下の更新プロセスはcronjobで1日1回行います。

satis-update

Fig.3 Satisリポジトリの更新



 

satis インストール

以下のコマンドで、satisをインストールします。
composer create-project composer/satis <satis_install_path> --stability=dev --keep-vcs
※ <satis_install_path>は、satisインストールパスに書き換えます。

 

satis 設定

以下はsatis conf ファイルの設定です。
 "name": "Composer Mirror",
 "homepage": "https://hogehoge/satis/",
 "repositories": [
	{ "type": "composer", "url": "https://packagist.org" }
 ],
 "require": {
	 "monolog/monolog": "*",
	 "jms/serializer": "*",
	 "phpunit/phpunit": "*",
	 "composer/installers":"*"
 },
 "archive": {
	 "directory": "dist",
	 "format": "zip",
	 "prefix-url": "https://hogehoge/satis/",
	 "skip-dev": false
 },
 "require-dependencies": true
repositoriesブロックでは、外部ライブラリの取得先のURLを設定します。

composerのdefaultリポジトリはhttps://packagist.org なので、こちらのURLをrepositoriesブロックに設定しておきます。

requireブロックでは、アーカイブしたいライブラリをすべて設定します。
ライブラリバージョンを全て「*」に設定すると全てのライブラリバージョンがアーカイブされます。その結果、プロジェクト側のライブラリのバージョンアップがスムーズになります。
新しいパッケージを追加したい場合は、ここに新しいパッケージ追加することで、そのパッケージがアーカイブされます。

archiveブロックオプションは、ダウンロードしたパッケージのフォーマット、保存先、ダウンロードURLなどに設定します。

require-dependenciesオプションは、trueを設定することで、requireブロックで指定したパッケージが依存しているパッケージもダウンロード対象になります。

 
SatisのGit submodule設定
プロジェクトの一部の外部ライブラリは、依然git submoduleとしてインストールされていることがわかりました。

そこで、こちらのsubmoduleについても、satisとcomposerで管理したいと思います。

Satisには、いくつかのパッケージタイプがあり、repositoryとrequireオプションブロックに以下のように設定を追加することで、git submoduleもarchiveできるようになります。
  • repositoryブロック
    {
    	"type":"package",
    	"package": {
    		"type": "fuel-package",
    		"name": "fuel/core",
    		"version": "dev-master",
    		"source": {
    			"url": "https://github.com/fuel/core",
    			"type": "git",
    			"reference":"XXXXXXXXXXXXXXXXXXXXXXXX"
    		}
    	}
    }
    
    referenceの値はgitのコミットhashになります。
  • requireロック
    "fuel/core": "*"
    
最終的なsatis設定は、以下の通りです。
{
	"name": "Composer Mirror",
	"homepage": "https://hogehoge/satis/",
	"repositories": [
		{
			"type":"package",
			"package": {
				"type": "fuel-package",
				"name": "fuel/core",
				"version": "dev-master",
				"source": {
					"url": "https://github.com/fuel/core",
					"type": "git",
					"reference":"fc60fb0d0832be05981ffa63cbd3d2810ddc19a7"
				}
			}
		},
		{ "type": "composer", "url": "https://packagist.org" }
	],
	"require": {
		"monolog/monolog": "*",
		"jms/serializer": "*",
		"phpunit/phpunit": "*",
		"fuel/core": "*",
		"composer/installers":"*"
	},
	"archive": {
		"directory": "dist",
		"format": "zip",
		"prefix-url": "https://hogehoge/satis/",
		"skip-dev": false
	},
	"require-dependencies": true
}

satisのrepository生成または更新

satisのrepository生成または更新のコマンドは、以下の通りです。
php -d memory_limit=-1 <satis_install_path>/bin/satis build <satis_conf_path> <satis_repository_path>
※ <satis_install_path>,<satis_conf_path>,<satis_repository_path>は、適切なパスに書き換えます。

※ <satis_repository_path>は外部ライブラリの保存先のパスです。

 

satis サーバ設定

Satisは、http経由でアクセスされるため、Webサーバの設定が必要です。

Nginxの場合は、以下の設定をserverブロックに追加します。
location ^~ /satis {
	alias <satis_repository_path> ;
	charset utf-8;
	index index.html index.htm;
}
※ <satis_repository_path>は、satisの外部ライブラリの保存先のパスに書き換えます。

 

satisのpage

Fig.4 Satisの画面



 

 

各プロジェクト設定

プロジェクト側で必要な設定は、以下の通りです。
  • repositoryブロック
    "repositories": {
    	"private-satis": {
    		"type": "composer",
    		"url": "https://hogehoge/satis/",
    		"packagist": false
    	},
    	"packagist": false
    },
    
    “packagist”: falseの設定を追加すると、packagist.orgにリクエストしなくなります。
  • requireブロックに以下の設定を追加
    "fuel/core": "dev-master"
    
    こちらの設定は、今回git submoduleをcomposer化した設定です。
  • configブロック
    "config": {
    	...
    	"preferred-install": "dist",
    	...
    },
    
    preferred-install設定にしなければ、sourceとしてインストールされていることになります。
    sourceだと、githubなどにリクエストしてしまいます。こちらの設定を追加することで、インストールされるのはsatis上にあるpackageのみになります。
  • (オプション) 今回のプロジェクトはfuel phpを利用するため、fuel/coreのパッケージがfuel/coreディレクトリにインストールされます。そのため、以下の設定を追加します。
    "extra": {
    	"installer-paths": {
    		"fuel/core": ["fuel/core"]
    	}
    }

結果

対象プロジェクトで、composer update実行した結果
Reading ./composer.json
Loading config file ./composer.json
Executing command (/var/hoge_project/releases/20151118105919): git describe --exact-match --tags
Executing command (/var/hoge_project/releases/20151118105919): git branch --no-color --no-abbrev -v
Failed to initialize global composer: Composer could not find the config file: /home/pdmp/.composer/composer.json
To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section
Loading plugin Composer\Installers\Plugin
Loading composer repositories with package information
Downloading https://hogehoge/satis/packages.json
Writing /home/foo_user/.composer/cache/repo/https---hogehoge-satis/packages.json into cache
Downloading https://hogehoge/satis/include/all%242ffd13e73a0e5f493952f5e87b65a5eca9fa60e3.json
Writing /home/foo_user/.composer/cache/repo/https---hogehoge-satis/include-all$2ffd13e73a0e5f493952f5e87b65a5eca9fa60e3.json into cache
Updating dependencies (including require-dev)
Analyzed 1934 packages to resolve dependencies
Analyzed 92896 rules to resolve dependencies

  - Removing symfony/process (2.8.x-dev 7d645ea)
  - Installing symfony/process (2.8.x-dev 63573e0)
Downloading https://hogehoge/satis//dist/symfony-process-63573e0afb71aa3ea2381d70d96b8d9063627794-zip-1a6c89.zip
    Downloading: 100%
Writing /home/foo_user/.composer/cache/files/symfony/process/63573e0afb71aa3ea2381d70d96b8d9063627794.zip into cache from /var/hoge_project/releases/20151118105919/fuel/vendor/symfony/process/8b95bcc271a140b9d0e77c1cc69385a1.zip
    Extracting archive
Executing command (CWD): unzip '/var/hoge_project/releases/20151118105919/fuel/vendor/symfony/process/8b95bcc271a140b9d0e77c1cc69385a1.zip' -d '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/4b823ab4' && chmod -R u+w '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/4b823ab4'

  - Removing symfony/dependency-injection (2.8.x-dev eae068f)
  - Installing symfony/dependency-injection (2.8.x-dev 4eb9fce)
Downloading https://hogehoge/satis//dist/symfony-dependency-injection-4eb9fce835b5d179b4e996b57504a6d90bf3ba5b-zip-d3301c.zip
    Downloading: 100%
Writing /home/foo_user/.composer/cache/files/symfony/dependency-injection/4eb9fce835b5d179b4e996b57504a6d90bf3ba5b.zip into cache from /var/hoge_project/releases/20151118105919/fuel/vendor/symfony/dependency-injection/44e7aad25b8c6815d8c0592511fa60ae.zip
    Extracting archive
Executing command (CWD): unzip '/var/hoge_project/releases/20151118105919/fuel/vendor/symfony/dependency-injection/44e7aad25b8c6815d8c0592511fa60ae.zip' -d '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/c8848821' && chmod -R u+w '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/c8848821'

  - Removing symfony/polyfill-mbstring (dev-master 0b6a894)
  - Installing symfony/polyfill-mbstring (dev-master 49ff736)
Downloading https://hogehoge/satis//dist/symfony-polyfill-mbstring-49ff736bd5d41f45240cec77b44967d76e0c3d25-zip-579409.zip
    Downloading: 100%
Writing /home/foo_user/.composer/cache/files/symfony/polyfill-mbstring/49ff736bd5d41f45240cec77b44967d76e0c3d25.zip into cache from /var/hoge_project/releases/20151118105919/fuel/vendor/symfony/polyfill-mbstring/d4ee82cecdc1009237c0d236258accd1.zip
    Extracting archive
Executing command (CWD): unzip '/var/hoge_project/releases/20151118105919/fuel/vendor/symfony/polyfill-mbstring/d4ee82cecdc1009237c0d236258accd1.zip' -d '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/29804340' && chmod -R u+w '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/29804340'

  - Removing symfony/filesystem (dev-master 232aa96)
  - Installing symfony/filesystem (dev-master 692d98d)
Downloading https://hogehoge/satis//dist/symfony-filesystem-692d98d813e4ef314b9c22775c86ddbeb0f44884-zip-138aac.zip
    Downloading: 100%
Writing /home/foo_user/.composer/cache/files/symfony/filesystem/692d98d813e4ef314b9c22775c86ddbeb0f44884.zip into cache from /var/hoge_project/releases/20151118105919/fuel/vendor/symfony/filesystem/4f98c531de03eb2f18005527fd7b803f.zip
    Extracting archive
Executing command (CWD): unzip '/var/hoge_project/releases/20151118105919/fuel/vendor/symfony/filesystem/4f98c531de03eb2f18005527fd7b803f.zip' -d '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/c0c2243a' && chmod -R u+w '/var/hoge_project/releases/20151118105919/fuel/vendor/composer/c0c2243a'

Reading ./composer.lock
Writing lock file
Generating autoload files

以上の実行結果から、外部リクエストは完全になくなり、すべてのパッケージリクエストはプライベートsatisに移ります。

私たちの環境では、deploy時間は2分48秒から40秒に短縮することができました。

まとめ

Satisを利用し、プライベートcomposerリポジトリを構築することで、以下の問題が解決できました。
  • 外部システムのトラブルによる、リリースの遅延・停止のリスク
  • インターネット経由で外部システムに接続することによるdeploy時間の長さ

 

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトからスマホアプリ開発まで、変化を楽しむエンジニアを募集しています。次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。