docker-compose で Error processing tar file(exit status 1): unexpected EOF が出るとき
こんなエラーに遭遇しました。
$ docker-compose up -d api Building api ERROR: Error processing tar file(exit status 1): unexpected EOF
軽く調べた感じだと、これという決定的な情報は見つからず。 ただどうやら、ワークディレクトリにパーミッションが変なファイルがあると発生するみたい。 今回の環境の場合、 volumes でマウントした MySQL のデータファイルが変なパーミッションで作成されていた。
$ ll -d db-data drwxr-xr-x. 7 systemd-bus-proxy ssh_keys 4096 Mar 24 23:27 db-data
しかし、類似したパーミッションのファイルを手で作成してもエラーは再現しなかった。 前述のどのファイルが悪さしているのか、はたまた合計ファイルサイズでおかしくなるのか、よくわからないけど、とりあえずファイルを消すかファイルが読める状態にしてあげると動くようになった。
$ sudo chown -R 1000:1000 db-data [sudo] password for ikasamak: $ ll -d db-data drwxr-xr-x. 7 ikasamak ikasamak 4096 Mar 24 23:27 db-data $ docker-compose up -d api Building api Step 1/10 : FROM php:5.6-apache 5.6-apache: Pulling from library/php f2b6b4884fc8: Downloading [=============================> ] 31.35 MB/52.61 MB 8db887c45800: Download complete 6e0e41c52c70: Downloading [=============> ] 20.49 MB/76.35 MB (以下略)
MySQL で split, join したいとき
素の SQL で DB にパッチをあてる運用とかがあると*1、こんなことがあります。
- SQL の条件に変数を使いたい
mysql> SELECT * FROM test; +----+------+ | id | name | +----+------+ | 1 | hoge | | 2 | fuga | | 3 | foo | | 4 | bar | +----+------+ 4 rows in set (0.00 sec) mysql> SELECT * FROM test WHERE name IN ('hoge', 'fuga'); # ←ここの IN の中とか! +----+------+ | id | name | +----+------+ | 1 | hoge | | 2 | fuga | +----+------+ 2 rows in set (0.00 sec)
- でも MySQL の変数は複数の値を持てない
mysql> SET @names = ('hoge', 'fuga'); ERROR 1241 (21000): Operand should contain 1 column(s)
- そうだ、
join
してsplit
しよう! ←いまここ
MySQL に join
, split
はあるのか
join
CONCAT_WS
を使います
https://dev.mysql.com/doc/refman/5.6/ja/string-functions.html#function_concat-ws
mysql> SELECT CONCAT_WS(',', 'hoge', 'fuga'); +--------------------------------+ | CONCAT_WS(',', 'hoge', 'fuga') | +--------------------------------+ | hoge,fuga | +--------------------------------+ 1 row in set (0.00 sec)
split
- 残念ながら、MySQL には
split
に相当する機能はないです - ただし、検索条件として使うなら
FIND_IN_SET
で代替できますFIND_IN_SET
の第二引数はカンマ区切りである必要があります- データにカンマが含まれているからパイプで繋いでたりしてるとアウトです、あきらめましょう
https://dev.mysql.com/doc/refman/5.6/ja/string-functions.html#function_find-in-set
mysql> SELECT FIND_IN_SET('fuga', 'hoge,fuga'); +----------------------------------+ | FIND_IN_SET('fuga', 'hoge,fuga') | +----------------------------------+ | 2 | +----------------------------------+ 1 row in set (0.00 sec) mysql> SELECT FIND_IN_SET('foo', 'hoge,fuga'); +---------------------------------+ | FIND_IN_SET('foo', 'hoge,fuga') | +---------------------------------+ | 0 | +---------------------------------+ 1 row in set (0.00 sec)
これらを組み合わせて
mysql> SET @names = CONCAT_WS(',', 'hoge', 'fuga'); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @names; +-----------+ | @names | +-----------+ | hoge,fuga | +-----------+ 1 row in set (0.00 sec) mysql> SELECT * FROM test WHERE FIND_IN_SET(name, @names); +----+------+ | id | name | +----+------+ | 1 | hoge | | 2 | fuga | +----+------+ 2 rows in set (0.00 sec)
できた!
*1:弊社です
Windows から Docker 実行時に鍵ファイルを渡して SSH したい
問題
- Windows の Docker は
volumes
でホストのファイルをマウントするとパーミッションが755
になる - しかもパーミッション変更できない
- しかし、SSH の Private key はパーミッション
600
とかじゃないとエラーになるので困った
$ winpty docker-compose exec proxy ls -l //key total 4 -rwxr-xr-x 1 root root 1692 Mar 17 20:42 id_rsa $ winpty docker-compose exec proxy chmod 0600 //key/id_rsa $ winpty docker-compose exec proxy ls -l //key total 4 -rwxr-xr-x 1 root root 1692 Mar 17 20:42 id_rsa $ winpty docker-compose exec proxy ssh -i //key/id_rsa $STEP_SERVER_USER@$STEP_SERVER @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Permissions 0755 for '/key/id_rsa' are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key "/key/id_rsa": bad permissions Permission denied (publickey).
解決方法
docker-entrypoint.sh
でマウントされた鍵ファイルのコピーを内部で作ってchmod
する
#!/bin/sh KEY_PATH=/root/.ssh KEY_FILE=key.pem KEY_FILEPATH=$KEY_PATH/$KEY_FILE mkdir -p $KEY_PATH cat /key/$INPUT_KEY_FILENAME > $KEY_FILEPATH chmod 0600 $KEY_FILEPATH SSH="ssh -o 'StrictHostKeyChecking no' \ -o 'ServerAliveInterval 15' \ -i $KEY_FILEPATH \ -D 0.0.0.0:$LOCAL_PORT \ -N \ $STEP_SERVER_USER@$STEP_SERVER" echo $SSH eval $SSH
踏み台サーバ経由でいろいろ (Web, Git, MySQL) アクセスしたい
社外から社内システムに接続しようと奮闘した記録です。 以下の前提でやっていきます。
Web アクセスしたい
使うものは以下。
1. トンネルを掘る
ssh stepserver -f -N -D 1080
-f
: バックグラウンドで実行-N
: コマンドを実行しない-D 1080
: ダイナミックポートフォワード。localhost:1080
に SOCKS プロキシをたてる。
これでlocalhost:1080
にプロキシとしてアクセスすると、stepserver
経由で通信ができます。
2. プロキシの設定
まずはプロキシの自動構成スクリプト (proxy.pac
) を準備します。
function FindProxyForURL(url, host) { if (shExpMatch(host, "stg.*.example.com") || dnsDomainIs(host, "git.example.com")) { return "SOCKS5 localhost:1080; DIRECT"; } else { return "DIRECT"; } }
- 上の例はステージング環境のアプリケーションと GitLab に Web アクセスしたい場合のサンプルです。
stg.*.example.com
とgit.example.com
にマッチする URL は SOCKS プロキシでアクセスし、他は直接アクセスします。- 細かい構文なんかは、以下を参考にすると良いです。
ブラウザに設定
今回は proxy.pac
をローカルに置いてますが、踏み台に Web サーバを立てて、そこに置いてもいいかもしれません。
Chrome、IE とかは共通ですけど、Firefox は独自の設定です。お使いのブラウザに合わせて設定してください。
Git アクセスしたい
1. SSH でアクセスする場合
~/.ssh/config
Host stepserver HostName ec2-XXX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com User ec2-user Identityfile ~/.ssh/stepserver.pem Host git.example.com HostName git.example.com IdentityFile ~/.ssh/git_id_rsa ProxyCommand ssh -W %h:%p stepserver
さっきの SOCKS プロキシを使うこともできます。その場合は ProxyCommand
を書き換えます。
ProxyCommand connect -S localhost:1080 %h %p
-S
オプションで SOCKS です。
2. HTTP/HTTPS でアクセスする場合
Git にプロキシの設定をします。 SOCKS プロキシ経由でいけるはずなんですが、今回の環境だとうまく動きませんでした。 Git サーバ側の設定が関係しているかも?
$ git config --global http.proxy localhost:1080 $ git config --global https.proxy localhost:1080 $ git config --list | grep proxy http.proxy=localhost:1080 https.proxy=localhost:1080 $ git clone http://git.example.com/ikasamak/test.git Cloning into 'test'... fatal: unable to access 'http://git.example.com/ikasamak/test.git/': Empty reply from server
MySQL アクセスしたい
ちょっと特殊な環境だったので、ちゃんと説明するとこんな感じです。
Windows 上 の Docker コンテナ (複数) のアプリケーションから MySQL 接続したい。
この場合、複数のコンテナからポートフォワーディングするのはしんどいし、 まずアプリケーションに手を入れたくないので、次の構成にしました。
やっていきます。
1. コンテナからアクセスするためのホスト側 IP アドレスを確認する
DockerNAT となっているものはコンテナから出てくるインタフェースなので、そこにはコンテナから繋がりません。
それ以外なら何でもいいようです。
今回は 192.168.11.2
とします。
2. トンネルを掘る
ssh stepserver -f -N -L 192.168.11.2:3306:mysql.example.com:3306
-L [bind_address:]port:host:hostport
: ローカルポートフォワーディング
3. ホストの hosts
で無理やり名前解決する
192.168.11.2 mysql.example.com
試しにコンテナから ping
を打ってみると、ちゃんと hosts
に書いた IP に飛んでいることが分かります。
$ winpty docker-compose exec db ping -c 1 mysql.example.com PING mysql.example.com (192.168.11.2): 56 data bytes 64 bytes from 192.168.11.2: icmp_seq=0 ttl=37 time=10.231 ms --- mysql.example.com ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max/stddev = 10.231/10.231/10.231/0.000 ms
このやり方は、Docker の DNS 設定が Automatic じゃないと出来ないかもしれません。
4. コンテナから接続確認
$ winpty docker-compose exec db bash root@0258fd400317:/# mysql -h mysql.example.com -u user -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 376064 Server version: 5.5.34-log MySQL Community Server (GPL) by Remi Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
これでうまくいってれば、コンテナ上のアプリケーションもうまく MySQL 接続できると思います!
まとめ
やっていることはどれもだいたい一緒です。
ダイナミックポートフォワーディングは便利なんですが、アプリケーション側が SOCKS プロキシに対応していないと使えないのが難点です。 そこを吸収してくれるソフトウェアもあるっぽいんですが、今回はそこまで踏み込まずにやりました。
あと、Docker for Windows の情報が無さすぎて泣きそうになりました。
参考
- 特定のネットワーク内からしか見れないウェブページを外から見る方法
- Use proxy auto-configuration (.pac) files with IEAK 11 (Internet Explorer Administration Kit 11 for IT Pros) | Microsoft Docs
- GitコマンドをProxy環境可で利用する。 - Qiita
- 外部の SSH サーバーに接続する方法
- Docker コンテナ内からホストの IP アドレスを知る - Qiita
- MySQLにssh経由で接続する色々な方法 | つかびーの技術日記
*1:ここで SOCKS を使わないのは、アプリケーション側での対応が必要になるからです
この間作った Bot は一定時間経つと死ぬことがわかった
さっそく職場で動かしてみたんですが、半日くらい放置してると死ぬことがわかりました。
そもそも
Ruby で Bot を作るなら、こちらの slack-ruby-bot
を使うほうが良さそうです。
slack-ruby-client
の README にも、Bot 作るなら slack-ruby-bot
使えよって書いてあります。
後述しますが、今回僕が作った Bot が死んでる原因にもちゃんと対策されているので、slack-ruby-bot
を使うのがいいんじゃないでしょうか。
やむを得ない事情
職場の Slack に Bot をチャンネルに invite する権限がなくて、今は slack-ruby-bot
の採用を断念してます。*1
そのうち権限を要求したい。
というわけで自前の Bot を無理やり直しました
原因
どうやら、RTM API のコネクション (WebSocket) は一定時間でサーバ側から切断されるようです。そりゃそうか。
対策
RTM API のコネクションが切れたら新しい接続を開始するようにしました。
def start! # RTM Connection client = Slack::RealTime::Client.new client.on :hello do ... client.on :message do |data| ... client.on :closed do |_data| puts "Client has disconnected successfully!" start! end end start!
でもこういうやり方でいいのかしら。ちょっと不安。
Bot の発言に反応する Slack Bot を Ruby で作ってました。
2 月から転職して新しい職場でバタバタしたり、風邪でダウンしたりでなかなかブログが書けませんでした。 いろいろ落ち着いてきたので、これからは週1くらいで何か書けたらなと思っています。
ことのはじまり
https://your-workspace.slack.com/customize/slackbot
つくりました
動作に必要な環境
- docker
- docker-compose
- WebSocket が通信できる環境
準備
1. Bot を Workspace に追加する
追加すると API Token が見れるので、控えておく。
反応するチャンネルに Bot を招待する
2. コードを clone
して .env
をつくる
$ git clone https://github.com/ikasam/slack-bot.git
$ cd slack-bot
$ cp .env.sample .env
.env
に設定する項目
SLACK_BOT_TOKEN=xoxp-XXXXXXXXXXX <- さっき控えた API Token SLACK_CHANNEL=#general <- 反応するチャンネル SLACK_CHANNEL_OPS=#bot-test <- (省略可) 接続成功をお知らせするチャンネル KEYWORD=hoge <- 反応するキーワード
これで準備は完了。
実行
$ docker-compose up -d
これだけ。
動作確認
SLACK_CHANNEL_OPS
を設定しているときは、接続成功時に以下のようにメッセージが飛んできます。
Bot の発言にも反応してくれている!
解説
slack-ruby-client とう gem をつかって作りました。
- Real Time Messaging API というものを使います。WebSocket で通信するので、プロキシ環境下だと動かないかも。
# RTM Connection client = Slack::RealTime::Client.new
- 接続がうまくいったら、Slack が
hello
を送信してきます。
client.on :hello do # 接続時の処理 end
- あらゆるイベントは
message
で送られてきます。
client.on :message do |data| if (not KEYWORD.empty?) and data.channel == CHANNEL_ID case data.text when /#{KEYWORD}/ then client.message channel: data.channel, text: "<!channel> yo!" when /#{BOT_USER_ID}/ then client.message channel: data.channel, text: "<@#{data.user}> Hi." end end end
もっと細かいことをしたいなら、Slack API のドキュメントを読みながらやると良いと思います。
参考
Slack API のドキュメント api.slack.com
slack-ruby-client を使わない実装。Real Time Messaging API の勉強になる。 studio-andy.hatenablog.com
Docker 上の PHP 環境に Composer コンテナでパッケージをインストールする
https://hub.docker.com/r/composer/composer/
リンク先に書いてあることが全てなんですが、それだとあんまりなのでちょっと説明しておきます。
ざっくりいうと、PHP 環境のコンテナとは別に Composer の入ったコンテナを用意して、
docker run --rm
でスポット的に動かします。
1. イメージを pull する
$ docker pull composer/composer
これを書いている時点で、Hub に push されているイメージのバージョンは 1.2.0 でした。
$ docker run --rm composer/composer --version Composer version 1.2.0 2016-07-19 01:28:52
最新のものがほしければ、ソースからビルドすればよいです。
2. composer.json
を書く
適当に欲しいものを書いてください。
{ "require": { "smarty/smarty": "~3.1", "ezyang/htmlpurifier": "*" } }
3. 実行する
$ docker run --rm -v $(pwd):/app composer/composer install Loading composer repositories with package information Updating dependencies (including require-dev) - Installing smarty/smarty (v3.1.31) Downloading: 100% - Installing ezyang/htmlpurifier (v4.9.3) Downloading: 100% Writing lock file Generating autoload files
4. docker run
がめんどくさい人は
PATH
が通ったところにシェルスクリプトを置きましょう。
$ sudo sh -c "cat >/usr/local/bin/composer" <<'EOF' #!/bin/sh export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin echo "Current working directory: '"$(pwd)"'" docker run --rm -v $(pwd):/app -v ~/.ssh:/root/.ssh composer/composer $@ EOF $ sudo chmod +x /usr/local/bin/composer $ composer --version Current working directory: '/home/ikasamak/work/docker-lamp/html/selfphp' Composer version 1.2.0 2016-07-19 01:28:52