AWS CloudFormation 実践 第1回 VPC編

今回から、何回かにわたってAWS CloudFormation の実践について書いていきます。

経緯

今年の夏に、SAA(AWS Solution Architect Assosiate)資格の期限が切れてしまうため、一念発起してSAP(同 Specialist)資格取得を決めました。従来は Udemy での勉強をしていましたが、今回はチャネルを変えて「Cloud Tech」さんのサイトで勉強を始めました。

現在は動画中心で勉強をすすめているのですが、実践編の都度、環境を作っては崩してを繰り返しているので手間がかかり、必要に応じてCloudFormationで基盤構築することを試してみようと思います。

これまでも、IaC(Infrastructure as Code)の重要性は理解していましたが、実際に手を動かして構築したことはなかったので、良い機会かと。ただ、目的はあくまで試験合格なので脱線注意でいきたいな、とは思っています。

CloudFormationとIaCの基礎知識

CloudFormationはYaml(またはJSON)形式で書かれたテンプレートファイルに従って、AWSのインフラ環境を自動構築するのサービスです。AWSインフラ構築はAWSコンソールからのGUI操作でも可能ですが、テンプレートファイルにまとめておくことで、新規構築、変更、破棄が簡単に行えます。構築するセットのことを「スタック」と呼びます。

このように、コード(テンプレートファイル記述)を使用してインフラ環境を構築することを 「Infrastrucure As Code(IaC)」と呼び、再利用性や、保守性の高さから昨今のトレンドとなっています。IaCの実現は、AWS専用のCloudFormationの他、Terraformや、Ansibleといった汎用ツールも存在します。

CloudFormationの実行方法

CloudFormationには、次のような実行方法があります。

  • AWS CLIで、ローカル環境のテンプレートファイルをAWSに対して実行する。
  • AWSマネージメントコンソールで、CloudFormationを選択して、作成したテンプレートファイルをアップロードする。
  • Code Pipelineを使用して、Gitに登録したテンプレートファイルをデプロイする

今回は、1点目のAWS CLIを使用してデプロイを行う方法を採用します。

事前準備

AWS CLIを使用するには、AWSのアカウントを作成の上で、次の準備が必要となります。

  1. AWS CLIをローカル環境にインストールする。
  2. AWSコンソールで IAM サービスを開き、CLIを使用するアカウントとアクセスキーを作成する
  3. ローカル環境でコマンドプロンプトから aws configure をコマンドを実行し、リージョン・アクセスキーを設定する。

ここでは詳細は割愛します。↓の公式ページを参照ください。

docs.aws.amazon.com

Cloud Formation テンプレート基礎知識

テンプレートファイル(ここではYaml形式で説明します)のスケルトンは以下のとおりとなっています(AWS公式ページを引用)

AWSTemplateFormatVersion: "version date"

Description:
  String

Metadata:
  template metadata

Parameters:
  set of parameters

Rules:
  set of rules

Mappings:
  set of mappings

Conditions:
  set of conditions

Transform:
  set of transforms

Resources:
  set of resources

Outputs:
  set of outputs

各セクションの内容は次の表のとおりです。

セクション 設定内容 必須
AWSTemplateFormatVersion テンプレートが準拠している AWS CloudFormation テンプレートバージョン。テンプレート形式バージョンは API または WSDL バージョンと同じではありません。テンプレート形式バージョンは API および WSDL バージョンとは関係なく変更できます。 ×
Description テンプレートを説明するテキスト文字列です。このセクションは、必ずテンプレートの Format Version セクションの後に記述する必要があります。 ×
Metadata テンプレートに関する追加情報を提供するオブジェクトです。 ×
Parameters 実行時 (スタックを作成または更新するとき) にテンプレートに渡す値です。テンプレートの Resources および Outputs セクションからのパラメータを参照できます。 ×
Rules スタックの作成またはスタックの更新時に、テンプレートに渡されたパラメータまたはパラメータの組み合わせを検証します。 ×
Mappings キーと関連する値のマッピングで、条件パラメータ値の指定に使用でき、ルックアップテーブルに似ています。Resources セクションと Outputs セクションで Fn::FindInMap 組み込み関数を使用することで、キーと対応する値を一致させることができます。 ×
Conditions スタックの作成中または更新中に、特定のリソースが作成されるかどうか、または特定のリソースプロパティに値が割り当てられるかどうかを制御する条件です。例えば、スタックが実稼働用であるかテスト環境用であるかに依存するリソースを、条件付きで作成できます。 ×
Transform サーバーレスアプリケーション (Lambda ベースアプリケーションとも呼ばれます) の場合は、使用する AWS Serverless Application Model (AWS SAM) のバージョンを指定します。変換を指定する場合は、AWS SAM 構文を使用して、テンプレート内のリソースを宣言できます。このモデルでは、使用できる構文と、その処理方法を定義します。 ×
Resources Amazon Elastic Compute Cloud インスタンスAmazon Simple Storage Service バケットなど、スタックリソースとそのプロパティを指定します。テンプレートの Resources と Outputs セクションのリソースを参照できます。
Outputs スタックのプロパティを確認すると返される値について説明します。たとえば、S3 バケット名の出力を宣言してから、aws cloudformation describe-stacks AWS CLI コマンドを呼び出して名前を表示することができます。 ×

表に示す通り「必須」なのは、Resoucesだけであり、これだけあればひとまず実行できます。

テンプレートファイル作成

ローカルに新しいYamlファイルを作成して、最低限の記述を記載します。今回は、cf-tutorial.yaml という名前でファイルを作成し、以下のとおり記述しました。

AWSTemplateFormatVersion: 2010-09-09

Resources: 

なお記述にあたっては、VsCodeを使用しています。Extentionsの、Cloud Formationを使用すると、リソース名から最低限のプロパティを自動出力してくれるので簡単に記述できます。

ただ、このExtentionは更新が止まっているせいか、すべてのリソースを出力してくれるわけではないようです。今回のケースでもNAT Gatewayは自分で執筆しました。

今回構築するAWSインフラ

一般的なEC2構成をCloudFormationで構築することを目指していきます。下図を参照。

  • リージョン内に、Subnetを二つ持つVPCを構築する
  • Subnetの一つはPublicとする。Internet Gatewayを介して外部からアクセスできるようにする。
  • もう一つのSubnetはPrivateとする。外部からの接続は禁止し、Public Subnetからしかアクセスできないこと。ただし、Private Subnet側から外部へはアクセスできること(NAT Gatewayを経由)

今回は上記のうち、EC2以外のネットルート(VPC、Subnet、IGW、NAT)までとし、EC2は次回に送ります。

VPC

最初にVPCを構成します。ここで、テンプレートファイルに対する基礎的な記述方法を学んでおきます。

AWSTemplateFormatVersion: 2010-09-09

