使って学ぶAWS

AWSで色々やってみるブログ

EC2とDockerで手軽にJenkinsを試す #1

第1回目はEC2へdockerをセットアップし、JenkinsのイメージをRunする。
Jenkinsの動作確認までをしていきたい。 第2回目でCodeシリーズあたりと連携をしていきたいと思う。

EC2へのDockerのインストール

そこら中に記事があるので、そちらを参照。

Jenkinsのセットアップ

jenkins blueoceanのダウンロード

docker pull jenkinsci/blueocean

Jenkins用のディレクトリ作成

mkdir ~/docker/jenkins

Imageの起動

こちらのサイトを多分に参考にさせて頂きました。

sudo docker run \         
-u root \
--rm \
-d \
-p 8080:8080 \
-v $HOME/docker/jenkins:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkinsci/blueocean

一行版。

sudo docker run -u root --rm -d -p 8080:8080 -v $HOME/docker/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean

Web画面でのJenkinsのセットアップ

上記設定で起動すれば、http://EC2-public-IP:8080にアクセスすると、初期画面が出る。 初期画面ではパスワードを入れろと言われるので、下記にあるパスワードを調べる。

cat ~/docker/jenkins/secrets/initialAdminPassword

入力してあげると、こんな画面が出る。
f:id:yohei_ok:20210217145024p:plain

Install suggested pluginを選ぶと、インストールが進む。
f:id:yohei_ok:20210217145105p:plain

インストールが終わると、初期ユーザー登録画面が出てくる。 ユーザ名やパスワードなどを登録する。 f:id:yohei_ok:20210217145135p:plain

わーい。
f:id:yohei_ok:20210217145332p:plain

Adminでログインになった場合

initial passwordを使ってLoginしたところ、Adminでログインになることがある。 その場合は、jenkinsの管理→ユーザーの管理からユーザーを作成しておけばOK。

Jenkinsの動作確認

まずは新規ジョブの作成→フリースタイル・プロジェクトのビルドを選ぶ。 f:id:yohei_ok:20210217190020p:plain

ビルド手順の追加→シェルの実行を選択。 f:id:yohei_ok:20210217190512p:plain

サンプルとして、Hello Worldの表示をテストしてみる。 下記のように入力し、保存。 f:id:yohei_ok:20210217191708p:plain

ビルドを実行。ビルド履歴を確認。 f:id:yohei_ok:20210217190737p:plain f:id:yohei_ok:20210217190944p:plain

コンソール出力を見ると、hello worldが出力されて、 エラーなくテストが成功していることがわかる。 f:id:yohei_ok:20210217191640p:plain ちなみに&JOB_NAMEはtestという新規ジョブを作ったため、その名前が出力されている。

付録(Dockerの便利コマンド)

# Imageのダウンロード
docker pull <Image>

# コンテナの一覧表示
docker ps -a

# イメージの一覧表示
docker images

# コンテナの削除
docker rm <container-ID>

# イメージの削除
docker rmi <image-ID>

CloudFormationで簡単に機械学習の開発環境を整える

はじめに

ちょっと検討をしてみようと思うたびに、VPC作ってSubnet作って、InternetGatewayとElasticIP取得して、NatGateway設置して・・
と単純なprivate, publicサブネットを置くだけでも何度もやると面倒くさい。

特に機械学習開発環境代わりに使うような場合は、複数AZにまたがるような可用性云々じゃなくて、検討用土台をさくっと作りたい。
でもSageMakerのNotebookインスタンスじゃ物足りないという人がいると思う。

構成図

前回も紹介したこれを自動で作ることにする。
●ポイント

  • 踏み台サーバーでセキュリティ対策
  • NATゲートウェイでPrivate環境でもソフトを更新可能
  • さくっと検討用に使いたいのでOneZone構成
  • 自分の練習用 f:id:yohei_ok:20210205194019p:plain

はまりどころ

いろいろハマったけど、下記はその一例。参考になれば。

NATGatewayでのElasticIPの指定

IPアドレスではなくIDで指定するため、下記のように"Fn::GetAtt::"を使って指定している。

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - ElasticIP
          - AllocationId

NATGatewayへのRoute

