Hejdaの見る夢

一人前のエンジニアを目指して頑張ったこととかをつらつら書くブログ

Azure Storage を Terraform で構築するサンプルを公開しました!! 🎉🎉🎉

動悸

Serverlessdays Tokyo 2019 にて素敵な登壇があり、実際に書いてみようと思いました。

  • Azure でサーバーレス、 Infrastructure as Code どうしてますか? by @dz_
    • Have fun! 💖

www.slideshare.net

Terraform は普段から書いているし、ちょうど Google Cloud Storage をいじっていたので、 同じストレージ系だからそこまで時間かけずにいけるだろうと思い、 Azure Storage の中の Azure Blob を調べるとこからはじめました。

調査と実際に簡単な検証、コードを公開出来るようにまとめるまでに 8 時間くらいかかりました。

Azure に慣れていないので、 Terraform において Azure にログインするとこで一回心が折れましたが、なんとかミニマムで公開出来るくらいにはまとまりました。

サンプルコード

以下の Repository が実際に作成したサンプルコードです。

github.com

実際にやってみる

Azure のアカウントの作成

ここは他に記事におまかせします。

Docker のインストール

実行環境を Dockerfile で用意しています。

したがって、作業マシンに Docker Engine をインストールしてください。

参考: Install Docker

Repository の取得

今回は自作した Repository を使って、挙動を確認していきます。

  • Repository を clone します。
cd {YOUR_WORKSPACE}
git clone https://github.com/iganari/package-azure.git
cd storage/terraform/
$ tree
.
├── Dockerfile
├── README.ja.md
├── README.md
├── docker-build-run.sh
├── images
│   └── irasutoya
│       ├── azarashi
│       │   ├── animal_chara_computer_azarashi.png
│       │   ├── animal_chara_smartphone_azarashi.png
│       │   └── animal_gomafu_azarashi_baby.png
│       ├── hiyoko
│       │   ├── animal_mark_hiyoko.png
│       │   ├── hiyoko.png
│       │   ├── hiyoko_baby.png
│       │   └── niwatori_hiyoko_koushin.png
│       └── penguin
│           ├── animal_chara_computer_penguin.png
│           ├── animal_chara_smartphone_penguin.png
│           ├── natsubate_penguin.png
│           └── ondanka_animal_penguin.png
├── provider.tf
├── resource_group.tf
├── storage.tf
├── terraform.tf
└── variables.tf

5 directories, 20 files

Terraform の設定ファイルの説明を簡単に示します。

  • terraform.tf
    • Terraform のバージョンを固定します。
    • Terraform はバージョンが上がるのが早いので可能な限り、固定したほうがいいでしょう。
terraform {
  required_version = "0.12.12"
}
resource "azurerm_resource_group" "arg_default" {
  name     = "${lookup(var.common, "rsg_name")}"
  location = "${lookup(var.common, "rsg_location")}"
}
resource "random_string" "asa-suffix" {
  length  = "8"
  upper   = false
  lower   = true
  number  = false
  special = false
}

resource "azurerm_storage_account" "asa_default" {
  name                     = "${lookup(var.storage, "asa_name")}${random_string.asa-suffix.id}"
  resource_group_name      = "${azurerm_resource_group.arg_default.name}"
  location                 = "${lookup(var.common, "rsg_location")}"
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_container" "asc_default" {
  name                  = "${lookup(var.storage, "asc_name")}"
  resource_group_name   = "${azurerm_resource_group.arg_default.name}"
  storage_account_name  = "${azurerm_storage_account.asa_default.name}"
  container_access_type = "private"
}

resource "azurerm_storage_blob" "asb_default_01" {
  name                   = "penguin_01.png"
  resource_group_name    = "${azurerm_resource_group.arg_default.name}"
  storage_account_name   = "${azurerm_storage_account.asa_default.name}"
  storage_container_name = "${azurerm_storage_container.asc_default.name}"
  type                   = "Block"
  source                 = "images/irasutoya/penguin/animal_chara_computer_penguin.png"
}
resource "azurerm_storage_blob" "asb_default_02" {
  name                   = "sample/penguin_02.png"
  resource_group_name    = "${azurerm_resource_group.arg_default.name}"
  storage_account_name   = "${azurerm_storage_account.asa_default.name}"
  storage_container_name = "${azurerm_storage_container.asc_default.name}"
  type                   = "Block"
  source                 = "images/irasutoya/penguin/animal_chara_smartphone_penguin.png"
}

