Corredor

ウェブ、プログラミングの勉強メモ。

CentOS Linux に Docker をインストールしてみた

コレまで

などに Docker をインストールして動作環境を作ってきたが、ホスト OS が Linux のマシンに Docker を入れたことがないな、と思い、今回 CentOS 7 に Docker をインストールしてみた。

厳密にいうと、RedHat Enterprise Linux をベースとした Oracle Linux 環境 (OCI の無料枠で作れる VM) で試したのだが、CentOS 系なら変わりないと思う。

公式にちゃんとドキュメントもあるので、コレを試した次第。

順にコマンドだけ記載する。

# 依存パッケージをインストールする
yum install -y yum-utils device-mapper-persistent-data lvm2

# リポジトリを登録する
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
ls /etc/yum.repos.d/docker-ce.repo

# インストール可能なバージョンを確認する
yum list docker-ce --showduplicates | sort -r

# バージョンを指定してインストールする
# yum install -y docker-ce-18.06.1.ce-3.el7
# 普通にインストールするなら以下
yum install -y docker-ce
# インストール中にリポジトリの鍵をインポートするか聞かれるので y と答えてインポートする

# Docker デーモンの状態を確認して起動する
systemctl status docker
systemctl start docker
systemctl status docker

# 一般ユーザ (ココでは opc ユーザ) も Docker を動かせるようにする
getent group | grep docker
usermod -aG docker opc
getent group | grep docker
# 一度ログアウトすると反映される

ということで最終確認してみたところ、エラーが出てしまった。

# 最後に追加した一般ユーザ (opc ユーザ) で試してみた
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:c3b4ada4687bbaa170745b3e4dd8ac3f194ca95b2d0518b417fb47e5879d9b5f
Status: Downloaded newer image for hello-world:latest
docker: Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"write /proc/self/attr/keycreate: permission denied\"": unknown.
ERRO[0005] error waiting for container: context canceled

パーミッション関連なので、SELinux かなんかかなぁと思って調べてみるとそのとおりだった。

ということで対応する。

# SELinux を一時的に無効化する
$ sudo setenforce 0

# 状況を確認する
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   permissive
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Max kernel policy version:      31

# 再度実行してみる → 今度は上手くいった
$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

SELinux をマシン再起動後も無効にしておきたい人は、次のように操作する。

$ sudo vi /etc/selinux/config

そしたら

SELINUX=enforcing

という行があると思うので、コレを

SELINUX=disabled

に書き換える。

とりあえずコレで Docker が使えるようになった。

Ansible を Docker コンテナに対して適用するためのお試し環境を作った

初めてのAnsible

初めてのAnsible

RedHat が開発する構成管理ツールである Ansible。サーバに対して行いたい設定を Playbook と呼ばれる YAML ファイルに起こして実行できる、いわゆる IaC : Infrastructure as Code を実現できるツールだ。1台のマスター・サーバから複数のスレーブ・サーバに対して同様のセットアップ処理を行わせたりできて、とても便利だ。

しかし、そんな Ansible のスクリプトを構築していく際、試しにスクリプトを適用できるサーバをホイホイと用意するのは難しい。自前で別の PC マシンを用意するにしても、Ansible に構築させた環境を一旦削除してやり直したい時もあるだろうし、クラウドでインスタンスを払い出すにしても、お金がかかって仕方ない。

そこで今回は、Ansible スクリプトを「試し打ち」する対象に Docker コンテナを使うことにした。手元のホストマシンに必要なのは Docker の実行環境のみ。Ansible をすぐに動かし始められるサンプルプロジェクトを作ったので、紹介していく。

サンプルプロジェクトの紹介

以降で紹介していくサンプルプロジェクトは以下。

github.com

とりあえず動かしてみたい人は、README.md のとおりにコマンドを叩いてみれば良い。

詳細説明

構成

このサンプルプロジェクトでは、以下の3つのコンテナを用意している。

  1. ansible-server : Ansible の実行環境 (ansible-playbook コマンドがインストールされている)
  2. target-1 : 処理を行わせたいサーバのテイで用意した CentOS コンテナ
  3. target-2 : 処理を行わせたいサーバのテイで用意した Debian コンテナ

これら3つのコンテナを、docker-compose コマンドで一気に起動できるようにしてある。

$ docker-compose up -d

コンテナ3つとも docker execbash のセッションに接続できるが、メインで使用するのは ansible-server だけだ。ansible-server にて ansible-playbook コマンドを実行して、target-1 サーバと target-2 サーバに同じ設定を行わせる、という関係である。

Ansible 実行環境と設定ファイル