Resources: 
# VPC
  cftVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/21
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: cf-tutorial-vpc
  • 冒頭「cftVpc」はテンプレート内のリソースIdで、任意につけられます。冒頭小文字のキャメルケースで記載するのが一般的なようで、名称は英数字のみ可能です(ハイフン不可)
  • Typeには、作成するリソースを指定します。設定できる内容は公式ドキュメントから選択します。
    AWS リソースおよびプロパティタイプのリファレンス
  • Propertiesには、リソースに応じた各種の設定内容を記述します。ここではVPCIPアドレス(CIDR)と、DNSサポートの有無、タグを設定しています。
  • 余談ですが、Tagsに設定するキー名は大文字小文字の区分けがされます。例えばNameタグをnameとしてしまうと、AWSマネジメントコンソールには何も表示されませんのでご注意ください。

このように、リソースに応じて設定するプロパティが異なるため、それぞれの設定を理解していく必要があります。

AWS CLIからCloudFormationでデプロイする

CloudFormationをうまく行うコツは、小さい単位でデプロイをおこなうことだそうです。一気に実行しようとすると、エラー時に原因を切り分けるのに苦労したり、トライ&エラーに時間がかかったりするためです。

実際、今回のような小さなものでもすべて記載してデプロイするのに、1回5分程度かかるため、細かなNGが重なると効率が悪かったりします。

デプロイは次のコマンドで実行します。

> aws cloudformation deploy --template-file ./cf.yaml --stack-name cf-tutorial
  • --template-file オプションの後ろには、テンプレートファイルを指定します。
  • --stack-name オプションの後ろには、スタックの名称(任意)を指定します。

最低限、これだけあれば実行可能ですが、詳細なオプションとして指定できるものは、こちらの公式ドキュメントを参照ください。

実行が正常終了すると、以下のように結果が表示されます。Successfully~と出ていれば成功です(エラー時は An error occurred~のように出ます)

> aws cloudformation deploy --template-file ./cf.yaml --stack-name cf-tutorial

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - cf-tutorial

AWSマネージメントコンソールでCloudFormationを見ると、当該スタック(cf-tutorial)に対して「CREATE_COMPLETE」が表示されていることが分かります。

同じく、AWSマネージメントコンソールでVPCを参照すると、上記のテンプレートファイルで指定したVPCが作成されていました。

以上で確認は終了です。以降で継続してリソースの追加をしていきます。

補足. デプロイがエラーになったら、リトライ前にDELETE STACKすること

CloudFormationのデプロイが失敗した後、再度、修正したテンプレートファイルでdeployをしようとすると、以下のようなエラーとなります。

> aws cloudformation deploy --template-file ./cf.yaml --stack-name cf-tutorial

An error occurred (ValidationError) when calling the CreateChangeSet operation: Stack:arn:aws:cloudformation:ap-northeast-1:922813957008:stack/cf-tutorial/579b4ab0-d0bf-11ee-809a-0a4a4aa379bb is in ROLLBACK_COMPLETE state and can not be updated.

リトライする前に、以下のコマンドでスタックを削除するようにしてください。

> aws cloudformation delete-stack --stack-name cf-tutorial

Internet Gateway

次にInternet Gatewayを記述します。

# Internet Gateway
  cftIgw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: cf-tutorial-igw

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref cftVpc    # !Ref関数でVPCリソース「cftVpc」を指定する
      InternetGatewayId: !Ref cftIgw    

正確には前半のcftIgwがInternetGatewayで、後半のAttachGateway(VPCGatewayAttachment)はVPCへのアタッチを示す別のリソースです。InternetGatewayのプロパティはほとんどなく、VPCGatewayAttachmentでVPCとInternetGatewayのIdを指定してリンクさせます。

  • !Ref のように「!」から始まる記述は、CloudFormationの組込み関数です。!Ref は指定したパラメータの値を返却します。VpcId: !Ref cftVpcVpcId パラメータに、前述のVPCを設定する、という意味です。

Subnet(Public)

次はPublicのサブネットの定義となります。Subnetに適用するルートテーブルも併せて定義します。

# Subnet (public)  
  cftPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true      # パブリックIPv4アドレスの自動割り当てを有効
      VpcId: !Ref cftVpc
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: cf-tutorial-public-subnet
  
  cftPublicRtb:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref cftVpc
      Tags:
        - Key: Name
          Value: cf-tutorial-public-rtb
  
  cftPublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref cftPublicRtb
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref cftIgw

  cftPublicRtAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref cftPublicSubnet
      RouteTableId: !Ref cftPublicRtb  

ここで重要なのは、3つ目にあるルートリソース(cftPublicRoute)です。DestinationCidrBlock: 0.0.0.0/0GatewayId: !Ref cftIgw が、マネージメントコンソールにおける、次のルートの設定に該当します。

上記で定義したルートをルートテーブル(cftPublicRtb)に紐づけしたら、最後にAWS::EC2::SubnetRouteTableAssociationリソース(cftPublicRtAssoc)で、サブネットに関連付けします。

  • SubnetリソースにあるMapPublicIpOnLaunch: trueは、 AWS マネジメントコンソールの「パブリックIPv4アドレスの自動割り当てを有効にする」(true=はい)に相当しています。

Subnet(Private)

Publicと同様の手順で、Privateサブネットも記述します。

# Subnet (private)  
  cftPrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false    # パブリックIPv4アドレスの自動割り当てを無効
      VpcId: !Ref cftVpc
      CidrBlock: 10.0.2.0/24
      Tags:
        - Key: Name
          Value: cf-tutorial-private-subnet
  
  cftPrivateRtb:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref cftVpc
      Tags:
        - Key: Name
          Value: cf-tutorial-private-rtb
  
  cftPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref cftPrivateRtb
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref cftNatgw    # ここでNAT Gatewayを指定する

  cftPrivateRtAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref cftPrivateSubnet
      RouteTableId: !Ref cftPrivateRtb  

構成自体はPublicとほぼ同じですが、Privateサブネットの通信先はInternet Gatewayではなく、NAT Gatewayとなります。

そこでルートリソース cftPrivateRoute には NatGatewayId プロパティとしてNAT Gatewayリソース!Ref cftNatgw を設定します(この cftNatgw リソースはまだ記述していません。この後登場します)

NAT Gateway

最後に、NAT Gatewayを記述します。NAT Gatewayは外部に通信するためのサービスですが、その実態はパブリックな仮想IPアドレスなので、AWSのElasticIPでパブリックIPを生成して割り当てします。

# NAT Gateway  
  cftNatgw:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt cftNatgwEip.AllocationId   # 下のElasticIPを割当てする
      SubnetId: !Ref cftPublicSubnet     # NATGateway自体はPublic Subnetに配置する
      Tags:
        - Key: Name
          Value: cf-tutorial-natgw

  cftNatgwEip:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

前後しますが、Privateサブネットからの送信先に、上記で作成したNAT Gatewayを設定することで、Privateサブネットからの通信を外部に振り向けることができるようになります。

テンプレートファイル全文

ここまでの記述内容をまとめると次のようになります。

AWSTemplateFormatVersion: 2010-09-09

Resources: 
# VPC
  cftVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/21
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: cf-tutorial-vpc
  
