diadia

興味があることをやってみる。自分のメモを残しておきます。

djangoとvuejsをcdn以外の方法で共存させる

npmにvueを入れて構築する方法を試みる。djangodjango rest frameworkを使う。

今回気になる事項は、

  1. どうやってdjangoとvueをcdn以外の方法で共存させる環境を構築するのか
  2. どういう仕組でdjangoとvueが連携する仕組みになっているのか
  3. vueを使うにあたり単一ファイルコンポーネント、VueルーターやVuexをどう使っていくかのサンプルがほしい

この辺をまとめられたら良いと思う。

参考資料

いつもお世話になっているNaritoさんのブログ

Narito Blog

インストール — Vue.js

1 vuecliを入れる

naritoさんのブログを参考にしてvuecliを使って構築する方法を試みる。

vueとvuecliは別々に分けられているらしく、ドキュメントが独立しているようだ。

Installation | Vue CLI

$ npm install -g @vue/cli

上記のコマンドでvue cliをインストールする。@vue/cliっていう表現で@が何を表すか気になったがこれは単純に@がついただけで意味はない。パッケージのネームとして@がつけられただけだという認識で良いと思う。

Warning regarding Previous Versions The package name changed from vue-cli to @vue/cli. If you have the previous vue-cli (1.x or 2.x) package installed globally, you need to uninstall it first with npm uninstall vue-cli -g or yarn global remove vue-cli.

2 vueプロジェクトを作成する

$ vue create frontend

プロジェクトを作成するとfrontendというディレクトリが生成される。このディレクトリにはnpmの設定関係のpackage.jsonやnode_modulesディレクトリ、vueのファイル関係を収めているsrcディレクトリが作られている。

プロジェクトを作ったときにはvuexとrouterをプロジェクトで使うように選択する。

3 App.vueを編集する

src以下にApp.vueがプロジェクトをvue作ると生成されている。これはどうやらsrc/main.jsが読み込まれ、このファイル内でApp.vueが読み込まれる。その流れでApp.vueが表示される流れになる。

App.vue

<template>
    <div id="app">
        <Header/>
        <router-view/>
        <Footer/>
    </div>
</template>

<script>
    import Header from "./components/Header"
    import Footer from "./components/Footer"

    export default {
        name: 'app',
        components: {Header, Footer}
    }
</script>

これで起動してみる。

npm run serve

4 単一ファイルコンポーネントを使う

cdnを使ったvueをdjangoで使う場合(MPAの場合)には、サーバーに各リクエストが送信され、返信されたファイルのscriptタグ内にVueを初期化するコードが埋め込まれている構造になっていた。 したがって、返信されるテンプレートには必ずVueの初期化が行われ、そのVue自身にmethodsやmountedを準備してやりvueを動かしていた。
今回のようなSPAの場合には、おそらくVueの初期化は一度限りでそのVueのライブラリを使ってページ遷移を実施する流れなのだと思われる。

5 vue routerをどうやって使うのか

必要な要素

  1. router/index.js内でVueにRouterを追加する(Vue.use(Router);)
  2. router/index.js内のRouter初期化のパラメータとしてroutesを追加する。このroutesがurl解決の役割を担う。
  3. 他のコンポーネントにrouter-link to="hoge"を書いてリンクできるようにセットする。
  4. router-viewタグに遷移先のページがレンダリングされるイメージなのでrouter-viewタグも忘れずにセットする。
routerに関して分かりやすい資料

https://b1tblog.com/2019/10/03/vue-router/

Vue Routerの書き方、使い方について解説 | ELOOP(イーループ) - 開発課題に取り組んで身につける実践型プログラミング学習サービス

routerに関するサンプルコード

GitHub - chiaki1990/vue_router_first_sample

疑問点

ランディングページでデータをデータベースから取得するにはどうするのか? それはvueの
ライフサイクルを利用すれば良い。

mountedに非同期通信の関数を設置しておき、mounted時に非同期通信が開始されるように構成しておけば良い。

(環境構築手順とは別に)vue cliについて少し分かったことをメモ

a. vueがどんな風に動いているか

最初にsrc/main.jsを読み込む流れになると思われる。そのファイルの中で、

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')

ここからApp.vueが読み込まれる。このApp.vueのファイルはtemplateタグを使っている。 templateタグ内では単一ファイルコンポーネントのタグを挿入したりしている。 そしてこの単一ファイルコンポーネントは同一ファイル内のscriptタグ内でimport して呼び出している。 このような流れで動いていると推測される。

<template>
    <div id="app">
        <Header/>
        <router-view/>
        <Footer/>
    </div>
</template>

<script>
    import Header from "./components/Header"
    import Footer from "./components/Footer"

    export default {
        name: 'app',
        components: {Header, Footer}
    }
</script>

<style>
</style>

b. vueとdjangoの組み合わせ方

主要なurlのルーティング機能はvueが担う。
vue routerが画面の内容を制御する。データベースからデータの取り出しはaxios等を使用して非同期処理でデータを取り出し、画面のデータとして反映する。
このデータの取り出しでdjangoのurls.pyを通じてdrfのapiviewを起動させ、データを取り出す仕組みとなっている。

typescriptのみで構築してみた

webpackやnpmの仕組みを分かっていないので一番シンプルな構成でtypescriptを実装する方法を試してみる。

参考資料

以下を記事に従って環境を構築してみた。プロジェクト名はsssとして作成してみた。

最新版TypeScript+webpack 5の環境構築まとめ(React, Vue.js, Three.jsのサンプル付き) - ICS MEDIA

1.下準備

macでnodebrewをインストールしてnode.jsをインストールする。 でnpmがあるbinのpathを通す。

そしてプロジェクトを作成する。

$ cd Desktop
$ mkdir sss

 

2. package.jsonを生成する

以下のコマンドでpackage.jsonを生成する。

$ npm init

3. 必要なパッケージを追加

webpackとそのwebpack-cliの追加。
typescriptとts-loaderの追加。

$ npm install -D webpack webpack-cli typescript ts-loader

4. 各設定ファイルを作成、編集する

package.json、tsconfig.json、webpack.config.jsを扱う。

