5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS CloudFormationで学生の演習用サーバー80台を立てる

5
Last updated at Posted at 2020-07-16

要件

  • 学生がSSHログインできてroot権限を渡せるサーバーを人数分作りたい。
  • Ubuntu で http サーバーを立ててページを公開できるようにしたい。
    • sudo 権限を渡す
  • なるべく渡す情報を減らしたいので、学籍番号でアクセスできるようにしたい
    • ssh student_id@student_id.example.com のようなホスト名でアクセスできるとよい
  • SSH鍵ペアは事前に作っておく
    • セキュアに渡せる経路は別途確保してある。
  • AWS それ自体の使い方を学習させるにはちょっと重い気がする
    • そういう時間を割けるならば AWS Education を使うのも手。無料だし。
  • 環境は演習が終わったらすぐ捨てるが、鍵は来週再利用する

CloudFormation

Infrastructure as Code に基づき、AWS の EC2 やセキュリティ設定を含むいろんなリソースを設定ファイル(テンプレート)一つで与えられる仕組みのようだ。
略称は cfn。

方法

cfn の Nested Stack で学生1人に割り当てるスタックを人数分コピペでインクルードする。

EC2 の起動イメージ (AMI) の準備

  • AMI は Ubuntu 18 LTS の公式を以下のように弄ったもの

apt-get update -y
apt-get install -y python-pip
apt-get install -y python-setuptools
mkdir -p /opt/aws/bin
python /usr/lib/python2.7/dist-packages/easy_install.py --script-dir /opt/aws/bin https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz



## EC2 インスタンス等の設定ファイル

* 学生に渡す EC2 と Route53 とポート設定をまとめたファイル。 https://gist.github.com/mmasko/66d34b651642525c63cd39251e0c2a8b を元に色々と細かく修正した。
    * cfn-init がエラーを吐くのだが原因はよくわからない。とりあえず動いているのでヨシ!
