もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

初めてOSSに貢献した話

はじめに

こんばんは。

簡単な話になりますが、ブログ書きます。

今回は生まれて初めてOSSに貢献した話。

このあたりの記事に触発されて、自分もできるかなと思い、探してやってみました。

qiita.com

qiita.com

本題

参考記事を確認しながら、以下のサイトで、得意言語の中から good first issuehelp wanted というラベルが付いたissueをあさりました。

github-help-wanted.com

そして、実際に自分が貢献したOSSはこちら

github.com

PHPUnitのスナップショットテストライブラリです。

スナップショットテスト自体やったことなかったのですが、PHPUnitは日常的に使っていたので、これで簡単なものならばいけるかも。。。?と思ってやりました。

自分が対応したIssueはこちらです。

github.com

JsonDriverクラス内で、配列のシリアライズを可能にするということでした。

そして実際に出したプルリクは以下。

github.com

作業自体はめちゃめちゃ簡単だったので、すぐ終わりました。

一番時間がかかったのはプルリクのコメント内容でしたw

グーグル翻訳先生に頼みながら、ニュアンスで乗り切りました。

マージされるまではドキドキしてましたが、マージされてホッとしました。

最後に

実際この対応行ったのは今年の1月とかなので、だいぶ昔の話なのですがブログにまとめてなかったので、今回ブログにしましたw

今後も簡単なものから少しづつ貢献していきたいなと思って、先程のサイトで自分ができそうなものをあさっています。

何個かめぼしいものを見つけているので、また貢献できたらブログにまとめようと思います。

Effective DevOpsオンライン輪読会Vol.3レポート

はじめに

輪読会 Vol.3終わりました。

今回は全体的に短かったので、初心者的な立ち位置で自分がやりたいと挙手させていただきました。

過去の開催はこちら。

輪読会 Vol.1

輪読会 Vol.2

本題

今回の資料はこちら

slideship.com

担当箇所は、第5章、第6章になります。

第5章 devopsに対する誤解とアンチパターン

1. devopsに対するよくある誤解

合計11個の誤解が紹介されていましたが、自分がピックアップしたのは以下。

  • devopsを取り入れるためにはX週間/Xヶ月かかる

実際に上の人に進言するときに、どうしても終了時期など聞かれると思うので、そういうときどうすればよいのだろう?

と考えていたのですが、 @tadaさん、@kdnaktさんがおしえてくれた 以下の記事を見て、なるほどと思いました。

developers.cyberagent.co.jp

やりたいという主張だけではなく、巻き込んでいくことが大事。

  • devopsとは自動化のことだ

自動化して節約できる時間よりも自動化のために使う時間のほうが多ければ、それは時間の使い方がよくなったとは言えない。 という言葉にはグサリときました。

よくそれが目的になってしまい、本来の目的を見失ってしまうことがありました。

それに加えて、devops = 自動化 というのは、まさにこういう考え方をしていました。

2. devopsのアンチパターン

  • 非難文化
  • サイロ
  • 根本原因分析
  • ヒューマンエラー

この中で気になったのは、「非難文化」と「ヒューマンエラー」でした。

非難文化

言葉的に強い言葉だったのでドキッとしましたが、意外と非難文化ってよくあることだと思いました。

非難する側に知らない間に回ってしまっているかもと考えると、さらにドキリとしました。

ヒューマンエラー

ミスを犯してまずい結果を引き起こしたのは誰かを考えることが中心になることが多く、ミスを犯した個人を見つけたところで議論は終わる。

この言葉が非常にドキリとしました。

なんというか、この文章の言い回しが大変に突き刺さる言葉でした。

第6章 効果的なdevopsのための4本柱

  • コラボレーション
  • アフィニティ
  • ツール
  • スケーリング

こちらは、第7章以降に詳しく解説されているので、概要程度でした。

終わりに

まだまだ序盤ですが、第5章のアンチパターンを読めただけでも、読んでよかったと思いました。

もう少し早めに出会いたかったなと思うほどです。

次回は @kdnaktさん です!

よろしくおねがいします!

Laravel Webアプリケーション開発を読んだ

はじめに

こんばんは。

今月の目標で、積本を2冊消化するという目標があるので、溜まっている本を眺める時間はもうやめて、重い腰を上げ積み本消化に乗り出しました。

一冊目は、去年辺りから積んでた Laravel Webアプリケーション開発です。

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

本題