ファイル名 処理
package.json 編集
tsconfig.json 新規作成
webpack.config.js 新規作成
a. package.jsonの編集内容
{
  "name": "sss", 
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "watch": "webpack -w"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "ts-loader": "^8.0.7",
    "typescript": "^4.0.3",
    "webpack": "^5.2.0",
    "webpack-cli": "^4.1.0"
  },
  "private": true
}
b. tsconfig.jsonの編集内容
$ vi tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "target": "es5", // TSはECMAScript 5に変換
    "module": "es2015", // TSのモジュールはES Modulesとして出力
  }
}
c. webpack.config.jsの編集内容
$ vi webpack.config.js 
module.exports = {
  // モード値を production に設定すると最適化された状態で、
  // development に設定するとソースマップ有効でJSファイルが出力される
  mode: 'development',

  // メインとなるJavaScriptファイル(エントリーポイント)
  entry: './src/main.ts',

  module: {
    rules: [
      {
        // 拡張子 .ts の場合
        test: /\.ts$/,
        // TypeScript をコンパイルする
        use: 'ts-loader',
      },
    ],
  },
  // import 文で .ts ファイルを解決するため
  // これを定義しないと import 文で拡張子を書く必要が生まれる。
  // フロントエンドの開発では拡張子を省略することが多いので、
  // 記載したほうがトラブルに巻き込まれにくい。
  resolve: {
    // 拡張子を配列で指定
    extensions: [
      '.ts', '.js',
    ],
  },
};

この状態でコンパイルしてみる

以下のコマンドでコンパイルを実行する。

$ npm run build

以下の結果が表示される。

> sss@1.0.0 build /Users/user/Desktop/sss
> webpack

[webpack-cli] Compilation finished
assets by status 0 bytes [cached] 1 asset

ERROR in main
Module not found: Error: Can't resolve './src' in '/Users/user/Desktop/sss'

webpack 5.2.0 compiled with 1 error in 187 ms
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! sss@1.0.0 build: `webpack`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the sss@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-10-24T12_07_51_536Z-debug.log

ERROR in main Module not found: Error: Can't resolve './src' in '/Users/user/Desktop/sss'

結果の中盤で以下のようなエラーが表示される。 これはどうやら、sssというプロジェクトにsrcというディレクトリが見つからなかったからエラーが出た。

てことでsrcディレクトリを実行する。

$ mkdir src

ついでsrc以下にコンパイル対象のtypescriptファイルを作成する。

$ vi src/main.ts
$ vi src/sub.ts

再度コンパイルしてみる

$ npm run build

結果

> sss@1.0.0 build /Users/user/Desktop/sss
> webpack

[webpack-cli] Compilation finished
asset main.js 827 bytes [emitted] (name: main)
./src/main.ts 1 bytes [built] [code generated]
webpack 5.2.0 compiled successfully in 1729 ms

分かったこと

コンパイルが成功すると、プロジェクトルートにdistディレクトリが生成される。でjavascriptファイルが出力される。 出力ファイル名はどのようにして決定されるのか? これはどうやらwebpack.config.jsのoutputの項目で定めることができるのではないかと思われるが、実際に試せていない。

またwebpack.config.jsで定められるentryのキーはsrc以下にコンパイル対象のtypescriptファイルを指定する。

entry: './src/main.ts',

サンプルコードをおいておく

https://github.com/chiaki1990/simple_typescript

npmに関して分かったこと

右も左も分からないnpmについて少しずつ試してみて分かったことを書き溜める。

パッケージのバージョンを変更をする

1 package.json内のパッケージのバージョンを編集する。

$ vi package.json

2 変更を反映する

$ npm update

npm install について

npm installコマンドを実行すると、node_modules/.bin/にインストールしたものが追加されることを確認した。 確認時の結果を残しておく。

$ ls node_modules/.bin/

# webpackのパッケージがないことを確認
autoprefixer        envinfo         json5           sass            tsserver
browserslist        errno           nanoid          semver          webpack-cli
cssesc          import-local-fixture    node-which      tsc

そこでwebpackを追加してみる。

$ npm install --save-dev webpack

# webpackの他依存関係のものも追加された
# 再度中身を確認する
$ ls node_modules/.bin/
acorn           cssesc          json5           node-which      sha.js          webpack
atob            envinfo         miller-rabin        rimraf          terser          webpack-cli
autoprefixer        errno           mkdirp          sass            tsc
browserslist        import-local-fixture    nanoid          semver          tsserver

# webpackが追加されていることが確認できる

sh: webpack: command not foundというエラーが出る

node_modules/.bin/にwebpackが追加されていなく、見つからないというエラーが出る場合があった。この場合にはwebpackをインストールすれば良い。

# webpackをインストール
$ npm install --save-dev webpack

ERROR in Entry module not foundのエラーが出る

ERROR in Entry module not found: Error: Can't resolve '/Users/username/Desktop/try_typescript/myapp/static/myapp/js/index.ts' in '/Users/username/Desktop/try_typescript'

このエラーはwebpack.config.js内のentryで定めたコンパイル対象のファイルが見つからないときに表示されるエラーだと分かった。自分の場合には実際にコンパイル対象のindex.tsファイルを作っていなかったのでエラーが出てしまった。

ということでentryで定めたコンパイル対象のファイルを作成すればエラーは解消される

$ vi index.ts

webpackに関するメモ

webpackのバージョン確認方法

$ ./node_modules/.bin/webpack -v


webpack-cli 4.1.0

webpack 5.2.0

webpackのバージョンはどうやら3と4系でwebpack.config.jsで書き方が変わるらしい。

webpack4に更新した時にこけた所まとめ - Qiita

npmのpackage.jsonについて分かったこと

package.jsonには、scripts項目が存在する。

どんなものか?

これはnpm runコマンドのあとに続行するコマンドを定義することができるようになる。

 $ npm init 

言うまでもないが、上記のコマンドを実行するとそのディレクトリにpackage.jsonというファイルが生成される。

少し分かったこと

scriptsには予約語が存在している。

  • start,
  • restart
  • stop
  • test

これが予約語である。しかしながらこれらの中身は決まっていないのでこれらの予約語には自分で中身をあてることになる。

その他にも自分で作ったキーをscriptsに追加する事ができる。

使い方は以下の通り。

$ npm run [スクリプト]

書き方

"scripts": {
    "start": "ng serve",
    "test": "ng test",
    "build": "ng build",
    "copy": "cpx ./dist/* ../server/",
    "precommit": "lint-staged",
    "ng": "ng",
  },
package.jsonのscriptに関する資料

package.json の scripts - Qiita

package.json内にscriptsの記述を設定する(npm-scripts)

typescriptメモ

基本の型(Basic Types, Primitive Type)

TypeScript: Handbook - Basic Types

  • Boolean
  • Number
  • String
  • Array
  • Tuple
  • Enum
  • Unknown
  • Any
  • Void
  • Null and Undefined
  • Never
  • Object

アロー関数の戻り値の型定義について理解していない。

環境構築

書いたtypescriptをjavascriptにトランスパイルするのでnpmが必要になる。

とりあえずdjnagoとtypescriptを共存させる環境を作ってみる。

参考とする記事:

【Django3.0】TypeScript と Sass でフロントエンドを書く【webpack4】 - AI can fly !!

環境

