nohupなncでのtarなファイル転送

TB級のデータ転送のように、scpがかったるくてやってられない場合にncでファイル転送をしようというケースありますよね?
そんなケースでは当然相当時間がかかるんで、途中で死なないようにnohupをつけたい、と思うわけで。

んで、そのままnohupつけると動かなくなるんですが、よくワークアラウンドを忘れるのでメモを残しておきます。

# ホストやポート番号等は適当に読み替えてください

ファイル受信側

nohup nc -d -l 12345 2> nohup.out | tar xvf - &

ポイントは2点

  • nohupが標準入力をcloseするので、-dでncが標準入力を無視するように指定
  • nohupは特に指定してないと標準エラー出力を標準入力とマージするので、標準エラー出力の出力先を明示的に指定

ファイル送信側

nohup tar cf - files | nc 192.168.10.10 12345 &

こっちのポイントは

  • ファイル名のような余計な出力をしないよう、tarのzオプションなんかはつけない

以上ですー

MackerelとECSのダイナミックポートマッピング

当方、現在の担当業務的にMackerelを両手で数えられるくらいのサーバにしか導入しておらず、しかも初期に設定をしたあとは絶賛放置中という超ライトユーザなのですが、先日なんの因果か他部署の方から「ECRのダイナミックポートマッピングしているときのコンテナのメトリクス取るのどうすればええん?」という相談をうけてしまいました。

完全無欠のMackerel情弱としては、極々テキトーなやり取りをせざるを得なかったのですが、なんだかそれなりにイイ感じまとまったみたいです。

kakakakakku.hatenablog.com kakakakakku.hatenablog.com

結論、id:kakku22 さんカッケー。

まあそれだけだとアレなわけで、事情があって歯牙にもかけてもらえなかった別の方法をこのエントリでは書いてみようと思います。