# Internet Gateway
  cftIgw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: cf-tutorial-igw
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref cftVpc
      InternetGatewayId: !Ref cftIgw

# Subnet (public)  
  cftPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true
      VpcId: !Ref cftVpc
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: cf-tutorial-public-subnet
  
  cftPublicRtb:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref cftVpc
      Tags:
        - Key: Name
          Value: cf-tutorial-public-rtb
  
  cftPublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref cftPublicRtb
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref cftIgw

  cftPublicRtAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref cftPublicSubnet
      RouteTableId: !Ref cftPublicRtb  

# Subnet (private)  
  cftPrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false
      VpcId: !Ref cftVpc
      CidrBlock: 10.0.2.0/24
      Tags:
        - Key: Name
          Value: cf-tutorial-private-subnet
  
  cftPrivateRtb:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref cftVpc
      Tags:
        - Key: Name
          Value: cf-tutorial-private-rtb
  
  cftPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref cftPrivateRtb
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref cftNatgw

  cftPrivateRtAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref cftPrivateSubnet
      RouteTableId: !Ref cftPrivateRtb  

# NAT Gateway  
  cftNatgw:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt cftNatgwEip.AllocationId
      SubnetId: !Ref cftPublicSubnet
      Tags:
        - Key: Name
          Value: cf-tutorial-natgw

  cftNatgwEip:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

デプロイを実施

上記のテンプレートファイル(cf.yaml)を、AWS環境に対してデプロイします。

デプロイは次のコマンドで実行します。

> aws cloudformation deploy --template-file ./cf.yaml --stack-name cf-tutorial

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - cf-tutorial

CloudFormationが正常終了すれば、作業終了です。マネージメントコンソールから、各リソースを開いて、内容がテンプレートファイルのとおり作られているかチェックしてみましょう。

エラー時の調査方法

Deploy後、エラーが帰った場合は、CloudFormation画面でスタックを調べます。

「イベント」タブを選択し、画面上部にある「根本原因を検出」ボタンを押すと、直接的なエラー原因にジャンプしますので、内容を確認して対応を行っていきます。

ハマったところ

今回の作業中、実際にエラーになった内容を例に、解決方法を示します。

  1. 記述ミス

    • エラー内容

        Resource handler returned message: "Value (ap-northeast-1) for parameter 
        availabilityZone is invalid. Subnets can currently only be created in the following
        availability zones: ap-northeast-1a, ap-northeast-1c, ap-northeast-1d. 
        (Service: Ec2, Status Code: 400, Request ID: 6edca7c9-57c2-44d5-9b8e-6c2788239eb9)"
        (RequestToken: 88105dee-6a98-d76e-c820-5d3eb0d15dcc, HandlerErrorCode: InvalidRequest)
      
        <日本語訳>
        リソース ハンドラーからメッセージが返されました: "パラメーター availabilityZone の値
        (ap-northeast-1) が無効です。サブネットは現在、ap-northeast-1a、ap-northeast-1c、
         ap-northeast-1d のアベイラビリティーゾーンでのみ作成できます。
        (サービス:ec2、ステータスコード:400、リクエストID:6edca7c9-57c2-44d5-9b8e-6c2788239eb9)」
        (RequestToken:88105dee-6a98-d76e-c820-5d3eb0d15dcc、HandlerErrorCode:InvalidRequest)
      
    • 原因:サブネットのプロパティにあるAZの指定を間違えた(--;)

      • 正 : AvailabilityZone: ap-northeast-1a
      • 誤 : AvailabilityZone: ap-northeast-1
  2. 設定内容の誤り

    • エラー内容

        Resource handler returned message: "The routeTable ID 'rtb-0cd66f4d6d462c986|0.0.0.0/0'
        does not exist (Service: Ec2, Status Code: 400, Request ID: 693a27c9-0a5e-47a3-8227-f44493db069d)"
        (RequestToken: 191fd476-0e73-194b-3a1a-fcc398d6af26, HandlerErrorCode: NotFound)
      
        <日本語訳>
        リソース ハンドラーがメッセージを返しました: "routeTable ID 'rtb-0cd66f4d6d462c986|0.0.0.0/0' が
        存在しません (サービス: ec2、状態コード: 400、要求 ID: 693a27c9-0a5e-47a3-8227-f44493db069d)" (RequestToken: 191fd476-0e73-194b-3a1a-fcc398d6af26、
        HandlerErrorCode: NotFound)
      
    • 原因:SubnetRouteTableAssociationRouteTableId 属性にRouteのIdを指定していた

        cftPublicRtb:
          Type: AWS::EC2::RouteTable
          Properties:
            VpcId: !Ref cftVpc
            Tags:
              - Key: name
               Value: cf-tutorial-public-rtb
      
        cftPublicRoute:
          Type: AWS::EC2::Route
          Properties:
            RouteTableId: !Ref cftPublicRtb
            DestinationCidrBlock: 0.0.0.0/0
            GatewayId: !Ref cftIgw
      
        cftPublicRtAssoc:
          Type: AWS::EC2::SubnetRouteTableAssociation
          Properties:
            SubnetId: !Ref cftPublicSubnet
            RouteTableId: !Ref cftPublicRoute   # ← cftPublicRtb が正解
      
  3. 設定誤り(処理が終わらない)

まとめ

実際に操作してみたところ、思った以上に簡単に実装できることが分かりました。特にVisual Studio Codeスニペットのおかげで、ゼロから調べる必要がないのは助かります。

今回作成した内容は以下のGitHubにもアップしておきます。

github.com

次回は作成したVPCにEC2を構築します。EC2リソースの構築のほか、セキュリティグループの設定内容や、EC2のログイン鍵の管理などをどのように行うか、といった疑問も出ると思うので、そのあたりの解消方法を学びたいと思います。

オンラインツール「EasyEDA」で基盤設計とPCB発注をやってみる

先日開発したポータブル式サーモグラフィを、基盤として再開発してみようと思います。

camelrush.hatenablog.com

前回まで

前回開発した時には、手元にあったユニバーサル基盤に、エナメル線で配線をしてはんだ付けをしていました。

ただ、あまりにも汚いし、何かのはずみで断線しそうです。

なので、以前からやってみたかった基盤設計に挑戦してみました。

事前情報、ソフトウェア「EasyEDA」

基盤設計には、これまでデスクトップアプリのフリーソフト「Kicad」というのが有名だと思っていたのですが、以下のYoutube記事で、Kicadよりも簡単にできる、という照会があったのでこちらを使うことにしました。

www.youtube.com

Webアプリケーションであり、オンラインで作業できるのでデータ保存なども楽に行えます。

設計開始ぃぃん

プロジェクト作成

  • 右上の「Register」から、サインアップを行います。

  • ログイン後、[Products]-[Standard Online Editor]を選択します。

  • 下の画面が表示されるので、「新しいプロジェクト」を選択します。

回路図の作成