# 環境作成コマンド
conda create -n try_typescript python==3.8
source activate try_typescript
pip install django==3.0
# OSバージョン
sw_vers

ProductName:    Mac OS X
ProductVersion: 10.15.6
BuildVersion:   19G73
# pythonバージョン
python -V

Python 3.8.5
# djangoバージョン
django-admin --version

3.0

環境構築コマンド

基本的なdjangoの下準備のコマンド(特にtypescriptに関係ない部分)

# プロジェクトの作成とその中でアプリを作成

mkdir try_typescript
cd try_typescript/
django-admin startproject config .
python manage.py startapp myapp
# config/settings.pyの編集(作成したmyappをプロジェクトに反映させる)

vi config/settings.py

# 以下のように追加

   INSTALLED_APPS = [
       "django.contrib.admin",
       "django.contrib.auth",
       "django.contrib.contenttypes",
       "django.contrib.sessions",
       "django.contrib.messages",
       "django.contrib.staticfiles",
       "myapp", # コレを追加
   ]
# 親urls.pyの編集

vi config/urls.py

# 以下のように編集
from django.contrib import admin
from django.urls import path
from django.urls import include # コレを追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path("myapp/", include("myapp.urls")), #コレを追加
]
# 子urls.py(myapp/urls.py)の編集
vi myapp/urls.py

# 以下のように編集

from django.urls import path
from . import views

app_name = "myapp"
urlpatterns = [
  path('', views.index, name='index'),
]
# views.pyを編集

vi myapp/views.py

# 以下のように関数を設置する
from django.shortcuts import render

# Create your views here.

def index(request): 
    context = {}
    context["key"] = "value"
    return render(request, "myapp/index.html", context)
# myapp/views.indexが返すテンプレートを作成する

mkdir -p myapp/templates/myapp
vi myapp/templates/myapp/index.html

# 以下のように記述

<h1>Hola!!</h1>
<p>{{ key }}</p>

開発サーバーを起動させて動くか確認

python manage.py runserver

# 起動後ブラウザで"localhost:8000/myapp"にアクセスし、作成したテンプレートが表示されればオッケイ
公開リポジトリ

GitHub - chiaki1990/base_dev_env: typescriptやvuejs環境構築練習用のベースリポジトリ

このコードをベースにtypescriptを共存させる手続きを加えていく

次にnpmをベースのコードに追加する

方法は以下を参考に。。。
node.jsのインストール方法(mac) - diadia

ベースのコードに追加する手続き(概要)

typescriptを使えるためにする手続きは以下の通りである

  1. npmコマンドを使って必要なパッケージを予めインストールする下準備
  2. 設定ファイルを調整する

上記の設定ファイルは、typescriptの詳細コンパイル設定を司るtsconfig.json、webpackの設定を司るwebpack.config.jsそしてnpm initで生成されるpackage.jsonである。

npmコマンドを使って必要なパッケージを予めインストールする下準備

$ cd try_typescript
$ npm init

npm init コマンドはコマンド実行したディレクトリにpackage.jsonファイルを作成する。

もっと詳しくは以下を参考に。。。

フロントエンド開発の3ステップ(npmことはじめ) - Qiita

必要なnpmパッケージをインストールするコマンドとその理由

必要な npm パッケージをインストールする。

# webpackとwebpack-cliのインストール
$ npm install --save-dev webpack webpack-cli

webpackにはいろいろな役割があるがコア機能はモジュールバンドラである。複数のjavascriptファイルを依存関係に従って一つのファイルとしてまとめ上げる。なぜ複数のjavascriptかといえばファイルごとに分けないとわけが分からなくなるから。でもファイルを分割すると読み込み順序に気をつける必要がある。この問題をwebpackは解決する。

# typescriptとtypescriptローダーのインストール
$ npm install --save-dev typescript ts-loader

webpackの機能にローダーの機能があり、それを実現すためのローダー及びローダー対象をインストールする感じなのだろう。
上記はtypescript系のコマンド。

# saasとsassローダーのインストール
$ npm install --save-dev sass sass-loader

webpackの機能にローダーの機能があり、それを実現すためのローダー及びローダー対象をインストールする感じなのだろう。
上記はsass系のコマンド。

# CSS関係
$ npm install --save-dev css-loader mini-css-extract-plugin postcss-loader autoprefixer

webpackの機能にローダーの機能があり、それを実現すためのローダー及びローダー対象をインストールする感じなのだろう。

@import などの依存関係の解決に css-loader 、CSS ファイルを出力するために mini-css-extract-plugin 、ベンダープレフィックスを付与するために postcss-loader と autoprefixer をインストールします。 ちなみに、ベンダープレフィックスの付与は必須ではありません。

この辺は深堀りしたいときにとっておく。

typescript初期化(tsconfig.jsonの作成)

# tscの実行
$ npx tsc init

(# 以下のコマンドでも可)
$ ./node_modules/.bin/tsc  --init 

npxはどうやらnpmでインストールしたパッケージを実行する際に使われる。npmでインストールしたパッケージを実行する際には"npm パッケージ"というコマンドは使えず、" ./node_modules/.bin/(パッケージ名) "のような形でパッケージを実行しなければならなかったんだけれども、npxを使うことでそういったパスを考えなくても直接パッケージを実行する事ができるメリットがあると分かった。npxコマンドが気に入らなければ、" ./node_modules/.bin/(パッケージ名) "を使ってtypescriptのパッケージを実行すれば良い。
tscを実行すると、tsconfig.jsonファイルが生成される。

このファイルの設定内容や設定オプションについても調べておく必要があるね。。。ちょっと大変。

tsconfig.jsonについてはどうやらtypescriptのコンパイルについて詳しく設定するためにあるファイルなのだろうと把握した。この設定詳細は公式ドキュメントにもあるし、qiitaの記事にもある。またtsconfig.jsonについて説明した記事も発見した。

tsconfig.jsonの設定項目の参考資料

TypeScript: Handbook - tsc CLI Options

TypeScript: TSConfig Reference - Docs on every TSConfig option

tsconfig.jsonの説明記事

tsconfig.jsonの全オプションを理解する(随時追加中) - Qiita

webpack.config.jsの設定内容

これは参考記事の内容をそのまま利用させてもらった。ここも詳しく理解しておく必要があるけどとりあえずdjango上でtypescriptを使えることが第一目標なので、必要になったら詳しく見ることにする。

# 手動でwebpack.config.jsを作成する
vi webpack.config.js
const path = require('path');
const autoprefixer = require('autoprefixer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // モード
  mode: 'development',

  // エントリーポイント
  entry: {
    '[application_a]/static/[application_a]/js/main': path.resolve(
      __dirname,
      '[application_a]/static/[application_a]/js/index.ts'
    ),
    '[application_b]/static/[application_b]/js/main': path.resolve(
      __dirname,
      '[application_b]/static/[application_b]/js/index.ts'
    )
  },

  // ファイル出力先
  output: {
    // 出力先ディレクトリ
    path: __dirname,
    // 出力ファイル名
    filename: '[name].bundle.js',
  },

  // ソースマップ
  devtool: 'cheap-module-eval-source-map',

  // ローダー
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
      },
      {
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true,
            },
          },
          {
            loader: 'css-loader',
            options: {
              url: false,
              sourceMap: true,
              importLoaders: 2,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [autoprefixer()],
            },
          },
          {
            loader: 'sass-loader',
            options: {
              implementation: require('sass'),
              sassOptions: {
                includePaths: ['./node_modules'],
              },
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },

  // モジュール解決
  resolve: {
    extensions: ['.ts', '.js'],
  },

  // プラグイン
  plugins: [
    new MiniCssExtractPlugin({
      moduleFilename: ({ name }) =>
        `${name.replace('/js/', '/css/')}.bundle.css`,
    }),
  ],
};

