LoginSignup
18
26

More than 5 years have passed since last update.

Laravelをskaffold使ってKubernetesで動かす

Last updated at Posted at 2018-11-30

Docker for MacにKubernetesが搭載された安定版がリリースされ、少し時間が立ちました。
minikubeとのお別れは完了しましたでしょうか?今回は、Laravelで作ったアプリケーションをKubernetes上で動かせるサンプルを作成しました。

ryosukes/laravel-k8s-sample

READMEを読めば、とりあえず動かすことは可能になるかと思うので、とりあえず動かしてみたいと思う方は試してみてください(初回はすごく時間かかるかもですが)。

僕自身が社内でLaravelを使っているため、今回はLaravelでサンプルを作成しましたが、フレームワークだったり言語を他のものにすげ替えても簡単に動作させることが可能だと思います。

この記事では、Mac上でLaravel on Kubernetesを動かしskaffoldを使って開発できるようにするまでの流れを解説していきたいと思います。

  • 目次
    • はじめに
    • Dockerイメージ作成
    • マニフェスト作成
    • クラスタにデプロイ、 開発する
    • まとめ

はじめに

必要となるLaravel, Docker for Macなどの準備を終えておきましょう

Laravel Installation
Docker for Mac

もしDocker for Macがインストール済で古いバージョン(Docker Community Edition 18.06.0-ceより古い)の場合は、Kubernetesが使えるよう最新版にアップデートしておきましょう。

次に、ターミナル上でKubernetesを操作するために kubectl というコマンドをインストールします。

kubectl のインストールが完了したら、Kubernetesのパッケージマネージャである helmをインストール。

後述する Ingress が使用できるよう helm を使用しパッケージをインストールします。

$ helm init
$ helm install --name my-release stable/nginx-ingress

最後に、Kubernetesでの開発に便利なツールである skaffold をインストールしておきます。

諸々のインストールが完了したら、作業を進めていきます。

Dockerイメージ作成

ここからコンテナの準備に入ります。まずは、Laravelを動かすためのDockerイメージを作成します。

サンプルでは、PHPとNginxが共存しているDockerイメージを作成しています。下記記事を参考にさせていただきました。

Dockerコンテナイメージのダイエット - Laravel編

※ このDockerファイルだと、開発時に毎回npm installcomposer installが走りビルドに時間がかかります。
STG/本番リリース用に使う分にはよいですが、開発用にはそれらを省いてビルド時間が短縮できるようイメージを構築できるとローカルでの開発がより楽に進められるようになるかと思います。
(すでにnpm,yarn,composerなどがインストール済みのイメージを別で用意し、composer installなどはローカルにあるvendorをマウントするなど)

マニフェスト作成

マニフェストと呼ばれるYAML形式の設定ファイルを作成し、それをKubernetesのクラスターに適用することで、k8s環境を構築します。

用意するマニフェストは下記。

  • Application(サンプルではLaravel)
    • Deployments
    • Services
    • Ingress
    • ConfigMaps
  • MySQL
    • Deployments
    • PersistentVolumes
    • PersistentVolumeClaims
    • Services
  • Redis
    • Deployments
    • Services

Laravel, MySQL, Redis用で分かれていますが、それぞれ細かく見ていきます。

Application

Deployments

deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sample-app
spec:
  replicas: 2 # Podの数
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 100% # ローリングアップデート時に許容できる超過して作られる Pod の最大数
      maxUnavailable: 0 # 最低 1 pod は ready な状態を保つ
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: sample-app/app-dev:latest # Dockerイメージを指定
        envFrom:
        - configMapRef:
            name: sample-app-config # configmapの名前を指定
        ports:
        - containerPort: 80

「いきなり設定ファイル出されてもわかんねーよ!」ということもあるかと思いますが、冷静に見ればそんなに難しいことは書いてありません。

