Caution

未チェックの項目のあるドラフト版です

Laravel環境の設定を切り離そう#

先ほど作ったLaravel環境のマニフェストは、このままだとセキュアな情報を内包したままで出しております。 この部分をできるだけ隠すことができないでしょうか。

secretの利用でDBの情報を隠す#

まず、データベース部分のマニフェストを見てみましょう。

Listing 107 バックエンドのマニフェストより抜粋#
          limits:
            memory: "128Mi"
            cpu: "500m"
        # 現時点では環境変数渡し
        env:
          - name: MARIADB_RANDOM_ROOT_PASSWORD
            value: "1"
          - name: MARIADB_DATABASE
            value: sample
          - name: MARIADB_USER
            value: memoadmin
          - name: MARIADB_PASSWORD
            value: admin
        volumeMounts:
          - mountPath: /var/lib/mysql
            name: db-store

この部分は、DB名やアカウント情報は秘匿しておいた方が良いでしょう。 対応するsecretを作成してみましょうか。

secretリソースの作成#

ということで早速作ってみます。

Listing 108 バックエンド向けのsecret例#
apiVersion: v1
kind: Secret
metadata:
  name: db
type: Opaque
data:
  # 例示用に各値の元のものを出していますが
  # 本来はやめましょうね
  # sample
  dbname: c2FtcGxl
  # memoadmin
  user: bWVtb2FkbWlu
  # admin
  password: YWRtaW4=

各変数の値はBase64エンコードで保持しておくことを忘れないでください(マニフェストが通らなくなります)。

マウントしての利用#

これらに対応するように、参照側をざっと書き改めてみます。 環境変数に値がでないように、ファイルに出す形式にしてみましょう。

Listing 109 バックエンドのsecret対応版#
# バックエンド(mariadb)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: mariadb:10.9.4
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        # 現時点では環境変数渡し
        env:
          - name: MARIADB_RANDOM_ROOT_PASSWORD
            value: "1"
          - name: MARIADB_DATABASE_FILE
            value: /config/dbname
          - name: MARIADB_USER_FILE
            value: /config/user
          - name: MARIADB_PASSWORD_FILE
            value: /config/password
        volumeMounts:
          - mountPath: /var/lib/mysql
            name: db-store
          - mountPath: /config
            name: db-secret
        ports:
        - containerPort: 3306
        readinessProbe:
          exec:
            command:
              - mysqladmin
              - ping
              - "-u"
              - memoadmin
              - "--password=admin"
              - "-h"
              - "127.0.0.1"
      volumes:
        - name: db-store
          persistentVolumeClaim:
            claimName: db-store
        - name: db-secret
          secret:
            secretName: db

ポイントは3カ所ありました。

  • secretもボリュームとして設定でき、キー名のファイルが作られます

  • ボリュームとして宣言したものを適当な場所にマウントさせます

  • (mariadb)各環境変数名に_FILEを付与し、参照ファイルを指定します

これにより、パスワードなどの情報を秘匿した状態で利用可能となります。

.envファイルの問題#

Laravelでは、データベース接続の設定は、プロジェクトディレクトリ上にある .env ファイルによって処理されます。

しかし、githubなど公開されうる場所でパスワード情報の入っているようなファイルを置くべきではありません。

この問題にどう対応するかはいろいろ考えられます。

  • パスワード以外の情報による .env を再構成し、秘匿情報はsecretを用いて環境変数で渡す

  • .envをconfigMapもしくはsecretで渡す

前者はMariaDB側で対応しているのに…感はあるのですが、実はsecret自体は存在しているので、secretKeyRefを用いることで同じ値を使い回せます(変更忘れを防げる)。 変数が見えても良いのであれば選択肢としては考慮にたると思います(この場合はもちろんmariadb側も_FILE使わないとなることでしょう)。

そこで今回は、後者からsecretで渡すことを考えてみたいと思います。

secretから渡す方法も、雑に2つ考えられます。

  • .env ファイルをsecretから作られた疑似ファイルに対してシンボリックリンクを貼って対応する

  • .env ファイルをsecretから作られた疑似ファイルをinitContainers内でコピーして対応する

後者はイメージを書き換えずに対応できるかと思うのですが、initContainersとcontainersによるコンテナが同じディレクトリを共有状態にしていないと効果を持たないため難しいと思われます(チャレンジしがいはありそうですが)。

今回は前者の方式でイメージを書き換えてから使ってみましょう。

forkして準備する#

各自で作業してもらいたいので、まずは元ソースをforkしていきましょう。

