BNBのプログラミング勉強記録

ガチのプログラミング初心者が駆け上がっていくブログ

Oracle Linux Server 逆引きコマンド

なんだこれ

マジで罠が多すぎるって。

VPSでDocker入れてるんですよ、そこでcomposeでrailsmysqlのコンテナ動かしてるんですよ、mysqlがなんか調子悪いんですよ、そしたらmysqlのコンテナにexecで入るじゃないですか、そんでmysqlのログ見ようとするじゃないですか、でもなんでかログが無いんですよ、んでChatGPTとかググったりしたらmy.cnfを修正するとログが出せるようになる(mysqlにログインしてコマンド打ってもいいけど)とのことでvimインストールしようとすると詰んだ。aptコマンドが無いってさ。

俺が間違ってたのは、ここでずっとChatGPTに聞きまくったこと。マジでミスった。というか、聞き方が悪かったのかな。まずはそのコンテナ(に使われてるDockerイメージ)のOSを特定する必要があるのでは?とふと思い、その方法をGPTに聞くと教えてくれた(/etc/os-releaseをcatで見ると書いてる)、それが件名のOracle Linux Serverである。でもGPTに対して、vimをどうインストールするかを聞いたがどれも違う(yum使えない)のでググったら一発。それ、および今後も色々使いそうなコマンドをメモするためにこの記事を書いた。

インストール

vim

microdnf install -y vim

MySQL逆引き

前提

mysql8.0コンテナにexecで入ると、OSはOracle Linux Server 8.8だった。そこで動かす前提。

Oracle Linux Server 8.8でのコマンド等

ログイン

mysql -u your_username -p
※mysql8.0のイメージから作ったコンテナにexecで侵入してイゴイゴする場合のユーザー名はrootだった

ログの出力をオンにし、出力場所も指定

下記をetc/my.cnfの「mysqld」の箇所に追加する。ただ、デフォルトだとcnfを編集できないという罠があるのでこの記事を見よう。
general_log = 1
general_log_file = /var/log/mysql/mysql.log

上記を反映

systemctl restart mysql


MySQLの中でやるコマンド

ログ出力するようにする

SET GLOBAL general_log = 'ON';

ログの場所を指定

SET GLOBAL general_log_file = '/var/log/mysql/mysql.log';
※エラー出た。設定ファイルに入れないとだめのよう。

とにかく安価かつ手軽に自分のWebサービスを公開するならVPSが最適なのかな

結論から書くと

調べたり試したりしてみた範囲内では、Kagoya cloud VPSにDockerを導入するのが最も手軽かつ安価だった。ただ、herokuやAWSなど色々試してきたり、ターミナル等のコマンドラインで少しばかりいごいごする知識があるのが前提ではあるので、これを「手軽」と呼んでいいのかは、自分でも少し疑問に思う。

VPSとは

あえて調べずに書くが、「CPUやメモリやOSを指定して、手軽にサーバーが使える」サービス。厳密には色々あったと思うけど。これを契約して、そこに前述のCPU等を指定したら、すぐにサーバーが使える。サーバー上で何かをインストールしたり、Dockerを起動させたりする際は、コマンドラインを使う。ブラウザ上のVPSの管理画面で使うこともできるが、基本はMacならターミナル、WinならコマンドプロンプトPowerShellとかGitBashかも)、あるいはWin/Mac共通だとVSCodeでコマンドを使うことになると思う。これらの、「VPS管理画面」以外のコマンドラインツールは、以下「ターミナル等」と記載する。

ターミナル等でどうやってVPSにアクセスするのか

俺のようなIT素人だと、そもそも「え、管理画面以外からアクセスできるんや!」ってなるんですわ。なんでできるか。SSLを使うから。SSLとは何か。すまん、概念はなんとなく分かってるつもり(公開鍵と秘密鍵でうまいこと通信)だが、説明は省く。とにかく、SSLで接続する。やり方については、どのVPSサービスも分かりやすいマニュアルを用意してくれているのでそれに従えばOK。