Deploymentsでは、その名の通り、LaravelアプリのDockerイメージが乗っかる Pods と呼ばれるコンテナグループのデプロイ管理定義を行います。

Deployments及びPodsを詳しく知りたい場合は、下記記事などが参考になります。

Services, Ingress

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-app-service
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: sample-app
ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sample-app-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    ingress.kubernetes.io/ssl-redirect: “true”
    ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
    - sample-laravel.localhost
    secretName: tls-certificate
  rules:
  - host: sample-laravel.localhost
    http:
      paths:
      - path: /
        backend:
          serviceName: sample-app-service
          servicePort: 80

ServiceとIngressでは、Kubernetes内でのネットワークの設定を行っています。Serviceでportや通信プロトコルを指定、IngressはAWSのALBのように、受け付けたリクエストのパスなどに応じてどのアプリケーションにリクエストを流すかの設定ができます。

また、Ingressではsslの設定を行うこともできます。サンプルではopensslで証明書を作成し、その証明書を使用しています。

# set common name 'sample-laravel.localhost'
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/nginx-selfsigned.key -out /tmp/nginx-selfsigned.crt; openssl dhparam -out /tmp/sample.pem 2048
$ kubectl create secret tls tls-certificate --key /tmp/nginx-selfsigned.key --cert /tmp/nginx-selfsigned.crt

Ingressではベーシック認証などもかけることができるので、ステージングでKubernetesを使用する際に参考にすることもできます。

なお、Services, Ingressに関して詳しく知りたい場合は下記記事が参考になるかと思います。

KubernetesクラスタにServiceとIngressを追加し、Webアプリケーションを外部に公開

ConfigMaps

configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-app-config
data:
  # ------------------------------------------------------------------------------
  # app
  # ------------------------------------------------------------------------------

  APP_NAME: laravel
  APP_ENV: local
  APP_DEBUG: "true"
  APP_URL: https://sample-laravel.localhost
  #APP_KEY:

  # NOTE: to use stern
  LOG_CHANNEL: errorlog

  # ------------------------------------------------------------------------------
  # DB
  # ------------------------------------------------------------------------------

  DB_HOST: mysql
  DB_USERNAME: root
  DB_PASSWORD: root

  DB_CONNECTION: mysql
  DB_DATABASE: sample
  DB_DATABASE_TEST: sample_test

  REDIS_HOST: redis-master

  # ------------------------------------------------------------------------------
  # MAIL
  # ------------------------------------------------------------------------------

  MAIL_DRIVER: smtp
  MAIL_HOST: smtp.mailtrap.io
  MAIL_PORT: "2525"
  MAIL_USERNAME: "null"
  MAIL_PASSWORD: "null"
  MAIL_ENCRYPTION: "null"

ConfigMapではLaravelの .env に設定する値など環境変数を記載します。ここは .envをそのまま使うでも良いと思いますが、ビルドしたアプリケーションイメージに.envを含めたくないといった場合、ConfigMapを使うと良いでしょう。また、本番運用を見越して使うのであれば、DBやメールなどの秘匿情報に関しては、KubernetesのSecretを使うのが良いです。KMSなどを使えば、安全に運用できます。

ファイルをAWS KMSで暗号化して安全にgit commitできるようにするmozilla/sopsの使い方

MySQL

続いてMySQLのPodの設定を見ていきます。

Deployments

mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.7
        name: mysql
        resources:
          requests:
            memory: 100Mi
            cpu: 100m
          limits:
            memory: 4000Mi
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: root
        - name: MYSQL_DATABASE
          value: sample
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

LaravelのDeploymentでは記載がありませんでしたが、cpuとmemoryの指定をしています。過去に作業していたとき、Sequel Proを使用して大きめなダンプデータを流したところフリーズしたことがありました。そのときに、cputとmemoryの割当を増やしスペックを上げたところフリーズしなくなったことがあります。この値は適宜変更して使えると良いと思います。

また、データの永続化をするためにvolumesで persistentVolumeClaims の設定をしています。このあたりは次で説明します。

