drone.io でARMビルド用agentを設定する

この記事は drone.io Advent Calendar 2017 - Adventar の24日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

f:id:ashphy22:20171223211410p:plain

droneでのテストは通常x86、x64で動かしていると思いますが、場合によっては異なるアーキテクチャでのテストを実行したい場合もあるかもしれません。

droneではv0.8からarmおよびarm64がサポートされるようになりました。ARMはRaspberry Piや、Androidなどの携帯端末、IoT機器などでも用いられていますので、こういった組み込み機器の開発にもCI/CDを導入できるということは大きなメリットがあります。

本日はdroneでarmを利用する方法をご紹介します。

ARM版のAgentを導入する

ARMがサポートされましたが、drone serverはx64で構いません。というかx64のイメージしか用意されていませんので、すでに立てているserverがあればそれを利用してください。

ARMでビルドしたい場合はARMの環境でagentを立てます。Raspberry Piにdockerを入れてagentにするのがお手軽ですが、qemuでエミュレーションすることも可能です。 agentはarm, arm64用のイメージが提供されています。tagにlinux-arm, linux-arm64 がありますので指定してください。環境変数DOCKER_ARCH=armを追加してください。

version: '2'

services:
  drone-agent:
    image: drone/agent:linux-arm
    command: agent
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_SERVER=127.0.0.1:9000
      - DRONE_SECRET=00e40bfb287a0a553c80297a
      - DOCKER_ARCH=arm

ARM版の.drone.yml

このままテストを実行するとアーキテクチャがわからず、droneがどのagentで実行したらいいかわからなくなります。 .drone.ymlplatform を追加することでこれを指定することができます。Agentは自動で選ばれるため異なるプラットフォームのagentをひとつのserverに混在させても構いません。

platform: linux/arm

pipeline:
  build:
    image: arm32v7/busybox:latest
    commands:
      - uname -a

ビルドを走らせると...

+ uname -a
Linux 39737a2a3d25 4.13.9-300.fc27.armv7hl #1 SMP Mon Oct 23 15:02:20 UTC 2017 armv7l GNU/Linux

armv7で動きました!

ARMのテストにはARM用のイメージが必要ですが、いくつか公式のイメージが用意されていますのでこれをベースにカスタマイズするとよいでしょう。

github.com

対応版のプラグインについて

ARMで実行するときの注意としてARM対応版のプラグインを利用しなければならないことがあります。公式のプラグインはそこそこ対応されていますが、自作のプラグインはビルドし直さなければならないかもしれません。

drone.io でKitchenCIを使ってインフラをテストする

この記事は drone.io Advent Calendar 2017 - Adventar の23日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

KitchenCIVMを立てて、Chefでプロビジョニングし、InSpecや、SeverSpecでテストする一連の流れを自動化するものです。

KitchenCIは通常Vagrantで操作しますが、kitchen-docker pluginを利用すると代わりにdockerを利用することができます。

github.com

これを利用し、drone上でdocker in dockerをすることでKitchenCIによるインフラ自動テストを設定してみましょう。

docker in docker を設定する

droneはdockerを利用してコンテナ上でテストを実行するものです。KitchenCIはコンテナを作ってその中にプロビジョニングを実施しますので、drone上でKitchenCIを動かすとコンテナの中にコンテナを立てることになります。これを docker in docker(dind) と呼びます。

dindをするには小細工が必要なのですが、公式イメージでdind用の docker イメージが提供されていますのでこれを利用します。 tagにdindと入っているものを利用します。

.drone.ymlのservicesにdindを設定します。ポート2375にDocker Portが上がってきます。 このdindイメージを利用するにはprivilegedフラグの設定が必要ですが、これはプロジェククトのTrustedフラグを有効にする必要があります。

services:
  docker:
    image: docker:stable-dind
    privileged: true
    command: [ "--storage-driver=vfs", "--tls=false" ]

他のコンテナから利用する際はDOCKER_HOSTを以下のように設定し、servicesで設定したdind用のポートに向けます。

pipeline:
  build:
    image: docker:latest
    environment:
      - DOCKER_HOST=tcp://docker:2375
    commands:
      - docker info

kitchen-dockerを設定する