最初に、回路図を作成します。

  • 回路図の画面が表示されるので、左側の「Common Library」または「Library」から、任意のパーツを選択して配置していきます。

  • パーツの選択は左下のような画面で行います。ダイアログの右側に回路図や、実物の絵が表示されるので、内容を確認しながら配置していきます。

    • 見つからない場合は「User Contributed」を選択してください。世界中のユーザが作成したパーツから探してくれるので、見つかることがあります。
  • これを繰り返して、下のとおり必要なパーツをひたすら配置していきます。

  • 次に「ツールバー」左上にある「Wire」をクリックして、回路上で配線をしていきます。

    • 図内のラベルはダブルクリックすることで書き換えることができます。

    • パーツを選んで「R」を押すと、回転(Rotation)します。

    • 使用しない端子には、ツールバーの「No Connect Flag」(×)を設定しましょう。しないと、後でエラーになります。

    • 端子同士をうまくつなげない場合は、描画間隔を調整します。右クリックメニューから「スナップサイズ」を変更し、最低の1とすることで、細かいサイズ調整ができます。

    • 基準電圧(GND)は、ツールバーの「Net Flag(GND)」で設定します。

    • 以下のとおり、回路図を作成しました。

  • 最後にメニューバーから「Design」-「Convert to Schematic to PCB」を選択します。この時、回路図のチェックも行ってくれます。エラーがある場合は左側にエラー箇所が表示されますので、訂正していきます。

シルクスクリーン作成

次に実際の部品の配置と、配線を行っていきます。

  • さきほどの「Convert to Schematic to PCB」の選択によって、PCBの作成画面が表示されます。

  • まずは紫のエリア内にパーツが収まるように部品を配置します。

    • 物理的なサイズを意識しながら配置を考えます。一般的なブレッドボードを意識する場合、端子間は 2.54mm です。回路図の時と同じように、右クリックメニューから「グリッドサイズ」を変更し、「100.0mil 2.540mm」を選択しましょう。また、端子はそのうえで移動させるので、「スナップサイズ」は半分の「100.0mil 1.270mm」とします。

    • パーツを選んで「R」を押すと、回転(Rotation)します。

    • 大まかにですが、次のように配置しました。

  • あらかじめひかれている、細い紫線は、回路図上の配線を示しています。これを実際の結線に変えていきます。メニューバーの「Track」を選択して、線を引いていき、すべてのパーツをつないでいきます。

    • 「Layers and Objects」ウィンドウの「トップレイヤ」「ボトムレイヤ」を使い分けます(トップは表面、ボトムは裏面)
      今回のように複雑な配線の場合であっても、異なる配線を交差することはできません。対策として、基盤に穴(ビア)をあけ、裏面をバイパスして、目的の端子に接続します。 下図でいうと、左が表面(赤い線)、右が裏面です。
    • 表/裏配線の操作方法は、マウスで配線を引いている最中に、キーボードで「B(Bottom=裏面)」または「T(Top=表面)」を押します。これにより、直前にクリックしたポイントに「ビア(穴)」が作られます。

    • ボード上にコメントを記載したい場合(今回は「Designed By ... 」で名前を入れました)は、「Layers and Objects」ウィンドウで「トップ(ボトム)シルクレイヤー」を選択し、メニューバーの「テキスト」を貼り付けます。

    • 配線を曲げる場合は直角に曲げず、2段階で斜めに折り返すことが推奨されています(直角にすると、プリントの過程で断線を起こすリスクが高いそうです)

  • 以上を踏まえながら、配線・基盤のコメントを行いました。

  • 出来上がったらメニューバーにある「2D」「3D」を選択してみましょう。プリント基板のイメージが表示されます。

  • 最後に、メニューバーの「Fabrication」-「One-click Order PCB/SMT」を選択します。ここで配線に問題がある場合はエラーが表示されますので、エラー内容に沿って訂正を行っていきます。

ガーバーファイルの作成と発注

  • 前述のシルクスクリーンにエラーがなければ、いよいよ発注に入ります。下の画面が表示されますので、右下の「One-click Order PCB/SMT」をクリックしてください。注文画面に遷移します。

    • 私の時は、なぜかエラーがでて遷移できませんでした。

    • この場合は、先の画面で「Generate Gerber」をクリックしましょう。zipファイルがダウンロードされます。この zipファイルを遷移先の JLCPCBサイトにアップロードして、注文に進みます。

  • JLCPCBサイトに遷移したら、注文内容を選択します。初めての場合は、あまり触らずに注文してよいでしょう。私もよくわかっていませんw

    • 右側に価格が表示されていますので、うっかり高価なオプションを選ばないよう注意しましょう。
    • PCB Qtyは発注枚数です。最低 5枚から選択できます。
    • PCB Colorは基盤の色です。好きな色を選択してください。
  • 作成内容が決まったら、発送方法を選択します。
    最短 1~3営業日(左)、最長 6~8営業日(右)の中から選択します。
    金額に大きく影響しますので、よほど急いでない場合は右(一番下)を選択しましょう。

  • 最後に、支払い方法を選択して(私はクレジットカードを選択しました。JCB不可)、支払い完了となります。


ちなみに今回の発注結果は、下のとおり、計 2.96$(433.8円)となりました。
勉強代にしても安いですよね!

うまくできたかは届いてのお楽しみです!

また後日!

ポータブル式サーモグラフィーの作成

PC修理でショート箇所を見つけるにあたり、発熱箇所がサーモグラフィーで見えると便利だな、と思い、ESP32とユニバーサル基盤もろもろを取り付けて、ポータブル式のカメラを作成しました。

www.youtube.com

仕様ポイント

外観

部品一式

部品 IC名 メーカー 価格
マイコン ESP32-WROOM-32 ACEIRMC ¥1,099
熱感知カメラ AMG8833 Conta ¥4,950
液晶ディスプレイ ILI9431 KKHMF ¥1,999
LDO レギュレータ(to 3.3v) AMS1117-3.3 DC VKLSVAN ¥59
リチウム電池充電器モジュール TP4056(Type-C) Aideepen ¥95
リチウムイオンバッテリ 不明 3.7V 800mAh 不明 (不良Controllerから拝借)

配線

配線ポイント
  • カメラと液晶への電圧は、LDOを経由して3.3Vに降圧した状態で入力する。

  • 3.7VバッテリからESP32(電圧3.6V迄)への入力も、本来降圧して行うべきだが、降圧するとESP32がうまく起動しない(電圧不足?)ため、そのまま入力。

  • 結線ルートは回路図のとおりだが、実際はエナメル線を使って下図のとおりつないでいるため、ぐちゃぐちゃにw

  • (配線図ではわかりませんが)ESP32、カメラ、ディスプレイの3つは、基盤に直付けせず、ピンソケットのうえに差し込むようにする。

制御プログラム

ESP32のプログラムは、今回は手抜きで、AMG8833のスケッチ例をそのまま使いました。

インポートライブラリ

Arduino IDEを起動して、ライブラリの管理から、以下のライブラリを追加でインストールする - Adafruit_AMG88xx_Library - Adafruit_ILI9341

スケッチ

