ksaitoの日記

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

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が稼働しているリージョンを有効化することで解決しました。

ネットワーク遅延

ネットワーク遅延について理解が曖昧だったのでおさらいしました。

調べ方

測定したいサーバのいずれかからpingコマンドを使って調べます。

gceのインスタンスからヤフージャパンとyahooを測定します。

$ ping -c 2 www.yahoo.co.jp
PING ***.jp (***.***.***.***): 56 data bytes
64 bytes from 183.79.248.252: icmp_seq=0 ttl=51 time=132.073 ms
64 bytes from 183.79.248.252: icmp_seq=1 ttl=51 time=131.703 ms
--- edge.g.yimg.jp ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 131.703/131.888/132.073/0.185 ms
$ ping -c 2 www.yahoo.com
PING ***.jp (***.***.***.***): 56 data bytes
64 bytes from 98.139.180.181: icmp_seq=0 ttl=48 time=29.914 ms
64 bytes from 98.139.180.181: icmp_seq=1 ttl=48 time=29.397 ms
--- atsv2-fp.wg1.b.yahoo.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 29.397/29.655/29.914/0.259 ms
$ 

各サイトは、単なるサンプルなので以降は、サンプル1、サンプル2とします。

読み方

最初の1行目で送信先IPアドレスとデータ部が56バイトのパケットを送信していることがわかります。パケットサイズは、-sオプションで指定が可能です。

-cで指定した回数送信が試みられます。1パケット1行の結果が表示されます。56バイトのデータ部と8バイトのヘッダで計64バイトのパケットが送信されます。time=がRTTです。

RTT(round-trip time)は、パケットを送信して応答があるまでの時間です。

最後の2行は、パケットロスの発生とRTTの統計情報がでます。

スループット

パケットの最大サイズとRTTからネットワークのスループットが分かります。論理的に最大で送信できるデータ量を把握したり、複数の測定結果を比較することで性能差や改善を確認するといったことができるようになります。

パケットの最大サイズ

ifconfigで対象のNICを指定してパケットの最大サイズを確認できます。例えば、下記のようにするとeth0のMTUを見ることでパケットの最大サイズが1,460バイトであることがわかります。

$ ifconfig eth0 | grep MTU
          UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
$ 

スループット

最初のサンプル1とサンプル2のRTTを使ってスループットを計算します。

計算 サンプル1 サンプル2
1秒間に送信できるパケット数 1,000ミリ秒÷131.888≒7.6回 1,000ミリ秒÷29.655≒33.7回
1秒間に送信できる最大サイズ(バイト/秒) 1,460バイト✕7.6回≒11,070バイト/秒 1,460バイト✕33.7≒49,233バイト
1秒間に送信できる最大ビット(ビット/秒) 11,070✕8ビット≒88,560ビット/秒 49,233✕8ビット≒393,863ビット
スループット 88,560÷1024≒86Kbps 393,863÷1024≒385Kbps

データ送信にかかる時間を計算する

スループットと送信データサイズから送信にかかる時間を計算します。

サンプル1のスループットで1GBのデータを送信するのにかかる時間です。

計算 サンプル1の計算式
データサイズ 8,589,934,592ビット=1GB✕1024✕1024✕1024✕8ビット
回線速度 88,064bps≒86Kbps✕1024
転送時間 8,589,934,592bit÷88,064bit✕伝送効率

転送効率が100%だったとしても27時間くらいかかります。

実測値から転送効率を計算する

実際にデータ送信にかかった時間から転送効率を計算します。

サンプル1のスループットで1GBのデータを送信するのに40時間かかった場合の転送効率です。

計算 サンプル1の計算式
送信時間 144,000秒 = 40時間✕60分✕60秒
データサイズ 8,589,934,592ビット=1GB✕1024✕1024✕1024✕8ビット
回線速度 88,064bps≒86Kbps✕1024
転送速度 59,652bps = 8,589,934,592ビット÷144,000秒
転送効率 0.677≒59,652bps÷88,064bps

このケースでは、転送効率は、67.7%となります。

gitで過去のコミットを書き換える

gitの最初のcommitにパスワードを書き込んでpushしたとします。さらに、しばらくcommit/pushを繰り返したとします。

下記の手順でパスワードを全てマスクした文字に書き換える事ができますが、cloneして作業している人すべてに影響がでるので作業前には関係者への連絡と準備が必須です。

事前確認

ブランチがクリーンなことを確認します。

$ git status
ブランチ master
nothing to commit, working tree clean
$

下記のようにpasswordが含まれているファイルをを確認します。

$ git grep password
file.txt:password
$

書き換え

filter-branch--tree-filterオプションで各コミットに実行するコマンドを指定します。 確認したfile.txtsedで書き換えます。

$ git filter-branch -f --tree-filter "sed -i -e 's/password/mask/' file.txt" HEAD
Rewrite c5974de297ec5360d0dfce499abccc3073ba0211 (2/2) (0 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten
$

確認

下記の通りpasswordは、消えて…

$ git grep password
$

maskになっています。

$ git grep mask
file.txt:mask
$

以上

pythonの開発環境その2

以前にdirenvとvirtualenvでpythonの開発環境を作る手順を書きました。

ksaito11.hatenablog.com

pythonzを使うとpythonのバージョンも選択できるようになります。

前提パッケージ

ubuntu17.04に下記の前提パッケー時をインストールします。

$ apt-get install -y \
            build-essential \
            zlib1g-dev \
            libbz2-dev \
            libssl-dev \
            libreadline-dev \
            libncurses5-dev \
            libsqlite3-dev \
            libgdbm-dev \
            libdb-dev \
            libexpat-dev \
            libpcap-dev \
            liblzma-dev \
            libpcre3-dev \
            curl \
            python-pip \
            virtualenv

pythonzのインストー

pythonzのREADME.mdの手順をansibleのplaybookにしました。

$ cat python.yml
---
- name: python develop environment
  hosts: all
  tasks:
    - name: pythonzのインストール確認
      stat: path="{{ ansible_user_dir }}/.pythonz/etc/bashrc"
      register: stat
    - name: pythonzのインストール
      shell: curl -kL https://raw.github.com/saghul/pythonz/master/pythonz-install | bash
      when: not stat.stat.exists
    - name: pythonzの設定
      lineinfile:
        dest: "{{ ansible_user_dir }}/.bashrc"
        state: present
        line: "[[ -s $HOME/.pythonz/etc/bashrc ]] && source $HOME/.pythonz/etc/bashrc"
$ ansible-playbook python.yml -c local

pythonのインストー

使いたいバージョンのpythonをインストールします。

$ pythonz install 3.5.0

direnvの設定

direnvの設定は、前回の記事を参照下さい。

direnvに使いたいバージョンを

$ direnv edit .
$ cat .envrc
layout python $(pythonz locate 3.5.0)
$

これで、使いたいバージョンのpythonを利用することが出来ます。

$ cd tmp
direnv: loading .envrc
direnv: export +VIRTUAL_ENV ~PATH
(0)18:31:05 ksaito@dind$ python --version
Python 3.5.0
$

pipのパッケージも環境毎に独立した物になります。

$ pip list --format=columns
Package       Version
------------- -------
appdirs       1.4.3
boto          2.46.1
packaging     16.8
pip           9.0.1
pkg-resources 0.0.0
setuptools    35.0.2
six           1.10.0
wheel         0.29.0
$