マイクロソフト系技術情報 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。

目次

概要

  • 運用環境へのデプロイについては、専任のエンジニアに相談する必要があるものと思われれる。

前提環境

インストール

手順1

以下の手順で検証・評価した。

プロジェクトの作成

ASP.NET Core MVCアプリケーションの作成

  • Dockerサポートなし
  • 認証なし

プロジェクトの設定

  • Hyper-V コンテナ上で動作するOS(Windows or Linux)を選択(ここではLinuxを選択)。
手順2

インストレーション

  • Docker for Windowsのインストール(ダウンロードに少々時間がかかる)
手順3
  • Docker for Windowsのインストール・ウィザード
    Docker ≒ Linuxコンテナなので下のCheck Boxは外しておく)
手順4
手順5

デバッグ実行の準備

手順6
  • 再起動後、Hyper-V上にLinux VMを確認できる(MobyLinuxVM)。
手順7
  • 再度デバッグ実行を行うと、以下のエラー・メッセージが表示される。
手順8
  • エラー・メッセージが以下のように変更される(Dockerが実行されていないとのこと)。
手順9
  • 暫く経つと、以下のダイアログが表示された。
    OKを押下して再起動する(再起動に少々時間がかかる)。
手順10
  • 再起動を行うと、以下のダイアログボックスが表示される
    (なお、ココでサインアップ・サインインはしなくてもイイ)。
手順11
  • この時点で、以下のdockerコマンドをcmdから実行すると、
    dockerコマンドが適切に実行され結果が返り、Docker(のクライアント)が実行されていることを確認する。
    > docker version
    > docker run hello-world
  • デバッグ実行を開始すると、以下のcmdが起動し「何か」がダウンロードされ、
    手順12

※ 後々、「from microsoft/aspnetcore」でググって、
  コンパイル済みのASP.NET Coreアプリケーションを実行するための
  公式のDocker Imageをダウンロードしていたことが解った。
  https://hub.docker.com/r/microsoft/aspnetcore/

  • 次いで、以下のエラー・メッセージが表示されるので、
手順13
  • メッセージ通り、Docker for Windowsの設定で、ボリューム共有を有効にする。
    Docker for Windowsの画面はタスクトレイ (Task tray) から起動する。
    手順14
  • 管理者アカウントのCredentialの入力を求められるので入力を行う。
  • 設定完了後、再度デバッグ実行を行うと、以下のダイアログが表示されるので、
    [アクセスを許可する]ボタンを押下し、VPNKitと言う組込みVPNツールのリスニング・ポートを開放する。
手順15
  • これにより、アプリケーションがHyper-V コンテナ内のDocker(Linuxコンテナ)で起動し、デバッグが開始される。

ブレークポイントを設定してデバッグ実行する。

設定の確認

  • ここまでの手順で、ソリューションに「docker-compose」が追加されているのを確認する。
  • この「docker-compose」が、スタートアップ・プロジェクトに設定されていることを確認する。

デバッグ実行

  • デバッグ実行を開始する(ドロップダウン・リストに「Docker」が選択されていることを確認する)。
  • ブレークポイントを設定して実行すると、
    以下のように、適切にデバッグ実行されていることを確認できる。
手順16
  • 本当にLinux上で動いているか心配なので、念のため、確認する。
    確認方法
    手順17

余談:PDBのタイプ

Docker側でライブラリのデバッグをする場合、ライブラリのPDBが、
「完全(full)」ではなくて、「ポータブル(portable)」である必要がある。

手順2

  • より実践的な開発環境を構成したい。
    • Webサーバ:Docker側のKestrelで動作する。
    • DB:ホスト側のDBで動作する。
  • 後者のDBへのアクセスが可能かどうかDBMSアプリケーションを使用して確認する。
    (DBMSアプリケーションとしては、ASP.NET Core対応されたOpen棟梁テンプレートを使用する。)

※ 平たく言って、Open棟梁テンプレートをDockerで動かす。

設定ファイル読み込み方法の変更

  • 早速エラーが発生。DBアクセス以前に設定ファイルへのアクセスができない。
