- 投稿日:2022-01-31T23:35:58+09:00
dockerのキホン
こんな人に読んでほしい 自分(備忘録) docker初心者 dockerを触ったことがあるけど、いまいちイメージが掴めていない人 dockerとは 簡単にいうと dockerとはなんなのか。簡単にいうと、パソコンの中にあれこれインストールしなくても環境構築できる便利なツールです。dockerなどで環境構築したことがない時に、プログラムを作ろうとして記事をググると、ターミナルを開いて(mac視点)、いろいろコマンドをポチポチするようにって記事よく見ますよね。 でも、それって後で何をいれたか分からなくなるし、なんかうまくいかないことってよくありますよね。環境構築は初学者の最初の壁です。その壁を壊すのがdockerです。 メリット ファイルの受け渡しで環境構築できるので、人的ミスが起こらない。 スクラップアンドビルドが容易にできる。 コマンドをいろいろポチポチしてたら、打ち忘れとか打ちミスとかありますよね? でも、dockerだとファイルを読み込むだけのなので人的ミスはかなり起こる可能性が減ります。 そしてスクラップアンドビルドも魅力的な要素です。例えばdbが壊れてマイグレーションとかが通らないことがよくあります。開発環境ならdbを作り直したほうが楽なことはよくあります。そういった場合にとても便利です。 もう少し踏み込んで dockerはコンテナ仮想化です。そして、VirtualBoxなどの仮想マシンとよく比較されます。 仮想マシンでは、ホストマシン上でハイパーバイザを利用しゲストOSを動かし、その上でミドルウェアなどを動かします。要するにPCの上に別のPCがあるような状態です。めちゃくちゃ重いです。個人的にはもうVirtualBox使いたくないです。 それに対し、コンテナ仮想化は、ホストマシンのカーネルを利用し、プロセスやユーザなどを隔離することで、あたかも別のマシンが動いているかのように動かすことができます。そのため、軽量で高速に起動、停止などが可能です。要するにそのPCの1プロセスとして動くので、PCが乱立してる形にはならないです。 補足しておくと、dockerはwindowsだろうがmacだろうが同じコマンドで動きます。このカラクリは内部でDockerデーモンというものが動いているからです。これはLinuxのプロセスでこいつが直接、プロセスを処理しているからです。 dockerの使い方 dockerのライフサイクル
- 投稿日:2022-01-31T21:21:26+09:00
ローカル環境 KMS で暗号鍵を固定化する (LocalStack)
AWS Key Management Service (KMS)は暗号鍵の生成・管理と、アプリケーションデータの暗号化・復号を提供してくれるサービスです。KMS を利用することで、暗号鍵ファイルを直接扱うリスクを回避することでできます。 以前の記事で紹介した LocalStack Docker コンテナで KMS モックサーバを実行し KMS を利用するアプリケーションを開発した際、他の開発メンバーの環境で暗号化したテストデータを復号できるよう、鍵ファイルを固定する必要があり、事前に作成した暗号鍵ファイルをインポートした KMS キーを生成するよう自動化しました。 その手順を共有します。 Docker compose file 設定 docker-compose.yml version: '3.8' services: localstack: container_name: localaws-container-localstack image: localstack/localstack:0.13.3 platform: linux/amd64 environment: SERVICES: kms EDGE_PORT: 4566 INIT_SCRIPTS_PATH: /docker-entrypoint-initaws.d DEFAULT_REGION: 'ap-northeast-1' KMS_PROVIDER: 'local-kms' ports: - '4566:4566' volumes: - ./init:/docker-entrypoint-initaws.d:ro - ./data:/data:rw 環境変数 SERVICES に kms を追加 環境変数 KMS_PROVIDER に local-kms 設定 初期化スクリプトを配置する init ディレクトリを /docker-entrypoint-initaws.d にマウント インポートする鍵ファイルを配置する data ディレクトリを /data にマウント ※Apple Silicon (M1)で実行する場合、通常 ARM 向けイメージが使用されますが、ARM 向けイメージでは本手順が実行できない不具合があるため platform: linux/amd64 で AMD64 用イメージを実行しています インポートする鍵ファイルを作成 以下のコマンドで生成した PlaintextKeyMaterial.b64 を data ディレクトリに配置 openssl rand -base64 -out PlaintextKeyMaterial.b64 32 docker-compose.ymlや初期化スクリプトとともに、このファイルをGitリポジトリにコミットし、開発メンバー間で共有 初期化スクリプト 以下のようなスクリプトを init ディレクトリに配置 init/kms.sh #!/bin/bash # JSONファイルを扱うために jq をインストール apt update -y && apt install jq -y cd /data # 暗号鍵をバイナリファイルに変換 openssl enc -d -base64 -A -in PlaintextKeyMaterial.b64 -out PlaintextKeyMaterial.bin # KMSキーを生成しKeyId取得 awslocal kms create-key --origin EXTERNAL > create-key-output.json KeyId=`jq -r '.KeyMetadata.KeyId' create-key-output.json` # PublicKeyとImportTokenを取得 awslocal kms get-parameters-for-import \ --key-id ${KeyId} \ --wrapping-algorithm RSAES_OAEP_SHA_1 \ --wrapping-key-spec RSA_2048 \ > get-parameters-for-import-output.json jq -r '.PublicKey' get-parameters-for-import-output.json > PublicKey.b64 openssl enc -d -base64 -A -in PublicKey.b64 -out PublicKey.bin jq -r '.ImportToken' get-parameters-for-import-output.json > ImportToken.b64 openssl enc -d -base64 -A -in ImportToken.b64 -out ImportToken.bin # 暗号鍵をインポート用に暗号化 openssl rsautl -encrypt \ -in PlaintextKeyMaterial.bin \ -oaep \ -inkey PublicKey.bin \ -keyform DER \ -pubin \ -out EncryptedKeyMaterial.bin # 暗号化した鍵マテリアルをインポート awslocal kms import-key-material \ --key-id ${KeyId} \ --encrypted-key-material fileb://EncryptedKeyMaterial.bin \ --import-token fileb://ImportToken.bin \ --expiration-model KEY_MATERIAL_DOES_NOT_EXPIRE # KMSキーのエイリアス awslocal kms create-alias --target-key-id ${KeyId} --alias-name 'alias/local-kms-key' # KMSキーの情報を確認 awslocal kms describe-key --key-id 'alias/local-kms-key' docker compose up でコンテナを起動すると シェルスクリプトが自動的に実行され、KMS キーを生成し鍵ファイルをインポートします KMS キー ID は毎回変わりますが、アプリケーションからはエイリアス 'alias/local-kms-key' を指定して利用できます 暗号化 aws kms encrypt --key-id 'alias/local-kms-key' --plaintext 'プレインテキスト' --endpoint http://localhost:4566 --region ap-northeast-1 復号 aws kms decrypt --key-id 'alias/local-kms-key' --ciphertext-blob '暗号化文字列(base64)' --endpoint http://localhost:4566 --region ap-northeast-1 参考資料
- 投稿日:2022-01-31T11:56:22+09:00
Linux on Power (ppc64le)をPCでお試し (binfmt_misc / qemu-user-static使用)
本記事ではIBM Powerサーバー上でのLinux、Linux on Powerをお手軽にPC上で動作させたい 場合のとても簡単なお試し方法を記載します。 Linux on Powerの評価はIBM Powerサーバー実機を用意して評価するのが一番なのですが、 実機での評価が難しい場合は、下記の方法でPCでのお試しが可能です。 Intelサーバー上でのプログラムがIBM Power上で問題なく動くか簡単なプログラムを試したい IBM Powerに対応したマルチアーキテクチャー対応のプログラムを作成したい IBM Powerに対応したマルチアーキテクチャー対応のコンテナを作成したい などといった用途でのお試しであればPC上での動作も可能です。 パフォーマンスを測定したい PCでは動作が難しい複数CPU / 大容量メモリー / 大容量ストレージ / 高速ネットワークを使用したプログラムを動かしたい 複数VMを連携させたい IBM Powerで動作するLinux以外のOS、AIX / IBM iを試したい といった場合は実機が必要になってしまうかと思います。。 今回はなるべくお手軽に試すためにDockerもしくはPodmanでのコンテナ環境を使用した方法となります。 Macでお試し Docker DesktopかPodmanを使用します。 Docker Desktopの場合 下記からDocker Desktopを導入後、以下でコンテナを立ち上げればOKです。 https://www.docker.com/products/docker-desktop docker run --rm -it --platform linux/ppc64le ubuntu:20.04 /bin/bash 上記はUbuntu 20.04のコンテナを使用する例です。 これだけでLinux on Power環境ができちゃいます 試しにこのコンテナ上でuname -mを実行してみると # uname -m ppc64le となりPowerアーキテクチャー環境になっていることがわかります。 Podmanの場合 Docker Desktopがライセンス上使えないといった場合はPodmanも使用できます。 下記の手順でPodmanを導入後、以下でコンテナを立ち上げればOKです。 https://podman.io/getting-started/installation#macos podman machine ssh sudo rpm-ostree install qemu-user-static podman machine ssh sudo systemctl reboot podman run --rm -it --platform linux/ppc64le ubuntu:20.04 /bin/bash Windowsでお試し WSL2環境でDockerもしくはPodmanで使用可能です。 下記はWSL2のディストリビューションにubuntu20.04を選択した場合のDocker環境でコンテナを立ち上げる例です。 sudo apt update sudo apt install qemu-user-static docker run --rm -it --platform linux/ppc64le ubuntu:20.04 /bin/bash Linuxでお試し Ubuntu環境であれば、上記WSL2環境と同じ手順で使用可能です。 その他の比較的新しい環境であれば、下記のqemu-user-staticのページにある手順で以下のようにコンテナで使用できるはずです。 https://github.com/multiarch/qemu-user-static docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker run --rm -it --platform linux/ppc64le ubuntu:20.04 /bin/bash 少し古いDockerの環境、例えばRHEL7/CentOS7のDocker環境だと、以下のような手順となります。 docker run --rm --privileged multiarch/qemu-user-static --reset wget https://github.com/multiarch/qemu-user-static/releases/download/v6.1.0-8/x86_64_qemu-ppc64le-static.tar.gz tar xzf x86_64_qemu-ppc64le-static.tar.gz sudo cp qemu-ppc64le-static /usr/bin docker run --rm -it -v /usr/bin/qemu-ppc64le-static:/usr/bin/qemu-ppc64le-static ppc64le/ubuntu:20.04 /bin/bash 上記のようにMac / Windows / Linuxのどの場合でも数コマンドでLinux on Powerのコンテナ 環境を作成することが可能です。 ちなみに同様の方法でIBM Zのコンテナも動作させることができると思います。 ARMのコンテナも動作させることが同様にでき、こちらは既にQiitaで多く記事化されているようです。 Linux on Powerのコンテナが動作する仕組み Linux on Powerのコンテナが動作する仕組みについても補足として記載しておきます。 Mac / Windows / Linuxのどの環境でも基本Linuxが動作しており (MacはHyperKitやQEMU+HVFのVM上 / Windows WSL2はHyper-VのVM上)、そのLinux上でbinfmt_miscおよびqemu-user-staticを使用して異なるアーキテクチャーのLinuxユーザープログラムを動かしています。 binfmt_miscとは? Linux kernelの機能でoffset/magic/maskとインタープリターのセットをkernelに教えておくことで特定の実行ファイルフォーマットを指定のインタープリターで動作させる仕組みです。 https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html Linux on Powerの動作のエントリは以下になります。 # cat /proc/sys/fs/binfmt_misc/qemu-ppc64le enabled interpreter /usr/bin/qemu-ppc64le-static flags: offset 0 magic 7f454c4602010100000000000000000002001500 mask ffffffffffffff00fffffffffffffffffeffff00 このエントリによって、このoffset/magic/maskのバイナリの実行は/usr/bin/qemu-ppc64le-staticに実行させてね、と指定しているわけです。 qemu-user-staticとは? その名の通りQEMUのUser Mode Emulationのstatic link版です。 https://github.com/multiarch/qemu-user-static QEMUはオープンソースのマシンエミュレーター・仮想化エンジンです。 QEMUはOS全体をエミュレーションするSystem Emulationとユーザープログラム部分のみをエミュレーションするUser Mode Emulationがあります。 LinuxのUser Mode Emulationにはkernelはエミュレーションせずに、ユーザープログラムのみをエミュレーションし、system callやPOSIX signalをうまく変換する仕組みが入っています。kernel部分はエミュレーションしないため、kernel部分のマシン固有の機能確認はできませんが、ユーザープログラムはより高速な実行が可能となります。 前述のqemu-ppc64le-staticがQEMUのUser Mode Emulationのppc64le版となっています。 コンテナ環境ではこの2つがうまく連携して動作することにより異なるアーキテクチャーのユーザープログラムのエミュレーションを行っています。 コンテナ内でppc64leバイナリが呼ばれる。 kernelに処理が移りbinfmt_miscによりppc64leバイナリが検知されてqemu-ppc64le-static経由でppc64leが実行される。 qemu-ppc64le-staticによりppc64leバイナリのUser Mode Emulationがなされる。 この際kernelはMac/WindowsはVM上のものが、Linuxはnative上のものがそのまま使用されます。そのため、コンテナ上でcat /proc/cpuinfoなどしても実際のマシンもしくはVMのCPU情報が表示されます。 また、ppc64leバイナリが呼ばれた際に使用されるqemu-ppc64le-staticはコンテナ空間のものとなりますが、Linux kernelでのbinfmt_misc設定時のbinaryを使用するfix-binaryオプションを使用することでコンテナ空間にqemu-ppc64le-staticがなくとも実行が可能となっています (kernel 4.8以降のオプションとなります)。 以上、PCにてLinux on Powerを簡単に試す方法について紹介させていただきました。 より多くのプラットフォームで自身のユーザープログラムを使ってもらいたい IBMや他のIBM Powerユーザーに自身のユーザープログラムのLinux on Power対応を依頼された などありましたらぜひ試してみてはいかがでしょうか?
- 投稿日:2022-01-31T09:08:45+09:00
Docker + Next.jsでAPIを叩く時のプチハマり
以前構築した Docker + Nginx + Node.js環境でAPIを叩く時にプチハマりしたことを書きます。 環境は↓ やりたいこと getServerSideProps内とNextPage内それぞれでAPIを叩きたい。 やったこと NextPage内 エンドポイントがlocalhost:8080/api/get-hogeであるとする。 const res = await fetch('http://localhost:8080/api/hoge', { method: 'GET', // 略 }); こちらはうまくいきました。が、 getServerSideProps内 const res = await fetch('http://localhost:8080/api/hoge', { method: 'GET', // 略 }); こっちは Server Error / Error: connect ECONNREFFUSED ~~って出ちゃいました 調べていると、クライアントサイドとサーバーサイドで叩くドメインを変えないいけないようなので、 const res = await fetch('http://host.docker.internal:8080/api/hoge', { method: 'GET', // 略 }); これでできました。 参考
- 投稿日:2022-01-31T08:42:42+09:00
Laravel+React&PKCEのSPA環境構築メモ
先日書いた Laravel+Vue&PKCEのSPA環境構築メモ のReact版も作成してみました。 ※ Laravel側は全く同じなので割愛し、見出しを合わせて差分のところだけ記載しています https://github.com/pei-miyapei/laravel-react-spa (こちらは簡単なAPIの実装とUIフレームワーク(Ant Design)を導入した状態です) 構成 API server コンテナ(SPA バックエンド、ユーザー管理) (割愛) SPA クライアントコンテナ(SPA フロントエンド) Vite React 17 TypeScript react-router js-pkce React+Vite+TypeScript環境の構築 Vue版との違いは、react-tsのテンプレートを使用しているというだけです。 あとはおなじ。 bash yarn yarn create vite temp --template react-ts vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], server: { host: true, port: 3000, // https: true, }, }); SPA側の実装 react-router, js-pkceをインストール bash yarn add react-router-dom@6 js-pkce ルーティングの設定 client/src/routes/Router.tsx import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { About } from '../views/About'; import { AuthorizationCallback } from '../views/Auth/AuthorizationCallback'; import { Home } from '../views/Home'; export const Router = () => { return ( <BrowserRouter> <Routes> <Route path='/' element={<Home />} /> <Route path='/about' element={<About />} /> </Routes> </BrowserRouter> ); }; App.tsxのコンテンツはreact-routerの中身(<router>)のみにしておきます ※ Vue版とファイル構成も合わせているだけで、別にApp.tsxを使う必要はありません client/src/App.tsx import './App.css'; import { Router } from './routes/Router'; function App() { return <Router />; } export default App; MasterPage.tsx(Layout)を追加しておく 未認証時に丸ごと非表示にしたいため、 別途レイアウト用のVueを作成してその中にコンテンツページを表示するようにしておきます。 Vue版ではLayout.vueになっています。 ※ ant-designを入れた際にLayoutというコンポーネント名が被ったため… client/src/views/MasterPage.tsx import { Link, Outlet } from 'react-router-dom'; export const MasterPage = () => { return ( <div id="nav"> <Link to='/'>Home</Link> | <Link to='/about'>About</Link> </div> <Outlet /> ); }; このコンポーネントの入れ子のルーティングは以下のように書けます client/src/routes/Router.tsx import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { About } from '../views/About'; import { Home } from '../views/Home'; import { MasterPage } from '../views/MasterPage'; export const Router = () => { return ( <BrowserRouter> <Routes> <Route path='/' element={<MasterPage />}> <Route path='/' element={<Home />} /> <Route path='/about' element={<About />} /> </Route> </Routes> </BrowserRouter> ); }; ネストされたルートについての詳細はこちら https://reactrouter.com/docs/en/v6/getting-started/tutorial#nested-routes トークンを保持する入れ物 これに関してはCookieなどに保存する場合は不要です。 ここでは変数内に持っているだけという状態で実装するため、その入れ物です。 (タブを閉じたら消えます) Vue版ではProvide/Injectという機能を使用しました。 Reactにも同様のContextという機能があり、これを使用します。 client/src/store/AuthContext.tsx import { createContext, useContext, useState } from 'react'; class AuthTokens { constructor(public accessToken = '', public refreshToken = '') {} } // ProviderProps const authProps = ( tokens = new AuthTokens(), handleSetTokens = (accessToken = '', refreshToken = '') => {} ) => { const hasToken = () => tokens.accessToken !== ''; return { tokens, handleSetTokens, hasToken }; }; export type AuthProps = ReturnType<typeof authProps>; // Context let AuthContext = createContext(authProps()); // Provider export const AuthProvider = ({ children }: any) => { const [tokens, setTokens] = useState(new AuthTokens()); const handleSetTokens = (accessToken = '', refreshToken = '') => { setTokens({ ...tokens, ...new AuthTokens(accessToken, refreshToken) }); }; const props = authProps(tokens, handleSetTokens); return <AuthContext.Provider value={props}>{children}</AuthContext.Provider>; }; // Consumer export const useAuthContext = () => useContext(AuthContext); Vue版だと上記をプラグインの機能を使用してアプリ全体に適用しました。 ReactではProviderの子要素で使用することができるので、こちらも最上位から適用します client/src/main.tsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { AuthProvider } from './store/AuthContext'; // ← 追記 ReactDOM.render( <React.StrictMode> <AuthProvider> {/* ← 追記 */} <App /> </AuthProvider> </React.StrictMode>, document.getElementById('root') ); ページの作成 残りの トークンチェック&認可リクエストするガワページ 認可コードコールバック用ページ を作成します。 トークンチェック&認可リクエストするガワページ 認可が必要なページにかませて使用 トークンがなければ認可リクエストを発行(if ~) トークンがなければデフォルトスロットの中身(コンテンツ)の描画を行わない(v-if) トークンは先ほど作成した、プラグインでprovideされた入れ物を召喚して確認(injectAuth) というコンポーネント client/src/components/Auth/AuthGuard.tsx import PKCE from 'js-pkce'; import { useAuthContext } from '../../store/AuthContext'; export const AuthGuard = ({ children }: any) => { const { hasToken } = useAuthContext(); if (!hasToken()) { const pkce = new PKCE({ client_id: '1', // `php artisan passport:client --public` したときのIDです redirect_uri: location.origin + '/auth/callback', // 戻ってくるURL authorization_endpoint: 'http://localhost/server/oauth/authorize', // Laravel側の認可エンドポイント requested_scopes: '*', }); location.replace(pkce.authorizeUrl()); return <></>; } else { return <>{children}</>; } }; を作成。 認証が必要なページにこのコンポーネントを導入します。 今回はコンテンツページは全部認証が必要なページとして、 最初に作成したグローバルメニューを持ったMasterPage.tsxに仕込みます client/src/views/MasterPage.tsx import { Link, Outlet } from 'react-router-dom'; import { AuthGuard } from '../components/Auth/AuthGuard'; export const MasterPage = () => { return ( <AuthGuard> <div id="nav"> <Link to='/'>Home</Link> | <Link to='/about'>About</Link> </div> <Outlet /> </AuthGuard> ); }; 認可コードコールバック用ページ Laravelで認証後戻ってくるページ。受け取った認可コードとトークンを交換します。 Laravel側で指定した /auth/callback のページになります。 js-pkceのexchangeForAccessTokenというメソッドでトークンを受け取れます。 それを用意した入れ物(injectAuth)に保存(auth.setToken)し、 ここでは再度トップ画面に戻ります client/src/views/Auth/AuthorizationCallback.tsx import PKCE from 'js-pkce'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuthContext } from '../../store/AuthContext'; export const AuthorizationCallback = () => { const pkce = new PKCE({ client_id: '1', redirect_uri: location.origin + '/auth/callback', token_endpoint: 'http://localhost/server/oauth/token', }); const { handleSetTokens } = useAuthContext(); const navigate = useNavigate(); useEffect(() => { pkce.exchangeForAccessToken(document.location.href).then((response) => { handleSetTokens(response.access_token, response.refresh_token); // 認証後に遷移するページへ navigate('/', { replace: true }); }); }, []); return <></>; }; このページを /auth/callback のURLで公開します client/src/routes/Router.tsx import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { About } from '../views/About'; import { AuthorizationCallback } from '../views/Auth/AuthorizationCallback'; // ← 追加 import { Home } from '../views/Home'; import { MasterPage } from '../views/MasterPage'; export const Router = () => { return ( <BrowserRouter> <Routes> <Route path='/auth/callback' element={<AuthorizationCallback />} /> {/* ← 追加 */} <Route path='/' element={<MasterPage />}> <Route path='/' element={<Home />} /> <Route path='/about' element={<About />} /> </Route> </Routes> </BrowserRouter> ); }; 一応完了 というわけでとりあえずこれでVue版同様、 React側にアクセスするとトークンがないためLaravelに遷移、 認証・承認後トークンをゲットして、ページがみられるようになると思います。 (これはAntDesignなどが入ってるので見た目違うと思いますが…) 締め Reactはまだ全然でして… おかしな点などがありましたらこっそりご教示ください…orz
- 投稿日:2022-01-31T08:28:14+09:00
Dockerでnginx + PHP(Laravel)+ MySQLのLEMP環境を構築する
概要 Docker, Docker Compose を使って、nginx + PHP(Laravel)+ MySQLのLEMP環境を構築する記事です。 検索するとDockerfileやdocker-compose.ymlの書き方については色んな方が記事を書いてくださっていて、動く環境を作ること自体は難しくありませんでした。 ですが、筆者はひとつコンテナを作っては動作確認していくという過程を経てすごく理解が深まったなと思うので、ファイルの書き方だけでなく動作確認したことやその結果を含めて記事に残しておきたいと思います。 全体像 最終的なディレクトリ構成は以下の通りです。 全コンテナを管理するdocker-compose.ymlがトップレベルにあり、同じくトップレベルにあるdockerディレクトリ配下に各コンテナのDockerfileや設定ファイルを置いています。 tree docker_sample/ ├── src // Laravelプロジェクトのソースコード ├── docker │ ├── app // PHPコンテナ │ │ ├── Dockerfile │ │ └── php.ini │ ├── db // MySQLコンテナ │ │ ├── Dockerfile │ │ └── my.cnf │ └── web // nginxコンテナ │ ├── Dockerfile │ └── default.conf └── docker-compose.yml // 全コンテナの管理 早速一つずつコンテナを作っていきます。 PHPのコンテナ(Laravelの開発環境) まずはLaravelの開発環境を構築するための、PHPのコンテナを作成します。 docker-compose.ymlへの記述 PHPコンテナ用のDockerfile PHPコンテナ用の設定ファイル(php.ini) について順に説明します。 docker-compose.yml(PHP) docker-compose.ymlのうち、PHPコンテナについての記述は以下の通りです。 ※docker-compose.ymlはインデントが意味を持つので注意。 docker-compose.yml version: "3.9" services: app: // サービス名 build: context: . dockerfile: ./docker/app/Dockerfile volumes: - ./src/:/app version: これはPHPコンテナについてではなく docker-compose.yml の先頭に書く設定ですが、Composeファイルのバージョンを表しています。 今回は最新の 3.9 を使います。(最新のバージョンは下記の公式サイトを参照) Compose file build: ビルドコンテキストを指定します。 context:ビルドコンテキスト(buildを実行する場所)の設定 dockerfile:buildするDockerfileまでのパス(docker-compose.ymlから見た相対パス) 参考記事: docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話 - Qiita volumes: ホスト側の ./src/をコンテナ側の /app にマウントするという意味になります。 ※ここで services: の中に書いているこの volumes: は バインドマウント を行っています。 参考記事:Dockerのマウント3種類についてわかったことをまとめる - Qiita 何が起きているかは実際の挙動を見てみた方が分かりやすいと思うので、後ほどまた動作確認します。 Dockerfile(PHP) Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに書きます。 まず作成するDockerfileの全文がこちらです。 /docker/app/Dockerfile FROM php:8.0-fpm ENV TZ Asia/Tokyo RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./docker/app/php.ini /usr/local/etc/php/php.ini COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer WORKDIR /app 一つずつ説明します。 FROM php:8.0-fpm FROMではイメージをビルドするためのベースイメージを設定します。 書き方は FROM イメージ名:タグ名 です。 ここではDocker HubからPHP公式のイメージをベースとして指定しています。 Php - Official Image | Docker Hub ENV TZ Asia/Tokyo ENVはコンテナ内のサーバー環境変数を設定します。 RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath RUN にはコンテナビルド時に実行するコマンドを書きます。 && で複数のコマンドをつなぎ、 \ で改行します。 ※ && と \ を使うことで複数コマンドを1レイヤーにまとめることができ、公式でもベストプラクティスのTipsとして挙げられています。 Intro Guide to Dockerfile Best Practices - Docker Blog apt-get update :インストール可能なパッケージの「一覧」を更新 apt-get -y install:パッケージをインストール docker-php-ext-install:PHPの拡張ライブラリをインストール ※Laravelのインストールや開発に必要な(もしくは便利な)パッケージや拡張ライブラリをインストールしています。 ※試しにこれらを全くインストールせず手順を進めてみると、コンテナを起動することはできますが、その後のLaravelプロジェクトを作成する時に大量のエラーに遭遇しました(笑) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini ローカルで(後ほど)作成する php.ini (PHPの設定ファイル)をDockerコンテナ内にコピーします。 書き方は COPY [ローカル側のパス] [コンテナ側のパス]です。 ※ローカル側のパスは、Dockerfileから見てではなくbuildコマンドを実行するディレクトリから見た相対パスです。今回はDocker composeを使ってイメージビルドを行うので、docker-compose.ymlから見た相対パスになっています。 COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer Laravelを使うためComposerをインストールします。 この書き方によりマルチステージビルドという方法でインストールされます。 ※マルチステージビルドという方法はイメージの軽量化に役立つようです。 公式ドキュメント:Use multi-stage builds WORKDIR /app コンテナを起動している時に $ docker-compose exec コンテナ名 bash というコマンドを実行すると、コンテナの中でbashを実行することができるのですが、 WORKDIRはその時のカレントディレクトリを指定しています。 設定ファイル(php.ini) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini で出てきたPHPの設定ファイル php.iniを作成します。 作成したファイルの中身がこちらです。 /docker//app/php.ini zend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanese こちらの記事の開発用php.iniをまるっとお借りしました。 開発用 / 本番用の設定例と、項目ごとの説明も載っていて面白かったので、ぜひご覧ください。 参考記事:PHP7.4 ぼくのかんがえたさいきょうのphp.ini - Qiita PHPコンテナを起動する ここまででPHPのコンテナの準備が出来たので、実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build docker compose up は docker-compose.yml に定義したサービスを起動します。 -d 「デタッチド」モードでコンテナを起動します。 (デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示する。「デタッチド」モードではバックグラウンドで動作する。) -build コンテナの開始前にイメージをビルドします。 (特に変更がない場合はキャッシュが使用される。) ※ $ docker-compose build → $ docker-compose up -d を順に行うのと同じです。 起動したコンテナを確認する 以下のコマンドで起動中のコンテナを一覧で確認することができます。 $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp STATUSがrunningになっていれば正常に起動しています。 今はPHPのコンテナしか作っていないので1つだけ表示されていますが、この後nginx, MySQLのコンテナを作り、最終的に3つのコンテナが表示されるようにします。 ここで今作業しているディレクトリの構成を確認してみます。 $ tree . ├── src // 作成された! ├── docker │ ├── app │ │ ├── Dockerfile │ │ └── php.ini └── docker-compose.yml するとdocker-compose.ymlに書いたバインドマウントの以下の部分を受けて、コンテナを起動したときに自動でsrcディレクトリが作成されたことが確認できます。(同様にコンテナ内にもappディレクトリが作成されています。) volumes: - ./src/:/app コンテナの中に入ってみる コンテナを起動中に以下のコマンドを実行すると、コンテナの中に入ってbashを実行することができます。 $ docker-compose exec app bash // appの部分はサービス名を指定する これで今コンテナの中に入れたので、Dockerfileの記述通りにコンテナが作られているか、またPHP・Composer・インストールした拡張機能が使えるか確認していきます。 // Dockerfileの「WORKDIR /app」で指定したカレントディレクトリ通りか確認 [app]:/app$ pwd /app // PHPのバージョン確認 [app]:/app$ php -v PHP 8.0.15 (cli) (built: Jan 26 2022 17:38:36) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.15, Copyright (c) Zend Technologies // Composerのバージョン確認 [app]:/app$ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.0.14 2021-05-21 17:03:37 // gitのバージョン確認 [app]:/app$ git --version git version 2.30.2 // インストール済の拡張機能の一覧 [app]:/app$ php -m [PHP Modules] bcmath intl pdo_mysql zip // たくさん出てくるので他は省略 // php.iniがコピー出来ているか確認 [app]:/app$ cat /usr/local/etc/php/php.ini zend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japaneseroot@0e3ba825df88 確認できたのでコンテナを抜けます。 $ exit // もしくは ctrl + d コンテナの外からコマンドを実行する 上記のコンテナの中で実行したコマンドは、 $ docker-compose exec サービス名 実行したいコマンド でコンテナの外から実行することもできます。(コンテナが起動中に限る) バインドマウントの挙動を確認する コンテナの外からコマンドが実行できるという確認も兼ねて、バインドマウントの動きを見る為、試しに以下のコマンドを実行してみます。 // コンテナ内の/app配下にファイルを作ってみる $ docker-compose exec app touch sample.php $ docker-compose exec app pwd /app $ docker-compose exec app ls sample.php コンテナ内の/app配下にファイルを作成しました。 この/appという場所は、docker-compose.ymlに書いた以下の記述の通りにバインドマウントされています。 volumes: - ./src/:/app これにより、プロジェクトディレクトリ配下の/srcにもsample.phpが作成されています。 $ ls src sample.php 以上より、コンテナ内の /app に対して行ったことが、プロジェクトの /src に反映していることが分かりました。 では次に /src への変更がコンテナ内の /app に対しても反映するか試してみます。 /src/sample.php をエディタで開いてファイルに hello と書きこみ、以下のコマンドを実行します。 $ docker-compose exec app cat sample.php hello // この時permission errorが出たら権限を変更する $ sudo chmod -R 777 ./src するとコンテナ内の ./app/sample.php も編集されていることがわかります。 このようにバインドマウントにより、ホスト側のディレクトリがコンテナ内へマウント出来ていることも確認できました。 試しに作成したファイルは不要なので消しておきます。 $ rm src/sample.php nginxのコンテナ(webサーバー) webサーバーとなるnginxのコンテナを作成します。 PHPコンテナのときと同じく、 docker-compose.ymlへの記述 nginxコンテナ用のDockerfile nginxコンテナ用の設定ファイル(default.conf) の流れで説明します。 docker-compose.yml(nginx) 既に作成しているdocker-compose.ymlに、nginxのコンテナについての部分を追記します。 docker-compose.yml services: api: // 中略 web: // サービス名 build: context: . dockerfile: ./docker/web/Dockerfile ports: - 8081:80 depends_on: - app volumes: - ./src/:/app 解説していきます。 build: ・volumes: ここはPHPコンテナと同様なので説明は省きます。 ports: ホスト側とコンテナ間のポート番号の対応付けを設定します。 書き方は ホスト側のポート番号 : コンテナのポート番号です。 ※今回ホスト側(自分のPC)は既に他の開発で使っているポートとの兼ね合いで 8081 を使いました。コンテナ側はnginxのデフォルトのポート番号である 80 にしています。 depends_on: サービスの起動順序を制御します。 web の depends_on に app と書いているので、 app → web の順に起動するように指定しています。 ※但しこの記述なしで $ docker-compose up -d --build をしてみても私の環境では全く問題なく動きました。が、サービス同士の依存関係を明示的に記すという意味でも書いておくに越したことはないという判断で書いています。 ※ nginxとphp間でTCPによるfpm接続についてや、depends_on オプションについては以下の記事がすごく勉強になったので是非読んでみてください。 参考記事:【docker-compose】depends_onとサービス名解決にまつわるエトセトラ - Qiita Dockerfile(nginx) 作成するDockerfileの全文はこちらです。 /docker/web/Dockerfile FROM nginx:1.20-alpine ENV TZ Asia/Tokyo COPY ./docker/web/default.conf /etc/nginx/conf.d/default.conf FROM , ENV , COPY の意味はPHPコンテナのDockerfileと同様なので詳しい説明は省略し、このファイルで設定していることを簡単にまとめます。 nginx公式のイメージ(Alpineベース)をベースイメージに使用 Nginx - Official Image | Docker Hub 参考記事:Dockerでよく利用されているAlpineは他のLinuxディストリビューションと比べて、どれだけ軽量なのか - プログラミングは芸術だ! 環境変数のタイムゾーンを設定する nginxの設定ファイル(default.conf)をコンテナ内にバインドマウント ※ちなみにnginxは1.18, 1.20などの偶数バージョンが安定バージョンであり、安定バージョンの使用を推奨されています。 公式ドキュメント:Installing NGINX Open Source 設定ファイル(default.conf) ./docker/web/default.confを作成します。 Laravel公式に用意されているnginxの設定例をべースに使います。 Laravel 8.x デプロイ root とfastcgi_passの設定のみ、このプロジェクトに合わせて書き換えています。 /docker/web/default.conf server { listen 80; server_name example.com; root /app/public; // 書き換え add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass app:9000; // 書き換え fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } root リクエストのルートディレクトリです。 root /app/public; と書いたので、 localhost:8081(docker-compose.ymlで設定したポート番号)にアクセスすると /app/publicを見に行きます。 fastcgi_pass FastCGIサーバーのアドレスです。 ※FastCGI:Webサーバ上で動くプログラムを一度起動したらしばらく待機させることによって、プログラムの開始と終了にかかる手間を減らし、動きを速くしたりWebサーバの負荷を軽減することができる仕組み。 app:9000; と書いたので、appコンテナの9000番ポートを指定しています。 nginxコンテナを起動する nginxのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)の2つが起動できました。 またdocker_sample-web-1の PORTS が 0.0.0.0:8081->80/tcp となっており、ホスト上の8081番ポートをコンテナの80番ポートへ割り当てられていることも確認できます。 コンテナの動作確認 nginxのバージョンを確認します。 $ docker-compose exec web nginx -v nginx version: nginx/1.20.2 コンテナの中に入ってみます。 $ docker-compose exec web bash OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH: unknown するとエラーになりました。 Alpineをベースとすると bash は使えず、 ash や sh は使えるようです。 $ docker-compose exec web ash [web]:/ $ pwd / 無事コンテナの中に入れました。 webサーバーとしての動作確認 リクエストに対してファイルを返しブラウザで表示できる nginxのコンテナからPHPのコンテナへphpを実行させることができる 上記2点を確認します。 $ mkdir src/public $ touch src/public/test.php test.phpを以下のように編集します。 /src/public/test.php <?php echo 'test.phpです'; phpinfo(); http://localhost:8081/test.php にアクセスすると以下のように表示され、webサーバーが正しく動作していることが確認できます。 試しに作成したファイルは不要なので消しておきます。 $ rm -rf src/* MySQLのコンテナ(データベース) データベースのMySQLコンテナを作成します。 以下の流れで説明します。 docker-compose.ymlへの記述 MySQLコンテナ用のDockerfile MySQLコンテナ用の設定ファイル(my.conf) docker-compose.yml(MySQL) 既に作成しているdocker-compose.ymlに、MySQLのコンテナについての部分を追記します。 docker-compose.yml services: // 中略 db: // サービス名 build: context: . dockerfile: ./docker/db/Dockerfile ports: - 3306:3306 environment: MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password TZ: 'Asia/Tokyo' volumes: - mysql-volume:/var/lib/mysql volumes: mysql-volume: build: ・ ports: 既出の通りです。 enviroment: 環境変数の設定です。 ※名前やDB名・ユーザー名・パスワードは好きなものを設定します。 ※実際のプロジェクト管理では、環境変数は .env に書いて .gitignore にするなどして、重要な情報が公開されないようにします。 MYSQL_DATABASE:DB名 MYSQL_USER:ユーザー名 MYSQL_PASSWORD:パスワード MYSQL_ROOT_PASSWORD:ルート権限のパスワード TZ:時間設定(Time Zone) volumes: 考え方はPHPやnginxのコンテナ同様ですが、先ほどまではホスト側のディレクトリを書いていたところに mysql-volume と書いています。 これにより mysql-colume という名前で作成した名前付きボリュームとコンテナ内を紐づけています。 Docker Volume Volumeとは、コンテナを破棄してもデータを永続的に保存できるように、コンテナ外に提供されているデータの保存領域です。 Dockerの管理下でホスト上にストレージ領域を確保しており、Linux なら /var/lib/docker/volumes/以下にあります。 参考記事:Docker、ボリューム(Volume)について真面目に調べた - Qiita なぜVolumeが必要なのか? コンテナが起動している間はDBのレコードは保存された状態が続きますが、例えば以下のようなコマンドでコンテナを新しく作り直したとするとデータベースの情報はゼロに戻ってしまいます。 // コンテナ削除 $ docker-compose down // コンテナ起動 $ docker-compose up -d これでは困るのでコンテナを破棄してもデータを残したい、、、というときにVolumesを使ってデータの永続化を行います。 ※ボリュームには名前付きボリュームと匿名ボリュームがありますが、通常は管理しやすい名前付きボリュームを使うと良いかと思います。 名前付きボリュームを作成する場合は、データの永続化対象のコンテナに対して volumes: オプションでバインドマウントを行うだけでなく、以下のようにdocker-compose.ymlのトップレベルでボリューム名を定義します。 docker-compose.yml volumes: - mysql-volume: Dockerfile(MySQL) 作成するDockerfileの全文はこちらです。 /docker/db/Dockerfile FROM mysql:8.0 COPY ./docker/db/my.cnf /etc/my.cnf 内容を簡単にまとめると、 公式のMySQLイメージをベースイメージに使用 Mysql - Official Image | Docker Hub MySQLの設定ファイル(my.cnf)をコンテナ内にバインドマウント ※ここで使用しているイメージではM1 Macでは動作しないという情報が見られました。どうやらOracleのMySQLチームがメンテしている mysql/mysql-server のイメージだと動作するようです。 mysql-server | Docker Hub 設定ファイル(my.conf) ./dicker/db/my.cnfを作成します。 色んな記事で作成されているmy.cnfを参考にさせていただきながら書きました。 参考記事:【Docker】docker-composeでmysqlのコンテナを立てる 文字コード タイムゾーン ログ の設定を行っています。 /docker/db/my.conf [mysqld] # character character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # timezone default-time-zone = SYSTEM log_timestamps = SYSTEM # Error Log log-error = mysql-error.log # Slow Query Log slow_query_log = 1 slow_query_log_file = mysql-slow.log long_query_time = 1.0 log_queries_not_using_indexes = 0 # General Log general_log = 1 general_log_file = mysql-general.log [mysql] default-character-set = utf8mb4 [client] default-character-set = utf8mb4 MySQLコンテナを起動する MySQLのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-db-1 "docker-entrypoint.s…" db running 0.0.0.0:3306->3306/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)、db(MySQLのコンテナ)の3つが起動できました。 コンテナの動作確認 MySQLのバージョンを確認します。 $ docker compose exec db mysql -V mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL) ※この後Laravelのプロジェクトを作成したら、このDBとLaravelを接続します。 そしてマイグレーションを行ってDBにテーブルを作成してから、MySQLにログインしてDBを使う動作確認をしたいと思います。 Laravelのインストール Laravelプロジェクトの作成 LEMP環境が構築できたので、Laravelのアプリケーションを作っていきます。 appコンテナに入り、Laravelをインストールします。 $ docker compose exec app bash [app]:/app$ composer create-project --prefer-dist "laravel/laravel=8.*" . [app]:/app$ php artisan -v Laravel Framework 8.81.0 コンテナ内の/app配下にLaravelのプロジェクトが新規作成され、ホスト側の/src配下にも同じくLaravelのプロジェクトが出来ました。 localhost:8081にアクセスして、ブラウザでもLaravelのウェルカムページが表示できることを確認します。 DB接続 appコンテナ(Laravel)からdbコンテナ(MySQL)へ接続する設定を行います。 Laravelではデータベースへの接続設定を .env ファイルに定義しているので、 /src/.env のDBの部分を以下のように修正します。 /src/.env DB_CONNECTION=mysql DB_HOST=db // MySQLコンテナのサービス名 DB_PORT=3306 DB_DATABASE=database DB_USERNAME=root DB_PASSWORD=passwor DB_HOST にはMySQLコンテナのサービス名を指定します。 その他の項目もMySQLコンテナで設定した値(今回はdocker-compose.ymlのenviromentで定義)と同じ値を指定します。 DBに接続出来ているか確認する為、以下のコマンドを実行してマイグレーションを行います。 $ docker compose exec app bash [app]:/app$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (55.42ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (51.11ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (45.12ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (75.74ms) MySQLを使ってみる MySQLのコンテナに入ってDBを確認してみます。 $ docker-compose exec db bash [db]:/$ mysql -u root -p // パスワードを求められるので入力 [db] mysql> use database; [db] mysql> show tables; +------------------------+ | Tables_in_database | +------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | users | +------------------------+ 5 rows in set (0.00 sec) 先ほどマイグレーションを実行したので、Laravelのデフォルトで用意されているマイグレーションファイル通りにテーブルが作成されていることが確認できました。 最後に これでDocker(Docker Compose)を使ったLEMP環境の構築が完了です。 今回作成した環境は非常にシンプルなものなので、業務で使うとなるともっと設定を細やかに行ったり、開発環境・ステージング環境・本番環境それぞれの設定ファイルを用意して設定を切り替えたりといった作業が必要になるかと思いますが、仕組みが分かればあとは全てこの延長にあるのかなと思います。 またいずれ環境ごとの設定ファイル切り替えだったり、あとはNodeのコンテナを使ってNext.jsの環境構築もやってみたいです。 参考記事
- 投稿日:2022-01-31T08:28:14+09:00
DockerでPHP(Laravel)+ nginx + MySQLのLEMP環境を構築する
概要 Docker, Docker Compose を使って、PHP(Laravel) + nginx + MySQLのLEMP環境を構築する記事です。 検索するとDockerfileやdocker-compose.ymlの書き方については色んな方が記事を書いてくださっていて、動く環境を作ること自体は難しくありませんでした。 ですが、筆者はひとつコンテナを作っては動作確認していくという過程を経てすごく理解が深まったなと思うので、ファイルの書き方だけでなく動作確認したことやその結果を含めて記事に残しておきたいと思います。 ↓ 完成後のリポジトリはこちらです。 全体像 最終的なディレクトリ構成は以下の通りです。 全コンテナを管理するdocker-compose.ymlがトップレベルにあり、同じくトップレベルにあるdockerディレクトリ配下に各コンテナのDockerfileや設定ファイルを置いています。 tree docker_sample/ ├── src // Laravelプロジェクトのソースコード ├── docker │ ├── app // PHPコンテナ │ │ ├── Dockerfile │ │ └── php.ini │ ├── db // MySQLコンテナ │ │ ├── Dockerfile │ │ └── my.cnf │ └── web // nginxコンテナ │ ├── Dockerfile │ └── default.conf └── docker-compose.yml // 全コンテナの管理 早速一つずつコンテナを作っていきます。 PHPのコンテナ(Laravelの開発環境) まずはLaravelの開発環境を構築するための、PHPのコンテナを作成します。 docker-compose.ymlへの記述 PHPコンテナ用のDockerfile PHPコンテナ用の設定ファイル(php.ini) について順に説明します。 docker-compose.yml(PHP) docker-compose.ymlのうち、PHPコンテナについての記述は以下の通りです。 ※docker-compose.ymlはインデントが意味を持つので注意。 docker-compose.yml version: "3.9" services: app: // サービス名 build: context: . dockerfile: ./docker/app/Dockerfile volumes: - ./src/:/app version: これはPHPコンテナについてではなく docker-compose.yml の先頭に書く設定ですが、Composeファイルのバージョンを表しています。 今回は最新の 3.9 を使います。(最新のバージョンは下記の公式サイトを参照) Compose file build: ビルドコンテキストを指定します。 context:ビルドコンテキスト(buildを実行する場所)の設定 dockerfile:buildするDockerfileまでのパス(docker-compose.ymlから見た相対パス) 参考記事: docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話 - Qiita volumes: ホスト側の ./src/をコンテナ側の /app にマウントするという意味になります。 ※ここで services: の中に書いているこの volumes: は バインドマウント を行っています。 参考記事:Dockerのマウント3種類についてわかったことをまとめる - Qiita 何が起きているかは実際の挙動を見てみた方が分かりやすいと思うので、後ほどまた動作確認します。 Dockerfile(PHP) Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに書きます。 まず作成するDockerfileの全文がこちらです。 /docker/app/Dockerfile FROM php:8.0-fpm ENV TZ Asia/Tokyo RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./docker/app/php.ini /usr/local/etc/php/php.ini COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer WORKDIR /app 一つずつ説明します。 FROM php:8.0-fpm FROMではイメージをビルドするためのベースイメージを設定します。 書き方は FROM イメージ名:タグ名 です。 ここではDocker HubからPHP公式のイメージをベースとして指定しています。 Php - Official Image | Docker Hub ENV TZ Asia/Tokyo ENVはコンテナ内のサーバー環境変数を設定します。 RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath RUN にはコンテナビルド時に実行するコマンドを書きます。 && で複数のコマンドをつなぎ、 \ で改行します。 ※ && と \ を使うことで複数コマンドを1レイヤーにまとめることができ、公式でもベストプラクティスのTipsとして挙げられています。 Intro Guide to Dockerfile Best Practices - Docker Blog apt-get update :インストール可能なパッケージの「一覧」を更新 apt-get -y install:パッケージをインストール docker-php-ext-install:PHPの拡張ライブラリをインストール ※Laravelのインストールや開発に必要な(もしくは便利な)パッケージや拡張ライブラリをインストールしています。 ※試しにこれらを全くインストールせず手順を進めてみると、コンテナを起動することはできますが、その後のLaravelプロジェクトを作成する時に大量のエラーに遭遇しました(笑) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini ローカルで(後ほど)作成する php.ini (PHPの設定ファイル)をDockerコンテナ内にコピーします。 書き方は COPY [ローカル側のパス] [コンテナ側のパス]です。 ※ローカル側のパスは、Dockerfileから見てではなくbuildコマンドを実行するディレクトリから見た相対パスです。今回はDocker composeを使ってイメージビルドを行うので、docker-compose.ymlから見た相対パスになっています。 COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer Laravelを使うためComposerをインストールします。 この書き方によりマルチステージビルドという方法でインストールされます。 ※マルチステージビルドという方法はイメージの軽量化に役立つようです。 公式ドキュメント:Use multi-stage builds WORKDIR /app コンテナを起動している時に $ docker-compose exec コンテナ名 bash というコマンドを実行すると、コンテナの中でbashを実行することができるのですが、 WORKDIRはその時のカレントディレクトリを指定しています。 設定ファイル(php.ini) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini で出てきたPHPの設定ファイル php.iniを作成します。 作成したファイルの中身がこちらです。 /docker//app/php.ini zend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanese こちらの記事の開発用php.iniをまるっとお借りしました。 開発用 / 本番用の設定例と、項目ごとの説明も載っていて面白かったので、ぜひご覧ください。 参考記事:PHP7.4 ぼくのかんがえたさいきょうのphp.ini - Qiita PHPコンテナを起動する ここまででPHPのコンテナの準備が出来たので、実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build docker compose up は docker-compose.yml に定義したサービスを起動します。 -d 「デタッチド」モードでコンテナを起動します。 (デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示する。「デタッチド」モードではバックグラウンドで動作する。) --build コンテナの開始前にイメージをビルドします。 (特に変更がない場合はキャッシュが使用される。) ※ $ docker-compose build → $ docker-compose up -d を順に行うのと同じです。 起動したコンテナを確認する 以下のコマンドで起動中のコンテナを一覧で確認することができます。 $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp STATUSがrunningになっていれば正常に起動しています。 今はPHPのコンテナしか作っていないので1つだけ表示されていますが、この後nginx, MySQLのコンテナを作り、最終的に3つのコンテナが表示されるようにします。 ここで今作業しているディレクトリの構成を確認してみます。 $ tree . ├── src // 作成された! ├── docker │ ├── app │ │ ├── Dockerfile │ │ └── php.ini └── docker-compose.yml するとdocker-compose.ymlに書いたバインドマウントの以下の部分を受けて、コンテナを起動したときに自動でsrcディレクトリが作成されたことが確認できます。(同様にコンテナ内にもappディレクトリが作成されています。) volumes: - ./src/:/app コンテナの中に入ってみる コンテナを起動中に以下のコマンドを実行すると、コンテナの中に入ってbashを実行することができます。 $ docker-compose exec app bash // appの部分はサービス名を指定する これで今コンテナの中に入れたので、Dockerfileの記述通りにコンテナが作られているか、またPHP・Composer・インストールした拡張機能が使えるか確認していきます。 // Dockerfileの「WORKDIR /app」で指定したカレントディレクトリ通りか確認 [app]:/app$ pwd /app // PHPのバージョン確認 [app]:/app$ php -v PHP 8.0.15 (cli) (built: Jan 26 2022 17:38:36) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.15, Copyright (c) Zend Technologies // Composerのバージョン確認 [app]:/app$ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.0.14 2021-05-21 17:03:37 // gitのバージョン確認 [app]:/app$ git --version git version 2.30.2 // インストール済の拡張機能の一覧 [app]:/app$ php -m [PHP Modules] bcmath intl pdo_mysql zip // たくさん出てくるので他は省略 // php.iniがコピー出来ているか確認 [app]:/app$ cat /usr/local/etc/php/php.ini zend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japaneseroot@0e3ba825df88 確認できたのでコンテナを抜けます。 $ exit // もしくは ctrl + d コンテナの外からコマンドを実行する 上記のコンテナの中で実行したコマンドは、 $ docker-compose exec サービス名 実行したいコマンド でコンテナの外から実行することもできます。(コンテナが起動中に限る) バインドマウントの挙動を確認する コンテナの外からコマンドが実行できるという確認も兼ねて、バインドマウントの動きを見る為、試しに以下のコマンドを実行してみます。 // コンテナ内の/app配下にファイルを作ってみる $ docker-compose exec app touch sample.php $ docker-compose exec app pwd /app $ docker-compose exec app ls sample.php コンテナ内の/app配下にファイルを作成しました。 この/appという場所は、docker-compose.ymlに書いた以下の記述の通りにバインドマウントされています。 volumes: - ./src/:/app これにより、プロジェクトディレクトリ配下の/srcにもsample.phpが作成されています。 $ ls src sample.php 以上より、コンテナ内の /app に対して行ったことが、プロジェクトの /src に反映していることが分かりました。 では次に /src への変更がコンテナ内の /app に対しても反映するか試してみます。 /src/sample.php をエディタで開いてファイルに hello と書きこみ、以下のコマンドを実行します。 $ docker-compose exec app cat sample.php hello // この時permission errorが出たら権限を変更する $ sudo chmod -R 777 ./src するとコンテナ内の ./app/sample.php も編集されていることがわかります。 このようにバインドマウントにより、ホスト側のディレクトリがコンテナ内へマウント出来ていることも確認できました。 試しに作成したファイルは不要なので消しておきます。 $ rm src/sample.php nginxのコンテナ(webサーバー) webサーバーとなるnginxのコンテナを作成します。 PHPコンテナのときと同じく、 docker-compose.ymlへの記述 nginxコンテナ用のDockerfile nginxコンテナ用の設定ファイル(default.conf) の流れで説明します。 docker-compose.yml(nginx) 既に作成しているdocker-compose.ymlに、nginxのコンテナについての部分を追記します。 docker-compose.yml services: api: // 中略 web: // サービス名 build: context: . dockerfile: ./docker/web/Dockerfile ports: - 8081:80 depends_on: - app volumes: - ./src/:/app 解説していきます。 build: ・volumes: ここはPHPコンテナと同様なので説明は省きます。 ports: ホスト側とコンテナ間のポート番号の対応付けを設定します。 書き方は ホスト側のポート番号 : コンテナのポート番号です。 ※今回ホスト側(自分のPC)は既に他の開発で使っているポートとの兼ね合いで 8081 を使いました。コンテナ側はnginxのデフォルトのポート番号である 80 にしています。 depends_on: サービスの起動順序を制御します。 web の depends_on に app と書いているので、 app → web の順に起動するように指定しています。 ※但しこの記述なしで $ docker-compose up -d --build をしてみても私の環境では全く問題なく動きました。が、サービス同士の依存関係を明示的に記すという意味でも書いておくに越したことはないという判断で書いています。 ※ nginxとphp間でTCPによるfpm接続についてや、depends_on オプションについては以下の記事がすごく勉強になったので是非読んでみてください。 参考記事:【docker-compose】depends_onとサービス名解決にまつわるエトセトラ - Qiita Dockerfile(nginx) 作成するDockerfileの全文はこちらです。 /docker/web/Dockerfile FROM nginx:1.20-alpine ENV TZ Asia/Tokyo COPY ./docker/web/default.conf /etc/nginx/conf.d/default.conf FROM , ENV , COPY の意味はPHPコンテナのDockerfileと同様なので詳しい説明は省略し、このファイルで設定していることを簡単にまとめます。 nginx公式のイメージ(Alpineベース)をベースイメージに使用 Nginx - Official Image | Docker Hub 参考記事:Dockerでよく利用されているAlpineは他のLinuxディストリビューションと比べて、どれだけ軽量なのか - プログラミングは芸術だ! 環境変数のタイムゾーンを設定する nginxの設定ファイル(default.conf)をコンテナ内にバインドマウント ※ちなみにnginxは1.18, 1.20などの偶数バージョンが安定バージョンであり、安定バージョンの使用を推奨されています。 公式ドキュメント:Installing NGINX Open Source 設定ファイル(default.conf) ./docker/web/default.confを作成します。 Laravel公式に用意されているnginxの設定例をべースに使います。 Laravel 8.x デプロイ root とfastcgi_passの設定のみ、このプロジェクトに合わせて書き換えています。 /docker/web/default.conf server { listen 80; server_name example.com; root /app/public; // 書き換え add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass app:9000; // 書き換え fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } root リクエストのルートディレクトリです。 root /app/public; と書いたので、 localhost:8081(docker-compose.ymlで設定したポート番号)にアクセスすると /app/publicを見に行きます。 fastcgi_pass FastCGIサーバーのアドレスです。 ※FastCGI:Webサーバ上で動くプログラムを一度起動したらしばらく待機させることによって、プログラムの開始と終了にかかる手間を減らし、動きを速くしたりWebサーバの負荷を軽減することができる仕組み。 app:9000; と書いたので、appコンテナの9000番ポートを指定しています。 nginxコンテナを起動する nginxのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)の2つが起動できました。 またdocker_sample-web-1の PORTS が 0.0.0.0:8081->80/tcp となっており、ホスト上の8081番ポートをコンテナの80番ポートへ割り当てられていることも確認できます。 コンテナの動作確認 nginxのバージョンを確認します。 $ docker-compose exec web nginx -v nginx version: nginx/1.20.2 コンテナの中に入ってみます。 $ docker-compose exec web bash OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH: unknown するとエラーになりました。 Alpineをベースとすると bash は使えず、 ash や sh は使えるようです。 $ docker-compose exec web ash [web]:/ $ pwd / 無事コンテナの中に入れました。 webサーバーとしての動作確認 リクエストに対してファイルを返しブラウザで表示できる nginxのコンテナからPHPのコンテナへphpを実行させることができる 上記2点を確認します。 $ mkdir src/public $ touch src/public/test.php test.phpを以下のように編集します。 /src/public/test.php <?php echo 'test.phpです'; phpinfo(); http://localhost:8081/test.php にアクセスすると以下のように表示され、webサーバーが正しく動作していることが確認できます。 試しに作成したファイルは不要なので消しておきます。 $ rm -rf src/* MySQLのコンテナ(データベース) データベースのMySQLコンテナを作成します。 以下の流れで説明します。 docker-compose.ymlへの記述 MySQLコンテナ用のDockerfile MySQLコンテナ用の設定ファイル(my.conf) docker-compose.yml(MySQL) 既に作成しているdocker-compose.ymlに、MySQLのコンテナについての部分を追記します。 docker-compose.yml services: // 中略 db: // サービス名 build: context: . dockerfile: ./docker/db/Dockerfile ports: - 3306:3306 environment: MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password TZ: 'Asia/Tokyo' volumes: - mysql-volume:/var/lib/mysql volumes: mysql-volume: build: ・ ports: 既出の通りです。 enviroment: 環境変数の設定です。 ※名前やDB名・ユーザー名・パスワードは好きなものを設定します。 ※実際のプロジェクト管理では、環境変数は .env に書いて .gitignore にするなどして、重要な情報が公開されないようにします。 MYSQL_DATABASE:DB名 MYSQL_USER:ユーザー名 MYSQL_PASSWORD:パスワード MYSQL_ROOT_PASSWORD:ルート権限のパスワード TZ:時間設定(Time Zone) volumes: 考え方はPHPやnginxのコンテナ同様ですが、先ほどまではホスト側のディレクトリを書いていたところに mysql-volume と書いています。 これにより mysql-colume という名前で作成した名前付きボリュームとコンテナ内を紐づけています。 Docker Volume Volumeとは、コンテナを破棄してもデータを永続的に保存できるように、コンテナ外に提供されているデータの保存領域です。 Dockerの管理下でホスト上にストレージ領域を確保しており、Linux なら /var/lib/docker/volumes/以下にあります。 参考記事:Docker、ボリューム(Volume)について真面目に調べた - Qiita なぜVolumeが必要なのか? コンテナが起動している間はDBのレコードは保存された状態が続きますが、例えば以下のようなコマンドでコンテナを新しく作り直したとするとデータベースの情報はゼロに戻ってしまいます。 // コンテナ削除 $ docker-compose down // コンテナ起動 $ docker-compose up -d これでは困るのでコンテナを破棄してもデータを残したい、、、というときにVolumesを使ってデータの永続化を行います。 ※ボリュームには名前付きボリュームと匿名ボリュームがありますが、通常は管理しやすい名前付きボリュームを使うと良いかと思います。 名前付きボリュームを作成する場合は、データの永続化対象のコンテナに対して volumes: オプションでバインドマウントを行うだけでなく、以下のようにdocker-compose.ymlのトップレベルでボリューム名を定義します。 docker-compose.yml volumes: - mysql-volume: Dockerfile(MySQL) 作成するDockerfileの全文はこちらです。 /docker/db/Dockerfile FROM mysql:8.0 COPY ./docker/db/my.cnf /etc/my.cnf 内容を簡単にまとめると、 公式のMySQLイメージをベースイメージに使用 Mysql - Official Image | Docker Hub MySQLの設定ファイル(my.cnf)をコンテナ内にバインドマウント ※ここで使用しているイメージではM1 Macでは動作しないという情報が見られました。どうやらOracleのMySQLチームがメンテしている mysql/mysql-server のイメージだと動作するようです。 mysql-server | Docker Hub 設定ファイル(my.conf) ./docker/db/my.cnfを作成します。 色んな記事で作成されているmy.cnfを参考にさせていただきながら書きました。 参考記事:【Docker】docker-composeでmysqlのコンテナを立てる 文字コード タイムゾーン ログ の設定を行っています。 /docker/db/my.conf [mysqld] # character character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # timezone default-time-zone = SYSTEM log_timestamps = SYSTEM # Error Log log-error = mysql-error.log # Slow Query Log slow_query_log = 1 slow_query_log_file = mysql-slow.log long_query_time = 1.0 log_queries_not_using_indexes = 0 # General Log general_log = 1 general_log_file = mysql-general.log [mysql] default-character-set = utf8mb4 [client] default-character-set = utf8mb4 MySQLコンテナを起動する MySQLのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-db-1 "docker-entrypoint.s…" db running 0.0.0.0:3306->3306/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)、db(MySQLのコンテナ)の3つが起動できました。 コンテナの動作確認 MySQLのバージョンを確認します。 $ docker compose exec db mysql -V mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL) ※この後Laravelのプロジェクトを作成したら、このDBとLaravelを接続します。 そしてマイグレーションを行ってDBにテーブルを作成してから、MySQLにログインしてDBを使う動作確認をしたいと思います。 Laravelのインストール Laravelプロジェクトの作成 LEMP環境が構築できたので、Laravelのアプリケーションを作っていきます。 appコンテナに入り、Laravelをインストールします。 $ docker compose exec app bash [app]:/app$ composer create-project --prefer-dist "laravel/laravel=8.*" . [app]:/app$ php artisan -v Laravel Framework 8.81.0 コンテナ内の/app配下にLaravelのプロジェクトが新規作成され、ホスト側の/src配下にも同じくLaravelのプロジェクトが出来ました。 localhost:8081にアクセスして、ブラウザでもLaravelのウェルカムページが表示できることを確認します。 DB接続 appコンテナ(Laravel)からdbコンテナ(MySQL)へ接続する設定を行います。 Laravelではデータベースへの接続設定を .env ファイルに定義しているので、 /src/.env のDBの部分を以下のように修正します。 /src/.env DB_CONNECTION=mysql DB_HOST=db // MySQLコンテナのサービス名 DB_PORT=3306 DB_DATABASE=database DB_USERNAME=root DB_PASSWORD=passwor DB_HOST にはMySQLコンテナのサービス名を指定します。 その他の項目もMySQLコンテナで設定した値(今回はdocker-compose.ymlのenviromentで定義)と同じ値を指定します。 DBに接続出来ているか確認する為、以下のコマンドを実行してマイグレーションを行います。 $ docker compose exec app bash [app]:/app$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (55.42ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (51.11ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (45.12ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (75.74ms) MySQLを使ってみる MySQLのコンテナに入ってDBを確認してみます。 $ docker-compose exec db bash [db]:/$ mysql -u root -p // パスワードを求められるので入力 [db] mysql> use database; [db] mysql> show tables; +------------------------+ | Tables_in_database | +------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | users | +------------------------+ 5 rows in set (0.00 sec) 先ほどマイグレーションを実行したので、Laravelのデフォルトで用意されているマイグレーションファイル通りにテーブルが作成されていることが確認できました。 最後に これでDocker(Docker Compose)を使ったLEMP環境の構築が完了です。 今回作成した環境は非常にシンプルなものなので、業務で使うとなるともっと設定を細やかに行ったり、開発環境・ステージング環境・本番環境それぞれの設定ファイルを用意して設定を切り替えたりといった作業が必要になるかと思いますが、仕組みが分かればあとは全てこの延長にあるのかなと思います。 またいずれ環境ごとの設定ファイル切り替えだったり、あとはNodeのコンテナを使ってNext.jsの環境構築もやってみたいです。 参考記事
- 投稿日:2022-01-31T07:26:05+09:00
dockerFile軽量化資料
初めに 業務でdockerファイルを軽量化する必要があり、その調査でまとめた情報を載せます。 時間がなかったのでかなり殴り書きのようなメモですが、同じ悩みを持った人の助けになればと思います。 私自身、dockerの経験が薄くコンテナの初回のような情報も載せてあります。 時間ができたら、整理して再度記事にしようと思ってます。 調査資料 docker軽量化 記事 資料 おまえのdockerファイルはまだ重い https://speakerdeck.com/stormcat24/oqian-falsedockerimezihamadazhong-i?slide=15 ↑スライドで視覚的にわかりやすい、どうして軽くしないといけないのかどんなものがいいのかのイメージが掴めやすい dockerのイメージとコンテナの違い https://and-engineer.com/articles/YaSPjRIAACAAkhMI dockerイメージのレイヤの考え方 https://www.itbook.info/network/docker02.html dockerコンテナイメージを軽くする理由 ・ポータビリティが向上する ・ノード障害等でコンテナが別ノードに移動する際により早く起動できる ・これらは、サービスの復旧の速度を早めることにつながる https://blog.mosuke.tech/entry/2020/07/09/container-image-size/ dockerファイル公式git libから現場の公式発表dockerコードが見れる https://github.com/docker-library/official-images 大体以下の三つに絞れる ・Alpine Linux ・Ubuntu ・Debian 厳しい ・CentOS 理由: モジュールが揃いすぎている=dockerのイメージとしては重くなってしまう とにかく軽い運用を目指すならAlpine Linux 安定的な運用を目指すならUbuntuかDebian Alpine Linux 軽量重視で作られたLinux ディストリビューション イメージのサイズがとにかく小さい しかし、Pythonなど一部の言語で速度が遅くなる可能性がある 理由:libcに一般的な互換性が不足している https://superuser.com/questions/1219609/why-is-the-alpine-docker-image-over-50-slower-than-the-ubuntu-image Debian Slim タグの説明 https://www.ted027.com/post/docker-debian-difference/ ↑これにどの時どのタグを使った方がいいのかの情報が記載されている https://prograshi.com/platform/docker/docker-image-tags-difference/ Distroless GoogleがメンテしているDockerのベースイメージ群 カンファレンス https://swampup2017.sched.com/ README https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md sysdigにも評価されている https://sysdig.jp/blog/dockerfile-best-practices-2/ shellもパッケージマネージャーもない=セキュリティとしては強くなる https://ceblog.mediba.jp/post/662840014480326656/コンテナイメージとしてdistrolessを使うべき理由って/amp 参考資料 Dockerイメージを軽量化する https://qiita.com/gorohash/items/2358b4b9e0c708e510d0 Dockerfileのアンチパターンと軽量化方法 https://qiita.com/takky/items/401344672a803469b896 dockerイメージの軽量化 https://qiita.com/ytanaka3/items/8c308db2ee58ea63626a 社内のdockerfileのベストプラクティスを公開します(Forcia) https://www.forcia.com/blog/002273.html dockerでRunをまとめた方がいいとは限らない https://future-architect.github.io/articles/20210121/ docker-slimについて コンテナの最適化ツール? 機能 特徴 ・静的および動的解析による最適化 ・最適化後にDockerFileを自動作成 ・Go製のDocker Container Imageサイズ圧縮自動化ツール ・静的(Dockerfile)及び動的(各レイヤーでのプロセスの起動情報など)な解析による最適化 ・ダイエットしたImageだけではなくDockerfileも作成してくれる(リバースエンジニアリング) ・デモではnode.jsのイメージが431.7MBから14.22MB(約97%削減!) https://junchang1031.hatenablog.com/entry/2016/02/11/171449 https://hawksnowlog.blogspot.com/2019/12/getting-started-with-docker-slim.html?m=0 GitHub https://github.com/docker-slim/docker-slim https://dockersl.im/ https://scrapbox.io/kimihiro-n/DockerSlim_%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F
- 投稿日:2022-01-31T06:39:49+09:00
wsl2 ubuntu gitlab runner install
インストールメモ Docerインストール sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" sudo apt update sudo apt install docker-ce sudo systemctl status docker GitLab Runnerインストール sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 sudo chmod +x /usr/local/bin/gitlab-runner sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash sudo /usr/local/bin/gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner sudo systemctl enable gitlab-runner sudo systemctl start gitlab-runner sudo systemctl status gitlab-runner.service GitLab Runner登録 sudo /usr/local/bin/gitlab-runner register Runtime platform arch=amd64 os=linux pid=8618 revision=98daeee0 version=14.7.0 Running in system-mode. Enter the GitLab instance URL (for example, https://gitlab.com/): https://gitlab.com/ Enter the registration token: gitlab runnerのトークン Enter a description for the runner: [DESKTOP-4BU08N3-wsl]: test Enter tags for the runner (comma-separated): test Registering runner... succeeded runner=bRHaFW_7 Enter an executor: kubernetes, custom, docker-ssh, ssh, virtualbox, docker+machine, docker-ssh+machine, docker, parallels, shell: docker Enter the default Docker image (for example, ruby:2.6): python:3.9 Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!