npm run biuldを実行するためにコンパイル対象ファイルを作成する

webpack.config.js内でentryを定めたが、それがコンパイル対象のファイルとなる。定めたファイルがない状態でnpmコマンドでコンパイルを実行しようとすると、エラーが生じてしまうことを確認した。したがって空ファイルでもいいからコンパイル対象のファイルを作成する。

vi index.ts

コンパイル実行

$ npm run build

コンパイルが成功する。

今回のコードをgithubに残す

https://github.com/chiaki1990/django_typescript_sample

今後の課題

typescriptをdjangoの中で使えるようになったわけだけれども実際にどうやって使っていくのかは分かっていない。 だからrest frameworkを使ってvueを使っていくのかとかそのへんを再度調べて形にしておきたい。

コマンドの種類について調べておきたい

npmはパッケージの管理を行うもの npxはパッケージを実行するためのもの

npm,npxについて解説してある参考資料

npm 5.2.0の新機能! 「npx」でローカルパッケージを手軽に実行しよう - Qiita

理解すると良いだろうということ

package.jsonで何を定めて、何ができるようになるかということ。どんな決まりで記述するのか等

djangoの構成を理解しておきたい。

エントリーポイントを後々理解しておきたい。 今回の場合はエントリーポイントは各アプリケーション下に存在する。index.tsという形で使われる。

webpackの具体的な用途

なぜ初心者は webpackが解らないのか?- Why can’t you understand the webpack? -

webpackの基本的な要素

  • Entry(エントリー)
  • Output(アウトプット)
  • Loader(ローダー)
  • Plugins(プラグイン)

webpackは以下のurlが簡潔に説明されていてわかりやすい。

www.slideshare.net

バックエンドでKotlinをさわる

お世話になった本は以下。

入門!実践!サーバーサイドKotlin (技術の泉シリーズ(NextPublishing)) | 横山 恭大 | 工学 | Kindleストア | Amazon

androidアプリケーションでKotlinは使っていたけれども、主戦場にしたいバックエンドでKotlinが使えてないのは残念なので触ってみる。あとwebフレームワーク自体はdjangoしか触ったことがないので、他のフレームワークを触ることでどういう考え方が共通しているのか確認したい思惑がある。

Springframeworkについて

spring frameworkフレームワークを束ねているものらしい。
その中から使いたいフレームワークを選択して構築していくらしい。
で、選択したフレームワークを実際に使えるように設定するには諸設定が必要になるようでそれが大変のよう。
その諸設定を簡単に解決する方法としてSpring Bootというフレームワークを使うようだ。これはSpring Initializerでファイルを生成し、それを使うとすぐ開発できるようになるといったもののようだ。

ビルドシステムについて

ビルドシステムはライブラリの依存関係を調整する機能とコンパイルする機能を持つ ビルドシステムにはAnt, Maven, Gradleといったものが存在する。JVM系の言語で開発する際にGradleといったビルドシステムにお世話になるのかなあと少ない経験から推測中。。。

プロジェクトの始め方

spring bootを使う場合プロジェクトの開始方法としてSpringInitializrを使う方法がある。 これは
https://start.spring.io/ にアクセスしてプロジェクトの条件を設定してやり、zipファイルを生成し、ダウンロードする。これを解凍し、そのディレクトリをIDEで開く(import)形で使用することだと分かった。 設定項目であるが、

設定項目 選択肢 自分が今回設定した内容
project Maven project またはGradle project Gradle projectを選択
language Java, GrrovyまたはKotlin Kotlinを選択
Spring Boot 様々なバージョンがある バージョン2.3.4を選択

今気になったこと

  • どういう感じでリクエストを受け入れるのか。 -> RequestMappingアノテーションを使ってurlを設定
  • どうやってhtmlを返すのか?そしてそれはどこに格納するのか? -> いわゆるControllerを使うと設定できる。当初はurls.py, views.pyといったファイルで明確に分けられているものかと思ったけどそうではなく、url解決と何を返すかは同一ファイルで併存する形式を取る。

@RestControllerについて

「@RestController」は、viewに遷移せずメソッドの戻り値がそのままHTTPリクエストのレスポンスとなります。このため、APIのようにデータだけ取得したい場合に用いられることが多いです。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.739-741). Kindle 版.

つまり@RestControllerはDjangoでいうHttpResponseクラスにが匹敵するものであると認識すればよいだろうか。

@RequestMappingについて

「@RequestMapping」についてです。こちらはリクエストを受け付けるパスを指定します。今回は「/」なので「http://localhost:8080」で受け付けます。「/hoge」にすると「http://localhost:8080/hoge」になります。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.744-746). Kindle 版.

これはDjangoでいうurls.pyに該当するところだと思えば良いと思う。

djangoの場合はurlsの解決とその挙動を厳密に分けていたわけだけれどもSpring Bootはそこをきっちり分けていないことが特徴だとわかる。 またアノテーションを付けてurlsの設定をする辺りはretrofit2のような感じで、全然違和感はない。むしろ親しみがあるやり方だ。

import org.springframework.webのimportが上手くいかない

build.gradle.ktsに依存関係のライブラリを記述するスペース(dependencies)があるが、そこに下記の修正を加えること。

//implementation("org.springframework.boot:spring-boot-starter")        //コメントアウト
implementation("org.springframework.boot:spring-boot-starter-web") //追加

上記のライブラリを追加することで@RestControllerや@RequestMappingを使えるようになる。

Thymeleafについて

Spring Bootに使われるThymeleafはHTMLを返す役割を果たすことが分かった。djangoでいうrender関数のようなものだと現状理解して進めている。

@Controllerについて

