ksaitoの日記

日々試したことの覚え書き

ファイルの作成と削除を待つ

シェルでファイルの作成と削除を待つのにinotify-toolsを使います。

インストール

aptでインストールします。

$ sudo apt install -y inotify-tools

ファイルの作成と削除を待つ

waitfile.shに指定した引数のファイルが作成されるのをfilewaitで待ちます。 その後、そのファイルが削除されるのをrmwaitで待ちます。

$ cat waitfile.sh
#! /bin/bash

fname=$1

filewait()
{
    ret=1
    while [ $ret == 1 ]
    do
        inotifywait -q -e create -t 1 . | grep $fname
        ret=$?
    done
}

rmwait()
{
    ret=1
    while [ $ret == 1 ]
    do
        inotifywait -q -e delete -t 1 . | grep $fname
        ret=$?
    done
}

echo waiting for $fname
filewait
echo $fname created!, waiting for $fname remove
rmwait
$

notifywaitの動作

最初は、よくわからず下記のようににファイルを指定してcreateイベントを待とうとしましたが、ファイルがないとNo such fileとなります。

$ inotifywait -e create fin.txt
Setting up watches.
Couldn't watch fin.txt: No such file or directory
$

ファイルがあると永遠に待ちます。

$ inotifywait -e create fin.txt
Setting up watches.
Watches established.

ディレクトリを指定すると次のようになります。 実行後に別の端末からfin.txtを作成すると./CREATE fin.txtが発生します。

$ inotifywait -e create .      
Setting up watches.
Watches established.
./ CREATE fin.txt
$

色々なファイルシステムで試してみると面白いかもしれません。

Raspberry pi 3

Raspberry pi 3のOSイメージをSDカードに約手順です。

環境

SDカード

diskutilでSDカードを認識しているデバイスを確認します。環境毎にことなります。 今回使ったMacBookのSDカードすロッドは、/dev/disk2でした。

フォーマット

下記のコマンドでフォーマットします。

$ diskutil eraseDisk FAT32 "RASPBERRYPI" MBR /dev/disk2
Started erase on disk2
Unmounting disk
Creating the partition map
Waiting for the disks to reappear
Formatting disk2s1 as MS-DOS (FAT32) with name RASPBERRYPI
512 bytes per physical sector
/dev/rdisk2s1: 30338080 sectors in 1896130 FAT32 clusters (8192 bytes/cluster)
bps=512 spc=16 res=32 nft=2 mid=0xf8 spt=32 hds=255 hid=8192 drv=0x80 bsec=30367744 bspf=14814 rdcl=2 infs=1 bkbs=6
Mounting disk
Finished erase on disk2
$

アンマウント

$ mount | grep disk2
/dev/disk2s1 on /Volumes/RASPBERRYPI (msdos, local, nodev, nosuid, noowners)
$ diskutil umountDisk disk2
Unmount of all volumes on disk2 was successful
$ mount | grep disk2
$ 

イメージの書き込み

ダウンロードしたOSイメージを書き込みます。

$ sudo dd bs=1m if=raspi.img of=/dev/disk2
Password:
7535+0 records in
7535+0 records out
7901020160 bytes transferred in 3354.628015 secs (2355260 bytes/sec)
$ 

コンテナとファイルのやりとり

docker cpコマンドでも良いのですが、ディレクトリごと出し入れしたい場合には、tarコマンドを使った方が便利です。

tar-Cオプオションは、tarを実行する前に指定されたディレクトリに移動します。 tar -C /tmp -cf - logは、(cd /tmp; tar cf - log)と同じ意味になりますがdockerコマンドで実行した場合、tar-Cオプションを使ったほがシンプルにかけます。

これを使って、コンテナとファイルをやりとりするパターンを考えます。

コンテナからホスト

ubuntuのログディレクトリを丸ごと、取り出したい場合は、次のようにします。

$ find
.
$ docker run --rm ubuntu:17.04 tar -C /var -cf - log | tar xf -
$ find
.
./log
./log/wtmp
./log/faillog
./log/dpkg.log
./log/alternatives.log
./log/apt
./log/apt/history.log
./log/apt/eipp.log.xz
./log/bootstrap.log
./log/btmp
./log/lastlog
$ 

すでに起動しているコンテナであればdocker execを使います。

$ docker exec コンテナ名 tar -C /var -cf - log | tar xf -

ホストからコンテナ

ホストのtarの結果をリダイレクトしてdocker execに処理させます。 -iオプションを忘れると標準入力が割り当てられず、ずーっと入力待ちになってしまいます。

$ tar cf - log | docker exec -i target tar -C /tmp -xf -

コンテナからコンテナへ

上記の2つを応用して、コンテナ間で一時ファイルを作らずにデータをやり取りできます。

srcコンテナの/var/logをtargetコンテナの/tmpディレクトリにコピーするには次のようにします。

$ docker exec target ls /tmp
$ docker exec src tar -C /var -cf - log | docker exec -i target tar -C /tmp -xf -
$ docker exec target ls /tmp
log
$

おまけ

tar-Cオプションが使えない場合、下記のようにすると同じことができます。

$ docker run --rm ubuntu:17.04 bash -c "(cd /var; tar cf - log)" | tar xf -

