Rails + MySQL on Kubernetesを試す

前回はMySQLがk8s上で動いたわーい、というところまでやった。今回はRubyOnRailsを動かしてみる。

前提として動かすRailsアプリが必要なんだけど、suburi/docker-mysql-railsというdocker-composeで動かせるものがあったのでありがたくforkして利用することにした(takaishi/docker-mysql-rails

MySQLの起動(前回と同じ)

前回と同じでOK。

apiVersion: v1
kind: ReplicationController
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    app: mysql
  template:
    metadata:
      name: mysql
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: passw0rd
        ports:
        - containerPort: 3306
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
    targetPort: 3306
    protocol: TCP
  type: LoadBalancer
  selector:
    app: mysql

作成。

$ kubectl create -f ./mysql.yml

Railsの起動

まずはイメージを用意する

kubernetesはイメージのビルドはできないようなので、dockerコマンドなどでイメージを作成して、レジストリに置く必要がある。DockerHubを使う手もあるが、仕事の場合、そうはいかない可能性も考えられる。今回はローカルにプライベートレジストリを構築し、そこにプッシュしてみる。

さて、プライベートなDockerレジストリを構築するにはどうしたらいいかなと思っていたら、 Local development with Kubernetes という記事を発見した。k8s上にレジストリを構築するもので、手っ取り早そうなのでこれを使うことにする。

apiVersion: v1
kind: ReplicationController
metadata:
  name: kube-registry-v0
  namespace: kube-system
  labels:
    k8s-app: kube-registry
    version: v0
spec:
  replicas: 1
  selector:
    k8s-app: kube-registry
    version: v0
  template:
    metadata:
      labels:
        k8s-app: kube-registry
        version: v0
    spec:
      containers:
      - name: registry
        image: registry:2
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
        env:
        - name: REGISTRY_HTTP_ADDR
          value: :5000
        - name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
          value: /var/lib/registry
        volumeMounts:
        - name: image-store
          mountPath: /var/lib/registry
        ports:
        - containerPort: 5000
          name: registry
          protocol: TCP
      volumes:
      - name: image-store
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: kube-registry
  namespace: kube-system
  labels:
    k8s-app: kube-registry
    kubernetes.io/name: "KubeRegistry"
spec:
  selector:
    k8s-app: kube-registry
  ports:
  - name: registry
    port: 5000
    protocol: TCP
---
apiVersion: v1
kind: Pod
metadata:
  name: kube-registry-proxy
  namespace: kube-system
spec:
  containers:
  - name: kube-registry-proxy
    image: gcr.io/google_containers/kube-registry-proxy:0.3
    resources:
      limits:
        cpu: 100m
        memory: 50Mi
    env:
    - name: REGISTRY_HOST
      value: kube-registry.kube-system.svc.cluster.local
    - name: REGISTRY_PORT
      value: "5000"
    - name: FORWARD_PORT
      value: "5000"
    ports:
    - name: registry
      containerPort: 5000
      hostPort: 5000
kubectl apply -f local-registry.yml

これで、ローカリリポジトリの準備が整った。後は、動かしたいRailsアプリのイメージを作成し、レジストリにプッシュすれはOK。

$ docker build -t localhost:5000/r_takaishi/docker-mysql-rails .
$ docker push localhost:5000/r_takaishi/docker-mysql-rails

いよいよ起動だ

Rails用のマニフェストは以下の通り。環境変数でDBのホストやパスワード等を渡している。また、コンテナの起動前にdb:createやdb:migrateを行うようにしてみた(が、このPostStartの実行ログがどこに保存されるのかがいまいち分かっていない)。

apiVersion: v1
kind: ReplicationController
metadata:
  name: rails
spec:
  replicas: 1
  selector:
    app: rails
  template:
    metadata:
      name: rails
      labels:
        app: rails
    spec:
      containers:
      - name: rails
        image: localhost:5000/r_takaishi/docker-mysql-rails
        command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
        env:
          - name: RAILS_ENV
            value: development
          - name: MYSQL_HOST
            value: mysql.default.svc.cluster.local
          - name: MYSQL_USER
            value: root
          - name: MYSQL_PASSWORD
            value: passw0rd
        lifecycle:
          postStart:
            exec:
              command:
                - /myapp/post-start.sh
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: rails
spec:
  ports:
  - port: 3000
    targetPort: 3000
    protocol: TCP
  type: LoadBalancer
  selector:
    app: rails
$ cat post-start.sh
#!/bin/bash

RAILS_ENV=$RAILS_ENV bundle exec rake db:create
RAILS_ENV=$RAILS_ENV bundle exec rake db:migrate

applyする。

kubectl apply -f ./rails.yml

こんな感じで、mysqlとrailsのpodが起動した状態となる。

$ kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-8kd4q   1/1       Running   2          4d
rails-l15mm   1/1       Running   0          28m

minikubeでアクセス先URLを取得して、アクセスする。

$ minikube service rails --url
http://192.168.99.100:31452

やったー!

スケールしてみる

コンテナの台数を増やしてみる。railsのpodが1台の所から始めよう。

$ kubectl get pod
NAME          READY     STATUS    RESTARTS   AGE
mysql-8kd4q   1/1       Running   2          4d
rails-8cnzn   1/1       Running   0          6m

scaleコマンドがある。便利だ。5台にしてみよう。

$ kubectl scale rc rails --replicas=5
replicationcontroller "rails" scaled

そうすると、podが5個に増える。

$ kubectl get pods
NAME          READY     STATUS              RESTARTS   AGE
mysql-8kd4q   1/1       Running             2          4d
rails-8cnzn   1/1       Running             0          7m
rails-nwz9l   0/1       ContainerCreating   0          2s
rails-q4f5z   0/1       ContainerCreating   0          2s
rails-tfcjw   0/1       ContainerCreating   0          2s
rails-zbjqw   0/1       ContainerCreating   0          2s

起動完了。30秒弱で1台から5台にスケールアウトできた。

$ kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-8kd4q   1/1       Running   2          4d
rails-8cnzn   1/1       Running   0          7m
rails-nwz9l   1/1       Running   0          25s
rails-q4f5z   1/1       Running   0          25s
rails-tfcjw   1/1       Running   0          25s
rails-zbjqw   1/1       Running   0          25s

減らすのも簡単。scaleコマンドで台数を1にしてやれば、1台になるまでコンテナが減ってくれる。

まとめ

RailsアプリとMySQLをKubernetes上で動かし、アプリの台数を増やしたり減らしたりしてみた。台数の増減がとにかく楽なので、仕事でも使いたいなあと思います。

 

 

 

 

3 Comments

  1. 拝見いたしました。参考になりました。

    一つ質問なのですが、LoadBalancerで双方のserviceを作った時にmysqlは外部に公開されてしまうのではないでしょうか?

    1. 確かに、LoadBalancerでサービスを作った場合はMySQLが外部に公開されてしまう可能性がありますね。1つ前のブログ記事で試していた、MySQLを動かすマニフェストをそのまま使ったのでこうなっていますが、実際にはRails用のPodからしかアクセスできないようにするのがよいのではないかと思います。

Leave a Reply

Your email address will not be published. Required fields are marked *