[ファイル]-[スケッチ例]-[Adafruits AMG88xx Library]-[thermal_cam_interpolate]

反省

なんとか実装することができましたが、エナメル線がぐちゃぐちゃで、手直しが難しくなっています。
機会があれば、次はプリント基板の設計で実装したいと思います! また、ESP32の電源まわりはまだ謎が多くて、3.3V降圧だとうまく動かないのもよくわからない…。

【ジャンク修理】一体型PCの修理に挑戦した話

この度、生まれてはじめて、(完全ではないけども)電気部品の修理ができました。

今回は調査の過程を含めて、この体験をアウトプットしたいと思います。

修理対象

修理の対象は、2019年に発表された、富士通の「ESPRIMO FH70/D1」です。

kakaku.com

製品の特長

ディスプレイ一体型PCで、CPUには第8世代のCore i7を搭載しており、6 Core 12 Threadの高性能機となっています。ゲーミングPCにこそなりませんが、業務使用等では十分な性能といえます。なお、テレビ視聴のような機能はありません。価格も、RAM16GB、NVMe 1TBのSSDを搭載すれば、中古相場(ヤフオク)でも 6万円は下らない値が付きます。

ジャンク理由

  • 起動せず。BIOS起動NG。(安定化電源からの入力では反応があるので、通電はしているようなのですが、BIOS起動せず、ファンすら回らない状態でした)

  • 液晶不良。別に用意していた起動品に接続したところ、画面全体に縦線が入り、表示が乱れていました。

  • ネジ不足。前オーナーによるものかもしれませんが、背面のネジが、正規ネジと異なっていました。長さが異なるので、ひょっとしてこれを無理にとりつけたせいで、圧迫されて液晶が壊れたのかもね…。

入手の経緯

  • これと同じ機種をいつものジャンクショップで手に入れました。
  • ショップ購入品が液晶不良だったのですが、何とか安く修理できないかと、メルカリを散策。
  • 「ジャンク、起動せず、液晶は無事」という、この商品を発見(送料込み 12,000円)。ここから液晶をもらって、いつものニコイチで直す作戦に。
  • 到着後に確認した結果、液晶も壊れていることが判明。出品者に問い合わせたところ、発送前は(おそらく大丈夫)だったとのこと。
  • 出品者の話を添えて、メルカリ事務局に「輸送中の事故では?」と通知した結果、輸送保証が適用。
  • この商品自体は無料で手に入れることができた。ただ、液晶不良のため、修理には使えず。
  • 最初の期待の修理には、やむなく正規ルートで液晶パネルを購入(送料込み 16,000円)して対応。パネル交換後、無事販売できた。

・・・ で、この機体があまりましたw

調査開始ぃぃんw

症状は前述したとおり、電源投入してもBIOS起動せず、ファンすら動かない、という状況。電源のLEDすらつかないので、電源口に問題があるのかも、と疑いつつ調査開始。

こういった場合、最低構成だけで電源がつくのかを調べるのが調査の基本。一旦電源を切って、背面を開け、ファンだけを接続して電源を投入しました。

しかし、写真右の状態で電源を入れても、ファンがうんともすんとも言わない…。

ここで、入力に使用していた安定化電源を見て、おかしな点に気づきました。なおこの電源は、出力値 19V、6.350A に設定してあります。

電圧の値が、設定値の19Vから、2.15Vまでさがっていました。正常な動作であれば、電圧は設定値の19V のまま、電流(A)が0.0A~数A の間でリアルタイムに変化するはずです。入力電圧が正しく上がらないので、PCが起動しないということはありそうです。

考えられるのは、PC内部でどこかの結線にショートが発生しており、電圧が上がらない。経験則からだと、具体的な事例は次のようなものがありました。

  • 内部接続している機器(グラボやメモリ、光学ドライブ等)が故障しており、電源を短絡(ショート)させている。

  • 端子(USB等)の内部が物理不良で接触してしまっていて、内部接点ができ、同じくショートしている。


過去に、USB端子の問題で起動しないノートパソコンを見たことがあります。

   ↓

  

端子同士が接触しており、そこが短絡してショートさせていたという問題でした。この時はピンセットで端子間を開けてあげることで起動するようになりました。


しかし基盤の端子類を見回した結果、特に問題なく、、、一旦マザーボードを筐体から外してじっくり見まわすことにしました。



うっかり火傷

マザーの全面は下のとおり。左が表、右が裏のようです。

さて。こんな時のために買っておいた、秘蔵のマイクロ顕微鏡。

拡大した画面をPCに映して、基盤全面を調べていきます。

何があるかな…。 

…。

…。

…なにか匂うな…焦げ臭いような。

…。

アチっっっ!

と思ってみてみると、マザーの一部が強く発熱していました。うっかり電源を入れたまま調べていたみたいです。

発熱箇所は裏面にある写真の回路。某Youtuberさんの修理動画では、発熱があった周辺のチップコンデンサが怪しいと聞いてたなぁ…。

ということで顕微鏡を近づけてみていくと…。

なんだかこの3つのチップコンデンサ…。一番下、おかしくないだろうか…。角度を変えてチェックすると…。

割れてる!?明らかにおかしい感じが…。

マルチテスタ―を導通モードにして、コンデンサの両端をつまんでみると、予想どおり抵抗が0近くになっていて、ショートしていました。ついでにまわりのコンデンサを調べると、いたるところのチップコンデンサがショート状態に…。

おそらく、ここが短絡しているせいで、となりの回路に大電流が流れて発熱していた!?さらに、このコンデンサが電圧を堰き止めないので、電源電圧が下がってしまっていたのでは!?と推測。影響範囲は見えないけど、周辺のコンデンサは、ここのあおりで両端が接触したように検出されてしまっているのかもしれず。。。

まずは絵的にも明らかに「割れ」ている、コイツをターゲットに修理を進めることにしました。



修理開始ぃぃ~ん

(撮影環境が整っていれば動画にしたかったんだけど…。俯瞰カメラがなく、静止記事になってしまったのが悔やまれます…)

修理といっても、このコンデンサの換えを持っているわけではないので、ひとまずマザーからとりはずして電流の導通を遮断するところまで進めることにします。

といっても、このサイズのハンダはうまくいった試しがないので、はんだの基本を再度Youtubeで調べて、故障覚悟でトライ。

www.youtube.com

動画に従ってはんだごてを「D形」(マイナスドライバのようなコテ)に付け替えて、いざ作業開始。

基盤部品を取り外す場合は、いきなり過熱しても既成のハンダは取れないので、最初はあえて追加ハンダを盛って、その後に加熱すれば簡単に取り外せるとのこと。

・・・せっせせっせ・・・。

(その後、ハンダもりもり載せて、下のとおり、なんとか取り外せました)

ぶれぶれになっててすみません。左がコンデンサを切り離したマザーボード、右がコンデンサです。この後、エタノールキムワイプで表面をふき取り、ハンダのヤニを洗浄しました。

