Things of interesting

プログラミングに関する技術ネタの備忘録

Proxy環境下でdocker toolbox + docker-compseを使えるようにする(windows7)

やること

普段はMac+Docker for Macの環境でアプリ開発を行っているのですが、会社内のプロキシネットワーク下+Windows7の環境でDockerを使えるようにする設定です。

Docker Toolboxのインストール

公式サイトからダウンロードしてインストールします。

Docker Toolbox | Docker

Docker Quickstart Terminal というソフトウェアもインストールされるので実行するとターミナルが開きます。以降の作業はこのターミナルで行います。

Dockerプロジェクトの作成(docker-compose)

以下ファイルを作成して適当な作業ディレクトリに配置します。

docker-compose.yml

hello:
  image: hello-world:latest

NOプロキシ設定と動作確認

以下NO_PROXY設定をexportします。小文字が大事!

$ export no_proxy=192.168.99.100

docker-compose up コマンドで hello_1 | Hello from Docker! が表示されたら成功です。

$ cd ${docker-compose.ymlを置いたディレクトリ}
$ docker-compose up

プロキシ設定は既に設定済みの前提なので最終形は以下のようになります。
※自身のプロキシサーバーが設定されていることを確認してください。

$ env | grep proxy 
http_proxy=http://login:plainpassword@proxy.corp.com:8000
https_proxy=http://login:plainpassword@proxy.corp.com:8000
no_proxy=192.168.99.100

はまったところ

DockerToolboxインストール後にターミナルでdocker psを打つと Forbiddenになりました。

$ docker ps
error during connect: Get https://192.168.99.100:2376/v1.33/containers/json: Forbidden

ネットで探すとNO_PROXYの設定でいけるとのことで設定するとうまくいきました。

$ export NO_PROXY=192.168.99.100
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED
STATUS              PORTS               NAMES

しかし、docker-compose は繋がりません。