RouteでGatewayIdではなく、NATGatewayIdを使う必要あり。

  PrivateRoute: 
    Type: AWS::EC2::Route
    Properties: 
      NatGatewayId: !Ref NATGateway

EC2のSecurityGroupの設定はリストで

SecurityGroupIdsのように、複数形になっているのでリストで渡すことをお忘れなく。

  PrivateInstance:
    Type: AWS::EC2::Instance
    Properties: 
      SecurityGroupIds: 
        - !Ref PrivateSecurityGroup

yamlファイル全体はこちら

これをcloud formationに入れることで一発で踏み台サーバーと検討用のprivateサーバーを起動することが可能。 インスタンスのファミリーとかサイズは適宜変えるとよい。

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  OneZone VPC/Subnet/EC2 Create

Parameters:
  # 踏み台サーバーはAmazonLinux & t2.microで簡素に
  BationHostImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  
  BationHostInstanceType:
    Type: String
    Default: t2.micro

  # 機械学習用インスタンスはDeepLearning AMIを使って、実験用と高機能用などを用意
  PrivateImageId:
    Type: String
    Default: ami-088585cfb750459af

  PrivateInstanceType:
    Type: String
    Default: m5.2xlarge
    AllowedValues : ["t2.micro", "m5.2xlarge"]

  # SSHでつなぐための接続元IP(自分のIP)
  MyIP:
    Description: IP address allowed to access EC2
    Type: String
    Default: 0.0.0.0/32

  # SSH用のKeyはすでにある前提
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"
    Default: .pem

Resources:
# #####################################
# VPC & IG
# #####################################
  VPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.5.0.0/16
      EnableDnsHostnames: false
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: my-vpc-stack

  InternetGateway: 
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags: 
        - Key: Name
          Value: my-ig-stack

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


# #####################################
# Subnet & NAT
# #####################################
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.5.0.0/24
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: public-subnet-stack
      VpcId: !Ref VPC

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.5.1.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: private-subnet-stack
      VpcId: !Ref VPC

  NatIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      Tags: 
        - Key: Name
          Value: nat-eip-stack

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - NatIP
          - AllocationId

      SubnetId: !Ref PublicSubnet
      Tags:
        - Key: Name
          Value: my-nat-stack

# #####################################
# RouteTable & Routing
# #####################################
  PublicRouteTable: 
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: rt-public-stack

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

  PublicRouteAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: rt-private-stack

  PrivateRoute: 
    Type: AWS::EC2::Route
    Properties: 
      RouteTableId: !Ref PrivateRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGateway

  PrivateRouteAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet

# #####################################
# SecurityGroup
# #####################################
  PublicSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow SSH to BationHost
        GroupName: allow-ssh-to-bation-stack
        VpcId: !Ref VPC
        SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP

  PrivateSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow SSH from bation
        GroupName: allow-ssh-from-bation-stack
        VpcId: !Ref VPC
        SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref PublicSecurityGroup

# #####################################
# EC2
# #####################################
  BationHost: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref BationHostImageId
      InstanceType: !Ref BationHostInstanceType
      KeyName: !Ref KeyName
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref PublicSecurityGroup
      Tags:
        - Key: Name
          Value: bation-host-stack

  EC2IP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref BationHost
      Tags: 
        - Key: Name
          Value: ec2-eip-stack

  PrivateInstance:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref PrivateImageId
      InstanceType: !Ref PrivateInstanceType
      KeyName: !Ref KeyName
      SubnetId: !Ref PrivateSubnet
      SecurityGroupIds: 
        - !Ref PrivateSecurityGroup
      Tags:
        - Key: Name
          Value: private-instance-stack

# SSH接続用にPublicとPrivateのIPを出力しておくと便利
Outputs:
  PublicIP:
    Value: !GetAtt BationHost.PublicIp
    Description: Public IP of BationHost

  PrivateIP:
    Value: !GetAtt PrivateInstance.PrivateIp
    Description: Private IP of EC2 instance

AWSアカウント間でのCodeCommitからのClone

AWSアカウントAのリポジトリからAWSアカウントBの人がCloneする

Bの人がAのアクセスキーIDとシークレットアクセスキーを持っている場合