HTMLを返す役割を担う。return でhtmlファイルを返す。

@GetMappingについて

RequestMappingとGetMappingの使い分けについてだけれども、おそらくRequestMappingの方はリクエストメソッドのGET以外に対応できると思われるけど、GetMappingはGetのみの対応になると思われる。GetMappingは@RequestMapping(method = RequestMethod.GET)のショートカットであるという説明している文章も見た。

Spring Boot組み込みのデータベースを使う際の手続き

  • 組み込みのデータベース=H2database
  • javaでDB操作(H2database) -> JPAを使う
プロジェクト作成時に行うこと

Spring Initilizerでプロジェクト作成時に以下を追加する。

  • Spring Web
  • Thymeleaf(template engine)
  • H2 Database(SQL)
  • Spring Data JPA(SQL)
DB接続情報

DB接続情報はapplication.propertiesに記載する。

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password
DBテーブルを作成するには?

djangoの場合はmodels.pyに作成したいmodel(テーブル名)をmodels.Modelを継承した形式で定義した。 一方kotlinを使ったH2databaseの場合には、data classを使って定義する。

package com.example.hello_db_app.demo_db

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity //このアノテーションがあるとテーブルが作成される
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int = 0,
        var name: String = ""
)

@Autowiredについて

依存性注入のアノテーションであり、該当するクラスのインスタンスフレームワークが自動でnewして代入してくれます。今回は「UserRepository」のインスタンスになります。こうすることでテストを書くときにモックに置き換えやすくなったり、共通処理を追加しやすくなったりします。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.915-918). Kindle 版.

とのことだったが、よく分からなかった。後にわかるだろうってことで今はパスする

@PostMappingについて

GetMappingと同じ類。RequestMappingでかつPOSTメソッドのみに対応するもの。

@RequestParamについて

これは指定した引数がリクエストパラメーターであることを宣言します。そのため「http://localhost:8080?id=10」として「@RequestParamid:Int」とするとidに10が渡ってきます。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.922-924). Kindle 版.

djangoの場合では

class HogeView(View):
    def get(self, request, *args, **kwargs)
        ...

のような形でself.requestから取り出したり、urlパラメーターから取り出す処理をしていたけど入れ込むことに関しては特に意識しないでやっていた。 Spring Bootの場合には、リクエストパラメータを指定しなければいけないってことが違いであり、ポイントなのであろう。

Spring Bootでテストコード

やること。

  • テストDBの準備
テストDBの準備

DBは/src/test/resources/application.propertiesの形で設置する。application.propertiesはオリジナルのファイルをコピーしてくれば良い。
ファイルには以下の内容を記述する。

spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/test_db_example
spring.datasource.username=username
spring.datasource.password=password

spring.jpa.hibernate.ddl-autoの値をcreateにすることでテスト実行時に新たなテーブルが作成され、すべてのテストを0から始めることができる。spring.datasource.urlの値はテストデータベースのurlでこれは任意?の値で良いと思われる。

@RunWith(SpringJUnit4ClassRunner::class)について

これはテスト用のライブラリにJUnit4を使用するという宣言です。SpringBootはデフォルトでJUnit4が組み込まれています。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1005-1006). Kindle 版.

@SpringBootTestについて

これはテスト時にSpringBootが必要な機能を使用する宣言です。今回のテストコードではモックの生成に使用します。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1006-1008). Kindle 版.

@Beforeについて

テストメソッドが実行される前に呼び出されることを宣言します。これによりテスト前の下準備を済ませます。今回はモックを生成しています。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1008-1010). Kindle 版.

@Columnについて

このアノテーションはエンティティの定義(モデル)の定義で使われる。クラスのプロパティ名とテーブルのフィールド名が異なるときに使われる。

server_side_kotlin_bbs_sample/Article.kt at master · fortegp05/server_side_kotlin_bbs_sample · GitHub

package com.example.app.bbs.domain.entity

import java.util.*
import javax.persistence.*

@Entity
data class Article (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id : Int = 0,
        var name : String = "",
        var title : String = "",
        var contents : String = "",
        @Column(name = "article_key")
        var articleKey : String = "",
        @Column(name = "register_at")
        var registerAt : Date = Date(),
        @Column(name = "update_at")
        var updateAt : Date = Date(),
        @Column(name = "user_id")
        var userId : Int? = null
)

Spring BootのDBまわりについて推測

Spring BootでもORMをやりたい場合には、djangoっぽい下準備が必要になる。 まずモデル。モデルはテーブルに一致したモデルを作成しなければならない。このモデルに対応する言葉としてSping BootではEntityという言葉が使われているけれども要はmodelってことなのだろう。 djangoの場合はモデルさえ作ったら、views.pyで各モデルのインスタンスを呼び出すなり、初期化生成して、その後save()メソッドを使ってデータを永続化していた。Spring Bootではこの点で少し異なりEntityを定義しただけではデータを永続化する処理をいきなりすることができない。そこで必要な概念がRepositoryという概念。これは要するに上で作ったEntityを利用したインターフェースを作り、ORMを利用する下準備をする。djangoではdjango.models.Modelを継承したクラスさえ作れば良かったけれども、Spring Bootではまずテーブルに対応するEntityを作成する。そしてデータ操作を可能にするために作成したEntityを利用してRepositryを作る。こういった差異があるのではないかと現状推測している。 このRepositoryには種類があり、JpaRepositoryとCrudRepositoryの種類が存在していることを確認している。

参考

SpringBoot JPARepository 使ってみた | Solowareの技術目録

応用情報技術者試験の参考書を読むのメモ

掛け算とか割り算はどうやって実現しているか?
-> シフト演算を使って掛け算や割り算を行っている。 シフト演算...ビットの並びをまとめて左にずらしたり、右にずらしたりすることで元の値の2倍や1/2倍という計算を実現している。

テストケース設計手法:

同値分割 これはテストデータを自分で用意してそのデータを選んでテストして実際の結果を確認する手法なのだろう。テストデータは有効範囲のデータ、有効範囲外のデータを準備する。

UMLについて

参考:https://www.itsenka.com/contents/development/uml/

UML(Unified Modeling Language)は、様々な開発現場で使用されている設計書の書式を統一する目的で規定された言語で、1997年にOMG ( Object Management Group ) により標準化されました。 但し、UMLによる標準化はあくまで表記方法であって、開発手法の方法論ではありません。

