オフライン本番環境での機械学習実行環境構築シナリオとAnsibleによる構築自動化(前編)

初版: 2020年12月4日

著者: 株式会社 日立ソリューションズ 堤 友希

監修: 株式会社 日立製作所


# はじめに
本投稿では外部インターネットからのセキュリティ攻撃を避ける必要のある本番環境の構築を想定し、
オフラインのベアメタル/仮想マシンにAnacondaを利用して機械学習の実行環境を構築します。

Anacondaの説明を下表に示します。

表1 Anacondaの説明

| パッケージ名 | 説明 |
| ------ | ------ |
| Anaconda | Anaconda はデータサイエンス向けのPythonパッケージなどを提供するプラットフォーム。デフォルトパッケージ数は約300。 |
| Miniconda | Anacondaの縮小版。デフォルトパッケージ数は約30。 | 

Anacondaはデフォルトで多数のパッケージが入っていて、
データサイエンティストやPythonベースのアプリ開発者、データエンジニアなどの
検証に利用されています。

一方で、本番環境では、提供するサービスなどに関係しないパッケージは
パッケージの脆弱性を突いたセキュリティ攻撃への対策や
エラーなどに起因する事故の未然防止、ライセンス調査/適合性審査の工数削減、
軽量化などの観点からできるだけ除きたいというニーズがあります。

上記のニーズを持つユーザには、本番環境で必要なパッケージのみを
選択的に利用できるMinicondaの方が向いています。

本投稿では、オフライン本番環境でのMinicondaによる機械学習実行環境の構築手順を示します。

後編でその手順をAnsibleで自動化する方法を紹介します。

*なお、商用でAnacondaのリポジトリを使用する場合はAnaconda Commercial Edition等のライセンスを購入する必要があります。詳細は下記リンクをご確認ください
https://www.anaconda.com/terms-of-service


# 投稿一覧
1. オフライン本番環境での機械学習実行環境構築シナリオとAnsibleによる構築自動化（前編）・・・本投稿
2. オフライン本番環境での機械学習実行環境構築シナリオとAnsibleによる構築自動化（後編）

# Minicondaを活用したオフライン本番環境における機械学習実行環境の構築チュートリアル

図1の概要図に示すように、本投稿では、本番環境に必要な最小限のライブラリ群（本投稿ではこれを任意パッケージと呼んでいます）を
オフラインの本番環境にインストールする手順を示します。

なお、図中のカスタムチャネルとは、オフライン環境でのインストール時にMinicondaが接続するローカルリポジトリのことで、
インストール対象のライブラリ群とその依存関係情報を含んでいます。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865566/13e87a60-6f35-8be6-46d6-0b4d98c8f4ac.png)

図1 Miniconadaを活用した本番環境構築の概要

## コンテナで実施すればよいのでは？

たしかに機械学習実行環境をRHELコンテナ内に実装し、
検証環境で実装したコンテナを本番環境にデプロイするのがよさそうに思えます。

しかし、今回想定している本番環境ではdockerを導入することができないということを前提としています。

## pipではダメなの？

pipは、本チュートリアルではAnacondaの代わりのパッケージ管理ソフトウェアとして利用できます。

pipと比してのAnacondaのメリットとしては、仮想環境構築が単体でできる点が挙げられます。

データサイエンティストは種々の検証のために、検証環境では多数の仮想環境を構築する必要があると想定されます。

その検証の容易さを評価し、今回はAnacondaを選択してみました。

検証で利用した仮想環境は、そのまま本番環境に移行したいという考え方を採用するため、
検証環境はAnacondaで、本番環境ではpipなどの使い分けも今回は実施しません。

## 前提のOS/ソフトウェアバージョン

OSとしては、本番環境用には商用利用可能なRHELを、検証環境用にはCentOSを選択しました。

表2 チュートリアルに利用するOS

| OS | バージョン | アーキテクチャ |
| ------ | ------ | ------ |
| RHEL | 7.6 | x64 |
| CentOS | 7.6 | x86_64 |

前述のように、今回はパッケージ管理ツールとしてMinicondaを利用します。

表3 チュートリアルに利用するMinicondaのバージョン

| パッケージ名 | バージョン |
| ------ | ------ |
| Miniconda | 4.7.12 |

任意パッケージについては、メジャーな機械学習モデル開発フレームワークのTensorFlowと
見やすいGUIのpython実行環境を提供するjupyter notebookを選択しました。

