20200628のdockerに関する記事は18件です。

VirtualBoxとDockerでお勉強用の環境を作ってみた

VirtualBoxとDockerでお勉強用の環境を作ってみた

  • 気になるソフトウェアを動かして、挙動を見てみたい。
  • ソフトウェアの設定を変えていろいろ実験したい
  • 複数のサーバーをつかうつもりでやってみたい
  • 失敗時に自力で環境を戻す自信がない(戻すのがめんどくさい)ので、環境の再作成を頻繁にしたい

など思ったときに使っている、お試し環境の構築手順です。主に自宅などでのお勉強での使用を想定しています。

仮想マシンをVirtualBoxで作成し、そこにDockerをインストールします。

Dockerを使うと、プロセスを隔離環境(コンテナ)で実行できます。仮想マシンと違い、コンテナは気軽に作成/削除できるため、失敗を気にせずに気軽にいろいろ実験するにはもってこいです。

環境

種別 OS 備考
ホスト(物理マシン) Windows 10 Home 1909 インターネットへ接続可能
ゲスト(仮想マシン) CentOS Linux release 7.8.2003 -

目次

  • 1. 仮想マシンの構築
    • 1.1 VirtualBoxのインストール
    • 1.2 CentOS7イメージのダウンロード
    • 1.3 仮想マシンの作成
    • 1.4 ネットワーク接続設定
    • 1.5 CentOS7インストール
    • 1.6 インターネット接続の確認
  • 2. CentOS 7初期設定
    • 2.1 カーネル最新化
    • 2.2 パッケージの更新など
    • 2.3 ホスト-ゲスト共有フォルダの有効化
    • 2.4 一般ユーザーの作成
    • 2.5 SSH設定
    • 2.6 mDNS設定
  • 3. Visual Studio Codeの導入
  • 4. 開発環境の構築
    • 4.1 Gitの最新化
    • 4.2 Dockerのインストール
    • 4.3 Python3のインストール
    • 4.4 docker-composeのインストール
    • 4.5 Visual Studio Code拡張機能インストール
  • 5. デモ
    • 5.1 最低限のWebサイトを構築してみる
    • 5.2 Apache Web Server + Tomcatで最小Webアプリを作ってみる
  • 6. まとめ

1. 仮想マシンの構築

1.1 VirtualBoxのインストール

VirtualBoxは仮想化ソフトウェアです。Windows用のインストーラーをダウンロードしてインストールします。インストール先はどこでもよいですが、ここではC:\App\VirtualBoxにインストールします。

1.2 CentOS7イメージのダウンロード

CentOS 7のDVDイメージを以下のURLからダウンロードします。時間がかかるので、気長に待ちます。

https://www.centos.org/download/

※ CentOS 8が既にリリースされていますが、まだDockerが公式にサポートしていません。

1.3 仮想マシンの作成

VirtualBoxマネージャーを開きます。新規をクリックします。

001_vm001.png

仮想マシンの名前と保存先を指定します。今回は以下のように設定します。

001_vm002.png

メモリは2.0GBとします。必要に応じて変えてください。

001_vm003.png

仮想ハードディスクを固定サイズ32GBで作成します。Dockerイメージ分の容量を確保するため、このくらいにしておきます。

001_vm004.png
001_vm005.png
001_vm006.png
001_vm007.png

以上で仮想マシンの作成が終わりました。

1.4 ネットワーク接続設定

仮想マシンがホストのネットワークにブリッジ接続するようにします。ホストからインターネット接続できるのであれば、仮想マシンからもインターネット接続できるようになると思います。

002_setting001.png

1.5 CentOS 7インストール

先ほどダウンロードしたCentOS 7のイメージを仮想マシンの光学ドライブにセットします。

002_setting002.png

仮想マシンを起動してCentOS 7をインストールします。ネットワークの接続はインストール時にオンにしておきます。

003_install001.png

1.6 インターネット接続の確認

rootユーザーで仮想マシンにログイン後、適当なインターネット上のWebサイトにアクセスできることを確認します。

# curl --head https://www.google.com/
HTTP/1.1 200 OK
Date: Sat, 14 Mar 2020 16:32:34 GMT
...

2. CentOS 7初期設定

不安であれば、作業前に仮想マシンのスナップショットを取っておくとよいです。

2.1 カーネル最新化

なんとなくカーネルを最新化してみます。以下を参考に実施していきます。

今回のカーネルのバージョンは以下です。

  • デフォルト:3.10.0
  • 最新:5.7.6

まずはELRepoを有効化します。

# yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm

kernel-mlをインストールします。

# yum --enablerepo=elrepo-kernel install kernel-ml
# yum list installed kernel*
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * elrepo: ftp.yz.yamagata-u.ac.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
インストール済みパッケージ
kernel.x86_64                              3.10.0-1127.el7                       @anaconda
kernel-ml.x86_64                           5.7.6-1.el7.elrepo                    @elrepo-kernel
kernel-tools.x86_64                        3.10.0-1127.el7                       @anaconda
kernel-tools-libs.x86_64                   3.10.0-1127.el7                       @anaconda

再起動後、新しいカーネル(5.7.6)で起動します。再起動後に以下のコマンドでカーネルのバージョンが最新版になっていることを確認します。

# uname --kernel-release
5.7.6-1.el7.elrepo.x86_64

残りの最新版カーネルパッケージをインストールします。yum swapを使って、古いカーネルパッケージの削除と新しいカーネルパッケージのインストールを同時に行います。

# yum --enablerepo=elrepo-kernel \
  swap \
  -- install kernel-ml-* \
  -- remove kernel kernel-tools kernel-tools-libs

yum swapを使わずにそのままインストールしようとすると、古いものと依存関係が競合して失敗します。

エラー: kernel-ml-tools conflicts with kernel-tools-3.10.0-1127.13.1.el7.x86_64
エラー: kernel-ml-tools-libs conflicts with kernel-tools-libs-3.10.0-1127.13.1.el7.x86_64

これで新しいカーネルのインストールが完了しました。以下で確認します。

# yum list installed kernel*
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * elrepo: ftp.yz.yamagata-u.ac.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
インストール済みパッケージ
kernel-ml.x86_64                       5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-devel.x86_64                 5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-doc.noarch                   5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-headers.x86_64               5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-tools.x86_64                 5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-tools-libs.x86_64            5.7.6-1.el7.elrepo      @elrepo-kernel
kernel-ml-tools-libs-devel.x86_64      5.7.6-1.el7.elrepo      @elrepo-kernel

2.2 パッケージの更新など

以下を実行して、CentOS 7のパッケージを最新化します。

# yum update

後で使うのでnet-toolsをインストールします。netstatコマンドが使えるようになります。

# yum install net-tools

終わったら、一度ゲストを再起動しておきます。

2.3 ホスト-ゲスト共有フォルダの有効化

ホスト-ゲストの間でフォルダの共有を行うことで、ファイルのやり取りを簡単にします。以下の情報を参考に実施します。

ゲストにVirtualBox - Guest Additionsのインストールが必要です。

まずは必要なパッケージをインストールします。

# yum groupinstall "development tools"

次に、Guest Additionsのディスクイメージを挿入します。ディスクイメージはWindowsホストであれば、VirtualBoxのインストールディレクトリにあります。

004_addition001.png

以下のようにマウントします。

# mkdir /media/cdrom
# mount /dev/cdrom /media/cdrom
mount: /dev/sr0 is write-protected, mounting read-only
# ls -l /media/cdrom
合計 45705
-r--r--r--. 1 root root      763  2月 20 22:27 AUTORUN.INF
dr-xr-xr-x. 2 root root     1824  5月 15 03:49 NT3x
dr-xr-xr-x. 2 root root     2652  5月 15 03:49 OS2
-r--r--r--. 1 root root      547  5月 15 03:49 TRANS.TBL
-r--r--r--. 1 root root  3755310  5月 15 03:41 VBoxDarwinAdditions.pkg
-r-xr-xr-x. 1 root root     3949  5月 15 03:41 VBoxDarwinAdditionsUninstall.tool
-r-xr-xr-x. 1 root root  6737330  5月 15 03:42 VBoxLinuxAdditions.run
-r--r--r--. 1 root root  9317888  5月 15 04:43 VBoxSolarisAdditions.pkg
-r-xr-xr-x. 1 root root 16832808  5月 15 03:46 VBoxWindowsAdditions-amd64.exe-r-xr-x
r-x. 1 root root  9863040  5月 15 03:45 VBoxWindowsAdditions-x86.exe
-r-xr-xr-x. 1 root root   270616  5月 15 03:43 VBoxWindowsAdditions.exe
-r-xr-xr-x. 1 root root     6384  5月 15 03:42 autorun.sh
dr-xr-xr-x. 2 root root      792  5月 15 03:49 cert
-r-xr-xr-x. 1 root root     4821  5月 15 03:42 runasroot.sh

Guest Additionsをインストールします。以下のコマンドを実行します。

# cd /media/cdrom
# sh ./VBoxLinuxAdditions.run

共有フォルダを設定するために、一度仮想マシンをシャットダウンします。終わったら仮想マシンの設定画面から共有フォルダの設定を開き、以下のように設定します。パスはお好みで設定してください。

005_shared001.png

  • ホスト(Windows)のフォルダ:C:\shared\centos7_sandbox
  • ゲスト(CentOS 7)のマウント先ディレクトリ:/mnt/host

これで指定したフォルダをホスト-ゲスト間で共有することができるようになりました。

2.4 一般ユーザーの設定

常にrootユーザーで作業するのは好ましくないです。root権限が不要の場面では一般ユーザーを使います。

ユーザーを作成します。名前はすきにつけましょう。ここではrolengraysとします。パスワードも設定しましょう。

# useradd --user-group rolengrays
# passwd rolengrays

2.3節で設定した共有フォルダにアクセスするためには、vboxsfグループに入る必要があります。

# usermod -aG vboxsf rolengrays

rolengraysユーザーでsudoできるようにします。sudoersファイルの文法エラーを防ぐため、visudoコマンドから編集します。

# visudo -f /etc/sudoers.d/rolengrays

ファイルの内容は以下の一行のみとします。

/etc/sudoers.d/rolengrays
rolengrays ALL = (ALL) ALL

2.5 SSH接続設定

2.4節で作成したユーザーでホストからSSH接続できるようにします。3節で使います。

ホストで、SSH用鍵を作成します。最近のWindows10にはデフォルトでSSHクライアント(OpenSSH)がインストールされています。

(ホストで実行)
C:\> ssh-keygen -t rsa -b 2048 -N "" -f %USERPROFILE%\.ssh\rolengrays.key

ホストで、公開鍵のほうを共有フォルダにコピーします。

(ホストで実行)
c:\> copy %USERPROFILE%\.ssh\rolengrays.key.pub C:\shared\centos7_sandbox
        1 個のファイルをコピーしました。

ゲストで、公開鍵認証の有効化するため、/etc/ssh/sshd_configを編集してPubkeyAuthentication yesのコメントアウトを外します。

(ゲストで実行)
...

- # PubkeyAuthentication yes
+ PubkeyAuthentication yes

...

終わったらsshdを再起動して設定を反映します。

(ゲストで実行)
# systemctl restart sshd

以下のようにrolengraysユーザーの公開鍵を登録します。パーミッションは正しく設定しないとSSH接続が失敗します。

(ゲストで実行)
# su - rolengrays
$ mkdir --mode 700 ${HOME}/.ssh
$ cat /mnt/host/rolengrays.key.pub >> ${HOME}/.ssh/authorized_keys
$ chmod 600 ${HOME}/.ssh/authorized_keys

後で使うので、ゲストのIPアドレスを調べておきます。

(ゲストで実行)
$ ip address

ホスト%USERPROFILE%\.ssh\configにSSH設定ファイルを作成します。調べたIPアドレスをHostNameに指定します。

%USERPROFILE%\.ssh\config
Host centos7_sandbox
    HostName 192.168.43.119
    User rolengrays
    IdentityFile "~/.ssh/rolengrays.key"

ホストからSSH接続してみます。SSH経由でコマンドを正しく実行できることを確認します。

(ホストで実行)
c:\>ssh centos7_sandbox cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)

2.6 mDNS設定

2.5節の手順で仕組み上はSSH接続ができるようになりました。しかし、DHCPで各ホストにIPアドレスを動的に割りあてている場合、ゲストのIPアドレスが意図せず変更されることがあります。そうなるとまたゲストのIPアドレスを調べおして、%USERPROFILE%\.ssh\configを更新する必要があります。これでは面倒なので、mDNSの仕組みを使って同じネットワーク内のIPアドレスをホスト名で解決できるようにします。

※ Windows 10のバージョンが古いと、mDNSクライアントがデフォルトで入っていない場合があります。

まずはゲストにホスト名を設定します。自分の好きな名前を付けましょう。ここではc7sandboxとします。終わったら、ゲストを再起動しておきます。

(ゲストで実行)
# hostnamectl set-hostname c7sandbox

続いて、ゲストにmDNSデーモンをインストールします。

(ゲストで実行)
# yum install avahi

avahi-daemonサービスを起動します。自動起動もONにしておきます。

(ゲストで実行)
# systemctl enable avahi-daemon
# systemctl start avahi-daemon

5353/UDPがLISTENしていることを確認します。

(ゲストで実行)
# netstat -nulp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:40913           0.0.0.0:*                           1464/avahi-daemon:
udp        0      0 0.0.0.0:68              0.0.0.0:*                           865/dhclient
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           1464/avahi-daemon:
udp        0      0 127.0.0.1:323           0.0.0.0:*                           718/chronyd
udp6       0      0 ::1:323                 :::*                                718/chronyd

最後にfirewalldの設定を変更し、外から5353/UDPへの通信を許可するようにします。デフォルトゾーンをhomeにします。

(ゲストで実行)
# firewall-cmd --set-default-zone=home
success

homeゾーンが、現在使っているインターフェースに設定されていることを確認します。