例えば俺の使ってるkagoyaだと、ターミナル等で所定の文字列「ssh -i 秘密鍵までのパス root@VPSIPアドレス」を入力するとターミナル等がVPSに繋がり、以後、ターミナル等で入力するコマンドは、自分の普段使っているコンピュータに対してではなく、契約したVPSに対して命令するものになる。

VSCodeだと、普段はアプリのフォルダを開いていごいごするが、リモートエクスプローラーという拡張機能を使ってSSHに接続し、VPS上のフォルダを開くことができる。基本はこちらでコードをいごいごして、コンテナを起動したりログを見たりはターミナル等でSSH接続して見るのがいいと思う。

ここに至るまで

その1 heroku

これは、自分のGithubの任意のリポジトリと連携させて、それをすぐ動かせるサービス。昔は無料だったらしいが、今は有料。執筆時点での最安の使い方は(以下月額)、基本料金5ドル、JawsDBという拡張機能でデータベース容量5MBまで、これが無料、計5ドル。執筆時点でざっくり750円。なお、よく紹介されてるheroku拡張機能のHeroku Postgresは1アプリごとに5ドルかかる。俺はそのことを忘れて練習用アプリ4つに適用していたことがある。請求を見てビビった。というか、JawsDBが無料なの、かなり後半に知ったぞ。

その2 AWS

なんか主流っぽいので頑張って覚えた。構成はECS、RDS。前者がコンテナをそのまま動かせるやつ、後者がデータベース。平均して13ドル(1900円)掛かっていた。ほとんど(11ドルくらい)がECSの料金。これを安くできんかなと思い、色々調べていた

その3 Kamal

Railsの作者が作ったサービス。ただ、動かし方が理解できず、その中で「自宅サーバーとかVPSがあれば動くっぽい」ということは理解できた感じ。今だと少しは理解できたので、そのうちやるかも。

その4 VPS+Dokku

前項でVPSの存在をうっすら知り、色々調べていた際に、Dokkuというものを知る。VPS上にインストールすることで、自分のVPSで「herokuみたいなやつ」使い放題!という夢のようなものである。VPSを契約し、初期設定としてOSのインストールと併せてDokkuのインストールまでやってくれるVPSサービスがあったので、これ幸いとばかりにインストール、Dokku起動、起動、起動・・・。あれ?マニュアル通りなのになぜか動かない、等いろいろあった。

結局、何とか動かせた、かと思いきやherokuと微妙に違う点が多々あり、デプロイはできるがherokuビルドパック(herokuのサーバー上にインストールしないといけないものが、ビルドパックとして用意されている。画像処理とか。)がそのまま使えない。それ故、herokuでは動いていたのにVPS上では一部の機能が動かない、等の不満があり、「これだったらコンテナをそのまま動かした方がシンプルでよくないか?」と思ってしまった。

その5 VPS+Docker

現状がこれ。VPS上に、git cloneで自分のアプリを持ってきて、次にDockerをインストールし、あとは自分のアプリのcomposeを起動するだけ。これだとURLがIPアドレスのままなのでカッコ悪いよね。なので、公開するとしたらドメインを取ってDNSを設定したり、SSL化(親切な人がcomposeの書き方をネットに挙げてくれてる)などやる必要はあるが、割とすんなりできるはず。少なくともDokkuよりは遥かに手軽だった。

なおVPSは、さくら、conoha、indigo、XServer、kagoyaと使い、kagoyaに決めた。500円。さくらはお試し期間があるのだが、何かを適用するために(DNSだったかな?)は本契約する必要があり、その最低利用期間が3ヶ月。前のめり過ぎて、それに気づかず契約しちゃったよ。よく読んでない俺が悪いんだが、なんか悪質だなあと思った。あと、XServerはお試し期間がない。なので、「とにかくVPSというものを体験してみたい」という俺のような人は、さくらとXServerは避けるのが無難と思います。この辺りは別記事を書きたい。

課題

バックアップ。俺はほぼ知識ゼロなので、これから勉強です。

Laravel俺用逆引きリファレンス