手順18
  • 以下のように調査すると、プロジェクト内のファイルがカレント・ディレクトリ(/app)にデプロイされることが解る。
手順19
  • 従って、
  • プロジェクト内にファイルを配置し、
    (「リンクとして追加」の設定はNG、「出力ディレクトリにコピー」の設定は不要)
    これを読み取る方向性で対応するか、
  • 旧Azure PaaS時と同様に、埋め込まれたリソースで対応できる。
    設定の仕方は、以下の「Azure Web Apps」で動作させる方法が参考になる。

SQL Serverへの接続

ホスト側のDBで動作させようとしたが、ハマりどころが多かったのでメモ。

接続文字列

  • ホストのIPアドレスは、10.0.75.1になる。
  • Windows認証ではなく、SQL Server認証の接続文字列を使用する。

Windows ファイアウォール

  • 必要時応じて設定する。
  • OFFにして通ったら、その後、ONにして絞るのがイイ。

SQL Server 構成マネージャー

Expressの場合は、SQL Server 構成マネージャーの設定も必要にある。

参考

変更点のスクショ

以下は、ASP.NET Core MVCのOpen棟梁テンプレートの変更点のスクショです。
(設定ファイル読み込み方法に「埋め込まれたリソース」を採用した場合)

手順20

手順3

概要

オーケストレーション

Docker Composeを使用して、コンテナ・オーケストレーションを行う。

  • Webサーバを、Kestrelからnginxプロキシ経由に変更する。
  • Docker Hubから、ASP.NET Core + nginx
    イメージ・ファイルを取得する(「nginx:latest」と「aspnetcore:latest」)。
  • 上記の2つのコンテナを作成を・管理する場合、Docker Composeを利用する。

構成

  • 各コンテナは、それぞれ独立したIPアドレスを持っており、dockerコマンドで調べることができる。

初期設定

手順1と同じ。

コンテナの管理

複数のコンテナを扱うので、そろそろ管理コマンド・ツールを理解する。

dockerコマンド

  • docker image
    • docker image ls
    • docker image rm image xxxxxxxxxxxx
  • docker ps
  • docker inspect id xxxxxxxxxxxx
  • , etc.

Kitematicのインストール

  • Windows環境からは、以下のようにDocker for Windows
    タスクトレイ (Task tray) の Context Menu からダウンロードする。
    手順21
    手順22
  • インストールはzip解凍でOK。
  • 解凍フォルダのexeを起動する。

Update-Package

Update-Packageを実行する。

Kestrelのポートを固定する。

Program.csに以下の行を追加する。

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseUrls("http://0.0.0.0:5000/") // ← ココを追加 localhost や 127.0.0.1 だとダメ。
        .Build();

Dockerファイルの編集

Dockerファイル(Dockerfile)の編集

ASP.NET Coreコンテナ

既定のDockerファイル(Dockerfile)を修正

  • 変更前
    FROM microsoft/aspnetcore:2.0 AS base
    WORKDIR /app
    EXPOSE 80
    
    FROM microsoft/aspnetcore-build:2.0 AS build
    WORKDIR /src
    COPY WebApplication1.sln ./
    COPY WebApplication1/WebApplication1.csproj WebApplication1/
    RUN dotnet restore -nowarn:msb3202,nu1503
    COPY . .
    WORKDIR /src/WebApplication1
    RUN dotnet build -c Release -o /app
    
    FROM build AS publish
    RUN dotnet publish -c Release -o /app
    
    FROM base AS final
    WORKDIR /app
    COPY --from=publish /app .
    ENTRYPOINT ["dotnet", "WebApplication1.dll"]
  • 変更後
  • aspnetcore:2.0 ---> latestと変更
    (試しに変更しただけなので変更しなくても良い)
  • EXPOSE 80 ---> 5000と変更
    これは、結局、Docker Composeファイルが優先される。
    FROM microsoft/aspnetcore:latest AS base
    WORKDIR /app
    EXPOSE 5000
    
    FROM microsoft/aspnetcore-build:latest AS build
    WORKDIR /src
    COPY WebApplication1.sln ./
    COPY WebApplication1/WebApplication1.csproj WebApplication1/
    RUN dotnet restore -nowarn:msb3202,nu1503
    COPY . .
    WORKDIR /src/WebApplication1
    RUN dotnet build -c Release -o /app
    
    FROM build AS publish
    RUN dotnet publish -c Release -o /app
    
    FROM base AS final
    WORKDIR /app
    COPY --from=publish /app .
    ENTRYPOINT ["dotnet", "WebApplication1.dll"]