(ゲストで実行)
# firewall-cmd --get-active-zones
home
  interfaces: enp0s3

※ homeゾーンではmdnsサービスがデフォルトで有効に設定されています。以下のコマンドで確認します。

(ゲストで実行)
# firewall-cmd --zone home --list-services
dhcpv6-client mdns samba-client ssh

※ mdnsサービスは5353/UDPを許可しています。

(ゲストで実行)
# firewall-cmd --service mdns --get-ports --permanent
5353/udp

firewalldを再起動して設定を反映します。

(ゲストで実行)
# systemctl restart firewalld

ホストの、%USERPROFILE%\.ssh\configファイルを書き換えます。ゲストのホスト名がc7sandboxなので、末尾に.localをつけたものをHostNameに指定します。

※ mDNSはデフォルトで、"ホスト名 + .local"で名前解決するためです。

%USERPROFILE%\.ssh\config
Host centos7_sandbox
    HostName c7sandbox.local
    User rolengrays
    IdentityFile "~/.ssh/rolengrays.key"

ホストからもう一度SSHできることを確認します。

(ホストで実行)
c:\>ssh centos7_sandbox hostname
c7sandbox

これでIPアドレスではなく、名前でホストからゲストにSSH接続ができました。

3. Visual Studio Codeの導入

Visual Studio Codeをインストールします。このテキストエディタには豊富な拡張機能があり、開発を強力にサポートしてくれます。

Remote Development拡張機能をインストールします。これを使うと、ゲストのファイルをホストで簡単に編集できます。

006_remote001.png

2.4節、2.5節のSSH設定ファイルをこの拡張機能が認識しているので、すぐにSSH接続できます。

006_remote002.png

4. 開発環境の構築

不安な場合は仮想マシンのスナップショットを取っておくとよいです。

4.1 Gitの最新化

開発環境ではバージョン管理システムが必要となる場面が多いです。Gitを使うことにします。ここまでの作業を実施している場合、既にGitがインストールされていますが、バージョンが古いので最新化します。(4.5節で導入するVisual Studio CodeのGit拡張機能がGitのバージョン2.0以上でうまく動作するため。)

現在のGitのバージョン(CentoOS 7デフォルト)は以下です。

# git --version
git version 1.8.3.1

執筆時点の最新版は2.27.0です。手順は以下を参考にします。

ビルド前の準備します。Gitのビルドに必要な以下のパッケージをインストールします。

# yum install \
  zlib-devel \
  perl-devel \
  openssl-devel \
  curl-devel \
  expat-devel \
  gettext-devel

ドキュメントのビルドに必要な以下のパッケージをインストールします。(ドキュメントはいらないかもしれませんが...)

# yum install \
  asciidoc \
  xmlto

既にある古いGitでGitそのもののリポジトリをcloneしてきます。

# cd /tmp
# git clone https://github.com/git/git.git

最新版をチェックアウトします。

# cd /tmp/git
# git checkout v2.27.0

ビルド/インストールします。インストール先は/usr/localにします。少し時間がかかるので気長に待ちましょう。

# make configure
# ./configure --prefix=/usr/local
# make all doc
# make install install-doc install-html

hashをリセットしたあと、新しいGitのバージョンを確認します。

# hash -r
# git --version
git version 2.27.0

4.2 Dockerのインストール

Docker Engineをインストールします。Dockerによって、プロセスを隔離した環境で実行できます。以下を参考にします。

必要なパッケージをインストールします。

# yum install \
  yum-utils \
  device-mapper-persistent-data \
  lvm2

リポジトリを有効化します。

# yum-config-manager \
  --add-repo \
  https://download.docker.com/linux/centos/docker-ce.repo

インストールします。

# yum install \
  docker-ce \
  docker-ce-cli \
  containerd.io

dockerサービスの自動起動を有効化、起動します。

# systemctl enable docker
# systemctl start docker

動作確認します。

# docker run hello-world

4.2.1 dockerグループへの追加

rolengraysユーザーをdockerグループに追加します。これでsudoなしでrolengraysユーザーがdockerコマンドを実行できます。

# usermod -aG docker rolengrays

※ ユーザーdockerグループに追加すると、Dockerホストでそのユーザーにroot権限を与えることとほぼ同じとなります。

4.3 Python3のインストール

Python3をインストールします。あとでインストールするdocker-composeの1.25.x以降のバージョンがPython2をサポートしないためです。(そしてPython2は既に公式のサポートが切れています。)CentOS 7はデフォルトだとPython2しかインストールされていません。

以下を参考にインストールします。

必要なパッケージをインストールします。

# yum groupinstall "development tools"
# yum install \
  bzip2-devel \
  gdbm-devel \
  libffi-devel \
  libuuid-devel \
  ncurses-devel \
  openssl-devel \
  readline-devel \
  sqlite-devel \
  tk-devel \
  wget \
  xz-devel \
  zlib-devel

ソースコードをダウンロードします。

# cd /tmp
# wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz
# tar -xzf Python-3.8.3.tgz

ビルド・インストールします。

# cd /tmp/Python-3.8.3
# ./configure --enable-shared
# make
# make install
# sh -c "echo '/usr/local/lib' > /etc/ld.so.conf.d/custom_python3.conf"
# ldconfig

バージョンを確認します。

# python3 --version
Python 3.8.3

4.4 docker-composeのインストール

docker-composeは同時に複数のコンテナを使うアプリケーションを定義・実行できるツールです。コンテナの実行はdockerコマンドでも実施できますが、オプションが長いコマンドを複数回打つのは非常に面倒です。docker-composeはこの問題を解決できます。以下の通りインストールします。

執筆時点の最新版は1.26.0です。

# curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
# docker-compose --version
docker-compose version 1.26.0, build d4451659

4.5 Visual Studio Code拡張機能のインストール

以下の拡張機能をインストールしておきます。細かい機能は紹介しませんが、便利です。実際にインストールして機能を確かめてみてください。

  • GitLens(行ごとのBlameができる、ファイルの編集履歴をGUIで確認できる、など)
  • Docker(実行中のコンテナや手元にあるイメージなどをGUIで表示してくれる、など)

ここまでで、環境構築は終わました。

5. デモ

利用例を紹介します。

5.1 最低限のWebサイトを構築してみる

最低限のWebサイトをDockerコンテナで構築してみます。最終的にカレントディレクトリ配下は以下のようになります。

.
├── c7-base
│   └── Dockerfile
├── c7-systemd
│   └── Dockerfile
└── docker-compose.yml

5.1.1 systemd有効コンテナイメージの作成

systemdを有効にしたコンテナの定義を作成します。./c7-systemd/Dockerfileを以下のようにします。centos - Docker HubからDockerfileを引用します。

./c7-systemd/Dockerfile
# https://hub.docker.com/_/centos より引用
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

c7-systemdのコンテナイメージをビルドします。

$ docker build -t local/c7-systemd ./c7-systemd

5.1.2 自分の基本イメージの作成

基本イメージを作成します。目的にかかわらず、自分がよく使う汎用パッケージや設定などをあらかじめコンテナイメージにしておくと便利です。./c7-base/Dockerfileを以下のようにします。先ほどビルドしたlocal/c7-systemdをベースにした上で、よく使うパッケージを自分で適当に選んでインストールするとよいです。デフォルトだとタイムゾーンがUTCなので、JSTに設定しておきます。

./c7-base/Dockerfile
FROM local/c7-systemd

# アップデート&パッケージインストール
RUN yum update -y \
  && yum install -y \
    wget \
    tree \
    which \
    net-tools

# タイムゾーン設定
RUN /usr/bin/ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

こちらもビルドします。

$ docker build -t local/c7-base ./c7-base

5.1.3 docker-compose.ymlの作成

docker-compose.ymlを作成します。通常、Dockerコンテナを実行するためには、場合によっては長いコマンドラインオプションを複数打たなければならず、面倒です。./docker-compose.ymlを以下のようにします。コンテナ1つだけ定義します。

./docker-compose.yml
---
version: "3.7"
services:
  c7-base:
    build:
      context: ./c7-base
    image: local/c7-base
    container_name: c7-base
    working_dir: /root
    privileged: true # systemdをコンテナ内で使用するための設定
    stdin_open: true
    tty: true
    # Dockerホストの'8080/TCP'をコンテナの'80/TCP'にマッピングするため
    ports:
      - published: 8080
        target: 80
        protocol: tcp
        mode: host
    volumes:
      # systemdをコンテナ内で使用するための設定
      - type: bind
        source: /sys/fs/cgroup
        target: /sys/fs/cgroup
        read_only: true

※ 上記のdocker-compose定義でのコンテナ実行は、以下のコマンドとほぼ同等です。

# docker run -it --privileged -w /root -p 8080:80 -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name c7-base local/c7-base

5.1.4 コンテナ内でのWebサーバーの作成作業

docker-composeでdockerコンテナを起動します。

$ docker-compose -f ./docker-compose.yml up -d

dockerコンテナにシェルをアタッチします。Visual Studio CodeのDocker拡張機能を使えば、コマンドなしでできます(コンテナを右クリック->Attach Shell)。

[rolengrays@c7sandbox ~]$ docker exec -it c7-base /bin/bash
[root@0b7b64ad6eb6 ~]# 

これでコンテナ内でコマンドを実行できるようになりました。試しにhttpdをインストールして、コンテナ内にWebサーバーを作ってみます。内容が"Hello From Container"のみの簡単なHTMLドキュメントも用意しておきます。

(コンテナ内で実行)
# yum install httpd
# echo "Hello From Container" > /var/www/html/index.html
# systemctl start httpd

終わったらexitコマンドで抜けます。

5.1.5 コンテナ内のWebサーバーにアクセス

ホストからこのWebサーバーにアクセスしてみます。ゲストの'8080/TCP'をコンテナの'80/TCP'にマッピングするように設定しているため、firewalldに8080/TCPを許可する設定をする必要があります。

まずはサービスを作成します。名前はhttp_8080とします。以下のようなXMLファイルを作成すればOKです。

/usr/lib/firewalld/services/http_8080.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>HTTP(8080)</short>
  <description>HTTP 8080 Port</description>
  <port protocol="tcp" port="8080"/>
</service>

ファイルを作成したら、リロードします。

(ゲストで実行)
# firewall-cmd --reload

次にhomeゾーンに先ほど作成したhttp_8080を追加します。追加したら、もう一度リロードします。

(ゲストで実行)
# firewall-cmd --permanent --zone home --add-service http_8080
# firewall-cmd --reload

これで8080/TCPに外からアクセスできるようになりました。ホストでWebブラウザを立ち上げて、http://c7sandbox.local:8080/index.htmlにアクセスしてみましょう。Hello From Containerが表示されれば成功です。

007_demo001.png

Webサーバーのインストールを最初からやり直したい場合は、以下のように一度downしてから、再度upすることで環境(コンテナ)を再構築できます。

$ docker-compose down
$ docker-compose up -d

これでインストールからやり直すことができます。今回はhttpdをインストールするだけの例ですが、代わりにnginxをインストールしてみたりと、ほかにもいろいろな実験を行うことができます。もう一度シェルをアタッチしてアクセスログを見たりもできます。

5.2 Apache Web Server + Tomcatでデモ用Webアプリを作ってみる

複数のコンテナを使用するパターンで試してみます。Apache Web ServerのコンテナとTomcatのコンテナを作成し、それらを連携させてみます。プロジェクトのディレクトリ構成は最終的に以下のようになります。

demo52
├── app
│   ├── Dockerfile
│   ├── build.sh
│   ├── conf
│   │   └── server.xml
│   ├── resource
│   │   ├── apache-tomcat-9.0.36.tar.gz
│   │   └── openjdk-14.0.1_linux-x64_bin.tar.gz
│   └── testapp
│       ├── pom.xml
│       (略)
├── docker-compose.yml
└── proxy
    ├── Dockerfile
    └── conf
        └── app.conf

以下に簡単なイメージを載せておきます。

009_ajp001.png

5.2.1 テスト用Webアプリの作成

Tomcatの上で動かす簡単なアプリを作ります。ビルドツールはMavenを使います。公式のDockerイメージを使いましょう。

まずはDockerホスト側にMavenローカルリポジトリを作成します。これをコンテナ内に置いてしまうと、コマンド実行後にダウンロードしたパッケージがコンテナごと破棄されてしまいます。こうなると2度目のビルド以降も大量のダウンロードが発生するため、時間の無駄になります。コンテナ実行後も永続化したいファイルはコンテナ外(ここではDockerホスト)に配備するのがよいです。

$ mkdir -p ${HOME}/data/maven_repo

以下のようにディレクトリを作成しておきます。

# mkdir -p ./app/conf ./app/resource ./proxy/conf

次にMavenでWebアプリプロジェクトのひな形を作ります。公式からMavenイメージをpullしてから、コンテナを作成・実行して必要なパッケージのダウンロードなどを行うため、少し時間がかかります。

$ docker run \
  -it \
  --rm \
  --name my-maven-project \
  -v $(pwd)/app:/usr/src/mymaven \
  -v ${HOME}/data/maven_repo:/root/.m2 \
  -w /usr/src/mymaven \
  maven:3.6.3-jdk-14 mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DarchetypeVersion=1.4 \
    -DgroupId=com.rolengrays.example \
    -DartifactId=testapp \
    -Dversion=1.0.0

終わったら、./app/testappというディレクトリができているはずです。

./app/testapp
├── pom.xml
└── src
    └── main
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

特にユーザーを指定せずにDockerコンテナからファイルを作成したので、所有者がrootユーザーになっています。自分のユーザーにしましょう。

$ sudo chown -R rolengrays:rolengrays ./app/testapp

次にpom.xmlを編集します。今回はJDK14を使いたいので、デフォルトから以下のように書き換えます。

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-   <maven.compiler.source>1.7</maven.compiler.source>
-   <maven.compiler.target>1.7</maven.compiler.target>
+   <maven.compiler.source>1.14</maven.compiler.source>
+   <maven.compiler.target>1.14</maven.compiler.target>
  </properties>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
