もりはやメモφ(・ω・ )

インフラなエンジニアからSREへ

PostgreSQLで2バイト文字が含まれるカラムかを調査する方法

これは

PostgreSQLで文字列型のカラムに 2バイト文字 が含まれるかを調査する方法です。まさかこんな調査が必要になるとは思っていなかったのですが、必要になったのでやりました。

  • これは
    • TL;DR
      • charの場合
      • varcharの場合
    • 検証
      • テストテーブルの作成&テストデータのInsert
      • char
    • varchar
    • 余談
    • 参考

TL;DR

以下をやるだけです。

charの場合

以下のようなクエリで 0なら2バイト無し です。

select count(*) from 対象テーブル
where octet_length(対象カラム) > 対象カラムの桁数;

varcharの場合

以下のようなクエリで 0なら2バイト無し です。

select count(*) from 対象テーブル
where octet_length(対象カラム) <> char_length(対象カラム);
続きを読む

AWXのバージョンアップをしました (2.1.0 -> 4.0.0)

f:id:morihaya:20190610004613p:plain
AWX4.0.0

  • TL;DR
    • 最初にバージョンアップについて調べました
    • サーバ
  • 手順
    • pipでtower-cliを導入(初回のみでいいはず)
    • tower-cliをセットアップ
    • AWXの情報をバックアップ(結局使わなかった)
    • PostgreSQLのバックアップ(結局使わなかった)
    • アップグレード
  • ハマったこと
    • 何も考えずにgit cloneしただけのブランチで作業を進めたらエラーで起動してこなかった
    • dockerのインターナルIPが変わったらしくてPostgreSQLのpg_hbaに引っかかっていたので多めに許可。

最新のAWXが4.0.0に対し、使っていたAWXが2.1.0と古いため、バージョンアップを行いましたのでメモ。 環境はシングルノードでdockerを利用してAWXを動かしていますが、PostgreSQLだけはコンテナじゃなくてノード上の物を利用しています。

TL;DR

要はこれをやっただけ。バックアップもとりましたが、結局利用していないです。 結果として2.1.0を利用している場合、DBさえあればスムーズにアップグレードができると言っていいのかもしれません(個人の環境ですが)

  • AWXとPostgreSQLのバックアップ*1
tower-cli receive --all > assets.json
pg_dump -d awx | gzip >  pg_dump-awx$(date +%Y%m%d-%H%M%S).sql
  • 最新版をgit pull*2
cd <任意の場所>/awx
git pull
git checkout -b 4.0.0 refs/tags/4.0.0
vim  installer/inventory
-> portやpostgresなどの固有設定を反映
  • 既存のAXWコンテナを停止

これはAWXに限定しない危険コマンドなので、手で一つずつ選択して止めることを個人的には推奨します。*3 私の環境はAWX専用だったので手順通りに行いました。

docker rm -f $(docker ps -aq)
  • 通常の手順に従いInstall
# Set the working directory to installer
cd installer

# Run the Ansible playbook
ansible-playbook -i inventory install.yml

*1:結局使わなかった

*2:cloneじゃないのは既存がすでにあるので

*3:でも手順にこうある

続きを読む

datadogのMulti-Organizationを開始するまで

これは

datadogを使っている状況で、新システムについては請求額や利用者を分割したい要望が出たため、Multiple-Organizationと言う機能を利用したい、と言う話です。

結論としては

Multiple-Organizationは便利でした。

  • 左下の自分のアイコンから表示されるメニューから、サクサクとOrganizationを変更することが出来ます
  • それ以外は基本的に新規アカウントをもう一つ作成したのとほぼ変わりませんので、クレジットカードやメンバーや監視対象は全て新規で登録する必要があります

手順だけ簡潔に

2019/05現在はサポートに連絡するしか無い様です。

  1. デフォルトでは無効のためdatadog supportへメール(他にチャットや電話?もあるらしいが私はメールで行った)
  2. datadogサポートからのメールの返信で「本当に有効にして良いかい?」って聞かれるので「OK!」と返信
  3. datadogサポートからのメールの返信で「有効にしたぜ!」と受信する頃には使える様になっている

実際の流れ

こんなツイートから調査を始めました。

"datadog Multi Organization" 的なキーワードで該当ドキュメントに到達。デフォルトでは無効で、サポートに連絡する必要があることを知ります。(面倒だなぁと言う率直な感想)

サポートに連絡するのは手間ですが、同じアカウントのままで異なるOrganizationに簡単にSwitchできるのは便利そうだという印象を受けます。

