DRYな備忘録

Don't Repeat Yourself.

CSS書きたくなさすぎ問題2017

css書きたくない。できればjsも書きたくない。js必要なの嫌。軽くやりたい。という個人の日記です。

参考

Gridだけやりたいやつは除外した。

  • Bootstrap
  • Materialize
  • mui
  • (追記)UIkit
  • (追記)Semantic UI
  • Pure
  • Bulma
  • Skelton
  • Spectre.css
  • Kube
  • Vuetify
  • Fictoan
  • avalanche
  • Beuter
  • Vanilla
  • Milligram
  • InvisCSS
  • Look
  • mini
  • Cutestrap
  • Buefy
  • siimple
  • Mobi
  • Modulr
  • Grd
  • Frow
  • Picnic CSS
  • Basscss
  • Blaze CSS
  • Furtive
  • LOTUS
  • Leaf CSS

後半疲れたっていうのもあり、僕的には、Skelton、Spectre、Fictoan、Milligram、Look、あたりが今後使ってみたいです。

Bootstrap

Bootstrap · The world's most popular mobile-first and responsive front-end framework.

大御所だし、最初に言っといてやるか感

Materialize

Documentation - Materialize

f:id:otiai10:20170530145913p:plain

material-uiのcssだけ版。個人事業で使ってるけど、部分的にjs要るし、ちょっとだるみ出てきた。これ使うならMaterial-UIでええやろ感ある。なお、Material-UIは個人趣味開発で使ってる。

mui

MUI - Material Design CSS Framework

f:id:otiai10:20170530150136p:plain

Materializeと同じ雰囲気を感じる。jsあるやん。

(追記)UIkit

Thanks to id:k-holy, id:kvx

UIkit

jQuery is required as well.

アッ、ハイ。

(追記)Semantic UI

Thanks to id:oukayuka, id:wazly

Semantic UI

Just link to these files in your HTML along with the latest jQuery.

アッ、ハイ。

Reactでやりたいんだったら Semantic UI React があるけど、僕が常日頃だるいなって思ってるのは、デザインとフロントのアーキテクチャがベタベタでいいのか?っていうアレ。変じゃね?なんでデザイン策定でReact使うかjQuery使うかみたいな二択せなアカンねんと。両方つかうとか論外。

Pure

Pure

f:id:otiai10:20170530150038p:plain

cssオンリーつったらいの一番に出てくるやつ

Bulma

Bulma: a modern CSS framework based on Flexbox

f:id:otiai10:20170530150843p:plain

わりといい感じ。demadoに使ってる。

Skelton

Skeleton: Responsive CSS Boilerplate

f:id:otiai10:20170530145504p:plain

こういうのでいいんだよ、こういうので。

Spectre.css

f:id:otiai10:20170530151001p:plain

よさそう。

Kube

f:id:otiai10:20170530151207p:plain

パンくずとかもあっておもしろそうだけど、JSけっこう書かされる気がする。

Vuetify

vuetifyjs.com

Vueコンポーネントベース。却下。

Fictoan

FICTOAN • Intuitive, minimalist responsive HTML+CSS boilerplate

f:id:otiai10:20170530151936p:plain

Overview読んで、あ〜これ好き、ってなった。今度使いたい

avalanche

avalanche | A package based CSS framework.

プロジェクト構成まで口出してこないでほしい

Beuter

Beauter | A simple framework for beautiful sites

f:id:otiai10:20170530152449p:plain

完全に好みで申し訳ないんだけど、トップページのUIがあんまり好きくなかったので印象悪い

Vanilla

Documentation

f:id:otiai10:20170530152631p:plain

menu itemがらみのクリック可領域がちょっと直感的ではない。そのへんもカスタマイズしろ、ってことなのかな

Milligram

Milligram - A minimalist CSS framework.

f:id:otiai10:20170530153019p:plain

