Fly.io の「fly console」と「fly ssh console」の違いがわからなかったので色々とコマンドを実行して調べてみた

### 前提

- [Fly.io](https://fly.io/)のページについては、2024年8月30日時点の構成で説明しています。
- 検証環境
    - MacBook Pro(Intel)
        - macOS Sonoma 14.5

---

これまで、[Fly.io](https://fly.io/)で稼働中（ `fly deploy` コマンドによってデプロイ済み）の仮想マシンにログインするには `fly console --machine XXXXXXXXXXXXXX` を利用していましたが、最近、 `fly ssh console --machine XXXXXXXXXXXXXX` でもログインできることを知りました。
これらのコマンドの違いがわからなかったので、今回は色々とコマンドを実行して試したことをまとめてみます。

---

前述のとおり、デプロイ済みの仮想マシンにログインするためのコマンドは以下の2種類があるようです。

- [fly console](https://fly.io/docs/flyctl/console/)
- [fly ssh console](https://fly.io/docs/flyctl/ssh-console/)

`fly console` と `fly ssh console` は機能的に似ているので、エイリアスのようなものかもしれないと思い「[Docs](https://fly.io/docs/) (Fly.io developer documentation)」のページを見てみると、それぞれ独立したページになっており、内容も異なることがわかりました。

まずは `fly console` の[Docs](https://fly.io/docs/flyctl/console/)を読んでみると、「新しい仮想マシン、または、既存の仮想マシンでコンソールを実行する」とのこと。
「既存の仮想マシン」に接続できることはわかりますが、「新しい仮想マシン」という部分がよくわかりませんでした。
また、 `console` コマンドは、 `console_command` という設定用のフィールドを持つようです。
`--machine` については、これまで私が利用していた方法なので、既存の仮想マシンへの接続時に指定するものだということは把握済みです。

![スクリーンショット 2024-08-30 22.46.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/3c53a6d0-07ba-dca3-76e3-c5fcecc13330.png)

---

#### --help オプション

`fly console --help` を実行すると、Docsのページと同様の説明が記載されていました。

```
% fly console --help
Run a console in a new or existing machine. The console command is specified by
the `console_command` configuration field. By default, a new machine is created
by default using the app's most recently deployed image. An existing machine
can be used instead with --machine.

Usage:
  fly console [flags]

Flags:
  -a, --app string                  Application name
  -C, --command string              command to run on SSH session
  -c, --config string               Path to application configuration file
      --dockerfile string           Path to a Dockerfile. Defaults to the
                                    Dockerfile in the working directory.
      --entrypoint string           ENTRYPOINT replacement
  -e, --env stringArray             Set of environment variables in the
                                    form of NAME=VALUE pairs. Can be
                                    specified multiple times.
      --file-literal stringArray    Set of literals to write to the
                                    Machine, in the form of
                                    /path/inside/machine=VALUE pairs,
                                    where VALUE is the base64-encoded raw
                                    content. Can be specified multiple times.
      --file-local stringArray      Set of files to write to the Machine,
                                    in the form of
                                    /path/inside/machine=<local/path>
                                    pairs. Can be specified multiple times.
      --file-secret stringArray     Set of secrets to write to the
                                    Machine, in the form of
                                    /path/inside/machine=SECRET pairs,
                                    where SECRET is the name of the
                                    secret. The content of the secret
                                    must be base64 encoded. Can be
                                    specified multiple times.
  -h, --help                        help for console
      --host-dedication-id string   The dedication id of the reserved
                                    hosts for your organization (if any)
  -i, --image string                image to use (default: current release)
      --machine string              Run the console in the existing
                                    machine with the specified ID
  -p, --port strings                Publish ports, format:
                                    port[:machinePort][/protocol[:handler[:handler...]]])
                                    		i.e.: --port 80/tcp --port 443:80/tcp:http:tls --port 5432/tcp:pg_tls
                                    		To remove a port mapping use '-' as handler, i.e.: --port 80/tcp:-
  -r, --region string               The target region (see 'flyctl
                                    platform regions')
  -s, --select                      Select the machine on which to
                                    execute the console from a list.
  -u, --user string                 Unix username to connect as (default
                                    "root")
      --vm-cpu-kind string          The kind of CPU to use ('shared' or
                                    'performance')
      --vm-cpus int                 Number of CPUs
      --vm-gpu-kind string          If set, the GPU model to attach
                                    (a100-pcie-40gb, a100-sxm4-80gb,
                                    l40s, a10, none)
      --vm-gpus int                 Number of GPUs. Must also choose the
                                    GPU model with --vm-gpu-kind flag
      --vm-memory string            Memory (in megabytes) to attribute to
                                    the VM
      --vm-size string              The VM size to set machines to. See
                                    "fly platform vm-sizes" for valid values
      --volume strings              Volume to mount, in the form of
                                    <volume_id_or_name>:/path/inside/machine[:<options>]
      --wg                          Determines whether communication with
                                    remote builders are conducted over
                                    wireguard or plain internet(https)
                                    (default true)

Global Flags:
  -t, --access-token string   Fly API Access Token
      --debug                 Print additional logs and traces
      --verbose               Verbose output
```

---

それでは、Docsの説明ではよくわからない部分があったので、実際に `fly console` を実行してみましょう。

#### オプション未指定（アプリケーションが存在しない状態）

まず、Fly.io上にアプリケーションが何も存在しない状態で `fly console` を実行します。
すると、 `app name` （アプリケーション名）が見つからないので、 `fly.toml` ファイル内 `app` の項目を追加するか、「 `-a` 」と共に指定せよというエラーが発生しました。

```
% fly console
Error: the config for your app is missing an app name, add an app field to the fly.toml file or specify with the -a flag
```

#### -a オプション（アプリケーションが存在しない状態）

次に、Fly.io上に存在しないアプリケーション名である `akase244-hellofly` を指定して実行してみます。
すると、存在しないアプリケーション名なので、 `akase244-hellofly` が見つからないというエラーが発生します。

```
% fly console -a akase244-hellofly
Error: failed to get app: Could not find App "akase244-hellofly"
```

アプリケーションが存在しない場合の動作は確認できましたので、次はアプリケーションを作成してからコマンドを実行してみます。
以前書いた「[Fly.io の「Hands-on」のページに従ってデプロイの練習をやってみた](https://qiita.com/akase244/items/ef9267f6e7c68ccb0f6a)」を参考に、 `akase244-hellofly` というアプリケーションを作成します。

```
% mkdir akase244-hellofly && cd $_
% fly launch --image flyio/hellofly:latest
```

Fly.ioのアプリケーション一覧の画面に `akase244-hellofly` が表示され、アプリケーションが作成されていることが確認できました。

![スクリーンショット 2024-09-01 11.21.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/f92555bd-38c8-e635-c62e-8460bee5289c.png)

仮想マシン一覧の画面には2台の仮想マシンが作成されていることが確認できました。

![スクリーンショット_2024-09-01_11_25_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/03f75280-d64e-a636-1b48-dff27cbb9248.png)

先程 `fly launch` を実行したディレクトリには `fly.toml` ファイルが作成されていますので、こちらを確認すると、 `app` の項目にアプリケーション名が指定されていることがわかります。（`fly launch` 実行時にカレントディレクトリ名がアプリケーション名として採用されるというのがデフォルトの動作のようです）

```
% grep -E '^app' fly.toml 
app = 'akase244-hellofly'
```

#### オプション未指定

アプリケーションの準備が整いましたので、この状態で `fly console` を実行します。すると「Created an ephemeral machine 〜」というメッセージが表示され、仮想マシンにログインすることができました。
ここで、Docsの「By default, a new machine is created by default using the app's most recently deployed image.」という説明を思い出してみます。
この「ephemeral machine」というのは、直近のデプロイイメージから作られた一時的な仮想マシン環境のようで、この環境にログインすることで構築が正しく行えているかといった調査などの利用用途なのではないかと予想しています。

```
% fly console
Created an ephemeral machine 32871542ceXXXX to run the console.
Connecting to fdaa:9:4eda:a7b:2de:28da:XXXX:2... complete
32871542ceXXXX:/goapp# 
```

`fly console` で接続を維持した状態で仮想マシン一覧を表示すると、先程の2台の状態から1台増えており、「Process」欄に「fly_app_console」と表示されていることが確認できます。
接続した状態の仮想マシンからログアウトすると、この一時的な環境は自動的に削除されるようで、仮想マシン一覧からも表示が消える仕組みになっています。

![スクリーンショット_2024-09-01_11_29_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/b87d41b0-258c-4e8f-e880-e63c282d7261.png)

#### --machine オプション

次は、 `fly console` に「 `--machine` 」を指定して、作成済みの仮想マシンにログインしてみます。
この方法は、Docsの「An existing machine can be used instead with --machine.」の部分の説明にあたります。

```
% fly console --machine 4d89440b6eXXXX
Connecting to fdaa:9:4eda:a7b:d86c:5baa:XXXX:2... complete
4d89440b6eXXXX:/goapp# 
```

#### -a オプション

次に「 `-a` 」を指定した形式で実行してみます。すると、「Created an ephemeral machine 〜」と表示されました。
つまり、引数に何も指定しない場合と同様で、実行しているディレクトリ直下に `fly.toml` が存在しており、その中で `app` が指定されている状態と同義の動作のようです。

```
% fly console -a akase244-hellofly
Created an ephemeral machine e78432e1a3XXXX to run the console.
Connecting to fdaa:9:4eda:a7b:2de:1629:XXXX:2... complete
e78432e1a3XXXX:/goapp# 
```

#### -c オプション / --config オプション

当然、以下のように「 `-c` 」または「 `--config` 」で `fly.toml` のパスを個別に指定した場合も同様の動作になります。

```
% fly console -c fly.toml
Created an ephemeral machine 908016e3cdXXXX to run the console.
Connecting to fdaa:9:4eda:a7b:2de:8331:XXXX:2... complete
908016e3cdXXXX:/goapp# 
```

```
% fly console --config fly.toml 
Created an ephemeral machine 17815615c0XXXX to run the console.
Connecting to fdaa:9:4eda:a7b:2df:8fca:XXXX:2... complete
17815615c0XXXX:/goapp# 
```

#### -C オプション

「 `-C` 」は接続と同時に実行するコマンドが指定可能で、コマンド実行後は接続が閉じられるようです。

```
% fly console -C "cat /etc/os-release"
Created an ephemeral machine 3d8dd79dc9XXXX to run the console.
Connecting to fdaa:9:4eda:a7b:2df:b142:XXXX:2... complete
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
Waiting for ephemeral machine 3d8dd79dc9XXXX to be destroyed ... done.
```

#### console_command

ここで、Docsの「The console command is specified by the `console_command` configuration field.」という説明を思い出したので、 `fly.toml` に以下のような設定を追加してみます。

```
% grep -E '^console_command' fly.toml  
console_command = 'cat /etc/os-release'
```

`fly.toml` に上記の設定を行った状態で `fly console` を実行すると `console_command` に指定されたコマンドが実行され、コマンド実行後は接続が閉じられます。

```
% fly console --machine 4d89440b6eXXXX
Connecting to fdaa:9:4eda:a7b:d86c:5baa:XXXX:2... complete
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
```

なお、 `fly.toml` の `console_command` については、「[App configuration (fly.toml)](https://fly.io/docs/reference/configuration)」の「[Console command](https://fly.io/docs/reference/configuration/#console-command)」にて説明されていますので、こちらもあわせてご覧ください。

![スクリーンショット 2024-08-31 21.56.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/5ba38cf3-0952-cced-0f32-c2be9240c796.png)

#### -s オプション

もう一つ気になるオプションとしては「 `-s` 」です。Fly.ioではアプリケーション作成時にデフォルトで仮想マシンが2台構築されるため、初期状態で複数台構成となります。
「Select the machine on which to execute the console from a list.」と説明されているので、このオプションを使えば複数台の仮想マシンの中から選択して接続できるようです。
「 `-s` 」を付与して実行してみると「Use arrows to move, type to filter」と表示され、稼働中の仮想マシン、及び、「create an ephemeral machine」の選択肢が表示されました。

```
% fly console -s
? Select a machine:  [Use arrows to move, type to filter]
> create an ephemeral machine (1 shared CPU, 256 MB of memory)
  nrt: 4d89440b6eXXXX fdaa:9:4eda:a7b:d86c:5baa:XXXX:2 XXXXXX-XXXX-1862
  nrt: 9185e27eb5XXXX fdaa:9:4eda:a7b:2df:ae29:XXXX:2 XXXXX-XXXXX-6369
```

上下のカーソルキーを入力すると、以下のように選択肢が切り替わります。

```
% fly console -s
? Select a machine:  [Use arrows to move, type to filter]
  create an ephemeral machine (1 shared CPU, 256 MB of memory)
> nrt: 4d89440b6eXXXX fdaa:9:4eda:a7b:d86c:5baa:XXXX:2 XXXXXX-XXXX-1862
  nrt: 9185e27eb5XXXX fdaa:9:4eda:a7b:2df:ae29:XXXX:2 XXXXX-XXXXX-6369
```

また、仮想マシンのIDやIPv6のアドレスなどの値を入力することで、選択肢をフィルタすることも可能です。

```
% fly console -s
? Select a machine: 4d89  [Use arrows to move, type to filter]
> nrt: 4d89440b6eXXXX fdaa:9:4eda:a7b:d86c:5baa:XXXX:2 XXXXXX-XXXX-1862
```

---

`fly console` には他にも様々なオプションが指定可能ですが、Docs(`--help`)の概要に説明されていた内容については、どのような操作が行えるのか理解できたのではないでしょうか。

続いて `fly ssh console` の[Docs](https://fly.io/docs/flyctl/ssh-console/)について見ていきましょう。
`fly ssh console` は「起動中の仮想マシンに接続できる」ようです。すごくシンプルな説明で非常にわかりやすいですね。

![スクリーンショット 2024-08-30 22.47.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/01baa587-996b-0cfe-5e15-52fcbc608dfa.png)

---

#### --help オプション

`fly ssh console --help` を実行すると、Docsのページと同様の説明が記載されています。

```
% fly ssh console --help
Connect to a running instance of the current app.

Usage:
  fly ssh console [flags]

Flags:
  -A, --address string         Address of VM to connect to
  -a, --app string             Application name
  -C, --command string         command to run on SSH session
  -c, --config string          Path to application configuration file
  -h, --help                   help for console
      --machine string         Run the console in the existing machine
                               with the specified ID
  -o, --org string             The target Fly.io organization
  -g, --process-group string   The target process group
      --pty                    Allocate a pseudo-terminal (default: on
                               when no command is provided)
  -q, --quiet                  Don't print progress indicators for WireGuard
  -r, --region string          The target region (see 'flyctl platform
                               regions')
  -s, --select                 select available instances
  -u, --user string            Unix username to connect as (default "root")

Global Flags:
  -t, --access-token string   Fly API Access Token
      --debug                 Print additional logs and traces
      --verbose               Verbose output
```

---

`fly console` の場合と同様に `fly ssh console` も色々と実行していきたいところですが、その前に、一旦「akase244-hellofly」のアプリケーションを削除しておきます。

```
% fly apps destroy akase244-hellofly      
Destroying an app is not reversible.
? Destroy app akase244-hellofly? (y/N) y
```

#### オプション未指定（アプリケーションが存在しない状態）

Fly.io上にアプリケーションが何も存在しない状態になりましたので `fly ssh console` を実行します。
引数に何も指定しない場合と「 `-a` 」を指定した場合は `fly console` と同様のエラーメッセージが表示されています。

```
% fly ssh console
Error: the config for your app is missing an app name, add an app field to the fly.toml file or specify with the -a flag
```

#### -a オプション（アプリケーションが存在しない状態）

```
% fly ssh console -a akase244-hellofly
Error: get app: Could not find App "akase244-hellofly"
```

では、再度「akase244-hellofly」のアプリケーションを作成し、2台の仮想マシンが存在する状態を構築します。

![スクリーンショット_2024-09-01_12_56_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/53bbcf72-e52f-d91b-34bf-53ef01e20db9.png)

#### オプション未指定

`fly ssh console` の引数に何も指定せずに実行すると今度は仮想マシンにログインできました。

```
% fly ssh console
Connecting to fdaa:9:4eda:a7b:2df:590c:XXXX:2... complete
6e8254d5c3XXXX:/goapp# 
```

ここで疑問が湧きます。Fly.ioはアプリケーションを作成するとデフォルトで2台の仮想マシンが構築されますが、引数に何も指定せずに実行した場合、どちらの仮想マシンに接続されるのでしょうか。
ということで、何度か同じコマンドを実行して接続と切断を繰り返してみると1台目に接続する場合と、以下のように2台目に接続される場合があるとわかりました。（仮想マシンのIDの表示が「6e8254d5c3XXXX」から「908016e3cdXXXX」に変わっています）
つまり、仮想マシンが複数稼働している場合、接続先をどのように選定しているかまではわかりませんが、「いずれかの仮想マシン」につながるようです。

```
% fly ssh console
Connecting to fdaa:9:4eda:a7b:2de:36bc:XXXX:2... complete
908016e3cdXXXX:/goapp# 
```

#### -a オプション

上記の「いずれかの仮想マシンにつながる」という動作は「 `-a` 」、「 `-c` ( `--config` )」を指定した場合も同様で、以下のとおり接続と切断を繰り返すといずれかの仮想マシンにつながることが確認できました。

「 `-a` 」を指定すると「6e8254d5c3XXXX」に接続する場合と「908016e3cdXXXX」に接続する場合があることを確認。

```
% fly ssh console -a akase244-hellofly
Connecting to fdaa:9:4eda:a7b:2df:590c:XXXX:2... complete
6e8254d5c3XXXX:/goapp#
```

```
% fly ssh console -a akase244-hellofly
Connecting to fdaa:9:4eda:a7b:2de:36bc:XXXX:2... complete
908016e3cdXXXX:/goapp# 
```

#### -c オプション

「 `-c` 」を指定すると「6e8254d5c3XXXX」に接続する場合と「908016e3cdXXXX」に接続する場合があることを確認。

```
% fly ssh console -c fly.toml
Connecting to fdaa:9:4eda:a7b:2df:590c:XXXX:2... complete
6e8254d5c3XXXX:/goapp#
```

```
% fly ssh console -c fly.toml
Connecting to fdaa:9:4eda:a7b:2de:36bc:XXXX:2... complete
908016e3cdXXXX:/goapp# 
```

#### -A オプション

「 `-A` 」を指定して、仮想マシンごとに割り当てられているIPv6形式のIPアドレスを引数に与えることで、仮想マシンに接続することができます。

```
% fly ssh console -A fdaa:9:4eda:a7b:2de:36bc:XXXX:2
Connecting to fdaa:9:4eda:a7b:2de:36bc:XXXX:2... complete
908016e3cdXXXX:/goapp# 
```

IPアドレスの値は仮想マシンの詳細画面で確認することが可能です。

![スクリーンショット_2024-09-01_13_19_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/0e7c33c9-3724-3b82-d776-77c7849aded0.png)

もしくは、 `fly machine list` を実行すると表示されます。

```
% fly machine list 
2 machines have been retrieved from app akase244-hellofly.
View them in the UI here (​https://fly.io/apps/akase244-hellofly/machines/)

akase244-hellofly
ID            	NAME            	STATE  	CHECKS	REGION	ROLE	IMAGE                	IP ADDRESS                     	VOLUME	CREATED             	LAST UPDATED        	PROCESS GROUP	SIZE                
6e8254d5c3XXXX	XXXXX-XXXXX-XXXX 	started	      	nrt   	    	flyio/hellofly:latest	fdaa:9:4eda:a7b:2df:590c:XXXX:2	      	2024-09-01T03:46:19Z	2024-09-01T04:18:17Z	app          	shared-cpu-1x:256MB	
908016e3cdXXXX	XXXXX-XXXXX-XXXX	started	      	nrt   	    	flyio/hellofly:latest	fdaa:9:4eda:a7b:2de:36bc:XXXX:2	      	2024-09-01T03:46:32Z	2024-09-01T04:06:07Z	app          	shared-cpu-1x:256MB	
```

#### -s オプション

「 `-s` 」は「 `fly console -s` 」とほぼ同等の機能で「ephemeral machine」の選択肢がないバージョンと考えれば良さそうです。

```
% fly ssh console -s
? Select VM:  [Use arrows to move, type to filter]
> nrt: 6e8254d5c3XXXX fdaa:9:4eda:a7b:2df:590c:XXXX:2 XXXXX-XXXXX-XXXX
  nrt: 908016e3cdXXXX fdaa:9:4eda:a7b:2de:36bc:XXXX:2 XXXXX-XXXXX-XXXX
```

#### -u オプション

最後は「 `-u` 」を試してみます。「 `--help` 」に「Unix username to connect as (default "root")」と記載されているとおり、仮想マシンに接続する際のユーザーは通常「root」になっています。
接続後に「 `whoami` 」を実行すると「root」と表示されます。

```
% fly ssh console --machine 6e8254d5c3XXXX
Connecting to fdaa:9:4eda:a7b:2df:590c:XXXX:2... complete
6e8254d5c3XXXX:/goapp# whoami
root
```

「 `-u` 」を指定したログインに使えそうなユーザーを確認したところ、どうやらなさそうです。

```
6e8254d5c3XXXX:/goapp# grep -v nologin /etc/passwd
root:x:0:0:root:/root:/bin/ash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
6e8254d5c3XXXX:/goapp# 
```

「 `-u` 」を指定したログインを検証するために「testuser」を作成します。

```
6e8254d5c3XXXX:/goapp# adduser testuser
Changing password for testuser
New password: 
Retype password: 
passwd: password for testuser changed by root
6e8254d5c3XXXX:/goapp# 
```

「 `-u` 」に「testuser」を指定して接続して、「 whoami 」を実行すると「testuser」と表示されます。

```
% fly ssh console --machine 6e8254d5c3XXXX -u testuser
Connecting to fdaa:9:4eda:a7b:2df:590c:XXXX:2... complete
6e8254d5c3XXXX:/goapp$ whoami
testuser
```

#### console_command

なお、 `fly ssh console` の `--help` に `console_command` の記述がないことでわかるとおり、  `console_command` の影響を受けるのは `fly console` のみとなります。

`fly ssh console` は他にもいくつかオプションが指定可能ですが、主要なオプションについての使用方法がわかったかと思います。

---

### まとめ

- 「 `fly console` 」、「 `fly ssh console` 」のどちらを使ってもよいが、「 `fly console` 」を利用すると意図せず「ephemeral machine」に接続される場合があるので、どちらを利用するにしても「 `--machine` 」を指定した方がよいのではないかと感じました。
- 「 `fly ssh console` 」で引数を与えない場合は、意図した仮想マシンに接続できていない可能性がある（例えば、「webアプリケーション用途」と「定期処理用途」で仮想マシンの「PROCESS GROUP」を分けている場合など）ので、こういった場合もやはり「 `--machine` 」を指定した方がよいかと思います。
- 「 `fly console` 」と「 `fly ssh console` 」では「 `-a` 」を指定した場合の挙動が異なるので、「 `-a` 」はなるべく使わない方がよさそうです。
- これまではFly.ioの画面上や `fly machine list` で仮想マシンのIDを調べて「 `--machine 6e8254d5c3XXXX` 」のように指定していましたが、「 `fly console` 」、「 `fly ssh console` 」のいずれの場合でも「 `-s` 」を指定することで稼働中の仮想マシンを選択して接続できることがわかりました。