[02] docker-composeを活用して即座にPython 3.9.4 を立てる ... ビルドコンテナ実行ツール作成


|本シリーズのトップページ|
|:--|
|https://qiita.com/robozushi10/items/eebd8068a0533ba49bea|

# はじめに

限られた物理PC の中で、次の Python バージョン
・Python 3.6.8
・Python 3.7.3
・Python 3.8.3
・Python 3.9.4 
をインストールしてプログラムを作成することになった.

ただし、物理PC を汚染させるわけにもいかないので Docker を活用した.
(操作しやすいように docker-compose を導入した)

また、物理ホスト側からバッチを使って、極力コンテナに「手動で」ログインせずに
Python コードを実行するようにした.

今後もこの環境構築は頻発すると思うので書き残しておく.

今回は [前回](https://qiita.com/robozushi10/items/fd5f3b0e503db1d62b47)作成した**「ビルドコンテナ」をホスト側から実行するツール** について記す.

なお、今回記すツールは、職場で導入されているツールの挙動を模倣して作成したものであるが、
職場ではもっと保護処理が多く且つ高機能である.

　

# 構築後のファイル構成

```
.
|-- PY-cli-v001.sh ............. 🛑今回作成するファイル (シェルスクリプト)
|-- PV ......................... 永続ボリューム
|   `-- py
|       `-- app ................ この直下に *.py を配置する
|           `-- a.py ........... サンプルコード
|-- assets
|   `-- py
|       |-- Dockerfile
|       `-- requirements.txt ... 導入したい Python パッケージを記述しておく
`-- docker-compose.yml
```


# 条件

[前回](https://qiita.com/robozushi10/items/fd5f3b0e503db1d62b47) の手順で実行環境を構築済みであるものとする


# 手順

## 1. PY-cli-v001.sh を作成する

下記コードは長々と書いてはあるが、結局のところ次を実行することが目的である.

> docker-compose exec py bash -c <コマンド>

で、上記実行時の付加機能として次を追加しているだけである.
・`-p` でコンテナ内でのカレントディレクトリを指定できる
・`-e` で環境変数を指定できる
・`-v` や `-V` で実行ログを出す

つまり、コードを読み解くくらいなら作成した方が早いので参考程度に.

```bash
#!/bin/bash
 
# docker-compose.yml で定義したサービス名
SVC=py
  
# オプション解析
ARGENV=()
while getopts :e:p:c:f:t:vV OPT; do
  case $OPT in
    e) ARGENV=(${ARGENV[@]} "-e $OPTARG") ;; # 環境変数を「-e」で繋げていく
    p) ARGPRJ=$OPTARG ;;       # Dockerコンテナ内でのプロジェクトのディレクトリ名
    c) CLIMODE="cli"
       break ;;      
    f) ARGDCY=$OPTARG ;;       # docker-compose.yml が別名の場合
    v) ARGDBGL="1"    ;;       # 弱デバッグモード
    V) ARGDBGL="1"
       ARGDBGH="1"    ;;       # 強デバッグモード
    t) ARGMODE="$OPTARG" ;;    # init, run, build,...etc
    :) echo "Mission arg: -$OPTARG" >&2; exit 1 ;;    # オプション引数がない (OPT = :)
    *) echo "Unknown option: -$OPTARG" >&2; exit 1 ;; # 不正なオプション (OPT = ?)
  esac
done
 
test "$CLIMODE" = "cli" && {
  shift $((OPTIND - 2)) 
} || {
  shift $((OPTIND - 1)) 
}
 
# -f は, docker-compose.yml という名称以外の場合に使用する.
test -z "$ARGDCY" && ARGDCY="docker-compose.yml"
 
test ! -z "$ARGDBGH" && {
  echo "[-e] ARGENV  => ${ARGENV[@]}"
  echo "[-p] ARGPRJ  => $ARGPRJ"
  echo "[-f] ARGDCY  => $ARGDCY"
  echo "[-t] ARGMODE => $ARGMODE"
  echo "[-c] ARGMODE => $@"
}
 
# -p で指定できるディレクトリは相対パス表記のみとする. 例: ./usr/src/app か usr/src/app
# 「././PV/py/src/」といったドット連続や絶対パス指定はエラーとする.
case $ARGPRJ in
  /*) echo "Error: absolute path not allowed: ARGPRJ => <$ARGPRJ>"
      exit 2 ;;
  ..) echo "Error: could not specify a different layer: ARGPRJ => <$ARGPRJ>"
      exit 2 ;;
  ././*) echo "Error: enter the dot duplicates not allowed: ARGPRJ => <$ARGPRJ>"
      exit 2 ;;
  ./*) DKRPRJ=`echo $ARGPRJ | sed 's%^./%/%g'` ;;
  *) DKRPRJ="/$ARGPRJ" ;;
esac
 
test ! -f $ARGDCY && {
  echo 'ERROR: could not found ./docker-compose.yml'
  exit 3
}
  
# ここでの ARGCLI に指定したコマンドが、コンテナ内部で実行される.
case "$ARGMODE" in
  *) # -c オプションで指定されたコマンド
    test ! -z "$DKRPRJ" && {
      ARGCLI="test -d $DKRPRJ && cd $DKRPRJ && $@"
    } || {
      ARGCLI="$@"
    }
    ;;
esac
 
# docker-compose exec <環境変数> <SVC> <コマンド> を実行する
docker-compose exec ${ARGENV[@]} $SVC bash -c "
  test ! -z \"$ARGDBGL\" && set -x
  eval $ARGCLI || {
      # eval $ARGCLI に失敗した場合
      echo \"[$ARGMODE]:ERROR: eval $ARGCLI\"
      test ! -z \"$ARGDBGL\" && set +x
      test ! -z \"$ARGDBGH\" && {
        echo \"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\"
        echo \"Debug Information \"
        echo \"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\"
        echo \"----------------------------------------------------------------\"
        echo \"# pwd\"
        echo \"----------------------------------------------------------------\"
        pwd
        echo \"----------------------------------------------------------------\"
        echo \"# env\"
        echo \"----------------------------------------------------------------\"
        env
      }
      exit 9
  }
  test ! -z \"$ARGDBGL\" && set +x
  test ! -z \"$ARGDBGH\" && {
    echo \"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\"
    echo \"Debug Information \"
    echo \"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\"
    echo \"----------------------------------------------------------------\"
    echo \"# pwd\"
    echo \"----------------------------------------------------------------\"
    pwd
    echo \"----------------------------------------------------------------\"
    echo \"# env\"
    echo \"----------------------------------------------------------------\"
    env
  }
"
test $? -ne 0 && {
    test ! -z "$ARGDBGH" && {
      echo "----------------------------------------------------------------"
      echo "\$ tree ./PV/$SVC --charset=C -a"
      tree ./PV/$SVC --charset=C -a
    }
    exit 44
}
 
test ! -z "$ARGDBGH" && {
  echo "----------------------------------------------------------------"
  echo "\$ tree ./PV/$SVC --charset=C -a"
  tree ./PV/$SVC --charset=C -a
}



```


## 2. 実行する

### 通常実行モード

a.py は[初回](https://qiita.com/robozushi10/items/fd5f3b0e503db1d62b47) で作成したコードであり、
単に「Hello Python 0394」と引数を表示するだけのコードである.

```bash
$ bash PY-cli-v001.sh -p usr/src/app -c 'python a.py 1 2 3'
Hello Python 0394
1
2
3
```

もう少し複雑な引数を与えてみる

```bash
$ bash PY-cli-v001.sh -p usr/src/app -c 'python a.py \'dateコマンドを実行します\' "`date`"'
Hello Python 0394
dateコマンドを実行します
Wed Aug  4 14:34:10 JST 2021

```

### 弱デバッグモード

`-v` を付与する

```bash
$ bash PY-cli-v001.sh -p usr/src/app -v -c 'python a.py 1 2 3'
+ eval test -d /usr/src/app
++ test -d /usr/src/app
+ cd /usr/src/app
+ python a.py 1 2 3
Hello Python 0394
1
2
3
+ test '!' -z 1
+ set +x
```

### 強デバッグモード

`-V` を付与する.

```bash
$ bash PY-cli-v001.sh -p usr/src/app -V -c 'python a.py 1 2 3'
[-e] ARGENV  => 
[-p] ARGPRJ  => usr/src/app
[-f] ARGDCY  => docker-compose.yml
[-t] ARGMODE => 
[-c] ARGMODE => python a.py 1 2 3
+ eval test -d /usr/src/app
++ test -d /usr/src/app
+ cd /usr/src/app
+ python a.py 1 2 3
Hello Python 0394
1
2
3
+ test '!' -z 1
+ set +x
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Debug Information 
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
----------------------------------------------------------------
# pwd
----------------------------------------------------------------
/usr/src/app
----------------------------------------------------------------
# env
----------------------------------------------------------------
PYTHONUNBUFFERED=1
HOSTNAME=6ad7045545ae
PYTHON_VERSION=3.9.4
LANGUAGE=ja_JP.UTF-8
PWD=/usr/src/app
HOME=/root
LANG=ja_JP.UTF-8
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
TERM=xterm
SHLVL=1
PYTHON_PIP_VERSION=21.1.1
PYTHONDONTWRITEBYTECODE=1
PYTHON_GET_PIP_SHA256=f499d76e0149a673fb8246d88e116db589afbd291739bd84f2cd9a7bca7b6993
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/1954f15b3f102ace496a34a013ea76b061535bd2/public/get-pip.py
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
OLDPWD=/usr/src/app
_=/usr/bin/env
----------------------------------------------------------------
$ tree ./PV/py --charset=C -a
./PV/py
`-- app
    `-- a.py

1 directory, 1 file

```