一瞬Vaporかな?って思ったけどちがう。圧巻の2kb。良いかもしれない。

InvisCSS

Invis CSS

なんかそういうことじゃねえんだよなー、っていう。僕のペインとスタートラインが違う。

Look

Look | Minimalistic CSS framework

f:id:otiai10:20170530153833p:plain

FieldsetsとかSegmentsとかおもしろそう。最後のCubeワロタ。

mini

mini.css - Minimal, responsive, style-agnostic CSS framework

f:id:otiai10:20170530154652p:plain

全体的にカクカクしてた

Cutestrap

Cutestrap

f:id:otiai10:20170530154909p:plain

さすがに機能薄すぎるんじゃねえかなと思った。

Buefy

Buefy: lightweight UI components for Vue.js based on Bulma

Vueのコンポーネントかー、残念。

siimple

siimple - Minimal CSS framework for flat and clean websites

f:id:otiai10:20170530155216p:plain

名前がややこしい。Almost Flat UIに似た印象を受けた。

Mobi

Mobi.css

Mobile Firstらしいけど、たぶん使わない。見た目が無骨すぎて。

Modulr

Modulr.css Frontend Framework

f:id:otiai10:20170530155717p:plain

色に対する並々ならぬ関心が感じられるが、これ↑残念すぎるだろ。

Grd

Grd - A CSS grid framework using Flexbox

もう頭使いたくないんです。ゆるして。

Frow

Frow CSS

f:id:otiai10:20170530160100p:plain

ほぼほぼGridとFormだけって感じ。でもFormはスタイリッシュでいい感じ。

Picnic CSS

Picnic CSS

f:id:otiai10:20170530160404p:plain

なかなかよい。「ここはすまんやけどjs書いてくれ」っていう態度も好印象です。書かねえけどな。

Basscss

Basscss

f:id:otiai10:20170530160556p:plain

Form置いてけぼりでいいんならアリなんじゃないですかね。

Blaze CSS

Blaze CSS - Open Source Modular CSS Toolkit

f:id:otiai10:20170530161108p:plain

なんかどっかで見たことあるんだよなあこの雰囲気。

Furtive

Furtive CSS

f:id:otiai10:20170530161232p:plain

Skeltonをちょっとカラフルにした印象がある。よさそう。

LOTUS

f:id:otiai10:20170530161407p:plain

フォームはシュッとしてるけどぜんたいてきに柔らかい印象。

Leaf CSS

Leaf BETA 1.0 - CSS Framework

f:id:otiai10:20170530161656p:plain

Material Designの軽量実装っぽい。いい感じなのだけれど、あまりメンテされなさそう。

雑感

  • 調べればキリがないわコレ
  • なんか適当な軸でマッピングにしてビジュアライズしてくれ誰か頼む
  • プログラマCSS考えさせるデザイナと仕事すんのつらい。デザイナからCSSフレームワーク納品されたら泣いて喜ぶ。せめてガイドラインぐらい作ってくれ。「え?出来ないんですか?ww」じゃねえよ、おめえのsketchファイルmarginあちこちバラバラだから!

GoのコードからDockerコンテナへのディスクボリュームのマウントを実装する

前々回、前回

で、GoのコードからDockerイメージのpullとDockerコンテナのrunを実装できたので、実践的なアプリケーションをつくっていきたいのだけれど、今回はdocker runにおける--volumeの指定がしたい。

実行するDockerイメージのサンプル

Dockerfile

FROM centos

ADD main.sh /bin/

ENTRYPOINT main.sh

main.sh

#!/bin/sh

dest="copy_`date`.txt"
cat /var/data/foo.txt > /var/data/${dest}

docker build . -t foo としてfooというtagのimageをつくる。