+         <configuration>
+           <source>14</source>
+           <target>14</target>
+         </configuration>
        </plugin>

各種ソースファイルを準備します。

$ mkdir -p ./app/testapp/src/main/java/com/rolengrays/example

Hello.javaでは、単純な文字列を生成するだけのクラスを定義します。

./app/testapp/src/main/java/com/rolengrays/example/Hello.java
package com.rolengrays.example;

public class Hello {
  public Hello() {}
  public static String greetings() { return new String("Hello From Container Web App"); }
}

index.jspでは、Helloクラスに文字列を出力させます。

./app/testapp/src/main/webapp/index.jsp
<%@ page import="com.rolengrays.example.Hello" %>
<html>
<body>
<h2>
  <% out.println(Hello.greetings()); %>
</h2>
</body>
</html>

ソースの準備が終わったので、ビルドしましょう。コマンドが長いため、ビルドスクリプトを作っておきます。

./app/build.sh
#!/bin/bash
docker run \
  --rm \
  --name my-maven-project \
  -v "$(pwd)/app/testapp":/usr/src/mymaven \
  -v ${HOME}/data/maven_repo:/root/.m2 \
  -w /usr/src/mymaven \
  maven:3.6.3-jdk-14 mvn package
$ chmod +x ./app/build.sh
$ ./app/build.sh

multi-stage buildで、コンテナイメージのビルド時にWebアプリのビルドを行い、成果物(warファイル)だけをコンテナイメージに残す方法があります。しかし、コンテナイメージのビルド時にMavenのローカルリポジトリを参照するうまい方法がないため、ビルドのたびに大量のダウンロードが発生します。それを避けるため、今回はWebアプリのビルドをコンテナイメージのビルド前にいちいち手動で行うこととします。

5.2.2 Tomcatのコンテナイメージの作成

必要なソフトウェアをダウンロードします。

ソフトウェア ダウンロードページ 保存先
OpenJDK 14 https://jdk.java.net/14/ ./app/resource/openjdk-14.0.1_linux-x64_bin.tar.gz
Tomcat 9 https://tomcat.apache.org/download-90.cgi ./app/resource/apache-tomcat-9.0.36.tar.gz
$ ls -l ./app/resource
合計 204952
-rw-rw-r--. 1 rolengrays rolengrays  11200905  6月  4 03:03 apache-tomcat-9.0.36.tar.gz
-rw-rw-r--. 1 rolengrays rolengrays 198665889  3月  5 19:40 openjdk-14.0.1_linux-x64_bin.tar.gz

TomcatがApache Web ServerとAJP通信できるように設定をします。Tomcat 9のアーカイブからserver.xmlを抜き出して、./app/confに配備します。

$ tar -C /tmp -xzf ./app/resource/apache-tomcat-9.0.36.tar.gz apache-tomcat-9.0.36/conf/server.xml
$ cp /tmp/apache-tomcat-9.0.36/conf/server.xml ./app/conf
$ rm -rf /tmp/apache-tomcat-9.0.36

その後、./app/conf/server.xmlのAJP設定部分を以下のように編集します。(デフォルトだとコメントアウトされています。)

./app/conf/server.xml
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector protocol="AJP/1.3"
               secretRequired="false"
               address="0.0.0.0"
               port="8009"
               redirectPort="8443" />
  • secretRequired="false"属性を追加します。ないとエラーになります。trueにした場合はほかにもいろいろ設定が必要で手間がかかるため、ここでは明示的にfalseを設定します。
  • address属性値を0.0.0.0に書き換えます。デフォルトはaddress::1でIPv6になっていますが、今回はDockerコンテナ・ネットワークをIPv6で構成していないため、IPv4でもListenできるように0.0.0.0に書き換えます。

./app/Dockerfileを作成します。

./app/Dockerfile
# 5.1節で作成した自分の基本イメージをベースとする
FROM local/c7-base

# 変数
ARG java_home="/usr/local/java/jdk-14.0.1"
ARG catalina_home="/usr/local/tomcat/apache-tomcat-9.0.36"

# JDK14のインストール
ADD ./resource/openjdk-14.0.1_linux-x64_bin.tar.gz /usr/local/java
ENV JAVA_HOME ${java_home}

# Tomcat9のインストール
ADD ./resource/apache-tomcat-9.0.36.tar.gz /usr/local/tomcat
ENV CATALINA_BASE ${catalina_home}
ENV CATALINA_HOME ${catalina_home}
## AJPの設定を記述したserver.xmlをコピー
COPY ./conf/server.xml ${catalina_home}/conf/server.xml

# ビルドしたwarの配備
COPY ./testapp/target/testapp.war ${catalina_home}/webapps

CMD [ "/bin/bash", "-c", "${CATALINA_HOME}/bin/catalina.sh run" ]

5.2.3 AJPプロキシのコンテナイメージの作成

Apache Web ServerをAJPプロキシサーバーとして構成します。以下のVirtualHost定義を作成します。これでプロキシサーバーへのアクセスをWebアプリサーバーの/testapp/パスへ転送します。

./app/app.conf
<VirtualHost *:80>
  ServerName c7sandbox.local
  ProxyPass / ajp://app:8009/testapp/
</VirtualHost>
./proxy/Dockerfile
FROM local/c7-systemd

RUN yum install -y httpd && systemctl enable httpd
COPY ./conf/app.conf /etc/httpd/conf.d/app.conf

5.2.4 Webアプリの構築

docker-composeの定義を作成します。以下の通り、2種類のコンテナを定義します。

./docker-compose.yml
---
version: "3.7"
services:
  # AJPプロキシサーバーのコンテナ
  proxy:
    build:
      context: ./proxy
    image: local/proxy
    networks:
      - testnet
    privileged: true
    stdin_open: true
    tty: true
    ports:
      - target: 80
        published: 8080
        protocol: tcp
        mode: host
    volumes:
      - type: bind
        source: /sys/fs/cgroup
        target: /sys/fs/cgroup
        read_only: true
  # Webアプリサーバーのコンテナ
  app:
    build:
      context: ./app
    image: local/app
    container_name: app # プロキシサーバーからappで名前解決できるようになります。
    networks:
      - testnet
    privileged: true
    stdin_open: true
    tty: true
    volumes:
      - type: bind
        source: /sys/fs/cgroup
        target: /sys/fs/cgroup
        read_only: true
networks:
  testnet:

5.2.5 コンテナの実行

コンテナを実行します。--buildでコンテナイメージをビルドしてから実行します。

$ docker-compose up -d --build

※ 5.1節で起動したコンテナが残っていたら、上記を実行する前に停止してください。

ホストOSでブラウザからhttp://c7sandbox.local:8080/にアクセスしてみましょう。以下のようにHello From Container Web Appが表示されれば成功しています。

008_demo001.png

設定変更などでいろいろ実験したい場合は、Dockerコンテナ内にシェルをアタッチして環境を手動で変更できます。もちろん、コマンドの代わりにVisual Studio CodeのDocker拡張機能を使ってアタッチしてもよいです。

$ docker ps -a
CONTAINER ID  IMAGE        COMMAND                 CREATED        STATUS        PORTS                 NAMES
08d677a1ce69  local/proxy  "/usr/sbin/init"        2 minutes ago  Up 2 minutes  0.0.0.0:8080->80/tcp  demo52_proxy_1
edc3f676c012  local/app    "/bin/bash -c '${CAT…"  2 minutes ago  Up 2 minutes                        app

プロキシサーバーのコンテナ名をdocker-compose.ymlで指定していないため、docker-composeが自動で命名しています。

$ docker exec -it demo52_proxy_1 /bin/bash
$ docker exec -it app /bin/bash

初期状態にコンテナを戻したい場合は、5.1節でやったように以下のコマンドを実行します。Dockerfileを編集した場合は、--buildオプションをつけてupしましょう。

$ docker-compose down
$ docker-compose up -d --build

6. まとめ

今回は、VirtualBoxとDockerで開発環境を構築してみました。Dockerで環境を隔離できて作り直しも楽なので、いろんなことを気軽に実験できます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker + Rails で Bundler v2.X を使う

Gemfile.lock の BUNDLED WITH と Docker イメージ内の bundler バージョンに乖離があるとエラーになる。

https://qiita.com/tanakaworld/items/e15ff9dbdd4b628378c2

次の通り対策ができる。

  1. イメージ内の gem をアップデート gem update --system
  2. 任意のバージョンの bundler をインストール gem install bundler -v <バージョン>

例:

Dockerfile
FROM ruby:2.6.2

ENV APP_HOME /app
WORKDIR $APP_HOME

COPY Gemfile $APP_HOME/Gemfile
COPY Gemfile.lock $APP_HOME/Gemfile.lock

ENV BUNDLER_VERSION 2.1.0
RUN gem update --system \
    && gem install bundler -v $BUNDLER_VERSION \
    && bundle install -j 4

COPY . $APP_HOME
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

できるだけ簡単にKubernetesする(Windows10)Part2

前回は、kubernetesを使えるようになるところまでで、終了しました。

今回は、実際にコンテナを動かしてみます。

とはいっても、いきなりコンテナをたくさん動かすと、
コンテナ同士のつながりが、わけわからなくなってしまうので、
簡単さを目的として、最初は1つのみ動かしてみたいと思います。

もし、現在、Kubernetesを使っていないとしたら、
CentOS や Ubuntu を使われているかもしれません。

そのあたりが身近な気がするので、実際に動かしてみましょう。

コンテナを動かすためのファイル作成

では、コマンドプロンプトを起動して、

C:\Users\test>

今、test というアカウントで操作しているのですが、
この階層で作ると、ごちゃごちゃしそうなので、
kubernetesディレクトリを作っておこうと思います。

C:\Users\test> mkdir kubernetes

問題なく作れたら、kubernetesディレクトリに移動しましょう。

C:\Users\test> cd kubernetes

C:\Users\test\kubernetes>

では、ここの階層に sample.yaml というファイルを作ってみてください。
ファイルを作成したら、以下の内容を書き、保存します。

apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: sample-container
      image: centos

centos ではなく、ubuntu を使いたい、という場合は、
centos と書いてある部分を ubuntu に変更するだけでOKです。

kubernetes の最初は何かと yamlファイルを書くことが多く、
いきなり長いものが出てくると、やる気をそがれるので、
シンプルなものから始めます。

コンテナ作成

ファイルを保存しましたら、「kubectl apply -f sample.yaml」という
コマンドを実行してみましょう。
このコマンドの意味ですが、簡単にいうと、sample.yaml を適用するものだ、
と思ってもらえれば、いまのところは大丈夫です。

C:\Users\test\kubernetes> kubectl apply -f sample.yaml
pod/sample-pod created

pod/sample-pod created が出たら、OKです。

もしここでエラーが出るようであれば「dir」コマンドで、
ファイルが存在しているか、ファイル名が間違っていないか、
を確認してみてください。

C:\Users\test\kubernetes> dir

2020/06/16  18:19    <DIR>          .
2020/06/16  18:19    <DIR>          ..
2020/06/16  18:26               129 sample.yaml

うまくいっているようであれば「kubectl get pods」のコマンドで、
何かしら見えるはずです。

C:\Users\test\kubernetes> kubectl get pods
NAME         READY   STATUS              RESTARTS   AGE
sample-pod   0/1     ContainerCreating   0          1s

どうやら今、コンテナを作ってくれているようです。
少し待ってみます。

C:\Users\test\kubernetes> kubectl get pods
NAME         READY   STATUS      RESTARTS   AGE
sample-pod   0/1     Completed   0          5s

なぜか完了されてしまいました。
READY部分が 0 のままですと、基本使えるようにはなってないです。

もう少し待って確認してみたところ、

C:\Users\test\kubernetes> kubectl get pods
NAME         READY   STATUS             RESTARTS   AGE
sample-pod   0/1     CrashLoopBackOff   1          10s

なんかダメそうですので、「kubectl delete -f sample.yaml」で、
一度削除しておきましょう。

C:\Users\test\kubernetes> kubectl delete -f sample.yaml
pod "sample-pod" deleted

コンテナを起動したままにする

ダメだった理由なのですが、コンテナは自分の仕事をしたら完了しようとする、
からのようです。
私、最初、ここでつまづき、そこそこ時間を使いました。

なので、とりあえず、何かしら仕事を与えてあげたいと思います。

さっきの sample.yaml ファイルを以下に修正して、上書きしてください。
追加したのは、下の2行です。

apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: sample-container
      image: centos
      command: ["/bin/sleep"]
      args: ["600"]

600 秒間 sleep させてみます。
これで、600 秒間は、仕事が終わらないため、
完了できないはずです。

ファイルを保存したら、再度「kubectl apply -f sample.yaml」コマンドを実行します。

C:\Users\test\kubernetes> kubectl apply -f sample.yaml
pod/sample-pod created

作成はうまくいったようですが、さっきと変わったか確認してみます。

C:\Users\test\kubernetes> kubectl get pods
NAME         READY   STATUS              RESTARTS   AGE
sample-pod   0/1     ContainerCreating   0          1s

今回もここまでは変わらなそうですね。

C:\Users\test\kubernetes> kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          6s

お、READY が 1/1 になり、Running ということは起動しているっぽいです。

コンテナへのアクセス

では、次に直接コンテナをさわってみたいので、
「kubectl exec -it sample-pod /bin/bash」を打ってみましょう。
このコマンドの意味ですが、sample-pod にアクセスするコマンドだ、
と思ってもらえれば、いまのところは問題ないかと思います。

C:\Users\test\kubernetes> kubectl exec -it sample-pod /bin/bash
[root@sample-pod /]#

ここまでくると、sshっぽい感じで、操作することが可能です。

やっと、Windows から逃れることができました。
ためしに、「cat /etc/redhat-release」コマンドで、
CentOS のバージョンを確認してみます。

[root@sample-pod /]# cat /etc/redhat-release
CentOS Linux release 8.1.1911 (Core)

CentOS 8 ですね。