ざっくり提案したのは以下のようなやり方。なお当方テキトーなので自分で試してないです(ぉ

ECSのtask-definitionで以下のように複数コンテナが動くようなタスク設定をする

  1. メトリクスを取りたい対象のコンテナ
    → 適当なポートでサービスが動いていて、そのポートでメトリクスをとれる
    → hostとのポートマッピングは動的で、host側からアクセスではポート固定できない
  2. mackerel-agentを動かすコンテナ
    → dockerのlink機能で上記のコンテナのポート(こっちは固定指定可)にアクセスしてメトリクス収集

要するに1タスクを1ホストとみなして、そこの中でメトリクス収集を完結させちまおう、というやり方ですね。
そーすることでMackerelの管理画面でもroleでのグラフもいい感じにまとまって見れるんじゃね?的な?的な?

 

やろうと思えば設定もカンタンで管理画面上も管理しやすい、という案なのですが、既に書いたように採用は見送られました。

理由は単純で、Mackerelの課金がホスト単位なので、1タスク1ホストとかでポンポン設定入れちゃうとアホみたいにオカネがかかるから、という。。。

そんなわけでMackerel様としては「ホストあたりの基本料金を極々安くして、その代わりメトリクス数で従量課金する」みたいな設定も可能にしていただけるとイイなぁと思う次第であります。
# じゃねーとみんなdatadogに逃げちゃうよ?

Symantecの証明書問題私的まとめ

Symantecの証明書でアレコレざわついているので、自分用にまとめます。

経緯等

以下にまとまっていまるので省略。

notchained.hatenablog.com

何がおこるか?

https://github.com/sleevi/explainer/blob/master/README.md

ざっくりまとめると以下の2点

  • 該当の証明書の有効期限を短いモノとして取り扱う(最終的には最大279日とみなす)
  • 該当の証明書はDomain Validatedとして扱う(EV証明書がEV証明書として扱われなくなる)

「該当の証明書」って?

https://chromium.googlesource.com/chromium/src/+/master/net/data/ssl/symantec/README.md

  • 証明書チェーンのルートCAの証明書が ここになければセーフ
  • subCAで発行した証明書の場合、それが"Excluded Sub-CAs"に記載されていればセーフ
  • それ以外はアウト

もっと具体的には?

個人的観測範囲で使われていて、該当するCAは以下

3/27くらいからChromeでEV証明書がEV証明書として扱われなくなったんだけど?

WindowsChrome 57では現象を確認できたが、MacChrome 57では確認できず。

経緯は以下

斜め読みしたカンジでは、"Symantecに激おこ事案"とは無関係の単なるバグか?
# タイミング悪すぎやろ。。。

どうすればいいの?

今後どう転ぶかわからないし、巻き込まれたくないならSymantecVeriSignのEV証明書をつかってるトコは、他社に乗り換えればいいと思う。

EV証明書じゃないなら、有効期限が短くなるだけといえば、それだけなので運用回避もアリかと。

私見

Symantecはギルティでも、Symantec発行の証明書を使ってるトコまで巻き込まれるのはツライ。

ただひたすらツライ。

5/2追記

まだちゃんと読んでないけど、リンクだけ

security.srad.jp

docker swarmのオーバーレイネットワークの安定性について

半年くらい前にこんな記事を書いたのですが、まあうまく行きませんでした。

okzk.hatenablog.com

頂いたブコメも試してみたんですけど、結果は芳しくなく。。。

Re: Dockerに載せたサービスをホットデプロイする - okzkメモ

--stop-grace-periodの設定とDockerfileのHEARTBEATとSTOPSIGNALの設定をすれば出来るはず

2016/08/17 06:19
b.hatena.ne.jp

そんな中、元記事のヒトも試してみたようですけど、同じ結果に。。。

h3poteto.hatenablog.com


そんな中、CVE-2016-9962も出ちゃったし、docker 1.13もリリースされたコトだし、ということでdocker 1.13でswarmモードをもう一回試してみました。

インストール後、swarm初期化

# docker swarm init
# docker service create --update-delay 5s -p 80:80 --name test --replicas 2 nginx

別ウインドウでログ等を確認しつつ

# watch -n 0.3 'curl -sI  http://localhost; docker ps -q | xargs -i sh -c "echo ----------- {};docker logs --tail 5 {}"'

パタパタイメージが切り替わるようにやる気なくワンライナー

# set -e
# while : ; do docker service update --image nginx:stable test ; sleep 15 ; docker service update --image nginx test; sleep 15 ; done

数十分放置してみましたが、特に問題ありませんでした。


というわけで元記事に対する半年遅れの回答になりますけどdocker 1.13のswarmモード使えば良いんじゃないでしょうか?

ubuntu 16.04でcloud-initでapt-get upgradeできない件

最近ubuntu 16.04を使い始めたんですけど、簡易プロビジョニングということで、 cloud-initのuser_dataのスクリプトで以下のようにパッケージの更新をしかけていました。

apt-get update
apt-get upgrade -y
# 他、アレコレと……

ところが先週くらいから、コイツが完全にハングするようになってしまいました。(´・ω・`)

原因はsnapdが更新されて、コイツがアップデートの中でsnapd.boot-ok.serviceを起動しようとするんですが、 cloud-initのuser_dataで与えられたスクリプトはシステムが完全に起動する前に実行されちゃうんですね。

ということで、

  • snapd.boot-ok.serviceがmulti-user.targetの起動を待つため、apt-get upgradeが完了しない
  • apt-get upgradeが完了しないから、multi-user.targetがいつまでたっても起動しない

という完全にデッドロックです。\(^o^)/

ソッコーでバグレポートも上がったんですけど、9/16日現在、まだcloud-initでのapt-get upgradeはハングします。 https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1621336

しょうがないので、一旦snapdを更新対象から外してapt-get upgradeするように運用回避してます。

apt-get update
echo snapd hold | dpkg --set-selections
apt-get upgrade -y
echo snapd install | dpkg --set-selections

んー、メンドクセ。

続、workerパターンをcontext化してみたら……

みなさん「みんなのGo言語」は予約ポチりましたか? 私はポチりました!

そんな「みんなのGo言語」の著者の一人であるid:lestrrat さんからの前エントリに対してマサカリが飛んできてます。

workerパターンをcontext化してみたら…… - okzkメモ

context.Contextを揮発性のないものでラップして持つのはよくないと思う。このほうがよりGoっぽいと思うが、どうだろう https://gist.github.com/lestrrat/c9b78369cf9b9c5d9b0c909ed1e2452e

2016/08/23 18:34

コメント内のgistのリンクはこちら

ちょっと時間あいちゃいましたけど、これについて思ったことを2点ほど……

まずはcontext関係ないとこから……

contextの使い方へのツッコミなのにいきなりcontextとは無関係なトコに言及しちゃうのですが、同時実行数の制御の仕方が前回までのコードと異なってるのにモヤモヤしちゃいました。

前回までのコードの方は以下のようなカンジ(以下「worker方式)」で

  1. 同時実行数分のworkerのgoroutineを立ち上げる
  2. その中で、ループでchannel経由で回ってきたjobを処理する

んで、頂いたgistの方は、こんなカンジ(以下「セマフォ方式)」

  1. job分goroutineを立ち上げておいて
  2. セマフォで同時実行数を制限する

元々の「goroutineを爆発させないため」という記事の流れもあったんでworker方式だったというのもあるんですけど、 実際どちらの実装方式も選べるなら、個人的には以下のような理由でやっぱりworker方式を選ぶような気がします。

  • なんとなくセマフォ方式よりworker方式の方が可読性が高い気がする
  • goroutineのそのもののコスト(どんなに軽量でもゼロではない)

可読性は個人の主観によるのでどうしても宗教論争になっちゃいますけど、後者の実行コストの方を検証してみましょう。

というわけで、簡易なベンチマークを用意してみました。

package main

import (
  "sync"
  "testing"
  "time"
)

const concurrency = 4

func BenchmarkLimitByWorkers(b *testing.B) {
  ch := make(chan struct{}, 10000)

  wg := sync.WaitGroup{}
  wg.Add(concurrency)
  for i := 0; i < concurrency; i++ {
    go func() {
      for range ch {
        // 何かしら処理の代わり
        // ベンチマークそのものに支配的にならないようにマイクロ秒だけsleep
        time.Sleep(time.Microsecond)
      }
      wg.Done()
    }()
  }

  for i := 0; i < b.N; i++ {
    ch <- struct{}{}
  }
  close(ch)
  wg.Wait()
}

func BenchmarkLimitBySemaphore(b *testing.B) {
  ch := make(chan struct{}, concurrency)

  wg := sync.WaitGroup{}
  wg.Add(b.N)
  for i := 0; i < b.N; i++ {
    go func() {
      ch <- struct{}{}
      // 何かしら処理の代わり
      // ベンチマークそのものに支配的にならないようにマイクロ秒だけsleep
      time.Sleep(time.Microsecond)
      <-ch
      wg.Done()
    }()
  }
  wg.Wait()
}

手元で実行してみた結果は以下のようなカンジ。

BenchmarkLimitByWorkers-4         300000              4872 ns/op
BenchmarkLimitBySemaphore-4       200000             12835 ns/op

想像どおりセマフォ方式の方がオーバーヘッドが大きいようです。
……が、結局のトコ無視できるくらいですね。

そんなわけでよっぽどパフォーマンス重視のトコ以外は可読性で判断すればいいという結論なんですけど、みなさん、どちらがお好みでしょうか?

contextの揮発性について

えーっと、そもそもなんですけど、contextって揮発性が求められるんですっけ???
極端な例でいうと、context.Background()で帰ってくるcontextなんかは全然揮発しないpackageのvarで定義されちゃってますけど……

さてさて、contextの用途的に、

  • Dispatcher全体のcontext
  • その処理の一部であるjobの処理のcontext

という流れで派生関係があるというのは、個人的には違和感ないですし、その派生関係を利用してDispacher全体をキャンセルさせるStopImmediately()みたいな実装もできたわけで……
# というかあの例はStopImmediately()のためにcontext対応したようなもんですケド(;・∀・)

んでもって、Start/StopみたいにライフサイクルがはっきりしているDispatcherのようなモノのcontextは、自身で(ラップして)管理させるほうがキレイなんじゃないかなぁと思います。


とはいえこういう規約もあるわけで、判断に悩むトコではあるんですけどね……

// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
//  func DoSomething(ctx context.Context, arg Arg) error {
//    // ... use ctx ...
//  }

でもこれはfunctionで使うcontextの渡し方に限定した内容だと思えば、Dispatcherが内部にcontextを保持するのは矛盾しないかなぁ……と思ってみたり。

い、いかがでしょうか???

……やっぱり、struct typeでラップするのは、、、ダメですかね???

workerパターンをcontext化してみたら……

はい、というわけで、前記事のworkerパターンをcontextつかったらどーなるか、についてです。
前の記事や、その元記事のソースを読んでいる前提ですので、未読の方はそちらの確認からお願いします。

さて、ざっくりとした変更の方針ですけど、以下の2点です。

  • contextを使うことで、workerの実装側でキャンセルできるようにする
  • workerに渡す値はcontext経由にする。

というのをヤッツケでやってつくってみました

以下変更点の解説です。

まず、元実装ではworkerで実行される処理がベタ書きだったのを汎用化するため、dispatcherのqueueに入れるjobをとqueueの定義を変更します。

type (
  job struct {
    proc func(context.Context)
    ctx  context.Context
  }

  Dispatcher struct {
    queue chan *job
    // ...
  }
)

workerの実装も合わせて変更します。

go func() {
  defer d.wg.Done()
  for j := range d.queue {
    j.proc(j.ctx)
  }
}()

さらにdispatcherは親contextとCancelFunc持つようにします。

type (
  Dispatcher struct {
    // ...
    ctx    context.Context
    cancel context.CancelFunc
  }
)

そして使う側がcontextを拡張して使えるようにcontextを返す関数を用意します。

func (d *Dispatcher) Context() context.Context {
  return d.ctx
}

job登録もcontextを使わないパターンと使うパターンで2種類用意します。
使わないパターンでは、dispatcherのctxをそのまま渡します。

func (d *Dispatcher) Add(proc func(context.Context)) {
  d.queue <- &job{proc: proc, ctx: d.ctx}
}

func (d *Dispatcher) AddWithContext(proc func(context.Context), ctx context.Context) {
  d.queue <- &job{proc: proc, ctx: ctx}
}

終了処理も変更しています。

Immediatelyな実装の方では、queueの読み捨てをやめてcontextのcancelを先に呼ぶようにしました。
ちゃんとImmediatelyに帰ってくるかはjobの実装次第ですが、読み捨てよりはお行儀が良い気がしてます。

func (d *Dispatcher) StopImmediately() {
  d.cancel()
  close(d.queue)
  d.wg.Wait()
}

Immediatelyじゃない実装もworkerが全部終了した後ですが、cancel()を実行しています。コレをしないとリークが発生します

func (d *Dispatcher) Stop() {
  close(d.queue)
  d.wg.Wait()
  d.cancel()
}

さて、ここまでやったので、元のgetをcontextを受け取って使うようにちょちょいっと修正して……

func get(ctx context.Context) {
  url := ctx.Value("url").(string)
  req, err := http.NewRequest("GET", url, nil)
  if err != nil {
    log.Fatal(err)
  }

  resp, err := http.DefaultClient.Do(req.WithContext(ctx))
  // ...
}

jobを登録するトコをこんなカンジで、context.WithValueでurlを渡すように修正します。

  for i := 0; i < 10; i++ {
    url := fmt.Sprintf("http://placehold.it/%dx%d", i, i)
    ctx := context.WithValue(d.Context(), "url", url)
    d.AddWithContext(get, ctx)
  }

こんなカンジでworkerパターンのcontext対応化が完了しましたー。

感想

httpのclientとかはcontextに対応していて、中断処理に対応したモノをさっくり作ることができてラクですねー。


(8/24 追記)
id:lestrrat さんからマサカリが飛んできております((((;゚Д゚))))ガクガクブルブル

workerパターンをcontext化してみたら…… - okzkメモ

context.Contextを揮発性のないものでラップして持つのはよくないと思う。このほうがよりGoっぽいと思うが、どうだろう https://gist.github.com/lestrrat/c9b78369cf9b9c5d9b0c909ed1e2452e

2016/08/23 18:34
b.hatena.ne.jp

コチラについて、別エントリを書くかも……