実行するGoのコード

   cc := &container.Config{
        Image: "foo",
        // Volumes: map[string]struct{}{"/var/data": struct{}{}},
        // これ要らんっぽいな... 🤔
    }
    hc := &container.HostConfig{
        // これが味噌
        Mounts: []mount.Mount{
            mount.Mount{
                Type:   mount.TypeBind,
                Source: "/Users/otiai10/tmp/hoge",
                Target: "/var/data",
            },
        },
        AutoRemove: true,
    }
    nc := &network.NetworkingConfig{}

    // あとは前回、前々回とおなじです。
    body, err := c.ContainerCreate(ctx, cc, hc, nc, "bar")
    if err != nil {
        panic(fmt.Errorf("ContainerCreate: %v", err))
    }
    fmt.Printf("Created Container:\n%+v\n", body)

    if err := c.ContainerStart(ctx, body.ID, types.ContainerStartOptions{}); err != nil {
        panic(fmt.Errorf("ContainerStart: %v", err))
    }

確認

% ls -l ~/tmp/hoge
total 8
-rw-r--r--  1 otiai10  staff  16  5 29 16:48 foo.txt
% go run main.go
Created Container:
{ID:e2b6e7aa7251f13d8e823dc4a4dddb327b3863e9632211698126b46d5975e724 Warnings:[]}
List of containers
 - e2b6e7aa7251f13d8e823dc4a4dddb327b3863e9632211698126b46d5975e724 (foo)
% ls -l ~/tmp/hoge
total 16
-rw-r--r--  1 otiai10  staff  16  5 30 11:59 copy_Tue May 30 02:59:06 UTC 2017.txt
-rw-r--r--  1 otiai10  staff  16  5 29 16:48 foo.txt
%

無事、ホスト(厳密にはGoのコードを実行する環境ではなく、このclient.Clientが向いてるdockerホストマシン)の/Uers/otiai10/tmp/hogeが、コンテナから見たときの/var/dataにマウントされていることが確認された。

参考と道のり

f:id:otiai10:20170530120709p:plain

雑感

  • ドキュメント散在してるなー、って思った
  • 急がず焦らず、じっくり読まないといけない

DRYな備忘録として

Docker

Docker

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

GoのコードからDockerイメージのpullを実装する(bufio.Scannerかわいい)

前回エントリ↓でコードからのイメージのpullが動かなくてあっれおかしーなーとなって悔しかったのでリベンジです。

otiai10.hatenablog.com

tl;dr

  • client.ImagePullの返り値はio.ReadCloser型とerror
  • このio.ReadCloserが、イメージのpullのprogressなどを表すHTTPのストリーム
  • このストリームへの書き込みが終わる(つまりdocker pullが完了する)まで待つ必要があった

以下読まなくてよいです

動かないコード

func main() {

    c, err := client.NewEnvClient()
    if err != nil {
        panic(fmt.Errorf("NewEnvClient: %v", err))
    }
    ctx := context.Background()

    // "debian"(:latestは省略可)のpullを試みる
    rp, err := c.ImagePull(ctx, "debian", types.ImagePullOptions{})
    if err != nil {
        panic(fmt.Errorf("ImageSave: %v", err))
    }
    defer rp.Close()
    // エラーさえ無ければイメージのpullが成功していると誤解していた

    cc := &container.Config{Image: "debian"}
    hc := &container.HostConfig{AutoRemove: true}
    nc := &network.NetworkingConfig{}
    body, err := c.ContainerCreate(ctx, cc, hc, nc, "bar")
    if err != nil {
        panic(fmt.Errorf("ContainerCreate: %v", err))
        // 結局ここで"debianなんてイメージは無い!"と叱られる
    }
    fmt.Printf("Created Container:\n%+v\n", body)

}

動くコード

前後省略します