表記種類 名前 説明
構造に関する表記 クラス図(Class Diagram) クラス構造を表現します。
構造に関する表記 オブジェクト図(Object Diagram) クラスをより具体化したオブジェクトで表現します。
構造に関する表記 パッケージ図(Package Diagram) クラスなどをグループ化し整理された関係を表現します。
構造に関する表記 コンポジット構造図(Composite Structure Diagram) クラスやコンポーネントの内部構造を表現します。
構造に関する表記 コンポーネント図(Component Diagram) コンポーネントの内部構造ならびにコンポーネント間の依存関係を表現します。
構造に関する表記 配置図(Deployment Diagram) システムの物理的な構成を表現します。
振る舞いに関する表記 ユースケース図(Use Case Diagram) ユーザなど外部からの要求に対するシステムの振る舞いを表現します。
振る舞いに関する表記 アクティビティ図(Activity Diagram) システムの実行における処理の流れや状態遷移を表現します。
振る舞いに関する表記 状態マシン図(State Machine Diagram) イベントにより引き起こされるオブジェクトの状態遷移を表現します。
振る舞いに関する表記 シーケンス図(Sequence Diagram) クラスやオブジェクト間の応答を時系列で表現します。
振る舞いに関する表記 コミュニケーション図(Communication Diagram) クラスやオブジェクト間の関連と応答を視覚的に表現します。
振る舞いに関する表記 相互作用概要図(Interaction Overview Diagram) 相互作用図(ユースケース図やシーケンス図など)を構成要素として、より広域な処理の流れを表現します。
振る舞いに関する表記 タイミング図(Timing Diagram) クラスやオブジェクトの状態遷移を時系列で表現します。

回路について

回路には組み合わせ回路と順序回路という2種類が存在する。

半加算器と全加算器 |加算器|説明| |------|-----| |半加算器|下位からの桁上りを考慮しない| |全加算器|下位からの桁上りを考慮する|

プロキシサーバの役割:

プロキシサーバは、内部ネットワーク内の端末からの要求に応じてインターネットへのアクセスを代理する装置です。単にプロキシともいいます。プロキシサーバを設置すると、キャッシュ機能によるレスポンス向上、認証やフィルタリングによるセキュリティ、内部ネットワークの秘匿化などの効果を期待できます。

NAPT

NAPT(Network Address Port Translation)は、プライベートIPアドレスグローバルIPアドレスの相互変換するNATの考え方にポート番号を組み合わせた技術です。

ファジング

ファジング(fuzzing)とは、検査対象のソフトウェア製品に「ファズ(英名:fuzz)」と呼ばれる問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで(未知の)脆弱性を検出する検査手法

ユーザインタフェースユーザビリティを評価:

RDBMSのロック: 2種類ある。共有ロック、専有ロック。

種類 説明
共有ロック データを読込むときに使うロックで、資源がこの状態の場合は他のトランザクションによる更新処理ができなくなる(読込みは可能)。
専有ロック データを更新するときに使うロックで、資源がこの状態の場合は他のトランザクションによる読込みや更新ができなくなる。

DB

主キーとするために列は制約(条件)を満たす必要がある。

制約種類 説明
一意制約 データに重複がなく必ず位置であること
NOT NULL制約 NULL値を許容しない

外部キーに関する制約

制約種類 説明
参照制約 外部キーに含まれる値は、参照先となる表の列内に必ず存在しなければならない

これは何かというと、外部キーの値が存在しないものを他のテーブルで参照するために外部キーを登録したり、外部キーの値が他のテーブルで使われているにも関わらず、当該外部キー値を削除するような行為を制限するということである。

正規化

正規化の目的...データに矛盾や重複を生じさせないこと

正規形種類 説明
第一正規形 繰り返し部分を分離させて、独立したレコードとした表
第二正規形 部分関数従属している列を切り出した形の表
第三正規形 繰り返し部分を分離させて、独立したレコードとした表

関数従属と部分関数従属の用語

用語 説明
関数従属 主キーが決まれば列の値が一意に定まる関係
部分関数従属 複合キーの一部の項目だけで列の値が一意に定まる関係

ノイマン型コンピュータとは

ノイマン型 - Wikipedia

ノイマン型(ノイマンがた、英: von Neumann architecture)は、コンピュータの基本的な構成法のひとつである。

ノイマン型コンピュータとは何? Weblio辞書

ノイマン型コンピュータでは、記憶部に計算手続きのプログラムが内蔵され、逐次処理方式で処理が行われる。中央演算部、制御部、記憶機構、入力部、出力部の5つの部分からなり、プログラム実行時には、主記憶装置から演算制御装置へ命令やデータが記憶レジスタを経由して転送され、命令は、命令アドレスレジスタにセットされたアドレスに沿って逐次的に実行される。今日の一般的なコンピュータシステムのほとんどが、このノイマン型である。

レジスタとは

レジスタとは - IT用語辞典 e-Words

レジスタとは、マイクロプロセッサ(MPU/CPU)内部にある、演算や実行状態の保持に用いる記憶素子。最も高速な記憶装置だが、一般的なCPU製品で数個から数十個(容量に換算して数十バイト程度)と数が限られる。GPUなど特殊なプロセッサでは数万個(数百キロバイト)のレジスタを内蔵するものもある。

つまりCPU内部にある記憶装置。

で、レジスタって一言で言っても色んな種類がある。

プログラムカウンタはプログラムレジスタと呼んだり、命令ポインタとも呼ぶことがあるらしい。

命令レジスタは実行中の処理を記憶するレジスタ。 インストラクションレジスタ(IR)とも言うらしい。 プログラムカウンタから次にやることが書いてあるメモリの場所を取得して、やることをメモリから読み出して記憶する。メモリからやることを読み出すことをフェッチと呼び、メモリからフェッチしたやることを解析することをデコードと言う。

xtech.nikkei.com

コンピュータの命令実行順序

  1. 命令フェッチ
  2. 命令の解読
  3. オペランド読み出し
  4. 命令の実行

みんなのコンピュータサイエンスを読んでみたときのメモ

**通読2回目 一回目では何を言っているのかほとんど理解できなかった。したがって読書で調べたこと、メモを残しておく。

第1章

「プールが温かければ、私は泳ぐ」の意味は、私は温水でしか泳がない、を示すわけではない。「冷たいプール」に関しては何も保証していません。
両条件を表現するには、双条件(biconditional)を使います。A↔B:プールが温かい場合にのみ、私は泳ぐ

2章 計算量

時間計算量とはなにか。
計算量の増加を記法 O記法で表現する。
計算量指数関数的に増加するアルゴリズムを避ける。

時間計算量:

時間計算量はT(n)と表現され、大きさがnの対象を処理するときにアルゴリズムが要する演算数を示します。

WladstonFerreiraFilho. みんなのコンピュータサイエンス (Japanese Edition) (Kindle の位置No.667-668). Kindle 版.

計算量は、書いたコードがどれだけ計算することになるかを計算すればよい。 で、計算する対象が大きくなるとどれだけ時間がかかるかは、T(n)を使って計算する。N/Mみたいな形で計算し、前提となる対象と比べて増えた計算対象がどれだけ時間がかかるか見積もることができるようになる。

