20201025のdockerに関する記事は18件です。

【祝】強化学習ライブラリTF2RL v1.0到達 ~CIやドキュメントサイトなど開発・利用環境の整備~

1. はじめに

以前紹介した友人が開発しているTensorFlow 2.x 向け強化学習ライブラリTF2RLが、諸々整備してバージョン1.0に到達しました?

TF2RL
(いつの間にかスターも300超えていてすごい!)

バージョン1.0到達以降も、まだまだ様々な強化学習アルゴリズムを追加しようと開発が進んでいます。(この記事を準備している間にも、v1.1.0が公開されてます。)

インストール方法や基本となる使い方は、公式ReadMeや、前の記事を読んでいただければと思うので、この記事では割愛します。

この記事では、私もお手伝いさせてもらって整備したアルゴリズム以外の部分について紹介します。

2. マルチプラットフォームテスト (PR 97)

GitHub Actions によって、Windows/macOS/Ubuntu のマルチプラットフォームで、push や pull requestの度にユニットテストを自動で走らせれるようになりました。

特に、Windowsの開発機を友人も私も持っておらず、なかなかサポートできていなかったのですが、このActionsによってWindows上での問題があぶり出されWindowsでも無事動作できるようになりました。
(Windowsでは、multiprocessing.Pool などを利用する際に渡すことができるのは、ネストしていないモジュール直下で定義されたクラス・関数だけみたいですね。なんでも fork()システムコールの代わりに利用している pickleのシリアライズが原因みたいですが、なんでそこに制約があるのかまでは理解できていません。)

同じコードを複数のTFとPythonのバージョンの関係もあって、下のように綺麗なマトリックス戦略になっていないのですが、もうちょっとうまく記述する方法はありませんかね。

.github/workflows/test.yml(抜粋)
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python: ['3.7', '3.8']
        TF: ['2.2', '2.3']
        include:
          - os: ubuntu-latest
            python: '3.7'
            TF: '2.0'
          - os: ubuntu-latest
            python: '3.7'
            TF: '2.1'
          - os: macos-latest
            python: '3.7'
            TF: '2.0'
          - os: macos-latest
            python: '3.7'
            TF: '2.1'
          - os: windows-latest
            python: '3.7'
            TF: '2.0'
          - os: windows-latest
            python: '3.7'
            TF: '2.1'

3. Super-Linterでコードの自動チェック (PR 102)

GitHub Super-Linterを利用し、元々手作業で行っていたLinterでのコードのルールチェックが自動化されました。

Super-Linterで躓いたのは、カスタム設定のファイルを .github/linters/ ディレクトリに置かないといけないことでした。 カスタム設定のファイル名自体は、Super-Linterの起動時のオプションで指定できますが、あくまで上記のディレクトリにおいていないと見つからずデフォルトの設定が使用されてしまいます。(最終的にわからなくて質問しました。)

4. Sphinx を利用したドキュメントサイト構築 (PR 107)

GitHub Actionsの中で、Sphinxを利用してドキュメントを自動生成しています。

docstring から自動でクラスリファレンスを生成する autodoc とMarkdown形式文書を取り込む recommonmark を有効にしています。(テーマはRead The Docsテーマとしました。)

conf.py
from recommonmark.transform import AutoStructify


extensions = ["sphinx.ext.autodoc", "recommonmark"]
html_theme = "sphinx_rtd_theme"


def setup(app):
    app.add_transform(AutoStructify)

ちょっとだけつまづいたのは、 sphinx-apidoc コマンドで autodocの雛形となる一式をソースコードから生成するのですが、自動生成される index.rst が 手作りの index.md より優先されてしまって、うまく表示ができなかったので(しばらく悩んだあとに) index.rst を削除するコードを追加しすることで表示できるようになりました。

GitHub Pagesへの公開は、MasterブランチかつFork先ではない時に、peaceiris/actions-gh-pagesを利用して公開しています。
(どうも、gh-pages というブランチを作ってそこに生成物をpushする仕組みみたいです。なのでよくある master ブランチの doc以下を公開という設定とは異なります。)

.github/workflows/doc.yml(抜粋)
  - uses: peaceiris/actions-gh-pages@v3
    with:
      github_token: ${{ secrets.GITHUB_TOKEN }}
      publish_dir: ./public
    if: (github.ref == 'refs/heads/master') && (github.repository == 'keiohta/tf2rl')

Sphinxによって自動的に書き出されたクラスレファレンスはこちら
(クラスリファレンス以外のチュートリアルも自分で書きたいと聞いていたので、Markdownで書けるように環境は整備しましたが、まだ無いようです。。。乞うご期待。)

5. Docker と GitHub Container Registry を利用した構築済みコンテナ環境の提供 (PR 111)

ログインせずともコンテナイメージをダウンロードできるGitHub Container Registryがベータ公開されたのに併せて、構築済みコンテナ環境を利用できるようにしました。(それ以前のGitHub Packagesはログインしないとpublicなイメージもダウンロードできないという謎仕様で、フォーラムでも何人もの人がおかしいって言い続けていました。)

docker run -it ghcr.io/keiohta/tf2rl/cpu:v1.1.0 bash

また、Linuxオンリーかつマルチプロセスでの学習(ApeX)でうまく行かない例が散見されるのでexperimentalという扱いですが、NVIDIAのGPUを利用できるGPUコンテナバージョンも準備しています。
(私がMacBook Proしかなく、NVIDIA GPUの刺さったマシンを手元に持っていないためデバッグが難しく、環境をもった有識者が支援してくれると助かります。)

docker run --gpus all -it ghcr.io/keiohta/tf2rl/nvidia:v1.1.0 bash

GPUコンテナについては、こちらの記事が非常に詳しくてためになりました。

コンテナイメージのビルドは以下のように設定してあり、tagつきでpushされたら、ビルドしてそのtag名をもったコンテナイメージをGitHub Container Registryに格納する仕組みになっています。

.github/workflows/docker.yml
name: docker

on:
  push:
    tags:
      - '**'

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      DOCKER_BUILDKIT: 1
    steps:
      - uses: actions/checkout@v2
      - uses: docker/build-push-action@v1
        with:
          registry: ghcr.io
          repository: ${{ github.repository_owner }}/tf2rl/cpu
          tag_with_ref: true
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GHCR_TOKEN }}
          dockerfile: Dockerfile
          always_pull: true
      - uses: docker/build-push-action@v1
        with:
          registry: ghcr.io
          repository: ${{ github.repository_owner }}/tf2rl/nvidia
          tag_with_ref: true
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GHCR_TOKEN }}
          dockerfile: Dockerfile.nvidia
          always_pull: true

docker/build-push-actionは v2 があるのですが、ログインが分離されて二度手間で、tagを手動で切り出さなければならず、使い勝手が悪かったので、あえて v1 で固定しています。

また、ベータ版だからなのか、GitHub Container Registryは、GitHub Actionsの自動生成されるシークレットでは認証が通らないので、Personal Access Tokenを作って設定する必要があります。

もうひとつ、忘れがちなのはGitHub Container Registry にpushされたイメージは初めはプライベートになっています。皆が使えるようにするには、https://github.com/<ユーザー名>?tab=packages から指定のイメージを選んで、ポチポチ公開設定する必要があります。

6. Trainer をコマンドラインプログラム以外 (Jupyter Notebook等) からも実行可能に (PR 105)

TF2RLは元々コマンドラインからのスクリプト実行を前提とした作りになっており、構築したモデルを学習させる Trainer クラスは argparse と強く結合してしまっており、Jupyter Notebook上で利用するには(できなくはないが)ちょっと面倒であった。

後方互換性を維持するかつ元のコードへの変更点を最小限にするために、argparse ではなく dict が渡されたときには空の Namespace を構築してその中に dict 指定のデータを詰め込むことにした。こうすることで、わざわざコマンドラインパラメータを模擬した文字列のリストを経由しないで、パラメータを設定できる。

tf2rl/experiments/trainer.py(抜粋)
        if isinstance(args, dict):
            _args = args
            args = policy.__class__.get_argument(Trainer.get_argument())
            args = args.parse_args([])
            for k, v in _args.items():
                if hasattr(args, k):
                    setattr(args, k, v)
                else:
                    raise ValueError(f"{k} is invalid parameter.")

7. TensorFlow Probability のバージョンの自動指定 (PR 113)

最近TF2RLがTensorFlow Probability (TFP) を利用するようになったのですが、TFPは動作するTensorFlow (TF)のバージョンが決まっている割にはインストール時にいい感じにしてくれなくて、何も考えずにインストールするとバージョン非互換でエラーが発生することがありました。

そこで、TF2RLの setup.py に以下のコードブロックを追加し、ユーザーがインストール済みのTFのバージョンに応じて、インストールすべきTFPのバージョンを切り替える方式を導入しました。

setup.py(抜粋)
tf_version = "2.3"  # Default Version
compatible_tfp = {"2.3": ["tensorflow~=2.3.0",
                          "tensorflow-probability~=0.11.0"],
                  "2.2": ["tensorflow-probability~=0.10.0"],
                  "2.1": ["tensorflow-probability~=0.8.0"],
                  "2.0": ["tensorflow-probability~=0.8.0"]}

try:
    import tensorflow as tf
    tf_version = tf.version.VERSION.rsplit('.', 1)[0]
except ImportError:
    pass


install_requires = [
    "cpprb>=8.1.1",
    "setuptools>=41.0.0",
    "numpy>=1.16.0",
    "joblib",
    "scipy",
    *compatible_tfp[tf_version]
]

~= は、バージョンの最後の数字はアップデートを許容するシンタックスで、この場合はメジャーバージョンやマイナーバージョンの更新は許可しないが、バグフィックス(だと思われる)更新は許可するためにこうしてあります。

TF2.4 が出たらエラーになりますが、それは他のTF2.4変更対応とともに対処することになるでしょう。

8. 最後に