迷ってても仕方ないのでメールを送信しました。

ちなみに英文メールはこんな感じです。私の英語力は低いですが意図は伝わるはず、の精神で書いてます。(ブログ記載にあたり、文面は一応配慮して具体的な組織名とかはぼかしました)

Dear Datadog support team.

My name is morihaya, engineer of SRE team for hogehoge inc.
We want to use sub-organization to separate billing , members , dashbords and more.

I belong to organization "fugafuga" on datadog.
Thank you.

そして2時間もしないうちに一時返答が来ます。

Hello morihaya,


Thanks for reaching out!

Could you please confirm you are looking for this multi -org functionality for fugafuga ?

https://docs.datadoghq.com/account_management/multi_organization/

Please let me know!
Regards, 
XXX

これに対して「頼むぜ!」と返答します。

Hello XXX,

You are right.
I'm looking for this multi -org functionality for fugafuga.
Please enable it.

Regards,
morihaya

その後も2時間もしないうちに返答が来ました。率直に言って迅速で素晴らしい対応と感じました。Good Job! datadog support!

サポート回答の締めは各種ドキュメントへのリンク集になっていて、まあ大体読んだものでしたが丁寧で良い対応だなぁと感心しました。

Hello morihaya,


We have enabled the multi org feature for fugafuga!
You can find some guide to the multi org features here:
https://docs.datadoghq.com/account_management/multi_organization/

https://docs.datadoghq.com/account_management/org_switching/

https://docs.datadoghq.com/account_management/faq/as-a-parent-account-admin-how-do-i-create-new-sub-organizations/


Let me know if you have any further questions on this matter!

Regards, 
XXX

今回の感想をまとめると以下。

  • Multi-Organizationは便利、サクサクOrganization Switch !
  • datadogのサポート優秀だなと言う感想
  • 英語メールなかなか書く機会も無いので良い練習になった

embulkのfilter pluginが定義ミスで読まれなかったメモ

これは

ものすごいしょーもないミスでembulkのfilterプラグインが読まれなかった事象に遭遇したのでメモ

何がどうだった

filters: とすべきところを filter: とsなしで記載した結果

  • embulkの処理自体は実行される(書式エラーとして判定されない)
  • filterプラグインは読まれない

という事象になった。

定義

filter-pluginが読まれなかった定義( filter: )

exec:
  min_output_tasks: 1
  max_threads: 1

in:
  type: sftp
  host: {{ env.SFTP_HOST }}
  user: {{ env.SFTP_USER }}
  password: {{ env.SFTP_PASSWORD }}
...
filter:
  - type: column
    columns:
      - { name: send_date }
      - { name: message_name }
      - { name: message_type }
      - { name: message_text }
      - { name: send_status }
      - { name: mobile_message_tracking_id }
      - { name: kokyaku_id }
  - type: row
    where: |-
      (
        send_date IS NOT NULL AND
        message_name IS NOT NULL AND
        message_text IS NOT NULL AND
        kokyaku_id IS NOT NULL
      )

out:
  type: postgresql
  host: {{ env.pg_host }} 
  user: {{ env.pg_user }}
  password: {{ env.pg_pass }}
  database: {{ env.database }}
  schema: {{ env.schema }}
.... 

filter-pluginが読まれる正しい定義( filters: )

exec:
  min_output_tasks: 1
  max_threads: 1

in:
  type: sftp
  host: {{ env.SFTP_HOST }}
  user: {{ env.SFTP_USER }}
  password: {{ env.SFTP_PASSWORD }}
...
filters:
  - type: column
    columns:
      - { name: send_date }
      - { name: message_name }
      - { name: message_type }
      - { name: message_text }
      - { name: send_status }
      - { name: mobile_message_tracking_id }
      - { name: kokyaku_id }
  - type: row
    where: |-
      (
        send_date IS NOT NULL AND
        message_name IS NOT NULL AND
        message_text IS NOT NULL AND
        kokyaku_id IS NOT NULL
      )

out:
  type: postgresql
  host: {{ env.pg_host }} 
  user: {{ env.pg_user }}
  password: {{ env.pg_pass }}
  database: {{ env.database }}
  schema: {{ env.schema }}
.... 

ログ

filter-pluginが読まれなかった時のログ