計算にかかる時間について

多くのアルゴリズムでは、対象の大きさが増加すると、演算の数が急増します。たとえば、私たちのカードソートのアルゴリズムでは、ちょっとした演算で26枚のカードをソートできますが、2倍の52枚のカードではソートに4倍の演算が必要です。

WladstonFerreiraFilho. みんなのコンピュータサイエンス (Japanese Edition) (Kindle の位置No.656-659). Kindle 版.

時間計算量はT(n)で表される。

ある計算を行う際に計算対象によっては計算の時間が変わることがある。 計算の対象は最善、最悪、平均の場合に分けられ、最悪の対象(サンプル)を用いると常に信頼できる基準が得られる。

大きさnの対象に対して、要する演算数をカウントすることで、アルゴリズムの時間計算量を調べることができます。

WladstonFerreiraFilho. みんなのコンピュータサイエンス (Japanese Edition) (Kindle の位置No.686-687). Kindle 版.

本書では選択ソートを用いてアルゴリズムの時間計算量を調べる方法を説明している。
自分は選択ソートを知らないのでこれが何なのかわからなかった。

選択ソートに関する説明:

選択ソート(基本選択法)とは - IT用語辞典 e-Words

選択ソート : アルゴリズム

選択ソート(Selectionsort)を丁寧に 解説してみた by Python - アメリカの大学で奮闘中

選択ソートをpythonで表現する

def selectionsort(l):
    n = len(l)
    for index in range(n-1):
        least = index
        for x in range(index + 1, n):
            if l[x] < l[least]:
                least = x
        tmp = l[index]
        l[index] = l[least]
        l[least] = tmp
    return l

if __name__ == '__main__':
    from random import shuffle
    l = list(range(0, 15))
    lcopy = l[:]
    shuffle(l)
    print('Unsorted')
    print(l)
    assert l != lcopy
    print('Sorted')
    print(selectionsort(l))
    assert l == lcopy
コードの説明

最初のforでリストのインデックスを定める。そのインデックスに未修正のリストから探しだした最小値を配置していく。こうすると、左から順番に最小値が並べられていく。

計算量の計算について

読んでも直感的にわかることはなかった。ここについては他の資料を探して以後理解に務めることにする。

戦略

アルゴリズム設計の戦略について考える章である。

  1. 反復処理
  2. 再帰処理
  3. 総当り攻撃
  4. バックトラック
  5. 発見的解法
  6. 分割統治法

反復処理による戦略は、条件が満たされるまで、たとえば、for、whileを使って処理を繰り返すことにあります。繰り返しの各処理は反復と呼ばれます。対象の最初から最後まで各要素に対して同じ演算を行うときに最適です

WladstonFerreiraFilho. みんなのコンピュータサイエンス (Japanese Edition) (Kindle の位置No.872-874). Kindle 版.

用語メモ:べき集合

参考: 有限集合の例でべき集合を求めるよ | 数学の星

べき集合とは部分集合の全体の集合のこと。例えば、{1,2,3}という集合があれば部分集合は以下になる。 {}, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}。
そして冪集合は{}, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}である。

べき集合を生成するスクリプトの書き方

Python 3.x - 冪集合の作成(Python3)|teratail

再帰処理

再帰処理を使う場合にはメモリを消費することになる。また直感的にわかりにくい。しかし高速に処理することができる。

総当たり攻撃

4章 データ構造について

重要な用語として抽象データ型(abstract data type, adt)とデータの構成(structure data)があることが分かった。

抽象データ型(AbstractDataType:ADT)は、所定のデータ型に対して意味をなす操作群の仕様に相当します。ADTは所定の型のデータを格納した変数に対して機能するインターフェイスとして定義され、これらによってデータがメモリ上にどのように配置されているか、どのように操作されているかという詳細はすべて隠されます。

WladstonFerreiraFilho. みんなのコンピュータサイエンス (Japanese Edition) (Kindle の位置No.1491-1494). Kindle 版.

おそらくこの文章はデータ型(文字列型、ブーリアン型、数値型)に対して操作する際に使われるインターフェースのことを抽象データ型と呼んでいると思われる。この抽象データ型があるおかげで、各データ型のデータ操作は一定の名前(操作名)でラッピングすることができるといったところか? データ型に対して抽象データ型が一対一対応していると思ったけど、この認識は誤りか??そもそも再利用するってどういう事??

抽象データ型の例

  • 基本データ型
  • スタック
  • キュー
  • 優先度付きキュー
  • リスト
  • ソート済みリスト
  • マップ(辞書)
  • 集合

データ構造(data structure)は、データ処理モジュールでのADTの実装手段を提供します。

AWS: Error creating VPCが出た

terraform applyを実行した結果以下のエラーが表示された。

Error: Error creating VPC: VpcLimitExceeded: The maximum number of VPCs has been reached.
    status code: 400, request id: c5e55b9d-d44d-437b-bcd3-2303794c1c46

stackoverflow

上記リンク先には以下のことが書いてある。

The soft limit for VPC is 5 per region , how many have you used ? – Kush Vyas Aug 29 '18 at 6:30

実際リージョンでVPCを5個作っていた。削除した結果VPCを新たに作成できた。

以下awsドキュメント

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/amazon-vpc-limits.html

terraform 既存のリソースを使うことについて

今まで勝手に思ってたことにterraformでインフラを立ち上げるときには、terraform内でリソースを生成し、それを組み合わせて環境を作り上げないといけないと思っていた。 しかしながら一度作成したリソースを消さなければ再利用することができる事がわかった。

再利用する場合には、terraformのコード内でリソースIDをハードコーディングすることで呼び出すことができる。

resource "aws_instance" "example" {
  ami                    = data.aws_ami.example.image_id 
  #vpc_security_group_ids = [aws_security_group.example.id]
  vpc_security_group_ids = ["sg-04ea23f14c90378ab"]
  #subnet_id              = aws_subnet.example.id
  subnet_id              = "subnet-0fe3b2c19c920dce4"
  key_name               = "id_rsa_ec2"
  instance_type          = "t2.micro"

  tags = {
    Name = "example"
  }

  user_data = <<EOF
EOF
}

vpc_security_group_idsやsubnet_iの値はterraform内で作成したリソースをid値として取り出すしかないと思っていたが、上記のようにidをマネジメントコンソールから確認しハードコーディングする。このようなやり方でも動くことを確認した。

ECRにpushとpullを試みたときのメモ

ECSを使うためにはどうやらECRが必要だということが判明した。
ECRはdockerhubのようなdocker Imageのコンテナのリポジトリサービスであるようだ。