そしてforkを呼び出します。

_images/laravel-sample-fork.png

Fig. 28 フォークの呼び出し#

フォーク後の名前は適当に選べますが、ここでは変えずにそのまま使うことにします(重複してなければOK)。

_images/laravel-sample-newfork.png

Fig. 29 forkした後の名前設定#

これで実行させれば、自分のリポジトリにforkを生成できるので、こちらから取得して書き換えていきます。

ということで、git cloneをしてから次に進みましょう。

Listing 110 コードの取得(USERNAMEは自分のアカウント名)#
PS> git clone https://github.com/USERNAME/laravel-sample-remastered.git

ファイルのコピーと削除#

.envは、クローンしたディレクトリのlaravel/sampleにあります。LinuxやmacOS環境ではドットファイルのため隠れて見えません。

Tip

もちろん ls -a で確認できますよ。

ここでは、dotenvという名前で作ることにします。

PS> kubectl create secret generic dotenv --from-file ./.env
secret/dotenv created

これで .env ファイルを取り込んだので削除可能ですが、マニフェストは消してしまうことが十分考えられます。 そのため、別の場所にコピーして削除し、リポジトリからファイルを削除しておきます。

Warning

パスワードの含まれるようなファイルのですので、取り扱いには十分注意しておきましょう。

削除したら、git側に認識させ、コミットして送信しておきましょう。

コマンドライン的にgitに認識させるとこのような感じになります。

Listing 111 コマンドラインでのgitによる削除処理#
PS> git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    .env

no changes added to commit (use "git add" and/or "git commit -a")

PS> git rm .env # 削除をリポジトリ内でも処理
rm 'laravel/sample/.env'

PS> git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    .env

PS> git commit -m "[del] 環境変数ファイルの削除"
PS> git push

ところが実際に運用する際、 .env ファイルが無いと意味がありません、 さらにマウントするときに、secretから.envファイルだけ切り出してここに置くのも難しいです。 そこで、 secretのマウントは別の場所(/config)にでも行うようにして、シンボリックリンクでごまかすことにしてみましょう。 このことを考慮すると、Dockerfileに若干手を入れることにもなります。

Listing 112 Dockerfile内、シンボリックリンクを作る#
FROM php
RUN --mount=type=tmpfs,destination=/var/cache/apt \
    --mount=type=tmpfs,destination=/var/lib/apt \
    apt-get update; \
    apt-get install -y git unzip
COPY --from=composer/composer /usr/bin/composer /usr/local/bin/composer
RUN docker-php-ext-install pdo_mysql pdo
WORKDIR /var/www/html
COPY sample /var/www/html
# secretをマウントして、そこにある.envをsymlinkで見せる
RUN ln -sf /config/.env /var/www/html/.env
RUN composer install

イメージの再生成#

あとはイメージを再生成して、Docker Hubの自分のリポジトリに上げておけば使えるでしょう。

PS> cd ..
# イメージ名は自分でアクセスできれば特に問いませんが、
# タグをきちんと設定した方が良いでしょう(ここではv1)
PS> docker build -t USERNAME/larabel-sample-remastered:v1 .
PS> docker push USERNAME/larabel-sample-remastered:v1

secretをマウントしよう#

あとはマニフェストの側で、イメージが利用する場所にsecretをマウントするだけです。

Listing 113 フロントエンド側#
# Laravelでのアプリ(frontend)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      volumes:
        - name: config
          secret:
            secretName: dotenv
      containers:
      - name: frontend
        image: densukest/laravel-sample-remastered:v1
        resources:
          limits:
            memory: "256Mi"
            cpu: "500m"
        ports:
        - containerPort: 80
        command:
          - php
          - artisan
          - serve
          - "--port=80"
          - "--host=0.0.0.0"
        volumeMounts:
          - name: config
            mountPath: /config
      initContainers:
        - name: init
          image: densukest/laravel-sample-remastered:v1
          command:
            - php
            - artisan
            - migrate
          volumeMounts:
            - name: config
              mountPath: /config

Caution

イメージ名(2カ所)をきちんと差し替え版イメージのリポジトリ名にしてください。

このマニフェストはbackendを参照するので、呼び出す前に必ずserviceリソース(svc-backend)も適用させてください。

PS> kubectl apply -f svc-backend.yml  # service/backend(ClusterIP)
PS> kubectl apply -f frontend.yml
PS> kubectl apply -f svc-frontend.yml # service/frontend(NodePort)
...
PS> minikube service frontend # open browser