結論
(ネットワークモードにhostを使用する以外は)無理!!!!!!!!!! だった。
前提条件
自宅サーバを動かしてる。OSはArchLinuxで試験的かつ個人利用のみでWebサーバとVPNサーバを動かしてる。
やりたかったこと
一番最初の目的はNextCloud(ストレージサーバ)の構築だった。異なるサーバソフトウェアを一つのサーバ機上に展開するのはヤバイとそろそろ気付き始めたので、勉強ついでにDockerを使って導入しよう、可能ならWebServerとVPNServerもコンテナとして移行させよう、なんてこと考えてた。
Docker composeの利用
調べ始め、よくわからないけどdocker-composeとか言うものを使えば良いらしいと聞き、ネットに散見しているNextCloudのdocker-compose.yamlを拾ってきて実行した。
参考にしたサイト:
- FUTURE SPIRITS様 【Docker+NextCloud】とりあえず何か動かしたい人向けサーバ学習ブログ
- engishoma様 NextcloudをDockerで構築する
だが、どう試してもアクセスができない。ブラウザからのアクセスもcurlからのアクセスも、Timeoutによって何も表示されない。真っ白な画面が表示されるだけ。信頼できるドメインに自分のドメインが登録できていないからかとそちらも試したが変わず。docker-composeのポートマッピングは80番ポートから変更しており、nftablesなどは一時的に無効化している。
参考にしたサイト:
サーバ内からcurlをしてみるとデータが取ってこれると気づいた。ということは、インターネットとの接続がうまく行ってない?と考え、Webサーバ用に動いているNginxに協力してもらい、サブドメインを見分けてNextCloudに転送してもらうよう設定した。
# こんなイメージ
server {
listen 80;
server_name nextcloud.example.com;
location / {
proxy_pass http://localhost:60000/;
}
}
Nginxの転送技を使うことで外部からのアクセスが可能になり、ログイン画面に到達できた。
NextCloudとDBサーバ間の通信
すんなり進んでほしい思いとは裏腹に、すぐに次の問題に直面した。NextCloudの管理用アカウントを入力しインストールボタンを押すと、次のエラーが表示された。
Error while trying to create admin account: An exception occurred in the driver: SQLSTATE[HY000] [2002] Connection refused
原因はNextCloudサーバとDBサーバとの間で正常な通信ができていないことと読み取った。DBサーバのユーザ名とパスワードをすべて同じ文字列にしてみたり、DBサーバの指定方法を"localhost"や"127.0.0.1"などに変えてみたり、DBサーバそのものをMariaDBからPostgreSQLに変更したりしたが結果は変わらなかった。そもそも公式に当たっていないのは論外ということか?と思い始めDockerHubやGitHubの公式リポジトリに当たって説明されているExamplesにそのまましたがって構築したが、同様のエラーが発生した。ちなみに、GitHubにある公式リポジトリにはmariadbの10.6が指定されていたが、これはそのままだと実行エラーが発生するため最新版にする必要がある。
参考にしたサイト:
- Nickellab Sakai様 Zenn | Dockerでpostgresqlを構築する
- mineaki27th (みね)様 Qiita | 【Docker】MySQLに接続できないエラー(php_network_getaddresses: getaddrinfo failed: Name or service not known)
- とーち様 DevelopersIO | dockerコンテナからmysqlに接続出来なくて困った話
- NextCloud help Nextcloud installation: Error while trying to create admin user
- Docker Hub | nextcloud
- GitHub | nextcloud/docker
- GitHub | nextcloud/all-in-one
公式から別途提供されているall-in-oneパッケージを見つけ、docker-composeを捨てることにした🥺。すると、次のエラーが発生した。
$ sudo docker run --init --sig-proxy=false --name nextcloud-aio-mastercontainer --restart always --publish 80:80 --publish 8080:8080 --publish 8443:8443 --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config --volume /var/run/docker.sock:/var/run/docker.sock:ro nextcloud/all-in-one:latest
Trying to fix docker.sock permissions internally...
Creating docker group internally with id 974
**Could not resolve the host nextcloud.com.**
Most likely the DNS resolving does not work.
You should be able to fix this by following https://dockerlabs.collabnix.com/intermediate/networking/Configuring_DNS.html
Apart from that, there has been this: https://github.com/nextcloud/all-in-one/discussions/2065
つまるところ、nextcloud.comのドメイン解決ができない。docker内のコンテナからnextcloud.comへの接続ができないということだった。
コンテナからのpingが帰ってこない
問題の切り分け
どうも、コンテナから外へ通信がうまく行っていないようである。少なくともドメイン解決ができていない。とりあえずこれを分離させるため、これまた見様見真似で新規のdockerコンテナを作成し、ここから8.8.8.8にpingを打ってみた。
$ docker run -itd --name alp alpine
6f6db48a1db25d04fbd175fbf263476634967c50f3d52710c1556234bea253a1
$ docker attach alp
/ # ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
^C
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
このように、ドメイン解決とは関係なく通信ができていないことがわかる。
(現状わかっている)唯一の解決策は、上記のdocker runコマンドに--net=hostを付けることだった。これについて説明する。
Dockerと3種類のネットワーク
Dockerには3種類のネットワークモードが存在する。noneとhost、bridgeの3つである。なお、Dockerそのものに触り始めたばっかりであるとともに、解説そのものがメインの記事ではないため説明が適当であっても勘弁してほしい。
「none」はコンテナが全く通信をしない場合に設定する。コンテナ間の通信はできず、もちろん外部のネットワークとも通信ができない。
「host」はコンテナがDockerを実行するホストのネットワークインターフェースをそのまま利用する設定である。一つのインターフェースを共有する形であるため、ホストが接続するネットワークに対してコンテナからそのままアクセスすることが可能である。ポートマッピングをする必要は当然無く、コンテナとしてサーバを立てれば外からそのままアクセスが可能となる。コンテナ間の通信も可能である。
「bridge」はソフトウェアブリッジを構築し、NAT/NAPTを経由してホスト含め外部のネットワークと通信を行う。コンテナにはそれぞれプライベートIPアドレスが割り振られ、IPを用いることでコンテナ間の通信が可能となる。Dockerではこれがデフォルトとなっている。
Docker内にネットワークを新規作成することも可能である。Dockerからデフォルトで提供されているものをそのまま利用するにはセキュリティ的なリスクが存在するため、各自で作成することが推奨されている。
参考にしたサイト:
- 水道屋さん様 Zenn | 😋 dockerコンテナのネットワーク
- ymz_note様 Zenn | 🌟【Docker】ブリッジネットワークの解像度を上げる試み
- Yu Watanabe様 Zenn | 😺 systemd-networkd入門
- MetricFire様 Qiita | 【Docker Network 第3章】-net = hostオプションを理解する
- Docker-docs-ja | Docker コンテナ・ネットワークの理解
ネットワークモードにhostを使うと
Dockerがつながらない事象について調べる中で、hostを設定するとうまく行くという事例がいくつかヒットした。つまるところ、Dockerのブリッジを使わなければうまく行くということである。しかしながら、これには大きな欠点がある。それはポートがそのまま適用されてしまう点である。現時点でNginxを使用しWebサーバを立てている。一方NextCloudは内部でApacheを使用しており、アクセスはブラウザがメインであるためNginxと競合してしまうのである。これではDockerを使わず直接デプロイするのと変わらないのだ。docker-composeを使った場合でもネットワークモードをhostにするとうまく行ったが、これでは意味がない。
ついでに、不具合があるならその原因を追求するのが条理である。なんとかして問題解決できるよう頑張ってみる。
参考にしたサイト
【本編】インターネットにつながらない原因究明
さて、どう調べてみても上記で例示したような、コンテナからサクッとgoogleへpingが届かないような状況は正常でないらしい。これを解消するためにまる一日費やしたが解決しなかったので、試したこととその意味を箇条書きに羅列する。誰か助けてほしい。
1. systemctl restart docker
Dockerがうまく働かないなら、Dockerそのものを再起動させるのが良いと考えた。効果はなかった。
2. systemctl restart docker.socket
Dockerは簡単にいえば通信部に別途ソケットファイルを用いている。Docker.serviceのトリガーにもこのソケットが設定されている。とりあえずこちらも再起動させた。効果はなかった。
3. reboot
とりあえず、なにかうまく行かない場合はPCを再起動させるのが定番である。効果はなかった。
4. systemctl stop iptables
, systemctl stop nftables
通信できないとなれば、やっぱり疑うべきはフィルタ部分である。もともとサーバではiptablesを動かしていたが途中からnftablesに移行した。どちらが影響しているかわからないため、両方とも動作を停止させ無効化させた。効果はなかった。
5. iptables -t nat -F
iptablesは-Fオプションによってフラッシュ(初期化)することができる。iptables内にはテーブルという枠組みが存在しており、パケットは全ていずれかのテーブルに割り振られる。そのうちNATに関するテーブルがnatテーブルであり、このコマンドではnatテーブル上のルールをリセットする。効果はなかった。
6. iptables -P FORWARD ACCEPT
iptables中のチェイン、FORWARDはパケットがマシンを中継する場合に適応される。Dockerのブリッジとマシン上のネットワークインターフェース(ここではeth0とする)は別物であるため、外から入ってきたパケットはeth0を経由してDockerへと入る。本来ならDockerがうまくiptablesを調整するが、これがうまく行っていない場合はFORWARDで止まるはずである。よってこのコマンドによってFORWARDに来るパケットを際限なく通過できるようにする。効果はなかった。
7. /etc/docker/daemon.json
をいじる
今回は関係ない話となるが、念の為DNSの設定をしてみた。例として次のように書き込む。
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
その後、Dockerのサービスをリスタートさせる。こうすると、コンテナ内のetc/resolv.conf
にDNSの解決先として8.8.8.8、8.8.4.4が設定される。aptのアップデートなどドメイン解決の点でエラーが発生している場合はこれが有効となる。効果はなかった。
8. コンテナ内の/etc/resolv.conf
に8.8.8.8などを設定する
コンテナ内にexecやattachなどで直接入り込み、nameserverとして8.8.8.8などを設定する。ひとつ上の方法を自分の手で行う形である。効果はなかった。
9. サーバ機の/etc/resolv.conf
に8.8.8.8などを設定する
コンテナの/etc/resolv.conf
はサーバ機、つまりホストマシンのresolv.confが引き継がれるらしい。元々8.8.8.8が設定されていたが、試しに8.8.4.4も追記してみた。効果はなかった。
10. runコマンドのオプションとして--dns=8.8.8.8
などを指定する
ふたつ上とほぼ同じである。効果はなかった。
11. docker system prune
参考にしたサイトではaptやapt-getのupdateの失敗対応方法として、容量不足を挙げられていた。サーバの空き容量に不安はなかったが、もしかするとDockerに割り当てられている容量が不足している?可能性も踏まえてDockerのキャッシュを消去した。効果はなかった。
12. brctl delbr docker0
Dockerが作成する仮想ブリッジはLinuxと深く結びついており、brctl(bridge-utils)からコントロールすることが可能である。docker0が仮想ブリッジの名前であるが、このコマンドで試しに消去している。その後Dockerのサービスを再起動させ、改めて構築させた。効果はなかった。
13. ip link delete docker0
ひとつ上とほぼ同じである。ここではip(iproute2)より仮想ブリッジであるdocker0を削除した。効果はなかった。
14. sysctl -w net.bridge.bridge-nf-call-iptables=0
Dockerのネットワークがbridgeモードで動作するとき、Linuxのカーネル機能を用いて仮想的なブリッジを作成している。このとき、カーネル部でパケット全体の処理を制御しているのがNetfilterである。よくフィルタとしてiptablesやnftablesを利用するが、これはユーザ向けのインターフェースであり内部ではNetfilterが制御されている。上のコマンドではブリッジに関するNetfilterを無効化するものである。この場合再起動後に無効化されるため、即時無効化する場合はproc/sys/net/bridge/bridge-nf-call-iptables
に0を格納するらしい。これにより制御を無効化するため、変なフィルタが掛かっていたとしてもスルーされて繋がるはずである。効果はなかった。
15. sysctl -w net.ipv4.ip_forward=1
6番の前提となる、IPフォワードを有効化する設定である。IPフォワードは異なるネットワークインターフェース間での通信を転送することである。何故かiptablesでの設定を先にしていたが、ここで改めて有効化させてみた。効果はなかった。
16. Dockerを再インストールする
何をどうやってもDockerがうまく動かないなら、最終的には再インストールしか無い。pacmanから-Rと-Sによって再インストールした。効果はなかった。
17. /ver/lib/docker
を削除する
Dockerのイメージやコンテナ等は全て/var/lib/docker
内に保存されている。Dockerをpacmanより削除するのみではこれらが削除されないため、Dockerを完全に削除するにはここまで消去する必要がある。効果はなかった。
18. Dockerをダウングレードする
最新のDockerのパッケージに未知のバグが含まれていて、奇跡的にエラーがおきているに違いない。古いパッケージを使えばもしかしたらうまく動くかもしれない!効果はなかった。
参考にしたサイト
- (4) 青色一号様 そんな今日この頃の技術ネタ | /*突然Dockerコンテナ内からネットワークに繋がらなくなったらiptablesを疑ってみよう*/
- (5) (12) stackoverflow | My docker container has no internet
- (5) (6) Qiita | iptables まとめ
- (5) (6) ArchWiki | iptables
- (7) DockerCommunityForums | Access to internet from containers connected to bridge
- (7) (10) stackoverflow | Network calls fail during image build on corporate network
- (7) (8) (9) (10) stackoverflow | Docker apt-get update fails
- (8) yangniao23様 Qiita | Dockerコンテナからインターネットに繋がらないときに確認すること
- (9) nanigashi0930様 磁気的駆動 | Dockerコンテナでapt update が失敗するのを対応したメモ
- (9) けんつ様 それが僕には楽しかったんです。 | Dockerコンテナ内から外部通信が向いたときにDNSが解決できない問題を解消する
- (10) stackoverflow | Dockerコンテナ内からapt-getコマンドエラー解決方法に関して
- (11) yu9824様 yu9824's Notes | Dockerでapt-get updateができないとき確認すること
- (11) gurayasu様 Zenn | [Docker]ERROR apt-get update && apt-get installの対処
- (12) (13) ArchWiki | ネットワークブリッジ
- (14) mochizuki875様 Qiita | Linux Bridgeを介した通信ができない
- (14) bashaway様 Qiita | netfilterとfirewalldとiptablesとnftablesの関係
- (14) mishima様 スラド | mishimaの日記: 仮想化するときのブリッジにはフィルタリングを無効化しよう
- (15) EDUCO様 LinuC | IPフォワード(IP Forward)
- (15) stackoverflow | Docker containers with bridge network cannot ping anything (even default gateway)
- (18) Docker Packages archive | Docker
最後に
途中、こんなこと言いました。
**不具合があるならその原因を追求するのが条理**である。なんとかして問題解決できるよう頑張ってみる。
駄目でした。どうもすみませんでした。
この後サーバ決まるごと構築し直してくる。
もし他の方法をご存知の方がいらっしゃいましたらご教授いただけますと幸いです。
駄文をお読みいただきありがとうございました。