Tocyukiのブログ

ギターと柔術とプログラミングが好き!

AWS DMSでDB移行をした際に移行されない属性を移行するためのSQL文

今、お仕事でとあるIaaSからAWSへのインフラ移行をしていて、DMS(Database Migration Service)を使っててめちゃ便利で最高なんですけど、これインデックスやらプライマリーキーに設定されているAUTO_INCREMENT属性やらもろもろ移行してくれないものがあります。

docs.aws.amazon.com

ただし、 AWS DMS はターゲットデータベース内にセカンダリインデックス、外部キー、ユーザーアカウントなどを自動的に作成しません。

docs.aws.amazon.com

列の AUTO_INCREMENT 属性は、ターゲットデータベース列に移行されません。

というわけでもろもろの属性を移行するためのSQLを書いたので覚書のため記載しておきます。
{SCHEMA_NAME}は対象のDBスキーマに読み替えてください。

Index移行用SQL

SELECT
  CONCAT(
    "ALTER TABLE ",
        t1.table_name,
        " ADD INDEX ",
    t1.index_name, 
    "(",
    (
      SELECT
        GROUP_CONCAT(DISTINCT t2.column_name SEPARATOR ', ')
      FROM
        information_schema.statistics AS t2
      WHERE
        t1.index_name = t2.index_name AND t1.table_name = t2.table_name
      ORDER BY
        t2.table_name,
        t2.index_name,
        t2.seq_in_index
    ),
    ");"
  ) AS alter_table_add_index_query
FROM
  information_schema.statistics AS t1
WHERE
  table_schema = "{SCHEMA_NAME}" AND index_name != "PRIMARY"
GROUP BY
  t1.table_name,
  t1.index_name
;

プライマリーキーのAUTO_INCREMENT属性移行用SQL

SELECT
 CONCAT(
   "ALTER TABLE ",
   TABLE_NAME,
   " CHANGE ",
   COLUMN_NAME,
   " ",
   COLUMN_NAME,
   " ",
   COLUMN_TYPE,
   " AUTO_INCREMENT;"
 ) as alter_auto_increment
FROM
 information_schema.columns
WHERE
 table_schema = "{SCHEMA_NAME}"
and is_nullable = "NO"
and column_key = "PRI"
and extra = "auto_increment"
;

おわりに

あとはそれぞれ生成したALTER文を移行先のDBで流せばOKです! 動作責任は負いかねますが、どなたかの助けになれば嬉しいです!

bashの変数展開で最後の文字を取り出す方法

Bashの変数展開で最後の文字だけ取得する際の仕様がちょっと分かりづらかったのでメモ

やりたいこと

以下のような変数と値があった場合に最後の文字であるdだけを変数展開で取り出したい!

text="Hello World"

やりかた

以下のように変数展開の:のうしろにスペースをいれてやる必要があった

echo ${text: -1}
d

スペースなしだと以下のようになる

echo ${text:-1}
Hello World

参考

genzouw.com

2020年の振り返りと2021年の目標

コロナに主役を完全に奪われた2020年でしたが、思い返してみるとそこそこ色々あったな〜と感じますなぁ

トピックとしては

  • 長女が小学生になる
  • 在宅勤務による生活スタイルの変化
  • 仕事でいくつかの大きめのPJTを独力で完走
  • AWS SysOps取得
  • 転職
  • 柔術再開

という感じかな

2020年の振り返り

とりあえず去年の振り返りと目標で照らし合わせてみると全然達成できてなくて笑えてくる感じだなぁw blog.tocyuki.com

勉強

目標 実績 達成率
TOEIC700点以上 470点 60%
AWS資格5冠達成
(残りSysOps/DVA/SAP/DOPの4つを取得)
SysOpsのみ取得 30%
今積まれている技術書やUdemyを消化する
※主にPython系を
すこーしだけ消化できた 30%

去年に1ヶ月セブ留学したけどそこで得られる英語力はたかが知れてるもので、帰国後にノー勉強で受けたTOEICの結果がそれを表している

資格も結局SysOpsの一つのみのしか取得できずもとりあえず一つは取得したということで自分を褒めてあげよう

積読になっていた技術書やUdemyも少しだけ消化できたのでそれもやっただけよしということで

仕事

目標 実績 達成率
監視基盤の再構築 Datadogを導入 100%
CI/CDパイプライン構築&運用にのせる 一部運用開始するまでできた 20%
GitHub、Slackの導入 Slackのみ導入(GitHub導入自体がなくなった) 30%
定期的な勉強会の実施 まったくなにもせず 0%