とりあえずpushとpullをできるようにしたい。

ドキュメントを読んでみると、ECRにpush, pullをするためには認証トークンが必要になると書かれていた。 またAmazon ECRを利用するためには事前準備としてdocker及びaws cliのインストールが必要だとわかった。

結果的にyoutubeの黒川さんの動画が一番わかりやすかったので、それに沿ってメモを残す。
youtube動画

ECRにアクセスするためにIAM ロールをEC2にアタッチする

IAMからロールを作成する。ec2に対して選択し、権限は以下の2つである。

  1. AmazonECS_FullAccess
  2. AmazonEC2ContainerRegistryFullAccess

上述の権限をもつロールをec2にアタッチする。

ECRにpush, pullするためにloginを実行する

# 以下を入力する(アカウントのリージョンに応じてregionの値を変更する)
aws ecr get-login --no-include-email --region us-west-2

# すると文字列が返されるので”docker login -u AWS -p ... ”の最後までをコピペする。

# Login Succeeded と表示される

ECRにpushする

ECRのリポジトリには"プッシュコマンドの表示"を選択すると、imageのビルドとプッシュ方法が書かれている。これに従ってpushすれば良い。

EC2インスタンスに複数のアパッチコンテナを起動して接続を試みる

今まで一つのサーバーにwebサーバーなどのソフトウェアが一つずつ入っている状況が自然なことだと考えてきたので、同じソフトウェアが同時に動く環境を用意し、いわゆるコンテナという概念を体感してみた。

方法としてはec2インスタンスにcontainerを3つ追加する。で一つは単純に既存のbridgeにコンテナを起動する。 残りの2つは、新しく作ったbridgeにコンテナをつなげる。

以下はec2インスタンス起動用のterraform設定ファイルである。

vpc.tf

# VPC

resource "aws_vpc" "example" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true # DNS解決を有効化
  enable_dns_hostnames = true # DNSホスト名を有効化

  tags = {
    Name = "example"
  }
}


# Subnet

resource "aws_subnet" "example" {
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-west-2a"
  vpc_id            = aws_vpc.example.id

  # trueにするとインスタンスにパブリックIPアドレスを自動的に割り当ててくれる
  map_public_ip_on_launch = true

  tags = {
    Name = "example"
  }
}


# Internet Gateway

resource "aws_internet_gateway" "example" {
  vpc_id = aws_vpc.example.id

  tags = {
    Name = "example"
  }
}


# Route Table

resource "aws_route_table" "example" {
  vpc_id = aws_vpc.example.id

  tags = {
    Name = "example"
  }
}

resource "aws_route" "example" {
  gateway_id             = aws_internet_gateway.example.id
  route_table_id         = aws_route_table.example.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "example" {
  subnet_id      = aws_subnet.example.id
  route_table_id = aws_route_table.example.id
}


# Security Group

resource "aws_security_group" "example" {
  vpc_id = aws_vpc.example.id
  name   = "example"

  tags = {
    Name = "example"
  }
}

# インバウンドルール(ssh接続用)
resource "aws_security_group_rule" "in_ssh" {
  security_group_id = aws_security_group.example.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
}

# インバウンドルール(http接続用)
resource "aws_security_group_rule" "in_http" {
  security_group_id = aws_security_group.example.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
}


# インバウンドルール(pingコマンド用)
resource "aws_security_group_rule" "in_icmp" {
  security_group_id = aws_security_group.example.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = -1
  to_port           = -1
  protocol          = "icmp"
}

# アウトバウンドルール(全開放)
resource "aws_security_group_rule" "out_all" {
  security_group_id = aws_security_group.example.id
  type              = "egress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
}
ec2.tf


# EC2 Instance

resource "aws_instance" "example" {
  ami                    = "ami-0873b46c45c11058d"
  vpc_security_group_ids = [aws_security_group.example.id]
  subnet_id              = aws_subnet.example.id
  key_name               = "id_rsa_ec2"
  instance_type          = "t2.micro"

  tags = {
    Name = "example"
  }

  user_data = <<EOF
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras install docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -a -G docker ec2-user
EOF
}


# Key Pair

resource "aws_key_pair" "example" {
  key_name   = "id_rsa_ec2"
  public_key = file("../id_rsa_ec2.pub")
}

手順

terraform apply

ec2インスタンスができたらマネジメントコンソールでセキュリティグループを追加する。 TCPで8000 - 9000 を開けておく。

ec2に接続する。

ssh -i id_rsa_ec2 ec2-user@パブリックIP

dockerfileを準備する。

mkdir ~/docker_practice
touch ~/docker_practice/Dockerfile
cd docker_practice
vi Dockerfile
#Dockerfileの内容

FROM centos:latest

RUN yum install -y httpd

#COPY ./index.html /var/www/html/index.html

EXPOSE 80 
RUN echo "Hello Apache." > /var/www/html/index.html

ENTRYPOINT ["/usr/sbin/httpd","-DFOREGROUND"]

次にImageをビルドした後にコンテナを起動していく

docker image build -t docker_practice

docker run -d -p 8000:80 docker_practice

ブラウザにパブリックIP:8000で接続できるか確認してみる。

dockerのbridgeとコンテナの情報を調べる。

docker network ls

docker network inspect bridge 

結果としてbridgeが属するサブネット及びgatewayがわかる。gatewayはbridge のアドレスみたいなものだと現段階では理解している。

次にdockerのコンテナの情報を調べる

docker container ls

docker network inspect (container_id)

この情報からbridgeと同じサブネット内にコンテナが作成されたことがわかった。

次に新しいブリッジを作り、そのブリッジにアタッチしたコンテナを2つ起動させる。

docker network create --attachable -d bridge --subnet=10.99.0.0/16 new_bridge
docker network ls

一覧でnew_bridgeが追加されていればオッケイ.また、docker network inspect new_bridgeでブリッジのサブネットが10.99.0.0/16であるかも確認する。

次にnew_bridgeにコンテナを2つ起動する。

docker run -d -p 8030:80 --net new_bridge docker_practice
docker run -d -p 8090:80 --net new_bridge docker_practice

このコンテナにそれぞれ接続できるかパブリックIP:8030 またはパブリックIP:8090で接続してみる。 できたらそれぞれのコンテナ情報を確認する。

docker container inspect (container_id)

そして内容にnew_bridgeという文言があったり、new_bridgeが属するサブネットが表示されれば、別のブリッジのコンテナが起動していることになる。

これらの確認は黒川さんのyoutubeのやり方をそのまま真似しただけだけど、とても勉強になった。 興味があればこちらを。
https://www.youtube.com/watch?v=h6uw5c5GB_U&list=PLtpYHR4V8Mg-jbuk4yoXhXwJtreodnvzg&index=2