.kitchen.yml をdockerを利用するように変更します。socketを指定してdindを利用します。

---
driver:
  name: docker
  socket: tcp://docker:2375

そこそこ時間はかかりますが、これでインフラのテストが自動化できます。

dind を Trustedプロジェクトでなくても利用できるようにする

Trustedフラグは管理者しか設定するしかないのですが、これは少々面倒です。 信頼できる利用者のみの環境であればserver設定時に環境変数DRONE_ESCALATEdockerを設定しておくとTrustedプロジェクトでなくても自動的にprivilegedフラグが有効になります。

drone.io のProject Visibilityとは

この記事は drone.io Advent Calendar 2017 - Adventar の22日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

f:id:ashphy22:20171219223332p:plain

バージョンアップして突然現れたProject Visibilityの項目。どんな意味があるのでしょうか?

これはそれぞれのプロジェクトの表示範囲を示しています。

可視性 説明
Public 誰でも表示できる
Private 権限のある人しか表示できない
Internal ログイン済みのユーザであれば誰でも表示できる

リポジトリがpublicであれば、droneでもPublic、privateであれば Private がデフォルトに設定されます。

前のバージョンにあったPUBLIC_MODEの設定値はなくなっていますので注意してください。

drone.io でsubmoduleをcloneするときにプロトコルを強制的に変更する

この記事は drone.io Advent Calendar 2017 - Adventar の21日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

submoduleを含むリポジトリ

submoduleを含むリポジトリでテストした場合以下のようなエラーが表示されることがあります。

+ git init
Initialized empty Git repository in /drone/src/drone.example.com/hoge/drone-test/.git/
+ git remote add origin https://drone.example.com/hoge/drone-test.git
+ git fetch --no-tags origin +refs/heads/master:
From https://drone.example.com/hoge/drone-test
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> origin/master
+ git reset --hard -q 91b0c8f8ea9a479cab3708ca40273df8
+ git submodule update --init --recursive
Submodule 'subproject' (git@drone.example.com:ci/subproject.git) registered for path 'subproject'
Cloning into '/drone/src/drone.example.com/hoge/drone-test/subproject'...
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
fatal: clone of 'git@drone.example.com:ci/subproject.git' into submodule path '/drone/src/drone.example.com/hoge/drone-test/subproject' failed
Failed to clone 'subproject'. Retry scheduled
Cloning into '/drone/src/drone.example.com/hoge/drone-test/subproject'...
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
fatal: clone of 'git@drone.example.com:ci/subproject.git' into submodule path '/drone/src/drone.example.com/hoge/drone-test/subproject' failed
Failed to clone 'subproject' a second time, aborting
exit status 1

これはsubmoduleをgitプロトコルで落としてこようとしてsshするための鍵が設定されていないため取得に失敗しエラーになっています。

通常submodule addする場合はhttpsを指定すれば十分だと思いますが、drone側で強制的にhttps指定に書き換えることができます。

.drone.yml に新しくcloneの項目を作ってください。 submodule_override を指定することで自由に書き換えることができます。

clone:
  git:
    image: plugins/git
    recursive: true
    submodule_override:
      test-kitchen: https://drone.example.com/ci/subproject.git

上記を設定することで以下のように

+ git config --global submodule.test-kitchen.url https://drone.example.com/ci/subproject.git

submoduleが上書きされ、cloneすることができます。

drone.io APIドキュメント

この記事は drone.io Advent Calendar 2017 - Adventar20日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

drone.io にはそこそこしっかりしたAPIがあるのですが、http://docs.drone.io/api-overview/ をみてがっかりしたかもしれません。

暫定的なAPIドキュメントを起こしておいたので公式サイトが整備されるまではご利用ください。

APIキーは https://drone.exmaple.com/account/token から取得してください。

Drone API

Current User
  • GET /api/user
    • アクセスしているユーザの情報
{
  "id": 1,
  "login": "ashphy",
  "email": "ashphy@ashphy.com",
  "avatar_url": "https://avatars3.githubusercontent.com/u/413896?",
  "active": false,
  "synced": 1513512678,
  "admin": true
}
Users (admin専用)
  • GET /api/users
    • ユーザ一覧
  • POST /api/users
    • ユーザ作成
  • GET /:login
    • 特定のユーザ情報
  • PATCH /:login
    • ユーザ情報の更新
  • DELETE /:login
    • ユーザの削除