TensorFlow は書きにくいから PyTorch って思っている方も結構いるようですが、TF 2.0 以降非常に書きやすくなっていると個人的には思います。

TF2RLのユーザーがもっと増えたらいいなと思っています。

TF2RLでも利用している経験再生(Experience Replay)用のライブラリ cpprb (GitHubミラー) を開発しています。
こちらもぜひ興味を持ってもらえると嬉しいです。

cpprb

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「API開発 + Swagger UIを利用したAPI検証」な環境をDockerで構築する

Swaggerを利用することでREST APIの仕様をドキュメント化できます。
SwaggerではREST APIの仕様をドキュメントしたファイルをSwagger Specと呼びます。

Swagger UIとはSwagger Specの情報を反映させた静的ページを生成するツールのことを言います。
Swagger UIはSwagger Specの情報を可視化するだけでなく、画面上からREST APIを実行する機能も提供しています。

今回は「開発中のAPIをSwagger Specでドキュメント化 → Swagger Specの情報が反映されたSwagger UIの画面からAPIリクエストの検証」という一連の作業が行えるDocker環境の構築手順について紹介します。

今回作成するDocker環境について

仕様は以下の通りです。

  • docker-compose upだけで環境が準備できる
  • 「/swagger-ui」でSwagger UIの画面が表示される
  • 「/swagger-ui」以外はAPI用のエンドポイントとする
  • Swagger Spec編集後、リロードでSwagger UIに変更内容が反映される
  • APIはRuby on RailsのAPIモードで作成する
  • DBはMySQLを利用

nginxをリバースプロキシとして利用することでAPIの開発環境とSwagger UIを組み合わせます。
図で表現すると以下のようになります。

スクリーンショット 2020-10-25 20.17.16.png

今回の利用する各種バージョンは以下の通りです。

  • Ruby on Rails: 6.0.3.2
  • Ruby: 2.7.1
  • MySQL: 8.0.21
  • nginx: 1.19.3

API開発環境をDockerに作成する

APIモードで作成する『Rails 6 x MySQL 8』Docker環境構築手順を参考に、RailsのAPIモードを利用してAPI開発環境を作成します。

Dockerfileとdocker-compose.ymlは以下の通りです。

Dockerfile
FROM ruby:2.7.1

# 作業ディレクトリを/rails_api_swaggerに指定
WORKDIR /rails_api_swagger

# ローカルのGemfileをDokcerにコピー
COPY Gemfile* /rails_api_swagger/

# /rails_api_swaggerディレクトリ上でbundle install
RUN bundle install
docker-compose.yml
version: '3'
services:
  api: # Ruby on Railsが起動するコンテナ
    build: .
    ports:
      - '3000:3000' # localhostの3000ポートでアクセスできるようにする
    volumes:
      - .:/rails_api_swagger # アプリケーションファイルの同期
    depends_on:
      - db
    command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
  db: # MySQLが起動するコンテナ
    image: mysql:8.0.21
    volumes:
      - mysql_data:/var/lib/mysql # データの永続化
      - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    command: --default-authentication-plugin=mysql_native_password # 認証方式を8系以前のものにする。
    environment:
      MYSQL_USER: 'webuser'
      MYSQL_PASSWORD: 'webpass'
      MYSQL_ROOT_PASSWORD: 'pass'
      MYSQL_DATABASE: 'rails_api_swagger_development'
volumes:
  mysql_data: # データボリュームの登録

RailsのAPIモードでは静的ページを生成する機能は除外されているため、Swagger UIを直接Railsアプリケーションに組み込むことはできませんが、今回の方法を利用すればAPIモードでもSwagger UIを利用できます。

サンプルとなるAPIを作成します。

# コンテナをバックグランドで起動
$ docker-compose up -d

# Eventを操作する機能(モデル、ビュー、コントローラー)を一括作成
$ docker-compose exec api rails g scaffold event title:string

# eventsテーブルを作成
$ docker-compose exec api rails db:migrate

# rails consoleでeventsのレコードを作成
$ docker-compose exec api rails c
> event = Event.new(title: 'サンプルイベント')
> event.save

localhost:3000/eventsにアクセスして以下のようなレスポンスが返ってくればOKです。

リバースプロキシの設定を行い、nginx経由でAPIにアクセスできるようにする

nginx経由でRailsアプリケーションにアクセスできるようリバースプロキシの設定を行ます。

default.conf
server {
  listen 80;
  server_name  localhost;

  # "/"にアクセスがあったときの処理
  location / {
    proxy_set_header Host localhost; # アクセス元のホストをlocalhostにする
    proxy_pass http://api:3000; # apiコンテナの3000ポートにリクエストを送る
  }
}

