20200715のdockerに関する記事は25件です。

WordPress プラグイン開発を Docker と VS Code で行う

この記事で出来るもの

 VS Code で XDebug を使い、 PHPUnit でテストが出来る WordPress プラグイン開発用の Dockerfile と docker-compose.yml と VS Code の設定ファイルと php.ini が出来上がります。

環境

  • Mac OS 15.5
  • Docker Desktop 2.3.0.3
  • VS Code 1.47.1
    • PHP Debug 1.13.0 ( Felix Becker 氏の作 )

 ディレクトリ構成は下記の通りだとします。ちなみに、プラグインディレクトリはアンダースコア区切りではなく、ハイフン区切りかキャメルケースを使いましょう。PHPUnitでハマります。
 また、既に yourPlugin.php には適切なヘッダー情報が書いてあるとします。適切なヘッダー情報がよく分からなかったら、 ここをコピペして用意しておきましょう。

current_directory
|_.vscode
 |_launch.json
|_docker-compose.yml
|_Dockerfile
|_php.ini
|_your-plugin-directory/
 |_yourPlugin.php
 |_tests/
  |_testYourPlugin.php

PHPUnit でハマるポイント

参考 : why is phpunit not taking directory which have a underscore '_'
上記への回答 : underscore chars in directory name

Dockerfile & docker-compose.yml

 Docker のベースイメージは、 Docker Official が提供するものです。これに XDebug と PHPUnit をインストールし、 Docker Compose クイックスタートにある docker-compose.yml を改変します。

Dockerfile

Dockerfile
FROM wordpress:latest

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

RUN curl -L https://phar.phpunit.de/phpunit-7.5.9.phar >> phpunit-7.5.9.phar && \
    chmod +x phpunit-7.5.9.phar && \
    mv phpunit-7.5.9.phar /usr/local/bin/phpunit

docker-compose.yml

docker-compose.yml
version: '3'

services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./your-plugin-directory:/var/www/html/wp-content/plugins/your-plugin-directory
      - ./php.ini:/usr/local/etc/php/php.ini
    ports:
      - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

 ここで一旦適当に touch php.ini してから docker-compose build をしましょう。
 出てきたログの中に php.ini に書き込む zend_extension の設定が書かれているので、注意してください。
 PHPUnit をグローバルにすべきではないと言う声も聞こえてきそうですが、ここは Docker の中でプラグインひとつを開発するための環境なので、あまり気にしていません。

php.ini と launch.json

 php.ini と .vscode/launch.json を書くことで、 VS Code で PHP Debug を使ったデバッグが出来る様になります。

php.ini

php.ini
; Docker Image をビルドすると、
; You should add "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so" to php.ini
; と言われるので素直に書きます。
; ここは人によって異なる可能性があるので注意が必要です。
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
[xdebug]
xdebug.remote_enable=1
xdebug.remote_autostart=1
; ホスト側のIP
; host.docker.internalはdockerのhostマシンのIPを解決してくれます。
; hostマシン以外のIPの場合は適宜IPを調べて設定してください。
xdebug.remote_host=host.docker.internal
; 空いているport番号(xdebugのデフォルトは9000)。
xdebug.remote_port=9000
; xdebugの出力するログの場所。今回は適当に/tmp配下に。
xdebug.remote_log=/tmp/xdebug.log

./vscode/launch.json

${workspaceRoot} を設定するのがキモです。

./vscode/launch.json
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "php",
      "name": "Docker debug",
      "request": "launch",
      "port": 9000,
      "pathMappings": {
        "/var/www/html/wp-content/plugins/your-plugin":"${workspaceRoot}/your-plugin"
      }
    }
  ]
}

 ここで docker-comopse up -d して、ブラウザから localhost:8000 にアクセスすることで Wordpress のセットアップが始まります。いつものセットアップが終わった後、管理ページにログインをしてプラグインを見に行くと、既にプラグインがインストールされているはずです。

確認

XDebug

 ちゃんと XDebug が使えるか確認します。そのために、プラグインディレクトリに下記の様なプラグインの雛形を作りましょう。

your-plugin-directory/yourPlugin.php

your-plugin-directory/yourPlugin.php
<?php
/*
Plugin Name: Your Plugin
Description: プラグインの雛形です。
Version:     0.0.1
Author:      rotelstift
Text Domain: your-plugin
License:     GPL2

*/

class Your_Plugin {
    private const MENU_SLUG = 'your_plugin';

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_pages' ) );
    }

    public function add_pages() {
        add_menu_page( 'Your Plugin Option', 'Your Plugin', 'activate_plugins', self::MENU_SLUG, array( $this, 'your_plugin_option_page' ) );
    }

    public function your_plugin_option_page() {
        echo 'This is your plugin.';
    }
}

$your_plugin = new Your_Plugin;

 このプラグインを有効化すると、管理ページサイドメニューの一番下に Your Plugin というリンクが追加されるはずです。
 そして、 VS Code のデバッガから echoのある行にブレークポイントを設定してデバッガをスタートさせてから、 Your Plugin のリンクをクリックしましょう。ちゃんとデバッガが動いていれば、 VS Code に処理が移るはずです。

PHPUnit

 PHPUnit が動くかどうかのテストは簡単です。以下のコマンドを打ちましょう。

docker-compose run --rm wordpress phpunit --version

 これでバージョン情報が表示されれば問題ありません。
 しかし、テストファイルを指定するときに若干の難があります。その難を回避するためには、以下の様にコマンドを打ってください。