前提

  • 「Laravelの教科書」という超初心者向けサイトを参考
  • MVCはPostで統一(belongstoとhasmany用にUserも出てくるけど)
  • sail導入前提なのでphp 〇〇のコマンドは全てsail 〇〇 と記述
  • Railsを4ヶ月ほど使ったので、それとの比較も書く
  • シンタックスハイライトのために、phpコードの行頭は統一

コマンドライン

アプリ作る

curl -s "https://laravel.build/アプリ名" | bash

sail立ち上げ

./vendor/bin/sail up

sail 〇〇 が使えるようにする

alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'

breez(全部のっけ的なスターターキット)

sail composer require laravel/breeze --dev
sail artisan breeze:install 
sail artisan migrate
sail npm install
sail npm run dev

日本語化

sail composer require askdkc/breezejp --dev
sail artisan breezejp

モデル作成(マイグレーション付き)

sail artisan make:model Post -m

マイグレートする

sail artisan migrate

マイグレートしたやつを戻す

sail artisan migrate:rollback

マイグレーションファイル追加(カラム追加)

sail artisan make:migration add_column_user_id_to_posts_table --table=posts

コントローラー作成(resource付き)

sail artisan make:controller PostController --resource --model=Post

コンポーネント作成

sail artisan make:component Message

tinker

起動

rails cみたいなやつ。データベースをいごいごできる。phpmyadminが素人くさすぎて抵抗があって調べたらこれが良さそうだった。

sail artisan tinker

モデル全件取得

$posts = \App\Models\Post::all();

モデル1件取得

$post = \App\Models\Post::find(1);

モデルに何のカラムが入ってるか見る

\Illuminate\Support\Facades\Schema::getColumnListing('posts');

モデル

belongsTo

<?php
    public function user() {
        return $this->belongsTo(User::class);
    } 

hasMany

<?php
    public function posts() {
        return $this->hasMany(Post::class);
    }

submitで受け付けるパラメーターを設定

railsのpermit的なやつか。モデルに書くんだねえ

<?php
    protected $fillable = [
        'title',
        'body',
        'user_id',
        'image',
    ];    

コントローラー

ビューを指定

この場合だと「postフォルダのcreate.blade.php」を指す

<?php
return view('post.create');

インスタンスを作る

<?php
$post=new Post();

インスタンスに代入

上の続き

<?php
$post->title=$request->title;
$post->body=$request->body;

インスタンスに代入(自動で、認証ユーザーのIDを取得)

この辺勉強中だが、Laravelってこのあたりのユーザー認証系が、標準で組み込まれてるみたい。この辺はRailsよりもかなりとっつきやすいのでは。

<?php
$post->user_id=auth()->user()->id;

インスタンスをデータベースに保存

<?php
$post->save();

リダイレクト

<?php
return redirect()->route('post.create');

メッセージ表示

Railsflash的なやつか?

<?php
return redirect()->route('post.create')->with('message', '投稿を作成しました');

バリデーション

<?php
        $inputs=$request->validate([
            'title'=>'required|max:255',
            'body'=>'required|max:1000',
            'image'=>'image|max:1024'
        ]);
        
        $post=new Post();
        $post->title=$inputs['title'];
        $post->body=$inputs['body'];
         //以下略。ちなみに、インスタンス作成時にわざわざinputsに置き換える必要もない

ビュー

共通のビュー

app.blade.phpの下記の部分が、他のビューの中身になる。言い換えると、それ以外は共通。Railsと一緒。

<?php
{{ $slot }}

中身にするには

上記の続き。各ビューは、下記タグで囲む。

<?php
<x-app-layout>
</x-app-layout>

フォーム

  • このケースは、postコントローラーのstoreアクションを使う
  • @csrfを入れることで、クロスサイトリクエストフォージェリ(Webアプリケーションのユーザーが意図しないリクエストを偽造し、ユーザーが意図しない処理を実行させる攻撃)を防ぐ
<?php
<form method="post" action="{{route('post.store')}}" enctype="multipart/form-data">
@csrf

コンポーネントを使う(breez標準付属のprimary-button)