というワケで ansible-server コンテナに接続する。

$ docker exec -it ansible-server /bin/bash

ansible-server コンテナは、このサンプルプロジェクトのルートディレクトリを /var/data ディレクトリにマウントしている。Ansible の処理内容を書く Playbook ファイルや、接続先を示す Inventry (Hosts) ファイルは ./ansible/ ディレクトリに格納してある。

接続先を示す Inventry (Hosts) ファイルは、たった3行。見てもらうと分かるが、ホスト名と、それをグルーピングする名前を指定しているだけ。

  • ./ansible/inventry-hosts.ini
[target]
target-1
target-2

そして Playbook ファイルの方は、echo とリダイレクトを使って、/tmp/ 配下にファイルを生成するだけ、というシンプルなサンプルコードを載せている。この中の hosts: プロパティで、上のインベントリファイルでいう [target] グループを指定している。

  • ./ansible/playbook.yaml
- hosts: target
  tasks:
  - name: Make test file
    shell: |
      echo 'Test' > /tmp/ansible-docker-test.txt

ansible-playbook コマンドで使用するのは以上の2つのファイルのみだ。

Ansible 実行環境から操作対象ホストへの SSH 接続

ansible-server から、target-1target-2 へは、SSH 接続できる必要がある。実際に ansible-server コンテナ内で、ssh コマンドを叩いて確かめてみよう。

# Ansible コンテナから Target 1 コンテナに SSH できるか確認する
$$ ssh target-1
# 初回のみ `yes` と回答する
# `root@target-1's password:` にパスワードを入力する

パスワードは ./docker/target-1/Dockerfile にて設定していて、mypassword である。SSH 接続時に公開鍵認証方式ではなく、パスワード認証方式を利用しているサーバ、というテイにしている。この辺りは、このサンプルプロジェクトを簡素化するためにこのようにしたもので、鍵認証でも Ansible は使える。

パスワードを入力して SSH 接続すると、ansible-server コンテナ内で、target-1 コンテナの中に入れたことになる。$ cat /etc/os-release などを叩いてみて、CentOS 環境であることを確認して欲しい。

同様に、target-2 へも接続できる。

# Ansible コンテナから Target 2 コンテナに SSH できるか確認する
$$ ssh target-2
# 初回のみ `yes` と回答する
# `root@target-2's password:` にパスワードを入力する

パスワードは ./docker/target-2/Dockerfile で設定しているとおり、mypassword (target-1 のパスワードと同じ)。コチラは Debian サーバだ。

Ansible を実行してみる

操作対象のサーバと接続できることが確認できたら、実際に Ansible を実行してみよう。

# Ansible を実行する
$$ ansible-playbook -i ./ansible/inventry-hosts.ini ./ansible/playbook.yaml --ask-pass
# SSH パスワードを入力する

-i オプションでインベントリファイル (接続先) を指定する。メインで Playbook ファイルを指定すれば、そのとおりに処理が行われていくというワケだ。ココで、今回用意した2台のサーバコンテナは、パスワードで SSH 接続するので、--ask-pass オプションを付けて、SSH 接続時のパスワードを入力するようにしている。ココでは target-1target-2 で同じ値にしていた mypassword を入力する。

すると、各コンテナの /tmp/ 配下に ansible-docker-test.txt というファイルが生成されるはずだ。

以上で Ansible を実行するところまでは試せたと思うので、あとは ./ansible/playbook.yaml を実装していって、それぞれのコンテナに設定が加えていけるか、試していってほしい。

コンテナを停止する

立ち上げたコンテナを停止・破棄するには、次のコマンドを実行する。

$ docker-compose rm --stop --force

やっていることは $ docker rm -f 【Container ID】 を3台分繰り返したのと同じだ。

Dockerfile の準備が地味に大変だった…

ココからは、このお試しキットを作るまでのお話。

Ansible 実行環境コンテナを作る

まず ansible コマンドや ansible-playbook コマンドが使える環境を作りたく、Ansible の公式イメージを探したのだが、Ansible 公式では Docker イメージのメンテを止めているようだった。

  • 参考:Docker Hub
    • ↑ コレは Ansible のテスト用イメージらしく ansible コマンドは同梱されていない
  • 参考:Docker Hub
    • ↑ メンテが止まっているイメージ

メンテが止まっている Dockerfile は以下。

CentOS 7 イメージに Ansible をインストールしているだけなので、自前でやることにする。Python 関連のライブラリも指定されているが、単純に yum install ansible だけで大丈夫そうだ。

また、操作対象ホストに ssh コマンドで接続確認できるようにしておきたいので、yum install git で Git とともに ssh コマンドを入れておく。