非常に簡単。 AWSアカウントBの人のEC2で、下記コマンドでAWSアカウントAのcredential情報を入力。 credentialを入れるので、できればprivate subnetで踏み台サーバー経由が良いと思う。

aws config --profile <ENVNAME>

その上で下記コマンドでリポジトリをCloneできる。

git clone codecommit://<ENVNAME>@<repository_name>
git clone codecommit::<REGION>://<repository_name>  # region指定の場合

Git認証情報がある場合

普通にネット経由でできます。 前回と同様の操作で、HTTPS経由でCloneできると思います。

CodeCommitからリポジトリをクローンする

IAMユーザーから認証情報を作成

AWS CodeCommitのHTTPS Git認証情報を生成をクリック。 f:id:yohei_ok:20210212185614p:plain

f:id:yohei_ok:20210212185802p:plain

ユーザー名とパスワードをダウンロードしておく。

Git cloneするだけ

git clone https://git-codecommit.ap-northeast-1.amazonaws.com/*********/

こうするとユーザ名とパスワードを聞かれるので、 先程ダウンロードしておいたユーザ名とパスワードを使えば良い。

毎回Credential情報入力するのが面倒な方へ

MaxOSXの場合は下記コマンドを使うと、OSXのキー保存機能で保存してくれる。 細かく知りたい方は別途検索すると良い。

git config --global credential.helper osxkeychain

VSCODEから踏み台サーバー越しにSSH接続をする

この記事で行っている多段SSHは既にベストプラクティスではないので、Session Managerを使うことをお勧めします。 構成はこちらのQiitaを参照のこと。

多段SSHしたい

機械学習の研究開発用だと、SageMakerのJupyter NoteBookじゃ物足りず、EC2にSSHで入って開発したくなる。僕の使い方が悪いのか・・?開発用にEC2を置くなら、Privateに開発用、Publicに踏み台サーバーを置くといいかなぁと思う。(特に会社とかで使う場合)

要はこういう状態 。

SSH config

VSCODESSHのconfigには下記を書けばOK。 どちらの.pemファイルもローカルのファイルを指定すれば良い。 publicEC2に鍵を置かなくて良い点が安心感。

Host public
  HostName PUBLIC EC2のIP
  User ec2-user
  IdentityFile ****.pem(フルパスで書いたほうが良い)

Host private
  Hostname PRIVATE EC2のIP
  User ec2-user
  IdentityFile ****.pem(フルパスで書いたほうが良い。)
  ProxyCommand ssh -W %h:%p public

VSCODE

あとは直接privateのほうを開くだけ。

EC2の設定どうする

SSHフルオープンが心配な場合は、Public EC2のセキュリティグループでインバウンドで自分のIPを指定すると良い。 さらにPrivate EC2のセキュリティグループのインバウンドに、Public EC2に設定してあるセキュリティグループを設定してあげればこちらも安心。

EC2へS3からデータをダウンロードする(VPCエンドポイント編)

事前準備

VPCエンドポイントを作成

VPCのエンドポイントを作成する。サービスとしてS3を選択。 f:id:yohei_ok:20210126205219p:plain フルアクセス設定と、RouteTableを指定して作ってしまえば、このVPC内の指定されたトラフィックはS3のエンドポイントへ向かうようになる。

AWS S3コマンドで見てみる

同一VPC内のEC2にSSHなどで入って、S3にあるバケットが確認できるようになっている。

aws s3 ls

データのダウンロードなどはaws s3のコマンドを別途調べると良いが、個人的にはよくsyncを使っている。

aws s3 sync s3://bucket-name/object-name ./DIRECTORY

EC2へSSHログインする

コマンド

ssh -i "./.ssh/***.pem" ec2-user@IPaddress

.sshフォルダ以下にpemファイルを置いておいて、基本的にこれだけで接続出来る。

事前準備

VPC

  • VPCにIGを設置する

サブネット

  • Public IPを有効にする
  • ルートテーブルで0.0.0.0/0をIG宛に設定する(VPC側に設定しても良い)

EC2

  • Public IP設定(Elastic IPで固定化しておくと便利)
  • セキュリティグループのインバウンド設定でSSHを許可。出来ればIPを限定するとセキュリティ的に安心。