برنامه نویسی

اجرای سرور VS Code در AWS

من عاشق کدنویسی هستم! من عاشق نوشتن هستم! من عاشق یادگیری، تلاش و بازی با خدمات جدید AWS هستم. مشکل این است که من همیشه به لپ تاپم برای کدنویسی دسترسی ندارم. من GitHub CodeSpaces را امتحان کردم اما واقعاً از این تجربه خوشم نیامد. من به چیزی مشابه اما متفاوت نیاز داشتم.

هدف

هدف ایجاد تنظیماتی بود که به من امکان می‌دهد با استفاده از VS Code از تبلت اندرویدی خود کدنویسی کنم. من نیاز به دسترسی به یک ترمینال کامل با SSH، AWS CLI، Git و موارد دیگر داشتم. بنابراین اجرای آن به صورت محلی روی تبلت گزینه ای نیست، حتی اگر نصب و اجرا شدن آن امکان پذیر باشد.

کد VS

VS Code از ابتدا به عنوان بخش مشتری و سرور ساخته شده است. هر دو بخش کلاینت و سرور می توانند روی یک کامپیوتر یا ماشین های مجازی مختلف اجرا شوند. این امکان اتصال به یک بخش سرور را اساساً از هر جایی، در صورتی که یک وب کلاینت یا مستقل باشد، می دهد.

تصویری که معماری VS-Code را نشان می دهد.

این امکان توسعه از راه دور را از طریق ابزارهای مختلف مانند SSH یا حالت تونل ویژه فراهم می کند.

راه حل

با درک نحوه ساختار VS Code، اجرای بخش سرور در یک نمونه EC2 و اتصال به آن از طریق vscode.dev، با استفاده از حالت تونل ویژه، باید کاملاً امکان پذیر باشد. باید این امکان را داشته باشد که از تبلت اندرویدی من اجرا شود. برای اجرای VS Code در نمونه EC2 از VS Code CLI استفاده می کنیم

VPC و LaunchTemplate ایجاد کنید

اول از همه ما به یک نمونه EC2 در حال اجرا نیاز داریم، و برای داشتن آن، اجازه دهید با ایجاد یک VPC اولیه با استفاده از یک الگوی اولیه Cloudformation شروع کنیم.

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: setup basic VPC

Parameters:
  ApplicationName:
    Type: String
  IPSuperSet:
    Type: String
    Description: The IP Superset to use for the VPC CIDR range, e.g 10.0
    Default: "10.0"

Resources:
  ##########################################################################
  #  VPC Base Infrastructure                                               #
  ##########################################################################
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: !Sub "${IPSuperSet}.0.0/16"
      Tags:
        - Key: Name
          Value: !Ref ApplicationName

  PublicSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: { Ref: "AWS::Region" }
      VpcId: !Ref VPC
      CidrBlock: !Sub ${IPSuperSet}.0.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${ApplicationName}-public-one

  PublicSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: { Ref: "AWS::Region" }
      VpcId: !Ref VPC
      CidrBlock: !Sub ${IPSuperSet}.1.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${ApplicationName}-public-two

  ##########################################################################
  #  Gateways                                                              #
  ##########################################################################
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref ApplicationName

  GatewayAttachement:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  ##########################################################################
  #  Route Tables & Routes                                                 #
  ##########################################################################
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${ApplicationName}-public-rt

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayAttachement
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetOneRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetOne
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetTwoRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetTwo
      RouteTableId: !Ref PublicRouteTable

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد باید یک نمونه EC2 و یک حجم EBS ایجاد کنیم. ولوم EBS برای ذخیره همه کدها استفاده می شود، به این ترتیب می توانیم نمونه EC2 را از بین ببریم و تمام تنظیمات و کدها را روی ولوم EBS نگه داریم. ولوم EBS در هنگام راه‌اندازی اولیه نصب می‌شود و همچنین به fstab اضافه می‌شود، بنابراین اگر نمونه را راه‌اندازی مجدد کنیم دوباره نصب می‌شود. برای آسان کردن ایجاد نمونه های جدید با همان پیکربندی، از LaunchTemplate استفاده می کنیم.

ما همچنین تمام اجزای مورد نیاز را در طول بوت اولیه با استفاده از UserScript نصب خواهیم کرد. ما همه چیز را در لینوکس آمازون 2023 اجرا خواهیم کرد.