以上のような Docker イメージで、とりあえず ansibleansible-playbookssh コマンドが動作する状態になった。

パスワードで SSH ログインできる CentOS 環境を作る

続いて、SSH 接続して Ansible を実行する、対象のサーバ target-1 を作る。ベースイメージは ansible-server と同じ CentOS 7 系。

SSH 接続される環境を用意するには、yum install openssh-server を実施しておく。その上で、Docker イメージはデフォルトでは root ユーザしかいないので、root ユーザで SSH ログインできるように色々設定した。

  • /usr/sbin/sshd -D 実行時に Could not load host key: と怒られるので、ssh-keygen で鍵ペアを3つ作っておいた
  • /etc/ssh/sshd_config にて PermitEmptyPasswords yes と設定する
  • chpasswd を使って root ユーザのパスワードを mypassword に設定する
    • Dockerfile 中の echo 'root:mypassword' | chpasswd という部分。パスワード文字列を変更したい場合はココを変える

さらに SSH 接続用に22番ポートを開放 (EXPOSE) し、sshd を起動しておく。

パスワードで SSH ログインできる Debian 環境を作る

同様に、root ユーザで SSH ログインできる、Debian OS 環境を作っていく。やることは CentOS と大差ないかなーと思っていたのだが、まぁまぁ詰まった。

ネット上の文献が、以前のバージョンの Debian 向けの設定だったので、Buster (v10) に合わせた設定に変更する必要があった。

  • /etc/ssh/sshd_config にて PermitRootLogin yes と設定する
  • /etc/pam.d/sshdsession required pam_loginuid.so となっている行を required から optional に変更する
  • その他必要そうな設定を適当に… (← 効果がよく分かってない)

とゆー感じ。

CentOS と違い、yum ではなく apt-get を使う点と、Ansible を実行するには Python がインストールされていないといけなかったので、apt-get install python も実施しておいた (CentOS には python コマンドが既に入っていた)。

なんやらかんやら設定して、とりあえず CentOS と同じように、root ユーザのパスワードを入力する方式で SSH 接続できるようになった。

Docker Compose 設定ファイルを書く

これら3つのコンテナを立ち上げるため、docker-compose.yaml ファイルを書いた。

tty: true とすると、コンテナを起動しっぱなしで置いておけるようなので設定した。便利ねぇ〜。

以上

とりあえず Ansible の素振り・お試し環境を Docker コンテナで作ることはできたので、よきよき。

入門Ansible

入門Ansible

Oracle Autonomous Transaction Processing・Autonomous Data Warehouse を無料枠で試してみた

Oracle の ATP : Autonomous Transaction ProcessingADW : Autonomous Data Warehouse を無料枠で作成し、同じく無料枠の Compute Instance より接続してみた。

Compute Instance の作成手順は以下の記事でまとめたのでご参考に。

neos21.hatenablog.com

ATP・ADW の作成

まずは ATP・ADW を作成してみる。どちらかだけでも良いし、両方作ったりしても良い。

  1. OCI 管理コンソール左上のハンバーガーメニュー → Autonomous Transaction Processing もしくは Autonomous Data Warehouse を選択する
  2. 「Create Autonomous Database」ボタンを押下する
  3. 「Provide basic information for the Autonomous Database」でコンパートメント、DB 名などを指定する
  4. 「Choose a workload type」で「Transaction Processing」もしくは「Data Warehouse」を選択する
  5. 「Choose a deployment type」で「Serverless」を選択する
  6. 「Configure the database」で「Always Free」にチェックを入れる
  7. 「Create administrator credentials」で任意のパスワードを入力する
    • 初回の DB ユーザ名は ADMIN で固定になっている
  8. 最下部の「Create Autonomous Database」ボタンを押下する

無料枠では2つまで DB が作れるので、ATP 1つ、ADW 1つを作って違いを試してみても良いだろう。

ウォレットのダウンロード

続いて、DB に接続するためのクレデンシャル情報である、「ウォレット」をダウンロードする。

  1. 作成した DB 名のリンクを押下し、詳細画面に移動する
  2. 「DB Connection」ボタンを押下し、「Download Wallet」ボタンを押下する。
  3. DB 作成時に入力したパスワードを入力し、Wallet_【DB 名】.zip ファイルをダウンロードする

Oracle Linux に SQL*Plus をインストールする

