VSCodeとEC2で実現する快適リモート開発

ローカルで開発したソースコードをEC2上にデプロイして動作確認するシーンがありました。
効率化するために、VSCodeから直接EC2へ接続しEC2上でコードの修正や実行をするための設定を行いましたので、その方法を紹介します。


手順は以下になります。最後に実動作を紹介します。
1. EC2作成
1. ローカルからEC2へssh接続するための設定
1. VSCodeからEC2へssh接続するための設定
1. 開発デモ


## 1. EC2作成

以下のテンプレートを使用してCloudFromationでサクッとEC2を作成します。
*CloudFromationからssmパラメータを参照できますので、それを利用して最新のAMIの情報を自動で取得してます。

<details><summary>ec2.yaml</summary>

```yaml
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  VpcId:
    Type: String
  PrivateSubnetId:
    Type: String
  Ec2ImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64

Resources:
  # Private key will be stored in the following.
  # AWS Systems Manager>Parameter Store>/ec2/keypair/key-xxxxxx
  KeyPair:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: !Sub ec2-for-dev

# ------------------------------------------------------------#
# IAM Role
# ------------------------------------------------------------#
  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: !Sub |
        {
          "Version": "2012-10-17",
          "Statement": [
          {
            "Effect": "Allow",
            "Principal": {
              "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
          }
          ]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
      Path: "/"
      RoleName: test-instance-role

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
        - !Ref InstanceRole

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group for EC2 Instance
      VpcId: !Ref VpcId

# ------------------------------------------------------------#
# EC2 Instance
# ------------------------------------------------------------#
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref Ec2ImageId
      InstanceType: t2.medium
      KeyName: !Ref KeyPair
      IamInstanceProfile:
        !Ref InstanceProfile
      SecurityGroupIds:
        - !Ref SecurityGroup
      SubnetId:
        !Ref PrivateSubnetId
      Tags:
      - Key: Name
        Value: !Sub ec2-for-dev

# ------------------------------------------------------------#
Outputs:
  KeyPairId:
    Value: !GetAtt KeyPair.KeyPairId


```
</details>

## 2. ローカルからEC2へssh接続するための設定

- Key情報取得

  上記手順でEC2を作成すると、/ec2/keypair/key-xxxという名前でAWS Systems ManagerのParameter Storeに鍵情報が保管されます。

  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/7e371256-1dd9-058f-2f19-bb83a5655347.png)

  上記の画面のShow decrypted valueから値を表示し、任意の場所にファイルを作成しssh接続のための鍵をコピペします。



```:e.g) C:\Users\xxx\\.ssh\ec2-for-dev.pem
-----BEGIN RSA PRIVATE KEY-----
             :
           (省略)
             :
-----END RSA PRIVATE KEY-----
```

- ssh config設定
*私の実行環境はプロキシ配下の環境で自己証明書を使用しているので、AWS CLIにその証明書を信頼してもらうために`--ca-bundle`オプションを指定してます。

```:e.g) C:\Users\xxx\\.ssh\config"
Host ec2-for-dev
  HostName i-xxx
  User ec2-user
  Port 22
  IdentityFile C:/Users/xxx/.ssh/ec2-for-dev.pem
  ProxyCommand "C:/Program Files/Amazon/AWSCLIV2/aws" ssm start-session --target %h --document-name AWS-StartSSHSession --parameters "portNumber=%p" --region ap-northeast-1 --ca-bundle "C:\Users\xxx\xxx.crt"

```


  以下のコマンドを実行してEC2へssh接続できることを確認

```
ssh ec2-for-dev
```

  接続すると以下のようになり、ローカルからEC2へ接続できるとこが確認できました。
(念のためEC2のidは伏せておきます)
```
The authenticity of host 'i-xxx (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:UUh4lQV/W0p23GHedtmItYyh/XXXXXXXXXXXXXXXXXXX
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'i-xxx' (ECDSA) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
```


## 3. VSCodeからEC2へssh接続するための設定

- VS CodeからRemote SSHをインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/cb75728c-3884-d094-3792-251af003486e.png)



- EC2へのSSH接続
`cntl + shift + p`で`Remote-SSH: Connect to Host`をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/c0a5c1a4-a783-ea1a-852f-0112b9af98b4.png)

  ssh configを読み込み該当の接続先が表示されるのでクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/318cae67-8cf8-3be8-6db5-b3f61aa34f7e.png)


  接続先OSを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/ff848af6-941b-5195-a5b8-2b731a010294.png)

  接続完了！
  Welcomeページが表示されるので、Open Folderをクリックしてec2-userのhome directoryを開いてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/ecc480b2-1828-0ee9-39f6-4c2c82973ba2.png)


  フォルダ内のファイルの作成者を信頼するかどうかメッセージが表示されます。
  私以外EC2に接続していないのでとりあえず信頼しますが、外部の環境に接続する際は注意が必要ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/46307d32-2b0b-a5ae-2ed8-92a23cd7d9c8.png)


##  4. 開発デモ

- app.py作成
接続すると左側に接続先環境のdirectoryが表示されます。
早速app.pyというファイルを作成し、適当に実装します。
せっかくなのでブラウザアプリを作成してローカルからアクセスしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/8890979e-32b7-3616-883e-a14098b731ed.png)


<details><summary>app.py</summary>

```python
# app.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, Flask on EC2!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
```
</details>

  Amazon Linuxにはpythonがデフォルトでinstallされてますが、pipはinstallされていないなので、以下のコマンドをVS Codeのterminalから実行しinstallします。
また、app.pyで必要なライブラリもインストールします。

```
sudo yum -y install python-pip
pip install flask
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/9b21c0d2-59bc-1997-f8e5-608a9b93f413.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/26046b19-b7ca-3aab-e0f4-cd84bed8cb50.png)

- app.py実行
Terminalから`python3 app.py`を実行すると、以下のメッセージが表示されます。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/52b26e5e-2635-4f13-4ce4-b2f9f1f12092.png)

  ポートの設定を確認すると、自動でポートフォワードが行われており、`localhost:5000`でブラウザアクセスできるようになっております。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/badc4441-e0c1-7a08-eb86-8d65d3ef3188.png)


  実際にローカルのブラウザからアクセスすると以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/526093e6-e737-fc5c-3dc6-a26c4dd8ca96.png)



以上になります。
EC2上で何か動作確認をしたい場合はスムーズに開発ができて便利ですね。