nginxの設定ファイルは/etc/nginx/nginx.confです。
設定ファイルにinclude /etc/nginx/conf.d/*.conf;という記述があることからも分かるように、設定ファイルでは/etc/nginx/conf.d配下の.confという拡張子の設定も読み込んでいます。

つまり、/etc/nginx/conf.d配下に今回作成した設定ファイルを配置することで、nginxコンテナをリバースプロキシとして利用できます。

docker-compose.ymlにnginxコンテナを追加します。

docker-compose.yml
services:
  api:
    (略)
  db:
    (略)
  nginx:
    image: nginx:1.19.3
    ports:
      - '80:80'
    command: [nginx-debug, '-g', 'daemon off;']
    volumes:
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
volumes:
  mysql_data:

[nginx-debug, '-g', 'daemon off;']はデバッグモードによる起動方法です。1

なお、swagger-uiのDockerイメージもnginxを利用していますが、nginx.confinclude /etc/nginx/conf.d/*.conf;の記述がありません。
ですので、swagger-uiのDockerイメージを利用する場合だと今回のアプローチはうまくいかないので注意してください。

コンテナ起動後、localhost:80/eventsにアクセスして以下のようなレスポンスが返ってくればOKです。

Swagger UIをnginxに組み込む

/swagger-uiにアクセスをしたらSwagger UIの画面が表示されるようにnginxにSwagger UIを組み込んでいきます。

Swagger UIの画面はswagger-ui/distによって構成されています。

dist配下のファイルをローカルにコピーし、nginxコンテナにバインドマウントすることで、Swagger UIをnginxに組み込みます。

dist配下のファイルをすべてコピーしてきてもよいのですが、unpkgを利用することでindex.htmlのみをコピーするだけでSwagger UIの画面が作成できます。 2

index.html
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Swagger UI</title>
-   <link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
+   <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css" >
    <style>
      html
      {
        box-sizing: border-box;
        overflow: -moz-scrollbars-vertical;
        overflow-y: scroll;
      }

      *,
      *:before,
      *:after
      {
        box-sizing: inherit;
      }

      body
      {
        margin:0;
        background: #fafafa;
      }
    </style>
  </head>

  <body>
    <div id="swagger-ui"></div>

-   <script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
+   <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js" charset="UTF-8"> </script>
-   <script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
+   <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>

    <script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "https://petstore.swagger.io/v2/swagger.json",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })
      // End Swagger UI call region

      window.ui = ui
    }
  </script>
  </body>
</html>

docker-compose.ymlを修正し、作成したindex.htmlをnginxのデフォルトの公開ディレクトリである/usr/share/nginx/html配下に配置します。

docker-compose.yml
services:![スクリーンショット 2020-10-25 18.23.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140792/22cf7096-e237-7c2e-3aa5-0959d4776657.png)

  api:
    (略)
  db:
    (略)
  nginx:
    image: nginx:1.19.3
    ports:
      - '80:80'
    command: [nginx-debug, '-g', 'daemon off;']
    volumes:
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
+     - ./nginx/html/swagger-ui:/usr/share/nginx/html/swagger-ui
    depends_on:
      - api
volumes:
  mysql_data:

/swagger-uiにアクセスしたらindex.htmlが表示されるようnginxの設定を追記します。

default.conf
server {
  listen 80;
  server_name  localhost;

  location / {
    proxy_set_header Host localhost;
    proxy_pass http://api:3000;
  }

  # "swagger-ui"にアクセスがあったときの処理
  location /swagger-ui {
    alias /usr/share/nginx/html/swagger-ui;
  }
}

コンテナ起動後、localhost:80/swagger-uiにアクセスして以下のような画面が表示されればOKです。

スクリーンショット 2020-10-25 18.23.07.png

ローカルのSwagger SpecがSwagger UI上に反映されるようにする

Swagger UIのurlを変更することで参照するSwagger Specを変更できます。

ローカルのSwagger Specを参照するように変更します。

index.html
- url: "https://petstore.swagger.io/v2/swagger.json",
+ url: "./api.yml",

上記の変更でローカル環境に配置された./nginx/html/swagger-ui/api.ymlの内容がSwagger UIへ反映されます。

なお、./nginx/html/swagger-ui/ディレクトリはバインドマウントされているので、ローカルでSwagger Spec編集後、リロードすればコンテナのSwagger UIに変更内容が反映されます。

サンプルとして作成したGET /eventsを実行するSwagger Specは以下の通りです。

api.yml
openapi: 3.0.2
info:
  title: サンプルAPI
  version: 1.0.0
servers:
  - url: http://localhost:3000
tags:
  - name: イベント
paths:
  /events:
    get:
      tags:
        - イベント
      description: イベント一覧取得
      responses:
        200:
          description: 成功
          content:
            application/json:
              schema:
                type: array
                description: イベントの配列
                items:
                  $ref: "#/components/schemas/Event"
components:
  schemas:
    Event:
      type: object
      properties:
        id:
          description: ID
          type: integer
          format: int64
          example: 1
        title:
          description: タイトル
          type: string
          example: サンプルイベント
        created_at:
          description: 作成日
          type: string
          format: date-time
          example: 2020-04-01 10:00
        updated_at:
          description: 更新日
          type: string
          format: date-time
          example: 2020-04-01 10:00

コンテナ起動後、以下のような画面が表示されればOKです。

スクリーンショット 2020-10-25 18.24.54.png

CORSの設定をする

Swagger UIはlocalhost:80、APIはlocalhost:3000で起動しています。
この状態でSwagger UIからAPIにリクエストを送るとオリジンをまたがっているためAccess to fetch at 'http://localhost:3000/events' from origin 'http://localhost' has been blocked by CORS policyというエラーが発生します。

スクリーンショット 2020-10-25 18.37.42.png

CORSの設定を行い、Swagger UIからAPIリクエストが送れるようにします。
今回はrack-corsを利用してCORSの設定を行ます。

Gemfile
gem 'rack-cors'
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  unless Rails.env.production?
    allow do
      origins(['localhost', /localhost:\d+\Z/])

      resource '*',
        headers: :any,
        methods: [:get, :post, :put, :patch, :delete, :options, :head]
    end
  end
end

コンテナ起動後、リクエストが正常に返ってくればOKです。

スクリーンショット_2020-10-25_18_39_30.png

参考: CRUD操作を行うSwagger Spec

  • GET /events
  • POST /events
  • GET /events/{id}
  • PATCH /events/{id}
  • DELETE /events/{id}

上記のエンドポイントに関するSwagger Specは以下の通りです。

api.yml
openapi: 3.0.2
info:
  title: サンプルAPI
  version: 1.0.0
servers:
  - url: http://localhost:3000
tags:
  - name: イベント
paths:
  /events:
    get:
      tags:
        - イベント
      description: イベント一覧取得
      responses:
        200:
          description: 成功
          content:
            application/json:
              schema:
                type: array
                description: イベントの配列
                items:
                  $ref: "#/components/schemas/Event"
    post:
      tags:
        - イベント
      description: イベント登録
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  example: サンプルイベント
      responses:
        201:
          description: 作成
  /events/{event_id}:
    get:
      tags:
        - イベント
      description: イベント詳細
      parameters:
        - name: event_id
          in: path
          description: イベントID
          required: true
          schema:
            type: integer
            format: int64
            example: 1
      responses:
        200:
          description: 成功
          content:
            application/json:
              schema:
                type: object
                $ref: "#/components/schemas/Event"
        404:
          description: event not found
    patch:
      tags:
        - イベント
      description: イベント更新
      parameters:
        - name: event_id
          in: path
          description: id
          required: true
          schema:
            type: integer
            format: int64
          example: 1
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  example: サンプルイベント
      responses:
        200:
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  activity:
                    $ref: "#/components/schemas/Event"
    delete:
      tags:
        - イベント
      description: イベント削除
      parameters:
        - name: event_id
          in: path
          description: id
          required: true
          schema:
            type: integer
            format: int64
            example: 1
      responses:
        204:
          description: No Content
components:
  schemas:
    Event:
      type: object
      properties:
        id:
          description: ID
          type: integer
          format: int64
          example: 1
        title:
          description: タイトル
          type: string
          example: サンプルイベント
        created_at:
          description: 作成日
          type: string
          format: date-time
          example: 2020-04-01 10:00
        updated_at:
          description: 更新日
          type: string
          format: date-time
          example: 2020-04-01 10:00

画面は以下のようになります。

スクリーンショット 2020-10-25 18.44.00.png

まとめ

以上でAPIとSwagger UIを統合した開発環境の構築手順の紹介を終わります。

  • nginxを利用することでSwagger UIとAPIを組み合わせる
  • nginxのリバースプロキシ設定は『/etc/nginx/conf.d』配下に作成
  • オリジンをまたがるリクエストをする際はCORSの設定が必要になる
  • 自作のSwagger SpecをSwagger UIに反映させるにはindex.htmlのurlを変更する

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OKI AE2100 & Node-REDでローコードIoTしてみた。その2 構築編

DockerでローコードIoTプラットフォーム構築編

はじめに

これはOKIの「AIエッジコンピューター」と呼ばれる AE2100 というゲートウェイ製品にNode-REDを載せて、ローコードIoTのプラットフォームにしようという記事のその2、DockerでローコードIoTプラットフォーム構築編 になります。

その1 PythonでRS-485シミュレータ準備編
その2 DockerでローコードIoTプラットフォーム構築編 (本記事)
その3 Node-REDでローコードIoT実践編 1 ダッシュボード作成
その4 Node-REDでローコードIoT実践編 2 MQTT Pub/Sub

前回の記事では、Windows 10 PC で動作する RS-485シミュレータを Python で作成しました。
今回はいよいよ AE2100 で Node-REDを立ち上げます。
内容としては、ほとんどDockerの作業になります。

Dockerfile を作り、Dockerコマンドを数回たたくと、 AE2100 で Node-RED が立ち上がります。

こんなことやります

image.png

図の ❷ のNode-REDをインストールしたDockerイメージを作成します。

Dockerイメージの構築

SSH で AE2100 にログインして Bash 上で作業します。

Dockerfile

AE2100 で実行するDockerコンテナのイメージを作成するDockerfileです。
これだけでOKです。

Dockerfile
FROM nodered/node-red:1.1.3-minimal
USER root
RUN apk add python3 make g++ linux-headers
RUN npm install node-red-node-serialport node-red-dashboard

このファイルの内容ですが、まずFROM命令でベースとなるイメージを、Docker Hub にある nodered/node-red の最小イメージ 1.1.3-minimal に指定します。
そしてこの Alpine Linuxベースのイメージに root になって、Node-REDのシリアル通信ドライバのビルドに必要な以下の4つのパッケージを apk add します。

  • python3
  • make
  • g++
  • linux-headers

最後に、今回の作業に必要な以下の2つのNode-REDライブラリをここで npm install してしまいます。

  • node-red-node-serialport
  • node-red-dashboard

node-red-node-serialport は今回 AE2100 上の Node-RED で RS-485 を使うためのライブラリです。
このライブラリは名前が示す通り、RS-232C/RS-485/RS-422等シリアル通信機能をNode-REDに追加するライブラリになります。通常、RS-232C通信で良く使われるライブラリですが、RS-485でも問題なく使えました(マルチドロップは試していません)。

node-red-dashboard はNode-REDで簡易ダッシュボードを作成するためのライブラリです。
非常によく利用されており、アップデートも頻繁に行われています。
本文執筆時のバージョンはそれぞれ 3.23.3 と 0.11.0 でした。

この2つのライブラリはNode-REDが立ち上がってからインストールしても良いのですが、今回必須ということで、イメージの中に組み込んでしまいます。

Docker イメージのビルド

それでは AE2100 でイメージをビルドしてみます。
上記のDockerfileがあるディレクトリで以下を実行します。

AE2100-Shell
# docker build --tag node-red_v1:appf .
  :
Successfully built 3691865b0133
Successfully tagged node-red_v1:appf

node-red-node-serialport のC++モジュールのビルドが始まると何やら赤字のログがたくさん出力されますが、"Successfully build..." が出れば大丈夫でしょう。
これで "node-red_v1:appf" のタグ名が付いたイメージができました。
公式のMinimalイメージ194MBに、もろもろ追加されて、計441MBのイメージになりました。。。
しかし 32GB の eMMC を搭載する AE2100 にとってはまだまだ余裕たっぷりです。

Docker コンテナの実行

コンテナの立ち上げです!
Node-REDにアクセスする1880番ポートとMQTT用の1883番ポートをそのままコンテナで使えるようにします。
AE2100 の RS-485 のデバイスファイルは /dev/ttyRS485 です。これもそのままコンテナで使えるようにします。
そして AE2100 ホストの /home/root/.node-red ディレクトリをコンテナの /data にボリュームマウントして、Node-REDの設定ファイルやフローは AE2100 ホスト側のファイルシステムに保存するようにします。
Node-RED のフローやライブラリは頻繁に更新されますからね。

# docker run -it -p 1880:1880 -p 1883:1883 --device=/dev/ttyRS485:/dev/ttyRS485 -v /home/root/.node-red:/data --name node-red-1 node-red_v1:appf

> node-red-docker@1.1.3 start /usr/src/node-red
> node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS "--userDir" "/data"

8 Sep 23:27:27 - [info] 

Welcome to Node-RED
===================

8 Sep 23:27:27 - [info] Node-RED version: v1.1.3
8 Sep 23:27:27 - [info] Node.js  version: v10.22.0
8 Sep 23:27:27 - [info] Linux 4.14.67-intel-pk-standard x64 LE
8 Sep 23:27:27 - [info] Loading palette nodes
8 Sep 23:27:29 - [info] Dashboard version 2.23.3 started at /ui
8 Sep 23:27:29 - [info] Settings file  : /data/settings.js
8 Sep 23:27:29 - [info] Context store  : 'default' [module=memory]
8 Sep 23:27:29 - [info] User directory : /data
8 Sep 23:27:29 - [warn] Projects disabled : editorTheme.projects.enabled=false
8 Sep 23:27:29 - [info] Flows file     : /data/flows.json
8 Sep 23:27:29 - [info] Creating new flow file
8 Sep 23:27:29 - [warn] 

---------------------------------------------------------------------
Your flow credentials file is encrypted using a system-generated key.

If the system-generated key is lost for any reason, your credentials
file will not be recoverable, you will have to delete it and re-enter
your credentials.

You should set your own key using the 'credentialSecret' option in
your settings file. Node-RED will then re-encrypt your credentials
file using your chosen key the next time you deploy a change.
---------------------------------------------------------------------

8 Sep 23:27:29 - [info] Server now running at http://127.0.0.1:1880/
8 Sep 23:27:29 - [info] Starting flows
8 Sep 23:27:29 - [info] Started flows

エラーなしに立ち上がったでしょうか?
Docker コマンドで確認してみますか。

# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                             PORTS                                            NAMES
122cd69c5b65        node-red_v1:appf    "npm start --cache /…"   15 seconds ago      Up 14 seconds (health: starting)   0.0.0.0:1880->1880/tcp, 0.0.0.0:1883->1883/tcp   node-red-1

実行時 "--name" オプションで指定した "node-red-1" という名前で動作しているようです。

Node-RED の実行確認

それでは PC のWebブラウザでこの Node-RED に接続してみましょう。
URLは以下のようになります。

http://AE2100のIPアドレス:1880

image.png

おなじみのフローエディタが表示されるはずです。

ネットワーク・パレットを見ると、一番下に "serial in"、"serial out"、"serial request" の3つのノードが追加されています。Dockerfile 内に記述した RUN 命令でインストールされた node-red-node-serialport ライブラリです。
image.png

MinimalのイメージでもMQTTやWebSocketのノードがあるのはさすがNode-REDですね。
ローコードIoT開発プラットフォームなので当然ですか。

パレットの一番下 dashboard も Dockerfile 内でインストールしたライブラリ node-red-dashboard のノード達です。

image.png

先に少し説明しましたが、これにより本当に簡単にちょっとしたダッシュボードを作ることができ、デバッグ支援にも使えるし、必須ともいえる外部ライブラリではないでしょうか。

さいごに

以上で AE2100 ローコードIoT開発プラットフォームの完成です!
Node-RED が立ち上がってしまえば、Node-RED 経験者にはこれ以上説明することはないのですが、せっかくなので、Node-RED 初心者向けにIoTらしい簡単な実践編の記事を書くことにしました。
まずは RS-485シミュレータからセンサーデータを取り込んで、Webブラウザ上の簡易ダッシュボードに表示するアプリを作成します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-compose --help 日本語訳

docker-compose --help 実行時に表示されるヘルプドキュメントの日本語訳。

Docker を使用してマルチコンテナーアプリケーションを定義し、実行します。

使用法:

docker-compose [-f <arg>...] [options] [--] [COMMAND] [ARGS...]
docker-compose -h|--help

オプション:

  • -f, --file FILE 代替の compose ファイルを指定します(デフォルト: docker-compose.yml )
  • -p, --project-name NAME 代替プロジェクト名を指定します(デフォルト: ディレクトリ名)
  • -c, --context NAME コンテキスト名を指定します。
  • --verbose より多くの出力を表示します。
  • --log-level LEVEL ログレベルを指定します。
    • DEBUG
    • INFO
    • WARNING
    • ERROR
    • CRITICAL
  • --no-ansi ANSI 制御文字を非表示にします。
  • -v, --version バージョンを表示して終了します
  • -H, --host HOST 接続するデーモンソケットを指定します。
  • --tls TLS を使用します; --tlsverify によって暗示されます。
  • --tlscacert CA_PATH この CA によってのみ署名された信頼証明書
  • --tlscert CLIENT_CERT_PATH TLS 証明書ファイルへのパス
  • --tlskey TLS_KEY_PATH TLS キーファイルへのパス
  • --tlsverify TLS を使用して接続を確認します
  • --skip-hostname-check デーモンのホスト名をクライアント証明書で指定された名前と照合しないようにします
  • --project-directory PATH 代替作業ディレクトリを指定します(デフォルト: compose ファイルのパス)
  • --compatibility 設定されている場合 Compose は v3 ファイルのキーを Swarm 以外の同等のものに変換しようとします(非推奨)
  • --env-file PATH 代替環境ファイルを指定します

Commands:

  • build サービスのビルドまたは再ビルドを実行します
  • config compose ファイルを検証して表示します
  • create サービスを作成します
  • down コンテナ、ネットワーク、イメージ、ボリュームを停止して削除します
  • events コンテナからリアルタイムのイベントを受信します
  • exec 実行中のコンテナでコマンドを実行します。
  • help コマンドのヘルプを表示します。
  • images イメージを列挙します。
  • kill コンテナを停止します。
  • logs コンテナからの出力を表示します。
  • pause サービスを一時停止します。
  • port ポートバインディングの為の公開ポートを表示します。
  • ps コンテナを列挙します。
  • pull サービスイメージをプルします。
  • push サービスイメージをプッシュします。
  • restart サービスを再起動します。
  • rm 停止されたコンテナを削除します。
  • run 一度限りのコマンドを実行します。
  • scale サービスに使用するコンテナ数を指定します。
  • start サービスを開始します。
  • stop サービスを停止します。
  • top 実行中のプロセスを表示します。
  • unpause サービスの一時停止を解除します。
  • up コンテナを作成して開始します。
  • version バージョン情報を表示して終了します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者向けCI/CD実践 - Part2 - CI/CDツール構築編

CI/CD環境を構築

本連載は、「CI/CDを実践してみたい」という超初心者向けの内容となっています
enterpriseレベルやproductionレベルの導入に際して、「CI/CDとは?」という感覚をつかむものとなれば幸いです。
利用するツール等はこちらで選定していますので、別のツールを利用する場合は、適宜お調べください。
大まかな内容としては、以下のようなフローとなります

  1. 環境構築
  2. CI/CDツールの構築<-今回はここを説明
  3. 開発プロジェクトの準備
    • GitHubにプロジェクトデータを登録
    • Jenkins Pluginのインストール
    • 手動ビルド
  4. CI/CDツールとその他ツールの併用例
    1. VCSとの協調
    2. テストツールとの協調
    3. インスペクションツールとの協調

概要

  • Jenkinsコンテナの実行
  • Jenkins初期設定
  • gmailを用いてビルド結果を自動通知
  • JDK/Ant/Mavenの自動インストール

CI/CDツールの構築

CI/CDツールには、以下のようなものがある

  • Jenkins
  • Drone
  • CircleCI
  • AWS CodeBuild
  • Tekton 

今回はJenkinsを導入する

Jenkinsコンテナの実行

Jenkinsの公式を参考にJenkinsコンテナを実行し、初期設定を行う

最初にnetworkおよびvolumeの設定を行う

$ docker network create jenkins
$ docker volume create jenkins-docker-certs   //Dockerデーモンに接続するために必要なDockerクライアントのTLS証明書を共有
$ docker volume create jenkins-data   //データ永続化に利用
$ docker network ls   //新規作成されたbridge network及びdefaultの3つのnetworkがある
$ docker volume ls

続いて、jenkinsコンテナとしてjenkinsci/blueoceanを実行

$ docker container run --name jenkins-blueocean --rm --detach \
  --network jenkins --env DOCKER_HOST=tcp://docker:2376 \
  --env DOCKER_CERT_PATH=/certs/client --env DOCKER_TLS_VERIFY=1 \
  --volume jenkins-data:/var/jenkins_home \
  --volume jenkins-docker-certs:/certs/client:ro \
  --publish 8080:8080 --publish 50000:50000 jenkinsci/blueocean

これで、jenkinsが実行される

jenkins dashboardへの初回ログイン

jenkinsのdashboardに入るためには、ブラウザ上でhttp://localhost:8080/と入力
passwordとして、コンテナを実行した際の値を入力
もしくは、下記コマンドを実行し初期パスワードを確認

$ docker exec jenkins-blueocean cat /var/jenkins_home/secrets/initialAdminPassword

続いて、pluginのインストールとユーザの作成を行う
Customizing Jenkins with pluginsにて以下のどちらかを選択
   Install suggested plugins(基本的にこちら)
   Select plugins to install

ユーザの作成では、以下のようにadmin権限を持った新規ユーザを作成

図5.png

これで初回ログイン時の設定は終了

jenkinsの初期設定

初期設定では、以下の2点に関して設定を行う

  • ビルド結果のメール通知
  • CI実現に必須なツールのインストール

初めに、「ビルド結果のメール通知」を設定
dashboard > 左ペインのJenkinsの管理 > 中央ペインのシステムの設定 >

Jenkins LocationにてJenkins URL,System Admin e-mail addressを入力
System Admin e-mail addressは通知メールの差出人

図6.png

E-mail Notificationにて以下のように設定
test mailの宛先を入力した後、Test configurationをクリック

図7.png

クリック後にEmail was successfully sentと表示されればメールが届いている
もし、Failed to send out e-mail

javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted. Learn more at 535 5.7.8  https://support.google.com/mail/?p=BadCredentials q14sm9594426pjp.43 - gsmtp

~~

と表示された場合、Googleアカウントの「安全性の低いアプリのアクセス」が無効(defaultは無効)
有効にする場合は、こちらを参考にしてください

続いて、「CI実現に必須なツールのインストール」を設定
dashboard > 左ペインのJenkinsの管理 > 中央ペインのGlobal Tools Configuration >

以下の通り入力しsave

図8.png

JDKのインストールに当たって、Oracleアカウントが必須なため、リンク先からユーザ名とパスワードを入力(入力前は"I agree to the Java SE ~~"付近にエラーが表示されている)
もしアカウントを持っていない場合、そこからアカウント作成が可能

図9.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Code-Server用docker-compose.yml

自分用

docker-compose.yml

version: "2.1"
services:
  coder:
    image: codercom/code-server
    container_name: Code-Server
    user: 0:0
    volumes:
      - /root/projects:/root/project
    ports:
      - 8000:8000
    entrypoint: "code-server --auth none --user-data-dir /root/project --bind-addr 0.0.0.0:8000"
    restart: unless-stopped

結構な頻度で画面が出てすぐ切断されることがある。どうも処理が重くて追い付かないなどの理由(?)でhandshakeに10秒以上かかるとタイムアウトで切断する模様…

node.jsインストール

codercom/code-serverイメージのOSはDebianの模様。aptは使えるがnodeは使えないのでインストール。これ動かしたら猛烈に時間がかかるので注意。

apt update && apt install -y npm && npm install -g n && n stable && apt remove -y --autoremove npm

Dockerfileに書くならこれも必要。

ENV DEBIAN_FRONTEND=noninteractive
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Exposedを使ってDBアクセス

kotlinでのDBアクセスにExposedを使ったので忘備録。
動作確認にはDockerでテスト用のDBをたてて確認しました。

DOckerの環境構築は以下を参考にしました。
・WSL2とDockerの導入
https://qiita.com/KoKeCross/items/a6365af2594a102a817b

・WSL2のメモリ枯渇対策
https://qiita.com/yoichiwo7/items/e3e13b6fe2f32c4c6120

Exposedとは

Kotlinで書かれたSQLライブラリです。
https://github.com/JetBrains/Exposed

サンプルプログラム

まずはDockerでDBを作成するためのdocker-compose.yamlファイル。

version: '3'
services:
  db:
    image: postgres:12.3
    ports:
      - 5433:5432
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: test_db

次にテーブル定義。

object Member : Table("member") {
    val id = integer("member_id").autoIncrement().primaryKey()
    val name = varchar("name", 50)
    val age = integer("age")
}

最後にメイン関数。

import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.SchemUtils.create

fun main() {
    Database.connect("jdbc:postgresql://localhot:5433/test_db", "org.postgresql.Driver", "admin", "admin")

    transaction {
        addLogger(StdOutSqlLogger)

        create(Member)

        Member.insert {
            it[id] = 1
            it[name] = "abc"
            it[age] = 20
        }

        Member.insert {
            it[id] = 2
            it[name] = "efd"
            it[age] = 30
        }

        for(member in Member.selectAll()) {
            println("${member[Member.id]}: ${member[Member.name]}: ${member[Member.age]}")
        }

        Member.update({Member.name eq "abc"}) {
            it[name] = "あいうえお"
        }

        for(member in Member.selectAll()) {
            println("${member[Member.id]}: ${member[Member.name]}: ${member[Member.age]}")
        }

        Member.deleteAll()

        drop(Member)
    }
}

やっつけですが、とりあえず一通りCRUDができることを確認できました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

8.Docker(docker buildの詳細、instruction)

docker buildコマンドの詳細

なぜdocker build実行時、dockerfileではなく、そのフォルダを指定するのか

docker build実行時、指定されたフォルダをdocker daemonに渡す。
docker damonがフォルダとDockerfileを元にdocker imageを作る。

なので、Dockerfileだけでなく、フォルダも必要。

buildするフォルダのことを「 build context 」と言う。
このcontextを使って、docker daemonがdockerfileからdocker imageを作る。

context ・・・ 環境や状況

なぜdocker build コマンドはbuild contextを使って、docker imageをbuildするのか

build時に、COPY, ADDなどのdocker instructionによって、build contextの中にあるファイルをdocker imageに組み込める。

docker daemonとは

・ dockerのオブジェクトを管理している。(実際に、コンテナやdocker imageを操作する)
・ docker composeなど他のDockerのツールとコミュニケーションする。

dockerオブジェクト ・・・ コンテナ、イメージ、ネットワークなどのこと

docker CLIのコマンド(「 docker build 」 や 「 docker run 」など)で、dockerのオブジェクトを管理しているdocker daemonに命令を送っている。

docker build -f [docker file] [build context]

build contextの中にdockerfileがない場合や、他のdockerfileを作り、devとtestと分けている場合は、 「-f」オプションを使い、docker build することで特定のdockerfileを指定して、docker build できる。

開発環境とテスト環境で異なるdockerfileを持っている場合などに「-f」を用いて、dockerfileを指定し、docker buildすることがある。

Dockerfileの名前は、
開発系ではDockerfile.dev
テストではDockerfiel.test
のような形のファイル名にすることが多い。

docker instruction

COPY

build時に、build contextの中にあるファイルをdocker imageに組み込める。
→ホストから何かファイルやフォルダをコンテナに渡せる。

COPY [src][destination]
src ・・・ ホストのファイルのpath
destination ・・・ コンテナ内で目的のファイルをおくファイルのpath

「COPY」と「ADD」は似ているが、基本的には「COPY」で問題ない。

ADD

build時に、build contextの中にあるファイルをdocker imageに組み込める。
→ホストから何かファイルやフォルダをコンテナに渡せる。

圧縮されたtarファイルをdocker buildした時に、docker imageに送り、さらにそれを解凍してくれる。

ADD [src][destination]

※「COPY」で、フォルダごとdocker imageにしても良いが、docker daemonに情報を渡す際に時間がかかる。
よって、重たいファイルがある時は、tarファイルで圧縮してから「ADD」でdocker imageに送る方が良い。

ENTRYPOINT

docker runした時のデフォルトのコマンドを指定できる。(CMDと同じ)

「ENTRYPOINT」では、docker runした時に上書きできない。
「CMD」では、docker runした時に上書きできる。

「ENTRYPOINT」がDockerfileにある場合はCMDの書き方が変わる。
CMDの形は「ENTRYPOINT」に指定したこのコマンドの引数を指定する。

例)
・ ENTRYPOINTなしの場合のCMD
CMD["ls", "--help", "引数", "引数", ・・・]

・ ENTRYPOINTありの場合のCMD
ENTRYPOINT["ls"]
CMD["--help", "引数", "引数", ・・・]
※docker run [image] -laのように引数(オプション)は上書きできる

ENV

環境変数をDockerfileで指定できる。
pathを通す時によく使う。

環境変数 ・・・ OSの上で動くあらゆるプロセスが情報を共有するために使う変数。keyとvalueの組み合わせ

ENV [key] [value]
ENV [key]=[value]

WORKDIR

Dockerfile内に記載されているdocker instructionの実行をするディレクトリを変更できる。

WORKDIR [絶対パス]

Dockerfileでは各instructionが実行される場所はルート直下。
Dockerfile内で、RUNでcdコマンドで移動しても、RUNで次のコマンドが実行されるのはルート直下。
しかし、それだと不便な場合があるので、「WORKDIR」で絶対パスを指定して、それ以降のinstructionを指定されたパスのところで実行する。

最後に

この記事は、かめさん( https://twitter.com/usdatascientist?s=21 )のudemyのdocker講座の( https://www.udemy.com/share/103aTRAEMfeVhaTXoB/ )書き起こしです。

※あくまで、自分のための書き残しとして投稿しているので、講座と異なる部分を含んでいる可能性があります。

かめさんのブログ( https://datawokagaku.com/docker_lecture/ )

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者向けCI/CD実践 - Part1環境構築編

CI/CD環境を構築

本連載は、「CI/CDを実践してみたい」という超初心者向けの内容となっています
enterpriseレベルやproductionレベルの導入に際して、「CI/CDとは?」という感覚をつかむものとなれば幸いです。
利用するツール等はこちらで選定していますので、別のツールを利用する場合は、適宜お調べください。
大まかな内容としては、以下のようなフローとなります

  1. 環境構築
  2. CI/CDツールの構築
  3. CI/CDツールの基本的な利用例
  4. CI/CDツールとその他ツールの併用例
    1. VCSとの協調
    2. テストツールとの協調
    3. インスペクションツールとの協調

環境構築

環境構築編では、CI/CDを実践するに必要な環境を整えます

概要

  • Windows 10に仮想化ソフトウェアVirtualBoxを用意
  • VirtualBoxで仮想マシンUbuntuを作成
  • Ubuntu上でDockerコンテナの実行環境を構築

既存環境

OS : Windows 10 Enterprise
プロセッサ : Intel Core i7
RAM : 16GB
システムの種類 : 64bit OS

仮想化ソフトウェアの用意

今回CI/CDを実践するにあたって、仮想環境を用意する
仮想環境には、「ホスト型」と「ハイパーバイザ型」があり。今回は前者を構築
ホスト型の仮想環境を構築する「仮想化ソフトウェア」には以下のようなものがある

  • VirtualBox
  • VMware Workstation Player
  • KVM

今回は「VirtualBox」を利用

インストールは既に済ませていたので、こちらを参考に進めてください

VirtualBoxのバージョンは6.1.14で実施

仮想マシンimageのダウンロード

仮想マシンのOSとしてUbuntuを利用する
imageのダウンロードはこちら
Ubuntuのバージョン情報等はこちらの通り

  • Ubuntu Focal 20.04 (LTS)
  • 64-bit PC (AMD64) desktop image

注意
その他のバージョンやimageを利用する場合はこちらまたは、こちらを参考にしてください
今回の検証では、仮想マシン上にコンテナとしてCI/CDツールを用意します
適切なOSを利用しないとdocker コンテナを利用できません

仮想マシンの作成

こちらを参考に仮想マシン作成

設定値は基本的にdefaultを採用し、以下の値のみ変更

図4.png

仮想マシンの初期設定

仮想マシンの設定は指示に迷うところは特にないかと思いますので、省略

その他、事前に設定しておくこととして以下のような内容があります。それぞれの設定はリンク先を確認

docker

最後に行う環境構築として、Docker Engineのインストールを行う
公式のこちらを参考に進めていく

手順だけ抜き出したものが以下になります
1. Dockerレポジトリの準備
- $ sudo apt-get update
- $ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent 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 $(lsb_release -cs) stable"
2. Docker Engineのインストール
-$ sudo apt-get update
- $ sudo apt-get install docker-ce docker-ce-cli containerd.io
- $ sudo docker run hello-world
3. 一般ユーザでDockerを利用できるよう設定
- $ cat /etc/group | grep docker
- sudo gpasswd -a $(whoami) docker
- $ cat /etc/group | grep docker
- sudo chmod 666 /var/run/docker.sock
4. Docker Composeのインストール
- sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
- docker-compose --version

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者向けCI/CD実践 - Part1 - 環境構築編

CI/CD環境を構築

本連載は、「CI/CDを実践してみたい」という超初心者向けの内容となっています
enterpriseレベルやproductionレベルの導入に際して、「CI/CDとは?」という感覚をつかむものとなれば幸いです。
利用するツール等はこちらで選定していますので、別のツールを利用する場合は、適宜お調べください。
大まかな内容としては、以下のようなフローとなります

  1. 環境構築<-今回はここ
  2. CI/CDツールの構築
  3. 開発プロジェクトの準備
    • GitHubにプロジェクトデータを登録
    • Jenkins Pluginのインストール
    • 手動ビルド
  4. CI/CDツールとその他ツールの併用例
    1. VCSとの協調
    2. テストツールとの協調
    3. インスペクションツールとの協調

環境構築

環境構築編では、CI/CDを実践するに必要な環境を整えます

概要

  • Windows 10に仮想化ソフトウェアVirtualBoxを用意
  • VirtualBoxで仮想マシンUbuntuを作成
  • Ubuntu上でDockerコンテナの実行環境を構築

既存環境

OS : Windows 10 Enterprise
プロセッサ : Intel Core i7
RAM : 16GB
システムの種類 : 64bit OS

仮想化ソフトウェアの用意

今回CI/CDを実践するにあたって、仮想環境を用意する
仮想環境には、「ホスト型」と「ハイパーバイザ型」があり。今回は前者を構築
ホスト型の仮想環境を構築する「仮想化ソフトウェア」には以下のようなものがある

  • VirtualBox
  • VMware Workstation Player
  • KVM

今回は「VirtualBox」を利用

インストールは既に済ませていたので、こちらを参考に進めてください

VirtualBoxのバージョンは6.1.14で実施

仮想マシンimageのダウンロード

仮想マシンのOSとしてUbuntuを利用する
imageのダウンロードはこちら
Ubuntuのバージョン情報等はこちらの通り

  • Ubuntu Focal 20.04 (LTS)
  • 64-bit PC (AMD64) desktop image

注意
その他のバージョンやimageを利用する場合はこちらまたは、こちらを参考にしてください
今回の検証では、仮想マシン上にコンテナとしてCI/CDツールを用意します
適切なOSを利用しないとdocker コンテナを利用できません

仮想マシンの作成

こちらを参考に仮想マシン作成

設定値は基本的にdefaultを採用し、以下の値のみ変更

図4.png

仮想マシンの初期設定

仮想マシンの設定は指示に迷うところは特にないかと思いますので、省略

その他、事前に設定しておくこととして以下のような内容があります。それぞれの設定はリンク先を確認

docker

最後に行う環境構築として、Docker Engineのインストールを行う
公式のこちらを参考に進めていく

手順だけ抜き出したものが以下になります
1. Dockerレポジトリの準備
- $ sudo apt-get update
- $ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent 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 $(lsb_release -cs) stable"
2. Docker Engineのインストール
-$ sudo apt-get update
- $ sudo apt-get install docker-ce docker-ce-cli containerd.io
- $ sudo docker run hello-world
3. 一般ユーザでDockerを利用できるよう設定
- $ cat /etc/group | grep docker
- sudo gpasswd -a $(whoami) docker
- $ cat /etc/group | grep docker
- sudo chmod 666 /var/run/docker.sock
4. Docker Composeのインストール
- sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
- docker-compose --version

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

https-portalコンテナのタイムゾーンをJSTに変更する

https-portalをDockerコンテナで運用していますが、タイムゾーンがUTCのままになっているコンテナがあることに気が付いてしまいました。
ログ見るときに困るのでJSTに変えたいと思います。

  • https-portal

タイムゾーンはUTCになっています。

$ docker-compose exec https-portal sh
# date
Sun Oct 25 03:36:07 UTC 2020

docker-composeで設定してみます。

docker-compose.yml
version: '3'

services:
  https-portal:
    container_name: https-portal
    image: steveltn/https-portal:1
    ports:
      - '80:80'
      - '443:443'
    restart: always
    environment:
      DOMAINS: 'example.net -> https://kusanagi01_httpd:8443 #production, example.com -> https://kusanagi02_httpd:8443 #local'
      ERROR_LOG: stderr
      ACCESS_LOG: stdout
      CLIENT_MAX_BODY_SIZE: 16M
      TZ: Asia/Tokyo                                                #追加
    networks:
      - shared-network

networks:
  shared-network:
    external: true

コンテナを作り直します。
JSTに変更されたことを確認できました。

$ docker-compose stop
Stopping https-portal ... done
$ docker-compose up -d
Recreating https-portal ... done
$ docker-compose ps
    Name       Command   State                    Ports                  
-------------------------------------------------------------------------
https-portal   /init     Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

$ docker-compose exec https-portal sh
# date
Sun Oct 25 12:49:10 JST 2020
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

symfony/http-kernelをphpstorm&dockerでphpunit&xdebug

1.ソースのダウンロード
2.dockerの設定
3.phpstormの設定

1. ソースのダウンロード

git clone https://github.com/symfony/http-kernel.git

2. dockerの設定

cd ./http-kernel
mkdir docker
touch docker/Dockerfile
touch docker/php.ini
touch docker-compose.yml
Dockerfile
FROM php:7.4

RUN pecl install xdebug \
    && docker-php-ext-enable xdebug

RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y git
php.ini
; timezone
date.timezone = Asia/Tokyo

; error reporing
log_errors = On
error_log = /dev/stderr

; xdebug
xdebug.remote_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_connect_back = 1
docker-compose.yml
version: "3"
services:
  php-cli:
    build: ./docker
    volumes:
      - ./:/var/www/html
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
  composer:
    image: composer
    volumes:
      - ./:/app
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    working_dir: /app

3. Composer Install

docker-compose run composer install
docker-compose run composer require --dev symfony/phpunit-bridge

ちなみにphpunit/phpunitは使いません。1

The PHPUnit bridge is designed to work with all maintained versions of Symfony components, even across different major versions of them. You should always use its very latest stable major version to get the most accurate deprecation report.

使うとエラーが出ます。

docker-compose run php-cli ./vendor/bin/phpunit Tests/
[25-Oct-2020 07:28:39 Asia/Tokyo] PHP Fatal error:  Trait 'Symfony\Bridge\PhpUnit\ExpectDeprecationTrait' not found in /var/www/html/Tests/Controller/ContainerControllerResolverTest.php on line 21

注意

docker-compose run composer ./vendor/bin/simple-phpunit

を一度実行させておくことで、symfony/phpunit-bridgeからphpunit/phpunitをインストールさせておく

3. phpstormの設定

File > Settings > Languages & Frameworks > PHP

phpstormがdockerのphpを実行するように設定します

CLI interpreterの右端にある...をクリック
Screenshot from 2020-10-25 07-33-45.jpg

追加の+ボタンクリックして、From Docker,...を選択
Screenshot from 2020-10-25 07-34-02.png

Docker-composeにチェックを入れ、dockerから実行させるphpのservice(この場合php-cli)を選択
Screenshot from 2020-10-25 07-34-26.png

OK押して、phpが読み込まれてversionなどが表示されてれば問題ありません
Screenshot from 2020-10-25 07-34-43.png

Phpunit

File > Settings > Languages & Frameworks > PHP > Test Frameworks

phpstormがdocker内のphpunitを実行するように設定します

追加の+ボタンから PHPUnit by Remote Interpreterを選択
Screenshot from 2020-10-25 07-37-21.png

php-cliを選択
Screenshot from 2020-10-25 07-37-40.png

  1. PHPUnit library の Path to phpunit.phar にチェック入れる
  2. Path to phpunit.pharに /var/www/html/vendor/bin/simple-phpunit
  3. Test Runner の Default configulation file にチェック入れ、/var/www/html/phpunit.xml.dist

Screenshot from 2020-10-25 10-00-25.png

いざ実行

editor右上の虫のアイコン左のセレクタでedit configurationを選択
画面が開いたら追加の+ボタンからPhpunitを選択し、下記のように設定
Screenshot from 2020-10-25 09-56-18.png

breakpointを貼って、simple-phpunitが選択されている状態で虫アイコンをクリック

Screenshot from 2020-10-25 08-30-20.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mac の VirtualBox の Ubuntu に SSH 接続して Docker をインストールするまでやる

1. Mac に VirtualBox をインストール

Homebrew のサイトでスクリプトを確認し、terminal で実行する

2020年10月25日時点では下記のスクリプト

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

Homebrew を利用して Mac に VirtualBox をインストールする

brew update && brew upgrade && brew upgrade --cask
brew cask install virtualbox

2. VirtualBox に Ubuntu をインストール

Ubuntu のイメージをダウンロードする

virtualbox に Ubuntu をインストールする

Ubuntu に Guest Additions をインストールする

3. Ubuntu への SSH 接続を設定

Ubuntu にホストオンリーアダプタを設定する

  • VirtualBox 上の Ubuntu の電源を落としておく
  • VirtualBox > File > Host Network Manager... を選択する
  • Create ボタンを押して vboxnet0 を作成する
  • DHCP Server は Enable にチェックする
  • Host Network Manager のウィンドウを Close する
  • VirtualBox の Settings ボタンを押す
  • Network タブを選択する
  • Adapter 2 を選択する(選択できなければ VirtualBox をいちど再起動する)
  • Enable Network Adapter にチェックを入れる
  • Attached to: で Host-only Adapter を選択する
  • OK ボタンを押す

Ubuntu に openssh-server をインストールする

VirtualBox 上の Ubuntu を起動し、terminal を開いて下記コードを実行する

sudo apt update && sudo apt upgrade
sudo apt install openssh-server
sudo systemctl enable ssh
sudo systemctl restart ssh
ip address

3: enp0s8 に 割り振られた inet の IP アドレスを確認する。

Mac からの SSH 接続を確立する

Mac の terminal で下記のような形式のコードを実行し、SSH 接続する
ただし、<user> は Ubuntu 上のユーザー名、<ip>は上記で確認した IP アドレス
例:ssh foo@192.0.2.0

ssh <user>@<ip>

以降は Ubuntu に SSH 接続した Mac の terminal から全て操作する

4. Ubuntu に Docker をインストール

Uninstall old versions

sudo apt-get remove docker docker-engine docker.io containerd runc

SET UP THE REPOSITORY

Update the apt package index and install packages to allow apt to use a repository over HTTPS:

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

Add Docker’s official GPG key:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Verify that you now have the key with the fingerprint 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88, by searching for the last 8 characters of the fingerprint.

sudo apt-key fingerprint 0EBFCD88

Use the following command to set up the stable repository.

sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

INSTALL DOCKER ENGINE

Update the apt package index, and install the latest version of Docker Engine and containerd, or go to the next step to install a specific version:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

Verify that Docker Engine is installed correctly by running the hello-world image.

sudo docker run hello-world

sudo なしで docker を扱う

sudo usermod -aG docker $USER
newgrp docker
docker run hello-world
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerFileに最新のNode.jsを追加した話

他で作成していたイメージにNode.jsをインストールしたかったが、調べてもすぐには出てこなかったためメモ

前提

Rustでwasmを触ってみようとして必要だった。
busterベースのコンテナを使用。

結論

こちらを追加した

ENV NODE_VERSION 15.0.1

RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \
    && case "${dpkgArch##*-}" in \
    amd64) ARCH='x64';; \
    ppc64el) ARCH='ppc64le';; \
    s390x) ARCH='s390x';; \
    arm64) ARCH='arm64';; \
    armhf) ARCH='armv7l';; \
    i386) ARCH='x86';; \
    *) echo "unsupported architecture"; exit 1 ;; \
    esac \
    && set -ex \
    && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
    && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
    && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs \
    && node --version \
    && npm --version

やっていることとしては
アーキテクチャを取得
公式サイトのurlを叩く
展開

ログに表示されたバージョン

+ node --version
v15.0.1
+ npm --version
7.0.3

考えたこと

最初はaptでインストールを行ったがバージョンが古く(10前後だった記憶)、せっかくなので最新にしようと考えた。しかし適当に調べても"正しい"方法が見つからないためDockerHubへ。
https://hub.docker.com/_/node

ここから自分の使っているイメージと似たようなOS(今回はbuster)をベースにしているイメージのDockerFileへ飛ぶ。
そこからnodeをインストールしている箇所を抜き出し、自分のDockerFileに適用。(本当はチェックサムやら何やらがあったが削除してしまった)

見事動いた。
以上。

出典

https://hub.docker.com/_/node
https://github.com/nodejs/docker-node/blob/d58d7e65c4f92ef22a190b0ca835ce62464ff3ba/15/buster/Dockerfile

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java GradleからのDocker利用

今回に限らず巨人の肩に乗ってパッケージを便利にしてきますか。
前回のパッケージをさっくり作ったところからスタートします。
なお、繰り返しですがソースコードあります。
適宜ご覧ください(READMEの口調が荒いですがお許しくだされ)。

導入プラグイン

今回利用するのは、米Palantir Technologies(データ分析業)の開発している、
docker pluginたちです。
「たち」というのは、docker, docker-run, docker-composeそれぞれに独立のプラグインがあるからですね。
https://github.com/palantir/gradle-docker
composeは今回はいいかなぁ...
ひとまずはgradleからcontainer起動までこぎつける、ここまでを扱います。

各手順

1. Dockerfile作成

まぁこれなければ動きませんよね...
今回作ったのはこんな感じです。

Dockerfile
FROM amazonlinux:latest

RUN yum update -y &&\
    yum upgrade -y &&\
    yum install java-11-amazon-corretto-headless -y

COPY ./build/libs/gradle-docker.jar /tmp/app.jar
ENTRYPOINT ["java", "-jar", "/tmp/app.jar"]

私はクラウド環境としてAmazon ECS / Fargate / EKS想定なのでamazonlinuxです(思考放棄)。
イメージサイズが重要であればubuntuやalpineでというのもありよりのありです。
alpineにcorretto突っ込むという荒業もできないことはないです...
Javaは11、LTSが無料で使えるのはありがたいですね。
ともかく、まずCOPYとENTRYPOINTはコメントアウトして、ちゃんと動くかまで確認します。

2. docker起動をビルドスクリプトに記述

ここが難所ですね。
まずプラグインを記入します。

build.gradle
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
  id 'com.palantir.docker' version '0.25.0'
  id 'com.palantir.docker-run' version '0.25.0'
}

それから、プラグインが持っているtask:dockerに対し、いろいろオプションを加えていくわけです。
copySpecの下りはわかりにくいですので後述します。

build.gradle
docker {
  // name: これはbuildされたコンテナイメージの名前になります。
  // コロンで区切った右側はタグとして認識されます。
  name "${project.name}:${project.version}"
  // いわずもがなDockerfileです。
  // パスは.\つけても素でも動きます。
  dockerfile file('Dockerfile')
  // Gradleのコピースペックを使って、ビルドした資材をpluginの利用先に持っていきます。
  copySpec.from('build/libs').into('build/libs')
  // docker buildのキャッシュ利用off
  noCache true
  // 依存関係定義:gradle dockerしたら、その前段でgradle buildが走ります
  dependsOn tasks.build
}

これで、基本command lineで"gradle docker"としたら、走るはずです。
※DockerfileのCOPY/ENTRYPOINTはコメントアウトから戻してください。

まぁせっかくなので、ええいままよとcontainer起動まで行きますか。
プラグインはもう入っているのでタスクのオプションを追加するだけです。
コンテナ起動オプションは、特に人それぞれ異なるので、
ドキュメントをきっちり読んでくださいね。

build.gradle
dockerRun {
  //コンテナ名です。
  name project.name
  // 起動するイメージ(dockerタスクで作ったやーつ)
  image "${project.name}:${project.version}"
  // コンテナをデーモン起動?:docker run "-d" <- こいつ
  daemonize false  // 否、我顕現す
  // 君はマグロか?(stopしたらauto terminate)
  clean true // いかにも、マグロだ。
}

と、これで以下のコマンドをたたけばひとまずものになるはずです。

$ ./gradlew docker
$ ./gradlew dockerRun
Hello World.

ただ、もう少しやるべきことはあります。

3. fatJarをつくる

現在はライブラリがないのでちゃんと動きますが、
フレームワーク・ライブラリを突っ込むと、classNotFoundで落ちます。
かくなる上は、buildするjarファイルにライブラリjarを突っ込むしかない(わけではないが楽)。
これもpluginを使えば楽です。
ShadowJar、今回はこれを使いましょう。
まずプラグイン追加して、

build.gradle
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
  id 'com.palantir.docker' version '0.25.0'
  id 'com.palantir.docker-run' version '0.25.0'
  id 'com.github.johnrengelman.shadow' version '6.1.0' // <-こいつ
}

それからタスクのオプションを追加します。
中にfinalizedByとあります。
これはdockerタスクを追加したときには伝えておりませんでしたが、
ちゃんとこのタスクが終わってから次のタスクに移るための明示です。
つまりこの場合、shadowJarタスクでjarを作り終わってからdockerタスクをさせたいということです。
後続するタスクのdependsOnをつけることで、確かにshadowJar->dockerで実行されます。
ただ、dockerタスクが動く前にshadowJarが完了することは保証されません。
正直ちょっと詰まりました...タスク依存ムズイ...

build.gradle
shadowJar {
  // jarタスクのを参照してくれないので、ここでもarchiveファイルは指定
  archiveFileName = "${project.name}.jar"
  // 依存関係
  finalizedBy tasks.docker
}

そして、前述のgradle docker/dockerRunコマンドでちゃんとイメージ作成・起動までこぎつけられたことでしょう。

内部での動き

dockerプラグイン君ですが、keyはイメージに突っ込むファイルです。
プラグインは/build/docker配下に資材(Dockerfile含)をため込み、
それをimageビルドに利用しています。
なので、dockerタスクのオプションでそれをしなければなりません。
プラグインはGradleの"copySpec"というAPIを使っています。
それで、
from /build/libs/*
to /build/docker/build/libs/*
こんな形でファイルをコピーさせているということですね。
それで、build/docker配下に同じくコピーされたDockerfileは、
その置き場所から./build/libs/your.jarを見つけ、COPYでイメージに突っ込んでいます。
ちなみに、task dockerのオプション file, copySpecを組み合わせて同様のことができます。

build.gradle
docker {
  name "${project.name}:${project.version}"
  dockerfile file('Dockerfile')
  files shadowJar.archiveFile.get()
  copySpec.into('build/libs')
  noCache true
  dependsOn tasks.shadowJar
}

filesオプションを事前に突っ込むことで、それもcopySpecに載せられます。
これを利用し、jarに梱包しない環境ファイルが別にある場合もイメージに突っ込めます。

開発に利用するなら

釈迦に説法かもしれませんが、
Dockerfileは書き換えての開発をお勧めします。
毎回gradle dockerでイメージビルドするのに、jdk落としてインスコしては萎えますので。
いっそjava環境まで作ったイメージをつくり、
以降はその部分をコメントアウトしてFROMを作ったイメージに向けるほうが圧倒的コスパです。

だそく

しかしこのプラグイン群すさまじいですね、やれることが多くて。
それこそimage pushやcompose利用など、いろいろできます。
こうしたものがあり、利用できるからエンジニアやれているのだと常々感じます。

ところで、話変わりますが「巨人の肩に乗る」って比喩でなくほんとに巨人なんですね。
SFの話です。
テューリアンは優しい。


それから、ちょっと面倒なので、docker rmiもgradleタスクで作ろうと思います。
プラグインに期待するのも1つですが、まぁできないことはないだろうと。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで環境構築 初心者編

dockerを使った環境構築を行ったのでその手順をご紹介します。
初めてdockerで環境構築を行ったので、少しでも参考になればと思います。

環境

環境     バージョン
PHP 7.0.27
FW CakePHP3.2.13
Apache 2.2.15
OS CentOS6.9
DB MySQL5.6.39

手順

  1. DockerフォルダDL
  2. Git Clone
  3. Docker コンテナに入る
  4. Apache起動
  5. ドキュメントルート設定
  6. Composer install
  7. mysql起動
  8. データベース作製
  9. app.phpの設定
  10. データベース接続

環境構築してみよう

DockerフォルダDL

今回は元々用意していたDockerFile,ymlファイルなどをまとめてダウンロードしてます。
下記、フォルダ構造になります。

-/
 |- docker-compose.yml
 |- docker
    |- db
    |   |- Dockerfile
    |   |- files
    |      |- entry.sh
    |      |- my.cnf
    |     
    |
    |- web
        |- Dockerfile
        |- files
           |- composer.phar
           |- dev.conf
           |- php.ini
           |- ssl.conf
 

Git Clone

githubにてプロジェクトの取得。
フォルダ構造は下記のようになりました。

-/
 |- project
 |- docker-compose.yml
 |- docker

Dockerコンテナに入る

コンテナに入る前にイメージの作製、コンテナを起動します。
コマンドラインで下記を実行する。

  • ビルド

    docker build
    // ビルドすることでdocker fileからイメージが得られます。
    
  • コンテナの起動

    $ docker-compose up -d
    // コンテナを起動します。
    

     下記のコマンドで、ビルドとコンテナの起動を一度に行えます。
  • ビルドとコンテナの起動

    $ docker-compose up -d --build
    // イメージのビルドから始めてコンテナを起動します。
    
$ docker ps
//起動中のコンテナの確認

 

オプション-dについて


$ docker-compose up
// ターミナルでコンテナを起動しているため、同じターミナルでは作業できない。

$ docker-compose up -d
// コンテナをバックグラウンドで起動するため、同じターミナルで作業ができる。

下記のコマンドでコンテナに入ります。

$ docker exec -it CONTAINER ID /bin/bash
//CONTAINER IDは docker psをした際に確認できます。

Apache起動


webコンテナ内で、下記コマンドでApache起動する。
service httpd start
Apache起動

service httpd stop
Apache停止

service httpd restart
Apache再起動

/etc/init.d/httpd status
Apache状態確認

ドキュメントルート設定

web内のhttpd.confのドキュメントルートの設定を確認します。
今回のディレクトリ構造だとvar/www/htmlで問題なさそうです。

composer install

composer installができませんでした。
アクセス権限の問題だと思い、アクセス権限777に変更しました。
アクセス権限に関しては下記の記事を参考にしました。
https://qiita.com/t-a-run/items/239ed690ece7a011804a

下記、実行できました。
因みに、composer installはprojectフォルダで行いました。

compser install

この状態でアクセスするとデータベースエラーとブラウザに表示されます。
エラーの内容は、そのような(接続した)ファイルは見つからない。

database error(SQLSTATE[HY000] [2002] No such file or directory

mysql起動

mysqlを動作させます。
webコンテナに入った時、同様にdbコンテナに入ります。

service mysqld status
//mysqlの起動状態の確認
service mysqld start
//mysqlの起動

ログイン前に初期のユーザー名とパスワードを確認します。
DBコンテナ内でmysqld.logを確認します。

cat mysqld.log

A temporary password is〜に書いてあるユーザー名パスワードを取得。
ユーザー名:root

データベースにログインします。

docker exec -it コンテナID mysql -u root -p

パスワード再設定->データベース作製->テーブル作成をおこないます。

app.phpの設定

git clone時には、app.phpは含まれていません。作成しましょう。

-/
 |- project
    |- config
        |-app.php

app.phpの書き方はネットで検索すると出てきます。今回は他で使っていたファイルをそのままコピーして中身を編集しました。主にデータベースのしか触っていないです。
 'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            /**
             * CakePHP will use the default DB port based on the driver selected
             * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
             * the following line and set the port accordingly
             */
            //'port' => 'non_standard_port_number',
            'username' => 'root',
            'password' => 'データベース作成時に設定したパスワード',
            'database' => 'データベース作成時に設定したパスワード',
            'port' => ポート番号,
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'flags' => [],
            'cacheMetadata' => true,
            'log' => false,