仕事では監視基盤にDatadogを提案&導入し、すごくスムーズにいき開発者の運用参加のきっかけと伴う品質の向上に大きく貢献できたかなと

あとは完全にブラックボックスとなっていたシステムのリプレースも一人で担当し、短納期&トラブルなしで完了することができた

やったこととしては以下

  • Ansible化&Terraform化
  • PHP5系&MySQL5.5のバージョンアップ
  • AmazonLinux1からAmazonLinux2へのバージョンアップ
  • ルートアカウントから別サービスアカウントへの移行

GitHubの導入は当初PJ化して進める予定だったけど色々あってPJ自体が頓挫し、Slackに関してはチーム内利用&通知に留まってしまった

勉強会の開催も色々思うところもありまったくやらず個人的に外部の勉強会に参加するに留まってしまった

とりあえずはそんなこんなもあり12月に転職して心機一転SREチームの立ち上げからインフラのグランドデザインから担当できるのでとても楽しみである!

健康

目標 実績 達成率
6月までの間最低週2回は追い込む コロナのため2月からまったくジムに行けずも年末から柔術開始 20%
体脂肪率15%を切る まったく目標におよばずも柔術を始め光が見え始める 10%
月から柔術再開し最低週2回稽古を行う コロナのため12月ぐらいから開始 50%

ゴールドジムを半年契約したものの結局コロナで12月と1月の2ヶ月しか通えず、筋力の向上もかなり中途半端なもので終わってしまったが、ジムに行ったのは初めてだったので器具を使ったトレーニングがどういうものなのか等がわかりそこはひとつの収穫だったのかなと思える

また12月から柔術も再開し、約1年半ぶりということでびっくりするぐらい動けず、紫帯をつけているのが恥ずかしい感じだけどこちらは引き続き継続していこう

その他

目標 実績 達成率
ブログを週に一度は書く 22記事 40%

アウトプットのルーティン化も必要だなとも思いつつ、アウトプット駆動で物事にとりかかるのも必要なのかなとも思ったので、質より量でやっていこうと思いました

総括

とりあえず、全然達成はできずとも足掻くように前進することはできているのかなという一点のみで自分を褒めよう

  • Datadogの提案・検証・導入まですべて一人で素早くできた
  • 結構大きめのプロジェクトを一人でトラブルなく実施できた
  • TOEIC受けた
  • SysOps取得した
  • Udemyや技術書を少しだけど消化できた
  • 柔術再開した
  • 転職した

12月から転職もして、より大きな裁量でSREとして働くことができているし、完全在宅になったことで家族との時間も確保できて、無駄な飲み会(自分は飲むのが好きなので出勤すると高確率で飲みに行ってしまう)も減らせて伴って出費も減り、柔術もコンスタントに行けて、とてもポジティブな要素はたくさんつくることができたという2020年ではあったので2021年も良い部分はそのままに頑張りたい

課題としてはルーティン化と継続が弱かったのでそこも強化していきたい

2021年の目標

今年も勉強、仕事、健康を軸に目標を立てて達成していきたい

勉強

TOEIC600点

  • 毎日iKnow!とスタディサプリをやる
  • TOEIC2回受験する

iknow.jp

app.eigosapuri.jp

AWS資格6冠達成

  • 2月にSAP取得
  • 4月にDVA取得
  • 7月にDOP取得
  • 8月にCloudPractitoner取得

という感じで取得できて行ったら良いなという感じで!

その他

  • 毎週Atcoder参戦
  • 今積まれている技術書やUdemyを消化する(主にPython系を)
  • 学んだことは必ずブログにアウトプットする

atcoder.jp

仕事

  • SREチーム立ち上げ
  • 新規インフラのグランドデザイン

健康

体脂肪率15%を切る

  • 柔術稽古を最低週2回死守
  • 暴飲暴食をなくす
  • お酒を控える

おわりに

とりあえず振り返りと目標の確認をしてみて、こりゃやばいなという危機感が持てたので粛々と頑張ろうと思ったのでした
そして一時期やっていた振り返りのアウトプットもするようにしよう

ヒアドキュメントでインデントを含めた文字列を使う方法

ヒアドキュメントでインデントを含めたものを使いたいときには<<だけではだめで- <<とすると行頭のタブを無視してくれるので、この書き方だと以下のような用途の際にスクリプトできれいに書けるので便利

