0
0

Claude 3.5と歩むGKEハンズオン

Last updated at Posted at 2024-08-03

概要

Google Cloud Platform (GCP)のGoogle Kubernetes Engine (GKE)を勉強するのに、ハンズオンが欲しかったので、Claude 3.5 Sonnetに作ってもらいました。

環境

  • マシン:Macbook Air (M1チップ、2020)
  • OS:Sonoma 14.6
  • Docker:v4.33.0
  • Node.js v20.5.1
  • Google Cloud SDK 486.0.0
  • MySQL Ver 8.0.36 for macos14.2 on arm64 (Homebrew)

アーキテクチャ

  • フロントエンド:Next.js+React+Typescript
  • バックエンド:Node.js+Express
  • データベース:MySQL

ハンズオン

以下、基本Claude 3.5 Sonnetの出力をベースに書いていきます。自分で追加・修正した部分は【】内にその旨記述します。
(念のため記述しますが)以下のコマンドはCursolのコンソールウィンドウより実施しています。

前提条件

  • Google Cloud Platformのアカウントを持っていること
  • gcloud CLIツールがインストールされていること
  • kubectl コマンドラインツールがインストールされていること
  • Dockerがインストールされていること

ステップ1: プロジェクトのセットアップ

  1. Google Cloud Consoleにログインし、新しいプロジェクトを作成します。

  2. (追加) Google Cloud SDKからGCPアカウントにログインします。:
    gcloud auth login

  3. ターミナルで以下のコマンドを実行し、プロジェクトを設定します:
    gcloud config set project YOUR_PROJECT_ID

    YOUR_PROJECT_IDは自分のプロジェクトIDに置き換えてください。GCPコンソールの以下の画面でプロジェクトIDを確認できます。
    GCP_console.png

  4. 必要なAPIを有効にします:
    gcloud services enable container.googleapis.com

ステップ2: GKEクラスタの作成

  1. 以下のコマンドでGKEクラスタを作成します:
    gcloud container clusters create my-cluster --num-nodes=3 --zone=us-central1-a
  2. クラスタの認証情報を取得します:
    gcloud container clusters get-credentials my-cluster --zone=us-central1-a

ステップ3: アプリケーション作成

  1. アプリケーションのディレクトリ構造を作成します:

    mkdir my-app
    cd my-app
    mkdir frontend backend database
    
  2. 【追加】 frontendアプリの作成

    cd frontend
    npx create-next-app frontend
    mv frontend/* .
    mv frontend/.* .
    rmdir frontend/
    
  3. 【一部修正】 各種ファイルの作成

    next.config.mjs
    /** @type {import('next').NextConfig} */
    const nextConfig = {
      reactStrictMode: true,
    };
    
    export default nextConfig;
    
    
    /src/app/page.tsx
    import { Suspense } from 'react';
    import HelloWorld from '@/app/components/HelloWorld/page';
    
    export default function Home() {
      return (
        <div>
          <Suspense fallback={<div>Loading...</div>}>
            <HelloWorld />
          </Suspense>
        </div>
      );
    }
    
    
    /src/app/components/HelloWorld/page.tsx
    'use server'
    import React from 'react';
    
    async function getMessage() {
      const backendUrl = `http://${process.env.BACKEND_SERVICE_HOST}:${process.env.BACKEND_SERVICE_PORT}`;
      const res = await fetch(`${backendUrl}/api/hello`, { cache: 'no-store' });
      if (!res.ok) {
        throw new Error('Failed to fetch data');
      }
      return res.json();
    }
    
    export default async function HelloWorld() {
      const data = await getMessage();
      return (
        <div>
          <h1>The message from backend is:</h1>
          <h2>{data[0].message}</h2>
        </div>
      );
    }
    
  4. 【追加】 backendアプリを作成

    cd ..
    cd backend
    npm init
    
  5. パッケージをインストール

    npm install express mysql cors
    
  6. server.jsを作成

    const express = require('express');
    const mysql = require('mysql2');
    const cors = require('cors');
    
    const app = express();
    app.use(cors());
    
    const db = mysql.createConnection({
      host: 'database',
      user: 'root',
      password: 'mysql',
      database: 'testdb'
    });
    
    db.connect((err) => {
      if (err) {
        console.error('Error connecting to database:', err);
        return;
      }
      console.log('Connected to database');
    });
    
    app.get('/api/hello', (req, res) => {
      db.query('SELECT * FROM hello_world', (err, results) => {
        if (err) {
          res.status(500).json({ error: err.message });
          return;
        }
        res.json(results);
      });
    });
    
    const PORT = 5001;
    app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
    
  7. databaseディレクトリ配下にinit.sqlファイルを作成

    init.sql
    USE testdb;
    CREATE TABLE IF NOT EXISTS hello_world (id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255));
    INSERT INTO hello_world (message) VALUES ('Hello World');
    