func main() {

    /* 省略(クライアントの初期化とかする) */

    // "debian"(:latestは省略可)のpullを試みる
    rp, err := c.ImagePull(ctx, "debian", types.ImagePullOptions{})
    if err != nil {
        panic(fmt.Errorf("ImageSave: %v", err))
    }
    defer rp.Close()

    // {{{ こっからが味噌!!

    // こういう構造のバッファが書き込まれる
    payload := struct {
        ID             string `json:"id"`
        Status         string `json:"status"`
        Progress       string `json:"progress"`
        ProgressDetail struct {
            Current uint16 `json:"current"`
            Total   uint16 `json:"total"`
        } `json:"progressDetail"`
    }{}

    // bufio.Scannerマジでかわいい
    scanner := bufio.NewScanner(rp)
    for scanner.Scan() {
        json.Unmarshal(scanner.Bytes(), &payload)
        fmt.Printf("\t%+v\n", payload)
    }

    // }}} ここまでが味噌!!

    /* 省略(得られたイメージを指定してコンテナつくったりする) */

}

上記では、bufio.Scannerを使ってHTTPのEOFまで待っている。ついでに、毎バッファ書き込みされるレスポンスを取り出して上げている。
↓こういうきれいな感じのペイロードが送られてくる。

f:id:otiai10:20170526152637p:plain

Scanner使わないんだったらioutil.ReadAllかなー他にストリームが終わるの同期的に待つのどうすっかなーと思ってググりました。

今回のように毎ペイロードが独立して意味のあるものであれば、Scanner使うのが良い気がするものの、もっと良い(単にReaderのEOFを待つための)方法があれば教えてくださいm(_ _)m

雑感

  • Goのコードからgithub.com/docker/docker/clientを使って (1) イメージのpull (2) コンテナ起動 が実現されたので、これでほぼ心置きなく俺々Dockerクライアントを自作できますね!やったね!
  • 腰椎椎間板ヘルニアになっちゃった。腰痛ぇ。

DRYな備忘録として

Docker

Docker

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

GoのコードからDockerコンテナの起動を実装する

やったこと

  • docker clientをGoのコードからimportする
  • container作成
  • container起動
  • container停止(& 自動削除)

苦労したこと

  • けっこうdocker(現moby)のコードの移り変わりが激しくて、vendorを固定するのが苦労した

できなかったこと

main.go

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/api/types/network"
    "github.com/docker/docker/client"
    "github.com/docker/go-connections/nat"
)

func main() {

    c, err := client.NewEnvClient()
    if err != nil {
        panic(fmt.Errorf("NewEnvClient: %v", err))
    }

    ctx := context.Background()

    /* 後述する「うごかなかったもの」がここに来ます */

    // 1) コンテナをつくる
    cc := &container.Config{
        Image:        "nginx",
        ExposedPorts: nat.PortSet{nat.Port("80"): struct{}{}},
        // Entrypoint:   strslice.StrSlice{"nginx", "-g", "daemon off;"},
    }
    hc := &container.HostConfig{
        // `--port 8080:80`
        PortBindings: nat.PortMap{
            nat.Port("80"): []nat.PortBinding{nat.PortBinding{HostPort: "8080"}},
        },
        // `--rm`
        AutoRemove: true,
    }
    // docker network らへん
    nc := &network.NetworkingConfig{}

    body, err := c.ContainerCreate(ctx, cc, hc, nc, "foo")
    if err != nil {
        panic(fmt.Errorf("ContainerCreate: %v", err))
    }
    fmt.Printf("Created Container:\n%+v\n", body)

    // 2) コンテナのスタート
    if err := c.ContainerStart(ctx, body.ID, types.ContainerStartOptions{}); err != nil {
        panic(fmt.Errorf("ContainerStart: %v", err))
    }

    // あること確認
    check(ctx, c)

    // 3) 20秒だけ延命
    time.Sleep(20 * time.Second)

    // 4) コンテナの停止
    //    --rm を投げてるのでコンテナ自体も削除される
    timeout := 5 * time.Second
    if err := c.ContainerStop(ctx, body.ID, &timeout); err != nil {
        panic(fmt.Errorf("ContainerStop: %v", err))
    }

    // ないこと確認
    check(ctx, c)
}

