Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails App Deployment with CodeDeploy

tannai
August 30, 2016

Rails App Deployment with CodeDeploy

tannai

August 30, 2016
Tweet

More Decks by tannai

Other Decks in Programming

Transcript

  1. RailsΞϓϦΛ σϓϩΠ͢Δ CodeDeploy Classmethod, Inc. Yuki Tannai ࣮ફAWSϓϩάϥϛϯά

  2. ࣗݾ঺հ • ୮಺༏ل • @yuukigoodman • αʔόͰಈ͘ϓϩάϥϜͱ͔AWS

  3. Agenda • σϓϩΠͱ͸ • σϓϩΠΛࢧ͑ΔAWS • CodeDeployͰRailsΛσϓϩΠ

  4. σϓϩΠͱ͸

  5. γεςϜΛ ར༻Մೳʹ͢Δ ׆ಈશൠͰ͋Δ — ja.wikipedia.org1 1 https://ja.wikipedia.org/wiki/ιϑτ΢ΣΞσϓϩΠϝϯτ

  6. • ͨͩίʔυΛμ΢ϯϩʔυͯ͠ىಈ͢Ε͹ྑ͍ ͱ͍͏΋ͷͰ͸ͳ͍ • σϓϩΠʹΑͬͯղܾ͍ͨ͠໰୊͕͋Γɺ ͦΕ͸ϓϩδΣΫτͷਐߦͱڞʹมԽ͢Δ • σϓϩΠ͸αʔϏεఏڙऀͷ໰୊Λղܾ͢Δ • ఏڙऀͷ໰୊ղܾ͸ར༻ऀͷՁ஋ʹͳΓಘΔ

    • σϓϩΠ͸඼࣭ͷͻͱͭ(QCD) • Quality/Cost/Delivery
  7. σϓϩΠࣗಈԽͷಈػ • ස౓͕ଟ͍ • ϛε͕Ͱ͖ͳ͍ • ϧʔνϯϫʔΫ • Ͱ΋౓ʑมΘΔ ࣗಈԽ͠ͳ͍ͱ΍ͬͯΒΜͶʔʂʂ

  8. σϓϩΠࣗಈԽͷ؍఺ • Ͳ͔͜ΒͲ͜ʹσϓϩΠ͢Δ͔ • ࣗಈσϓϩΠͷ࣮ߦτϦΨʔ • ͍ͭ୭͕σϓϩΠ͢Δ͔ • ࣦഊΛͲ͏ݕग़͠ɺͲ͜ʹ௨஌͢Δ͔ •

    Ͳͷ͘Β͍มߋ͕ೖΔ͔
  9. σϓϩΠ͸ϓϩδΣΫτͷਐߦͱͱ΋ʹมԽ͢Δ • Ϗδωε: ࢓༷มߋ΍ೲظ୹ॖ • ૊৫: ϝϯόʔͷೖΕସ͑΍૿ݮɺ੒ख़౓ͷෆ ۉҰ͞ • ٕज़:

    όʔδϣϯΞοϓ΍ύϑΥʔϚϯε ͜ΕΒͷཧ༝ʹΑΓσϓϩΠ΋มߋ͞ΕΔ
  10. มߋʹ଱͑͏Δ σϓϩΠࣗಈԽͷ ϓϩάϥϜΛॻ͘

  11. ͦͷͨΊͷ໨ඪઃఆͱઃܭ͕େࣄ

  12. σϓϩΠΛࢧ͑Δ AWS

  13. None
  14. EC2 • ΦʔιυοΫεͳσϓϩΠλʔήοτ • େ఍ͷ৔߹͸EC2ʹσϓϩΠ͢Δ • Πϯελϯε࡞੒ͱϓϩϏδϣχϯά͕ඞཁ

  15. None
  16. Elastic Beanstalk • HerokuΈ͍ͨͳαʔϏε • EC2/RDS/ELBϦιʔεΛ؆୯ʹ؅ཧ͢Δ • ࣮ମ͸CFn • ઐ༻ͷίϚϯυ(eb-cli)͕͋Δ

    • eb deploy
  17. None
  18. Cloud Formation • AWSϦιʔεͷߏ੒؅ཧ • ϒϥ΢β΍CLIͰ΍͍ͬͯΔ͜ͱΛJSONͰهड़ ͢Δͱ࣮ߦͯ͘͠ΕΔ • AWSެࣜఏڙπʔϧͳͷͰ҆৺ •

    ࠶ݱੑ͕ٻΊΒΕΔ৔߹͸ඇৗʹ༗ޮ
  19. None
  20. EC2 Container Service • EC2Ϋϥελ্Ͱͷίϯςφ࣮ߦΛ؅ཧ • Dockerӡ༻ʹৄ͍͠ਓɺ ͥͻ࠙਌ձͰڭ͍͑ͯͩ͘͞ >< •

    Container Registry΋౦ژʹདྷͨ
  21. None
  22. Code Deploy • EC2΁ͷίʔυͷ഑ஔͱىಈΛ؅ཧ͢ΔαʔϏε • ຊ೔ͷओਓެ • EC2ʹAgentΛΠϯετʔϧ͓ͯ͘͠

  23. CodeDeployͰ RailsΛσϓϩΠ

  24. େ·͔ͳྲྀΕ • Ansible Playbookͷ࡞੒ • CodeDeployઃఆͷ࡞੒ • Cloud Formation Templateͷ࡞੒

    • σϓϩΠͯ͠ΈΔ • ௥Ճཁ݅Λ࣮૷͢Δ • ࣦഊ࣌ͷ௨஌ • Blue Green Deployment
  25. Ansible Playbookͷ࡞੒

  26. webservers.yml - hosts: all remote_user: ec2-user vars: rails_env: "{{ lookup('env',

    'RAILS_ENV') }}" roles: - common - nginx - fluentd - logrotate - ruby - node - rails - ssh
  27. roles/common/tasks/main.yml - name: install yum security plugin yum: pkg=yum-plugin-security state=latest

    sudo: yes - name: update yum security shell: yum -y --security update-minimal sudo: yes
  28. roles/common/tasks/main.yml - name: install yum packages yum: name={{ item }}

    state=present sudo: yes with_items: - gcc-c++ - glibc-headers - openssl-devel - readline - readline-devel - libyaml-devel - libffi-devel - libxml2 - libxslt - libxml2-devel - libxslt-devel - zlib - zlib-devel - mysql-devel - git - ruby-devel
  29. roles/node/tasks/main.yml - name: install nodejs yum: name=nodejs enablerepo=epel sudo: yes

  30. roles/ruby/tasks/main.yml - name: check rbenv presence command: which rbenv ignore_errors:

    true register: rbenv_path - name: Install rbenv sudo: yes git: repo=https://github.com/sstephenson/rbenv.git dest={{ rbenv_root }} when: rbenv_path.rc != 0 - name: Install ruby-build sudo: yes git: repo=https://github.com/sstephenson/ruby-build.git dest={{ rbenv_root }}/plugins/ruby-build when: rbenv_path.rc != 0
  31. roles/ruby/tasks/main.yml - name: create shims dir sudo: yes file: path={{

    rbenv_root }}/shims state=directory when: rbenv_path.rc != 0 - name: create versions dir sudo: yes file: path={{ rbenv_root }}/versions state=directory when: rbenv_path.rc != 0 - name: change owner of install dir sudo: yes command: chgrp -R {{ app_user }} {{ rbenv_root }} when: rbenv_path.rc != 0 - name: change mode of install dir sudo: yes command: chmod -R g+rwxXs {{ rbenv_root }} when: rbenv_path.rc != 0
  32. roles/ruby/tasks/main.yml - name: Set rbenv env file sudo: yes copy:

    src=rbenv.sh dest=/etc/profile.d mode=0775 when: rbenv_path.rc != 0 - name: check ruby version shell: rbenv versions | grep {{ ruby_version }} > /dev/null ; echo $? ignore_errors: true register: ruby_is_installed changed_when: ruby_is_installed.stdout != '0' - name: Install ruby with rbenv shell: bash -lc "rbenv install {{ ruby_version }}" args: creates: "{{ rbenv_root }}/versions/{{ ruby_version }}/bin/ruby"
  33. roles/ruby/tasks/main.yml - name: check ruby version shell: ruby -v |

    grep {{ ruby_version }} > /dev/null ; echo $? register: is_correct_version changed_when: is_correct_version.stdout != '0' - name: Switch Ruby version shell: bash -lc "rbenv global {{ ruby_version }} && rbenv rehash" when: is_correct_version.stdout != '0' - name: update rubygems shell: bash -lc "rbenv exec gem update --system" - name: install common gems shell: bash -lc "rbenv exec gem install bundler io-console --no-ri --no-rdoc" args: creates: "{{ rbenv_root }}/shims/bundle"
  34. CodeDeployઃఆͷ࡞੒

  35. RAILS_ROOT/appspec.yml version: 0.0 os: linux files: - source: / destination:

    /home/ec2-user/app permissions: - object: /home/ec2-user pattern: "**" owner: ec2-user group: ec2-user mode: 755
  36. RAILS_ROOT/appspec.yml hooks: ApplicationStop: - location: script/codedeploy/deregister_from_elb.sh - location: script/codedeploy/application_stop.sh timeout:

    300 runas: ec2-user BeforeInstall: - location: script/codedeploy/before_install.sh timeout: 300 runas: root
  37. RAILS_ROOT/appspec.yml AfterInstall: - location: script/codedeploy/after_install.sh timeout: 3600 runas: ec2-user ApplicationStart:

    - location: script/codedeploy/application_start.sh timeout: 3600 runas: ec2-user ValidateService: - location: script/codedeploy/verify_service.sh timeout: 3600 runas: ec2-user - location: script/codedeploy/register_with_elb.sh
  38. ApplicationStop • σϓϩΠલʹΞϓϦΛఀࢭ͢Δ • ELB഑Լͷ৔߹͸ొ࿥ղআ͢Δ • EC2ࣗ਎Λstand byʹ͢Δ • deregister_from_elb.sh͸

    awslabsϦϙͰ഑෍
  39. ApplicationStop • application_stop.shͰదٓRailsΛఀࢭ͢ Δ • ͜ͷ࣌఺ͰELB͔Βִ཭͞Ε͍ͯΔͷͰࢭΊ ͳͯ͘΋ྑ͍

  40. BeforeInstall • ιʔείʔυΛEC2ʹίϐʔ͢Δ • ओʹAnsible(ͱCapistrano)ͷ४උ • appspec.ymlͰrunas: rootΛ๨Εͳ͍

  41. BeforeInstall #!/bin/bash yum list installed python-pip &> /dev/null if [

    $? != 0 ]; then yum install -y python-pip fi pip list | grep -q ansible if [ $? != 0 ]; then pip install ansible fi mkdir -p /data/app chown -R ec2-user:ec2-user /data/app
  42. AfterInstall • ίϐʔͱunzipͷ࣍ʹ࣮ߦ͞ΕΔ • ࣗ෼ͷEC2λάΛಡΜͰઃఆϑΝΠϧΛμ΢ϯ ϩʔυͯ͠ධՁ

  43. AfterInstall #!/bin/bash set -u METADATA=`ec2-describe-instances $(ec2-metadata -i | cut -d"

    " -f 2) --region ap-northeast-1` BUCKET_NAME=`echo "$METADATA" | grep VariablesBucket | cut -f 5` KEY_NAME=`echo "$METADATA" | grep VariablesKey | cut -f 5` sudo aws s3api get-object --bucket $BUCKET_NAME \ --key $KEY_NAME $APPLICATION_VARIABLES_PATH \ --region ap-northeast-1 . $APPLICATION_VARIABLES_PATH /usr/local/bin/ansible-playbook $DESTINATION_PATH/ansible/site.yml \ -i $DESTINATION_PATH/ansible/codedeployhosts --connection=local
  44. ApplicationStart • Railsͷىಈ४උͱىಈΛߦͳ͏ • ࠓճ͸capistranoͰٵऩ͍ͯ͠Δ

  45. ApplicationStart #!/bin/bash set -u . /etc/profile.d/rbenv.sh . $APPLICATION_VARIABLES_PATH cd $DESTINATION_PATH

    rbenv exec bundle exec cap local deploy
  46. ValidateService • ΞϓϦέʔγϣϯͷىಈͱELBొ࿥Λߦͳ͏ • ҎԼͷεΫϦϓτ͕exit code 0ͳΒ࣍ͷ registerwithelb.sh͕૸Δ • registerwithelb.sh΋AWSLabsͰެ։͞

    Ε͍ͯΔ #!/bin/bash curl -s http://localhost/health_check
  47. Cloud Formation Templateͷ࡞੒

  48. ͦΕΛه͢ʹ͸ ༨ന͕଍Γͳ͍

  49. ΍ͬͯΔ͜ͱ • ELBͷ࡞੒ • Launch Configͷ࡞੒ • User DataͰCodeDeploy AgentͷΠϯε

    τʔϧ • Auto Scaling Groupͷ࡞੒ • Route53Ϩίʔυͷ࡞੒ ※CodeDeployϦιʔε͸࡞੒ͯ͠ͳ͍
  50. Launch Config "UserData": { "Fn::Base64": { "Fn::Join": [ "", [

    "#!/bin/bash -xe\n", "yum update -y aws-cfn-bootstrap; yum install -y aws-cli\n", "sed -i 's/Defaults requiretty/Defaults:root !requiretty/g' /etc/sudoers\n", "cd /home/ec2-user/\n", "aws s3 cp 's3://aws-codedeploy-us-east-1/latest/codedeploy-agent.noarch.rpm' . ", "|| error_exit 'Failed to download AWS CodeDeploy Agent.'\n", "yum -y install codedeploy-agent.noarch.rpm || error_exit 'Failed to install AWS CodeDeploy Agent.' \n" ] ] } },
  51. CFn Create Stack

  52. CodeDeployϦιʔεΛखಈͰ࡞੒ • ؀ڥม਺shΛS3ʹஔ͘ • CFnͰͰ͖ͨASGΛࢦఆͯ͠Aplication/ DeploymentGroup࡞੒

  53. σϓϩΠͯ͠ΈΔ

  54. ௥Ճཁ݅: ΑΓҰ૚ͷࣗಈԽ

  55. ྲྀΕ • εΫϦϓτͰσϓϩΠ༻zipΛ࡞੒ • S3ʹ഑ஔ • LambdaͰCodeDeploy

  56. εΫϦϓτͰσϓϩΠ༻zipΛ࡞੒ gulp.task('clean', function() { return del(['./tmp/dest']); }); gulp.task('zip', function() {

    return gulp.src('**') .pipe(excludeGitignore()) .pipe(zip(bundleName + '.zip')) .pipe(gulp.dest('./tmp/dest')); }); gulp.task('upload', function(){ var publisher = awspublish.create({ params: { Bucket: BUCKET } }); return gulp.src('./tmp/dest/' + bundleName + '.zip') .pipe(publisher.publish()) .pipe(awspublish.reporter()); });
  57. S3ʹzipΛஔ͘ͱಈ͘Lambda exports.handler = (event, context, callback) => { const message

    = JSON.parse(event.Records[0].Sns.Message); const params = { applicationName: `my-application`, deploymentGroupName: `my-deployment-group`, revision: { revisionType: 'S3', s3Location: { bucket: message.bucketName, bundleType: 'zip', key: message.keyName } } };
  58. S3ʹzipΛஔ͘ͱಈ͘Lambda codedeploy.createDeployment(params, (err, data) => { if (err) { console.log(err);

    callback(err); } else { callback(null, data); } });
  59. ௥Ճཁ݅: ىಈΛૣ͘͢Δ

  60. AMIΛͱΔ • σϓϩΠঢ়ଶͷAMI࡞੒ • CFnमਖ਼

  61. ௥Ճཁ݅: B-G σϓϩΠ

  62. • ELB+ASGΛDeploymentGroupΛ2ͭ࡞੒ • CodeDeloy੒ޭ࣌ʹSNSܦ༝ͰLambdaΛىಈ • LambdaͰRoute53 ALIASϨίʔυͷ޲͖ઌΛ ม͑Δ

  63. ·ͱΊ

  64. CodeDeployͰҎԼΛୡ੒Մೳ • Ansible΍γΣϧεΫϦϓτͷφϨοδΛస༻ Ͱ͖Δ • χʔζʹ߹ΘͤͨσϓϩΠγεςϜΛ࡞ΕΔ