この記事は NTTコミュニケーションズ Advent Calendar 2020 の4日目です。
昨日は@yuki_uchidaさんのWebSocketの次の技術!?WebTransportについての解説とチュートリアルでした!
はじめに
何している人
社会人7年目。イノベーションセンターという組織でDevOpsやSREに取り組んでいます。
最近ではCloudNative Days Tokyo 2020にて [Cloud Native環境におけるエンタープライズシステムに対する高可用性実現への取り組み] (https://event.cloudnativedays.jp/cndt2020/talks/3) を発表させていただいたり,KubeCon 2020 EUにて Deliver Your Cloud Native Application with Design Pattern as Code というタイトルで発表させていただいております。
今回紹介したいこと
新型ウイルスの影響もあり,多くのエンジニアの方々がリモートワークをされているのではないかと思います。そんな状況だとお家の環境ってとても大切ですよね。今日はそんなお家環境を充実させながらKubernetesを勉強する環境を作りつつ,Raspberry Piなんかを活用しながら,64-bit ARMも遊べる環境をK3sを使って作るお話をしようと思います。
実際にデプロイするアプリ(おうちk3sユースケース!)は,不要な広告をブロックする手作りお家DNS CacheコンテナをCoreDNSを利用して立てますよ!
K3sとは
まずK3sとは何かについて。Rancher Labs社が発表したOSSで,比較的リソースの少ない環境に対してもインストール可能な軽量Kubernetes Distributionです。利用していて感じる利点は,たった40MB程度のシングルバイナリでKubernetes Master/Nodeを動かせるためRaspberry Piのようなデバイスを利用してKubernetes クラスタが組めることです。また,バイナリの入れ替えでバージョンアップができるためメンテナンスがすごく楽です。AArch64/ARM64 に対応していることも重要で,IoTやEdgeのユースケースに適しています。
Ref: The Lightweight Kubernetes Distribution Built for the Edge
構成について
物理構成
※ 動作を保証するものではありません。
構成例の1つとして参照していただければと思います。特に電源に関してはRaspberry Pi 4が要求する5V 3Aを満たしていないので気になる方は純正電源やPoEをご検討すると良いと思います。でもこういう構成で利用している人がいるって情報、、、助かりますよね(・∀・) これで1年ほど運用して特に問題は出ておりません。
Raspberry Pi4 ModelB 4GBを4台用意し,1台はMaster,残り3台はNodeとして構成します。
ネットワークを構成するルータやスイッチ等は別途用意する必要があります。
※最近は8GBのPi 4も出たので気になる方はそちらも
- Raspberry Pi4 ModelB 4GB ラズベリーパイ4 技適対応品 × 4
- 32GB TOSHIBA 東芝 microSDHCカード CLASS10 UHS-I対応 R:100MB/s × 4
- SANWA SUPPLY つめ折れ防止カテゴリ7LANケーブル 2m ブラックホワイト × 4
- Rampow USB Type C ケーブル【2m/二本組/保証付き】急速充電 USB3.0 タイプc ケーブル QuickCharge3.0対応 × 2(2本組を2つでケーブルは4本)
- Anker PowerPort I PD - 1 PD & 4 PowerIQ × 1
- 積層式ケース for Raspberry Pi 4 / Pi 3 Model B+ 専用 保護用クリア・アクリルケース カバー アルミヒートシンク二個付き Clear/透明 (4段) × 1
ここにパイがあるじゃろ?
( ^ω^)
⊃パイ⊂
これをこうして…
( ^ω^)
≡⊃⊂≡
こうじゃ
( ^ω^)
⊃K3s Cluster⊂
利用するOS
今回は,Ubuntu Server 18.04 ARM 64 Bit を利用しています。公式サイトでは既にUbuntu 20.04.1への誘導が始まっています。今回の記事にあたりMasterノードのみUbuntu 20.04.1に上げましたが,日本のAptキャッシュレポジトリがまだfocalに対応していない(アドベントカレンダー執筆時点)ため日本国内から遊びながら利用する場合はまだ18.04の方が良いかと思います。こういう所で書くと対応してくれるんだよなぁ|ω・`)ちら
現在,日本国内に多くのAptミラーレポジトリが存在しますがその全てがARM 64 bitのパッケージを保持してくれているわけでは有りません。私はUbuntu Japanese Teamがメンテナンスしてくれている富山大学さんのミラーを参照しています。Ubuntu ARM 64 bitを利用したことがある方はご存知かと思いますが,AptレポジトリのURL構成がx86/x64とは異なる(ubuntu-ports)ことに注意して下さい。本家のports.ubuntu.comは北米や欧州のサーバに回されることが多いので,Ubuntu Japanese Team並びに富山大学さんには感謝しか有りません。
参考sources.listファイル
deb http://jp.archive.ubuntu.com/ubuntu-ports/ bionic main restricted universe multiverse
deb-src http://jp.archive.ubuntu.com/ubuntu-ports/ bionic main restricted universe multiverse
deb http://jp.archive.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse
deb-src http://jp.archive.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe multiverse
deb http://jp.archive.ubuntu.com/ubuntu-ports/ bionic-backports main restricted universe multiverse
deb-src http://jp.archive.ubuntu.com/ubuntu-ports/ bionic-backports main restricted universe multiverse
deb http://jp.archive.ubuntu.com/ubuntu-ports bionic-security main restricted universe multiverse
deb-src http://jp.archive.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse
OSセットアップにおけるTips
GPUに割り当てるメモリを少なくする
Server用途で利用する際はRaspberry PiでGPUに割り当てるメモリを減らすことで若干メモリを多く利用できます。公式手順はこちら。Ubuntu Serverの場合は/boot/firmware/config.txt と少しファイルの場所が違うことに注意して下さい。
128MB 与える場合
# cat /boot/firmware/config.txt | grep gpu_mem
gpu_mem=128
OSが利用できるメモリ量
# cat /proc/meminfo|grep Mem
MemTotal: 3831204 kB
MemFree: 2063800 kB
MemAvailable: 3010796 kB
これを16MBに減らすと
# cat /boot/firmware/config.txt | grep gpu_mem
gpu_mem=16
MemTotalがちょっと増える
# cat /proc/meminfo|grep Mem
MemTotal: 3935468 kB
MemFree: 418776 kB
MemAvailable: 3424796 kB
Cgroupを有効にする
K3sのようにコンテナを利用する場合はCgroupの有効化が必要です。検索に引っかかるようにエラーログも載せておきます。
Feb 18 14:47:52 kmaster k3s[17960]: time="2020-02-18T14:47:52.543535964Z" level=fatal msg="failed to find memory cgroup, you may need to add \"cgroup_memory=1 cgroup_enable=memory\" to your linux cmdline (/boot/cmdline.txt on a Raspberry Pi)"
このような場合は,Cgroupが正しく有効化されていない可能性があります。下記のファイルにcgroup_memory=1 cgroup_enable=memoryを追加して下さい。
# cat /boot/firmware/nobtcmd.txt
net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory
K3sをインストールする
K3sのインストールはとても簡単です。ここに手順を書くほどでもなく本家のインストール手順を参照していただければと思います。
DNS Cacheコンテナを立てる
IoTやEdgeクラスタにおいてもユースケースが重要です。せっかくクラスタを作っても上に乗せるアプリがないと面白さがないですよね。今回はお家で利用できるDNS CacheサーバをARMコンテナで立ててしまおうというユースケースです。なお,今回立てるDNSコンテナはフルリゾルバでは有りません。
DNS LookupはTLSで暗号化する
今回のDNS Cacheコンテナは上位のキャッシュDNSサーバへ再帰問い合わせをするのが役割です。CoreDNSはDNS Lookupの暗号化する方式をいくつかサポートしているので,今回はDNS over TLSを用いた例をご紹介します。また今回指定する上位サーバはCloudflareを利用します。
これらを有効化することでDNSのクエリを通信事業者(我々(゚∀゚))等から秘匿することができます。
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
policy round_robin
}
広告を配信しているドメインをフィルターする
普段生活していると危険なサイトや不要な広告は予めブロックして欲しいと感じることがよくあります。今回は悪いインターネットさん(名前は悪そうですが個人的には好きです)が提供している広告除去用ホストファイルというのをCoreDNSにインテグレーションしてその実現をしてみます。
下記のようにローカルに存在するHostsファイルをDNS解決に利用するようにします。HostsファイルはCoreDNSのInitContainerで上記のサイトよりダウンロードしてきてプロビジョニングします。
hosts /etc/adblock-host/hosts {
fallthrough
}
※上記のHostsファイルの利用は飽くまで自己責任でお願い致します
KubernetesのYamlにする
CoreDNSの設定はファイルはConfigmapとして与えます。ヘルスチェック等のPluginも有効化しておいた方が実践的なので有効化してあります。あとはCoreDNSのDeploymentにInitContainerを設定してHostsファイルを起動時に取得するようにします。よってPodが起動するたびに最新のHostsファイルを取得します。そして最後にServiceを作ります。今回はClusterIPで作成し,ゲートウェイを兼ねているルータでK3sクラスタへルーティングを書いています。もちろんK3sが提供するコントローラーを用いてType LoadBalancerで作成してもOKです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
labels:
k3s-app: dns-server
spec:
replicas: 1
selector:
matchLabels:
k3s-app: dns-server
template:
metadata:
labels:
k3s-app: dns-server
spec:
containers:
- name: sekinet-dns
image: coredns/coredns:1.8.0
args:
- -conf
- /etc/coredns/Corefile
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
readinessProbe:
failureThreshold: 5
httpGet:
path: /ready
port: 8181
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
- name: adblock-host
mountPath: /etc/adblock-host
readOnly: true
initContainers:
- name: download-adblock-hosts
image: lucashalbert/curl
args:
- -v
- https://warui.intaa.net/adhosts/hosts.txt
- --output
- /etc/adblock-host/hosts
volumeMounts:
- name: adblock-host
mountPath: /etc/adblock-host
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
- name: adblock-host
emptyDir: {}
---
apiVersion: v1
data:
Corefile: |
.:53 {
hosts /etc/adblock-host/hosts {
fallthrough
}
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
policy round_robin
}
cache 300
log
errors
health
ready
reload
}
kind: ConfigMap
metadata:
name: coredns
---
apiVersion: v1
kind: Service
metadata:
labels:
name: dns-server
spec:
clusterIP: 10.43.0.53
type: ClusterIP
selector:
k3s-app: dns-server
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
sessionAffinity: None
動作確認
こんな感じでCoreDNSがログを出力します
# kubectl -n yournamespace logs coredns-56fd7bd897-l642s
[INFO] 10.42.1.1:3133 - 41029 "AAAA IN qiita.com. udp 27 false 512" NOERROR qr,rd,ra 138 0.040631835s
[INFO] 10.42.1.1:44694 - 60473 "A IN qiita.com. udp 27 false 512" NOERROR qr,rd,ra 102 0.045602764s
[INFO] 10.42.1.1:38573 - 46515 "AAAA IN ssl.gstatic.com. udp 33 false 512" NOERROR qr,aa,rd,ra 76 0.00043194s
[INFO] 10.42.1.1:61158 - 47323 "A IN ssl.gstatic.com. udp 33 false 512" NOERROR qr,rd,ra 64 0.02043012s
[INFO] 10.42.1.1:8413 - 10128 "A IN clients6.google.com. udp 37 false 512" NOERROR qr,rd,ra 126 0.006006271s
[INFO] 10.42.1.1:57616 - 9111 "AAAA IN clients6.google.com. udp 37 false 512" NOERROR qr,rd,ra 138 0.020215048s
もちろんパケットキャプチャしても再帰問い合わせのクエリはTLSで保護されて中身を見ることは出来ません。
まとめ
今回は,お家Kubernetesのユースケースの1つとして,Raspberry Pi 4, Ubuntu 18.04 ARM 64-bit, K3s, CoreDNS等を組み合わせて実用できるお家DNS Cacheコンテナをご紹介しました。最近はパブリッククラウドサービスを利用する機会が多く,自分で意識的にやらないとEdgeやIoTのユースケースを触る機会が少なくなってしまったなぁと思います。しかし,OSの管理からKubernetesのプラットフォーム,その上に乗るアプリまで常に新しいものに触れる機会はとても大事なのでこの記事を読んでくれた方々もパブリッククラウドなら5分で出来ることを何時間もかけてやってみませんか?(^^)
明日は @khrd さんのKindでVirtualClusterを試そうです!お楽しみに!!!
追伸: 水曜どうでしょうの新作が楽しみです♪
お家DNSレコードを見ていて面白かったこと
- 自分の活動量がDNSクエリ量に比例するので可視化すると面白そう
- クエリレコードの殆どがAやAAAAレコードなのですが,最近HTTPSレコードがチラホラ出るようになりました。Apple, Google, Akamai等が先んじて提供していることが観測できました。