- 投稿日:2021-01-20T19:38:41+09:00
Docker-proxyでらくらくにIPv6対応しようとしたらがいつの間にか出来なくなっていた件
(自分用の備忘録)
やりたいこと
デュアルスタックのDockerホスト上で、Dockerプロキシを利用してIPv4シングルスタックのDockerコンテナでIPv6のサービスをしたい。
共通環境
Ubuntu 20.04
# cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=20.04 DISTRIB_CODENAME=focal DISTRIB_DESCRIPTION="Ubuntu 20.04.1 LTS"# uname -a Linux server 5.8.0-36-generic #40~20.04.1-Ubuntu SMP Wed Jan 6 10:15:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux期待している挙動
以下のようなdocker-composeで適当なDockerコンテナを起動し、
ports
でポートフォワーディングを実施する。version: '3' services: grafana: image: grafana/grafana ports: - 3000:3000コンテナ起動後、該当のポートフォワーディングを行うdocker-proxyプロセスが同時に立ち上がる。
root 120970 0.0 0.0 475320 3912 ? Sl 18:27 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3000 -container-ip 172.18.0.4 -container-port 3000
- IPv4のトラフィック iptablesに以下のようなDNATルールが登場するため、docker-proxyプロセスに達する前に処理される。
# iptables -t nat -L DOCKER Chain DOCKER (2 references) target prot opt source destination RETURN all -- anywhere anywhere RETURN all -- anywhere anywhere DNAT tcp -- anywhere anywhere tcp dpt:3000 to:172.18.0.3:3000ちなみに返りのパケットはPOSTROUTINGでIPマスカレードされる
# iptables -t nat -L POSTROUTING Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 anywhere MASQUERADE all -- 172.18.0.0/16 anywhere MASQUERADE tcp -- 172.18.0.3 172.18.0.3 tcp dpt:3000 MASQUERADE tcp -- 172.18.0.4 172.18.0.4 tcp dpt:8888
- IPv6のトラフィック IPv6の場合IPv4とは異なりDNATのルールが挿入されず、上述docker-proxyプロセスがL4〜レベルで変換を実施する。
実際の挙動
これまで
確認したバージョン:
Docker version 19.03.14, build 5eb3275d40
Type
がIPv6
になっており、IPv4/IPv6の両方でLISTENすることがわかる。参考:
netstatやlsofでLISTENしているアドレスポートが IPv6と表示されてもIPv4でアクセスできる罠
# lsof -i:3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME docker-pr 120970 root 4u IPv6 1065660 0t0 TCP *:3000 (LISTEN)最近
確認したバージョン:
Docker version 20.10.2, build 2291f61
Type
がIPv4
になっており、IPv4でしかLISTENしていない。
そのためIPv6でサービス提供が行えていないない# lsof -i:3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME docker-pr 14845 root 4u IPv4 6231154 0t0 TCP *:3000 (LISTEN)対処方法
Docker versionを19.0代(多分 未検証)未満にすると以前までの挙動に戻る。
公式ドキュメントを参照して古いバージョンのdocker-ce, docker-ce-cliをinstallする。- installできるdocker-ceのバージョンを確認
# apt-cache madison docker-ce docker-ce | 5:20.10.2~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages docker-ce | 5:20.10.1~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages docker-ce | 5:20.10.0~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages docker-ce | 5:19.03.14~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
- 新しいdocker-ceをuninstall
# apt-get remove docker docker-engine docker.io containerd runc
- 古いdocker-ce, docker-ce-cliをinstall
# apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io所感と今後
このような仕様変更が行われた経緯に関してはま調べられていないが、めっちゃ困る
そもそもIPv4はiptablesのDNATで変換が入るのでプロキシ要らないようになっているのに、なぜ
TYPE: IPv4
でdocker-proxyはLISTENするのか... なんならTYPE: IPv6
でLISTENしてもIPv4でサービス提供できるのに...
- 投稿日:2021-01-20T14:45:47+09:00
【Docker Network 第3章】-net = hostオプションを理解する
シリーズ
【Docker Network 第1章】
【Docker Network 第2章】はじめに
コンテナは現在、あらゆる開発サイクルにおいて必要とされている機能になっています。 コンテナネットワークがどのように機能するかを理解することは私たちにとって不可欠であります。これは、サービス通信の観点から重要であるだけでなく、インフラストラクチャセキュリティの重要な側面を形成しているからという理由でもあります。
この記事では、Dockerコンテナーで使用できるさまざまなネットワークモードについて簡単に学習し、ホストモードネットワークについて詳しく解説していきます。
ネットワークモードの概要
None
Noneは、コンテナがネットワークスタックを受信するという点で単純ですが、外部ネットワークインターフェイスがありません。 その代わりに、ループバックインターフェイスを受信します。 rktコンテナプロジェクトとDockerコンテナプロジェクトはどちらも、NoneまたはNullネットワークが使用されている場合に同様の動作を提供します。 コンテナネットワーキングのこのモードには、コンテナのテスト、後のネットワーク接続のためのコンテナのステージング、外部通信を必要としないコンテナへの割り当てなど、さまざまな用途があります。
ブリッジモード
Linuxブリッジは、同じホスト上のコンテナーが通信できるホスト内部ネットワークを提供しますが、各コンテナーに割り当てられたIPアドレスはホストの外部からアクセスできません。 ブリッジネットワーキングは、シングルホストネットワーキングを提供するNATおよびポートマッピングにiptablesを活用します。 ブリッジネットワークはデフォルトのDockerネットワークタイプ(つまり、docker0)であり、仮想ネットワークインターフェイスペアの一方の端がブリッジとコンテナの間に接続されます。
作成フローの例を次に示します。
- ホストにブリッジがプロビジョニングされます。
- 各コンテナの名前空間は、そのブリッジ内でプロビジョニングされます。
- コンテナのethXは、プライベートブリッジインターフェイスにマッピングされます。
- NATを使用したIptablesは、各プライベートコンテナとホストのパブリックインターフェイスの間のマッピングに使用されます。
NATは、ホストを超えた通信を提供するために使用されます。 ブリッジネットワークは、ポートの競合の問題を解決し、1つのホストで実行されているコンテナにネットワークの分離を提供しますが、NATの使用に関連するパフォーマンスコストがあります。
ホストモード
このアプローチでは、新しく作成されたコンテナーがそのネットワーク名前空間をホストと共有し、より高いパフォーマンスを提供し、NATの必要性を排除します。 ただ、欠点としてポートの競合が発生します。 コンテナはホストのすべてのネットワークインターフェースにアクセスできますが、特権モードで展開されていない限り、コンテナはホストのネットワークスタックを再構成しないことがあります。
ホストネットワークは、Mesos内で使用されるデフォルトのタイプです。 つまり、フレームワークでネットワークタイプが指定されていない場合、新しいネットワーク名前空間はコンテナではなくホストネットワークに関連付けられます。 ネイティブネットワークと呼ばれることもあるホストネットワークは、概念的に単純であり、理解、トラブルシューティング、および使用が容易になります。
オーバーレイ
オーバーレイはネットワークトンネルを使用して、ホスト間で通信を提供します。これにより、あるホストから次のホストにネットワークサブネットをトンネリングすることで、コンテナを同じマシン上にあるかのように動作させることができます。本質的には、1つのネットワークを複数のホストにまたがっています。仮想拡張可能ローカルエリアネットワーク(VXLAN)など、多くのトンネリングテクノロジが存在します。
VXLANは、マルチホストネットワークが1.9リリースでネイティブ機能として導入されたDockerlibnetworkに最適なトンネリングテクノロジーです。この機能の導入により、DockerはHashiCorpのSerfをゴシッププロトコルとして活用することを選択しました。これは、ネイバーテーブルの交換と収束時間の効率性のために選択されました。
他のトンネリングテクノロジーのサポートが必要な場合は、Flannelが最適です。 udp、vxlan、host-gw、aws-vpc、またはgceをサポートしており、クラウドプロバイダーのトンネルタイプごとに、アカウントまたは仮想プライベートクラウド(VPC)専用に、プロバイダーのルーティングテーブルにルートが作成されます。パブリッククラウドのサポートは、とりわけ、オーバーレイがハイブリッドクラウドのユースケースに最適に対応し、パブリックポートを開かなくてもスケーリングと冗長性を提供することを考えると、オーバーレイドライバーにとって特に重要です。
マルチホストネットワークでは、Dockerデーモンを起動するときに追加のパラメーターと、Key-Valueストアが必要です。一部のオーバーレイは、分散キー値ストアに依存しています。コンテナオーケストレーションを行っている場合は、分散型のKey-Valueストアがすでに存在します。
オーバーレイは、ホスト間の通信の課題に焦点を当てているため、2つの異なるオーバーレイネットワークに接続されている同じホスト上のコンテナは、相互にセグメント化されているためローカルブリッジを介して相互に通信できません。
アンダーレイ
アンダーレイネットワークドライバーは、ホストインターフェイス(つまり、eth0の物理ネットワークインターフェイス)を、ホスト上で実行されているコンテナーまたはVMに直接公開します。 このようなアンダーレイドライバーの2つは、メディアアクセス制御仮想ローカルエリアネットワーク(MACvlan)とインターネットプロトコルVLAN(IPvlan)です。 MACvlanおよびIPvlanドライバーの操作と動作は、ネットワークエンジニアには非常によく知られています。 どちらのネットワークドライバーも、ブリッジネットワークよりも概念的に単純であり、ポートマッピングの必要性を排除し、より効率的です。 さらに、IPvlanにはL3モードがあり、多くのネットワークエンジニアの共感を呼んでいます。 ほとんどのパブリッククラウドの制限(または機能の欠如)を考えると、アンダーレイは、オンプレミスのワークロード、セキュリティ上の懸念、トラフィックの優先順位、またはコンプライアンスに対処する必要がある場合に特に役立ち、ブラウンフィールドでの使用に最適です。 アンダーレイネットワーキングでは、VLANごとに1つのブリッジが必要になる代わりに、サブインターフェイスごとに1つのVLANが可能になります。
ホストモードネットワークの詳細を理解する
ホストモードは非常に単純に見えますが、展開するときに留意する必要のある項目がいくつかあります。 例を見て、私たちが話していることを確認しましょう。 まず、ホスト上で基本的なWebコンテナを開始しましょう。
docker run -d --name=web1 --net=host vaibhavthakur/docker:webinstance1dockerrunコマンドで「–net = host」フラグを渡しています。 また、ポートマッピングを指定していません。 イメージがダウンロードされると、dockerはイメージを「web」と呼ばれるコンテナとして実行します。 このコンテナをデーモンとして実行するようにdockerに指示したので、次はコンテナのbashシェルに接続しましょう。
docker exec -it web1 /bin/bash
これにより、コンテナ内のシェルに移動し、コンテナで使用可能なすべてのネットワークアダプタを確認する必要があります。 以下に示すように行うことができます。
root@docker-1:~# docker exec -it web1 /bin/bash [root@docker-1 /]# ifconfig docker0 Link encap:Ethernet HWaddr 02:42:9F:DE:3C:3C inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0 inet6 addr: fe80::42:9fff:fede:3c3c/64 Scope:Link UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 b) TX bytes:180 (180.0 b) ens4 Link encap:Ethernet HWaddr 42:01:0A:80:0F:E8 inet addr:10.128.15.232 Bcast:0.0.0.0 Mask:255.255.255.255 inet6 addr: fe80::4001:aff:fe80:fe8/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1460 Metric:1 RX packets:99119 errors:0 dropped:0 overruns:0 frame:0 TX packets:10016 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:236101229 (225.1 MiB) TX bytes:881725 (861.0 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:336 errors:0 dropped:0 overruns:0 frame:0 TX packets:336 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:35505 (34.6 KiB) TX bytes:35505 (34.6 KiB)ここで、コンテナを終了し、ホストで同じコマンドを実行する必要があります。
root@docker-1:~# ifconfig docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:9fff:fede:3c3c prefixlen 64 scopeid 0x20<link> ether 02:42:9f:de:3c:3c txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2 bytes 180 (180.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ens4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460 inet 10.128.15.232 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::4001:aff:fe80:fe8 prefixlen 64 scopeid 0x20<link> ether 42:01:0a:80:0f:e8 txqueuelen 1000 (Ethernet) RX packets 99198 bytes 236116665 (236.1 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10074 bytes 888616 (888.6 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 336 bytes 35505 (35.5 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 336 bytes 35505 (35.5 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 root@docker-1:~#コマンドの出力はまったく同じであることがわかります。 これは、ホストIPを使用してコンテナにアクセスできる必要があることを意味します。 クラウドプロバイダーを使用している場合、ポート80で受け入れるファイアウォールルールがマシンで有効になっていることに注意してください。
インスタンスのパブリックIPでサービスにアクセスしてみましょう。
web1コンテナ内で実行されているサービスからの応答を確認できます。コンテナの実行中にポートマッピングを提供せず、ホストノードネットワークを明示的に指定したため、ポート80でコンテナ内で実行されているApacheサーバーにアクセスできることに注意してください。
では、別のことを試してみましょう。 このホストでホストモードネットワークを使用して同じサービスの別のコンテナを実行し、何が起こるかを確認します。
docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2注意深くみてみると、コンテナは起動すらしませんでした。 ログを見てみましょう。
root@docker-1:~# docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2 Unable to find image vaibhavthakur/docker:webinstance2' locally webinstance2: Pulling from vaibhavthakur/docker Image docker.io/jonlangemak/docker:webinstance2 uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/ a3ed95caeb02: Already exists 3a5831f32025: Already exists 1070eeab2b98: Already exists 86ba7b11cc42: Already exists 8ea0f21db18b: Already exists 772c76531fbf: Already exists 04120008298a: Already exists 6a221adc032a: Already exists 4a835ccd3c00: Already exists 78e705e491cf: Already exists c343940bb393: Already exists 663a5158a2b7: Already exists 09cf647f3fa5: Pull complete Digest: sha256:62883de5c6d7786ec4267b8a0ccb9b104dea7523c9db934ecb911b78a5d983ee Status: Downloaded newer image for vaibhavthakur/docker:webinstance2 2019-12-26 04:57:29,606 CRIT Supervisor running as root (no user in config file) 2019-12-26 04:57:29,619 INFO supervisord started with pid 1 2019-12-26 04:57:29,621 INFO spawned: 'httpd' with pid 6 2019-12-26 04:57:29,664 INFO exited: httpd (exit status 1; not expected) 2019-12-26 04:57:29,665 INFO received SIGCLD indicating a child quit 2019-12-26 04:57:30,668 INFO spawned: 'httpd' with pid 7 2019-12-26 04:57:30,707 INFO exited: httpd (exit status 1; not expected) 2019-12-26 04:57:30,707 INFO received SIGCLD indicating a child quit 2019-12-26 04:57:32,711 INFO spawned: 'httpd' with pid 8 2019-12-26 04:57:32,749 INFO exited: httpd (exit status 1; not expected) 2019-12-26 04:57:32,750 INFO received SIGCLD indicating a child quit 2019-12-26 04:57:35,755 INFO spawned: 'httpd' with pid 9 2019-12-26 04:57:35,796 INFO exited: httpd (exit status 1; not expected) 2019-12-26 04:57:35,796 INFO received SIGCLD indicating a child quit 2019-12-26 04:57:36,798 INFO gave up: httpd entered FATAL state, too many start retries too quicklyしたがって、これは、エントリポイントプロセスがすぐに終了したため、コンテナが起動できなかったことを明確に示しています。 もう少し深く掘り下げて、コンテナの中に入りましょう。
root@docker-1:~# docker exec -it web2 /bin/bash [root@docker-1 /]# service httpd status httpd dead but subsys locked [root@docker-1 /]# service httpd start Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80 (98)Address already in use: make_sock: could not bind to address 0.0.0.0:80 no listening sockets available, shutting down Unable to open logs [FAILED]最初にhttpdサービスのステータスを確認したところ、停止していることがわかりました。 次に、起動しようとしましたが、ポート80にバインドできなかったため、再度失敗しました。もう一度テストを実行して、ポート80のステータスを確認してみましょう。
[root@docker-1 /]# netstat -plnt Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 :::80 :::* LISTEN - tcp 0 0 :::22 :::* LISTEN - [root@docker-1 /]#ポート80が何らかのプロセスによって占有されていることがはっきりとわかります。このプロセスは、web1という名前のコンテナです。 したがって、web2コンテナがweb1が実行されているのと同じホスト上のポート80にバインドしようとしたため、失敗したと結論付けて良いでしょう。 それでは、web1コンテナを強制終了し、web2コンテナでhttpdサービスを開始してみましょう。
web1を強制終了した後、web2でhttpdを起動し、成功しました。
[root@docker-1 /]# service httpd start Starting httpd: [ OK ] [root@docker-1 /]#最後のテストとして、ポート80でインスタンスにアクセスしましょう。
web2コンテナ内で実行されているサービスからの応答を確認できました。
まとめ
ネットワークトラフィックはdocker0ブリッジとiptablesポートマッピングを通過する必要がないため、ホストモードネットワークはコンテナのネットワークパフォーマンスを向上させることを理解することが重要です。 この記事で、ホストモードネットワークとは何か、および他のネットワークオプションとの違いについて明確に説明してきました。 ご不明な点がございましたら、お気軽にお問い合わせください。
コンテナの監視を検討している場合は、MetricFireを介してHosted Prometheusで監視できます。 こちらから無料トライアルにサインアップするか、デモを予約して直接お問い合わせください。 詳細については、Dockerネットワークに関する3部構成のシリーズをご覧ください。このシリーズでは、さまざまな種類のDockerネットワークについて詳しく説明しています。
- 投稿日:2021-01-20T09:09:22+09:00
Docker と開発環境構築の忘備禄
初学者だが、今後もノートパソコンなど環境構築することになるので、メモをする。
Vagrant 2.2.14
Ubuntu 14.04.6 LTS
Docker version 1.6.2勉強していたら講師が使用しているコマンド実行してエラー。
動画通りにいかない。調べたら、私の環境が古い。
Dockerのコマンドが新しく変わっている。
バージョンUPにはVagrant 上のUbuntu 14.04.6 LTSからUbuntu 16.04.7 LTSにアップグレードする必要がある。
OSアップデートUbuntu 14.04LTSから16.04.6 LTSにアップデートしてみた
を参考にアップグレード
Dockerのバージョンを別に上げると思っていたら一緒にアップグレードできた。
最新は
Dcoker version 18.09.7これで、新しいDockerコマンドが実行できるようになった。
Vagrant上でVSCodeを使いたいので、VS codeのインストールと設定が必要。
「Vagrant上のファイルをVSCodeで編集する」 For Windowsを参考にインストール設定をする。
- 投稿日:2021-01-20T08:10:21+09:00
【いまさら】Docker Compose で Redmine しよう with Let's Encrypt
やりたいこと
- Redmine を使って個人のタスク管理 (勉強、家事、趣味) などをしたい
- お金はかけたくない、イニシャルコストは 10K 程度、ランニングは月々数百円 くらいが良い
やろうと思ったこと
wrike も考えた
無料プランではタイムトラック機能が使えないらしい。
何に時間を使ってるかも管理したいので、残念。planio を使おうと思った
無料プランではタグを使うためのプラグインを入れられない。有料プランは個人で使うには高すぎる。
クラウドにホストしようかと思った
bitnami を使ってさくっとクラウドにホストしようかととも考えたが、ランニングコストがアホほどかかる。
bitnami の必須構成で組んでも月々数千円かかる。やることになったこと
自宅サーバを立てる
今どきではない感が半端ないが、クラウド() が自分のユースケースにはあっていないため、仕方がなく自分で運用保守することにした。
筐体は少電力省スペースの Raspberry Pi 4 4GB を購入した。どういうケースにしようとか SD カードどうしようとか考えるのが面倒なので、日本の販売代理店のスターターキットを買うことにした。数年前に買ったラズパイ3はメモリ 1GB だったのに、やっぱりムーアの法則ってあるんだなぁ...。
Docker Compose を使う
今どきならk8s とかでしょ!と思うんですが、全然、勉強をしていないので、Web に投稿されている手順や設定ファイルを読み解くことができず...。なにか変えたくなった時に手も足も出なくなりそう....。
コンテナの構成は、リバースプロキシ、AP サーバ (Redmine)、DB サーバ (MariaDB) のそれっぽい構成にする。
オレオレ自己証明書を卒業する
いつの間にやら個人でも気軽に無料で(!) SSL 証明書を取得できるような世の中になっていた。Let’s Encrypt という認証局である。実際には Certbot という ACME クライアントを使用することになる。
やったこと
ネットワークの事前設定
- DDNS へのドメインの登録
- ルータのポートフォワーディングの設定
OS インストールから Docker Compose のインストールまで
基本的に他の Qiita の記事の通りの実施で問題ないです。
地味にネットワークの設定ファイルの変更が初回起動の際に反映されておらず、netplan の設定ファイルの保存先を探したり、自分で netplan のコマンドを実行したりした。なんだよ netplan って。Redmine を動かす (やっと本題)
設定のパクリ元
ディレクトリ構成
ディレクトリ、ファイルの配置は以下の通り。
./ ├── docker-compose.yml ├── redmine │ ├── config.ru │ ├── plugins │ └── themes └── reverse_proxy ├── Dockerfile ├── default.conf.template └── sslRedmine の設定をする
docker-compose.yml
の内、AP サーバ (Redmine) の部分は以下の通り。x-DEFINE: &APP_RELATIVE_URL_ROO /redmine x-DEFINE: &DB_PASSWORD redmine_passward x-DEFINE: &TIME_ZONE Asia/Tokyo (略) app: image: redmine:latest container_name: app ports: - 3000:3000 volumes: - ./redmine/plugins:/usr/src/redmine/plugins - ./redmine/themes:/usr/src/redmine/public/themes - ./redmine/config.ru:/usr/src/redmine/config.ru environment: TZ: *TIME_ZONE REDMINE_DB_MYSQL: db REDMINE_DB_PASSWORD: *DB_PASSWORD RAILS_RELATIVE_URL_ROOT: *APP_RELATIVE_URL_ROO depends_on: - db restart: alwaysプラグインやテーマは必要に応じてボリュームでマッピングしているディレクトリに格納するようにする。
config.ru
はサブディレクトリ運用する (例えば https://my.home.com/redmine でアクセスする) ために変更が必要な設定ファイルです。変更後内容は以下の通り。RAILS_RELATIVE_URL_ROOT の値はdocker-compose.yml
で定義している。# This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) map ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do run Rails.application endMariaDB の設定をする
docker-compose.yml
の 内、DB サーバ (MariaDB) の部分は以下の通り。特筆すべき点は特にない。x-DEFINE: &DB_PASSWORD redmine_passward x-DEFINE: &TIME_ZONE Asia/Tokyo (略) db: image: mariadb:latest container_name: db ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: *DB_PASSWORD MYSQL_DATABASE: redmine TZ: *TIME_ZONE volumes: - ./data/db:/var/lib/mysql command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci restart: alwaysnginx + certbot の設定をする
リバースプロキシ (nginx) と ssl 証明書の取得 (certbot) の設定を行う。
docker-compose.yml
の リバースプロキシの部分は以下の通り。x-DEFINE: &APP_RELATIVE_URL_ROO /redmine (略) reverse_proxy: container_name: reverse_proxy build: context: ./reverse_proxy network: host args: - CERTBOT_EMAIL=xxxxx@yyyyy.zzzzz #replace with your own email - DOMAIN_LIST=my.home.com #replace with your own domains environment: APP_ROOT: *APP_RELATIVE_URL_ROO volumes: - ./reverse_proxy/default.conf.template:/etc/nginx/templates/default.conf.template - ./reverse_proxy/ssl:/etc/letsencrypt ports: - "80:80" - "443:443" restart: alwaysリバースプロキシのイメージは nginx の公式イメージではなく、自分でカスタムしたものをビルドする。
イメージビルド時に渡す二つのパラメータCERTBOT_EMAIL
DOMAIN_LIST
は Let's Encrypt に登録する連絡用メールアドレスと ssl 証明書を取得する対象のドメイン名である。これらの値はdocker-compose.yml
から設定している。
environments
で指定しているのは Redmine が動くサブディレクトリ名である。
volumes
で指定しているのは nginx の設定ファイルと、ssl 認証の格納先である。リバースプロキシの
Dockerfile
の内容は以下の通りである。FROM nginx:latest ARG CERTBOT_EMAIL=default@default.com ARG DOMAIN_LIST # Expose ports. EXPOSE 80 EXPOSE 443 RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y cron certbot \ && certbot certonly --dry-run --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \ # && certbot certonly --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \ && rm -rf /var/lib/apt/lists/* \ && echo "@monthly /usr/local/bin/certbot renew --nginx >> /var/log/cron.log 2>&1" >/etc/cron.d/certbot-renew \ && crontab /etc/cron.d/certbot-renew VOLUME /etc/letsencrypt CMD [ "sh", "-c", "cron && ./docker-entrypoint.sh nginx -g 'daemon off;'" ]細かな説明はパクリ元を参照願う。
パクリ元から変更しているのは、
certbot
のインストールをapt
で行うようにした。その影響でcertbot-auto
ではなくなっている。entorypoint.sh
から20-envsubst-on-templates.sh
が呼ばれるようにCMD
を変更 (ダサいとは思っているが他に思いつかない)
default.conf.template
の内容は以下の通りである。
(https の設定は certbot の成功を一度確認してからコメントを外す)server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location ${APP_ROOT} { proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://app:3000${APP_ROOT}/; } # --- For CertBot --- location ^~ /.well-known/acme-challenge/ { root /usr/share/nginx/html/; } location = /.well-known/acme-challenge/ { return 404; } # ----------------- #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } #server { # listen 443; # server_name shop.nureteni.awa.co.jp; # # ssl on; # ssl_certificate /etc/letsencrypt/live/${MY_DOMAIN_NAME}.cert; # ssl_certificate_key /etc/letsencrypt/live/${MY_DOMAIN_NAME}.cert.key; # # location ${APP_ROOT} { # # proxy_set_header X-Forwarded-Host $host:$server_port; # proxy_set_header X-Forwarded-Server $host; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_pass http://app:3000${APP_ROOT}/; # } # # --- For CertBot --- # location ^~ /.well-known/acme-challenge/ { # root /usr/share/nginx/html/; # } # # location = /.well-known/acme-challenge/ { # return 404; # } #}以上で
docker-compose up -d
で必要なコンテナが立ち上がる。
あとはhttps://my.home.com/redmine
で Redmine にアクセスできる。おわりに
細かいことに詰まることが多く、スキマ時間にやっていたけれども 2 週間近くの時間を消費することになった。
マイナーなユースケースだと思うが、この地球のどこかの人のためになればと思い、この記事を投稿した。
- 投稿日:2021-01-20T00:18:21+09:00
Docker php-apacheを自己証明書でSSL対応(自分メモ)
前回の記事でphp-apache+mysqlを作りましたが、今回はそれを自己証明書を作り、SSL対応するまでを書いていきます。
ゴールはchromeから警告なしで、アクセスできるようになるまでです。環境
マシン macOS Catalina 10.15.7
構成
前回終了時こんな感じで終わり
任意のディレクトリ │ ├── docker-compose.yml │ ├── html/ │ └── index.html │ ├── mysql/ │ ├── Dockerfile │ ├── data/ │ ├── init/ │ │ └── init.sql │ └── my.cnf │ ├── php-apahce/ ├── Dockerfile └── php.ini↓
今回はこんな感じになります。
任意のディレクトリ │ ├── docker-compose.yml 更新 │ ├── html/ │ └── index.html │ ├── mysql/ │ ├── Dockerfile │ ├── data/ │ ├── init/ │ │ └── init.sql │ └── my.cnf │ ├── php-apahce/ ├── Dockerfile 更新 ├── ssl.conf 追加 └── ssl/ ├──ssl.key 追加 └──ssl.crt 追加構築
さっそく始めましょう
自己証明書の作成
まずは自己証明書を作りましょう
とりあえずディレクトリ作って移動ksk@ksknoMacBook-Pro work % mkdir php-apache/ssl ksk@ksknoMacBook-Pro work % cd php-apache/ssl秘密鍵作成
ksk@ksknoMacBook-Pro ssl % openssl genrsa -out ssl.key 2048 Generating RSA private key, 2048 bit long modulus ............+++ .+++ e is 65537 (0x10001)CSR作成
Common Nameだけ「localhost」と入力して、後全部Enter
ksk@ksknoMacBook-Pro ssl % openssl req -new -sha256 -key ssl.key -out ssl.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) []: State or Province Name (full name) []: Locality Name (eg, city) []: Organization Name (eg, company) []: Organizational Unit Name (eg, section) []: Common Name (eg, fully qualified host name) []:localhost Email Address []: error, no objects specified in config file problems making Certificate Requestsan.txt作成
最近の自己証明書でchromeの警告回避するには、これがいるとかいらないとか
ksk@ksknoMacBook-Pro ssl % echo "subjectAltName = DNS:localhost" > san.txt証明書の作成
10年とかにしたら警告が出た気がするんで、1年にしました。
ksk@ksknoMacBook-Pro ssl % openssl x509 -req -sha256 -days 365 -signkey ssl.key -in ssl.csr -out ssl.crt -extfile san.txt証明書の準備は完了
ssl.conf
ssl.confを前回作った既存のコンテナのssl.confを基にして作りたい為、とりあえずコンテナの状態確認
ksk@ksknoMacBook-Pro ssl % cd ../ ksk@ksknoMacBook-Pro php-apache % docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c43e97e5c37 work_mysql "docker-entrypoint.s…" 20 minutes ago Up 20 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp db 35889b716286 work_php-apache "docker-php-entrypoi…" 20 minutes ago Up 20 minutes 0.0.0.0:8080->80/tcp webwebコンテナの方のコンテナIDを使いssl.confをローカルにコピー
ksk@ksknoMacBook-Pro php-apache % docker cp 35889b716286:/etc/apache2/sites-available/ssl.conf ./ssl.conf編集
/etc/httpd/ssl/に配置するものとする。ssl.conf〜〜〜 SSLCertificateFile /etc/httpd/ssl/ssl.crt SSLCertificateKeyFile /etc/httpd/ssl/ssl.key 〜〜〜Dockerfile編集
前回の記事からすると、
RUN mkdir -p /etc/httpd/ssl
以降を追記してます。FROM php:7.4-apache COPY ./php.ini /usr/local/etc/php/ RUN apt-get update RUN apt-get install -y zip unzip vim libpng-dev libpq-dev RUN docker-php-ext-install pdo_mysql RUN mkdir -p /etc/httpd/ssl RUN a2enmod ssl COPY ./ssl.conf /etc/apache2/sites-available/ssl.conf COPY ./ssl/ssl.key /etc/httpd/ssl/ssl.key COPY ./ssl/ssl.crt /etc/httpd/ssl/ssl.crt RUN a2ensite ssl CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]docker-compose.ymlの編集
8443から443ポートに接続できるようにします
docker-compose.ymlversion: '3' services: php-apache: build: ./php-apache/ volumes: - ./html:/var/www/html ports: - 8080:80 - 8443:443 container_name: web mysql: build: ./mysql/ volumes: - "./mysql/data:/var/lib/mysql" - "./mysql/init:/docker-entrypoint-initdb.d" environment: - MYSQL_ROOT_PASSWORD=docker - MYSQL_DATABASE=mydb - MYSQL_USER=appuser - MYSQL_PASSWORD=appuser1 container_name: db ports: - 3306:3306これでとりあえず準備完了
再度build&起動
まず前のやつ残ってたら消します。
ksk@ksknoMacBook-Pro cd ../ ksk@ksknoMacBook-Pro work docker container stop web ksk@ksknoMacBook-Pro work docker container rm web ksk@ksknoMacBook-Pro work docker container stop db ksk@ksknoMacBook-Pro work docker container rm dbbuild と 起動
ksk@ksknoMacBook-Pro work docker-compose build ksk@ksknoMacBook-Pro work docker-compose up -d起動確認
ksk@ksknoMacBook-Pro work % dodker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c43e97e5c37 work_mysql "docker-entrypoint.s…" 20 minutes ago Up 20 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp db 35889b716286 work_php-apache "docker-php-entrypoi…" 20 minutes ago Up 20 minutes 0.0.0.0:8080->80/tcp, 0.0.0.0:8443->443/tcp web証明書の登録
先ほどのssl.crtが入っているフォルダにアクセスして
これをダブルクリックキーチェーン開くと、先ほど登録した証明書がドメイン名で現れるので、ダブルクリックします。
「この証明書を使用する時」を「常に信頼」にすると全てが「常に信頼」になるので、それでこのウィンドウを閉じてください。
最後は画面にアクセスして確認
警告なしに、アドレスの横の鍵マークも不穏な空気を出さずにアクセスできました。
以上。