nginxコンテナ

nginxフォルダにDockerfileファイル(Dockerfile)を作成する。
※ nginxフォルダは、Docker Composeファイル(docker-compose.yml)と同じ階層に作成。

FROM nginx:latest
COPY default.conf /etc/nginx/conf.d/default.conf

nginx設定ファイルの追加

nginxフォルダにdefault.confを作成する。
※ nginxフォルダは、Docker Composeファイル(docker-compose.yml)と同じ階層に作成。

既定値

server {
    listen        80;
    server_name   example.com *.example.com;
    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $http_host;
        proxy_cache_bypass $http_upgrade;
    }
}

変更後

server {
    listen        80;
    server_name   localhost;
    location / {
        proxy_pass         http://xxx.xxx.xxx.xxx:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $http_host;
        proxy_cache_bypass $http_upgrade;
    }
}

※ xxx.xxx.xxx.xxxには、

  • 以下コマンドを使用して、
    ASP.NET CoreコンテナのIPAddressとして取得する。
    • docker ps
    • docker inspect id xxxxxxxxxxxx

Docker Composeファイルの編集

Docker Composeファイル(docker-compose.yml)の編集

既定値

version: '3'
services:
  webapplication1:
    image: webapplication1
    build:
      context: .
      dockerfile: /WebApplication1/Dockerfile

変更後

version: '3'
services:
  nginx-proxy:
    image: nginx-proxy
    build:
      context: ./nginx
      dockerfile: Dockerfile
    ports:
      - "8888:80"
    links:
      - webapplication1
  webapplication1:
    image: webapplication1
    build:
      context: ./WebApplication1
      dockerfile: Dockerfile
    ports:
      - "5000:5000"

補足

Dockerファイル

Dockerファイル(Dockerfile)には様々な設定が可能。

  • Dockerfileを使用する場合、
    buildにcontextとdockerfileを指定する。
  • Dockerfileが必要ない場合、
    buildはサボってimageをそのまま利用することも可能。

Docker Composeファイル

  • ports
    • ポートを公開(expose)する。
      • 「ホスト : コンテナ」で、ホストとポートを指定する。
      • コンテナのポートのみ指定(ホスト側のポートはランダム)。
    • 公式のイメージでは、記載がなくてもデフォルト・ポートが公開(expose)される。
  • links
    • コンテナ内の/etc/hostsに指定したサービス名が追加される。
    • サービス名とリンク・エイリアス(サービス : エイリアス)の指定も可能。
  • volumes
    • ホストのディレクトリをマウントする。
    • 再デプロイせずにローカルファイルの変更を反映できる。
    • また、データストアのデータファイルにも使用できる。

手順4

概要

手順3

  • SessionストアのKVS(Redis)と
  • DataストアのDBMS(PostgreSQL)の

コンテナを追加してみる。

永続化

  • 永続化の方法には、
  • ホストのディレクトリにマウントする方法
  • データコンテナを使う方法
  • 起動時に都度、初期化する方法

がある。

  • それぞれ、その用途から、
  • KVS(Redis)には、「ホストのディレクトリにマウントする方法」
    (1つの負荷分散クラスタのSessionストアとして利用するため)
  • DBMS(PostgreSQL)には、「起動時に都度、初期化する方法」
    (ココでは、主に、テスト自動化での利用を検討しているため)

を採用する。

構成図

構成図

初期設定

手順3と同じ。

Redis

構築

  • Docker Composeファイル(docker-compose.yml)へ追記
    以下のセクションを追加する。
    • redis:
      redis:
        image: redis
        volumes:
          - ./redis/data:/data
          - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
        command: redis-server --appendonly yes
        ports:
          - "6379:6379"
        restart: always
  • /dataをマウントすると良い
  • appendonly yesがないとデータが作られない
  • webapplication1:
    linksにサービス名を記載する。
    links:
      - redis

