Dockerのビルドのレスポンスを整形して出力する

F#でDockerイメージをビルドする - locabloではレスポンスをそのまま出力していたのでパースして出力できるようにしました。

F#っぽいコードにならなかったのが残念…

let formatResponse line =
    let root = JObject.Parse(line)
    let hasProp key =
        root.Properties()
        |> Seq.map (fun x -> x.Name)
        |> Seq.contains key
    let valueOf (key : string) = string root.[key]

    let newLine = System.Environment.NewLine
    let builder = new System.Text.StringBuilder()
    if hasProp "id" then
        builder.AppendFormat("{0}: ", valueOf "id") |> ignore
    if hasProp "progress" then
        builder.AppendFormat("{0} {1}{2}", valueOf "status", valueOf "progress", newLine) |> ignore
    elif hasProp "stream" then
        builder.AppendFormat("{0}", valueOf "stream") |> ignore
    elif hasProp "status" then
        builder.AppendFormat("{0}{1}", valueOf "status", newLine) |> ignore
    else
        builder.Append(line) |> ignore

    builder.ToString()

let outputBuildResponse (responseStream : Stream) : unit =
    seq {
        use reader = new StreamReader(responseStream, Text.Encoding.UTF8)
        while not reader.EndOfStream do
            yield reader.ReadLine()
    }
    |> Seq.map formatResponse
    |> Seq.iter (printf "%s")

前回の記事の出力がこれで、

{"stream":"Step 1/2 : FROM busybox\n"}
{"status":"Pulling from library/busybox","id":"latest"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":16384,"total":699311},"progress":"[=\u003e                                                 ] 16.38 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":260087,"total":699311},"progress":"[==================\u003e                                ] 260.1 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":424839,"total":699311},"progress":"[==============================\u003e                    ] 424.8 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":490375,"total":699311},"progress":"[===================================\u003e               ] 490.4 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":539527,"total":699311},"progress":"[======================================\u003e            ] 539.5 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":621447,"total":699311},"progress":"[============================================\u003e      ] 621.4 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1cae461a1479"}
{"status":"Download complete","progressDetail":{},"id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":32768,"total":699311},"progress":"[==\u003e                                                ] 32.77 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Pull complete","progressDetail":{},"id":"1cae461a1479"}
{"status":"Digest: sha256:c79345819a6882c31b41bc771d9a94fc52872fa651b36771fbe0c8461d7ee558"}
{"status":"Status: Downloaded newer image for busybox:latest"}
{"stream":" ---\u003e c75bebcdd211\n"}
{"stream":"Step 2/2 : RUN ls\n"}
{"stream":" ---\u003e Running in 74350655bd58\n"}
{"stream":"bin\ndev\netc\nhome\nproc\nroot\nsys\ntmp\nusr\nvar\n"}
{"stream":" ---\u003e 7fc898365619\n"}
{"stream":"Removing intermediate container 74350655bd58\n"}
{"stream":"Successfully built 7fc898365619\n"}

formatResponse関数を使ってパースして出力するとこうなります。

Step 1/2 : FROM busybox
latest: Pulling from library/busybox
1cae461a1479: Pulling fs layer
1cae461a1479: Downloading [=>                                                 ] 15.93 kB/699.3 kB
1cae461a1479: Downloading [===============>                                   ] 211.8 kB/699.3 kB
1cae461a1479: Downloading [=======================>                           ] 326.5 kB/699.3 kB
1cae461a1479: Downloading [=========================>                         ] 359.3 kB/699.3 kB
1cae461a1479: Downloading [============================>                      ] 392.1 kB/699.3 kB
1cae461a1479: Downloading [==============================>                    ] 424.8 kB/699.3 kB
1cae461a1479: Downloading [=================================>                 ]   474 kB/699.3 kB
1cae461a1479: Downloading [=====================================>             ] 523.1 kB/699.3 kB
1cae461a1479: Downloading [========================================>          ] 572.3 kB/699.3 kB
1cae461a1479: Downloading [============================================>      ] 621.4 kB/699.3 kB
1cae461a1479: Downloading [==============================================>    ] 654.2 kB/699.3 kB
1cae461a1479: Downloading [=================================================> ]   687 kB/699.3 kB
1cae461a1479: Downloading [==================================================>] 699.3 kB/699.3 kB
1cae461a1479: Verifying Checksum
1cae461a1479: Download complete
1cae461a1479: Extracting [==>                                                ] 32.77 kB/699.3 kB
1cae461a1479: Extracting [=======================================>           ] 557.1 kB/699.3 kB
1cae461a1479: Extracting [==================================================>] 699.3 kB/699.3 kB
1cae461a1479: Extracting [==================================================>] 699.3 kB/699.3 kB
1cae461a1479: Pull complete
Digest: sha256:c79345819a6882c31b41bc771d9a94fc52872fa651b36771fbe0c8461d7ee558
Status: Downloaded newer image for busybox:latest
 ---> c75bebcdd211