<?php
<x-primary-button class="mt-4">
送信する
</x-primary-button>

コンポーネントコンストラクター設定

Messageというコンポーネントの場合。下記はMessage.phpに書く。オブジェクト指向コンストラクターですな。あ、「コンポーネントコンストラクター」って「長久命の長助さん」っぽいな

<?php
    public $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

コンポーネント本体

上記の続き(上記とセットでコンポーネントを作るのが基本のやり方)。下記はmessage.blade.phpに書く。issetは、変数が存在するかの真偽値を返すphpの組み込み関数。Railsだとこんなの付けなくても変数だけ書いたんでよかったような・・。exist?か。いかん、この辺の理解が浅いな。戻り値をどうするかで使い分けるんだっけ。trueではなかったときにnilかfalseかというところで。

<?php
@if(isset($message))
<div>
    {{$message}}
</div>
@endif

コンポーネントを使用する

<?php
// <x-ファイル名> </ファイル名>
// <x-ファイル名 :データ名=データの定義> </ファイル名>
// <x-ファイル名 :データ名=データの定義 />

<x-message :message="session('message')" />

匿名コンポーネント

上記の例でいくと、コンストラクター用のMessage.phpを作らず、message.blade.phpだけを作ること。使用の仕方は上記と同じ。

<?php
@props(['message'])

@if(isset($message))
<div>
    {{$message}}
</div>
@endif

標準コンポーネント(バリデーションエラー)

<?php
<x-input-error class="mb-4" :messages="$errors->all()"/>

バリデーションエラー後、入力したものを自動入力してくれる

これもRailsのform_withだと標準ではあるけど、こっちの方が明示的で好感が持てるなあ。ほんまform_with嫌い。書き方が柔軟すぎて。

<?php
value="{{old('title')}}" 

コンポーネント(カスタマイズしたバリデーションエラー)

Laravel教科書では、画像を再添付させるためにカスタムコンポーネントを使用している。注意書きレベルのものをエラーと同じ文字装飾で表示するのってどうなんだろう。なんか引っかかるなあ。

<?php
@props(['errors'])

@if ($errors->any())
    <div {{ $attributes }}>
        <div class="font-medium text-red-600">
            エラーの内容を確認してください。
        </div>

        <ul class="mt-3 list-disc list-inside text-sm text-red-600">
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach

            @if(empty($errors->first('image')))
                <li>画像ファイルがあれば、再度、選択してください。</li>
            @endif
        </ul>
    </div>
@endif

ルーティング

resource

<?php
Route::resource('post', PostController::class);

上記を分解

<?php
Route::get('post', [PostController::class, 'index'])->name('post.index');
Route::get('post/create', [PostController::class, 'create'])->name('post.create');
Route::post('post', [PostController::class, 'store'])->name('post.store');
Route::get('post/{post}', [PostController::class, 'show'])->name('post.show');
Route::get('post/{post}/edit', [PostController::class, 'edit'])->name('post.edit');
Route::patch('post/{post}', [PostController::class, 'update'])->name('post.update');
Route::delete('post/{post}', [PostController::class, 'destroy'])->name('post.destroy');

マイグレーションファイル

モデル新規作成時にカラムを設定

nullableとすると、必須ではなくなる。これ、もしかしてバリデーション的なものなのか?この辺は勉強。

<?php
$table->string('title');
$table->text('image')->nullable();

foreignId

ここもよく分からん。integerではないのは、他のモデルのidを使うからか?それともbelongsしてるからか?

<?php
$table->foreignId('user_id');

特定のカラムの後に追加

一発目のマイグレート以降のカラム追加時に使う。

<?php
$table->foreignId('user_id')->after('image');

カラム削除

どうも、LaravelはRailsと違って、マイグレーションファイルは常にupとdownを書かないといけないようだ。(ほんと?)

<?php
$table->dropColumn('user_id');

Laravelを最新版公式に沿ってシンプルに導入。あと感想。

結論

環境構築は親切だけどMVCの書き方はRailsに慣れてたらだいぶ癖があるかも。Railsってスゲーんだな、って思ったわ。