表4 チュートリアルに利用する任意パッケージとそのバージョン

| パッケージ名 | バージョン |
| ------ | ------ |
| Python | 3.7.6 |
| TensorFlow | 2.1.0 |
| notebook | 6.0.3 |

表5 チュートリアル作業の自動化に利用するAnsibleのバージョン

| パッケージ名 | バージョン |
| ------ | ------ |
| Ansible | 2.9.9 |

## Minicondaインストール&仮想環境構築

今回は、検証環境と本番環境ともにMinicondaをインストールします。

Minicondaインストーラは以下で取得できます。

Miniconda3-4.7.12-Linux-x86_64.sh

https://repo.anaconda.com/miniconda/Miniconda3-4.7.12-Linux-x86_64.sh

別バージョンが必要な場合、Minicondaインストーラは以下のアーカイブより検索し、取得できます。

https://repo.anaconda.com/miniconda/

以下にCentOSでMinicondaをインストールする例を示します。

```:CentOSのコンソール画面
[root@localhost ~]# bash Miniconda3-4.7.12-Linux-x86_64.sh

Welcome to Miniconda3 4.7.12

In order to continue the installation process, please review the license
agreement.
Please, press ENTER to continue
>>>
===================================
Miniconda End User License Agreement
===================================

Do you accept the license terms? [yes|no]
[no] >>> yes

Miniconda3 will now be installed into this location:
/root/miniconda3

  - Press ENTER to confirm the location      #この選択肢に従い、ENTERを押下。
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/root/miniconda3] >>> 
					・
					・
					・
Do you wish the installer to initialize Miniconda3
by running conda init? [yes|no]
[no] >>> yes
					・
					・
					・
Thank you for installing Miniconda3!
[root@localhost ~]#
```

図2 Minicondaのインストール例

次に仮想環境を構築します。

```:CentOSのコンソール画面
#bash起動時設定読み込みファイル”.bashrc”にminicondaの初期化設定が反映されているので、
再読み込みを実施します。
[root@localhost ~]# source .bashrc
(base) [root@localhost ~]#

#仮想環境を作成します。ここでは”testenv”という名前の仮想環境にしてみました。

(base) [root@localhost ~]# conda create -n testenv -y --offline
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: /root/miniconda3/envs/testenv



Preparing transaction: done
Verifying transaction: done
Executing transaction: done
```

図3-1 仮想環境構築例-1

## 任意パッケージのインストール

```:CentOSのコンソール画面
#作成した仮想環境に移行します。

(base) [root@localhost ~]# conda activate testenv
(testenv) [root@localhost ~]#

#以降は検証環境のみでの手順です。
#まず、任意パッケージに含める各パッケージを仮想環境にインストールしてしまいます。
#こうすることで、各パッケージとその依存ライブラリのダウンロードURL(詳細は後述)を
#取得できるようになるため、後でカスタムチャネル作成時に各パッケージをダウンロードする
#際の工数を削減できます。
#はじめに、python以外の任意パッケージのpythonバージョンを固定するために、pythonをインストールします。
#プロキシ環境下では、デフォルトでは以下のようなConnectionErrorが出力します。

(testenv) [root@localhost ~]# conda install python=3.7.6 -y
Collecting package metadata (current_repodata.json): failed

CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/main/linux-64/current_repodata.json>
Elapsed: -

An HTTP error occurred when trying to retrieve this URL.
HTTP errors are often intermittent, and a simple retry will get you on your way.

If your current network has https://www.anaconda.com blocked, please file
a support request with your network engineering team.

ConnectionError(MaxRetryError("HTTPSConnectionPool(host='repo.anaconda.com', port=443): Max retries exceeded with url: /pkgs/main/linux-64/current_repodata.json (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f1035724210>: Failed to establish a new connection: [Errno -2] Name or service not known'))"))

#このような場合は、/rootディレクトリにMinicondaの設定ファイルである”.condarc”を作成し、プロキシ設定をします。

(testenv) [root@localhost ~]# vi .condarc

#“.condarc”に記述する内容を以下に示します。

proxy_servers:
  http: http://<username>:<password>@<proxy>:8080
  https: https://<username>:<password>@<proxy>:8080
ssl_verify: <SSL証明書の配置パス>/<SSL証明書ファイル名> #SSL証明書あれば

#プロキシが通ったので、インストールできるようになりました。
#TensorFlowとnotebookも同じ要領でインストールします。

(testenv) [root@localhost ~]# conda install python=3.7.6 -y
Collecting package metadata (current_repodata.json): done
Solving environment: failed with initial frozen solve. Retrying with flexible solve.
Collecting package metadata (repodata.json): done
Solving environment: done
					・
					・
					・
pip-20.2.4           | 1.7 MB    | ##################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done


```