resource "azurerm_storage_blob" "asb_default_03" {
  count = "${length(var.image-hiyoko)}"

  # same version
  name = "sample03/hiyoko/${element(var.image-hiyoko, count.index)}"
  # # rename version
  # name = "sample03/hiyoco-${lookup(var.image-hiyoko, count.index)}"

  resource_group_name    = "${azurerm_resource_group.arg_default.name}"
  storage_account_name   = "${azurerm_storage_account.asa_default.name}"
  storage_container_name = "${azurerm_storage_container.asc_default.name}"
  type                   = "Block"
  source                 = "images/irasutoya/hiyoko/${element(var.image-hiyoko, count.index)}"
}
  • variables.tf
    • 変数をまとめておく設定ファイルです。
variable "common" {
  type = "map"
  default = {
    rsg_name     = "rsg-sample"
    rsg_location = "West Europe"
  }
}

variable "storage" {
  type = "map"
  default = {
    asa_name = "asasample"  # name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long.
    asc_name = "asc-sample"  # Only lowercase alphanumeric characters and hyphens allowed.
  }
}

variable "image-hiyoko" {
  default = [
    "animal_mark_hiyoko.png",
    "hiyoko_baby.png",
    "hiyoko.png",
    "niwatori_hiyoko_koushin.png"
  ]
}

以上が Terraform の設定ファイルになります。

また、 Dockerfile を用いた環境を構築するために、以下のようのなシェルスクリプトも用意しました。

  • docker-build-run.sh
#!/bin/bash

set -xeu

_I_TAG='pkg-azure-storage-tf'
BASEPATH=$(cd `dirname $0`; pwd)
DIREPATH=`echo $BASEPATH | awk -F\/ '{print $NF}'`

set +eu
docker rm -f ${_I_TAG}
set -eu

docker build . -t ${_I_TAG}

docker run --rm \
           -it \
           -v $BASEPATH:/opt/iganari/$DIREPATH \
           -w /opt/iganari/$DIREPATH \
           -h ${_I_TAG} \
           --name ${_I_TAG} \
           ${_I_TAG} \
           /bin/sh

上記のファイルを用意したら、実行していきます。

Docker コンテナを用いて、 Terraform を実行する

  • Docker コンテナの起動をします。
    • 以降、 🐳 がついているものはこの Docker コンテナ上で実行しているものとします。
sh docker-build-run.sh
  • 🐳 az コマンドのバージョンの確認します。
    • 執筆当時は 2 系を使用していますが、読むタイミングによってはバージョンがもっと進んでいる可能性があります。
# az --version
azure-cli (2.0.57)
  • 🐳 Terraform のバージョンを確認します。
    • 執筆当時は 0.12.12 系を使用していますが、読むタイミングによってはバージョンがもっと進んでいる可能性があります。
    • しかし、今回の環境においては terraform.tf で Terraform のバージョンを指定しているため、このバージョンでしか実行出来ないようになっています。
    • Dockerfile にもこのバージョンを指定してインストールしているため、このバージョンが使用出来ない場合はそもそも Docker コンテナが起動しない可能性があります。
# terraform --version
Terraform v0.12.12
  • 🐳 az コマンドを持ちいて、 Azure にログインします。
az login
### 例

# az login
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code {ここに認証用のコードが出力されています} to 

f:id:nari_kyu:20191028050452p:plain

### ブラウザでの認証が完了する以下のように、現在使用出来るサブスクリプションが表示されます。