$ docker-compose --verbose up
(省略)
requests.exceptions.ProxyError: HTTPSConnectionPool(host='192.168.99.100', port=
2376): Max retries exceeded with url: /v1.21/version (Caused by ProxyError('Cann
ot connect to proxy.', error('Tunnel connection failed: 403 Forbidden',)))
Failed to execute script docker-compose

小一時間悩んだ後、小文字にしたらいけました。。。

まとめ

docker toolbox:windows の no_proxy は、dockerコマンドは大文字でもOKですが、docker-composeは小文字じゃないとダメ!
※バージョンに依存する問題なのかもしれません。以下のバージョンを利用する際は参考にしてみてください。

$ docker-compose.exe -v
docker-compose version 1.16.1, build 6d1ac219

$ docker -v
Docker version 17.10.0-ce, build f4ffd25

SpringBoot+Gradleで別rootのプロジェクトへ依存関係をつけたビルド構成にする

やること

開発中のプロダクトで以下のようなgit repository上の構成にしたかったのですが、gradleがサポートしているmulti projectの構成だとうまいやり方がみつからなかったので以下のような方法で対応してみました。

  • A: サーバー共通処理やドメインモデルをまとめたレポジトリ
  • B: 個別の業務処理を行うサーバーのソースを格納したレポジトリでAに依存
  • C: 個別の業務処理を行うサーバーのソースを格納したレポジトリでAに依存

Gradle multi project についてはこちら

以下リンクに公式の説明があります
第57章 マルチプロジェクトのビルド
フラット階層だと以下のような構成になるイメージです。

GitRoot
 |
 |-root
 |   |--build.gradle
 |   |--settings.gradle
 |-appA
 |-appB

やり方

Gradle設定ファイルの編集

以下の構成の前提です。 各プロジェクトのひな形はSPRING INITIALIZRで作っておきます。(ParentProjectは単独で起動させないのでのBoot用Javaソースは削除)

ChildProject
  |--build.gradle
  |--settings.gradle

ParentProject
  |--build.gradle

子プロジェクト側の設定に以下を追加します。

  • settings.gradleに以下記載(なければ新規作成してください)
include ':ParentProject'
project(':ParentProject').projectDir = new File('../ParentProject')
  • build.gradleに以下記載
dependencies {
    compile project(':ParentProject')
}

親プロジェクト側の設定に以下を追加します。

  • build.gradleに以下記載
springBoot {
    mainClass = "org.gradle.sample.Scan"
}

Javaクラスの作成

親プロジェクト側にComponentScanのエントリーポイントになるクラスを用意します。 DI対象のクラスはこの下に配置します。

ParentProject
  |--src.xx
      |--Application.java
      |--service
          |--SampleService.java

Application.java

@ComponentScan
public class Application {
}

SampleService.java

@Component
public class SampleService {
    @PostConstruct
    public void test() {
        System.out.print("DI success!");
    }
}

動作確認

ChildProjectを起動します。

cd ${ChildProjectのルートディレクトリ}
./gradlew bootRun

"DI success!"が表示されれば成功です。

VSCodeでJavaプログラム(SpringBoot)のデバッグ

code.visualstudio.com

少し前までプラグインで用意されていたDebuggerだとSpringBoot/Gradle使ったアプリでうまくデバッグできなかったのですが、MicrosoftからJavaDebuggerがリリースされデバッグできるようになっていました。

ありがたや。

bitcoindからのメッセージをZeroMQ経由(+SpringBoot)で購読する

やること

bitcoindにはJSON-RPC以外にZeroMQを介したメッセージ購読ができるようになっていますが、これをSpringBootアプリケーション上にサンプル実装します。

ZeroMQの実装ライブラリとしてjeromqを使用します。
GitHub - zeromq/jeromq: Pure Java ZeroMQ

公式ドキュメント
bitcoin/zmq.md at master · bitcoin/bitcoin · GitHub

開発環境

  • Windows10
  • Java8

bitcoindのインストールと起動

  1. 公式サイトからインストーラをダウンロードしてインストールします。
    ダウンロード - ビットコイン

  2. 設定ファイル作成して適当なディレクトリに配置します。
    bitcoin.conf

     regtest=1  
     server=1  
     rpcuser=username
     rpcpassword=password
     zmqpubrawblock=tcp://127.0.0.1:28332
     zmqpubhashblock=tcp://127.0.0.1:28332
     zmqpubrawtx=tcp://127.0.0.1:28332
     zmqpubhashtx=tcp://127.0.0.1:28332
    

    実装の確認をするだけなので regtest 環境で起動させます。

  3. bitcoindを起動します。

    $ cd ${bitcoin intall dir}/daemon
    $ ./bitcoind.exe -conf=/path/to/bitcoin.conf
    

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

Spirng Initializr等で作成したテンプレートプロジェクトにjeromqライブラリを追加します。

build.gradle

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile group: 'org.zeromq', name: 'jeromq', version: '0.4.2'
}

zmqクライアント部分は公式のpython実装を参考に作成します。
https://github.com/bitcoin/bitcoin/tree/master/contrib/zmq

購読対象のメッセージを設定しないと受信できないので、追加する場合はsubscribeメソッドを呼んでください(コメントアウトの部分)。
bitcoindは以下4種類のメッセージをサポートしています。

socket.subscribe("hashtx"); // transactionのハッシュ値
socket.subscribe("hashblock"); // blockのハッシュ値
socket.subscribe("rawblock"); // blockの生データ
socket.subscribe("rawtx");  // transactionの生データ

BitcondZmqClient.java

@Slf4j
@Component
public class BitcondZmqClient {
    private static final String url = "tcp://127.0.0.1:28332";

    @PostConstruct
    private void init() {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Runnable runner = this::connectZmqServer;
        executor.execute(runner);
    }

    private void connectZmqServer() {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket socket = context.socket(ZMQ.SUB);

        socket.connect(url);
        socket.subscribe("rawblock");
        // socket.subscribe("hashblock");
        // socket.subscribe("rawtx");
        // socket.subscribe("hashtx");
        log.info("connect to " + url);

        while (!Thread.currentThread().isInterrupted()) {
            String topic = socket.recvStr();
            log.info("client - topic:" + topic);
            if (socket.hasReceiveMore()) {
                // byte[] body = socket.recv();
                String body = socket.recvStr();
                switch (topic) {
                    case "rawblock":
                        log.info("client - body:" + body);
                        break;
                    default:
                        break;
                    }
            }
            if (socket.hasReceiveMore()) {
                String sequence = socket.recvStr();
                log.info("client - sequence:" + sequence);
            }
        }
        socket.close();
        context.term();
    }

}

ComponentScan対象になるように@SpringBootApplication配下のディレクトリに配置してください。
メインの業務処理とは分けられるように別スレッドでZmqClientを起動しています。

ZeroMQ経由のメッセージ購読確認

  1. SpringBootアプリケーションを起動します。

  2. regtest環境とbitcoin-cliコマンドを使ってブロックをマイニングします。

     $ cd ${bitcoin intall dir}/daemon
     $ ./bitcoin-cli.exe -conf=/path/to/bitcoin.conf generate 1
    
  3. ログにブロックの情報が表示されれば成功です。

memo

トランザクション情報もメッセージを変えることで購読できるので試してみてください。

regtest環境を初期構築した後はマイニング報酬を得るのに100ブロック掘る必要があるので sendtoaddress コマンド実行時にお金が足りないと表示される場合には generate 100 で100ブロックを掘ってみてください。

Werckerを使ってGithubへのreleaseとJarアップロードを自動化する

やること

SpringBootでプロジェクトを開発する際に共通で使う機能などをライブラリ化して参照できるようにしたい。

プロジェクトの依存関係で解決してもいいですが、共通ライブラリが肥大化するような場合だとローカルビルドが面倒なので今回はJar化したものを配布することにします。

Wercker設定ファイルの編集

前回の続きから

hnak.hatenablog.jp

公式ドキュメントにリリースとアップロードの手順があるのでこれを参考にします。

GitHub - wercker/step-github-create-release: wercker step for deploying to GitHub releases

GitHub - wercker/step-github-upload-asset: A wercker step for adding an asset to a GitHub release.

wercker.yml に以下のdeployワークフローを追加します。

deploy:
  steps:
    - script:
        name: get version
        code: |
          VERSION=`grep ^version build.gradle | awk -F\' '{print $2}'`
    - github-create-release:
        token: $GITHUB_TOKEN
        tag: $VERSION
    - github-upload-asset:
        token: $GITHUB_TOKEN
        file: build/libs/appname-${VERSION}.jar
        content-type: application/zip

Jarの名前はbuild.gradleから設定できるので以下をbuild.gradleに追加してください。
versionは適当にbuild.gradleからgrepしてます。

jar.baseName = 'appname'
version = '0.0.1-SNAPSHOT'

Weckerの設定変更

Pipeline追加

WebページへログインしワークフローのメニューからAdd New Pipelineを選択します。 f:id:hnakanoya:20171027103849p:plain

YML Pipeline nameにwercker.ymlで設定したdeployを入力してcreateを押します。 f:id:hnakanoya:20171027104104p:plain

ワークフローに先ほど追加したdeployを繋げます。 f:id:hnakanoya:20171027104243p:plain

Githubトークンの取得

WerckerからGithubへpushするのにGithubトークンが必要なので作成します。

GithubのWebページへ行って以下のリンクで設定画面を開きます。
Settings→Developer settings→Personal access tokens f:id:hnakanoya:20171027105701p:plain

Generate new tokenを選択して、新規トークンを作成します。
今回はpublicレポジトリを使うのでpublic_repoのみにチェックをいれます。 f:id:hnakanoya:20171027105742p:plain

トークンが生成されたらコピーしておきます。 f:id:hnakanoya:20171027105906p:plain

Githubトークン設定

Environmentのページを開いてValueに先ほどコピーしたトークンを設定します。
keyはGITHUB_TOKENとしてください。

Wercker実行確認

最初に設定したwercker.ymlをPUSHすると自動的にweckerも実行されます。

deployのpipelineが正常に実行され、githubのreleasesにJarがアップロードされていれば成功です。

参照側プロジェクトの設定

build.gradleに以下を追加

def urlFile = { url, name ->
    File file = new File("$buildDir/libs/${name}.jar")
    file.parentFile.mkdirs()
    if (!file.exists()) {
        new URL(url).withInputStream { downloadStream ->
            file.withOutputStream { fileOut ->
                fileOut << downloadStream
            }
        }
    }
    files(file.absolutePath)
}
dependencies {
    compile urlFile('https://github.com/nakanoya150151/siw/releases/download/0.0.1/siw-0.0.1-SNAPSHOT.jar?raw=true', 'siw')
}

とりあえずこれで連携できますが、Mavenレポジトリを立てたほうがきれいですね。。。
Jar化も含めてやってくれるWebサービスもあるようです。

www.jitpack.io

上のjitpackを紹介していた記事
JitPack.ioでGitHub上のJavaプロジェクトを簡単にライブラリとして参照する · tehepero note(・ω<) 2.0

課題

毎回のPUSHでreleasesを作成しようとしてしまうので制御できるように修正予定です。。。

Springboot+GradleのアプリケーションをWercker/Codecov連携する

やること

SpringBoot+Gradleで作ったアプリケーションをWercker/Codecovと連携させてCI環境を作ります。

  • Wercker : ビルド・デプロイ環境を提供するCIサービスです。類似のサービスにCircleCIやTravisCIがありますが無料で使える枠が大きいのとDockerイメージが使えるので今回はこれをビルド環境に使ってみます。
    http://www.wercker.com/

  • Codecov : テストカバレッジを計測するCIサービスです。テストレポート作成にはJacocoというライブラリを使います。類似のサービスにはcoverallsがありますが、codecovを勧める記事が多かったので今回はこれを使ってみました。
    https://codecov.io/gh

実行環境

  • Windows10
  • Java8

Wercker環境構築

準備

SPRING INITIALIZR等を使って適当なSpringBootアプリケーションを作成します。
https://start.spring.io/
レポジトリを作成したらGithub上にPushしておきます。

アカウント作成

Githubアカウントを使ってwerckerアカウントを作成します。
https://app.wercker.com/sessions/new
先ほどのレポジトリを選択してください。

定義ファイル作成

公式ドキュメントのガイドに従ってwercker.ymlを作成します(プロジェクトルートに配置)。
http://devcenter.wercker.com/docs/quickstarts/building/java

wercker.yml

# docker box definition
box:
  id: openjdk
  ports:
    - "8080"

# Build definition
build:
  # The steps that will be executed on build
  steps:
    # A step that executes `gradle build` command
    - script:
        name: run gradle
        code: |
          chmod +x ./gradlew
          ./gradlew --full-stacktrace -q --project-cache-dir=$WERCKER_CACHE_DIR build

chmodを追加していますが、なしで実行したところPermissionErrorが発生したので追加しています。 f:id:hnakanoya:20171017184300p:plain

wercker.ymlをPUSHするとwercker上でビルドが実行されます。 f:id:hnakanoya:20171017190100p:plain

Codecov環境構築

準備

Jacocoを使用するのでbuild.gradleに以下の記述を追加します。 デフォルトのjacocoTestReportタスクだとレポートがhtml形式なのでタスクを定義してXML形式で出力します。

apply plugin: 'jacoco'
task codeCoverageReport(type: JacocoReport) {
    executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
    reports {
        xml.enabled true
        xml.destination "${buildDir}/reports/jacoco/report.xml"
        html.enabled false
        csv.enabled false
    }
    classDirectories = fileTree(dir: './build/classes/main')
    sourceDirectories = files('src/main/java')
}

Codecovアカウント作成

Werckerと同じようにGithubアカウントを使用してアカウント作成・レポジトリ連携を設定します。 完了画面でトークンが表示されるのでコピーしておいてください。 f:id:hnakanoya:20171017191700p:plain 画面を閉じてしまった場合はレポジトリの画面からSettingsへ飛ぶと確認できたはずです。

定義ファイル更新

codecov用の定義を追加します。CODECOV_TOKENにはトークンを設定してください。

# docker box definition
box:
  id: openjdk
  ports:
    - "8080"

# defining the dev pipeline
dev:
  steps:
    # A step that executes `gradle bootRun` command
    - script:
      name: run gradle
      code: |
        ./gradlew bootRun

# Build definition
build:
  # The steps that will be executed on build
  steps:
    # A step that executes `gradle build` command
    - script:
        name: run gradle
        code: |
          chmod +x ./gradlew
          ./gradlew --full-stacktrace -q --project-cache-dir=$WERCKER_CACHE_DIR build
          ./gradlew codeCoverageReport
    - script:
        name: post coverage to codecov
        code: |
          CODECOV_TOKEN={ここにトークンをペースト}
          bash <(curl -s https://codecov.io/bash) -t $CODECOV_TOKEN

wercker.ymlをPUSHするとwercker上でビルドが実行され、レポートがcodecovに連携されカバレッジレポートが表示されます。

以上です。