企業におけるOSSを用いたコンテナ開発のリスクと対策について

著者: 吉町優, 株式会社日立製作所

# はじめに
普段の業務ではOSSとコンテナ技術を積極的に活用し、製品/サービスの開発速度向上に役立てています。
OSSの活用は当たり前となりましたが、製品/サービスの素材としてOSSを使うには多くの課題があります。
今回はOSS利用する上でのリスクと対策、そして流行のコンテナ開発におけるOSS観点について紹介します。

# OSS利用の問題点
OSSを活用した製品/サービス開発には多くの課題があります。
例えば、以下のようなOSS利用のリスクがあります。
- 社内環境(ネットワークプロキシなど)の制約
- コピーレフトライセンス(GPLなど)の伝播の考慮
- OSSパッケージの保管/管理方法

実際に製品/サービス開発する際に考えられるリスクを紹介します。

## 製品/サービス開発におけるOSS利用のリスク
### 社内ネットワークの制約
yum, pipなどのOSSパッケージはオンラインリポジトリサーバからインストールするため、社内環境ではプロキシの影響を受けることがあります。
インストールの過程で認証が必要になることや、コンテナ内にログが残ることがあります。

### パッケージの再入手性
OSSの特性上バージョンアップが早く、過去に入手したバージョンの配布パッケージが再入手できなくなることがあります。

### 環境の再現性の低下
OSSパッケージの再入手不可、オンラインリポジトリ依存 というのは将来的に同一環境のソフトウェア/システムを構築できないリスクに繋がります。
これらのリスクを回避するために、製品/サービスの素材としてOSSを採用する場合は社内ローカル環境で完結して成果物を構築できることが必要です。

## コンテナにおけるリスク
### コンテナ再ビルド時の構成の変化
マイクロサービス化の流れに合わせてコンテナを用いた製品/サービス開発も増えてきています。
個人利用ではDocker Hubなどの構築済みコンテナイメージを使うことも多いかと思います。
Docker Hubなどの構築済みコンテナイメージではコンテナのビルドでapkやrpmなどのパッケージ(依存を含む)はオンラインリポジトリからインストールされるものが多いです。
構築済みコンテナイメージを利用・運用する中で、セキュリティパッチなどの任意の修正が必要になった際、時の経過により同梱OSSの取得バージョンの変化やOSSパッケージ自体の取得が不可能になることがあります。

### ベースコンテナの同梱OSSが不明瞭
構築済みイメージをそのまま使わずとも、ベースコンテナにDocker Hubなどの構築済みイメージを使っている場合は同様の構成変化のリスクがあります。
製品/サービスでは仕様維持のために導入済みのコンテナのOSSを変更できないことがあるので、同一環境を再現できないことは大きなリスクです。

## OSSライセンスのリスク
OSSのライセンスはMITライセンスやApacheライセンスのように多くのライセンス種があり、この中でコピーレフトライセンスと呼ばれるGPLがあります。
ここでGPLについて深くは触れませんが、OSSの取り込み方によって独自開発部分のソース開示が必要になります。
このため、コンテナにGPLが含まれる場合[GPL汚染](https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9%E6%84%9F%E6%9F%93)が発生するリスクがあります。
このように不明瞭にOSSを利用することにはリスクがあり、コンテナに同梱するOSSを管理する必要があります。

# 開発でOSSを活用するために達成したいこと
このようにOSSに課題は多いですが、メリットも多いため積極的に使っていきたいものです。
流行のコンテナを製品/サービス開発をするためにやりたことは以下です。

- 社内ネットワーク(Proxyなど)の制約を回避したい
    - 社内ローカル環境でコンテナビルドが完結できる
- アップデート/脆弱性 対応ができるようにしたい
    - コンテナ内の同梱OSSを単体で入れ替えができる
- コピーレフトライセンスのライセンス伝播リスクを回避したい
    - 同梱OSSが不明瞭なコンテナは使わない

これらを達成するためにプライベートリポジトリを作成することしました。

# プライベートリポジトリの構築
## docker-composeによる構築例
プライベートリポジトリの構築に`docker-compose`を採用しました。
プライベートリポジトリを使うことで、製品/サービスで使うOSSを明確に管理できます。
管理したいパッケージ種は多くあり、以下のように必要なリポジトリを追加すると使いやすいです。

#### プライベートリポジトリの構築例
リポジトリ|ポート番号| 構成OSS
 :---  | :--- | :---
FileServer| 10001 | nginx
yum| 10002 | nginx
apt| 10003 | debian-repository
apk| 10004 | nginx
pypi| 10005 | pypi-server
Dockerレジストリ | 80 | Nexus Repository Manager 3

### OSSパッケージを管理しやすい
OSSパッケージ種単位でリポジトリサービスのコンテナをデプロイでき、ポートフォワード機能による受信ポートの変換も可能です。
`Development`, `Staging`, `Production`毎にリポジトリを分け、誤ったOSSの混入を防ぐ仕組みも実現可能です。

# コンテナイメージ管理
## パッケージとしてのコンテナイメージの永続管理
製品/サービスをコンテナで開発する場合、コンテナイメージがソフトウェアパッケージであり保守管理対象になります。
社内のローカル環境で完結してコンテナイメージを再ビルドできることは大切ですが、一度ビルドしたイメージを保管する仕組みも必要です。
コンテナを再生成する場合はコンテナイメージのラベルの変化やベースイメージの変化の影響を受けるため、将来にわたり同一のコンテナを再生成できる保証はありません。
コンテナイメージのハッシュ値からイメージの変化を検知するためにも、製品/サービスのリリース版のコンテナイメージの管理が必要です。

## コンテナイメージの保護
コンテナイメージを開発/管理する際、Dockerレジストリをプライベートで運用し作成したコンテナイメージをpushやpullすることになります。
しかし開発中に誤って同一タグのコンテナイメージをpushすると上書きされてしまいます。
このため製品/サービスのリリースバージョンのコンテナイメージを書き込みできないように保護することが好ましいです。

### コンテナイメージの保護の構成例
[Nexus Repository Manager 3](https://github.com/sonatype/nexus-public)にはDockerレジストリ機能があり、`hosted`, `proxy`, `group`の考えがあります。
複数のDockerレジストリを構築可能で、複数のレジストリを纏めたgroupレジストリを作ることができます。
Dockerレジストリに格納されている過去リリース分のコンテナイメージをpullして使うことはありますが、pushでイメージ更新することはありません。
開発中に新たなコンテナイメージをレジストリにpushしますが、誤ってコンテナを上書きしてしまうリスクがありました。
そこで、group内のDockerレジストリを開発向けpush用の`書き込み可能なDockerレジストリ`と過去リリースイメージpull用の`読み込み専用なDockerレジストリ`に分けます。
さらにリバースプロキシサーバをDockerレジストリ向けの通信の間に配置し、HTTP Methodを条件に`pull`と`push`のリクエストを適切なDockerレジストリに分類します。

![docker_registry_figure1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548646/dc20bd00-e8aa-5923-9599-eea70628d1ec.png)

これにより、Dockerクライアント側でレジストリ構成を意識せずイメージが保護されたDockerレジストリを使うことができます。

# まとめ
OSSのライセンス、パッケージの管理方法、コンテナ開発のノウハウを紹介しました。
OSSは個人では手軽に使えますが、企業として製品/サービスにOSSを取り込む場合は正しくリスクを認識し、対策が必要です。
企業の業務活動においてOSS特性を理解した上で活用し、OSSコミュニティを盛り上げていきましょう。