authenticate.
[
  {
    "cloudName": "AzureCloud",
    "id": "xxxxxxxxxxxxxxxxxxxxx",
    "isDefault": false,
    "name": "xxxxxxxxxxxxxxxxxxxxx",
    "state": "Warned",
    "tenantId": "xxxxxxxxxxxxxxxxxxxxx",
    "user": {
      "name": "xxxxxxxxxxxxxxxxxxxxx",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "id": "xxxxxxxxxxxxxxxxxxxxx",
    "isDefault": true,
    "name": "xxxxxxxxxxxxxxxxxxxxx",
    "state": "Enabled",
    "tenantId": "xxxxxxxxxxxxxxxxxxxxx",
    "user": {
      "name": "xxxxxxxxxxxxxxxxxxxxx",
      "type": "user"
    }
  },
.
.
.
以下、割愛
  • 🐳 現在のデフォルトのサブスクリプションを確認します。
    • 環境によっては、複数出てくるかもしれませんが、重要なのは IsDefault のカラムが Trure になっているものです。
az account
OR
az account list -o table
### 例

# az account list -o table
A few accounts are skipped as they don't have 'Enabled' state. Use '--all' to display them.
Name                        CloudName    SubscriptionId                        State    IsDefault
--------------------------  -----------  ------------------------------------  -------  -----------
iganari-dev                 AzureCloud   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  Enabled  True
_s_id='{上記でSubscriptionIdの項のもの}'
az account set --subscription ${_s_id}
### 確認のため
az account list -o table
  • 🐳 Terraform の初期設定を行います。
    • 実際に Terraform を実行するために必要なプラグイン等をダウンロードします。
    • 今回は random と azurerm のプラグインが必要になります。
terraform init
### 例

# terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "random" (hashicorp/random) 2.2.1...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 1.35.0...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.azurerm: version = "~> 1.35"
* provider.random: version = "~> 2.2"

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.
  • 🐳 Terraform にてテストを実行します。
    • 新規作成の場合は特に問題は出ないかと思いますが、もし問題があれば修正しましょう。
terraform plan
# terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.arg_default will be created
  + resource "azurerm_resource_group" "arg_default" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "rsg-sample"
      + tags     = (known after apply)
    }
.
.
.
割愛
.
.
.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
  • 🐳 テストが無事に通ったら、実際にリソースを作るコマンドを実行します。
terraform apply
### 例
# terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.arg_default will be created
  + resource "azurerm_resource_group" "arg_default" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "rsg-sample"
      + tags     = (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 とします。
*** 下記が yes と入力した後の出力です。
********************************************

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

random_string.asa-suffix: Creating...
random_string.asa-suffix: Creation complete after 0s [id=sxvygxvo]

.
.
.
割愛
.
.
.

azurerm_storage_blob.asb_default_03[0]: Creation complete after 8s [id=https://asasamplesxvygxvo.blob.core.windows.net/asc-sample/sample03/hiyoko/animal_mark_hiyoko.png]

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

**********************************************************
*** 上記のように出たら、実行完了なので、ブラウザで確認してみましょう。
**********************************************************

ブラウザでリソースを確認します。

  • ストレージ アカウント にて asasample から始まるリソースをクリックします。
    • asasample より後ろは乱数を生成しています。

f:id:nari_kyu:20191028050715p:plain

f:id:nari_kyu:20191028050730p:plain

  • コンテナーをクリックし、asc-sample をクリックします。

f:id:nari_kyu:20191028050803p:plain

f:id:nari_kyu:20191028050813p:plain

  • 試しに penguin_01.png をクッリクし、ダウンロードをしてみます。

f:id:nari_kyu:20191028051013p:plain

f:id:nari_kyu:20191028051027p:plain

  • 問題無くダウンロード出来れば、いらすとやさんからお借りしたペンギンの画像がダウンロードされます。
    • せっかくなので、他の画像もダウンロードしてみてくださいね!

f:id:nari_kyu:20191028051041p:plain

リソースの削除

  • 検証が終わったら、 Azure のリソースは削除しましょう。

    • Azure Storage はアカウント作成後 12ヶ月を過ぎると無料枠が無くなり、通常の課金が発生します。
  • 🐳 Terraform にてリソース削除をします。

terraform destroy

まとめとか感想とか

Terraform を通じて、 Azure のリソースの IaC(Infrastructure as Code) をすこしだけ触れられたかと思います。

難しく無いと言えば難しくないのですが、慣れないとかなり手こずる箇所もあるかと思いますので、サンプルを作ってみましたが如何でしたでしょうか??

動かなかったり、脆弱性があった場合はぜひ Issues にて教えて頂けると幸いです(-人-) ---> issues

ソースには細かい Tips も結構いれてあるのですが、細部まで説明出来なかったので、また他の記事で書きたいと思います。

Have fun! 💖

Hacktoberfest 2019 に参加してみた 1/3

Hacktoberfest 2019 の概要(公式サイトより転載)

f:id:nari_kyu:20191025040805p:plain

Event details

原文

Hacktoberfest® is open to everyone in our global community. Whether you’re a developer, student learning to code, event host, or company of any size, you can help drive growth of open source and make positive contributions to an ever-growing community. All backgrounds and skill levels are encouraged to complete the challenge.

  • Hacktoberfest is open to everyone in our global community!
  • Pull requests can be made in any GitHub-hosted repositories/projects.
  • Sign up anytime between October 1 and October 31.

翻訳

Hacktoberfest は、グローバルコミュニティのすべての人が参加出来ます。 開発者、コードを学ぶ学生、イベントの主催者、またはあらゆる規模の企業であっても、オープンソースの成長を促進し、成長を続けるコミュニティに積極的に貢献することが出来ます。 すべての背景とスキルレベルの人がチャレンジを完了するように願っています(奨励されています)。

  • Hacktoberfest は、グローバルコミュニティのすべての人が参加出来ます!
  • プルリクエストは、GitHub がホストする repositories / projects で行うことが出来ます。
  • 10 月中( 10/1 ~ 10/31 )であれば、いつで開始可能です。

Twitter

  • アカウント

twitter.com

参加の方法(特に初心者)

  • 下記のブログが非常に分かりやすいです :)
    • Check! Hacktoberfest 2019 に参加してOSSにコントリビュートしよう! by dzeyelid
      • 特に初心者がどのようにすればいいかも(日本語で)説明して頂けているので、初参加のかたは必見です!!
      • 公式の推奨のやり方も翻訳されているので、常連も一度は目を通してみるといいでしょう ;)
      • Have fun! 🙌

medium.com

実際に参加するに当たってやったこと

  • FacebookTwitter で宣言
    • Hacktoberfest 2018 はパートナーと2人で「やるぞ!!」と約束してはいたものの、実際には手を動かすことは無かったので、イベント後にとても悔しい思いをしました :(
    • なので、自分を追い込むために先に宣言しました。
  • 社内の人に声掛け
    • 自分を追い込むため、より多くの人を巻き込むために会社の Slack で参加を募り、結果的に自分含め4人のエンジニアが集まってくれました :)
    • これでもう後戻り出来ない環境を作り、後は Slack でお互い情報交換しながら、個々のペースで PR を作っていきました。
    • 今年初めて参加する同僚は上記のブログを参考にスムーズに始められたし、実は過去 3 年続けている同僚もいたので、割とわいわいやりながら進められたと思います。
  • 当時のウキウキ具合が分かる自分のツイート

その後

今回、実際に参加するに当たって分かったことも多いので、それもまとめることによってまだ未経験のかたの参考になればと思います!(`・ω・´)ゞ

Hacktoberfest 2019 に参加してみた 2/3 へ続きます(WIP)

GCP: Ingress によるバックエンド サービスの構成 を修正してみた

要旨

実行方法

準備

  • gcloud コマンドによる認証を行います
gcloud auth login
  • GKE を起動するプロジェクトを設定します
    • ${YOUR_PROJECT} はご自分のプロジェクトを入れて下さい
gcloud config set project ${YOUR_PROJECT}
  • GKE の Zone を予め設定
    • asia-northeast1-a を指定してますが、他の Zone でも問題無いです
gcloud config set compute/zone asia-northeast1-a

Kubernetes Engine クラスタの作成

  • Cluster の名前を先に決めておきます
export cl_name='ingress-test'
echo ${cl_name}
  • GKE を構築します
    • テストのため、 Node は2台で起動する
gcloud beta container clusters create ${cl_name} --num-nodes=2
gcloud beta container clusters get-credentials ${cl_name}
  • Node の確認
kubectl get node -o wide

Deployment を作成する

  • マニュフェスト作成します
vim my-bsc-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-bsc-deployment
spec:
  selector:
    matchLabels:
      purpose: bsc-config-demo
  replicas: 2
  template:
    metadata:
      labels:
        purpose: bsc-config-demo
    spec:
      containers:
      - name: hello-app-container
        image: gcr.io/google-samples/hello-app:1.0
  • 実行します
kubectl apply -f my-bsc-deployment.yaml
  • 確認します
kubectl get deployment
### 例

$ kubectl get deployment
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
my-bsc-deployment   2/2     2            2           55s

BackendConfig を作る

  • マニュフェスト作成します
vim my-bsc-backendconfig.yaml
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: my-bsc-backendconfig
spec:
  timeoutSec: 40
  connectionDraining:
    drainingTimeoutSec: 60
  • 実行します
kubectl apply -f my-bsc-backendconfig.yaml
  • 確認します
kubectl get backendconfig
### 例

$ kubectl get backendconfig
NAME                   AGE
my-bsc-backendconfig   31s

Service の作成

  • マニュフェスト作成します
apiVersion: v1
kind: Service
metadata:
  name: my-bsc-service
  labels:
    purpose: bsc-config-demo
  annotations:
    beta.cloud.google.com/backend-config: '{"ports": {"80":"my-bsc-backendconfig"}}'
spec:
  type: NodePort
  selector:
    purpose: bsc-config-demo
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  • 実行します
kubectl apply -f my-bsc-service.yaml
  • 確認します
kubectl get service
### 例


$ kubectl get service
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.39.240.1   <none>        443/TCP        107m
my-bsc-service   NodePort    10.39.251.4   <none>        80:32093/TCP   8s

Ingress の作成

  • マニュフェスト作成します
vim my-bsc-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-bsc-ingress
spec:
  backend:
    serviceName: my-bsc-service
    servicePort: 80
  • 実行します
kubectl apply -f my-bsc-ingress.yaml
  • 確認します
kubectl get ingress
### 例

$ kubectl get ingress
NAME             HOSTS   ADDRESS   PORTS   AGE
my-bsc-ingress   *                 80      4s

ブラウザで確認します

と確認が出来れば、成功です

f:id:nari_kyu:20191005094630p:plain

  • 実際にブラウザで確認します

f:id:nari_kyu:20191005094651p:plain

考察したこと

リソースの 削除

  • 以下のコマンドを実施します
kubectl delete ingress my-bsc-ingress
kubectl delete service my-bsc-service
kubectl delete backendconfig my-bsc-backendconfig
kubectl delete deployment my-bsc-deployment

Kubernetes Engine クラスタの削除

  • 以下のコマンドを実施します
gcloud beta container clusters delete ${cl_name}

まとめ

  • たまたま検証していた時に見つけて、気になったので直せてよかったです
  • 本家も修正PRを送りたいけど、 GCP のドキュメントって OSS では無いので出来ませんでした
    • 他のクラウドOSS にしているところもあるのですごく残念だな〜と思いました

Associate Cloud Engineer に合格しました 🎉🎉🎉

Associate Cloud Engineer とは 🤔

試験の申込みから受験まで 🙂

勉強方法 🤔

試験を受けるにあたって注意点 🧐

  • 当日の試験は一切の持ち込みや筆記具はありません
    • メモ用紙とかもありません
  • 本人確認として、身分証明書 が 2 個必要です
    • 試験を予約した後に来るメールに書いてはあるのですが、普通に見落としていて焦りました(^^;
  • 試験を予約・受験する際に Google Cloud Webassessor にユーザ登録をしなくていれないのですが、受ける言語ごとにユーザ登録しないといけません
    • 日本語の試験を受けたい場合は日本語を登録したユーザ、英語の試験を受けたい場合は英語を登録したユーザが必要になります
    • 2019/08 現在の話です🔥

感想とか 😊

  • もともと実務で GCP を触っているのでそこまでちゃんとした試験勉強はしてませんでした
  • そのかわり、模擬試験を「なぜこの選択肢が合っていて、なぜこの選択肢が間違っているのか」を自信持って説明出来るようになるまで繰り返しました
    • ざっと、 10 回はやりました(無料なので気兼ねなく繰りかえせます
  • 実際の問題は模擬試験に比べ、「今ふう」でした
    • というより、模擬試験がすこしレトロのままかもしれません
  • Web テストとか、就活の SPI 試験ぶりだったので、緊張しましたw
  • Web テスト後、暫定ではあるけど結果がすぐに分かります

今後について 😊

  • 現在は暫定であり、ちゃんとした結果は後日メールで来るとのことでまずはそれを待ちます
    • 認定証が来たら、 Credential.net に登録してみようかと思います 😊
  • 次の試験は Professional Cloud Architect か Professional Cloud Developer を受けようと思っています
    • 勉強方法は Coursera と Qwiklabs を予定中
      • 同僚の多くが Coursera をやって試験に合格しているので、試験対策としては良いのかなと思っています 🤔