Compute Instance に SSH 接続し、SQL*Plus をインストールする。

  1. SQL*Plus の動作に必要な基本ツール (Basic) と本体を、次のページよりダウンロードする
    • Oracle Instant Client Downloads
    • 「Instant Client Downloads for Linux x86-64 (64-bit) 」リンクを押下する
    • oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm oracle-instantclient19.3-sqlplus-19.3.0.0.0-1.x86_64.rpm をダウンロードする
  2. ダウンロードした2つの .rpm ファイルを OCI インスタンスに scp 等でアップロードする
    • $ scp ./oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm my-oci:/home/opc
    • $ scp ./oracle-instantclient19.3-sqlplus-19.3.0.0.0-1.x86_64.rpm my-oci:/home/opc
  3. 先程ダウンロードしたウォレットの Zip ファイルを OCI インスタンスにアップロードする
    • $ scp ./Wallet-【DB 名】.zip my-oci:/home/opc
  4. OCI インスタンスに SSH ログインする
  5. .rpm ファイルを使用して rpm コマンドでインストールする
    • $ sudo rpm -ivh oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm
    • $ sudo rpm -ivh oracle-instantclient19.3-sqlplus-19.3.0.0.0-1.x86_64.rpm
    • コレで sqlplus コマンドが動作するようになっている
  6. ウォレットの Zip ファイルを ~/wallet (/home/opc/wallet) ディレクトリ配下に解凍する
    • $ mkdir ~/wallet$ mv ./Wallet-【DB 名】.zip ~/wallet
    • $ cd ~/wallet$ unzip ./Wallet-【DB 名】.zip
  7. 解凍したファイルのうち、sqlnet.ora ファイルを開き、DIRECTORY="?/network/admin"となっている部分を、このファイルが格納されているディレクトリパスに書き換える
    • WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/home/opc/wallet")))
  8. 次のように環境変数を設定する
    • $ export TNS_ADMIN='/home/opc/wallet'
  9. tnsnames.ora を参照して DB 名を確認し、次のように sqlplus コマンドを実行する
    • $ sqlplus 'ADMIN/【パスワード】@myatp_high'
    • $ sqlplus 'ADMIN/【パスワード】@myadw_high'
    • tnsnames.ora で定義されている DB 名に接続できる。ATP と ADW を作成した場合、1つのウォレットファイルに複数の接続先が同梱されていると思われるので、適宜編集して利用する

以上。コレで SQL*Plus を使って ATP や ADW に接続できた。接続した後の画面は通常の Oracle DB と特に変わりないかな。

SQL*Plus のダウンロードが地味に面倒。あとウォレットとかいうかったるい認証機構で、古い Oracle DB 民は戸惑う…。

参考文献

絵で見てわかるシステム構築のためのOracle設計 DB Selection

絵で見てわかるシステム構築のためのOracle設計 DB Selection

Python + Selenium + ChromeDriver 環境を Docker Compose でまとめてみた

プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化

プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化

以前、Python + Selenium を使って Chrome ブラウザを操作する、スクレイピングのサンプルプロジェクトを紹介した。

今回はこの環境を Docker Compose で構築して、ホスト OS 環境を汚すことなく動かせるようにしてみる。

コード全量が置いてある GitHub リポジトリは以下。

github.com

Docker Compose とは?

Docker Compose とは、複数の Docker コンテナを組み合わせて使用する際に、うまいことまとめて実行してくれるツール。Docker 本体に同梱されているので、Docker をインストールしてあれば docker-compose コマンドが既に動作するはずだ。

例えば、

  1. Python を動かすコンテナ
  2. Chrome ブラウザがインストールしてあるコンテナ
  3. Python コンテナから Chrome コンテナを操作できるように仲介する Selenium Hub コンテナ

というように、起動する順序やポートの関係性が重要な時に、それをシェルスクリプト等で実現するのではなく、Docker Compose の定義体 (YAML ファイル) に起こしておくことで、あとはよしなにやってもらえる、という便利なコマンドだ。

今回自分が作った例では、複数のコンテナを扱うことはないのだが、Docker Compose を使うと

  • $ docker run -v `pwd`:/data -it python:3.7.5-buster /bin/bash

などという長ったらしいコマンドを打つ必要がなくなり、

  • $ docker-compose up -d

と打つだけで実行可能になって楽なので、使ってみた。

Docker イメージ環境をイメージ (想像) する

さて、今回動かしたいプロジェクトは、

  • Python の実行環境
  • Chrome ないしは Chromium ブラウザ
  • Selenium

が OS 上にインストールされていないと、サンプルコードだけ持ち込んでも動作させられない。

愚直にやるとしたら、Dockerfile

  • Ubuntu など適当な OS をベースイメージにする
  • apt-get などを使って Python をインストール
  • Chromium ブラウザをインストール
  • pip で Selenium をインストール