目次

  1. Laravelの概要
  2. Laravelアーキテクチャ
  3. アプリケーションアーキテクチャ
  4. HTTPリクエストとレスポンス
  5. データベース
  6. 認可と認証
  7. 処理の分離
  8. コンソールアプリケーション
  9. テスト
  10. アプリケーション運用
  11. テスト駆動開発の実践

読もうと思った理由

ちょうどLaravelの実務が始まるところだったので素振りするために買いました。

ざっくりとした感想

帯には 実践パターン と ユースケース で学ぶ開発現場で使えるプロのテクニック という感じで、パット見玄人向けな本かなと感じてましたが、中はかなり丁寧に書かれており、初心者とはいわないですが、これからLarevel学びたいと思っている経験者の人にはスラスラと入れるような内容でした。

正直Laravel自体使用者も多いので、日本語ドキュメントがわんさと出てきます。

なので、こういった手合の本はなくても良いかな?と思いましたが、本として時間をとって読むのと、サラッと必要なときにwebページを見るのでは違いがあると思いますので、自分としては良かったです。

ただ、いかんせん本を購入してから読み終えるまでにかなり時間が立ってしまっていて、本の内容はある程度実務でこなしたあとだったので、ある程度ベースの知識が出来ている状態で読むことになりました。。。

それでも、今まで雰囲気で使ってたところとかを、おさらいするいいキッカケになったかと思います。

読んで学んだ事

すでに実務で経験済みのところなどが多かったので、比較的知っている内容が多かったです。

ただ、雰囲気で使っていた部分などがあったので、以下のあたりは勉強になりました。

  1. Laravelアーキテクチャ の サービスコンテナバインド周り

  2. Laravelアーキテクチャ の deferプロパティによる遅延実行

  3. 処理の分離の全般

特に、7章の処理の分離にでてくる、イベント系の機能は、自分が実務で実装した箇所には触らなかったので、勉強になりました。

また、よくあるFWの入門書?解説書?と違い、Controller、Modelにベタっと書いちゃうサンプルコードではなく、運用を意識した設計でリファクタリングをするというフェーズが設けられていたのが印象的でした。

自分も今レガシーコードを四苦八苦しながら設計頑張っているので、なるほどなるほどと、とても勉強になりました。

終わりに

話は飛んじゃいますが、同じSlackチームに所属している @budougumi0617さんの、15日間勉強してAWS ソリューションアーキテクト アソシエイト試験に合格した というブログを見て、資格試験に無縁な生活をしていた自分ですが、AWSの試験受けてみようと思いました。

twitter.com

budougumi0617.github.io

とりあえず目下の目標としては、積んでる本全部読んでからですが・・・w

今年の目標として、一つ資格合格を上げておきたいと思います。

では、現場からは以上です。

CricleCIから踏み台サーバを経由して対象サーバへデプロイ

はじめに

こんばんは。

またまた備忘録です。

しかも今回はちょっとした小ネタレベルです。

関わったプロジェクトで、踏み台サーバを経由して本番サーバへデプロイしたいということになり、こんな感じで良いかな?という、以下の方法でまとまりました。

本題

1. CircleCIにPrivate Keyを登録する

CircleCI -> Settings -> Corporation -> ProjectのSSH Permissionsのページで、private keyを登録します。

f:id:kojirooooocks:20190513033154p:plain

2. CircleCI用に使用するssh configファイルを作成

# vi /path/to/project/.circleci/circleci_ssh_config

Host bastion_host
  HostName ************
  User     ************
  IdentityFile /home/circleci/.ssh/id_rsa_{sshkeyを登録した際に表示されるfingerprintsで、「:」を抜いたもの}
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null
Host prod_server
  HostName     ************
  User         ************
  IdentityFile /home/circleci/.ssh/id_rsa_{sshkeyを登録した際に表示されるfingerprintsで、「:」を抜いたもの}
  ProxyCommand ssh -W %h:%p bastion_host
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null

3. CircleCIのconfig.ymlにsshkeyとsshconfigを登録する

executors:
  deploy:
    working_directory: ~/workspace

commands:
  # sshKeyをCircleCIのconfigに設定する
  deploy_ssh_setting:
    steps:
      - add_ssh_keys:
          fingerprints:
            - "{sshkeyを登録した際に表示されるfingerprints}"
      - run: cat ~/workspace/.circleci/circleci_ssh_config >> ~/.ssh/config

jobs:
  deploy:
    executor:
      name: deploy
    steps:
      - checkout
      - deploy_ssh_setting

これで一応狙ったことは出来ます。