// コンテナのリストを取得する確認用の関数
func check(ctx context.Context, c *client.Client) {

    containers, err := c.ContainerList(ctx, types.ContainerListOptions{All: true})
    if err != nil {
        panic(err)
    }

    fmt.Println("List of containers")
    for _, container := range containers {
        fmt.Printf(" - %s (%s)\n", container.ID, container.Image)
    }
}

これで、 go run main.go とすると、20秒だけホストの8080にnginxが現れるということになります。

f:id:otiai10:20170524163022p:plain

f:id:otiai10:20170524162509p:plain

f:id:otiai10:20170524163152p:plain

よっしゃ。

うごかなかったもの

   rp, err := c.ImagePull(ctx, "nginx:latest", types.ImagePullOptions{})
    if err != nil {
        panic(fmt.Errorf("ImagePull: %v", err))
    }
    defer rp.Close()
    // エラーは無いんだけど、ContainerCreateで参照できないんだよなあ

    // じゃあImageSaveかなと思ったけど引数がImageIDsなので、とりあえずハードコードで投げる
    rs, err := c.ImageSave(ctx, []string{"bf2b4c2d7bf53b4d0d28fa6af60e51c418317d2ada40ed6e5d5c290248d2a469"})
    if err != nil {
        panic(fmt.Errorf("ImageSave: %v", err))
    }
    defer rs.Close()
    // まあ無いと言われる

    // 確認用
    images, err := c.ImageList(ctx, types.ImageListOptions{})
    if err != nil {
        panic(fmt.Errorf("ImageList: %v", err))
    }
    for _, img := range images {
        fmt.Printf("%+v\n", img)
    }

    // ImagePullで得たio.ReadCloserに色々入ってんのかな?
    // やたらでかそうだし、もしかしてイメージそのもの?

この部分が動かなかったので、あらかじめホストマシンで docker pull nginx を実行しないと動かないコードになってしまった。くやしい。次はこれを解決したいです。

DRYな備忘録として

追記

できました otiai10.hatenablog.com

Docker

Docker

不要になったDockerコンテナの一括削除

% docker ps -a -q -f "status=exited" | xargs docker rm
  • -a --all runningではないコンテナも含めすべて表示
  • -q --quiet container idのみ表示
  • -f --filter 色々でフィルタリングできる
  • を、xargsdocker rmの引数に投げた

DRYな備忘録として

Docker for Macのインストールと複数VMをつくるまで

インストールのログ

f:id:otiai10:20170523100821p:plain

f:id:otiai10:20170523100650p:plain

f:id:otiai10:20170523100724p:plain

f:id:otiai10:20170523100952p:plain

f:id:otiai10:20170523101108p:plain

% which docker
/usr/local/bin/docker
% docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
% docker-machine ls
NAME   ACTIVE   DRIVER   STATE   URL   SWARM   DOCKER   ERRORS
% docker-compose ps
ERROR:
        Can not find a suitable configuration file in this directory or any
        parent. Are you in the right directory?

        Supported filenames: docker-compose.yml, docker-compose.yaml

%

docker-machinedocker-composeも入ってるやん?VM複数つくっていいのかな。VirtualBoxではなくHyperkitという差はある。

まずは適当になんか立てる

% docker-machine ls
NAME   ACTIVE   DRIVER   STATE   URL   SWARM   DOCKER   ERRORS

docker toolbox的に言えば、machineがひとつも立ってないんだからdockerホストいなくね?っていう気持ちだけど、とりあえずやってみる。自作のOCRサーバ立ててみる。

