8.開発環境の完成
完成しました。こちらで公開しております。
当初考えていた内容との差
DNSのコンテイナー
最初書いていた内容からhost情報を設定ファイルにまとめて定義する方法に変更。hosts-dnsmasq.sampleをコピーしてhosts-dnsmasqを作成します。
127.0.0.1 local 127.0.0.1 73.local
プライベートIPアドレスの逆引を上位のDNSへ問い合わせしない。
# Do not use /etc/hosts as nameserver no-resolv # Use this file as a hosts file addn-hosts=/etc/hosts-dnsmasq # プライベートIPアドレスの逆引を上位のDNSへ問い合わせしない(無駄なので) bogus-priv # Log all dns queries log-queries
ウェブサーバー
DockfileがphpenvでインストールするVersionの変更や実行位置を変更して効率よくしました。
vhosts.conf.sampleをコピーしてvhosts.confを作成します。
Include conf.d/extra/7.3_virtual.conf
phpenvの新しいVersionのインストール用のShellをshellsフォールドを作成して入れました。
まだまだ改善します。
GithubのActionsでLaravelのデプロイ
背景
既にBitbucketのpipelineを使用してLaravelのアプリをデプロイしていましたが、Githubではどのように実装したら良いか試してみました。
前提条件
- Laravelをローカルでデプロイヤーとしてdeployerを利用している(公開鍵を利用)
要求内容
- ローカルでのdeployerを利用するデプロイはそのまま使用可能
- Actionsを利用したデプロイもdeployerを利用
- Staging環境のデプロイはbranchのpushで自動デプロイ
- 本番環境のデプロイは手動デプロイ
事前設定
Githubの設定
利用するレポジトリの「Settings > Secrets」に入って、「New secret」クリックし公開鍵を登録します。
サーバー(対象)
CIで実装されるので、前提条件で書いた公開鍵を利用したsshの接続することと同様にDeployerの実行の中でGithubからのソースのcloneする際にもパスワードが入力しなくても可能にする必要が有ります。
サーバーで以下の作業が必要です。
公開鍵の作成
鍵を入れるフォルダに移動しましょう。
$cd ~/.ssh
はじめて鍵を生成するときは何もないはずです。次のコマンドで鍵を生成します。
$ssh-keygen -t rsa
他のオプションを利用しても良いですが、ここでは一番簡単な感じでやります。
$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/Users/(username)/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again:
鍵のファイル名やパスワードを聞かれますが、3回エンターを押して飛ばします。 id_rsaとid_rsa.pubの2つの鍵が生成されます。既にid_rsaが存在する人は上書きされてしまうので注意です。
鍵の登録
Githubに入って上記で作成した鍵を登録します。https://github.com/settings/sshで公開鍵の設定が出来ます。
ローカルのデプロイでも使用していれば既に登録されている物があると思います。画面右上の「Add SSH key」のボタンを押します。
- Title:公開鍵名(任意)
- Key:公開鍵の中身
なお、鍵の中身のクリップボードへのコピーは以下の感じです。鍵の名前は自分の作成したもの。
$ pbcopy < ~/.ssh/id_rsa.pub (Mac) $ clip < ~/.ssh/id_rsa.pub (Windows)
接続のテスト
サーバーで以下を実行してみます。
$ ssh -T git@github.com Hi (account名)! You've successfully authenticated, but GitHub does not provide shell access.
で返ってきたら接続が可能とことです。
しかし、鍵を作るときに名前を指定していれば、うまくいかないかもしれません。それは、ssh接続の際「~/.ssh/id_rsa」、「~/.ssh/id_dsa」、「~/.ssh/identity」しかデフォルトでは見にいかないからです。 それに対応するためには~/.ssh/configを作成し、以下の内容を追記します。
Host github github.com
HostName github.com
IdentityFile ~/.ssh/id_rsa #ここに自分の鍵のファイル名
User git
テスト
$ ssh -T github
Actionsのworkflowを作成
対象リポジトリの「Actions」に入って新しくTempleteを利用して作成しても良いです。
トリガー
Staging
Stagingは要求内容にも書いてますが、特にエンジニアーが管理する必要がないかと思いましてgitにPushするタイミングでデプロイが動くと良いかと思いました。
name: deploy dev on: push: branches: - deploy/dev
production
本番の場合はGitのPushミス等の可能性でエンジニアーが手動でする必要があるかと思いましたので、以下のトリガーにしました。
name: deploy pro on: workflow_dispatch: branches: - deploy/pro
テスト
他の記事や資料、Githubのテンプレートではphpunitを利用する例が多です。
jobs: laravel-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Copy .env run: php -r "file_exists('.env') || copy('.env.example', '.env');" - name: Install Dependencies run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - name: Generate key run: php artisan key:generate - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache - name: Create Database run: | mkdir -p database touch database/database.sqlite - name: Execute tests (Unit and Feature tests) via PHPUnit env: DB_CONNECTION: sqlite DB_DATABASE: database/database.sqlite run: vendor/bin/phpunit
でも今回はウェブの状態を確認するように構成してみました。以下のように書きました。
jobs: test: name: serve test runs-on: ubuntu-latest services: mysql: image: mariadb:10.4.11 ports: - 3306:3306 options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 env: MYSQL_ROOT_PASSWORD: pass MYSQL_DATABASE: db_sample env: DB_CONNECTION: mysql DB_HOST: 127.0.0.1 DB_PORT: 3306 DB_DATABASE: db_sample DB_USERNAME: root DB_PASSWORD: pass steps: - uses: actions/checkout@v2 - name: cache vendor id: cache uses: actions/cache@v1 with: path: ./vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-composer- - name: composer install if: steps.cache.outputs.cache-hit != 'true' run: composer install -n --prefer-dist - name: copy .env run: cp .env.ci .env - name: generate key run: php artisan key:generate - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache - name: migrate run: php artisan migrate - name: run serve run: | php artisan serve & sleep 5 - name: test run: curl -vk http://localhost:8000
.env.ciファイルは既存のローカル環境で使用している物を再利用しました。
デプロイ
jobかstepに以下のような条件で分けることで共通化も可能でしたが、今回トリガーで分けるのでファイル次第が別にするしかなかったです。
if: github.ref == 'refs/heads/deploy/staging' <- ブランチが「deploy/staging」の場合
以下になりました。
deploy: name: deploy runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 - name: cache vendor id: cache uses: actions/cache@v1 with: path: ./vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-composer- - name: composer install if: steps.cache.outputs.cache-hit != 'true' run: composer install -n --prefer-dist - name: ssh_key copy run: | mkdir -p /home/runner/.ssh touch /home/runner/.ssh/deployerで設定されているファイル名 echo "${{ secrets.SSH_DEPLOY_KEY }}" > /home/runner/.ssh/deployerで設定されているファイル名 chmod 600 /home/runner/.ssh/deployerで設定されているファイル名 - name: add known hosts run: ssh-keyscan サーバーIP >> ~/.ssh/known_hosts - name: install deploy run: | curl -LO https://deployer.org/deployer.phar mv deployer.phar ./deployer/dep chmod +x ./deployer/dep - name: deploy dev run: cd ./deployer && ./dep deploy dev -vvv
- .deployer:deployerの設定が入っているフォルダーで、laravelプロジェクト/deployerの感じで設定されている状況です。
- dep deploy dev:deployerで設定されているstage名「->stage('dev')」です。
課題
本番のデプロイをトリガーで手動でしているのでファイルが別々になって共通的な部分が再利用されない状況です。手動でデプロイする他の方があるかとjob等を他のymlファイルをインクルード等出来れば良いかと思いました。
Macでphpenvを利用して7.4インストール
Macでインストールでエラー
krb5のインストールが必要な時
No package 'krb5-gssapi' found No package 'krb5' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables KERBEROS_CFLAGS and KERBEROS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.
opensslのインストール(Update)が必要な時
configure: error: Package requirements (openssl >= 1.0.1) were not met: No package 'openssl' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables OPENSSL_CFLAGS and OPENSSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.
icu4cのインストールが必要な時
configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir configure: error: Package requirements (icu-uc >= 50.1 icu-io icu-i18n) were not met: No package 'icu-uc' found No package 'icu-io' found No package 'icu-i18n' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables ICU_CFLAGS and ICU_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.
libeditのインストールが必要な時
configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir configure: WARNING: libedit directory ignored, rely on pkg-config configure: error: Package requirements (libedit) were not met: No package 'libedit' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables EDIT_CFLAGS and EDIT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.
libxml2のインストールやパス設定が必要な時
configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir warning: unsupported relocation in debug_info section. note: while processing /private/var/tmp/php-build/source/7.4.0/ext/opcache/.libs/zend_accelerator_util_funcs.o /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ext/opcache/.libs/opcache.a(shared_alloc_shm.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ext/opcache/.libs/opcache.a(shared_alloc_shm.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: /var/tmp/php-build/source/7.4.0/modules/opcache.a(shared_alloc_shm.o) has no symbols /var/tmp/php-build/source/7.4.0/ext/libxml/libxml.c:34:10: fatal error: 'libxml/parser.h' file not found #include <libxml/parser.h> ^~~~~~~~~~~~~~~~~ 1 error generated. make: *** [ext/libxml/libxml.lo] Error 1
bzip2のインストールやパス設定が必要な時
configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir configure: error: Please reinstall the BZip2 distribution
libiconvのインストールやパス設定な時
configure: WARNING: unrecognized options: --with-png-dir, --with-libxml-dir, --with-icu-dir configure: error: Please specify the install prefix of iconv with --with-iconv=<DIR>
解決
% brew install autoconf bzip2 icu4c krb5 libedit libiconv libjpeg libpng libxml2 libzip oniguruma openssl@1.1 pkg-config tidy-html5 % PKG_CONFIG_PATH="/usr/local/opt/krb5/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libedit/lib/pkgconfig:/usr/local/opt/libjpeg/lib/pkgconfig:/usr/local/opt/libpng/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig:/usr/local/opt/libzip/lib/pkgconfig:/usr/local/opt/oniguruma/lib/pkgconfig:/usr/local/opt/openssl@1.1/lib/pkgconfig:/usr/local/opt/tidy-html5/lib/pkgconfig" \ PHP_BUILD_CONFIGURE_OPTS="--with-bz2=/usr/local/opt/bzip2 --with-iconv=/usr/local/opt/libiconv" \ phpenv install 7.4snapshot
PHPのDTOの作成:カプセル化
背景
Javaのエンジニアの経験から見るとPhpのクラスの甘さが良いとも悪いとも言えます。その中でDTOで使用するクラスをもうちょっとある属性&関数のみ使用するようにしてカプセル化をしたいところです。
定義してない属性の設定や参照の禁止
public function __set($key, $value){ if (property_exists($this, $key)) { $this->{$key} = $value; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); } public function __get($key) { if (property_exists($this, $key)) { return $this->{$key}; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); }
初期ソースは簡単に考えましたが、ちょっと待って!! 「property_exists」ってpublicだけじゃない〜〜〜
それでこんな感じにしました。
public function __set($key, $value){ if ($this->existsProperty($key)) { $this->{$key} = $value; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); } public function __get($key) { if ($this->existsProperty($key)) { return $this->{$key}; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); } private function existsProperty($propertyName) { $reflection = new ReflectionObject($this); $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); foreach ($properties as $property) { if ($property->getName() === $propertyName) return true; } return false; }
Setter/Getterの自動化
Javaの実装でgetter/setterの作成の面倒です。Lombokのような物もありますね。それで以下のソースを用意しました。
public function __call($name, $args) { if (strncmp($name, 'get', 3) === 0) { $pascalCName = substr($name, 3); $snakeCName = $this->pascalCase2SnakeCase($pascalCName); if (property_exists($this, $snakeCName)) { return $this->{$snakeCName}; } } else if (strncmp($name, 'set', 3) === 0) { $pascalCName = substr($name, 3); $snakeCName = $this->pascalCase2SnakeCase($pascalCName); if (property_exists($this, $snakeCName)) { return $this->{$snakeCName} = reset($args); } } throw new \BadMethodCallException('Method "'.$name.'" does not exist.'); } private function pascalCase2SnakeCase($input) { preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches); $ret = $matches[0]; foreach ($ret as &$match) { $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match); } return implode('_', $ret); }
このソースではpublic以外のも設定可能にしないといけないので「property_exists」を利用しました。
完成
abstract class Dto { public function __call($name, $args) { if (strncmp($name, 'get', 3) === 0) { $pascalCName = substr($name, 3); $snakeCName = $this->pascalCase2SnakeCase($pascalCName); if (property_exists($this, $snakeCName)) { return $this->{$snakeCName}; } } else if (strncmp($name, 'set', 3) === 0) { $pascalCName = substr($name, 3); $snakeCName = $this->pascalCase2SnakeCase($pascalCName); if (property_exists($this, $snakeCName)) { return $this->{$snakeCName} = reset($args); } } throw new \BadMethodCallException('Method "'.$name.'" does not exist.'); } public function __set($key, $value){ if ($this->existsProperty($key)) { $this->{$key} = $value; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); } public function __get($key) { if ($this->existsProperty($key)) { return $this->{$key}; } throw new \BadMethodCallException('Method "'.$key.'" does not exist.'); } private function pascalCase2SnakeCase($input) { preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches); $ret = $matches[0]; foreach ($ret as &$match) { $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match); } return implode('_', $ret); } private function existsProperty($propertyName) { $reflection = new ReflectionObject($this); $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); foreach ($properties as $property) { if ($property->getName() === $propertyName) r[f:id:FattyRabbit:20201230123544j:plain]eturn true; } return false; } }
クラス名のセンスはないので。。。
phpenvでインストール:configure: error: Cannot find libz
インストール環境
OS
macOS Catalina version 10.15.3
Error
$ phpenv install 7.1.19 [Info]: Loaded extension plugin [Info]: Loaded apc Plugin. [Info]: Loaded composer Plugin. [Info]: Loaded github Plugin. [Info]: Loaded uprofiler Plugin. [Info]: Loaded xdebug Plugin. [Info]: Loaded xhprof Plugin. [Info]: Loaded zendopcache Plugin. [Info]: php.ini-production gets used as php.ini [Info]: Building 7.1.19 into /Users/******/.anyenv/envs/phpenv/versions/7.1.19 [Skipping]: Already downloaded and extracted https://secure.php.net/distributions/php-7.1.19.tar.bz2 [Preparing]: /var/tmp/php-build/source/7.1.19 ----------------- | BUILD ERROR | ----------------- Here are the last 10 lines from the log: ----------------------------------------- configure: error: Cannot find libz ----------------------------------------- The full Log is available at '/tmp/php-build.7.1.19.20200227165241.log'. [Warn]: Aborting build.
解決
$ brew install autoconf bison bzip2 curl icu4c libedit libjpeg libiconv libpng libxml2 libzip openssl re2c tidy-html5 zlib $ CONFIGURE_OPTS="--with-zlib-dir=$(brew --prefix zlib) --with-bz2=$(brew --prefix bzip2) --with-curl=$(brew --prefix curl) --with-iconv=$(brew --prefix libiconv) --with-libedit=$(brew --prefix libedit) --with-readline=$(brew --prefix readline) --with-tidy=$(brew --prefix tidy-html5)" phpenv install [version]
環境変数等をまとめて「phpenv install [version]」だけ出来る方法がまだ見つからない状態です。