はじめに
筆者が開発を続けているRaindという自作コンテナランタイムについて紹介しようと思います。
GitHub Repoはこちら: https://github.com/shizuku198411/Raind
筆者自身は数年前からコンテナランタイムを開発しており、最初はコンテナの仕組みを知るために始めたのですが、コンテナランタイムの仕組みは非常に奥深く、何度も様々な言語/仕組みでランタイムを開発していました。
もちろんランタイム開発だけではなく日常的にDocker/Kubernetesといったコンテナ基盤を活用しています。
コンテナランタイムを開発しながら成熟した2つの仕組みを利用していると、既存の仕組みの便利さにも気づきますが、もう少しこうしたい!というものも出てくるものです。
DockerにはDockerの扱いやすさがあります。
一方でKubernetesにはPod/Service/Ingressのような、アプリケーションをworkloadとして扱うための強いモデルがあります。
ただ、この2つは普段かなり別の世界として扱われます。
- Docker 的に単体コンテナを起動する
- Kubernetes 的に Pod / Deployment / Service / Ingress としてデプロイする
- 通信制御やトラフィック可視化を runtime レベルで扱う
こういうものを、できるだけ1つのランタイムの中で扱えるようにしたいと考えるようになりました。
その方向で作っているのがRaindです。
この記事では、Raindがどういうランタイムなのか、どういう意図で拡張してきたのか、実際にどう使えるのかを、開発記録としてまとめます。
Raindとは
Raindは、ざっくり言うと Docker/Kubernetes の Container/Pod を統合的に扱うためのコンテナランタイムです。
単体コンテナを起動するだけではなく、Pod、Deployment、Service、Ingress、通信ポリシー、ネットワーク可視化までを、1つの runtime stack として扱うことを目指しています。
内部的には、大きく次のような構成です。
+-------+ +-----------+ +---------+
| raind | -----> | condenser | -----> | droplet |
+-------+ +-----------+ +---------+
CLI high-level runtime low-level runtime
-
raind: CLI -
condenser: API / controller / resource 管理 / network / policy / ingress などを扱う high-level runtime -
droplet: namespace / cgroup / mount / pivot_root などを扱う low-level container runtime
Docker 的な container run もできます。
raind container run --name web -p 8080:80 nginx:latest
一方で、Kubernetes-styleのYAMLを resource apply して、Deployment/Service/Ingress まで作ることもできます。
raind resource apply -f manifest.yaml
Raindがやりたいのは、Kubernetes を完全に再実装することではなく、
Kubernetes-styleな考え方を取り込みつつ、単体コンテナ・Pod・Service・Ingress・通信制御・可視化を、1つのランタイムの中で扱えるようにすることです。
開発意図
Docker 的な扱いやすさと Kubernetes 的な構造をまとめたい
Docker は単体コンテナを動かす体験がかなり良いです。
docker run nginx
みたいな感覚で、すぐにコンテナを起動できます。
一方で、アプリケーションを少し大きい単位で見ると、PodやServiceやIngressのようなモデルがほしくなります。
例えば、アプリケーションをデプロイする場合は、アプリ本体だけではなくアプリへのアクセス経路も構築したくなります。
app
├── Pod
│ └── containers
├── Service
│ └── stable L4 endpoint
└── Ingress
└── host/path based routing
Kubernetesではこれらがかなりよく整理されているので、このような場合はDockerではなくKubernetesを利用することが一般的です。
ただ、Kubernetesは本格的な構成でもあるため、
ちょっとPodとしてアプリを起動したい、ServiceとIngressの流れを確認したい、というためにKubernetesを構築するのは少し手間がかかります。
単体コンテナはDocker Likeに扱える。
でも、必要になったら Pod/Deployment/Service/IngressのようなKubernetes-styleのリソースも同じランタイムで扱える。
この「DockerとKubernetesの間の距離を縮める」ようなランタイムを作りたい、というのが Raindの大きな方針です。
通信制御・可視化を runtime レベルで組み込みたい
もう1つやりたかったのが、通信制御や可視化をランタイムレベルで組み込むことです。
DockerやKubernetesでもネットワーク制御の仕組みはあります。
ただ、自分が作りたかったのは、コンテナやPodの通信をランタイム自体が把握していて、通信ログやポリシーと自然につながる形です。
Raindでは、通信を大きく2つに分けています。
East-West
コンテナ間 / Pod 間の内部通信
North-South
コンテナや Pod から外部への通信
現状の方針としては、次のようにしています。
- コンテナ間通信は default deny
- 外部通信は default allow / observe
- 必要な内部通信だけ policy で許可する
- 通信ログでは、可能な限り IP ではなく container 名に戻して表示する
コンテナ間通信をdefault denyにしているのは、内部通信を明示的に許可する形にしたかったからです。
逆に外部通信は、最初から全部止めると扱いづらいので、まずは observe しながら許可する形にしています。
ここはZero Trust Networkをコンテナランタイムに落とし込んでみる、という意図もあります。
昨今コンテナも必ずしも安全な環境ではなくなりつつあり、サプライチェーン攻撃等による不審な通信などもしっかりと把握する必要があるためです。
利用イメージ
Raind では、大きく次のような使い方を想定しています。
Raind
├── Single Container
├── Bottle
├── Resources
│ ├── Pod
│ ├── ReplicaSet
│ ├── Deployment
│ ├── Service
│ └── Ingress
└── Traffic Observability & Control
Single Container: Docker Like
一番シンプルなのは、Dockerのように単体コンテナを起動する使い方です。
raind container run --name web -p 8080:80 nginx:latest
この場合はDockerと同じく、イメージをpullして、rootfsを展開し、namespace/cgroup/mountを構成して、コンテナを起動します。
単体コンテナとして起動したい場合は、この使い方が一番シンプルです。
Bottle: Docker Compose Like
Raind にはBottleという単位もあります。
これはDocker Compose的に、複数のコンテナをまとめて扱うための仕組みです。
Docker Composeとほぼ同等のYAMLで定義が可能です。
bottle:
name: wordpress
services:
wp:
image: wordpress:latest
env:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=wordpress
- WORDPRESS_DB_NAME=wordpress
ports:
- "11240:80"
capAdd:
- CAP_NET_BIND_SERVICE
depends_on:
- db
db:
image: mysql:latest
env:
- MYSQL_ROOT_PASSWORD=wordpress
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
policies:
- type: east-west
source: wp
destination: db
protocol: tcp
dest_port: 3306
comment: "wp -> db 3306/tcp"
単体コンテナより少し大きい単位で、関連するコンテナ群をまとめて起動・管理したいときに使うイメージです。
さらに、冒頭でも記載したとおり通信制御を定義することができます。
少々面倒に思えますが、アプリケーションとして本当に必要な通信のみを許可することで
セキュリティ向上にも通信要件の整理にもつながります。
Resources: Kubernetes Like
Raind ではKubernetes-styleのmanifestをデプロイできます。
ここでは実際の例を1つ載せます。
次のmanifestでは、Namespace、Deployment、Service、Ingressを定義しています。
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-web
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: demo-web
template:
metadata:
labels:
app: demo-web
spec:
containers:
- name: web
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: demo-svc
namespace: demo
spec:
selector:
app: demo-web
ports:
- port: 80
targetPort: 80
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
namespace: demo
spec:
tls:
- hosts:
- demo.local
rules:
- host: demo.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-svc
port:
number: 80
manifestに関してはkubernetesとの互換性を意識しているため、上記のmanifestはkuberenetesでも同様にデプロイできるものです。
適用すると、Namespace用のネットワークが作られ、Deployment/Service/Ingressが作成されます。
$ raind resource apply -f manifest.yaml
namespace: demo applied (network: rns2a97516c354b)
deployment: 01kv2x6ft6183we8g2e696z3ad applied
service: 01kv2x6ftbetxc6dp5r7cr337t applied
ingress: 01kv2x6h2vmysh72jeff7mz2ph applied (tls: enabled, hosts: demo.local)
各リソースの確認は以下のようになります。
# Deployment
$ raind resource deploy ls -n demo
DEPLOYMENT ID NAME NAMESPACE DESIRED CURRENT READY REPLICASET CREATED
01kv2x6ft6183we8g2e696z3ad demo-web demo 2 2 2 01kv2x6hbvr3c4sqx476k7sjsm less than a minutes
REPLICASETが表示されている通り、RaindでもPod数を維持するためのReplicaSet的な仕組みを持っています。
# pod
$ raind resource pod ls -n demo
POD ID NAME NAMESPACE UID READY STATE CREATED
01kv2x6hc74yxrmqgrmhxwsjax demo-web-01kv2x6h demo 1/1 running less than a minutes
01kv2x6ppvmmmk915a4660brwd demo-web-01kv2x6p demo 1/1 running less than a minutes
# service
$ raind resource service ls -n demo
SERVICE ID NAME NAMESPACE TYPE PORTS CREATED
01kv2x6ftbetxc6dp5r7cr337t demo-svc demo ClusterIP 80->80/tcp less than a minutes
# ingress
workshop@raind-dev-4ec1a7cf:~/manifest$ raind resource ingress ls -n demo
INGRESS ID NAME NAMESPACE HOSTS TLS PATHS BACKENDS CREATED
01kv2x6h2vmysh72jeff7mz2ph demo-ingress demo demo.local demo.local demo.local/(Prefix) demo-svc:80 less than a minutes
Ingressは一般的なkubernetesとは異なり、condenserに内蔵したgatewayで受ける構成です。
ServiceはClusterIPとしてPodへの安定した入口になり、IngressはHost/PathでServiceへルーティングします。
このあたりは、Kubernetesっぽい使い方をしつつも、実装は内蔵gateway/iptablesをベースとした軽量/シンプルな構成としてRaindの中で完結するようにしています。
Traffic Observability & Control
最後に、Raindでかなり重視しているのが通信の可視化と制御です。
RaindではコンテナやPodを起動するだけではなく、その通信をランタイムの中で扱えるようにしています。
ここはRaindの特徴的な機能のひとつなので、実際の操作をお見せします。
ポリシー追加
src-containerからwebのTCP/80への通信を許可します。
$ raind policy add --type ew -s src-container -d web -p tcp --dport 80
policy: 01kv2wh273j8ya1bq1yqgg2mnv created
ポイントは、IPではなくコンテナ名でポリシー設定を行える点です。
ポリシーコミット
ネットワーク制御は設定を誤ると影響が大きいため、明示的にcommitすることで実際のネットワーク制御へ反映します。
$ raind policy commit
This operation will affect the container network.
Are you sure you want to commit? (y/n): y
policy commit success
ポリシー確認
$ raind policy ls
FLAG: [*] - Applied, [+] - Apply next commit, [-] - Remove next commit, [ ] - Not applied
POLICY TYPE : East-West
CURRENT MODE: deny_by_default
FLAG POLICY ID SRC CONTAINER DST CONTAINER PROTOCOL DST PORT ACTION COMMENT REASON
[*] 01kv2wax5mhdbka0f0j5jtcxxr src-container dst-container icmp * ALLOW
[*] 01kv2wh273j8ya1bq1yqgg2mnv src-container web tcp 80 ALLOW
>> DENY ALL EAST-WEST TRAFFIC <<
============================
POLICY TYPE : North-South
CURRENT MODE: observe
FLAG POLICY ID SRC CONTAINER DST ADDR PROTOCOL DST PORT ACTION COMMENT REASON
>> ALLOW ALL NORTH-SOUTH TRAFFIC <<
この出力で見えている通り、East-West(コンテナ間通信)はdeny_by_defaultです。
許可した通信だけが通ります。
一方で North-South(外部通信)はobserveです。
外部通信は許可しつつ、ログとして観測します。
通信ログ
実際の通信ログはこんな感じです。
$ raind logs netflow
2026-06-14 19:56:01 ALLOW FROM: src-container => TO: dst-container {ICMP type=8 code=0}
2026-06-14 19:58:58 ALLOW FROM: src-container => TO: dst-container {ICMP type=8 code=0}
2026-06-14 19:59:03 DENY FROM: src-container => TO: web {ICMP type=8 code=0}
2026-06-14 19:59:03 DENY FROM: src-container => TO: web {ICMP type=8 code=0}
2026-06-14 19:59:05 DENY FROM: src-container => TO: web {ICMP type=8 code=0}
2026-06-14 19:59:13 ALLOW FROM: src-container => TO: 151.101.2.132 {TCP/443}
2026-06-14 19:59:15 ALLOW FROM: src-container => TO: 151.101.2.132 {TCP/443}
2026-06-14 19:59:20 ALLOW FROM: src-container => TO: web {TCP/80}
2026-06-14 19:59:22 ALLOW FROM: src-container => TO: web {TCP/80}
ポリシー設定と同じく、src-containerやwebのように、通信元・通信先がコンテナ名として確認が可能です。
Raindはランタイム内でコンテナやPodの状態を持っているので、通信ログを出すときにIPからコンテナ名へマッピングしています。
このあたりは、ランタイムとして通信を見ているからこそやりたかった部分です。
まとめ
Raindは、Docker的な単体コンテナ実行と、Kubernetes的なPod/Service/Ingressといったリソースを、1つのランタイムの中で扱うために作っています。
今できることをまとめると、こんな感じです。
Raind
├── Docker Like
│ └── Single Container
├── Docker Compose Like
│ └── Bottle
├── Kubernetes Like
│ ├── Pod
│ ├── Deployment
│ ├── Service
│ └── Ingress
└── Runtime Level
├── Traffic Policy
├── Netflow Logs
├── DNS Observability
└── Container/Pod name mapping
まだ実験的なランタイムですが、だいぶ形になってきました。
特に最近は、ClusterIP Service、HTTP/HTTPS Ingress、Ingress用TLS証明書、通信ポリシー、netflow可視化などがつながってきて、単なるコンテナ起動ツールではなく「workload runtime」として見えてきた感じがあります。
今後は、Ingress の拡張、TLS 周りの整理、policy / observability の強化、runtime の安定化あたりを進めていく予定です。