Oracle Cloudでdiscordのbotを無料ホストする方法

# はじめに
[Heroku](https://jp.heroku.com/home)の無料枠が11月いっぱいで終わっちゃう！　みんなのbotをホストしてくれていたのに！
まあ無料なのでそんなこともあります。問題は、次どこにしようってことなんだが……。

色々探した結果、[Oracle Cloud](https://www.oracle.com/jp/cloud/)の無料枠が良さげだったのでここに記録しておきます。
Herokuほどお手軽ではないですが、どうせ一度セットアップしてしまえば放置するものなので頑張りましょう。
なおこの記事ではpython環境のdiscord.py前提とします。jsの方はわからん。
あとlsとかcdとか最低限のLinuxコマンド知識があるものとします。

# 用意するもの
1. デプロイするbot
1. Oracle Cloudアカウント
1. [teraterm](https://ja.osdn.net/projects/ttssh2/)などのSSH接続できるシェルアプリ
1. (optional)[WinSCP](https://winscp.net/eng/docs/lang:jp)などのSFTP対応のファイル転送ソフト

# わかる人向けの概略
1. Oracle CloudでOracle Linuxのインスタンスを1つ作ります。
1. teratermとかで接続します。
1. Pythonのバージョンが古いので3.9とかに上げます。
1. gitとかWinSCPとかでbotのファイル一式をアップロードします
1. nohupでプロセスを開始します。完了！

# 詳細手順
## Oracle Cloudを用意する
[Oracle Cloud](https://www.oracle.com/jp/cloud/)はその名の通りOracleが提供しているクラウドプラットフォームです。Oracle Linuxとかいう独自のLinux環境をホストできます。
まずは公式ページにアクセスしてアカウントを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/eca86b40-9ee9-b0c8-d863-6fa5cf5651a9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/20ff266e-83e0-3c0a-da05-9d042d7b254a.png)
アカウント作成は見ればわかると思うので省略します。
メルアドの他に地域とか支払情報とか色々求められます。無料枠のためにクレカ教えるのはなんか腹立ちますがまあしょうがないですね。
地域選択のところで東京地域は需要が高いので云々みたいな警告が出ますが、私は東京を選んで今のところなんも問題ないです。
晴れてアカウントが作られるとダッシュボードにアクセスできるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/07ac0a66-8a51-1cdf-0ec2-b55349fd42eb.png)
「インスタンス[^instance]」をクリックしてインスタンス一覧を出します。
ここの読み込みは10秒くらい待たされがちなのでページ変わってしばらく真っ白でも焦らず耐えてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/57fe0724-a075-6685-b415-1a6d3c49af84.png)
作りたてなので一覧は空だと思います。「インスタンス作成」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/7f5dd6c2-437f-a019-0abb-e449fe38649e.png)
「名前」のところにbotの名前を付けておくとよいです。
他はデフォルトのままでよいです。
「配置」と「イメージとシェイプ」それぞれに「Always Free対象」と出ていれば無料枠です。

SSHキーはわかる人はよしなにしてください。
わからない人は、「キー・ペアを自動で生成」を選んでおいて、「秘密キーの保存」をクリックして.keyファイルをダウンロードしておいてください。
※このファイルをなくすとアクセスできなくなるので注意してください。

キーの用意ができたら「作成」ボタンを押してください。
インスタンス一覧に戻ると今作ったインスタンスが出現しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/e36602f7-208e-8d35-2ebf-42ffc6605bc5.png)
スクショだと実行中になってますが、作った直後だと準備中とかになってると思います。すぐ終わるのでしばし待ちましょう。

## Oracle Linuxに接続する
わかる人は読み飛ばしてください。
シェルってなんじゃいという人は[teraterm](https://ja.osdn.net/projects/ttssh2/)をインストールしてください。
まずはインスタンスの接続情報を確認します。
インスタンス一覧で先ほど作ったものを選択すると、こんな画面が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/4094a189-603c-4e03-3ee1-fe3f48e4adb1.png)
右上にあるIPアドレスとユーザー名がログイン情報です。

さて、teratermを起動するとこんな画面が出ますので、ホストにインスタンスのIPアドレスを入力し、接続方法でSSHを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/73e1365e-a6d3-e777-e496-25b57bcdc009.png)
続けて認証画面が出ますので、ユーザー名にインスタンスのユーザー名、
秘密鍵(秘密キー)にはインスタンス作成時にダウンロードしておいた秘密鍵ファイルを指定します。
秘密鍵がパスワードなのでパスワード欄は空欄のままで大丈夫です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/fbc97dcd-d502-dd0e-57cb-be7d79c03e58.png)
秘密鍵ファイル選択時、デフォルトだと.keyファイルが表示されてないので右下で「すべてのファイル」を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/e61149c8-547b-6056-b543-26575bf94f6e.png)
OKを押してこんな感じで接続できれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/b17823e4-251a-95d9-2d9a-f44fc9c4f18f.png)

## python環境を構築する
Oracle Clouldは親切なのでなんとPython環境は最初から構築されています。
ただしバージョンが3.6.8です。discord.pyの要件が3.8以上なので古すぎです。有難迷惑……っ！
このままでは使えないので新たに3.9をインストールすることにします。

以下のコマンドを実行します。
`sudo yum install python3.9`[^yum]
パスワードを聞かれたらOracle Cloudアカウントのパスワードを入力します。
コンソールにしばらく色々出た後で完了のメッセージが出ます。
完了したら次のコマンドを実行してください。
`python3.9 --version`
次のようにバージョン表示が出ればインストール完了です。
`Python 3.9.7`