以前あげたLaravel-Deployerでのデプロイでまさにこの方法でbackend_serverへデプロイしてます。

終わり

もっとスマートなやり方があればぜひ教えてくださいm( )m

小さなネタになりましたが、現場からは以上です。

Effective DevOpsオンライン輪読会はじめました

f:id:kojirooooocks:20190513005434j:plain

こんばんは。

自分が所属している challenge-every-month というSlackチームのメンバーで、 Effective DevOps のオンライン輪読会が開催され、その会に参加させてもらいました。

Effective DevOps ―4本柱による持続可能な組織文化の育て方

Effective DevOps ―4本柱による持続可能な組織文化の育て方

参加しましたといっても、#1 は自己紹介や輪読会の進め方などのすり合わせでした。

参加者は、 @tadaさんと@kdnaktさんと、自分です。

twitter.com

twitter.com

開催のきっかけは、 @tadaさんが@kdnaktさんのブログを読んで、オンラインで一緒に読み進めませんか?と話していたところを僕が空気を読まず突っ込んだという感じです。

kdnakt.hatenablog.com

f:id:kojirooooocks:20190513005630p:plain

f:id:kojirooooocks:20190513005656p:plain

#1 では、前述した通り、各自の自己紹介と今後の進め方などを話しました。

進め方は以下。

  • 1週間に1回開催
  • 1回の読む量は1章分で30〜40ページ程度
  • 初回については、1~4章分(各章の内容によって、読む量は変動する)

輪読会でやりたいことは以下。

  • 読んだ章の中身の理解のすり合わせ
  • 実際の現場での実践の話

また、途中で輪読会に参加したいという方が現れた場合も、読み進めている場所からスムーズに参加できる人であれば、歓迎しますというルールにしました。

↑こんな偉そうなこと言ってますが、Effective DevOps については、自分は全く読んだこともなく、 DevOpsについても分かっているような分かってないようなくらいの知識です。

なので、とにかく読み進めつつ、お二人から諸々吸収できればという気持ちでいますw

また、初回の開催でわかったのですが、自分のマイクがとにかく小さいみたいなので、これを気にヘッドセットを買いました。

次からは少しはクリアになるかと。。。!

次回は5/19に開催予定なので、その時の感想も ブログにしようとおもいます。

現場からは以上です。

PS

@tadaさん @kdnaktさん

初回の開催は本当は5/11だったのにいきなりリスケさせてすいませんでした...

Laravelでjson web token試してみました

はじめに

こんばんは。

今回も前回同様、作業した備忘録です。

今回試したのは LaravelでのJWTです。

こちらの jwt-auth というライブラリです。

設定自体はめちゃめちゃ簡単です。

公式のドキュメントに書いているとおりで、ほぼ迷うことはありません。

本題

1. インストール

$ composer require tymon/jwt-auth

2. 設定

# configファイル生成
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"


# secret key作成
$ php artisan jwt:secret

# auth設定
# vi config/auth.php
# multi-authだとこんな感じ?

...

'guards' => [
    'admin' => [
        'driver'   => 'jwt',
        'provider' => 'admin',
        'hash'     => true,
    ],
    'customer' => [
        'driver'   => 'jwt',
        'provider' => 'customer',
        'hash'     => true,
    ],
],


'providers' => [
    'admin' => [
        'driver' => 'eloquent',
        'model'  => App\Models\AdminUser::class,
    ],
    'customer' => [
        'driver' => 'eloquent',
        'model'  => App\Models\Customer::class,
    ],
],

3. routing設定

<?php

Route::group(['middleware' => ['auth:admin']], function () {
    Route::get('/staff', 'StaffController@list')->name('StaffList');
    Route::get('/auth/refresh', 'AuthController@refresh')->name('AuthenticateRefresh');
});

Route::post('/auth/login', 'AuthController@login')->name('Authenticate');
Route::delete('/auth/logout', 'AuthController@logout')->name('AuthenticateLogout');

4. Controller設定