接続確認

WSLから、redis-cliをインストールして確認する。

  • "Ubuntu 18.04 LTS on Windows 10"の場合、
    以下の準備が必要だった。
    sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe"
  • インストール
    sudo apt-get install redis-tools
  • 動作確認
    $ redis-cli -h 127.0.0.1
    127.0.0.1:6379> set mystr1 "abc"
    OK
    127.0.0.1:6379> get mystr1
    "abc"
    127.0.0.1:6379>

分散キャッシュの実装

コッチのほうが新しい。

  • NuGet
    (既定でMicrosoft.AspNetCore?.Allに含まれる)
    • Microsoft.AspNetCore?.Session
    • Microsoft.Extensions.Caching.Redis.Core
  • 以下を実装する。
    • Startup.cs
      using System;
      using Microsoft.AspNetCore.Builder;
      using Microsoft.AspNetCore.Hosting;
      using Microsoft.Extensions.Configuration;
      using Microsoft.Extensions.DependencyInjection;
      using Microsoft.AspNetCore.Http;
      
      namespace WebApplication1
      {
          public class Startup
          {
              public Startup(IConfiguration configuration)
              {
                  Configuration = configuration;
              }
      
              public IConfiguration Configuration { get; }
      
              // This method gets called by the runtime. Use this method to add services to the container.
              public void ConfigureServices(IServiceCollection services)
              {
                  // Redisを設定
                  services.AddDistributedRedisCache(option =>
                  {
                      option.Configuration = "redis";
                      option.InstanceName = "redis";
                  });
      
                  // Sessionを使用する。
                  services.AddSession(options =>
                  {
                      // Set a short timeout for easy testing.
                      options.IdleTimeout = TimeSpan.FromSeconds(10);
                      options.Cookie.HttpOnly = true;
                  });
      
                  services.AddMvc();
              }
      
              // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
              public void Configure(IApplicationBuilder app, IHostingEnvironment env)
              {
                  if (env.IsDevelopment())
                  {
                      app.UseBrowserLink();
                      app.UseDeveloperExceptionPage();
                  }
                  else
                  {
                      app.UseExceptionHandler("/Home/Error");
                  }
      
                  app.UseStaticFiles();
      
                  // Sessionを使用する。
                  app.UseSession();
      
                  app.UseMvc(routes =>
                  {
                      routes.MapRoute(
                          name: "default",
                          template: "{controller=Home}/{action=Index}/{id?}");
                  });
              }
          }
      }
  • HomeController?.cs
    public IActionResult Index()
    {
        HttpContext.Session.SetString("TestId", DateTime.Now.ToString());
        return View();
    }
    
    public IActionResult About()
    {
        ViewData["Message"] = "Your application description page. " + HttpContext.Session.GetString("TestId");
        return View();
    }

テスト

  • 画面
    手順23
  • redis-cli
    127.0.0.1:6379> keys *
    1) "redis69ed5ead-0115-372b-8ef7-665bc769e747"
    2) "mystr1"

PostgreSQL

構築

  • postgresフォルダにpostgres設定ファイルを追加
    ※ postgresフォルダは、Docker Composeファイル(docker-compose.yml)と同じ階層に作成。
    • 初期化用SQLを ./postgres/init/init.sh に作成
      psql -U postgres << "EOSQL"
      
      CREATE DATABASE postgres;
      \c postgres;
      
      --------------------
      -- TABLE: Shippers 
      --------------------
      CREATE TABLE Shippers(
          ShipperID      integer    NOT NULL,
          CompanyName    VARCHAR(40)    NOT NULL,
          Phone          VARCHAR(24),
          CONSTRAINT PK_Shippers PRIMARY KEY (ShipperID)
      );
      
      --------------------
      -- Sequence: ShipperID
      --------------------
      CREATE SEQUENCE TS_ShipperID;
      
      --------------------
      -- INSERT
      --------------------
      INSERT INTO Shippers (ShipperID, CompanyName, Phone) VALUES(nextval('TS_ShipperID'), 'Speedy Express', '(503) 555-9831');
      INSERT INTO Shippers (ShipperID, CompanyName, Phone) VALUES(nextval('TS_ShipperID'), 'United Package', '(503) 555-3199');
      INSERT INTO Shippers (ShipperID, CompanyName, Phone) VALUES(nextval('TS_ShipperID'), 'Federal Shipping', '(503) 555-9930');
      
      EOSQL
  • Docker Composeファイル(docker-compose.yml)へ追記
    以下のセクションを追加する。
    • postgres:
         image: postgres
         volumes:
           #- ./postgres/data:/var/lib/postgresql/data
           - ./postgres/init:/docker-entrypoint-initdb.d
         environment:
           - POSTGRES_USER=postgres
           - POSTGRES_PASSWORD=seigi@123
         ports:
             - "5432:5432"
         restart: always
  • webapplication1:
    linksにサービス名を記載する。
    links:
      - redis
      - postgres