PersistentVolumes, PersistentVolumeClaims

データに関して、Podやクラスターが終了してもなくならないよう、データの永続化を行う必要があります。そのためにあるのが、PersistentVolumes および PersistentVolumeClaimsの2つです。

mysql-pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/lib/mysql"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

サンプルではそれぞれの設定の記述が短いため一つのyamlファイルにまとめてしまっています。このマニフェストを用意しDeploymentsで設定を記述することで、PersistentVolumes でストレージ領域を確保、そのストレージを PersistentVolumeClaimes でPodに領域を紐付けるということが可能になります。

詳しくは下記記事が参考になると思います。

Kubernetsに出てくる用語:PVCとは?

Redis

Deployment

redis-deployment.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: redis-master
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: master
      tier: backend
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: redis
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379

特筆するするところはないのですが、roleやreplicasを変更したマニフェストを作成するなどしmaster -slave のクラスタ構成を作成することも可能です。

クラスタを起動する

さて、以上でDockerとKubernetesの準備が完了したところで、Docker for MacでDocker/Kubernetesを起動しておきます。

領域のキャプチャ 29.png

Kubernetes 2018-11-17 15-55-51.png

2つともグリーンのまるポチがついてrunningな状態になってればOKです。

クラスタにデプロイ、 開発する

クラスタにデプロイするには、skaffoldを利用するのが便利です。

skaffold.png

skaffoldはアプリケーションコードが含まれたDockerイメージをKubernetes上にデプロイするのに便利なGoogle謹製のツールです。

skaffoldのリポジトリにskaffold用の設定サンプルファイルがあるのでそれを編集します。ファイルはこれ

設定を変更したものが

apiVersion: skaffold/v1alpha2
kind: Config
build:
  tagPolicy:
    sha256: {}
  artifacts:
  - imageName: sample-app/app-dev # 好きなコンテナ名を指定してOK
    workspace: .
    docker:
      dockerfilePath: Dockerfile # skaffold.yamlと同じディレクトリ
  local:
    skipPush: true
deploy:
  kubectl:
    manifests:
    - kubernetes/app/* # skaffoldがmanifestファイルの変更を反映してくれる

になります。

この設定ファイルが用意できたら、skaffold run というコマンドを叩くとKubernetesのクラスター上にアプリケーションをデプロイできるようになります。

開発するときは、 ファイルの変更が発生したらすぐ自動更新したくなると思います。そこで、skaffold devというコマンドを叩くとしてくれるようになります。

詳しくは下記記事が参考になるかと思います。

Kubernetesの開発環境で困っているならskaffoldを使え

まとめ

以上で、Kubernetes上でLaravelが動かせるようになったのではないかと思います。Kubernetesは従来のWebの開発環境ではあまり目・耳にしたことがないような単語や概念が多く、学習コストが高めです。しかし、マイクロサービス化を進めたり、安定したサービスを提供するのにとても有用なツールとなっていると思います。

今回Laravelで試しに作ってみて、フルスタックなフレームワークで開発したアプリケーションを載せようとすると、容量が大きいため開発時にKuberenetes上に反映するのに多少時間がかかるなぁと思いました。特にDockerファイルのMulti Stage Buildでnpm installやらアセットのコンパイルがビルド時のタスクとしてあると、べらぼうに時間がかかります。Dockerイメージをビルドする前後で、何かしら工夫する必要があるなと感じました。

コンテナ技術をうまく利用し、より安全にスケーラブルなアプリを作るのであれば、こういったツールの採用とともに、言語・フレームワーク・サービスの境界などの見直しが行えるとよりメリットを享受できそうです。

既存サービスでの採用は難しいところもあるかと思いますが、新規で開発するのであればKubernetesを使わない手はないなと思います。(既存サービスでも、思い切ってローカルではKubernetesを使わないというのも、一つの手かもしれません)

18
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
26