* Elastic IP で固定IPを割り当てている。
    * 80人となると `The maximum number of addresses has been reached.` のエラーが出る。 [上限値の変更申請を日本語で出せる。](https://qiita.com/msyk_tym/items/e3bbed8d382d06a6a329)
* example.com は Route53 で確保したドメイン名に置き換える。 例えば `.name` は最も安い TLD の一つのようだった (Route53 の何かの PDF ドキュメントに載っていた価格一覧を見る限り)。
* kisojikken というのは講義の名前



## 初期ファイルとスクリプト

3つのスクリプトをスタック作成時においている。

* `/etc/kisojikken/mkusers.sh (学籍番号)`
    * ユーザを作る
* `/etc/kisojikken/pubkey.sh`
    * 公開鍵をダウンロードしてきて .ssh/authorized_key に加える
    * 公開鍵の tar.gz を置いてある URL をハードコードしている:-p
* `/etc/kisojikken/init.sh (学籍番号)`
    * スタック作成時に上記2つのスクリプトを呼び出す 

その他の `/etc/cfn/cfn-hup.conf` などは参考元のスクリプトからそのまま持ってきたもので、 `cfn-init` が使っている (はず)

## テンプレート (ネストされる側)

```yaml
Parameters:
  EnvironmentSize:
    Type: String
    Default: t3a.nano
    AllowedValues:
      - t2.nano
      - t3a.nano
      - t3a.small
    Description: Select instance size
  KeyPair:
    Default: somekey
    Description: Existing keypair
    Type: AWS::EC2::KeyPair::KeyName
  StudentID:
    Description: Student ID to create
    Type: String
  SecurityGroupName:
    Default: kisojikken2020-securitygroup
    Description: Security Group's Name
    Type: String
  ImageId:
    Default: ami-02fdb41f362247f2e
    Description: AMI Image Id
    Type: String
Resources:
  DNSRecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: example.com.
      Name: {"Fn::Join": [ ".", [ !Ref StudentID, "example.com" ]]}
      Type: A
      TTL: '300'
      ResourceRecords:
      - !Ref PublicIP
    DependsOn: PublicIP  
  PublicIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      InstanceId: !Ref NestedInstance
  NestedInstance:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref EnvironmentSize
      SecurityGroups:
        - !Ref SecurityGroupName
      KeyName: !Ref KeyPair
      Tags:
        - Key: Name
          Value: !Ref StudentID
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource NestedInstance --configsets setup --region ${AWS::Region}
          /etc/kisojikken/init.sh ${StudentID}
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource NestedInstance --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          setup:
            - "configure_cfn"
        configure_cfn:
          files:
            /etc/kisojikken/init.sh:
              content: !Sub |
                #!/bin/bash
                set -e
                
                USERS=$*
                /etc/kisojikken/mkusers.sh $USERS
                /etc/kisojikken/pubkey.sh
              mode: "000555"
              owner: root
              group: root
            /etc/kisojikken/mkusers.sh:
              content: !Sub |
                #!/bin/bash
                set -e
                
                USERS=$*
                
                for U in $USERS
                do
                    adduser --disabled-password --gecos "" $U
                    usermod -G sudo $U
                done
              mode: "000555"
              owner: root
              group: root
            /etc/kisojikken/pubkey.sh:
              content: !Sub |
                #!/bin/bash
                set -e
                PUBKEYDIR=/etc/kisojikken2020/pubkeys
                
                mkdir -p $PUBKEYDIR
                pushd $PUBKEYDIR > /dev/null
                curl -s (鍵が置いてあるURL) |tar zxf - --overwrite
                popd > /dev/null
                
                for u in `ls /home`
                do
                    if [ -d $PUBKEYDIR/$u ];
                    then
                	SSHDIR=/home/$u/.ssh
                	mkdir -p $SSHDIR
                	cat $PUBKEYDIR/$u/id_rsa.pub >> $SSHDIR/authorized_keys
                	chown -R $u $SSHDIR
                	chmod -R go-rwx $SSHDIR
                	echo ssh key added for user $u
                    fi
                done
              mode: "000555"
              owner: root
              group: root
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                verbose=true
                interval=5
              mode: "000400"
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.NestedInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource NestedInstance --configsets setup --region ${AWS::Region}
              mode: "000400"
              owner: root
              group: root
            /lib/systemd/system/cfn-hup.service:
              content: !Sub |
                [Unit]
                Description=cfn-hup daemon
                
                [Service]
                Type=simple
                ExecStart=/opt/aws/bin/cfn-hup
                Restart=always
                
                [Install]
                WantedBy=multi-user.target
              mode: "000400"
              owner: root
              group: root
          commands:
            01_enable_cfn-hup:
              command: "systemctl enable cfn-hup.service"
            02_start_cfn-hup:
              command: "systemctl start cfn-hup.service"

テンプレート (ネストする側)

Parameters:
  EnvironmentSize:
    Type: String
    Default: t3a.nano
    AllowedValues:
      - t2.nano
      - t3a.nano
      - t3a.small
    Description: Select instance size
  KeyPair:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance.
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
    Type: AWS::EC2::KeyPair::KeyName
  SecurityGroupName:
    Description: Security Group's Name
    Default: kisojikken2020-securitygroup
    Type: String
  ImageId:
    Default: ami-02fdb41f362247f2e
    Type: String
  InstanceTemplateURL:
    Default: (上記 kinstance.template のURL)
    Description: template url 
    Type: String

Resources:
  y3033111:
    Type: 'AWS::CloudFormation::Stack'
    Properties:
      TemplateURL: !Ref InstanceTemplateURL
      Parameters:
        EnvironmentSize: !Ref EnvironmentSize
        SecurityGroupName: !Ref SecurityGroupName
        ImageId: !Ref ImageId
        KeyPair: !Ref KeyPair
        StudentID: *****
  z3033003:
    Type: 'AWS::CloudFormation::Stack'
    Properties:
      TemplateURL: !Ref InstanceTemplateURL
      Parameters:
        EnvironmentSize: !Ref EnvironmentSize
        SecurityGroupName: !Ref SecurityGroupName
        ImageId: !Ref ImageId
        KeyPair: !Ref KeyPair
        StudentID: *****
.... (これが人数分つづく)
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?