図3-2 仮想環境構築例-2


## 任意パッケージのダウンロード


次に任意パッケージのダウンロードの例を示します。

```:CentOSのコンソール画面

#パッケージのインストールが完了したら、conda list –explicit でインストールしたパッケージのダウンロードURL情報が出力できます。

(testenv) [root@localhost ~]# conda list --explicit
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: linux-64
@EXPLICIT
https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda
https://repo.anaconda.com/pkgs/main/linux-64/_tflow_select-2.3.0-mkl.conda
					・
					・
					・

#カスタムチャネル化するディレクトリ内に、OSのアーキテクチャ別のディレクトリを作成し、
#アーキテクチャ別に任意パッケージとその依存ライブラリを分けます。
#後述しますが、分ける理由はカスタムチャネル化に必要な手順のためです。
#下記の例では、custom-channelというディレクトリに、linux-64とnoarchのディレクトリを作成しています。

(testenv) [root@localhost custom-channel]# tree
.
├─ linux-64
└─ noarch

#各パッケージがどのアーキテクチャに対応しているかはダウンロードURLを見れば判別できます。

(testenv) [root@localhost ~]# conda list --explicit
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: linux-64
@EXPLICIT
https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda
https://repo.anaconda.com/pkgs/main/linux-64/_tflow_select-2.3.0-mkl.conda
					・
					・
					・
https://repo.anaconda.com/pkgs/main/noarch/rsa-4.6-py_0.conda
					・
					・
					・

#ダウンロードURL情報を利用して、ダウンロードスクリプトを作成します。
#ダウンロードツールとしてwgetを利用し、norarchのパッケージをダウンロードするスクリプトの例を以下に示します。

#!/bin/sh
wget https://repo.anaconda.com/pkgs/main/noarch/attrs-20.2.0-py_0.conda && \
wget https://repo.anaconda.com/pkgs/main/noarch/backcall-0.2.0-py_0.conda && \
					・
					・
					・
wget https://repo.anaconda.com/pkgs/main/noarch/keras-preprocessing-1.1.0-py_1.conda && \
wget https://repo.anaconda.com/pkgs/main/noarch/tensorflow-estimator-2.1.0-pyhd54b08b_0.conda

#プロキシ下の環境でダウンロードする場合、wgetのデフォルト設定ではダウンロードに失敗します。
#下記はlinux-64のディレクトリにダウンロードしようとして失敗した例です。

(testenv) [root@localhost linux-64]# ./linux-64_dl-script.sh
--2020-11-03 00:05:23--  https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda
repo.anaconda.com (repo.anaconda.com) をDNSに問いあわせています... 失敗しました: 名前またはサービスが不明です.
wget: ホストアドレス `repo.anaconda.com' を解決できませんでした。

#rootディレクトリにwgetの設定ファイルである”.wgetrc”を作成し、プロキシ設定をします。

(testenv) [root@localhost ~]# vi .wgetrc

#プロキシ下でダウンロード可能にするため、”.wgetrc”を編集し、wgetに対しプロキシの設定を実施します。

use_proxy=yes
http_proxy=http://<username>:<password>@<proxy>:8080
https_proxy=https://<username>:<password>@<proxy>:8080

#再度ダウンロードスクリプトを実行すると、ダウンロードが開始されます。
#noarchでも同じ要領で実施します。

(testenv) [root@localhost linux-64]# ./linux-64_dl-script.sh
--2020-11-03 00:11:30--  https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda
obprx01.intra.hitachi.co.jp (obprx01.intra.hitachi.co.jp) をDNSに問いあわせています... 158.213.204.12
obprx01.intra.hitachi.co.jp (obprx01.intra.hitachi.co.jp)|158.213.204.12|:8080 に接続しています... 接続しました。
Proxy による接続要求を送信しました、応答を待っています... 200 OK
長さ: 3473 (3.4K) [binary/octet-stream]
`_libgcc_mutex-0.1-main.conda' に保存中

100%[===========================================================================>] 3,473       --.-K/s 時間 0s

2020-11-03 00:11:31 (31.3 MB/s) - `_libgcc_mutex-0.1-main.conda' へ保存完了 [3473/3473]
					・
					・
					・