Step 2/2 : RUN ls
 ---> Running in 5c8584208f21
bin
dev
etc
home
proc
root
sys
tmp
usr
var
 ---> b95a8e54a8f1
Removing intermediate container 5c8584208f21
Successfully built b95a8e54a8f1

F#でDockerイメージをビルドする

概要

DockerにはDocker Engine APIがあり、HTTPを使ってアクセスできます。

Docker Engine API and SDKs | Docker Documentation

また、各言語向けにSDKがあります。

SDKs for Docker Engine API | Docker Documentation

今回はDocker for Windowsを使ってF#からDockerイメージのビルドを実行します。

環境は次の通りです。

Docker for Windowsの設定

Docker for WindowsをインストールしてDockerが使えるようになったら、ローカルのDocker EngineにアクセスするためにTLSを無効にします。有効な状態で使うこともできると思いますが今回はそこまで調べられてませんので、無効にした状態でアクセスします。

やり方は、まずタスクトレイのDockerアイコンを右クリックして(なければまずはDockerを起動します)、"Settings…“をクリックします。

f:id:LocaQ:20170606234230p:plain:w250

ウィンドウが開いたら、左のエリアで"General"を選び、右側のエリアの"Expose daemon on tcp://localhost:2375 without TLS"のチェックを外します。

f:id:LocaQ:20170606235242p:plain:w500

Dockerイメージのビルド

Docker.DotNet

.NET向けのSDKとしてDocker.DotNetがあるのでこれを使います。

GitHub - Microsoft/Docker.DotNet: .NET (C#) Client Library for Docker API

ただし、記事作成時点でNuGetで公開されているバージョン(2.124.3)にはまだDockerイメージをビルドするAPIは含まれていません。GithubのmasterブランチにはAPIが存在するので、自分でライブラリをビルドして使います。

ビルドは簡単で、Visual Studioで開いて"Docker.DotNet"というプロジェクトをビルドするだけです。他のプロジェクトは今回は使いません。

ビルド後、自分のプロジェクトの参照に、

を追加します。

コード

DockerイメージのビルドをするF#のコードです。

let buildDockerImage (tar : byte[]) : Stream =
    use client = (new DockerClientConfiguration(new Uri("http://localhost:2375"))).CreateClient()
    use dockerfileTarStream = new MemoryStream(tar)

    let imageBuildParams = new ImageBuildParameters()
    imageBuildParams.Tags <- new List<string>()
    imageBuildParams.Tags.Add("build-test")

    client.Images.BuildImageFromDockerfileAsync(dockerfileTarStream, imageBuildParams)
    |> Async.AwaitTask
    |> Async.RunSynchronously

let outputBuildResponse (responseStream : Stream) : unit =
    seq {
        use reader = new StreamReader(responseStream, Text.Encoding.UTF8)
        while not reader.EndOfStream do
            yield reader.ReadLine()
    }
    |> Seq.iter (printfn "%s")

buildDockerImage関数はdockerのコマンドだと

docker build -t build-test .

と同じことをします。

buildDockerImage関数ではまずDockerClientオブジェクトを作成します。

use client = (new DockerClientConfiguration(new Uri("http://localhost:2375"))).CreateClient()

作成するときのURIには上の"Docker for Windowsの設定"でチェックをつけたところに書かれていた"http://localhost:2375"を指定します。

次にビルドパラメータ(ImageBuildParameters)のオブジェクトを作成します。

let imageBuildParams = new ImageBuildParameters()
imageBuildParams.Tags <- new List<string>()
imageBuildParams.Tags.Add("build-test")

imageBuildParams.Tagsにイメージ名である"build-test"を指定します。これは、"docker build"コマンドの"-t"オプションに指定する値に該当します。

最後にDocker Engine APIにDockerイメージのビルドをリクエストします。

client.Images.BuildImageFromDockerfileAsync(dockerfileTarStream, imageBuildParams)
|> Async.AwaitTask
|> Async.RunSynchronously

1番目の引数はDockerfileをTARデータを持つSystem.Streamオブジェクトで、この後でデータの作成方法を説明します。2番目の引数はビルドパラメータです。

buildDockerImage関数を実行してリクエストに成功するとHTTPレスポンスのデータを読み取れるStreamオブジェクトが返ります。

outputBuildResponse関数はbuildDockerImage関数で返すHTTPレスポンスのStreamオブジェクトを読み込んで標準出力に出力する関数です。F#のシーケンスとyieldを使って1行受信するごとに出力します。

TARデータの作成

Docker Engine APIEngine API v1.24 | Docker Documentation)の"BUILD IMAGE FROM A DOCKERFILE"の項目を読むと、リクエスト例が

