mito’s blog

IT技術メインの雑記。思い立ったが吉日。

YAMLの理解が深まったらPlaybookの理解も深まった気がした話

この記事は、Ansible Advent Calendar 2021 22日目のエントリです。
個人の感覚によるものが大きい話です。


はじめに

「Playbookで何をするとかしないとかじゃなくて、YAMLは配列の塊という理解をするほうが先です」と言われた。


YAMLExcelで表す

---
- hosts: r1
  gather_facts: no

  tasks:
    - name: enabled interfaces
      junos_interfaces:
        config:
          - name: ge-0/0/0
            enabled: true
        state: overridden

- hosts: r2
  gather_facts: no

  tasks:
    - name: enabled interfaces
      junos_interfaces:
        config:
          - name: ge-0/0/0
            enabled: true
        state: overridden

f:id:mst-it:20211224154524p:plain

Playbookのhostsも1playの変数であり、Playbook1ファイルの中に、複数のPlayが入る。
なぜなら配列の塊だから。YAML[2][2][10]みたいなイメージ。
各セルのキーと値の状態にするのがAnsible。


感想

「Playbookを実行する」は「記載内容通りに実行する」ものではなく、「記載内容の状態にする」のほうが合うと思った。
試しに、こういう状態にしたいという考えでPlaybookを書いてみたら、妙にすっきりした。

でも、説明する時はインストールする、設定変更すると、わかりやすいから何々を実行すると言ってしまうんだろうなぁ。

Terraformに入門、AWS EC2でインスタンスを立てた

この記事は、Terraform Advent Calendar 2021 23日目のエントリです。


はじめに

Terraformは名前だけ知ってました。なお、Windows端末で立てていきます。


まとめ

Terraformでガワを作って、Ansibleで中身整えるで良さそう。
あと勉強しながらこの記事を書いていましたが、ながら執筆って良いかもしれない。
こういうログのほうが記事に良いなって思うたびにやり直すので。


AzureでVM作成と比較した

なるほど、Terraformってほぼ同じ感じで使いまわせるんだなぁと思いました。

qiita.com


まずは用語や雰囲気をおさえた

まずは、社内でお勧めされていた以下の動画を視聴し、用語と雰囲気を知りました。
https://www.youtube.com/watch?v=h1MDCp7blmg


次にWindows端末で環境構築した

1. Pathが通ってるフォルダ(例えばc:\Windows直下)に、terraform.exeを置きます。
https://www.terraform.io/downloads

2. AWS CLIをインストールします。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-windows.html

公式ドキュメントだと、Chocolateyを利用する方法のようです(本記事では未実施)
https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started


コマンドを打った

まずはバージョンの確認とhelpを見てみます。

C:\Users\ユーザ名>terraform version
Terraform v1.1.0
on windows_amd64

C:\Users\ユーザ名>aws --version
aws-cli/2.4.6 Python/3.8.8 Windows/10 exe/AMD64 prompt/off

C:\Users\ユーザ名>terraform -help
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure

All other commands:
  console       Try Terraform expressions at an interactive command prompt
  fmt           Reformat your configuration in the standard style
  force-unlock  Release a stuck lock on the current workspace
  get           Install or upgrade remote Terraform modules
(略)

Global options (use these before the subcommand, if any):
  -chdir=DIR    Switch to a different working directory before executing the
                given subcommand.
  -help         Show this help output, or the help for a specified subcommand.
  -version      An alias for the "version" subcommand.

いろいろ機能があるんだなぁと思いつつ、Main commandsを実行するため、各種ファイルを作成していきます。


  • 認証情報の作成

「C:\Users\ユーザ名\」配下に「.aws」フォルダを作り、「credentials」ファイルを作成します。
メモ帳で作成、改行コードもCRLFで大丈夫です。
しばらくCMDでコマンド打ってなかったので、lsでもviでもcatでもない、なんだっけと調べるほうが時間かかりました。

[default]
aws_access_key_id=AKI***********
aws_secret_access_key=***********


適当な名前のディレクトリを作って、移動しましょう。

C:\Users\ユーザ名>mkdir study_terraform
C:\Users\ユーザ名>cd study_terraform


  • main.tfの作成

main.tfファイルに、作成するインスタンスのリソース情報を記載します。
リソース情報はなるべく少なくし、デフォルト設定任せにしています。
AMIは好きなものを選んでください。
main.tfも、認証情報同様メモ帳などで作って大丈夫です。

provider "aws" {
    region = "ap-northeast-1"
}

resource "aws_instance" "mito-ec2" {
    ami           = "ami-********"
    instance_type = "t2.micro"
    key_name = "キー名"
    associate_public_ip_address = "true"
    tags = {
        Name = "mito-ec2"
    }
}