なぜ使おうと思った?

地理的な問題で、Rails書ける仕事ってなかなか募集していないので、ならば次にやるべきフレームワークは?という観点でLaravelを選んだ。WordPressも同時進行で触ってみる予定。

どのような動きをしたか

色んなワードでググったが、そこには、まごうことなき地獄が広がっていた。QiitaやZennの初心者が導入しました記事、ベテランによるあれやこれやカスタマイズしまくってる(あんま参考にならない)導入事例、Windows向けの丁寧なチュートリアル(一番参考になりそうだったけど、Macと結構違うんですよね・・。導入後は参考にできそうだけど)、はしょりの多い分かりにくい公式、などなど。

とりあえず、phpの環境構築自体が鬱陶しい感じだったので、ならば学んだDockerを使おうかと思いそれで色々見てると、金言発見。

根本的な間違いは「初心者が書いた変なブログを参考にしてること」
Laravelで絶対にやってはいけないことの筆頭。
初心者から見ると「ブログを書いてる人はきっとLaravelに詳しいんだろう」と勘違いするけどそんなことは全くなく「ブログを書いてるのも99%は初心者」
ベテランから見ると間違ったことばかり書いてる初心者で迷惑すぎる存在。

開発環境にDockerを使いたいなら公式のLaravel Sailを使う。これ以外は全部無視していい。
公式ドキュメト通りにlaravel.buildで新規プロジェクトを作ればそのまますぐsailが使えるように全部準備される。
DBが使えるように.envも自動で変更されるので自分で変更するような余計なことはしなくていい。
初心者が間違えるのは変なブログを見てしなくていい余計なことをするから。

引用元: https://teratail.com/questions/r1tal080g333xx

あまりに耳が痛いが、早めにこの回答に出会えて良かったと思う。という訳で公式見ながらLaravel Sailを使ったら、あっさり導入完了。Docker最高。そりゃあさあ、Dockerなしで導入する知識もあった方がいいと思いますよ。でも、とりあえず、LaravelやPHPの概念に触れたいんですよ。衝動なんですよ、これは。

Laravel Sailとは

非常に説明しにくいが、Laravel公式が(Railsと違って)親切にも用意してくれている、「LaravelをDockerで簡単に導入できるようにしてくれる」一連のコマンドのようなものです。フレームワークやライブラリではない。

やった手順

以下、MacでDockerをインストールしている前提で、導入〜ビュー表示までです。Qiitaの素人記事とか玄人記事と違って、「2023/09/06時点の最新版のLaravelの公式ドキュメントにのっとって最低限のことだけやってる」と思います。思い・・ます。

  1. ターミナル開く
  2. cdにて、アプリのフォルダがいっぱい入ってるフォルダに行く(俺の場合はprojects)
  3. curl -s "https://laravel.build/任意のアプリ名" | bash を実行(なんか不安になるよな、このコマンド。引数渡せるのか?って思うし)
  4. 5分くらいかかる
  5. cdで任意のアプリ名に移動
  6. ./vendor/bin/sail up を実行すると、docker compose upとかrails sみたく、以後ログが出るようになるので、それ以降にLaravel関連のコマンドを使う場合はターミナルで別窓を作ったりVSCode上のターミナルを使う
  7. alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail' を実行すると、以後sail 〇〇でphp 〇〇の代わりのコマンドを使えるようになる。すごいね、Docker。すごいね、Sail。ちなみにここまでは全て公式ドキュメントどおり。以下は、公式ドキュメントのMVCのそれぞれの箇所を見てもあまりシンプルな事例を書いてないせいで、他のチュートリアルとかChatGPTとかで調べた。いや、ほんまRailsチュートリアルとかドキュメントと違って、公式ドキュメントって、各項目、のっけから応用しまくりなコードがバンバン出てくるのよ。どうなってんの?Railsが優しすぎるのか?やさしさに包まれすぎてるのか?
  8. sail artisan make:controller PostController でコントローラーを作る。今回はpostという名前にします
  9. sail artisan make:model Post でモデルを作る。今回はデータベースいろわんのであまり意味ないですが。
  10. mkdir resources/views/post と touch resources/views/post/index.blade.php でビューファイルを作ります。ここにビューファイルを置きます。
  11. web.phpに下記コード1を追加(ルーティングの設定です)
  12. PostController.phpのclass PostController extends Controller内にコード2を追加
  13. index.blade.phpをコード3にまるっと書き換え(サンプルなんでなんでもいいですが)
  14. この状態でlocalhost/post に行くと、サンプルページが表示されるはず。

