Git のフックを利用したデプロイの方法

Gitクラウド

Git のフックを利用した本番環境適用の仕組みや post-update などのスクリプトの書き方、必要となる SSH 接続の設定方法を説明します。

GitHubの場合はこちら
参考: GitHub Actions を利用した本番環境適用(デプロイ)の方法


Git のフックを利用したデプロイの仕組み
Git には フック【hooks】という仕組みがあり、Git に対するアクションをトリガーとしてなんらかのスクリプト処理を行うことができます。
例えばリモートリポジトリ(中央リポジトリ)に修正コードをpushしたタイミングで本番環境にもその修正コードを自動で反映させる(デプロイ)ということもできます。
基本的には、masterブランチへのpushで本番環境へ、developブランチへのpushでステージング環境へデプロイするフックを作成します。
下図はmasterブランチから本番環境へのデプロイを表しています。

フックによるデプロイ

origin masterへのpush)が完了すると、リモートリポジトリのフックスクリプトが動作します()。
フック処理では本番環境に SSH 接続し、本番環境側からorigin masterpullするよう記述します()。
pullが成功すると本番環境のワーキングディレクトリが更新され、本番用のソースコード等がここへ配置されます。
ワーキングディレクトリの一部をドキュメントルートに指定することにより、利用者は最新の環境をブラウザでアクセスできるようになります。


フックスクリプトの書き方
フックのスクリプトは.git/hooks配下にあります。
フックスクリプトには色々な種類があり、Git へのアクションにおける様々なタイミングでスクリプトを走らせることが可能ですが、デプロイに関しては以下のスクリプトを利用するのが良いでしょう。

post-receive
push処理が終了した後で1回呼び出されます。
引数はありませんが、pushされた参照のリストを標準入力から受け取ることができます。
このスクリプトではpush処理を中断させることはできませんが、クライアント側ではこのスクリプトが終了するまで接続を切断できません。
このスクリプトが出力する標準出力の内容はクライアントに返されます。

post-update
push処理が終了した後で1回呼び出されます。
実際に更新されたすべてのrefの名前を可変数の引数で受け取ります。
このスクリプトではpush処理を中断させることはできませんが、クライアント側ではこのスクリプトが終了するまで接続を切断できません。
このスクリプトが出力する標準出力の内容はクライアントに返されます。

下記は post-update によるフックスクリプトの記述例です。

#!/bin/sh

# 基本的に複数のブランチが同時に更新されることはないため、第一引数をチェックする
if [ $1 = "refs/heads/master" ]; then
	echo "deploy production"
	# GIT_DIR 環境変数が優先されるため、--git-dirでディレクトリを明示指定する
	ssh production "cd /path/to && git --git-dir=.git pull origin master"

elif [ $1 = "refs/heads/develop" ]; then
	echo "deploy staging"
	ssh staging "cd /path/to && git --git-dir=.git pull origin develop"

fi

# リポジトリがHTTP公開の場合に必要(これがないと最新の情報をpullできない)
exec git update-server-info

上記の例では SSH 接続を簡略化するために、.ssh/configファイルを事前に設定しています。

Host production
  Hostname production.myserver.com
  User ec2-user
  IdentityFile /path/to/keys/production.pem

Host staging
  Hostname staging.myserver.com
  User ec2-user
  IdentityFile /path/to/keys/staging.pem


本番環境サーバに SSHD を設定する
デプロイはリモートリポジトリサーバが修正コードを適用する側(本番環境)のサーバに SSH で接続し、git pullすることで行います。
したがって本番サーバやステージングサーバ側が SSH 接続を受け入れるよう設定する必要があります。
具体的には SSHD(SSH デーモン) を起動し、22 番ポート(変更可能)を開けるという手順になります。


※ Linux 編はこちら

SSHD の設定(Windows Server)
Windows Server にはデフォルトで SSHD が入っていませんのでインストールから必要になりますが、Git for Windows には SSHD も含まれていますのでこれを利用しましょう。
ちなみに Git for Windows は以下の URL からダウンロードできます。

https://gitforwindows.org/

まずは git bash を起動し、SSHD が利用する var フォルダを作成します。

>mkdir /var
>mkdir /var/empty
>mkdir /var/log
>touch /var/log/lastlog # SSH 接続のログファイル

sshd_config ファイルを設定します。
パスワードを聞いてくるとそこでスクリプトがストップしてしまいますので、ここでは公開鍵認証のみ設定します。
LogLevel を DEBUG にしておくことで、エラー発生時の原因究明に役立つログを出力してくれます。

>cd /etc/ssh
>vim sshd_config

・・・
PubkeyAuthentication yes # 公開鍵による認証あり
PasswordAuthentication no # パスワード認証なし
LogLevel DEBUG # デバッグログ
・・・

次に SSH ポートを開放します。
コントロールパネルより、Windows ファイアウォールの「詳細設定」画面を開きます。
(セキュリティが強化された Windows ファイアウォール)

Windowsファイアウォール

受信規則から新しい規則を作成します。

受信規則

ウィザードにしたがってそれぞれ入力していきます。

規則の種類:ポート
プロトコルおよびポート:TCP、特定のローカルポート 22
操作:接続を許可する
プロファイル:ドメイン、プライベート、パブリック
名前:SSH

受信規則ウィザード

受信規則が作成できました。

セキュリティが強化されたWindowsファイアウォール

SSH 接続に IP アドレスアクセス制限をかける場合は、作成した規則をダブルクリックし、スコープタブを開きます。
「これらの IP アドレス」を選択し、許容するアドレスを追加します。

IPアクセス制限

SSHD の起動には sshd というユーザが必要です。
セキュリティの観点からデーモンプロセスを root 権限で起動しないための特権分離オプションにより、SSHD は sshd ユーザによって起動されます。
sshd ユーザが存在しない場合は SSHD 起動時に以下のエラーが発生します。

Privilege separation user sshd does not exist

sshd ユーザを追加する手順は以下の通りです。

スタートメニューから「Windows 管理ツール」→「コンピューターの管理」を開き、左メニューの「ローカルユーザーとグループ」→「ユーザー」を選択します。

コンピューターの管理

上部メニューの「操作」から「新しいユーザー」をクリックします。

コンピューターの管理-ユーザーの追加

ユーザー名を sshd としてユーザーを作成します。
パスワードは無期限の方が運用が楽かなと思います。

コンピューターの管理-ユーザーの追加-新しいユーザー

最後に SSHD を起動します。

>/user/bin/sshd.exe

Windows にリブートがかかった場合にも SSHD が起動するようスタートアップに登録しておくと良いでしょう。
スタートアップフォルダは以下になります。

C:/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup

なお、Git の SSHD を利用しない場合は、以下の Windows 向け OpenSSH を利用すると良いでしょう。
こちらはサービス化されるので Windows にリブートがかかった場合にも自動で再起動されます。

Releases · PowerShell/Win32-OpenSSH
Win32 port of OpenSSH. Contribute to PowerShell/Win32-OpenSSH development by creating an account on GitHub.

なお、デフォルトの sshd_config の場所は以下になります。

C:/ProgramData/ssh/sshd_config

公開鍵の登録
SSH キー(公開鍵と秘密鍵)を作成します。
SSH キーの作成は SSH 接続する側でもされる側でもどちらで作成しても良いですが、SSH 接続側で秘密鍵(id_rsa)ファイルが必要となりますので、取り扱いにご注意ください。
以下は RSA 方式の場合になります。
ssh-keygenコマンドを利用するには Git をインストールする必要があります。

>ssh-keygen -t rsa -b 4096

キーの保存先やパスフレーズの設定を聞かれますが、そのままEnterを繰り返したデフォルト設定で大丈夫です。
なお、キーの保存先はデフォルトでは以下の場所にid_rsaid_rsa.pubの名前で保存されますので、もしすでに作成しているキーが存在している場合は退避しておいてください。

Windows
%UserProfile%\.ssh

Linux
/home/{ユーザー}/.ssh

作成された公開鍵(id_rsa.pub)を SSHD へ登録して完了です。
下記ファイルにid_rsa.pub(RSA方式の場合)の内容を追記します。

%UserProfile%/.ssh/authorized_keys

ssh-rsa AAAflkajeflaj・・・

これで秘密鍵(id_rsa)を使用してSSH 接続することができるようになりました。
この設定ファイルの場所やファイル名は sshd_config の AuthorizedKeyFile ディレクティブで変更が可能です。

AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

SSH 接続してみましょう

SSHD の設定(Linux)
AWS など大抵の VPS では、すでにインストール済みになっているかと思いますが、まずは openssh-server をインストールし起動します。

>dnf install openssh openssh-server # CentOS
>apt install openssh-server # Ubuntu
>systemctl start sshd # sshdの起動
>systemctl enable sshd # 自動起動ON

セキュリティの観点から root ユーザによる SSH 接続やパスワード認証を禁止します。

>vi /etc/ssh/sshd_config
# rootログイン禁止
#PermitRootLogin yes
↓
PermitRootLogin no
PasswordAuthentication no # パスワード認証を禁止する

>systemctl restart sshd # sshdの再起動

SSH 接続するポートを開放します。

(>firewall-cmd --remove-service=ssh --zone=public --permanent # 設定の削除)
>firewall-cmd --add-service=ssh --zone=public --permanent # ポートの開放
>firewall-cmd --reload # 設定をリロード
>firewall-cmd --list-all # 設定確認

SSH 接続に IP アドレスアクセス制限をかける場合は、以下のコマンドを実行します。

>firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="許可するIPアドレス" service name="ssh" accept"
>firewall-cmd --reload
>firewall-cmd --list-all # 設定確認

設定ファイル(/etc/firewalld/zones/public.xml)を直接編集することもできます。

>vi /etc/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <service name="dhcpv6-client"/>
  <service name="cockpit"/>
  <service name="http"/>
  <service name="https"/>
  # ここから
  <rule family="ipv4">
    <source address="許可するIPアドレス"/>
    <service name="ssh"/>
    <accept/>
  </rule>
  # ここまでを追記
</zone>

公開鍵の登録
SSH キー(公開鍵と秘密鍵)を作成します。
SSH キーの作成は SSH 接続する側でもされる側でもどちらで作成しても良いですが、SSH 接続側で秘密鍵(id_rsa)ファイルが必要となりますので、取り扱いにご注意ください。
以下は RSA 方式の場合になります。
ssh-keygenコマンドを利用するには Git をインストールする必要があります。

>ssh-keygen -t rsa -b 4096

キーの保存先やパスフレーズの設定を聞かれますが、そのままEnterを繰り返したデフォルト設定で大丈夫です。
なお、キーの保存先はデフォルトでは以下の場所にid_rsaid_rsa.pubの名前で保存されますので、もしすでに作成しているキーが存在している場合は退避しておいてください。

Windows
%UserProfile%\.ssh

Linux
/home/{ユーザー}/.ssh

作成された公開鍵(id_rsa.pub)を SSHD へ登録して完了です。
下記ファイルにid_rsa.pub(RSA方式の場合)の内容を追記します。

/home/{ユーザ名}/.ssh/authorized_keys

>cd /home/{ユーザ名}/.ssh
>mv id_rsa.pub authorized_keys
>cat id_rsa.pub >> authorized_keys # すでにauthorized_keysが存在する場合は追記
>chomod 600 authorized_keys

これで秘密鍵(id_rsa)を使用してSSH 接続することができるようになりました。
この設定ファイルの場所やファイル名は sshd_config の AuthorizedKeyFile ディレクティブで変更が可能です。

AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

SSH 接続してみましょう
秘密鍵(id_rsa)を SSH 接続する側に持って行きます。
SSH 接続してみましょう。

>ssh -i /path/to/keys/id_rsa ec2-user@11.22.33.44 # ログインユーザ名@サーバのIPアドレス
>ssh -i /path/to/keys/id_rsa ec2-user@myserver.com # ログインユーザ名@ドメイン名

秘密鍵は/path/to/keysに配置しています。
ログインユーザ名とサーバのIPアドレスを指定することで SSH 接続できました。
この辺りのオプション設定が面倒であれば、コンフィグファイルにあらかじめエイリアスを設定することができます。

Windows
%UserProfile%/.ssh/config

Linux
/home/{ユーザ名}/.ssh/config

Host SSH-Server
  StrictHostKeyChecking no # know_hostsへの登録等の確認を省略
  Hostname myserver.com
  User ec2-user
  IdentityFile /path/to/keys/id_rsa

# 複数設定できます
Host TestRep
  Hostname github.com
  User git
  IdentityFile /path/to/keys/id_rsa

なお、自動デプロイなどのスクリプトから SSH 接続する場合、確認メッセージによって途中で動作が止まらないよう config の設定にて StrictHostKeyCheckingno にしておく必要があります。

sshコマンドを実行して SSH 接続します。

>ssh SSH-Server


GitHub へ SSH 接続する場合
GitHub へ SSH 接続してgit clone等を行う場合は、秘密鍵を GitHub へ登録します。
当該リポジトリの上部メニュー「Settings」より左メニュー「Deploy keys」をクリックし、「Add deploy key」ボタンをクリックします。

SSH キーの登録


Title にタイトルを入力し、Key に秘密鍵(id_rsa)の内容をコピーします。
コピーする内容はヘッダ部(—–BEGIN OPENSSH PRIVATE KEY—–など)やフッター部も含めてコピーします。

Allow write access にチェックを入れることで、git push が可能となります。
チェックを入れない場合は参照のみとなります。
Add key」をクリックして登録完了です。

ちなみにgit cloneする場合は、以下のようなコマンドになります。
config でキーの場所等を設定済みであれば、2行目のようにコマンドが簡略化できます。

>git -c core.sshCommand="ssh -i /path/to/keys/id_rsa" clone git@github.com:yanox2/TestRep.git
>git clone TestRep:yanox2/TestRep.git

以上です。

ちなみに、これはリモートリポジトリサーバから本番サーバなどへの SSH 接続の話です(図1の②)。
ややこしい話ですが、Git 環境をクローンするサーバがリモートリポジトリに SSH でアクセスするためには、これとは逆方向の SSH 接続設定が必要ですね(図1の③)。
こちらについては、以下の記事に説明がありますので参照してください。

Git を利用した開発環境の設定

SSH 接続のまとめ

設定元SSH 接続設定ファイル(設定メニュー)内 容
自環境する側(ssh).ssh/config秘密鍵(id_rsa)
自環境される側(sshd).ssh/authorized_keys
(sshd_config)
公開鍵(id_rsa.pub)
GitHubされる側(git cloneなど)(repository)Settings > Deploy keys (for repository)
(user)Settings > SSH and GPG keys (for user)
公開鍵(id_rsa.pub)



参考: Git を利用した開発環境・テスト環境・本番環境の構成
参考: Git ワークフロー(ブランチモデル)とその手順
参考: Git コンフリクトの解消
参考: git push を Chatwork や Slack へ通知する方法

コメント