output "ec2-ip"{
    value = aws_instance.mito-ec2.public_ip
}


  • terraform initの実行

ワークスペースの初期化や、実行に必要なバイナリファイルをダウンロードします。

C:\Users\ユーザ名\study_terraform>terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v3.69.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

C:\Users\ユーザ名\study_terraform>
C:\Users\ユーザ名\study_terraform>dir
 Volume in drive C is Windows
 Volume Serial Number is ****

 Directory of C:\Users\ユーザ名\study_terraform

2021/12/22  23:07    <DIR>          .
2021/12/22  23:07    <DIR>          ..
2021/12/22  23:07    <DIR>          .terraform
2021/12/22  23:07             1,077 .terraform.lock.hcl
2021/12/22  22:36               358 main.tf
               2 File(s)          1,435 bytes
               3 Dir(s)  53,891,817,472 bytes free

C:\Users\ユーザ名\study_terraform>


  • terraform validateの実行

構文チェックを行います。

C:\Users\ユーザ名\study_terraform>terraform validate
Success! The configuration is valid.


C:\Users\ユーザ名\study_terraform>


  • terraform planの実行

作成されるリソースが確認できます。
なお、作成だけでなく、コードを更新した場合は既存環境との差分やリソースの変更なども確認できます。

C:\Users\ユーザ名\study_terraform>terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.mito-ec2 will be created
  + resource "aws_instance" "mito-ec2" {
      + ami                                  = "ami-********"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = true
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "キー名"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "mito-ec2"
        }
      + tags_all                             = {
          + "Name" = "mito-ec2"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + ec2-ip = (known after apply)

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
you run "terraform apply" now.

C:\Users\ユーザ名\study_terraform>


  • terraform applyの実行

main.tfで設定したリソースを一気に作ります。

C:\Users\ユーザ名\study_terraform>terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.mito-ec2 will be created
  + resource "aws_instance" "mito-ec2" {
      + ami                                  = "ami-********"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = true
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "キー名"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "mito-ec2"
        }
      + tags_all                             = {
          + "Name" = "mito-ec2"
        }
      (略)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + ec2-ip = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.mito-ec2: Creating...
aws_instance.mito-ec2: Still creating... [10s elapsed]
aws_instance.mito-ec2: Still creating... [20s elapsed]
aws_instance.mito-ec2: Still creating... [30s elapsed]
aws_instance.mito-ec2: Creation complete after 36s [id=i-02abe6f0e4497ed52]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

ec2-ip = "52.199.5.247"

C:\Users\ユーザ名\study_terraform>
C:\Users\ユーザ名\study_terraform>dir
 Volume in drive C is Windows
 Volume Serial Number is ****

 Directory of C:\Users\ユーザ名\study_terraform

2021/12/22  23:15    <DIR>          .
2021/12/22  23:15    <DIR>          ..
2021/12/22  23:07    <DIR>          .terraform
2021/12/22  23:07             1,077 .terraform.lock.hcl
2021/12/22  22:36               358 main.tf
2021/12/22  23:15             3,943 terraform.tfstate
               3 File(s)          5,378 bytes
               3 Dir(s)  53,894,037,504 bytes free

C:\Users\ユーザ名\study_terraform>


f:id:mst-it:20211222234419j:plain

ステータスチェック中であり、すぐ起動が完了するわけではないよね。
起動まで待つ仕組みはあるでしょう。

また、tfstateファイルに作成したリソース情報が記載されます。

{
  "version": 4,
  "terraform_version": "1.1.0",
  "serial": 2,
  "lineage": "*********",
  "outputs": {
    "ec2-ip": {
      "value": "52.199.5.247",
      "type": "string"
    }
  },
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "mito-ec2",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "ami-******",
       (略)
        }
      ]
    }
  ]
}


  • terraform destroyの実行

作成した環境を一気に壊します!