Repos
{
  "id": 1,
  "owner": "hoge",
  "name": "drone-test",
  "full_name": "hoge/drone-test",
  "avatar_url": "https://drone.example.com/avatars/u/9999?",
  "link_url": "https://drone.example.com/hoge/drone-test",
  "scm": "git",
  "clone_url": "https://drone.example.com/hoge/drone-test.git",
  "default_branch": "master",
  "timeout": 60,
  "visibility": "public",
  "private": false,
  "trusted": true,
  "gated": true,
  "active": true,
  "allow_pr": true,
  "allow_push": true,
  "allow_deploys": true,
  "allow_tags": true,
  "last_build": 999,
  "config_file": ".drone.yml"
}
Builds
  • GET /api/repos/:owner/:name/builds
    • ビルドの一覧
[{
  "id": 1,
  "number": 1,
  "parent": 0,
  "event": "push",
  "status": "success",
  "error": "",
  "enqueued_at": 1513007740,
  "created_at": 1513007740,
  "started_at": 1513007740,
  "finished_at": 1513007754,
  "deploy_to": "",
  "commit": "91b0c8f8ea9a479cab3708ca40273df8",
  "branch": "master",
  "ref": "refs/heads/master",
  "refspec": "",
  "remote": "https://drone.example.com/ci/drone-test.git",
  "title": "",
  "message": "commit massage",
  "timestamp": 0,
  "sender": "ashphy",
  "author": "ashphy",
  "author_avatar": "https://drone.example.com/avatars/u/17?",
  "author_email": "ashphy@ashphy.com",
  "link_url": "https://drone.example.com/hoge/drone-test/commit/91b0c8f8ea9a479cab3708ca40273df8",
  "signed": false,
  "verified": true,
  "reviewed_by": "",
  "reviewed_at": 0
}]
  • GET /api/repos/:owner/:name/builds/:number
    • 特定のビルド情報
  • POST /api/repos/:owner/:name/builds/:number
    • ビルドの再実行
  • DELETE /api/repos/:owner/:name/builds/:number
    • admin専用 実行中のまま終わらなくなってしまったビルドのゾンビプロセスを殺す。
Logs
  • GET /api/repos/:owner/:name/logs/:number/:pid
    • ビルド中の出力
[{
  "proc": "rebuild-cache",
  "pos": 57,
  "out": "time=\"2017-12-11T15:55:49Z\" level=debug msg=\"Directory found at vendor/bundle"\n"
}]
  • GET /api/repos/:owner/:name/logs/:number/:pid/:proc
    • プロセスごとの出力
Files

