TL;DR
今回の流れは大まかに以下です。
- VagrantでVirtualbox(CentOS7)を立ち上げ
- Shellスクリプトで、Dockerのプロビジョニング
- ホスト(ローカルのMac)からVMのCentOSに立ち上げたDockerコンテナのnginxにアクセス
VMマトリョーシカの初体験であります。
VagrantでVitrualboxにCentOS7を入れて立ち上げる
Vagrantfileは下記の内容で進めました。
# -*- mode: ruby -*-
# # vi: set ft=ruby :
## Version constraint
Vagrant.require_version ">= 1.3.5"
## Plugin constraint
unless Vagrant.has_plugin?("vagrant-hostsupdater")
raise 'Missing plugin `vagrant-hostsupdater`! Install it by `vagrant plugin install vagrant-hostsupdater` command before vagrant up.'
end
# VM config
Vagrant.configure("2") do |config|
config.ssh.insert_key = false
config.vm.box = "centos/7"
config.vm.box_version = "2004.01"
config.vm.hostname = "vagrant-test.localhost.com"
config.vm.network :private_network, ip: "192.168.0.1"
config.vm.provider :virtualbox do |v|
v.name = "centos7_vagrant_test"
v.customize ["modifyvm", :id, "--memory", 2048]
v.cpus = 1
v.check_guest_additions = false
v.functional_vboxsf = false
v.gui = false
end
# VM NFS
config.vm.synced_folder ".", "/home/cwd/src", id: "home", :nfs => true, :mount_options => ['nolock,vers=3,udp,actimeo=2']
# VM provisioning
config.vm.provision "shell", run: "always", inline: <<-SHELL
echo Hello, World
SHELL
config.vm.provision "shell", run: "always", :path => "./provision/local/scripts/docker-init.sh"
end
詳しい項目の詳細については、
こちらの記事も参考にご覧ください。
参考:「Vagrant+VirtualBox」ローカルで仮想環境を立ち上げてみる
「vagrant up」でvm立ち上げ
vagrant up
ポイント
-
Vagrantfile
があるディレクトリに移動(cd)し、vagrant up
をする -
vagrant-hostsupdaterを入れておくとmacの
/etc/hosts
を自動で更新設定してくれるので便利なのでその判定をする-
vagrant halt
vm停止後も、/etc/hosts
から追加したホストを削除してくれる。
-
-
config.vm.provision
でrun: "always"
にしないと、provisioningは初回しか走らないので、必要に応じて設定する -
:path
は、Vagrantfileがあるディレクトリを基準に相対パスで.sh
シェルスクリプトのファイルを指定する
Shellスクリプトで、Dockerのプロビジョニング
Shellスクリプトとは、コンピュータに対する命令をまとめて書くファイルで、
Linuxコマンドとか、自分でインストールしたコマンドとかで命令を書いて、バッチを作れるやつ。
試行錯誤を重ね、初めて自分で書いてみたものが下記。(なかなか大変だったこりゃ。)
(※何度も自分のローカルで動作確認したけど、もしかしたらエラー出て動かないかも・・・?)
# !/bin/bash
# Directory Definition
FIRST_OUTPUT=~/docker-compose
LAST_OUTPUT=/usr/local/bin/
# Install jq command if necessary
jq --version >/dev/null 2>&1
if [ $? -ne 0 ]; then
sudo yum -y install epel-release
sudo yum -y install jq --enablerepo=epel
fi
# Install wget command if necessary
wget --version
if [ $? -ne 0 ]; then
sudo yum -y install wget
fi
# Download the latest Docker
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
wait
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
wait
sudo yum install -y docker-ce docker-ce-cli containerd.io
wait
# Download the latest docker compose
DOCKER_COMPOSE_LATEST_VERSION=$(curl -L https://api.github.com/repos/docker/compose/releases/latest | jq .name -r)
wget "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_LATEST_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -O ~/docker-compose
wait
# Make a directory & Move the installed docker-compose to the appropriate folder
sudo mkdir -p $LAST_OUTPUT
sudo mv ${FIRST_OUTPUT} ${LAST_OUTPUT}/docker-compose
# Change Owner & Mode for the Docker Compose
sudo chown root:root ${LAST_OUTPUT}/docker-compose
sudo chmod +x ${LAST_OUTPUT}/docker-compose
# Start Docker
sudo systemctl start docker
sudo systemctl status docker
# Run containers using docker-compose
cd /home/cwd/src/provision/local
sudo ${LAST_OUTPUT}/docker-compose up -d --remove-orphans
やっていることの解説
- 最初に
docker-compose
をインストールする場所を定義 -
CentOS
はまっさらなPC状態で、使いたかったjq
コマンドとwget
コマンドが入ってなかったので、最初にインストール-
$?
には直前のコマンドの実行結果が返ってきて、成功すると0
が入るからそれで判定が可能。 -
jq
コマンドは、JSONパースしてくれて、name
属性だけを-r
つまり""
を取り除いて取得してくれるってやつ。- これでversionの名前がキレイにとれる。
-
- 最新版のDockerをインストール
- 最新版のDocker-composeをインストール:OSとハードウェア的に互換あるやつ
-
uname -s
はkernel nameを出力してくれる -
uname -m
はmachine hardware nameを出力してくれる
-
- インストールした
docker-compose
を仮置きしてたので、最終的に置きたい場所のためにフォルダ作成- そして移動
-
docker-compose
のオーナーをroot
に変更し、全ての人に実行権限だけを付与するように変更 -
Docker
をスタートして、一応statusなんかをコンソールに出しちゃって -
docker-compose.yml
のあるディレクトリに移動し、docker-composeのバイナリファイルがあるディレクトリを指定しながら、docker-compose up
。-
-d
はバックグラウンド実行。いわゆるデーモンみたいな -
--remove-orphans
は、docker-compose.yml
にかかれていないサービスのコンテナを削除するってオプション
-
改善点
ってな感じで、自分で適当にバッチを書いてみたが、
もっとよく書けたり無駄があったり、するから改善の余地は多々ありそうw
あとは、ログ出力echo
とか1>&2
など、標準出力、標準エラー出力も意識した書き方をしないといけないと思っている。
Docker-compose.yml
version: "3.8"
services:
web:
image: nginx:latest
# build: ./local/docker
container_name: web
ports:
- 8080:80
stdin_open: true
tty: true
privileged: true
最初Dockerfileを使う予定だったけど、一旦は難易度をさげて、通常のDocker Hubのimageを使う
tty: true
でコンテナが起動し続ける
いざ、nginxにアクセス!!
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12exxe38xxx7 nginx:latest "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp web
0.0.0.0:8080->80/tcp
と立ち上がったので、
さっそく、ブラウザからアクセスしたい。
IPアドレスにポート番号でアクセス
Vagrantfileに設定したipアドレスにポート番号をつけて、
http://192.168.0.1:8080/
にアクセス!!!!!!!
ホストネームにポート番号でアクセス
次に、ホストネームでアクセスする!!!
http://vagrant-test.localhost.com:8080/
できた。。
ホスト名にポートつけるのかっこ悪いしなんだか気持ち悪いなあ、、、
でもまだ解消方法が分からないので次回挑戦。
ポート番号として 80 番を使用する場合だけは例外で、ポート番号を省略した場合は 80 番が指定されたものとして扱われます。そのため本来であれば「http://www.example.com:80/ 」のようにアクセスする代わりに「http://www.example.com/ 」のようにポート番号を省略してアクセスすることができます。
参考元:80番以外のポート番号を使用した場合のWebサーバへのアクセス
ここまでくるのに、1週間以上はかかったなあ。
大変でしたが、つながると気持ちいいですね!!
ハマったポイント
Vagrantfile
フォルダ階層につまづき・・・
config.vm.synced_folder
で、どうやってフォルダがマウントされるかという実感が持てず、
そもそもマウントされていないんじゃないかってところで何時間も調査したが、
単純にvagrantのvirtualbox内の階層を理解していなかっただけだった。
vagrant ssh
で、ログインしたときに最初にたどり着くディレクトリが
/home/vagrant
で、そこからスタートするのかとかマジで知らんしって状態だったんだけど、
自分が指定したのは
config.vm.synced_folder ".", "/home/cwd/src",
つまり、
/home/cwd/src/
であって、
ログイン後に一回cd ../
してls -la
をすれば、自分の階層を見つけられたのだった。
ど素人すぎて、階層の蟻地獄にハマりました。
Docker-compose
アクセスがリセットされてしまう問題。
curl: (56) Recv failure: Connection reset by peer
これでずっと苦しむ。
ymlファイルに、以下のプロパティを設定したら、上記エラーがでて、nginxがhtmlを返さなくなってしまった。
command: tail -f /dev/null
user: $USER_ID:$GROUP_ID
原因が完全には理解していないものの、
おそらく
tail -f /dev/null
すると常にコマンドが実行状態となり、忙しくて返って来なくなる
user
はユーザー権限の問題と衝突してダメ
という予想です。
sudo docker exec -it web /bin/bash
curl localhost:8080
このコマンドで、dockerコンテナに入り込んで、コンテナのnginxが起動してるか確認ができる。
Nginxのdefault.conf
無駄にdefault.confを理解もしないでいじり倒してしまった
defalt.conf
の設定も、よくわからないネットで拾ってきたものをマウントさせて使ってしまったから、ダメだった。
nginxのインストールしたてホヤホヤのものをそのまま使えば、よかったのに。
curl: (56) Recv failure: Connection reset by peer
が出続けた。
listen
の設定がすごく大事だった。
# default.confの一部
server {
listen 80;
listen [::]:80;
server_name localhost;
リクエスト&レスポンスの流れ
- まずは、localhostは127.0.0.1に解決される。
- 次に、Port Forwardingがなされ、
127.0.0.1:8080 -> 172.17.0.x:80
- 「ホストの全インターフェースの8080ポート宛通信をコンテナの80ポートに転送する」
-
172.17.0.x:8000 -x-> 127.0.0.1:8000
- ホストマシンからのリクエストがコンテナのIPアドレス 172.17.0.xの80ポートに届いたが一方で、アプリ側は
127.0.0.1:8080
でしかlistenしておらず、172.17.0.x:80
ではListenしてないみたいになっていた。
- ホストマシンからのリクエストがコンテナのIPアドレス 172.17.0.xの80ポートに届いたが一方で、アプリ側は
-
0.0.0.0
でサーバを建てると、そのホスト(今回の場合、コンテナ)が持つ全てのインターフェースでLISTENできる
まとめ
VMマトリョーシカは、泥沼でした。
深海のような水圧で、押しつぶされながらも、なんとか呼吸し、水面に脱出できました。
∈( 'Θ' )∋ .。○⚪︎
以上、ありがとうございました。