ステップ4: ローカル環境のDocker環境でアプリケーションをテスト

  1. 【nodeのバージョンのみ修正】 frontendディレクトリ配下にフロントエンド(React)のDockerfileを作成します:

    frontend/Dockerfile
    FROM node:20
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    RUN npm run build
    CMD ["npm", "start"]
    
  2. 【nodeのバージョンのみ修正】 backendディレクトリ配下にバックエンド(Node.js)のDockerfileを作成します:

    backend/Dockerfile
    FROM node:20
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    EXPOSE 5001
    CMD ["node", "server.js"]
    
  3. 【mysqlのバージョンとMYSQL_DATABASEを修正】 databaseディレクトリ配下にデータベース(MySQL)のDockerfileを作成します:

    database/Dockerfile
    FROM mysql:8.0
    ENV MYSQL_ROOT_PASSWORD=rootpassword
    ENV MYSQL_DATABASE=testdb
    COPY ./init.sql /docker-entrypoint-initdb.d/
    

    MYSQL_ROOT_PASSWORDrootpasswordから予測しにくい文字列へ適宜変更してください。

  4. 【一部修正】 アプリケーションのルートディレクトリにdocker-compose.yamlを作成します:

    docker-compose.yaml
    version: '3'
    services:
      frontend:
        build:
          context: ./frontend/
          dockerfile: Dockerfile
        ports:
          - "3000:3000"
        depends_on:
          - backend
    
      backend:
        build:
          context: ./backend/
          dockerfile: Dockerfile
        ports:
          - "5001:5001"
        depends_on:
          - database
        environment:
          - DB_HOST=database
          - DB_USER=root
          - DB_PASSWORD=rootpassword
          - DB_NAME=testdb
    
      database:
        build:
          context: ./database/
          dockerfile: Dockerfile
        ports:
          - "3306:3306"
        volumes:
          - ./mysql-data:/var/lib/mysql
    

    MYSQL_ROOT_PASSWORDrootpasswordから予測しにくい文字列へ適宜変更してください。

  5. docker composeでビルドします。:
    docker compose up -d

  6. 【追加】 単体テストを行います。:
    database:
    docker exec -it my-app-database-1 bashあるいはDocker Desktopのexecタブでコンテナ内に入り、以下を順に実行します。

    mysql -u root -p
    use testdb;
    select * from hello_world;
    

    Hello Worldと表示されればOKです。
    mysql.png

    backend:
    docker logs my-app-backend-1またはDocker Desktopで以下のようなログが表示されればOKです。
    backend.png

    以下のようなログが出力される場合は、databaseコンテナの起動が完了する前にbackendコンテナが起動してしまい、接続が上手くいっていません。コンテナを再起動してください。

    Server running on port 5001
    Error connecting to database: Error: connect ECONNREFUSED 172.18.0.2:3306
        at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16) {
      errno: -111,
      code: 'ECONNREFUSED',
      syscall: 'connect',
      address: '172.18.0.2',
      port: 3306,
      fatal: true
    }
    

    :

    frontend:
    docker logs my-app-frontend-1またはDocker Desktopで以下のようなログが表示されればOKです。
    frontend.png

  7. 結合テストを行います。:
    ブラウザでhttp://localhost:3000にアクセスし、以下の画面が表示されていれば成功です。
    成功画面.png

ステップ5: アプリケーションのコンテナ化

  1. 【追加】 docker buildxを設定します(開発環境がMacbookのため):

    docker buildx create --name mybuilder
    docker buildx use mybuilder
    docker buildx inspect --bootstrap
    

    参考:Dockerの「マルチCPUアーキテクチャ」に対応したイメージをビルドする

    筆者の開発環境はMacBook Air (M1)であり、プロセッサがARM64です。そのため、ローカル環境でdocker buildを実行すると、イメージのアーキテクチャがARM64になります。一方、GKEのデフォルトのCPUはx86_64であるため、ARM64イメージを利用すると動作しない場合があります。そこで、docker buildxを用いてマルチCPUアーキテクチャに対応したイメージをビルドします。

  2. 【修正】 各コンポーネントのDockerイメージをビルドします:

    docker buildx build --platform linux/amd64,linux/arm64 -t gcr.io/YOUR_PROJECT_ID/frontend:v1 ./frontend --push
    docker buildx build --platform linux/amd64,linux/arm64 -t gcr.io/YOUR_PROJECT_ID/backend:v1 ./backend --push
    docker buildx build --platform linux/amd64,linux/arm64 -t gcr.io/YOUR_PROJECT_ID/database:v1 ./database --push
    

    YOUR_PROJECT_IDは自分のプロジェクトIDに置き換えてください