コード1

// use〇〇の下に
use App\Http\Controllers\PostController;

// 最下に
Route::get('/post', [PostController::class, 'index']);

コード2

    public function index()
    {
        return view('post.index');
    }

コード3

<!DOCTYPE html>
<html>
<head>
    <title>Sample Page</title>
</head>
<body>
    <h1>Welcome to the Sample Page</h1>
    <p>This is the sample view for the index action of SampleController.</p>
</body>
</html>

これから

モデルをいごいごしまくるど!おー!!

GitHubのこの世で一番シンプルなシェルスクリプト(add,commit,pushするだけ)

なんすかこれ

仕事だともっと複雑なシェルスクリプトが必要なんだろうけど、一人でいごいごやってるだけの俺のような人間からすると、もっとシンプルなやつが欲しい。という訳でほとんど検索にも引っかからないようなサイトながら、俺もそんなサイトに助けられまくってるので書くぜ〜

前提(俺の環境)

  • MacBookPro2015(Monterey)
  • VSCode
  • ターミナルはzsh(標準)
  • GitHub
  • ブランチ名はmain

手順

  1. アプリのルートフォルダ上にg.shというファイルを作る
  2. そこに下記のコード1をコピペ
  3. cdでアプリのルートフォルダに移動しておく
  4. そこで「./g.sh "コミットメッセージ"」と入力。コミットメッセージは任意のものを入れてください。ちなみに俺は日本語でgと入力すると「./g.sh ""」と変換されるようにしています。
  5. まとめてadd,commit,pushしてくれる

コード1

#!/bin/bash

git add .
git commit -m "$1"
git push origin main

ツッコミ待ち

ご指摘お待ちしています。もちろん必要があれば、もっと別のシェルスクリプトも作ります。ただ、現状こればっかなので。本当はブランチをもっと作るべきなんだろうけど。あとは、2023/08/28現在、CircleCIでのECSデプロイに失敗し続けてるので、コンテナ停止&再起動〜ECSサービス更新の一連の流れをシェルスクリプトにしようと思います。本当はCircleCIでやりたいんです。

Dockerコマンド

起動・終了・削除

compose.ymlを起動

新しい書き方だとハイフンいらんらしいがネットに書いてるやつほとんどハイフンついてる
docker compose up

上記をバックグラウンドで起動(ログが出ない)

docker compose up -d

特定のコンテナのみcompose up

docker-compose up -d service1

上記を再ビルド

docker compose up --build

上記を停止

docker compose stop

特定のコンテナのみcompose stop

docker-compose stop service1

上記を停止し削除

docker compose down

特定のコンテナ停止

docker stop NAMESもしくはコンテナIDの一部
(いずれもdocker psしたら分かる)

すべてのコンテナを停止

docker stop $(docker ps -q)

特定のコンテナ削除

docker rm NAMESもしくはコンテナIDの一部

すべてのコンテナを削除

docker rm $(docker ps -q)

特定のイメージを削除

docker image rm <イメージ名またはイメージID>
もしくは
docker rmi <イメージ名またはイメージID>

全てのイメージを削除

docker image prune -a



状態把握

コンテナの状況を見る(起動中のものだけを対象)

docker ps

コンテナの状況を見る(すべて)

docker ps -a

イメージ見る

docker images

ログを見る(直近が出てくる)

docker compose logs

ログを見る(流し続ける)

docker compose logs -f



コンテナの中でいごいごする

コンテナ内でコマンドラインを起動

docker exec -it <コンテナ名またはコンテナID> bashとか