(testenv) [root@localhost linux-64]#


```

図4 任意パッケージのダウンロード

## 任意パッケージのカスタムチャネル化

次に任意パッケージのカスタムチャネル化を実施します。

```:CentOSのコンソール画面
#linux-64とnoarchディレクトリにパッケージを振り分けてダウンロードを完了した状態とします。
#まず、カスタムチャネル化に必要なconda-buildをインストールします。

(testenv) [root@localhost custom-channel]# conda install conda-build -y
Collecting package metadata (current_repodata.json): done
Solving environment: done
					・
					・
					・
liblief-0.10.1       | 1.7 MB    | ########################################################################## | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done

#Conda-buildをインストールすると、下記のようにconda indexコマンドが利用できます。
#このコマンドでcustom-channelディレクトリがカスタムチャネル化されるので、
#custom-channelディレクトリを本番環境のRHELサーバにコピーします。
#コピーの方法としては、セキュアなエンドポイントや物理媒体を経由するなどがあります。

(testenv) [root@localhost custom-channel]# conda index .
Subdir: linux-64:   0%|
					・
					・
					・

```

図5 任意パッケージのカスタムチャネル化

## 本番環境への任意パッケージインストール

次に本番環境への任意パッケージインストールを実施します。

```:RHELのコンソール画面
#これ以降は本番環境のRHELサーバに移行します。
#既にRHELサーバへのMinicondaのインストールやカスタムチャネルのコピーは完了しているものとします。

#下記のように”proenv”という仮想環境を作成します。
(base) [root@nvm006694 ~]# conda create -n proenv -y --offline
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: /root/miniconda3/envs/proenv

#１行目のように、作成したカスタムチャネルをチャネルとして指定します。
#２行目では、デフォルトのチャネルを指定から外します。
#３行目では、カスタムチャネルのみがチャネルとして指定されていることを確認しています。

(base) [root@nvm006694 ~]# conda config --add channels /root/custom-channel
(base) [root@nvm006694 ~]# conda config --remove channels defaults
(base) [root@nvm006694 ~]# conda config --get channels
--add channels '/root/custom-channel'   # highest priority

#作成した仮想環境に入ってから、カスタムチャネル経由でpythonをインストールします。
#同じ要領でnotebookもインストールできます。

(base) [root@nvm006694 ~]# conda activate proenv
(proenv) [root@nvm006694 ~]# conda install python -y
Collecting package metadata (current_repodata.json): done
Solving environment: done
							・
							・
							・
he following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    ca-certificates-2020.10.14 |                0         121 KB  file:///root/custom-channel
							・
							・
							・
pip-20.2.4           | 1.7 MB    | ##################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done

```

図6 本番環境へのパッケージインストール

```:RHELのコンソール画面

#TensorFlowのインストールでは以下のようなエラーが出ることがあります。このエラーの原因と回避方法を以下に示します。

(proenv) [root@nvm006694 ~]# conda install tensorflow
							・
							・
							・
Found conflicts! Looking for incompatible packages.
UnsatisfiableError: The following specifications were found to be incompatible with each other:

```

図7 TensorFlowのインストールエラー

カスタムチャネル用ディレクトリ内のアーキテクチャ別のディレクトリには、“repodata.json”と”current_repodata.json”というメタデータが存在します。

メタデータ内の特定のライブラリのdepends項目に記載のバージョンと実際のライブラリのバージョンが異なるバグが存在するため、エラーが発生します。（実際は"mkl-2020.2-256.conda“ >= 2020）

このため、バージョンを実態に合わせることでバグを回避することができます。

TensorFlowの他にも、mklの依存ライブラリであるmkl-serviceやmkl_fftなどで同件が発生することを確認できます。

他にもlibgcc-ngやlibstdcxx-ngといったライブラリでも同件が発生します。

検証環境でデバッグ&回避策適用を実施してから、本番環境にインストールすることを推奨します。

![図8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865566/54f19668-33e5-b72f-f7f8-d15658e10180.png)


図8 インストールエラーの修正例

# まとめ

本投稿では、できるだけ最小限のパッケージ数で機械学習実行環境を構築する手法を紹介しました。

後編では環境構築の自動化をAnsibleで実施するノウハウを示します。