ما از Instance Connect استفاده خواهیم کرد تا بتوانیم به نمونه متصل شویم. بنابراین ما باید محدوده IP برای Instance Connect را به گروه امنیتی خود اضافه کنیم. برای یافتن محدوده IP می توانیم ip-ranges.json را دانلود کنیم.

AWSTemplateFormatVersion: "2010-09-09"
Description: Base setup for VS Code EC2 resources

Parameters:
  AmiId:
    Type: String
    Default: ami-04b1c88a6bbd48f8e # AMI for Amazon Linux 2023 in eu-west-1
  InstanceType:
    Type: String
    Description: Type of instance to use for EC2 runners.
    Default: t3.large
  AvailabilityZone:
    Type: String
    Description: The availability zone to run in
    Default: eu-west-1a
  InfrastructureStackName:
    Type: String
    Description: The name of the stack with the Infrastructure resources

  ServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Security Group
      VpcId:
        Fn::ImportValue: !Sub ${InfrastructureStackName}:VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 18.202.216.48/29 # Instance Connect in eu-west-1

  SecurityGroupInboundAllowSelf:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref ServerSecurityGroup
      IpProtocol: tcp
      FromPort: "0"
      ToPort: "65535"
      SourceSecurityGroupId: !Ref ServerSecurityGroup

  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyName: AllwoEC2Actions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ec2:*
                Resource: "*"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action: sts:AssumeRole

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref InstanceRole

  CodeEbsVolume:
    Type: AWS::EC2::Volume
    Properties:
      AvailabilityZone: !Ref AvailabilityZone
      Encrypted: false
      Size: 32
      VolumeType: gp3

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        EbsOptimized: True
        IamInstanceProfile:
          Arn: !GetAtt EC2InstanceProfile.Arn
        ImageId: !Ref AmiId
        InstanceType: !Ref InstanceType
        SecurityGroupIds:
          - !GetAtt ServerSecurityGroup.GroupId
        UserData:
          Fn::Base64:
            Fn::Sub: |
              #!/bin/bash -xe
              sudo -s
              yum update -y
              yum install -y jq
              yum install git -y
              TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
              VOLUME_ID=${CodeEbsVolume}
              INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
              REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
              aws ec2 attach-volume --volume-id $VOLUME_ID --device /dev/xvdf --instance-id $INSTANCE_ID --region $REGION
              mkdir vscode-data
              chown -R ec2-user:ec2-user /vscode-data
              mount /dev/xvdf /vscode-data
              echo -e "/dev/xvdf /vscode-data xfs  defaults,nofail  0  2" >> /etc/fstab
              curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz
              tar -xf vscode_cli.tar.gz
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمونه EC2 را راه اندازی کنید

حالا بیایید از آن LaunchTemplate برای شروع یک نمونه EC2 استفاده کنیم. این بار اجازه دهید به سراغ کنسول AWS برویم تا آن را راه اندازی کنیم.

به بخش EC2 بروید و از منوی کشویی Launch Instance from template را انتخاب کنید.

با انتخاب LaunchTemplate که به تازگی ایجاد کردیم و نسخه مورد استفاده در صورت وجود بیشتر از یک شروع کنید. در مورد من من 6 نسخه دارم و دوست دارم از جدیدترین آنها استفاده کنم.

تصویری که قسمت اول راه اندازی نمونه را نشان می دهد.

اکنون به پایین بروید و مطمئن شوید که AMI صحیح انتخاب شده است، باید آمازون Linux 2023 AMI باشد.

تصویری که قسمت اول راه اندازی نمونه را نشان می دهد.

در نهایت یک زیرشبکه را در VPC که به تازگی ایجاد کرده ایم انتخاب کنید، باید منطقه در دسترس بودن حجم EBS که ایجاد کرده ایم باشد. در مورد من، حجم EBS را در eu-west-1a ایجاد کرده‌ام، بنابراین نمونه باید در یک زیرشبکه در آن AZ باشد.

تصویری که قسمت اول راه اندازی نمونه را نشان می دهد.

بقیه موارد را به عنوان پیش فرض بگذارید و روی گزینه Launch Instance کلیک کنید

VSCode را در نمونه شروع کنید