という感じに実装すると思う。勿論それでも良いのだが、今回は既に Python + Chromium + Selenium が揃っているベースイメージを見つけたので、それを利用しようと思う。

Docker コンテナ上に Chrome・Chrome Driver・Selenium をインストールする

自分が使用したのは joyzoursky/python-chromedriver:3.7-selenium というイメージだ。

何をやっているか調べるため、Dockerfile を読んでみる。

中を読むと、python:3.7 という公式イメージをベースに、Chrome と Chrome Driver をインストール、さらに pip で Selenium をインストールしている。なるほど、このぐらいなら自分で実装しても良いレベルだが、今回はコレをそのまま利用することにしよう。

Docker Compose ファイルを書く

さて、自分のプロジェクトディレクトリで、docker-compose.yaml ファイルを書く。内容は以下のとおり。

my-python:
  # ディレクトリを指定するとその配下の Dockerfile を取得して実行する
  build: ./
  # `$ docker ps` 時に NAME として見える名前
  container_name: my-python
  # 勝手に終了しないよう設定しておく
  command: tail -f /dev/null
  # `$ docker exec -it my-python bash` でアタッチした際のカレントディレクトリとなる
  working_dir: /root/app/
  # ボリュームのマウント
  volumes:
    - ./:/root/app/
  # 環境変数
  environment:
    - TZ=Asia/Tokyo
  • build で指定しているのは、ベースイメージとなる Dockerfile の所在
    • コレの代わりに image と書くと、既存の Docker イメージ名を指定してベースにできる
  • command で謎の tail を行っているのは、起動したコンテナを終了させずに置いておくためのイディオム
  • volumes 部分で、共有ディレクトリをマウントしている。docker run 時に指定する -v (--volume) オプションと同等だ

あとは読めば分かるレベルかと。

さて、build プロパティではディレクトリを指定しているが、そのディレクトリの配下にある Dockerfile を探すようになっている。今回は単一の Docker イメージを使うので、プロジェクトルートディレクトリ直下にしたが、必要に応じて複数イメージ用のディレクトリを分けて置いておくと良い。

その Dockerfile の内容は次のとおり。

# ココで前述のベースイメージを指定している
FROM joyzoursky/python-chromedriver:3.7-selenium

# - Vi・Vim が入っていないのでインストールする
# - pip をアップデートして pipienv をインストールする
RUN set -x && \
  apt-get update && \
  apt-get install -y vim && \
  pip install --upgrade pip && \
  pip install pipenv

このプロジェクトがすぐに動かせるよう、pipenv をインストールしてある。また、コンテナ内でちょっとした編集ができるように vim も入れているというワケ。

Docker Compose で起動してみる

このような設定ファイルを用意したら、次のコマンドを実行する。

$ docker-compose up -d
Building my-python
# …中略…
Creating my-python ... done

-d--detach (デタッチ) の意味。対象のイメージが存在しなければその場で docker pull および docker build を行い、そのまま docker run してくれるのだが、すぐにアタッチする必要はないので、-d を付けている。

このコマンドで Dockerfile をベースにしたビルドまでが完了し、コンテナが起動していることが確認できるだろう。

$ docker ps
CONTAINER ID        IMAGE                                COMMAND               CREATED             STATUS              PORTS               NAMES
81526a50b822        practice-python-scraping_my-python   "tail -f /dev/null"   10 seconds ago      Up 9 seconds                            my-python

それではこのコンテナにアタッチしてみる。

$ docker exec -it my-python bash

docker-dompose.yamlworking_dir プロパティで指定したディレクトリにいる状態で、bash 接続できる。

ココからサンプルプロジェクトを動かしていくには、以下のように実行していく。

# 環境構築
$$ pipenv install --dev

# スクリプト実行
$$ pipenv run crawl
$$ pipenv run scrape

共有ディレクトリを設定しているので、結果はホスト OS からも確認できるだろう。

以上

docker コマンドにオプションをたくさん付けて何個もコンテナを起動したりしていたのが、全てを設定ファイルに書いておいてコマンド一発で起動できるようになった。

複数コンテナを扱わなくても楽になることが多いので、Docker Compose はぜひ覚えておきたい。

Chrome や Chrome Driver をどのようにコンテナ化するかという方法は他にも考えられ、今回の方法だけが唯一の正解というワケではないので、各自「ビルド時間が短く済法」だったり「イメージサイズが小さく済む構成」だったり「スケールしやすい構成」だったりを考えてみて欲しい。

Dockerによるアプリケーション開発環境構築ガイド(リフロー版)

Dockerによるアプリケーション開発環境構築ガイド(リフロー版)