cat mysqld.log
A temporary password isに書いてあるパスワードを取得
->dbコンテナでmysqlをrestartし、ログイン->パスワード再設定->データベース作製->テーブル作成->

データベース接続

sequel proを使い、クイック接続にて接続テストを実行しました。
無事に接続完了しました。

ブラウザでlocalhostと検索すると無事開けました。

参考資料

docker-compose.yml の内容を理解しよう
https://futureys.tokyo/lets-understand-contents-of-docker-compose-yml/

Docker compose ことはじめハンズオン
https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a

Dockerfileの書き方と使い方
https://blog.codecamp.jp/docker-file-how-to

起動中の docker コンテナのシェルに入る
https://qiita.com/sekizo/items/27cc9b406332afc674f6

データベース作製
https://noumenon-th.net/programming/2019/04/01/docker-entrypoint-initdb01/

chmodで設定する権限の書き方
https://qiita.com/irasally/items/6ebc3c68e22905fb7330

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaジャーニー:快適なパッケージを求めて

ほぼ動的型付け言語しか使用しないチームに転職し、
┌( ^o^┌ )┐ <- これのごとく型付けェ型付けェと禁断症状に...
欲求不満に任せ、いつのまにか理想のGradleパッケージのジャーニーへと旅立っていました。
せっかくなので情報整理がてらやりたいこと・過程を紹介します。
あと、Githubにpublicのrepositoryを作っているので、
それをご覧いただくもよし、そのまま流用するもよし(そんな価値があればよいですが...)。
Github Link: gradle-docker
※本記事はJava/Gradle/Dockerなど基礎知識はすでにある前提でお読みください。