با اجرای نمونه، اکنون باید VSCode را راه اندازی کنیم. ما قبلاً CLI را در اسکریپت کاربر دانلود کرده ایم. بیایید با اتصال به نمونه با استفاده از Instance Connect شروع کنیم.

به نمونه بروید و Connect را انتخاب کنید.

تصویر گزینه های اتصال نمونه EC2 را نشان می دهد.

اگر اتصال موفقیت آمیز باشد، باید ترمینالی مانند این را ببینید.

تصویری که اتصال به نمونه EC2 را نشان می دهد.

اکنون باید سرور VSCode را با استفاده از CLI راه اندازی کنیم، این کار را با این دستور انجام می دهیم.


~/code  tunnel --accept-server-license-terms --name vscode-demo-tunnel

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن –accept-server-license-terms به دستور به طور خودکار شرایط مجوز را می پذیرد. همچنین باید به تونل خود یک نام بدهیم که با پارامتر –name انجام می شود. در دستور بالا نام vscode-demo-tunnel را به تونل می دهیم.

اکنون باید نتیجه ای مانند این به ما بدهد.

تصویری که فرمان موفقیت آمیز را نشان می دهد.

کاری که اکنون باید انجام دهیم این است که به سرور دسترسی داشته باشیم، که با پیمایش به URL هایلایت شده انجام می شود. این باید ما را به این صفحه ببرد.

تصویر صفحه فعال سازی دستگاه را نشان می دهد.

در این صفحه باید کدی را که هنگام راه‌اندازی سرور VSCode داده‌ایم ارائه کنیم.
پس از یک فعال سازی، اکنون باید GitHub را برای سرور مجاز کنیم.

تصویری که صفحه مجوز GitHub را نشان می دهد.

اگر همه چیز کار کند، باید در صفحه موفقیت قرار بگیریم.

تصویری که صفحه موفقیت GitHub را نشان می دهد.

با بازگشت به صفحه اتصال نمونه، اکنون باید ببینیم که سرور در حال اجرا است و یک URL برای اتصال به آن.

تصویری که سرور VSCode را در حال اجرا نشان می دهد.

اکنون می‌توانیم به URL برجسته‌شده، که در اصل «https://vscode.dev/tunnel/tunnel-name» است، برویم. VSCode باید در پنجره مرورگر بارگیری شود و به ما امکان دسترسی به یک ویرایشگر VSCode کامل با دسترسی ترمینال و همه چیز را بدهد.

تصویری که کلاینت VSCode را در حال اجرا نشان می دهد.

در گوشه پایین سمت چپ وضعیت اتصال به طور کامل قابل مشاهده است.

این همان کاری است که ما باید انجام دهیم تا سرور VS Code روی یک نمونه EC2 اجرا شود و به ما اجازه دهد از هر مرورگری به آن متصل شویم. اساسا ما نسخه میزبانی شده خود از GitHub CodeSpaces را ایجاد کرده ایم.

اما، هنوز پیشرفت هایی وجود دارد که می توانیم انجام دهیم.

اضافه کردن مقداری اتوماسیون اضافی

اتصال به نمونه هر بار با استفاده از Instance Connect برای راه اندازی سرور چندان عملی نیست. انجام این کار از طریق تبلت اندرویدی کارساز است، اما من ترجیح می‌دهم این مرحله را کمی بیشتر به صورت خودکار انجام دهم.

چیزی که عالی خواهد بود این است که بتوانیم سرور VS Code را به عنوان سرویسی راه اندازی کنیم که می تواند در پس زمینه اجرا شود. بنابراین اول از همه اجازه دهید یک اسکریپت پوسته به نام vscodestart.sh ایجاد کنیم که سرور را راه اندازی می کند.

#!/bin/sh
~/code  tunnel --accept-server-license-terms --name vscode-demo-tunnel
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این همان دستوری است که در هنگام راه‌اندازی دستی از آن استفاده کردیم. در مرحله بعد یک سرویس لینوکس ایجاد می کنیم که می توانیم از systemd استفاده کنیم، بنابراین بیایید فایل سرویس را با نام vscode.service ایجاد کنیم.

[Unit]
After=network.target

[Service]
User=ec2-user
Group=ec2-user
ExecStart=/usr/local/bin/vscodestart.sh