<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class AuthController extends Controller
{
    /**
     * Login Action.
     * @throws UnauthorizedHttpException
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(): \Illuminate\Http\JsonResponse
    {
        $requestData = request(['email', 'password']);
        $credentials = [
            'mailaddAddress' => $requestData['email'],
            'password'       => $requestData['password'],
        ];

        if (!$token = auth()->attempt($credentials)) {
            throw new UnauthorizedHttpException('Invalid credentials', '認証失敗');
        }

        return $this->respondWithToken((string) $token);
    }

    /**
     * Logout Action.
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout(): \Illuminate\Http\JsonResponse
    {
        auth()->logout();
        return response()->json('OK');
    }

    /**
     * RefreshToken.
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh(): \Illuminate\Http\JsonResponse
    {
        return response()->json([
            'accessToken' => auth()->refresh(),
            'expiresIn'   => auth()->factory()->getTTL() * 60,
        ]);
    }

    /**
     * Get the token array structure.
     *
     * @param string $token
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken(string $token): \Illuminate\Http\JsonResponse
    {
        $adminUser = auth()->user();
        return response()->json([
            'accessToken' => $token,
            'firstName'   => $adminUser->firstName,
            'lastName'    => $adminUser->lastName,
            'id'          => $adminUser->id,
            'mailAddress' => $adminUser->mailAddress,
            'expiresIn'   => auth()->factory()->getTTL() * 60,
        ]);
    }
}

5. Model設定

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class AdminUser extends Authenticatable implements JWTSubject
{
    use Notifiable;

    protected $table = 'admin_users';

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims(): array
    {
        return [];
    }
}

f:id:kojirooooocks:20190510014611p:plain

expiresIn はローカルなのでとんでもない値に設定していますw

終わり

簡単ですが、こんな感じです。

あと備忘録として残しておかないといけないのなんだったけかな・・・?

とりあえず現場からは以上です。

Laravel-Deployerを使ったデプロイ

はじめに

こんにちは。

この記事は challenge-every-month全員でアウトプット芸人 Advent Calendar 8日目の記事です。

前日の記事は、激アウトプット芸人 の @kdnaktさんの記事です。

twitter.com

kdnakt.hatenablog.com

4月からGW明けまで異常に忙しく、なかなかブログや挑戦を行えていない僕です。

今回のブログも作業の備忘録的なブログでございます。

Laravelプロジェクトのデプロイをどうしようかと考えていたのですが、Deployerを試してみようと思い、Laravel-Porjectに特化したLaravel-Deployerなるものを発見したのでこちらを使用しました。

Laravel-DeployerはCapistranoと同じくシンボリックリンク型のデプロイツールで、そのデプロイ方法は大きく5つ存在するようです。

  1. Basic strategy
  2. First deploy strategy
  3. Locally-built strategy
  4. Git pull only strategy (no zero downtime)
  5. Upload strategy

全部検証しているわけではないのですが、基本的にHostサーバ側で git pullしちゃう形になってるみたいです。

また、.envstorage などは sharedに登録されるようですが、 基本的には .env は .gitignore対象になっていると思うので、 git pullしたあとだと .env が存在しません。

.env がない場合は、勝手に空の .env を作成して sheardFileに登録されます。

それによりもれなくエラーとなります。

このあたり参考サイトをみてても手動で解決しているようでした。

この流れで自分が変えたかったのは、以下です。

  • Host側でgit pullしちゃうところ(単純に時間短縮したい)
  • ファイルがない場合勝手に .env 作っちゃう(期待している .env を作ったり上書きしたりしたい)

こちらを対応するため、先ほど紹介したデプロイ方法をベースにカスタムのデプロイ方法を作りました。

本題

1. インストール

$ composer require lorisleiva/laravel-deployer
$ php artisan deploy:init

2. deploy.php修正

今回は自分が変えたかったところを対応するために、 Default deployment strategy を変更します。

# config/deploy.php

<?php

declare(strict_types=1);

return [
    /*
    |--------------------------------------------------------------------------
    | Default deployment strategy
    |--------------------------------------------------------------------------
    |
    | This option defines which deployment strategy to use by default on all
    | of your hosts. Laravel Deployer provides some strategies out-of-box
    | for you to choose from explained in detail in the documentation.
    |
    | Supported: 'basic', 'firstdeploy', 'local', 'pull'.
    |
    */

    'default' => 'custom',

3. recipeとなるphpファイル修正

基本ベースは upload strategy をベースにしており、host側で git pullするところを、 git pullせずrsyncを行うように変更しています。

# recipe/custom_strategy.php

<?php

declare(strict_types=1);

namespace Deployer;

set('writable_dirs', [
    'bootstrap/cache',
    'storage',
    'storage/app',
    'storage/app/public',
    'storage/app/tmp',
    'storage/framework',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs',
]);

desc('Custom Strategy');
task('strategy:custom', [
    'hook:start',
    'deploy:prepare',
    'deploy:lock',
    'deploy:release',
    'custom:upload',
    'deploy:shared',
    'deploy:vendors',
    'deploy:writable',
    'hook:ready',
    'deploy:symlink',
    'deploy:unlock',
    'cleanup',
    'hook:done',
]);

set('custom_upload_path', __DIR__ . '/../');
set('custom_upload_vendors', false);
set('custom_upload_options', function () {
    $options = [
        '--exclude=.git',
        '--exclude=.circleci',
        '--exclude=.env.develop',
        '--exclude=.env.local',
        '--exclude=.env.production',
        '--exclude=.env.staging',
        '--exclude=.env.testing',
        '--exclude=server.php',
        '--exclude=sg.sh',
        '--exclude=tests',
        '--exclude=recipe',
        '--exclude=.editorconfig',
        '--exclude=.gitattributes',
        '--exclude=.gitignore',
        '--exclude=.php_cs.cache',
        '--exclude=.php_cs.dist',
        '--exclude=_ide_helper.php',
        '--exclude=codecov.yml',
        '--exclude=coverage.xml',
        '--exclude=phpcs.xml',
        '--exclude=phpstan.neon.dist',
        '--exclude=phpunit.xml',
        '--exclude=readme.md',
    ];

    if (!get('custom_upload_vendors')) {
        $options[] = '--exclude=/vendor';
    }

    return compact('options');
});

desc('Upload a given folder to your hosts');
task('custom:upload', function (): void {
    $configs = array_merge_recursive(get('custom_upload_options'), [
        'options' => ['--delete'],
    ]);

    upload('{{custom_upload_path}}/', '{{release_path}}', $configs);
});

4. デプロイ実行

デプロイ自体は以下のコードで実行できます。

$ php artisan deploy

今回は CircleCIからデプロイするようにしましたので、CircleCI上で checkoutした状態のコードをデプロイします。

このデプロイフローの中で、 .env の問題も解決させています。

# .circleci/config.yml

  # デプロイ実行
  execute_deployer:
    steps:
      - run: cp ~/workspace/.env.production ./.env
      - run: echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> ./.env
      - run: echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> ./.env
      - run: echo "DB_PASSWORD=$DB_PASSWORD" >> ./.env
      - run: php artisan deploy:run deploy:unlock
      - run: php artisan deploy -vv

特に変わったことはしておらず、事前に用意している production環境用の .envファイルを .env ファイルとしてコピーしつつ、デプロイを行うという方法です。

その際に、awsのアクセスキーやシークレットキー・本番DBのパスワードなども環境変数からコピーしています。

deployコマンドを実行する前に deploy:unlock を実行しているのは、万が一デプロイ中に何かしら失敗した場合に .lockファイルが残っている可能性があり、そのファイルを消さないと次のデプロイが失敗してしまうから、必ず消すようにしています。

5. デプロイ結果

実際のデプロイ結果は以下です。

$ ls -la
合計 412
drwxr-xr-x 10 ec2-user ec2-user    207  426 21:48 .
drwxrwxr-x  7 ec2-user ec2-user     55  426 21:46 ..
-rw-r--r--  1 ec2-user ec2-user   1487  426 21:48 .env
drwxr-xr-x 10 ec2-user ec2-user    131  426 21:44 app
-rwxr-xr-x  1 ec2-user ec2-user   1686  426 21:44 artisan
drwxr-xr-x  3 ec2-user ec2-user     34  426 21:44 bootstrap
-rw-r--r--  1 ec2-user ec2-user   2882  426 21:44 composer.json
-rw-r--r--  1 ec2-user ec2-user 400814  426 21:44 composer.lock
drwxr-xr-x  2 ec2-user ec2-user   4096  426 21:44 config
drwxr-xr-x  4 ec2-user ec2-user     37  426 21:44 database
drwxr-xr-x  2 ec2-user ec2-user     40  426 21:44 public
drwxr-xr-x  3 ec2-user ec2-user     18  426 21:44 resources
drwxr-xr-x  2 ec2-user ec2-user    102  426 21:44 routes
lrwxrwxrwx  1 ec2-user ec2-user     20  426 21:46 storage -> ../../shared/storage
drwxrwxr-x 51 ec2-user ec2-user   4096  426 21:46 vendor

終わりに

今回はこのような感じです。

このあたりブログに書き留めるために作業中にまとめておきたかったのですが、とにかく忙しく思い出しながら書きました。。。

嗚呼。忙しい。

現場からは以上です。

参考サイト