C:\Users\ユーザ名\study_terraform>terraform destroy
aws_instance.mito-ec2: Refreshing state... [id=i-02abe6f0e4497ed52]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.mito-ec2 will be destroyed
  - resource "aws_instance" "mito-ec2" {
      - ami                                  = "ami-********" -> null
      - arn                                  = "arn:aws:ec2:ap-northeast-1:*******:instance/i-02abe6f0e4497ed52" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "ap-northeast-1a" -> null
      - cpu_core_count                       = 1 -> null
      - cpu_threads_per_core                 = 1 -> null
      (略)
      - tags                                 = {
          - "Name" = "mito-ec2"
        } -> null
      - tags_all                             = {
          - "Name" = "mito-ec2"
        } -> null
      (略)
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Changes to Outputs:
  - ec2-ip = "52.199.5.247" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.mito-ec2: Destroying... [id=i-02abe6f0e4497ed52]
aws_instance.mito-ec2: Still destroying... [id=i-02abe6f0e4497ed52, 10s elapsed]
aws_instance.mito-ec2: Still destroying... [id=i-02abe6f0e4497ed52, 20s elapsed]
(略)
aws_instance.mito-ec2: Still destroying... [id=i-02abe6f0e4497ed52, 2m20s elapsed]
aws_instance.mito-ec2: Destruction complete after 2m25s

Destroy complete! Resources: 1 destroyed.

C:\Users\キー名\study_terraform>

気持ちいい。。。


問い合わせ窓口・管理としてBacklogを使っているお話

この記事は、Backlog Advent Calendar 2021 の13日目のエントリです。


はじめに

Backlogを社外からの問い合わせ窓口、問い合わせ管理として使っています。
フローは、まず社外の方に起票していただき、対応から解決まで更新し、クローズ連絡を受けて閉じます。

Backlogを問い合わせ管理に使ってよかったなと思うところを上げます。

  • システムが回答期限のチェックをしてくれるので対応が漏れにくい
  • 更新通知をslackに飛ばせるので気づきやすい
  • 最新の回答が一目でわかり、過去の履歴も追いやすい
    • 途中参加の方にも共有しやすい

以上より、社外の方がブロック等されずBacklogに接続できるのであれば、例えばファイルで管理するより圧倒的に運用しやすいです。


社外にBacklogを展開する際の管理しやすい設定

特別な設定ではないのですが、いくつかお勧めの設定があります。


ユーザのチーム作成

会社ごと、所属部署ごとにチームを作ると、管理がしやすいです。また、社内向けでもチームを作っておくと便利です。
チームは、「ユーザの招待」のほか「プロジェクトへの参加」にも関係しています。


ユーザの招待

ユーザを招待するには、「メールアドレス」「所属するチーム」「権限」を指定します。

所属するチームの指定には、注意してください。
デフォルトで自社のチームにチェックが入っているため、社外の方を招待する際には、このチェックを外します。

招待の方法は、以下の2パターンが考えられます。

  • 招待するユーザが少なく、メールアドレスを入手している場合
    • 個々に、Backlog招待の専用リンクを送ります。

f:id:mst-it:20211209122139j:plain


  • 招待するユーザが決まっていない、メールアドレスが分からない場合
    • リンクを知っていれば参加できる招待リンクを、一部の方に送ります。
    • この招待リンクは有効期限が設定でき、無期限にもできます。
    • 無期限にして招待リンクの存在を忘れるよりは、期間を設定しておいて、例えば招待リンクが無効だったと連絡をもらったほうがいいかもしれません。

f:id:mst-it:20211209122158j:plain


プロジェクトへの参加

問い合わせ用のプロジェクトを作成し、任意のチームを追加します。
この設定により、見るべきプロジェクトが見るべきチームに表示されます。
プロジェクトは複数あるでしょうし、これで不用意な共有は避けられます。
社内でもチーム分けしておいたほうが便利だと思った理由は、この設定によるものです。

f:id:mst-it:20211209122431j:plain


備考

見返してみると、自分向けのメモでした。まぁこれで忘れてもヨシ!

Udemy「アジャイル・プロジェクトマネジメント短期集中講座」を受講して分かったこと

この記事は、Udemy Advent Calendar 2021 の13日目のエントリです。

コース

www.udemy.com


はじめに

受講前の私は、アジャイルをインターネット検索した程度の知識であり、アジャイルプロジェクトのマネジメントはしたことありません。
ウォーターフォールでのプロジェクトマネジメントが主です。

受講前にMVPとは何か一読しておくと、一歩目のつまずきは回避できる気がします。
MVP(Minimum Viable Product)とは?実践するメリットと検証方法 | UX MILK


受講の感想

まず、英語の講座になりますが日本語の字幕表示があります。トランスクリプションの表示で、まとめられた字幕が読めますので、 英語が分からなくても何とかなります。

「ユーザーストーリー」「バックログ」「レトロスペクティブ」「スプリント」「スクラムマスター」などの用語説明や アジャイルのよくある誤解とQA、どう進めたらいいのかツールを使っての例があり、流れが理解しやすいです。特にQAは検索して気になってた疑問でしたので、かなり腹落ちしました。
アジャイル開発におけるツールのサンプルや説明資料もダウンロードでき、アフターサポート的なものもあります。
コースが90分と短いので何度も繰り返して視聴しやすく、私は投稿時点で3周目でしょうか。 用語はわかっていても実践となるとアジャイルって難しいです。

なお、アジャイルの体系的な学習をしたことがない方には、特にこのコースをお勧めします。
検索して得た情報はだいたい日本式アジャイルといったようなもので、受講して初めてこの違いに気づけました。
受注者か発注者のどちらかに都合よく解釈されたものが多く、ときにはウォーターフォールで仕様が固まったところから作っていく、よくあるパターンの説明かなとも思いました。
その是非はともかく、違いを理解したり、一部だけアジャイルの要素を取り入れてみるなど工夫していくといいのかなと考えています。

アジャイルの体系的な学習をしたことがない方は、ぜひ受講してみてください。


本コースについて

このコースで学べること


などがあります。
コース登録後に利用できるもの、アフターサポートもあります。
ご興味があれば、ぜひリンク先に飛んでみてください。

また、コースの対象受講者は以下です。

  • アジャイル開発について学びたい人
  • アジャイル開発の経験がない初心者 「ユーザーストーリー」「バックログ」「レトロスペクティブ」「スプリント」「スクラムマスター」「プロダクトオーナー」「MVP」> 「スウィムレーン」「カンバン」等の用語を聞いたことがあるが、具体的に何を示すのか知りたい人
  • 開発者、プロジェクトマネジャー、ビジネスアナリスト、ソリューションアーキテクト、データベースアドミニストレーター


備考

アジャイルで進めて、契約期間の終了間際(納期間際ではない)に要望が出てきたらどうするんだろう、体力で押し切るのかなと思っていたら、IPAに納得のいく答えがありました。


開発対象全体の要件や仕様を確定してから開発を行うウォーターフォール開発とは異なり、アジャイル開発は、そのプロセスの中で、機能の追加・変更や優先順位の変更、先行リリース部分の改善などに柔軟に対応することができる手法です。
そのため、本版は、あらかじめ特定した成果物の完成に対して対価を支払う請負契約ではなく、ベンダ企業が専門家として業務を遂行すること自体に対価を支払う準委任契約を前提としています。

www.ipa.go.jp


IPAの「アジャイル開発の進め方」も読むといいかもしれません。

https://www.ipa.go.jp/files/000081487.pptx


最後に。アジャイルだろうがウォーターフォールだろうが、炎上するときはするよね。

VBAでパラメータ.yamlを作る

この記事は、エーピーコミュニケーションズ Advent Calendar 2021 の7日目のエントリです。

はじめに

Excelは便利だしVBAでもいいじゃない。

やったこと

Excelパラメータシートの一部を、Yamlファイルとして出力します。


このExcelパラメータが、

f:id:mst-it:20211205184025j:plain


匠(VBA)の手により、

f:id:mst-it:20211205184046j:plain
f:id:mst-it:20211205184056j:plain


Yamlに生まれ変わりました!

f:id:mst-it:20211205184113j:plain



VBA

以下がコードです。
Excelパラメータの図と同じ形式のデータを用意する必要があります。

'変数の宣言を強制する
Option Explicit

'グローバル変数(関数をまたいで使用する)
'データの検索範囲
'データ行の始め(Constで宣言した変数は定数となり、書き換えできない)
Const ROW_START As String = 4
'データ行の終わり
Const ROW_END As String = 10
'データ列の始め
Const COLUMN_START As String = "A"
'データ列の終わり
Const COLUMN_END As String = "H"

'YAMLの行数
Const YAML_MAX_COLUMUN As Long = 6

'YAMLを生成するパラメータの行番号
Dim YAML_NUMBER As Long
'YAMLのファイル内容(項目と値の2つをセットとし、YAML_MAX_COLUMUN 行数分を用意する)
Dim YAML_CONTENTS(2, YAML_MAX_COLUMUN) As String


'ボタンクリックで呼び出す関数
Sub create_yaml()

    Dim res_number As Boolean

    'YAMLに起こす番号を選択
    res_number = input_number()

    If res_number = False Then
        MsgBox "ないわー、それはないわー"
        Exit Sub
    End If

    'YAMLの内容を初期化
    Call format_yaml

    'YAMLの内容をセット
    Call set_yaml

    'YAMLを上書き作成
    Call output_yaml

End Sub


'YAMLに起こす番号を選択する関数
'    マクロに設定する関数ではないため、Privateをつけている
'    関数の処理結果(Boolean)を返したいため、subではなくFunctionにしている
Private Function input_number() As Boolean

    'この関数内でエラーが起きても処理を続ける。親関数でエラーハンドリングをしたいため。
    On Error Resume Next

    '数字に型変換して格納する
    YAML_NUMBER = CLng(InputBox("番号を数字で入力してください", "番号", ""))

    '番号が存在するかチェック
    input_number = vlookup_parameters(1)

    '番号が何も入力されていない
    If YAML_NUMBER = 0 Then
       input_number = False
    End If

    '入力内容が正しくない場合、Falseをセットする
    If Err.Number <> 0 Then
        input_number = False
    End If

End Function


'YAMLの内容を初期化する関数
'パラメータを増減するなら、この関数を修正する
Private Sub format_yaml()

    'YAMLファイルとなる配列を初期化する
    YAML_CONTENTS(0, 0) = "---"
    YAML_CONTENTS(0, 1) = "daikoumoku: "
    YAML_CONTENTS(0, 2) = "router: "
    YAML_CONTENTS(0, 3) = "settings:"
    YAML_CONTENTS(0, 4) = "  interface: "
    YAML_CONTENTS(0, 5) = "  description: "

    YAML_CONTENTS(1, 0) = ""
    YAML_CONTENTS(1, 1) = ""
    YAML_CONTENTS(1, 2) = ""
    YAML_CONTENTS(1, 3) = ""
    YAML_CONTENTS(1, 4) = ""
    YAML_CONTENTS(1, 5) = ""

End Sub


'パラメータをYamlにする配列へセットする関数
'パラメータを増減するなら、この関数を修正する
Private Sub set_yaml()

    'vlookupで大項目の列を指定し、値をとってくる
    YAML_CONTENTS(1, 1) = vlookup_parameters(2)

    'vlookupで機器名の列を指定し、値をとってくる
    YAML_CONTENTS(1, 2) = vlookup_parameters(3)

    '機器の設定
    'interface
    YAML_CONTENTS(1, 4) = vlookup_parameters(4)
    'description
    YAML_CONTENTS(1, 5) = vlookup_parameters(5)

    '値の整形
    Dim i As Long
    For i = 0 To (YAML_MAX_COLUMUN - 1)
        '全ての半角SPを削除(値に半角SPが入っていたら、意図したコマンドにならないため)
        YAML_CONTENTS(1, i) = Replace(YAML_CONTENTS(1, i), " ", "")

        '全角文字が入っていたら"で囲む
        If Len(YAML_CONTENTS(1, i)) <> LenB(StrConv(YAML_CONTENTS(1, i), vbFromUnicode)) Then
            '""""は1,4つめが文字くくり、2つめはエスケープ文字、3つめが実際の文字
            YAML_CONTENTS(1, i) = """" & YAML_CONTENTS(1, i) & """"
        End If
    Next

End Sub

'Excelから指定のパラメータを返す関数
Private Function vlookup_parameters(i As Long) As String

    'Vlookup関数を呼び出している。引数はセルに記載するときのVlookupと同じ
    vlookup_parameters = Application.WorksheetFunction.VLookup(YAML_NUMBER, Range(Cells(ROW_START, COLUMN_START), Cells(ROW_END, COLUMN_END)), i, False)

End Function


'YAMLファイルを作成する関数
Private Sub output_yaml()

    'ファイル名に大項目_YYMMDDhhmm.ymlを設定する
    Dim yaml_name As String
    Dim yaml_date As Date

    yaml_date = Now
    yaml_name = vlookup_parameters(2) & "_" & Year(yaml_date) & Month(yaml_date) & Day(yaml_date) & Hour(yaml_date) & Minute(yaml_date) & ".yml"

    'ファイルパスの設定(本エクセルと同じフォルダを指定する)
    Dim file_path As String
    file_path = ActiveWorkbook.Path & "/" & yaml_name

    'ここからファイル書き込み
    Dim n As Long
    n = FreeFile
    Open file_path For Output As #n

    Dim i As Long
    For i = 0 To (YAML_MAX_COLUMUN - 1)
        Print #n, YAML_CONTENTS(0, i); YAML_CONTENTS(1, i)
    Next

    Close #n
    'ここまでファイル書き込み

    '処理が終わったかわかるようにメッセージを出す
    MsgBox "YAMLファイルを作成しました", vbInformation

    'ファイルを開く
    Dim WSH
    Set WSH = CreateObject("Wscript.Shell")
    WSH.Run file_path, vbNormalFocus
    Set WSH = Nothing

End Sub



備考

Option Base 1は使ったことないんだけど、おそらくわかりやすくなる?他言語との違いはどうだったかなぁ。
あと、Yamlファイル名の変数はNamlがいいかなと思ったけどNamulだった。

機械学習に触れてみた

はじめに

タイトル通りの機械学習に触れてみたという内容です。
先日、社内の勉強会で同タイトルで発表しました。
最近ブログを書いていなかったこともあり、それならブログネタにしようかと。
なお、数学や理論については書きません。


目次


環境

Google Colaboratoryを使用します。
https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja

Colaboratory(略称: Colab)は、ブラウザから Python を記述、実行できるサービスです。次の特長を備えています。

  • 環境構築が不要
  • GPU への無料アクセス
  • 簡単に共有


機械学習のさわりを知る

何の役に立つの??

身近なものでは、迷惑メールの分類、翻訳、手書き文字の判別に使われています。
サービスのアカウント登録時に表示される「私はロボットではありません」も機械学習のようです。


機械学習とはなんぞや?

通常のシステムでは、Iuputを受けてどうOutputするか全てのパターンが考えられたプログラムが処理します。
機械学習では、欲しいOutputに沿うinputを用意し、いい感じになるよう機械学習が処理します。


機械学習の種類

  • 教師あり学習
    • 教師からの例題として事前にデータと正解を与え、それを元に学習します。
    • 例えば、長文の解析(どの単語がどのくらいの頻度で含まれているか)をするには、どんな特徴を持っているかを表すベクトル(教師)が必要で、文字の種類はアルファベット、単語はこれですといった情報を事前に与えます。
  • 教師なし学習
    • 出力すべきものが予め決まっていません。
    • データの背後に存在する本質的な構造を抽出するために用いられますが、どのように使えばいいのかよくわかりませんでした。
  • 強化学習
    • エージェント(行動の主体)と環境(状況や状態)が登場します。
    • エージェントが現在の環境を観測し、それにもとづいて意思決定を行い、行動します。行動すると、エージェントの環境が変化し、エージェントは何らかの報酬(行動の結果)が得られます。この報酬が高くなるよう、より良い行動を学習します。
  • 深層学習
    • 調べていません。
  • 教師あり学習強化学習の違い
    • パブロフの犬に良く例えられています。
    • 教師あり学習」は、「ベルの音」と「餌がもらえる」ことの関連付け作業が最初からすんでいる状態です。ベルの音で餌がもらえるという教師がいます。
    • 強化学習」は、この関連付け作業を学習(行動、歩いたり鳴いたり)する過程で、自分で「ベルの音で餌がもらえること」を発見していきます。


教師あり学習を20行で試す

よく題材に上がっているようなので、XORで教師あり学習を試します。
パターンは4パターンのみのため、学習結果が分かりやすいです。


XORの演算結果を学習し、結果を予測する

どのような処理をしているか、print文を入れて変数の中身を表示しています。


### 目的:XORの演算結果を学習し、結果を予測します
import pandas as pd 
  # データ解析を支援する機能を提供するライブラリ。データ構造と演算を提供する
from sklearn import svm 
  # scikit-learn(サイキット・ラーン) 機械学習のフレームワーク。サンプルデータも含む

# XORの教師、演算結果
pre_xor_input = [
    [0, 0, 0],  #データA、データB、結果の順に格納
    [0, 1, 1],
    [1, 0, 1],
    [1, 1, 0]
]

# 入力を学習データとラベルに分ける
pre_xor_df = pd.DataFrame(pre_xor_input)
pre_xor_data  = pre_xor_df.loc[:,0:1] # データ
pre_xor_label = pre_xor_df.loc[:,2]   # ラベル(=回答)

# 変数の中身を表示
print("教師データpre_xor_inputを:\n", pre_xor_input)
print("pre_xor_dfに整形して:")
print(pre_xor_df)
print("データpre_xor_dataと:")
print(pre_xor_data)
print("ラベルpre_xor_labelに分ける:")
print(pre_xor_label)

# データの学習
clf = svm.SVC()           # SVM(Support Vector Machine)、機械学習モデルの一種
clf.fit(pre_xor_data, pre_xor_label) # fitの第1引数にデータ、第2引数にラベルを指定

# 予測したいデータの入力 
xor_input = [
    [1,1],
    [0,1]
]
xor_df = pd.DataFrame(xor_input)
xor_data  = xor_df.loc[:,0:1] 

# データの予測
res = clf.predict(xor_data)

# 予測の結果
print("\n以下の入力データのXOR結果を:")
print(xor_data)
print("回答する:\n", res)

コード通りに動かした結果が以下です。

教師データpre_xor_inputを:
 [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]]