換えの部品がないので、ひとまずこれにて処置終了。画像を取り忘れましたが、この後、ショートしていた周辺のチップコンデンサにテスターを充てて再度チェックすると、ショートが解消されていました!これは希望ありか!?



動作チェック

仮組の状態でCPUファンをつなぎ、安定化電源に接続。

ドキドキしながら安定化電源のスイッチをON!すると…。

動いた!!動いたぞー!!!!!

ファンが全力で回転しはじめました。安定化電源を見ると、こちらも正常に電圧出力ができていることを確認。

背面パネルの接続等を行って組みなおした結果がこれです。

↓(液晶がまだ故障中なのはご愛敬で。後で通販で買って入れ替える予定です)

ついにやりました!!!(涙)

人生で初めて、ハンダで電子部品の修理に成功しました!いや、まだコンデンサ入れ替えできてないから、途中だけども(笑)。

今後の目指すところ

いつかはストリートジャンカー協会に入会できるよう、今後とも精進していきたいと思います!

【ノートPC修理】LIFEBOOK AH53/M AH42/T 分解・液晶ディスプレイ交換

今年の5月から、中古PCの修理・せどり販売を始めました。

具体的には、ジャンクショップからPCを購入し、購入したPCのオーバーホール(内部清掃、グリス再塗布、電池交換)、SSD換装・メモリ増設、Windowsセットアップなどを行って、メルカリやヤフオクで販売する流れです。たまに、ノートPCの液晶ディスプレイ・キーボードの差し替えといった交換作業も行ってます。

ジャンク系Youtuber(お〇おじ様、熊〇郎様)のような、通電しないPCの修理などもやってみたいのですが、まだまだ実力不足でそこまで手が出せてないのが現状です。

今回は、液晶破損しているノートPCを購入し、販売するまでの実作業をアウトプットしていきたいと思います。

故障ノートPCの修理(ニコイチ作戦)

今回購入したPCはこちらです。

富士通 LIFEBOOK AH53/M。写真にあるとおり、液晶割れが生じています。2014年のモデルではありますが、基本スペックは割と高く、4コア、8スレッドのCore i7 4702MQを搭載しているため、捨て置くには惜しいとおもい、購入しました。

液晶ディスプレイを直接修理するのは不可能なため、このような場合は「液晶の交換」が必要となります。作業自体は慣れればそれほど難しくありませんが、正規で交換品を購入しようとすると、この世代のものでも8,000円~10,000円ほどかかります。

そこで同じモデルのディスプレイを搭載した、安価なジャンクPCを購入してそこから剥ぎ取り、こちらに移しかえるといった方法を取りました。

2つの商品のパーツを集めて1つの商品に仕上げることを「ニコイチ」(2個を1個に)と呼んだりします。

ニコイチPCを購入

下に、今回の修理に使用するPCを並べました。

左が修理対象のPC(Core i7、液晶破損、¥5,940)、右が部品取り用PC(Celeron、液晶OK、¥1.980)です。 二つ合わせて購入したところ、価格は¥7,920となりました。

修理開始ぃぃ~ん

まず最初に、部品取り用PC(以下、「ドナー」とします)から、正常な液晶ディスプレイを取り外します。

ドナーPC分解

まずは背面から、バッテリを取り外します。バッテリをつけていると、マザーボードに通電したままの状態となるため、作業中に思わぬところでショートを起こしてしまう危険性があります。