このままだと素のpythonしかないのでここから色んなパッケージを追加するわけですが、素すぎてpipが入ってないのでまずpipを入れます。
以下のコマンドを実行してget-pip.pyをインスタンス上にダウンロードします。
`wget https://bootstrap.pypa.io/get-pip.py`
「‘get-pip.py’ saved」と出ればダウンロード完了です。
次に以下のコマンドでpython3.9にpipをインストールします。
`python3.9 get-pip.py`
なんやかんやとログがいっぱい流れた後に、インストールが完了します。

普通のpythonガイドだとpythonの実行コマンドはまんま「python」または「python3」になってると思いますが、この環境では既にインストール済みの3.6.8が実行されてしまうので毎回「python3.9」とバージョンまで指定してあげてください。
もっとスマートなやり方があるのかもしれないけどbotごときにそこまでがんばりたくないし……。

## botをセットアップする

python環境ができたので、最後に必要なパッケージをインストールしていきます。
まずはbot本体のソースコード一式を用意します。Herokuを使っていた方は多分githubとかにアップロードしてあったと思うので、直接cloneできます。
まずは以下のコマンドでgitをインストールしておきます。
`sudo yum install git`
次にgit用のSSH鍵を用意します。これはOracle Cloud用の鍵とは別なので注意してください。
解説はググればいっぱい出るので割愛します。これとか。

https://codelikes.com/github-ssh-connection/

gitにSSH接続できたら、以下のコマンドでcloneします。
`git clone botのリポジトリ.git`

gitを使いたくないとかコマンドわからんになった人はWinSCPなどのSFTPソフトからもファイルを転送できます。
シェルの時と同様IPアドレスとユーザー名、SSH秘密鍵があれば接続できます。
`/home/[ユーザー名]`というディレクトリがホームなので、そこにファイルを配置すればOKです。

次にログインに使うトークンです。Herokuの場合はGUIで環境変数を設定してましたが、めんどくさいのでdotenvを使いましょう。
botの.pyファイルと同じディレクトリに「.env」というファイルを作って、こんな感じでトークンを保存しておきます。
`DISCORD_BOT_TOKEN=YOUR_BOT_TOKEN_HERE`
変数名はソースコードに合わせてください。
そしてbotのソースコードの先頭らへんにこんな感じで.envの読み込み処理を追加しておきます。
```python
from dotenv import load_dotenv
load_dotenv()
```
これで.envに設定したトークンを環境変数として読み込めるようになります。

実行ファイルが出揃ったらいよいよ実行です。
cdコマンドでbotの.pyファイルがあるディレクトリに移動して、以下のコマンドでbotを開始します。
`python3.9 [botのmainファイル名.py]`

最初は多分、なんかのパッケージがないみたいなエラーが出て止まると思います。
pythonが素すぎるせいなので、順次インポートしましょう。
ないと言われたパッケージ名を元に以下のようにコマンドを実行します。
`python3.9 -m pip install パッケージ名`
たとえばdiscord.pyが無いと「discordがない」みたいなエラー(詳しい本文忘れた)が出るので、以下のようにします
`python3.9 -m pip install discord`
これをエラーがなくなるまで繰り返します。他の設定がちゃんとできてればそのうち動きます。

## botをデプロイする
botが動くようになったら、最後にこれを常時起動できるようにします。
普通にpythonコマンドで実行するとシェルを切断した時にbotも停止してしまいます。
切断後もbotが動き続けるようにするには、以下のコマンドを実行します。
`nohup python3.9 [botのmainファイル名.py] &`
nohupは切断時に強制終了させなくするコマンドです。これに続けてbot実行のコマンドを実行することで常時起動状態にします。
シェルを切断してもbotが動いていることを確認してください。
やったー。

メンテなどでbotを停止したいときは、PIDを特定してkillコマンドを使います。
まずは以下のコマンドを実行します。
`ps -ef | grep python3.9`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627722/bb2d1f35-90ba-2ae9-feac-1802167df195.png)[^give_me_prompt]
結果が2行くらい出ると思います。右端がプロセス名です。
プロセス名が「python3.9 [botのmainファイル名.py]」になっている行の、左から2番目の数値をコピーしてください。画像で言うと「907451」の箇所です。これがPID(プロセスID)です。
そして、このプロセスIDを使って以下のコマンドを実行します
`kill -9 PID`
実行すると直ちに終了します。オンラインステータスが少しの間オンラインのまま残ることがありますが、すでに反応はなくちゃんと死んでいるはずです。
おしまい。

# おわりに
やっぱり本格的なサービスなのでできることが多い反面DIY度が高いですね。
でもLinuxでなんやかんやしたことのある人にとっては特に新しく勉強することもなく使える類だと思います。使えた。
雰囲気でやってる人は……がんばれ。超がんばれ。

ところでdotenvじゃない正規の方(？)の環境変数設定法を調べたけど全然出ん。
.bash_profileに書いてsourceしても無視されるし英語でも情報がない。何これ。知ってる人はコメント頂けると誰かを救えるかもしれないし救えないかもしれない。

[^instance]:Oracle Cloudでは仮想のLinuxマシンをユーザーに割り当てて使ってもらうのだが、この仮想マシンが実際に動いているその1つ1つを指してインスタンスという。「実体」みたいな訳語が使われる。「インスタンスを作る」とはつまり「この設定のマシンを仮想環境上に構築する」みたいな意味。
[^yum]:違うかも。ここで試行錯誤して手間取ったから結局どれが正解だったかわかんなくなっちゃった。sudoはよくないぜみたいなWarningが出たのは覚えてるのだが……だめだったらコメントで教えてください
[^give_me_prompt]:どーでもいいですがこのgive_me_promptとかいう珍名botの正体はAI生成画像に特定絵文字でリアクションするとメタデータからpromptを拾ってDMしてくれるやつです。内輪用ですが欲しいという方にはお譲りしておりますのでtwitterでDMなどください。