pre_xor_dfに整形して:
   0  1  2
0  0  0  0
1  0  1  1
2  1  0  1
3  1  1  0
データpre_xor_dataと:
   0  1
0  0  0
1  0  1
2  1  0
3  1  1
ラベルpre_xor_labelに分ける:
0    0
1    1
2    1
3    0
Name: 2, dtype: int64

以下の入力データのXOR結果を:
   0  1
0  1  1
1  0  1
回答する:
 [0 1]

整形の処理もわかり、結果は想定通りでした。


学習するデータをいじってみる

  • [2,2]の結果を予測してみます。
以下の入力データのXOR結果を:
   0  1
0  2  2
回答する:
 [0]

同じ値だから、0を返したのでしょうか。


  • 次は、[3,3]の結果を予測します。
以下の入力データのXOR結果を:
   0  1
0  3  3
回答する:
 [0]

[2,2]と同じで、0を返しています。


  • 少しひねって[100,100]の結果を予測します。
以下の入力データのXOR結果を:
     0    1
0  100  100
回答する:
 [1]

1が返ってきた。決定した理由が分からない。


  • 逆に教師の[1, 1, 0]を削除し、[0,0][0,1][1,0][1,1]の結果を予測してみます。
教師データpre_xor_inputを:
 [[0, 0, 0], [0, 1, 1], [1, 0, 1]]