2019-05-15 07:14:41.720 +0900: Embulk v0.9.9
2019-05-15 07:14:42.421 +0900 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected.
2019-05-15 07:14:44.598 +0900 [INFO] (main): Gem's home and path are set by default: "/home/embulk/.embulk/lib/gems"
2019-05-15 07:14:46.353 +0900 [INFO] (main): Started Embulk v0.9.9
2019-05-15 07:14:46.414 +0900 [INFO] (0001:transaction): Loaded plugin embulk-input-sftp (0.2.12)
2019-05-15 07:14:46.436 +0900 [INFO] (0001:transaction): Loaded plugin embulk-output-postgresql (0.8.1)
2019-05-15 07:14:46.476 +0900 [INFO] (0001:transaction): Connecting to sftp://myuser:***@hogehoge.com:22/
2019-05-15 07:14:46.479 +0900 [INFO] (0001:transaction): Getting to download file list

filter-pluginが正しい書式で読まれた時のログ oaded plugin embulk-filter-column とちゃんと出てる

2019-05-15 07:34:11.426 +0900: Embulk v0.9.9
2019-05-15 07:34:12.139 +0900 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected.
2019-05-15 07:34:14.306 +0900 [INFO] (main): Gem's home and path are set by default: "/home/embulk/.embulk/lib/gems"
2019-05-15 07:34:16.017 +0900 [INFO] (main): Started Embulk v0.9.9
2019-05-15 07:34:16.075 +0900 [INFO] (0001:transaction): Loaded plugin embulk-input-sftp (0.2.12)
2019-05-15 07:34:16.098 +0900 [INFO] (0001:transaction): Loaded plugin embulk-output-postgresql (0.8.1)
2019-05-15 07:34:16.127 +0900 [INFO] (0001:transaction): Loaded plugin embulk-filter-column (0.7.1)
2019-05-15 07:34:16.145 +0900 [INFO] (0001:transaction): Loaded plugin embulk-filter-row (0.5.1)
2019-05-15 07:34:16.174 +0900 [INFO] (0001:transaction): Connecting to sftp://myuser:***@hogehoge.com:22/

今後に向けて

慣れで下手に手打ちするとしょーもないミスを産むので、ちゃんと動いてるコードをコピペするのも大事だなぁ。

AWS Code兄弟の自分なりの理解メモ殴り書き

これは

AWSのCode兄弟を最近触り始めていて、雰囲気がわかったので一度言語化しておこう&ブログ止まっててまずいので小ネタで、という記事

想定読者

完全に自分

  • EC2へのデプロイ(Lambda/ECSは出てこない)
  • GitLabCIとかJenkinsでパイプラインは作ってきた
  • 完全にCode兄弟初めて

一言で言うと

  • CodeCommit -> 超シンプルなGitLab/GitHub。pushとpullとpullreqができる
  • CodeBuild -> コンテナ使ってビルドやテストできる。成果物はS3に置ける
  • CodeDeploy -> CodeBuildが作った成果物をEC2にデプロイできる。AgentをEC2に入れておく必要がある(AmazonLinux2はデフォで入ってる)。オンプレのインスタンスにもデプロイできる
  • CodePipline -> 上述した3兄弟(Commit/Build/Deploy)を順番に実行するジョブ管理的な役割

GitLabCI触ってるとgitlab-ci.ymlでbuild/deploy/pipline全部書いちゃうケースが多かったので、最初なんでこんなに分割されてるんだと思ったけれど、慣れてくると役割がしっかりしてて良くできてるなあと関心。

なお最低限の理解が上の箇条書きだと感じており、付加的な機能もまだまだあるので全くもって完全に理解してない。

理解の勘所

  • Pipelineは最後に作れば良いので、Commit/Build/Deployをそれぞれ単体で動かせるとこまで最初にやる
  • CodeBuildはbuildspec.ymlを書いてCodeCommitに置く
  • CodeBuildで使うカスタムイメージはECRに置く(Dockerfile自分で書く)
  • CodeDeployはappspec.ymlとhook用のシェルを書いてCodeCommitに置く
  • hook用のシェルもbuildの成果物に入れる必要がある

ハマりどころ

  • appspec.ymlでのCodeDeployを使ったデプロイは、直接ファイルを上書きできないのでhook用のシェルで対応する。4年越しのissueがあり、hookシェルの中での対応方法がレスされてる
  • CodeBuild単体とCodePipelineからの実行で、secondaryなsourceの展開場所が異なるのでbuildspec.ymlで吸収した
    • build単体実行だとカレントに
    • pipeline実行だと全く別のパスに(これ本当にトラップ)

具体的にはこんな感じで吸収した。理由としてはsettings.gradleの中で includeFlat してビルドするケースだった。

version: 0.2