% docker run --rm -p 8080:8080 otiai10/ocrserver
Unable to find image 'otiai10/ocrserver:latest' locally
latest: Pulling from otiai10/ocrserver
386a066cd84a: Pull complete
75ea84187083: Pull complete
88b459c9f665: Pull complete
a31e17eb9485: Pull complete
1b272d7ab8a4: Pull complete
eca636a985c1: Pull complete
08158782d330: Pull complete
fb9f7b3131a3: Pull complete
fcf51ff1519d: Pull complete
afdc21dc8865: Pull complete
2e81650bd01b: Pull complete
Digest: sha256:221d7ffd1ca2daa2dd2bff6e8d888873302f7031c05df5c2bd61c2d73cd14326
Status: Downloaded newer image for otiai10/ocrserver:latest
[ocrserver] listening on port :8080

f:id:otiai10:20170523102435p:plain

動いとる。いい感じ。別ターミナルから

% docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                    NAMES
0c2091fcfda3        otiai10/ocrserver   "/bin/sh -c /go/bi..."   About a minute ago   Up About a minute   0.0.0.0:8080->8080/tcp   stupefied_tesla
% docker-machine ls
NAME   ACTIVE   DRIVER   STATE   URL   SWARM   DOCKER   ERRORS
% docker kill 0c2091fcfda3
0c2091fcfda3
% docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
%

いい感じ。

プロジェクトごと別々のDockerホストを扱う

% docker-machine create --help
Usage: docker-machine create [OPTIONS] [arg...]

Create a machine

Description:
   Run 'docker-machine create --driver name' to include the create flags for that driver in the help text.
% docker-machine create foo
Creating CA: /Users/otiai10/.docker/machine/certs/ca.pem
Creating client certificate: /Users/otiai10/.docker/machine/certs/cert.pem
Running pre-create checks...
Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path"

まあそうだよな。VirtualBox入れてないもん。じゃあHyperkitかな?

% docker-machine create --driver hyperkit foo
Driver "hyperkit" not found. Do you have the plugin binary "docker-machine-driver-hyperkit" accessible in your PATH?
%

なぬ?hyperkitのdocker-machineドライバー無いのか。

Currently, there is no docker-machine create driver for HyperKit, so you will use virtualbox driver to create local machines.

今んとこ、複数Dockerホスト立てる場合は、docker for macが立てたhyperkitのvmではなく、docker-machine経由で、VirtualBoxVMを立てる必要があるっぽい。ということで、掲題の目的を達成するためには

  1. VirtualBoxのインストー
  2. docker-machine createする
  3. docker-machine envをevalする
  4. そこに向けてdocker runする

という今までのおなじみのワークフローをすればよいっぽい。

それにしても、エラーメッセージの docker-machine-driver-hyperkit っていうのはちょっと気になる。

README読むほどに、まあ無理してこれ使わんでも、VirtualBoxでdocker-machineで立てればいいか、という気持ちになるので、放置でよいかと思った。とりあえずMacのHyperkitのホストに対してdocker/docker-composeでプロジェクトごとに分ける運用で十分かという結論。

DRYな備忘録として

Docker入門

Docker入門

pkg_resources.DistributionNotFound: The '自作pythonスクリプト==0.0.3' distribution was not found and is required by the application

問題

jobとしてシェルスクリプトを投げるタイプのjob-queueシステムにおいて、シェルスクリプトから pip install --user で入れたpython binを参照させると、どうやらimportでコケている模様。

# 中略
pkg_resources.DistributionNotFound: The 'filtertool==0.0.3' distribution was not found and is required by the application

原因

  • --user で入れたものの lib のpathは、環境変数 $PYTHONUSERBASE/lib を参照しているが、
  • job-queueシステムのシェルスクリプトではその環境変数が見つからないのでsystemのlibを見に行って、
  • 自作スクリプトがライブラリとして存在してないよ! 」という状況が起きる

解決

例) uge_cooker を使っている場合

env.json

{
    "PYTHONUSERBASE": "/home/otiai10/opt/python/2.7.10/packages"
}

DRY

Pythonスタートブック

Pythonスタートブック