pre_xor_dfに整形して:
   0  1  2
0  0  0  0
1  0  1  1
2  1  0  1
データpre_xor_dataと:
   0  1
0  0  0
1  0  1
2  1  0
ラベルpre_xor_labelに分ける:
0    0
1    1
2    1
Name: 2, dtype: int64

以下の入力データのXOR結果を:
   0  1
0  0  0
1  0  1
2  1  0
3  1  1
回答する:
 [0 1 1 1]

[1,1]の結果が1に変わりましたね。どこかで見たのですが、教師データ[1, 1, 0]がないため、残りの教師データで1が含まれると、1を返す学習をしているそうです。


感想

わからん。機械学習難しすぎ。
ひとまず使うだけでも大変だということがわかりました。

Ubuntuでオンプレ版NetBox2.11を建てる

はじめに

先日リリースされたNetBox2.11を触りたかったので、オンプレ版を建てました。

環境


NetBox2.11の構築

公式のインストールガイドを参照しながら進めていきます。

netbox.readthedocs.io


# apt update
# apt install -y postgresql libpq-dev
# systemctl start postgresql
# systemctl enable postgresql
# sudo -u postgres psql
psql (12.6 (Ubuntu 12.6-0ubuntu0.20.04.1))
Type "help" for help.

postgres=# CREATE DATABASE netbox;
CREATE DATABASE
postgres=# CREATE USER netbox WITH PASSWORD 'パスワード';
CREATE ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE netbox TO netbox;
GRANT
postgres=# \q

