takaya030の備忘録

PHP、Laravel、Docker などの話がメインです

Express を Node.js 22 + TypeScript で動かしてみる

先日 Ubuntu 22.04 にインストールした Node.js 22 と TypeScript を使って Express を動かしてみるハンズオン

takaya030.hatenablog.com

検証環境

Ubuntu 22.04.5 LTS

$ node -v
v22.21.0

$ tsc -v
Version 5.9.3

プロジェクトの作成

プロジェクトのディレクトリを作成する

$ mkdir hello_world_ts

プロジェクトディレクトリに移動して npm init でプロジェクトの初期化を行う

$ cd hello_world_ts

$ npm init -y

Express のインストール

npm を使って Express をインストールする

$ npm install express

TypeScript で Express のコードを記述するために @types/express パッケージもインストールする
(このパッケージは実行時には必要ないため dev 環境にインストールする)

$ npm install --save-dev @types/express

サンプルコード作成

下記の内容で server.ts ファイルを作成

hello_world_ts/server.ts

import * as express from "express";

const app = express();

app.get("/", (req, res) => {
    res.send("Hello World!");
});

app.listen(3000, () => {
    console.log("Server is runnning on http://localhost:3000");
});

ビルド

tsc コマンドで server.ts をビルドする

$ tsc server.ts

サーバーの起動

ビルドによって server.js が作成されるので、 node コマンドで実行する

$ node server.js
Server is runnning on http://localhost:3000

WEB ブラウザで http://localhost:3000 にアクセスすると "Hello World!" の文字列が表示されることを確認する

Ctrl + C を押すとサーバーが終了する

参考サイト

qiita.com

Ubuntu 22.04 に Node.js 22 と TypeScript をインストールする

検証環境

Ubuntu 22.04.5 LTS

NodeSource APT リポジトリの追加

システムのパッケージリストの更新

$ sudo apt update -y

Node.js 22 をインストールするためのスクリプトの実行

$ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -

Node.js 22 のインストール

再度パッケージリストを更新した後、Node.js 22 をインストール

$ sudo apt update -y
$ sudo apt install -y nodejs

正常にインストールされたか確認

$ node -v
v22.21.0

TypeScript のインストール

npm を使用して、TypeScript をグローバルにインストール

$ sudo npm install -g typescript

正常にインストールされたか確認

$ tsc -v
Version 5.9.3

参考サイト

qiita.com

www.typescriptlang.org

Minikube で Laravel12 を動かす

検証環境

Windows11 Home Edition (version 24H2)

$ wsl --version
WSL バージョン: 2.6.1.0
カーネル バージョン: 6.6.87.2-1
WSLg バージョン: 1.0.66
MSRDC バージョン: 1.2.6353
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.26100.6584

# WSL にインストールした Linux
Ubuntu 22.04.5 LTS

# Ubuntu にインストールした Docker
Docker version 28.3.3, build 980b856
Docker Compose version v2.39.1

$ php --version
PHP 8.2.29 (cli) (built: Jul  1 2025 16:29:21) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.29, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.29, Copyright (c), by Zend Technologies

$ composer --version
Composer version 2.8.5 2025-01-21 15:23:40

$ minikube version
minikube version: v1.36.0
commit: f8f52f5de11fc6ad8244afac475e1d0f96841df1-dirty

$ kubectl version
Client Version: v1.33.4-dispatcher
Kustomize Version: v5.6.0
Server Version: v1.33.1

Laravel のインストール

$ composer create-project laravel/laravel laravel-project "12.*"

正しくインストールされたか確認

$ cd laravel-project
$ php artisan --version
Laravel Framework 12.30.1

storage フォルダ以下のパーミッションを 777 に変更

$ chmod 777 -R ./storage

正常に動作するか確認するため開発用サーバーを起動

$ php artisan serve

WEBブラウザhttp://localhost:8000 を開いて以下の welcome ページが表示されるか確認する

Ctrl+C で開発用サーバーを終了させる

composer install 時の PHP バージョンの指定

下記コマンドで composer.jsonPHP バージョン指定情報を追加する

$ composer config platform.php 8.2.29