[Install]
WantedBy=default.target
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا بیایید فایل vscode.service را در /etc/systemd/system/ و vscodestart.sh را در /usr/local/bin/ کپی کنیم.
اکنون می توانیم سرور را با استفاده از دستور راه اندازی کنیم:

systemctl start vscode.service
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به این ترتیب سرور VS Code اکنون می تواند در پس زمینه اجرا شود. خوب این اولین قدم در اتوماسیون بود. برای مرحله دوم، یک سند مدیریت سیستم های AWS ایجاد می کنیم که می توانیم آن را از AWS CLI یا کنسول اجرا کنیم. سپس سند SSM دستور start را در نمونه EC2 اجرا می کند که سرویس سرور VS Code را شروع می کند. بنابراین ما آن را به قالب CloudFormation خود اضافه می کنیم.


  StartVCodeServerDocument:
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Command
      Content:
        schemaVersion: "2.2"
        description: Command Document for VS Code start server service
        mainSteps:
          - action: "aws:runShellScript"
            name: "startserver"
            inputs:
              runCommand:
                - sudo systemctl start vscode.service

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با قرار دادن سند SSM می‌توانیم به کنسول بپریم و سند را روی نمونه خود اجرا کنیم. حتی اگر این یک راه حل کاملاً خودکار نباشد، گامی در مسیر درست است. من اکنون می توانم سرور VS Code را به راحتی در یک نمونه EC2 راه اندازی کنم و تا زمانی که به مرورگر دسترسی داشته باشم از هر دستگاهی کد بنویسم.

گوچاس

در طول پروژه چند اشتباه و چیزهایی وجود داشت که باید در نظر بگیریم. اول از همه، اولین باری که یک سرور در یک نمونه EC2 راه اندازی می شود، باید آن را با استفاده از کد تولید شده توسط سرور VS Code فعال کنیم. این یک راه‌حل کاملاً خودکار را کمی سخت‌تر می‌کند، زیرا باید در اولین بار به نمونه متصل شویم.

همچنین باید در نظر داشته باشیم که برای هر حساب GitHub محدودیت 5 تونل وجود دارد. این بدان معناست که اگر ششمین تونل را شروع کنیم، اولین تونل بازیافت خواهد شد.

شرایط سرویس همچنین فقط استفاده از سرور VS Code را برای استفاده شخصی یا درون یک شرکت مجاز می‌کند. شما مجاز به میزبانی و اجرای آن به عنوان راه حل SaaS نیستید.

نتیجه گیری و مرحله بعدی

با اجرای سرور VS Code بر روی نمونه EC2 که مالک آن هستم، اکنون کنترل کامل هزینه آن، دسترسی آن را در دست دارم، و این امکان را به من می دهد که اگر بخواهم تمام مخازن خود را شبیه سازی کنم. من اکنون می توانم اساساً برای هر پروژه ام با تلاش و هزینه بسیار کم از هر دستگاهی کد بنویسم تا زمانی که بتوانم یک مرورگر را اجرا کنم و به اینترنت دسترسی داشته باشم. این اکنون به من این امکان را می دهد که وقتی در اتوبوس هستم یا هر جایی که به لپ تاپم دسترسی ندارم از رایانه لوحی اندروید خود کدنویسی یا وبلاگ نویسی کنم.

پس مراحل بعدی چیست؟
من قصد دارم یک راه‌اندازی کاملاً خودکار ایجاد کنم که بتوانم نمونه‌های سرور را از یک صفحه وب شروع و متوقف کنم. البته این راه‌حل بدون سرور در تمام قسمت‌هایی است که برای نمونه EC2 خود انتظار می‌رود.

همانطور که دیدید من نمونه EC2 را در یک زیر شبکه عمومی اجرا می کنم، این به این دلیل است که اولین باری که سرور را راه اندازی می کنم باید به آن متصل شوم. با Instance Connect Endpoint جدید می‌توانم به نمونه متصل شوم حتی اگر در یک زیرشبکه خصوصی باشد.

کلمات پایانی

این یک پروژه واقعا سرگرم کننده بود. این واقعا عالی است که ببینید چقدر می توانید با کد و تلاش بسیار کم انجام دهید. در ادامه با ما همراه باشید.

فراموش نکنید که من را در لینکدین دنبال کنید و توییتر برای مطالب بیشتر، و بقیه وبلاگ های من را بخوانید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا