TECH PLAY

Wedding Park/ウエディングパーク

Wedding Park/ウエディングパーク の技術ブログ

206

こんにちは。SREチーム  インフラエンジニアの綿引です。 今回は MySQL の透過的暗号化 について記載したいと思います。 因みに透過的暗号化が使用できるのは MySQL 5.7.11 から ですのでご認識のほど。 前回 は RDS の暗号化について軽く触れましたが、 その際はWebコンソールからボックスにチェックを入れるだけでした。 今回の MySQL の透過的暗号化を使用する場合には設定が必要です。 では早速やっていきたいと思います。 環境 ・OS : CentOS 7.1 ・データベース : MySQL 5.7.22 設定 まずは暗号化の前に検証用のテストテーブルを作成します。 ・データベース : test ・テーブル : test カラムは適当に id , name とし、2レコードほど、insert しております。 mysql> select * from test.test; +------+-------+ | id | name | +------+-------+ | 1 | test | | 2 | test2 | +------+-------+ 2 rows in set (0.00 sec) なお、エンジンは InnoDB です。 少し脱線致しますが MyIsam でも暗号化(alter table XX ENCRYPTION=’Y’)ができたのですが、 mysqldump を取得して、インポートする際に失敗したので、 暗号化する際はエンジンが InnoDB であることを確認した方が良いです。 こちらに関してはまた別途書きたいと思います。 では、次に OS 上でどのように見えるか確認します。 ibd ファイルはバイナリのため strings コマンドで確認します。 # strings /var/lib/mysql/test/test.ibd ・ ・ ・ test test2 先ほど insert した name 列の文字が確認できる状態ですね。 次は暗号化用の設定を行なっていきたいと思います。 プラグインのインストール まずはプラグインのインストールです。 $ mysql -uroot -p mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%'; Empty set (0.00 sec) mysql> install plugin keyring_file soname 'keyring_file.so'; Query OK, 0 rows affected (0.01 sec) mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%'; +--------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +--------------+---------------+ | keyring_file | ACTIVE | +--------------+---------------+ 1 row in set (0.00 sec) なお、MySQL 5.7.11 ではデフォルトのプラグインでしたが、 MySQL 5.7.12 以降はデフォルトで入っていないので明示的にインストールする必要があります。 my.cnf修正 次に my.cnf に以下の2行を追加していきます。 early-plugin-load=keyring_file.so keyring_file_data=/var/lib/mysql/mysql-keyring/keyring パラメータの意味としては、 1行目の early-plugin-load がロードするプラグインを指定します。 先ほどインストールしたキーリングプラグイン(keyring_file.so)ですね。 2行目の keyring_file_data はキーリングファイルの置き場所です。 他のデータベースファイルと混在しない場所が推奨されているので、 次の項目で専用のディレクトリを作成していきます。 因みに以下もパラメータとして必要なのですが、 MySQL 5.6.6以降はデフォルトで有効なのでいじってなければ大丈夫です。 innodb_file_per_table=1 キーリングファイルの置き場所(ディレクトリ)作成 上記で指定したディレクトリを作成していきます。 $ sudo mkdir -p /var/lib/mysql/mysql-keyring $ sudo ls -ltr /var/lib/mysql/ $ sudo chown -R mysql:mysql /var/lib/mysql/mysql-keyring $ sudo chmod 750 /var/lib/mysql/mysql-keyring $ sudo ls -ltr /var/lib/mysql/ MySQL 再起動 後は MySQL を再起動していきます。 $ sudo systemctl status mysqld $ sudo systemctl restart mysqld $ sudo systemctl status mysqld 暗号化するぞ! ここまで出来れば暗号化できる環境の準備は整いました。 暗号化の方法としては、以下です。 ・新規テーブル : Create オプションに 「ENCRYPTION=’Y’」 を付与する ・既存テーブル : 対象テーブルに対して、 「alter table テーブル名 ENCRYPTION=”Y”」 を実施する 今回は検証用にテーブルを作成しているので、 alter table 文で暗号化していきます。 まずは対象テーブルが暗号化されていないことを確認します。 確認には information_schema の tables を使用します。 mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables -> where TABLE_SCHEMA = 'test'; +--------------+------------+--------+----------------+ | TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS | +--------------+------------+--------+----------------+ | test | test | InnoDB | | +--------------+------------+--------+----------------+ 1 row in set (0.00 sec) 暗号化されると、上記の CREATE_OPTIONS に ENCRYPTION=’Y’ が入ります。 では alter table 文を実行します。 mysql> alter table test.test ENCRYPTION='Y'; Query OK, 2 rows affected (0.02 sec) Records: 2 Duplicates: 0 Warnings: 0 再度、information_schema の tables を確認しましょう。 mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables -> where TABLE_SCHEMA = 'test'; +--------------+------------+--------+----------------+ | TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS | +--------------+------------+--------+----------------+ | test | test | InnoDB | ENCRYPTION="Y" | +--------------+------------+--------+----------------+ 1 row in set (0.01 sec) CREATE_OPTIONS が ENCRYPTION=”Y” となっているので暗号化されたようですね。 実際にどうなっているか確認していきましょう。 当然ながら、select 文は問題なく発行できます。 mysql> select * from test.test; +------+-------+ | id | name | +------+-------+ | 1 | test | | 2 | test2 | +------+-------+ 2 rows in set (0.00 sec) では、先ほどは確認できた OS から strings コマンドを発行したらどうでしょう。 # strings /var/lib/mysql/test/test.ibd 2063f200-7132-11e8-bf60-08002751d15e T#*a 3omg ・ ・ ・ RaR&h es{Ds ,j6Z 先ほどは出力されていた、test などの文言が表示されず、 暗号化された文字列が出力されていますね。 想定通り暗号化できたようです。 最後にログを見て問題ないことを確認しましょう。 ERROR が出てる、、 以下のようなエラーが出ておりました。。 $ sudo cat /var/log/mysqld.log ・ ・ ・ 2018-06-16T08:14:49.059936Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:15:00.149256Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:15:55.047206Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:16:16.390285Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:16:21.920555Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:16:28.583733Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 2018-06-16T08:16:58.977404Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring' 今回キーリングファイルの置き先として /var/lib/mysql/mysql-keyring ディレクトリ配下を指定しましたが、 datadir にて /var/lib/mysql/ を指定しているので、 mysql-keyring ディレクトリをデータベースと捉えてしまったようです。 そこで、「これ何? DB? いる?」 というメッセージが ERROR として出力された模様。 確かに show databases で確認すると以下のような表記になります。 mysql> show databases; +------------------------+ | Database | +------------------------+ | information_schema | | mysql | | #mysql50#mysql-keyring | | performance_schema | | sys | | test | +------------------------+ 6 rows in set (0.00 sec) #mysql50#mysql-keyring というのがそれっぽいですね。 DML は問題なく発行できたので挙動的には問題なさそうですが、メッセージの量がハンパない、、 ざっと見た感じ MySQL の再起動時やテーブルアクセス時にも出力されているみたい。。 対応していきましょう。 方法としては2つあり、一つはキーリングファイルの置き場所を変更する。 もう一つは既存の設定はそのままにパラメータを一つ追加する方法です。 設定変更するのは面倒なので、後者を実施していきます。 my.cnf再修正 設定は簡単です。以下を追加します。 ignore-db-dir=mysql-keyring ですので最終的な my.cnf としては以下のようになりました。 early-plugin-load=keyring_file.so keyring_file_data=/var/lib/mysql/mysql-keyring/keyring ignore-db-dir=mysql-keyring MySQL 再起動 再度、再起動していきます。 $ sudo systemctl status mysqld $ sudo systemctl restart mysqld $ sudo systemctl status mysqld この後ログを確認しましたが、 もう [ERROR] Invalid (old?) table or database name ・・・ というエラーは出てなさそう。。 show databases の結果も問題なさそうです。 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test | +--------------------+ 5 rows in set (0.00 sec) まとめ 想定外もありましたが、透過的暗号化ができて良かったです。 特にアプリケーション側を変更せずに、インフラ側だけで対応できる点は非常に良いと思います。 皆さまも是非ご興味があれば。 この度は、ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは、SREチームの西脇(@yasuhiro1711)です。今回は特別番組ということで、33時間クッキング、【Kubernetesのラズベリーパイ包み ウエパ風】をお送りしたいと思います。見逃しそうな方はぜひ録画予約をお願いします。 サイバーの美味しそうなパイ包みを見て、ウエパでも物理的にパイ包みを作ってみることにしました。ローカルマシンやクラウドでもクラスターを組んで利用することは可能ですが、このクッキングを通じてKubernetesのそもそもの理解を深めることを目的としてやりました。 ※ なおこちらの記事は、至る所をサイバーエージェントを非常〜〜にオマージュしおります。この記事を了承頂きまして、本当にありがとうございます! オマージュ記事は最後にリンクがございます。 一家に一台、Kubernetesの時代に この10年でスマホが爆発的に普及したように、Kubernetesが一家に一台の時代がやってきます。本日作るラズベリーパイ包みはそんな時代にもってこい。家庭でお子様からおじいちゃんまで幅広く愛されるであろう、手のひらサイズのKubernetesです。 完成品 完成品はこちらとなります。ジャーン! 材料 (1クラスタ分) では、まずは材料のご紹介です。 材料名 個数 Raspberry Pi 3 Model B 3 個 ヒートシンク 3 セット microSD カード (16 GB) 3 枚 4 段積層式 Raspberry Pi 3 ケース 1 個 6 ポート 50W USB 充電器 1 個 2.4A microUSB ケーブル 5 本 コンパクト無線親機 (WMR-433W-BK) 1 個 microUSB 給電 スイッチングハブ (LAN-SW05PSBE) 1 個 0.3m LAN ケーブル 3 本 0.15m LAN ケーブル 1 本 3M 強力両面テープ 少々 結束テープ 少々 六角スペーサー 適量 ウエパ のステッカー プライスレス これだけの材料を集めるとこんな感じになりました。 そのままでも美味しそうなラズベリーパイが6個もありますね。 作り方 ネタの下準備 1, ラズパイの開封 まずは、ラズベリーパイを開封します。 傷つけないよう気をつけましょう。 2, ヒートシンク設置 続いてヒートシンクをつけます。両面テープを剥がすのが意外に辛かったです。魚の鱗取りのようでした。 3, 4 段積層式のケースの 下 3 段に Raspberry Pi を取り付け アクリル板の保護シールを剥がします。剥がしづらいと聞いていましたが今回購入のものは、爪を使えば意外と簡単でした。 剥がしたらアクリル板にラズパイを取り付けます。このようにできればOKです。わくわくわく! その後,2段目、3段目と作ってまいります。おーいい感じ。 4, 最上段に USB 充電器を取り付け 写真のようなかたちでUSB充電器をとりつけます。両面テープでがっちりとめましょう。ちなみに今回の充電器はギリギリの大きさでしたので、購入時に大きさはしっかり見ておいた方がよかったです。 設置後、天板まで設置ができました。 5, 最上段にスイッチングハブとコンパクト無線親機を取り付け それぞれ両面テープで固定します。向きはあなたの感性で。ケーブルの長さには注意しましょう。 6, 最後にケーブリングを丁寧に実施 ここがエンジニアそれぞれの個性を出すところです。私はこんな感じで決めました!  7, ネットワークの設定 一番上に設置している、無線LANルーターに接続します。その管理画面にて、別wifiに接続することでインターネットへの疎通もとれます。 OS のインストール ここからの部分は、いくつかの方法を試して理解を深めましたが、ここではパッケージを用いた分かりやすい構築手順を記載して進めていきます。まずは、Raspberry PIにOSをインストールします。今回はRaspbianを利用します。 マイクロSDカードにOSイメージ作成 これより、マイクロSDカードにイメージを書き込みする作業をmacOS上で行います。 以下のURLからRaspbian stretch liteをダウンロードします。 https://www.raspberrypi.org/downloads/raspbian/ ダウンロードしたzipファイルを解凍して、マイクロSDカードに書き込みます。 ディスクのパスは環境ごとに違うので注意が必要です。 // zipファイルを解凍する $ cd ~/Download $ unzip 2017-09-07-raspbian-stretch-lite.zip Archive: 2017-09-07-raspbian-stretch-lite.zip inflating: 2017-09-07-raspbian-stretch-lite.img // SD card のパスを確認 $ diskutil list // SD card をアンマウントする sudo diskutil umountDisk /dev/disk2 // dd コマンドでイメージを書き込む $ sudo dd bs=1m if=2018-04-18-raspbian-stretch-lite.img of=/dev/rdisk2 conv=sync 1Mと説明しているサイトが多いが、小文字ですんなりいけた。なんやねん。 OSイメージをSDカードに書き込めたら、いくつか設定を行います。これらの作業もmacOS上で行います。 ssh の有効化 sshを有効にするための設定をしますにsshという名前のファイルを作成します。 $ cd /Volumes/boot $ touch ssh この作業を行うことにより、Raspbianの起動後にsshdが起動し、リモートログインが可能になります。 # cgroups の有効化 次にcgroupsのcpusetとmemoryを有効化するために、以下の設定を行います。 $ cd /Volumes/boot $ vi cmdline.txt cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1を追加します。 Bootと初期設定 作成したSDカードを、Raspberry PIに挿入し、電源をいれます。 起動後はデフォルトでraspberrypi.localの名前でアクセスすることができます。 slogin pi@raspberrypi.local (初期パスワードは raspberry) その後、各OSのホスト名の設定、hostsの設定を行います。3台は、k8s01(master)、k8s02(node)、k8s03(node) としました。 スワップ削除 k8s 1.8からスワップが有効だとkubeletが起動しないので実施する。 sudo dphys-swapfile swapoff sudo dphys-swapfile uninstall sudo update-rc.d dphys-swapfile remove DockerとKubernetesコンポーネントのインストール Docker インストール curl -sSL https://get.docker.com/ | sh kubelet kubeadm kubectl kubernetes-cni のインストール kubelet kubeadm kubectl kubernetes-cniをインストールしていきます。 apt-transport-httpsをインストール 自分の環境では既にインストール済みでした。 apt-get install -y apt-transport-https 鍵登録 curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - aptのソースリストにkubernetesを追加 echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list aptを更新します。 sudo apt-get update kubernetesの各モジュールをインストールします。 apt-get install kubelet kubeadm kubectl kubernetes-cni ※ インストールに成功すると自動的にkubeletが起動します。 masterのセットアップ(k8s01でのみ実施) master のセットアップを行います。 以下を実行 sudo kubeadm init --pod-network-cidr=10.244.0.0/16 もしinitに失敗していたら、kubeadm reset で戻せます。成功すると以下のようなメッセージが流れます。 Your Kubernetes master has initialized successfully! To start using your cluster, you need to run (as a regular user): mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: http://kubernetes.io/docs/admin/addons/ You can now join any number of machines by running the following on each node as root: kubeadm join --token 6391e6.b4b1bb96a7bdea7c 192.168.13.11:6443 --discovery-token-ca-cert-hash sha256:90be9b3dd4d44c1225d5c3e52af49db25d5c3e5226a5a708fe0af36dadb kubeadm init で表示されたメッセージに従って、以下のコマンドも実行。 rootから一般ユーザになって実行します。 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl が正しく実行できるか確認。 $kubectl version Client Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.5", GitCommit:"cce11c6a185279d037023e02ac5249e14daa22bf", GitTreeState:"clean", BuildDate:"2017-12-07T16:16:03Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/arm"} Server Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.13", GitCommit:"290fb182489a396dce5d136451388f9b12f29c94", GitTreeState:"clean", BuildDate:"2018-05-15T18:08:47Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/arm"} kubectl get nodes kubectl get nodes NAME STATUS ROLES AGE VERSION k8s01 NotReady master 5m v1.8.5 kubectl get pods –all-namespaces -o wide kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE kube-system etcd-k8s01 1/1 Running 0 6m 192.168.13.11 k8s01 kube-system kube-apiserver-k8s01 1/1 Running 0 6m 192.168.13.11 k8s01 kube-system kube-controller-manager-k8s01 1/1 Running 0 6m 192.168.13.11 k8s01 kube-system kube-dns-66ffd5c588-fns2f 0/3 Pending 0 6m <none> <none> kube-system kube-proxy-tfwl8 1/1 Running 0 6m 192.168.13.11 k8s01 kube-system kube-scheduler-k8s01 1/1 Running 0 6m 192.168.13.11 k8s01 おや、 kube-system kube-dns-66ffd5c588-fns2f 0/3 となっており、dnsがうまくうごいていないことがわかります。 falnnel のデプロイ kubectl apply -f <(curl -s https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml |sed 's/amd64/arm/g') マスター(k8s01)に、node2台をjoin(k8s02,k8s03) masterで、init が成功した時に、トークンも含めたjoinのコマンドが流れました。これでnodeを登録をします。 kubeadm join --token 6391e6.b4b1bb96a7bdea7c 192.168.13.11:6443 --discovery-token-ca-cert-hash sha256:90be9b3dd4d44c1225d5c3e52af49dbf611baf1a611d926a5a708fe0af36dadb 登録後 まだNotReadyですが、、 pi@k8s01:~ # kubectl get nodes NAME STATUS ROLES AGE VERSION k8s01 Ready master 15m v1.8.5 k8s02 NotReady <none> 35s v1.8.5 k8s03 NotReady <none> 34s v1.8.5 Readyに変わります。 pi@k8s01:~ # kubectl get nodes NAME STATUS ROLES AGE VERSION k8s01 Ready master 16m v1.8.5 k8s02 Ready <none> 1m v1.8.5 k8s03 Ready <none> 1m v1.8.5 できましたね。これでクラスターが組めましたのでこの上にpodを作成してアプリケーションを乗せて検証できるようになりました。 まとめ 【Kubernetesのラズベリーパイ包み〜ウエパ風〜】を作って見ました。 物理的であるので実際に自らkubernetesの動きを理解していくのには、非常によいものとなりました。また、いくつかのパターンでの構築や、dashbordやmetallbのテストなどにも踏み込んでいけたので、研修としてGoodです。 オライリーの入門Kubernetesにも似た例が載っているので参考にしてみてください。 エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集! オマージュ記事 お世話になりました。ありがとうございます。 3日間クッキング【Kubernetes のラズペリーパイ包み “サイバーエージェント風”】 https://developers.cyberagent.co.jp/blog/archives/14721/
SREチーム  エンジニアの阿久津です。 今回はNginxの403 Forbiddenが表示された時のチェックポイントについて記事にしたいと思います。 環境 Vagrant 1.9.5 CentOS Linux release 7.1.1503 (Core) Nginx 1.14.0 前提 設定ファイル  /etc/nginx/default.conf DocumentRoot  /var/hoge テストページ  /var/hoge/index.html 事象 テストページを表示しようとすると403 Forbiddenが表示される。 チェックポイント ①Nginxの再起動をしていない 設定ファイルを変更した場合、Nginxを再起動しないと変更が反映されません。 そのため再起動していない場合は実施します。 ■再起動 $ sudo systemctl restart nginx.service ■リロード Nginxを終了せずに、変更した設定の反映を行います。 $ sudo systemctl reload nginx.service ②ファイルに読み取り権限がない 権限には「所有者」「所有グループ」「その他のユーザ」がありますが、「その他のユーザ」に読み取り権(r)が付与されているか確認してください。 ※今回の場合だと、/var/hoge/index.html の権限になります。 ■その他のユーザに読み取り権がない場合 $ ls -l /var/hoge/index.html -rw-r----- 1 root root 9 Jun 1 08:14 index.html ■対応 ファイルに読み取り権を付与します。 $ sudo chmod o+r /var/hoge/index.html $ ls -l /var/hoge/index.html -rw-r--r--. 1 root root 9 Jun 1 08:14 /var/hoge/index.html ③ディレクトリの権限に実行権がない DocumentRootまでの各ディレクトリで、「その他のユーザ」に実行権(x)が付与されているか確認してください。 今回だと「/」, 「/var」, 「/var/hoge」の3つの内のいずれかにディレクトリに実行権(x)がないと、テストページが表示されません。 ■ /var/hoge/ に実行権がない場合 $ ls -ld /var/hoge/ drwxr-xr--. 2 root root 23 Jun 1 08:14 /var/hoge/ ■対応 ディレクトリに実行権を付与する。 $ sudo chmod o+x /var/hoge $ ls -ld /var/hoge drwxr-xr-x. 2 root root 23 Jun 1 08:14 /var/hoge/ ④特定のURLへのアクセス制限してしまっている Nginxで特定のURLへのアクセス制限をする場合、confファイルのlocation内に記述します。 location内でアクセスを拒否していないか確認してみてください。 ■すべてのアクセスを拒否する場合 $ cat /etc/nginx/conf.d/default.conf ... location / { root /var/hoge; index index.html index.htm; deny all; # 全てのアクセスを拒否 } ... 上記のように「deny all;」と記述すると、どこからでもアクセス不可となってしまいテストページが表示されません。 そのため以下のような方法でテストページへのアクセスを許可することができます。 ■すべてのアクセスを許可する場合 ... location / { root /var/hoge; index index.html index.htm; allow all; # 全てのアクセスを許可 } ... ■特定のIPのみアクセスを許可する場合 ... location / { root /var/hoge; index index.html index.htm; allow 192.168.33.1; # 特定のIPのみアクセスを許可 deny all; } ... 上記の書き方だと 192.168.33.1 からのアクセスのみ許可しています。 ⑤SELinuxが有効になっている SELinuxが有効になっている場合、DocumentRootへのアクセスが拒否されている可能性があります。 SELinuxを利用する予定がない場合は無効に設定しましょう。 ⑤-1. SELinuxを無効にする場合 ■SELinuxの状態確認 $ getenforce Enforcing Enforcingと表示される場合、SELinuxは有効になっている状態です。 ■SELinuxを無効にする SELinuxの設定ファイルは /etc/selinux/config になるため、こちらを編集します。 $ sudo vi /etc/selinux/config ... SELINUX=disabled ← enforcing から disabled に変更 ... ■サーバ再起動 SELinuxの設定を反映するためにサーバの再起動を行います。 $ sudo shutdown -r now ■SELinuxの状態を再度確認 $ getenforce Disabled Disabledと表示されれば、SELinuxは無効に変更されています。 ⑤-2. SELinuxを有効のまま運用する場合 新しいDocumentRootにはSELinuxのラベルを付ける必要があります。 ■元DocumentRootのラベルを確認 $ ls -ld --context /usr/share/nginx/html drwxr-xr-x. root root system_u:object_r:usr_t:s0 /usr/share/nginx/html ■ラベルの付与 新DocumentRootに元DocumentRootと同じラベルを付与します。 $ sudo chcon system_u:object_r:usr_t:s0 /var/hoge -R ■ラベルの確認 以下のように表示されていれば、元DocumentRootと同じラベルが付与されています。 $ ls -ld --context /var/hoge drwxr-xr-x. root root system_u:object_r:usr_t:s0 /var/hoge まとめ 今回はNginxで403 Forbiddenが表示された時のチェックポイントを記事にさせていただきました。 以前Apacheでも403エラーの壁にぶち当たったことがあるのですが、Nginxでも403エラーの壁に阻まれてしまいました…NginxもApacheと似ていて、ファイルやディレクトリの権限周りやSELinuxを見直せば403エラーは解消できることがわかりました。 ぜひ参考にしていただけると幸いです。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは、SREチーム、エンジニアの西脇(@yasuhiro1711)です。今日は、circleci/go-ecs-ecrを使って、CircleCI からECS にデプロイをしてみたいと思います。(参考リンクには非常にお世話になりました。ありがとうございます。) 今回は題材にちょうど合う、CircleCIを通じて、AWS ECS/ECR にデプロイするGoアプリケーションがあったのでこれを利用していきます。勉強の題材にとてもよかったです。(しかし注意として、今回利用の「circleci/go-ecs-ecr」は、2018年6月現在、すでに更新されていないため、現在のCircleCIとAWS環境に自分で合わせないと動作できない可能性もあります。ご注意ください。) circleci/go-ecs-ecr: Example project for deploying a Go application to AWS ECS/ECR via CircleCI. [https://github.com/circleci/go-ecs-ecr](https://github.com/circleci/go-ecs-ecr) 最終的に目指すのはこのような設計です。(ECSクラスタ部分は図としては詳細には書いておりません。) 今回の構成では、GitHubにユーザからpushがされると、CircleCIがそれを自動検知し、build実行を始めます。すると最新のソースがECRのレジストリに登録されて、ECSにて定義更新等が走り、EC2内のDockerコンテナにデプロイされる仕組みです。 事前準備 これらは事前に準備しておきましょう。今回は詳細は触れません。 GitHubアカウント CircleCI(GitHub連携)アカウント AWS Credentials ローカルMac端末にてaws cliコマンド導入済みであること circleci/go-ecs-ecr準備 早速始めましょう。zipダウンロードかforkしてきます。 GitHub上でポチポチして、forkします。 ソースの中身を見てみます。注意としては、このままだとリージョンが全てバージニアになるので、東京に全部変更してしまいましょう。 対象ファイル:circle.yml 、deploy.sh 変更点:バージニア (us-east-1) から 東京 (ap-northeast-1) に変更する。 AWSのECRサービスにリポジトリを作成 ECR に go-sample-webapp という名前でリポジトリを作成しておきます。名前は今回利用のアプリケーションと揃えます。 一覧から、リポジトリの作成を選択します。 リポジトリ名を設定します。 リポジトリの作成完了 完了すると、ECRへの接続方法がここで詳しく表示されます。(これは後からでも参照できますので、大丈夫です。) docker-login つづいて、docker-pushの時にログインを促されるため、aws ecr get-login コマンドで,ECR にログインできることを確認しておきます。(ちゃんとみてないですが、今回の場合は実はいらないかもしれない。) ローカルMACにて、 $ $(aws ecr get-login --region ap-northeast-1) 実施すると、エラーが発生。対応します。以下が原因でした。 小ネタ1 変なeが出力されるぞ。これを参考にして。 【小ネタ】Docker17.06以上からAmazon ECRへのdockerログインコマンドが変わった dockerが17.06以上だと、 –no-include-email オプションが必要なのだそう。 $(aws ecr get-login –region ap-northeast-1 –no-include-email) これに変更することで無事に実行。 そしてもう一つ。 小ネタ2 ローカルmacで、Docker起動してないと、以下のようなエラーになるよ。たまに忘れるので。 bash-3.2# $(aws ecr get-login --region ap-northeast-1 --no-include-email) WARNING! Using --password via the CLI is insecure. Use --password-stdin. Warning: failed to get default registry endpoint from daemon (Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?). Using system default: https://index.docker.io/v1/ Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? というわけで、 $(aws ecr get-login --region ap-northeast-1 --no-include-email) で実行。 実行履歴 bash-3.2# $(aws ecr get-login --region ap-northeast-1 --no-include-email) WARNING! Using --password via the CLI is insecure. Use --password-stdin. Login Succeeded ECS で仮のタスク定義を作成 CircleCIで実行するbuildで、自動的にECSの「タスク定義」を作成されるようにするため、ここでは ECSクラスタを起動するためだけに仮のタスク定義を作成しておきます。 タスク定義にて、「新しいタスク定義の作成」を選択し、 名前は sample-webapp-task としておきます。 ※コンテナ定義も今回は仮で適当に入れて進めます 次に ECSクラスタと ECSサービスを作成していきます。 ECSクラスタを作成 まずは、ECSクラスタを作成しましょう。 クラスターの作成 を選択。 テンプレート選んで、 今回は以下の項目で設定しました。 クラスタ名 : sample-webapp-cluster インスタンスタイプ : t2.micro インスタンス数 : 3 ネットワーク : 既存VPCを利用 セキュリティ : TCP 80をIP制限かけて解放 コンテナインスタンス IAM ロール : デフォルト 作成完了! 続いて、ECSサービスの作成に取り掛かります。 ECSサービス作成 作成したクラスターに入って、サービスタブで、「作成」 こんな感じで設定します。 サービス名 : sample-webapp-service タスク定義名 : sample-webapp-task-family(自動的にデプロイされるので設定はなし。) Desired Count : 1 最小ヘルス率 : デフォルト 最大率 : デフォルト ELB :  なし autoscalling : なし 画像と数値が違いますが、この画面です。 作成が完了すると、このような画面に遷移します。 CircleCI の設定 続いて、CircleCIでの作業です。通常、初めて利用する場合はCircleCIとGithubの連携をしないといけませんが、今回は省きまして、プロジェクトの追加からです。 project を追加 Add Projectsを選択 今回のprojectで、「Set up Project」ボタンをクリックして、projectの設定に進みます ※ 今はCicleCIがversion2.0 になっているので、この画面では、2.0を促してきます。 2.0にしたい場合はここでこの手順のように、2.0の書き方にymlを変更する必要があります。) これでプロジェクトは登録完了です。このままbuildしたいけど、追加設定が必要です。 利用IAMユーザへ権限付与(作業はAWS) CircleCI用の IAM User を作成します。 今回は、既存アカウントを利用しましたが、利用ユーザに以下権限をアタッチします。 AmazonEC2ContainerRegistryFullAccess AmazonEC2ContainerServiceFullAccess Environment Variables に環境変数を設定 以下の環境変数を設定します。 AWS_ACCOUNT_ID xxxxxxxx AWS_ACCESS_KEY_ID xxxxxxxx AWS_SECRET_ACCESS_KEY xxxxxxxx 左メニューからinsightsを選択し、今回のプロジェクトの歯車ボタンを押す。 メニューから環境変数設定を選び、設定。 設定したら、buildを走らせます。 お! deploy.sh の下記で止まりました。 確認してみて、 最終的に、 $(aws ecr get-login --region ap-northeast-1 --no-include-email) の記述を、 $(aws ecr get-login --region ap-northeast-1) に変更することで解決しました。うーん、なるほどです。 その結果 ビルド成功 デプロイされ、起動したアプリのIPに接続すると、無事に、「Hello World!」の表示が確認できます。 これで、一連の設定が出来ました。せっかくなのでこのまま、アプリを編集しての再デプロイしてみましょう。 継続的インテグレーションしてみる アプリの出力を「Hello World!」から「Hello World! weddingpark」と変更します。(Hello World!の文字列出力部分はbuildの時のtestでも利用しているので変更には気をつけましょう。) 今回はテストなので、GitHub上で修正してpushします。すると、自動でCircleCIがキックされbuildを開始し、デプロイまでされて、、 成功! 簡単にアプリ修正からリリースまでが自動で実行できました。 デプロイの仕組み 今回の仕組みで大事なのは、CircleCI向けの設定と、deploy.sh の設定です。それぞれどんなことをしているのか、簡単に見て見ます。 circle.yml docker build を実行して、Dockerイメージを作成。 テストでは docker runでコンテナを実行、起動。アプリの起動を確認する。 masterブランチにpushされた場合には deploy.sh を実行する。 deploy.sh AWS CLI の設定をする ECR に Dockerイメージをpushする タスク定義のリビジョンを更新。最新のDockerイメージをサービスに反映 サービスに紐付くタスク定義を更新して ECSクラスターにデプロイをする エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集! とてもお世話になった参考サイト CircleCI で ECS にデプロイをするサンプルプロジェクト circleci/go-ecs-ecr を試した – kakakakakku blog CircleCI+ECS+ECR環境でDockerコンテナのCD(継続的デプロイ)環境を構築する -後編- | Developers.IO
こんにちは。QAチームでマネージャーをしているエンジニアの斉藤(@saik1010)です。 弊社QAチームで品質向上を目的として取り組んでいる、 Python の Webスクレイピング を使用した自動リンクチェッカー(HTTPステータスコードのチェック)について、 環境構築〜実際にリンクチェックを実行するところまでご紹介しようと思います。 環境 環境 バージョン macOS Sierra 10.12.6 Python 3.6.4 pyenv 1.2.1 1. Pythonのインストール Homebrew を使用し、Python3をインストールします。 汎用性の考えて、バージョン切り替えが容易にできる pyenv を使用します。 pyenvのインストール $ brew install pyenv インストールしたいpythonのバージョンを確認 $ pyenv install --list pythonをインストール $ pyenv install 3.6.4 pythonのバージョン変更 $ pyenv global 3.6.4 PATHを通す $ vi ~/.bash_profile ↓追記する export PATH="$HOME/.pyenv/shims:$PATH" 設定を反映する $ source ~/.bash_profile 2. Pythonライブラリのインストール requests HTTP通信を行うためのライブラリです。 http://requests-docs-ja.readthedocs.io/en/latest/ $ pip install requests BeautifulSoup HTMLやXMLファイルからデータを取り出すためのライブラリです。 https://www.crummy.com/software/BeautifulSoup/bs4/doc/ $ pip install beautifulsoup4 3. HTTPステータスコードのチェック インストールしたライブラリを利用して、HTTPステータスコードを実装します。 まずはPythonファイルを作成し、以下のコードを書きましょう。 ※私は、 test.py としました ソースコード import requests from bs4 import BeautifulSoup # アクセスするURL resp = requests.get("https://www.google.co.jp/") # BeauttifulSoupを使用してHTML整形 soup = BeautifulSoup(resp.text, 'html.parser') # aタグからURLを取得し、HTTPリクエストを送る for link in soup.find_all('a'): # URLを取得し、HTTPリクエスト url = link.get('href') if url is None: continue elif url == '#': continue resp = requests.get(url) # 結果をCSVに出力する f = open('test.csv','a') f.write(str(resp.status_code) + ',' + url + '\n') f.close() 実行する $ python test.py 実行結果 下記内容が test.csv に出力されていればOKです。 200,https://www.google.co.jp/imghp?hl=ja&tab=wi 200,https://www.youtube.com/?gl=JP&tab=w1 200,https://www.google.co.jp/intl/ja/options/ 200,http://www.google.co.jp/history/optout?hl=ja 200,https://accounts.google.com/ServiceLogin?hl=ja&passive=true&continue=https://www.google.co.jp/ 200,http://www.google.co.jp/intl/ja/services/ 200,https://www.google.co.jp/setprefdomain?prefdom=US&sig=__0sQtcCi2TjmCTGxrJwKLaSvy39U%3D まとめ いかがでしたでしょうか? かなりシンプルにHTTPステータスコードチェックの実装ができました! HTMLにある要素はなんでも取得できそうなので、ステータスコード以外にも幅広く使えそうです。次回は再帰処理を利用して、リンクを深掘りした実装をしていこうと思います! ◉筆者執筆の「5分でできる自動化」シリーズ 5分でできる!Seleniumでブラウザテスト自動化入門 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。SREチーム インフラエンジニアの綿引です。 2018/4/24 に Node.js 10系がリリースされましたね。 (私の誕生日です。) そこで今回は Node.js のアップデートを Ansible を用いて実施する という記事を書きたいと思います。 Node.js のアップデート方法に関しては、 1. 既存の Node.js(過去に yum でインストール)をアンインストール後、 2. Ansible の shell モジュール を用い Node.js を rpm でインストールする という形にしました。 10 系を使うものの Node.js に関しての性能や新機能などの検証は行わないので ご容赦のほどをお願い致します。 Node.js のバージョンについて まず Node.js のバージョンに関してですが、 今回 10系をインストールするにあたって LTS などの用語をもう一度調べ直してみました。 LTS (Long Term Support) Node.js のメジャーバージョン(6.X 、8.X など)は大きく分けて偶数と奇数に分けられます。 偶数 が LTS版 と呼ばれる、サポート(※1)が長期に受けられるもので、 奇数 が最新機能版で、サポート期間も LTS版に比べると非常に短くなっております。 ※1 … バグの修正やセキュリティアップデート リリースステータス リリースステータス ・ 各ステータスの期間などに関しては、 Github にて公開されている以下をご確認頂くのが最も早いと思うので、 リンクを載せておきます。 https://github.com/nodejs/Release 因みに私なりにまとめると、、 Node.js のリリースステータスとして、以下の3つが存在します。 ・CURRENT ・ACTIVE LTS ・MAINTENANCE LTS リリースされると、 LTS版、奇数版、どちらもステータスとしては『CURRENT』になり、その後LTS版のみ、 長期サポート期間である 『ACTIVE LTS』・『MAINTENANCE LTS』 ステータスに移行します。 奇数版に関しては、『CURRENT』が終了した時点でサポートが終了する仕組みのようです。 それぞれの期間に関しては、 『CURRENT』 が 約6ヶ月 、『Active』 が 18ヶ月 、『Maintenance』 が 12ヶ月 となっているようです。 上記を全てまとめると以下です。 リリースステータス 内容 期間 CURRENT  現在の最新版  約6ヶ月 ACTIVE LTS  サポートが積極的に行われる  18ヶ月 MAINTENANCE LTS  最低限のサポートが行われる  12ヶ月 最後に Node.js 10系 のサポート期間 に関してですが、 LTS版なので以下のようになります。 Node.js 10系 の リリースステータス 期間 CURRENT 2018/4 〜 2018/10 Active LTS 2018/10 〜 2020/4 Maintenance LTS 2020/4 〜 2021/4 こちら参考にして頂けたら幸いです。 環境 では Node.js にも詳しくなったところで、早速構築してきます。 環境に関しては サーバ 1台、クライアント 2台としており OS は CentOS 6、7 どちらも使用する形で進めて行きたいと思います。 環境情報の詳細は以下です。 ローカル ・macOS 10.13.3 ・Vagrant 2.0.3 サーバ (1台) ・ホスト名 ans ・CentOS 7.1 ・Ansible 2.4.2.0 クライアント (2台) 1台目 ・ホスト名 anc ・CentOS 7.1 ・Node.js 6.8.1 (過去に yum で入れております) 2台目 ・ホスト名 an6 ・CentOS 6.5 ・Node.js 6.8.1 (過去に yum で入れております) ※ ホスト名は適当につけたので気にしないでください。 図にするこのような感じです。 Ansible 設定 Ansible の設定は以下となります。 ディレクトリ構成 ディレクトリ構成は以下のようにしております。 ベストプラクティス にならって ansible-playbook を実行する際は、 トッププレイブックである node_version_up-YYYYMMDD.yml をキックし、 処理自体はその配下のディレクトリの Roles 配下で制御していきます。 /opt └ ansible/ ├ node_version_up-YYYYMMDD.yml # トッププレイブック ├ hosts # インベントリファイル └ playbook/ ├ nodejs_version_up.yml # トッププレイブックから読み込むプレイブック └ nodejs_version_up/ # Roles └ tasks └ main.yml node_version_up-YYYYMMDD.yml(トッププレイブック) トッププレイブックの中身としては nodejs_version_up.yml を読み込むだけになっております。 --- - include: playbook/nodejs_version_up.yml hosts(インベントリファイル) インベントリファイルには、クライアント2台を an_clients というグループとして設定致しました。 [an_clients] anc an6 nodejs_version_up.yml (トッププレイブックから読み込むプレイブック) こちらは roles を用い、 nodejs_version_up ディレクトリ配下のファイルを実行する形になっています。 # /opt/ansible/playbook/nodejs_version_up.yml - hosts: an_clients remote_user: vagrant become: yes roles: - nodejs_version_up main.yml tasks ディレクトリ配下の実行ファイルです。 処理自体は非常に簡単でしたので、シンプルな作りとしております。 中身に関しては冒頭でも記載致しましたが、 一度、アンインストールして rpm でインストールする形をとっております。 # file: /opt/ansible/playbook/nodejs_version_up/tasks/main.yml - name: remove nodejs yum:  name: nodejs state: absent - name: install nodejs 10.3.0 shell: rpm -Uvh https://rpm.nodesource.com//pub_10.x/el/6/x86_64/nodejs-10.3.0-1nodesource.x86_64.rpm 実行 では実行して行きます。 [vagrant@ans ~]$ cd /opt/ansible [vagrant@ans ansible]$ ansible-playbook -i hosts node_version_up-20180601.yml [DEPRECATION WARNING]: 'include' for playbook includes. You should use 'import_playbook' instead. This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. PLAY [an_clients] ************************************************************************************************************************************************************************************************************ TASK [Gathering Facts] ******************************************************************************************************************************************************************************************************* ok: [anc] ok: [an6] TASK [nodejs_version_up : remove nodejs] ************************************************************************************************************************************************************************************* ok: [anc] ok: [an6] TASK [nodejs_version_up : install nodejs 10.3.0] ***************************************************************************************************************************************************************************** [WARNING]: Consider using yum, dnf or zypper module rather than running rpm changed: [anc] changed: [an6] PLAY RECAP ******************************************************************************************************************************************************************************************************************* an6 : ok=3 changed=1 unreachable=0 failed=0 anc : ok=3 changed=1 unreachable=0 failed=0 では確認していきます。 クライアント 1台目(CentOS7) こちらは問題なさそうです。 [vagrant@anc ~]$ node -v v10.3.0 [vagrant@anc ~]$ [vagrant@anc ~]$ npm -v 6.1.0 クライアント 2台目 (CentOS6) こちらも大丈夫そうですね。 [vagrant@an6 ~]$ node -v v10.3.0 [vagrant@an6 ~]$ [vagrant@an6 ~]$ npm -v 6.1.0 [vagrant@an6 ~]$ まとめ Node.js を Ansible でアップデートする情報より、Node.js の LTS の情報の方に力を入れてしまいました。 Ansible に関しては、対象機能のプレイブックを一回作ってしまえば、 あとは使い回すだけなので運用が非常に楽になりオススメです。 引き続き Ansible を活用していきたいと思いました。 この度は、ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。QAチームでマネージャーをしているエンジニアの斉藤(@saik1010)です。 今回は、弊社と ユニファ株式会社 さんのエンジニア・デザイナーチームで開催した合同LT会(ライトニングトーク)の様子をレポートしたいと思います! 合同LT会の目的 他社のエンジニア・デザイナーとの交流や情報収集を目的として、3ヶ月〜半年に1回のスパンで合同LT会を開催しております。事前にお互いの会社に聞いてみたいことを擦り合わせ、テーマ設定をした上で進めていきます。外部のLT会とは違い、2社というクローズドな場ということもあり、少し踏み込んだ話をできるという点も、合同LT会ならではの大きな魅力の1つです! 開催に至った経緯 弊社QAチームは、今年の1月に立ち上がったばかりの新しいチームのため、情報収集も兼ねて JaSST’18 Tokyo のイベントに参加しました。その中の1つのセッションである 「Web.JaSST ~ウェブ系QAがみんなのお悩みに全力で提案を返す会~」 に登壇されていたユニファさんのQAマネージャーの方と交流をさせて頂いたことから、今回の合同LT会を実施するに至りました! テーマ設定 今回は、お互いに知りたいことを事前に打ち合わせた結果、以下のテーマでLT会を実施致しました。 ▼ウエディングパーク→ユニファ ・QAチームがいる組織の開発方法 ・自動テスト(ユニットテスト)の進め方 ▼ユニファ→ウエディングパーク ・サービスをリプレイスした時の話 ・事業部を分けたサービスで開発しているエンジニアの話 当日の様子 当日は緊張を早めにほぐして、リラックスした場にしたいという意図もあり、ビアバッシュ形式で乾杯からスタート!まずはお互いの会社紹介も兼ねて、会社説明を実施した後にLT発表を行いました! その後はテーマに沿って、お互いにLT発表を実施しました!質問も多く飛び交い、予定時間をオーバしてしまうほど大盛り上がり! LT発表後は懇親会!弊社側の参加人数が多く、ユニファさんを囲ってしまう形になりましたが、懇親会も大盛り上がりで時間もあっという間に! まとめ 今回はQAチーム同士のご縁があり、合同LT会を開催することができました。 お互いに事前にテーマ設定したこともあり、外部の勉強会ではなかなか聞けない、踏み込んだ内容のお話しすることができ、両社にとって非常に有意義な時間になりました!その場で次回も開催しましょうとの声も上がるほど! ユニファさんありがとうございました!! 補足 こちらの内容については、弊社の Wantedly でも紹介しておりますので、よろしければご覧ください。 また、ユニファさんの 技術ブログ や Wantedly でもご紹介して頂いておりますので、合わせてご確認ください! ※弊社と一緒に合同LT会をやってみたいという企業様がいらっしゃいましたら、是非お声がけ頂けると嬉しいです! Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
初めまして。SREチーム エンジニアの阿久津です。 今回は仕事の一環でLinuC(LPIC) Level1を取得したことについて記事にしたいと思います。 ※受験した当時は「LPIC」という名称でしたが、今は「LinuC」という名称に変わったようですね。 1. LinuC(LPIC)について 1-1. LinuC(LPIC)とは Linux技術を証明するための資格試験の一つ。正式名称は「Linux Professional Certification」で、略して「リナック」と読みます。 2016年12月時点で、全世界で53万人以上(国内だと約29万人)が受験して、18万人以上の認定者が生まれている資格とのことです。 引用 : LPI-JAPAN 「LPICの魅力をデータで見る」 1-2. LinuC(LPIC)の種類(グレード) LinuC(LPIC)には以下3つのレベルが存在します。 ※今回はLinuC(LPIC) Level1の101試験と102試験を受験しました 1-3. LinuC(LPIC) Level1出題範囲 ■101試験 システムアーキテクチャ Linuxのインストールとパッケージ管理 GNUとUnixのコマンド デバイス、Linuxファイルシステム、ファイルシステム階層標準 ■102試験 シェル、スクリプト、およびデータ管理 ユーザインターフェイスとデスクトップ 管理業務 重要なシステムサービス ネットワークの基礎 セキュリティ 2. 勉強方法 2-1. LinuC(LPIC)とLinuxについて知る まず最初にLinuC(LPIC)を詳しく知るために入門書を購入しました。 ※購入した本は以下になります。 1週間でLPICの基礎が学べる本 第2版 (徹底攻略) この入門書はLinuC(LPIC)の受験方法やLinuxの基本操作について書かれている内容になってます。 LinuC(LPIC)を詳しく知りたい、Linuxの基礎を学びたい人にはオススメです。 2-2. LinuC(LPIC)の試験問題を解いてみる 入門書をある程度理解できたら、LinuC(LPIC) Level1の問題集を購入しました。 ※購入した本は以下になります。 Linux教科書 LPICレベル1 スピードマスター問題集 こちらの問題集は実際にLinuC(LPIC)の試験に出る問題がまとめられています。 難易度的には難しい内容でしたが、各問題の解説が詳しく書かれているので理解しやすかったです。 またLinuC(LPIC) Level1の試験範囲を全て網羅しているので、買っておいて損はないと思います。 2-3. Ping-tでひたすら問題を解く(※超重要) Ping-t は、LinuC(LPIC)やCCNAなどの試験対策用の学習サイトです。 LinuC(LPIC)の試験はPCを使うので、 Ping-t のWEB問題を解いておくとさらに安心です。 また101試験の問題であれば無料で利用できるのでおすすめです。 ※102試験からは有料になります 3. 受験方法と結果 3-1. 受験方法 ピアソンVUE というサイトで受験の予約ができます。 試験会場はテストセンターになります。 3-2. 受験結果 101試験、102試験、どちらも初回で合格できました。 実は受験当日まで選択肢問題しか出題されないと思っていたのですが、記述問題も出題されることを知ってかなりテンパった記憶があります。 そんな過ちを犯さないように、Ping-tのコマ問(記述問題)は解いておいたほうが良いです。 4. 仕事で役に立ったこと ・Linuxの基礎的な話は理解できる 取得する前は、先輩が言っていることや会議での技術的な会話を理解できませんでした。 例) 先輩「rootユーザに切り替えてみて」 自分「え…?」 これが本当に辛い… 取得してからは、結構話についていけるようになりました。 あと任せてもらえる仕事の領域を広げることができたのも大きなポイントです。 ・自信がつく 取得する前は、1つのコマンド打つだけでもビクビクしていました。 「このコマンド打ったらサーバダウンしないかな…」とか「これ実行して大丈夫かな…」とか。 取得してからは、基礎的なコマンドは理解できているので自信を持ってコマンドを実行できます。 正直これが一番嬉しかったです。 5. おわりに 今回はLinuC(LPIC) Level1を取得した話を記事にさせて頂きました。 LinuC(LPIC)はLinuxの基礎をおさえることができ、仕事には必ず役に立つのでオススメです。 次はLevel2が待っているので、引き続き頑張ります。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 IT×ブライダルで業界を変える!プロフェッショナルなWEBエンジニア募集!
こんにちは。SREチーム エンジニアの阿久津です。 今回は Redash と Superset という2つのツールを比較検証してみましたので それについて記事にしたいと思います。 ツールについて 両者はオープンソースのダッシュボードツールです。 簡単に言うと、DB等のデータを可視化(表やグラフ)してくれるツールになります。 ■Redash 公式サイト : https://redash.io/ ■Superset 公式サイト : https://superset.incubator.apache.org/index.html 比較したこと データソース連携 データ操作 グラフ ダッシュボード 比較まとめ 1. データソース連携 1-1. Redash Typeという項目のプルダウンからデータソースを選択します。 選択したデータソースによって入力する項目が切り替わるので、それに沿って情報を入力します。 ※以下はデータソースをMySQLを選んだ場合になります 連携できるデータソースは以下になります。 1-2. Superset Redashとは一変して SQLAlchemy URI という入力項目があり、自分が手入力しなければなりません。 ここを理解するのに ドキュメント を見なければならなかったので、正直大変でした。 連携できるデータソースは SQLAlchemy が対応しているものに限るような気がします。 ドキュメントを見ると、以下のデータソースは連携できるようです。 ・Firebird ・Microsoft SQL Server ・MySQL ・Oracle ・PostgreSQL ・SQLite ・Sybase Redashのほうが連携できるデータソースは多そうですね。 2. データ操作 2-1. Redash RedashはシンプルにSQLを書く前提で作られています。 基本的にSQLを実行してその結果を可視化(表・グラフ化)する流れになります。 普段業務などでSQLを使っている方には非常に使いやすいと思いますが、 その反面SQLを触ったことがない方にとってはハードルが高めかなと思います。 2-2. Superset SupersetはSQLを書くというよりもGUIから直感的に操作できるように作られています。 そのためSQLを書いたことがない方でも、比較的に触りやすいかなと思います。 「SQL Lab」という機能を使うと、SQLを書くことができるのでSQL経験者でも使いやすいと思います。 3. グラフ 3-1. Redash 表や棒グラフなどのスタンダードなものは揃っています。 以下にRedashの デモサイト のグラフを載せておきます。 ・ピボットテーブル ・カウンター などなど他にもたくさんあります。 3-2. Superset こちらも同様でスタンダートなグラフは揃っています。 以下にサンプルのグラフを載せておきます。 ・Sankey ・Directed Force Layout などなど面白いグラフが満載です。 どちらも多種なグラフを備えているので、個人的には大きな差はないかなという印象でした。 4. ダッシュボード 4-1. Redash 作った表やグラフは以下のようなダッシュボードにまとめることができます。 ■ダッシュボードでできること ・オートリロード機能(定期的にグラフを更新する) ・グラフの並び替え ・アラート機能(設定した値を超えた場合に、メールやSlackに通知できる) 個人的にはアラート機能があるのは嬉しいポイントでした。 常に監視していないといけないデータがある場合は、通知があると便利ですね。 4-2. Superset こちらもRedashと同様にダッシュボードにまとめることができます。 ■ダッシュボードでできること ・オートリロード機能(定期的にグラフを更新する) ・グラフの並び替え ・ダッシュボード上でグラフの表示領域をカスタマイズできる グラフの表示領域がカスタマイズできるのは良いポイントですね。 複数のグラフを並べて見比べたり、色々できそうです。 おそらくRedashではできなかったはずです(間違っていたらすいません)。 あとSupersetには残念ながらアラート機能がありませんでした。 5. 比較まとめ 5-1. Redash 良い点◯ ・データソース連携が簡単 ・多種なグラフがある ・アラート機能がある 悪い点✕ ・SQL初心者にはハードルが高い ・ダッシュボードのカスタマイズ性が低い 5-2. Superset 良い点◯ ・GUI操作ができるため、SQL初心者でも使えそう ・多種なグラフがある ・ダッシュボードのカスタマイズ性が高い 悪い点✕ ・データソース連携が難しく、連携できるデータソースが少ない ・アラート機能がない まとめ 今回はRedashとSupersetの比較検証に関してお話させて頂きました。 個人的にはRedashがシンプルで使いやすい印象でしたが、両者良いところがあって迷いますね。 引き続きダッシュボードツールは見ていきたいです。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 IT×ブライダルで業界を変える!プロフェッショナルなWEBエンジニア募集!
こんにちは。SREチーム インフラエンジニアの綿引です。 今回は Ansible で SSL証明書更新を自動化したみたというお話です。 SSL証明書の更新って時間がかかりますよね。。後ヒューマンエラーも怖い。。 そこで 自動化 出来たら素敵!と思い vagrant で検証してみました。 興味のある方は是非見て頂ければと思います。 環境・構成 環境・構成は以下となっております。 ■ サーバ側設定 ・ ホスト名 : ans ・ IP : 172.19.12.101 ・ 構成管理ツール : ansible/2.4.2.0 ・ (配布用)証明書の保存ディレクトリ : /opt/ansible/ssl/ ■ スレーブ側設定 ・ ホスト名 : anc ・ IP : 172.19.12.102 ・ WEBサーバ : Apache/2.4.6 ・ 証明書の保存ディレクトリ : /etc/httpd/conf/ssl.crt/ ■ 証明書情報 ・ サーバ証明書ファイル名 : test.crt ・ 中間CA証明書ファイル名 : test.cer ・ 秘密鍵ファイル名 : test.key やりたいこと やりたいことの流れとしては、以下です。 1. ansible サーバ側に新しい SSL 証明書を配置 2. サーバ側でスクリプトをキック 3. サーバ側の新しい SSL 証明書を、クライアントに配布 4. その後、クライアント側の apache を reload して新しい証明書を適用 では早速やってみたいと思います。 まずAnsibleとは その前にそもそも Ansible とは何ぞや、特徴は何だというのを簡単に、、 ・構成管理ツール ︎ 同じようなプロダクトとしては chef や puppet などですね ・エージェントレス ︎ クライアント側にエージェントを入れなくて済むのはありがたい ・シンプル ︎ 他の構成管理ツールと比べると記述などがシンプル 個人的にはエージェントレスは嬉しい限りです。 対象サーバの台数が多いとエージェントをインストールするのも面倒ですし、 エージェントのインストールに引きずられて、他のパッケージやソースを入れることもないので。 Ansible のインストール yum で Absible インストールします。くせもなく非常に簡単でした。 $ yum install epel-release $ yum install ansible $ ansible --version ansible 2.4.2.0 config file = /etc/ansible/ansible.cfg configured module search path = [u'/home/vagrant/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Jun 24 2015, 00:41:19) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)] 設定ファイルの作成 インストールも完了したので、次は設定を行っていきます。 実行に必要なファイルは(基本的に)以下の2つです。 ■ インベントリファイル (hosts ファイル) ・操作したいリモートサーバのホスト名やIPを記述 ・インベントリファイルに記載されたリモートサーバに対し操作が実行される ・尚、リモートサーバはグループ化することができ、そのグループに対し操作ができる ■ playbook(モジュール) ・yaml 形式 ・リモートサーバに実行させたい処理を記述した定義ファイル ・Ansible の モジュール を使用し、ファイルコピーやサービス起動など様々な命令が可能 モジュールの数は現在 1,300 ほどあるみたいなので、かなり多くの要望に対応できそうです。 ただ普段使いするのはそこまで多くなさそう。。 今回使用したのは以下のモジュールです。 ◆ 使用したモジュール(一部) No. モジュール名 用途 1 file ディレクトリの作成や、対象ファイルの文字列置換 2 copy ファイルコピー 3 service サービス開始、停止 4 shell OSコマンドの実施 モジュールには他にも yum などがあり、 パッケージインストールも ansible にて可能なので サーバの自動構築を行う際などに便利そうです。 インベントリファイル作成 まずはインベントリファイルから作成していきます。 適当に /opt 配下に ansible ディレクトリを作り、 その直下に hosts という名前のインベントリファイルを作成します。 $ sudo mkdir /opt/ansible/ $ cd /opt/ansible/ $ sudo vi hosts 先ほどのグループ指定の書き方をします。 “[ ]” 内にグループ名を記載し、参加させたいリモートサーバを羅列します。 [test1] 172.19.12.102 この記載だと test1 を選べば、第4オクテット 102 (ancサーバ) に対し実行します。 またホスト名で記載することも可能です。 もちろん以下のように複数台を選択することも可能です。 [test2] 172.19.12.103 172.19.12.104 yaml ファイル作成 次はクライアント側の操作を定義する yaml ファイルを作成します。 $ cd /opt/ansible/ $ sudo vi ssl_update.yaml 今回はこのような形で書いてみました。(汚いですが、、) また変数を使いたかったため、 vars_files というモジュールを使用しております。 vars_files は変数を定義したファイルを外に出すことができるので重宝しました。 - hosts: test1 remote_user: vagrant become: yes vars_files: - /opt/ansible/vars.yml tasks: - name: 1. create backupdir file: path={{ dest_ssl_dir }}/{{ date }} state=directory owner=root group=root mode=0644 - name: 2. backup file shell: cp -p {{ dest_ssl_dir }}/{{ crt_file }} {{ dest_ssl_dir }}/{{ date }}/{{ crt_file }}; cp -p {{ dest_ssl_dir }}/{{ ica_file }} {{ dest_ssl_dir }}/{{ date }}/{{ ica_file }}; cp -p {{ dest_ssl_dir }}/{{ key_nopw_file }} {{ dest_ssl_dir }}/{{ date }}/{{ key_nopw_file }} - name: 3. check file shell: diff -s {{ dest_ssl_dir }}/{{ crt_file }} {{ dest_ssl_dir }}/{{ date }}/{{ crt_file }}; diff -s {{ dest_ssl_dir }}/{{ ica_file }} {{ dest_ssl_dir }}/{{ date }}/{{ ica_file }}; diff -s {{ dest_ssl_dir }}/{{ key_nopw_file }} {{ dest_ssl_dir }}/{{ date }}/{{ key_nopw_file }} - name: 4. update crt_file copy: src={{ new_ssl_dir }}/{{ crt_file }} dest={{ dest_ssl_dir }}/{{ crt_file }} owner=root group=root mode=0644 - name: 5. update ica_file copy: src={{ new_ssl_dir }}/{{ ica_file }} dest={{ dest_ssl_dir }}/{{ ica_file }} owner=root group=root mode=0644 - name: 6. update key_nopw_file copy: src={{ new_ssl_dir }}/{{ key_nopw_file }} dest={{ dest_ssl_dir }}/{{ key_nopw_file }} owner=root group=root mode=0644 - name: 7. httpd configcheck shell: httpd -t - name: 8. httpd reload for CentOS 7 service: name=httpd state=reloaded when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "7") - name: 9.httpd reload for CentOS 56 shell: /etc/init.d/httpd graceful when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "5") or (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") yaml ファイルの内容ですが、各コメントの部分は以下の通りです。 項目 内容 1. create backupdir 証明書バックアップ用のディレクトリを作成(YYYYMMDD) 2. backup file 証明書のバックアップを取得 3. check file 現在のファイルとバックアップに差分がないか確認 (※1) 4. update crt_file サーバ証明書ファイルを更新 5. update ica_file 中間CA証明書ファイルを更新 6. update key_nopw_file 秘密鍵ファイルを更新 7. httpd configcheck Apache の configcheck を行う (※2) 8. httpd reload for CentOS 7 apache の reload が実行される (※3) 9. httpd reload for CentOS 56 apache の reload が実行される (※4) ※1 ・・・ 差分があった場合は diff の返り値が “0” 以外となるためエラーとなる ※2 ・・・ httpd.conf の記述に問題がある場合は返り値が ”0” 以外となるためエラーとなる ※3 ・・・ CentOS 7 systemctl reload htttpd が実行される ※4 ・・・ CentOS 5 か 6 だった場合に /etc/init.d/httpd graceful が実行される 最後に /opt/ansible/vars.yml の中身です。 date: "{{ lookup('pipe','date +%Y%m%d') }}" crt_file: test.crt ica_file: test.cer key_nopw_file: test.key new_ssl_dir: /opt/ansible/ssl dest_ssl_dir: /etc/httpd/conf/ssl.crt 実行 ansible を実行するには ansible-playbook コマンドを実行します。 (使い方例) : $ ansible-playbook -i hostsファイル yamlファイル $ ansible-playbook -i hosts ssl_update.yaml PLAY [test1] ******************************************************************************************************************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************************************************************************************************************** ok: [anc] TASK [1. create backupdir] ****************************************************************************************************************************************************************************************** changed: [anc] TASK [2. backup file] *********************************************************************************************************************************************************************************************** changed: [anc] TASK [3. check file] ************************************************************************************************************************************************************************************************ changed: [anc] TASK [4. update crt_file] ******************************************************************************************************************************************************************************************* ok: [anc] TASK [5. update ica_file] ******************************************************************************************************************************************************************************************* ok: [anc] TASK [6. update key_nopw_file] ************************************************************************************************************************************************************************************** ok: [anc] TASK [7. httpd configcheck] ***************************************************************************************************************************************************************************************** changed: [anc] TASK [8. httpd reload for CentOS 7] ********************************************************************************************************************************************************************************* changed: [anc] TASK [9.httpd reload for CentOS 56] ********************************************************************************************************************************************************************************* skipping: [anc] PLAY RECAP ********************************************************************************************************************************************************************************************************** anc : ok=9 changed=5 unreachable=0 failed=0 実行後、ブラウザで証明書の更新日を確認したら問題なく更新されておりました。成功です。 hostsファイルのグループ分けやディレクトリ構成、yamlファイルの記載など、 まだまだブラッシュアップが必要ですが一旦使えそうです。 まとめ 今回は Ansible で SSL 証明書をアップデートしました。 自身の環境でそこそこ上手くいったので、サービスに載せられないか検討中です。 皆さまもまだ Ansible 使ったことないんだよねーという方がいらっしゃれば、 参考にして頂けますと幸いです。 ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは、岩橋聡吾です。 やってみよう!AWSでWEBサーバー環境構築、久しぶりの続編です。 第4回は「AWS Lambda」「Amazon API Gateway」を使ったサーバーレスな画像リサイズAPIをクラウド上に構築して見たいと思います。 アーキテクチャ・設計概要 今回は以下のようなアーキテクチャで、Clientが指定した任意のサイズの画像を返却するAPIを構築していきます。 ◉S3: クラウドストレージ。 [設計概要] オリジナル画像の置き場を作成。 ◉Lambda: 最低限のプログラムのみでアプリケーションの運用が可能なサーバーレスプラットフォーム。他のAWSサービスと連携が可能。 [設計概要] S3から画像データを受け取り、リサイズを実施、それをBase64形式にして返却されるよう作成。 ◉API Gateway: APIの玄関。手軽に柔軟で拡張的なAPIベースの構築が可能。 [設計概要] Lambdaから受け取ったBase64形式の画像データをバイナリに変換して返却されるよう作成。 ◉CloudFront: 初回アクセス時に返却したコンテンツをキャッシュ。次回以降は有効期限内であればキャッシュよりコンテンツを返却。 [設計概要] 受けたリクエストにヘッダーを設定してバックエンドに渡すようなリクエスト窓口を作成。 この4つのサービスを連携させて、   https://xxx.xxx.xxx/{ファイル名}?width={横幅} のような形で呼び出しができるAPIを構築していきます。 S3 S3(Amazon Simple Storage Service)はインターネット用のクラウドストレージです。よくご存知の方も多いと思いますので細かな説明は割愛して、早速設定していきたいと思います。 今回は、リサイズ元のオリジナル画像の置き場を作ります。 バケット作成 コンソールからS3のページに遷移をして、「+パケットを作成する」ボタンをクリック、モーダルが表示されます。 ・パケット名 ・リージョン を設定して「作成」ボタンをクリックします。 オリジナル画像セット 検証用に先ほど作ったバケットに直接画像をアップします(neko.jpg)。 因みに今回はこのねこちゃんの画像をアップしました。 Lambda AWS Lambda はサーバーをプロビジョニングしたり管理しなくてもコードを実行できるコンピューティングサービスで、最低限のプログラムのみでアプリケーションの運用が可能です。他のAWSサービスのイベントを受け取り、ここで設定した関数をキックさせたりできるので、これまで以上にAWS上でバリエーション豊かなアーキテクチャーを実現することができます。 今回は、リクエストから「画像名」と「指定サイズ」受け取り、S3より「オリジナル画像」を取得し「指定サイズ」に加工し返却するような関数を設定します。 関数の外枠作成 コンソールからLambdaのページに遷移をして、「関数の作成」ボタンをクリックします。 ・名前 ・ランタイム:C#, Go, Java, Node.js, Python から選択 ・ロール:「テンプレートから新しいロールを作成」を選択 ・ロール名 ・ポリシーテンプレート:「S3 オブジェクトの読み取り専用アクセス権限」を追加 を設定して「関数の作成」ボタンをクリックします。 これでLambdaとS3の連携が設定(LambdaとCloudWatchLogsとの連携はデフォルトで設定されます)され、関数の外枠ができました。 関数の中身作成 【関数コード】 ・コード エントリ タイプ:「コードをインラインで編集」を選択 ・ランタイム(今回はNode.js 6.10としました) ・ハンドラ(デフォルト) ・index.js(今回は以下の内容で作成) 'use strict'; const imgmgck = require('imagemagick'); const aws = require('aws-sdk'); const s3 = new aws.S3({ apiVersion: '2006-03-01' }); exports.handler = (event, context, callback) => { // S3にあるオリジナル画像情報 const s3OrgImageFile = { Bucket: 'resize-s3-images', // S3で作ったバケット Key: event.filename, // リクエストで指定された画像名 }; // リクエストで指定された画像横幅 const width = event.width; // S3にあるオリジナル画像取得 s3.getObject(s3OrgImageFile, (err, data) => { if (err) { // 取得失敗 console.log('取得失敗:' + err); throw err; } else { // 取得成功 console.log('取得成功'); // リサイズ実施 imgmgck.resize({ srcData: data.Body, width: width }, function(err, stdout, stderr) { if (err) { // リサイズ失敗 console.log('リサイズ失敗:' + err); throw err; } else { // リサイズ成功 console.log('リサイズ成功'); callback(null, new Buffer(stdout, 'binary').toString('base64')); } }); } }); }; 【基本設定】 ・メモリ(MB)(今回は256MBとしました) ※大きさに応じて課金額が変わります ・タイムアウト(デフォルト) 右上の「保存」ボタンをクリックします。 テスト Lambdaでは作成した関数をクラウド上でテストすることが可能です。 先ずはテストを設定していきます。 右上の「テスト」ボタンをクリック、モーダルが表示されます。 ・イベントテンプレート:「API Gateway Authorizer」を選択 ・イベント名(今回はFromApiGatewayとしました) (イベント内容は以下) { "authorizationToken": "incoming-client-token", "methodArn": "arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/[method]/[resourcePath]", "type": "TOKEN", "filename": "neko.jpg", "width": "300" } 「作成」ボタンをクリックします。 これでテストの設定ができました。右上のプルダウンより「FromApiGateway」を選択、「テスト」ボタンをクリックしテストを実施します。 上手くいったようです、無事Base64形式の画像データが返却されました。 API Gateway Amazon API Gateway はスケーラブルなAPIの玄関を作るのに優れています。また簡単に、保守、監視、保護などが行えます。Lambdaとの相性も良く組み合わせることで、サーバーレスなAPIの構築が可能です。 ここでは、リクエストを受け付ける為のエンドポイントとその受け渡し先(先ほど作ったLambda関数)を設定していきます。 APIの外枠作成 コンソールからAPI Gatewayページに遷移をして、「+APIの作成」ボタンをクリックします。 ・API名(今回はresize-s3-imagesとしました) ・エンドポイントタイプ:「エッジ最適化」を選択 を設定して「APIの作成」ボタンをクリックします。これでAPIの大枠ができました。 次にリソース(APIのエンドポイント)を作っていきます。 リソースの「/」上で、「アクション」をクリック、「リソースの作成」を選択します。 ・プロキシーリソースとして設定:チェックなし ・リソース名 ・リソースパス:波括弧でパラメータ定義(リクエスト受け手のLambdaに渡すパラメータ、 今回は{filename}としました) を設定して「リソースの作成」ボタンをクリックします。これでリソース(APIのエンドポイント)ができました。 API諸設定 【メソッド】 メソッドを作成します。 直前で作ったリソース「/{filename}」上で、「アクション」をクリックし「メソッドの作成」を選択、さらに表示されたプルダウンより「GET」を選択します。 ・結合タイプ:「Lambda 関数」を選択 ・Lambda プロキシ統合の使用:チェックなし ・Lambda リージョン:前述のLambdaで設定したものと同リージョンを指定 ・Lambda 関数:前述のLambdaで設定したものを指定 ・デフォルトタイムアウトの使用:チェック を設定して「保存」ボタンをクリックします。 【統合リクエスト】 以下のような形でAPIの呼び出しを想定している為、{ファイル名}・{横幅}に当たる部分のパラメータをLambdaに渡すように設定する必要があります。   https://xxx.xxx.xxx/{ファイル名}?width={横幅} メソッドの実行画面で「統合リクエスト」をクリック、本文マッピングテンプレートを開きます。 ・リクエスト本文のパススルー:「テンプレートが定義されていない場合 (推奨)」を選択 (Content-Typeは image/png,image/jpeg,image/gif としてテンプレート内容は以下) { "filename": "$input.params('filename')", "width": "$input.params('width')" } を設定して「保存」ボタンをクリックします。 【統合レスポンス】 LambdaからはBase64形式で画像データが返却されるので、ここでバイナリデータに変換する必要があります。 メソッドの実行画面で「統合レスポンス」をクリックします。 ・コンテンツの処理:「バイナリに変換(必要な場合)」を選択 を設定して「保存」ボタンをクリックします。 デプロイとローカル確認 【デプロイ】 デプロイをして公開します。 「アクション」をクリックし「APIのデプロイ」を選択、モーダルが表示されます。 ・デプロイされるステージ:「新しいステージ」を選択 ・ステージ名(今回はproductionとしました) を設定して「デプロイ」ボタンをクリックします。 【ローカル確認】 ショルダーメニューから「ステージ」を選択、「production」をクリックします。「URLの呼び出し」を取得できます。 ローカルのターミナル上でcurlコマンド使ってリサイズ画像取得の確認できます。 $ curl --request GET "Accept: image/jpeg" -H "Content-Type: image/jpeg" https://zwhfujd9te.execute-api.us-west-2.amazonaws.com/production/neko.jpg?width=300 > neko_resized.jpg 以下のようなリサイズ画像が取得できていれば成功です。 CloudFront CloudFrontはAWSクラウド上のCDN(コンテンツデリバリーネットワーク)です。こちらは 第3回 で紹介させて頂いたので細かな説明は割愛します。 何故CloudFrontが必要か さて、何故今回のアーキテクチャーにCloudFrontが必要になるのでしょうか。   https://zwhfujd9te.execute-api.us-west-2.amazonaws.com/production/neko.jpg?width=300 上はここまでに作成してきたAPIのURLです。これをimgタグにセットすれば(もしくは直接ブラウザで叩けば)使える!と思われるかもしれませんが、上手くいきません。この場合以下のようなレスポンスが返されます。 imgタグにセットするだけでは、リクエストヘッダーに「Content-Type:image/xxx」「Accept:image/xxx」がセットされない為、API Gateway側の「統合リクエスト」の「本文マッピングテンプレート」でサポートしていないヘッダーとなり「Unsupported Media Type」が返却されます。 では、API Gateway側の「統合リクエスト」の「本文マッピングテンプレート」を調整すればいいのでは?と考えました。が…確かにこの場合、そのパケットはLambdaにたどり着けるのですが、今度は「統合レスポンス」でバイナリ変換がされなくなります。これは「統合レスポンス」がバイナリ変換するかどうかを、リクエストヘッダー「Content-Type:image/xxx」「Accept:image/xxx」を見て決めている為です。 そこでCloudFrontの登場です。CloudFrontからOrigin(今回でいうと「zwhfujd9te.execute-api.us-west-2.amazonaws.com」)へパケットを転送する際に、カスタムヘッダーを設定できる機能があります。これを使ってリクエストに強制的に「Content-Type:image/xxx」「Accept:image/xxx」のヘッダーをつけてあげると上手くいきそうです。やってみましょう。 CloudFrontにカスタムヘッダーをつけて設定 コンソールからCloudFrontのページに遷移をして、「Create Distribution」ボタンをクリックします。 Web(上)の方の「Get Started」ボタンをクリックします。 【Origin Settings】 ・Origin Domain Name:「zwhfujd9te.execute-api.us-west-2.amazonaws.com」を選択 ・Origin ID(今回はresize-imagesとしました) ・Origin Protocol Policy:「HTTPS Only」を選択 ・Origin Custom Headers:Accept・Content-Type共にimage/png,image/jpeg,image/gifを設定 ※これが一番やりたかったこと 【Default Cache Behavior Settings】 ・Query String Forwarding and Caching:「Forward all, cache based on all」を選択 ※これを設定しないとGETパラメータをOriginへ渡せません を設定して「Create Distribution」ボタンをクリックします。 これでCloudFrontの設定が完了しました。今作ったDistributionを見てみましょう。 上の画面の「General」タブで、配布された「Domain Name」が確認できます。これを使ってブラウザで以下のようなURLを叩いて画像が表示されるかを確認します。   https://d298y89zsryi7u.cloudfront.net/production/neko.jpg?width=300 上手くいきました(設定したDistributionが反映されるまで数分かかる場合があります)。URLのwidthを任意の値に変えても取得することが確認できました。CloudFrontのキャッシュ機能の説明については割愛していますが、Lambdaでの画像リサイズは決して軽い処理ではないのでCloudFrontで上手くキャッシュができるようにチューニングすることをお薦めします。 これで、サーバーレスな画像リサイズAPIの完成です。 第4回目の最後に 今回のやってみようAWSシリーズでは、「Lambda」を使ったサーバーレスな画像リサイズAPIをクラウド上に構築手順をざっとご紹介しましたが、AWSではまだまだ多くのインフラサービスが提供されており、そのうちのどれもが実際のWEBサービス運営に役立つ強力な武器となります。今後もこのようなシリーズ記事を通じて皆さんに有益な情報を提供できればと思います。 またこのシリーズでこれまでインフラと縁遠かった方でもAWSに対する理解が少しでも深まっていれば幸いです。 ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ◉シリーズ ・やってみよう!AWSでWEBサーバー環境構築(シリーズ第1回) ・やってみよう!AWSでWEBサーバー環境構築(シリーズ第2回) ・やってみよう!AWSでWEBサーバー環境構築(シリーズ第3回) ◉筆者のおすすめ記事 ・【DB設計入門|ER図|MySQL】コンビニレシートから学ぶ!データモデリング手法 ・【機械学習入門|Python|scikit-learn】結局何ができる?cheat-sheetから解説してみる篇
こんにちは、もうすぐ新卒入社して2年目になる高嶋(@__1016t)です。 わたしはコードレビューで、基本的なコードの書き方やPHPでつかえる便利機能など、たくさん勉強してきました。 今回はその一部をまとめてみました。 1. インデントをそろえる インデントとは? ソースコードの見た目を整えてくれるもので、 ifやforなど波カッコ{}で囲まれるブロック内の各行をtabキーやスペースで字下げすることをいいます。 ▼インデントなし if ($dessert == 'papico') { for ($i = 0; $i <= 3; $i++) { echo 'デザートはパピコです'; } } else { echo 'デザートはパピコではありません'; } ▼インデントあり if ($dessert == 'papico') { for ($i = 0; $i <= 3; $i++) { echo 'デザートはパピコです' } } else { echo 'デザートはパピコではありません' } インデントしてあるコードのほうが、見やすいのがわかります。 入社してから知った技 逆tabインデント tabキーでインデントした行を元の位置に戻したい! そんなとき、いつもdeleteキーで戻していました。 しかしshift + tabキーで戻せるのです! 複数行一括字下げ インデントしたい行が複数ある! そんなとき、いつも一行ずつインデントしていました。 しかし、複数行を範囲選択しtabキーをおすと・・・ 一括でインデントすることができるのです(逆tabインデントもしかり) 2. phpdocをかく phpdocとは、クラスや関数などの定義前に記述するブロックコメントのことです。 phpdocを書いておくと、 – 後にソースを見返した時理解する時間が短縮される – ドキュメントを自動生成できるようになる というメリットがあります! /** * @return string */ public function sample() { $dessert = 'papico'; return $dessert; } 詳しい書き方についてはこちらがおすすめです! phpDocumentorの書き方 – Qiita 3. 演算子をつかいこなす 演算子にはさまざまありますが、わたしが指摘されたのは 条件演算子(三項演算子) と Null合体演算子 です。 条件演算子(三項演算子)とは? 条件により返す値を切り替えることができる演算のことです。 たとえば、answerが10より小さいときはそのままanswerを、 10以上のときは10と返すプログラムをif文、条件演算子でかくとき、 それぞれ以下のようになります。 ▼if文 if ($answer < 10) { $result = $answer; } else { $result = 10; } ▼条件演算子 // [条件] ? [TRUEの場合の返り値] : [FALSEの場合の返り値] ; $result = ($answer < 10) ? $answer : 10; if文よりも条件演算子のほうがシンプルにかけます。 わたしはずっと条件演算子はif文の省略形だ、と思っていたのですが違うようです。 つかいわけについてはこの記事でお勉強させていただきました! 三項演算子は悪か? – Qiita Null合体演算子とは? PHP7から追加された新機能で、「〜がなかったら」という処理を簡単にかくことができます。 たとえば、dessertがなかったら買いにいく、あったらdessertと返す処理を さきほどの条件演算子、Null合体演算子でかくときそれぞれ以下のようになります。 ▼条件演算子 $result = isset($dessert) ? $dessert : '買いにいく'; ▼Null合体演算子 // [未定義か判定したい変数] ?? [デフォルト値] $result = $dessert ?? '買いにいく'; 条件演算子よりもシンプルに書けることがわかります。 わたしは定義されていない変数によるエラーを防ぐため、 ?? を乱用してしまいがちなのですが、 処理コストがかかるためつかいすぎには注意が必要です。 最後に 今回コードレビューで指摘されたことを記事にまとめてみて、 わかったつもりでも文章にできるほど理解できていない事が多いな、と感じました。 今後も指摘をうけたことはまとめてチェックし、指摘数も減らしていけたらいいなと思います! Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ≪19卒≫IT×ブライダルで業界を変えるエンジニア募集!≪未知変えろ≫
こんにちは。サーバサイドエンジニア兼QAエンジニアの斉藤(@saik1010)です。 弊社QAチームでも取り組みを始めた、 Selenium を使用したブラウザテスト自動化について、 環境構築〜簡単なプログラムを実行するまでをご紹介しようと思います! 動作環境 環境 バージョン macOS Sierra 10.12.6 Ruby 2.4.2 rbenv 1.1.1 RSpec 3.7.0 Capybara 2.16.0 Selenium Webdriver 3.60 ChromeDriver 2.35 もくじ 概要 構築手順 プログラム作成〜実行 まとめ 概要 今回は、手元のMacに Selenium+Ruby+Capybara をインストールして、 手軽でシンプルなブラウザテストの環境を構築していきます。 弊社のメイン言語はPHP/Goを採用しておりますが、両言語とも Selenium で公式サポートされていないため、将来的な運用保守を考慮して、 Ruby を採用しました。 また、 Ruby には Capybara という強力なサードパーティ製のラッパーがあり、かなりシンプル(※1)に Selenium のコードを書くことができることも1つの大きな選定要因になっています。 ※1 個人的な感覚ですが、ピュアなJavaScriptがjQueryで書けるようになるぐらいの関係性に近い 構築手順 では早速、環境構築をしていきましょう! HomeBrewのインストール インストールには HomeBrew を使用すると簡単に環境構築を行うことができます。 ※既にインストール済みの場合はスキップしてください $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" $ brew -v Homebrew 1.5.11 Homebrew/homebrew-core (git revision 847b9; last commit 2018-03-20) Rubyのインストール そのまま、最新版の Ruby を入れてしまっても良いのですが、 複数バージョンを管理することも想定して、 rbenv を利用し、 Ruby をインストールします。 まずは rbenv からインストールをしていきましょう! # rbenvのインストール $ brew install rbenv $ rbenv --version rbenv 1.1.1 # 初期設定のスクリプトを設定する $ vi ~/.bash_profile eval "$(rbenv init -)" #追記 $ source ~/.bash_profile # 正しくインストールできているか確認 $ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash Checking for `rbenv' in PATH: /usr/local/bin/rbenv Checking for rbenv shims in PATH: OK Checking `rbenv install' support: /usr/local/bin/rbenv-install (ruby-build 20180224) Counting installed Ruby versions: 2 versions Checking RubyGems settings: OK Auditing installed plugins: OK 続けて rbenv を利用して、 Ruby をインストールします! # インストール可能なrubyのバージョン確認 $ rbenv install -l # Rubyのインストール $ rbenv install 2.4.2 $ rbenv global 2.4.2 $ rbenv rehash $ ruby -v ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16] Seleniumのインストール Rubygems を使用して、 Selenium をインストールします! 今回は、自動テスト対象としてChromeを利用するため、WebDriverはChromeDriverのみをインストールしていますが、IEやFirefoxなどでも専用のWebDriverをインストールすることで、動作させることが可能です。 ※対象のブラウザは、 こちら をご確認ください! $ gem install selenium-webdriver $ brew install chromedriver 関連ライブラリのインストール 続けて Capybara と RSpec をインストールします! こちらも Rubygems で簡単に入れることができます。 $ gem install capybara $ gem install rspec プログラム作成〜実行 それではインストールした Selenium+Ruby+Capybara を利用して、簡単なサンプルプログラムを実装してみましょう! Selenium の公式サイトで、サンプルサイトが提供されているので、そちらをアクセスして入力フォームの自動テストを実装していきます! # 使用するライブラリを指定 require 'capybara/rspec' # seleniumでchoromeを使用する Capybara.default_driver = :selenium Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end # テストケース feature 'Seleniumでテストサンプル' do scenario '入力フォームの確認' do # サンプルサイトへアクセス visit 'http://example.selenium.jp/reserveApp/' # フォームへ入力(htmlタグのid指定) fill_in('reserve_year', with: '2018') fill_in('reserve_month', with: '4') fill_in('reserve_day', with: '1') fill_in('reserve_term', with: '3') fill_in('headcount', with: '5') fill_in('guestname', with: 'Selenium テスト') # ラジオボタンを選択(htmlタグのid指定) choose('breakfast_off') # チェックボックスを選択(htmlタグのid指定) check('plan_a') # ボタン押下 click_on '次へ' end end 実際に実装したプログラムを実行してみます!Chromeが自動で立ち上がり、テストが実行されればOKです! $ rspec 作成したファイル名.rb . Finished in 4.76 seconds (files took 0.25653 seconds to load) 1 example, 0 failures まとめ いかがでしたでしょうか? 「5分で」 はちょっと盛りすぎましたが・・・(笑) シンプルかつ、少ないコード量でブラウザテストの自動化が実現できました!他にもスクリーンショットを撮るなど、 Selenium には多くの機能が提供されているので、弊社では回帰テストなどに利用していこうと考えています。 本番運用に向けては、テストサーバへの環境構築やヘッドレスブラウザの利用など、まだまだ課題はありますが、ひとまず運用に乗せてから検証を進めてみようと思います。 また後日、こちらのブログで検証結果を報告させて頂きますので、続編に乞うご期待ください! Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。SREチーム インフラエンジニアの綿引です。 MySQL の特定のバージョンをインストールしたい! だけど yum でさくっとインストールしようとしたら、マイナーバージョンが上がっていて同じにならない。。 なんてことよくありますよね。そうです。先週の私です。 そこで今回は古いバージョンの MySQL を yum インストールする手順を記載したいと思います。 手順 1. 依存関係解決のために、最新のリポジトリを追加する まずは最新のリポジトリをインストールしましょう。 $ cd /etc/yum.repos.d/ $ ls -l $ sudo rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm $ ls -l 2. wget をインストールする この後ファイルを落としてくるので、入ってなければ wget をインストールしましょう。 $ sudo yum list installed wget $ sudo yum install wget $ sudo yum list installed wget 3. 過去にマイナーバージョンが入った tar ファイルを取得する MySQL の公式サイトには過去のバージョンの MySQL 達がアーカイブされています。 MySQL Product Archives 例えば、 MySQL Community Server が欲しい時には、上記URLにアクセスし 以下を実施していくとお好みのバージョンに出会えます。 ・ MySQL Community Server を選択 ・ Product Version を選択 ・ Operating System を選択 ・ OS Version を選択 これでお目当の tar ファイルが表示されました。 これをサーバにダウンロードして、展開後にインストールしていきます。 $ cd /usr/local/src $ ls -l $ sudo wget https://downloads.mysql.com/archives/get/file/mysql-5.7.19-1.el7.x86_64.rpm-bundle.tar $ ls -l $ sudo tar -xvf mysql-5.7.19-1.el7.x86_64.rpm-bundle.tar $ ls -l $ sudo yum list installed | grep mysql 4. rpm を指定して localinstall する rpm コマンドでもインストールできますが、依存関係が面倒なので yum の localinstall を実施していきます。 そして必要なパッケージはまとめてインストールしましょう。 「mysql-community-server だけ 5.7.19 入れれば、依存関係がきっと何かうまいことやって、必要なパッケージ全部 5.7.19 になるだろ!」 $ sudo yum localinstall mysql-community-server-5.7.19-1.el7.x86_64.rpm と何も考えずやったら痛い目を見ます。そうです。先週の私です。 (後で見たら server が 5.7.21 で client が 5.7.21でした。。) まとめてインストールしていきます。 $ sudo yum localinstall mysql-community-server-5.7.19-1.el7.x86_64.rpm \ mysql-community-client-5.7.19-1.el7.x86_64.rpm \ mysql-community-common-5.7.19-1.el7.x86_64.rpm \ mysql-community-devel-5.7.19-1.el7.x86_64.rpm \ mysql-community-libs-5.7.19-1.el7.x86_64.rpm \ mysql-community-libs-compat-5.7.19-1.el7.x86_64.rpm $ sudo yum list installed | grep mysql これで バージョンが 5.7.19 になっていればOKです! まとめ 今回は古いバージョンの MySQL を yum インストールしてみました。 個人環境を整えたい方、負荷分散用の DB サーバを構築したい方など参考にして頂ければ幸いです。 ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。サーバーサイドエンジニアの@akane_256です。 今日は、PHP_CodeSniffer+GitHub+CircleCIを使って、PHPのコードレビュー(コーディングルールの徹底)を一部自動化したことについて書きたいと思います。 目次 興味を持った背景 今回やったこと 利用ツールについて 実装の流れ ハマったところ まとめ 興味を持った背景 開発が進むにつれて、他の人が書いたコードと自分が書いたコードで、統一感を保つのが難しくなってきたな、という実感があったのとコーディングルールにあっていないコードを自動で見つけられたら便利だよね、と同僚と話す機会があったので試してみました。その他にも、下記のようなメリットがあるのではないかと考えました。 少ない人数でコードレビュー時間を十分に取れない場合でもソースコードの品質を保てる。 もし人が増えた時でも、ソースコードの書き方に統一感をもたせられる。 自分のコードのいけない部分がわかるのでレベルアップに繋がる。 今回やったこと 今回は下記のような流れで作成しました。 1. ソースコードをGitHubにpushする 2. CircleCIがpushをフックにテストを実行する。テストの中で、 PHP_CodeSniffer のコマンドを叩いてソースコードの静的解析を実行する 3. 結果をPull Requestにコメントとして表示させる 4. テスト結果をSlackに通知する 完成イメージ。コーディングルールに合わない部分を指摘してコメントしてくれます。 利用ツールについて PHP_CodeSnifferとは? GitHub上にある、PHPのソースコード整形OSSです。 PHP_CodeSniffer 定義されたコーディングルールの違反を検出したり、自動整形してくれます。 コードをきれいにして一貫性を保つために使います。 CircleCIとは? アプリケーション作成時の品質改善や納期の短縮のために使うツールです。今回は品質改善(=コーディングルールの統一)に使います。 CIツールはたくさんあって選ぶのが大変でしたが、主に下記の理由からCircleCIにしました。 GitHubとの連携が簡単。GitHubのアカウントを作成すれば、CircleCIはGitHubのアカウントでログインすれば終わり。 設定は、WebのUIもしくはymlファイルで管理できる。 1ヶ月1500分(=25時間)までなら無料(1コンテナ)。※2018/2時点 CircleCI 2.0がでたタイミングで、開発も盛んに行われている。 meetupなど盛んに行われており盛り上がっている。 UIが綺麗。 実装の流れ アカウント準備 GitHubアカウント作成 リポジトリをpush CircleCIのアカウント作成(GitHubアカウントでログイン) 利用ツールの連携準備 GitHubとCircleCIの連携 CirlcleCIとSlackの連携 プロジェクトの設定準備 composer.jsonの編集 テストコマンドをまとめた check_syntax.sh をリポジトリに追加 CircleCIの設定ファイル circle.yml をリポジトリに追加 1のアカウント準備は簡単だと思うので割愛します。利用ツールの連携準備から書いていきます。 利用ツールの連携準備 GitHubとCircleCIの連携 GitHubのPull Requestにコードの解析結果をコメントで書き込みするために、GitHubのアクセストークンをCircleCIに登録する必要があります。 これがないとCircleCIがGitHubにアクセスできず、若干ハマりました。。 GitHubのアクセストークンは、 Setting > Developer settings > Personal access tokens > Generate new token から発行します。 発行したトークンの値はコピーしておきます。コピーし忘れると、また再度発行することになるので忘れずに。 名前はわかりやすく、 CircleCI とかにしておくと良いと思います。 次は、CircleCI側の設定です。 CircleCIの画面を開き、プロジェクトの Setting > Environment Variables > Add variable から、先ほどの値をペーストして保存します。 こちらの名前もわかりやすく、 GITHUB_ACCESS_TOKEN とかにしておくと良いと思います。 CircleCIとSlackの連携 CircleCIの画面から連携できるようになっています。 Setting > Chat Notifications から、SlackのWebhook URLを登録する部分があるので、こちらにSlackのWebhookのURLを登録します。 ちなみに、連携が完了すると、ビルド終了時に通知がきます。テスト失敗時は赤色、成功時は緑色で表示されます。 (一部文字を伏せています) 今回はPull Requestの量が多くないのですぐにビルドが終わったため、Slackへ通知するメリットはあまり感じられませんでしたがビルドが数分以上かかる場合は、チャットで通知してくれると便利です。 これで利用ツールの連携準備が整いました! プロジェクトの設定準備 次は、実際にソースコードなどを管理しているプロジェクトの設定準備です。 前提条件として、今回の検証に使ったプロジェクトのディレクトリ構成はこのような形式です。 PHPのパッケージ管理には、composerを利用します。 ├── application │ └── composer.json │ : ├── check_syntax.sh ├── circle.yml : composer.jsonの編集 composer.jsonを編集して、 php_codesniffer をインストールする記述を追加します。 こういった整形ツールは開発環境のみで使うので、 require-dev の部分に追記します。 application/composer.json "require-dev": { "squizlabs/php_codesniffer": "^3.1" } テストコマンドをまとめた check_syntax.sh をリポジトリに追加 ここが今回の一番の肝の部分です。PHPの一般的なコーディング規約である PSR-2 にのっとって解析するようにしています。 #!/bin/bash set -v if [ "${CIRCLE_BRANCH}" != "develop" ]; then # GitHubのPull Requestにコメントをするために必要なgemのインストールをします echo gem install gem install --no-document checkstyle_filter-git saddler saddler-reporter-github # pull requestがないか確認します if [ -z "${CI_PULL_REQUEST}" ]; then # when not pull request REPORTER=Saddler::Reporter::Github::CommitReviewComment else REPORTER=Saddler::Reporter::Github::PullRequestReviewComment fi # git diffからphpファイルを取り出し、php_codesniferのコマンドを実行します echo saddler git diff --name-only origin/develop \ | grep -e '.php$' \ | xargs application/vendor/bin/phpcs -n --standard=PSR2 --report=checkstyle \ | checkstyle_filter-git diff origin/develop \ | saddler report --require saddler/reporter/github --reporter $REPORTER fi CircleCIの設定ファイル circle.yml をリポジトリに追加 コードの解析を実行するプログラムが用意できたので、そのプログラムをCircleCIから実行できるように設定します。 CircleCIの設定ファイル circle.yml をプロジェクトルートディレクトリに追加します。CircleCIはこのymlファイルをみて、ビルドを実行します。 今回は、 test 部分をoverride(上書き)して、先ほど追加した .check_syntax.sh を実行するように記載しました。 circle.yml machine: timezone: Asia/Tokyo php: version: 5.6.17 test: override: - composer install --dev --no-interaction --working-dir=application - ./check_syntax.sh これで完成です。GitHubのpushに応じて、CircleCIでテストが実行され、結果を通知してくれるようになります。 ハマったところ CircleCIはクラウドサービスなので、ちょっとしたプログラムの修正後の動作確認でも、pushしなければいけなかったのが辛かったです。(CircleCI2.0からローカルで実行できるようなので試してみたいです。) CircleCIとGitHubの連携。アクセストークンの登録ができていないのを気づかないまま実装を進めていたので、CircleCIが「Pull Requestないよ」とエラーになってしまい、少しハマりました。 まとめ PHPは、標準ではコードを整形する機能が付いていないので、気をつけないと複数人でバラバラのコーディングスタイルになりがちです。こうして整形用のツールを使ってコードの一貫性を保つことは、自分以外のエンジニアのために大事なことだと思います。また、こうしてCIを使って自動で指摘がされるようにしておくと、新しい人が入っても一貫したルールを保つことができるので、ぜひ活用していきたいと思います。 また、実装にあたりこちらの記事をがっつり参考にさせていただきました。ありがとうございます。 規約に沿ってないPHPコードを駆逐する CircleCIからESLintの指摘結果をPull Requestにコメントする Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。サーバサイドエンジニア兼QAエンジニアの斉藤(@saik1010)です。 本日は、今年の1月9〜1月12日に開催したエンジニア職インターンシップの運営について、お話しさせて頂こうと思います。 当日の様子は Wantedly でもご覧になれますので、合わせてご確認頂ければと思います! 取り組んだテーマ 例年、対象サービスに機能追加をするというテーマ設定で行ってきましたが、少しマンネリ化してきたこともあり、今年は新しいテーマを模索していました。 そこで、弊社エンジニア・デザイナーでいくつかのチームに分かれ、社内コンペを実施してテーマを決定することに。 見事、熱戦を制したチームが考えたテーマはこちら!! 思わず式場見学にいきたくなる! とびっきりのタイアップページ(広告ページ)を考えよう! 実際に 結婚式場を見学 してから「企画〜開発」までを行うカリキュラムというのが大きな目玉となっています! 今年のテーマのポイント 社内コンペの中で、この案を採用したポイントをご紹介します。 ブライダルに接点の少ない学生に結婚式場を見学してもらい、 ブライダルへの理解 を深めてもらえること 企画から立案することで、エンジニアだけでなく、 ディレクターとのコミュニケーション を取ることができること LPをゼロから作るので、「デザイン〜サーバサイド」まで 幅広い開発 を経験できること 運営メンバー 今年の運営メンバーは、2017年度に入社した新卒・中途メンバーのフレッシュな3名で行いました。 全員、弊社のエンジニアインターンを運営した経験はありません。(笑) 参加人数 今回は9名の学生が参加してくれました! 1チーム3名の全3チームで 優勝 をかけて競い合います。 メンターには、現役エンジニアが 1チーム2名専属で付く手厚いサポート も弊社インターンの魅力の一つ! ちなみに優勝したチームは、代表取締役社長である日紫喜と 豪華会食 に行くことができます!(学生にとっては貴重な経験!) 主なインターンの流れ 弊社のインターンでは、「普段行なっているチーム開発を体験してもらいたい!」との思いから、実際に現役エンジニアが行なっている開発フローに沿って作業を進めていきます。簡単にですが、今回の作業フローを紹介します。 企画作成 企画レビュー 仕様設計 仕様設計レビュー(今回は未実施) 開発 コードレビュー テスト 最終プレゼン発表 運営する上で注力したポイント 現場社員からのレビュー 「実際の開発現場を肌で感じてもらいたい!」との思いから、実際の開発でもレビューをメインで担当しているメンバーに「レビュー〜フィードバック」まで受けられる時間を作りました。参加してくれた学生の声を聞くと、普段はコードなどの成果物を第三者にみてもらえる機会は少ないようで、非常に好評でした! 物づくりの楽しさを感じてもらう 今回はあえて「仕様設計レビュー」を割愛しました。意図としては、自分たちで考え抜いたコンテンツや機能を実装して形にしていくことの楽しさを感じてもらった方がやりがいがあるだろうと考えたからです。 チーム開発を体験してもらう ウエディングパークでは、チーム(組織)で開発するというスタイルを強みとしています。お互いの強みや弱み、みんなで一つのものを作り上げる楽しさを感じてもらい、エンジニアとして働くことのイメージを持ってもらいたいと考えました。 実際に参加した学生からは、チーム開発を経験することで「とても良い体験ができた」や「チーム開発の難しさを感じた」との声が多くあり、学生にとっても非常に良い経験になったよう!! 運営する上で工夫したポイント 準備タスクの整理 今回は社内コンペを実施したということもあり、準備期間が2週間程度しかありませんでした。その中で運営主導で舵を取り、いかに円滑に進めていけるかがインターンを成功させるための大きな鍵を握ります。 ですが、こういう時こそエンジニアの腕の見せ所!!普段の開発経験が活かされます。(笑) とにかく、やるべきことをリスト化して洗い出しました。その中で「 自分たちではできない=別部署に依頼しなければならないタスク 」から優先的に作業をしていきます。 例えば・・・下記のようなタスクが該当します。 開発環境の構築→SREチーム※1 インターン生が使用するPCの手配→ISチーム※2 ※1 弊社サービスのインフラ周りを管理しているチーム ※2 社員のPCやメールなど社内情報システムを管理しているチーム メンター間の情報共有 各チームに専属で2名のメンターが付きますが、自チーム以外の状況が把握できるように、インターン生が帰宅後に毎日振り返りを実施しました。運営全体でフォローしていける体制づくりを意識することにより、他チームへのフォローなども含め、当日の進行が非常にスムーズに進みました。 社員とのコミュニケーション メンター社員とのコミュニケーションは多く取れますが、その他のエンジニア・デザイナーやディレクターと交流する機会は、なかなかありません。ただ、今回参加してくれた学生の中には、デザインを勉強している学生やWeb開発をやったことがない学生もいたので、より多くの社員と交流をしてもらい、今後の就職活動の参考にしてほしいと考えていました。 特に弊社のオフィスがある表参道はランチが美味しいので、日替わりで社員とランチに行ってもらいました。ランチの美味しさは学生のみんなも気に入ってくれたようで、「今日はどこに連れてってもらえるかなー?」とワクワクしていました。(笑) 運営する上で苦労したポイント メンター社員のスケジュール確保 メンターとして参加してくれる社員には、ある程度事前にスケジュールは確保していましたが、通常業務もあることから、少し負担が多くかかってしまいました。 ただ、弊社にはこういう時こそ チームでカバーする 良さがあります。メンター社員は自分の時間を犠牲にしてでも、インターンを優先して徹底的にサポートをしてくれました。 ※メンター以外の社員も応援に駆けつけて、インターン生をサポートしてくれる場面も!! どこまでサポートするべきかの線引き メンターとしての役割は、インターン生が開発できるようにサポートすることです。ただ、全てをサポートしてしまえば良いかというと、そうとは限りません。あくまで主役は「 インターンに来てくれた学生 」なので、インターン生の意見を尊重しつつ、物事を考えるヒントになるようなことを心掛けてサポートしました。 まとめ いかがでしたでしょうか? 今回は、エンジニアインターンの運営を通して、多くのことを経験し学ぶことができましたので、ご紹介させて頂きました。 ぜひ、これからインターンを始めようと思っている企業様やインターンの参加しようと思っている学生さんの参考になれば幸いです。 特に学生のみなさん!エンジニアインターンは、毎年行なっておりますので、 来年度の参加をお待ちしております! 弊社の現役エンジニアが徹底してサポートしますよ! Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは、SREチーム エンジニアの西脇(@yasuhiro1711)です。今日は、チーム運用改善の話をしたいと思います。これからチーム運用をしていく方々に少しでも響けばいいな、参考事例になればいいな、と思って書いておきます。ちなみに、テーマを変えて続編も書く予定ですので楽しみにしてもらえればと思います。 チームに課題が出て来た 私のSREチームはこれまでは2人と少数精鋭でした。そこに昨年全く違う背景を持つメンバーが入って来ました。(ちなみにSREチームに改名した件も近々どこかで書こうと思います。)最初は仕事のやり方を変えずにやっていたものの、仕事が増えて連携する場面が増えると、2人のときの阿吽の呼吸だけではどうしても回らなくなることが増えてきました。他にも思考のすれ違いなど幾つか課題が出始めました。項目にするとこんな感じです。 少数メンバーのため、人に依存したタスクが多かった。 チームメンバー同士での現状把握が出来ていない。 タスクの可視化や優先度の把握が不十分。 ロードマップの優先度付けも弱い。 それぞれこれまでやれてなくて出て来た課題なので、向き合って進めればいいのですが、 さて、どうやって解決していくのが楽しいか。やるなら楽しく進めたいものです。 そうだ! うちはRedmineを使ってるので、 Redmineをフル活用すれば、「あーして、こうして」、結構いいかも。 と思いつきました。思いついたら吉日。即日動きました。 Redmineを活用しよう Redmineでチームの課題に対して何ができるか。それと、チームの運用はどうあるべきかを情報収集して考えて、以下を活用してみました。追加したプラグインは3つだけです。 「カンバン方式」を導入しよう 課題感から、一番にやりたかったことは、「カンバン方式」を導入することです。 昨年オライリーから出版されている、あの書籍でも有名ですよね。 O’Reilly Japan – カンバン仕事術 https://www.oreilly.co.jp/books/9784873117645/ この書籍の中でもかかれていますが、「カンバン方式」を取り入れると効果のある状況というのは以下のような状況です。 納期によく遅れる 見積もりが不正確なことが多い チームは仕事に追われている 優先度がよく分からない 色々なところからチームに仕事がくる 誰が何をやっているのかよくわからない あ、まさにチームの状態かもー。混乱状態だ。と思いました。 プロジェクトも1人ずつで複数抱え、その中で全サービスの運用・障害対応も入り、購買やセキュリティ対応、パフォーマンスチューニング等もしているので、仕事の幅が広く、複数人でプロジェクト全体を見通すのはなかなか大変な状況でした。 今回は、「カンバン方式」導入にあたり、Redmineでは有名なプラグイン Redmine Agile plugin http://redmineup.com/pages/plugins/agile を利用しました。 slack連携しよう これはやりたかったからやってみました。 slack連携もプラグインで簡単に可能です。通知を飛ばすことで、チケットやwikiなどの更新情報を入手しやすくなりましたし、地味に他のメンバーからの通知が多いと、「こんなに仕事進んでいるのか」とよい刺激を受けます。 Redmine Slack https://github.com/sciyoshi/redmine-slack 工数管理を自分でしよう チーム運用だからこそ、工数の概念と、工数のレベル感をチームで共通認識で持つために、全てのチケットを工数管理していきます。 有名なこのプラグインを導入しました。 Redmine Work Time plugin http://www.redmine.org/plugins/redmine_work_time Redmine運用ルール これらプラグインで実現できる小技もたくさんありますが、それよりもまず、チームの共通ルールを作りました。 1、会社で時間を使う事を全てをチケット化しよう。 プロジェクトタスク、運用タスクだけでなく、個人の技術検証や、個人目標への取り組み、場合によっては、業務には関係ない社内タスク 等まで、メンバーが会社で時間を使うことになるタスクすべてをチケット化して見える化をします。もちろん、このブログの記事執筆もチケット化しています。 こうすることで、業務チケットでチームメンバーに進捗が伝わるのはもちろんのこと、個人タスクもチームのRedmineで管理することで、仲間が今、何を目標に頑張っており、何の技術を追求したくて仕事をしているのか分かりやすくなり、チームメンバー同士で支え合うことができます。 2, 定例ではやってることを語り合おう。周りに刺激を与え合おう。 現在、週1で定例をしています。そこでは「カンバン」を見て進捗確認し合います。 まずは各自の完了チケットの報告から始めます。自分が完了させたタスクを胸を張って自慢、説明してもらいます。毎週、メンバーがどれだけのチケットをどのように完了させているかを聞かされるので、顔には出さないものの刺激をもらってモチベーションアップになってます。 モチベーションアップ以外にも、ミスをなくす、いろんな考え方を知る、再発防止を進めるなどとても良い運用につながります。 説明完了したものはその場で「完了ステータス」から「確認済み」のステータスにする運用にしているのですが、数が多いほど、カンバン上でドラッグ&ドロップしていくのは快感を覚えますw その後は担当中タスクの進捗確認、新規案件の話などもしますが、チケット数が多すぎるのでできるだけ短時間で、ポイントに絞って進めます。 全員で全体を見ることになるので俯瞰的に物事を見られるようになります。人に指摘されて初めてわかることも人間には絶対にあります。そんな主観を客観に変える作業でもあります。 3, 予定工数と工数の記録を適当でいいのでつけよう。 これ、今までやっていないと正直面倒です。でも適当でいいので、「実施タスクにかける工数見積」と「実際にかかった工数」は記録をするようにしています。適当というのが大事です。入力の精神的コストを下げるために必要だと思っています。できるだけ、自然に記録を付けられるように入力ルールをシステム化したり、お互い指摘したり、一度記録をつける癖をつければ、少しずつ苦ではなくなってきています。(まだ苦ではあります)これはメンバーには自分の見積の精度をあげていくのに役立ちますし、マネージャーからするとチームの生産性を考える上で非常に有効なデータとなります。 導入の結果 運用を変更していった結果、成果が出てきました。 仕事が見える化された。 隠れて見えなかった仕事が見える事で、誰が何をどのくらいしているのかが、Redmineを見ればわかるようになります。 チケットの進捗さえ見れば、急な代打案件でもこなせます。 タスクの流れが明確になった。 ワークフローでステータス管理することで、仕事進捗が良いのか、悪いのか、どのステータスで止まりやすいのか。また、外部のチームからの仕事の依頼から完了の確認までのフローが分かりやすくなりました。また、WIP制限をする事で、メンバー同士で仕事量を調整し合う文化も生まれました。 ※ WIP制限とは、担当中のタスクの上限数を決定し、それを超えないように運用していくことです。数を抑えるよりも、「xxさん、業務多くない?」って会話が生まれたりするのが意外と大事です。 メンバーの自主性が大きく伸びた。 全員がリーダーのように、主体的・自主的な動きをたくさん取るようになったと感じます。それにより小さな情報でもチケットやコメントに情報が集まるようになり、情報整理の時間が短縮できています。また、他のメンバーのタスクへのコメントも活発になり結果、チームで品質が上がっています。 変化していくことを楽しめるようになった。 この運用をしていると、改善していくことを楽しめるようになります。何かを進めて成果になることが楽しくなってきます。 チームの信頼度アップ お互いのタスクが見えていることは、自然とお互いの信頼度を上げていきます。この運用をしていると自然に信頼度は上がってきました。 以上の結果から、最初の課題を見直してみると、、 少数メンバーのため、人に依存したタスクが多かった。 →クリア! チームメンバー同士での現状把握が出来ていない。 →クリア! タスクの可視化や優先度の把握が不十分。 →クリア! ロードマップの優先度付けも弱い。 →クリア! よーし! 今回は2ヶ月ほどの日々の改善でここまで出来ました。 また次の新しい課題も見えてきたので、次回はそこへの取り組みもお話しできたらと思います。 まとめ 今回チーム運用改善をしてみて思うことは、「仕事や成果が見える化することは、周りに刺激を与える」ということです。人は刺激をもらってモチベーションに変えて動く生き物ですので、刺激が大事というのが改めてよく分かりました。今回はチーム運用改善のハシリですので、もっと運用経験を貯めてどんどんブログでも公開していきたいと思います。 工数もつけているので、今後は作業のリードタイムやスループットをはじめ、生産性アップに注目して見てみたいと思っています。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは、サーバーサイドエンジニアの菅原です。 今回はJavaScriptの非同期処理について今更ながら学んでみました。昔ながらのCallback、ES6から追加されたPromise、ES7から追加されたasync await、さらにはRxJsについても調べてみました。 背景 昨今はUXの需要の高まりから非同期処理を書くことが多くなり、リアルタイム性やチャット機能、パフォーマンス改善のためにも非同期処理のコードを書くことが増えています。ただ非同期処理のJavaScriptのコードは処理が増えるごとにコールバック地獄と呼ばれる可読性が損なわれる危険性があることも事実です。 今回は以下のプログラミング条件から非同期処理について簡単なコードを用いての紹介と補足でRxJsを用いた非同期処理を紹介していきます。 実行プログラムの条件 1, 認証ユーザかチェックを行う。 2, 1が成功するとウエパちゃんを表示させる 3, 1,2を順に実行する。 ウエパちゃん ウエディングパーク公式キャラクターです。 ウエディングパークのサイトなどでも活躍しています。 Callbackを用いた非同期処理 これは非同期処理でも基本的なCallbackをしているJavaScriptコードです。 ①②ともにsetTimeoutの中でcbメソッドを使いCallbackしています。 ③の実行ではcheckAuthの関数を呼び出した中にネストしてfetchWpの関数を呼び出しています。 以下のコード量の時は特に可読性が悪くはないですが、追加する関数が増えれば増えるほどネストが深くなってしまい、可読性がなくなり冗長なソースになってしまいます。 // ①認証ユーザかチェックを行う。 const checkAuth = cb => { console.log('Checking Auth...') setTimeout(() => { cb(true); }, 2000); }; // ②ウエパちゃんを表示させる const fetchWp = cb => { console.log('Fetching WP...') setTimeout(() => { cb({ name: "ウエパちゃん" }); }, 2000); }; // ③実行 checkAuth(auth => { if (auth) { fetchWp(wp => { console.log(wp.name) }); } }); Promiseを用いた非同期処理 (ES6) ES2015(ES6)から追加仕様として加わったPromise。 Promiseは3つの状態(resolve, reject, done)を持ちます。 ①②の関数をPromiseでオブジェクトにラップしておきます。 ③の実行時にthenというチェーンメソッドでその後の処理を記述します。 続けて処理を書く場合もメソッドチェーンでつなげていくことでネストが深くならずにコードがすっきりするでしょう // ①認証ユーザかチェックを行う。 const checkAuth = () => { return new Promise((resolve, reject) => { console.log('Checking Auth...'); setTimeout(() => { resolve(true); }, 2000); }); }; // ②ウエパちゃんを表示させる const fetchWp = () => { return new Promise((resolve, reject) => { console.log('Fetching Wp...'); setTimeout(() => { resolve({ name: "ウエパちゃん" }); }, 2000); }); }; // ③実行 checkAuth() .then( isAuth => { if (isAuth) { return fetchWp() } } ) .then( wp => { console.log(wp.name) } ) async awaitを用いた非同期処理 (ES7) ES2016(ES7)から追加仕様として加わったasync await Promiseと共存して使用できます。 ①②でPromiseでラップし、③の実行時にasyncの中でawaitで関数を呼び出しています。 awaitはasyncで定義された関数内にしか使えません。また通常のコールバックやNodeコールバックは共用できません。awaitは関数の実行が終わるまで待機します。またPromiseでラップしている箇所をasync関数で定義することも可能です。その場合async関数が暗黙的にPromiseを返し、関数の戻り値がPromiseの処理完了の値になります。 // ①認証ユーザかチェックを行う。 const checkAuth = () => { return new Promise((resolve, reject) => { console.log('Checking Auth...'); setTimeout(() => { resolve(true) }, 2000) }) } // ②ウエパちゃんを表示させる const fetchWp = () => { return new Promise((resolve, reject) => { console.log('Fetching Wp...'); setTimeout(() => { resolve({ name: "ウエパちゃん" }); }, 2000) }) } // ③実行 (async function(){ const isAuth = await checkAuth() let user = null; if (isAuth) { wp = await fetchWp() } console.log(wp.name); })(); RxJsを用いた非同期処理 (補足) 補足にRxJs(Reactive Extensions)を用いてコードを書いています。「Observerパターン」でイベントやデータをストリームとして扱えることからプログラミングの可読性を向上させています。開発元はMicrosoftであり、ライブラリとしての信頼性もあります。コードではObservable.createでデータを作りObservableをハンドリングし、最後にsubscribeで出力しています。 // ①認証ユーザかチェックを行う。 const checkAuth = () => { return Rx.Observable.create(observer => { console.log('Checking Auth...') setTimeout(() => { observer.next(true); }, 2000); }) }; // ②ウエパちゃんを表示させる const fetchWp = () => { return Rx.Observable.create(observer => { cosole.log('Fetching Wp...'); setTimeout(() => { observer.next({name: 'ウエパちゃん'}); }, 2000); }) }; // ③実行 Rx.Observable.of(true) .switchMap(event => checkAuth()) .switchMap(isAuth => { if (isAuth) { return fetchWp() } }) .subscribe(wp => { console.log(wp.name); }) まとめ いかがでしたでしょうか?非同期処理だけで様々な書き方ができます。 個人的にはES7のasync awaitが今後主流になっていくのではと思いましたが、Promiseが導入されてから可読性が一気に上がったのでPromiseでも問題ないと思いました。特にES7を導入していないサイトなどもあるのでPromiseで賄っていけるのではないかと思います。またトランスパイラを導入していなくてもjQueryのDefferd+Promiseである程度カバーできると思いました。 まだまだ熱い非同期処理界隈ですが皆さんもぜひ試してみてください。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
こんにちは。サーバーサイドエンジニアの@akane_256です。 今日は、Headless Chromeを使ったブラウザテストの自動化(入門)について書きたいと思います。 目次 Headless Chromeとは? 興味を持った背景 今回やったこと 実際のソースコード まとめ Headless Chromeとは? Chrome59(2017/6月頃)から搭載された機能 ChromeをGUIなしでコマンドラインから実行できる DOMのノードを取得したり、画面キャプチャを撮ったりできる ツールと組み合わせて自動テストに使ったりする ブラウザを開いて、特定のページを開き、自分で目視確認しなくても、 あらかじめ作ったプログラムをコマンドから実行をすれば 自動でキャプチャをとったり指定の要素を取得したりすることができます。 興味を持った背景 私は普段 Ringraph という結婚指輪・婚約指輪の口コミサイトの開発をしています。 サイトは毎週のようにリリースがあり、日々改善をしています。 リリースをする際にテストを実施していますが、 ページがきちんと表示されていて 出るべき要素が出ていること ユーザーログイン・口コミの投稿がきちんとできること などなど、確認することはたくさんあり、それを毎回確認するのはとても大変です。 テストの負担を減らし、エンジニアの心の負担を少しでも減らしたいので(笑)、 少しずつ自動化できないかな、と思って調べました。 今回やったこと 今回は入門編ということで、とてもシンプルですが、 Headless Chromeを使って複数ページのtitleのテキストを取得するプログラムを作成しました。 これだけでも、応用すればtitleやdescription、ページの要素の中身の確認等を自動できるので、実際に運用できたら便利です。 実際のソースコード 今回は、 Puppeteer(パペティアー) というライブラリを使って開発をしました。 Puppeteer(パペティアー) とは、Googleが開発・公開しているHeadless Chromeを操作するためのNode.jsのライブラリです。 ドキュメントもわかりやすく、使いやすいです。 JSなのでフロントエンジニアさんは使いやすいのではないでしょうか。 しかもChromeを開発しているGoogleの公式ライブラリなので、とても安心感があります。最近も開発が盛んに行われているようです。 https://github.com/GoogleChrome/puppeteer Puppeteerを利用するには、Node.js v6.4.0以上と、 Node.jsのパッケージ管理をする yarn もしくは npm が必要です。 ない場合はインストールが必要です。 今回は下記のバージョンで試しました。 <br />node -v node v8.4.0 yarn --version 0.27.5 早速、試してみます。 まずは適当にディレクトリを作成・移動します。 ディレクトリ内で yarn のコマンドを実行します。 <br />mkdir puppeteer_test cd puppeteer_test yarn add puppeteer yarn add を実行したあとは、 package.json というパッケージを管理するための管理ファイルが作成されるのと、 node_modules ディレクトリ配下に、 puppeteer と依存モジュール群がインストールされます。 次に、プログラムを書くファイルを作成し、中身を記述していきます。 <br />touch main.js vi main.js 中身のイメージ。公式のGitHubにもサンプルコードが載っています。 <br />const puppeteer = require('puppeteer'); // URL you want to access const urls = [ 'https://example.com', 'https://www.google.com' ]; const title = []; (async () => { // The method launches a browser instance with given arguments. const browser = await puppeteer.launch(); // return new page object const page = await browser.newPage(); for(i = 0; i < urls.length; i++){ await page.goto(urls[i], {waitUntil: 'networkidle2'}); // add title to array title.push(await page.title()); } console.log(title); browser.close(); })(); ファイルが作成できたら、最後に実行します。 うまくいくと、プログラムで指定したWebサイトのタイトルタグがconsoleに表示されます。 <br />node main.js [ 'Example Domain', 'Google' ] インストールからファイル作成・実行まで、シンプルで、結構簡単にできました。 まとめ 他に二つくらい試しましたがPuppeteerが一番簡単そうですぐに動きました。 ( Lighthouse と、あともう一つくらい試しました。) テストケースを書けば実際に運用できそうで、使い方もシンプルです。 PuppeteerはAPIのドキュメントもしっかり書いてありブラウザテスト入門者にもわかりやすいです。 今後は、JSが絡んだ操作の動作確認をしたいです。 読んでくださり、ありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
結婚写真を撮影できるスタジオ検索サイト「Photorait」担当エンジニアの武田(@takedajs)です。 先日、自分が担当しているPhotoraitでスタジオを探し実際に結婚写真を撮影してきました。自分が携わってるサービスをユーザとして使うのは、感慨深いですね!一つ夢が叶いました涙 さて本題です。 最近Firefoxの大きなバージョンアップ(Firefox Quantum)があり、ページ読み込み速度の大幅な改善などありましたが、その一方で最新の開発手法(以下、WebExtensions)ではないFirefoxアドオンが利用できなくなりました。僕の場合、利用していたFirefoxアドオンが全て利用不可に。。。マジか。。。 そこで今回は、最新のFirefoxでも利用できる、作業ミスを減らすFirefoxアドオンを開発し公開しましたので紹介します!ターゲットユーザが少ないと思うので、かなりニッチなアドオンです笑 開発したアドオン 開発したFirefoxアドオン「TabColor」は、指定したキーワードが表示したページのURLに入ってる時、ブラウザのタブ全体を赤くするシンプルなアドオンです。 TabColor – Firefox 向けアドオン: https://addons.mozilla.org/ja/firefox/addon/tabcolor/ 例えば、キーワードに「wikipedia」と入力します。 表示しているURLに「wikipedia」があると、正規表現でマッチさせ、このようにタブ全体を赤くします。 開発した理由 本番環境であることに気づかずに、間違えて作業してしまうミスをなくすためです。 Photoraitではユーザが撮影スタジオに資料請求や来店予約を行いたい時、サイト上のフォームから申込を行うことができます。申込される度に、該当の撮影スタジオ宛にメールが送られます。 フォームはユーザビリティ向上のために改修や機能追加をすることも多く、開発する度に正しく申込ができるかどうか開発環境でテストをしています。 もし、この時のテストを何かの間違いで開発環境ではなく本番環境で行ってしまった場合、撮影スタジオにテストで入力した情報(XSSテスト用の文字など)が送られてしまい、サイト運営の信頼に関わってきます。 以下に開発環境と本番環境のフォーム画面を並べてみました。URL以外に大きな違いがなく、開発環境かどうか一瞬で判断することができず、ミスが起きる可能性がありました。 左: 開発環境 右: 本番環境 (画像はセキュリティ的にURLが書いてあるアドレスバーを表示させていませんが、このときタブは赤く表示されていません。) このような時に、TabColorを利用して本番環境だけ赤くすれば、本番環境でないことを一瞬で判断できるのでミスを限りなく0にできます。 実際に僕は、「タブ全体が赤くなる = 本番環境」とするように、以下のページが赤くなるキーワードをTabColorに登録しています。 ・本番環境の公開サイトページ ・本番環境の管理画面ページ ・本番環境のphpMyAdmin phpMyAdminは開発環境と本番環境でサイト自体のデザインを変えていますが、念のためタブ全体が赤くなるようにしています。 中身の説明 大まかにですが実装部分について説明していきます。WebExtensionsでは、様々な機能をもったJavaScript APIが提供されており、TabColorでも以下の2つのAPIを主に利用してます。 ・storage API – キーワードを保存時に利用 ・theme API – タブ全体を赤くする時に利用 ファイル構成 以下アドオンのファイル構成です。 % tree . ├── background.js /* タブを赤くする処理 */ ├── icons │   ├── tab_color_30.png │   └── tab_color_60.png ├── manifest.json ├── popup /* キーワード入力フォーム関連 */ │   ├── keyword_save.css │   ├── keyword_save.html │   └── keyword_save.js /* キーワード登録処理 */ └── tab_red.jpg takedajs/TabColor: https://github.com/takedajs/TabColor キーワード登録処理 keyword_save.jsでは、キーワードの登録処理を行っています。 ・storageに保存されているキーワードがある場合、キーワードをvalueに設定してフォームを作成しています。 ・フォームの保存ボタンが押下された時、storage内のキーワードを全て削除し、フォームに入力されているキーワードをstorageに保存しています。 keyword_save.js //キーワードを登録するためのフォームを作成 var storage_keywords = []; browser.storage.local.get('value', function(items) { storage_keywords = items.value; }); window.setTimeout( function(){ // キーワード登録フォーム作成 for (var i=0; i < 10; i++) { var input = document.createElement("input"); input.setAttribute("type","text"); input.setAttribute("class","text"); // 登録されている要素がある場合 if (storage_keywords != undefined) { input.setAttribute("value", storage_keywords[i]); } document.getElementById("keywords").appendChild(input); } }, 300 ); // キーワードをストレージに保存 document.addEventListener("click", function(e) { if (e.target.classList.contains("save")) { // ストレージ内のデータを削除 browser.storage.local.remove("value"); var keywords = document.getElementsByClassName("text"); var array_keywords = []; for (var i = 0; i < keywords.length; i++) { array_keywords.push(keywords[i].value); } browser.storage.local.set({'value': array_keywords}, function() {}); browser.tabs.reload(); alert("Completion of registration"); } }); タブを赤くする処理 この処理の実装は試行錯誤しました。 試行錯誤① 当初TabColorは、WebExtensionsではなく古い開発手法であるAdd-on SDKを利用して開発しました。タブ全体を赤くする現在とは違い、該当するページのタブだけを赤くしていました。 これはXPCOMという仕組みを利用し、Firefoxのデザインを変更できるユーザのローカルファイルであるuserchrome.cssを上書くことで表現していました。ですが、WebExtensionsではXPCOMが利用不可になり、指定のタブだけ赤くすることを断念することに。 試行錯誤② WebExtensionsで開発し直した当初は、以下のようにタブではなくサイトの左側を赤くする表現方法をとっていました。 Javascriptでbodyタグに以下のstyleを追加や削除するだけなので簡単に実装できます。 border-left: solid 15px red; 社内LT会でこの時のTabColorを紹介したのですが、「サイトのレイアウトが崩れるから使わない笑」との温かい声を多数頂いたので、「簡単な実装に逃げず、何とかしてタブを赤くしよう!!」とその日は枕を濡らしながら決意したのを今でも覚えています。 完成版 ローカルファイルのuserchrome.cssは上書けないので、代替案はないかと提供されているJavaScript API一覧を眺めたり、既に公開されているFirefoxで同様の実装をしているものはないか調査しました。その結果、テーマとして赤い背景を設定することで実装が可能であると分かりました。 テーマとは、ブラウザーのデザインを変えられるあのテーマです。 theme API を利用することで、自分が作成した画像をテーマとしてブラウザに設定できます。以下の赤い横長画像を設定することで、タブの背景だけ赤くなっているようにごまかしています。画像の高さは、タブの高さを何度も測って同じ高さになるように良い感じに設定しています。ちなみに、高さ32px x 幅2500px が良い感じでした。 background.jsでは、キーワードにマッチしたURLのページがアクティブになった時、先程説明した赤い横長画像をテーマとして設定しています。マッチしないURLのページがアクティブになった時は、テーマを削除しています。 background.js // タブを赤く見せるテーマ const themes = { images: { headerURL: '../tab_red.jpg', }, colors: { accentcolor: '#F9F8F8', textcolor: 'black', } }; // タブが更新された時 browser.tabs.onUpdated.addListener((id, changeInfo, tab) => { url_match(tab); }); // タブがアクティブになった時 browser.tabs.onActivated.addListener((activeInfo) => { var active_tab = browser.tabs.get(activeInfo.tabId); active_tab.then((tab) => { url_match(tab); }) }); // テーマ設定、削除処理 function url_match(tab) { var storage_keywords = []; browser.storage.local.get('value', function(items) { storage_keywords = items.value; }); window.setTimeout( function(){ // 現在見ているページが、登録したキーワードに一致するか確認 var isMatch = false; for (var i = 0; i < storage_keywords.length; i++) { if (storage_keywords[i]) { var regexp = new RegExp(storage_keywords[i], 'i'); isMatch = regexp.test(tab.url); if (isMatch) { break; } } } if (isMatch) { browser.theme.update(themes); } else { browser.theme.reset(); } }, 300 ); } まとめ 作業ミスを減らすために開発したFirefoxアドオン「TabColor」の紹介をしました。 本番環境の時だけタブの色が赤くなることで、一瞬で開発環境と本番環境の区別をつけることができます。 TabColorが初めてのFirefoxアドオン開発だったのですが、公式サイトが充実しているのと豊富なAPI提供のおかげで、想像していたより簡単にアドオンを作ることができました。 WebExtensionsで開発したFirefoxアドオンはChromeの機能拡張とも互換性があるみたいなので、次はChrome版のTabColorを作成してみたいと思います。社内の人達はほとんどChromeユーザなので、Chrome版開発したらみんな使ってくれると良いなー笑 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにきてください。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集! 参考 WebExtensions – Mozilla | MDN https://developer.mozilla.org/ja/Add-ons/WebExtensions