やりたいこと

  1. Dockerにすんなり疎通できる
  2. テストのフローはきっちり(Junit*Matcher - Jacocoでカバレッジ)(YET)
  3. Logも今風(slf4j*logback - Jsonized Log)(YET)
  4. 書き方も統制しよう(formatter, linter)(YET)

ここまでやれば、まぁ禁断症状は治まるかと...
あと、これをベースにAWSのコンソール操作を自動化するツールも作るつもりなので...

プロローグ

生まれたての小鹿パッケージ、作るだけ、作りますか。
gradle initの時の選択肢は、まぁオーソドックスですね。
javaですし、DSLはgroovyですし、テストはJUnitですし?。

$ cd '任意のディレクトリ'
$ mkdir 'project name'
$ cd 'project name'
$ gradle init --type java-application
$ gradle wrapper

これで、よくある、ローカルにjavaさえ入っていれば
- maven,gradleなくともできますよ、
- gradleは固定ですよ
の環境が出来上がりですね。
あとは最低限のプロジェクト・パッケージ情報をシコシコ仕込みますか。

gradle.build
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
}

repositories {
  mavenCentral()
}
// パッケージのバージョン指定
project.version = '0.1.1'
// javaランタイムは11(15はそのうち...)
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

dependencies {
  testImplementation 'junit:junit:4.13'
}