以前、誤って端子間に作業用のネジを落としてしまい、PCを1台おシャカにしたことがありました(T-T

改めて、ディスプレイを取り外しにかかります。このモデルでは、画面の四隅にプラスチックの四角いカバーがあるのですが、その裏面に隠しネジがあり、ディスプレイが固定されています。

ピンセットでカバーを外して、ドライバーでネジを取り外します。

四隅すべてのネジを外したら、液晶とカバの隙間に薄いヘラを差し込み、カバーを外していきます。プラスチックのツメでかみ合っている状態なので、ここは慎重に行わないとツメが折れて修復できなくなります。

すべてのツメをはがすことができたら、外枠を取り外します。

下のようなフレームが見えたら、液晶を固定している四隅のネジも外します。

ネジを外したら、取り外せるように液晶だけを手前に倒します。この時、赤枠のケーブルは接続されたままになっているので、強引に外れてしまわないよう、注意します。

上記の赤枠のケーブルを外します。最初に、保護されているシールをピンセットなどでゆっくりはがします。その後、水平方向にゆっくりと引き抜けば、簡単にケーブルが外れます。ケーブルを抑えている金属が折れたりしないよう、注意して作業します。

無事、ドナーPCから正常な液晶を取り外すことに成功しました。

背面の型番を確認すると、LG社の「LP156WGB(TL)(A1)」という型番ということが分かります。試しにこの型番をネットで確認してみると、Yahooショップで「6,244円」で売られていました。送料を含めると「7,744円」となります。

今回のドナーPCは 1,980円 で購入したので、正規に購入するよりも安上がりであることが分かります。

修理PCへの液晶入れ替え

こちらも分解の流れは同じです。

ドナーPCのように、四隅のカバーを外し、ネジを取り除いて液晶を外します。

ここで気付いたのですが、液晶の型が違っていました(逆さまですみません)。ドナーPCが「LP156WGB(TL)(A1)」であるのに対して、こちらは「LTN156AT30-H」と書かれています。

後述しますが、異なる型の液晶がつながるかどうかは保証がありませんので、博打です。ピン数も同じなので、今回はこのままつないでみることにします。

ここで一度四隅のネジだけを仮止めして、PCの電源を入れてみます。

やりました! 無事、修理PC側でドナーの液晶が表示されました。修理成功のようです。

あとは液晶のフレームの取り付け、ネジの締め直し、ネジ隠しをして修理完了です。

ついでに背面を開けてHDDをSSDに換装し、Windowsをセットアップしました。

また、液晶には手垢がついていましたので、精製水を使ってきれいに清掃。液晶表面は、エタノールなどを使用しないこと。

ドナーPCの再利用

さて、液晶を取り外したドナーPCが残りましたが、ここからも取れるものは取っていきます。1,980円で購入したドナーですが、液晶と同じように、パーツ別に分けると単体で値を出せるほど、良いものが埋まっている場合があります。

バッテリ

残量があれば、同じ種類のPCに使いまわすことができます。今回、修理用のノートのバッテリが空の状態であったのに対して、ドナーのバッテリに残量があったため、こちらも使わせてもらうことにしました(採用)

光学ドライブ

光学ドライブは、Bluray対応か否かで製品価値が大きく違います。Bluray搭載であればネットで2,000円強で売却できます(ドナー自体が 2,000円 弱なので、これだけで元が取れることもあります)。今回のドナーは残念ながらDVDドライブでした(不採用)

無線カード

無線カードが搭載されている場合はその機能を確認します。カードにはWifiのみに対応できるものと、Bluetoothを併用できるものがあります。またWifiの通信規格として、802.11acに対応したものであれば、高速な5GHz通信ができるため、これも価値があります(遅いPCのカードと入れ替えてもよいです)。今回はまさに、802.11ac対応、かつ Bluetooth対応のカードでした。修理用PCにも同じものがあるため、取りかえる必要はありませんが、外して保管しておくことにします。

ちなみに、メルカリでは 1,000円で売られていました。

メモリ

搭載メモリも抜き取ります。こちらは容量次第で、他のPCに増設することも可能です。今回は、DDR3 低電圧対応の4GBが1枚あるだけでした。2枚で 8GBのノートPCを構成することはできますが、昨今Windowsで8GBのノートPCはあまり人気が出ないので、再利用は難しいかな、と思われます。メルカリ等でも価格は500円程度なので、あまり価値はなさそうですね。

キーボード

意外と大事なのがこのキーボードです。ノートPCはデスクトップと違って簡単に交換が利かないため、正常に入力できるキーボードは 交換用部品として4,000円弱で取引されています。



上記の他に、世代によってはCPUが取り外せるものがあり、CPUも人気です。また、特殊な例ですが、今回のようにマザーボード自体が生きているのであれば、通電不可となったPCを生き返らせることもできますので、需要があります。外枠のパームレストや天板なども、ジャンカーによっては購入したがる方もいますね。

以上のように、ドナーとして、1,980円で購入したPCでも、各種部品別に分けていくと 数千円の価値があります。すぐに売れるものではありませんが、ヤフオク等で気長にまてば、ジャンカーの皆さんが購入してくれることがあります。

液晶交換の注意点

本来は型番が分かった時点で、その型番を仕入れて入れ替えるのが修理の王道と思いますが、今回は安く仕上げるためにドナーPCからの入れ替えを行いました。

このようにニコイチで異なるPC間の液晶ディスプレイを交換する場合、当然、液晶は互換性があるものでなくてはいけません。今回はほぼ同時期に発表された、同じメーカーのノートPCであったため、「同じ液晶が使われているだろう」という推測で着手しました(実際、液晶の型番は違いましたね)偶然規格が一致していたのかうまく入れ替えができましたが、液晶同士の互換性に関しては、ネット上にもほとんど情報がないため、同じものがハマるかどうかはほとんど博打です。

各ノートPCと使用されている液晶の型番の対応について、データベースなどがあれば非常に助かるのですが…。

IoTでカイワレ大根プランターの育成を監視してみよう

久しぶりのIoTネタ。土壌湿度計を使用したプランターの育成状況を可視化してました。 今回は自動給水は行わず、あくまでデータの可視化と、水分不足時のLINE通知までとしています。

詳細な構築手順や、実行ソースは、以下のGithubを参照ください。

github.com

システム概要

  • プランターの水分量と、周囲の気温、湿度をセンサーで10分おきに収集し、Webページからグラフデータとして可視化する。
  • プランターの水分量が規定値の割合を下回った場合は、あらかじめ登録したLINEのチャンネルに、給水を求める通知を行う。

プランタとセンサ

ESP32に土壌湿度計と、温度・湿度計を接続し、ESP32からWifiネットワーク経由で、AWSにデータを送るように実装しました。

上記をエッジデバイスとした、システム全体の構成イメージは次のとおりです。

Webページでの可視化

AWS S3に静的Web(HTML/JS)を配置し、そこからAPI Gateway/Lambda経由で、格納したデータを取得し、Chart.jsでブラウザにグラフを表示させます。

LINE通知

データ登録をトリガとしてLambdaを起動させ、水分量が閾値を下回った場合は、LINEに通知を飛ばすよう、設定しておきます。これにより、水分不足をLINE通知で受けることができるようになります。

まとめ

現時点で約半月ほど動かしてましたが、動作は安定しているようです。ただ、すでに秋に入って気温が低下したことや、植っているカイワレがすでに寿命であることから、あまり水分が減らず、通知が来ることがほぼないですが・・・。

冬の間に、自動給水や、電池駆動の方法を検討し、来年はプチトマトなどを育ててみたいと考えています。

Oracle PL/SQLからMySQLへの移行苦労話

Oracleをバックエンドとする老朽化システムのマイグレーションにおいて、ライセンス料の課題から、無償のMySQLRDBをダウンサイジングする、というのはよくある話です。しかし、テーブルデータだけではなく、サーバサイドに実装されているOracle PL/SQLがあった場合、これらもMySQLに移植し、クライアントの移行コストを抑える、という選択肢が出てきます。

ところが、OraclePL/SQLMySQLのサーバ処理(Procedure、Function)は、仕様的にいくつかの差があるため、実際に移行しようとすると、なかなか骨が折れます。今回は、実際に移行してみた経験から、大変だった苦労話をいくつかピックアップしてご紹介します。

パラメータ付カーソルが使えない

PL/SQLで複数行のデータ処理を行う場合、よくカーソルを使用します。MySQLにもカーソルは存在するのですが、こちらでは引数を指定する(Oracleのパラメータ付カーソル)ことができません。

このため、MySQLでカーソルの抽出条件を動的に変更するためには、予めカーソルが参照数ための広域変数を定義し、カーソルをOPENする前に、毎回変数に値をセットする必要があります。

<Oracleの場合>

  -- カーソル定義
  CURSOR cur(pi_num1 INT, pi_num2 INT) IS -- パラメタ付きでカーソルを定義する
    SELECT fieldA, filedB
      FROM table1
     WHERE field1 = pi_num1
       AND field2 = pi_num2;

BEGIN
  -- 処理部
  OPEN cur(11, 22); CLOSE cur;   -- 引数として条件を指定できる
  OPEN cur(33, 44); CLOSE cur;   
  OPEN cur(55, 66); CLOSE cur;   

<MySQLの場合>

BEGIN
  -- 変数定義
  DECLARE pi_num1    INT;      -- 条件に使用する変数を定義する
  DECLARE pi_num2    INT;

  -- カーソル定義
  DECLARE cur CURSOR FOR 
    SELECT fieldA, filedB
      FROM table1
     WHERE field1 = pi_num1    -- 定義した変数をSQLに渡す
       AND field2 = pi_num2;

  -- 処理部
  SET pi_num1 = 11;            -- カーソルOPEN前に、毎回変数をセットする
  SET pi_num2 = 22;
  OPEN cur; CLOSE cur;

  SET pi_num1 = 33;
  SET pi_num2 = 44;
  OPEN cur; CLOSE cur;

  SET pi_num1 = 55;
  SET pi_num2 = 66;
  OPEN cur; CLOSE cur;

パラメタによって、同一の問合せをカーソルで再利用できるメリットが半減してしまい、コードも冗長となります。

%ROWTYPEが使用できない

Oracleでは変数の一種として、テーブルの行を模した「%ROWTYPE」が使用できます。これは、Procedureの引数の返り値のI/Oや、先のカーソルのFETCH結果の受け取りで使用されます。

しかし、MySQLにはこの「%ROWTYPE」がありません。テーブルの行そのものを変数に格納することができないため、必要となるフィールド毎に個別に変数を定義して、それぞれ代入・取り出し操作を行う必要があります。

<Oracleの場合>

  -- カーソル定義
  CURSOR cur IS
    SELECT *
      FROM table1;

  rec    table1%ROWTYPE;   -- %ROWTYPEで変数recを宣言

BEGIN
  -- 処理部
  OPEN cur;
  FETCH cur INTO rec;                               -- recに、table1の1行文が取り出される
  result := rec.fieldA || rec.fieldB || rec.fieldC; -- rec.[列名]で、任意の値が取得できる
  RETURN result;

<MySQLの場合>

BEGIN

  -- 変数定義
  DECLARE rec_fieldA    VARCHAR(10);   -- フィールド毎の格納変数を宣言
  DECLARE rec_fieldB    VARCHAR(10);
  DECLARE rec_fieldC    VARCHAR(10);

  DECLARE cur CURSOR FOR 
    SELECT fieldA, filedB, fieldC      -- FETCHで受け取るフィールドを個別に指定 ※
      FROM table1;

  OPEN cur; CLOSE cur;
  FETCH cur INTO rec_fieldA, rec_fieldB, rec_fieldC;  -- SELECT句と同じ並びで変数を指定
  SET result = CONCAT(rec.fieldA, rec.fieldB, rec.fieldC);

SQL部分を、Oracleのように SELECT * FROM ~とすることも可能ですが、その場合は、FETCH INTO に対象テーブルのすべてのフィールドを列挙しないといけません。フィールド数不一致でエラーとなります。

変数宣言、SQLのSELECT句、FETCHのINTO先、いずれにも、カーソルから取得するフィールドを列挙する必要があるため、コードが非常に大きくなります。

このROWTYPEと、前述のパラメタ付カーソルが使用できない件によって、PL/SQLのカーソルをMySQLに移植すると、次のように非常に長いコードになります。

<MySQLのカーソル使用例>

BEGIN
  -- 変数宣言
  DECLARE ci_fieldA  AS INT;
  DECLARE ci_fieldB  AS INT;
  DECLARE ci_fieldC  AS INT;
  DECLARE ci_fieldD  AS INT;
  DECLARE ci_fieldE  AS INT;
  -- カーソルにINPUTしたい変数をすべて定義

  DECLARE co_field1  AS INT;
  DECLARE co_field2  AS INT;
  DECLARE co_field3  AS INT;
  DECLARE co_field4  AS INT;
  DECLARE co_field5  AS INT;
  -- カーソルからOUTPUTしたいフィールド分すべて定義

   -- カーソル宣言
  DECLARE cur CURSOR FOR
  SELECT fiedl1, field2, field3, field4, field5 ...  -- 取得したいフィールドはすべて列挙
    FROM table1
   WHERE fieldA = ci_fieldA  
     AND fieldB = ci_fieldB  
     AND fieldC = ci_fieldC;  
     AND fieldD = ci_fieldD;  
     AND fieldE = ci_fieldE;  

   -- 処理部
  SET ci_fieldA = 11;
  SET ci_fieldB = 22;
  SET ci_fieldC = 33;
  SET ci_fieldD = 44;
  SET ci_fieldE = 55;
  -- カーソルにINPUTしたい変数すべてに値を設定

  OPEN cur;
  FETCH cur INTO co_field1, co_field2, 
        co_field3, co_field4, co_field5 .... -- カーソルから受け取る変数をすべて列挙
  CLOSE cur;

カーソル属性 %NOTFOUND等が使用できない。

次もカーソルに関する問題です。カーソルをループ内でFETCHするとき、すべての行を読み取ったかを判断するために、Oracleでは「カーソル名%NOTFOUND」属性によって判断します。MySQLでは、この「読み切った」ということをハンドラで判定します。

<Oracleの場合>

BEGIN
  -- カーソル定義
  CURSOR cur IS
    ... 
IS
  OPEN cur;
  LOOP
    FECH cur INTO rec;
    EXIT WHEN cur%NOTFOUND;  -- %NOTFOUNDを参照してループを抜ける

    ... (ループごとの処理)

  END LOOP;
  CLOSE cur;

<MySQLの場合>

BEGIN

  -- 変数定義
  DECLARE done INT DEFAULT FALSE;  -- ループ終了判定変数(初期値はFALSE)

  -- カーソル定義
  DECLARE cur CURSOR FOR
    ... 

  -- NOTFOUNDハンドラ 定義
  DECLARE CONTINUE HANDLER 
    FOR NOT FOUND SET done = TRUE;  -- NOTFOUND時にdoneにTRUEを設定

  OPEN cur;
  loop1 : LOOP
    FECH cur INTO rec;

    IF done THEN    -- doneがTRUEになったら処理終了
        LEAVE loop1;
    END IF;

    ... (ループごとの処理)

  END LOOP loop1;
  CLOSE cur;

こちらは先ほどのような機能不足ではなく、OracleMySQLの「仕様(お作法)の違い」ではありますが、地味に書き換えが面倒です。カーソルはPL/SQLの主要機能のため数が多く、ひとつのProcedureに複数のカーソルがあった場合には、それぞれを判定するための終了判定変数を作る必要がある(あるいはそれぞれをBEGINで括り、干渉を避ける)など、移行に手間がかかります。

その他

上記以外にも、二つのDBMSには組込み関数の名称の違いや、内部仕様の差異など、多くの違いがあります。

以下に一部をご紹介します。

概要 Oracle MySQL 移行時の注意点
NULL値判定の違い '(空)'もNULLもNULL扱い('' IS NULL = TRUE) '(空)'はNULLではない
('' IS NULL = FALSE)
入力値に応じて複数の条件式に分ける。
  IF (A='') OR (A IS NULL) THEN
これ以外にも、NVLやISNULLといったNULL判定に注意が必要である
DECODEが使えない DECODE(A, 1, 'yes', 'no'); CASE A WHEN 1 THEN 'yes', 'no' END 左記のとおり書き換える
配列データの取り回し VARRAY、連想配列、ネスト表などが使用できる 配列がないため、JSON型のリストを使用する
例 '[ 1, 2, 3 ...]'
データの加工・取出しには、MySQL組込み関数である「JSON関数」を使用する
NULL置き換え方法の違い NVL(abc, 0); IFNULL(abc, 0); 左記のとおり書き換える
文字列連結方法の違い data := a || b || c; SET data = CONCAT(a, b, IFNULL(c, '')); CONCATは一つでもNULLがあると結合結果がNULLとなるため、NULLが入りえる変数にはIFNULLと併用する。
再帰ルーチンの実装 できる できない 別途実装方法を検討

まとめ

今回、実際に移行を行ってみて、OraclePL/SQLの優秀さを改めて知るとともに、MySQLの非力さにうんざりしました。特に、カーソル制御まわりのI/Oがチープすぎで、古のCOBOLのようだと、最初は目を疑いました。引き渡し用の変数が相当数必要になるため、書き換えでミスが生まれそうで怖かったです。

こういったリプレース作業は、いつか、巷の生成AIがさくっとこなしてくれるようになるんでしょうね(遠い目)