# psql --username netbox --password --host localhost netbox
Password: パスワード
psql (12.6 (Ubuntu 12.6-0ubuntu0.20.04.1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

netbox=> \conninfo
You are connected to database "netbox" as user "netbox" on host "localhost" (address "127.0.0.1") at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
netbox=> \q

# apt install -y redis-server
# redis-cli ping
PONG

# apt install -y python3 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev
# wget https://github.com/netbox-community/netbox/archive/v2.11.0.tar.gz
--2021-04-19 14:08:46--  https://github.com/netbox-community/netbox/archive/v2.11.0.tar.gz
Resolving github.com (github.com)... 52.69.186.44
Connecting to github.com (github.com)|52.69.186.44|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/netbox-community/netbox/tar.gz/v2.11.0 [following]
--2021-04-19 14:08:47--  https://codeload.github.com/netbox-community/netbox/tar.gz/v2.11.0
Resolving codeload.github.com (codeload.github.com)... 52.68.31.213
Connecting to codeload.github.com (codeload.github.com)|52.68.31.213|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘v2.11.0.tar.gz’

v2.11.0.tar.gz                       [      <=>                                               ]   6.77M  5.59MB/s    in 1.2s    

2021-04-19 14:08:48 (5.59 MB/s) - ‘v2.11.0.tar.gz’ saved [7101381]

# tar -xzf v2.11.0.tar.gz -C /opt
# ln -s /opt/netbox-2.11.0/ /opt/netbox
# ls -l /opt | grep netbox
lrwxrwxrwx 1 root root   19 Apr 19 14:09 netbox -> /opt/netbox-2.11.0/
drwxrwxr-x 7 root root 4096 Apr 16 13:52 netbox-2.11.0

# adduser --system --group netbox
Adding system user `netbox' (UID 115) ...
Adding new group `netbox' (GID 122) ...
Adding new user `netbox' (UID 115) with group `netbox' ...
Creating home directory `/home/netbox' ...
# chown --recursive netbox /opt/netbox/netbox/media/

# cd /opt/netbox/netbox/netbox/
# cp configuration.example.py configuration.py
# python3 ../generate_secret_key.py
XXXXXXXXXXXXパスワード50文字XXXXXXXXXXXXXXXXXXXXXXX

# vim configuration.py
- ALLOWED_HOSTS = []             
+ ALLOWED_HOSTS = ['*']             
DATABASE = {
    'NAME': 'netbox',         # Database name
    'USER': 'netbox',               # PostgreSQL username
-    'PASSWORD': '',           # PostgreSQL password
+    'PASSWORD': 'XXXXXX',           # PostgreSQL password
    'HOST': 'localhost',      # Database server
    'PORT': '',               # Database port (leave blank for default)
    'CONN_MAX_AGE': 300,      # Max database connection age
}
- SECRET_KEY = ''
+ SECRET_KEY = 'XXXXXXXXXXXパスワード50文字XXXXXXXXXXXXXXXXXXX'





- TIME_ZONE = 'UTC'
+ TIME_ZONE = 'Asia/Tokyo'
# /opt/netbox/upgrade.sh

# source /opt/netbox/venv/bin/activate
(venv) # cd /opt/netbox/netbox
(venv) # python3 manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: 
Password: パスワード
Password (again): パスワード
Superuser created successfully.
(venv) # deactivate

# cp /opt/netbox/contrib/gunicorn.py /opt/netbox/gunicorn.py
# cp -v /opt/netbox/contrib/*.service /etc/systemd/system/
'/opt/netbox/contrib/netbox-rq.service' -> '/etc/systemd/system/netbox-rq.service'
'/opt/netbox/contrib/netbox.service' -> '/etc/systemd/system/netbox.service'

# systemctl daemon-reload
# systemctl start netbox netbox-rq
# systemctl enable netbox netbox-rq
Created symlink /etc/systemd/system/multi-user.target.wants/netbox.service → /etc/systemd/system/netbox.service.
Created symlink /etc/systemd/system/multi-user.target.wants/netbox-rq.service → /etc/systemd/system/netbox-rq.service.

# systemctl enable netbox netbox-rq
Created symlink /etc/systemd/system/multi-user.target.wants/netbox.service → /etc/systemd/system/netbox.service.
Created symlink /etc/systemd/system/multi-user.target.wants/netbox-rq.service → /etc/systemd/system/netbox-rq.service.
# systemctl status netbox.service
● netbox.service - NetBox WSGI Service
     Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2021-04-19 14:32:11 UTC; 18s ago
       Docs: https://netbox.readthedocs.io/en/stable/
   Main PID: 11299 (gunicorn)
      Tasks: 6 (limit: 4706)
     Memory: 336.5M
     CGroup: /system.slice/netbox.service
             ├─11299 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>
             ├─11316 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>
             ├─11317 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>
             ├─11318 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>
             ├─11319 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>
             └─11320 /opt/netbox-2.11.0/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /o>

Apr 19 14:32:11 XXXXXXXXXX systemd[1]: Started NetBox WSGI Service.
Apr 19 14:32:11 XXXXXXXXXX gunicorn[11299]: [2021-04-19 14:32:11 +0000] [11299] [INFO] Starting gunicorn 20.1.0
Apr 19 14:32:11 XXXXXXXXXX gunicorn[11299]: [2021-04-19 14:32:11 +0000] [11299] [INFO] Listening at: http://127.0.0.1:8001>
Apr 19 14:32:11 XXXXXXXXXX gunicorn[11299]: [2021-04-19 14:32:11 +0000] [11299] [INFO] Using worker: threads

# apt install -y nginx
# cp /opt/netbox/contrib/nginx.conf /etc/nginx/sites-available/netbox
# vim /etc/nginx/sites-available/netbox
server {
-    listen 443 ssl;
+ #    listen 443 ssl;
    # CHANGE THIS TO YOUR SERVER'S NAME
-     server_name netbox.example.com;
-     ssl_certificate /etc/ssl/certs/netbox.crt;
-     ssl_certificate_key /etc/ssl/private/netbox.key;
+ #    server_name netbox.example.com;
+ #    ssl_certificate /etc/ssl/certs/netbox.crt;
+ #    ssl_certificate_key /etc/ssl/private/netbox.key;
    client_max_body_size 25m;

    location /static/ {
        alias /opt/netbox/netbox/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    # Redirect HTTP traffic to HTTPS
    listen 80;
    server_name _;
    return 301 https://$host$request_uri;
}

# rm /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/netbox /etc/nginx/sites-enabled/netbox
# systemctl restart nginx


ブラウザで以下に接続します。

http://(IPアドレス)/


雑記

docker-compose版が待ち遠しい。