phases:
  pre_build:
    commands:
      - gradle --version
      - pwd
      - bash -c "[ ! -d ../<reponame> ] && mv ${CODEBUILD_SRC_DIR_<reponame>} ../"
      - ls -l ../
      - ls -l
  build:
    commands:
      - echo Build started on `date`
      - echo Building by gradle ...
      - gradle war
  post_build:
    commands:
      - echo Build completed on `date`
      - ls -l build/gradle/libs/hogehoge.war

artifacts:
  files:
    - build/gradle/libs/hogehoge.war
    - appspec.yml
    - codedeployhooks/*
  discard-paths: yes
  • CodeDeployのhookシェルのライフサイクルを理解する必要があるけど、とりあえずApplicationStopは初回時実行されないのでAfterInstall使っておく方が安全かもしれない

Jenkinsfileでタイムスケジュール実行

Jenkinsfileでcron定期実行指定

v2になってから久しい*1Jenkinsですが、Jenkinsfileによるジョブのコード化がとても良いです。

小ネタでJenkinsfileでのcron実行の方法を調べたのでメモです。

Documentのcron-syntaxに書いてある通りなのですが、以下のようにcronでかけます。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    triggers {
        cron('H */4 * * 1-5')   <-ここ
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

ここで面白いのが H の部分。 これは標準のcronには無いパラメタで、乱数的な意味になります。具体的にはサンプルの例だと、分に H が指定あるため 00-59 のどこか、となります。

しかし注意点として、ドキュメントにも以下のように記載があり、乱数(random)では無いとのことです。ジョブ名からハッシュで数値が決定され、毎回変動するわけではありません。

The H symbol can be thought of as a random value over a range, but it actually is a hash of the job name, not a random function, so that the value remains stable for any given project.

おまけ

簡単だと思って追加したところ、以下のエラーが発生しました。 これは読んで字のごとくなのですが、trigger句が複数回出ているぞ!というエラーです。

WorkflowScript: 23: Multiple occurrences of the triggers section @ line 23, column 5.

というのもサンプルを見てagent anyの下に以下を追加したのですが

   ...略...
    agent any
    triggers {
       cron('H 22 * * *')
    }
   ...略...

実はJenkinsfileの下の方にGitLab連携用にtriggersが記載されており、重複していたというお粗末な理由でした。これはサンプルをそのまま使うとありそうな内容なので、自戒として記載しておきます。

    triggers {
        cron('H 22 * * *')   <-ここに追加するのが正しかった
        gitlab(
            triggerOnPush: true,
            triggerOnMergeRequest: true,
            branchFilterType: 'All',
            addNoteOnMergeRequest: true,
            addCiMessage: true
        )
    }

参考までにエラー全文を記載すると以下(マスクしてます)。 Multiple occurrences of ther xxxxx section が出たら今後は一発でわかりますね。

Branch indexing
 > git rev-parse --is-inside-work-tree # timeout=10
Setting origin to https://mygitlab/oreno/project.git
 > git config remote.origin.url https://mygitlab/oreno/project.git # timeout=10
Fetching origin...
Fetching upstream changes from origin
 > git --version # timeout=10
 > git config --get remote.origin.url # timeout=10
using GIT_ASKPASS to set credentials Git username/password for https://mygitlab/oreno/project.git
 > git fetch --tags --progress origin +refs/heads/*:refs/remotes/origin/* # timeout=10
Seen branch in repository origin/master
Seen 1 remote branch
Obtained Jenkinsfile from e294460e7fba067df5a48f5faaf2e45780ba6187
Running in Durability level: MAX_SURVIVABILITY
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 23: Multiple occurrences of the triggers section @ line 23, column 5.
       triggers {
       ^

1 error

    at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
    at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1085)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:603)
    at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:581)
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:558)
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
    at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
    at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:131)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:125)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:560)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:521)
    at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:320)
    at hudson.model.ResourceController.execute(ResourceController.java:97)
    at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE

*1:2016/04/20らしい

ブースから見た光景@デブサミ2019

f:id:morihaya:20190218022706j:plain:w30
morihaya

デブサミ2019に「健康スポンサー」として弊社(オイラ大地)もブースを出す幸運に恵まれ、加えて中の人をやってきましたのでその感想を綴ります。

event.shoeisha.jp

  • デブサミとは
  • 豪華なスポンサー陣
    • We are "健康スポンサー" !!
  • ブースから見える風景
    • 意外と人はひっきりなし
    • 当たり前だけど十人十色
    • もうちょっと良くするには
  • 余談
  • 総括すると最高だった
続きを読む