// Runするとき、Appのmain関数を起点に指定
application {
  mainClassName = 'info.akotadakura.App'
}

// UTF-8強要します。
tasks.withType(JavaCompile).all {
  options.encoding = "UTF-8"
}

// java -jarするとき、Appのmain関数を起点に指定
// あとjarファイル名にバージョンつくの嫌だ!
jar {
  archiveFileName = "${project.name}.jar"
  manifest {
    attributes 'Main-Class' : 'info.akotadakura.App'
  }
}

実に初々しい...
と、ここにいろいろ付け加えていくことになります。
まずはコンテナ対応、Docker Nativeな時代ですからね。

P.S.
初投稿なので、まさかりはお手柔らかに.....

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradleジャーニー:快適なパッケージを求めて

ほぼ動的型付け言語しか使用しないチームに転職し、
┌( ^o^┌ )┐ <- これのごとく型付けェ型付けェと禁断症状に...
欲求不満に任せ、いつのまにか理想のGradleパッケージのジャーニーへと旅立っていました。
せっかくなので情報整理がてらやりたいこと・過程を紹介します。
あと、Githubにpublicのrepositoryを作っているので、
それをご覧いただくもよし、そのまま流用するもよし(そんな価値があればよいですが...)。
Github Link: gradle-docker
※本記事はJava/Gradle/Dockerなど基礎知識はすでにある前提でお読みください。