なんか成果物を保存できる機能があるらしい? Enterprise専用かもしれない。

  • GET /api/repos/:owner/:name/files/:number
  • GET /api/repos/:owner/:name/files/:number/:proc/*file
Secrets
  • GET /api/repos/:owner/:name/secrets
    • secretの一覧
  {
    "id": 9999,
    "name": "downstream_token",
    "image": null,
    "event": [
      "push",
      "tag",
      "deployment"
    ]
  }
]
  • POST /api/repos/:owner/:name/secrets
    • secretの設定
  • GET /api/repos/:owner/:name/secrets/:secret
    • secret情報
  • PATCH /api/repos/:owner/:name/secrets/:secret
    • secret更新
  • DELETE /api/repos/:owner/:name/secrets/:secret
    • secret削除
Registry
  • GET /api/repos/:owner/:name/registry
  • POST /api/repos/:owner/:name/registry
  • GET /api/repos/:owner/:name/registry/:registry
  • PATCH /api/repos/:owner/:name/registry/:registry
  • DELETE /api/repos/:owner/:name/registry/:registry
Repo設定変更 (admin専用)
  • PATCH /api/repos/:owner/:name
  • DELETE /api/repos/:owner/:name
  • POST /api/repos/:owner/:name/chown
  • POST /api/repos/:owner/:name/repair
    • DeactivateしてからActivateし直す。リポジトリ側との連携がおかしくなった場合に直せるかもしれない。
  • POST /api/repos/:owner/:name/move
Gated Builds

おそらくGated Build用。ただGated Buildがうまく動かないので未検証。  - POST /api/repos/:owner/:name/builds/:number/approve - POST /api/repos/:owner/:name/builds/:number/decline

Job
  • DELETE /api/repos/:owner/:name/builds/:number/:job
    • 実行中ビルドのキャンセル
Badges
  • GET /api/badges/:owner/:name/status.svg
    • ビルドステータス表示用のバッジ
  • GET /api/badges/:owner/:name/cc.xml
    • CCMenu用の設定ファイル
Queue (admin専用)
  • GET /api/info/queue
    • ビルドキューの情報
{
  "pending": null,
  "running": null,
  "stats": {
    "worker_count": 4,
    "pending_count": 0,
    "running_count": 0,
    "completed_count": 0
  }
}
Builds (admin専用)
  • GET /api/builds
    • 実行中のビルド情報
[
  {
    "owner": "hoge",
    "name": "drone-test",
    "full_name": "hoge/drone-test",
    "number": 9999,
    "event": "push",
    "status": "running",
    "created_at": 1513595666,
    "started_at": 1513595666,
    "commit": "91b0c8f8ea9a479cab3708ca40273df8",
    "branch": "master",
    "ref": "refs/heads/master",
    "remote": "https://drone.example.com/foo/bar.git",
    "message": "commit log",
    "author": "ashphy",
    "author_avatar": "https://drone.example.com/avatars/u/9999?",
    "author_email": "ashphy@ashphy.com"
  }
]

リクエストパラメータは要望があれば調べます...

drone.io で1つのagentで複数のビルドを同時に実行する

この記事は drone.io Advent Calendar 2017 - Adventar の19日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

droneのビルドの同時実行数はバージョンが上がるたびに様々に変化してきました。

v0.3

drone.toml の worker に好きなだけ docker.sock を設定します。

[worker]
nodes=[
  "unix:///var/run/docker.sock",
  "unix:///var/run/docker.sock",
]

v0.4

Web UIから管理者アカウントで「Nodes」から好きなだけ設定します。

v0.5

agentに環境変数DOCKER_MAX_PROCSで設定します。デフォルトは2です。

  drone-agent:
    image: drone/agent:0.8.0
    command: agent
    restart: always
    ports:
      - 3000:3000
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_SERVER=drone-server:9000
      - DRONE_SECRET=00e40bfb287a0a553c80297a
      - DRONE_HEALTHCHECK=true
      - DOCKER_MAX_PROCS=4

v0.7 以上

どのバージョンからかわからないのですが、 DOCKER_MAX_PROCSDRONE_MAX_PROCS になっています。デフォルトは1と、これも変更になっています。 実はこの変数はドキュメントにありません。1つのホストに複数のAgentを立てていた人はこの設定値を使うようにするほうが良いでしょう。

drone.io でagentごとに特定のビルドのみ実行するフィルタを設定する

この記事は drone.io Advent Calendar 2017 - Adventar の17日目の記事です。

drone.io は Goで作られたオープンソースのCD (Continuous Delivery)環境です。

github.com

特定ビルドを特定のagentに処理させたいということがあるかと思います。例えば重いテストを専用のagentに隔離させるであるとか、Trustedフラグが有効になっていて専用のデバイスが接続されているagentで実行させたいとかです。

agentには環境変数DRONE_FILTERを設定することでフィルタを設定することができます。

environment:
  - DRONE_FILTER=repo IN ('drone', 'docker', 'foo', 'bar')

SQLっぽい独自の構文で条件は記述することができます。

DRONE_FILTER=repo == 'drone'

演算子==, !=, <, <=, >, >=, AND, OR, NOT, IN, GLOB, REGEXP, BETWEEN が利用できます。

DRONE_FILTER=repo GLOB 'hoge/some-prefix-*'
DRONE_FILTER=repo NOT GLOB 'hoge/some-prefix-*'

と、ここまで書いてなんですが、 drone v0.8ではfilterが動作しないバグがあります ので次のバージョンをお待ちください。