POST /v1.24/build HTTP/1.1

{{ TAR STREAM }}

となっています。先ほどのbuildDockerImage関数の引数"tar"にTARフォーマットのデータを表すバイト配列を渡すと、Docker.DotNetのBuildImageFromDockerfileAsync()の第一引数に渡されてこのHTTPリクエストのボディになります。

SharpZipLib

TARデータを作るのに今回はSharpZipLibというライブラリを使います。バージョンは0.86.0です。

GitHub - icsharpcode/SharpZipLib: #ziplib is a Zip, GZip, Tar and BZip2 library written entirely in C# for the .NET platform.

これはNuGetでプロジェクトに追加します。

コード

Dockerfileを読み込んでTARデータを作る関数のコードです。

/// "dockerfileDirPath"はDockerfileがあるディレクトリのパス。
let createDockerfileTar (dockerfileDirPath : string) : byte[] =
    let generateTar (outputStream : Stream) : unit =
        use archive = TarArchive.CreateOutputTarArchive(outputStream)
        let dockerFileEntry = TarEntry.CreateEntryFromFile("Dockerfile")
        archive.WriteEntry(dockerFileEntry, false)

    let currentDirectory = Directory.GetCurrentDirectory()
    try
        use memoryStream = new MemoryStream()

        Directory.SetCurrentDirectory(dockerfileDirPath)
        generateTar memoryStream

        memoryStream.ToArray()
    finally
        Directory.SetCurrentDirectory(currentDirectory)

createDockerfileTar関数はDockerfileを読み込んでTARデータを作成、バイト配列にして返します。

内部のgenerateTar関数でDockerfileを読み込んでTARデータを作っています。TarArchiveやTarEntryがSharpZipLibで定義されている型で、

use archive = TarArchive.CreateOutputTarArchive(outputStream)

で引数に渡されたStreamオブジェクトにデータを書き込むようなアーカイブオブジェクトを作り、

let dockerFileEntry = TarEntry.CreateEntryFromFile("Dockerfile")
archive.WriteEntry(dockerFileEntry, false)

でDockerfileをTARに追加しています。

archiveはDisposeメソッドが呼ばれないとデータの書き込みが行われないようなので、関数にして関数終了時にデータがStreamオブジェクト(outputStream)に書き込まれるようにしています。

また、カレントディレクトリをDockerfileがあるディレクトリにしないとDocker Engine APIにリクエストを投げた時に失敗する(理由はわかりませんが)ので、TARデータの作成前後でカレントディレクトリを変更しています。

実行

アプリケーション全体のコードです。

open Docker.DotNet
open Docker.DotNet.Models
open ICSharpCode.SharpZipLib.Tar
open System
open System.Collections.Generic
open System.IO

let createDockerfileTar (dockerfileDirPath : string) : byte[] =
    let generateTar (outputStream : Stream) : unit =
        use archive = TarArchive.CreateOutputTarArchive(outputStream)
        let dockerFileEntry = TarEntry.CreateEntryFromFile("Dockerfile")
        archive.WriteEntry(dockerFileEntry, false)

    let currentDirectory = Directory.GetCurrentDirectory()
    try
        use memoryStream = new MemoryStream()

        Directory.SetCurrentDirectory(dockerfileDirPath)
        generateTar memoryStream

        memoryStream.ToArray()
    finally
        Directory.SetCurrentDirectory(currentDirectory)