やりたいこと

  1. Dockerにすんなり疎通できる
  2. テストのフローはきっちり(Junit*Matcher - Jacocoでカバレッジ)(YET)
  3. Logも今風(slf4j*logback - Jsonized Log)(YET)
  4. 書き方も統制しよう(formatter, linter)(YET)

ここまでやれば、まぁ禁断症状は治まるかと...
あと、これをベースにAWSのコンソール操作を自動化するツールも作るつもりなので...

プロローグ

生まれたての小鹿パッケージ、作るだけ、作りますか。
gradle initの時の選択肢は、まぁオーソドックスですね。
javaですし、DSLはgroovyですし、テストはJUnitですし?。

$ cd '任意のディレクトリ'
$ mkdir 'project name'
$ cd 'project name'
$ gradle init --type java-application
$ gradle wrapper

これで、よくある、ローカルにjavaさえ入っていれば
- maven,gradleなくともできますよ、
- gradleは固定ですよ
の環境が出来上がりですね。
あとは最低限のプロジェクト・パッケージ情報をシコシコ仕込みますか。

gradle.build
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
}

repositories {
  mavenCentral()
}
// パッケージのバージョン指定
project.version = '0.1.1'
// javaランタイムは11(15はそのうち...)
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

dependencies {
  testImplementation 'junit:junit:4.13'
}

// Runするとき、Appのmain関数を起点に指定
application {
  mainClassName = 'info.akotadakura.App'
}

// UTF-8強要します。
tasks.withType(JavaCompile).all {
  options.encoding = "UTF-8"
}

// java -jarするとき、Appのmain関数を起点に指定
// あとjarファイル名にバージョンつくの嫌だ!
jar {
  archiveFileName = "${project.name}.jar"
  manifest {
    attributes 'Main-Class' : 'info.akotadakura.App'
  }
}

実に初々しい...
と、ここにいろいろ付け加えていくことになります。
まずはコンテナ対応、Docker Nativeな時代ですからね。

P.S.
初投稿なので、まさかりはお手柔らかに.....

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む