接続確認

WSLから、postgresql-clientをインストールして確認する。

  • インストール
    sudo apt-get install postgresql-client
  • 動作確認
    $ psql -h 127.0.0.1 -d postgres -U postgres -c "select * from shippers"
    Password for user postgres:
     shipperid |   companyname    |     phone
    -----------+------------------+----------------
             1 | Speedy Express   | (503) 555-9831
             2 | United Package   | (503) 555-3199
             3 | Federal Shipping | (503) 555-9930
    (3 rows)

データ・アクセスの実装

  • 以下を実装する。
    • HomeController?.cs
      public IActionResult About()
      {
          string count = "";
          using (var con = new NpgsqlConnection("HOST=postgres;DATABASE=postgres;USER ID=postgres;PASSWORD=seigi@123;"))
          {
              con.Open();
              var cmd = new NpgsqlCommand(@"select count(*) from shippers", con);
              count = cmd.ExecuteScalar().ToString();
          }
          ViewData["Message"] = "Your application description page. " + count + "件";
          return View();
      }

テスト

  • 画面
    手順24

手順5

VS2019で素のプロジェクト・テンプレートを
生成してDocker Composeをやってみた。
手順4までは、VS2017で実行している)

検証

  • 手順25
    手順25
  • 手順26
    手順26
  • 手順27
    手順27
  • 手順28
    手順28

結果

VS2019 では、「コンテナー オーケストレーター」が追加されている。

  • VS2017では「Dockerサポート」でDocker Composeが構成されていたが、
  • VS2019の「Dockerサポート」ではDocker Fileが追加されるダケになった。
  • VS2017 の既定の Docker Compose を VS2019 で使用する場合は、
    「コンテナー オーケストレーター」の Docker Compose を選択する必要がある。

手順6

こちらでは、Dockerファイルを分析する。

Dockerファイル

Dockerファイルとは?

コチラ

生成されたDockerファイル

コチラ

Docker Composeファイル

Docker Composeとは?

コチラ

生成されたDocker Composeファイル

  • docker-compose.yml
    version: '3.4'
    
    services:
      webapplication1:
        image: ${DOCKER_REGISTRY-}webapplication1
        build:
          context: .
          dockerfile: WebApplication1/Dockerfile
  • docker-compose.override.yml
    カスタマイズはコチラに入れろの意味らしい。
    version: '3.4'
    
    services:
      webapplication1:
        environment:
          - ASPNETCORE_ENVIRONMENT=Development
          - ASPNETCORE_URLS=https://+:443;http://+:80
        ports:
          - "80"
          - "443"
        volumes:
          - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
          - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
  • environment:
    環境変数の値を設定する。
    • ASPNETCORE_ENVIRONMENT:最近、既定で使われて無い感ある。
    • ASPNETCORE_URLS=https:使い所が不明感ある。
  • ports:
    • コンテナ・ポートの指定
    • ホスト・ポートはランダムに指定される。
  • volumes:
    不明(要調査)

単体使いを研究する。