ステップ6: Kubernetes マニフェストの作成

  1. フロントエンドのデプロイメントとサービス:
    frontend-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: frontend
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: frontend
      template:
        metadata:
          labels:
            app: frontend
        spec:
          containers:
          - name: frontend
            image: gcr.io/YOUR_PROJECT_ID/frontend:v1
            ports:
            - containerPort: 3000
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: frontend
    spec:
      type: LoadBalancer
      ports:
      - port: 80
        targetPort: 3000
      selector:
        app: frontend
    
    バックエンドのデプロイメントとサービス:
    backend-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: backend
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: backend
      template:
        metadata:
          labels:
            app: backend
        spec:
          containers:
          - name: backend
            image: gcr.io/YOUR_PROJECT_ID/backend:v1
            ports:
            - containerPort: 5000
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: backend
    spec:
      ports:
      - port: 5000
      selector:
        app: backend
    
    データベースのデプロイメントとサービス:
    database-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: database
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: database
      template:
        metadata:
          labels:
            app: database
        spec:
          containers:
          - name: database
            image: gcr.io/YOUR_PROJECT_ID/database:v1
            ports:
            - containerPort: 3306
            env:
            - name: MYSQL_ROOT_PASSWORD
              value: rootpassword
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: database
    spec:
      ports:
      - port: 3306
      selector:
        app: database
    

    YOUR_PROJECT_IDは自分のプロジェクトIDに置き換えてください

ステップ7: アプリケーションのデプロイ

  1. 作成したマニフェストファイルを適用します:

    kubectl apply -f frontend-deployment.yaml
    kubectl apply -f backend-deployment.yaml
    kubectl apply -f database-deployment.yaml
    
  2. デプロイメントの状態を確認します:

    kubectl get deployments
    kubectl get pods
    kubectl get services
    

    以下の説明では、kubectl get podsの出力結果のNAME列に表示されている文字列を「pod名」としています。
    kubectl_get_pods.png

  3. 【追加】 単体テストをします。:
    database:
    kubectl exec -it <databaseのpod名> -- /bin/bashでコンテナ内に入り、以下を順に実行します。

    mysql -u root -p
    use testdb;
    select * from hello_world;
    

    Hello Worldと表示されればOKです。
    kubectl_database.png

    backend:
    kubectl logs <backendのpod名>で以下のようなログが表示されればOKです。
    kubectl_backend.png

    以下のようなログが出力される場合は、database podのコンテナの起動が完了する前にbackend podのコンテナが起動してしまい、接続が上手くいっていません。podをkubectl rollout restart deployment backendで再起動してください。

    Server running on port 5001
    Error connecting to database: Error: connect ECONNREFUSED 172.18.0.2:3306
        at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16) {
      errno: -111,
      code: 'ECONNREFUSED',
      syscall: 'connect',
      address: '172.18.0.2',
      port: 3306,
      fatal: true
    }
    

    :

    frontend:
    docker logs <frontendのpod名>で以下のようなログが表示されればOKです。
    kubectl_frontend.png

  4. 結合テストをします。:
    フロントエンドサービスの外部IPアドレスを取得します。

    kubectl get service frontend
    

    kubectl.png
    EXTERNAL-IPに表示されているIPアドレスにアクセスして、以下のような画面が表示されれば成功です。
    成功画面.png

ステップ8: アプリケーションへの後始末

  1. クリーンアップ
    ハンズオンが終了したら、リソースを削除してコストを節約します:
    クラスタを削除します:

    gcloud container clusters delete my-cluster --zone=us-central1-a
    

    Container Registryの画像を削除します:

    gcloud container images delete gcr.io/YOUR_PROJECT_ID/frontend:v1 --force-delete-tags
    gcloud container images delete gcr.io/YOUR_PROJECT_ID/backend:v1 --force-delete-tags
    gcloud container images delete gcr.io/YOUR_PROJECT_ID/database:v1 --force-delete-tags
    

    YOUR_PROJECT_IDは自分のプロジェクトIDに置き換えてください

作業用フォルダ内ディレクトリ構造

ディレクトリ構造.png

0
0
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
0
0