CentOS 6 → 7 よりは、変わらなかった気がしますが、
私、普段はまだ CentOS 7 を使っています。
もし CentOS 8 以外を使いたい場合は、
さっきの sample.yaml ファイルの image 部分を変更すればOKです。

ひとまず、exit で抜けて、一度削除しておきましょう。

[root@sample-pod /]# exit
exit

C:\Users\test\kubernetes> kubectl delete -f sample.yaml
pod "sample-pod" deleted

image の変更

では、どのように image 部分を変更するかですが、

Docker Hub の CentOS ページ
「Supported tags and respective Dockerfile links」という部分に、
使えるタグが載っています。

タグ付けは簡単でして、image 部分の centos のあとに、
:タグ名
と書くだけです。

たとえば、centos7.8.2003 のタグを使いたいとしたら、
以下のように修正します。

apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: sample-container
      image: centos:centos7.8.2003
      command: ["/bin/sleep"]
      args: ["600"]

これで、再度ファイルを apply して、

C:\Users\test\kubernetes> kubectl apply -f sample.yaml
pod/sample-pod created

コンテナの中に入って、バージョンを確認してみます。

C:\Users\test\kubernetes> kubectl exec -it sample-pod /bin/bash
[root@sample-pod /]# cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)

変わってますね、問題なさそうです。

ちなみに、タグを指定していない場合は、デフォルトが latest なので、
centos:latest を指定しているのと同じです。
latest の image が、CentOS 8 だったようです。

Docker Hubでコンテナ検索していただけると、
かなりいろいろ出てきますので、試しに好きな image を利用してみてください。

次回は、複数コンテナを動かしてみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker ディスク容量を圧迫し、docker-compose buildできない時の対処法

No space left on deviceエラー

docker-compose buildコマンド実行時、

OSError: [Errno 28] No space left on device

というエラーが発生し、ビルドができなくなってしまいました。

この問題を解決してくれたコマンドは以下になります。

docker system prune

これは、不要なリソースを全て削除してくれるコマンドです。
自動で使われていないと思われるリソースを削除します。
ローカル開発環境であればこの方法が一番簡単なようです。
docker system prune | Docker Documentation

その他、docker環境を掃除してくれるコマンド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

どんなに小さなサイドプロジェクトでもCI/CDパイプラインは大いに役立つ

こちらの記事は、Luc Juggery 氏により2018年9月に公開された『 Even the Smallest Side Project Deserves its CI/CD Pipeline 』の和訳です。
本記事は原著者から許可を得た上で記事を公開しています。

Image

TL;DR

今を行く最新のツールを使えば、シンプルなCI/CDパイプラインを構築することは難しくありません。個人的なサイドプロジェクトであっても、CI/CDパイプラインを構築することで多くのことを学ぶことができるでしょう。Docker、GitLab、Portainerはこのようなセットアップで使うのに最適なコンポーネントです。

サンプルプロジェクト

南フランスのソフィア・アンティポリス地域で技術イベントをよく主催する私は、今後開催されるイベントを全部知る方法はないのかとよく聞かれていました(ミートアップ、ジャグ、自治会主催のものなど)。これまでそういったイベントを全てリストアップしている所は一つも無かったので、常に最新のイベントの一覧情報を更新する https://sophia.events というシンプルなウェブページを思いつきました。このプロジェクトはGitLab上で確認できます。

免責事項: これは単純なプロジェクトですが、プロジェクトの複雑さはここでは重要ではありません。これから説明するCI/CDパイプラインのコンポーネントは、さらに複雑なプロジェクトでも同じように使うことができますが、今回の様なマイクロサービスには特に適しています。

コードの概要

物事をなるべくシンプルに保つため、基本的には新しいイベントが追加されるたびにそのイベントのjsonファイルが追加される様になっています。このファイルの一部は以下のスニペットの様になっています(フランス語になっている部分はごめんなさい)。

events.json
{
  "events": [
    {
      "title": "All Day DevOps 2018",
      "desc": "We're back with 100, 30-minute practitioner-led sessions and live Q&A on Slack. Our 5 tracks include CI/CD, Cloud-Native Infrastructure, DevSecOps, Cultural Transformations, and Site Reliability Engineering. 24 hours. 112 speakers. Free online.",
      "date": "17 octobre 2018, online event",
      "ts": "20181017T000000",
      "link": "https://www.alldaydevops.com/",
      "sponsors": [{"name": "all-day-devops"}]
    },
    {
      "title": "Création d'une Blockchain d'entreprise (lab) & introduction aux smart contracts",
      "desc": "Venez avec votre laptop ! Nous vous proposons de nous rejoindre pour réaliser la création d'un premier prototype d'une Blockchain d'entreprise (Lab) et avoir une introduction aux smart contracts.",
    "ts": "20181004T181500",
    "date": "4 octobre à 18h15 au CEEI",
    "link": "https://www.meetup.com/fr-FR/IBM-Cloud-Cote-d-Azur-Meetup/events/254472667/",
    "sponsors": [{"name": "ibm"}]
    },
    
  ]
}

このファイルにmustacheのテンプレートが適用され、最終的なウェブアセットが生成されます。

Dockerマルチステージビルド

ウェブアセットを生成したら、それらはターゲットマシンにデプロイされたnginxイメージにコピーされます。

マルチステージビルドのおかげで、ビルドは以下の二段階に分かれて行われます:

  • アセットの生成
  • アセットを含む最終的なイメージを作成

ビルドに使うDockerfileは以下の通りです:

# Generate the assets
FROM node:8.12.0-alpine AS build
COPY . /build
WORKDIR /build
RUN npm i
RUN node clean.js
RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html# Build the final image used to serve them
FROM nginx:1.14.0
COPY --from=build /build/*.html /usr/share/nginx/html/
COPY events.json /usr/share/nginx/html/
COPY css /usr/share/nginx/html/css
COPY js /usr/share/nginx/html/js
COPY img /usr/share/nginx/html/img

ローカルテスト

サイトの生成をテストするために、レポジトリをクローンしてtest.shスクリプトを実行します。するとイメージが作成され、そこからコンテナが実行されます。

$ git clone git@gitlab.com:lucj/sophia.events.git$ cd sophia.events$ ./test.sh
Sending build context to Docker daemon  2.588MB
Step 1/12 : FROM node:8.12.0-alpine AS build
---> df48b68da02a
Step 2/12 : COPY . /build
---> f4005274aadf
Step 3/12 : WORKDIR /build
---> Running in 5222c3b6cf12
Removing intermediate container 5222c3b6cf12
---> 81947306e4af
Step 4/12 : RUN npm i
---> Running in de4e6182036b
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN www@1.0.0 No repository field.added 2 packages from 3 contributors and audited 2 packages in 1.675s
found 0 vulnerabilitiesRemoving intermediate container de4e6182036b
---> d0eb4627e01f
Step 5/12 : RUN node clean.js
---> Running in f4d3c4745901
Removing intermediate container f4d3c4745901
---> 602987ce7162
Step 6/12 : RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html
---> Running in 05b5ebd73b89
Removing intermediate container 05b5ebd73b89
---> d982ff9cc61c
Step 7/12 : FROM nginx:1.14.0
---> 86898218889a
Step 8/12 : COPY --from=build /build/*.html /usr/share/nginx/html/
---> Using cache
---> e0c25127223f
Step 9/12 : COPY events.json /usr/share/nginx/html/
---> Using cache
---> 64e8a1c5e79d
Step 10/12 : COPY css /usr/share/nginx/html/css
---> Using cache
---> e524c31b64c2
Step 11/12 : COPY js /usr/share/nginx/html/js
---> Using cache
---> 1ef9dece9bb4
Step 12/12 : COPY img /usr/share/nginx/html/img
---> e50bf7836d2f
Successfully built e50bf7836d2f
Successfully tagged registry.gitlab.com/lucj/sophia.events:latest
=> web site available on http://localhost:32768

出力の最後に表示されたURLから、ウェブページにアクセスできるようになります:

Image

ターゲット環境

クラウド事業者が提供する仮想マシン

お気づきの通り、このウェブサイトはクリティカルなものではなく(1日に数十件の訪問しかない)そのため、1台の仮想マシンでしか動作させていません。このサイトはヨーロッパの有名なクラウド事業者であるExoscale上でDocker Machineを使って作成したものです。

もしExoscaleを使ってみたいのなら、私にメッセージしてもらえれば20€の割引クーポンを差し上げますよ。

SwarmモードのDockerデーモン

上記の仮想マシン上で動作しているDockerデーモンはSwarmモードで動作するように設定されているので、スタック、サービス、設定、シークレットプリミティブとDocker Swarmの優れていて使いやすいオーケストレーション機能を使用することができます。

Dockerスタックとして実行中のアプリケーション

以下のファイルでは、ウェブアセットを含むnginxウェブサーバを動作させるサービスを定義しています。

docker-compose.yml
version: "3.7"
services:
  www:
    image: registry.gitlab.com/lucj/sophia.events
    networks:
      - proxy
    deploy:
      mode: replicated
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
networks:
  proxy:
    external: true

いくつかの説明:

  • イメージはgitlab.comでホストされているプライベートレジストリにあります(ここではDocker Hubは関係ありません)。
  • サービスは2つのレプリカを持つレプリケートモードになっており、1つのサービスに対して2つのタスク/コンテナが同時に実行されていることを意味します。VIP(仮想IPアドレス)はSwarmによってサービスに関連付けられており、そのサービスを対象とした各リクエストが2つのレプリカの間で負荷分散されるようになっています。
  • 新しいバージョンのウェブサイトをデプロイし、サービスが更新されると、まず先に1つ目のレプリカが更新され、10秒後に2つ目のレプリカが更新されます。これにより、更新プロセス中もウェブサイトが利用可能な状態であることが保証されます。ロールバック戦略を使うこともできましたが、現時点では必要ありません。
  • サービスは外部のプロキシネットワークに接続されているので、TLS終端はwwwサービスにリクエストを送信することができます(このTLS終端はSwarm上に配置された別のサービスで実行されていますが、このプロジェクトの外部にあります)。

スタックは下記のコマンドで実行できます:

$ docker stack deploy -c sophia.yml sophia_events

Portainerを使って管理する

PortainerはDockerホストやDocker Swarmクラスタを簡単に管理できる優れたWeb UIです。以下は、Swarmで利用可能なスタックを一覧表示するPortainerインターフェースのスクリーンショットです。

Image

現在のセットアップでは以下の3つのスタックが表示されています:

  • Portainer自身
  • 今回のウェブサイトを実行しているサービスを含むsophia_events
  • TLS終端であるtls

sophia_eventsスタックにあるwwwサービスの詳細をリストアップすると、Service webhookが有効になっていることがわかります。これはPortainer 1.19.2(執筆時点での最新バージョン)から利用可能な機能で、HTTP Postのエンドポイントを定義することで、サービスの更新をトリガーとして呼び出すことができます。詳細は後述しますが、GitLabランナーはこのwebhookの呼び出しを担当しています。

Image

注意: スクリーンショットからわかるように、今回私はPortainer UIへlocalhost:8888からアクセスしています。Portainerのインスタンスは外部に公開したくないので、以下のコマンドで開いたsshトンネルを使ってアクセスしています。

ssh -i ~/.docker/machine/machines/labs/id_rsa -NL 8888:localhost:9000 $USER@$HOST

続けて、8888番ポートのローカルマシンをターゲットにしたすべてのリクエストが、sshを介して仮想マシンの9000番ポートに送信されます。9000番はPortainerが仮想マシン上で動作しているポートですが、このポートはExoscaleの設定でセキュリティグループによってブロックされているため、外部には開放されていません。

注意: 上記のコマンドでは、VMに接続するために使用するsshキーは、VM作成時にDocker Machineが生成したものを使用しています。

GitLabランナー

GitLabランナーとは.gitlab-ci.ymlファイルで定義されたアクションを実行するプロセスのことです。このプロジェクトでは、仮想マシン上でコンテナとして動作する独自のランナーを定義しています。

まずは、いくつかのオプションをつけたランナーを登録します:

CONFIG_FOLDER=/tmp/gitlab-runner-configdocker run — rm -t -i \
-v $CONFIG_FOLDER:/etc/gitlab-runner \
gitlab/gitlab-runner register \
  --non-interactive \
  --executor "docker" \
  —-docker-image docker:stable \
  --url "https://gitlab.com/" \
  —-registration-token "$PROJECT_TOKEN" \
  —-description "Exoscale Docker Runner" \
  --tag-list "docker" \
  --run-untagged \
  —-locked="false" \
  --docker-privileged

これらのオプションの中にあるproject_tokenはGitLab.comのプロジェクトページから提供されているもので、外部ランナーの登録に使用するものです。

Image

ランナーを登録したら以下の様に実行します:

CONFIG_FOLDER=/tmp/gitlab-runner-configdocker run -d \
--name gitlab-runner \
—-restart always \
-v $CONFIG_FOLDER:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest

登録して実行まですると、ランナーがGitLab.comのプロジェクトページに表示される様になります:

Image

このランナーは、新しいコミットがリポジトリにプッシュされるたびに与えられた作業、つまり.gitlab-ci.ymlファイルで定義されているテスト、ビルド、デプロイの各段階を順次実行します。

gitlab-ci.yml
variables:
  CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
  DOCKER_HOST: tcp://docker:2375
stages:
  - test
  - build
  - deploy
test:
  stage: test
  image: node:8.12.0-alpine
  script:
    - npm i
    - npm test
build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  script:
    - docker image build -t $CONTAINER_IMAGE:$CI_BUILD_REF -t $CONTAINER_IMAGE:latest .
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
    - docker image push $CONTAINER_IMAGE:latest
    - docker image push $CONTAINER_IMAGE:$CI_BUILD_REF
  only:
    - master
deploy:
  stage: deploy
  image: alpine
  script:
    - apk add --update curl
    - curl -XPOST $WWW_WEBHOOK
  only:
    - master
  • テストの段階では、event.jsonファイルのフォーマットが正しいかどうか、イメージが欠落していないかどうかなどの事前チェックを行います。
  • ビルドの段階ではイメージをビルドし、GitLabレジストリにプッシュします。
  • デプロイの段階では、Portainerに送られたWebhookを使ってサービスの更新をトリガーします。WWW_WEBHOOK変数は、GitLab.comのプロジェクトページのCI/CD設定で定義しています。

Image

注意事項:
このランナーはSwarm上のコンテナで実行されており、共有ランナーというGitLab上でホストされている他のプロジェクトと共有する公開ランナーを使うこともできましたが、今回はランナーが(Webhookを送信するために)Portainerエンドポイントにアクセスする必要があり、Portainerには外部からアクセスされたくはなかったため、クラスター内でランナーを動かす方が安全だと判断しました。

また、ランナーはコンテナ内で動作しているため、ホスト上で公開されている9000番ポートを介してPortainerと連絡を取るために、Docker0ブリッジネットワークのIPアドレスに対してWebhookを送信しています。そのため、Webhookは以下のような形式になっています。

http://172.17.0.1:9000/api[…]a7-4af2-a95b-b748d92f1b3b

デプロイの流れ

新しいバージョンのサイトの更新は、以下のような流れで行われます:

Image

  1. ある開発者がGitLabにいくつかの変更をプッシュしたとします。この変更には、基本的には1つかいくつかの新しいイベントとイベントのスポンサーのロゴがevents.jsonファイルに含まれています。
  2. GitLabランナーが.gitlab-ci.ymlで定義されているアクションを実行します。
  3. GitLabランナーがPortainerで定義されたWebhookを呼び出します。
  4. Webhookを受信すると、Portainerは新しいバージョンのwwwサービスをデプロイします。これはDocker Swarm APIを呼び出して行います。起動時にソケット/var/run/docker.sockがバインドマウントされているので、PortainerはAPIにアクセスできます。 このUnixソケットの使い方について詳しく知りたい場合は、私の以前の記事を参照してください。
  5. これでユーザーは新しいバージョンのウェブサイトを閲覧できる様になります。

コードの一部を変更して、その変更をコミット/プッシュしてみましょう。

$ git commit -m 'Fix image'
$ git push origin master

下のスクリーンショットは、GitLab.comのプロジェクトページ内のコミットによってトリガーされたパイプラインを示しています。

Image

Portainer側では、Webhookを受信してサービスの更新が行われました。ここでははっきりとは確認できませんが、1つ目のレプリカが更新されている間、2つ目のレプリカから引き続きWebサイトにアクセスできる状態になっています。そして数秒後に2つ目のレプリカも同じ様に更新されます。

Image

まとめ

このような小さなプロジェクトでも、CI/CDパイプラインの設定を経て、(ずっと前から私のTo Learnリストに載っていた)GitLabに慣れるための良い練習になりました。GitLabは優れたプロフェッショナル製品です。また、Portainerの最新バージョン(1.19.2)で利用できるようになった待望のWebhook機能で遊ぶとても良い機会にもなりました。今回のようなサイドプロジェクトに、Docker Swarmはとても簡単に使えて、間違いなくぴったりな選択肢だったと思います!

翻訳協力

Original Author: Luc Juggery
Thank you for letting us share your knowledge!

この記事は以下の方々のご協力により公開する事が出来ました。
改めて感謝致します。
選定担当: @upaldus
翻訳担当: @upaldus
監査担当: takujio
公開担当: @r_pg10

ご意見・ご感想をお待ちしております

今回の記事は、いかがだったでしょうか?
・こうしたら良かった、もっとこうして欲しい、こうした方が良いのではないか
・こういったところが良かった
などなど、率直なご意見を募集しております。
いただいたお声は、今後の記事の質向上に役立たせていただきますので、お気軽にコメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
みなさまのメッセージをお待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

単一コンテナでのfluentd利用チュートリアル

またもや身内向けなので淡白に書いていきます。

通常はdocker-composeでコンテナ間でサービス連携するのがCoolだと思います。
ただ、諸事情により他サービスがコンテナで動いてない且つfluentdの環境はコンテナにしてホストOSの環境を汚したくないというニッチな人向けチュートリアル

fluentd環境構築

docker-hubからイメージをpull、何も明示しないとlatestになるので
ほしいイメージはこちらから探してみてください。

$docker pull fluend

fluentd.confの設定

dockerは絶対パスでマウントするため今回はホスト上の/_fluentd/etc/fluentd.conf
に設定を記述します。
今回はコンテナ上のfluentdと疎通確認したいだけなので公式ドキュメントにある
httpプロトコル監視サンプルを利用します。

fluentd.conf
<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

<match test.cycle>
  @type stdout
</match>

fluentdコンテナの立ち上げ

$docker run -ti -p 8888:8888 --rm -v /_fluentd/etc:/fluentd/etc \
fluentd -c /fluentd/etc/fluentd.conf -v

順に解説していきます。

マウント

-v /_fluentd/etc:/fluentd/etc
ホストの/_fluentd/etcをマウントすることでホストで書いた設定を
コンテナに反映できるようにしてます。

confファイルの設定

td-agentを利用している場合はconfファイルが別の名前だったりします。

そういう方向けに、以下オプションでファイル名を明示的に設定できます。

-c /fluentd/etc/<conf-file>

portのマッピング

設定ファイルで記述したポート8888をリッスンできるようにします。
-p 8888:8888

疎通テスト

curl -i -X POST -d 'json={"action":"login","user":2}' \ 
  http://localhost:8888/test.cycle

ここでurlにtest.cycleという文言を含めるのがキモです。
当たり前ですがfluentdでは<match>で定義したタグのみ監視します。

うまく行けば以下のようなログが出力されます。

log.rb
2020-06-28 08:20:20.195562574 +0000 test.cycle:  
  {"action":"login","user":2}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker 問題なかったコンテナでいきなりError: ENOENT: no such file or directory, uv_cwd

(自分用メモ)

症状

土日だけ勉強しているとかでPCを久々に開いた時にコンテナ立ち上げたときに
ちゃんと今まで通りに動かない場合がある。

Error: ENOENT: no such file or directory, uv_cwd とかのエラーがでる。

npm list とか打っただけで以下の通り

$ npm list
Error: ENOENT: no such file or directory, uv_cwd
    at process.wrappedCwd (internal/bootstrap/switches/does_own_process_state.js:129:28)
    at process.cwd (/usr/local/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:10:19)
    at Conf.loadPrefix (/usr/local/lib/node_modules/npm/lib/config/load-prefix.js:46:24)
    at load_ (/usr/local/lib/node_modules/npm/lib/config/core.js:109:8)
    at Conf.<anonymous> (/usr/local/lib/node_modules/npm/lib/config/core.js:96:5)
    at Conf.emit (events.js:315:20)
    at ConfigChain._resolve (/usr/local/lib/node_modules/npm/node_modules/config-chain/index.js:281:34)
    at ConfigChain.add (/usr/local/lib/node_modules/npm/node_modules/config-chain/index.js:259:10)
    at Conf.add (/usr/local/lib/node_modules/npm/lib/config/core.js:338:27)
    at Conf.<anonymous> (/usr/local/lib/node_modules/npm/lib/config/core.js:314:25)
internal/bootstrap/switches/does_own_process_state.js:129
    cachedCwd = rawMethods.cwd();
                           ^

Error: ENOENT: no such file or directory, uv_cwd
    at process.wrappedCwd (internal/bootstrap/switches/does_own_process_state.js:129:28)
    at process.cwd (/usr/local/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:10:19)
    at process.errorHandler (/usr/local/lib/node_modules/npm/lib/utils/error-handler.js:181:30)
    at process.emit (events.js:315:20)
    at process._fatalException (internal/process/execution.js:165:25) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'uv_cwd'
}

解決策

(自分の場合はMAC)起動しているdocker自体を再起動してやりましょう。
アイコン右クリックでrestart的な

これでもう一度コンテナ立ち上げてやるとちゃんと動きました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$docker build でエラー

$docker build .
実行時に下記エラーが発生
Error response from daemon: Dockerfile parse error line 1: FROM requires either one or three arguments

原因:
DockerfileのFROM部分に半角スペースがあったため
例)
誤 FROM ubuntu: latest
         ↑この部分が不要
正 FROM ubuntu:latest

クセで半角スペースを入れてしまった。
エラー文をよく読めばすぐ解決できたが、エラー文でググっても基本的すぎて、
解決方法が見つけられず。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerをインストールしてJava実行環境をつくる

新しいパソコンでJava実行環境を構築するにあたり、せっかくならDockerでやってみようと思った備忘録

環境

  • Windows 10 Home (64bit)

Dockerのインストール

  1. VirtualBoxとかKitematicとか全部入りの Docker Toolbox [link] をダウンロード
  2. Setup画面はデフォルト設定でOK、最後の画面で確認し[Install]をクリック
  3. デスクトップに作成されたDocker Quickstart Terminalのショートカット(>?みたいなやつ)を起動させると色々表示されるので待つ
  4. 画像のようにクジラの絵が出たら完了2020-06-27 (1).png
  5. Dockerターミナルにdocker run hello-worldと入力しEnterを押すと、"hello-world" という名前のDockerイメージをダウンロードしてくれる2020-06-27 (2).png

Dockerイメージのダウンロード

そのままターミナルでコマンド入力してもできるけど、今回は視覚的にわかりやすいKitematicを使ってDockerイメージをダウンロードしてみる

  1. デスクトップのショートカットから Kitematic を起動
  2. "Setup Initialization" の画面で [USE VIRTUALBOX] を選択、DockerHubアカウントは作成しなくてもOK
  3. セットアップが完了して表示された画面の検索ボックスにキーワードを入力(今回はjava)
  4. 出てきた候補から今回は "openjdk" を選び、[CREATE] ボタンを押す2020-06-27 (4).png
  5. 左側のContainers欄に "openjdk" が表示され、(先程ターミナルからダウンロードした)"hello-world" と同じように灰色アイコンの表示になったらダウンロード完了

"Connecting to Docker Hub" と表示されダウンロードが進まない場合
  • 上の画像左下の DOCKER CLI をクリックしてPowerShellを起動
  • docker pull scrapinghub/splashと入力してEnter
  • しばらく待つと諸々のダウンロードが完了しているので、この状態でKitematicに戻るとダウンロードが完了してた
  • ダメなら一度Dockerイメージのダウンロードを中止して、再度 [CREATE] ボタンを押してダウンロードを試みる

Dockerイメージからコンテナをつくる

これも Kitematic でやろうと思ったら、何回 [START] をクリックしてもすぐストップしてしまったので Docker のターミナルに戻って作業

  1. docker run -it --name testcont openjdk:latestを実行してコンテナを起動状態で作成("testcont" はコンテナの名前)
  2. 完成!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ryzen 9 マシンで Docker と WSL2 のために Virtualization を有効化する

開発効率の向上を目指して Ryzen 9 3900X のマシンをゲットしてみました。
image.png

image.png

私は頻繁に docker を使うのですが、Windows で Docker を使うときには VirtualizationをBIOSで有効化する必要があります。これはCPUやマザーボード毎に異なる様子です。

私の場合は派手なBIOSの画面が出てきて、設定の画面がわからずどこやねん!ってなりましたが、単純に検索すればよかったようです。
image.png
image.png
image.png

SVM Mode がその設定ですので enableにします。

しっかり成功しました。

>systeminfo

Host Name:                 DESKTOP-KIUTRHV
OS Name:                   Microsoft Windows 10 Enterprise
OS Version:                10.0.19041 N/A Build 19041
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Workstation
OS Build Type:             Multiprocessor Free
Registered Owner:          N/A
Registered Organization:   N/A
Product ID:                00329-00000-00003-AA340
Original Install Date:     6/27/2020, 2:57:46 PM
System Boot Time:          6/27/2020, 9:23:22 PM
System Manufacturer:       Micro-Star International Co., Ltd.
System Model:              MS-7A38
System Type:               x64-based PC
Processor(s):              1 Processor(s) Installed.
                           [01]: AMD64 Family 23 Model 113 Stepping 0 AuthenticAMD ~3800 Mhz
BIOS Version:              American Megatrends Inc. B.40, 11/7/2019
Windows Directory:         C:\WINDOWS
System Directory:          C:\WINDOWS\system32
Boot Device:               \Device\HarddiskVolume2
System Locale:             en-us;English (United States)
Input Locale:              en-us;English (United States)
Time Zone:                 (UTC-08:00) Pacific Time (US & Canada)
Total Physical Memory:     32,716 MB
Available Physical Memory: 29,086 MB
Virtual Memory: Max Size:  37,836 MB
Virtual Memory: Available: 32,671 MB
Virtual Memory: In Use:    5,165 MB
Page File Location(s):     C:\pagefile.sys
Domain:                    WORKGROUP
Logon Server:              \\DESKTOP-KIUTRHV
Hotfix(s):                 5 Hotfix(s) Installed.
                           [01]: KB4534170
                           [02]: KB4537759
                           [03]: KB4545706
                           [04]: KB4560366
                           [05]: KB4566866
Hyper-V Requirements:      A hypervisor has been detected. Features required for Hyper-V will not be displayed.

こちらに、代表的な virtualization の設定名が載っていました。バラバラっすね。

Intel Virtualization Technology,
AMD-V,
Hyper-V
VT-X
Vanderpool
SVM

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【k8s】mysqlから学ぶConfigMap&Secret

はじめに

kubernetes(以下k8s)でmysqlのdeploymentを作成するしていく中でk8sのConfigMp,Secretについて学べるなと考えました。
そこで本記事では、k8sでmysqlのdeploymentを作成する手順を記載と共にconfigmapやsecretの使用方法についてd記載していきます。

この記事で学べること

  • k8s deployment
  • k8s ConfigMap
  • k8s Secret
  • kubectlコマンドの基礎

全体図

構築する実行環境は以下の通りになります。
k8s-sql.png

deployment

podの拡張概念。
図の通り、deploymentがrs(replicaset)を生成し、rsがpodを生成してくれます。
podは一つ以上のコンテナからできています。

k8s-deploy.png

今回の場合は、mysqlコンテナが一つ入ったpodが生成されます。

configMap

k8s Config&Storageリソースの一つ。
configMapはその名の通り、(configuration mapping)以下のような設定情報をコンテナにマッピングすることができます。

  • 環境変数
  • ファイル(volume)

configmapとして切り分けておくことで、設定ファイルを複数のコンテナに即座に反映させることができます。
今回はmysqlコンテナに初期化時にdatabaseを生成するための設定ファイルをマッピングします。

secret

k8s Config&Storageリソースの一つ。
secretはconfigmapと異なりパスワードなどの以下のような機密情報をコンテナにマッピングするために使用します。

  • 環境変数
  • ファイル(volume)

今回は、環境変数にmysqlのパスワード情報をマッピングします。

環境構築手順

リソースの作成

deploymentの作成

deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        # コンテナのどの場所にマッピングするかを記述する。
        volumeMounts:
        - name: sql-init-config
          # docker-entrypoint-initdb.dディレクトリ下にスクリプトファイルを入れておくと初期化時データベースを作成してくれる。
          mountPath: /docker-entrypoint-initdb.d
        env:
          - name: MYSQL_ROOT_PASSWORD
            # secretを使用して環境変数を設定する。
            valueFrom:
                secretKeyRef:
                  name: sql-secret
                  key: password
        ports:
        - containerPort: 800
      # podに提供するvolumeを指定する。
      volumes:
        # volumeMounts.nameと一致させる必要がある。
        - name: sql-init-config
        # volumeとしてconfigMapを使う。
          configMap:
              name: db-init-configmap
              items:
                - key: init.sql
                  path: init.sql

ここで、着目すべき部分は以下の通りです。

deploy.yaml
apiVersion: apps/v1
# ・・・・・・・・・・・・・・・・・・・・
    spec:
# =============コンテナについて書く。================
      containers:
      - name: mysql
        image: mysql:5.7
        # コンテナのどの場所にマッピングするかを記述する。
        volumeMounts:
        - name: sql-init-config
          # docker-entrypoint-initdb.dディレクトリ下にスクリプトファイルを入れておくと初期化時データベースを作成してくれる。
          mountPath: /docker-entrypoint-initdb.d
        env:
          - name: MYSQL_ROOT_PASSWORD
            # secretを使用して環境変数を設定する。
            valueFrom:
                secretKeyRef:
                  name: sql-secret
                  key: password
        ports:
        - containerPort: 800
# =======================================

# --------podに提供するvolumeを指定する。---------
      volumes:
        # volumeMounts.nameと一致させる必要がある。
        - name: sql-init-config
        # volumeとしてconfigMapを使う。
          configMap:
              name: db-init-configmap
              items:
                - key: init.sql
                  path: init.sql
# -----------------------------------------
  • コンテナのどの場所にマウントするか指定する
    • spec.containers[*].volumeMountsに記述する
  • 環境変数として、secretをマッピングする
    • spec.containers[*].env.valueFrom.secretKeyRefに記述する。
  • コンテナ(pod)に提供するvolumeを指定する。
    • spec.volumesに記述する。

configmapを作成する。

configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: db-init-configmap
data:
  # init.sqlファイルをしてコンテナ内にマッピングされる。
  init.sql: |
    CREATE DATABASE IF NOT EXISTS app;

secretを作成

secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: sql-secret
type: Opaque
data:
  # パスワードをbase64でエンコードした値
  password: cGFzc3dvcmQ=

エンコードのやり方は

$ encode -n [sqlのパスワード] | base64

ファイルを作成が終わったら、kubectlコマンドでそれぞれのリソースを作成します。

$ kubectl apply -f deploy.yaml  

deployment.apps/mysql created
$ kubectl apply -f configmap.yaml 

configmap/db-init-configmap created
$ kubectl apply -f secret.yaml
secret/sql-secret created

ちゃんと設定できているか確認する

そのためmysqlのコンテナにログインしてみます。

まずは、deploymentから作成されたmysqlのpodを確認します。

$ kubectl get po
NAME                    READY   STATUS    RESTARTS   AGE
mysql-b6cff87cf-9dfbx   1/1     Running   0          3m54s

mysqlのpodが起動していることが確認できます。このpod名を利用してコンテナ内にログインしまう。

コマンドは以下の通りです。

$ kubectl exec -it mysql-b6cff87cf-9dfbx bash

環境変数を確認

root@mysql-b6cff87cf-9dfbx:/# env | grep MYSQL_ROOT     
MYSQL_ROOT_PASSWORD=password

すると、MYSQL_ROOT_PASSWORDが環境変数として設定されていることがわかります。

configmapの確認

実際にマウント先に指定したdocker-entrypoint-initdb.ディレクトリの中身を見てみます。

root@mysql-b6cff87cf-9dfbx:/# ls docker-entrypoint-initdb.d/
init.sql

ちゃんとinit.sqlファイルがマウントされていることがわかります。
また、ファイルの中身を見てみると。。

root@mysql-b6cff87cf-9dfbx:/# cat docker-entrypoint-initdb.d/init.sql 
CREATE DATABASE IF NOT EXISTS app;

configmapで指定した中身と一緒になっています。

mysqlにappデータベースが生成されているか確認

まずは、mysqlにログインしてみます。

root@mysql-b6cff87cf-9dfbx:/# mysql -u root -p
Enter password: (passwordと入力してください。)
///
mysql> 

これでログインできました。

次にデータベースの一覧をみてみます。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| app                |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

appデータベースが生成されていることが確認できました。

まとめ

  • configMap

    • 設定ファイルなどをコンテナにマッピングすることができる。
    • コンテナのどこにマッピングするか(spec.container[].volumeMounts)などを記載する
  • secret

    • パスワードの機密情報をコンテナにマッピングすることができる。
    • 環境変数にマッピングする場合は、spec.container[].env.valuefromに記述する
  • configmapやsecretを使用するときは、コンテナにどの種類のvolumeを使用するか(spec.volumes)などを記述する必要がある。

  • kubectl

    • リソース(deployment,configmapなど)を作成する
      • kubectl apply -f [リソースのファイルパス]
    • podの一覧を表示する
      • kubectl get po
    • pod内にログインする
      • kubectl exec -it [pod名] bash
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Linux2にDockerをインストールする

Docker初心者の私がAmazon Linux2(AMI)イメージから作成したEC2インスタンスに
Dockerをインストールし色々と触ってみたので備忘録として残します。

・インスタンスで使用するパッケージを更新する

すでに実施済みのインスタンスであれば不要です

実行コマンド

sudo yum update -y

実行結果

・・・
Updated:
  amazon-linux-extras.noarch 0:1.6.11-1.amzn2             amazon-linux-extras-yum-plugin.noarch 0:1.6.11-1.amzn2
  ca-certificates.noarch 0:2019.2.32-76.amzn2.0.2         cloud-init.noarch 0:19.3-3.amzn2
  kernel-tools.x86_64 0:4.14.181-140.257.amzn2            python.x86_64 0:2.7.18-1.amzn2
  python-devel.x86_64 0:2.7.18-1.amzn2                    python-libs.x86_64 0:2.7.18-1.amzn2
  selinux-policy.noarch 0:3.13.1-192.amzn2.6.2            selinux-policy-targeted.noarch 0:3.13.1-192.amzn2.6.2

Complete!

・Amazon Linux ExtrasからDockerをインストールする

Amazon Linux Extrasとは何ぞやということについてはAWS(公式)-Amazon Linux 2-よくある質問の[Amazon Linux Extras]を確認してください。

実行コマンド

sudo amazon-linux-extras install docker

実行結果

・・・
Installed:
  docker.x86_64 0:19.03.6ce-4.amzn2

Dependency Installed:
  containerd.x86_64 0:1.3.2-1.amzn2                  libcgroup.x86_64 0:0.41-21.amzn2  pigz.x86_64 0:2.3.4-1.amzn2.0.1
  runc.x86_64 0:1.0.0-0.1.20200204.gitdc9208a.amzn2

Complete!

この時点ではまだインストールしただけなのでDockerホストは起動していません。
試しに稼働中のDockerコンテナを表示するコマンドを実行してみます。

sudo docker ps

「Dockerデーモンにアクセス出来ません」とエラーになります。

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

・Dockerデーモンを起動する

実行コマンド

sudo service docker start

実行結果

Redirecting to /bin/systemctl start docker.service

Dockerデーモンが起動したのか、再度確認してみます。

sudo docker ps

まだ一つもコンテナを起動していないので一つも表示されませんが、Dockerデーモンが稼働していることは確認できました。

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

・Dockerコマンドをsudo無しで実行出来るようにする

毎回Dockerコマンド(psなど)を実行するのに"sudo"を付けるのは面倒なので、Dockerグループに
Dockerを利用するユーザーを登録します。
今回はデフォルトで作成される"ec2-user"で行います。

実行コマンド

sudo usermod -a -G docker ec2-user

実行結果(/etc/groupの登録内容を確認)
dockerグループのサブグループにec2-userが含まれていることが確認できる

cat /etc/group | grep docker

docker:x:993:ec2-user

一度ログアウトしてから先程実行したdocker psを実行すると、エラーとならずに実行できる
ことが確認できる。
※ログアウトしないと上記グループ登録が反映されていないことがある


これで作成したEC2インスタンスでDockerを利用する準備が整いました。
Dockerを利用して色々と試して行こうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueを複数人で開発する環境を試してみた

背景

Vue開発してると、色々ライブラリインストールとかが発生する。そんな事をしていると、複数人で開発していると、人によってインストールタイミングが違うので、結果モジュールのバージョン違ったりして将来的なトラブルの種になりそうと感じていた。なにより、production環境と開発環境が違ってくる状況になるのが一番まずい。
調べていると、Qiitaに良記事が色々あり、その問題を解消できそうだったので実際に試してみた。

実現したい事

  • ソースコード管理はgit
  • gitに存在する設定ファイルなどの情報で、複数人が同構成環境でVueの開発が出来るように
  • モジュールの変更も簡単に複数人の環境へ再現したい
  • 各種モジュールのバージョンは全指定(後にインストールした人とのバージョンが違うとか無い様に)
  • ホスト側で行ったソース編集をすぐに反映したい(開発モード使用したい)

ベースとなるのはこちらの記事。Dockerはこの手の環境に関する検討を1から簡単にやり直せるので、本当にすごく良いです。※最初は別記事参考にしたりしたので、細かい所で違う部分あります。
ローカルを汚さずdockerを使ってvue.jsの開発環境を作る[vuecli4]

前準備

docker/docker-compose インストール

さすがにdockerはホスト側にインストールしなければいけないので、以下サイトを参考にインストール
Ubuntu 18.04にDockerをインストールする(+docker-composeも)
もちろん、OSがWinでもMacでもDockerは使えるので、開発者のOSが違っても対応できるのもDockerの良い点ですね。

gitリポジトリ準備

  • githubでテスト用プロジェクト作成(vueprj-test01)
  • ホストでクローン(git clone https://github.com/silverbox/vueprj-test01.git)
  • developブランチ準備(vueprj-test01 に移動して、 git checkout -b develop origin/master

初期構築

docker-compose.yml, Dockerfile 準備

  • ホスト側のgitリポジトリフォルダ(vueprj-test01)以下に app フォルダを準備
  • コンテナ側は /app 以下に実プロジェクト作成
  • nodejs用のDockerイメージは(公式サイト)、現時点でのLTSの最新版っぽい12.18.1を使う
  • vue/cliとvue/cli-initは公式サイトで、現時点でのリリース最新版っぽい4.4.1を使う
vueprj-test01/docker-compose-vueini.yml
version: '3.7'
services:
  frontvue:
    build:
      context: .
      dockerfile: Dockerfile-vue
    ports:
      - 8080:8080
    volumes:
      - ./context:/app
      - /app/node_modules
    container_name: vueprj-test01
    tty: true
Dockerfile-vue
FROM node:12.18.1-alpine

WORKDIR /app

RUN apk update
RUN npm install -g @vue/cli@4.4.1
RUN npm install -g @vue/cli-init@4.4.1

ビルドして起動

vueprj-test01配下
sudo docker-compose -f docker-compose-vueini.yml build
sudo docker-compose -f docker-compose-vueini.yml up
Creating network "vueprj-test01_default" with the default driver
Creating vueprj-test01 ... done
Attaching to vueprj-test01

起動状態になった。

Vueプロジェクト作成

コンテナにログイン

bashは使えなかったのでshを使用。
docker exec -it vueprj-test01 sh

バージョン確認。指定はちゃんと反映されてる。

コンテナ内
/app # vue --version
@vue/cli 4.4.1
/app # node -v
v12.18.1

vueプロジェクト作成(選択肢はパッケージにnpm選択した事以外はデフォルトを選択)

コンテナ内
/app # vue create .
コンテナ内で開発モードで実行。
/app # npm run serve
ホスト側vueprj-test01配下。変更できるように権限変更
sudo chown -R ubuntu:ubuntu .

この状態でホスト側コードエディタで、vueprj-test01/app/src/App.vue<HelloWorld msg="Welcome to Your Vue.js App"/> 部分の文言を変更して自動的に更新される事確認。

gitにpush

既に.gitignoreが作成されているので、node_modulesとか除外されてる。
git add .
git commit -m "base project"
[develop 4ce861d] base project
 13 files changed, 11687 insertions(+)
 create mode 100755 Dockerfile-vue
 create mode 100644 app/.gitignore
 create mode 100644 app/README.md
 create mode 100644 app/babel.config.js
 create mode 100644 app/package-lock.json
 create mode 100644 app/package.json
 create mode 100644 app/public/favicon.ico
 create mode 100644 app/public/index.html
 create mode 100644 app/src/App.vue
 create mode 100644 app/src/assets/logo.png
 create mode 100644 app/src/components/HelloWorld.vue
 create mode 100644 app/src/main.js
 create mode 100755 docker-compose-vueini.yml

別の開発者を想定して環境構築

このgitリポジトリを元に別フォルダにcloneして、構築を行う。
本来は別サーバーでやりたいところだけど、dockerなのでコンテナを一回削除すればほぼ別サーバーと同じ条件になるのでそれで実験。

一応再ビルドして、Dockerコンテナ起動。

コンテナ削除して、新フォルダ作成。そしてそのフォルダ以下で構築。

~/vueprjtest/vueprj-test01$ sudo docker-compose -f docker-compose-vueini.yml rm
~/vueprjtest/vueprj-test01$ mkdir ~/vueprjtest2
~/vueprjtest/vueprj-test01$ cd ~/vueprjtest2
~/vueprjtest2/vueprj-test01$ git clone https://github.com/silverbox/vueprj-test01.git
~/vueprjtest2/vueprj-test01$ cd vueprj-test01
~/vueprjtest2/vueprj-test01$ sudo docker-compose -f docker-compose-vueini.yml build
~/vueprjtest2/vueprj-test01$ sudo docker-compose -f docker-compose-vueini.yml up

再度コンテナ内に入る。この状態では、コンテナに標準にインストールされている部分以外は無い状態。ただ、gitにはpackage.json,package-lock.jsonが上がっているので、コンテナ内から見える状態になっている。npm install でそれを反映。そして開発モードで実行。

コンテナ内
npm install
npm run serve

同様に、ホスト側の新フォルダ配下、vueprj-test01/app/src/App.vue<HelloWorld msg="Welcome to Your Vue.js App Test"/> 部分の文言を変更して自動的に更新される事確認。

ライブラリ追加

別開発者がライブラリを追加した事想定。ついでに、先は手動で行った処理を加える(本来は最初のgit push時に反映しておくべき)。

Dockerfile-vue:element-uiとaxios追加
FROM node:12.18.1-alpine

WORKDIR /app

COPY app/package.json .
COPY app/package-lock.json .

RUN apk update
RUN npm install -g @vue/cli@4.4.1
RUN npm install -g @vue/cli-init@4.4.1
RUN npm install
RUN npm install --save element-ui@2.13.2
RUN npm install --save axios@0.19.2
main.jsでaxiosを宣言して代入してみる。
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

Vue.config.productionTip = false

Vue.prototype.$axios = axios;

new Vue({
  render: h => h(App),
}).$mount('#app')
再ビルド
sudo docker-compose -f docker-compose-vueini.yml rm
sudo docker-compose -f docker-compose-vueini.yml build
sudo docker-compose -f docker-compose-vueini.yml up

そして、http://localhost:8080 でページが動く事確認。

結論

  • .gitignoreとかはちゃんとvue createで作ってくれる。
  • ライブラリ管理もpackage.json,package-lock.jsonで管理されて、それがnpm installで再現される。
  • なのでpackage.jsonが作られるまでの準備のDockerfileと、それ以後のDockerfileは分ける。
  • ライブラリ変わったら別開発者はコンテナ再作成か、コンテナ内でnpm installすればよさそう。

はまったポイント

最初、vue init webpack . でvueプロジェクト作成をしていた。
しかし、http://localhost:8080/ でアクセス出来る事を確認する時にどうもうまく行かない。そんな事を色々調べていたら、以下の神ページを発見。
vue-cli のテンプレートを Docker で動かす時の注意
config/index.js を変えなくてはいけない、というか、vue init webpack . は古い形式らしい事も知る。
書かれている通り、localhost部分を0.0.0.0にしたらホストからアクセスできるようになった。

動作したとはいえ、きれいな環境を目指しているのでvueプロジェクト作成やり直し。

ホスト側vueprj-test01配下。Ctrl+Cでdocker-composeを止める。
sudo docker-compose -f docker-compose-vueini.yml rm
rm -r app
mkdir app
sudo docker-compose -f docker-compose-vueini.yml up

環境構築も簡単にやり直せるのがDockerの良い所ですね。

ちなみにこの変更で開発モード実行コマンドも npm run dev から npm run serve 変わったらしい。configがdevとproで分かれたりもしてない?取り急ぎ今回は、目的とは違う部分なので気にしない事に。

参考にさせて頂いたサイト

Ubuntu 18.04にDockerをインストールする(+docker-composeも)
ローカルを汚さずdockerを使ってvue.jsの開発環境を作る[vuecli4]
Vue.jsの開発環境をDockerで構築する手順
vue-cli バージョン指定してインストールする
vue-cli のテンプレートを Docker で動かす時の注意

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWX v13.0.0 のインストール docker-compose編

はじめに

ansible/awx: AWX Project のインストーラを使って、docker-composeで稼働する環境を作成します。
また、v10.0.0からRabbitMQに変わってRedisを使っている影響で、ホストの設定を変更する必要があるのでそれも事前に対応します。

cf. Replace clustered RabbitMQ with something simpler · Issue #5443 · ansible/awx

前提条件

  • OSはCentOS7
# cat /etc/redhat-release 
CentOS Linux release 7.7.1908 (Core)
# docker version
Client: Docker Engine - Community
 Version:           19.03.11
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        42e35e61f3
 Built:             Mon Jun  1 09:13:48 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.11
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       42e35e61f3
  Built:            Mon Jun  1 09:12:26 2020
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

# docker-compose version
docker-compose version 1.26.0, build d4451659
docker-py version: 4.2.1
CPython version: 3.7.7
OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019

構成概要

インストーラの配置先: /root/awx
docker-composeのあるPATH: /var/lib/awx

AWXやAWXから実行されるAnsibleは、すべてコンテナ上で実行します。

また、デフォルトではAnsibleのバージョンも固定されているので、複数バージョンのAnsibleが必要な場合は、AWXのコンテナイメージを変更するなどの対応が必要になると思います。

その場合は、Docker Registryの用意も必要になるのでご注意下さい。

参考情報

構築手順

事前準備

overcommit_memoryの設定を変更

overcommit_memoryの設定を変更しないと実行時に以下の様な警告が出ます。

redis_1  | 1:M 18 Jun 2020 14:38:52.466 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

その為、以下を参考に overcommit_memory=1 が常時、反映されるようにします。
cf. 7.5. システムメモリー容量の設定 Red Hat Enterprise Linux 7 | Red Hat Customer Portal

transparent hugepages (THP) を無効化

THPを無効化しないと実行時に以下の様な警告が出ます。

redis_1  | 1:M 18 Jun 2020 14:38:52.466 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.

その為、以下を参考に transparent hugepages (THP) を無効にする

cf. Red Hat Enterprise Linux 7 で transparent hugepages (THP) を無効にする - Red Hat Customer Portal

管理用のデータベースを作成

AWXで利用する外部DBのPostgreSQLにて、ユーザ awx、データベース awx を作成し、AWXが稼働するホストから接続出来るように設定します。

$ sudo su - postgres

# psql
postgres=# create database awx;
postgres=# create user awx with password ********;

pg_hba.conf の設定も忘れずに。

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
:
host    sameuser        awx             XXX.XXX.XXX.XXX/XX           password

AWXのインストーラの準備

インストーラを v13.0.0 を指定してclone

# git clone -b 13.0.0 https://github.com/ansible/awx.git
# cd awx/installer

インストーラの実行にansibleを使うので、venv使って ansible v2.9.10 で実行出来るようにする。
また、docker-composeでAWXが稼働するので Prerequisites にある通り、pipで docker、docker-composを入れる。

[root@awx01 installer]# python3 -m venv .venv
[root@awx01 installer]# source .venv/bin/activate

(.venv) [root@awx01 installer]# whereis pip
pip: /usr/bin/pip3.6 /root/awx/installer/.venv/bin/pip /root/awx/installer/.venv/bin/pip3.6

(.venv) [root@awx01 installer]# pip install --upgrade pip

(.venv) [root@awx01 installer]# vim requirements.txt
(.venv) [root@awx01 installer]# cat requirements.txt
docker
docker-compose==1.26.0
ansible==2.9.10

(.venv) [root@awx01 installer]# pip install -r requirements.txt 

AWXインストールの為のパラメータの設定

inventoryを編集

(.venv) [root@awx01 installer]# vi inventory (←inventoryファイルを編集する、下記詳細)

以下を参考に環境に合わせて Inventory を変更

最終的にこんな感じ。

localhost ansible_connection=local ansible_python_interpreter="/usr/bin/env python3"

[all:vars]
dockerhub_base=ansible

awx_task_hostname=awx
awx_web_hostname=awxweb
host_port=80
host_port_ssl=443
docker_compose_dir="/var/lib/awx"

pg_hostname=(利用するPostgreSQLのホスト名)
pg_username=awx
pg_password=(設定したDBのパスワード)
pg_database=awx
pg_port=5432

admin_user=admin
admin_password=(AWXの管理用パスワードを設定)

create_preload_data=True

secret_key=(任意の文字列)

# 必要ならHTTP PROXYを設定しておく
http_proxy=http://(プロキシホスト):(ポート)
https_proxy=http://(プロキシホスト):(ポート)
no_proxy="127.0.0.1,localhost"

project_data_dir=/var/lib/awx/projects

※Ansible v2.9 v2.7 を共存したいなど、カスタムの仮想環境を追加したい場合は、以下を参考にすると良いです。
cf. [Ansible/AWX] AWX でカスタムの仮想環境(venv)パスを認識させる方法 - てくなべ (tekunabe)

インストーラ実行(docker-compose環境作成&AWXコンテナ起動)

(.venv) [root@awx01 installer]# ansible-playbook -i inventory install.yml
 :
TASK [local_docker : Update CA trust in awx_task container] ******************************************************************
changed: [localhost]

PLAY RECAP *******************************************************************************************************************
localhost                  : ok=12   changed=5    unreachable=0    failed=0    skipped=90   rescued=0    ignored=0   

failed=0 でない場合は、エラーログが出ているのでInventoryやホストの設定を見直して下さい

コンテナが起動しているか確認

(.venv) [root@awx01 installer]# docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                  NAMES
51f268490cf6        ansible/awx:12.0.0   "tini -- /usr/bin/la…"   4 minutes ago       Up 4 minutes        8052/tcp               awx_task
60df1e18b2bf        ansible/awx:12.0.0   "tini -- /bin/sh -c …"   4 minutes ago       Up 4 minutes        0.0.0.0:80->8052/tcp   awx_web
cde0749edde4        redis                "docker-entrypoint.s…"   4 minutes ago       Up 4 minutes        6379/tcp               awx_redis

また、インストーラは、以下の様にセットアップして稼働している

[root@awx01 ~]# cd /var/lib/awx
[root@awx01 awx]# ls
credentials.py  docker-compose.yml  environment.sh  nginx.conf  projects  redis.conf  redis_socket  SECRET_KEY
[root@awx01 awx]# docker-compose ps
  Name                 Command               State          Ports        
-------------------------------------------------------------------------
awx_redis   docker-entrypoint.sh /usr/ ...   Up      6379/tcp            
awx_task    tini -- /usr/bin/launch_aw ...   Up      8052/tcp            
awx_web     tini -- /bin/sh -c /usr/bi ...   Up      0.0.0.0:80->8052/tcp

AWXの各コンテナが完全に起動準備が整うと、以下の様なログを出すのでしばらく待つ。

[root@awx01 awx]# docker-compose logs -f
 :
web_1    | RESULT 2
web_1    | OKREADY
task_1   | RESULT 2
task_1   | OKREADY

AWXの起動後の手直し

こんな感じでRedisの警告が出てるのでその対策 1 を実施する。

redis_1  | 1:M 18 Jun 2020 14:38:52.466 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

上記の警告がAWXインストーラでサポートしていないので手動でRedisサービスを変更する。
また、docker-composeのversionも3に変更(2ではsysctls 2 が使えないので)。

AWXを停止

[root@awx01 awx]# docker-compose down
Stopping awx_task  ... done
Stopping awx_web   ... done
Stopping awx_redis ... done
Removing awx_task  ... done
Removing awx_web   ... done
Removing awx_redis ... done
Removing network awx_default

/var/lib/awx/docker-compose.ymlを以下の様に変更

version: '3'        # ← ここを 2から3に
services:
 :
  redis:
  :
    sysctls:                       # この2行を追加
      net.core.somaxconn: '511'    #
補足

長時間稼働すると、AWXサービスから出力される各コンテナのログでDiskがいっぱい!なんて事にならないように、以下を参考に、syslogに出力するとか、ログローテートするなどの対策を行って下さい。

AWXを起動

[root@awx01 awx]# docker-compose up -d

※実行後は、 docker-compose logs -f で実行の様子を確認すること

ブラウザで、 http://(ホストのIPアドレス)/ にアクセスすると下図の様にログイン画面が出てくるので、AWXのインストーラのInventoryで設定した admin_useradmin_password を使ってログインする。
2020-06-27-18-38-26.png

FAQ:アップデートどないするの?

基本的にインストーラをイチから作成し直す(流用しない)

  1. データベースは外部のPostgreSQLにあるので、データベースをバックアップ
  2. /root/awx/awx にcloneしたインストーラをリネーム ※なんかあった時の切り戻し用
  3. docker-compose down でAWXを停止
  4. /var/lib/awx をリネーム ※なんかあった時の切り戻し用
  5. いつもの通り再セットアップ ※Inventoryだけ、2でリネームしたやつを使い回す

アップデートする際の注意事項

  • 下図の様にScheduled Jobsを指定している場合は、停止するのを忘れずに 2020-06-27-18-42-26.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dockerで複数のコンソールに入る

docker exec -it [Container name] /bin/bash

http://w-endo.hatenablog.com/entry/2017/10/17/161424

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Remote ContainerでReactのDocker開発環境を構築

概要

  • 過程より結果が大事だと3部と5部のラスボスが言っていたのでまず成果物を記載します。
  • 開発環境は下記のリポジトリにあるのでお好きに使って下さい。
    • react-docker-development-environment
    • そのままpushをすると、私のリポジトリの内容が変更されてしまうので、pushとかは自分のリポジトリを作成して行って下さい。
    • VSCodeの拡張機能は自分が普段開発に使用している物をインストールするように設定しております。他にも何かオススメがあれば教えてください
    • 使用して気に入ったのなら☆を付けてくれると励みになります。

前提

  • Docker
  • Remote-Container

  • なぜDockerでReactの開発環境を作るのか

    • プロジェクトでnodeのバージョンなどを固定できる
    • ホストマシンにNodeを入れなくてOK
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了
  • なぜRemote-ContainerでReactの開発環境を作るのか

    • DockerだけだとVSCodeの拡張機能などを入れる手間がかかる。(プロジェクト配下の.vscodeに設定を書けば推奨の拡張機能として表示させることは可能だったはず)
    • Remote-Containerならコンテナを開くときに拡張機能をインストールしてくれるので共有が楽
    • VSCodeの設定もプロジェクト単位で統一可能。(フォーマッターなど)
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了(超重要)

使い方

  1. react-docker-development-environmentを git clone
  2. VSCodeの拡張機能でRemote Containerを追加する
  3. VSCodeで Ctr + Shift + p と入力
  4. Remote-Containers: Open Folder in Container と入力
  5. プロジェクト配下のdockerという名前のフォルダを選択する(.devcontainer配下が展開される)
  6. 実際に開発環境として使用する場合、ssh-agentを有効にしないとPushできません
    1. VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

CreateReactApp

今回の環境はReactでTypeScriptを使用し、storybookの環境構築を済ませた環境になっております。
自分で環境構築を行う場合、以下の手順でできます。

  1. Nodeを使用可能なコンテナを起動
    1. Docker Hub - Node
  2. コンテナ内で以下のコマンドを実行

    npm install -g create-react-app
    ## TypeScriptでReactのApplication構築を行う(テスト環境も配備)
    create-react-app sample --typescript
    cd sample
    # Reactのプロジェクトフォルダ配下で実行することでstorybookの環境構築を行う
    npx -p @storybook/cli sb init --type react_scripts
    

実装した内容

実際に環境変数で使用している内容の解説をします。

Dockerfile
# ベースとするDockerImage
FROM node:13.12.0-slim
WORKDIR /usr/app
# パッケージの依存関係に関するファイルをホストからDocker内にコピー
COPY package.json yarn.lock tsconfig.json ./
# yarn install する
RUN yarn
# yarn install 後、全ファイルをコピーすることで、パッケージ関連がコピーされ、キャッシュが効くようになり高速化
COPY . .
CMD [ "yarn", "start" ]
docker-compose.yaml
    # compose fileのフォーマットバージョン(バージョンによって使える記法が違う)
    version: '3.7'
    services:
        node:
            build:
            # 使用するDockerFileのDirectoryを指定
            context: ../../
            # 使用するDockerFileの名前を指定(Dockerfile.Nodeはdocker-compose.yamlと同じディレクトリにあるが、contextで2階層上を使用すると指定している、だからわざわざdocler/.devcontainer/ という風にしてDockerfile.Nodeを指定)
            dockerfile: docker/.devcontainer/Dockerfile.Node
            volumes:
            # ローカルのディレクトリが接続(マウント)する作業ディレクトリを指定(srcフォルダをusr/app/src にマウントしている)
            - ../../src:/usr/app/src
            # 外部に対して公開するポート番号
            ports:
            - "3000:3000"
            # コンテナを起動させ続けるか管理するフラグ
            tty: true
devcontainer.json
    {
        "name": "Existing Docker Compose (Extend)",
        "dockerComposeFile": [
            "docker-compose.yml"
        ],
        "service": "node",
        "workspaceFolder": "/usr/app/", // VSCodeのTerminalが開くディレクトリを設定
        "settings": {
            "terminal.integrated.shell.linux": null,
            "files.trimTrailingWhitespace": true, // ファイル保存時に行末の空白自動削除
            "editor.formatOnSave": true // ファイル保存時に自動フォーマットが実行される
        },
        // Remote-Container で起動した際にVSCodeの拡張機能をインストール
        "extensions": ["eamodio.gitlens","chakrounanas.turbo-console-log","visualstudioexptteam.vscodeintellicode","esbenp.prettier-vscode","dbaeumer.vscode-eslint","shardulm94.trailing-spaces","msjsdiag.debugger-for-chrome","christian-kohler.path-intellisense","usernamehw.errorlens","gizak.shortcuts","coenraads.bracket-pair-colorizer-2"],
        // Remote-Container で起動した際にgitをインストール
        "postCreateCommand": "apt-get update && apt-get install -y git",
    }

苦戦した点

  1. コンテナ内でホットリロードが効かない

    1. Reactのプロジェクトフォルダ配下にenvファイルを配置することで解決

      env
          // コンテナ内でホットリロードを有効にするための環境変数
          CHOKIDAR_USEPOLLING=true
      
  2. yarn start が遅い

    1. 一番苦労した点、コンテナ内だと5分かかることもありました。(ローカルだと10秒で終わる)
    2. 原因はyarn がキャッシュされておらず一々node_modulesを作成してたから
      1. 下記の記事の内容で解決しました
        1. Dockerfile入門する
    3. Docker for Macは遅いらしいのでMacの方は注意が必要らしいです。
      1. フロントエンドの開発環境に Docker は不要(少なくともMacでは)

経緯

  1. Excelや社内Wikiなどで書かれた環境構築資料の通りにやってもエラーが出て環境構築できない
  2. 結果、環境構築だけで時間が消える
  3. プロジェクトメンバーが増える度に同じ時間が無駄になる
  4. 溜まっていく疲労と虚無感
  5. Remote-Containerなら秒で終わる!
  6. これからはコンテナが入っていれば環境構築が秒で終わる
  7. みんな幸せ

参考資料

Reactの開発環境をDockerで整えよう

Docker Composeの仕様について混乱しがちな箇所を整理した

VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

Dockerfile入門する

docker-composeでreact環境構築(更新版)

Todo-App-Client

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

express mysql docker-compose Error: connect ECONNREFUSED

:o: 解決策

const express = require('express')
const app = express()

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'mysql', // localhostは間違い, 0.0.0.0は間違い
  port     : 3306,
  user     : 'root',
  password : 'password',
  database : 'testdb'
});

対象

docker-composeでnodejs(express), mysqlを立ち上げて接続しようとしてエラーが出た人。

自分の場合、hostを修正した場合に成功しました。
user, password, databaseは各自確認してください。

https://stackoverflow.com/questions/53250559/connecting-nodejs-docker-container-to-a-mysql-docker-container-docker-compose

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL]

はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL]

それなりに時間を書けて取り組んだので改善点等コメントいただけると嬉しいです
GitHubにコードも上げてありますので、必要があれば公開いたします同様にコメントください

なぜやるか

  • 今後AWSのEC2上にNGINX + puma (Rails) + PostgreSQLの環境でアプリをデプロイしたい
  • 開発環境も同じ構成でやりたい
  • 実務での開発を意識してDockerを使ってローカル開発環境を構築したい
  • Railsチュートリアルでお世話になったAWS Cloud9ではなく、VS Codeを使いたい

何を得たか

  • Dockerの基礎知識、概念
  • 快適なローカル開発環境 (VSCode素敵)
  • 初学者が開発環境構築までやると時間対効果よくない(丸一日かかりました...)
  • ただし、一度構築してしまえば環境の再現は恐ろしく簡単、環境の共有という視点も納得(シェル書けばあっという間?)
  • AWSデプロイもこのまま行けそう、という謎の自信
  • 一次情報大事という気づき
  • Railsチュートリアルの製作者たちが様々な工夫で環境構築のハードルを劇的に下げてくれていることへの感謝と、それ以上の柔軟性と実用性を提供してくれるDockerの素晴らしさ

結論

手順通りに進むと、表題のRails開発環境が手に入る、はずです

初学者がポートフォリオ作成のロードマップの中でDockerを導入するのであれば、
先人のお知恵を借りて、既存の開発環境を自分のローカル環境に取り入れるくらいの感覚のほうが良いかもしれない

時間をかけてRailsチュートリアルに取り組める、かつ私のような物好きは
Dockerで環境構築から初めて、環境の差による不具合を敢えて乗り越えて行くのも粋かもしれない

環境・スキル

  • Mac OS (Mojave)
  • Docker for Mac, Docker composeインストール済み
  • Ruby 2.5
  • Rails 5.2.4.3
  • Nginx 1.15.8
  • PostgreSQL
  • Rails チュートリアル1周目、Docker初心者

参考

Docker公式docs: 基本的にこの流れを踏襲しています
Quickstart: Compose and Rails | Docker Documentation

nginx公式docs: 設定例
Full Example Configuration | NGINX

nginxほか設定例
nginxについてまとめ(設定編) - Qiita

pumaの設定はこちらを参考
既存のRailsアプリのローカル開発環境をDockerの仮想環境に切り替える - りょ〜Blog

ボリュームマウントの構成はこちら
Docker + Rails + Puma + Nginx + MySQL - Qiita

備忘録

ディレクトリ構成

(予め用意するもののみ記載)

sample_app ---- Dockerfile
            |-- Gemfile
            |-- Gemfile.lock
            |-- nginx ---- Dokerfile
            |          |-- nginx.conf
            |
            |-- config ---- database.yml (後で自動生成)
            |           |-- puma.rb (後で自動生成)
            |
            |-- environments --db.env (今回うまく使えてません)
            |
            |-- docker-compose.yml
            |-- entrypoint.sh


Dockerfile (Ruby)作成

まずはruby用のDockerfileを用意

Dockerfile
FROM ruby:2.5

RUN apt-get update -qq && \
    apt-get install -y nodejs \
                       postgresql-client

RUN mkdir /myapp
WORKDIR /myapp

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

RUN bundle install
#ホストからコンテナにコピー(ホスト側は相対パス)
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#ソケット接続するのでいらないはず
#EXPOSE 3000

# puma.sockを配置するディレクトリを作成
RUN mkdir -p tmp/sockets

Gemfile(これはあとのrails newコマンドで上書きされる)

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>5'

Gemfile.lock
空のファイルで良いのでtouchコマンドのみ

Gemfile.lock
touch Gemfile.lock

entrypoint.sh

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

Dockerfile (NGINX)作成

次にnginx用のDockerファイルを用意

/nginx/Dockerfile
FROM nginx:1.15.8

# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/myapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/myapp.conf

上で呼び出される
nginx.confファイル
(ログの部分はコメントアウトを外すと、ディレクトリが見つからないとエラーが出ます。どうしても解決できずコメントアウトのまま)

/nginx/nginx.conf
user  root;
worker_processes  1;

events{
    worker_connections  512;
}

# ソケット接続
http {
  upstream myapp{
    server unix:///myapp/tmp/sockets/puma.sock;
  }
  server { # simple load balancing
    listen 80;
    server_name localhost;

    #ログを記録しようとするとエラーが生じます
    #root /myapp/public;
    #access_log logs/access.log;
    #error_log logs/error.log;

    location / {
      proxy_pass http://myapp;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
    }
  }
}

Docker-composeを作成

これらのコンテナをうまくゴニョゴニョしてくれるというdocker-compose
docker-compose.ymlを以下のように記述
env_file:は書いているものの、うまく使えていないと思います)

docker-compose.yml
version: '3'
services:
  app:
    build: .
    env_file:
      - ./environments/db.env
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
    depends_on:
      - db
  db:
    image: postgres
    env_file:
      - ./environments/db.env
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: nginx
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - "80:80"
    depends_on:
      - app
volumes:
    public-data:
    tmp-data:
    log-data:
    db-data:

Gemインストール

カレントディレクトリをDocker fileが配置されている場所に移動し
以下のdocker-compose runコマンドでRailsを展開する
(この時Gemファイルが上書きされるはず)

docker-compose run app rails new . --force --no-deps --database=postgresql

Gemをインストールするためにdocker-compose buildコマンドを実施
(Gemファイルおよび、Dockerファイルに変更がある場合のみ再度このコマンドが必要)

Now that you’ve got a new Gemfile, you need to build the image again. (This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.)
(Docker 公式 docs)

docker-compose build

設定ファイルの修正

config/puma.rbの中身を以下で置き換える

/sample_app/config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true

config/database.ymlの中身を以下で置き換える

/sample_app/config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

コンテナを起動

docker-compose upコマンドでアプリたちをを起動する

docker-compose up

処理が止まっているように見えてもエラーが出ていなければサーバーが起動しているはずです、
docker-compose up -dでターミナルの入力を継続できます

doker psで起動しているコンテナを確認すると
nginx(sample_app_web), puma(sample_app_app), DB(postgres)の3つが起動しているはず

$ docker ps
CONTAINER ID        IMAGE             COMMAND                                ...   PORTS                NAMES
7188a5bb9d3c        sample_app_web    "/bin/sh -c '/usr/sb…"   ...   0.0.0.0:80->80/tcp   sample_app_web_1
41759b7d078e        sample_app_app    "entrypoint.sh bash …"   ...                        sample_app_app_1
1c81a75b5c22        postgres          "docker-entrypoint.s…"   ...   5432/tcp             sample_app_db_1

この時点でlocalhost:80にアクセスすると、
Railsのエラー画面、あともう少し
データベースがないよと言われるので

DBを作成する

docker-compose run app rake db:create

完成

再びlocalhost:80で...
スクリーンショット 2020-06-27 22.14.46.png

最後に

これRailsのコマンドはどうやって打つの?という残念なレベル...
docker-compose run appに続けて記述するそうです
appはRailsが動いているコンテナですね

$ docker-compose run app rails -v
Starting sample_app_db_1 ... done
Rails 5.2.4.3

動いているコンテナを終了するのは
(これをやらないと次回起動時エラーが出る)

$ docker-compose down

再度起動するには

$ docker-compose up
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む