下記コマンドで composer.lock に反映する

$ composer update platform.php 8.2.29

docker イメージビルドの準備

下記のファイルをプロジェクトフォルダ以下に作成する

laravel-project/.dockerignore

strage/framework/cache/**
strage/framework/sessions/**
strage/framework/testing/**
strage/framework/views/**
strage/logs/**

laravel-project/Dockerfile

FROM php:8.2.29-apache as devapp
EXPOSE 8080
COPY --chown=www-data:www-data . /var/www
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN chmod 777 -R /var/www/storage/ && \
    echo "Listen 8080" >> /etc/apache2/ports.conf && \
    echo "ServerName 127.0.0.1" >> /etc/apache2/apache2.conf && \
    a2enmod rewrite

laravel-project/000-default.conf

<VirtualHost *:8080>

  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/public/

  <Directory /var/www/>
    AllowOverride All
    Require all granted
  </Directory>

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

laravel-project/docker-compose.build.yml

services:
  app: 
    build:
      context: .
      dockerfile: ./Dockerfile
      target: devapp
    image: "laravel-project/app"
    ports: 
      - "8080:8080"

docker イメージのビルド

下記コマンドで docker イメージをビルドする

$ docker compose -f docker-compose.build.yml build app

ビルドが成功するとイメージが作成される

$ docker images
REPOSITORY                     TAG             IMAGE ID       CREATED          SIZE
laravel-project/app            latest          50377b1aa8d9   27 minutes ago   564MB

作成したイメージを使ってコンテナが起動するか確認する

$ docker compose -f docker-compose.build.yml up -d

WEBブラウザhttp://localhost:8080 を開いて先ほどと同様の welcome ページが表示されるか確認する

下記コマンドでコンテナを終了させる

$ docker compose -f docker-compose.build.yml down

minikube で動かすための準備

minikube を起動する

$ minikube start

kubectl のコンテキストが minikube になっているか確認

$ kubectl config get-contexts
CURRENT   NAME       CLUSTER    AUTHINFO   NAMESPACE
*         minikube   minikube   minikube   default

dockerリポジトリminikube のものに切り替える

$ eval $(minikube docker-env)

$ docker images
REPOSITORY                                TAG        IMAGE ID       CREATED         SIZE
registry.k8s.io/kube-scheduler            v1.33.1    398c985c0d95   4 months ago    73.4MB
registry.k8s.io/kube-controller-manager   v1.33.1    ef43894fa110   4 months ago    94.6MB
registry.k8s.io/kube-apiserver            v1.33.1    c6ab243b29f8   4 months ago    102MB
registry.k8s.io/kube-proxy                v1.33.1    b79c189b052c   4 months ago    97.9MB
registry.k8s.io/etcd                      3.5.21-0   499038711c08   5 months ago    153MB
registry.k8s.io/coredns/coredns           v1.12.0    1cf5f116067c   10 months ago   70.1MB
registry.k8s.io/pause                     3.10       873ed7510279   16 months ago   736kB
gcr.io/k8s-minikube/storage-provisioner   v5         6e38f40d628d   4 years ago     31.5MB

minikubeリポジトリに Laravel のイメージを作成する

$ docker compose -f docker-compose.build.yml build app

マニフェストの作成

laravel-project の直下に k8s フォルダを作成してマニフェストを配置する

$ cd laravel-project
$ mkdir k8s

laravel-project/k8s/myapp-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myapp
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  strategy: {}
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: laravel-project/app:latest
        name: app
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
        resources: {}
status: {}

laravel-project/k8s/myapp-service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: myapp
  name: myapp
spec:
  ports:
  - name: "8080"
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: myapp
  type: LoadBalancer
status:
  loadBalancer: {}

アプリケーションの起動

$ cd laravel-project
$ kubectl apply -f k8s/myapp-deployment.yaml
$ kubectl apply -f k8s/myapp-service.yaml

リソースが作成されたか確認

$ kubectl get all
NAME                         READY   STATUS    RESTARTS   AGE
pod/myapp-58b77d6f4f-rc86b   1/1     Running   0          12s

NAME                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          27d
service/myapp        LoadBalancer   10.98.235.87   <pending>     8080:32538/TCP   7s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/myapp   1/1     1            1           12s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/myapp-58b77d6f4f   1         1         1       12s

動作確認

minikube tunnel を実行

$ minikube tunnel
✅  Tunnel successfully started

📌  NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...

🏃  Starting tunnel for service myapp.

WEBブラウザhttp://localhost:8080 を開いて先ほどと同様の welcome ページが表示されるか確認する

Ctrl+Cminikube tunnel を終了させる

後始末

$ kubectl delete -f k8s/myapp-service.yaml
$ kubectl delete -f k8s/myapp-deployment.yaml

$ eval $(minikube docer-env -u)
$ minikube stop

参考サイト

kubernetes.io

qiita.com

Lravel12 で Hello World

検証環境

Windows11 Home Edition (version 24H2)

$ wsl --version
WSL バージョン: 2.5.9.0
カーネル バージョン: 6.6.87.2-1
WSLg バージョン: 1.0.66
MSRDC バージョン: 1.2.6074
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.26100.4652

# WSL にインストールした Linux
Ubuntu 22.04.4 LTS

# Ubuntu にインストールした Docker
Docker version 27.4.1, build b9d17ea
Docker Compose version v2.32.1

$ php --version
PHP 8.2.29 (cli) (built: Jul  1 2025 16:29:21) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.29, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.29, Copyright (c), by Zend Technologies

$ composer --version
Composer version 2.8.5 2025-01-21 15:23:40

Laravel のインストール

$ composer create-project laravel/laravel laravel-project "12.*"

正しくインストールされたか確認

$ cd laravel-project
$ php artisan --version
Laravel Framework 12.20.0

storage フォルダ以下のパーミッションを 777 に変更

$ chmod 777 -R ./storage

Laravel Sail のセットアップ

下記コマンドで Laravel Sail をセットアップする

$ php artisan sail:install --devcontainer --php=8.2

下記画面では mysql を選択する

Laravel Sail の起動

$ ./vendor/bin/sail up -d

起動した後、初回のマイグレーションを実行

$ ./vendor/bin/sail artisan migrate

コントローラーの作成

コントローラーのファイル作成

$ ./vendor/bin/sail artisan make:controller HelloController

作成された app/Http/Controllers/HelloController.php を下記内容に変更

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HelloController extends Controller
{
    //
  public function index()
  {
    return view('hello');
  }
}

ビューの作成

下記内容で resource/views/hello.blade.php を作成

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>My First Page</title>
</head>
<body>
  <h2>Hello World</h2>
</body>
</html>

ルートの変更

routes/web.php を下記内容に変更

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HelloController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/hello', [HelloController::class, 'index']);

動作確認

WEB ブラウザで http://localhost/hello を開いて下記画像のように表示されれば成功です

参考サイト

readouble.com

qiita.com

Go言語(golang)と gin で Google API のアクセストークンを取得する

golang + gin で Google API の OAuth2 認証のプログラムを作成したときのメモ

検証環境

Windows11 Home Edition (version 24H2)

$ go version
go version go1.23.8 windows/amd64

ファイル構成

goauth
├── .env
└── main.go

goauth/.env

SAMPLE_MESSAGE="Hello, GoWorld!"
OAUTH_CLIENT_ID="Your-Client-Id"
OAUTH_CLIENT_SECRET="Your-Client-Secret"
OAUTH_REDIRECT_URL="http://localhost:8080/login"

goauth/main.go

package main

import (
    "crypto/rand"
    "errors"
    "fmt"
    "net/http"
    "os"

    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"

    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
)

func main() {
    err := godotenv.Load(".env")
    if err != nil {
        fmt.Printf("Cannot load env file: %v", err)
    }

    engine := gin.Default()
    store := cookie.NewStore([]byte("secret"))
    engine.Use(sessions.Sessions("mysession", store))
    engine.GET("/", homeHandler)
    engine.GET("/login", loginHandler)
    engine.Run(":8080")
}

func homeHandler(c *gin.Context) {
    msg := os.Getenv("SAMPLE_MESSAGE")

    c.JSON(http.StatusOK, gin.H{
        "message": msg,
    })
}

func loginHandler(c *gin.Context) {
    cid := os.Getenv("OAUTH_CLIENT_ID")
    csec := os.Getenv("OAUTH_CLIENT_SECRET")
    rdurl := os.Getenv("OAUTH_REDIRECT_URL")

    errparam := c.Query("error")
    code := c.Query("code")
    state := c.Query("state")

    conf := oauth2.Config{
        ClientID:     cid,
        ClientSecret: csec,
        RedirectURL:  rdurl,
        Scopes: []string{
            "https://www.googleapis.com/auth/datastore",
        },
        Endpoint: google.Endpoint,
    }

    session := sessions.Default(c)
    oauth2state, _ := session.Get("oauth2state").(string)

    if len(errparam) > 0 {
        fmt.Printf("Error: %v", errparam)
    } else if len(code) == 0 {
        oauth2state, _ := MakeRandomStr(16)
        url := conf.AuthCodeURL(oauth2state, oauth2.AccessTypeOffline)
        session.Set("oauth2state", oauth2state)
        if err := session.Save(); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "fail to save the session"})
            return
        }
        c.Redirect(http.StatusMovedPermanently, url)
        return
    } else if len(state) == 0 || state != oauth2state {
        session.Set("oauth2state", oauth2state)
        if err := session.Save(); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "fail to save the session"})
            return
        }
        c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid state"})
        return
    } else {
        ctx := c.Request.Context()
        tok, err := conf.Exchange(ctx, code)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot exchange token"})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "msg":           "success to exchange token",
            "access_token":  tok.AccessToken,
            "token_type":    tok.TokenType,
            "refresh_token": tok.RefreshToken,
            "expiry":        tok.Expiry,
            "expires_in":    tok.ExpiresIn,
        })
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "client_id":     cid,
        "client_secret": csec,
        "redirect_url":  rdurl,
        "error":         errparam,
        "code":          code,
        "state":         state,
    })
}

func MakeRandomStr(digit uint32) (string, error) {
    const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    // 乱数を生成
    b := make([]byte, digit)
    if _, err := rand.Read(b); err != nil {
        return "", errors.New("unexpected error")
    }

    // letters からランダムに取り出して文字列を生成
    var result string
    for _, v := range b {
        // index が letters の長さに収まるように調整
        result += string(letters[int(v)%len(letters)])
    }
    return result, nil
}

パッケージのダウンロード

D:\>cd goauth
D:\goauth>go mod init github.com/takaya030/goauth
D:\goauth>go mod tidy 

プログラムの実行

D:\>cd goauth
D:\goauth>go run main.go

WEB ブラウザで http://localhost:8080/login を開いて以下のような JSON が表示されたら成功

{
  "access_token": "ya49.aBCDE...",
  "expires_in": 0,
  "expiry": "2025-04-10T10:31:47.8575645+09:00",
  "msg": "success to exchange token",
  "refresh_token": "1//0fjsGJI2...",
  "token_type": "Bearer"
}

参考サイト

pkg.go.dev

kitagry.github.io

qiita.com

qiita.com

Lumen で Laravel Sail を使用する

Lumen に Laravel Sail をインストールしたときの作業手順のメモ

検証環境

Windows11 Home Edition (version 24H2)

D:\>wsl --version
WSL バージョン: 2.3.26.0
カーネル バージョン: 5.15.167.4-1
WSLg バージョン: 1.0.65
MSRDC バージョン: 1.2.5620
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.26100.2605

# installed ubuntu version
Ubuntu 22.04.4 LTS (Jammy Jellyfish)

# docker
Docker version 27.4.1, build b9d17ea
Docker Compose version v2.32.1
github.com/docker/buildx v0.19.3 48d6a39

Ubuntu 環境に devbox をインストール

下記コマンドで devbox をインストール

$ curl -fsSL https://get.jetify.com/devbox | bash

正常にインストールされたか確認

$ devbox version
0.13.7
$ devbox help
Instant, easy, predictable development environments

Usage:
  devbox [flags]
  devbox [command]

Available Commands:
  add         Add a new package to your devbox
  auth        Devbox auth commands
  cache       Collection of commands to interact with nix cache
  completion  Generate the autocompletion script for the specified shell
  create      Initialize a directory as a devbox project using a template
  generate    Generate supporting files for your project
  global      Manage global devbox packages
  help        Help about any command
  info        Display package info
  init        Initialize a directory as a devbox project
  install     Install all packages mentioned in devbox.json
  list        List installed packages
  rm          Remove a package from your devbox
  run         Run a script or command in a shell with access to your packages
  search      Search for nix packages
  secrets     Interact with devbox secrets in jetify cloud.
  services    Interact with devbox services.
  shell       Start a new shell with access to your packages
  shellenv    Print shell commands that create a Devbox Environment in the shell
  update      Update packages in your devbox
  version     Print version information

Flags:
  -h, --help    help for devbox
  -q, --quiet   suppresses logs

Use "devbox [command] --help" for more information about a command.

Lumen のプロジェクトを作成

Lumen プロジェクトのディレクトリを作成して phpcomposer をインストール

$ mkdir lumen-project
$ cd lumen-project

$ devbox init
$ devbox add php82 php82Packages.composer

composer を使って Lumen をインストール

$ devbox shell
(devbox) $ composer create-project laravel/lumen ./tmp
(devbox) $ mv ./tmp/* ./tmp/.* .
(devbox) $ rmdir ./tmp

(devbox) $ php artisan --version
Laravel Framework Lumen (10.0.4) (Laravel Components ^10.0)

Laravel Sail のインストール

composer を使って Laravel Sail をインストール

(devbox) $ composer require laravel/sail --dev

boostrap/app.php に下記を追加

if(env('APP_ENV') == 'local')
{
    $app->register(Laravel\Sail\SailServiceProvider::class);
}

sail 関連の artisan コマンドが追加されているか確認

(devbox) $ php artisan list
    .
    .
    .
 sail
  sail:add             Add a service to an existing Sail installation
  sail:install         Install Laravel Sail's default Docker Compose file
  sail:publish         Publish the Laravel Sail Docker files
    .
    .
    .

php artisan sail:install を実行する

(devbox) $ php artisan sail:install --devcontainer

Laravel Sail のカスタマイズ

インストール直後の状態では HTTP サーバーが起動しないためカスタマイズを行う

docker-compose.yml

laravel.testenvironmentSUPERVISOR_PHP_COMMAND を追加

--- a/docker-compose.yml  2025-01-02 17:27:54.006158767 +0900
+++ b/docker-compose.yml  2025-01-02 17:14:04.886161856 +0900
@@ -17,6 +17,7 @@
             XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
             XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
             IGNITION_LOCAL_SITES_PATH: '${PWD}'
+            SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS -S 0.0.0.0:80 -t /var/www/html/public"
         volumes:
             - '.:/var/www/html'
         networks:

.env

WWWGROUPWWWUSER を追加

WWWGROUP=1000
WWWUSER=1000

動作確認

下記コマンドで sail を立ち上げる

$ ./vendor/bin/sail up -d

http://localhost にアクセスして以下のレスポンスが返れば成功

Lumen (10.0.4) (Laravel Components ^10.0)

トラブルシューティング

artisan sail:publish が動作しない

artisan sail:publish は内部で artisan vendor:publish を呼び出しているため Lumen では動作しない
下記手順で artisna sail:publish と同じ結果が得られる

vendor/laravel/sail/ 以下の docker 関連ファイルを ./docker/ 以下へコピー

$ mkdir ./docker
$ cp -rf ./vendor/laravel/sail/runtimes/* ./docker/
$ cp -rf ./vendor/laravel/sail/database/* ./docker/

必要に応じて docker-compose.ymlcontext などのパス指定を変更する

--- a/docker-compose.yml  2025-01-02 18:36:41.176152586 +0900
+++ b/docker-compose.yml  2025-01-02 18:37:11.586153133 +0900
@@ -1,7 +1,7 @@
 services:
     laravel.test:
         build:
-            context: './vendor/laravel/sail/runtimes/8.4'
+            context: './docker/8.4'
             dockerfile: Dockerfile
             args:
                 WWWGROUP: '${WWWGROUP}'

参考サイト

readouble.com

www.jetify.com

www.php.net

stackoverflow.com

Docker で Amon2 の開発環境を構築 (2024年7月版)

はじめに

以前 Docker で Amon2 の開発環境構築の記事を書きましたが、当時と比較して構成が変わってきたので改めて記事にまとめました

takaya030.hatenablog.com

検証環境

Windows11 Home Edition (version 23H2)

D:\>wsl --version
WSL バージョン: 2.2.4.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.61
MSRDC バージョン: 1.2.5326
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26091.1-240325-1447.ge-release
Windows バージョン: 10.0.22631.3880

# installed ubuntu version
Ubuntu 22.04.4 LTS (Jammy Jellyfish)

# docker
Docker version 25.0.3, build 4debf41
Docker Compose version v2.24.6

ディレクトリ構成

amon2
├── .dockerignore
├── Dockerfile
├── compose.build.yml
├── compose.yml
└── cpanfile

各種設定ファイル

amon2/.dockerignore

.git
Dockerfile
compose.yml
compose.build.yml
local

amon2/Dockerfile

マルチステージビルドでイメージを作成します
base では Carton のインストールとワークディレクトリの作成
devapp ではイメージへのモジュールのインストールとローカルで編集したソースのイメージへの書き込みを行っています

FROM perl:5.40 AS base

WORKDIR /tmp

# install Carton
RUN cpanm Carton

# create appuser
RUN useradd -d /home/appuser -m -s /bin/bash -u 1000 appuser
USER 1000
RUN mkdir /home/appuser/app
WORKDIR /home/appuser/app

FROM base AS devapp

USER 1000
COPY cpanfile cpanfile.snapshot /home/appuser/app/
RUN carton install
COPY . /home/appuser/app/
EXPOSE 3000

CMD ["carton","exec","--","plackup","-p","3000","-r","myapp/app.psgi"]

amon2/compose.build.yml

base ターゲットをビルドするための compose ファイル
これでビルドしたイメージを使って Amon2 をインストールします

services:
  base:
    build:
      context: .
      dockerfile: ./Dockerfile
      target: base
    image: "takaya030/amon2-base"
    volumes:
      - '.:/home/appuser/app'

amon2/compose.yml

devapp ターゲットのビルドと起動のための compose ファイル
Amon2 をインストールした後はこちらを使ってアプリケーションのイメージをビルドします

services:
  devapp:
    build:
      context: .
      dockerfile: ./Dockerfile
    image: "takaya030/amon2-devapp"
    volumes:
      - '.:/home/appuser/app'
    ports:
      - "3000:3000"
    command: ["carton","exec","--","plackup","-p","3000","-r","myapp/app.psgi"]

amon2/cpanfile

requires 'Plack', '1.0051';
requires 'Amon2', '6.16';
requires 'Amon2::Lite', '0.13';

base イメージのビルド

$ cd amon2
$ docker pull perl:5.40
$ docker compose -f compose.build.yml build base

Amon2 のインストール

base イメージを使ってインストールします

$ docker compose -f compose.build.yml run --rm base carton install

サンプルアプリケーションの作成

$ docker compose -f compose.build.yml run --rm base carton exec -- amon2-setup.pl --flavor=Lite myapp

devapp イメージのビルド

こちらがアプリケーションのイメージとなります

$ docker compose build devapp

動作確認

すべてのビルドが成功した場合は以下のような2個のイメージが作成されます

$ docker images
REPOSITORY                                                 TAG         IMAGE ID       CREATED         SIZE
takaya030/amon2-devapp                                     latest      1bc12fd5ab1c   3 hours ago     1.08GB
takaya030/amon2-base                                       latest      1108e2b78874   3 hours ago     1GB

以下のコマンドでアプリケーションのコンテナが起動します

$ docker compose up -d

web ブラウザで http://localhost:3000 にアクセスして以下の画面が表示されれば成功です

アプリケーションを停止するには下記のコマンドを入力します

$ docker compose down

参考サイト

takaya030.hatenablog.com

qiita.com