let buildDockerImage (tar : byte[]) : Stream =
    use client = (new DockerClientConfiguration(new Uri("http://localhost:2375"))).CreateClient()
    use dockerfileTarStream = new MemoryStream(tar)

    let imageBuildParams = new ImageBuildParameters()
    imageBuildParams.Tags <- new List<string>()
    imageBuildParams.Tags.Add("build-test")

    client.Images.BuildImageFromDockerfileAsync(dockerfileTarStream, imageBuildParams)
    |> Async.AwaitTask
    |> Async.RunSynchronously

let outputBuildResponse (responseStream : Stream) : unit =
    seq {
        use reader = new StreamReader(responseStream, Text.Encoding.UTF8)
        while not reader.EndOfStream do
            yield reader.ReadLine()
    }
    |> Seq.iter (printfn "%s")

[<EntryPoint>]
let main argv = 
    let dockerfilePath = argv.[0]
    let dockerfileDir = Directory.GetParent(dockerfilePath).FullName

    createDockerfileTar dockerfileDir
    |> buildDockerImage
    |> outputBuildResponse
    0

main関数はコマンドライン引数でDockerfileのパスを受け取って親ディレクトリのパスを取得しています。その後、TARデータ作成→Dockerイメージのビルドリクエストの送信→レスポンスの出力、という処理をしています。

また、次のような内容のDockerfileを作って保存し、

FROM busybox
RUN ls

先ほどのアプリケーションの引数にDockerfileのパスを指定して実行すると次のように出力されてDockerイメージがビルドされます。

> .\DockerBuild.exe ..\..\..\Dockerfile
{"stream":"Step 1/2 : FROM busybox\n"}
{"status":"Pulling from library/busybox","id":"latest"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":16384,"total":699311},"progress":"[=\u003e                                                 ] 16.38 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":260087,"total":699311},"progress":"[==================\u003e                                ] 260.1 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":424839,"total":699311},"progress":"[==============================\u003e                    ] 424.8 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":490375,"total":699311},"progress":"[===================================\u003e               ] 490.4 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":539527,"total":699311},"progress":"[======================================\u003e            ] 539.5 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":621447,"total":699311},"progress":"[============================================\u003e      ] 621.4 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Downloading","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1cae461a1479"}
{"status":"Download complete","progressDetail":{},"id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":32768,"total":699311},"progress":"[==\u003e                                                ] 32.77 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Extracting","progressDetail":{"current":699311,"total":699311},"progress":"[==================================================\u003e] 699.3 kB/699.3 kB","id":"1cae461a1479"}
{"status":"Pull complete","progressDetail":{},"id":"1cae461a1479"}
{"status":"Digest: sha256:c79345819a6882c31b41bc771d9a94fc52872fa651b36771fbe0c8461d7ee558"}
{"status":"Status: Downloaded newer image for busybox:latest"}
{"stream":" ---\u003e c75bebcdd211\n"}
{"stream":"Step 2/2 : RUN ls\n"}
{"stream":" ---\u003e Running in 74350655bd58\n"}
{"stream":"bin\ndev\netc\nhome\nproc\nroot\nsys\ntmp\nusr\nvar\n"}
{"stream":" ---\u003e 7fc898365619\n"}
{"stream":"Removing intermediate container 74350655bd58\n"}
{"stream":"Successfully built 7fc898365619\n"}

> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
build-test          latest              68c39e9a6aa8        37 seconds ago      1.11 MB
busybox             latest              c75bebcdd211        3 weeks ago         1.11 MB

HTTPレスポンスの出力の1行1行はJSONで、Dockerビルド中に中で標準出力に出力された文字列が返ってきます。標準出力の内容なので中に改行コード("\n")が含まれています。また、">“が”\u003e"にエスケープされています。

2017/06/13追記:レスポンスを整形して出力できるようにしました。

Dockerのビルドのレスポンスを整形して出力する - locablo

Azure Container RegistryへDockerイメージを登録する

Dockerのイメージを登録・管理できるAzure Container Registryを使ってみたので手順をまとめます。