Dockerファイル単体使い

  • 生成されたプロジェクトにDockerサポートを追加する。
  • テストの意味合いも含め、コンテナ・ポートを5000に変更する。
  • Dockerfile
    EXPOSE 80 → EXPOSE 5000 に変更する。
    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
    WORKDIR /app
    EXPOSE 5000
    EXPOSE 443
  • Program.cs
    UseUrls?メソッドでエンドポイントを変更する。
    webBuilder.UseStartup<Startup>()
      .UseUrls("http://0.0.0.0:5000/");
  • 以下を実行してビルドする。
  • ソリューションのルート・フォルダに移動して、
    >cd ...\WebApplication1
  • 以下のコマンドでDockerfileでビルドする。
    >docker build -f WebApplication1/DockerFile -t dotnetapp-dev .
  • ビルドの結果(コンテナ・イメージ)を確認する。
    >docker images
    REPOSITORY                             TAG                 IMAGE ID            CREATED             SIZE
    dotnetapp-dev                          latest              beccad7e863b        14 minutes ago      212MB
  • コンテナの実行と動作確認
  • 以下のコマンドでコンテナを実行する。
    コンテナ・ポート(5000)を、ホスト・ポート(8888)にマッピング
    >docker run --rm -p 8888:5000 dotnetapp-dev

Docker Compose単体使い

  • 生成されたプロジェクトに「コンテナー オーケストレーター」の Docker Compose を選択して追加。
  • docker-compose.yml と docker-compose.override.yml が生成されるのでマージする。
    (マージすると言いつつ、以下の部分を修正してある。)
  • 環境変数は削除する(使用しないので)
  • ビルドは削除する(前段で生成されたイメージを使用するので)。
  • コンテナ・ポート(5000)を、ホスト・ポート(8888)にマッピング
    version: '3.4'
    
    services:
      webapplication1:
        image: dotnetapp-dev:latest
        ports:
          - "8888:5000"
          - "443"
        volumes:
          - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
          - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
  • コンテナの実行と動作確認
  • 以下のコマンドでコンテナを実行する。
    docker-compose up -d

手順7

  • ...そう思ったけど、面倒なのでやってない(出来るという情報はキャッチしている)。

続き

Visual Studio Kubernetes Tools

Visual Studio Code Docker extension

サンプル

github.com

MVC_Sample

https://github.com/daisukenishino2/EvaluateAspNetCoreOnDocker/tree/master/MVC_Sample

WebApplication1

https://github.com/daisukenishino2/EvaluateAspNetCoreOnDocker/tree/master/WebApplication1

git clone後にDocker Composeで動かす方法。

スタートアップ・プロジェクトに指定

「docker-compose」をスタートアップ・プロジェクトに指定する。

手順1

「Docker Compose」をデバッグ実行

次いで、デバッグで、「Docker Compose」を指定して、実行する。

手順1

参考

Microsoft Docs

きよくらの備忘録

ONE-RUN

銀の光と碧い空

データストア・コンテナ関連

コチラに移動。

OSSコンソーシアム

Wiki

Blog


Tags: :.NET開発, :.NET Core, :Hyper-V, :仮想化, :コンテナ, :IaC


添付ファイル: fileDebugComposePj.png 333件 [詳細] fileSetCompose2StartUpPj.png 414件 [詳細] file28.png 460件 [詳細] file27.png 412件 [詳細] file26.png 366件 [詳細] file25.png 505件 [詳細] filestructure.png 657件 [詳細] file24.png 573件 [詳細] file23.png 612件 [詳細] file22.png 671件 [詳細] file21.png 623件 [詳細] file20.png 657件 [詳細] file19.png 627件 [詳細] file18.png 633件 [詳細] file17.png 793件 [詳細] file16.png 605件 [詳細] file15.png 596件 [詳細] file14.png 615件 [詳細] file10.png 665件 [詳細] file13.png 591件 [詳細] file12.png 535件 [詳細] file11.png 721件 [詳細] file9.png 609件 [詳細] file8.png 633件 [詳細] file7.png 674件 [詳細] file6.png 646件 [詳細] file5.png 689件 [詳細] file4.png 656件 [詳細] file3.png 633件 [詳細] file2.png 630件 [詳細] file1.png 669件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2022-02-21 (月) 11:07:02 (789d)