docker-compose run --rm wordpress /bin/bash -c "cd wp-content/plugins/your-plugin && phpunit tests/*"
# 以下の二つはなぜかエラーになる
docker-compose run --rm wordpress phpunit your-plugin/tests/*
docker-compose run --rm wordpress phpunit wp-content/plugins/your-plugin/tests/*

 サンプル用のテストファイルはこちらのリンク先のものを使えばいいでしょう。

参考資料

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

Docker × Spring Boot環境構築

Docker × Spring Boot環境構築

概要

Dockerを使って、Spring Bootの環境を構築します。
動作確認として、Hello Worldをブラウザに表示させます。

環境

  • macOS Catalina バージョン10.15.5
  • Docker version 19.03.8
  • docker-compose version 1.25.5
  • openjdk 14.0.1 2020-04-14
  • Spring Boot 2.3.1

構成

最終的には以下のような構成になります。

├── docker-compose.yml
└── server
    ├── HELP.md
    ├── build
    │   ├── classes
    │   │   └── java
    │   │       ├── main
    │   │       │   └── com
    │   │       │       └── example
    │   │       │           └── api
    │   │       │               └── ApiApplication.class
    │   │       └── test
    │   │           └── com
    │   │               └── example
    │   │                   └── api
    │   │                       └── ApiApplicationTests.class
    │   ├── generated
    │   │   └── sources
    │   │       ├── annotationProcessor
    │   │       │   └── java
    │   │       │       ├── main
    │   │       │       └── test
    │   │       └── headers
    │   │           └── java
    │   │               ├── main
    │   │               └── test
    │   ├── libs
    │   │   └── api-0.0.1-SNAPSHOT.jar
    │   ├── reports
    │   │   └── tests
    │   │       └── test
    │   │           ├── classes
    │   │           │   └── com.example.api.ApiApplicationTests.html
    │   │           ├── css
    │   │           │   ├── base-style.css
    │   │           │   └── style.css
    │   │           ├── index.html
    │   │           ├── js
    │   │           │   └── report.js
    │   │           └── packages
    │   │               └── com.example.api.html
    │   ├── resources
    │   │   └── main
    │   │       ├── application.properties
    │   │       ├── static
    │   │       └── templates
    │   ├── test-results
    │   │   └── test
    │   │       ├── TEST-com.example.api.ApiApplicationTests.xml
    │   │       └── binary
    │   │           ├── output.bin
    │   │           ├── output.bin.idx
    │   │           └── results.bin
    │   └── tmp
    │       ├── bootJar
    │       │   └── MANIFEST.MF
    │       ├── compileJava
    │       └── compileTestJava
    ├── build.gradle
    ├── gradle
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew
    ├── gradlew.bat
    ├── settings.gradle
    └── src
        ├── main
        │   ├── java
        │   │   └── com
        │   │       └── example
        │   │           └── api
        │   │               └── ApiApplication.java
        │   └── resources
        │       ├── application.properties
        │       ├── static
        │       └── templates
        └── test
            └── java
                └── com
                    └── example
                        └── api
                            └── ApiApplicationTests.java

手順

1. docker-compose.yml作成

今回はjavaコンテナ1つのみのシンプルな構成です。

docker-compose.yml
version: '3.6'
services:
  java:
    image: openjdk:14-slim
    ports:
      - 8080:8080
    tty: true
    volumes:
      - ./server:/srv:cached
    working_dir: /srv

2. Gradleプロジェクト作成

2.1 spring initializrにてGradleプロジェクトの雛形を作成
スクリーンショット 2020-07-10 11.37.29.png

2.2 Gradleプロジェクトの雛形をダウンロード
スクリーンショット 2020-07-10 11.43.22.png

2.3 プロジェクトに展開
プロジェクトディレクトリ直下にserverディレクトリを作成します。
2.2でダウンロードした api.zipを展開し、中身をserver直下に移します。

3. アプリケーションファイルを編集

server/src/main/java/com/example/api/ApiApplication.javaを以下のように編集します。

server/src/main/java/com/example/api/ApiApplication.java
package com.example.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ApiApplication {

    @RequestMapping("/")
    public String home() {
        return "Hello World";
    }

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

}

4. Docker起動

// docker環境ビルド
% docker-compose build

// dockerをバックグラウンドで起動
% docker-compose up -d

5. Gradleのビルド

// javaコンテナにインスペクション
% docker-compose exec java bash

// gradleビルド
root@62acca270468:/srv# sh gradlew build
...
BUILD SUCCESSFUL in 1m 38s
5 actionable tasks: 3 executed, 2 up-to-date

server直下にbuildディレクトリができていることを確認する。

6. アプリケーションの実行

root@62acca270468:/srv# java -jar build/libs/api-0.0.1-SNAPSHOT.jar

7. 動作確認

http://localhost:8080/ にアクセスしてみましょう。
Hello Worldが表示されていたら成功です。

参考

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

Docker + Rails6 + PostgreSQL 環境構築

はじめに

今までRails5 + MySQL での開発しか行ってこなかった自分でしたが、初めてRails6 + PostgreSQL での環境構築を行ったため備忘録も兼ねて記述していきたいと思います。

必要なファイル

作業フォルダを作成します。
今回は sample_app とします。

$ mkdir sample_app

そして必要なファイルを以下の構成で作成します。

$ touch ○○(ファイル名)

- sample_app
    - Dockerfile
    - docker-compose.yml
    - Gemfile
    - Gemfile.lock
    - entrypoint.sh

ではそれぞれを編集していきます。

Dockerfile

Rails6 からWebpackerがデフォルトで導入されたためインストールする必要があります。
そのため、DockerfileにはWebpackerをインストールするために必要なyarnNode.jsをインストールするように記述します。

FROM ruby:2.6.5

# 必要なライブラリインストール
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

# yarnパッケージ管理ツールをインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
  curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update && apt-get install -y yarn

# Node.jsをインストール
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - && \
  apt-get install nodejs

RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose.yml

docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

Gemfile

source 'https://rubygems.org'
gem 'rails', '~> 6'

Gemfile.lock

ファイルを作成だけして、編集は何もしません。

entrypoint.sh

entrypoint.sh
set -e

rm -f /myapp/tmp/pids/server.pid

exec "$@"

Railsアプリの作成

作成したDockerfile元にイメージがbuildし、生成されたコンテナ内でrails newを実行します

$ docker-compose run web rails new . --force --no-deps --database=postgresql --skip-bundle

bundle install

rails newコマンドによりGemfile内の記述が変更されているためbundle installをする必要があります。
イメージをbuildすることでbundle installも実行されるため以下のコマンドを実行します。

$ docker-compose build

database.yml

rails newで作成されたdatabase.ymlを以下のように編集します。

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test

webpackerのインストール

$ docker-compose run web bundle exec rails webpacker:install

コンテナを起動

$ docker-compose up

DB作成

$ docker-compose run web rake db:create

おわり

これでhttp://localhost:3000/にアクセスするとサーバーが立ち上がっていることを確認できるかと思います。
最後まで見ていただき、ありがとうございました。

参考記事
DockerでRuby on Railsの環境構築を行うためのステップ【Rails 6対応】
Rails6 開発時につまづきそうな webpacker, yarn 関係のエラーと解決方法
PostgreSQL(Docker)にRails(Docker)が接続できなくなったから調べてみた。(could not translate host name "db" to address: Name or service not known)

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

Azure App Serviceに環境変数が設定されなくて困った。と思ったら原因が違った話

はじめに

自作のdenoアプリのコンテナをAzure App Serviceにデプロイしたときに、環境変数が設定されないと思ったら、単にアプリ側で環境変数を読み込むようにしていなかった話です。

以下は試行錯誤のメモですが、結論は上記なのであまり読む意味はないかもしれないです。

事の発端

Qiita APIを使用して、特定のユーザがLGTMした記事一覧を取得するWebアプリ(deno)を作っていました。
ローカルの開発環境で正常にレスポンスが返却されることを確認し、Azure Web Appsにデプロイしたところ・・・

{"message":"Unauthorized","type":"unauthorized"}

こんなレスポンスが返ってきてしまいました。
どうやらQiita APIの認証が正しくされていないようです。

私の作ったアプリでは、環境変数API_KEYにQiita APIのアクセストークンを設定しているので、環境変数がうまく読み込めていないことが推測されます。

Azureポータルを確認すると、

image.png

確かにAPI_KEYは設定されています。

おかしいな?と思い、Kuduコンソールからログを見てみると、こんな感じのログが出ていました(値はマスクしています)。

LogFiles/yyyy_mm_dd_XXX_docker.log(抜粋)
docker run -d -p 6740:5000 --name XXX -e WEBSITE_CORS_ALLOWED_ORIGINS=XXX -e WEBSITE_CORS_SUPPORT_CREDENTIALS=XXX -e WEBSITES_ENABLE_APP_SERVICE_STORAGE=XXX -e WEBSITES_PORT=5000 -e WEBSITE_SITE_NAME=XXX -e WEBSITE_AUTH_ENABLED=XXX -e WEBSITE_ROLE_INSTANCE_ID=XXX -e WEBSITE_HOSTNAME=XXX -e WEBSITE_INSTANCE_ID=XXX -e HTTP_LOGGING_ENABLED=XXX XXX  

docker runの時に-e API_KEY=XXXがついていない!他のはあるのに!
ここが原因だろうと推測し、焦点を当てて調べました。

ドキュメントには以下の記載があります。
Things You Should Know: Web Apps and Linux | Microsoft Docs

App Settings are injected into your app as environment variables at runtime.
Applies to Azure App Service on Linux Applies to Web App for Containers
If you need to set an environment variable for your application, simply add an App Setting in the Azure portal. When your app runs, we will inject the app setting into the process as an environment variable automatically.

感覚的にはこれは正しく、実際他のアプリでは普通に環境変数を読み込めています。

次に、以下のIssueを見つけました。

When I tail the log stream of my container, I can see the actual command that is ran in order to spin the container up. While it does contain the following ENV_VARS, the ones I set as application settings are still missing.

結構昔のIssueですが、同じような現象が起こっている人がいるっぽいです。
これはAzureのバグか・・・?
だとするとお手上げです。

DockerfileのENVに設定したり、.envをPushしてしまえば回避はできそうですが、アクセストークンを公開するのは当然やりたくありません。

なんとかして、環境変数を入れ込むことできないかなぁ・・・

考えた苦肉の策

Azureポータル上からDockerコンテナ起動時のエントリーポイントを上書きし、「環境変数設定+アプリ実行コマンド」をdocker run時に実行するようにしました。

具体的には、以下の「スタートアップ コマンド」に、
image.png

以下を設定しました。

sh -c "export API_KEY=XXX && deno run --allow-net --allow-read index.ts"

※もういらないだろうと思い、ポータルから設定した環境変数は消しました

再びKuduコンソールからログを確認すると、

LogFiles/yyyy_mm_dd_XXX_docker.log(抜粋)
docker run -d -p 8761:5000 --name XXX -e WEBSITE_CORS_ALLOWED_ORIGINS=XXX -e WEBSITE_CORS_SUPPORT_CREDENTIALS=XXX -e WEBSITES_ENABLE_APP_SERVICE_STORAGE=XXX -e WEBSITES_PORT=5000 -e WEBSITE_SITE_NAME=XXX-e WEBSITE_AUTH_ENABLED=XXX -e WEBSITE_ROLE_INSTANCE_ID=XXX -e WEBSITE_HOSTNAME=XXX -e WEBSITE_INSTANCE_ID=XXX -e HTTP_LOGGING_ENABLED=XXX XXX sh -c "export API_KEY=0090f2939950cb952d569adee4a15caddae4d807 && deno run --allow-net --allow-read index.ts" 

確かにコマンドは渡っていそうです。



しかし、レスポンスは相変わらずUnauthorized。

これでも無理なのはおかしい!!



・・・もしかして、アプリ側の問題なのでは?

示された道筋

現状のコードは以下の通りです。

index.ts(抜粋)
import { config } from "https://deno.land/x/dotenv/mod.ts";

// .env読み込み
let env = config();

// 変数利用
headers: {
  Authorization: `Bearer ${env.API_KEY}`,
},

待てよ・・・
Node.jsと違ってprocess.envで読み込んでいるわけじゃないから、このコードだと.envファイルからしか読み込まないんじゃ・・・?

環境変数を直で読み込む方法を調べると、

github.com/denoland/deno/releases/latest/download/lib.deno.d.ts - deno doc

const Deno.env: { get(key: string): string | undefined, set(key: string, value: string): void, delete(key: string): void, toObject(): { index: string: string } }

Deno.env.getというのが該当しそう。

ついでに、

Deno

--allow-env Allow environment access for things like getting and setting of environment variables.

なんか--allow-envってオプションあるわ・・・

というわけで、「スタートアップ コマンド」を以下のように変更し、

--allow-envを追加
sh -c "export API_KEY=XXX && deno run --allow-net --allow-read --allow-env index.ts"

ソースコードを以下のように変更しPushすると・・・

index.ts(抜粋)
import { config } from "https://deno.land/x/dotenv/mod.ts";

// 環境変数読み込み
let env = config();
if (Deno.env.get("API_KEY")) {
  env.API_KEY = Deno.env.get("API_KEY")!;
}

// 変数利用
headers: {
  Authorization: `Bearer ${env.API_KEY}`,
},

動・・・かない!動かない!
相変わらずUnauthorizedです。

・・・いや、こうなってくると、そもそもポータルから設定した環境変数は実はちゃんと存在していて、単に読み込めていなかった説があるのでは・・・?

本当の原因

「スタートアップ コマンド」を削除し、Azureポータルからもう一度API_KEYを設定し再ビルドすると、何事もなかったかのように動きました。

つまり、「開発環境では.envから環境変数を読み込むためにconfigモジュールを使っていたが、本番環境では環境変数を直で読み込むためにDeno.env.get()を使う必要があった」

というのが本当の原因と言えそうです。

ただし、謎も二つ残ります。

  • なぜAzureポータルから環境変数を設定しても、ログのdocker run-eで渡らないのか(それでなぜ動くのか)。
  • なぜ「スタートアップ コマンド」に環境変数を渡してもなぜ動かないのか

でもまあ、今回は動いたのでよしとします。

おわりに

早とちりはよくないですね。

ちなみに、https://deno.land/x/dotenv/load.tsを使用すれば.envに記述した環境変数もDeno.env.get()で取得できたみたいです。はじめに試した時に、VS Code上で「Denoモジュールが解決できないよ」的なエラーが(何故か)出ていたため、使えないと思ってスルーしちゃっていました。

無事構築できた手順の記事はこちらです。
↓↓↓↓
Deno in Dockerなアプリ開発

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

Deno in Dockerなアプリ開発

はじめに

練習がてら、タイトルの技術を使用した簡単なアプリを作ってみました。
ソースコードはこちら(UI)こちら(サーバ)です。

アプリそのものというより、構築方法に主眼を置いた記事です。
以下が本稿で説明する内容です。

  • DenoコンテナをVS Code Remote Containersで開発するための環境構築方法
  • DenoコンテナをGitHubからAzureに自動デプロイする方法

アプリの概要

Qiitaで良い記事を見つけると、ストックをしますが見返すことがほぼありません。これではストックの意味がないので、過去にストックした記事をランダムで表示させることで、過去に覚えた(はず)の知識のおさらいをするアプリです。

作成したアプリはこちらです。ユーザ名欄に任意のユーザ(ユーザ自身を想定)を入力し、表示ボタンを何回か押してみてください。

構築の練習が目的のため、アプリ自体はかなりしょぼいのでご了承ください。
またAzure Web Appsの無料枠を使っているため初回アクセス時は重いです。

主な使用技術

  • Azure App Service ・・・ サーバアプリのデプロイ先
  • Azure DevOps ・・・ サーバアプリの自動デプロイ
  • Deno ・・・ サーバアプリ作成に使用
  • VS Code Remote Containers(Docker) ・・・ 開発環境として使用
  • GitHub Pages ・・・ 画面のデプロイ先1
  • React ・・・ 画面作成に使用1

構成

UIとサーバのリポジトリを分けており、UIはGitHub Pages、サーバはコンテナ化してAzure DevOpsのPipelinesで自動デプロイするようにしています。

また、開発環境は二つともVS Code Remote Containersで開発するようにしています。

image.png

DenoコンテナをVS Code Remote Containersで開発するための環境構築方法

重要な部分だけかいつまんで説明します。コードの全体はこちらにあります。

VS Code Remote Containersの設定をする

devcontainer.jsonを以下のように設定します。拡張機能のdenoland.vscode-denoをインストールし、"deno.enable": trueとするところがポイントです。

.devcontainer/devcontainer.json
{
  "name": "Your-Memory-Server",
  "dockerFile": "Dockerfile",
  "shutdownAction": "none",
  "appPort": "5000:5000",
  "extensions": [
    "denoland.vscode-deno",
    "eamodio.gitlens",
    "mhutchie.git-graph"
  ],
  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash",
    "editor.formatOnSave": true,
    "editor.tabSize": 2,
    "editor.detectIndentation": false,
    "editor.insertSpaces": true,
    "deno.enable": true
  }
}

Dockerfileは以下のようにしました。denoのインストールは公式の通りですが、私の環境ではパスを設定しないとdenoコマンドが動きませんでした。また、開発用にgitやcurlがほしかったためベースイメージをbuildpack-depsとしています。

.devcontainer/Dockerfile
FROM buildpack-deps:stretch

RUN curl -fsSL https://deno.land/x/install/install.sh | sh
ENV DENO_INSTALL /root/.deno
ENV PATH $DENO_INSTALL/bin:$PATH

この状態でVS CodeのRemote-Containers: Reopen in Containerをクリックすることでコンテナ内に入り、開発が可能です。
image.png

VS Codeでdenoアプリをデバッグする

launch.jsonを以下のように設定し、F5でデバッグ可能です。

.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "runtimeExecutable": "deno",
      "runtimeArgs": [
        "run",
        "--inspect-brk",
        "--allow-net",
        "--allow-read",
        "-A",
        "index.ts"
      ],
      "port": 9229
    }
  ]
}

--allow-netはサーバ起動、--allow-read.env読み込みのために指定しています。

本番ビルド用のDockerfileを作成する

上記のDockerfileはあくまで開発用のコンテナを作成するためのものなので、実際にAzureにデプロイするコンテナを作成するDockerfileも別で用意します。

Dockerfile
FROM buildpack-deps:stretch-curl

ENV DIRPATH /opt/app
WORKDIR $DIRPATH

RUN apt-get update && apt-get install -y --no-install-recommends \
    unzip

RUN curl -fsSL https://deno.land/x/install/install.sh | sh
ENV DENO_INSTALL /root/.deno
ENV PATH $DENO_INSTALL/bin:$PATH

COPY . $DIRPATH

ENTRYPOINT deno run --allow-net --allow-read --allow-env index.ts

本番環境では.envを介さずに直接環境変数を読み込むため、--allow-envを追加しています。

コード

リクエストパラメータとしてユーザ名を投げるとそのユーザがストックした記事一覧をレスポンスとして返す簡単なサーバです。deno標準のhttpライブラリを使用していますが、どうやらルーティングやリクエストパラメータのパース機能が用意されていないようなので、しょうがなく自前実装しています。

本格的にやるなら以下のようなライブラリを使った方がよさそうです。

また、ストック記事は100件しか取得していませんしキャッシュもしていません。これは、単に実装の手を抜いたためです。

index.ts
import { serve } from "https://deno.land/std/http/server.ts";
import { config } from "https://deno.land/x/dotenv/mod.ts";

let env = config();
if (Deno.env.get("API_KEY")) {
  env.API_KEY = Deno.env.get("API_KEY")!;
}

for await (const req of serve({ port: 5000 })) {
  if (req.method === "GET" && req.url.split("?")[0] === "/api/v1/article") {
    let params: { [key: string]: any } = {};
    const paramAry = req.url.split("?")[1].split("&");
    for (const param of paramAry) {
      const [key, value] = param.split("=");
      params[key] = value;
    }
    const res = await fetch(
      `https://qiita.com/api/v2/users/${params.username}/stocks?per_page=100`,
      {
        headers: {
          Authorization: `Bearer ${env.API_KEY}`,
        },
      },
    );
    const json = res.json();
    const data = await json;
    req.respond({
      status: 200,
      headers: new Headers({
        "Content-Type": "application/json",
      }),
      body: JSON.stringify(data),
    });
  } else {
    req.respond({
      status: 404,
      body: JSON.stringify({}),
    });
  }
}

DenoコンテナをGitHubからAzureに自動デプロイする方法

デプロイ先のAzure Web Appsを作成する

はじめに、Azureポータルでデプロイ先のコンテナを作成します。
image.png

ここの詳細な手順は省略します。

自動デプロイ設定をする

  1. 作成したAzure Web Appsで、デプロイセンターを選択し、Get startedをクリックします。
    image.png

  2. 最初にデプロイ元を選択します。ここではGitHubを選択し、Nextをクリックします。
    image.png

  3. 次にGitHubの中から対象のリポジトリを選択します。Authorizeをクリックし、GitHubにログインします。
    image.png

  4. 対象のリポジトリとブランチを選択し、Nextをクリックします。
    image.png

  5. 次に、対象のリポジトリからビルド用のDockerfileと公開ポートを選択します。
    今回は開発用と本番用で二種類のDockerfileがあるため、本番用を選択するように気をつけます。Portにはコンテナ内のアプリのポートを指定します(仮に5000とすると、docker run -p 80:5000になるイメージ)。
    image.png

  6. Azure DevOpsのプロジェクト名などを入力し、最後にDoneをクリックします。
    image.png

完了すると、Azure DevOpsにデプロイ用のパイプラインが作成されます。これにより、指定したGitHubリポジトリのmasterブランチからビルドされたDockerイメージがAzure Container RegistryにPushされ、Azure App Serviceにアプリがデプロイされます。

おわりに

AzureはUIが綺麗で好きです。次はこの構成でちゃんとしたアプリを作成したいですね。

ちなみに、この構成を作るにあたって結構はまりました。
結果的には大した事のない問題だったのですが、一応記事にしてあります。
↓↓↓↓
Azure App Serviceに環境変数が設定されなくて困った。と思ったら原因が違った話


  1. 本稿では触れません。 

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

Docker+anaconda上でtensorflowを動かしてみる

はじめに

新しくPCを購入したのでDockerを使用してpythonの環境構築してCNNを動かしてみました。
Dockerはほぼ初心者なのでこれを機に勉強していこうと思います。


今回の流れとしては
1.Docker上で公式のanaconda3イメージを取得する
2.anaconda3イメージを基にコンテナを作成
3.anaconda3にtensorflowを導入する
4.JupyterNoteookをブラウザから開く
5.Dockerのメモリを増やす
6.tensorflowを使用してCNNを構築する
となっています。

環境

  • MacOS Catalina version 10.15.5

  • Docker version 19.03.8

1.Docker上で公式のanaconda3イメージを取得する

anacondaとは、データサイエンスをする方には必須なパッケージが一通り揃っているオープンソースのプラットフォームです。
こちらは公式からanaconda3のイメージがあるのでありがたく使っていきます。

https://hub.docker.com/r/continuumio/anaconda3/

それでは、Dockerを起動してAnaconda3のイメージを取得します。

% docker pull continuumio/anaconda3

Using default tag: latest
latest: Pulling from continuumio/anaconda3
68ced04f60ab: Pull complete
57047f2400d7: Pull complete
8b26dd278326: Pull complete
Digest: sha256:6502693fd278ba962af34c756ed9a9f0c3b6236a62f1e1fecb41f60c3f536d3c
Status: Downloaded newer image for continuumio/anaconda3:latest
docker.io/continuumio/anaconda3:latest

pullはDockerイメージを取得するコマンドです。

2.anaconda3イメージを基にコンテナを作成

次にコンテナを作成していきます。

% docker run --name anaconda -it -p 8888:8888 -v /Users/xxxx/docker/anaconda:/home continuumio/anaconda3 /bin/bash
(base) root@xxxx:/# conda list

各コマンド、オプションの意味は以下です。

  • run : コンテナを生成します。

  • --name [名前] : コンテナの名前を設定します。

  • -it : コンテナ内を操作できます。

  • -p [ホスト側のポート番号:コンテナのポート番号] : コンテナのポートをホスト側にバインドします。

  • -v [ホスト側のディレクトリ:コンテナ側のディレクトリ] : ホスト側のディレクトリをコンテナ内に
    マウントします。

3.anaconda3にtensorflowを導入する

anaconda3に事前からインストールされているパッケージを確認してみます。

base) root@xxxx:/# conda list
# packages in environment at /opt/conda:
#
# Name                    Version                   Build  Channel
_ipyw_jlab_nb_ext_conf    0.1.0                    py37_0  
_libgcc_mutex             0.1                        main  
alabaster                 0.7.12                   py37_0  
anaconda                  2020.02                  py37_0  
anaconda-client           1.7.2                    py37_0  
anaconda-navigator        1.9.12                   py37_0  
anaconda-project          0.8.4                      py_0  
argh                      0.26.2                   py37_0  
asn1crypto                1.3.0                    py37_0  
astroid                   2.3.3                    py37_0  
astropy                   4.0              py37h7b6447c_0  
atomicwrites              1.3.0                    py37_1  
attrs                     19.3.0                     py_0  
.
.
.
yaml                      0.1.7                had09818_2  
yapf                      0.28.0                     py_0  
zeromq                    4.3.1                he6710b0_3  
zict                      1.0.0                      py_0  
zipp                      2.2.0                      py_0  
zlib                      1.2.11               h7b6447c_3  
zstd                      1.3.7                h0b5b093_0  

anaconda3のコンテナに入っているのでdockerコマンドは使えません。
anaconda terminalで使用できるconda listでパッケージを見ます。


anaconda3はtensorflowが最初からインストールされていないので、conda installを用いて
tensorflowとその周辺のパッケージをインストールしてきます。

base) root@xxxx:/# conda install tensorflow

4.JupyterNotebookをブラウザから開く

大学時代に愛用していたJupyterNotebookをブラウザから開けるようにします。

base) root@xxxx:/# jupyter notebook --port 8888 --ip=0.0.0.0 --allow-root
.
.
.
To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-12-open.html
    Or copy and paste one of these URLs:
        http://81127b992594:8888/?token=22ab4d7b42e6629eb76dad08af9c8e6b1d5b59e0f0050f73
     or http://127.0.0.1:8888/?token=22ab4d7b42e6629eb76dad08af9c8e6b1d5b59e0f0050f73



末尾のURLをブラウザに入力するとJupyterNotebookが起動します。

jupyter.png

5.Dockerのメモリを増やす

CNNを構築する前にDockerはデフォルトではメモリが2Gまでしか使用できないので、CNNで学習させようとするとオーバーフローします。なので7Gまで使用できるように設定を変更していきます。


1. デスクトップの上部にあるDockerアイコンをクリック
2. Preferenceをクリック
3. Resourcesのタブをクリック
4. Memoryを7Gにする
スクリーンショット 2020-07-15 22.12.29.png

6.tensorflowを使用してCNNを構築する

最後にCNNを構築していきます。データセットは定番のCIFAR10をダウンロードし、3層の畳み込み層、最適化はadam法、損失関数はクロスエントロピー誤差を使用する至ってシンプルな構築でいきます。
スクリーンショット 2020-07-09 22.45.21.png


tensorflowを問題なくインポートできて、メモリもオーバーフローしなかったのでこれで環境構築終了です!

終わりに

Dockerを軽い気持ちで触ってみましたが、奥が深すぎて沼にハマりそうでした...
anaconda3にtensorflowを導入かつJupyterNotebookを開くという、おそらくDocker経験者なら簡単なことにかなり難航しました。Dockerはやっておいて損はないので積極的に使っていきたいです。
とりあえずtensorflowを動かすことができる環境を構築することができたので、これから色々深層学習についてこの環境で試していこうと思います。

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

初心者がWeb開発するために Docker + Rails6 + PostgreSQL の環境構築をする話

はじめに

いろいろあって友人とWeb開発を行うことになりました。
初心者のため勉強しながらの開発となるので、
これからは勉強した内容のアウトプットのためにいろいろ記事にしようと思います。
まず第一歩として環境構築からです。

環境

ubuntu 18.04

0.前提

Dockerインストール済み
まだの方はこちらから
https://docs.docker.com/compose/install/

1.必要なディレクトリ・ファイル準備

開発したい場所に開発をするディレクトリを作成します。
今回ディレクトリの名前は 'myapp'

$ mkdir myapp
$ cd myapp

作成したディレクトリの中にアプリケーション構築に必要なファイルを作成します。

  • Dockerfile
  • Gemfile
  • Gemfile.lock
  • entrypoint.sh
  • docker-compose.yml
$ touch Dockerfile Gemfile Gemfile.lock entrypoint.sh docker-compose.yml

作成したファイルに下記の内容を記述します。

Dockerfile
FROM ruby:2.6.6

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client yarn
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.3'
Gemfile.lock

Gemfileは何も書かなくていいです。

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

これで必要なファイルは揃いました。

2.プロジェクトの構築

作成した5つのファイルを利用してアプリケーションを生成します。
まずターミナルを使ってdocker-compose runを実行します。

$ docker-compose run web rails new . --force --no-deps --database=postgresql

最後に下記のようなWebpackerのインストール成功メッセージが表示されたら生成完了です。
Webpacker successfully installed ? ?

次はdocker-compose builをします。

$ docker-compose build

最後に下記のようなSuccess情報が表示されたら成功です。
Successfully built xxxxxxxxxx
Successfully tagged myapp_web:latest

3.データベースの設定・作成

まずデータベースの設定を行うために以下のファイルを編集します。

  • config/database.yml

編集内容は下記を記述してください

database.yml
# 設定箇所のみ抜粋
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

編集を終えたら下記のコマンドを実行し、データベースを作成します。

$ docker-compose run web rake db:create

最後に下記のようなメッセージが最後に表示されたら作成成功です。
Starting myapp_db_1 ... done
Created database 'myapp_development'
Created database 'myapp_test'

4.Dockerを起動

dockerを起動するために下記のコマンドを実行します。

$ docker-compose up

実行が完了すると下記の内容がコンソールに表示されます。

myapp_db_1 is up-to-date
Starting myapp_web_1 ... done
Attaching to myapp_db_1, myapp_web_1
(省略)
web_1  | => Booting Puma
web_1  | => Rails 6.0.3.2 application starting in development
web_1  | => Run `rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 4.3.5 (ruby 2.6.6-p146), codename: Mysterious Traveller
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop

表示されたら http://localhost:3000 にアクセスして下の画像と同じページが表示されれば、構築完了です。
Screenshot from 2020-07-15 22-01-17.png

最後に

この環境構築で下記のような問題が発生して、3日ほど時間をかけてようやく解決しました。
この記事で書こうか迷ったのですが構築まできれいにまとめたかったので、解決方法は後日記事に書こうと思います。

Creating network "myapp_default" with the default driver
Creating myapp_db_1 ... done
sh: 1: node: not found
Webpacker requires Node.js >= 8.16.0 and you are using 4.8.2
Please upgrade Node.js https://nodejs.org/en/download/

あと冒頭に書いてある”いろいろあって友人とWeb開発することになった”という話は、
Web開発が終了してリリースしたときに一緒に記事にして投稿します。

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

初心者がWeb開発するためにDocker+Rails6+Postgresqlの環境構築をする話

はじめに

いろいろあって友人とWeb開発を行うことになりました。
初心者のため勉強しながらの開発となるので、
これからは勉強した内容のアウトプットのためにいろいろ記事にしようと思います。
まず第一歩として環境構築からです。

環境

ubuntu 18.04

0.前提

Dockerインストール済み
まだの方はこちらから
https://docs.docker.com/compose/install/

1.必要なディレクトリ・ファイル準備

開発したい場所に開発をするディレクトリを作成します。
今回ディレクトリの名前は 'myapp'

$ mkdir myapp
$ cd myapp

作成したディレクトリの中にアプリケーション構築に必要なファイルを作成します。

  • Dockerfile
  • Gemfile
  • Gemfile.lock
  • entrypoint.sh
  • docker-compose.yml
$ touch Dockerfile Gemfile Gemfile.lock entrypoint.sh docker-compose.yml

作成したファイルに下記の内容を記述します。

Dockerfile
FROM ruby:2.6.6

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client yarn
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.3'
Gemfile.lock

Gemfileは何も書かなくていいです。

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

これで必要なファイルは揃いました。

2.プロジェクトの構築

作成した5つのファイルを利用してアプリケーションを生成します。
まずターミナルを使ってdocker-compose runを実行します。

$ docker-compose run web rails new . --force --no-deps --database=postgresql

最後に下記のようなWebpackerのインストール成功メッセージが表示されたら生成完了です。
Webpacker successfully installed ? ?

次はdocker-compose builをします。

$ docker-compose build

最後に下記のようなSuccess情報が表示されたら成功です。
Successfully built xxxxxxxxxx
Successfully tagged myapp_web:latest

3.データベースの設定・作成

まずデータベースの設定を行うために以下のファイルを編集します。

  • config/database.yml

編集内容は下記を記述してください

database.yml
# 設定箇所のみ抜粋
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

編集を終えたら下記のコマンドを実行し、データベースを作成します。

$ docker-compose run web rake db:create

最後に下記のようなメッセージが最後に表示されたら作成成功です。
Starting myapp_db_1 ... done
Created database 'myapp_development'
Created database 'myapp_test'

4.Dockerを起動

dockerを起動するために下記のコマンドを実行します。

$ docker-compose up

実行が完了すると下記の内容がコンソールに表示されます。

myapp_db_1 is up-to-date
Starting myapp_web_1 ... done
Attaching to myapp_db_1, myapp_web_1
(省略)
web_1  | => Booting Puma
web_1  | => Rails 6.0.3.2 application starting in development
web_1  | => Run `rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 4.3.5 (ruby 2.6.6-p146), codename: Mysterious Traveller
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop

表示されたら http://localhost:3000 にアクセスして下の画像と同じページが表示されれば、構築完了です。
Screenshot from 2020-07-15 22-01-17.png

最後に

この環境構築で下記のような問題が発生して、3日ほど時間をかけてようやく解決しました。
この記事で書こうか迷ったのですが構築まできれいにまとめたかったので、解決方法は後日記事に書こうと思います。

Creating network "myapp_default" with the default driver
Creating myapp_db_1 ... done
sh: 1: node: not found
Webpacker requires Node.js >= 8.16.0 and you are using 4.8.2
Please upgrade Node.js https://nodejs.org/en/download/

あと冒頭に書いてある”いろいろあって友人とWeb開発することになった”という話は、
Web開発が終了してリリースしたときに一緒に記事にして投稿します。

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

ローカル環境でLambda開発を進める際の私的ベストプラクティス

TL;DR

  • AWSマネジメントコンソール上ではLambdaのコードを編集しない
  • コードは全てバージョン管理が有効になったローカルIDE上で開発し、docker-lambdaを利用して迅速にテストできるようにする

ローカルでLambdaを開発するフローを考える

最も単純なLambdaの開発プロセス

  • AWSマネジメントコンソールを利用した最も単純なLambda開発のフローを整理すると、以下のフローになります
  1. ローカルでコードを書く
  2. AWSマネジメントコンソールにログイン
  3. 関数を新規作成、ローカルで編集したコードをブラウザ上のエディタにコピー&ペースト
  4. 関数の実行テスト
  5. 失敗した場合は原因を特定してローカルのコードを再編集(ここで直接ブラウザ上のエディタを編集したくなるが、ローカルのコードと同期が取れなくなってしまう)
  6. 修正したコードををブラウザ上のエディタにコピー&ペースト
  7. 成功するまで4〜6を繰り返す必要がある(!)。手作業でコピペするのは辛く、イケてない。
  • Lambda上で動作することが保証されているコードを一回だけ動かすようなケースであれば、上記までのやりかたでもそこまで不便な点は感じないかもしれません。
  • しかし実際の業務においてはコーディングとテスト実行を幾度も繰り返すことになるはずです。その場合、上記フローでの開発作業には以下のような問題があります
    • AWSマネジメントコンソール上でコードを修正してしまうと、バージョン管理されているローカルのコードと同期が取れなくなってしまう
    • Lambda Layersの開発・テストをする場合、圧縮してデプロイなど非効率的で手間のかかる作業が必要になる

dockerコンテナを利用した効率的なlambda開発

  • https://github.com/lambci/docker-lambda
  • リアルなLambda実行環境を模した、Lambdaのローカル開発環境のサンドボックスを提供してくれるOSSプロジェクト。詳細はリンク先を。
  • ローカル環境でコーディングしているLambda function、Layersを都度コンテナ内で実行して確認することができるので、効率的なLambdaの開発が可能になります。
  • リアルなLambda実行環境とは、Lambda関数が実行される際にマネージドでLambdaのコードがデプロイされるAMIのことです。
    • ファイル構造
    • 権限
    • 環境変数
    • ユーザ
    • プロセス
    • など

docker-lambdaを利用したLambdaの開発プロセス

Lambdaが正常に動作することのテストを、以下の2ステップまで圧縮できます。
1. ローカルでコードを書く
2. docker-lambda上で実行テスト(docker runするだけで実行完了!)
3. デプロイ

  • lambda関数のテストが、以下のコマンドだけで完了するのです。感動しますね。
docker run --rm \
  -v <code_dir>:/var/task:ro,delegated \
  [-v <layer_dir>:/opt:ro,delegated] \
  lambci/lambda:<runtime> \
  [<handler>] [<event>]

docker-lambdaを利用したLambda function実行の例

  • docker-lambdaのREADMEに記載にある通り、コンテナ内の/var/taskに関数のハンドラー、/optにLayersのコードをマウントして実行することで、コンテナ内で関数を実行されることができます。
  • これでいちいちLayerを圧縮する必要も、ローカルのコードを都度マネジメントコンソールのエディタ上にペーストする必要もなくなります。
  • 以下のサンプルでは、ハンドラーが/develop/lambda-local-proj/function、Layersが/develop/lambda-local-proj/layerに存在しており、それをdocker-lambdaの指定のディレクトリにマウントして起動しています。
  • ランタイムにPythonを利用しています。
# function、layerのディレクトリをマウントして実行
(venv) 6477:lambda-local-proj sentorei$ docker run --rm -v /Users/sentorei/develop/lambda-local-proj/function:/var/task:ro,delegated -v /Users/sentorei/develop/lambda-local-proj/layer:/opt:ro,delegated lambci/lambda:python3.8 lambda_function.lambda_handler
# ステータスコード200が返され、正常に実行できていることが確認できる
START RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a Version: $LATEST
END RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a
REPORT RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a  Init Duration: 1835.48 ms       Duration: 588.13 ms     Billed Duration: 600 ms Memory Size: 1536 MB    Max Memory Used: 31 MB

{"statusCode":200,"body":"\"Hello from Lambda!\""}
  • 煩雑なテスト作業から脱出し、ぜひ皆さんも快適なLambda開発を始めましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-composeによるRails6(APIモード)+MySQLのDocker環境作成(for Mac)

はじめに

バイト先で全面改修を進めていく上で、環境をdocker化しようという話が出ました。
フロントエンドNuxt.js、バックエンドRails6の構成で作ることになり、バックエンド部分を自分が担当することになったのでDockerfileをごりごり書いていきましょー!ということで勉強も兼ねて実装し、出来上がった構成を紹介します。
Railsコンテナの作り方は、Dockerの公式ページに簡単に書いてありますが、今回はRails6に考慮したDockerfileの書き方と、MySQLコンテナの作り方も合わせて紹介していこうと思います。

自身のスキル

個人でrailsを触り始めたのは1年半ほど前、webエンジニアとしての実務での開発経験は1年強と期間的には長くはありません。
しかし、大学でつけた幅広い前提知識や、どんどん色々なことに挑戦する姿勢を利用して素早く成長しています。
前のプロジェクトでもdocker周りには触らせていただいていて、データベースのコンテナの変更(MarinaDB->MySQL)や、初期設定用のシェルスクリプトの作成などを行っていました。
自分で1からdockerを作る機会がなかったので今回挑戦した形です。

docker環境作成

ここから先は実際にdockerによる環境を作っていきます。

ファイル構成

まずは簡単にファイル構成を紹介します。
作成するアプリケーションの名前をsampleとすると、今回関係するファイルは以下のようなファイル構成になっています。

sample 
|- backend --- app
|           |- bin
|           |- config --- ...
|           |          |- database.yml
|           |          |- ...
|           |- ...
|           |- Dockerfile
|           |- Gemfile
|           |- Gemfile.lock
|           |- ...
|- db --- data --- ...
|      |- my.cnf
|- docker-compose.yml

実際はもっと多くのファイルが存在する(作成される)ことになりますが、今回操作するファイルはこれらのファイルのみになります。

必要ファイルの作成

続いてdockerに関するファイルを記述していきます。

backend/Dockerfile

これはRailsのコンテナをビルドするための手順を記したファイルになります。内容を下に載せ、それぞれどのような処理を行っているのかをコメントで説明しています。

backend/Dockerfile
# 使用するイメージとバージョン
FROM ruby:2.7.1

# 必要なライブラリをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs

# 以下はrails6以降(APIモード除く)で必要
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn

# コンテナ内にappというフォルダを作成
# ※ローカルPCにフォルダが作成されるわけではない
RUN mkdir /app
# ルートをappディレクトリに変更
ENV APP_ROOT /app
WORKDIR $APP_ROOT

# ローカルPC内のGemfile(.lock)をコンテナ内にコピー
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# bundle installを実行しローカルPCのファイルたちをコンテナ内にコピー
RUN bundle install
ADD . $APP_ROOT

# コンテナがlistenするポート番号
EXPOSE 3000

# 実行されるコマンド
CMD ["rails", "server", "-b", "0.0.0.0"]

rails6からはWebpackerがデフォルトとなったので、yarnがインストールされていないとrails newをするときにエラーとなってしまいます。
ただし、後述のAPIモードで作成する場合にはyarnのインストールは不要です。

backend/Gemfile

RailsをインストーするためにあらかじめGemfileを記述しておきます。
このファイルは後ほどrails newをするときに書き換えられます。
ここではrails6以降のバージョンを指定しています。

backend/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'

backend/Gemfile.lock

bundle installした際に依存関係を元にライブラリの設計図のようなものを作ってくれます。
空のものを作成しておきます。ファイルが存在しないと後でエラーになりました。

backend/Gemfile.lock

db/data/

ディレクトリだけ用意しておいてあげましょう

db/my.cnf

MySQLの設定に関するファイルです。

my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
sql_mode=''

[client]
default-character-set=utf8mb4

文字コードの設定やsql_modeの設定を行っています。
特にsql_modeの設定はしておいた方が良いと思います。
というのも、MySQL5.7からデフォルトのsql_modeが変更となっており、特にonly_full_group_byなどのモードで度々エラーを吐いてしまうことがあるので、どのsql_modeにするかは明示的に書いておきましょう。今回は設定なし(MySQL5.6.5以前のデフォルト)を明示的に指定しています。
(この辺りでも追々記事が書きたい)

docker-compose.yml

いよいよdocker-composeを書きます。
簡単な説明をコメントで記述してあります。

docker-compose.yml
version: "3"
services:
  # MySQL
  db:
    #ビルドするイメージ
    image: mysql:5.7
    # 環境変数の指定
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: spportunity
      MYSQL_USER: root
      MYSQL_PASSWORD: root
      TZ: 'Asia/Tokyo'
    # 文字コードをutf8mb4に設定
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      # db/dataディレクトリをコンテナのmysqlディレクトリにマウント
      - ./db/data/:/var/lib/mysql
      # /db/my.cnfファイルをコンテナ内のmy.cnf設定ファイルにマウント
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 3306:3306

  #Rails  
  backend:
    # ビルド元の指定
    build:
      context: ./backend
      dockerfile: Dockerfile
    # コンテナ名の指定
    container_name: "sample-backend"
    # 起動時のコマンド指定 前のプロセスを終了してからrails sをしている
    command: bash -c "rm -f tmp/pids/server.pid && rails s -p 3000 -b '0.0.0.0'"
    # backendフォルダ内のファイルをコンテナ内のappディレクトリにマウント
    volumes:
      - ./backend/:/app
    ports:
      - "3000:3000"
    # dbコンテナよりも後に起動
    depends_on:
      - db

これでファイルの準備はできたのでrailsアプリケーションを作成していきます。

railsアプリケーションの作成

docker-composeコマンドを利用してrailsアプリケーションを作成します。
現在のディレクトリがsampleであることを確認してください。

通常モードでrailsを作成(dockerfileでyarnのinstallが必要)

% docker-compose run backend rails new . --force --no-deps --database=mysql --skip-bundle
  • --force...Gemfileを書き換えます
  • --database=mysql...データベースにMySQLを指定します
  • --skip-bundle...bundleをスキップします(まだGemfile.lockは変更されません)

APIモードでrailsを作成

% docker-compose run backend rails new . --force --no-deps --database=mysql --skip-bundle --api
  • --api...APIモードでの作成オプション

完了すると、backend/フォルダ内にrails関連のフォルダやファイルが大量に出来上がります。
APIモードと通常モードの違いやファイルの差分はまた記事にします。

コンテナのビルド

% docker-compose build

このコマンドを実行することで、MySQLのコンテナの作成と、railsコンテナでのbundle installが行われます。
コンソールを見ているとDockerfileの内容を順番に実行している様子が分かるかと思います。

railsのdbへの接続設定

backend/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: root
  host: db

development:
  <<: *default
  database: sample


test:
  <<: *default
  database: sample_test

production:
  <<: *default
  database: sample_production
  username: <%= ENV['APP_DATABASE_USER'] %>
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>

接続情報を、hostを先ほど作成したMySQLコンテナのイメージ、ユーザー名とパスワードを先ほど作成したコンテナの環境変数のものに変更します。

(APIモードでなければ)Webpackerのインストール

% docker-compose run backend rails webpacker:install

データベースの作成

% docker-compose run backend rails db:create

dockerの起動

ここまで来れば設定は完了です。
あとはdockerを起動するのみです!

% docker-compose up

ブラウザでRailsが立ち上がっていること確認

ブラウザで http://localhost:3000/ へアクセスすると、無事Railsが立ち上がっています。
スクリーンショット 2020-07-15 21.24.05.png

拡張性

今回は言及しませんでしたが、frontendコンテナなどを追加することによってフロントエンドアプリケーションとAPIサーバを兼ね備えた環境を一発で作ることができるようになります。
その際は、同様の手順でDockerfileの作成やdocker-composeへのコンテナの追加などを行っていくことになりまね。
僕はまだフロントエンド初心者なので、ゆくゆくはフロントのコンテナも作っていきたいです。

感想

やっぱりdockerは便利ですね。
自分個人の開発ではdockerは使っていませんでしたが、これからは積極的に使っていこうかなと思っています。
またその際には記事にしてあげていければと思います。

qiitaの記事を書くのが初めてで、どれくらいのものを書けばいいのか悩みながら書いていたら、だいぶ長くなってしまいました。
ただ、とても内容が濃い記事を何本も書いている人がたくさんいて、自分はまだまだだなぁと日々感じています。
僕はまだ内容があるものは頻繁には書けないかもしれませんが、少しずづ知識を記事化していければと思います。

参考にしたもの

以下のqiitaの記事を参考にさせていただきました。
書き方も真似させていただき、かなり参考になりました。
ありがとうございます。

https://qiita.com/azul915/items/5b7063cbc80192343fc0
https://qiita.com/kodai_0122/items/795438d738386c2c1966

上記の記事に加えて、APIモードやMySQLコンテナのマウントや設定なども盛り込んだ内容となっているので、書き方が似ている点はご容赦いただければと思います。

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

Dockerの全imageを強制消去

自分用のメモです。
使い方間違えてたら連絡ください。

Dockerのイメージをコンテナを消しても-fでオプション設定してもどうしても消せない時がありその後何とか時間をかけて解決しました。

docker image rm $(docker image ls -a -q) -f

これでで消えました。

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

初心者がはまりがちなDockerのエラーに対する解決法

記事の背景

Le Wagon Tokyo (https://www.lewagon.com/ja/tokyo) という、主にRubyとRailsを教えるフランス発の英語のプログラミングスクールを卒業後、Dockerを勉強し始めました。卒業後に取り組んだプロジェクトでDockerの構築を行ったのですが、多くのエラーに直面しました。独学で試行錯誤しながらエラー解決を行ったので、非常に多くの時間がかかってしまいました。私のように、Dockerを初めて触ってエラーに苦労した人も多いと思うので、私が使った解決方法を参考にしていただければと思い、この記事を書くことにしました。

1 PermissionError (Permission denied :‘~~~/your_app/tmp/db’)

Dockerの公式ドキュメントを参照する限り、このエラーはLinuxユーザーだけのようですが、LinuxユーザーにとってPermissionは厄介になりうるので共有します。このエラーは、Dockerが一時的なデータベース(dbフォルダ)を作るのですが、現在ログインしているLinuxユーザーがそのデータベースを使う権利がないため発生します。

まずはこのコマンドでそのテンポラリーフォルダに移動します。

$ cd ~~~(ご自身のアプリまでのパス)/(ご自身のアプリ名)/tmp

以下のコマンドでどのユーザーがdbフォルダを使えるか確認します。

$ ls -la

現在ログインしているLinuxユーザーの名前が表示されていなければ、そのユーザーに以下のコマンドで権限を与えます。

$ sudo chown -R (Linuxユーザーの名前) . 
password for (Linuxユーザーの名前):

sudoコマンドなので、パスワードの入力が求められます。ここまで行けば、"docker-compose build"や"docker-compose up"など主要なDockerコマンドは問題なく実行できるはずです。

注:
似たようなエラーで、私は"FATAL: could not open file “
global/pg_filenode.map”: Permission denied"というエラーをよく見たのですが、こちらは"docker-compose stop"→"docker-compose up"でリスタートすれば大丈夫です。

2 No space left on your device

Dockerfileの設定によって、Dockerイメージの容量が大きくなってしまった場合に起きるエラーです。不要なイメージ並びにコンテナは以下のコマンドで簡単に削除できます。

$ docker image prune
$ docker container prune

これらのコマンドを入力すると、以下のメッセージが表示されます。

WARNING! This will remove all dangling images (もしくはcontainers).
Are you sure you want to continue? [y/N]

dangling imagesは不要なイメージという意味なので、"y"を入力してください。容量が確保され、PermissionError解決時と同様の主要なDockerコマンドを実行できるようになっているはずです。

もしもさらに削除する必要がある場合は、以下で削除するイメージを探します。

$ docker images

消したいイメージのIDを確認した後、以下で削除します。

$ docker image rm -f (消したいイメージのID)

参考文献

英語ですが、この本はRuby on RailsのプロジェクトでDockerを使う方法をかなり詳細に論じているので、おすすめです。

Docker for Rails Developers: https://pragprog.com/titles/ridocker/

残念ながら、英語も含めてRails用のDockerの資料はまとまっているものが少ないので、特にRails歴が浅く、Dockerについての易しい資料が読みたいということであれば、英語ですが読む価値はあると思います。

Mediumの英語の記事

本テーマに関してより詳細に、Mediumに英語で記事を書いたので、そちらもご覧になってみてください。

https://medium.com/@shogohida_81081/5-solutions-to-common-errors-of-docker-for-beginners-c04dc1237c78

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

Docker run で React.js を動かす -- alpine とは

THIS IS JUST A PRESONAL DEV LOG React startをDockerで 前提 ## stage 3 - Create React App の npm start で React を起動する Dockerfile を作り、React コンテナを起動できること - Next.js を起動する Dockerfile を作り、Next.js コンテナを起動できること ## stage 4 - Docker Compose とは何か理解し説明できること - Docker Compose を用いてデータベースを使うアプリケーションを起動できること (例: Laravel, Ruby on Rails, Nest.js, etc.) これの stage 3 を達成するためにやる。 Local Project作成 npm init react-app docker-react --use-npm npx: installed 98 in 5.736s npx create-react-app ではなくnpm init react-app AppNameで作成する create-react-appでできるものと同じだった。 Remote GitHub Project 連携 GitHubでdocker-reactのrepository作成。 git remote add origin git@github.com:kaede0902/docker-react.git remote origin にkaede0902/docker-reactを設定。 git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore README.md package-lock.json package.json public/ src/ .gitignoreを先にcommit 他をreact initとしてcommit そしてpush Dockerfile 次にDockerfileをかく Dockerfile FROM node:13.12.12.0-alpine WORKDIR /app ENV PATH /app/node_modules/.bin:$PATH COPY package.json COPY package-lock.json RUN npm install --silent RUN npm install react-scripts@3.4.1 -g --silent COPY . ./ CMD ["npm", "start"] Dockerにnode13.12をpullしてきて 作業ディレクトリとしてDockerに /app を作成 Dockerの環境のPATHを /app/node_modules/.bin に設定 package(-lock).json を rootの./にMacから複製してDockerに作成。 Dockerにpackage.jsonに沿って依存関係をinstall package.jsonに書いてなかったreact-script@3.4.1をinstall COPY . ./ これはよくわからない、おまじない??? . を ./ にcopyするってなんだろう、日報の質問に書く docker build 書いてある通りにアルパインを使ってのエラー docker build -t docker-react:dev . Step 1/9 : FROM node:13.12.12.0-alpine manifest for node:13.12.12.0-alpine not found: manifest unknown: manifest unknown node 13.12のmanifest unknownとでた。 ググるとこれにあたる。 https://hub.docker.com/r/mhart/alpine-node/ この13.12...alpine っていうのがmhartさんの作ったversionで、13は廃止されて12か14を使えってことなのかな? nodeだけでやった時には FROM node:12 だけだしこれでやる Alpineの正体。減量モデル 教えていた堕胎。 node:-alpine This image is based on the popular Alpine Linux project, available in the alpine official image. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general. なお alpineは軽くするためにglibcといったLinuxの基盤中の基盤のライブラリを差し替えてたりする といった独自エラーの原因になることがあるので気をつけたほうがよさそう。 でも 本番で使ってるDockerでもかなりスリム化頑張ってやってる とのことなのでじゃあnode:12だけ使っていればいいやということにはならない Imageですけど、基本的には公式のイメージにalpineベースのものがある場合が多い(言語環境とかフレームワークとか)ので、セキュリティ的な意味でもそれを使うのが吉です ただ今回はnode:12(公式イメージ?)使います!!! docker run docker run \ -it \ --rm \ -v ${PWD}:/app \ -v /app/node_modules \ -p 3001:3000 \ -e CHOKIDAR_USEPOLLING=true \ docker-react:dev を実行 optionは-pしか理解してないです! -rmは実行後のimage 削除? Compiled successfully! You can now view docker-react in the browser. Local: http://localhost:3000 On Your Network: http://172.17.0.2:3000 Note that the development build is not optimized. To create a production build, use npm run build. と出るが、実際 localhost3000 にアクセスしても This site can’t be reachedlocalhost refused to connect. Try: Checking the connection Checking the proxy and the firewall ERR_CONNECTION_REFUSED localhost 3001 にアクセスすればReactが動いている docker runで出ている表示はDocker側のサーバーの情報らしい。 確かに見ているのはReactがいうとおりlocalだが、それはDockerのlocalだった!!! 別のペインでlorem50をぶち込んで保存されたら即座にcompileされて反映されたのを確認!!!ヨシ!! コンテナ間の通信ではEXPOSE 必要 ナシ!!! -p 3001:3000 はMac:Docker だった!! XXXX:YYYY と指定したならば、Mac の localhost:XXXX がコンテナのポート YYYY に繋がるということですね。 既に説明されていたっ 次は Next.js を起動する Dockerfile を作り、Next.js コンテナを起動できること します
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flask+Docker+Vue.js+AWS...でゲームWebAppを作ってみた。

イントロで曲当てクイズ『イントロドン!』
コロナ自粛中のリモート呑みで遊べるゲームアプリが欲しくて作ってみました!
お酒呑みながら、みんなでガヤガヤ、時にはひとりでじっくりと楽しんでもらえたら嬉しいです。

●こちらのリンクから遊べます♪
http://introdon.akinko.work/

●Githubにソースコード、ゲームルールを公開しました
https://github.com/akiraseto/introdon

スクリーンショット 2020-07-15 14.33.44.pngスクリーンショット 2020-07-15 14.30.52.pngスクリーンショット 2020-07-15 14.30.27.png
スクリーンショット 2020-07-15 14.30.04.pngintrodon.akinko.work_user_entrance(iPhone 6_7_8) (1).png

ゲーム内容

楽曲のイントロ部分を聴いて曲名を当てるゲーム。4択問題で全10問出題。

遊べるモードは2つ

ひとりでじっくりモード

時間制限無しでじっくり解答

みんなで早押しモード

最大5人同時参加の早押し形式
正解順に高得点をGet
勝敗は合計点によるランキング発表!

PCブラウザ・スマホについて

PCブラウザ、スマートフォンともに対応していますが、
スマホ版は、出題ごとに「音楽を再生する」ボタンをタップしないとイントロが流れない仕様です。
(PCブラウザ版は自動で曲が流れる)

モバイル版Chrome、Safariのブラウザポリシーによりメディア要素の自動再生は禁止。
ユーザーの意図的な操作によってメディアを再生する。

残念ながらスマホ版は、みんなモードだとかなり不利になってしまいます。

技術内容

楽曲情報を「itunes api」から取得して問題を作成。
https://itunes.apple.com/search

楽曲情報をDBから取得して問題作成。
該当する楽曲がDB内で少ない場合、itunes APIから楽曲情報を取得し、被った情報を削除した上でDBに記録。同時に問題も作成します。

全体の流れ

  1. ゲームを開始する
  2. DBから該当する楽曲情報を取得
  3. 楽曲がある場合は、選択肢4×10問の問題を作成。
  4. 問題をsessionに渡す
  5. sessionから引き出して出題
  6. 解答内容をDBに記録
  7. 全10問解答後にDBからログを取得し、ランキング、正誤内容を表示

楽曲情報が足りない場合、
3. itunes APIから該当する楽曲情報を取得
4. duplicateする情報を削除
5. API取得した楽曲情報をDBに記録
6. 問題を作成(以下、同じ)

技術構成

主な技術構成は以下となります。

  • Flask
  • Docker
  • Nginx
  • Gunicorn
  • Vue.js + Jinja2
  • MariaDB
  • AWS
  • テスト(pytest, CircleCI)

Flask

MTVフレームワーク

Model, Template, ViewのMTVフレームワークに沿って開発。

ディレクトリ構成

  .
  ├── introdon
  │  ├── __init__.py
  │  ├── config_flask.py
  │  ├── models
  │  │  ├── games.py
  │  │  ├── logs.py
  │  │  ├── songs.py
  │  │  └── users.py
  │  ├── scripts
  │  ├── static
  │  ├── templates
  │  │  ├── _render_field.html
  │  │  ├── admin
  │  │  ├── games
  │  │  ├── layout.html
  │  │  └── users
  │  └── views
  │    ├── __init__.py
  │    ├── config_introdon.py
  │    ├── form.py
  │    ├── games.py
  │    ├── songs.py
  │    ├── users.py
  │    └── views.py
  ├── manage.py
  └── server.py

models

一言で、データベース連携の機能
単純なCRUDだけでなく、ビジネスロジックもModelにコーディングしてデータの取り回しを担当させる。

classmethod

インスタンスを作成して使い回したり、アトリビュートを使用することが無い場合は、その場で使えるクラスメソッドが便利です。

introdon/models/users.py
 # Classの中でデコレータをつけて宣言

@classmethod
def fetch_user_records(cls, users_id_list: list) -> list:
introdon/views/games.py
# インスタンス作らずにその場で使える

users_record_list = User.fetch_user_records(users_id_list)

traceback.print_exc

try exceptで例外表示に使用。例外が発生した際に、エラー詳細を表示させて原因が追いやすくなります。

introdon/models/games.py
#DB書き込み時

this_game = Game(**records)
db.session.add(this_game)
try:
  db.session.commit()
except:
#例外が発生したら

  db.session.rollback()
#DBロールバックし

  traceback.print_exc()
#エラーをスタックトレースして出力

エラーが発生した際、
①スタックトレースを抽出して、
②書式も整えた上で出力してくれます。
エラー解決にはprint(),loggerよりもおすすめです。

templates

TemplateはMTVの中で一番分かり易い描画(render)機能です。
その反面、フロントエンドフレームワークと連携しだすと一番複雑化しやすい箇所でもあります。

Jinja2を通して共通パーツをまとめた方が全体のコードがスッキリします。

introdon/templates/users/index.html
{% from "_render_field.html" import render_field %}
{% extends "layout.html" %}
{% block body %}

# 内容

{% endblock %}

ナビバーや、ヘッダーなど共通のパーツはlayout.htmlから読み込み
フォームなど使い回すパーツはmacroにして_render_field.htmlから読み込んでます

views

viewにビジネスロジックまで書いてしまうと、あっという間に肥大化&複雑化してスパゲティコードになってしまいます。(T_T)
APIのI/Oインターフェイスぐらいの認識にして、なるべくスリム化するように常に意識します。
viewはあくまで

  • 値の入出力
  • 処理全体の制御

この2つの役割に徹した方が良いです。
Flaskは比較的自由が効くフレームワークなのでいくらでもviewに書けてしまいますので。。

static

画像などのstaticなファイル置き場となります。
今回は、正解不正解などのSE音源を格納しました。

app.run設定:config_flask.py

flaskアプリ起動のapp.run()の設定ファイルです。
ファイル場所:introdon/config_flask.py

設定変数をファイルにまとめて一括で読み込ませています。
今回は環境変数によって分岐させて、読み込む変数をモードごとに変えています。

SECRET_KEY

データベースと、セッション情報を暗号化するためのキー。
分かり辛い方がいいので、os.random(24)で毎回値を変えてます。

DEBUG

DEBUGモードをオンにすると

  • ファイル変更したらappが自動リロードされる。
  • ブラウザにエラー内容を出力する
  • ツール:DebugToolBarが使用可能になる

開発では Trueにして本番では必ずFalseにします。

SQLALCHEMY_DATABASE_URI

SQLAlchemyを通してDBを利用する際の接続先です。
SQLAlchemyとMySQL(MariaDB)にはpymysqlのスキーマが必要。

SQLALCHEMY_RECORD_QUERIES

デバッグ出力用にSQL内容を記録します。
DebugToolBarで SQLを確認したいなら設定が必要。

よく使う基本機能

Flaskで高頻度でお世話になる機能をまとめてみました。

ルーター

route

関数とURLをバインドするデコレーター
POST,GETの許可を制御する

errorhandler

サーバーエラー発生時に処理を制御します

introdon/views/views.py
@app.errorhandler(500)
def internal_server_error(error):
 flash('Internal server error')
 return redirect(url_for('index'))

サーバーエラー500が発生したら
①flashメッセージを作り、
②indexメソッドバインドしているURLにリダイレクトさせる。

レスポンス

render_template

テンプレートhtmlを描画します。
jinja2を使ってhtml側に変数を渡すことができます。

flash

Flashメッセージは処理が終わった事や、エラーが発生した場合にユーザーに知らせるメッセージです。

  1. render時にview側でflash()に入力されたメッセージがセットされます。
  2. template側でget_flashed_messages()でメッセージを受け取り描画します。
introdon/templates/layout.html
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
  #内容をhtmlで出力
{% endfor %}
{% endif %}
{% endwith %}

複数受け取りも可能。
layout.htmlですべてのtemplateページにメッセージを出力できるよう設定しています。

redirect

任意のページにredirectすることもできます。
特にurl_forと組み合わせて使うことが多いです。

url_for

メソッド名を引数に取ると、バインドしているURLの文字列を返します。

return redirect(url_for('start_multi'))
  1. start_multiメソッドがバインドしているURLに文字列変換
  2. 文字列変換されたURLにリダイレクト

また、templateのhtmlでよく使うのが、
ディレクトリ名のstaticを第1引数、ファイル名の指定で素材ファイルとリンクすることができます。

url_for('static', filename='right.mp3')

staticディレクトリの'right.mp3'のリンク文字列を作成

jsonify

名前のとおりjson形式にシリアライズします。

request

ユーザーのrequest内容が格納されています。

よく使うのが、ユーザーがformに入力した内容の取得です。

introdon/views/songs.py
# song登録画面
@app.route('/admin/song', methods=['POST'])
def add_song():
 term = request.form['term']

POSTで送信されたformのname=termにユーザーが入力した値を取得します。

flask.requestはユーザーの様々なリクエスト内容を取得できるので使いみちが幅広いです。

# POSTでリクエストされているなら
if request.method == 'POST':
pass

#GETでクエリが'game_id'の内容を取得
game_id = request.args.get('game_id')

などなどあります。

session

ユーザーごとに、リクエスト(ページ)をまたいで情報を利用することができるSessionを手軽に利用できるようになります。
利用するには、app.run設定のSECRET_KEYを設定する必要があります。

DBとの接続

ORMとしてSQLAlchemyを使用しました。
MariaDB(MySQL)との接続にはpymysqlをスキームにかませる必要があります。

flask-script

DB環境の初期化をコマンド実行できるようにしました。
以下のコマンドで必要なTableが作成されDBを初期化します。

 python manage.py init_db

アプリ開始時にコマンド実行します。

flask-marshmallow

オブジェクトをシリアライズ化してくれます。
クイズ作成時に、SQLAlchemyでオブジェクト化された楽曲情報をjsonフォーマットに格納するために使用しました。

ログイン系の処理

flaskの拡張プラグインを利用してコーディング。

flask-login

  • ユーザーのログイン
  • ログアウト
  • そのユーザーはログイン状態か
  • ログインしている場合のユーザー情報取得

password_hash

  • パスワードをハッシュ化:generate_password_hash
  • ハッシュ化パスワードを確認:check_password_hash

フォームの処理

flask-WTF

フォームを作る際の便利な機能が揃っています。

  • form内容のvalidate
  • form項目の設定
  • CSRF対策

管理画面をかんたん設置したい

Adminユーザーから簡単な管理画面を閲覧・操作したい。
でも、イチからコーディングするのはシンドい。。これもflaskのプラグイン使います。

flask_admin

自分で作るのに比べると圧倒的に楽に管理画面が作れます。

DB内TableのCRUD操作がWebの管理画面から可能になります。
細かくカスタマイズすることも可能です。

スクリーンショット 2020-07-15 14.35.14.pngスクリーンショット 2020-07-15 14.35.24.pngスクリーンショット 2020-07-15 14.35.30.png

開発に便利なツール

使うと開発作業が捗るツールで、利用をおすすめします。

flask-debugtoolbar

DEBUGモードで利用できるようになる開発援助ツールです。
Webアプリ上にツールバーとして表示されます。変数や履歴、ヘッダー内容、SQL処理内容をブラウザから確認できるようになります。

Dockerでシステム構築

Webサーバー、アプリサーバー、DBサーバーをそれぞれDockerコンテナでシステム構築しました。
正確には、テスト用のDBコンテナがひとつ追加で4つのコンテナ構成をdocker-composeで構築しています。

システムのモードとして3種類あります。
Flaskコンテナの環境変数FLASK_ENVを変更することで以下の3つのモードでBuildします。

  • PROD
    • 本番モード
    • Gunicorn使用
  • DEV
    • 開発環境モード
    • DEBUGオン
    • debugtoolbarが表示
    • エラー時、内容がWEBに表示
    • コード変更でwebアプリが自動リロード
  • TEST
    • Func,Unitテストが自動実行
    • テスト用DBコンテナを使用
    • テストCoverageを表示

Nginx

Webサーバーに使用しました。

Nginxはメモリ使用量が小さく、処理も早い。
リバースプロキシの機能、ロードバランサ、HTTPキャッシュを持ち合わせています。
また、Gunicornは公式ページでNginxの使用を推奨しています。

Gunicorn

WSGIに使用しました。

uWSGIも検討しましたが、
①Gunicornの方が扱いやすくネット上の使用事例が多く(海外も含めて)、
②公式サイトも解説が丁寧だったのでGunicornを採用しました。

docker-composeを通して、FALSK_ENV=PRODなら以下から起動するようにしています。

flask_env.sh
gunicorn server:app -c gunicorn_setting.py

MariaDB

MySQLと完全互換があり、MySQLより処理が早い、MariaDBを使用しました。
完全互換なのでSQLAlchemyや、スキーマなどMySQLの設定がそのまま使えます。

フロントエンド

Jinja2 + Vue.js + Bootstrapで作成しました。

MPA(Multi Page Application)としてJinja2を基本に、Vue.jsをCDNで読み込んで組み合わせて実装。
CSSはBootstrapを利用してレスポンシブデザインにしてPC、スマホの両ブラウザ対応にしています。

FontAwesomeを初めて使用したのですが、簡単なタグのみで手軽にアイコンを組み込めるのでおすすめです。
https://fontawesome.com/

AWS

1つのEC2インスタンスに全てのDockerコンテナを構築しました。

順当ならECSを使って、コンテナ毎にEC2インスタンスに分配して制御管理するのがベストプラクティスだと思います。
1つのEC2インスタンスに収めた理由として
①AWS無料枠内に収めたい
②クラスタリングが必要なほどのアクセス数ない
③趣味なので強い可用性も堅牢性も求めない
上記理由で、今回は見送りました。

ただ、ECSは思ったよりも導入しやすく便利そうなので今後の別アプリで検討してみたいです。
さらに、Kubenatesを導入しても面白そうです。

テスト

pytestで書いています。
python標準のunittestよりもシンプルにかけて読みやすく、プラグインも豊富なためpytestを採用しました。

CIには CircleCIを利用しました。
DockerのDBコンテナを使ったテストはせずに、mock利用のテストだけをCircleCI上で実行しています。

終わりに

楽しかった。。
Flaskは先人達の優れたプラグインが多くあり、組み合わせることでやれることが加速的に多くなっていく。。
その「やれることが増えていく感」が気持ちいいフレームワークです。皆様も機会があればぜひ触ってみてください!

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

DockerでLaravelの環境構築した話

この記事を参考にDocker環境構築

Dockerを使ってLaravel開発環境構築
https://qiita.com/A-Kira/items/1c55ef689c0f91420e81

ハマるポイント1 phpのdockerfile記述

このままだと、Laravelのインストールがうまくいかない・・・(PHPのバージョンが7.3以上じゃないとだめみたい)
→バージョン7.2だったのを7.4へ変更

zlib1g-devで引っかかる
→libzip-devに変更

ハマるポイント2 nginx 404

cloneして、
composer installして、
.env作って、

docker-compose exec php bash
php artisan migrate

一応、動くようになったけど・・・TOPページ以外、nginx 404になる
https://qiita.com/isaatsu0131/items/f1eafe7522f0bf0ff043

location / {
   try_files $uri $uri/ /index.php?$query_string;
 }

default.confのロケーションの記述を修正

ハマるポイント3 storageへのシンボリックリンク

動作チェックしてみたけど・・・画像が表示されない

docker-compose exec php bash
php artisan storage:link

以上

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

10分でAlibabaクラウド上のDockerにPyODPSを実行してJupyter Notebookをインストールする

この記事では、Alibaba Cloud ECSインスタンスにPyODPSを実行してDockerJupyter Notebookをインストールする方法を紹介します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

なぜJupyter Notebook

データセットを探索するときには、まずデータをロードして便利な形式にすることから始めなければなりません。また、データセットが非常に大きい場合は、ディスクから学習データを読み込むだけで30分近くかかることもあります。モデルのフィッティングを行う場合は、通常、最初に特徴行列を設定したり、他の前処理を行ったりする必要があります。これは、最初に戻って、また同じサイクルを繰り返すことを意味します。

Notebookはこの映画のような運命からあなたを救ってくれます。Jupyter Notebookを使ったコーディングは、マトリックスの戦闘シーンのようなものです。Notebookは、データを一度だけ読み込む必要があるので、反復したり新しい実験を試したりするのがはるかに速くなります。

Notebookは、一部のセルを通常のコードからMarkdownに切り替えることができるため、プレゼンテーションに最適です。Markdownは習得が早く、使いやすいです。いくつかのMarkdownセルを使って作業を紹介することで、あなたのスクリプトが洗練されたプロフェッショナルなものになり、最高のデータサイエンスを前面に出すことができます。

一部の人々は一回でコードを書く能力を持っています: 彼らはしばらくの間問題について考え、座って解決策を練ります。しかし、私のような人は、問題を解決するのに一度では済まないことがあります。私はいつも、答えに向かって少しずつつまずきながら、前に向かってもがいています。Notebookはそのようなワークフローに本当に適しています。なぜなら、次のビットに移る準備が整うまで、何度でも好きなだけ各セルを再実行して、小さなバリエーションをたくさん試すことができるからです。変更をテストしたいときに、毎回すべてのコードを実行する必要はありません。

image.png

図1. Jupyter Notebookからのショット例(出典:https://goo.gl/xWsl4E)

この記事では、Alibaba Cloud Elastic Compute Service (ECS)インスタンス上でDockerにPyODPSを実行しているJupyter Notebookをインストールする方法を紹介します。

愛すべきMaxCompute (旧ODPS)

ウェブサイト(http://pyodps.readthedocs.io)の定義からPyODPSについては、ODPS SDKのPython版であり、ODPSオブジェクトの基本的な操作を提供し、ODPS上でデータ解析を容易に行うためのデータフレームワークを提供しています。ODPSは現在MaxComputeとしてリブランドされていますが、SDKの名称はODPSのままです。

image.png

図2. MaxCompute (ODPS) の構成

前提条件

1、ECSインスタンス(最低でも4コア8GB)
2、時間は約10分

インストール方法

1、インスタンスにDockerをインストールする
ご参考までに、Ansible playbookを使ってECS上で自動でDockerインストールを行うコードリポジトリをご覧ください。

2、JupyterでNotebookコンテナをインストール
Nodeから実行してインスタンスにイメージをインストールするか、プレイブックスクリプトに追加します。

$ docker pull jupyter/notebook  

インストールが動作することを確認するには、以下のコードを実行してください。

$ docker run -p 8080:8888 jupyter/notebook jupyter notebook --no-browser

イメージのエクスポート先のボリュームは.ipynbファイルが格納されています。以下のようにホストパスにボリュームをマウントしてみてください。

$ docker run -p 8080:8888 /home/notebooks:/notebooks jupyter/notebook jupyter notebook --no-browser

Webブラウザから [http://あなたのノードのIPアドレス]:8080 にアクセスします。

3、PyODPSのインストール
コンテナに取り付けて次のステップに進みます。

$ docker exec -i -t [container name] /bin/bash

pipを更新します。コンテナ内でこのコマンドを実行します。

$ pip install --upgrade pip

コンテナ内でこれを実行してPyODPSをインストールします。

$ pip install 'pyodps[full]'

unittestを実行して、インストールが成功していることを確認します。

$ python -m unittest discover

4、インストールの最終調整
コミットして新しい画像を作成します。

$ docker commit -c='CMD ["jupyter", "notebook", "--no-browser"] ' [existing container name] [your new container name with tag]

5、通常通りNotebookを実行する
Jupyterを実行する

$ docker run -p 8080:8888 [your container name with tag]

Notebook WebUIは、[http://あなたのノードのIPアドレス]:8080から見ることができます。

これで完了です! 手順に正しく従っていれば、Jupyter Notebook を Alibaba Cloud Elastic Compute Service (ECS) インスタンスに MaxCompute service for PyODPS でセットアップしているはずです。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

Ubuntu 16.04でDockerプライベートレジストリサーバーを設定する

このチュートリアルでは、Ubuntu 16.04でAlibaba Cloud ECS上に独自のDockerプライベートレジストリサーバを設定します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

必要条件

1、Ubuntuがインストーされたレジストリサーバ用の新しいAlibaba Cloudインスタンス。
2、Ubuntuがインストールされたレジストリクライアント用の新しいAlibaba Cloudインスタンス。
3、レジストリサーバーインスタンスに192.168.0.101の静的IPアドレスが設定され、レジストリクライアントインスタンスに192.168.0.102の静的IPアドレスが設定されます。
4、サーバー上にRootパスワードが設定されます。

Alibaba Cloud ECSインスタンスを起動する

まず、Alibaba Cloud ECS Consoleにログインします。新しいECSインスタンスを作成し、少なくとも2GBのRAMを搭載したオペレーティングシステムとしてUbuntu 16.04を選択します。ECSインスタンスに接続し、rootユーザーとしてログインします。

Ubuntu 16.04インスタンスにログインしたら、以下のコマンドを実行して、ベースシステムを最新の利用可能なパッケージで更新します。

apt-get update -y

ホストの設定

まず、各インスタンスに/etc/hostsファイルを設定し、各インスタンスがホスト名で通信できるようにします。

各インスタンスに/etc/hostsファイルを設定するには、以下のコマンドを実行します。

nano /etc/hosts

以下の行を追加します。

192.168.0.101 registry-server
192.168.0.102 registry-client

完了したら、ファイルを保存して閉じます。

次に、レジストリ・サーバ・インスタンス上で以下のコマンドを実行することで、名前解決を確認することができます。

ping registry-client
ping registry-server

Dockerのインストール

始める前に、両方のインスタンスにDockerをインストールする必要があります。デフォルトでは、Ubuntu 16.04のデフォルトリポジトリにはDocker CEの最新版がありません。そのため、Docker CEのリポジトリをシステムに追加する必要があります。

まず、以下のパッケージをシステムにインストールします。

apt-get install -y apt-transport-https software-properties-common ca-certificates curl -y

次に、以下のコマンドでDocker CE GPGキーをダウンロードして追加します。

wget https://download.docker.com/linux/ubuntu/gpg 
apt-key add gpg

次に、/var/lib/dpkg/lockDocker CE リポジトリを APT に追加します。

echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" | tee /etc/apt/sources.list.d/docker.list

最後に、以下のコマンドを実行してリポジトリを更新し、Docker CEをインストールします。

apt-get update -y
apt-get install docker-ce -y

インストールが完了したら 以下のコマンドでDockerの状態を確認します。

systemctl status docker

出力します。

docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-08-06 19:49:28 IST; 41s ago
     Docs: https://docs.docker.com
 Main PID: 13024 (dockerd)
   CGroup: /system.slice/docker.service
           ├─13024 /usr/bin/dockerd -H fd://
           └─13054 docker-containerd --config /var/run/docker/containerd/containerd.toml

Aug 06 19:49:27 Node1 dockerd[13024]: time="2018-08-06T19:49:27.240011773+05:30" level=info msg="ClientConn switching balancer to \"pick_first\
Aug 06 19:49:27 Node1 dockerd[13024]: time="2018-08-06T19:49:27.241034794+05:30" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0
Aug 06 19:49:27 Node1 dockerd[13024]: time="2018-08-06T19:49:27.243219799+05:30" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0
Aug 06 19:49:27 Node1 dockerd[13024]: time="2018-08-06T19:49:27.244280675+05:30" level=info msg="Loading containers: start."
Aug 06 19:49:27 Node1 dockerd[13024]: time="2018-08-06T19:49:27.970064077+05:30" level=info msg="Default bridge (docker0) is assigned with an I
Aug 06 19:49:28 Node1 dockerd[13024]: time="2018-08-06T19:49:28.161884804+05:30" level=info msg="Loading containers: done."
Aug 06 19:49:28 Node1 dockerd[13024]: time="2018-08-06T19:49:28.192309817+05:30" level=info msg="Docker daemon" commit=0ffa825 graphdriver(s)=o
Aug 06 19:49:28 Node1 dockerd[13024]: time="2018-08-06T19:49:28.193198210+05:30" level=info msg="Daemon has completed initialization"
Aug 06 19:49:28 Node1 systemd[1]: Started Docker Application Container Engine.

Dockerレジストリのインストール

始める前に、レジストリサーバインスタンス上でDocker private Registryを作成する必要があります。まず、以下のコマンドでDocker Hubからレジストリイメージをダウンロードします。

docker pull registry

出力します。

Using default tag: latest
latest: Pulling from library/registry
4064ffdc82fe: Pull complete 
c12c92d1c5a2: Pull complete 
4fbc9b6835cc: Pull complete 
765973b0f65f: Pull complete 
3968771a7c3a: Pull complete 
Digest: sha256:51bb55f23ef7e25ac9b8313b139a8dd45baa832943c8ad8f7da2ddad6355b3c8
Status: Downloaded newer image for registry:latest

レジストリイメージをダウンロードしたら、Dockerレジストリを保護するための自己署名証明書を生成する必要があります。なぜなら、Dockerノードは、プライベートレジストリへのイメージのアップロードやダウンロードにTLSを介した安全な接続を使用しているからです。

レジストリサーバに移動し、以下のコマンドを実行して証明書を生成します。

mkdir /etc/certs
cd /etc/certs
openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 365 -out ca.crt

出力します。

Generating a 4096 bit RSA private key
.........................++
.........................................++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IN
State or Province Name (full name) [Some-State]:Gujarat
Locality Name (eg, city) []:Junagadh
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Alibaba
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:registry-server
Email Address []:hitjethva@gmail.com

この証明書を信頼するためには、生成されたca.crt証明書を全てのレジストリクライアントシステムにコピーする必要があります。

ここで、以下のコマンドを実行して、証明書情報を持ったDockerのレジストリコンテナを起動します。

docker run -d -p 5000:5000 --restart=always --name registry -v /etc/certs:/etc/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/etc/certs/ca.crt -e REGISTRY_HTTP_TLS_KEY=/etc/certs/ca.key registry

これで、以下のコマンドを使用して実行中のレジストリコンテナを確認することができます。

docker ps

出力します。

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS                    NAMES
304fa6da95d4        registry            "/entrypoint.sh /etc…"   12 seconds ago      Restarting (1) 3 seconds ago   0.0.0.0:5000->5000/tcp   registry

レジストリクライアント上にDockerイメージを作成する

次に、プライベートレジストリサーバにアップロードするためのDockerコンテナイメージをレジストリクライアント上で作成して構築します。

ここでは、Dockerfileを使ってUbuntu 16.04ベースのApache Webサーバーイメージを構築します。

まず、以下のコマンドでDockerfileを作成します。

mkdir Dockerimage
cd Dockerimage
nano Dockerfile

以下の行を追加します。

FROM ubuntu:16.04

LABEL project="Apache Web Server Image"
LABEL maintainer "hitjethva@gmail.com"

RUN apt-get update
RUN apt-get install -y apache2

VOLUME /var/www/html

ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_PID_FILE=/var/run/apache2/apache2$SUFFIX.pid
ENV APACHE_LOCK_DIR=/var/lock/apache2

RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR

EXPOSE 80

CMD ["apache2","-DFOREGROUND"]

さて、以下のコマンドを実行して、Dockerfileを使ってApacheのWebサーバイメージを構築します。

docker build -t ubuntu:apachev1.0 .

出力します。

Sending build context to Docker daemon  2.048kB
Step 1/14 : FROM ubuntu:16.04
16.04: Pulling from library/ubuntu
8ee29e426c26: Pull complete 
6e83b260b73b: Pull complete 
e26b65fd1143: Pull complete 
40dca07f8222: Pull complete 
b420ae9e10b3: Pull complete 
Digest: sha256:3097ac92b852f878f802c22a38f97b097b4084dbef82893ba453ba0297d76a6a
Status: Downloaded newer image for ubuntu:16.04
 ---> 7aa3602ab41e
Step 2/14 : LABEL project="Apache Web Server Image"
 ---> Running in 2c0995179821
Removing intermediate container 2c0995179821
 ---> cd5b35df03c2
Step 3/14 : LABEL maintainer "hitjethva@gmail.com"
 ---> Running in 3a28f13e1418
Removing intermediate container 3a28f13e1418
 ---> b4f0713f0fcf
Step 4/14 : RUN apt-get update
 ---> Running in 7c1f0d548b90
Removing intermediate container 7c1f0d548b90
 ---> adb9a635b7f1
Step 5/14 : RUN apt-get install -y apache2
 ---> Running in 0235c7866eb6
Removing intermediate container 0235c7866eb6
 ---> 2b5f2c282c79
Step 6/14 : VOLUME /var/www/html
 ---> Running in e9b58c5f7ddd
Removing intermediate container e9b58c5f7ddd
 ---> 4a2f62e669b7
Step 7/14 : ENV APACHE_RUN_USER www-data
 ---> Running in 9ec982161d2d
Removing intermediate container 9ec982161d2d
 ---> 548e324848d6
Step 8/14 : ENV APACHE_RUN_GROUP www-data
 ---> Running in 1cf084f71b1b
Removing intermediate container 1cf084f71b1b
 ---> db0461896c00
Step 9/14 : ENV APACHE_LOG_DIR /var/log/apache2
 ---> Running in e2bdf40d1f4b
Removing intermediate container e2bdf40d1f4b
 ---> bbbde0ba0289
Step 10/14 : ENV APACHE_PID_FILE=/var/run/apache2/apache2$SUFFIX.pid
 ---> Running in 2277820a5e13
Removing intermediate container 2277820a5e13
 ---> af92b486cafb
Step 11/14 : ENV APACHE_LOCK_DIR=/var/lock/apache2
 ---> Running in 6e667a32abdb
Removing intermediate container 6e667a32abdb
 ---> b85b2b568744
Step 12/14 : RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR
 ---> Running in 8c0031a631cb
Removing intermediate container 8c0031a631cb
 ---> 106b4813eef0
Step 13/14 : EXPOSE 80
 ---> Running in 09712f52ce38
Removing intermediate container 09712f52ce38
 ---> 4364dd9cefa9
Step 14/14 : CMD ["apache2","-DFOREGROUND"]
 ---> Running in ffb9e1dda041
Removing intermediate container ffb9e1dda041
 ---> 4464cfdff2c6
Successfully built 4464cfdff2c6
Successfully tagged ubuntu:apachev1.0

上のコマンドでは、ubuntu:apachev1.0という名前のDockerイメージをビルドしています。

以下のコマンドでリストアウトできます。

docker images

出力します。

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              apachev1.0          4464cfdff2c6        36 seconds ago      254MB
ubuntu              16.04               7aa3602ab41e        10 days ago         115MB
httpd               <none>              74ad7f48867f        9 months ago        177MB

次に、「regyserver:portnumber/image name:tag」形式でdockerイメージの名前を変更する必要があります。

生成されたイメージの名前を変更するには、以下のコマンドを使用します。

docker tag ubuntu:apachev1.0 registry-server:5000/ubuntu:apachev1.0

では、以下のコマンドで再度検証してみましょう。

docker images

出力します。

REPOSITORY                    TAG                 IMAGE ID            CREATED              SIZE
ubuntu                        apachev1.0          4464cfdff2c6        About a minute ago   254MB
registry-server:5000/ubuntu   apachev1.0          4464cfdff2c6        About a minute ago   254MB
ubuntu                        16.04               7aa3602ab41e        10 days ago          115MB
httpd                         <none>              74ad7f48867f        9 months ago         177MB

レジストリサーバにDockerイメージをアップロードする

これで、レジストリクライアント上にDockerイメージの準備ができました。起動する前に、レジストリサーバからレジストリクライアントにca.crt証明書をコピーする必要があります。

レジストリクライアントのインスタンスに移動して、以下のコマンドを実行します。

mkdir -p /etc/docker/certs.d/registry-server:5000
scp root@registry-server:/etc/certs/ca.crt /etc/docker/certs.d/registry-server/

ここで、以下のコマンドでdockerサービスを再起動します。

systemctl restart docker

次に、以下のコマンドでプライベートレジストリサーバにdockerイメージをアップロードします。

docker push registry-server:5000/ubuntu:apachev1.0

出力します。

The push refers to repository [registry-server:5000/ubuntu]
1e2d2004eac5: Pushed 
90c9e108cca6: Pushed 
6ad516d3083c: Pushed 
bcff331e13e3: Pushed 
2166dba7c95b: Pushed 
5e95929b2798: Pushed 
c2af38e6b250: Pushed 
0a42ee6ceccb: Pushed 
apachev1.0: digest: sha256:a644ec3477b8616d2956b3254cd67bebb08ded68dce25a9f4a773de1dfae122b size: 1988

また、以下のコマンドを使って、アップロードした画像をレジストリサーバーからダウンロードすることもできます。

docker pull registry-server:5000/ubuntu:apachev1.0

出力します。

apachev1.0: Pulling from ubuntu
8ee29e426c26: Pull complete 
6e83b260b73b: Pull complete 
e26b65fd1143: Pull complete 
40dca07f8222: Pull complete 
b420ae9e10b3: Pull complete 
17cc14b524da: Pull complete 
a672de99207d: Pull complete 
28fef3b105af: Pull complete 
Digest: sha256:a644ec3477b8616d2956b3254cd67bebb08ded68dce25a9f4a773de1dfae122b
Status: Downloaded newer image for registry-server:5000/ubuntu:apachev1.0

おめでとうございます。これでUbuntu 16.04でDockerのプライベートレジストリサーバのセットアップが完了しました。これで、レジストリサーバーへのDockerイメージのアップロードやダウンロードが簡単にできるようになりました。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

Railsのdockerコンテナのよく使うdocker-composeコマンドとMySqlコンテナの繋ぐ方法

これは何?

こちらはdocker composerで作成したRails,MySqlコンテナ、自分がターミナルで操作するコマンドの自分用メモです。

コンテナの操作

コントローラー作成

controller_nameは自分で定義する

$ docker-compose run --rm web rails generate controller controller_name

web : docker-compose.ymlに定義したservices名

Model作成

model_nameは自分で定義する 引数にname:stringとかでnameカラムを作れます。

$ docker-compose run --rm web rails generate model model_name name:string 

ルーティング変更

config/routes.rbを編集後実行

$ docker-compose run --rm web rake routes

DB作り

$ docker-compose run --rm web rake db:create

マイグレーション

$ docker-compose run --rm web rake db:migrate

seed実行

$ docker-compose run --rm web rake db:seed

DB接続

コンテナへSSH接続 〜 mysqlコンテナ内でmysqlコマンド実行

SSHで接続

$ docker exec -it $MYSQL_CONTAINER_NAME bash

でコンテナ内部に入り。

\$MYSQL_CONTAINER_NAME は \$ docker ps で調べる($ docker-compose ps ではなく)

作成したMySQLユーザでmysqlコマンドをいれる

$ mysql -uroot -p db_name

MySql workbenchで接続

docker-compose.ymlのportを

.
.
.
ports:
      - "3306:3306"

で設定して(別のポート使うとworkbenchが接続できなくなる)、
workbenchのconnectionsの設定は以下のように

Connection Method: Standard を選択

Hostname : 127.0.0.1
Port : 3306(docker run時の特に指定してなければ)
Username : root
Password : docker run時の指定した環境変数

参考資料

serverコンテナ参考
dbコンテナ参考
seedとは
$docker run 公式説明

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

RailsとMysqlのdockerコンテナのよく使うコマンド

これは何?

こちらはdockerで作成したRails,MySqlコンテナ、自分がターミナルで操作するコマンドの自分用メモです。

コンテナの操作

コントローラー作成

controller_nameは自分で定義する

$ docker-compose run --rm web rails generate controller controller_name

web : docker-compose.ymlに定義したservices名

Model作成

model_nameは自分で定義する 引数にname:stringとかでnameカラムを作れます。

$ docker-compose run --rm web rails generate model model_name name:string 

ルーティング変更

config/routes.rbを編集後実行

$ docker-compose run --rm web rake routes

DB作り

$ docker-compose run --rm web rake db:create

マイグレーション

$ docker-compose run --rm web rake db:migrate

seed実行

$ docker-compose run --rm web rake db:seed

DB接続

コンテナへSSH接続 〜 mysqlコンテナ内でmysqlコマンド実行

SSHで接続

$ docker exec -it $MYSQL_CONTAINER_NAME bash

でコンテナ内部に入り。

\$MYSQL_CONTAINER_NAME は \$ docker ps で調べる($ docker-compose ps ではなく)

作成したMySQLユーザでmysqlコマンドをいれる

$ mysql -uroot -p db_name

MySql workbenchで接続

docker-compose.ymlのportを

.
.
.
ports:
      - "3306:3306"

で設定して(別のポート使うとworkbenchが接続できなくなる)、
workbenchのconnectionsの設定は以下のように

Connection Method: Standard を選択

Hostname : 127.0.0.1
Port : 3306(docker run時の特に指定してなければ)
Username : root
Password : docker run時の指定した環境変数

参考資料

serverコンテナ参考
dbコンテナ参考
seedとは
$docker run 公式説明

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

Alibaba Cloud ECSを使用した事前学習済みのWord2VecディープラーニングREST APIの作成と導入

このチュートリアルでは、Word2Vecという事前学習済みのディープラーニングモデルを、一からREST APIを構築して他のサービスで利用できるようにします。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

前提知識

1、Alibaba Cloud Elastic Compute Service (ECS)インスタンスなどのUnixベースのマシンで、できればより多くの計算能力を持つことが望ましい。
2、pythonpipコマンドの理解
3、Linuxオペレーティングシステムを使用してフォルダやファイルを作成、閲覧、編集する方法の知識

Wordベクター入門

Wordベクターは、その柔軟性とトレーニングのしやすさから、最近ディープラーニングの世界を揺るがしています。ワードエンベッディング(word embeddings)は、NLPの分野に革命をもたらしました。

その中核となるのは、ワードエンベッディングとは、それぞれが単一の単語に対応するワードベクトルであり、そのベクトルが単語を「意味する」ようなものである。これは、king - queen = boy - girlのベクトルのような特定の現象によって実証されます。単語ベクトルは、レコメンドエンジンから英語を実際に理解するチャットボットまで、あらゆるものを構築するために使用されています。

もう一つの考慮すべき点は、どのようにして単語の埋め込みを得るかということです。単語の埋め込みはランダムではなく、ニューラルネットワークを訓練することによって生成されます。最近の強力なワードエンベッディングの実装は、GoogleのWord2Vecという名前のもので、言語の中で他の単語の隣に現れる単語を予測して学習します。例えば、「猫」という単語に対して、ニューラルネットワークは「子猫」と「猫」という単語を予測します。このように、お互いに「近くに」現れる言葉を直感的にベクトル空間に配置することができるのです。

しかし、迅速にプロトタイプを作成し、展開プロセスを簡素化するために、Googleのような他の大企業の事前学習済みモデルを使用するのは業界標準です。このチュートリアルでは、GoogleのWord2Vecの事前学習済みワードエンベッディングをダウンロードして使用します。これを行うには、作業ディレクトリで以下のコマンドを実行します。

wget http://magnitude.plasticity.ai/word2vec/GoogleNews-vectors-negative300.magnitude

Pythonの環境設定

Pythonの環境を設定することは、機械学習アプリケーションを開発する上で明らかに重要な要素です。しかし、このプロセスは見落とされがちです。Python の依存関係を使用する場合のベストプラクティスは、明示的な requirement.txt ファイルと同時に仮想環境を使用することです。これにより、複数のマシンや環境にまたがるデプロイメントと開発の両方において、ライブラリの管理が容易になります。

最初に、python モジュールである virtualenv をインストールします。これにより、ライブラリが互いに干渉しないように作業ディレクトリを分離することができます。

pip3 install virtualenv

次に、venvという名前の仮想環境を作成します。同じpythonのバージョンを指定することと、一貫して使用することの両方が重要であることに注意してください。最良のサポートを得るためには、Python 3 を使用することをお勧めします。venvフォルダにはrequirements.txtで指定したすべてのpythonモジュールが格納されます。

virtualenv -p python3 venv

仮想環境を作ったとはいえ、まだ起動していません。プロジェクトとその依存関係を使用するには、sourceを使用してソースを作成しなければなりません。実際にsourceを呼び出すファイルは、binという名前のフォルダにあるactivateという名前のファイルです。

source venv/bin/activate

プロジェクトを終了したり、仮想環境を切り替えたい場合は、deactivateコマンドを使って仮想環境を終了させます。

deactivate

Magnitudeパッケージのインストール

ダウンロードしたワードエンベッディングモデルは.magnitude形式です。この形式では、SQLを使って効率的にモデルをクエリすることができるため、本番サーバーに最適なエンベッディング形式となっています。.magnitude形式を読めるようにする必要があるので、pymagnitudeパッケージをインストールします。また、モデルによって作られたディープラーニング予測を後で提供するためにflaskをインストールします。

pip3 install pymagnitude flask

また、以下のコマンドで依存性トラッカーに追加します。これによりrequirements.txtというファイルが作成され、Pythonライブラリが保存されるので、後で再インストールすることができます。

pip3 freeze > requirements.txt

モデル予測の作成

まず、単語の埋め込みを開いたり、問い合わせたりするためのファイルを作成します。

touch model.py

次に、model.pyに以下の行を追加してMagnitudeをインポートします。

from pymagnitude import Magnitude 
vectors = Magnitude('GoogleNews-vectors-negative300.magnitude')

単語の引数を提供するqueryメソッドを使用することでgensimパッケージとディープラーニングモデルを楽しむことができます。

cat_vector = vectors.query('cat')
print(cat_vector)

しかし、私たちのAPIのコアについては、2つの単語間の意味の違いを返す関数を定義します。これは、レコメンデーションエンジンのようなもの(類似した単語を含むコンテンツを表示する)のためのほとんどのディープラーニングソリューションのバックボーンとなっています。

similaritymost_similarを使用することで、この関数で遊ぶことができます。

print(vectors.similarity("cat", "dog"))
print(vectors.most_similar("cat", topn=100))

以下のように類似度計算機を実装します。このメソッドは次節でFlask APIから呼び出されます。この関数は0から1の間の実数を返すことに注意してください。

def similarity(word1, word2):
    return vectors.similarity(word1, word2)

REST APIでモデルをラップする

service.pyというファイルに以下の内容でサーバーを作成します。コンテンツを提供するために Flask という名前のサーバーフレームワークを使用します。DjangoのようなWebベースのサーバーフレームワークは他にもありますが、オーバーヘッドが最小限で、統合が容易で、ディープラーニングコミュニティ内でサポートされていることから、Flaskを使用します。

service.pyという名前のファイルを以下の内容で作成します。サーバー機能を扱うためにflaskrequestをインポートし、先ほど書いたモジュールから類似性エンジンをインポートします。

from flask import Flask, request
from model import similarity

app = Flask(__name__)

@app.route("/", methods=['GET'])
def welcome():
    return "Welcome to our Machine Learning REST API!"

@app.route("/similarity", methods=['GET'])
def similarity_route():
    word1 = request.args.get("word1")
    word2 = request.args.get("word2")
    return str(similarity(word1, word2))

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000, debug=True)

私たちのサーバはむしろむき出しですが、@app.routeデコレータを使ってより多くのルートを作成することで簡単に拡張することができます。

アプリケーションのDocker化

Dockerはアプリケーションをコンテナ化するのに便利なツールです。コンテナとは、動作に必要な依存関係をすべて含んだ自己完結型のアプリケーションのことです。開発やテストが容易になるだけでなく、複数のマシンを使うことが多いデプロイにも特に便利です。Dockerコンテナは、仮想マシンなどの重い仮想化が使用するハードウェア層ではなく、オペレーティングシステム層でのみ仮想化するため、軽量です。

コンテナ化のプロセスを開始するには、まずDockerfileを作成することから始めます。DockerfileはDockerプロセス全体のエントリーポイントです。このファイルを使用して、依存関係の定義、ファイルへのアクセス、環境変数の設定、アプリケーションの実行を行います。

touch Dockerfile

次に、現在のディレクトリがDockerfileのディレクトリコンテナであることを認識するためのDocker用のコマンドを追加します。次に、サーバー用のPythonの依存関係をインストールします。

WORKDIR /
ADD requirements.txt /
RUN pip install -r requirements.txt

次に、ワードエンベッディングをダウンロードできるようにwgetをインストールします。MV Dockerコマンドを使用して、Flaskサーバで使用している規約に合わせて名前を変更します。

RUN apt install wget
RUN wget http://magnitude.plasticity.ai/word2vec/GoogleNews-vectors-negative300.magnitude

最後に、最後の行をDockerfileに追加してサーバーを起動します。これでFlaskサーバーが起動します。

CMD [ "python", "./service.py" ]

Docker化されたアプリケーションを実行する

イメージを作成するためにDockerを使用することで、モデルを独自の標準化されたコンテナにビルドすることができます:モデルのインスタンスを瞬時に作成するための標準化された命令セットです。

最初にdocker buildコマンドを実行し、イメージの名前を作成するために-tフラグを指定し、DockerにDockerfileがカレントディレクトリにあることを知らせるために.を指定します。

docker build -t model .

最後に、docker runコマンドを使ってイメージを実行し、-pフラグを指定してモデルを8000番ポート(Flaskサーバが動作しているポート)にバインドし、8000番ポート(ローカルホストで使用したいポート)に公開します。

docker run -p 8000:8000 model

APIコールの作成
私たちのサーバーはlocalhost:8000で利用できるようになりました。localhost:8000/similarity?word1=cat&word2=dogでデータベースにクエリを実行し、ブラウザまたは別のAJAXクライアントを介してレスポンスを表示します。

APIをテストするもう一つのオプションはコマンドラインを使うことです。GETルートを使用してAPIをテストするためにブラウザ(すなわちChromeやSafari)を使用することができますが、POSTリクエストを使用できないために制限されています。別の方法としては、Unix オペレーティングシステムに同梱されている curl ツールを使用する方法があります。

curl を使って word1word2 の両方の引数を指定し、コマンドラインでレスポンスを表示します。

curl -X GET 'http://localhost:8000/similarity?word1=dog&word2=cat'

私たちの端末では、正確に分類された回答を見ることができるはずです。

展開

このプロセス全体は、ローカルマシンでも、Alibaba Cloudなどのクラウドサービスプロバイダでも実行できます。しかし、Dockerの利点は、ローカルマシン上でモデルを開発し(つまりservice.pyファイルを作成)、その後Dockerを使用してサーバー上でモデルを実行できることです。これにより、高速な開発と高速なデプロイの両方が可能になります。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

Githubのプルリク時に発生するエラー「You can't perform that action at this time.」の解決法

起きた問題

laravelで掲示板らしきものをgithubで管理しながら、制作していた時に急にプルリクができなくなってしまった。

環境

dockerとlaravelとgithub

発生したエラー

dockerコンテナの外から

git add .
git commit -m "コミット名"
git push origin "ブランチ名"

をし、github上でプルリクをしようとしたが、
スクリーンショット 2020-07-15 13.01.16.png
上記の赤枠内の部分が表示されない。仕方なく、
スクリーンショット 2020-07-15 0.56.48.png
黒枠からプルリクを選択→create pull requestを選択

Pull request creation failed. Validation failed You can't perform that action at this time.

というエラーがgithub上に出力された。

原因

制作した掲示板はgithubログインできる使用だったのだが、その掲示板に今ログインしているuserとプルリクしたgithubのアカウントが異なっていたためと思われる。

解決策

掲示板をプルリクしたいgithubアカウントでログインし直すと解決できた。

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

docker jupyter themes をもとに戻したかったが、苦戦した記録

Dockerfilleに

RUN -jt onedark

を入れていたので、
この記述を消したがうまく行かないので

docker imageを消して再buildしてもらうことに
image 全部消す

docker-compose rm

その後に、

docker-compose up --build

で起動したが、onedarkはそのまま

RUN jt -r

を入れて

docker-compose up --build

を実行するが、jt -r 実行できないよと言われる。

起動した後に直接jt -r をしてjupyter を起動した。

docker exec -it {docker image} bash
jt -r

そのあとdocker stopしたあと、docker buildしなおして解決

docker stop {dockerimage}
docker-compose up --build
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ENI技術をベースにしたコンテナネットワーク 導入事例とECSコンテナネットワークマルチNICソリューション

本記事では、Alibaba CloudのENI(Elastic Network Interface)技術をベースにしたコンテナネットワークの基礎を紹介します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

従来のコンテナネットワークソリューション

ここでは、従来のコンテナネットワークの動作原理を紹介します。

CNIは、Cloud Native Computing Foundation (CNCF)が運営するオープンソースプロジェクトです。これは、Linuxコンテナネットワーク管理のためのプラグインを開発するための標準を開発し、主要なベンダーにソースコードライブラリを提供しています。よく知られているCNIプラグインには、CalicoやFlannelなどがあります。Calico は Flex/Bird を通じて BGP などのプロトコルを実装し、それらを分散型のインメモリデータベースに格納して大規模なレイヤ 3 ネットワークを構築し、異なるホスト上のコンテナが ARP を送信せずに異なるサブネット上のコンテナと通信できるようにします。

Flannelは、VXLANなどのトンネリング技術に基づいたコンテナオーバーレイネットワークを実装しています。Calico/FlannelなどのCNIは、コンテナネットワークを構成するためにVETHペアを使用します。VETH デバイスのペアが作成され、一方の端がコンテナにバインドされ、もう一方の端が VM にバインドされます。VMは、ネットワークプロトコルスタック(オーバーレイネットワーク)、Iptables(Calicoプラグイン)、Linux Bridgeなどの技術を介してコンテナネットワークを転送します。(コンテナネットワークがECSのブリッジを介してvSwitchに接続されている場合、VPCはECSレベルにしか到達できず、コンテナネットワークはブリッジ上のプライベートネットワークとなります)。

現在主流となっているコンテナネットワークのワークフローを下図に示しますが、マルチNICコンテナネットワークとは以下の点で異なります。

image.png

1、ホスト1上のコンテナから送信されたメッセージは、VETHを介してVM上のLinux Bridgeに送信され、Linux Bridgeはフォワーディングロジックを実行して、メッセージVM上のNICをホスト上に配置されたvSwitchに送信します。
2、ホスト2上のVMは、vSwitchから送信されたメッセージを受信し、Linux Bridgeのフォワーディングロジックを用いてVETHを介してコンテナに送信します。
ネットワークシステム全体では、VMはネットワーク構成のためにK8SなどのオーケストレーションシステムのCNIプラグインを内部的に必要とします。vSwitchはOpenflowやNetconfなどの通信プロトコルをサポートしており、これらはSDN(Software Defined Network)コントローラを介して管理・設定されます。主流のToRスイッチは、リモート設定にNetconfプロトコルを使用します。OpenflowをサポートするSDN物理スイッチも市場に出回っています。

ネットワーク全体を管理するためには、2つの異なるネットワーク制御システムが必要になります。構成が比較的複雑で、実装の仕組みなどの要因で一定のパフォーマンスのボトルネックが存在します。ホスト上のセキュリティポリシーをコンテナアプリケーションに適用出来ません。

マルチNICコンテナネットワーク

VMに動的にホットスワップ可能な複数のNIC(Network Interface Card)がある場合、これらのNICをコンテナネットワーク上で使用できるため、コンテナネットワークではLinux VETHやBridgeなどの技術を利用する必要がなくなります。同時に、ホスト上の仮想スイッチ(vSwitch)にメッセージが転送されるため、プロセスが簡素化され、ネットワークパフォーマンスが向上します。

ソリューションの概要

次の図に示すように、VMとコンテナからのトラフィックを転送するために、ホスト上でvSwitchが動作しています。vSwitchには複数の仮想NICが接続されています。コンテナがVM上で起動されると、仮想NICはホスト上でコンテナが配置されているVMに動的にバインドされ、その後、VM内でNICはコンテナが配置されているネットワークネームスペースにバインドされ、コンテナ内のネットワークトラフィックをNICを介してホスト上に配置されているvSwitchに直接送信することができます(つまり、コンテナのネットワークはvSwitchに直接接続することができます)。

image.png

ACL、QoS、Session などのルールが vSwitch で適用され、トラフィックを転送します。ホスト 1 の VM 上で動作しているコンテナがホスト 2 の VM 上で動作しているコンテナにアクセスする場合、トラフィックは一般的に以下のプロセスを経ます。

1、ネットワークメッセージがコンテナコアのネットワークプロトコルスタックを通過します。ルートが照会された後、メッセージは eth0 NIC を介して送信されます。
2、ホスト上のvSwitchは、仮想ポートを介してコンテナからのメッセージを受信し、vSwitchのフォワーディングロジックを実行して、物理ネットワークポートを介してトップオブラック(ToR)スイッチにパケットを送信します。コンテナまたはVMネットワークに仮想プライベートクラウド(VPC)が確立されている場合、メッセージはVXLANなどのトンネリング技術を使用してカプセル化する必要があります。
3、ToRスイッチはルートを問い合わせ、ホスト2の物理ポートに接続してメッセージを転送します。
4、ホスト2のvSwitchは物理ポートのメッセージを受信し、転送ロジックを介してコンテナに接続する仮想ポートに送信します。
5、相手側から送られてきたメッセージをコンテナ内のプロトコルスタックeth0が受信し、コンテナ内のネットワークプロトコルスタックで処理します。

ソリューションの特徴

VM上でコンテナを稼働させる従来のソリューションと比較して、高いパフォーマンスと管理のしやすさ、強力なアイソレーションが特徴です。

VPCへの直接接続

マルチNICソリューションでは、コンテナがVPCネットワークプレーンに直接アクセスできるため、各コンテナはEIP、SLB、Anti-DoS Pro、セキュリティグループ、HAVIP、NAT、ユーザールーティングなどのVPCネットワーク機能をフルに提供することができます。

Cross-VPC

マルチNICソリューションを介してVPCのネットワークプレーンに直接アクセスするコンテナは、ピア機能などのVPCの一部の高度な機能を利用することができます。また、クラウド製品へのアクセスにはCross-VPC ENIを利用することができ、異なるVPC内の複数のNICをコンテナに割り当てることができます。これにより、複数のVPCをまたいでコンテナを利用することができます。

ハイパフォーマンスを実現

マルチNICソリューションでは、コンテナ内のネットワークトラフィックは、VM上のIptables/Bridgeを経由して転送される必要はなく、ホスト上にあるvSwitchに直接転送されます。これにより、VM上のデータメッセージ転送ロジックが不要となり、データコピー処理が簡素化されるため、コンテナネットワークのパフォーマンスが大幅に向上します。次の表は、1回のテストでの異なるソリューションの基本性能データを示しています。

Single-thread (Mbps) Single-thread (pps) Multi-thread (pps) TBase test 1 KB (QPS)
Linux Bridge 32.867 295,980 2,341,669 363,300
Multi-NIC solution 51.389 469,865 3,851,922 470,900
Performance improvement 56.35% 58.7% 64.49% 29.6%

強力な分離

従来のブリッジソリューションでは、すべてのコンテナインスタンスが同じ大きなレイヤ2ネットワーク上にあるため、ブロードキャスト、マルチキャスト、未知のユニキャストが氾濫してしまうという問題がありました。マルチNICソリューションが提供する直接接続機能とECSネットワークが提供するACLやセキュリティグループ機能により、セキュリティの分離を効果的に確保することができます。コンテナの管理画面上でもコンテナネットワークのトラフィックを見ることができません。セキュリティルールはVMレベルではなくコンテナレベルで適用されます。

簡単な管理

管理システムがコンテナを VM にディスパッチすると、制御システムは VM が配置されているホスト上の vSwitch 上に NIC を作成し、ホットスワップで VM に NIC を挿入し、VM 上のコンテナネットワークネームスペースに NIC を設定します。vSwitchのトラフィック転送ルールを設定し、xGW上でHaVIPを設定することで、外部アプリケーションやクライアントはコンテナが提供するサービスにアクセスすることができます。

また、マルチNICソリューションは、コンテナの移行を容易にします。同じホストに移行した別のVMを例にとると、K8SのKubeletモジュールはアプリケーションを移行した後、CNIプラグインによるネットワークの再設定、コンテナIPとVIPの管理、コンテナアプリケーションへのアクセス方法の設定を行います。全体のプロセスは複雑ですが、NICソリューションを使えば簡単にできます。コンテナがVMにディスパッチされた後、古いコンテナにバインドされているNICを古いVMから外し、新しいコンテナがあるVMに挿入します。そして、NICはVM上のコンテナネットワークネームスペースにバインドされます。新しいコンテナは、これ以上ネットワークを再設定する必要がなく、通常通り通信することができます。

DPDKサポート

その優れた性能上の優位性から、DPDKが普及し、DPDKをベースにしたアプリケーションがどんどん開発されています。従来のコンテナネットワークでは、ネットワークデバイスとしてVETHを使用していますが、現在はDPDKのPDKドライバを直接使用することができないため、DPDKベースのアプリケーションをコンテナ内で直接使用することはできません。マルチNICソリューションでは、コンテナは一般的な E1000 または virtio_net デバイスである ECS ネットワークデバイスを使用します。どちらのデバイスにもPMDドライバが搭載されており、コンテナはこのデバイスを使用してDPDKベースのアプリケーションを直接実行することができるため、コンテナ内のアプリケーションのネットワークパフォーマンスが向上します。

VM マルチNIC

物理ホストのクロスドメインを有効にするには、物理マシンに複数のNICを挿入する必要があります。PCIスロットの制限とコストのため、物理マシンに2つ以上のNICを配置することは稀です。ハードウェアデバイスの電源を多かれ少なかれオンまたはオフにすると、システム全体にパルスが追加され、マシンの安定性に影響を与え、デバイスのホットスワップが制限されます。一般的なホットスワップデバイスはUSBデバイスです。PCIデバイスのホットスワップは、2つの列挙の必要性と電力効果のため、近年まで制限的なサポートを受けていませんでした。

仮想環境では、仮想NICの低コストと柔軟性により、VMの可用性が大幅に向上します。ユーザーは必要に応じてNICを動的に割り当てたり、解放したりすることができ、VMの正常な動作に影響を与えることなく、VMへのNICのプラグインやプラグの抜き差しを動的に行うことができます。libvirt/qemu が仮想デバイスをシミュレートする方法には、物理ホストでは実現できない以下の利点があります。

リソースの制限

システムにメモリなどの十分なリソースがあれば、複数のNICをシミュレートして同じVMに割り当てることができ、1つのVMに64枚、あるいは128枚のNICをインストールすることも可能です。ソフトウェアでシミュレートされたNICは、物理的なハードウェア環境のものに比べて、はるかに少ないコストで実現できます。また、マルチキューやメインストリームハードウェアのアンインストール機能もサポートしており、システムの柔軟性が向上しています。

ダイナミックホットスワップ

VM上のNICはソフトウェアによってシミュレートされます。そのため、NICが必要な場合には、NICをシミュレートするためにソフトウェアによっていくつかの基本的なリソースが割り当てられます。ホットスワップフレームワークにより、libvirt/qemu を実行中の VM に簡単にバインドすることができ、VM は NIC を使ってネットワークメッセージを送信することができます。NIC が不要になったら、VM を停止させることなく、libvirt/qemu インターフェイスコールで NIC を "unplugged" することができます。NIC に割り当てられたリソースは破棄され、NIC に割り当てられたメモリはリサイクルされ、中断は回復します。

コンテナネットワークの実装

ここでは、VMのマルチNICを利用してコンテナネットワーク通信をステップごとに実装する方法を説明します。

1、Alibaba CloudコンソールでVMインスタンスを作成し、インスタンス作成時に複数のNICを選択します。すると、VM上に複数のNICが表示されます。
2、VM上にコンテナアプリケーションをデプロイします。

~# docker run -itd --net none ubuntu:16.04

注:Docker起動時にコンテナのネットワークタイプをnoneに指定します。

3、VM にログオンし、NIC の 1 つをコンテナの名前空間にバインドします。次の例では、新しく動的に挿入されたNICはeth2で、コンテナのネットワークネームスペースは2017です(明確にするために、docker inspectで見たPIDをネットワークネームスペースとして使用しています)。

~# mkdir /var/run/netns
~# ln -sf /proc/2017/ns/net /var/run/netns/2017
~# ip link set dev eth2 netns 2017
~# ip netns exec 2017 ip link set eth2 name eth0
~# ip netns exec 2017 ip link set eth0 up
~# ip netns exec 2017 dhclient eth0

注: リリースバージョンによっては、ユーザーが手動で接続を作成してコンテナのネットワークネームスペースを「作成」する必要がない場合があります。eth2 をコンテナのネットワークネームスペースにバインドした後、eth0 に名前を変更します。

4、VM とコンテナ内の NIC 構成ステータスを表示します。
VM 上に NIC がまだ存在するかどうかを確認します。

~# ifconfig -a

コンテナ内に新しく設定されたNICがあるかどうかを確認します。

/# ifcofig -a

VMからeth2が削除され、コンテナに適用されたことがわかります。

5、手順1から4を繰り返して、別のVMとコンテナを起動します。
6、sockperfなどのツールを使ってパフォーマンスのテストや比較を行います。

$ cat server.sh

#!/bin/bash
for i in $(seq 1 $1)
do
    sockperf server --port 123`printf "%02d" $i` &
Done

$ sh server.sh 10
$ cat client.sh

#!/bin/bash
for i in $(seq 1 $1)
do
    sockperf tp -i 192.168.2.35 --pps max --port 123`printf "%02d" $i` -t 300 &
done

$ sh client 10

Antの金融ユースケース

Tier-Base (TBase) は Redis に似た分散型 KV データベースです。C++で書かれており、ほとんどすべてのRedisデータ構造をサポートしています。また、バックエンドとしてRocksDBをサポートしています。TBaseはAnt Financialで広く利用されています。ここでは、本ソリューションのTBaseビジネステストについて紹介します。

従来のLinuxブリッジテスト

テスト環境

サーバー:16C60G×1(ハーフA8)

クライアント:4C8G×8

TBaseサーバーの導入:7G×7インスタンス

TBaseクライアントの展開:8 x (16スレッド+1クライアント) => 128スレッド+8クライアント

試験報告書

Operation Packet size Clients NIC load1 CPU QPS AVG rt 99th rt
set 1 KB 8 424 MB 7.15 44% 363,300 0.39 ms < 1 ms
get 1 KB 8 421 MB 7.06 45% 357,000 0.39 ms < 1 ms
set 64 KB 1 1,884 MB 2.3 17% 29,000 0.55 ms < 5 ms
set 128 KB 1 2,252 MB 2.53 18% 18,200 0.87 ms < 6 ms
set 256 KB 1 2,804 MB 2.36 20% 11,100 1.43 ms < 5 ms
set 512 KB 1 3,104 MB 2.61 20% 6,000 2.62 ms < 10 ms

ENI マルチNICテスト

テスト環境

サーバー:16C60G×1(ハーフA8)

クライアント:4C8G×8

TBaseサーバーの導入:7G×7インスタンス

TBaseクライアントの展開:16 x (16スレッド+1クライアント) => 256スレッド+16クライアント

試験報告書

Operation Packet size Clients NIC load1 CPU QPS AVG rt 99th rt
set/get 1 KB 16 570 MB 6.97 45% 470,900 0.30 ms < 1 ms

テストの結論

ENIのマルチNICソリューションをベースに、ブリッジソリューションと比較して、全体的なパフォーマンスが向上し、遅延が大幅に短縮されています(QPSが30%向上し、平均遅延が23%短縮されています)。16C60Gのサーバーを使用し、QPSが470,900程度だとします。この場合、平均rtは0.30ms、99番目のrtは1ms以下となります。CPUの45%、29%、18%、2%がそれぞれユーザ、sys、si、stで消費されています。Linux Bridgeと比較して、multi-NCソリューションの特徴は、siによるCPU消費量が大幅に少ないことです。カーネルキュー分散により、st の CPU 消費量を複数の異なるコアに分散させることで、処理リソースの使用量をよりバランスよくすることができます。

VPCルートテーブルであるFlannel/Canalのソリューションでは、帯域幅とスループットの大幅な損失はありません。遅延はホストに対して0.1ms程度になります。QPSのテストにはNginxを使用しており、ページが小さい場合は10%程度の損失となります。ENIソリューションでは、ホストとの相対的な帯域幅とスループットの大幅な損失はなく、遅延はホスト上よりもわずかに低くなります。アプリケーションテストでは、PODがiptablesの対象になっていないため、ホストネットワーク上よりも10%程度パフォーマンスが向上しています。デフォルトのFlannel VXLANでは、帯域幅とスループットの損失が5%程度、Nginxスモールページテストの最大QPSでは、ホストとの相対的な性能低下が30%程度となっています。

概要

本稿では、VMのマルチNICホットスワップに基づくコンテナネットワークソリューションを紹介しました。VM用のNICを動的にホットスワップしてコンテナに適用し、コンテナネットワークのデータメッセージの送受信を行い、VM上で動作する仮想ソフトウェアスイッチを介してネットワークメッセージを転送することで、コンテナネットワークの管理・制御システムの複雑さを大幅に軽減し、ネットワークパフォーマンスを向上させ、コンテナネットワークのセキュリティを強化しています。

Alibaba Cloud Elastic Network Interface(ENI)は、VPC内のECSインスタンスに接続できる仮想ネットワークインターフェースです。ENIを利用することで、高可用性のクラスタを構築し、低コストでフェイルオーバーを実装し、洗練されたネットワーク管理を実現することができます。ENI機能はすべてのリージョンで利用可能です。ENIの詳細については、以下のページを参照してください。

1、機能と紹介:https://www.alibabacloud.com/help/doc-detail/58496.htm
2、ユーザーガイド: https://www.alibabacloud.com/help/doc-detail/58503.htm
3、開発者ガイド:https://www.alibabacloud.com/help/doc-detail/25485.htm

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

コンテナイメージをDocker Hubにプッシュする

はじめに

コンテナイメージを別の環境でも使用したい、他の人と共有したい、と思ったときに真っ先に思いつく方法は、Docker Hubでコンテナイメージを共有することだと思います。

私がDockerのコンテナイメージをDocker Hubにプッシュしたときに行ったことを書き記しておきます。

環境

  • macOS Catalina
  • git 2.27.0
  • Docker 19.03.8

サンプルプロジェクトをGitから取得

サンプルプロジェクトはDockerの公式のものを利用させていただいております。リンクはこちらになります。

$ git clone https://github.com/dockersamples/node-bulletin-board
$ cd node-bulletin-board/bulletin-board-app

Dockerfileの作成

Dockerfileはコンテナを定義するものです。ご自分のお好きなDockerfileを作成していただいて、任意のディレクトリに置いておきます。本記事ではDocker公式のものを利用させていただいているので、Dockerfileは以下のものをとなっております。ソースはこちらです。

FROM node:current-slim
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
EXPOSE 8080
CMD [ "npm", "start" ]
COPY . .

コンテナイメージのビルド

ここでDockerfileの通りにコンテナイメージを作成します。このコマンドを打つ際には、今いるディレクトリにDockerfileがあることを確認しておきます。

$ docker build --tag bulletinboard:1.0 .

正常に終了していれば、Successfully tagged bulletinboard:1.0と出力されているかと思います。

$ docker images

とTerminal上で打ち、Repositoryに存在していることを確かめておくのもいいかもしれません。

コンテナの起動

コンテナイメージを作成したら、そのコンテナを起動します。

$ docker run --publish 8000:8080 --detach --name bb bulletinboard:1.0

コンテナが起動できたら、そのコンテナ上で動いている掲示板アプリケーションにアクセスできることを確認しておきます。ブラウザでlocalhost:8000にアクセスします( http://localhost:8000 )。

アクセスできることを確認したら、このコンテナイメージはDocker Hubにプッシュしないので削除しておきましょう。

$ docker rm --force bb

Docker Hubでプッシュする

さて、お待たせしました。やっと本題です。ちなみにDocker HubにプッシュするにはDocker Hubアカウントが必要です。もしDocker Hubアカウントがない方はこちらでDocker IDを作成しておいてください。

Docker IDが作成できたら、Terminal上でDocker Hubにログインします。

$ docker login

認証画面(CLI)が出てきたら、Docker IDとご自身で設定されたパスワードを入力してログインを完了させておきます。

ここで注意点なのですが、Docker Hubにプッシュできるコンテナイメージにも条件があります。コンテナイメージの名前を<Docker ID>/<Repository Name>:<tag>としておかないといけない、という条件です。

Docker Hubにプッシュしたことのない人からすれば寝耳に水な訳です。私はこのパターンでした。面倒な作業をもう一回やらないといけないのかと思いましたが、公式ページを見ていると後からでもできるようでした。

$ docker tag bulletinboard:1.0 <Docker ID>/bulletinboard:1.0

というコマンドを打ち、コンテナイメージの名前・タグを変更します。これでDocker Hubにプッシュできるようになりました。Docker Hubにプッシュします。

$ docker push <Docker ID>/bulletinboard:1.0

エラーが何もなかった場合、ご自身のDocker Hubのリポジトリにプッシュされているハズです。確認しておきましょう( https://hub.docker.com/ )。

おわりに

Dockerは最近注目され始めている技術で、自分の分野(データサイエンス)にもDockerが浸透し始めました。正直自分はネットワークとかサーバの分野には明るくないのですが、自分のローカルを汚さなくて良い点、別の環境でも同じ分析環境を作成できる点で非常に嬉しいです。自分はパソコンを購入して再度環境構築するのが面倒だったので、いっそのこと、とDockerを導入してみました。Gitも勉強しているので、DockerとGitは親和性が高いので今後は連携させて行きたいですね。

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

Docker Desktop for Mac の kubernetes に Helm で Redmineをインストールする

はじめに

Helmを使用してRedmineをインストールします。

環境

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.4", GitCommit:"c96aede7b5205121079932896c4ad89bb93260af", GitTreeState:"clean", BuildDate:"2020-06-18T02:59:13Z", GoVersion:"go1.14.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:18:29Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/amd64"}
$ helm version
version.BuildInfo{Version:"v3.2.4", GitCommit:"0ad800ef43d3b826f31a5ad8dfbb4fe05d143688", GitTreeState:"dirty", GoVersion:"go1.14.3"}

手順

インストール

$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install redmine bitnami/redmine
NAME: redmine
LAST DEPLOYED: Wed Jul 15 01:07:13 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the Redmine URL:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w redmine'


  export SERVICE_IP=$(kubectl get svc --namespace default redmine --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
  echo "Redmine URL: http://$SERVICE_IP/"

2. Login with the following credentials

  echo Username: user
  echo Password: $(kubectl get secret --namespace default redmine -o jsonpath="{.data.redmine-password}" | base64 --decode)

起動するのに3分くらいかかりました。

$ kubectl get deployment
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
redmine   1/1     1            1           4m44s
$ kubectl get svc
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes        ClusterIP      10.96.0.1       <none>        443/TCP        12d
redmine           LoadBalancer   10.110.55.117   localhost     80:31979/TCP   3m15s
redmine-mariadb   ClusterIP      10.97.11.188    <none>        3306/TCP       3m15s

パスワード設定

$ export SERVICE_IP=$(kubectl get svc --namespace default--template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
$ echo "Redmine URL: http://$SERVICE_IP/"
Redmine URL: http://localhost/
$ echo Password: $(kubectl get secret --namespace default redmine -o jsonpath="{.data.redmine-password}" | base64 --decode)
Password: XXXXXXXXXX

localhost:80にアクセスするとGUIが表示されます。User:userPassword:XXXXXXXXXXでログインできます。

プラグインのインストール

pod名を確認し、podにログインします。

$ kubectl get pod
NAME                       READY   STATUS    RESTARTS   AGE
redmine-749bcf6778-rlccb   1/1     Running   1          27m
redmine-mariadb-0          1/1     Running   0          27m
$ kubectl exec -it redmine-749bcf6778-rlccb /bin/bash

プラグインをインストールするディレクトリに移動します。

# cd /opt/bitnami/redmine/plugins
# ls
README
# cat README 
Put your Redmine plugins here.

インストールできるプラグインはPlugins Directoryで一覧を見ることが出来ます。
今回はIssue Templatesを例にインストールします。

# git clone https://github.com/akiko-pusu/redmine_issue_templates.git
Cloning into 'redmine_issue_templates'...
remote: Enumerating objects: 118, done.
remote: Counting objects: 100% (118/118), done.
remote: Compressing objects: 100% (80/80), done.
remote: Total 6045 (delta 60), reused 81 (delta 38), pack-reused 5927
Receiving objects: 100% (6045/6045), 1.24 MiB | 1.41 MiB/s, done.
Resolving deltas: 100% (3706/3706), done.
# cd ..
# bundle update
# bundle exec rake redmine:plugins:migrate RAILS_ENV=production
== 1 CreateIssueTemplates: migrating ==========================================
...
== 20200418114157 CreateJoinTableGlobalNoteTemplateProject: migrated (0.0095s)

再起動しなくてもOKです(多分)。自分の場合は一回ログアウトして、再ログインしたら[管理]-[プラグイン]画面にプラグインが表示されていました。

スクリーンショット 2020-07-15 2.29.12.png

参考

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