はじめに
先日簡単なマイクロサービスをdeployしたのですが、サーバーを起動した時に自動でアプリケーションが動くように設定していなかった。だからやる。
このタスクの学び
参考
基本的にはこれを参考にします。 やってみる前にざっと読んでみました。 とても丁寧でいい記事。
やってみよ
ファイルの用意
2つのファイルを準備する必要があります。
- ユニットファイル(起動時に実行される設定ファイル)
- シェルスクリプト(ユニットファイルを通じて実行させるファイル)
しかし今回はバイナリファイルの実行のみなので、シェルスクリプトは作らない方針で行きます。
ユニットファイル
とりあえずこんな感じのユニットファイルを作った。
[Unit] Description=Start nabelog API server [Service] Type=simple ExecStart=/usr/local/src/main & Restart=no [Install] WantedBy=multi-user.target
Type=simple -> プロセスの起動方法を指定している。simpleはデフォルトであり、プロセスが起動した時点で起動完了のステータスとする。 ExecStart=/usr/local/src/main & -> 起動時に実行していているコマンド。バイナリファイルをバックグラウンド実行している。 WantedBy=multi-user.target -> 起動の依存を一応表している。(一応というのは、Unitに記載するBefore・Afterで十分だからだ。)multi-user.targetは通常のサービスの実行グループだと思ってもらえれば良い。
このファイルを移動させて、権限設定を行う。
なお、フォルダ階層は/etc/systemd/system/
におく。
(ユーザー自ら追加したものはここに入るらしい。)
# mv start-apiserver.service /etc/systemd/system/ # cd /etc/systemd/system/ # sudo chown root:root start-apiserver.service # sudo chmod 644 start-apiserver.service
自動起動設定
ユニットファイルを自動起動設定にする
ユニットファイルを追加,削除,修正したら必ず以下のコマンドを実行します。 これをすることで、システムがユニットファイルを認知します。
# sudo systemctl daemon-reload # sudo systemctl enable start-apiserver.service Created symlink /etc/systemd/system/multi-user.target.wants/start-apiserver.service → /etc/systemd/system/start-apiserver.service. # sudo systemctl status start-apiserver.service ○ start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: inactive (dead)
enabledとなっているので、起動待機状態になっております。
起動してみる
# sudo systemctl start start-apiserver.service # sudo systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-14 19:21:19 JST; 1min 43s ago Process: 580417 ExecStart=/usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 580417 (code=exited, status=1/FAILURE) CPU: 10ms Jan 14 19:21:19 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 14 19:21:19 ik1-127-70159.vs.sakura.ne.jp main[580417]: 2023/01/14 19:21:19 Error loading .env file Jan 14 19:21:19 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, stat> Jan 14 19:21:19 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
あんれ、起動してない。というか.env fileが読み込めないって言ってんな。 普通に実行してみる。
# cd /usr/local/src/ # ./main :@tcp(:3306)/~~~~~~~ ...........................
envファイルは読み込めてそうだな。だけど、dbと接続できてない。 rootじゃなくてrockyユーザーでやってみるか。
# ./main DB接続成功 [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] Loaded HTML Templates (2): - - index.html [GIN-debug] GET / --> main.main.func1 (4 handlers) [GIN-debug] GET /hello --> main.main.func2 (4 handlers) [GIN-debug] GET /shops --> main.main.func3 (4 handlers) [GIN-debug] POST /shops --> main.main.func4 (4 handlers) [GIN-debug] GET /shops/:id --> main.main.func5 (4 handlers) [GIN-debug] PATCH /shops/:id --> main.main.func6 (4 handlers) [GIN-debug] DELETE /shops/:id --> main.main.func7 (4 handlers) [GIN-debug] GET /influencers --> main.main.func8 (4 handlers) [GIN-debug] POST /influencers --> main.main.func9 (4 handlers) [GIN-debug] GET /influencers/:id --> main.main.func10 (4 handlers) [GIN-debug] PATCH /influencers/:id --> main.main.func11 (4 handlers) [GIN-debug] DELETE /influencers/:id --> main.main.func12 (4 handlers) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on :8080
あれ、繋がった...? じゃあ、rockyアカウントでユニットファイルを実行してみる。
# systemctl start start-apiserver.service ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ==== Authentication is required to start 'start-apiserver.service'. Authenticating as: rocky Password: ==== AUTHENTICATION COMPLETE ==== # systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-14 19:34:36 JST; 7s ago Process: 580585 ExecStart=/usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 580585 (code=exited, status=1/FAILURE) CPU: 8ms Jan 14 19:34:36 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 14 19:34:36 ik1-127-70159.vs.sakura.ne.jp main[580585]: 2023/01/14 19:34:36 Error loading .env file Jan 14 19:34:36 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, stat> Jan 14 19:34:36 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
変わんね〜〜。
シェルスクリプトを通じて実行できるか確認。
作成
./main
これを実行。
# sh start-main.sh DB接続成功 [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] Loaded HTML Templates (2): - - index.html
[GIN-debug] GET / --> main.main.func1 (4 handlers) [GIN-debug] GET /hello --> main.main.func2 (4 handlers) [GIN-debug] GET /shops --> main.main.func3 (4 handlers) [GIN-debug] POST /shops --> main.main.func4 (4 handlers) [GIN-debug] GET /shops/:id --> main.main.func5 (4 handlers) [GIN-debug] PATCH /shops/:id --> main.main.func6 (4 handlers) [GIN-debug] DELETE /shops/:id --> main.main.func7 (4 handlers) [GIN-debug] GET /influencers --> main.main.func8 (4 handlers) [GIN-debug] POST /influencers --> main.main.func9 (4 handlers) [GIN-debug] GET /influencers/:id --> main.main.func10 (4 handlers) [GIN-debug] PATCH /influencers/:id --> main.main.func11 (4 handlers) [GIN-debug] DELETE /influencers/:id --> main.main.func12 (4 handlers) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on :8080
おお、起動した。 じゃあ、やっぱりshellスクリプトを実行する形にするか。
シェルスクリプトの準備
ファイルの作成
シェルスクリプト自体はさっきのファイル。 一応改めて書いておく。
#!/bin/sh
./main &
権限の付与
権限付与して、確認する。
# sudo chown root:root start-main.sh # sudo chmod 755 start-main.sh # ll drwxr-xr-x 2 rocky rocky 4096 Dec 24 17:46 config -rw-r--r-- 1 rocky rocky 1212 Jan 2 15:09 go.mod -rw-r--r-- 1 rocky rocky 11547 Jan 2 15:09 go.sum -rwxr-xr-x 1 rocky rocky 15576907 Jan 2 15:09 main -rw-r--r-- 1 rocky rocky 6376 Jan 2 15:09 main.go drwxr-xr-x 2 rocky rocky 4096 Dec 24 16:25 mock drwxr-xr-x 2 rocky rocky 4096 Dec 24 16:25 model -rwxr-xr-x 1 root root 19 Jan 14 19:55 start-main.sh drwxr-xr-x 2 rocky rocky 4096 Dec 24 16:25 templates
試しに実行
# sudo sh start-main.sh
.....................
多分なんだけど、root権限で実行しようとするとエラー起こるわ。 root以外の権限で実行してみると、envファイルを読み込めてないわ。
。。。。。あーーーーーーー! root環境に環境変数を置いてないからかも。 確認。
# echo $NABELOG_ENV production # su - Password: Last login: Sat Jan 14 19:02:09 JST 2023 on pts/2 Last failed login: Sat Jan 14 20:04:50 JST 2023 from ~~~~ on ssh:notty There were 298 failed login attempts since the last successful login. # echo $NABELOG_ENV #
その通りすぎた。ということで、root環境にも環境変数を導入してあげましょ。
++ export NABELOG_ENV="production"
反映と確認。
# source ~/.bashrc # echo $NABELOG_ENV production
いいね、そしたら実行してみよう。
# sudo sh start-main.sh
.....................
だめなんかい!!! ようわからんかったけど、まぁ実行ユーザー変えれば良いかな。
ユニットファイルの実行ユーザーを変更
systemdの.serviceファイルで、実行ユーザーを指定する - Qiita
これを参考にしてやってみる。 変更後のファイルがこちら。 UserとGroupを足しました。
[Unit]
Description=Start nabelog API server
[Service]
Type=simple
ExecStart=/usr/local/src/main &
Restart=no
User=rocky
Group=rocky
[Install]
WantedBy=multi-user.target
じゃあ、ユニットファイルを反映して実行。
# sudo systemctl reset-failed start-apiserver.service # sudo systemctl start start-apiserver.service # sudo systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-14 20:27:18 JST; 10s ago Process: 581608 ExecStart=/usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 581608 (code=exited, status=1/FAILURE) CPU: 9ms Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp main[581608]: 2023/01/14 20:27:18 Error loading .env file Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, stat> Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
なんやねん!!!!
ユニットファイルからシェルスクリプトを実行
以下のように変更
#!/bin/sh cd /usr/local/src/ ./main &
[Unit] Description=Start nabelog API server After=local-ts.target ConditionPathExists=/opt/nabelog/ [Service] Type=simple Restart=no User=rocky Group=rocky ExecStart=/opt/nabelog/start-main.sh StandardOutput=journal+console [Install] WantedBy=multi-user.target
$ sudo systemctl daemon-reload $ sudo systemctl reset-failed start-apiserver.service $ sudo systemctl start start-apiserver $ sudo systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-14 20:27:18 JST; 10s ago Process: 581608 ExecStart=/usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 581608 (code=exited, status=1/FAILURE) CPU: 9ms Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp main[581608]: 2023/01/14 20:27:18 Error loading .env file Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, stat> Jan 14 20:27:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
なんかエラーは出ないけど、Deactivatedになって、何もされない。 せめてログに出てほしいなぁ。 別のコマンドでログを出してみるも
$ journalctl -xeu start-apiserveer.service ~ ~ -- No entries --
何もでねぇ〜〜。 これを参考にちょっと変えてみるか。
systemdので起動したアプリケーションのprintfを標準出力に表示したい | Armadillo
[Unit] Description=Start nabelog API server After=local-ts.target ConditionPathExists=/opt/nabelog/ [Service] Type=simple Restart=no User=rocky Group=rocky ExecStart=/opt/nabelog/start-main.sh StandardOutput=tty [Install] WantedBy=multi-user.target
結論、でねぇ。 journalctlでログを探ってみてるが、なんとも手がかりがない。げせぬ。
ユニットファイルの実行対象をmainに戻してもう一度考察
そしたらちゃんと前のエラーログも出た。よかった。 そして、エラーログを見たら新たな事実がわかった。
調べてみたらやっぱりそう。
systemd のユーザーインスタンスは .bashrc などに設定された環境変数を全く継承しません。
参考: systemd/ユーザー - ArchWiki
いやしろよww
そしたら、以下の方針。
systemdでも環境変数を読み込むようにする
参考
以下を作成
NABELOG_ENV="production"
そして、ユニットファイルを編集
++ EnvironmentFile=/etc/sysconfig/nabelog_env
リロードして、実行。
$ sudo systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-21 18:31:18 JST; 7s ago Process: 710112 ExecStart=/usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 710112 (code=exited, status=1/FAILURE) CPU: 8ms Jan 21 18:31:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 21 18:31:18 ik1-127-70159.vs.sakura.ne.jp main[710112]: 2023/01/21 18:31:18 Error loading .env.production file Jan 21 18:31:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, status=1/FAILURE Jan 21 18:31:18 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
おおーーー。環境変数読み込めてる〜〜!
ユニットファイルを編集
じゃあ、ユニットファイルからcdファイルも実行するようにする。 けど、これみる限りシェル実行でいけるっぽいな、やってみよ。
[CentOS7] systemdにサービスを登録して、サーバ起動時に自動でサービスを立ち上げる | RE:ENGINES
うーん、ダメだな。できなかった。 そしたら、シェル実行はやめて、コマンド自体をユニットファイルに書こう。
[Unit] Description=Start nabelog API server [Service] Type=simple ExecStart=cd /usr/local/src; /usr/local/src/main & Restart=no User=rocky Group=rocky [Install] WantedBy=multi-user.target
$ sudo systemctl status start-apiserver.service × start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Sat 2023-01-21 22:44:32 JST; 6s ago Process: 713420 ExecStart=cd /usr/local/src/ && /usr/local/src/main & (code=exited, status=1/FAILURE) Main PID: 713420 (code=exited, status=1/FAILURE) CPU: 2ms Jan 21 22:44:32 ik1-127-70159.vs.sakura.ne.jp systemd[1]: Started Start nabelog API server. Jan 21 22:44:32 ik1-127-70159.vs.sakura.ne.jp cd[713420]: /usr/bin/cd: line 2: cd: too many arguments Jan 21 22:44:32 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Main process exited, code=exited, status=1/FAILURE Jan 21 22:44:32 ik1-127-70159.vs.sakura.ne.jp systemd[1]: start-apiserver.service: Failed with result 'exit-code'.
cdがダメっぽいな。 なんか、WorkingDirectoryで設定できるっぽい。
[Unit] Description=Start nabelog API server [Service] WorkingDirectory=/usr/local/src/ EnvironmentFile=/etc/sysconfig/nabelog_env Type=simple ExecStart=/usr/local/src/main & Restart=no User=rocky Group=rocky StandardOutput=journal+console [Install] WantedBy=multi-user.target
じゃあ、実行。
$ sudo systemctl status start-apiserver.service ● start-apiserver.service - Start nabelog API server Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2023-01-21 22:50:23 JST; 17min ago Main PID: 713575 (main) Tasks: 3 (limit: 2696) Memory: 8.7M CPU: 36ms CGroup: /system.slice/start-apiserver.service └─713575 /usr/local/src/main "&"Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: [GIN-debug] PATCH /influencers/:id --> main.main.func11 (4 handlers) Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: [GIN-debug] DELETE /influencers/:id --> main.main.func12 (4 handlers) Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default Jan 21 22:50:23 ik1-127-70159.vs.sakura.ne.jp main[713575]: [GIN-debug] Listening and serving HTTP on :8080 Jan 21 22:50:43 ik1-127-70159.vs.sakura.ne.jp main[713575]: DB接続成功
へ!!!でけた!!!!wwwwww WorkingDirectoryを設定すればよかったのか。 enable設定もして、掃除して終わろう。
$ sudo systemctl enable start-apiserver.service
$ rm -rf /opt/nabelog/
確認作業
さくらインターネットでサーバーを再起動してみましょ。 サーバーをシャットダウンして、また起動してみる。 順番は、client → db → serverの順番にしましょう。
client起動
おおーー、おk
db起動
ちゃんと接続できるからok
server起動
データがちゃんと取得できる!!
よし、done。