# configuration of td-agent
cat - << 'EOM' > /etc/td-agent/td-agent.conf
@include conf.d/*.conf

<match fluent.**>
  @type file
  path /var/log/td-agent/fluent.log
</match>
EOM

参考

qiita.com

qiita.com

【AWS】 instance profileを削除する方法

instance profileはCLIからしか削除できないっぽかったのでメモ

状況

Terraformでapply処理が途中でコケてしまいその影響なのか以下のエラーが表示され先に進めなくなってしまった

Error: Error creating IAM instance profile example-app-iam-instance-profile: EntityAlreadyExists: Instance Profile example-app-iam-instance-profile already exists.
        status code: 409, request id: 7e7d16b7-4ca2-4737-8210-b42c0a8620bb

削除方法

管理コンソールから削除しようとしたけど、どこからも削除ができなさそうだったが、CLIで削除ができそうだったので以下コマンドで無事削除できた

aws iam delete-instance-profile --instance-profile-name example-app-iam-instance-profile

参考情報

awscli.amazonaws.com

【Terraform】 skip_final_snapshot を設定しないでRDSを作成したらterraform destroyできなくなった

というわけで、これちょいちょいやってしまうんですが、Terraformでskip_final_snapshot = trueを設定しないでRDSリソースを作成して、その後削除しようとすると以下のエラーが出るようになってしまったんですよ

Error: RDS Cluster FinalSnapshotIdentifier is required when a final snapshot is required

というわけで対応手順をメモして行こうと思います

再現方法

以下のような感じでRDSのリソースを作成する

resource "aws_rds_cluster" "db" {
  cluster_identifier      = "${var.name}-${var.env}-db"
  engine                  = local.engine
  engine_version          = local.engine_version
  engine_mode             = local.engine_mode
  availability_zones      = values(var.azs)
  master_username         = aws_ssm_parameter.db_username.value
  master_password         = aws_ssm_parameter.db_password.value
  backup_retention_period = var.backup_retention_period
  preferred_backup_window = local.preferred_backup_window

  tags = merge(var.common_tags, {
    Name = "${var.name}-${var.env}-db"
    Role = "RDS"
  })
}

で、applyはうまくいくのでその後、destroyを実行すると・・・

Error: RDS Cluster FinalSnapshotIdentifier is required when a final snapshot is required

というエラーが出てしまい、Terraformでのこのリソースの削除ができなくなってしまいました

修正方法

stackoverflowの回答を参考に以下を実施したところ解決した

  • backup_retention_periodの値を0にする
  • preferred_backup_windowのパラメータを削除する
  • skip_final_snapshot = trueapply_immediately = trueを追加する

以下が修正後のコード

resource "aws_rds_cluster" "db" {
  cluster_identifier      = "${var.name}-${var.env}-db"
  engine                  = local.engine
  engine_version          = local.engine_version
  engine_mode             = local.engine_mode
  availability_zones      = values(var.azs)
  master_username         = aws_ssm_parameter.db_username.value
  master_password         = aws_ssm_parameter.db_password.value
  backup_retention_period = 0
  skip_final_snapshot     = true
  apply_immediately       = true

  tags = merge(var.common_tags, {
    Name = "${var.name}-${var.env}-db"
    Role = "RDS"
  })
}

そしてapplyを実施後、destroyで正常にリソースが削除されるようになりました〜!

参考情報

stackoverflow.com

【Terraform】 countからfor_eachへ書き換えたらスプラット演算子の動きが変わった

いままでcountで記述していたものをfor_eachに直してみたところスプラット演算子の動きが変わったのでメモ

状況

いままではVPCのサブネットリソースをcountで複数作ってそれをoutputで他のモジュールに渡して利用しておりました

resource "aws_subnet" "private_data" {
  count                   = length(var.azs)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = cidrsubnet(aws_vpc.vpc.cidr_block, 8, 11 + count.index)
  availability_zone       = element(var.azs, count.index)
  map_public_ip_on_launch = false
}

output "data_private_subnets" {
  value = aws_subnet.private_data
}

他のモジュールで使うときはこんな感じで使ってた

resource "aws_db_subnet_group" "db" {
  name        = "${var.name}-${terraform.workspace}-db-subnet"
  description = "Database Subnet Group for ${var.name}-${terraform.workspace}"
  subnet_ids  = var.data_private_subnets[*].id # この部分
}

そんでもって、for_eachを使って書き直したのがこちら

resource "aws_subnet" "private_db" {
  for_each          = var.azs
  cidr_block        = cidrsubnet(aws_vpc.vpc.cidr_block, 8, index(values(var.azs), each.value) + 11)
  availability_zone = each.value
  vpc_id            = aws_vpc.vpc.id
  map_public_ip_on_launch = false
}

すると以下のようなエラーが出るようになった

Error: Unsupported attribute

  on modules/aws/database/subnet_group.tf line 4, in resource "aws_db_subnet_group" "db":
   4:   subnet_ids  = var.data_private_subnets[*].id

This object does not have an attribute named "id".

Issue

とりあえずIssueを漁ってみたら以下が見つかった

github.com

github.com

Issueを読んでみると以下のように修正すれば大丈夫っぽいことがわかった

The short answer is that if you were to modify your code to values(aws_route53_record.record)[*].fqdn it will work as you're expecting it to.

修正

Issueに書いてあるとおり、aws_db_subnet_groupのリソース作成部分を以下のように修正したら問題なく動くようになった

resource "aws_db_subnet_group" "db" {
  name        = "${var.name}-${terraform.workspace}-db-subnet"
  description = "Database Subnet Group for ${var.name}-${terraform.workspace}"
  subnet_ids  = values(var.private_subnets_db)[*].id # この部分
}

おわりに

for_each便利だけど思わぬ落とし穴?にハマってしまいました

Intellij IDEA の ideavim で h,j,k,l で連続移動できない場合の修正方法

自宅のMacでIntellij IDEAにideavimのプラグインを入れて使っているんですが、なぜか、h,j,k,lのキーバインドで移動ができず困っていたらこちらにソリューションが記載されていて、試してみたら解消できたのでメモ

t.co

実行したコマンド

defaults write -g ApplePressAndHoldEnabled -bool false

Bashの変数で大文字、小文字の変換をsedを使わないでやる方法

mattnさんの以下ツイートが流れてきてめちゃ便利じゃんと感動したのでメモ

まとめ

最初の文字だけ大文字にする

$ os=linux; echo ${os^}
Linux

全部大文字にする

$ os=linux; echo ${os^^}
LINUX

最初の文字だけ小文字にする

$ os=LinuX; echo ${os,}
linuX

最初と最後の文字だけ小文字にする

$ os=LinuX; echo ${os,,}
linux

大文字小文字を入れ替える

$ os=LiNuX; echo ${os~~}
lInUx

指定文字置換

$ os=Linux; echo ${os//inu/umi}
Lumix

おわりに

これらの記法はbash v4以降で使えるものなので、MacとかのBashだと動きません\(^o^)/
Bash Tipsすきだわ〜\(^o^)/

php-fpm + apache環境での環境変数の持たせ方

php-fpm環境下でApache httpdのSetEnvで設定された環境変数は、PHPのenv()等では取得できるが、
execで実行されるコマンドには、SetEnvで設定された環境変数が反映されなかったので(php-fpmの仕様?)実施した回避策を書く!

要約

  • ApacheのSetEnvは使わず/etc/environmentに環境変数を記載して利用する

/etc/environmentの環境変数利用手順

/etc/systemd/system/httpd.serviceを作成

  • 以下内容で作成する
.include /lib/systemd/system/httpd.service

[Unit]
After=network.target remote-fs.target nss-lookup.target httpd-init.service cloud-config.service

[Service]
UMask=002
EnvironmentFile=/etc/environment

/etc/systemd/system/php-fpm.serviceを作成

  • 以下内容で作成する
.include /lib/systemd/system/php-fpm.service

[Unit]
After=syslog.target network.target cloud-config.service

[Service]
UMask=002
EnvironmentFile=/etc/environment
PrivateTmp=false

/etc/php-fpm.d/www.confに1行追加

  • 以下を追記
clear_env = no

あとは/etc/environmentに環境変数を記載していくだけ!

mysqldumpをするときによく使うオプションとリストア方法

概要

手順

事前準備

  • --default-character-setでどの文字コードで mysqldump を取得するか、対象 DB の現在の文字コードを確認する
$ mysql -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} -e 'show variables like "character_set_database";'

mysqldump取得

圧縮しない時

  • テーブル単位の取得の場合
$ mysqldump -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} --single-transaction --default-character-set=${CHARASET} --routines ${DB_NAME} ${TABLE_NAME_1} ${TABLE_NAME_2} > hogehoge_`date +%Y%m%d`.sql
  • DBスキーマ単位で取得の場合
$ mysqldump -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} --single-transaction --default-character-set=${CHARASET} --routines --add-drop-database --databases ${DB_NAME} > hogehoge_`date +%Y%m%d`.sql

圧縮する時

  • テーブル単位の取得の場合
$ mysqldump  -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} --single-transaction --default-character-set=${CHARASET} --routines ${DB_NAME} ${TABLE_NAME_1} ${TABLE_NAME_2} | gzip > hogehoge_`date +%Y%m%d`.sql.gz
  • DBスキーマ単位で取得の場合
$ mysqldump -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} --single-transaction --default-character-set=${CHARASET} --routines --add-drop-database --databases ${DB_NAME} | gzip > hogehoge_`date +%Y%m%d`.sql.gz

各オプションの内容

single-transaction

  • 対象がinnodbのときのみ有効(myIsamでは効果がない)
  • dump取得時にテーブルロックをしないため、更新時の待ちが発生しない。

default-character-set

  • dump時の文字コードを指定する。
  • 指定した文字列に変換してdumpを作成する。
  • 指定しない場合、サーバー側で指定されているデフォルト文字コードが自動指定されるため、取得側とリストア側で文字コードのデフォルトが違う場合には、文字化けが起きる可能性が高まる。

routines

  • ストアドルーチン (プロシージャーおよび関数) も一緒にdumpに含める。

add-drop-database

  • 指定したDBスキーマをdrop database(削除)するクエリをdumpに含める。

リストア方法

TABLEのリストア

$ mysql -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST}  -D ${DB_NAME} < hogehoge_`date +%Y%m%d`.sql

DBのリストア

$ mysql -u${DB_USERNAME} -p${DB_PASSWORD} -h${DB_HOST} < hogehoge_`date +%Y%m%d`.sql

SQLを勉強したい時に活用できる無料サイト

基本的なSQLを学びたい場合、以下の2つのサイトで事足りるなと思ったのでメモ

SQL bolt

sqlbolt.com

英語だけど問題もシンプルなのでそんなに理解に苦しむことなく、サクサクと進めることができ、一通りのSQL構文を身につけることができるので、オススメです

ただし、なぜかChromeでは文字入力がずれてしまっているのでサファリ等他のブラウザでの実行をオススメします!

HackerRank

www.hackerrank.com

SQL boltで基本的なSQLを学習したらあとはHackerRankでSQLの問題を解きまくればOK!

こちらも英語だけど、Google翻訳プラグインを使えば特に問題ないかと思います!

そんな感じでSQL初心者の方は是非チャレンジしてみてください〜\(^o^)/

Pythonでインストールされているはずのpipモジュールが読み込みできない場合の対処

boto3を使ってデプロイスクリプトを作成していたら、インストールされているはずのboto3が読み込みできないというエラーが発生

$ python3 test.py
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    import boto3
ModuleNotFoundError: No module named 'boto3'

確認してみると、やはりしっかりとインストールされている

$ pip3 show boto3
Name: boto3
Version: 1.14.34
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: UNKNOWN
License: Apache License 2.0
Location: /usr/local/lib/python3.7/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: 

モジュール読み込みパスが足りていないじゃないのかと疑い、以下のスクリプトを作成し確認

import sys
import pprint
pprint.pprint(sys.path)

やはりLocation: /usr/local/lib/python3.7/site-packagesがないことが判明

['/Users/{USERNAME}/.ghq/repository_path',
 '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python37.zip',
 '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7',
 '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload',
 '/Users/{USERNAME}/Library/Python/3.7/lib/python/site-packages',
 '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages']

なんでこんなことが起こるのかまでは調べられてないけど、とりあえず、環境変数PYTHONPATHにLocationで表示されたパスを追加したらエラーは出なくなったのでよしとしようw

export PYTHONPATH='/usr/local/lib/python3.7/site-packages:$PYTHONPATH'  

後日談

python2系を動かすとエラーとなってしまったので、やはり根本解決にはなっていない模様

Your PYTHONPATH points to a site-packages dir for Python 3.x but you are running Python 2.x!
     PYTHONPATH is currently: "/usr/local/lib/python3.7/site-packages:$PYTHONPATH"
     You should `unset PYTHONPATH` to fix this.