流れは次の通りです。

  1. Docker for Windowsをインストールする
  2. Azure Container Registryでレジストリを作成する
  3. WindowsでDockerイメージを作成する
  4. Container Registryへ作成したDockerイメージを登録する

PCはWindows 10 Pro (64bit)です。

※注意:記事作成時点ではAzure Conteiner Registryに登録したDockerイメージを削除できないようです。

Docker for Windowsのインストー

Docker for WindowsHyper-Vを使うのでWindows 10(64bit)のProfessionalエディションなどが必要(https://docs.docker.com/docker-for-windows/install/#what-to-know-before-you-install)ですが、Docker Toolbox(https://docs.docker.com/toolbox/overview/)を使えばその他WindowsでもDockerを使えるようです。

インストールはDocker公式ページのドキュメントに従って行います。

Install Docker for Windows | Docker Documentation

Docker for WindowsにはStableチャンネルやEdgeチャンネルがあるようですが、今回はStableチャンネルのインストーラーをダウンロードしてインストールしました。

インストーラーの指示に従ってインストールをした後、Hyper-Vが無効の状態の場合はDockerを起動するとダイアログが表示されるようです。

f:id:LocaQ:20170528230819p:plain:w500

ダイアログに書かれているようにHyper-Vを有効にするとVirtualBoxが使えなくなります。それで問題なければOKボタンをクリックするとHyper-Vが有効になりPCが再起動します。

再起動したらdockerコマンドが使えるか確認します。

コマンドプロンプトPowershellを開いて次のコマンドを実行します。

> docker --version
Docker version 17.03.1-ce, build c6d412e
> docker-compose --version
docker-compose version 1.11.2, build f963d76f
> docker-machine --version
docker-machine.exe version 0.10.0, build 76ed2a6

またはVisual Studio Codeで端末ウィンドウを開いても確認できます。

f:id:LocaQ:20170528231251p:plain:w600

各コマンドでバージョンが表示されればOKです。

コンテナーレジストリの作成

Azure Container Registryのドキュメントの手順に沿って作成します。

プライベート Docker レジストリの作成 - Azure Portal | Microsoft Docs

  1. Azure Portalを開く
  2. Marketplaceで「Azure コンテナー レジストリ」を検索する
  3. 発行元がMicrosoftの[Azure コンテナー レジストリ」を選択する
  4. [作成]をクリックする
  5. コンテナーレジストリ名などを入力して[作成]をクリックする

f:id:LocaQ:20170528230512p:plain:w350

レジストリ名には英数字しか使えないようです。

また、ここで管理ユーザーを有効にするとdockerコマンドでContainer Registryへログインする時に、ユーザー名にレジストリ名を、パスワードにアクセスキーを使えるようになります。今回はPCで作成したイメージをdockerコマンドでContainer Registryに登録したいので有効にします。

Dockerイメージの作成

Dockerイメージを作成するにはDockerfileが必要ですが、MonoをインストールしたUbuntuのコンテナを作成するDockerfileが既にあるのでそれを例に使います。Dockerfileの内容は次の通り。

FROM ubuntu:xenial-20161010
RUN apt-get update
RUN apt-get install -y mono-complete

任意のフォルダにこのDockerfileを保存します。

次はコマンドプロンプトまたはPowershellを開き、Dockerfileを保存したフォルダに移動後、Dockerイメージをビルドします。

> cd [Dockerfileのあるフォルダ]
> docker build -t [レジストリ名].azurecr.io/mono .

[レジストリ名]はContainer Registryを作成したときに指定した名前です。"-t"オプションの値は一定のフォーマットでなければなりません(次の章で説明します)。また、最後に" .“(ピリオドの前に空白あり)があります。

ビルドが終わったらイメージが作成できているか確認します。

> docker images
REPOSITORY                        TAG       IMAGE ID        CREATED        SIZE
[レジストリ名].azurecr.io/mono    latest    a5c78a372b93    6 hours ago    747 MB

Container RegistryへのDockerイメージの登録

Dockerイメージのリポジトリ名のフォーマット

Dockerイメージのリポジトリ名は次のフォーマットになっている必要があります。

[レジストリ名].azurecr.io/[Dockerイメージ名]

[Dockerイメージ名]は名前空間付きDockerイメージの名前です。名前空間はDockerイメージが属するグループのことで、"/“で区切って複数レベルの階層を構成できます。詳しくは次のドキュメントの[主な概念]のリポジトリとイメージを参照してください。

Azure のプライベート Docker コンテナー レジストリ | Microsoft Docs

“docker build"の時の”-t"オプションに指定した値はこのフォーマットになっています。

もし上記のフォーマットになっていなかったり、名前を変えたい場合はdockerコマンドで変更できます。

> docker tag [現在のリポジトリ名] [新しいリポジトリ名]

Container Registryへの登録

Container RegistryへのDockerイメージの登録は次のドキュメントを参考にします。

Docker イメージをプライベート Azure レジストリにプッシュする | Microsoft Docs

まずはdockerコマンドでContainer Registryへログインします。

> docker login [レジストリ名].azurecr.io/ -u [ユーザー名] -p [パスワード]
Login Succeeded

上記ドキュメントではAzure Active DirectoryのサービスプリンシパルというもののIDとパスワードを使っていますが、Container Registry作成時に管理ユーザーを有効にしているので別途ユーザーとパスワードが作成されています。そのユーザーとパスワードは、

  1. Azure Portalを開く
  2. 作成したContainer Registoryを選択
  3. [アクセスキー]をクリックする

で確認できます。

f:id:LocaQ:20170529003751p:plain:w600

ログイン後、Container RegistryへDockerイメージをプッシュします。

> docker push [レジストリ名].azurecr.io/mono
The push refers to a repository [[レジストリ名].azurecr.io/mono]
...
latest: digest: sha256:6e9a312338c88dcfb10fede48e5f0ff0d423ff15ed387510842d2945cd3633c9 size: 1782

プッシュが終わったら、

  1. Azure Portal
  2. 作成したContainer Registoryを選択
  3. [リポジトリ]をクリック

と操作してプッシュしたDockerイメージが表示されるか確認します。

f:id:LocaQ:20170529003247p:plain:w400

リポジトリ一覧に"mono"があれば登録ができています。

3Dプリンタでボックス止めを作った

洗面所にバスタオルを入れてるボックスがあるんですが、引き出すと滑って前に出てくる上に落下して不便でした。で、せっかく3Dプリンタがあるので固定具を作ってみました。

f:id:LocaQ:20170220221949j:plain:w400

Fusion360モデリング3Dプリンタで試作を2回くらいして形が決まったものがこれです。

f:id:LocaQ:20170220224759p:plain:w370

これを4組み分作ります。2枚目の手前の2列の部品が3つずつなのは試作で既に1つずつ出来てるためです。

f:id:LocaQ:20170220220938p:plain:w400 f:id:LocaQ:20170220221008j:plain:w400 f:id:LocaQ:20170220221633j:plain:w400

3Dプリンタで印刷してサポートを取り、ミニルーターでバリを取りました。

f:id:LocaQ:20170220221937j:plain:w400

これをボックスの手前下側と奥の上側にはめ込みます。

f:id:LocaQ:20170220222003j:plain:w400 f:id:LocaQ:20170220222015j:plain:w400

これでバスタオルを出す時に引き出しを目いっぱい出しても滑ることも落ちることも無くなりました。

f:id:LocaQ:20170220222024j:plain:w400

形を考えるのとモデリングが15~20分、印刷が8時間(試作を除く)くらいでした。印刷は朝に始めて仕事に言ったので8時間でも長く感じません。こういう部品が簡単に作れるのが3Dプリンタの面白いところです。

ダヴィンチMini wの印刷精度を検証

ダヴィンチMini wで印刷したネジ穴の直径がちょっと小さい気がしたのでどの程度の正確に印刷できるのか検証してみました。

下は検証に使う3Dモデルで、左上が水平な穴が開いたモデル、左下が正方形の面を押し出したモデル、右が垂直な穴が開いたモデルです。

f:id:LocaQ:20170210235129p:plain:w500

この3つのモデルを印刷してノギスで実寸を測ります。印刷設定は次の通りです。

項目
品質 非常に良い
材料 PLA
ラフト なし
底辺 なし
サポート なし
内部充填密度 中(30%)
インフィルタイプ 格子
シェル 普通
レイヤの高さ 0.1
印刷速度 普通

水平な穴のサイズ

印刷したものと各穴のモデル上のサイズです。

f:id:LocaQ:20170211123334j:plain:w500

この穴の直径の実寸は次のようになりました。

モデルの直径(mm) 直径の実寸(mm) 誤差(mm)
0.5 - -
1.0 - -
1.5 - -
2.0 1.7 -0.3
2.5 2.2 -0.3
3.0 2.7 -0.3
3.5 3.2 -0.3
4.0 3.8 -0.2
4.5 4.2 -0.3
5.0 4.8 -0.2
5.5 5.1 -0.4
6.0 5.7 -0.3
6.5 6.2 -0.3
7.0 6.8 -0.2
7.5 7.3 -0.2

水平な穴を空けるとだいたい0.2~0.3mmほど直径が小さくなるようです。0.5mmの穴は完全に埋まっています。1.0mmと1.5mmの穴はほぼ埋まっていて直径を計測できませんでしたが一応表から裏までの穴は開いていたので、ピンバイスで穴を空けることができます。また、穴は見た目真円ではなくちょっと歪です。

正方形のサイズ

印刷したものと各直方体のモデル上のサイズです。バリがあって真上からの写真だと小さいサイズが分かりにくいので斜めからの写真も載せます。

f:id:LocaQ:20170209205745j:plain:w500 f:id:LocaQ:20170209210038j:plain:w500

各直方体の縦と横の長さの実寸は次のようになりました。

モデルの1辺の長さ(mm) 縦の実寸(mm) 横の実寸(mm) 縦の誤差(mm) 横の誤差(mm)
0.5 0.8 - +0.3 -
1.0 1.1 1.1 +0.1 +0.1
1.5 1.5 1.5 0.0 0.0
2.0 2.1 2.2 +0.1 +0.2
2.5 2.6 2.6 +0.1 +0.1
3.0 3.0 3.0 0.0 0.0
3.5 3.5 3.6 0.0 +0.1
4.0 4.0 4.0 0.0 0.0
4.5 4.5 4.6 0.0 +0.1
5.0 5.0 5.1 0.0 +0.1
5.5 5.5 5.5 0.0 0.0
6.0 6.1 6.1 +0.1 +0.1
6.5 6.5 6.6 0.0 +0.1
7.0 7.0 7.1 0.0 +0.1
7.5 7.4 7.5 -0.1 0.0

誤差の範囲は-0.1mm~+0.3mmですがほぼ0.0mm~+0.1mmの範囲に収まっています。円よりも正確に印刷できるようです。0.5mm四方のものはバリと太さがほぼ一緒のためあまり正確に計測できません。一応直方体部分も印刷されているのでバリが出なければこのサイズでも印刷できそうです。

垂直な穴のサイズ

印刷したものと各直方体のモデル上のサイズです。印刷時は2枚目のように立てた状態になっています。

f:id:LocaQ:20170209234110j:plain:w500 f:id:LocaQ:20170209210253j:plain:w500

この穴の直径の実寸は次のようになりました。

モデルの直径(mm) 水平方向の直径の実寸(mm) 垂直方向の直径の実寸(mm) 水平方向の直径の誤差(mm) 垂直方向の直径の誤差(mm)
0.5 - - - -
1.0 - - - -
1.5 1.2 1.0 -0.3 -0.5
2.0 1.6 1.7 -0.4 -0.3
2.5 2.1 2.4 -0.4 -0.1
3.0 2.7 2.6 -0.3 -0.4
3.5 3.2 3.1 -0.3 -0.4
4.0 3.7 3.5 -0.3 -0.5
4.5 4.0 4.2 -0.5 -0.3
5.0 4.7 4.6 -0.3 -0.4
5.5 5.2 5.2 -0.3 -0.3
6.0 5.6 5.3 -0.4 -0.7
6.5 6.0 6.4 -0.5 -0.1
7.0 6.4 6.4 -0.6 -0.6
7.5 6.9 6.9 -0.6 -0.6

誤差は-0.7mm~-0.1mmと大きめでした。縦方向の穴をサポートなしで印刷しているので誤差が大きくなるのは当然ですが、意外なのは幅も誤差が大きいことです。高さは重力があるのでサポートがなければその影響を受けますが幅はサポートの有無や重力は影響しない(と思う)ので理由はよく分かりません。