Dockerfileでexitステータスが0以外を返す処理を扱う

Dockerfileで外部のシェルを実行する場合に、そのシェルが正常終了したのに終了ステータスが0以外を返すことがあります。 dockerは、0以外のステータスが返されるとビルドが失敗したと判断して処理を中断してしまいます。

Dockerfileを下記のように記載することで、ステータスを正しく認識してビルドを完了させることができます。

サンプル

lsコマンドで存在しないファイル名を指定すると終了ステータスは、2になります。

$ ls sadfasf
ls: 'sadfasf' にアクセスできません: そのようなファイルやディレクトリはありません
$?
2
$

このコマンドを外部シェルに見立ててDockerfileを作成します。 3行目の|| RET=$?で終了ステータスをRETに保持し、|| trueで終了ステータスに関係なく処理を正常終了させます。 4行目の&& test $RET -eq 2で0以外で正常と判断するステータスを検査します。

$ cat -n Dockerfile
     1  FROM oraclelinux:7-slim
     2
     3  RUN ls sadfasf || RET=$? || true \
     4   && test $RET -eq 2
$

下記のように正しくビルドできます。

$ docker build -t test .
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM oraclelinux:7-slim
 ---> c0feb50f7527
Step 2/2 : RUN ls asdfa || RET=$? || true  && test $RET -eq 2
 ---> Using cache
 ---> cb6a5b73e7a1
Successfully built cb6a5b73e7a1
$

2行目を書き換えて、存在するパスを指定します。

$ cat -n Dockerfile
     1  FROM oraclelinux:7-slim
     2
     3  RUN ls / || RET=$? || true \
     4   && test $RET -eq 2
$

終了ステータスが0になるとエラーと判断してビルドに失敗します。 これで、どのようなステータスを返すシェルであっても正しく扱うことができます。

$ docker build -t test .
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM oraclelinux:7-slim
 ---> c0feb50f7527
Step 2/2 : RUN ls / || RET=$? || true  && test $RET -eq 2
 ---> Running in 444fb925563a
/bin/sh: line 0: test: -eq: unary operator expected
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
The command '/bin/sh -c ls / || RET=$? || true  && test $RET -eq 2' returned a non-zero code: 2
$

su: cannot open session: Permission denied

Oracle 12cをOracle Linuxにインストールする際に、事前準備をするパッケージがあります。 これを設定すると、oracleユーザのsuできません。

設定

インストールマニュアルに従って下記のように設定します。

$ cd /etc/yum.repos.d
$ curl -O http://public-yum.oracle.com/public-yum-ol7.repo
$ yum install -y oracle-database-server-12cR2-preinstall
$

現象

suすると下記のようなエラーが出ます。

bash-4.2# su - oracle
Last login: Sat Sep  2 01:35:56 UTC 2017 on pts/0
su: cannot open session: Permission denied
bash-4.2#

対応

memlockの設定をコメントアウトすると現象は、回避できます。

bash-4.2# sed -i -e 's/^\(oracle   hard   memlock\)/#\1/' /etc/security/limits.d/oracle-database-server-12cR2-preinstall.conf
bash-4.2# su - oracle
Last login: Sat Sep  2 01:40:15 UTC 2017 on pts/0
[oracle@oracle12c ~]$

dockerコンテナからkeychainを使ってgithubに接続する際に困ったこと

かなり前のブログに、ssh秘密鍵パスフレーズを設定し、keychainを使って毎回パスフレーズを入力しなくて良い方法を書きました。

ksaitoの日記: keychain

この方法をdockerコンテナに応用した場合、ちょっと厄介です。

例えば、こんな風にgithubに接続済みの環境があります。

$ /usr/bin/ssh -T git@github.com
Hi ****! You've successfully authenticated, but GitHub does not provide shell access.
$

環境をdocker化して下記のように実行するとエラーになります。

$ docker exec -it dev /usr/bin/ssh -T git@github.com
Permission denied (publickey).
$

docker execは、デフォルトだとrootで接続となるのでユーザを指定します。

$ docker exec dev id
uid=0(root) gid=0(root) groups=0(root)
$ docker exec -u ksaito dev /usr/bin/ssh -T git@github.com
Permission denied (publickey).
$

keychainを使うのに必要な環境変数が必要だろうということでログインシェルを読み込ませてみるとうまくいきました。

$ docker exec -u ksaito dev bash -lc "/usr/bin/ssh -T git@github.com"
Hi ***! You've successfully authenticated, but GitHub does not provide shell access.
$

.bash_profileに書かれているkeychain環境変数の設定が必要ということがわかりました。

$ docker exec -u ksaito dev bash -c "source ~/.keychain/`hostname`-sh; /usr/bin/ssh -T git@github.com"
Hi ***! You've successfully authenticated, but GitHub does not provide shell access.
$

ansiblegitタスクをdocker connection pluginでセットアップする場合には、ログインシェルを読み込ませることができるのか調べる必要があります…

ECSにELBやAutoScaleを設定するときに必要な設定

ECSにサービスを登録する際に、ELBとAutoScaleを設定すると下記のエラーが表示されました。

これは、認証情報-アカウント設定の「Security Token Service リージョン」が無効担っているためで、同メニューからECSが稼働しているリージョンを有効化することで解決しました。